コード例 #1
0
        protected override async Task <IWebSocket> OnUserDataWebSocketAsync(Action <object> callback)
        {
            return(await ConnectPublicWebSocketAsync("/", async (_socket, msg) =>
            {
                var token = msg.ToStringFromUTF8();
                var response = JsonConvert.DeserializeObject <BaseMessage>(token);
                switch (response.Type)
                {
                case ResponseType.Subscriptions:
                    var subscription = JsonConvert.DeserializeObject <Subscription>(token);
                    if (subscription.Channels == null || !subscription.Channels.Any())
                    {
                        Trace.WriteLine($"{nameof(OnUserDataWebSocketAsync)}() no channels subscribed");
                    }
                    else
                    {
                        Trace.WriteLine($"{nameof(OnUserDataWebSocketAsync)}() subscribed to " +
                                        $"{string.Join(",", subscription.Channels.Select(c => c.ToString()))}");
                    }
                    break;

                case ResponseType.Ticker:
                    throw new NotImplementedException($"Not expecting type {response.Type} in {nameof(OnUserDataWebSocketAsync)}()");

                case ResponseType.Snapshot:
                    throw new NotImplementedException($"Not expecting type {response.Type} in {nameof(OnUserDataWebSocketAsync)}()");

                case ResponseType.L2Update:
                    throw new NotImplementedException($"Not expecting type {response.Type} in {nameof(OnUserDataWebSocketAsync)}()");

                case ResponseType.Heartbeat:
                    var heartbeat = JsonConvert.DeserializeObject <Heartbeat>(token);
                    Trace.WriteLine($"{nameof(OnUserDataWebSocketAsync)}() heartbeat received {heartbeat}");
                    break;

                case ResponseType.Received:
                    var received = JsonConvert.DeserializeObject <Received>(token);
                    callback(received.ExchangeOrderResult);
                    break;

                case ResponseType.Open:
                    var open = JsonConvert.DeserializeObject <Open>(token);
                    callback(open.ExchangeOrderResult);
                    break;

                case ResponseType.Done:
                    var done = JsonConvert.DeserializeObject <Done>(token);
                    callback(done.ExchangeOrderResult);
                    break;

                case ResponseType.Match:
                    var match = JsonConvert.DeserializeObject <Match>(token);
                    callback(match.ExchangeOrderResult);
                    break;

                case ResponseType.LastMatch:
                    //var lastMatch = JsonConvert.DeserializeObject<LastMatch>(token);
                    throw new NotImplementedException($"Not expecting type {response.Type} in {nameof(OnUserDataWebSocketAsync)}()");

                case ResponseType.Error:
                    var error = JsonConvert.DeserializeObject <Error>(token);
                    throw new APIException($"{error.Reason}: {error.Message}");

                case ResponseType.Change:
                    var change = JsonConvert.DeserializeObject <Change>(token);
                    callback(change.ExchangeOrderResult);
                    break;

                case ResponseType.Activate:
                    var activate = JsonConvert.DeserializeObject <Activate>(token);
                    callback(activate.ExchangeOrderResult);
                    break;

                case ResponseType.Status:
                    //var status = JsonConvert.DeserializeObject<Status>(token);
                    throw new NotImplementedException($"Not expecting type {response.Type} in {nameof(OnUserDataWebSocketAsync)}()");

                default:
                    throw new NotImplementedException($"Not expecting type {response.Type} in {nameof(OnUserDataWebSocketAsync)}()");
                }
            }, async (_socket) =>
            {
                var marketSymbols = (await GetMarketSymbolsAsync()).ToArray();
                var nonce = await GetNoncePayloadAsync();
                string timestamp = nonce["nonce"].ToStringInvariant();
                byte[] secret = CryptoUtility.ToBytesBase64Decode(PrivateApiKey);
                string toHash = timestamp + "GET" + "/users/self/verify";
                var subscribeRequest = new
                {
                    type = "subscribe",
                    channels = new object[]
                    {
                        new
                        {
                            name = "user",
                            product_ids = marketSymbols,
                        }
                    },
                    signature = CryptoUtility.SHA256SignBase64(toHash, secret),                     // signature base 64 string
                    key = PublicApiKey.ToUnsecureString(),
                    passphrase = CryptoUtility.ToUnsecureString(Passphrase),
                    timestamp = timestamp
                };
                await _socket.SendMessageAsync(subscribeRequest);
            }));
        }
コード例 #2
0
        protected override async Task <IEnumerable <ExchangeOrderResult> > OnGetCompletedOrderDetailsAsync(string marketSymbol = null, DateTime?afterDate = null)
        {
            // We can increase the number of orders returned by including a limit parameter if desired
            List <ExchangeOrderResult> orders = new List <ExchangeOrderResult>();
            var payload = await GetNoncePayloadAsync();

            payload.Add("openClosed", "CLOSED");   // returns both closed and cancelled
            if (marketSymbol != null)
            {
                payload.Add("currencyPair", marketSymbol);
            }
            if (afterDate != null)
            {
                payload.Add("issuedFrom", ((DateTime)afterDate).UnixTimestampFromDateTimeMilliseconds());
            }

            JToken token = await MakeJsonRequestAsync <JToken>("/exchange/client_orders?" + CryptoUtility.GetFormForPayload(payload, false), null, await GetNoncePayloadAsync());

            foreach (JToken order in token)
            {
                orders.Add(ParseClientOrder(order));
            }
            return(orders);
        }
コード例 #3
0
        protected override async Task <IEnumerable <ExchangeTransaction> > OnGetDepositHistoryAsync(string currency)
        {
            List <ExchangeTransaction> deposits = new List <ExchangeTransaction>();

            var payload = await GetNoncePayloadAsync();

            payload.Add("start", CryptoUtility.UtcNow.AddYears(-1).UnixTimestampFromDateTimeMilliseconds()); // required. Arbitrarily going with 1 year
            payload.Add("end", CryptoUtility.UtcNow.UnixTimestampFromDateTimeMilliseconds());                // also required
            payload.Add("types", "DEPOSIT,WITHDRAWAL");                                                      // opting to return both deposits and withdraws.

            // We can also include trades and orders with this call (which makes 3 ways to return the same data)
            JToken token = await MakeJsonRequestAsync <JToken>("/exchange/payment/history/transactions?" + CryptoUtility.GetFormForPayload(payload, false), null, await GetNoncePayloadAsync());

            foreach (JToken tx in token)
            {
                deposits.Add(ParseTransaction(tx));
            }

            return(deposits);
        }
コード例 #4
0
 /// <summary>
 /// Convert seconds to a period string, or throw exception if seconds invalid. Example: 60 seconds becomes 1m.
 /// </summary>
 /// <param name="seconds">Seconds</param>
 /// <returns>Period string</returns>
 public virtual string PeriodSecondsToString(int seconds) => CryptoUtility.SecondsToPeriodString(seconds);
