Skip to content

Commit b545420

Browse files
authored
Orderbook ws implementation (#863)
1 parent 5405908 commit b545420

File tree

4 files changed

+169
-2
lines changed

4 files changed

+169
-2
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ The following cryptocurrency exchanges are supported:
6060
| KuCoin | x | x | T R B | |
6161
| LBank | x | x | R | |
6262
| Livecoin | x | x | | |
63-
| MEXC | x | x | | |
63+
| MEXC | x | x | B | |
6464
| NDAX | x | x | T R | |
6565
| OKCoin | x | x | R B | |
6666
| OKEx | x | x | T R B O | |

src/ExchangeSharp/API/Exchanges/MEXC/ExchangeMEXCAPI.cs

+108-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
33
using System.Linq;
44
using System.Threading.Tasks;
55
using ExchangeSharp.Models;
6+
using Newtonsoft.Json;
67
using Newtonsoft.Json.Linq;
8+
using ExchangeSharp.API.Exchanges.MEXC.Models;
79

810
namespace ExchangeSharp
911
{
@@ -21,6 +23,7 @@ private ExchangeMEXCAPI()
2123
MarketSymbolSeparator = string.Empty;
2224
MarketSymbolIsUppercase = true;
2325
RateLimit = new RateGate(20, TimeSpan.FromSeconds(2));
26+
WebSocketOrderBookType = WebSocketOrderBookType.FullBookFirstThenDeltas;
2427
}
2528

2629
public override Task<string> ExchangeMarketSymbolToGlobalMarketSymbolAsync(string marketSymbol)
@@ -369,6 +372,110 @@ protected override async Task OnCancelOrderAsync(string orderId, string marketSy
369372
await MakeJsonRequestAsync<JToken>("/order", BaseUrl, payload, "DELETE");
370373
}
371374

375+
protected override async Task<IWebSocket> OnGetDeltaOrderBookWebSocketAsync(
376+
Action<ExchangeOrderBook> callback,
377+
int maxCount = 20,
378+
params string[] marketSymbols
379+
)
380+
{
381+
if (marketSymbols == null || marketSymbols.Length == 0)
382+
{
383+
marketSymbols = (await GetMarketSymbolsAsync()).ToArray();
384+
}
385+
386+
var initialSequenceIds = new Dictionary<string, long>();
387+
388+
return await ConnectPublicWebSocketAsync(
389+
string.Empty,
390+
(_socket, msg) =>
391+
{
392+
var json = msg.ToStringFromUTF8();
393+
394+
MarketDepthDiffUpdate update = null;
395+
try
396+
{
397+
update = JsonConvert.DeserializeObject<MarketDepthDiffUpdate>(json, SerializerSettings);
398+
}
399+
catch
400+
{
401+
}
402+
403+
if (update == null || string.IsNullOrWhiteSpace(update.Channel) || update.Details == null)
404+
{
405+
return Task.CompletedTask;
406+
}
407+
408+
if (update.Details.Version < initialSequenceIds[update.Symbol])
409+
{
410+
// A deprecated update should be ignored
411+
return Task.CompletedTask;
412+
}
413+
414+
var eventTimeDateTime = update.EventTime.UnixTimeStampToDateTimeMilliseconds();
415+
416+
var orderBook = new ExchangeOrderBook
417+
{
418+
IsFromSnapshot = false,
419+
ExchangeName = Name,
420+
SequenceId = update.Details.Version,
421+
MarketSymbol = update.Symbol,
422+
LastUpdatedUtc = eventTimeDateTime,
423+
};
424+
425+
if (update.Details.Asks != null)
426+
{
427+
foreach (var ask in update.Details.Asks)
428+
{
429+
orderBook.Asks[ask.Price] = new ExchangeOrderPrice
430+
{
431+
Price = ask.Price,
432+
Amount = ask.Volume,
433+
};
434+
}
435+
}
436+
437+
if (update.Details.Bids != null)
438+
{
439+
foreach (var bid in update.Details.Bids)
440+
{
441+
orderBook.Bids[bid.Price] = new ExchangeOrderPrice
442+
{
443+
Price = bid.Price,
444+
Amount = bid.Volume,
445+
};
446+
}
447+
}
448+
449+
callback(orderBook);
450+
451+
return Task.CompletedTask;
452+
},
453+
async (_socket) =>
454+
{
455+
foreach (var marketSymbol in marketSymbols) // "Every websocket connection maximum support 30 subscriptions at one time." - API docs
456+
{
457+
var initialBook = await OnGetOrderBookAsync(marketSymbol, maxCount);
458+
initialBook.IsFromSnapshot = true;
459+
460+
callback(initialBook);
461+
462+
initialSequenceIds[marketSymbol] = initialBook.SequenceId;
463+
464+
var subscriptionParams = new List<string>
465+
{
466+
$"[email protected]@{marketSymbol}"
467+
};
468+
469+
await _socket.SendMessageAsync(new WebSocketSubscription
470+
{
471+
Method = "SUBSCRIPTION",
472+
Params = subscriptionParams,
473+
});
474+
}
475+
}
476+
);
477+
}
478+
372479
private async Task<JToken> GetBalance()
373480
{
374481
var token = await MakeJsonRequestAsync<JToken>("/account", payload: await GetNoncePayloadAsync());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using Newtonsoft.Json;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Text;
5+
6+
namespace ExchangeSharp.API.Exchanges.MEXC.Models
7+
{
8+
internal class MarketDepthDiffUpdateDetailItem
9+
{
10+
[JsonProperty("p")]
11+
public decimal Price { get; set; }
12+
13+
[JsonProperty("v")]
14+
public decimal Volume { get; set; }
15+
}
16+
17+
internal class MarketDepthDiffUpdateDetails
18+
{
19+
public List<MarketDepthDiffUpdateDetailItem> Asks { get; set; }
20+
21+
public List<MarketDepthDiffUpdateDetailItem> Bids { get; set; }
22+
23+
[JsonProperty("e")]
24+
public string EventType { get; set; }
25+
26+
[JsonProperty("r")]
27+
public long Version { get; set; }
28+
}
29+
30+
internal class MarketDepthDiffUpdate
31+
{
32+
[JsonProperty("c")]
33+
public string Channel { get; set; }
34+
35+
[JsonProperty("d")]
36+
public MarketDepthDiffUpdateDetails Details { get; set; }
37+
38+
[JsonProperty("s")]
39+
public string Symbol { get; set; }
40+
41+
/// <summary>
42+
/// Milliseconds since Unix epoch
43+
/// </summary>
44+
[JsonProperty("t")]
45+
public long EventTime { get; set; }
46+
}
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace ExchangeSharp.API.Exchanges.MEXC.Models
6+
{
7+
internal class WebSocketSubscription
8+
{
9+
public string Method { get; set; }
10+
11+
public List<string> Params { get; set; }
12+
}
13+
}

0 commit comments

Comments
 (0)