Skip to content

Commit 0ababfc

Browse files
authored
Digifinex: API improvements (#531)
* add more servers and find the fastest one at runtime solve 10008 time error raise rate limit to 240 times per minute candle add `limit` parameter support * support `limit` parameter in GetRecentTradesAsync * Fixed resource competition in GetFastestUrl()
1 parent f8985e9 commit 0ababfc

File tree

1 file changed

+68
-17
lines changed

1 file changed

+68
-17
lines changed

src/ExchangeSharp/API/Exchanges/Digifinex/ExchangeDigifinexAPI.cs

Lines changed: 68 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System;
33
using System.Collections.Generic;
44
using System.Linq;
5+
using System.Net.Http;
56
using System.Text;
67
using System.Threading;
78
using System.Threading.Tasks;
@@ -11,10 +12,21 @@ namespace ExchangeSharp
1112

1213
public partial class ExchangeDigifinexAPI : ExchangeAPI
1314
{
15+
string[] Urls =
16+
{
17+
"openapi.digifinex.com",
18+
"openapi.digifinex.vip",
19+
"openapi.digifinex.xyz",
20+
};
21+
string fastestUrl = null;
22+
int failedUrlCount;
23+
int successUrlCount;
24+
1425
public override string BaseUrl { get; set; } = "https://openapi.digifinex.vip/v3";
15-
public override string BaseUrlWebSocket { get; set; } = "wss://openapi.digifinex.com/ws/v1/";
26+
public override string BaseUrlWebSocket { get; set; } = "wss://openapi.digifinex.vip/ws/v1/";
1627
int websocketMessageId = 0;
1728
string timeWindow;
29+
TaskCompletionSource<int> inited = new TaskCompletionSource<int>();
1830

1931
public ExchangeDigifinexAPI()
2032
{
@@ -23,6 +35,35 @@ public ExchangeDigifinexAPI()
2335
MarketSymbolIsUppercase = true;
2436
WebSocketOrderBookType = WebSocketOrderBookType.FullBookFirstThenDeltas;
2537
NonceStyle = NonceStyle.UnixSeconds;
38+
RateLimit = new RateGate(240, TimeSpan.FromMinutes(1));
39+
GetFastestUrl();
40+
}
41+
42+
void GetFastestUrl()
43+
{
44+
var client = new HttpClient();
45+
foreach (var url in Urls)
46+
{
47+
var u = url;
48+
client.GetAsync($"https://{u}").ContinueWith((t) =>
49+
{
50+
if (t.Exception != null)
51+
{
52+
var count = Interlocked.Increment(ref failedUrlCount);
53+
if (count == Urls.Length)
54+
inited.SetException(new APIException("All digifinex URLs failed."));
55+
return;
56+
}
57+
if (Interlocked.Increment(ref successUrlCount) == 1)
58+
{
59+
fastestUrl = u;
60+
//Console.WriteLine($"Fastest url {GetHashCode()}: {u}");
61+
BaseUrl = $"https://{u}/v3";
62+
BaseUrlWebSocket = $"wss://{u}/ws/v1/";
63+
inited.SetResult(1);
64+
}
65+
});
66+
}
2667
}
2768

2869
#region ProcessRequest
@@ -31,30 +72,26 @@ protected override async Task OnGetNonceOffset()
3172
{
3273
try
3374
{
75+
await inited.Task;
3476
var start = CryptoUtility.UtcNow;
3577
JToken token = await MakeJsonRequestAsync<JToken>("/time");
3678
DateTime serverDate = CryptoUtility.UnixTimeStampToDateTimeSeconds(token["server_time"].ConvertInvariant<long>());
3779
var end = CryptoUtility.UtcNow;
38-
var now = start + TimeSpan.FromMilliseconds((end - start).TotalMilliseconds / 2);
80+
var now = start + TimeSpan.FromMilliseconds((end - start).TotalMilliseconds);
3981
var timeFaster = now - serverDate;
40-
if (timeFaster <= TimeSpan.Zero)
41-
{
42-
timeWindow = (timeFaster.Negate().TotalSeconds * 10).ToStringInvariant();
43-
NonceOffset = TimeSpan.FromSeconds(2.5);
44-
}
45-
else
46-
{
47-
NonceOffset = now - serverDate; // how much time to substract from Nonce when making a request
48-
}
82+
timeWindow = "30"; // max latency of 30s
83+
NonceOffset = now - serverDate; // how much time to substract from Nonce when making a request
84+
//Console.WriteLine($"NonceOffset {GetHashCode()}: {NonceOffset}");
4985
}
5086
catch
5187
{
52-
// eat exception
88+
throw;
5389
}
5490
}
5591

5692
protected override async Task ProcessRequestAsync(IHttpWebRequest request, Dictionary<string, object> payload)
5793
{
94+
await inited.Task;
5895
var query = request.RequestUri.Query.TrimStart('?');
5996
if (CanMakeAuthenticatedRequest(payload))
6097
{
@@ -124,6 +161,7 @@ private async Task<ExchangeMarket> ParseExchangeMarketAsync(JToken x)
124161

125162
protected internal override async Task<IEnumerable<ExchangeMarket>> OnGetMarketSymbolsMetadataAsync()
126163
{
164+
await inited.Task;
127165
JToken obj = await MakeJsonRequestAsync<JToken>("markets");
128166
JToken data = obj["data"];
129167
List<ExchangeMarket> results = new List<ExchangeMarket>();
@@ -179,7 +217,7 @@ protected override async Task<ExchangeOrderBook> OnGetOrderBookAsync(string mark
179217

180218
protected override async Task<IEnumerable<ExchangeTrade>> OnGetRecentTradesAsync(string marketSymbol, int? limit = null)
181219
{
182-
JToken obj = await MakeJsonRequestAsync<JToken>($"/trades?symbol={marketSymbol}&limit=500"); // maximum limit = 500
220+
JToken obj = await MakeJsonRequestAsync<JToken>($"/trades?symbol={marketSymbol}&limit={limit??500}"); // maximum limit = 500
183221
return obj["data"].Select(x => new ExchangeTrade
184222
{
185223
Id = x["id"].ToStringInvariant(),
@@ -194,9 +232,6 @@ protected override async Task<IEnumerable<ExchangeTrade>> OnGetRecentTradesAsync
194232
protected override async Task<IEnumerable<MarketCandle>> OnGetCandlesAsync(
195233
string marketSymbol, int periodSeconds, DateTime? startDate = null, DateTime? endDate = null, int? limit = null)
196234
{
197-
if (limit != null)
198-
throw new ArgumentException("Non-null limit is not supported", "limit");
199-
200235
string period;
201236
if (periodSeconds <= 60 * 720)
202237
period = (periodSeconds / 60).ToStringInvariant();
@@ -208,6 +243,20 @@ protected override async Task<IEnumerable<MarketCandle>> OnGetCandlesAsync(
208243
throw new ArgumentException($"Unsupported periodSeconds: {periodSeconds}", "periodSeconds");
209244

210245
var url = $"/kline?symbol={marketSymbol}&period={period}";
246+
if (startDate != null && endDate != null && limit != null)
247+
throw new ArgumentException("Cannot specify `startDate`, `endDate` and `limit` all at the same time");
248+
if (limit != null)
249+
{
250+
if (startDate != null)
251+
endDate = startDate + TimeSpan.FromSeconds(limit.Value * periodSeconds);
252+
else
253+
{
254+
if (endDate == null)
255+
endDate = DateTime.Now;
256+
startDate = endDate - TimeSpan.FromSeconds((limit.Value-1) * periodSeconds);
257+
}
258+
}
259+
211260
if (startDate != null)
212261
url += $"&start_time={new DateTimeOffset(startDate.Value).ToUnixTimeSeconds()}";
213262
if (endDate != null)
@@ -394,6 +443,7 @@ protected override async Task OnCancelOrderAsync(string orderId, string marketSy
394443

395444
protected override async Task<IWebSocket> OnGetTradesWebSocketAsync(Func<KeyValuePair<string, ExchangeTrade>, Task> callback, params string[] marketSymbols)
396445
{
446+
await inited.Task;
397447
if (callback == null)
398448
{
399449
return null;
@@ -477,8 +527,9 @@ protected override async Task<IWebSocket> OnGetDeltaOrderBookWebSocketAsync(Acti
477527
{
478528
return null;
479529
}
530+
await inited.Task;
480531

481-
return await ConnectWebSocketAsync(string.Empty, (_socket, msg) =>
532+
return await ConnectWebSocketAsync(string.Empty, (_socket, msg) =>
482533
{
483534
//{
484535
// "method": "depth.update",

0 commit comments

Comments
 (0)