コード例 #1
0
 private ExchangeRate CreateExchangeRate(KeyValuePair <string, ExchangeTicker> ticker)
 {
     if (notFoundSymbols.Contains(ticker.Key))
     {
         return(null);
     }
     try
     {
         var tickerName = _ExchangeAPI.ExchangeSymbolToGlobalSymbol(ticker.Key);
         if (!CurrencyPair.TryParse(tickerName, out var pair))
         {
             notFoundSymbols.Add(ticker.Key);
             return(null);
         }
         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);
     }
     catch (ArgumentException)
     {
         notFoundSymbols.Add(ticker.Key);
         return(null);
     }
 }
コード例 #2
0
            public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax node)
            {
                var exchangeName = node.Expression.ToString();

                if (exchangeName.StartsWith("ERR_", StringComparison.OrdinalIgnoreCase))
                {
                    Errors.Add(RateRulesErrors.PreprocessError);
                    return(base.VisitInvocationExpression(node));
                }

                var currencyPair = node.ArgumentList.ChildNodes().FirstOrDefault()?.ToString();

                if (currencyPair == null || !CurrencyPair.TryParse(currencyPair, out var pair))
                {
                    Errors.Add(RateRulesErrors.InvalidCurrencyIdentifier);
                    return(RateRules.CreateExpression($"ERR_INVALID_CURRENCY_PAIR({node.ToString()})"));
                }
                else
                {
                    var rate = Rates.GetRate(exchangeName, pair);
                    if (rate == null)
                    {
                        Errors.Add(RateRulesErrors.RateUnavailable);
                        return(RateRules.CreateExpression($"ERR_RATE_UNAVAILABLE({exchangeName}, {pair.ToString()})"));
                    }
                    else
                    {
                        return(RateRules.CreateExpression(rate.ToString()));
                    }
                }
            }
