diff --git a/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts b/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts index 1b677005d..8c1ba5b41 100644 --- a/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts +++ b/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts @@ -30,7 +30,7 @@ import ms from 'ms'; export class ExchangeRateDataService { private currencies: string[] = []; private currencyPairs: DataGatheringItem[] = []; - private derivedCurrencyFactors = new Map(); + private derivedCurrencyFactors: { [currencyPair: string]: number } = {}; private exchangeRates: { [currencyPair: string]: number } = {}; public constructor( @@ -136,13 +136,12 @@ export class ExchangeRateDataService { public async initialize() { this.currencies = await this.prepareCurrencies(); this.currencyPairs = []; + this.derivedCurrencyFactors = {}; this.exchangeRates = {}; - this.derivedCurrencyFactors = new Map(); - // Initialize derived currencies factor map in both directions for (const { currency, factor, rootCurrency } of DERIVED_CURRENCIES) { - this.derivedCurrencyFactors.set(`${currency}${rootCurrency}`, 1 / factor); - this.derivedCurrencyFactors.set(`${rootCurrency}${currency}`, factor); + this.derivedCurrencyFactors[`${currency}${rootCurrency}`] = 1 / factor; + this.derivedCurrencyFactors[`${rootCurrency}${currency}`] = factor; } for (const { @@ -274,70 +273,65 @@ export class ExchangeRateDataService { return this.toCurrency(aValue, aFromCurrency, aToCurrency); } + const derivedCurrencyFactor = + this.derivedCurrencyFactors[`${aFromCurrency}${aToCurrency}`]; let factor: number; if (aFromCurrency === aToCurrency) { factor = 1; + } else if (derivedCurrencyFactor) { + factor = derivedCurrencyFactor; } else { - const derivedCurrencyFactor = this.getDerivedCurrencyFactor( - aFromCurrency, - aToCurrency - ); + const dataSource = + this.dataProviderService.getDataSourceForExchangeRates(); + const symbol = `${aFromCurrency}${aToCurrency}`; - if (derivedCurrencyFactor !== null) { - factor = derivedCurrencyFactor; + const marketData = await this.marketDataService.get({ + dataSource, + symbol, + date: aDate + }); + + if (marketData?.marketPrice) { + factor = marketData?.marketPrice; } else { - const dataSource = - this.dataProviderService.getDataSourceForExchangeRates(); - const symbol = `${aFromCurrency}${aToCurrency}`; + // Calculate indirectly via base currency - const marketData = await this.marketDataService.get({ - dataSource, - symbol, - date: aDate - }); + let marketPriceBaseCurrencyFromCurrency: number; + let marketPriceBaseCurrencyToCurrency: number; - if (marketData?.marketPrice) { - factor = marketData?.marketPrice; - } else { - // Calculate indirectly via base currency - - let marketPriceBaseCurrencyFromCurrency: number; - let marketPriceBaseCurrencyToCurrency: number; - - try { - if (aFromCurrency === DEFAULT_CURRENCY) { - marketPriceBaseCurrencyFromCurrency = 1; - } else { - marketPriceBaseCurrencyFromCurrency = ( - await this.marketDataService.get({ - dataSource, - date: aDate, - symbol: `${DEFAULT_CURRENCY}${aFromCurrency}` - }) - )?.marketPrice; - } - } catch {} - - try { - if (aToCurrency === DEFAULT_CURRENCY) { - marketPriceBaseCurrencyToCurrency = 1; - } else { - marketPriceBaseCurrencyToCurrency = ( - await this.marketDataService.get({ - dataSource, - date: aDate, - symbol: `${DEFAULT_CURRENCY}${aToCurrency}` - }) - )?.marketPrice; - } - } catch {} + try { + if (aFromCurrency === DEFAULT_CURRENCY) { + marketPriceBaseCurrencyFromCurrency = 1; + } else { + marketPriceBaseCurrencyFromCurrency = ( + await this.marketDataService.get({ + dataSource, + date: aDate, + symbol: `${DEFAULT_CURRENCY}${aFromCurrency}` + }) + )?.marketPrice; + } + } catch {} - // Calculate the opposite direction - factor = - (1 / marketPriceBaseCurrencyFromCurrency) * - marketPriceBaseCurrencyToCurrency; - } + try { + if (aToCurrency === DEFAULT_CURRENCY) { + marketPriceBaseCurrencyToCurrency = 1; + } else { + marketPriceBaseCurrencyToCurrency = ( + await this.marketDataService.get({ + dataSource, + date: aDate, + symbol: `${DEFAULT_CURRENCY}${aToCurrency}` + }) + )?.marketPrice; + } + } catch {} + + // Calculate the opposite direction + factor = + (1 / marketPriceBaseCurrencyFromCurrency) * + marketPriceBaseCurrencyToCurrency; } } @@ -374,129 +368,120 @@ export class ExchangeRateDataService { for (const date of dates) { factors[format(date, DATE_FORMAT)] = 1; } + return factors; } - // Check if this is a conversion between a derived currency and its root currency - const derivedCurrencyFactor = this.getDerivedCurrencyFactor( - currencyFrom, - currencyTo - ); + const derivedCurrencyFactor = + this.derivedCurrencyFactors[`${currencyFrom}${currencyTo}`]; - if (derivedCurrencyFactor !== null) { - // Use the fixed mathematical factor for derived currencies + if (derivedCurrencyFactor) { for (const date of dates) { factors[format(date, DATE_FORMAT)] = derivedCurrencyFactor; } + return factors; } - // Continue with standard exchange rate logic - { - const dataSource = - this.dataProviderService.getDataSourceForExchangeRates(); - const symbol = `${currencyFrom}${currencyTo}`; - - const marketData = await this.marketDataService.getRange({ - assetProfileIdentifiers: [ - { - dataSource, - symbol - } - ], - dateQuery: { gte: startDate, lt: endDate } - }); + const dataSource = this.dataProviderService.getDataSourceForExchangeRates(); + const symbol = `${currencyFrom}${currencyTo}`; - if (marketData?.length > 0) { - for (const { date, marketPrice } of marketData) { - factors[format(date, DATE_FORMAT)] = marketPrice; + const marketData = await this.marketDataService.getRange({ + assetProfileIdentifiers: [ + { + dataSource, + symbol } - } else { - // Calculate indirectly via base currency + ], + dateQuery: { gte: startDate, lt: endDate } + }); - const marketPriceBaseCurrencyFromCurrency: { - [dateString: string]: number; - } = {}; - const marketPriceBaseCurrencyToCurrency: { - [dateString: string]: number; - } = {}; + if (marketData?.length > 0) { + for (const { date, marketPrice } of marketData) { + factors[format(date, DATE_FORMAT)] = marketPrice; + } + } else { + // Calculate indirectly via base currency + + const marketPriceBaseCurrencyFromCurrency: { + [dateString: string]: number; + } = {}; + const marketPriceBaseCurrencyToCurrency: { + [dateString: string]: number; + } = {}; + + try { + if (currencyFrom === DEFAULT_CURRENCY) { + for (const date of dates) { + marketPriceBaseCurrencyFromCurrency[format(date, DATE_FORMAT)] = 1; + } + } else { + const marketData = await this.marketDataService.getRange({ + assetProfileIdentifiers: [ + { + dataSource, + symbol: `${DEFAULT_CURRENCY}${currencyFrom}` + } + ], + dateQuery: { gte: startDate, lt: endDate } + }); - try { - if (currencyFrom === DEFAULT_CURRENCY) { - for (const date of dates) { - marketPriceBaseCurrencyFromCurrency[format(date, DATE_FORMAT)] = - 1; - } - } else { - const marketData = await this.marketDataService.getRange({ - assetProfileIdentifiers: [ - { - dataSource, - symbol: `${DEFAULT_CURRENCY}${currencyFrom}` - } - ], - dateQuery: { gte: startDate, lt: endDate } - }); - - for (const { date, marketPrice } of marketData) { - marketPriceBaseCurrencyFromCurrency[format(date, DATE_FORMAT)] = - marketPrice; - } + for (const { date, marketPrice } of marketData) { + marketPriceBaseCurrencyFromCurrency[format(date, DATE_FORMAT)] = + marketPrice; } - } catch {} + } + } catch {} - try { - if (currencyTo === DEFAULT_CURRENCY) { - for (const date of dates) { - marketPriceBaseCurrencyToCurrency[format(date, DATE_FORMAT)] = 1; - } - } else { - const marketData = await this.marketDataService.getRange({ - assetProfileIdentifiers: [ - { - dataSource, - symbol: `${DEFAULT_CURRENCY}${currencyTo}` - } - ], - dateQuery: { - gte: startDate, - lt: endDate + try { + if (currencyTo === DEFAULT_CURRENCY) { + for (const date of dates) { + marketPriceBaseCurrencyToCurrency[format(date, DATE_FORMAT)] = 1; + } + } else { + const marketData = await this.marketDataService.getRange({ + assetProfileIdentifiers: [ + { + dataSource, + symbol: `${DEFAULT_CURRENCY}${currencyTo}` } - }); - - for (const { date, marketPrice } of marketData) { - marketPriceBaseCurrencyToCurrency[format(date, DATE_FORMAT)] = - marketPrice; + ], + dateQuery: { + gte: startDate, + lt: endDate } + }); + + for (const { date, marketPrice } of marketData) { + marketPriceBaseCurrencyToCurrency[format(date, DATE_FORMAT)] = + marketPrice; } - } catch {} + } + } catch {} - for (const date of dates) { - try { - const factor = - (1 / - marketPriceBaseCurrencyFromCurrency[ - format(date, DATE_FORMAT) - ]) * - marketPriceBaseCurrencyToCurrency[format(date, DATE_FORMAT)]; - - if (isNaN(factor)) { - throw new Error('Exchange rate is not a number'); - } else { - factors[format(date, DATE_FORMAT)] = factor; - } - } catch { - let errorMessage = `No exchange rate has been found for ${currencyFrom}${currencyTo} at ${format( - date, - DATE_FORMAT - )}. Please complement market data for ${DEFAULT_CURRENCY}${currencyFrom}`; - - if (DEFAULT_CURRENCY !== currencyTo) { - errorMessage = `${errorMessage} and ${DEFAULT_CURRENCY}${currencyTo}`; - } + for (const date of dates) { + try { + const factor = + (1 / + marketPriceBaseCurrencyFromCurrency[format(date, DATE_FORMAT)]) * + marketPriceBaseCurrencyToCurrency[format(date, DATE_FORMAT)]; - Logger.error(`${errorMessage}.`, 'ExchangeRateDataService'); + if (isNaN(factor)) { + throw new Error('Exchange rate is not a number'); + } else { + factors[format(date, DATE_FORMAT)] = factor; + } + } catch { + let errorMessage = `No exchange rate has been found for ${currencyFrom}${currencyTo} at ${format( + date, + DATE_FORMAT + )}. Please complement market data for ${DEFAULT_CURRENCY}${currencyFrom}`; + + if (DEFAULT_CURRENCY !== currencyTo) { + errorMessage = `${errorMessage} and ${DEFAULT_CURRENCY}${currencyTo}`; } + + Logger.error(`${errorMessage}.`, 'ExchangeRateDataService'); } } } @@ -504,16 +489,6 @@ export class ExchangeRateDataService { return factors; } - private getDerivedCurrencyFactor( - currencyFrom: string, - currencyTo: string - ): number | null { - const factor = this.derivedCurrencyFactors.get( - `${currencyFrom}${currencyTo}` - ); - return factor ?? null; - } - private async prepareCurrencies(): Promise { let currencies: string[] = [DEFAULT_CURRENCY];