Skip to content

Commit 309bed1

Browse files
authored
add trade stream to FTX (#721)
- refactored FTX to add FTX.US - added ability to parse local DateTime in addition to UTC - renamed Iso8601 enum to Iso8601UTC
1 parent 2456a21 commit 309bed1

File tree

15 files changed

+718
-561
lines changed

15 files changed

+718
-561
lines changed

src/ExchangeSharp/API/Exchanges/BitMEX/ExchangeBitMEXAPI.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ protected override Task<IWebSocket> OnGetTradesWebSocketAsync(Func<KeyValuePair<
255255
foreach (var t in data)
256256
{
257257
var marketSymbol = t["symbol"].ToStringInvariant();
258-
await callback(new KeyValuePair<string, ExchangeTrade>(marketSymbol, t.ParseTrade("size", "price", "side", "timestamp", TimestampType.Iso8601, "trdMatchID")));
258+
await callback(new KeyValuePair<string, ExchangeTrade>(marketSymbol, t.ParseTrade("size", "price", "side", "timestamp", TimestampType.Iso8601UTC, "trdMatchID")));
259259
}
260260
}, async (_socket) =>
261261
{
@@ -426,7 +426,7 @@ protected override async Task<IEnumerable<MarketCandle>> OnGetCandlesAsync(strin
426426
var obj = await MakeJsonRequestAsync<JToken>(url);
427427
foreach (var t in obj)
428428
{
429-
candles.Add(this.ParseCandle(t, marketSymbol, periodSeconds, "open", "high", "low", "close", "timestamp", TimestampType.Iso8601, "volume", "turnover", "vwap"));
429+
candles.Add(this.ParseCandle(t, marketSymbol, periodSeconds, "open", "high", "low", "close", "timestamp", TimestampType.Iso8601UTC, "volume", "turnover", "vwap"));
430430
}
431431
candles.Reverse();
432432

@@ -468,7 +468,7 @@ public async Task<IEnumerable<ExchangeTrade>> GetHistoricalTradesAsync(
468468
var obj = await MakeJsonRequestAsync<JToken>(url);
469469
foreach (var t in obj)
470470
{
471-
trades.Add(t.ParseTrade("size", "price", "side", "timestamp", TimestampType.Iso8601, "trdMatchID"));
471+
trades.Add(t.ParseTrade("size", "price", "side", "timestamp", TimestampType.Iso8601UTC, "trdMatchID"));
472472
}
473473

474474
return trades;
@@ -709,7 +709,7 @@ private ExchangePosition ParsePosition(JToken token)
709709
LiquidationPrice = token["liquidationPrice"].ConvertInvariant<decimal>(),
710710
Leverage = token["leverage"].ConvertInvariant<decimal>(),
711711
LastPrice = token["lastPrice"].ConvertInvariant<decimal>(),
712-
TimeStamp = CryptoUtility.ParseTimestamp(token["currentTimestamp"], TimestampType.Iso8601)
712+
TimeStamp = CryptoUtility.ParseTimestamp(token["currentTimestamp"], TimestampType.Iso8601UTC)
713713
};
714714
return result;
715715
}

src/ExchangeSharp/API/Exchanges/Bithumb/ExchangeBithumbAPI.cs

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ The above copyright notice and this permission notice shall be included in all c
1212

1313
using System;
1414
using System.Collections.Generic;
15+
using System.Linq;
1516
using System.Threading.Tasks;
1617

1718
using Newtonsoft.Json.Linq;
@@ -21,6 +22,8 @@ namespace ExchangeSharp
2122
public sealed partial class ExchangeBithumbAPI : ExchangeAPI
2223
{
2324
public override string BaseUrl { get; set; } = "https://api.bithumb.com";
25+
public override string BaseUrlWebSocket { get; set; } = "wss://pubwss.bithumb.com/pub/ws";
26+
2427

2528
private ExchangeBithumbAPI()
2629
{
@@ -112,15 +115,16 @@ protected override (string baseCurrency, string quoteCurrency) OnSplitMarketSymb
112115
protected override async Task<IEnumerable<string>> OnGetMarketSymbolsAsync()
113116
{
114117
List<string> marketSymbols = new List<string>();
115-
string marketSymbol = "all";
118+
string marketSymbol = "all_BTC";
116119
var data = await MakeRequestBithumbAsync(marketSymbol, "/public/ticker/$SYMBOL$");
117120
foreach (JProperty token in data.Item1)
118121
{
119122
if (token.Name != "date")
120123
{
121-
marketSymbols.Add(token.Name);
122-
}
123-
}
124+
marketSymbols.Add($"{token.Name}_KRW");
125+
if (token.Name != "BTC") marketSymbols.Add($"{token.Name}_BTC");
126+
}
127+
}
124128
return marketSymbols;
125129
}
126130

@@ -169,7 +173,60 @@ protected override async Task<IEnumerable<KeyValuePair<string, ExchangeOrderBook
169173
}
170174
return books;
171175
}
172-
}
176+
177+
protected override async Task<IWebSocket> OnGetTradesWebSocketAsync(Func<KeyValuePair<string, ExchangeTrade>, Task> callback, params string[] marketSymbols)
178+
{
179+
/*
180+
{
181+
"type" : "transaction",
182+
"content" : {
183+
"list" : [
184+
{
185+
"symbol" : "BTC_KRW", // currency code
186+
"buySellGb" : "1", // Execution type (1: sell execution, 2: buy execution)
187+
"contPrice" : "10579000", // Execution price
188+
"contQty" : "0.01", // contract quantity
189+
"contAmt" : "105790.00", // Execution amount
190+
"contDtm" : "2020-01-29 12:24:18.830039", // Execution time
191+
"updn" : "dn" // Compare with the previous price: up-up, dn-down
192+
}
193+
]
194+
}
195+
}
196+
*/
197+
if (marketSymbols == null || marketSymbols.Length == 0)
198+
{
199+
marketSymbols = (await GetMarketSymbolsAsync(true)).ToArray();
200+
}
201+
return await ConnectPublicWebSocketAsync(null, messageCallback: async (_socket, msg) =>
202+
{
203+
JToken parsedMsg = JToken.Parse(msg.ToStringFromUTF8());
204+
if (parsedMsg["status"].ToStringInvariant().Equals("0000"))
205+
return; // either "Connected Successfully" or "Filter Registered Successfully"
206+
else if (parsedMsg["status"].ToStringInvariant().Equals("5100"))
207+
{
208+
Logger.Error("Error in exchange {0} OnGetTradesWebSocketAsync(): {1}", Name, parsedMsg["resmsg"].ToStringInvariant());
209+
return;
210+
}
211+
else if (parsedMsg["type"].ToStringInvariant().Equals("transaction"))
212+
{
213+
foreach (var data in parsedMsg["content"]["list"])
214+
{
215+
var exchangeTrade = data.ParseTrade("contQty", "contPrice", "buySellGb", "contDtm", TimestampType.Iso8601UTC, null, typeKeyIsBuyValue: "2");
216+
217+
await callback(new KeyValuePair<string, ExchangeTrade>(parsedMsg["market"].ToStringInvariant(), exchangeTrade));
218+
}
219+
}
220+
}, connectCallback: async (_socket) =>
221+
{
222+
await _socket.SendMessageAsync(new
223+
{
224+
type = "transaction",
225+
symbols = marketSymbols,
226+
});
227+
});
228+
}
229+
}
173230