コード例 #3
0
        private async Task <PairRate> CreateExchangeRate(T exchangeAPI, KeyValuePair <string, ExchangeTicker> ticker)
        {
            if (notFoundSymbols.TryGetValue(ticker.Key, out _))
            {
                return(null);
            }
            if (ticker.Key.Contains("XMR"))
            {
            }
            try
            {
                CurrencyPair pair;
                if (ticker.Value.Volume.BaseCurrency is not null && ticker.Value.Volume.QuoteCurrency is not null)
                {
                    pair = new CurrencyPair(ticker.Value.Volume.BaseCurrency, ticker.Value.Volume.QuoteCurrency);
                }
                else
                {
                    var tickerName = await exchangeAPI.ExchangeMarketSymbolToGlobalMarketSymbolAsync(ticker.Key);

                    if (!CurrencyPair.TryParse(tickerName, out pair))
                    {
                        notFoundSymbols.TryAdd(ticker.Key, ticker.Key);
                        return(null);
                    }
                }
                return(new PairRate(pair, new BidAsk(ticker.Value.Bid, ticker.Value.Ask)));
            }
コード例 #4
0
        private async Task <PairRate> CreateExchangeRate(T exchangeAPI, KeyValuePair <string, ExchangeTicker> ticker)
        {
            if (notFoundSymbols.TryGetValue(ticker.Key, out _))
            {
                return(null);
            }
            try
            {
                var tickerName = await exchangeAPI.ExchangeMarketSymbolToGlobalMarketSymbolAsync(ticker.Key);

                if (!CurrencyPair.TryParse(tickerName, out var pair))
                {
                    notFoundSymbols.TryAdd(ticker.Key, ticker.Key);
                    return(null);
                }
                if (ReverseCurrencyPair)
                {
                    pair = new CurrencyPair(pair.Right, pair.Left);
                }
                return(new PairRate(pair, new BidAsk(ticker.Value.Bid, ticker.Value.Ask)));
            }
            catch (ArgumentException)
            {
                notFoundSymbols.TryAdd(ticker.Key, ticker.Key);
                return(null);
            }
        }
コード例 #5
0
        public async Task <IActionResult> GetRates2(string currencyPairs, string storeId, CancellationToken cancellationToken)
        {
            var store = CurrentStore ?? await _storeRepo.FindStore(storeId);

            if (store == null)
            {
                var err = Json(new BitpayErrorsModel {
                    Error = "Store not found"
                });
                err.StatusCode = 404;
                return(err);
            }
            if (currencyPairs == null)
            {
                currencyPairs = store.GetStoreBlob().GetDefaultCurrencyPairString();
                if (string.IsNullOrEmpty(currencyPairs))
                {
                    var result = Json(new BitpayErrorsModel()
                    {
                        Error = "You need to setup the default currency pairs in 'Store Settings / Rates' or specify 'currencyPairs' query parameter (eg. BTC_USD,LTC_CAD)."
                    });
                    result.StatusCode = 400;
                    return(result);
                }
            }

            var rules = store.GetStoreBlob().GetRateRules(_networkProvider);
            var pairs = new HashSet <CurrencyPair>();

            foreach (var currency in currencyPairs.Split(','))
            {
                if (!CurrencyPair.TryParse(currency, out var pair))
                {
                    var result = Json(new BitpayErrorsModel()
                    {
                        Error = $"Currency pair {currency} uncorrectly formatted"
                    });
                    result.StatusCode = 400;
                    return(result);
                }
                pairs.Add(pair);
            }

            var fetching = _rateProviderFactory.FetchRates(pairs, rules, cancellationToken);
            await Task.WhenAll(fetching.Select(f => f.Value).ToArray());

            return(Json(pairs
                        .Select(r => (Pair: r, Value: fetching[r].GetAwaiter().GetResult().BidAsk?.Bid))
                        .Where(r => r.Value.HasValue)
                        .Select(r =>
                                new Rate
            {
                CryptoCode = r.Pair.Left,
                Code = r.Pair.Right,
                CurrencyPair = r.Pair.ToString(),
                Name = _currencyNameTable.GetCurrencyData(r.Pair.Right, true).Name,
                Value = r.Value.Value
            }).Where(n => n.Name != null).ToArray()));
        }
コード例 #6
0
        public async Task <PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
        {
            var result  = new List <PairRate>();
            var symbols = await GetSymbolsAsync(cancellationToken);

            var helper = (ExchangeKrakenAPI)await ExchangeAPI.GetExchangeAPIAsync <ExchangeKrakenAPI>();

            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);

            var tickers = new List <KeyValuePair <string, ExchangeTicker> >();

            foreach (string symbol in symbols)
            {
                var ticker = ConvertToExchangeTicker(symbol, apiTickers[symbol]);
                if (ticker != null)
                {
                    try
                    {
                        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)));
                        }
                        else
                        {
                            notFoundSymbols.TryAdd(symbol, symbol);
                        }
                    }
                    catch (ArgumentException)
                    {
                        notFoundSymbols.TryAdd(symbol, symbol);
                    }
                }
            }
            return(result.ToArray());
        }
コード例 #7
0
        public async Task <ExchangeRates> GetRatesAsync()
        {
            string url = Exchange == CoinAverageName ? $"https://apiv2.bitcoinaverage.com/indices/{Market}/ticker/short"
                                         : $"https://apiv2.bitcoinaverage.com/exchanges/{Exchange}";

            var request = new HttpRequestMessage(HttpMethod.Get, url);
            var auth    = Authenticator;

            if (auth != null)
            {
                await auth.AddHeader(request);
            }
            var resp = await HttpClient.SendAsync(request);

            using (resp)
            {
                if ((int)resp.StatusCode == 401)
                {
                    throw new CoinAverageException("Unauthorized access to the API");
                }
                if ((int)resp.StatusCode == 429)
                {
                    throw new CoinAverageException("Exceed API limits");
                }
                if ((int)resp.StatusCode == 403)
                {
                    throw new CoinAverageException("Unauthorized access to the API, premium plan needed");
                }
                resp.EnsureSuccessStatusCode();
                var rates = JObject.Parse(await resp.Content.ReadAsStringAsync());
                if (Exchange != CoinAverageName)
                {
                    rates = (JObject)rates["symbols"];
                }

                var exchangeRates = new ExchangeRates();
                foreach (var prop in rates.Properties())
                {
                    ExchangeRate exchangeRate = new ExchangeRate();
                    exchangeRate.Exchange = Exchange;
                    if (!TryToBidAsk(prop, out var value))
                    {
                        continue;
                    }
                    exchangeRate.BidAsk = value;
                    if (CurrencyPair.TryParse(prop.Name, out var pair))
                    {
                        exchangeRate.CurrencyPair = pair;
                        exchangeRates.Add(exchangeRate);
                    }
                }
                return(exchangeRates);
            }
        }
