Browse Source

Merge 3c9e70ffad into 78134020a2

pull/6866/merge
Andrea Bugeja 4 days ago
committed by GitHub
parent
commit
5821579882
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 6
      apps/api/src/app/import/import.service.ts
  2. 144
      apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts
  3. 5
      apps/api/src/services/market-data/market-data.service.ts
  4. 3
      package.json

6
apps/api/src/app/import/import.service.ts

@ -284,7 +284,11 @@ export class ImportService {
); );
// If there is no asset profile or if the asset profile belongs to a different user, then create a new asset profile // If there is no asset profile or if the asset profile belongs to a different user, then create a new asset profile
if (!existingAssetProfile || existingAssetProfile.userId !== user.id) { if (
!existingAssetProfile ||
(existingAssetProfile.userId !== null &&
existingAssetProfile.userId !== user.id)
) {
const assetProfile: CreateAssetProfileDto = omit( const assetProfile: CreateAssetProfileDto = omit(
assetProfileWithMarketData, assetProfileWithMarketData,
'marketData' 'marketData'

144
apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts

@ -229,32 +229,59 @@ export class ExchangeRateDataService {
return 0; return 0;
} }
let normalizedValue = aValue;
let normalizedFromCurrency = aFromCurrency;
let normalizedToCurrency = aToCurrency;
const fromDerived = DERIVED_CURRENCIES.find(
(c) => c.currency === aFromCurrency
);
if (fromDerived) {
normalizedFromCurrency = fromDerived.rootCurrency;
normalizedValue = normalizedValue / fromDerived.factor;
}
const toDerived = DERIVED_CURRENCIES.find(
(c) => c.currency === aToCurrency
);
if (toDerived) {
normalizedToCurrency = toDerived.rootCurrency;
normalizedValue = normalizedValue * toDerived.factor;
}
let factor: number; let factor: number;
if (aFromCurrency === aToCurrency) { if (normalizedFromCurrency === normalizedToCurrency) {
factor = 1; factor = 1;
} else { } else {
if (this.exchangeRates[`${aFromCurrency}${aToCurrency}`]) { if (
factor = this.exchangeRates[`${aFromCurrency}${aToCurrency}`]; this.exchangeRates[`${normalizedFromCurrency}${normalizedToCurrency}`]
) {
factor =
this.exchangeRates[
`${normalizedFromCurrency}${normalizedToCurrency}`
];
} else { } else {
// Calculate indirectly via base currency // Calculate indirectly via base currency
const factor1 = const factor1 =
this.exchangeRates[`${aFromCurrency}${DEFAULT_CURRENCY}`]; this.exchangeRates[`${normalizedFromCurrency}${DEFAULT_CURRENCY}`];
const factor2 = this.exchangeRates[`${DEFAULT_CURRENCY}${aToCurrency}`]; const factor2 =
this.exchangeRates[`${DEFAULT_CURRENCY}${normalizedToCurrency}`];
factor = factor1 * factor2; factor = factor1 * factor2;
this.exchangeRates[`${aFromCurrency}${aToCurrency}`] = factor; this.exchangeRates[`${normalizedFromCurrency}${normalizedToCurrency}`] =
factor;
} }
} }
if (isNumber(factor) && !isNaN(factor)) { if (isNumber(factor) && !isNaN(factor)) {
return factor * aValue; return factor * normalizedValue;
} }
// Fallback with error, if currencies are not available // Fallback with error, if currencies are not available
Logger.error( Logger.error(
`No exchange rate has been found for ${aFromCurrency}${aToCurrency}`, `No exchange rate has been found for ${normalizedFromCurrency}${normalizedToCurrency}. Please complement market data for USD${normalizedFromCurrency} and USD${normalizedToCurrency}.`,
'ExchangeRateDataService' 'ExchangeRateDataService'
); );
@ -271,22 +298,42 @@ export class ExchangeRateDataService {
return 0; return 0;
} }
let normalizedValue = aValue;
let normalizedFromCurrency = aFromCurrency;
let normalizedToCurrency = aToCurrency;
const fromDerived = DERIVED_CURRENCIES.find(
(c) => c.currency === aFromCurrency
);
if (fromDerived) {
normalizedFromCurrency = fromDerived.rootCurrency;
normalizedValue = normalizedValue / fromDerived.factor;
}
const toDerived = DERIVED_CURRENCIES.find(
(c) => c.currency === aToCurrency
);
if (toDerived) {
normalizedToCurrency = toDerived.rootCurrency;
normalizedValue = normalizedValue * toDerived.factor;
}
if (isToday(aDate)) { if (isToday(aDate)) {
return this.toCurrency(aValue, aFromCurrency, aToCurrency); return this.toCurrency(
normalizedValue,
normalizedFromCurrency,
normalizedToCurrency
);
} }
const derivedCurrencyFactor =
this.derivedCurrencyFactors[`${aFromCurrency}${aToCurrency}`];
let factor: number; let factor: number;
if (aFromCurrency === aToCurrency) { if (normalizedFromCurrency === normalizedToCurrency) {
factor = 1; factor = 1;
} else if (derivedCurrencyFactor) {
factor = derivedCurrencyFactor;
} else { } else {
const dataSource = const dataSource =
this.dataProviderService.getDataSourceForExchangeRates(); this.dataProviderService.getDataSourceForExchangeRates();
const symbol = `${aFromCurrency}${aToCurrency}`; const symbol = `${normalizedFromCurrency}${normalizedToCurrency}`;
const marketData = await this.marketDataService.get({ const marketData = await this.marketDataService.get({
dataSource, dataSource,
@ -303,28 +350,28 @@ export class ExchangeRateDataService {
let marketPriceBaseCurrencyToCurrency: number; let marketPriceBaseCurrencyToCurrency: number;
try { try {
if (aFromCurrency === DEFAULT_CURRENCY) { if (normalizedFromCurrency === DEFAULT_CURRENCY) {
marketPriceBaseCurrencyFromCurrency = 1; marketPriceBaseCurrencyFromCurrency = 1;
} else { } else {
marketPriceBaseCurrencyFromCurrency = ( marketPriceBaseCurrencyFromCurrency = (
await this.marketDataService.get({ await this.marketDataService.get({
dataSource, dataSource,
date: aDate, date: aDate,
symbol: `${DEFAULT_CURRENCY}${aFromCurrency}` symbol: `${DEFAULT_CURRENCY}${normalizedFromCurrency}`
}) })
)?.marketPrice; )?.marketPrice;
} }
} catch {} } catch {}
try { try {
if (aToCurrency === DEFAULT_CURRENCY) { if (normalizedToCurrency === DEFAULT_CURRENCY) {
marketPriceBaseCurrencyToCurrency = 1; marketPriceBaseCurrencyToCurrency = 1;
} else { } else {
marketPriceBaseCurrencyToCurrency = ( marketPriceBaseCurrencyToCurrency = (
await this.marketDataService.get({ await this.marketDataService.get({
dataSource, dataSource,
date: aDate, date: aDate,
symbol: `${DEFAULT_CURRENCY}${aToCurrency}` symbol: `${DEFAULT_CURRENCY}${normalizedToCurrency}`
}) })
)?.marketPrice; )?.marketPrice;
} }
@ -338,18 +385,18 @@ export class ExchangeRateDataService {
} }
if (isNumber(factor) && !isNaN(factor)) { if (isNumber(factor) && !isNaN(factor)) {
return factor * aValue; return factor * normalizedValue;
} }
Logger.error( Logger.error(
`No exchange rate has been found for ${aFromCurrency}${aToCurrency} at ${format( `No exchange rate has been found for ${normalizedFromCurrency}${normalizedToCurrency} at ${format(
aDate, aDate,
DATE_FORMAT DATE_FORMAT
)}`, )}. Please complement market data for USD${normalizedFromCurrency} and USD${normalizedToCurrency}.`,
'ExchangeRateDataService' 'ExchangeRateDataService'
); );
return undefined; return aValue;
} }
private async getExchangeRates({ private async getExchangeRates({
@ -366,27 +413,36 @@ export class ExchangeRateDataService {
const dates = eachDayOfInterval({ end: endDate, start: startDate }); const dates = eachDayOfInterval({ end: endDate, start: startDate });
const factors: { [dateString: string]: number } = {}; const factors: { [dateString: string]: number } = {};
if (currencyFrom === currencyTo) { let normalizedCurrencyFrom = currencyFrom;
for (const date of dates) { let normalizedCurrencyTo = currencyTo;
factors[format(date, DATE_FORMAT)] = 1; let conversionFactorFrom = 1;
} let conversionFactorTo = 1;
return factors; const fromDerived = DERIVED_CURRENCIES.find(
(c) => c.currency === currencyFrom
);
if (fromDerived) {
normalizedCurrencyFrom = fromDerived.rootCurrency;
conversionFactorFrom = 1 / fromDerived.factor;
} }
const derivedCurrencyFactor = const toDerived = DERIVED_CURRENCIES.find((c) => c.currency === currencyTo);
this.derivedCurrencyFactors[`${currencyFrom}${currencyTo}`]; if (toDerived) {
normalizedCurrencyTo = toDerived.rootCurrency;
conversionFactorTo = toDerived.factor;
}
if (derivedCurrencyFactor) { if (normalizedCurrencyFrom === normalizedCurrencyTo) {
for (const date of dates) { for (const date of dates) {
factors[format(date, DATE_FORMAT)] = derivedCurrencyFactor; factors[format(date, DATE_FORMAT)] =
conversionFactorFrom * conversionFactorTo;
} }
return factors; return factors;
} }
const dataSource = this.dataProviderService.getDataSourceForExchangeRates(); const dataSource = this.dataProviderService.getDataSourceForExchangeRates();
const symbol = `${currencyFrom}${currencyTo}`; const symbol = `${normalizedCurrencyFrom}${normalizedCurrencyTo}`;
const marketData = await this.marketDataService.getRange({ const marketData = await this.marketDataService.getRange({
assetProfileIdentifiers: [ assetProfileIdentifiers: [
@ -400,7 +456,8 @@ export class ExchangeRateDataService {
if (marketData?.length > 0) { if (marketData?.length > 0) {
for (const { date, marketPrice } of marketData) { for (const { date, marketPrice } of marketData) {
factors[format(date, DATE_FORMAT)] = marketPrice; factors[format(date, DATE_FORMAT)] =
conversionFactorFrom * marketPrice * conversionFactorTo;
} }
} else { } else {
// Calculate indirectly via base currency // Calculate indirectly via base currency
@ -413,7 +470,7 @@ export class ExchangeRateDataService {
} = {}; } = {};
try { try {
if (currencyFrom === DEFAULT_CURRENCY) { if (normalizedCurrencyFrom === DEFAULT_CURRENCY) {
for (const date of dates) { for (const date of dates) {
marketPriceBaseCurrencyFromCurrency[format(date, DATE_FORMAT)] = 1; marketPriceBaseCurrencyFromCurrency[format(date, DATE_FORMAT)] = 1;
} }
@ -422,7 +479,7 @@ export class ExchangeRateDataService {
assetProfileIdentifiers: [ assetProfileIdentifiers: [
{ {
dataSource, dataSource,
symbol: `${DEFAULT_CURRENCY}${currencyFrom}` symbol: `${DEFAULT_CURRENCY}${normalizedCurrencyFrom}`
} }
], ],
dateQuery: { gte: startDate, lt: endDate } dateQuery: { gte: startDate, lt: endDate }
@ -436,7 +493,7 @@ export class ExchangeRateDataService {
} catch {} } catch {}
try { try {
if (currencyTo === DEFAULT_CURRENCY) { if (normalizedCurrencyTo === DEFAULT_CURRENCY) {
for (const date of dates) { for (const date of dates) {
marketPriceBaseCurrencyToCurrency[format(date, DATE_FORMAT)] = 1; marketPriceBaseCurrencyToCurrency[format(date, DATE_FORMAT)] = 1;
} }
@ -445,7 +502,7 @@ export class ExchangeRateDataService {
assetProfileIdentifiers: [ assetProfileIdentifiers: [
{ {
dataSource, dataSource,
symbol: `${DEFAULT_CURRENCY}${currencyTo}` symbol: `${DEFAULT_CURRENCY}${normalizedCurrencyTo}`
} }
], ],
dateQuery: { dateQuery: {
@ -471,16 +528,17 @@ export class ExchangeRateDataService {
if (isNaN(factor)) { if (isNaN(factor)) {
throw new Error('Exchange rate is not a number'); throw new Error('Exchange rate is not a number');
} else { } else {
factors[format(date, DATE_FORMAT)] = factor; factors[format(date, DATE_FORMAT)] =
conversionFactorFrom * factor * conversionFactorTo;
} }
} catch { } catch {
let errorMessage = `No exchange rate has been found for ${currencyFrom}${currencyTo} at ${format( let errorMessage = `No exchange rate has been found for ${normalizedCurrencyFrom}${normalizedCurrencyTo} at ${format(
date, date,
DATE_FORMAT DATE_FORMAT
)}. Please complement market data for ${DEFAULT_CURRENCY}${currencyFrom}`; )}. Please complement market data for ${DEFAULT_CURRENCY}${normalizedCurrencyFrom}`;
if (DEFAULT_CURRENCY !== currencyTo) { if (DEFAULT_CURRENCY !== normalizedCurrencyTo) {
errorMessage = `${errorMessage} and ${DEFAULT_CURRENCY}${currencyTo}`; errorMessage = `${errorMessage} and ${DEFAULT_CURRENCY}${normalizedCurrencyTo}`;
} }
Logger.error(`${errorMessage}.`, 'ExchangeRateDataService'); Logger.error(`${errorMessage}.`, 'ExchangeRateDataService');

5
apps/api/src/services/market-data/market-data.service.ts

@ -35,8 +35,9 @@ export class MarketDataService {
where: { where: {
dataSource, dataSource,
symbol, symbol,
date: resetHours(date) date: { lte: resetHours(date) }
} },
orderBy: { date: 'desc' }
}); });
} }

3
package.json

@ -72,7 +72,7 @@
"@date-fns/utc": "2.1.1", "@date-fns/utc": "2.1.1",
"@internationalized/number": "3.6.6", "@internationalized/number": "3.6.6",
"@ionic/angular": "8.8.5", "@ionic/angular": "8.8.5",
"@keyv/redis": "4.4.0", "@keyv/redis": "^5.1.6",
"@nestjs/bull": "11.0.4", "@nestjs/bull": "11.0.4",
"@nestjs/cache-manager": "3.1.0", "@nestjs/cache-manager": "3.1.0",
"@nestjs/common": "11.1.19", "@nestjs/common": "11.1.19",
@ -118,6 +118,7 @@
"http-status-codes": "2.3.0", "http-status-codes": "2.3.0",
"ionicons": "8.0.13", "ionicons": "8.0.13",
"jsonpath": "1.3.0", "jsonpath": "1.3.0",
"keyv": "^5.6.0",
"lodash": "4.18.1", "lodash": "4.18.1",
"marked": "17.0.2", "marked": "17.0.2",
"ms": "3.0.0-canary.1", "ms": "3.0.0-canary.1",

Loading…
Cancel
Save