コード例 #5
0
        protected decimal ClampOrderPrice(string symbol, decimal outputPrice)
        {
            ExchangeMarket market = this.GetExchangeMarket(symbol);

            return(market == null ? outputPrice : CryptoUtility.ClampDecimal(market.MinPrice, market.MaxPrice, market.PriceStepSize, outputPrice));
        }
コード例 #6
0
        protected override async Task OnGetHistoricalTradesAsync(System.Func <IEnumerable <ExchangeTrade>, bool> callback, string symbol, DateTime?sinceDateTime = null)
        {
            /* [ {
            *  "a": 26129,         // Aggregate tradeId
            *       "p": "0.01633102",  // Price
            *       "q": "4.70443515",  // Quantity
            *       "f": 27781,         // First tradeId
            *       "l": 27781,         // Last tradeId
            *       "T": 1498793709153, // Timestamp
            *       "m": true,          // Was the buyer the maker?
            *       "M": true           // Was the trade the best price match?
            *  } ] */

            symbol = NormalizeSymbol(symbol);
            string baseUrl = "/aggTrades?symbol=" + symbol;
            string url;
            List <ExchangeTrade> trades = new List <ExchangeTrade>();
            DateTime             cutoff;

            if (sinceDateTime == null)
            {
                cutoff = DateTime.UtcNow;
            }
            else
            {
                cutoff        = sinceDateTime.Value;
                sinceDateTime = DateTime.UtcNow;
            }
            url = baseUrl;

            while (true)
            {
                JArray obj = await MakeJsonRequestAsync <Newtonsoft.Json.Linq.JArray>(url);

                if (obj == null || obj.Count == 0)
                {
                    break;
                }
                if (sinceDateTime != null)
                {
                    sinceDateTime = CryptoUtility.UnixTimeStampToDateTimeMilliseconds(obj.First["T"].ConvertInvariant <long>());
                    if (sinceDateTime.Value < cutoff)
                    {
                        sinceDateTime = null;
                    }
                }
                if (sinceDateTime != null)
                {
                    url = baseUrl + "&startTime=" + ((long)CryptoUtility.UnixTimestampFromDateTimeMilliseconds(sinceDateTime.Value - TimeSpan.FromHours(1.0))).ToStringInvariant() +
                          "&endTime=" + ((long)CryptoUtility.UnixTimestampFromDateTimeMilliseconds(sinceDateTime.Value)).ToStringInvariant();
                }
                foreach (JToken token in obj)
                {
                    // TODO: Binance doesn't provide a buy or sell type, I've put in a request for them to add this
                    trades.Add(new ExchangeTrade
                    {
                        Amount    = token["q"].ConvertInvariant <decimal>(),
                        Price     = token["p"].ConvertInvariant <decimal>(),
                        Timestamp = CryptoUtility.UnixTimeStampToDateTimeMilliseconds(token["T"].ConvertInvariant <long>()),
                        Id        = token["a"].ConvertInvariant <long>(),
                        IsBuy     = token["m"].ConvertInvariant <bool>()
                    });
                }
                trades.Sort((t1, t2) => t1.Timestamp.CompareTo(t2.Timestamp));
                if (!callback(trades))
                {
                    break;
                }
                trades.Clear();
                if (sinceDateTime == null)
                {
                    break;
                }
                await Task.Delay(1000);
            }
        }
コード例 #7
0
        /// <summary>
        /// Clamp price using market info. If necessary, a network request will be made to retrieve symbol metadata.
        /// </summary>
        /// <param name="marketSymbol">Market Symbol</param>
        /// <param name="outputPrice">Price</param>
        /// <returns>Clamped price</returns>
        protected async Task <decimal> ClampOrderPrice(string marketSymbol, decimal outputPrice)
        {
            ExchangeMarket market = await GetExchangeMarketFromCacheAsync(marketSymbol);

            return(market == null ? outputPrice : CryptoUtility.ClampDecimal(market.MinPrice, market.MaxPrice, market.PriceStepSize, outputPrice));
        }
コード例 #8
0
        protected override IWebSocket OnGetDeltaOrderBookWebSocket(Action <ExchangeOrderBook> callback, int maxCount = 20, params string[] marketSymbols)
        {
            if (marketSymbols == null || marketSymbols.Length == 0)
            {
                marketSymbols = GetMarketSymbolsAsync().Sync().ToArray();
            }
            string combined = string.Join("/", marketSymbols.Select(s => this.NormalizeMarketSymbol(s).ToLowerInvariant() + "@depth@100ms"));

            return(ConnectWebSocket($"/stream?streams={combined}", (_socket, msg) =>
            {
                string json = msg.ToStringFromUTF8();
                var update = JsonConvert.DeserializeObject <MultiDepthStream>(json);
                string marketSymbol = update.Data.MarketSymbol;
                ExchangeOrderBook book = new ExchangeOrderBook {
                    SequenceId = update.Data.FinalUpdate, MarketSymbol = marketSymbol, LastUpdatedUtc = CryptoUtility.UnixTimeStampToDateTimeMilliseconds(update.Data.EventTime)
                };
                foreach (List <object> ask in update.Data.Asks)
                {
                    var depth = new ExchangeOrderPrice {
                        Price = ask[0].ConvertInvariant <decimal>(), Amount = ask[1].ConvertInvariant <decimal>()
                    };
                    book.Asks[depth.Price] = depth;
                }
                foreach (List <object> bid in update.Data.Bids)
                {
                    var depth = new ExchangeOrderPrice {
                        Price = bid[0].ConvertInvariant <decimal>(), Amount = bid[1].ConvertInvariant <decimal>()
                    };
                    book.Bids[depth.Price] = depth;
                }
                callback(book);
                return Task.CompletedTask;
            }));
        }
コード例 #9
0
        protected override async Task ProcessRequestAsync(IHttpWebRequest request, Dictionary <string, object> payload)
        {
            if (CanMakeAuthenticatedRequest(payload))
            {
                request.AddHeader("KC-API-KEY", PublicApiKey.ToUnsecureString());
                request.AddHeader("KC-API-TIMESTAMP", payload["nonce"].ToStringInvariant());
                request.AddHeader("KC-API-PASSPHRASE", CryptoUtility.SHA256Sign(Passphrase.ToUnsecureString(), PrivateApiKey.ToUnsecureString(), true));
                var endpoint = request.RequestUri.PathAndQuery;
                //For Gets, Deletes, no need to add the parameters in JSON format
                var message = "";
                var sig     = "";
                if (request.Method == "GET" || request.Method == "DELETE")
                {
                    //Request will be a querystring
                    message = string.Format("{0}{1}{2}", payload["nonce"], request.Method, endpoint);
                    sig     = CryptoUtility.SHA256Sign(message, PrivateApiKey.ToUnsecureString(), true);
                }
                else if (request.Method == "POST")
                {
                    message = string.Format("{0}{1}{2}{3}", payload["nonce"], request.Method, endpoint, CryptoUtility.GetJsonForPayload(payload, true));
                    sig     = CryptoUtility.SHA256Sign(message, PrivateApiKey.ToUnsecureString(), true);
                }
                request.AddHeader("KC-API-KEY-VERSION", 2.ToStringInvariant());
                request.AddHeader("KC-API-SIGN", sig);
            }

            if (request.Method == "POST")
            {
                string msg     = CryptoUtility.GetJsonForPayload(payload, true);
                byte[] content = msg.ToBytesUTF8();
                await request.WriteAllAsync(content, 0, content.Length);
            }
        }