コード例 #8
0
 public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
 {
     try
     {
         return(reader.TokenType == JsonToken.Null ? null :
                CurrencyPair.TryParse((string)reader.Value, out var result) ? result :
                throw new JsonObjectException("Invalid currency pair", reader));
     }
     catch (InvalidCastException)
     {
         throw new JsonObjectException("Invalid currency pair", reader);
     }
 }
コード例 #9
0
        private static Sensitivity ReadSensitivityFx(EntryObject o)
        {
            if (!Enum.TryParse(o.ProductClass, out Model.Product product))
            {
                throw new InvalidDataException($"The CRIF file contains a {o.RiskType} entry with an invalid ProductClass property.");
            }

            if (product != Model.Product.RatesFx)
            {
                throw new InvalidDataException($"The CRIF file contains a {o.RiskType} entry associated to a product class other than {Model.Product.RatesFx}.");
            }

            Amount          amount          = ReadAmount(o);
            RegulationsInfo regulationsInfo = ReadRegulationsInfo(o);
            TradeInfo       tradeInfo       = ReadTradeInfo(o);

            if (o.RiskType == "Risk_FX")
            {
                if (!Currency.TryParse(o.Qualifier, out Currency currency))
                {
                    throw new InvalidDataException("The CRIF file contains a Risk_FX entry with an invalid Qualifier property.");
                }

                if (!String.IsNullOrEmpty(o.Bucket) || !String.IsNullOrEmpty(o.Label1) || !String.IsNullOrEmpty(o.Label2))
                {
                    throw new InvalidDataException("The CRIF file cannot specify Bucket, Label1 and Label2 properties for Risk_FX entries.");
                }

                return(Sensitivity.FxDelta(currency, amount, regulationsInfo, tradeInfo));
            }

            if (!CurrencyPair.TryParse(o.Qualifier, out CurrencyPair pair))
            {
                throw new InvalidDataException("The CRIF file contains a Risk_FXVol entry with an invalid Qualifier property.");
            }

            if (!Tenor.TryParse(o.Label1, out Tenor tenor))
            {
                throw new InvalidDataException("The CRIF file contains a Risk_FXVol entry with an invalid Label1 property.");
            }

            if (!String.IsNullOrEmpty(o.Bucket) || !String.IsNullOrEmpty(o.Label2))
            {
                throw new InvalidDataException("The CRIF file cannot specify the Label2 property for Risk_EquityVol entries.");
            }

            return(Sensitivity.FxVega(pair, tenor, amount, regulationsInfo, tradeInfo));
        }
