Beispiel #1
0
        /// <summary>
        /// Convert string to <see cref="CandlestickInterval"/>.
        /// </summary>
        /// <param name="s">The string.</param>
        /// <returns></returns>
        public static CandlestickInterval ToCandlestickInterval(this string s)
        {
            Throw.IfNullOrWhiteSpace(s, nameof(s));

            switch (s.Trim().ToLower())
            {
            case "1m": return(CandlestickInterval.Minute);

            case "3m": return(CandlestickInterval.Minutes_3);

            case "5m": return(CandlestickInterval.Minutes_5);

            case "15m": return(CandlestickInterval.Minutes_15);

            case "30m": return(CandlestickInterval.Minutes_30);

            case "60m":
            case "1h": return(CandlestickInterval.Hour);

            case "2h": return(CandlestickInterval.Hours_2);

            case "4h": return(CandlestickInterval.Hours_4);

            case "8h": return(CandlestickInterval.Hours_8);

            case "12h": return(CandlestickInterval.Hours_12);

            case "24h":
            case "1d": return(CandlestickInterval.Day);

            case "3d": return(CandlestickInterval.Days_3);

            case "1w": return(CandlestickInterval.Week);

            case "1M": return(CandlestickInterval.Month);

            default:
                throw new ArgumentException($"{nameof(ToCandlestickInterval)}: interval not supported: {s}");
            }
        }
        /// <summary>
        /// Get latest (non-compressed) trades.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="symbol"></param>
        /// <param name="limit"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        public static async Task <string> GetTradesAsync(this IBinanceHttpClient client, string symbol, int limit = default, CancellationToken token = default)
        {
            Throw.IfNull(client, nameof(client));
            Throw.IfNullOrWhiteSpace(symbol, nameof(symbol));

            if (client.RateLimiter != null)
            {
                await client.RateLimiter.DelayAsync(token : token)
                .ConfigureAwait(false);
            }

            var request = new BinanceHttpRequest("/api/v1/trades");

            request.AddParameter("symbol", symbol.FormatSymbol());

            if (limit > 0)
            {
                request.AddParameter("limit", limit);
            }

            return(await client.GetAsync(request, token)
                   .ConfigureAwait(false));
        }
        /// <summary>
        /// Ping a user data stream to prevent a time out.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="apiKey"></param>
        /// <param name="listenKey"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        public static async Task <string> UserStreamKeepAliveAsync(this IBinanceHttpClient client, string apiKey, string listenKey, CancellationToken token = default)
        {
            Throw.IfNullOrWhiteSpace(apiKey, nameof(apiKey));
            Throw.IfNullOrWhiteSpace(listenKey, nameof(listenKey));

            if (client.RateLimiter != null)
            {
                await client.RateLimiter.DelayAsync(token : token)
                .ConfigureAwait(false);
            }

            var request = new BinanceHttpRequest("/api/v1/userDataStream")
            {
                ApiKey = apiKey
            };

            request.AddParameter("listenKey", listenKey);

            // TODO
            // A POST will either get back your current stream (and effectively do a PUT) or get back a new stream.
            //return await client.PostAsync(request, token)
            return(await client.PutAsync(request, token)
                   .ConfigureAwait(false));
        }
