Skip to content

Commit 4da6f4c

Browse files
authored
Moving to OKEx V5 API #2 (#674)
1 parent 4b30c62 commit 4da6f4c

File tree

2 files changed

+108
-74
lines changed

2 files changed

+108
-74
lines changed

src/ExchangeSharp/API/Exchanges/OKGroup/ExchangeOKExAPI.cs

Lines changed: 103 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ The above copyright notice and this permission notice shall be included in all c
1010
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1111
*/
1212

13-
using ExchangeSharp.OKGroup;
14-
using Newtonsoft.Json.Linq;
1513
using System;
1614
using System.Collections.Generic;
15+
using System.Linq;
1716
using System.Threading.Tasks;
17+
using ExchangeSharp.OKGroup;
18+
using Newtonsoft.Json.Linq;
1819

1920
namespace ExchangeSharp
2021
{
@@ -27,6 +28,11 @@ public sealed partial class ExchangeOKExAPI : OKGroupCommon
2728
public string BaseUrlV5 { get; set; } = "https://okex.com/api/v5";
2829
protected override bool IsFuturesAndSwapEnabled { get; } = true;
2930

31+
public override string PeriodSecondsToString(int seconds)
32+
{
33+
return CryptoUtility.SecondsToPeriodString(seconds, true);
34+
}
35+
3036
protected internal override async Task<IEnumerable<ExchangeMarket>> OnGetMarketSymbolsMetadataAsync()
3137
{
3238
/*
@@ -61,61 +67,54 @@ protected internal override async Task<IEnumerable<ExchangeMarket>> OnGetMarketS
6167
]
6268
}
6369
*/
64-
List<ExchangeMarket> markets = new List<ExchangeMarket>();
70+
var markets = new List<ExchangeMarket>();
6571
parseMarketSymbolTokens(await MakeJsonRequestAsync<JToken>(
6672
"/public/instruments?instType=SPOT", BaseUrlV5));
67-
if (IsFuturesAndSwapEnabled)
68-
{
69-
parseMarketSymbolTokens(await MakeJsonRequestAsync<JToken>(
70-
"/public/instruments?instType=FUTURES", BaseUrlV5));
71-
parseMarketSymbolTokens(await MakeJsonRequestAsync<JToken>(
72-
"/public/instruments?instType=SWAP", BaseUrlV5));
73-
}
73+
if (!IsFuturesAndSwapEnabled)
74+
return markets;
75+
parseMarketSymbolTokens(await MakeJsonRequestAsync<JToken>(
76+
"/public/instruments?instType=FUTURES", BaseUrlV5));
77+
parseMarketSymbolTokens(await MakeJsonRequestAsync<JToken>(
78+
"/public/instruments?instType=SWAP", BaseUrlV5));
79+
return markets;
80+
7481
void parseMarketSymbolTokens(JToken allMarketSymbolTokens)
7582
{
76-
foreach (JToken marketSymbolToken in allMarketSymbolTokens)
77-
{
78-
var isSpot = marketSymbolToken["instType"].Value<string>() == "SPOT";
79-
var baseCurrency = isSpot
80-
? marketSymbolToken["baseCcy"].Value<string>()
81-
: marketSymbolToken["settleCcy"].Value<string>();
82-
var quoteCurrency = isSpot
83-
? marketSymbolToken["quoteCcy"].Value<string>()
84-
: marketSymbolToken["ctValCcy"].Value<string>();
85-
var market = new ExchangeMarket
86-
{
87-
MarketSymbol = marketSymbolToken["instId"].Value<string>(),
88-
IsActive = marketSymbolToken["state"].Value<string>() == "live",
89-
QuoteCurrency = quoteCurrency,
90-
BaseCurrency = baseCurrency,
91-
PriceStepSize = marketSymbolToken["tickSz"].ConvertInvariant<decimal>(),
92-
MinPrice = marketSymbolToken["tickSz"].ConvertInvariant<decimal>(), // assuming that this is also the min price since it isn't provided explicitly by the exchange
93-
MinTradeSize = marketSymbolToken["minSz"].ConvertInvariant<decimal>(),
94-
QuantityStepSize = marketSymbolToken["lotSz"].ConvertInvariant<decimal>(),
95-
};
96-
markets.Add(market);
97-
}
83+
markets.AddRange(from marketSymbolToken in allMarketSymbolTokens
84+
let isSpot = marketSymbolToken["instType"].Value<string>() == "SPOT"
85+
let baseCurrency = isSpot ? marketSymbolToken["baseCcy"].Value<string>() : marketSymbolToken["settleCcy"].Value<string>()
86+
let quoteCurrency = isSpot ? marketSymbolToken["quoteCcy"].Value<string>() : marketSymbolToken["ctValCcy"].Value<string>()
87+
select new ExchangeMarket
88+
{
89+
MarketSymbol = marketSymbolToken["instId"].Value<string>(),
90+
IsActive = marketSymbolToken["state"].Value<string>() == "live",
91+
QuoteCurrency = quoteCurrency,
92+
BaseCurrency = baseCurrency,
93+
PriceStepSize = marketSymbolToken["tickSz"].ConvertInvariant<decimal>(),
94+
MinPrice = marketSymbolToken["tickSz"].ConvertInvariant<decimal>(), // assuming that this is also the min price since it isn't provided explicitly by the exchange
95+
MinTradeSize = marketSymbolToken["minSz"].ConvertInvariant<decimal>(),
96+
QuantityStepSize = marketSymbolToken["lotSz"].ConvertInvariant<decimal>()
97+
});
9898
}
99-
100-
return markets;
10199
}
102100

103101
protected override async Task<ExchangeTicker> OnGetTickerAsync(string marketSymbol)
104102
{
105103
var tickerResponse = await MakeJsonRequestAsync<JToken>($"/market/ticker?instId={marketSymbol}", BaseUrlV5);
106-
var symbol = tickerResponse["instId"].Value<string>();
107-
return await ParseTickerV5Async(tickerResponse, symbol);
104+
var symbol = tickerResponse[0]["instId"].Value<string>();
105+
return await ParseTickerV5Async(tickerResponse[0], symbol);
108106
}
109107

110108
protected override async Task<IEnumerable<KeyValuePair<string, ExchangeTicker>>> OnGetTickersAsync()
111109
{
112-
List<KeyValuePair<string, ExchangeTicker>> tickers = new List<KeyValuePair<string, ExchangeTicker>>();
110+
var tickers = new List<KeyValuePair<string, ExchangeTicker>>();
113111
await parseData(await MakeJsonRequestAsync<JToken>("/market/tickers?instType=SPOT", BaseUrlV5));
114-
if (IsFuturesAndSwapEnabled)
115-
{
116-
await parseData(await MakeJsonRequestAsync<JToken>("/market/tickers?instType=FUTURES", BaseUrlV5));
117-
await parseData(await MakeJsonRequestAsync<JToken>("/market/tickers?instType=SWAP", BaseUrlV5));
118-
}
112+
if (!IsFuturesAndSwapEnabled)
113+
return tickers;
114+
await parseData(await MakeJsonRequestAsync<JToken>("/market/tickers?instType=FUTURES", BaseUrlV5));
115+
await parseData(await MakeJsonRequestAsync<JToken>("/market/tickers?instType=SWAP", BaseUrlV5));
116+
return tickers;
117+
119118
async Task parseData(JToken tickerResponse)
120119
{
121120
/*{
@@ -148,49 +147,83 @@ async Task parseData(JToken tickerResponse)
148147
foreach (JToken t in tickerResponse)
149148
{
150149
var symbol = t["instId"].Value<string>();
151-
ExchangeTicker ticker = await ParseTickerV5Async(t, symbol);
150+
var ticker = await ParseTickerV5Async(t, symbol);
152151
tickers.Add(new KeyValuePair<string, ExchangeTicker>(symbol, ticker));
153152
}
154153
}
155-
156-
return tickers;
157154
}
158155

159-
protected override async Task<IEnumerable<ExchangeTrade>> OnGetRecentTradesAsync(string marketSymbol, int? limit)
156+
protected override async Task<IEnumerable<ExchangeTrade>> OnGetRecentTradesAsync(string marketSymbol,
157+
int? limit = null)
160158
{
161-
limit = limit ?? 500;
159+
limit ??= 500;
162160
marketSymbol = NormalizeMarketSymbol(marketSymbol);
163-
List<ExchangeTrade> trades = new List<ExchangeTrade>();
164-
var recentTradesResponse = await MakeJsonRequestAsync<JToken>($"/market/trades?instId={marketSymbol}&limit={limit}", BaseUrlV5);
165-
foreach (var t in recentTradesResponse)
166-
{
167-
trades.Add(
168-
t.ParseTrade(
169-
amountKey: "sz",
170-
priceKey: "px",
171-
typeKey: "side",
172-
timestampKey: "ts",
173-
timestampType: TimestampType.UnixMilliseconds,
174-
idKey: "tradeId"));
161+
var recentTradesResponse =
162+
await MakeJsonRequestAsync<JToken>($"/market/trades?instId={marketSymbol}&limit={limit}", BaseUrlV5);
163+
return recentTradesResponse.Select(t => t.ParseTrade(
164+
"sz", "px", "side", "ts", TimestampType.UnixMilliseconds, "tradeId"))
165+
.ToList();
166+
}
167+
168+
protected override async Task<ExchangeOrderBook> OnGetOrderBookAsync(string marketSymbol, int maxCount = 100)
169+
{
170+
var token = await MakeJsonRequestAsync<JToken>($"/market/books?instId={marketSymbol}&sz={maxCount}", BaseUrlV5);
171+
return token[0].ParseOrderBookFromJTokenArrays(maxCount: maxCount);
172+
}
173+
174+
protected override async Task<IEnumerable<MarketCandle>> OnGetCandlesAsync(string marketSymbol, int periodSeconds, DateTime? startDate = null, DateTime? endDate = null, int? limit = null)
175+
{
176+
/*
177+
{
178+
"code":"0",
179+
"msg":"",
180+
"data":[
181+
[
182+
"1597026383085", timestamp
183+
"3.721", open
184+
"3.743", high
185+
"3.677", low
186+
"3.708", close
187+
"8422410", volume
188+
"22698348.04828491" volCcy (Quote)
189+
],..
190+
]
175191
}
192+
*/
176193

177-
return trades;
194+
var candles = new List<MarketCandle>();
195+
var url = $"/market/history-candles?instId={marketSymbol}";
196+
if (startDate.HasValue)
197+
url += "&after=" + (long)startDate.Value.UnixTimestampFromDateTimeMilliseconds();
198+
if (endDate.HasValue)
199+
url += "&before=" + (long)endDate.Value.UnixTimestampFromDateTimeMilliseconds();
200+
if (limit.HasValue)
201+
url += "&limit=" + limit.Value.ToStringInvariant();
202+
var periodString = PeriodSecondsToString(periodSeconds);
203+
url += $"&bar={periodString}";
204+
var obj = await MakeJsonRequestAsync<JToken>(url, BaseUrlV5);
205+
foreach (JArray token in obj)
206+
candles.Add(this.ParseCandle(token, marketSymbol, periodSeconds, 1, 2, 3, 4, 0, TimestampType.UnixMilliseconds, 5, 6));
207+
return candles;
178208
}
179209

180210
private async Task<ExchangeTicker> ParseTickerV5Async(JToken t, string symbol)
181211
{
182212
return await this.ParseTickerAsync(
183-
t,
184-
symbol,
185-
askKey: "askPx",
186-
bidKey: "bidPx",
187-
lastKey: "last",
188-
baseVolumeKey: "vol24h",
189-
quoteVolumeKey: "volCcy24h",
190-
timestampKey: "ts",
191-
timestampType: TimestampType.UnixMilliseconds);
213+
token: t,
214+
marketSymbol: symbol,
215+
askKey: "askPx",
216+
bidKey: "bidPx",
217+
lastKey: "last",
218+
baseVolumeKey: "vol24h",
219+
quoteVolumeKey: "volCcy24h",
220+
timestampKey: "ts",
221+
timestampType: TimestampType.UnixMilliseconds);
192222
}
193223
}
194224