コード例 #10
0
        public async Task <ExchangeRates> GetRatesAsync()
        {
            var result  = new ExchangeRates();
            var symbols = await GetSymbolsAsync();

            var    normalizedPairsList = symbols.Where(s => !notFoundSymbols.ContainsKey(s)).Select(s => _Helper.NormalizeSymbol(s)).ToList();
            var    csvPairsList        = string.Join(",", normalizedPairsList);
            JToken apiTickers          = await MakeJsonRequestAsync <JToken>("/0/public/Ticker", null, new Dictionary <string, object> {
                { "pair", csvPairsList }
            });

            var tickers = new List <KeyValuePair <string, ExchangeTicker> >();

            foreach (string symbol in symbols)
            {
                var ticker = ConvertToExchangeTicker(symbol, apiTickers[symbol]);
                if (ticker != null)
                {
                    try
                    {
                        string global = null;
                        if (symbol.StartsWith("DASH", StringComparison.OrdinalIgnoreCase))
                        {
                            var p2 = symbol.Substring(4);
                            p2     = p2 == "XBT" ? "BTC" : p2;
                            global = $"{p2}_{symbol.Substring(0, 4)}";
                        }
                        else
                        {
                            global = _Helper.ExchangeSymbolToGlobalSymbol(symbol);
                        }
                        if (CurrencyPair.TryParse(global, out var pair))
                        {
                            result.Add(new ExchangeRate("kraken", pair.Inverse(), new BidAsk(ticker.Bid, ticker.Ask)));
                        }
                        else
                        {
                            notFoundSymbols.TryAdd(symbol, symbol);
                        }
                    }
                    catch (ArgumentException)
                    {
                        notFoundSymbols.TryAdd(symbol, symbol);
                    }
                }
            }
            return(result);
        }
コード例 #11
0
 public override SyntaxNode VisitIdentifierName(IdentifierNameSyntax node)
 {
     if (
         (!IsInvocation || IsArgumentList) &&
         CurrencyPair.TryParse(node.Identifier.ValueText, out var currentPair))
     {
         var replacedPair = new CurrencyPair(left: currentPair.Left == "X" ? pair.Left : currentPair.Left,
                                             right: currentPair.Right == "X" ? pair.Right : currentPair.Right);
         if (IsInvocation) // eg. replace bittrex(BTC_X) to bittrex(BTC_USD)
         {
             ExchangeRates.Add(new ExchangeRate()
             {
                 CurrencyPair = replacedPair, Exchange = _ExchangeName
             });
             return(SyntaxFactory.IdentifierName(replacedPair.ToString()));
         }
         else // eg. replace BTC_X to BTC_USD, then replace by the expression for BTC_USD
         {
             var bestCandidate = parent.FindBestCandidate(replacedPair);
             if (nested > MaxNestedCount)
             {
                 Errors.Add(RateRulesErrors.TooMuchNestedCalls);
                 return(RateRules.CreateExpression($"ERR_TOO_MUCH_NESTED_CALLS({replacedPair})"));
             }
             var innerFlatten = CreateNewContext(replacedPair);
             var replaced     = innerFlatten.Visit(bestCandidate);
             if (replaced is ExpressionSyntax expression)
             {
                 var hasBinaryOps = new HasBinaryOperations();
                 hasBinaryOps.Visit(expression);
                 if (hasBinaryOps.Result)
                 {
                     replaced = SyntaxFactory.ParenthesizedExpression(expression);
                 }
             }
             if (Errors.Contains(RateRulesErrors.TooMuchNestedCalls))
             {
                 return(RateRules.CreateExpression($"ERR_TOO_MUCH_NESTED_CALLS({replacedPair})"));
             }
             return(replaced);
         }
     }
     return(base.VisitIdentifierName(node));
 }