Beispiel #4
0
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="symbol">The symbol.</param>
        /// <param name="id">The trade ID.</param>
        /// <param name="price">The price.</param>
        /// <param name="quantity">The quantity.</param>
        /// <param name="buyerOrderId">The buyer order ID.</param>
        /// <param name="sellerOrderId">The seller order ID.</param>
        /// <param name="time">The time.</param>
        /// <param name="isBuyerMaker">Is buyer maker.</param>
        /// <param name="isBestPriceMatch">Flag indicating if the trade was the best price match.</param>
        public Trade(
            string symbol,
            long id,
            decimal price,
            decimal quantity,
            long buyerOrderId,
            long sellerOrderId,
            DateTime time,
            bool isBuyerMaker,
            bool isBestPriceMatch)
        {
            Throw.IfNullOrWhiteSpace(symbol, nameof(symbol));

            if (id < 0)
            {
                throw new ArgumentException($"{nameof(Trade)}: ID must not be less than 0.", nameof(id));
            }
            if (price < 0)
            {
                throw new ArgumentException($"{nameof(Trade)}: price must not be less than 0.", nameof(price));
            }
            if (quantity <= 0)
            {
                throw new ArgumentException($"{nameof(Trade)}: quantity must be greater than 0.", nameof(quantity));
            }

            Symbol           = symbol.FormatSymbol();
            Id               = id;
            Price            = price;
            Quantity         = quantity;
            BuyerOrderId     = buyerOrderId;
            SellerOrderId    = sellerOrderId;
            Time             = time;
            IsBuyerMaker     = isBuyerMaker;
            IsBestPriceMatch = isBestPriceMatch;
        }
        /// <summary>
        /// Get the deposit address for an asset.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="user"></param>
        /// <param name="asset"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        public static async Task <string> GetDepositAddressAsync(this IBinanceHttpClient client, IBinanceApiUser user, string asset, CancellationToken token = default)
        {
            Throw.IfNull(client, nameof(client));
            Throw.IfNullOrWhiteSpace(asset, nameof(asset));

            if (client.RateLimiter != null)
            {
                await client.RateLimiter.DelayAsync(token : token)
                .ConfigureAwait(false);
            }

            var request = new BinanceHttpRequest("/wapi/v3/depositAddress.html")
            {
                ApiKey = user.ApiKey
            };

            request.AddParameter("asset", asset.FormatSymbol());

            await client.SignAsync(request, user, token)
            .ConfigureAwait(false);

            return(await client.GetAsync(request, token)
                   .ConfigureAwait(false));
        }
