Feature/increase robustness of exchange rates by always getting quotes (#2875)
* Always get quotes (with fallback to historical data) * Update changelog
This commit is contained in:
parent
217bb6aa5a
commit
ba3cf82c6e
@ -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/),
|
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).
|
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
|
## 2.39.0 - 2024-01-14
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
@ -243,7 +243,7 @@ export class BenchmarkService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const exchangeRateAtStartDate =
|
const exchangeRateAtStartDate =
|
||||||
exchangeRates[currentSymbolItem.currency]?.[
|
exchangeRates[`${currentSymbolItem.currency}${userCurrency}`]?.[
|
||||||
format(startDate, DATE_FORMAT)
|
format(startDate, DATE_FORMAT)
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -275,7 +275,9 @@ export class BenchmarkService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const exchangeRate =
|
const exchangeRate =
|
||||||
exchangeRates[format(marketDataItem.date, DATE_FORMAT)];
|
exchangeRates[`${currentSymbolItem.currency}${userCurrency}`]?.[
|
||||||
|
format(marketDataItem.date, DATE_FORMAT)
|
||||||
|
];
|
||||||
|
|
||||||
const exchangeRateFactor =
|
const exchangeRateFactor =
|
||||||
isNumber(exchangeRateAtStartDate) && isNumber(exchangeRate)
|
isNumber(exchangeRateAtStartDate) && isNumber(exchangeRate)
|
||||||
@ -300,7 +302,10 @@ export class BenchmarkService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (currentSymbolItem?.marketPrice && !includesToday) {
|
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 =
|
const exchangeRateFactor =
|
||||||
isNumber(exchangeRateAtStartDate) && isNumber(exchangeRate)
|
isNumber(exchangeRateAtStartDate) && isNumber(exchangeRate)
|
||||||
|
@ -298,7 +298,8 @@ export class PortfolioCalculator {
|
|||||||
start,
|
start,
|
||||||
step,
|
step,
|
||||||
symbol,
|
symbol,
|
||||||
exchangeRates: exchangeRatesByCurrency[currencies[symbol]],
|
exchangeRates:
|
||||||
|
exchangeRatesByCurrency[`${currencies[symbol]}${this.currency}`],
|
||||||
isChartMode: true
|
isChartMode: true
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -565,7 +566,11 @@ export class PortfolioCalculator {
|
|||||||
for (const item of lastTransactionPoint.items) {
|
for (const item of lastTransactionPoint.items) {
|
||||||
const marketPriceInBaseCurrency = marketSymbolMap[endDateString]?.[
|
const marketPriceInBaseCurrency = marketSymbolMap[endDateString]?.[
|
||||||
item.symbol
|
item.symbol
|
||||||
]?.mul(exchangeRatesByCurrency[item.currency]?.[endDateString]);
|
]?.mul(
|
||||||
|
exchangeRatesByCurrency[`${item.currency}${this.currency}`]?.[
|
||||||
|
endDateString
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
grossPerformance,
|
grossPerformance,
|
||||||
@ -585,7 +590,8 @@ export class PortfolioCalculator {
|
|||||||
end,
|
end,
|
||||||
marketSymbolMap,
|
marketSymbolMap,
|
||||||
start,
|
start,
|
||||||
exchangeRates: exchangeRatesByCurrency[item.currency],
|
exchangeRates:
|
||||||
|
exchangeRatesByCurrency[`${item.currency}${this.currency}`],
|
||||||
symbol: item.symbol
|
symbol: item.symbol
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -33,6 +33,10 @@ export class YahooFinanceDataEnhancerService implements DataEnhancerInterface {
|
|||||||
symbol = `${DEFAULT_CURRENCY}${symbol}`;
|
symbol = `${DEFAULT_CURRENCY}${symbol}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (symbol.includes(`${DEFAULT_CURRENCY}ZAC`)) {
|
||||||
|
symbol = `${DEFAULT_CURRENCY}ZAc`;
|
||||||
|
}
|
||||||
|
|
||||||
return symbol.replace('=X', '');
|
return symbol.replace('=X', '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,14 +7,14 @@ export const ExchangeRateDataServiceMock = {
|
|||||||
}): Promise<any> => {
|
}): Promise<any> => {
|
||||||
if (targetCurrency === 'CHF') {
|
if (targetCurrency === 'CHF') {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
CHF: {
|
CHFCHF: {
|
||||||
'2015-01-01': 1,
|
'2015-01-01': 1,
|
||||||
'2017-12-31': 1,
|
'2017-12-31': 1,
|
||||||
'2018-01-01': 1,
|
'2018-01-01': 1,
|
||||||
'2023-01-03': 1,
|
'2023-01-03': 1,
|
||||||
'2023-07-10': 1
|
'2023-07-10': 1
|
||||||
},
|
},
|
||||||
USD: {
|
USDCHF: {
|
||||||
'2015-01-01': 0.9941099999999999,
|
'2015-01-01': 0.9941099999999999,
|
||||||
'2017-12-31': 0.9787,
|
'2017-12-31': 0.9787,
|
||||||
'2018-01-01': 0.97373,
|
'2018-01-01': 0.97373,
|
||||||
|
@ -64,11 +64,12 @@ export class ExchangeRateDataService {
|
|||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
for (let currency of currencies) {
|
for (let currency of currencies) {
|
||||||
exchangeRatesByCurrency[currency] = await this.getExchangeRates({
|
exchangeRatesByCurrency[`${currency}${targetCurrency}`] =
|
||||||
startDate,
|
await this.getExchangeRates({
|
||||||
currencyFrom: currency,
|
startDate,
|
||||||
currencyTo: targetCurrency
|
currencyFrom: currency,
|
||||||
});
|
currencyTo: targetCurrency
|
||||||
|
});
|
||||||
|
|
||||||
let previousExchangeRate = 1;
|
let previousExchangeRate = 1;
|
||||||
|
|
||||||
@ -82,17 +83,25 @@ export class ExchangeRateDataService {
|
|||||||
let dateString = format(date, DATE_FORMAT);
|
let dateString = format(date, DATE_FORMAT);
|
||||||
|
|
||||||
// Check if the exchange rate for the current date is missing
|
// 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
|
// If missing, fill with the previous exchange rate
|
||||||
exchangeRatesByCurrency[currency][dateString] = previousExchangeRate;
|
exchangeRatesByCurrency[`${currency}${targetCurrency}`][dateString] =
|
||||||
|
previousExchangeRate;
|
||||||
|
|
||||||
Logger.error(
|
if (currency === DEFAULT_CURRENCY) {
|
||||||
`No exchange rate has been found for ${DEFAULT_CURRENCY}${currency} at ${dateString}`,
|
Logger.error(
|
||||||
'ExchangeRateDataService'
|
`No exchange rate has been found for ${currency}${targetCurrency} at ${dateString}`,
|
||||||
);
|
'ExchangeRateDataService'
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// If available, update the previous exchange rate
|
// If available, update the previous exchange rate
|
||||||
previousExchangeRate = exchangeRatesByCurrency[currency][dateString];
|
previousExchangeRate =
|
||||||
|
exchangeRatesByCurrency[`${currency}${targetCurrency}`][dateString];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,24 +145,20 @@ export class ExchangeRateDataService {
|
|||||||
getYesterday()
|
getYesterday()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (Object.keys(result).length !== this.currencyPairs.length) {
|
const quotes = await this.dataProviderService.getQuotes({
|
||||||
// Load currencies directly from data provider as a fallback
|
items: this.currencyPairs.map(({ dataSource, symbol }) => {
|
||||||
// if historical data is not fully available
|
return { dataSource, symbol };
|
||||||
const quotes = await this.dataProviderService.getQuotes({
|
}),
|
||||||
items: this.currencyPairs.map(({ dataSource, symbol }) => {
|
requestTimeout: ms('30 seconds')
|
||||||
return { dataSource, symbol };
|
});
|
||||||
}),
|
|
||||||
requestTimeout: ms('30 seconds')
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const symbol of Object.keys(quotes)) {
|
for (const symbol of Object.keys(quotes)) {
|
||||||
if (isNumber(quotes[symbol].marketPrice)) {
|
if (isNumber(quotes[symbol].marketPrice)) {
|
||||||
result[symbol] = {
|
result[symbol] = {
|
||||||
[format(getYesterday(), DATE_FORMAT)]: {
|
[format(getYesterday(), DATE_FORMAT)]: {
|
||||||
marketPrice: quotes[symbol].marketPrice
|
marketPrice: quotes[symbol].marketPrice
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,6 +258,7 @@ export class ExchangeRateDataService {
|
|||||||
`No exchange rate has been found for ${aFromCurrency}${aToCurrency}`,
|
`No exchange rate has been found for ${aFromCurrency}${aToCurrency}`,
|
||||||
'ExchangeRateDataService'
|
'ExchangeRateDataService'
|
||||||
);
|
);
|
||||||
|
|
||||||
return aValue;
|
return aValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user