Skip to content

Commit ae96858

Browse files
authored
fix Poloniex trade stream (#738)
- simplified OnGetMarketSymbolsAsync() - updated OnGetMarketSymbolsMetadataAsync() to use returnticker endpoint instead - changed OnGetTradesWebSocketAsync() to send messageIds instead of symbol names - also changed to using epoch_ms instead of timestamp for more precision
1 parent fe0929c commit ae96858

File tree

1 file changed

+50
-33
lines changed

1 file changed

+50
-33
lines changed

src/ExchangeSharp/API/Exchanges/Poloniex/ExchangePoloniexAPI.cs

Lines changed: 50 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -326,29 +326,31 @@ protected override async Task<IReadOnlyDictionary<string, ExchangeCurrency>> OnG
326326

327327
protected override async Task<IEnumerable<string>> OnGetMarketSymbolsAsync()
328328
{
329-
List<string> symbols = new List<string>();
330-
var tickers = await GetTickersAsync();
331-
foreach (var kv in tickers)
332-
{
333-
symbols.Add(kv.Key);
334-
}
335-
return symbols;
329+
return (await GetMarketSymbolsMetadataAsync()).Where(x => x.IsActive.Value).Select(x => x.MarketSymbol);
336330
}
337331

338332
protected internal override async Task<IEnumerable<ExchangeMarket>> OnGetMarketSymbolsMetadataAsync()
339333
{
340-
//https://poloniex.com/public?command=returnOrderBook&currencyPair=all&depth=0
341-
/*
342-
* "BTC_CLAM": {
343-
"asks": [],
344-
"bids": [],
345-
"isFrozen": "0",
346-
"seq": 37268918
347-
}, ...
334+
//https://docs.poloniex.com/#returnticker
335+
/*
336+
{
337+
"BTC_BTS": {
338+
"id": 14,
339+
"last": "0.00000090",
340+
"lowestAsk": "0.00000091",
341+
"highestBid": "0.00000089",
342+
"percentChange": "-0.02173913",
343+
"baseVolume": "0.28698296",
344+
"quoteVolume": "328356.84081156",
345+
"isFrozen": "0",
346+
"postOnly": "0",
347+
"high24hr": "0.00000093",
348+
"low24hr": "0.00000087"
349+
},...
348350
*/
349351

350-
var markets = new List<ExchangeMarket>();
351-
Dictionary<string, JToken> lookup = await MakeJsonRequestAsync<Dictionary<string, JToken>>("/public?command=returnOrderBook&currencyPair=all&depth=0");
352+
var markets = new List<ExchangeMarket>();
353+
Dictionary<string, JToken> lookup = await MakeJsonRequestAsync<Dictionary<string, JToken>>("/public?command=returnTicker");
352354
// StepSize is 8 decimal places for both price and amount on everything at Polo
353355
const decimal StepSize = 0.00000001m;
354356
const decimal minTradeSize = 0.0001m;
@@ -357,16 +359,18 @@ protected internal override async Task<IEnumerable<ExchangeMarket>> OnGetMarketS
357359
{
358360
var market = new ExchangeMarket { MarketSymbol = kvp.Key, IsActive = false };
359361

360-
string isFrozen = kvp.Value["isFrozen"].ToStringInvariant();
361-
if (string.Equals(isFrozen, "0"))
362+
string isFrozen = kvp.Value["isFrozen"].ToStringInvariant();
363+
string postOnly = kvp.Value["postOnly"].ToStringInvariant();
364+
if (string.Equals(isFrozen, "0") && string.Equals(postOnly, "0"))
362365
{
363366
market.IsActive = true;
364367
}
365368

366369
string[] pairs = kvp.Key.Split('_');
367370
if (pairs.Length == 2)
368371
{
369-
market.QuoteCurrency = pairs[0];
372+
market.MarketId = kvp.Value["id"].ToStringLowerInvariant();
373+
market.QuoteCurrency = pairs[0];
370374
market.BaseCurrency = pairs[1];
371375
market.PriceStepSize = StepSize;
372376
market.QuantityStepSize = StepSize;
@@ -440,10 +444,19 @@ protected override async Task<IWebSocket> OnGetTickersWebSocketAsync(Action<IRea
440444

441445
protected override async Task<IWebSocket> OnGetTradesWebSocketAsync(Func<KeyValuePair<string, ExchangeTrade>, Task> callback, params string[] marketSymbols)
442446
{
443-
Dictionary<int, Tuple<string, long>> messageIdToSymbol = new Dictionary<int, Tuple<string, long>>();
447+
Dictionary<int, string> messageIdToSymbol = new Dictionary<int, string>();
448+
Dictionary<string, int> symbolToMessageId = new Dictionary<string, int>();
449+
var symMeta = await GetMarketSymbolsMetadataAsync();
450+
foreach (var symbol in symMeta)
451+
{
452+
messageIdToSymbol.Add(int.Parse(symbol.MarketId), symbol.MarketSymbol);
453+
symbolToMessageId.Add(symbol.MarketSymbol, int.Parse(symbol.MarketId));
454+
}
444455
return await ConnectPublicWebSocketAsync(string.Empty, async (_socket, msg) =>
445456
{
446457
JToken token = JToken.Parse(msg.ToStringFromUTF8());
458+
if (token.Type == JTokenType.Object && token["error"] != null)
459+
throw new APIException($"Exchange returned error: {token["error"].ToStringInvariant()}");
447460
int msgId = token[0].ConvertInvariant<int>();
448461

449462
if (msgId == 1010 || token.Count() == 2) // "[7,2]"
@@ -459,18 +472,17 @@ protected override async Task<IWebSocket> OnGetTradesWebSocketAsync(Func<KeyValu
459472
var dataType = data[0].ToStringInvariant();
460473
if (dataType == "i")
461474
{
462-
var marketInfo = data[1];
463-
var market = marketInfo["currencyPair"].ToStringInvariant();
464-
messageIdToSymbol[msgId] = new Tuple<string, long>(market, 0);
475+
// can also populate messageIdToSymbol from here
476+
continue;
465477
}
466478
else if (dataType == "t")
467479
{
468-
if (messageIdToSymbol.TryGetValue(msgId, out Tuple<string, long> symbol))
469-
{ // 0 1 2 3 4 5
470-
// ["t", "<trade id>", <1 for buy 0 for sell>, "<price>", "<size>", <timestamp>]
471-
ExchangeTrade trade = data.ParseTrade(amountKey: 4, priceKey: 3, typeKey: 2, timestampKey: 5,
472-
timestampType: TimestampType.UnixSeconds, idKey: 1, typeKeyIsBuyValue: "1");
473-
await callback(new KeyValuePair<string, ExchangeTrade>(symbol.Item1, trade));
480+
if (messageIdToSymbol.TryGetValue(msgId, out string symbol))
481+
{ // 0 1 2 3 4 5 6
482+
// ["t", "<trade id>", <1 for buy 0 for sell>, "<price>", "<size>", <timestamp>, "<epoch_ms>"]
483+
ExchangeTrade trade = data.ParseTrade(amountKey: 4, priceKey: 3, typeKey: 2, timestampKey: 6,
484+
timestampType: TimestampType.UnixMilliseconds, idKey: 1, typeKeyIsBuyValue: "1");
485+
await callback(new KeyValuePair<string, ExchangeTrade>(symbol, trade));
474486
}
475487
}
476488
else if (dataType == "o")
@@ -484,14 +496,19 @@ protected override async Task<IWebSocket> OnGetTradesWebSocketAsync(Func<KeyValu
484496
}
485497
}, async (_socket) =>
486498
{
499+
IEnumerable<int> marketIDs = null;
487500
if (marketSymbols == null || marketSymbols.Length == 0)
488501
{
489-
marketSymbols = (await GetMarketSymbolsAsync()).ToArray();
502+
marketIDs = messageIdToSymbol.Keys;
503+
}
504+
else
505+
{
506+
marketIDs = marketSymbols.Select(s => symbolToMessageId[s]);
490507
}
491508
// subscribe to order book and trades channel for each symbol
492-
foreach (var sym in marketSymbols)
509+
foreach (var id in marketIDs)
493510
{
494-
await _socket.SendMessageAsync(new { command = "subscribe", channel = NormalizeMarketSymbol(sym) });
511+
await _socket.SendMessageAsync(new { command = "subscribe", channel = id });
495512
}
496513
});
497514
}

0 commit comments

Comments
 (0)