Fix Kraken API

This commit is contained in:
nicolas.dorier 2024-02-01 10:09:32 +09:00
parent a8fdc4798d
commit 02663a149e
No known key found for this signature in database
GPG Key ID: 6618763EF09186FE

View File

@ -8,6 +8,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Rating; using BTCPayServer.Rating;
using ExchangeSharp; using ExchangeSharp;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
@ -16,7 +17,7 @@ namespace BTCPayServer.Services.Rates
// Make sure that only one request is sent to kraken in general // Make sure that only one request is sent to kraken in general
public class KrakenExchangeRateProvider : IRateProvider public class KrakenExchangeRateProvider : IRateProvider
{ {
public RateSourceInfo RateSourceInfo => new("kraken", "Kraken", "https://api.kraken.com/0/public/Ticker?pair=ATOMETH,ATOMEUR,ATOMUSD,ATOMXBT,BATETH,BATEUR,BATUSD,BATXBT,BCHEUR,BCHUSD,BCHXBT,DAIEUR,DAIUSD,DAIUSDT,DASHEUR,DASHUSD,DASHXBT,EOSETH,EOSXBT,ETHCHF,ETHDAI,ETHUSDC,ETHUSDT,GNOETH,GNOXBT,ICXETH,ICXEUR,ICXUSD,ICXXBT,LINKETH,LINKEUR,LINKUSD,LINKXBT,LSKETH,LSKEUR,LSKUSD,LSKXBT,NANOETH,NANOEUR,NANOUSD,NANOXBT,OMGETH,OMGEUR,OMGUSD,OMGXBT,PAXGETH,PAXGEUR,PAXGUSD,PAXGXBT,SCETH,SCEUR,SCUSD,SCXBT,USDCEUR,USDCUSD,USDCUSDT,USDTCAD,USDTEUR,USDTGBP,USDTZUSD,WAVESETH,WAVESEUR,WAVESUSD,WAVESXBT,XBTCHF,XBTDAI,XBTUSDC,XBTUSDT,XDGEUR,XDGUSD,XETCXETH,XETCXXBT,XETCZEUR,XETCZUSD,XETHXXBT,XETHZCAD,XETHZEUR,XETHZGBP,XETHZJPY,XETHZUSD,XLTCXXBT,XLTCZEUR,XLTCZUSD,XMLNXETH,XMLNXXBT,XMLNZEUR,XMLNZUSD,XREPXETH,XREPXXBT,XREPZEUR,XXBTZCAD,XXBTZEUR,XXBTZGBP,XXBTZJPY,XXBTZUSD,XXDGXXBT,XXLMXXBT,XXMRXXBT,XXMRZEUR,XXMRZUSD,XXRPXXBT,XXRPZEUR,XXRPZUSD,XZECXXBT,XZECZEUR,XZECZUSD"); public RateSourceInfo RateSourceInfo => new("kraken", "Kraken", "https://api.kraken.com/0/public/Ticker");
public HttpClient HttpClient public HttpClient HttpClient
{ {
get get
@ -31,39 +32,6 @@ namespace BTCPayServer.Services.Rates
HttpClient _LocalClient; HttpClient _LocalClient;
static readonly HttpClient _Client = new HttpClient(); static readonly HttpClient _Client = new HttpClient();
// ExchangeSymbolToGlobalSymbol throws exception which would kill perf
readonly ConcurrentDictionary<string, string> notFoundSymbols = new ConcurrentDictionary<string, string>(new Dictionary<string, string>()
{
{"ADAXBT","ADAXBT"},
{ "BSVUSD","BSVUSD"},
{ "QTUMEUR","QTUMEUR"},
{ "QTUMXBT","QTUMXBT"},
{ "EOSUSD","EOSUSD"},
{ "XTZUSD","XTZUSD"},
{ "XREPZUSD","XREPZUSD"},
{ "ADAEUR","ADAEUR"},
{ "ADAUSD","ADAUSD"},
{ "GNOEUR","GNOEUR"},
{ "XTZETH","XTZETH"},
{ "XXRPZJPY","XXRPZJPY"},
{ "XXRPZCAD","XXRPZCAD"},
{ "XTZEUR","XTZEUR"},
{ "QTUMETH","QTUMETH"},
{ "XXLMZUSD","XXLMZUSD"},
{ "QTUMCAD","QTUMCAD"},
{ "QTUMUSD","QTUMUSD"},
{ "XTZXBT","XTZXBT"},
{ "GNOUSD","GNOUSD"},
{ "ADAETH","ADAETH"},
{ "ADACAD","ADACAD"},
{ "XTZCAD","XTZCAD"},
{ "BSVEUR","BSVEUR"},
{ "XZECZJPY","XZECZJPY"},
{ "XXLMZEUR","XXLMZEUR"},
{"EOSEUR","EOSEUR"},
{"BSVXBT","BSVXBT"}
});
string[] _Symbols = Array.Empty<string>(); string[] _Symbols = Array.Empty<string>();
DateTimeOffset? _LastSymbolUpdate = null; DateTimeOffset? _LastSymbolUpdate = null;
readonly Dictionary<string, string> _TickerMapping = new Dictionary<string, string>() readonly Dictionary<string, string> _TickerMapping = new Dictionary<string, string>()
@ -76,47 +44,54 @@ namespace BTCPayServer.Services.Rates
{ "ZEUR", "EUR" }, { "ZEUR", "EUR" },
{ "ZJPY", "JPY" }, { "ZJPY", "JPY" },
{ "ZCAD", "CAD" }, { "ZCAD", "CAD" },
{ "ZGBP", "GBP" } { "ZGBP", "GBP" },
{ "XXMR", "XMR" },
{ "XETH", "ETH" },
{ "USDC", "USDC" }, // On A=A purpose
}; };
string Normalize(string ticker)
{
_TickerMapping.TryGetValue(ticker, out var normalized);
return normalized ?? ticker;
}
readonly ConcurrentDictionary<string, CurrencyPair> CachedCurrencyPairs = new ConcurrentDictionary<string, CurrencyPair>();
private CurrencyPair GetCurrencyPair(string symbol)
{
if (CachedCurrencyPairs.TryGetValue(symbol, out var pair))
return pair;
var found = _TickerMapping.Where(t => symbol.StartsWith(t.Key, StringComparison.OrdinalIgnoreCase))
.Select(t => new { KrakenTicker = t.Key, PayTicker = t.Value }).FirstOrDefault();
if (found is not null)
{
pair = new CurrencyPair(found.PayTicker, Normalize(symbol.Substring(found.KrakenTicker.Length)));
}
if (pair is null)
{
found = _TickerMapping.Where(t => symbol.EndsWith(t.Key, StringComparison.OrdinalIgnoreCase))
.Select(t => new { KrakenTicker = t.Key, PayTicker = t.Value }).FirstOrDefault();
if (found is not null)
pair = new CurrencyPair(Normalize(symbol.Substring(0, symbol.Length - found.KrakenTicker.Length)), found.PayTicker);
}
if (pair is null)
CurrencyPair.TryParse(symbol, out pair);
CachedCurrencyPairs.TryAdd(symbol, pair);
return pair;
}
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken) public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
{ {
var result = new List<PairRate>(); var result = new List<PairRate>();
var symbols = await GetSymbolsAsync(cancellationToken); var symbols = await GetSymbolsAsync(cancellationToken);
var helper = (ExchangeKrakenAPI)await ExchangeAPI.GetExchangeAPIAsync<ExchangeKrakenAPI>(); JToken apiTickers = await MakeJsonRequestAsync<JToken>("/0/public/Ticker", null, null, cancellationToken: cancellationToken);
var normalizedPairsList = symbols.Where(s => !notFoundSymbols.ContainsKey(s)).Select(s => helper.NormalizeMarketSymbol(s)).ToList();
var csvPairsList = string.Join(",", normalizedPairsList);
JToken apiTickers = await MakeJsonRequestAsync<JToken>("/0/public/Ticker", null, new Dictionary<string, object> { { "pair", csvPairsList } }, cancellationToken: cancellationToken);
foreach (string symbol in symbols) foreach (string symbol in symbols)
{ {
var ticker = ConvertToExchangeTicker(symbol, apiTickers[symbol]); var ticker = ConvertToExchangeTicker(symbol, apiTickers[symbol]);
if (ticker != null) if (ticker != null)
{ {
try var pair = GetCurrencyPair(symbol);
{ if (pair is not null)
string global = null;
var mapped1 = _TickerMapping.Where(t => symbol.StartsWith(t.Key, StringComparison.OrdinalIgnoreCase))
.Select(t => new { KrakenTicker = t.Key, PayTicker = t.Value }).SingleOrDefault();
if (mapped1 != null)
{
var p2 = symbol.Substring(mapped1.KrakenTicker.Length);
if (_TickerMapping.TryGetValue(p2, out var mapped2))
p2 = mapped2;
global = $"{mapped1.PayTicker}_{p2}";
}
else
{
global = await helper.ExchangeMarketSymbolToGlobalMarketSymbolAsync(symbol);
}
if (CurrencyPair.TryParse(global, out var pair))
result.Add(new PairRate(pair, new BidAsk(ticker.Bid, ticker.Ask))); result.Add(new PairRate(pair, new BidAsk(ticker.Bid, ticker.Ask)));
else
notFoundSymbols.TryAdd(symbol, symbol);
}
catch (ArgumentException)
{
notFoundSymbols.TryAdd(symbol, symbol);
}
} }
} }
return result.ToArray(); return result.ToArray();