Skip to content

Commit 34794b4

Browse files
authored
add trade stream for Crypto.com exchange (#746)
1 parent ade82d1 commit 34794b4

File tree

1 file changed

+130
-0
lines changed

1 file changed

+130
-0
lines changed
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
using Newtonsoft.Json.Linq;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
8+
namespace ExchangeSharp
9+
{
10+
public sealed partial class ExchangeCryptoComApi : ExchangeAPI
11+
{
12+
public override string BaseUrl { get; set; } = "https://api.crypto.com/v2";
13+
public override string BaseUrlWebSocket { get; set; } = "wss://stream.crypto.com/v2";
14+
15+
public ExchangeCryptoComApi()
16+
{
17+
NonceStyle = NonceStyle.UnixMilliseconds;
18+
NonceOffset = TimeSpan.FromSeconds(0.1);
19+
// WebSocketOrderBookType = not implemented
20+
MarketSymbolSeparator = "_";
21+
MarketSymbolIsUppercase = true;
22+
// ExchangeGlobalCurrencyReplacements[] not implemented
23+
}
24+
25+
protected override async Task<IEnumerable<string>> OnGetMarketSymbolsAsync()
26+
{
27+
var instruments = await MakeJsonRequestAsync<JToken>("public/get-instruments");
28+
var markets = new List<ExchangeMarket>();
29+
foreach (JToken instrument in instruments["instruments"])
30+
{
31+
markets.Add(new ExchangeMarket
32+
{
33+
MarketSymbol = instrument["instrument_name"].ToStringUpperInvariant(),
34+
QuoteCurrency = instrument["quote_currency"].ToStringInvariant(),
35+
BaseCurrency = instrument["base_currency"].ToStringInvariant(),
36+
});
37+
}
38+
return markets.Select(m => m.MarketSymbol);
39+
}
40+
41+
protected override async Task<IWebSocket> OnGetTradesWebSocketAsync(Func<KeyValuePair<string, ExchangeTrade>, Task> callback, params string[] marketSymbols)
42+
{
43+
if (marketSymbols == null || marketSymbols.Length == 0)
44+
{
45+
marketSymbols = new string[] { "" };
46+
}
47+
return await ConnectPublicWebSocketAsync("/market", async (_socket, msg) =>
48+
{
49+
/*{
50+
{{
51+
"code": 0,
52+
"method": "subscribe",
53+
"result": {
54+
"instrument_name": "YFI_BTC",
55+
"subscription": "trade.YFI_BTC",
56+
"channel": "trade",
57+
"data": [
58+
{
59+
"dataTime": 1645139769555,
60+
"d": 2258312914797956554,
61+
"s": "BUY",
62+
"p": 0.5541,
63+
"q": 1E-06,
64+
"t": 1645139769539,
65+
"i": "YFI_BTC"
66+
}
67+
]
68+
}
69+
}}
70+
} */
71+
JToken token = JToken.Parse(msg.ToStringFromUTF8());
72+
if (token["method"].ToStringInvariant() == "ERROR" || token["method"].ToStringInvariant() == "unknown")
73+
{
74+
throw new APIException(token["code"].ToStringInvariant() + ": " + token["message"].ToStringInvariant());
75+
}
76+
else if (token["method"].ToStringInvariant() == "public/heartbeat")
77+
{
78+
if (token["message"].ToStringInvariant() == "server did not receive any client heartbeat, going to disconnect soon")
79+
throw new APIException(token["code"].ToStringInvariant() + ": " + token["message"].ToStringInvariant());
80+
}
81+
else if (token["method"].ToStringInvariant() == "subscribe" && token["result"] != null)
82+
{
83+
var result = token["result"];
84+
var dataArray = result["data"].ToArray();
85+
for (int i = 0; i < dataArray.Length; i++)
86+
{
87+
JToken data = dataArray[i];
88+
var trade = data.ParseTrade("q", "p", "s", "t", TimestampType.UnixMilliseconds, "d");
89+
string marketSymbol = data["i"].ToStringInvariant();
90+
if (dataArray.Length == 100) // initial snapshot contains 100 trades
91+
{
92+
trade.Flags |= ExchangeTradeFlags.IsFromSnapshot;
93+
if (i == dataArray.Length - 1)
94+
trade.Flags |= ExchangeTradeFlags.IsLastFromSnapshot;
95+
}
96+
await callback(new KeyValuePair<string, ExchangeTrade>(marketSymbol, trade));
97+
}
98+
}
99+
}, async (_socket) =>
100+
{ /* We recommend adding a 1-second sleep after establishing the websocket connection, and before requests are sent.
101+
* This will avoid occurrences of rate-limit (`TOO_MANY_REQUESTS`) errors, as the websocket rate limits are pro-rated based on the calendar-second that the websocket connection was opened.
102+
*/
103+
await Task.Delay(1000);
104+
105+
/*
106+
{
107+
"id": 11,
108+
"method": "subscribe",
109+
"params": {
110+
"channels": ["trade.ETH_CRO"]
111+
},
112+
"nonce": 1587523073344
113+
}
114+
*/
115+
var subscribeRequest = new
116+
{
117+
// + consider using id field in the future to differentiate between requests
118+
method = "subscribe",
119+
@params = new
120+
{
121+
channels = marketSymbols.Select(s => string.IsNullOrWhiteSpace(s) ? "trade" : $"trade.{s}").ToArray(),
122+
},
123+
nonce = await GenerateNonceAsync(),
124+
};
125+
await _socket.SendMessageAsync(subscribeRequest);
126+
});
127+
}
128+
}
129+
public partial class ExchangeName { public const string CryptoCom = "CryptoCom"; }
130+
}

0 commit comments

Comments
 (0)