コード例 #10
0
        // DONE
        protected override async Task <ExchangeOrderResult> OnPlaceOrderAsync(ExchangeOrderRequest order)
        {
            // In Aquanow market order, when buying crypto the amount of crypto that is bought is the receiveQuantity
            // and when selling the amount of crypto that is sold is the deliverQuantity
            string amountParameter = order.IsBuy ? "receiveQuantity" : "deliverQuantity";
            string amountReceived  = order.IsBuy ? "deliverQuantity" : "receiveQuantity";
            string feesCurrency    = order.IsBuy ? order.MarketSymbol.Substring(0, order.MarketSymbol.IndexOf('-')) : order.MarketSymbol.Substring(order.MarketSymbol.IndexOf('-') + 1);
            var    payload         = await GetNoncePayloadAsync();

            payload["ticker"]        = order.MarketSymbol;
            payload["tradeSide"]     = order.IsBuy ? "buy" : "sell";
            payload[amountParameter] = order.Amount;
            order.ExtraParameters.CopyTo(payload);
            JToken token = await MakeJsonRequestAsync <JToken>("/trades/v1/market", null, payload, "POST");

            var orderDetailsPayload = await GetNoncePayloadAsync();

            //{
            //   "type": "marketOrderSubmitAck",
            //   "payload": {
            //     "orderId": "cfXXXXXX-56ce-4df8-9f1e-729e87bf54d8",
            //     "receiveCurrency": "BTC",
            //     "receiveQuantity": 0.00004,
            //     "deliverCurrency": "USD",
            //     "deliverQuantity": 0.369124,
            //     "fee": 0.000001
            //   }
            //}

            JToken result = await MakeJsonRequestAsync <JToken>($"/trades/v1/order?orderId={token["payload"]["orderId"].ToStringInvariant()}", null, orderDetailsPayload, "GET");

            // {
            //   "priceArrival": 9223.5,
            //   "orderId": "24cf77ad-7e93-44d7-86f8-b9d9a046b008",
            //   "remainingQtyBase": 0,
            //   "tradeSize": 0.0004,
            //   "exchangeOrderId": "-",
            //   "tradePriceAvg": 9223.5,
            //   "fillPct": 100,
            //   "finalizeReturnedQtyBase": 0,
            //   "tradeSide": "buy",
            //   "exchangeClientOrderId": "-",
            //   "tradeTime": 1594681810754,
            //   "childOrderCount": 0,
            //   "fillFeeQuote": 0,
            //   "itemDateTime": 1594681811719,
            //   "baseSymbol": "USD",
            //   "strategy": "MARKET",
            //   "fillQtyQuote": 0.0004,
            //   "usernameRef": "-",
            //   "fillQtyBase": 3.6894,
            //   "priceMarket": "-",
            //   "symbol": "BTC-USD",
            //   "tradeStatus": "COMPLETE",
            //   "commissionRate": 20,
            //   "createdAt": 1594681810756,
            //   "message": "-",
            //   "priceLimit": 9223.5,
            //   "quoteSymbol": "BTC",
            //   "remainingQtyQuote": 0,
            //   "orderIdParent": "24cf77ad-7e93-44d7-86f8-b9d9a046b008",
            //   "orderType": "parentOrder",
            //   "updatedAt": 1594681811941,
            //   "tradeDuration": 0,
            //   "username": "******",
            //   "fillFeeQuoteAqua": 0.0000001
            // }
            ExchangeOrderResult orderDetails = new ExchangeOrderResult
            {
                OrderId      = result["orderId"].ToStringInvariant(),
                AmountFilled = result["fillQtyQuote"].ToStringInvariant().ConvertInvariant <decimal>(),
                Amount       = payload[amountParameter].ConvertInvariant <decimal>(),
                OrderDate    = CryptoUtility.UnixTimeStampToDateTimeMilliseconds(result["tradeTime"].ConvertInvariant <double>()),
                Message      = result["message"].ToStringInvariant(),
                IsBuy        = order.IsBuy,
                Fees         = token["payload"]["fee"].ConvertInvariant <decimal>(),
                FeesCurrency = feesCurrency,
                MarketSymbol = order.MarketSymbol,
                Price        = result["priceArrival"].ToStringInvariant().ConvertInvariant <decimal>(),
            };

            switch (result["tradeStatus"].ToStringInvariant())
            {
            case "COMPLETE":
                orderDetails.AveragePrice = result["tradePriceAvg"].ToStringInvariant().ConvertInvariant <decimal>();
                orderDetails.Result       = ExchangeAPIOrderResult.Filled;
                break;

            default:
                orderDetails.Result = ExchangeAPIOrderResult.Error;
                break;
            }
            return(orderDetails);
        }
コード例 #11
0
        /// <summary>
        /// This is a private call on Kucoin and therefore requires an API Key + API Secret. Calling this without authorization will cause an exception
        /// </summary>
        /// <param name="symbol"></param>
        /// <param name="periodSeconds"></param>
        /// <param name="startDate"></param>
        /// <param name="endDate"></param>
        /// <param name="limit"></param>
        /// <returns></returns>
        protected override async Task <IEnumerable <MarketCandle> > OnGetCandlesAsync(string symbol, int periodSeconds, DateTime?startDate = null, DateTime?endDate = null, int?limit = null)
        {
            List <MarketCandle> candles = new List <MarketCandle>();

            string periodString;

            if (periodSeconds <= 60)
            {
                periodString = "1"; periodSeconds = 60;
            }
            else if (periodSeconds <= 300)
            {
                periodString = "5"; periodSeconds = 300;
            }
            else if (periodSeconds <= 900)
            {
                periodString = "15"; periodSeconds = 900;
            }
            else if (periodSeconds <= 1800)
            {
                periodString = "30"; periodSeconds = 1800;
            }
            else if (periodSeconds <= 3600)
            {
                periodString = "60"; periodSeconds = 3600;
            }
            else if (periodSeconds <= 86400)
            {
                periodString = "D"; periodSeconds = 86400;
            }
            else
            {
                periodString = "W"; periodSeconds = 604800;
            }

            endDate   = endDate ?? DateTime.UtcNow;
            startDate = startDate ?? DateTime.UtcNow.AddDays(-1);

            // this is a little tricky. The call is private, but not a POST. We need the payload for the sig, but also for the uri
            // so, we'll do both... This is the only ExchangeAPI public call (private on Kucoin) like this.
            var payload = await GetNoncePayloadAsync();

            payload.Add("symbol", symbol);
            payload.Add("resolution", periodString);
            payload.Add("from", (long)startDate.Value.UnixTimestampFromDateTimeSeconds());        // the nonce is milliseconds, this is seconds without decimal
            payload.Add("to", (long)endDate.Value.UnixTimestampFromDateTimeSeconds());            // the nonce is milliseconds, this is seconds without decimal
            var addPayload = CryptoUtility.GetFormForPayload(payload, false);

            // The results of this Kucoin API call are also a mess. 6 different arrays (c,t,v,h,l,o) with the index of each shared for the candle values
            // It doesn't use their standard error format...
            JToken token = await MakeJsonRequestAsync <JToken>("/open/chart/history?" + addPayload, null, payload);

            if (token != null && token.HasValues && token["s"].ToStringInvariant() == "ok")
            {
                int childCount = token["c"].Count();
                for (int i = 0; i < childCount; i++)
                {
                    candles.Add(new MarketCandle
                    {
                        ExchangeName    = this.Name,
                        Name            = symbol,
                        PeriodSeconds   = periodSeconds,
                        Timestamp       = DateTimeOffset.FromUnixTimeSeconds(token["t"][i].ConvertInvariant <long>()).DateTime,
                        ClosePrice      = token["c"][i].ConvertInvariant <decimal>(),
                        HighPrice       = token["h"][i].ConvertInvariant <decimal>(),
                        LowPrice        = token["l"][i].ConvertInvariant <decimal>(),
                        OpenPrice       = token["o"][i].ConvertInvariant <decimal>(),
                        ConvertedVolume = token["v"][i].ConvertInvariant <double>(),
                        BaseVolume      = token["v"][i].ConvertInvariant <double>() * token["c"][i].ConvertInvariant <double>()
                    });
                }
            }
            return(candles);
        }
