mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-03-03 09:29:10 +01:00
Refactor rate handling to prevent error of exchange name
This commit is contained in:
parent
605a0fd3c9
commit
a8ac01cd8b
17 changed files with 115 additions and 184 deletions
|
@ -11,7 +11,15 @@ namespace BTCPayServer.Rating
|
|||
Dictionary<string, ExchangeRate> _AllRates = new Dictionary<string, ExchangeRate>();
|
||||
public ExchangeRates()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ExchangeRates(string exchangeName, IEnumerable<PairRate> rates)
|
||||
{
|
||||
foreach (var rate in rates)
|
||||
{
|
||||
Add(new ExchangeRate(exchangeName, rate.CurrencyPair, rate.BidAsk));
|
||||
}
|
||||
}
|
||||
public ExchangeRates(IEnumerable<ExchangeRate> rates)
|
||||
{
|
||||
|
@ -218,6 +226,26 @@ namespace BTCPayServer.Rating
|
|||
return $"({Bid.ToString(CultureInfo.InvariantCulture)} , {Ask.ToString(CultureInfo.InvariantCulture)})";
|
||||
}
|
||||
}
|
||||
|
||||
public class PairRate
|
||||
{
|
||||
public PairRate(CurrencyPair currencyPair, BidAsk bidAsk)
|
||||
{
|
||||
if (currencyPair == null)
|
||||
throw new ArgumentNullException(nameof(currencyPair));
|
||||
if (bidAsk == null)
|
||||
throw new ArgumentNullException(nameof(bidAsk));
|
||||
this.CurrencyPair = currencyPair;
|
||||
this.BidAsk = bidAsk;
|
||||
}
|
||||
public CurrencyPair CurrencyPair { get; }
|
||||
public BidAsk BidAsk { get; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{CurrencyPair} == {BidAsk}";
|
||||
}
|
||||
}
|
||||
public class ExchangeRate
|
||||
{
|
||||
public ExchangeRate()
|
||||
|
|
|
@ -60,14 +60,13 @@ namespace BTCPayServer.Services.Rates
|
|||
{
|
||||
public class LatestFetch
|
||||
{
|
||||
public ExchangeRates Latest;
|
||||
public PairRate[] Latest;
|
||||
public DateTimeOffset NextRefresh;
|
||||
public TimeSpan Backoff = TimeSpan.FromSeconds(5.0);
|
||||
public DateTimeOffset Updated;
|
||||
public DateTimeOffset Expiration;
|
||||
public Exception Exception;
|
||||
public string ExchangeName;
|
||||
internal ExchangeRates GetResult()
|
||||
internal PairRate[] GetResult()
|
||||
{
|
||||
if (Expiration <= DateTimeOffset.UtcNow)
|
||||
{
|
||||
|
@ -77,7 +76,7 @@ namespace BTCPayServer.Services.Rates
|
|||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"The rate has expired ({ExchangeName})");
|
||||
throw new InvalidOperationException($"The rate has expired");
|
||||
}
|
||||
}
|
||||
return Latest;
|
||||
|
@ -108,7 +107,6 @@ namespace BTCPayServer.Services.Rates
|
|||
{
|
||||
state.LastUpdated = fetch.Updated;
|
||||
state.Rates = fetch.Latest
|
||||
.Where(e => e.Exchange == ExchangeName)
|
||||
.Select(r => new BackgroundFetcherRate()
|
||||
{
|
||||
Pair = r.CurrencyPair,
|
||||
|
@ -128,8 +126,7 @@ namespace BTCPayServer.Services.Rates
|
|||
{
|
||||
var fetch = new LatestFetch()
|
||||
{
|
||||
ExchangeName = state.ExchangeName,
|
||||
Latest = new ExchangeRates(rates.Select(r => new ExchangeRate(state.ExchangeName, r.Pair, r.BidAsk))),
|
||||
Latest = rates.Select(r => new PairRate(r.Pair, r.BidAsk)).ToArray(),
|
||||
Updated = updated,
|
||||
NextRefresh = updated + RefreshRate,
|
||||
Expiration = updated + ValidatyTime
|
||||
|
@ -207,7 +204,7 @@ namespace BTCPayServer.Services.Rates
|
|||
}
|
||||
|
||||
LatestFetch _Latest;
|
||||
public async Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken)
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
LastRequested = DateTimeOffset.UtcNow;
|
||||
var latest = _Latest;
|
||||
|
@ -241,7 +238,6 @@ namespace BTCPayServer.Services.Rates
|
|||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var previous = _Latest;
|
||||
var fetch = new LatestFetch();
|
||||
fetch.ExchangeName = ExchangeName;
|
||||
try
|
||||
{
|
||||
var rates = await _Inner.GetRatesAsync(cancellationToken);
|
||||
|
|
|
@ -9,24 +9,22 @@ using Newtonsoft.Json.Linq;
|
|||
|
||||
namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public class BitbankRateProvider : IRateProvider, IHasExchangeName
|
||||
public class BitbankRateProvider : IRateProvider
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
public BitbankRateProvider(HttpClient httpClient)
|
||||
{
|
||||
_httpClient = httpClient ?? new HttpClient();
|
||||
}
|
||||
public string ExchangeName => "bitbank";
|
||||
|
||||
public async Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken)
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _httpClient.GetAsync("https://public.bitbank.cc/prices", cancellationToken);
|
||||
var jobj = await response.Content.ReadAsAsync<JObject>(cancellationToken);
|
||||
return new ExchangeRates(((jobj["data"] as JObject) ?? new JObject())
|
||||
return ((jobj["data"] as JObject) ?? new JObject())
|
||||
.Properties()
|
||||
.Select(p => new ExchangeRate(ExchangeName, CurrencyPair.Parse(p.Name), CreateBidAsk(p)))
|
||||
.ToArray());
|
||||
|
||||
.Select(p => new PairRate(CurrencyPair.Parse(p.Name), CreateBidAsk(p)))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private static BidAsk CreateBidAsk(JProperty p)
|
||||
|
|
|
@ -10,26 +10,23 @@ using Newtonsoft.Json.Linq;
|
|||
|
||||
namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public class BitpayRateProvider : IRateProvider, IHasExchangeName
|
||||
public class BitpayRateProvider : IRateProvider
|
||||
{
|
||||
public const string BitpayName = "bitpay";
|
||||
private readonly HttpClient _httpClient;
|
||||
public BitpayRateProvider(HttpClient httpClient)
|
||||
{
|
||||
_httpClient = httpClient ?? new HttpClient();
|
||||
}
|
||||
|
||||
public string ExchangeName => BitpayName;
|
||||
|
||||
public async Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken)
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _httpClient.GetAsync("https://bitpay.com/rates", cancellationToken);
|
||||
var jarray = (JArray)(await response.Content.ReadAsAsync<JObject>(cancellationToken))["data"];
|
||||
return new ExchangeRates(jarray
|
||||
return jarray
|
||||
.Children<JObject>()
|
||||
.Select(jobj => new ExchangeRate(ExchangeName, new CurrencyPair("BTC", jobj["code"].Value<string>()), new BidAsk(jobj["rate"].Value<decimal>())))
|
||||
.Select(jobj => new PairRate(new CurrencyPair("BTC", jobj["code"].Value<string>()), new BidAsk(jobj["rate"].Value<decimal>())))
|
||||
.Where(o => o.CurrencyPair.Right != "BTC")
|
||||
.ToArray());
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,21 +7,20 @@ using Newtonsoft.Json.Linq;
|
|||
|
||||
namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public class ByllsRateProvider : IRateProvider, IHasExchangeName
|
||||
public class ByllsRateProvider : IRateProvider
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
public ByllsRateProvider(HttpClient httpClient)
|
||||
{
|
||||
_httpClient = httpClient ?? new HttpClient();
|
||||
}
|
||||
public string ExchangeName => "bylls";
|
||||
|
||||
public async Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken)
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _httpClient.GetAsync("https://bylls.com/api/price?from_currency=BTC&to_currency=CAD", cancellationToken);
|
||||
var jobj = await response.Content.ReadAsAsync<JObject>(cancellationToken);
|
||||
var value = jobj["public_price"]["to_price"].Value<decimal>();
|
||||
return new ExchangeRates(new[] { new ExchangeRate(ExchangeName, new CurrencyPair("BTC", "CAD"), new BidAsk(value)) });
|
||||
return new[] { new PairRate(new CurrencyPair("BTC", "CAD"), new BidAsk(value)) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -11,17 +11,15 @@ using ExchangeSharp;
|
|||
|
||||
namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public class ExchangeSharpRateProvider : IRateProvider, IHasExchangeName
|
||||
public class ExchangeSharpRateProvider : IRateProvider
|
||||
{
|
||||
readonly ExchangeAPI _ExchangeAPI;
|
||||
readonly string _ExchangeName;
|
||||
public ExchangeSharpRateProvider(string exchangeName, ExchangeAPI exchangeAPI, bool reverseCurrencyPair = false)
|
||||
public ExchangeSharpRateProvider(ExchangeAPI exchangeAPI, bool reverseCurrencyPair = false)
|
||||
{
|
||||
if (exchangeAPI == null)
|
||||
throw new ArgumentNullException(nameof(exchangeAPI));
|
||||
exchangeAPI.RequestTimeout = TimeSpan.FromSeconds(5.0);
|
||||
_ExchangeAPI = exchangeAPI;
|
||||
_ExchangeName = exchangeName;
|
||||
ReverseCurrencyPair = reverseCurrencyPair;
|
||||
}
|
||||
|
||||
|
@ -30,9 +28,7 @@ namespace BTCPayServer.Services.Rates
|
|||
get; set;
|
||||
}
|
||||
|
||||
public string ExchangeName => _ExchangeName;
|
||||
|
||||
public async Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken)
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await new SynchronizationContextRemover();
|
||||
var rates = await _ExchangeAPI.GetTickersAsync();
|
||||
|
@ -43,14 +39,14 @@ namespace BTCPayServer.Services.Rates
|
|||
|
||||
var exchangeRates = await Task.WhenAll(exchangeRateTasks);
|
||||
|
||||
return new ExchangeRates(exchangeRates
|
||||
return exchangeRates
|
||||
.Where(t => t != null)
|
||||
.ToArray());
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
// ExchangeSymbolToGlobalSymbol throws exception which would kill perf
|
||||
ConcurrentDictionary<string, string> notFoundSymbols = new ConcurrentDictionary<string, string>();
|
||||
private async Task<ExchangeRate> CreateExchangeRate(KeyValuePair<string, ExchangeTicker> ticker)
|
||||
private async Task<PairRate> CreateExchangeRate(KeyValuePair<string, ExchangeTicker> ticker)
|
||||
{
|
||||
if (notFoundSymbols.TryGetValue(ticker.Key, out _))
|
||||
return null;
|
||||
|
@ -64,11 +60,7 @@ namespace BTCPayServer.Services.Rates
|
|||
}
|
||||
if(ReverseCurrencyPair)
|
||||
pair = new CurrencyPair(pair.Right, pair.Left);
|
||||
var rate = new ExchangeRate();
|
||||
rate.CurrencyPair = pair;
|
||||
rate.Exchange = _ExchangeName;
|
||||
rate.BidAsk = new BidAsk(ticker.Value.Bid, ticker.Value.Ask);
|
||||
return rate;
|
||||
return new PairRate(pair, new BidAsk(ticker.Value.Bid, ticker.Value.Ask));
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace BTCPayServer.Services.Rates
|
|||
_Providers = providers;
|
||||
}
|
||||
|
||||
public async Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken)
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
foreach (var p in _Providers)
|
||||
{
|
||||
|
@ -31,7 +31,7 @@ namespace BTCPayServer.Services.Rates
|
|||
}
|
||||
catch(Exception ex) { Exceptions.Add(ex); }
|
||||
}
|
||||
return new ExchangeRates();
|
||||
return Array.Empty<PairRate>();
|
||||
}
|
||||
|
||||
public List<Exception> Exceptions { get; set; } = new List<Exception>();
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public interface IHasExchangeName
|
||||
{
|
||||
string ExchangeName { get; }
|
||||
}
|
||||
}
|
|
@ -9,6 +9,6 @@ namespace BTCPayServer.Services.Rates
|
|||
{
|
||||
public interface IRateProvider
|
||||
{
|
||||
Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken);
|
||||
Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ using Newtonsoft.Json.Linq;
|
|||
namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
// Make sure that only one request is sent to kraken in general
|
||||
public class KrakenExchangeRateProvider : IRateProvider, IHasExchangeName
|
||||
public class KrakenExchangeRateProvider : IRateProvider
|
||||
{
|
||||
public KrakenExchangeRateProvider()
|
||||
{
|
||||
|
@ -87,9 +87,9 @@ namespace BTCPayServer.Services.Rates
|
|||
{ "ZGBP", "GBP" }
|
||||
};
|
||||
|
||||
public async Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken)
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var result = new ExchangeRates();
|
||||
var result = new List<PairRate>();
|
||||
var symbols = await GetSymbolsAsync(cancellationToken);
|
||||
var normalizedPairsList = symbols.Where(s => !notFoundSymbols.ContainsKey(s)).Select(s => _Helper.NormalizeMarketSymbol(s)).ToList();
|
||||
var csvPairsList = string.Join(",", normalizedPairsList);
|
||||
|
@ -117,7 +117,7 @@ namespace BTCPayServer.Services.Rates
|
|||
global = await _Helper.ExchangeMarketSymbolToGlobalMarketSymbolAsync(symbol);
|
||||
}
|
||||
if (CurrencyPair.TryParse(global, out var pair))
|
||||
result.Add(new ExchangeRate("kraken", pair.Inverse(), new BidAsk(ticker.Bid, ticker.Ask)));
|
||||
result.Add(new PairRate(pair.Inverse(), new BidAsk(ticker.Bid, ticker.Ask)));
|
||||
else
|
||||
notFoundSymbols.TryAdd(symbol, symbol);
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ namespace BTCPayServer.Services.Rates
|
|||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private static ExchangeTicker ConvertToExchangeTicker(string symbol, JToken ticker)
|
||||
|
|
|
@ -21,9 +21,9 @@ namespace BTCPayServer.Services.Rates
|
|||
return _Instance;
|
||||
}
|
||||
}
|
||||
public Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken)
|
||||
public Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(new ExchangeRates());
|
||||
return Task.FromResult(Array.Empty<PairRate>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,9 +79,9 @@ namespace BTCPayServer.Services.Rates
|
|||
result.Latency = query.Latency;
|
||||
if (query.Exception != null)
|
||||
result.ExchangeExceptions.Add(query.Exception);
|
||||
foreach (var rule in query.ExchangeRates)
|
||||
foreach (var rule in query.PairRates)
|
||||
{
|
||||
rateRule.ExchangeRates.SetRate(rule.Exchange, rule.CurrencyPair, rule.BidAsk);
|
||||
rateRule.ExchangeRates.SetRate(query.Exchange, rule.CurrencyPair, rule.BidAsk);
|
||||
}
|
||||
}
|
||||
rateRule.Reevaluate();
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace BTCPayServer.Services.Rates
|
|||
{
|
||||
_inner = inner;
|
||||
}
|
||||
public async Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken)
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
DateTimeOffset now = DateTimeOffset.UtcNow;
|
||||
try
|
||||
|
@ -35,7 +35,7 @@ namespace BTCPayServer.Services.Rates
|
|||
catch (Exception ex)
|
||||
{
|
||||
Exception = ex;
|
||||
return new ExchangeRates();
|
||||
return Array.Empty<PairRate>();
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -46,37 +46,15 @@ namespace BTCPayServer.Services.Rates
|
|||
public class QueryRateResult
|
||||
{
|
||||
public TimeSpan Latency { get; set; }
|
||||
public ExchangeRates ExchangeRates { get; set; }
|
||||
public PairRate[] PairRates { get; set; }
|
||||
public ExchangeException Exception { get; internal set; }
|
||||
public string Exchange { get; internal set; }
|
||||
}
|
||||
public RateProviderFactory(IHttpClientFactory httpClientFactory)
|
||||
{
|
||||
_httpClientFactory = httpClientFactory;
|
||||
// We use 15 min because of limits with free version of bitcoinaverage
|
||||
CacheSpan = TimeSpan.FromMinutes(15.0);
|
||||
InitExchanges();
|
||||
}
|
||||
TimeSpan _CacheSpan;
|
||||
public TimeSpan CacheSpan
|
||||
{
|
||||
get
|
||||
{
|
||||
return _CacheSpan;
|
||||
}
|
||||
set
|
||||
{
|
||||
_CacheSpan = value;
|
||||
InvalidateCache();
|
||||
}
|
||||
}
|
||||
public void InvalidateCache()
|
||||
{
|
||||
if (Providers.TryGetValue(CoinGeckoRateProvider.CoinGeckoName, out var coinAverage) && coinAverage is BackgroundFetcherRateProvider c)
|
||||
{
|
||||
c.RefreshRate = CacheSpan;
|
||||
c.ValidatyTime = CacheSpan + TimeSpan.FromMinutes(1.0);
|
||||
}
|
||||
}
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly Dictionary<string, IRateProvider> _DirectProviders = new Dictionary<string, IRateProvider>();
|
||||
public Dictionary<string, IRateProvider> Providers
|
||||
|
@ -94,7 +72,7 @@ namespace BTCPayServer.Services.Rates
|
|||
yield return new AvailableRateProvider("hitbtc", "HitBTC", "https://api.hitbtc.com/api/2/public/ticker");
|
||||
yield return new AvailableRateProvider("ndax", "NDAX", "https://ndax.io/api/returnTicker");
|
||||
|
||||
yield return new AvailableRateProvider(CoinGeckoRateProvider.CoinGeckoName, "Coin Gecko", "https://api.coingecko.com/api/v3/exchange_rates");
|
||||
yield return new AvailableRateProvider("coingecko", "CoinGecko", "https://api.coingecko.com/api/v3/exchange_rates");
|
||||
yield return new AvailableRateProvider("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");
|
||||
yield return new AvailableRateProvider("bylls", "Bylls", "https://bylls.com/api/price?from_currency=BTC&to_currency=CAD");
|
||||
yield return new AvailableRateProvider("bitbank", "Bitbank", "https://public.bitbank.cc/prices");
|
||||
|
@ -103,14 +81,14 @@ namespace BTCPayServer.Services.Rates
|
|||
void InitExchanges()
|
||||
{
|
||||
// We need to be careful to only add exchanges which OnGetTickers implementation make only 1 request
|
||||
Providers.Add("binance", new ExchangeSharpRateProvider("binance", new ExchangeBinanceAPI(), true));
|
||||
Providers.Add("bittrex", new ExchangeSharpRateProvider("bittrex", new ExchangeBittrexAPI(), true));
|
||||
Providers.Add("poloniex", new ExchangeSharpRateProvider("poloniex", new ExchangePoloniexAPI(), true));
|
||||
Providers.Add("hitbtc", new ExchangeSharpRateProvider("hitbtc", new ExchangeHitBTCAPI(), true));
|
||||
Providers.Add("ndax", new ExchangeSharpRateProvider("ndax", new ExchangeNDAXAPI(), true));
|
||||
Providers.Add("binance", new ExchangeSharpRateProvider(new ExchangeBinanceAPI(), true));
|
||||
Providers.Add("bittrex", new ExchangeSharpRateProvider(new ExchangeBittrexAPI(), true));
|
||||
Providers.Add("poloniex", new ExchangeSharpRateProvider(new ExchangePoloniexAPI(), true));
|
||||
Providers.Add("hitbtc", new ExchangeSharpRateProvider(new ExchangeHitBTCAPI(), true));
|
||||
Providers.Add("ndax", new ExchangeSharpRateProvider(new ExchangeNDAXAPI(), true));
|
||||
|
||||
// Handmade providers
|
||||
Providers.Add(CoinGeckoRateProvider.CoinGeckoName, new CoinGeckoRateProvider(_httpClientFactory));
|
||||
Providers.Add("coingecko", new CoinGeckoRateProvider(_httpClientFactory));
|
||||
Providers.Add("kraken", new KrakenExchangeRateProvider() { HttpClient = _httpClientFactory?.CreateClient("EXCHANGE_KRAKEN") });
|
||||
Providers.Add("bylls", new ByllsRateProvider(_httpClientFactory?.CreateClient("EXCHANGE_BYLLS")));
|
||||
Providers.Add("bitbank", new BitbankRateProvider(_httpClientFactory?.CreateClient("EXCHANGE_BITBANK")));
|
||||
|
@ -132,11 +110,11 @@ namespace BTCPayServer.Services.Rates
|
|||
|
||||
foreach (var supportedExchange in GetCoinGeckoSupportedExchanges())
|
||||
{
|
||||
if (!Providers.ContainsKey(supportedExchange.Id))
|
||||
if (!Providers.ContainsKey(supportedExchange.Id) && supportedExchange.Id != "coingecko")
|
||||
{
|
||||
var coingecko = new CoinGeckoRateProvider(_httpClientFactory)
|
||||
{
|
||||
Exchange = supportedExchange.Id
|
||||
UnderlyingExchange = supportedExchange.Id
|
||||
};
|
||||
var bgFetcher = new BackgroundFetcherRateProvider(supportedExchange.Id, coingecko);
|
||||
bgFetcher.RefreshRate = TimeSpan.FromMinutes(1.0);
|
||||
|
@ -191,8 +169,9 @@ namespace BTCPayServer.Services.Rates
|
|||
var value = await wrapper.GetRatesAsync(cancellationToken);
|
||||
return new QueryRateResult()
|
||||
{
|
||||
Exchange = exchangeName,
|
||||
Latency = wrapper.Latency,
|
||||
ExchangeRates = value,
|
||||
PairRates = value,
|
||||
Exception = wrapper.Exception != null ? new ExchangeException() { Exception = wrapper.Exception, ExchangeName = exchangeName } : null
|
||||
};
|
||||
}
|
||||
|
|
|
@ -200,67 +200,27 @@ namespace BTCPayServer.Tests
|
|||
rateProvider.Providers.Clear();
|
||||
|
||||
var coinAverageMock = new MockRateProvider();
|
||||
coinAverageMock.ExchangeRates.Add(new Rating.ExchangeRate()
|
||||
{
|
||||
Exchange = "coingecko",
|
||||
CurrencyPair = CurrencyPair.Parse("BTC_USD"),
|
||||
BidAsk = new BidAsk(5000m)
|
||||
});
|
||||
coinAverageMock.ExchangeRates.Add(new Rating.ExchangeRate()
|
||||
{
|
||||
Exchange = "coingecko",
|
||||
CurrencyPair = CurrencyPair.Parse("BTC_CAD"),
|
||||
BidAsk = new BidAsk(4500m)
|
||||
});
|
||||
coinAverageMock.ExchangeRates.Add(new Rating.ExchangeRate()
|
||||
{
|
||||
Exchange = "coingecko",
|
||||
CurrencyPair = CurrencyPair.Parse("BTC_LTC"),
|
||||
BidAsk = new BidAsk(162m)
|
||||
});
|
||||
coinAverageMock.ExchangeRates.Add(new Rating.ExchangeRate()
|
||||
{
|
||||
Exchange = "coingecko",
|
||||
CurrencyPair = CurrencyPair.Parse("LTC_USD"),
|
||||
BidAsk = new BidAsk(500m)
|
||||
});
|
||||
coinAverageMock.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("BTC_USD"), new BidAsk(5000m)));
|
||||
coinAverageMock.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("BTC_CAD"), new BidAsk(4500m)));
|
||||
coinAverageMock.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("BTC_LTC"), new BidAsk(162m)));
|
||||
coinAverageMock.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("LTC_USD"), new BidAsk(500m)));
|
||||
rateProvider.Providers.Add("coingecko", coinAverageMock);
|
||||
|
||||
var bitflyerMock = new MockRateProvider();
|
||||
bitflyerMock.ExchangeRates.Add(new Rating.ExchangeRate()
|
||||
{
|
||||
Exchange = "bitflyer",
|
||||
CurrencyPair = CurrencyPair.Parse("BTC_JPY"),
|
||||
BidAsk = new BidAsk(700000m)
|
||||
});
|
||||
bitflyerMock.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("BTC_JPY"), new BidAsk(700000m)));
|
||||
rateProvider.Providers.Add("bitflyer", bitflyerMock);
|
||||
|
||||
var quadrigacx = new MockRateProvider();
|
||||
quadrigacx.ExchangeRates.Add(new Rating.ExchangeRate()
|
||||
{
|
||||
Exchange = "quadrigacx",
|
||||
CurrencyPair = CurrencyPair.Parse("BTC_CAD"),
|
||||
BidAsk = new BidAsk(6000m)
|
||||
});
|
||||
quadrigacx.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("BTC_CAD"), new BidAsk(6000m)));
|
||||
rateProvider.Providers.Add("quadrigacx", quadrigacx);
|
||||
|
||||
var bittrex = new MockRateProvider();
|
||||
bittrex.ExchangeRates.Add(new Rating.ExchangeRate()
|
||||
{
|
||||
Exchange = "bittrex",
|
||||
CurrencyPair = CurrencyPair.Parse("DOGE_BTC"),
|
||||
BidAsk = new BidAsk(0.004m)
|
||||
});
|
||||
bittrex.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("DOGE_BTC"), new BidAsk(0.004m)));
|
||||
rateProvider.Providers.Add("bittrex", bittrex);
|
||||
|
||||
|
||||
var bitfinex = new MockRateProvider();
|
||||
bitfinex.ExchangeRates.Add(new Rating.ExchangeRate()
|
||||
{
|
||||
Exchange = "bitfinex",
|
||||
CurrencyPair = CurrencyPair.Parse("UST_BTC"),
|
||||
BidAsk = new BidAsk(0.000136m)
|
||||
});
|
||||
bitfinex.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("UST_BTC"), new BidAsk(0.000136m)));
|
||||
rateProvider.Providers.Add("bitfinex", bitfinex);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,15 +10,15 @@ namespace BTCPayServer.Tests.Mocks
|
|||
{
|
||||
public class MockRateProvider : IRateProvider
|
||||
{
|
||||
public ExchangeRates ExchangeRates { get; set; } = new ExchangeRates();
|
||||
public List<PairRate> ExchangeRates { get; set; } = new List<PairRate>();
|
||||
|
||||
public MockRateProvider()
|
||||
{
|
||||
|
||||
}
|
||||
public Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken)
|
||||
public Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(ExchangeRates);
|
||||
return Task.FromResult(ExchangeRates.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2684,14 +2684,14 @@ noninventoryitem:
|
|||
var all = string.Join("\r\n", factory.GetSupportedExchanges().Select(e => e.Id).ToArray());
|
||||
foreach (var result in factory
|
||||
.Providers
|
||||
.Where(p => p.Value is BackgroundFetcherRateProvider bf && !(bf.Inner is CoinGeckoRateProvider cg && !cg.CoinGeckoRate))
|
||||
.Where(p => p.Value is BackgroundFetcherRateProvider bf && !(bf.Inner is CoinGeckoRateProvider cg && cg.UnderlyingExchange != null))
|
||||
.Select(p => (ExpectedName: p.Key, ResultAsync: p.Value.GetRatesAsync(default), Fetcher: (BackgroundFetcherRateProvider)p.Value))
|
||||
.ToList())
|
||||
{
|
||||
|
||||
Logs.Tester.LogInformation($"Testing {result.ExpectedName}");
|
||||
result.Fetcher.InvalidateCache();
|
||||
var exchangeRates = result.ResultAsync.Result;
|
||||
var exchangeRates = new ExchangeRates(result.ExpectedName, result.ResultAsync.Result);
|
||||
result.Fetcher.InvalidateCache();
|
||||
Assert.NotNull(exchangeRates);
|
||||
Assert.NotEmpty(exchangeRates);
|
||||
|
@ -2788,12 +2788,12 @@ noninventoryitem:
|
|||
class SpyRateProvider : IRateProvider
|
||||
{
|
||||
public bool Hit { get; set; }
|
||||
public Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken)
|
||||
public Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Hit = true;
|
||||
var rates = new ExchangeRates();
|
||||
rates.Add(new ExchangeRate("coinaverage", CurrencyPair.Parse("BTC_USD"), new BidAsk(5000)));
|
||||
return Task.FromResult(rates);
|
||||
var rates = new List<PairRate>();
|
||||
rates.Add(new PairRate(CurrencyPair.Parse("BTC_USD"), new BidAsk(5000)));
|
||||
return Task.FromResult(rates.ToArray());
|
||||
}
|
||||
|
||||
public void AssertHit()
|
||||
|
@ -2890,7 +2890,6 @@ noninventoryitem:
|
|||
|
||||
var factory = CreateBTCPayRateFactory();
|
||||
factory.Providers.Clear();
|
||||
factory.CacheSpan = TimeSpan.FromSeconds(1);
|
||||
var fetcher = new RateFetcher(factory);
|
||||
factory.Providers.Clear();
|
||||
var fetch = new BackgroundFetcherRateProvider("spy", spy);
|
||||
|
|
Loading…
Add table
Reference in a new issue