diff --git a/CHANGELOG.md b/CHANGELOG.md index 235d050a5..ab6de3c7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Changed + +- Inreased the robustness of the exchange rates by always getting quotes in the exchange rate data service + ## 2.39.0 - 2024-01-14 ### Changed diff --git a/apps/api/src/app/benchmark/benchmark.service.ts b/apps/api/src/app/benchmark/benchmark.service.ts index 40fd59d9a..8430b4aa3 100644 --- a/apps/api/src/app/benchmark/benchmark.service.ts +++ b/apps/api/src/app/benchmark/benchmark.service.ts @@ -243,7 +243,7 @@ export class BenchmarkService { }); const exchangeRateAtStartDate = - exchangeRates[currentSymbolItem.currency]?.[ + exchangeRates[`${currentSymbolItem.currency}${userCurrency}`]?.[ format(startDate, DATE_FORMAT) ]; @@ -275,7 +275,9 @@ export class BenchmarkService { } const exchangeRate = - exchangeRates[format(marketDataItem.date, DATE_FORMAT)]; + exchangeRates[`${currentSymbolItem.currency}${userCurrency}`]?.[ + format(marketDataItem.date, DATE_FORMAT) + ]; const exchangeRateFactor = isNumber(exchangeRateAtStartDate) && isNumber(exchangeRate) @@ -300,7 +302,10 @@ export class BenchmarkService { ); if (currentSymbolItem?.marketPrice && !includesToday) { - const exchangeRate = exchangeRates[format(new Date(), DATE_FORMAT)]; + const exchangeRate = + exchangeRates[`${currentSymbolItem.currency}${userCurrency}`]?.[ + format(new Date(), DATE_FORMAT) + ]; const exchangeRateFactor = isNumber(exchangeRateAtStartDate) && isNumber(exchangeRate) diff --git a/apps/api/src/app/portfolio/portfolio-calculator.ts b/apps/api/src/app/portfolio/portfolio-calculator.ts index 61f6d7590..0f6667949 100644 --- a/apps/api/src/app/portfolio/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/portfolio-calculator.ts @@ -298,7 +298,8 @@ export class PortfolioCalculator { start, step, symbol, - exchangeRates: exchangeRatesByCurrency[currencies[symbol]], + exchangeRates: + exchangeRatesByCurrency[`${currencies[symbol]}${this.currency}`], isChartMode: true }); @@ -565,7 +566,11 @@ export class PortfolioCalculator { for (const item of lastTransactionPoint.items) { const marketPriceInBaseCurrency = marketSymbolMap[endDateString]?.[ item.symbol - ]?.mul(exchangeRatesByCurrency[item.currency]?.[endDateString]); + ]?.mul( + exchangeRatesByCurrency[`${item.currency}${this.currency}`]?.[ + endDateString + ] + ); const { grossPerformance, @@ -585,7 +590,8 @@ export class PortfolioCalculator { end, marketSymbolMap, start, - exchangeRates: exchangeRatesByCurrency[item.currency], + exchangeRates: + exchangeRatesByCurrency[`${item.currency}${this.currency}`], symbol: item.symbol }); diff --git a/apps/api/src/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service.ts b/apps/api/src/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service.ts index 0ef622c42..930e0c7f1 100644 --- a/apps/api/src/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service.ts +++ b/apps/api/src/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service.ts @@ -33,6 +33,10 @@ export class YahooFinanceDataEnhancerService implements DataEnhancerInterface { symbol = `${DEFAULT_CURRENCY}${symbol}`; } + if (symbol.includes(`${DEFAULT_CURRENCY}ZAC`)) { + symbol = `${DEFAULT_CURRENCY}ZAc`; + } + return symbol.replace('=X', ''); } diff --git a/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.mock.ts b/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.mock.ts index 1fc88bb6a..e32af51d3 100644 --- a/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.mock.ts +++ b/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.mock.ts @@ -7,14 +7,14 @@ export const ExchangeRateDataServiceMock = { }): Promise => { if (targetCurrency === 'CHF') { return Promise.resolve({ - CHF: { + CHFCHF: { '2015-01-01': 1, '2017-12-31': 1, '2018-01-01': 1, '2023-01-03': 1, '2023-07-10': 1 }, - USD: { + USDCHF: { '2015-01-01': 0.9941099999999999, '2017-12-31': 0.9787, '2018-01-01': 0.97373, 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 d180d8dd2..36dd5c0a2 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 @@ -64,11 +64,12 @@ export class ExchangeRateDataService { } = {}; for (let currency of currencies) { - exchangeRatesByCurrency[currency] = await this.getExchangeRates({ - startDate, - currencyFrom: currency, - currencyTo: targetCurrency - }); + exchangeRatesByCurrency[`${currency}${targetCurrency}`] = + await this.getExchangeRates({ + startDate, + currencyFrom: currency, + currencyTo: targetCurrency + }); let previousExchangeRate = 1; @@ -82,17 +83,25 @@ export class ExchangeRateDataService { let dateString = format(date, DATE_FORMAT); // Check if the exchange rate for the current date is missing - if (isNaN(exchangeRatesByCurrency[currency][dateString])) { + if ( + isNaN( + exchangeRatesByCurrency[`${currency}${targetCurrency}`][dateString] + ) + ) { // If missing, fill with the previous exchange rate - exchangeRatesByCurrency[currency][dateString] = previousExchangeRate; + exchangeRatesByCurrency[`${currency}${targetCurrency}`][dateString] = + previousExchangeRate; - Logger.error( - `No exchange rate has been found for ${DEFAULT_CURRENCY}${currency} at ${dateString}`, - 'ExchangeRateDataService' - ); + if (currency === DEFAULT_CURRENCY) { + Logger.error( + `No exchange rate has been found for ${currency}${targetCurrency} at ${dateString}`, + 'ExchangeRateDataService' + ); + } } else { // If available, update the previous exchange rate - previousExchangeRate = exchangeRatesByCurrency[currency][dateString]; + previousExchangeRate = + exchangeRatesByCurrency[`${currency}${targetCurrency}`][dateString]; } } } @@ -136,24 +145,20 @@ export class ExchangeRateDataService { getYesterday() ); - if (Object.keys(result).length !== this.currencyPairs.length) { - // Load currencies directly from data provider as a fallback - // if historical data is not fully available - const quotes = await this.dataProviderService.getQuotes({ - items: this.currencyPairs.map(({ dataSource, symbol }) => { - return { dataSource, symbol }; - }), - requestTimeout: ms('30 seconds') - }); + const quotes = await this.dataProviderService.getQuotes({ + items: this.currencyPairs.map(({ dataSource, symbol }) => { + return { dataSource, symbol }; + }), + requestTimeout: ms('30 seconds') + }); - for (const symbol of Object.keys(quotes)) { - if (isNumber(quotes[symbol].marketPrice)) { - result[symbol] = { - [format(getYesterday(), DATE_FORMAT)]: { - marketPrice: quotes[symbol].marketPrice - } - }; - } + for (const symbol of Object.keys(quotes)) { + if (isNumber(quotes[symbol].marketPrice)) { + result[symbol] = { + [format(getYesterday(), DATE_FORMAT)]: { + marketPrice: quotes[symbol].marketPrice + } + }; } } @@ -253,6 +258,7 @@ export class ExchangeRateDataService { `No exchange rate has been found for ${aFromCurrency}${aToCurrency}`, 'ExchangeRateDataService' ); + return aValue; }