コード例 #12
0
 /// <summary>
 /// Update the trade info via API
 /// </summary>
 public void Update()
 {
     Ticker       = ExchangeInfo.API.GetTicker(Symbol);
     RecentTrades = ExchangeInfo.API.GetRecentTrades(Symbol).ToArray();
     if (RecentTrades.Length == 0)
     {
         Trade = new Trade();
     }
     else
     {
         Trade = new Trade {
             Amount = (float)RecentTrades[RecentTrades.Length - 1].Amount, Price = (float)RecentTrades[RecentTrades.Length - 1].Price, Ticks = (long)CryptoUtility.UnixTimestampFromDateTimeMilliseconds(RecentTrades[RecentTrades.Length - 1].Timestamp)
         };
     }
     Orders = ExchangeInfo.API.GetOrderBook(Symbol);
 }
コード例 #13
0
        protected override IWebSocket OnGetTickersWebSocket(Action <IReadOnlyCollection <KeyValuePair <string, ExchangeTicker> > > callback, params string[] symbols)
        {
            void innerCallback(string json)
            {
                #region sample json

                /*
                 * {
                 *  Nonce : int,
                 *  Deltas :
                 *  [
                 *      {
                 *          MarketName     : string,
                 *          High           : decimal,
                 *          Low            : decimal,
                 *          Volume         : decimal,
                 *          Last           : decimal,
                 *          BaseVolume     : decimal,
                 *          TimeStamp      : date,
                 *          Bid            : decimal,
                 *          Ask            : decimal,
                 *          OpenBuyOrders  : int,
                 *          OpenSellOrders : int,
                 *          PrevDay        : decimal,
                 *          Created        : date
                 *      }
                 *  ]
                 * }
                 */
                #endregion

                var    freshTickers = new Dictionary <string, ExchangeTicker>(StringComparer.OrdinalIgnoreCase);
                JToken token        = JToken.Parse(json);
                token = token["D"];
                foreach (JToken ticker in token)
                {
                    string marketName = ticker["M"].ToStringInvariant();
                    var(baseCurrency, quoteCurrency) = ExchangeMarketSymbolToCurrencies(marketName);
                    decimal  last = ticker["l"].ConvertInvariant <decimal>();
                    decimal  ask  = ticker["A"].ConvertInvariant <decimal>();
                    decimal  bid  = ticker["B"].ConvertInvariant <decimal>();
                    decimal  baseCurrencyVolume  = ticker["V"].ConvertInvariant <decimal>();
                    decimal  quoteCurrencyVolume = ticker["m"].ConvertInvariant <decimal>();//NOTE: Bittrex uses the term BaseVolume when referring to QuoteCurrencyVolume
                    DateTime timestamp           = CryptoUtility.UnixTimeStampToDateTimeMilliseconds(ticker["T"].ConvertInvariant <long>());
                    var      t = new ExchangeTicker
                    {
                        MarketSymbol = marketName,
                        Ask          = ask,
                        Bid          = bid,
                        Last         = last,
                        Volume       = new ExchangeVolume
                        {
                            BaseCurrencyVolume  = baseCurrencyVolume,
                            BaseCurrency        = baseCurrency,
                            QuoteCurrencyVolume = quoteCurrencyVolume,
                            QuoteCurrency       = quoteCurrency,
                            Timestamp           = timestamp
                        }
                    };
                    freshTickers[marketName] = t;
                }
                callback(freshTickers);
            }

            return(new BittrexWebSocketManager().SubscribeToSummaryDeltas(innerCallback));
        }
コード例 #14
0
 /// <summary>
 /// Return a rounded amount if needed
 /// </summary>
 /// <returns>Rounded amount or amount if no rounding is needed</returns>
 public decimal RoundAmount()
 {
     return(this.ShouldRoundAmount ? CryptoUtility.RoundAmount(this.Amount) : this.Amount);
 }
コード例 #15
0
        protected override async Task OnGetHistoricalTradesAsync(Func <IEnumerable <ExchangeTrade>, bool> callback, string symbol, DateTime?startDate = null, DateTime?endDate = null)
        {
            const int            maxCount = 100;
            string               baseUrl  = "/trades/t" + symbol + "/hist?sort=" + (startDate == null ? "-1" : "1") + "&limit=" + maxCount;
            string               url;
            List <ExchangeTrade> trades = new List <ExchangeTrade>();

            decimal[][] tradeChunk;
            while (true)
            {
                url = baseUrl;
                if (startDate != null)
                {
                    url += "&start=" + (long)CryptoUtility.UnixTimestampFromDateTimeMilliseconds(startDate.Value);
                }
                tradeChunk = await MakeJsonRequestAsync <decimal[][]>(url);

                if (tradeChunk == null || tradeChunk.Length == 0)
                {
                    break;
                }
                if (startDate != null)
                {
                    startDate = CryptoUtility.UnixTimeStampToDateTimeMilliseconds((double)tradeChunk[tradeChunk.Length - 1][1]);
                }
                foreach (decimal[] tradeChunkPiece in tradeChunk)
                {
                    trades.Add(new ExchangeTrade {
                        Amount = Math.Abs(tradeChunkPiece[2]), IsBuy = tradeChunkPiece[2] > 0m, Price = tradeChunkPiece[3], Timestamp = CryptoUtility.UnixTimeStampToDateTimeMilliseconds((double)tradeChunkPiece[1]), Id = (long)tradeChunkPiece[0]
                    });
                }
                trades.Sort((t1, t2) => t1.Timestamp.CompareTo(t2.Timestamp));
                if (!callback(trades))
                {
                    break;
                }
                trades.Clear();
                if (tradeChunk.Length < maxCount || startDate == null)
                {
                    break;
                }
                await Task.Delay(5000);
            }
        }
