Skip to content

Commit

Permalink
Orderbook ws implementation (#863)
Browse files Browse the repository at this point in the history
  • Loading branch information
v36u authored Dec 31, 2024
1 parent 5405908 commit b545420
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 2 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ The following cryptocurrency exchanges are supported:
| KuCoin | x | x | T R B | |
| LBank | x | x | R | |
| Livecoin | x | x | | |
| MEXC | x | x | | |
| MEXC | x | x | B | |
| NDAX | x | x | T R | |
| OKCoin | x | x | R B | |
| OKEx | x | x | T R B O | |
Expand Down
109 changes: 108 additions & 1 deletion src/ExchangeSharp/API/Exchanges/MEXC/ExchangeMEXCAPI.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ExchangeSharp.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using ExchangeSharp.API.Exchanges.MEXC.Models;

namespace ExchangeSharp
{
Expand All @@ -21,6 +23,7 @@ private ExchangeMEXCAPI()
MarketSymbolSeparator = string.Empty;
MarketSymbolIsUppercase = true;
RateLimit = new RateGate(20, TimeSpan.FromSeconds(2));
WebSocketOrderBookType = WebSocketOrderBookType.FullBookFirstThenDeltas;
}

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

protected override async Task<IWebSocket> OnGetDeltaOrderBookWebSocketAsync(
Action<ExchangeOrderBook> callback,
int maxCount = 20,
params string[] marketSymbols
)
{
if (marketSymbols == null || marketSymbols.Length == 0)
{
marketSymbols = (await GetMarketSymbolsAsync()).ToArray();
}

var initialSequenceIds = new Dictionary<string, long>();

return await ConnectPublicWebSocketAsync(
string.Empty,
(_socket, msg) =>
{
var json = msg.ToStringFromUTF8();

MarketDepthDiffUpdate update = null;
try
{
update = JsonConvert.DeserializeObject<MarketDepthDiffUpdate>(json, SerializerSettings);
}
catch
{
}

if (update == null || string.IsNullOrWhiteSpace(update.Channel) || update.Details == null)
{
return Task.CompletedTask;
}

if (update.Details.Version < initialSequenceIds[update.Symbol])
{
// A deprecated update should be ignored
return Task.CompletedTask;
}

var eventTimeDateTime = update.EventTime.UnixTimeStampToDateTimeMilliseconds();

var orderBook = new ExchangeOrderBook
{
IsFromSnapshot = false,
ExchangeName = Name,
SequenceId = update.Details.Version,
MarketSymbol = update.Symbol,
LastUpdatedUtc = eventTimeDateTime,
};

if (update.Details.Asks != null)
{
foreach (var ask in update.Details.Asks)
{
orderBook.Asks[ask.Price] = new ExchangeOrderPrice
{
Price = ask.Price,
Amount = ask.Volume,
};
}
}

if (update.Details.Bids != null)
{
foreach (var bid in update.Details.Bids)
{
orderBook.Bids[bid.Price] = new ExchangeOrderPrice
{
Price = bid.Price,
Amount = bid.Volume,
};
}
}

callback(orderBook);

return Task.CompletedTask;
},
async (_socket) =>
{
foreach (var marketSymbol in marketSymbols) // "Every websocket connection maximum support 30 subscriptions at one time." - API docs
{
var initialBook = await OnGetOrderBookAsync(marketSymbol, maxCount);
initialBook.IsFromSnapshot = true;

callback(initialBook);

initialSequenceIds[marketSymbol] = initialBook.SequenceId;

var subscriptionParams = new List<string>
{
$"[email protected]@{marketSymbol}"
};

await _socket.SendMessageAsync(new WebSocketSubscription
{
Method = "SUBSCRIPTION",
Params = subscriptionParams,
});
}
}
);
}

private async Task<JToken> GetBalance()
{
var token = await MakeJsonRequestAsync<JToken>("/account", payload: await GetNoncePayloadAsync());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;

namespace ExchangeSharp.API.Exchanges.MEXC.Models
{
internal class MarketDepthDiffUpdateDetailItem
{
[JsonProperty("p")]
public decimal Price { get; set; }

[JsonProperty("v")]
public decimal Volume { get; set; }
}

internal class MarketDepthDiffUpdateDetails
{
public List<MarketDepthDiffUpdateDetailItem> Asks { get; set; }

public List<MarketDepthDiffUpdateDetailItem> Bids { get; set; }

[JsonProperty("e")]
public string EventType { get; set; }

[JsonProperty("r")]
public long Version { get; set; }
}

internal class MarketDepthDiffUpdate
{
[JsonProperty("c")]
public string Channel { get; set; }

[JsonProperty("d")]
public MarketDepthDiffUpdateDetails Details { get; set; }

[JsonProperty("s")]
public string Symbol { get; set; }

/// <summary>
/// Milliseconds since Unix epoch
/// </summary>
[JsonProperty("t")]
public long EventTime { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace ExchangeSharp.API.Exchanges.MEXC.Models
{
internal class WebSocketSubscription
{
public string Method { get; set; }

public List<string> Params { get; set; }
}
}

0 comments on commit b545420

Please sign in to comment.