195-
public partial class ExchangeName { public const string OKEx = "OKEx"; }
225+
public partial class ExchangeName
226+
{
227+
public const string OKEx = "OKEx";
228+
}
196229
}

src/ExchangeSharp/Utility/CryptoUtility.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,8 +1137,9 @@ public static byte[] AesEncryption(byte[] input, byte[] password, byte[] salt)
11371137
/// Convert seconds to a period string, i.e. 5s, 1m, 2h, 3d, 1w, 1M, etc.
11381138
/// </summary>
11391139
/// <param name="seconds">Seconds. Use 60 for minute, 3600 for hour, 3600*24 for day, 3600*24*30 for month.</param>
1140+
/// <param name="capitalAfterMinute">Capitalize all letters after m, i.e. 5s, 1m, 30m, 1H, 2H, 3D, 1W, 1M, etc.</param>
11401141
/// <returns>Period string</returns>
1141-
public static string SecondsToPeriodString(int seconds)
1142+
public static string SecondsToPeriodString(int seconds, bool capitalAfterMinute = false)
11421143
{
11431144
const int minuteThreshold = 60;
11441145
const int hourThreshold = 60 * 60;
@@ -1152,15 +1153,15 @@ public static string SecondsToPeriodString(int seconds)
11521153
}
11531154
else if (seconds >= weekThreshold)
11541155
{
1155-
return seconds / weekThreshold + "w";
1156+
return seconds / weekThreshold + (capitalAfterMinute ? "W" : "w");
11561157
}
11571158
else if (seconds >= dayThreshold)
11581159
{
1159-
return seconds / dayThreshold + "d";
1160+
return seconds / dayThreshold + (capitalAfterMinute ? "D" : "d");
11601161
}
11611162
else if (seconds >= hourThreshold)
11621163
{
1163-
return seconds / hourThreshold + "h";
1164+
return seconds / hourThreshold + (capitalAfterMinute ? "H" : "h");
11641165
}
11651166
else if (seconds >= minuteThreshold)
11661167
{

0 commit comments

Comments
 (0)