コード例 #16
0
        /// <summary>
        /// Place a limit order by first querying the order book and then placing the order for a threshold below the bid or above the ask that would fully fulfill the amount.
        /// The order book is scanned until an amount of bids or asks that will fulfill the order is found and then the order is placed at the lowest bid or highest ask price multiplied
        /// by priceThreshold.
        /// </summary>
        /// <param name="symbol">Symbol to sell</param>
        /// <param name="amount">Amount to sell</param>
        /// <param name="isBuy">True for buy, false for sell</param>
        /// <param name="orderBookCount">Amount of bids/asks to request in the order book</param>
        /// <param name="priceThreshold">Threshold below the lowest bid or above the highest ask to set the limit order price at. For buys, this is converted to 1 / priceThreshold.
        /// This can be set to 0 if you want to set the price like a market order.</param>
        /// <param name="thresholdToAbort">If the lowest bid/highest ask price divided by the highest bid/lowest ask price is below this threshold, throw an exception.
        /// This ensures that your order does not buy or sell at an extreme margin.</param>
        /// <param name="abortIfOrderBookTooSmall">Whether to abort if the order book does not have enough bids or ask amounts to fulfill the order.</param>
        /// <returns>Order result</returns>
        public static async Task <ExchangeOrderResult> PlaceSafeMarketOrderAsync(this ExchangeAPI api, string symbol, decimal amount, bool isBuy, int orderBookCount = 100, decimal priceThreshold = 0.9m,
                                                                                 decimal thresholdToAbort = 0.75m, bool abortIfOrderBookTooSmall = false)
        {
            if (priceThreshold > 0.9m)
            {
                throw new APIException("You cannot specify a price threshold above 0.9m, otherwise there is a chance your order will never be fulfilled. For buys, this is " +
                                       "converted to 1.0m / priceThreshold, so always specify the value below 0.9m");
            }
            else if (priceThreshold <= 0m)
            {
                priceThreshold = 1m;
            }
            else if (isBuy && priceThreshold > 0m)
            {
                priceThreshold = 1.0m / priceThreshold;
            }
            ExchangeOrderBook book = await api.GetOrderBookAsync(symbol, orderBookCount);

            if (book == null || (isBuy && book.Asks.Count == 0) || (!isBuy && book.Bids.Count == 0))
            {
                throw new APIException($"Error getting order book for {symbol}");
            }
            decimal counter   = 0m;
            decimal highPrice = decimal.MinValue;
            decimal lowPrice  = decimal.MaxValue;

            if (isBuy)
            {
                foreach (ExchangeOrderPrice ask in book.Asks.Values)
                {
                    counter  += ask.Amount;
                    highPrice = Math.Max(highPrice, ask.Price);
                    lowPrice  = Math.Min(lowPrice, ask.Price);
                    if (counter >= amount)
                    {
                        break;
                    }
                }
            }
            else
            {
                foreach (ExchangeOrderPrice bid in book.Bids.Values)
                {
                    counter  += bid.Amount;
                    highPrice = Math.Max(highPrice, bid.Price);
                    lowPrice  = Math.Min(lowPrice, bid.Price);
                    if (counter >= amount)
                    {
                        break;
                    }
                }
            }
            if (abortIfOrderBookTooSmall && counter < amount)
            {
                throw new APIException($"{(isBuy ? "Buy" : "Sell") } order for {symbol} and amount {amount} cannot be fulfilled because the order book is too thin.");
            }
            else if (lowPrice / highPrice < thresholdToAbort)
            {
                throw new APIException($"{(isBuy ? "Buy" : "Sell")} order for {symbol} and amount {amount} would place for a price below threshold of {thresholdToAbort}, aborting.");
            }
            ExchangeOrderRequest request = new ExchangeOrderRequest
            {
                Amount            = amount,
                OrderType         = OrderType.Limit,
                Price             = CryptoUtility.RoundAmount((isBuy ? highPrice : lowPrice) * priceThreshold),
                ShouldRoundAmount = true,
                MarketSymbol      = symbol
            };
            ExchangeOrderResult result = await api.PlaceOrderAsync(request);

            // wait about 10 seconds until the order is fulfilled
            int       i        = 0;
            const int maxTries = 20; // 500 ms for each try

            for (; i < maxTries; i++)
            {
                await System.Threading.Tasks.Task.Delay(500);

                result = await api.GetOrderDetailsAsync(result.OrderId, symbol);

                switch (result.Result)
                {
                case ExchangeAPIOrderResult.Filled:
                case ExchangeAPIOrderResult.Canceled:
                case ExchangeAPIOrderResult.Error:
                    break;
                }
            }

            if (i == maxTries)
            {
                throw new APIException($"{(isBuy ? "Buy" : "Sell")} order for {symbol} and amount {amount} timed out and may not have been fulfilled");
            }

            return(result);
        }
コード例 #17
0
        protected internal override IWebSocket OnGetTickersWebSocket(Action <IReadOnlyCollection <KeyValuePair <string, ExchangeTicker> > > callback)
        {
            void innerCallback(string json)
            {
                #region sample json

                /*
                 * {
                 *  Nonce : int,
                 *  Deltas :
                 *  [
                 *      {
                 *          MarketName     : string,
                 *          High           : decimal,
                 *          Low            : decimal,
                 *          Volume         : decimal,
                 *          Last           : decimal,
                 *          BaseVolume     : decimal,
                 *          TimeStamp      : date,
                 *          Bid            : decimal,
                 *          Ask            : decimal,
                 *          OpenBuyOrders  : int,
                 *          OpenSellOrders : int,
                 *          PrevDay        : decimal,
                 *          Created        : date
                 *      }
                 *  ]
                 * }
                 */
                #endregion

                var    freshTickers = new Dictionary <string, ExchangeTicker>(StringComparer.OrdinalIgnoreCase);
                JToken token        = JToken.Parse(json);
                token = token["D"];
                foreach (JToken ticker in token)
                {
                    string   marketName = ticker["M"].ToStringInvariant();
                    decimal  last       = ticker["l"].ConvertInvariant <decimal>();
                    decimal  ask        = ticker["A"].ConvertInvariant <decimal>();
                    decimal  bid        = ticker["B"].ConvertInvariant <decimal>();
                    decimal  volume     = ticker["V"].ConvertInvariant <decimal>();
                    decimal  baseVolume = ticker["m"].ConvertInvariant <decimal>();
                    DateTime timestamp  = CryptoUtility.UnixTimeStampToDateTimeMilliseconds(ticker["T"].ConvertInvariant <long>());
                    var      t          = new ExchangeTicker
                    {
                        Ask    = ask,
                        Bid    = bid,
                        Last   = last,
                        Volume = new ExchangeVolume
                        {
                            ConvertedVolume = volume,
                            ConvertedSymbol = marketName,
                            BaseVolume      = baseVolume,
                            BaseSymbol      = marketName,
                            Timestamp       = timestamp
                        }
                    };
                    freshTickers[marketName] = t;
                }
                callback(freshTickers);
            }

            var client = SocketManager;
            return(client.SubscribeToSummaryDeltas(innerCallback));
        }