コード例 #12
0
        public async Task <IActionResult> Rates(RatesViewModel model, string command = null, string storeId = null, CancellationToken cancellationToken = default)
        {
            model.SetExchangeRates(GetSupportedExchanges(), model.PreferredExchange);
            model.StoreId = storeId ?? model.StoreId;
            CurrencyPair[] currencyPairs = null;
            try
            {
                currencyPairs = model.DefaultCurrencyPairs?
                                .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                                .Select(p => CurrencyPair.Parse(p))
                                .ToArray();
            }
            catch
            {
                ModelState.AddModelError(nameof(model.DefaultCurrencyPairs), "Invalid currency pairs (should be for example: BTC_USD,BTC_CAD,BTC_JPY)");
            }
            if (!ModelState.IsValid)
            {
                return(View(model));
            }
            if (model.PreferredExchange != null)
            {
                model.PreferredExchange = model.PreferredExchange.Trim().ToLowerInvariant();
            }

            var blob = StoreData.GetStoreBlob();

            model.DefaultScript      = blob.GetDefaultRateRules(_NetworkProvider).ToString();
            model.AvailableExchanges = GetSupportedExchanges();

            blob.PreferredExchange    = model.PreferredExchange;
            blob.Spread               = (decimal)model.Spread / 100.0m;
            blob.DefaultCurrencyPairs = currencyPairs;
            if (!model.ShowScripting)
            {
                if (!GetSupportedExchanges().Select(c => c.Name).Contains(blob.PreferredExchange, StringComparer.OrdinalIgnoreCase))
                {
                    ModelState.AddModelError(nameof(model.PreferredExchange), $"Unsupported exchange ({model.RateSource})");
                    return(View(model));
                }
            }
            RateRules rules = null;

            if (model.ShowScripting)
            {
                if (!RateRules.TryParse(model.Script, out rules, out var errors))
                {
                    errors = errors ?? new List <RateRulesErrors>();
                    var errorString = String.Join(", ", errors.ToArray());
                    ModelState.AddModelError(nameof(model.Script), $"Parsing error ({errorString})");
                    return(View(model));
                }
                else
                {
                    blob.RateScript = rules.ToString();
                    ModelState.Remove(nameof(model.Script));
                    model.Script = blob.RateScript;
                }
            }
            rules = blob.GetRateRules(_NetworkProvider);

            if (command == "Test")
            {
                if (string.IsNullOrWhiteSpace(model.ScriptTest))
                {
                    ModelState.AddModelError(nameof(model.ScriptTest), "Fill out currency pair to test for (like BTC_USD,BTC_CAD)");
                    return(View(model));
                }
                var splitted = model.ScriptTest.Split(',', StringSplitOptions.RemoveEmptyEntries);

                var pairs = new List <CurrencyPair>();
                foreach (var pair in splitted)
                {
                    if (!CurrencyPair.TryParse(pair, out var currencyPair))
                    {
                        ModelState.AddModelError(nameof(model.ScriptTest), $"Invalid currency pair '{pair}' (it should be formatted like BTC_USD,BTC_CAD)");
                        return(View(model));
                    }
                    pairs.Add(currencyPair);
                }

                var fetchs      = _RateFactory.FetchRates(pairs.ToHashSet(), rules, cancellationToken);
                var testResults = new List <RatesViewModel.TestResultViewModel>();
                foreach (var fetch in fetchs)
                {
                    var testResult = await(fetch.Value);
                    testResults.Add(new RatesViewModel.TestResultViewModel()
                    {
                        CurrencyPair = fetch.Key.ToString(),
                        Error        = testResult.Errors.Count != 0,
                        Rule         = testResult.Errors.Count == 0 ? testResult.Rule + " = " + testResult.BidAsk.Bid.ToString(CultureInfo.InvariantCulture)
                                                            : testResult.EvaluatedRule
                    });
                }
                model.TestRateRules = testResults;
                return(View(model));
            }
            else // command == Save
            {
                if (StoreData.SetStoreBlob(blob))
                {
                    await _Repo.UpdateStore(StoreData);

                    StatusMessage = "Rate settings updated";
                }
                return(RedirectToAction(nameof(Rates), new
                {
                    storeId = StoreData.Id
                }));
            }
        }
