diff --git a/src/ExchangeSharp/API/Exchanges/OKGroup/ExchangeOKExAPI.cs b/src/ExchangeSharp/API/Exchanges/OKGroup/ExchangeOKExAPI.cs index de707837..80528a5c 100644 --- a/src/ExchangeSharp/API/Exchanges/OKGroup/ExchangeOKExAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/OKGroup/ExchangeOKExAPI.cs @@ -11,17 +11,186 @@ The above copyright notice and this permission notice shall be included in all c */ using ExchangeSharp.OKGroup; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; namespace ExchangeSharp { - public sealed partial class ExchangeOKExAPI : OKGroupCommon + public sealed partial class ExchangeOKExAPI : OKGroupCommon { - public override string BaseUrl { get; set; } = "https://www.okex.com/api/v1"; + public override string BaseUrl { get; set; } = "https://www.okex.com/api/v1"; public override string BaseUrlV2 { get; set; } = "https://www.okex.com/v2/spot"; public override string BaseUrlV3 { get; set; } = "https://www.okex.com/api"; - public override string BaseUrlWebSocket { get; set; } = "wss://real.okex.com:8443/ws/v3"; + public override string BaseUrlWebSocket { get; set; } = "wss://real.okex.com:8443/ws/v3"; + public string BaseUrlV5 { get; set; } = "https://okex.com/api/v5"; protected override bool IsFuturesAndSwapEnabled { get; } = true; + + protected internal override async Task> OnGetMarketSymbolsMetadataAsync() + { + /* + { + "code":"0", + "msg":"", + "data":[ + { + "instType":"SWAP", + "instId":"LTC-USD-SWAP", + "uly":"LTC-USD", + "category":"1", + "baseCcy":"", + "quoteCcy":"", + "settleCcy":"LTC", + "ctVal":"10", + "ctMult":"1", + "ctValCcy":"USD", + "optType":"C", + "stk":"", + "listTime":"1597026383085", + "expTime":"1597026383085", + "lever":"10", + "tickSz":"0.01", + "lotSz":"1", + "minSz":"1", + "ctType":"linear", + "alias":"this_week", + "state":"live" + }, + ... + ] + } + */ + List markets = new List(); + parseMarketSymbolTokens(await MakeJsonRequestAsync( + "/public/instruments?instType=SPOT", BaseUrlV5)); + if (IsFuturesAndSwapEnabled) + { + parseMarketSymbolTokens(await MakeJsonRequestAsync( + "/public/instruments?instType=FUTURES", BaseUrlV5)); + parseMarketSymbolTokens(await MakeJsonRequestAsync( + "/public/instruments?instType=SWAP", BaseUrlV5)); + } + void parseMarketSymbolTokens(JToken allMarketSymbolTokens) + { + foreach (JToken marketSymbolToken in allMarketSymbolTokens) + { + var isSpot = marketSymbolToken["instType"].Value() == "SPOT"; + var baseCurrency = isSpot + ? marketSymbolToken["baseCcy"].Value() + : marketSymbolToken["settleCcy"].Value(); + var quoteCurrency = isSpot + ? marketSymbolToken["quoteCcy"].Value() + : marketSymbolToken["ctValCcy"].Value(); + var market = new ExchangeMarket + { + MarketSymbol = marketSymbolToken["instId"].Value(), + IsActive = marketSymbolToken["state"].Value() == "live", + QuoteCurrency = quoteCurrency, + BaseCurrency = baseCurrency, + PriceStepSize = marketSymbolToken["tickSz"].ConvertInvariant(), + MinPrice = marketSymbolToken["tickSz"].ConvertInvariant(), // assuming that this is also the min price since it isn't provided explicitly by the exchange + MinTradeSize = marketSymbolToken["minSz"].ConvertInvariant(), + QuantityStepSize = marketSymbolToken["lotSz"].ConvertInvariant(), + }; + markets.Add(market); + } + } + + return markets; + } + + protected override async Task OnGetTickerAsync(string marketSymbol) + { + var tickerResponse = await MakeJsonRequestAsync($"/market/ticker?instId={marketSymbol}", BaseUrlV5); + var symbol = tickerResponse["instId"].Value(); + return await ParseTickerV5Async(tickerResponse, symbol); + } + + protected override async Task>> OnGetTickersAsync() + { + List> tickers = new List>(); + await parseData(await MakeJsonRequestAsync("/market/tickers?instType=SPOT", BaseUrlV5)); + if (IsFuturesAndSwapEnabled) + { + await parseData(await MakeJsonRequestAsync("/market/tickers?instType=FUTURES", BaseUrlV5)); + await parseData(await MakeJsonRequestAsync("/market/tickers?instType=SWAP", BaseUrlV5)); + } + async Task parseData(JToken tickerResponse) + { + /*{ + "code":"0", + "msg":"", + "data":[ + { + "instType":"SWAP", + "instId":"LTC-USD-SWAP", + "last":"9999.99", + "lastSz":"0.1", + "askPx":"9999.99", + "askSz":"11", + "bidPx":"8888.88", + "bidSz":"5", + "open24h":"9000", + "high24h":"10000", + "low24h":"8888.88", + "volCcy24h":"2222", + "vol24h":"2222", + "sodUtc0":"0.1", + "sodUtc8":"0.1", + "ts":"1597026383085" + }, + ... + ] + } + */ + + foreach (JToken t in tickerResponse) + { + var symbol = t["instId"].Value(); + ExchangeTicker ticker = await ParseTickerV5Async(t, symbol); + tickers.Add(new KeyValuePair(symbol, ticker)); + } + } + + return tickers; + } + + protected override async Task> OnGetRecentTradesAsync(string marketSymbol, int? limit) + { + limit = limit ?? 500; + marketSymbol = NormalizeMarketSymbol(marketSymbol); + List trades = new List(); + var recentTradesResponse = await MakeJsonRequestAsync($"/market/trades?instId={marketSymbol}&limit={limit}", BaseUrlV5); + foreach (var t in recentTradesResponse) + { + trades.Add( + t.ParseTrade( + amountKey: "sz", + priceKey: "px", + typeKey: "side", + timestampKey: "ts", + timestampType: TimestampType.UnixMilliseconds, + idKey: "tradeId")); + } + + return trades; + } + + private async Task ParseTickerV5Async(JToken t, string symbol) + { + return await this.ParseTickerAsync( + t, + symbol, + askKey: "askPx", + bidKey: "bidPx", + lastKey: "last", + baseVolumeKey: "vol24h", + quoteVolumeKey: "volCcy24h", + timestampKey: "ts", + timestampType: TimestampType.UnixMilliseconds); + } } public partial class ExchangeName { public const string OKEx = "OKEx"; } -} \ No newline at end of file +}