Beispiel #6
0
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="path"></param>
        public BinanceHttpRequest(string path)
        {
            Throw.IfNullOrWhiteSpace(path, nameof(path));

            _path = path;
        }
        /// <summary>
        /// Send in a new order.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="user"></param>
        /// <param name="symbol"></param>
        /// <param name="side"></param>
        /// <param name="type"></param>
        /// <param name="quantity"></param>
        /// <param name="price"></param>
        /// <param name="newClientOrderId">A unique id for the order. Automatically generated if not sent.</param>
        /// <param name="timeInForce"></param>
        /// <param name="stopPrice">Used with STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, and TAKE_PROFIT_LIMIT orders.</param>
        /// <param name="icebergQty">Used with iceberg orders.</param>
        /// <param name="recvWindow"></param>
        /// <param name="isTestOnly">If true, test new order creation and signature/recvWindow; creates and validates a new order but does not send it into the matching engine.</param>
        /// <param name="newOrderRespType">Set the response JSON.</param>
        /// <param name="token"></param>
        /// <returns></returns>
        public static async Task <string> PlaceOrderAsync(this IBinanceHttpClient client, IBinanceApiUser user, string symbol, OrderSide side, OrderType type, decimal quantity, decimal price, string newClientOrderId = null, TimeInForce?timeInForce = null, decimal stopPrice = 0, decimal icebergQty = 0, long recvWindow = default, bool isTestOnly = false, PlaceOrderResponseType newOrderRespType = PlaceOrderResponseType.Result, CancellationToken token = default)
        {
            Throw.IfNull(client, nameof(client));
            Throw.IfNull(user, nameof(user));
            Throw.IfNullOrWhiteSpace(symbol, nameof(symbol));

            if (quantity <= 0)
            {
                throw new ArgumentException("Order quantity must be greater than 0.", nameof(quantity));
            }

            if (recvWindow == default)
            {
                recvWindow = client.DefaultRecvWindow;
            }

            if (user.RateLimiter != null)
            {
                await user.RateLimiter.DelayAsync(token : token)
                .ConfigureAwait(false);
            }

            var request = new BinanceHttpRequest($"/api/v3/order{(isTestOnly ? "/test" : string.Empty)}")
            {
                ApiKey = user.ApiKey
            };

            request.AddParameter("symbol", symbol.FormatSymbol());
            request.AddParameter("side", side.ToString().ToUpperInvariant());
            request.AddParameter("type", type.AsString());
            request.AddParameter("newOrderRespType", newOrderRespType.ToString().ToUpperInvariant());
            request.AddParameter("quantity", quantity);

            if (price > 0)
            {
                request.AddParameter("price", price);
            }

            if (timeInForce.HasValue)
            {
                request.AddParameter("timeInForce", timeInForce.ToString().ToUpperInvariant());
            }

            if (!string.IsNullOrWhiteSpace(newClientOrderId))
            {
                request.AddParameter("newClientOrderId", newClientOrderId);
            }

            if (stopPrice > 0)
            {
                request.AddParameter("stopPrice", stopPrice);
            }

            if (icebergQty > 0)
            {
                // Automatically set time-in-force to GTC if not set.
                if (!timeInForce.HasValue)
                {
                    timeInForce = TimeInForce.GTC;
                }

                if (timeInForce != TimeInForce.GTC)
                {
                    throw new BinanceApiException("Any order with an icebergQty MUST have timeInForce set to GTC.");
                }

                request.AddParameter("icebergQty", icebergQty);
            }

            if (recvWindow > 0)
            {
                request.AddParameter("recvWindow", recvWindow);
            }

            await client.SignAsync(request, user, token)
            .ConfigureAwait(false);

            return(await client.PostAsync(request, token)
                   .ConfigureAwait(false));
        }
        /// <summary>
        /// Get compressed, aggregate trades. Trades that fill at the time, from the same order, with the same price will have the quantity aggregated.
        /// If fromdId, startTime, and endTime are not sent, the most recent aggregate trades will be returned.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="symbol"></param>
        /// <param name="fromId">ID to get aggregate trades from INCLUSIVE.</param>
        /// <param name="limit">Default 500; max 500.</param>
        /// <param name="startTime">Timestamp in ms to get aggregate trades from INCLUSIVE.</param>
        /// <param name="endTime">Timestamp in ms to get aggregate trades until INCLUSIVE.</param>
        /// <param name="token"></param>
        /// <returns></returns>
        public static async Task <string> GetAggregateTradesAsync(this IBinanceHttpClient client, string symbol, long fromId = BinanceApi.NullId, int limit = default, DateTime startTime = default, DateTime endTime = default, CancellationToken token = default)
        {
            Throw.IfNull(client, nameof(client));
            Throw.IfNullOrWhiteSpace(symbol, nameof(symbol));

            if (client.RateLimiter != null)
            {
                await client.RateLimiter.DelayAsync(token : token)
                .ConfigureAwait(false);
            }

            var request = new BinanceHttpRequest("/api/v1/aggTrades");

            request.AddParameter("symbol", symbol.FormatSymbol());

            if (fromId >= 0)
            {
                request.AddParameter("fromId", fromId);
            }

            if (startTime != default)
            {
                if (startTime.Kind != DateTimeKind.Utc)
                {
                    throw new ArgumentException("Date/Time must be UTC.", nameof(startTime));
                }

                request.AddParameter("startTime", startTime.ToTimestamp());
            }

            if (endTime != default)
            {
                if (endTime.Kind != DateTimeKind.Utc)
                {
                    throw new ArgumentException("Date/Time must be UTC.", nameof(endTime));
                }

                request.AddParameter("endTime", endTime.ToTimestamp());
            }

            if (startTime == default || endTime == default)
            {
                if (limit > 0)
                {
                    request.AddParameter("limit", limit);
                }
            }
            else
            {
                if (endTime < startTime)
                {
                    throw new ArgumentException($"Time ({nameof(endTime)}) must not be less than {nameof(startTime)} ({startTime}).", nameof(endTime));
                }

                if ((endTime - startTime).Duration() >= TimeSpan.FromHours(24))
                {
                    throw new ArgumentException($"The interval between {nameof(startTime)} and {nameof(endTime)} must be less than 24 hours.", nameof(endTime));
                }
            }

            return(await client.GetAsync(request, token)
                   .ConfigureAwait(false));
        }