コード例 #13
0
        public async Task <IActionResult> GetRates2(string currencyPairs, string storeId)
        {
            if (storeId == null || currencyPairs == null)
            {
                var result = Json(new BitpayErrorsModel()
                {
                    Error = "You need to specify storeId (in your store settings) and currencyPairs (eg. BTC_USD,LTC_CAD)"
                });
                result.StatusCode = 400;
                return(result);
            }

            var store = this.HttpContext.GetStoreData();

            if (store == null || store.Id != storeId)
            {
                store = await _StoreRepo.FindStore(storeId);
            }
            if (store == null)
            {
                var result = Json(new BitpayErrorsModel()
                {
                    Error = "Store not found"
                });
                result.StatusCode = 404;
                return(result);
            }
            var rules = store.GetStoreBlob().GetRateRules(_NetworkProvider);

            HashSet <CurrencyPair> pairs = new HashSet <CurrencyPair>();

            foreach (var currency in currencyPairs.Split(','))
            {
                if (!CurrencyPair.TryParse(currency, out var pair))
                {
                    var result = Json(new BitpayErrorsModel()
                    {
                        Error = $"Currency pair {currency} uncorrectly formatted"
                    });
                    result.StatusCode = 400;
                    return(result);
                }
                pairs.Add(pair);
            }

            var fetching = _RateProviderFactory.FetchRates(pairs, rules);
            await Task.WhenAll(fetching.Select(f => f.Value).ToArray());

            return(Json(pairs
                        .Select(r => (Pair: r, Value: fetching[r].GetAwaiter().GetResult().Value))
                        .Where(r => r.Value.HasValue)
                        .Select(r =>
                                new Rate()
            {
                CryptoCode = r.Pair.Left,
                Code = r.Pair.Right,
                CurrencyPair = r.Pair.ToString(),
                Name = _CurrencyNameTable.GetCurrencyData(r.Pair.Right, true).Name,
                Value = r.Value.Value
            }).Where(n => n.Name != null).ToArray()));
        }
コード例 #14
0
        public async Task <IActionResult> GetRates2(string currencyPairs, string storeId, CancellationToken cancellationToken)
        {
            storeId = await GetStoreId(storeId);

            if (storeId == null)
            {
                var result = Json(new BitpayErrorsModel()
                {
                    Error = "Debe especificar storeId (en la configuración de su tienda)"
                });
                result.StatusCode = 400;
                return(result);
            }
            var store = this.HttpContext.GetStoreData();

            if (store == null || store.Id != storeId)
            {
                store = await _StoreRepo.FindStore(storeId);
            }
            if (store == null)
            {
                var result = Json(new BitpayErrorsModel()
                {
                    Error = "Tienda no encontrada"
                });
                result.StatusCode = 404;
                return(result);
            }

            if (currencyPairs == null)
            {
                currencyPairs = store.GetStoreBlob().GetDefaultCurrencyPairString();
                if (string.IsNullOrEmpty(currencyPairs))
                {
                    var result = Json(new BitpayErrorsModel()
                    {
                        Error = "Debe configurar los pares de divisas predeterminados en 'Configuraciones / tarifas de la tienda' o especificar el parámetro de consulta 'currencyPairs' (por ejemplo, BTC_USD, LTC_CAD)."
                    });
                    result.StatusCode = 400;
                    return(result);
                }
            }


            var rules = store.GetStoreBlob().GetRateRules(_NetworkProvider);

            HashSet <CurrencyPair> pairs = new HashSet <CurrencyPair>();

            foreach (var currency in currencyPairs.Split(','))
            {
                if (!CurrencyPair.TryParse(currency, out var pair))
                {
                    var result = Json(new BitpayErrorsModel()
                    {
                        Error = $"Par de divisas {currency} formateado incorrectamente"
                    });
                    result.StatusCode = 400;
                    return(result);
                }
                pairs.Add(pair);
            }

            var fetching = _RateProviderFactory.FetchRates(pairs, rules, cancellationToken);
            await Task.WhenAll(fetching.Select(f => f.Value).ToArray());

            return(Json(pairs
                        .Select(r => (Pair: r, Value: fetching[r].GetAwaiter().GetResult().BidAsk?.Bid))
                        .Where(r => r.Value.HasValue)
                        .Select(r =>
                                new Rate()
            {
                CryptoCode = r.Pair.Left,
                Code = r.Pair.Right,
                CurrencyPair = r.Pair.ToString(),
                Name = _CurrencyNameTable.GetCurrencyData(r.Pair.Right, true).Name,
                Value = r.Value.Value
            }).Where(n => n.Name != null).ToArray()));
        }