コード例 #18
0
        /// <summary>
        /// Parse a JToken into a ticker
        /// </summary>
        /// <param name="api">ExchangeAPI</param>
        /// <param name="token">Token</param>
        /// <param name="marketSymbol">Symbol</param>
        /// <param name="askKey">Ask key</param>
        /// <param name="bidKey">Bid key</param>
        /// <param name="lastKey">Last key</param>
        /// <param name="baseVolumeKey">Base currency volume key</param>
        /// <param name="quoteVolumeKey">Quote currency volume key</param>
        /// <param name="timestampKey">Timestamp key</param>
        /// <param name="timestampType">Timestamp type</param>
        /// <param name="baseCurrencyKey">Base currency key</param>
        /// <param name="quoteCurrencyKey">Quote currency key</param>
        /// <param name="idKey">Id key</param>
        /// <returns>ExchangeTicker</returns>
        internal static ExchangeTicker ParseTicker(this ExchangeAPI api, JToken token, string marketSymbol,
                                                   object askKey, object bidKey, object lastKey, object baseVolumeKey,
                                                   object quoteVolumeKey  = null, object timestampKey     = null, TimestampType timestampType = TimestampType.None,
                                                   object baseCurrencyKey = null, object quoteCurrencyKey = null, object idKey                = null)
        {
            if (token == null || !token.HasValues)
            {
                return(null);
            }
            decimal last = token[lastKey].ConvertInvariant <decimal>();

            // parse out volumes, handle cases where one or both do not exist
            token.ParseVolumes(baseVolumeKey, quoteVolumeKey, last, out decimal baseCurrencyVolume, out decimal quoteCurrencyVolume);

            // pull out timestamp
            DateTime timestamp = (timestampKey == null ? CryptoUtility.UtcNow : CryptoUtility.ParseTimestamp(token[timestampKey], timestampType));

            // split apart the symbol if we have a separator, otherwise just put the symbol for base and convert symbol
            string baseCurrency;
            string quoteCurrency;

            if (baseCurrencyKey != null && quoteCurrencyKey != null)
            {
                baseCurrency  = token[baseCurrencyKey].ToStringInvariant();
                quoteCurrency = token[quoteCurrencyKey].ToStringInvariant();
            }
            else if (string.IsNullOrWhiteSpace(marketSymbol))
            {
                throw new ArgumentNullException(nameof(marketSymbol));
            }
            else
            {
                (baseCurrency, quoteCurrency) = api.ExchangeMarketSymbolToCurrencies(marketSymbol);
            }

            // create the ticker and return it
            JToken askValue = token[askKey];
            JToken bidValue = token[bidKey];

            if (askValue is JArray)
            {
                askValue = askValue[0];
            }
            if (bidValue is JArray)
            {
                bidValue = bidValue[0];
            }
            ExchangeTicker ticker = new ExchangeTicker
            {
                MarketSymbol = marketSymbol,
                Ask          = askValue.ConvertInvariant <decimal>(),
                Bid          = bidValue.ConvertInvariant <decimal>(),
                Id           = (idKey == null ? null : token[idKey].ToStringInvariant()),
                Last         = last,
                Volume       = new ExchangeVolume
                {
                    BaseCurrencyVolume  = baseCurrencyVolume,
                    BaseCurrency        = baseCurrency,
                    QuoteCurrencyVolume = quoteCurrencyVolume,
                    QuoteCurrency       = quoteCurrency,
                    Timestamp           = timestamp
                }
            };

            return(ticker);
        }
コード例 #19
0
        private ExchangeOrderResult ParseOrder(JToken token)
        {
            /*
             * "symbol": "IOTABTC",
             * "orderId": 1,
             * "clientOrderId": "12345",
             * "transactTime": 1510629334993,
             * "price": "1.00000000",
             * "origQty": "1.00000000",
             * "executedQty": "0.00000000",
             * "status": "NEW",
             * "timeInForce": "GTC",
             * "type": "LIMIT",
             * "side": "SELL",
             * "fills": [
             *    {
             *      "price": "4000.00000000",
             *      "qty": "1.00000000",
             *      "commission": "4.00000000",
             *      "commissionAsset": "USDT"
             *    },
             *    {
             *      "price": "3999.00000000",
             *      "qty": "5.00000000",
             *      "commission": "19.99500000",
             *      "commissionAsset": "USDT"
             *    },
             *    {
             *      "price": "3998.00000000",
             *      "qty": "2.00000000",
             *      "commission": "7.99600000",
             *      "commissionAsset": "USDT"
             *    },
             *    {
             *      "price": "3997.00000000",
             *      "qty": "1.00000000",
             *      "commission": "3.99700000",
             *      "commissionAsset": "USDT"
             *    },
             *    {
             *      "price": "3995.00000000",
             *      "qty": "1.00000000",
             *      "commission": "3.99500000",
             *      "commissionAsset": "USDT"
             *    }
             *  ]
             */
            ExchangeOrderResult result = new ExchangeOrderResult
            {
                Amount       = token["origQty"].ConvertInvariant <decimal>(),
                AmountFilled = token["executedQty"].ConvertInvariant <decimal>(),
                Price        = token["price"].ConvertInvariant <decimal>(),
                IsBuy        = token["side"].ToStringInvariant() == "BUY",
                OrderDate    = CryptoUtility.UnixTimeStampToDateTimeMilliseconds(token["time"].ConvertInvariant <long>(token["transactTime"].ConvertInvariant <long>())),
                OrderId      = token["orderId"].ToStringInvariant(),
                MarketSymbol = token["symbol"].ToStringInvariant()
            };

            switch (token["status"].ToStringInvariant())
            {
            case "NEW":
                result.Result = ExchangeAPIOrderResult.Pending;
                break;

            case "PARTIALLY_FILLED":
                result.Result = ExchangeAPIOrderResult.FilledPartially;
                break;

            case "FILLED":
                result.Result = ExchangeAPIOrderResult.Filled;
                break;

            case "CANCELED":
            case "PENDING_CANCEL":
            case "EXPIRED":
            case "REJECTED":
                result.Result = ExchangeAPIOrderResult.Canceled;
                break;

            default:
                result.Result = ExchangeAPIOrderResult.Error;
                break;
            }

            ParseAveragePriceAndFeesFromFills(result, token["fills"]);

            return(result);
        }