Beispiel #9
0
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="user"></param>
        /// <param name="symbol"></param>
        /// <param name="id"></param>
        /// <param name="clientOrderId"></param>
        /// <param name="price"></param>
        /// <param name="originalQuantity"></param>
        /// <param name="executedQuantity"></param>
        /// <param name="status"></param>
        /// <param name="timeInForce"></param>
        /// <param name="orderType"></param>
        /// <param name="orderSide"></param>
        /// <param name="stopPrice"></param>
        /// <param name="icebergQuantity"></param>
        /// <param name="time"></param>
        /// <param name="isWorking"></param>
        /// <param name="fills"></param>
        public Order(
            IBinanceApiUser user,
            string symbol,
            long id,
            string clientOrderId,
            decimal price,
            decimal originalQuantity,
            decimal executedQuantity,
            OrderStatus status,
            TimeInForce timeInForce,
            OrderType orderType,
            OrderSide orderSide,
            decimal stopPrice,
            decimal icebergQuantity,
            DateTime time,
            bool isWorking,
            IEnumerable <Fill> fills = null)
            : this(user)
        {
            Throw.IfNullOrWhiteSpace(symbol, nameof(symbol));

            if (id < 0)
            {
                throw new ArgumentException($"{nameof(Order)}: ID must not be less than 0.", nameof(id));
            }

            if (price < 0)
            {
                throw new ArgumentException($"{nameof(Order)}: price must not be less than 0.", nameof(price));
            }
            if (stopPrice < 0)
            {
                throw new ArgumentException($"{nameof(Order)}: price must not be less than 0.", nameof(stopPrice));
            }

            if (originalQuantity < 0)
            {
                throw new ArgumentException($"{nameof(Order)}: quantity must not be less than 0.", nameof(originalQuantity));
            }
            if (executedQuantity < 0)
            {
                throw new ArgumentException($"{nameof(Order)}: quantity must not be less than 0.", nameof(executedQuantity));
            }
            if (icebergQuantity < 0)
            {
                throw new ArgumentException($"{nameof(Order)}: quantity must not be less than 0.", nameof(icebergQuantity));
            }

            Symbol           = symbol.FormatSymbol();
            Id               = id;
            ClientOrderId    = clientOrderId;
            Price            = price;
            OriginalQuantity = originalQuantity;
            ExecutedQuantity = executedQuantity;
            Status           = status;
            TimeInForce      = timeInForce;
            Type             = orderType;
            Side             = orderSide;
            StopPrice        = stopPrice;
            IcebergQuantity  = icebergQuantity;
            Time             = time;
            IsWorking        = isWorking;

            Fills = fills ?? new Fill[] { };
        }