174231
public partial class ExchangeName { public const string Bithumb = "Bithumb"; }
175232
}

src/ExchangeSharp/API/Exchanges/Bittrex/ExchangeBittrexAPI.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ protected override async Task<ExchangeTicker> OnGetTickerAsync(string marketSymb
230230
{
231231
JToken ticker = await MakeJsonRequestAsync<JToken>("/markets/" + marketSymbol + "/ticker");
232232
//NOTE: Bittrex uses the term "BaseVolume" when referring to the QuoteCurrencyVolume
233-
return await this.ParseTickerAsync(ticker, marketSymbol, "askRate", "bidRate", "lastTradeRate", "volume", "quoteVolume", "updatedAt", TimestampType.Iso8601);
233+
return await this.ParseTickerAsync(ticker, marketSymbol, "askRate", "bidRate", "lastTradeRate", "volume", "quoteVolume", "updatedAt", TimestampType.Iso8601UTC);
234234
}
235235

236236
protected override async Task<IEnumerable<KeyValuePair<string, ExchangeTicker>>> OnGetTickersAsync()
@@ -241,7 +241,7 @@ protected override async Task<IEnumerable<KeyValuePair<string, ExchangeTicker>>>
241241
foreach (JToken ticker in tickers)
242242
{
243243
marketSymbol = ticker["symbol"].ToStringInvariant();
244-
ExchangeTicker tickerObj = await this.ParseTickerAsync(ticker, marketSymbol, "askRate", "bidRate", "lastTradeRate", "volume", "quoteVolume", "updatedAt", TimestampType.Iso8601);
244+
ExchangeTicker tickerObj = await this.ParseTickerAsync(ticker, marketSymbol, "askRate", "bidRate", "lastTradeRate", "volume", "quoteVolume", "updatedAt", TimestampType.Iso8601UTC);
245245
tickerList.Add(new KeyValuePair<string, ExchangeTicker>(marketSymbol, tickerObj));
246246
}
247247
return tickerList;
@@ -319,7 +319,7 @@ protected override async Task<IEnumerable<ExchangeTrade>> OnGetRecentTradesAsync
319319
JToken array = await MakeJsonRequestAsync<JToken>(baseUrl);
320320
foreach (JToken token in array)
321321
{
322-
trades.Add(token.ParseTrade("quantity", "rate", "takerSide", "executedAt", TimestampType.Iso8601, "id"));
322+
trades.Add(token.ParseTrade("quantity", "rate", "takerSide", "executedAt", TimestampType.Iso8601UTC, "id"));
323323
}
324324
return trades;
325325
}
@@ -488,7 +488,7 @@ protected override async Task<IEnumerable<MarketCandle>> OnGetCandlesAsync(strin
488488
{
489489
//NOTE: Bittrex uses the term "BaseVolume" when referring to the QuoteCurrencyVolume
490490
MarketCandle candle = this.ParseCandle(token: jsonCandle, marketSymbol: marketSymbol, periodSeconds: periodSeconds,
491-
openKey: "open", highKey: "high", lowKey: "low", closeKey: "close", timestampKey: "startsAt", timestampType: TimestampType.Iso8601,
491+
openKey: "open", highKey: "high", lowKey: "low", closeKey: "close", timestampKey: "startsAt", timestampType: TimestampType.Iso8601UTC,
492492
baseVolumeKey: "volume", quoteVolumeKey: "quoteVolume");
493493
if (startDate != null && endDate != null)
494494
{

src/ExchangeSharp/API/Exchanges/Bybit/ExchangeBybitAPI.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ protected override async Task<IWebSocket> OnGetTradesWebSocketAsync(Func<KeyValu
279279
priceKey: "price",
280280
typeKey: "side",
281281
timestampKey: "timestamp",
282-
timestampType: TimestampType.Iso8601,
282+
timestampType: TimestampType.Iso8601UTC,
283283
idKey: "trade_id");
284284
await callback(new KeyValuePair<string, ExchangeTrade>(dataRow["symbol"].ToStringInvariant(), trade));
285285
}
@@ -927,7 +927,7 @@ private ExchangePosition ParsePosition(JToken token)
927927
AveragePrice = token["entry_price"].ConvertInvariant<decimal>(),
928928
LiquidationPrice = token["liq_price"].ConvertInvariant<decimal>(),
929929
Leverage = token["effective_leverage"].ConvertInvariant<decimal>(),
930-
TimeStamp = CryptoUtility.ParseTimestamp(token["updated_at"], TimestampType.Iso8601)
930+
TimeStamp = CryptoUtility.ParseTimestamp(token["updated_at"], TimestampType.Iso8601UTC)
931931
};
932932
if (token["side"].ToStringInvariant() == "Sell")
933933
result.Amount *= -1;

src/ExchangeSharp/API/Exchanges/Coinbase/ExchangeCoinbaseAPI.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ protected override async Task<IReadOnlyDictionary<string, ExchangeCurrency>> OnG
236236
protected override async Task<ExchangeTicker> OnGetTickerAsync(string marketSymbol)
237237
{
238238
JToken ticker = await MakeJsonRequestAsync<JToken>("/products/" + marketSymbol + "/ticker");
239-
return await this.ParseTickerAsync(ticker, marketSymbol, "ask", "bid", "price", "volume", null, "time", TimestampType.Iso8601);
239+
return await this.ParseTickerAsync(ticker, marketSymbol, "ask", "bid", "price", "volume", null, "time", TimestampType.Iso8601UTC);
240240
}
241241

242242
protected override async Task<ExchangeDepositDetails> OnGetDepositAddressAsync(string symbol, bool forceRegenerate = false)
@@ -375,7 +375,7 @@ protected override async Task<IWebSocket> OnGetTickersWebSocketAsync(Action<IRea
375375
JToken token = JToken.Parse(msg.ToStringFromUTF8());
376376
if (token["type"].ToStringInvariant() == "ticker")
377377
{
378-
ExchangeTicker ticker = await this.ParseTickerAsync(token, token["product_id"].ToStringInvariant(), "best_ask", "best_bid", "price", "volume_24h", null, "time", TimestampType.Iso8601);
378+
ExchangeTicker ticker = await this.ParseTickerAsync(token, token["product_id"].ToStringInvariant(), "best_ask", "best_bid", "price", "volume_24h", null, "time", TimestampType.Iso8601UTC);
379379
callback(new List<KeyValuePair<string, ExchangeTicker>>() { new KeyValuePair<string, ExchangeTicker>(token["product_id"].ToStringInvariant(), ticker) });
380380
}
381381
}, async (_socket) =>
@@ -438,7 +438,7 @@ protected override async Task<IWebSocket> OnGetTradesWebSocketAsync(Func<KeyValu
438438

439439
private ExchangeTrade ParseTradeWebSocket(JToken token)
440440
{
441-
return token.ParseTradeCoinbase("size", "price", "side", "time", TimestampType.Iso8601, "trade_id");
441+
return token.ParseTradeCoinbase("size", "price", "side", "time", TimestampType.Iso8601UTC, "trade_id");
442442
}
443443

444444
protected override async Task<IWebSocket> OnUserDataWebSocketAsync(Action<object> callback)
@@ -556,7 +556,7 @@ protected override async Task OnGetHistoricalTradesAsync(Func<IEnumerable<Exchan
556556
{
557557
Callback = callback,
558558
EndDate = endDate,
559-
ParseFunction = (JToken token) => token.ParseTrade("size", "price", "side", "time", TimestampType.Iso8601, "trade_id"),
559+
ParseFunction = (JToken token) => token.ParseTrade("size", "price", "side", "time", TimestampType.Iso8601UTC, "trade_id"),
560560
StartDate = startDate,
561561
MarketSymbol = marketSymbol,
562562
Url = "/products/[marketSymbol]/trades",
@@ -578,7 +578,7 @@ protected override async Task<IEnumerable<ExchangeTrade>> OnGetRecentTradesAsync
578578
List<ExchangeTrade> tradeList = new List<ExchangeTrade>();
579579
foreach (JToken trade in trades)
580580
{
581-
tradeList.Add(trade.ParseTrade("size", "price", "side", "time", TimestampType.Iso8601, "trade_id"));
581+
tradeList.Add(trade.ParseTrade("size", "price", "side", "time", TimestampType.Iso8601UTC, "trade_id"));
582582
}
583583
return tradeList;
584584
}

0 commit comments

Comments
 (0)