コード例 #20
0
        public override IEnumerable <ExchangeTrade> GetHistoricalTrades(string symbol, DateTime?sinceDateTime = null)
        {
            const int maxCount = 100;

            symbol = NormalizeSymbol(symbol);
            string baseUrl = "/trades/t" + symbol + "/hist?sort=" + (sinceDateTime == null ? "-1" : "1") + "&limit=" + maxCount;
            string url;
            List <ExchangeTrade> trades = new List <ExchangeTrade>();

            decimal[][] tradeChunk;
            while (true)
            {
                url = baseUrl;
                if (sinceDateTime != null)
                {
                    url += "&start=" + (long)CryptoUtility.UnixTimestampFromDateTimeMilliseconds(sinceDateTime.Value);
                }
                tradeChunk = MakeJsonRequest <decimal[][]>(url);
                if (tradeChunk == null || tradeChunk.Length == 0)
                {
                    break;
                }
                if (sinceDateTime != null)
                {
                    sinceDateTime = CryptoUtility.UnixTimeStampToDateTimeMilliseconds((double)tradeChunk[tradeChunk.Length - 1][1]);
                }
                foreach (decimal[] tradeChunkPiece in tradeChunk)
                {
                    trades.Add(new ExchangeTrade {
                        Amount = Math.Abs(tradeChunkPiece[2]), IsBuy = tradeChunkPiece[2] > 0m, Price = tradeChunkPiece[3], Timestamp = CryptoUtility.UnixTimeStampToDateTimeMilliseconds((double)tradeChunkPiece[1]), Id = (long)tradeChunkPiece[0]
                    });
                }
                trades.Sort((t1, t2) => t1.Timestamp.CompareTo(t2.Timestamp));
                foreach (ExchangeTrade t in trades)
                {
                    yield return(t);
                }
                trades.Clear();
                if (tradeChunk.Length < 500 || sinceDateTime == null)
                {
                    break;
                }
                Task.Delay(5000).Wait();
            }
        }
コード例 #21
0
        /// <summary>
        /// Clamp quantiy using market info. If necessary, a network request will be made to retrieve symbol metadata.
        /// </summary>
        /// <param name="marketSymbol">Market Symbol</param>
        /// <param name="outputQuantity">Quantity</param>
        /// <returns>Clamped quantity</returns>
        protected async Task <decimal> ClampOrderQuantity(string marketSymbol, decimal outputQuantity)
        {
            ExchangeMarket market = await GetExchangeMarketFromCacheAsync(marketSymbol);

            return(market == null ? outputQuantity : CryptoUtility.ClampDecimal(market.MinTradeSize, market.MaxTradeSize, market.QuantityStepSize, outputQuantity));
        }
コード例 #22
0
        protected override async Task <IEnumerable <ExchangeOrderResult> > OnGetOpenOrderDetailsAsync(string symbol = null)
        {
            List <ExchangeOrderResult> orders = new List <ExchangeOrderResult>();
            // { "SELL": [{ "oid": "59e59b279bd8d31d093d956e", "type": "SELL", "userOid": null, "coinType": "KCS", "coinTypePair": "BTC", "direction": "SELL","price": 0.1,"dealAmount": 0,"pendingAmount": 100, "createdAt": 1508219688000, "updatedAt": 1508219688000 } ... ],
            //   "BUY":  [{ "oid": "59e42bf09bd8d374c9956caa", "type": "BUY",  "userOid": null, "coinType": "KCS", "coinTypePair": "BTC", "direction": "BUY", "price": 0.00009727,"dealAmount": 31.14503, "pendingAmount": 16.94827, "createdAt": 1508125681000, "updatedAt": 1508125681000 } ... ]
            var payload = await GetNoncePayloadAsync();

            if (symbol != null)
            {
                payload["symbol"] = symbol;
            }

            JToken token = await MakeJsonRequestAsync <JToken>("/order/active-map?" + CryptoUtility.GetFormForPayload(payload, false), null, payload);

            if (token != null && token.HasValues)
            {
                foreach (JToken order in token["BUY"])
                {
                    orders.Add(ParseOpenOrder(order));
                }
                foreach (JToken order in token["SELL"])
                {
                    orders.Add(ParseOpenOrder(order));
                }
            }
            return(orders);
        }
コード例 #23
0
 /// <summary>
 /// Return a rounded amount if needed
 /// </summary>
 /// <returns>Rounded amount or amount if no rounding is needed</returns>
 public decimal RoundAmount()
 {
     return ShouldRoundAmount ? CryptoUtility.RoundAmount(Amount) : Amount;
 }
コード例 #24
0
        protected override async Task ProcessRequestAsync(IHttpWebRequest request, Dictionary <string, object> payload)
        {
            if (CanMakeAuthenticatedRequest(payload))
            {
                request.AddHeader("KC-API-KEY", PublicApiKey.ToUnsecureString());
                request.AddHeader("KC-API-NONCE", payload["nonce"].ToStringInvariant());

                var endpoint = request.RequestUri.AbsolutePath;
                var message  = string.Format("{0}/{1}/{2}", endpoint, payload["nonce"], CryptoUtility.GetFormForPayload(payload, false));
                var sig      = CryptoUtility.SHA256Sign(Convert.ToBase64String(message.ToBytesUTF8()), PrivateApiKey.ToUnsecureString());

                request.AddHeader("KC-API-SIGNATURE", sig);

                if (request.Method == "POST")
                {
                    string msg     = CryptoUtility.GetFormForPayload(payload, false);
                    byte[] content = msg.ToBytesUTF8();
                    await request.WriteAllAsync(content, 0, content.Length);
                }
            }
        }
コード例 #25
0
        protected decimal ClampOrderQuantity(string symbol, decimal outputQuantity)
        {
            ExchangeMarket market = this.GetExchangeMarket(symbol);

            return(market == null ? outputQuantity : CryptoUtility.ClampDecimal(market.MinTradeSize, market.MaxTradeSize, market.QuantityStepSize, outputQuantity));
        }
コード例 #26
0
 public override string PeriodSecondsToString(int seconds)
 {
     return(CryptoUtility.SecondsToPeriodString(seconds, true));
 }
コード例 #27
0
        /// <summary>
        /// Limited to the last 100 open orders
        /// </summary>
        /// <param name="marketSymbol"></param>
        /// <returns></returns>
        protected override async Task <IEnumerable <ExchangeOrderResult> > OnGetOpenOrderDetailsAsync(string marketSymbol = null)
        {
            List <ExchangeOrderResult> orders = new List <ExchangeOrderResult>();
            var payload = await GetNoncePayloadAsync();

            payload.Add("openClosed", "OPEM");
            if (marketSymbol != null)
            {
                payload.Add("currencyPair", marketSymbol);
            }

            JToken token = await MakeJsonRequestAsync <JToken>("/exchange/client_orders?" + CryptoUtility.GetFormForPayload(payload, false), null, await GetNoncePayloadAsync());

            foreach (JToken order in token)
            {
                orders.Add(ParseClientOrder(order));
            }
            return(orders);
        }