Beispiel #10
0
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="symbol"></param>
        /// <param name="period"></param>
        /// <param name="priceChange"></param>
        /// <param name="priceChangePercent"></param>
        /// <param name="weightedAveragePrice"></param>
        /// <param name="previousClosePrice"></param>
        /// <param name="lastPrice"></param>
        /// <param name="lastQuantity"></param>
        /// <param name="bidPrice"></param>
        /// <param name="bidQuantity"></param>
        /// <param name="askPrice"></param>
        /// <param name="askQuantity"></param>
        /// <param name="openPrice"></param>
        /// <param name="highPrice"></param>
        /// <param name="lowPrice"></param>
        /// <param name="volume"></param>
        /// <param name="quoteVolume"></param>
        /// <param name="openTime"></param>
        /// <param name="closeTime"></param>
        /// <param name="firstTradeId"></param>
        /// <param name="lastTradeId"></param>
        /// <param name="tradeCount"></param>
        public SymbolStatistics(
            string symbol,
            TimeSpan period,
            decimal priceChange,
            decimal priceChangePercent,
            decimal weightedAveragePrice,
            decimal previousClosePrice,
            decimal lastPrice,
            decimal lastQuantity,
            decimal bidPrice,
            decimal bidQuantity,
            decimal askPrice,
            decimal askQuantity,
            decimal openPrice,
            decimal highPrice,
            decimal lowPrice,
            decimal volume,
            decimal quoteVolume,
            DateTime openTime,
            DateTime closeTime,
            long firstTradeId,
            long lastTradeId,
            long tradeCount)
        {
            Throw.IfNullOrWhiteSpace(symbol, nameof(symbol));

            if (weightedAveragePrice < 0)
            {
                throw new ArgumentException($"{nameof(SymbolStatistics)}: price must not be less than 0.", nameof(weightedAveragePrice));
            }
            if (previousClosePrice < 0)
            {
                throw new ArgumentException($"{nameof(SymbolStatistics)}: price must not be less than 0.", nameof(previousClosePrice));
            }
            if (lastPrice < 0)
            {
                throw new ArgumentException($"{nameof(SymbolStatistics)}: price must not be less than 0.", nameof(lastPrice));
            }
            if (lastQuantity < 0)
            {
                throw new ArgumentException($"{nameof(SymbolStatistics)}: quantity must not be less than 0.", nameof(lastQuantity));
            }
            if (bidPrice < 0)
            {
                throw new ArgumentException($"{nameof(SymbolStatistics)}: price must not be less than 0.", nameof(bidPrice));
            }
            if (bidQuantity < 0)
            {
                throw new ArgumentException($"{nameof(SymbolStatistics)}: quantity must not be less than 0.", nameof(bidQuantity));
            }
            if (askPrice < 0)
            {
                throw new ArgumentException($"{nameof(SymbolStatistics)}: price must not be less than 0.", nameof(askPrice));
            }
            if (askQuantity < 0)
            {
                throw new ArgumentException($"{nameof(SymbolStatistics)}: quantity must not be less than 0.", nameof(askQuantity));
            }
            if (openPrice < 0)
            {
                throw new ArgumentException($"{nameof(SymbolStatistics)}: price must not be less than 0.", nameof(openPrice));
            }
            if (highPrice < 0)
            {
                throw new ArgumentException($"{nameof(SymbolStatistics)}: price must not be less than 0.", nameof(highPrice));
            }
            if (lowPrice < 0)
            {
                throw new ArgumentException($"{nameof(SymbolStatistics)}: price must not be less than 0.", nameof(lowPrice));
            }
            if (lowPrice > highPrice)
            {
                throw new ArgumentException($"{nameof(SymbolStatistics)}: low price must be less than or equal to high price.", nameof(lowPrice));
            }

            if (volume < 0)
            {
                throw new ArgumentException($"{nameof(SymbolStatistics)}: volume must be greater than or equal to 0.", nameof(volume));
            }
            if (quoteVolume < 0)
            {
                throw new ArgumentException($"{nameof(SymbolStatistics)}: volume must be greater than or equal to 0.", nameof(quoteVolume));
            }

            if (openTime >= closeTime)
            {
                throw new ArgumentException($"{nameof(SymbolStatistics)}: open time must be less than close time.", nameof(openTime));
            }

            //if (firstTradeId < 0) // ...fails due to symbol 'ETC' (?) with -1 trade ID.
            //    throw new ArgumentException($"{nameof(Symbol24hrStats)} trade ID must be greater than 0.", nameof(firstTradeId));
            //if (lastTradeId < 0) // ...fails due to symbol 'ETC' (?) with -1 trade ID.
            //    throw new ArgumentException($"{nameof(Symbol24hrStats)} trade ID must be greater than 0.", nameof(lastTradeId));
            if (lastTradeId < firstTradeId)
            {
                throw new ArgumentException($"{nameof(SymbolStatistics)}: last trade ID must be greater than or equal to first trade ID.", nameof(lastTradeId));
            }

            if (tradeCount < 0)
            {
                throw new ArgumentException($"{nameof(SymbolStatistics)}: trade count must be greater than or equal to 0.", nameof(tradeCount));
            }

            // TODO: Binance API stream occasionally returns invalid trade counts...
            //if (tradeCount != 0 && tradeCount != lastTradeId - firstTradeId + 1)
            //    throw new ArgumentException($"{nameof(SymbolStatistics)}: trade count must be equal to last trade ID - first trade ID + 1.", nameof(tradeCount));

            Symbol               = symbol.FormatSymbol();
            Period               = period;
            PriceChange          = priceChange;
            PriceChangePercent   = priceChangePercent;
            WeightedAveragePrice = weightedAveragePrice;
            PreviousClosePrice   = previousClosePrice;
            LastPrice            = lastPrice;
            LastQuantity         = lastQuantity;
            BidPrice             = bidPrice;
            BidQuantity          = bidQuantity;
            AskPrice             = askPrice;
            AskQuantity          = askQuantity;
            OpenPrice            = openPrice;
            HighPrice            = highPrice;
            LowPrice             = lowPrice;
            Volume               = volume;
            QuoteVolume          = quoteVolume;
            OpenTime             = openTime;
            CloseTime            = closeTime;
            FirstTradeId         = firstTradeId;
            LastTradeId          = lastTradeId;
            TradeCount           = tradeCount;
        }