コード例 #28
0
        public override IEnumerable <ExchangeTrade> GetHistoricalTrades(string symbol, DateTime?sinceDateTime = null)
        {
            /* [ {
            *  "a": 26129,         // Aggregate tradeId
            *       "p": "0.01633102",  // Price
            *       "q": "4.70443515",  // Quantity
            *       "f": 27781,         // First tradeId
            *       "l": 27781,         // Last tradeId
            *       "T": 1498793709153, // Timestamp
            *       "m": true,          // Was the buyer the maker?
            *       "M": true           // Was the trade the best price match?
            *  } ] */

            symbol = NormalizeSymbol(symbol);
            string baseUrl = "/aggTrades?symbol=" + symbol;
            string url;
            List <ExchangeTrade> trades = new List <ExchangeTrade>();
            DateTime             cutoff = DateTime.UtcNow;

            while (true)
            {
                url = baseUrl;
                if (sinceDateTime != null)
                {
                    url += "&startTime=" + CryptoUtility.UnixTimestampFromDateTimeMilliseconds(sinceDateTime.Value) +
                           "&endTime=" + CryptoUtility.UnixTimestampFromDateTimeMilliseconds(sinceDateTime.Value + TimeSpan.FromDays(1.0));
                }
                JArray obj = MakeJsonRequest <Newtonsoft.Json.Linq.JArray>(url);
                if (obj == null || obj.Count == 0)
                {
                    break;
                }
                if (sinceDateTime != null)
                {
                    sinceDateTime = CryptoUtility.UnixTimeStampToDateTimeMilliseconds(obj.Last["T"].Value <long>());
                    if (sinceDateTime.Value > cutoff)
                    {
                        sinceDateTime = null;
                    }
                }
                foreach (JToken token in obj)
                {
                    // TODO: Binance doesn't provide a buy or sell type, I've put in a request for them to add this
                    trades.Add(new ExchangeTrade
                    {
                        Amount    = token["q"].Value <decimal>(),
                        Price     = token["p"].Value <decimal>(),
                        Timestamp = CryptoUtility.UnixTimeStampToDateTimeMilliseconds(token["T"].Value <long>()),
                        Id        = token["a"].Value <long>(),
                        IsBuy     = token["m"].Value <bool>()
                    });
                }
                trades.Sort((t1, t2) => t1.Timestamp.CompareTo(t2.Timestamp));
                foreach (ExchangeTrade t in trades)
                {
                    yield return(t);
                }
                trades.Clear();
                if (sinceDateTime == null)
                {
                    break;
                }
                System.Threading.Thread.Sleep(1000);
            }
        }
コード例 #29
0
        public override IEnumerable <ExchangeTrade> GetHistoricalTrades(string symbol, DateTime?sinceDateTime = null)
        {
            string baseUrl = "/products/" + symbol.ToUpperInvariant() + "/candles?granularity=" + (sinceDateTime == null ? "3600.0" : "60.0");
            string url;
            List <ExchangeTrade> trades = new List <ExchangeTrade>();

            decimal[][] tradeChunk;
            while (true)
            {
                url = baseUrl;
                if (sinceDateTime != null)
                {
                    url += "&start=" + System.Web.HttpUtility.UrlEncode(sinceDateTime.Value.ToString("s", System.Globalization.CultureInfo.InvariantCulture));
                    url += "&end=" + System.Web.HttpUtility.UrlEncode(sinceDateTime.Value.AddMinutes(5.0).ToString("s", System.Globalization.CultureInfo.InvariantCulture));
                }
                tradeChunk = MakeJsonRequest <decimal[][]>(url);
                if (tradeChunk == null || tradeChunk.Length == 0)
                {
                    break;
                }
                if (sinceDateTime != null)
                {
                    sinceDateTime = CryptoUtility.UnixTimeStampToDateTimeSeconds((double)tradeChunk[0][0]);
                }
                foreach (decimal[] tradeChunkPiece in tradeChunk)
                {
                    trades.Add(new ExchangeTrade {
                        Amount = tradeChunkPiece[5], IsBuy = true, Price = tradeChunkPiece[3], Timestamp = CryptoUtility.UnixTimeStampToDateTimeSeconds((double)tradeChunkPiece[0]), Id = 0
                    });
                }
                trades.Sort((t1, t2) => t1.Timestamp.CompareTo(t2.Timestamp));
                foreach (ExchangeTrade t in trades)
                {
                    yield return(t);
                }
                trades.Clear();
                if (sinceDateTime == null)
                {
                    break;
                }
                Task.Delay(1000).Wait();
            }
        }
コード例 #30
0
        /// <inheritdoc />
        protected async override Task <ExchangeOrderResult> OnPlaceOrderAsync(ExchangeOrderRequest order)
        {
            //{
            //	"market": "XRP-PERP",
            //  "side": "sell",
            //  "price": 0.306525,
            //  "type": "limit",
            //  "size": 31431.0,
            //  "reduceOnly": false,
            //  "ioc": false,
            //  "postOnly": false,
            //  "clientId": null
            //}

            IEnumerable <ExchangeMarket> markets = await OnGetMarketSymbolsMetadataAsync();

            ExchangeMarket market = markets.Where(m => m.MarketSymbol == order.MarketSymbol).First();

            var payload = await GetNoncePayloadAsync();

            var parameters = new Dictionary <string, object>(StringComparer.OrdinalIgnoreCase)
            {
                { "market", market.MarketSymbol },
                { "side", order.IsBuy ? "buy" : "sell" },
                { "type", order.OrderType.ToStringLowerInvariant() },
                { "size", order.RoundAmount() }
            };

            if (!string.IsNullOrEmpty(order.ClientOrderId))
            {
                parameters.Add("clientId", order.ClientOrderId);
            }

            if (order.IsPostOnly != null)
            {
                parameters.Add("postOnly", order.IsPostOnly);
            }

            if (order.OrderType != OrderType.Market)
            {
                int precision = BitConverter.GetBytes(decimal.GetBits((decimal)market.PriceStepSize)[3])[2];

                if (order.Price == null)
                {
                    throw new ArgumentNullException(nameof(order.Price));
                }

                parameters.Add("price", Math.Round(order.Price.Value, precision));
            }
            else
            {
                parameters.Add("price", null);
            }

            parameters.CopyTo(payload);

            order.ExtraParameters.CopyTo(payload);

            var response = await MakeJsonRequestAsync <JToken>("/orders", null, payload, "POST");

            ExchangeOrderResult result = new ExchangeOrderResult
            {
                OrderId       = response["id"].ToStringInvariant(),
                ClientOrderId = response["clientId"].ToStringInvariant(),
                OrderDate     = CryptoUtility.ToDateTimeInvariant(response["createdAt"]),
                Price         = CryptoUtility.ConvertInvariant <decimal>(response["price"]),
                AmountFilled  = CryptoUtility.ConvertInvariant <decimal>(response["filledSize"]),
                AveragePrice  = CryptoUtility.ConvertInvariant <decimal>(response["avgFillPrice"]),
                Amount        = CryptoUtility.ConvertInvariant <decimal>(response["size"]),
                MarketSymbol  = response["market"].ToStringInvariant(),
                IsBuy         = response["side"].ToStringInvariant() == "buy"
            };

            return(result);
        }