introduce exchange rate service to fetch rates based on date query

Co-authored-by: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
pull/270/head
Valentin Zickner 3 years ago
parent e255b76053
commit a91f6c783e

@ -2,8 +2,10 @@ import { DataProviderService } from '@ghostfolio/api/services/data-provider/data
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
import { MarketDataService } from '@ghostfolio/api/services/market-data.service'; import { MarketDataService } from '@ghostfolio/api/services/market-data.service';
import { DataSource, MarketData } from '@prisma/client'; import { DataSource, MarketData } from '@prisma/client';
import { Big } from 'big.js';
import { CurrentRateService } from './current-rate.service'; import { CurrentRateService } from './current-rate.service';
import { DateQuery } from './interfaces/date-query.interface';
jest.mock('@ghostfolio/api/services/market-data.service', () => { jest.mock('@ghostfolio/api/services/market-data.service', () => {
return { return {
@ -57,6 +59,26 @@ jest.mock('@ghostfolio/api/services/exchange-rate-data.service', () => {
ExchangeRateDataService: jest.fn().mockImplementation(() => { ExchangeRateDataService: jest.fn().mockImplementation(() => {
return { return {
initialize: () => Promise.resolve(), initialize: () => Promise.resolve(),
getExchangeRates: ({
dateQuery,
sourceCurrencies,
destinationCurrency
}: {
dateQuery: DateQuery;
sourceCurrencies: string[];
destinationCurrency: string;
}) => {
return [
{
date: new Date(),
exchangeRates: {
USD: new Big(1),
CHF: new Big(1),
EUR: new Big(1)
}
}
];
},
toCurrency: (value: number) => { toCurrency: (value: number) => {
return 1 * value; return 1 * value;
} }

@ -1,9 +1,10 @@
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
import { MarketDataService } from '@ghostfolio/api/services/market-data.service'; import { MarketDataService } from '@ghostfolio/api/services/market-data.service';
import { resetHours } from '@ghostfolio/common/helper'; import { DATE_FORMAT, resetHours } from '@ghostfolio/common/helper';
import { Injectable } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { isBefore, isToday } from 'date-fns'; import { Big } from 'big.js';
import { format, isAfter, isBefore, isToday } from 'date-fns';
import { flatten } from 'lodash'; import { flatten } from 'lodash';
import { GetValueObject } from './interfaces/get-value-object.interface'; import { GetValueObject } from './interfaces/get-value-object.interface';
@ -77,6 +78,13 @@ export class CurrentRateService {
}[] }[]
>[] = []; >[] = [];
const sourceCurrencies = Object.values(currencies);
const exchangeRates = await this.exchangeRateDataService.getExchangeRates({
dateQuery,
sourceCurrencies,
destinationCurrency: userCurrency
});
if (includeToday) { if (includeToday) {
const today = resetHours(new Date()); const today = resetHours(new Date());
promises.push( promises.push(
@ -112,17 +120,59 @@ export class CurrentRateService {
symbols symbols
}) })
.then((data) => { .then((data) => {
return data.map((marketDataItem) => { const result = [];
return { let j = 0;
date: marketDataItem.date, for (const marketDataItem of data) {
marketPrice: this.exchangeRateDataService.toCurrency( const currency = currencies[marketDataItem.symbol];
while (
j + 1 < exchangeRates.length &&
!isAfter(exchangeRates[j + 1].date, marketDataItem.date)
) {
j++;
}
let exchangeRate: Big;
if (currency !== userCurrency) {
exchangeRate = exchangeRates[j]?.exchangeRates[currency];
for (
let k = j;
k >= 0 && !exchangeRates[k]?.exchangeRates[currency];
k--
) {
exchangeRate = exchangeRates[k]?.exchangeRates[currency];
}
} else {
exchangeRate = new Big(1);
}
let marketPrice: number;
if (exchangeRate) {
marketPrice = exchangeRate
.mul(marketDataItem.marketPrice)
.toNumber();
} else {
if (!isToday(marketDataItem.date)) {
Logger.error(
`Failed to get exchange rate for ${
currencies[marketDataItem.symbol]
} to ${userCurrency} at ${format(
marketDataItem.date,
DATE_FORMAT
)}, using today's exchange rate as a fallback`
);
}
marketPrice = this.exchangeRateDataService.toCurrency(
marketDataItem.marketPrice, marketDataItem.marketPrice,
currencies[marketDataItem.symbol], currencies[marketDataItem.symbol],
userCurrency userCurrency
), );
}
result.push({
date: marketDataItem.date,
marketPrice: marketPrice,
symbol: marketDataItem.symbol symbol: marketDataItem.symbol
}; });
}); }
return result;
}) })
); );

@ -26,7 +26,7 @@ import {
baseCurrency, baseCurrency,
ghostfolioCashSymbol ghostfolioCashSymbol
} from '@ghostfolio/common/config'; } from '@ghostfolio/common/config';
import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper'; import { DATE_FORMAT, parseDate, resetHours } from '@ghostfolio/common/helper';
import { import {
Accounts, Accounts,
PortfolioDetails, PortfolioDetails,
@ -43,7 +43,7 @@ import type {
OrderWithAccount, OrderWithAccount,
RequestWithUser RequestWithUser
} from '@ghostfolio/common/types'; } from '@ghostfolio/common/types';
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable, Logger } from '@nestjs/common';
import { REQUEST } from '@nestjs/core'; import { REQUEST } from '@nestjs/core';
import { AssetClass, DataSource, Type as TypeOfOrder } from '@prisma/client'; import { AssetClass, DataSource, Type as TypeOfOrder } from '@prisma/client';
import Big from 'big.js'; import Big from 'big.js';
@ -52,6 +52,7 @@ import {
format, format,
isAfter, isAfter,
isBefore, isBefore,
isToday,
max, max,
parse, parse,
parseISO, parseISO,
@ -735,20 +736,24 @@ export class PortfolioService {
}; };
} }
public getFees(orders: OrderWithAccount[], date = new Date(0)) { public getFees(
orders: OrderWithAccount[],
exchangeRates: { [date: string]: { [currency: string]: Big } },
date = new Date(0)
) {
return orders return orders
.filter((order) => { .filter((order) => {
// Filter out all orders before given date // Filter out all orders before given date
return isBefore(date, new Date(order.date)); return isBefore(date, new Date(order.date));
}) })
.map((order) => { .map((order) =>
return this.exchangeRateDataService.toCurrency( this.convertCurrency({
order.fee, exchangeRates,
order.currency, ...order,
this.request.user.Settings.currency value: order.fee
); })
}) )
.reduce((previous, current) => previous + current, 0); .reduce((previous, current) => current.plus(previous), new Big(0));
} }
public async getReport(impersonationId: string): Promise<PortfolioReport> { public async getReport(impersonationId: string): Promise<PortfolioReport> {
@ -786,6 +791,7 @@ export class PortfolioService {
currency, currency,
userId userId
); );
const exchangeRates = await this.exchangeRateForOrders(orders);
return { return {
rules: { rules: {
accountClusterRisk: await this.rulesService.evaluate( accountClusterRisk: await this.rulesService.evaluate(
@ -831,7 +837,7 @@ export class PortfolioService {
new FeeRatioInitialInvestment( new FeeRatioInitialInvestment(
this.exchangeRateDataService, this.exchangeRateDataService,
currentPositions.totalInvestment.toNumber(), currentPositions.totalInvestment.toNumber(),
this.getFees(orders) this.getFees(orders, exchangeRates).toNumber()
) )
], ],
{ baseCurrency: currency } { baseCurrency: currency }
@ -851,11 +857,20 @@ export class PortfolioService {
currency currency
); );
const orders = await this.orderService.getOrders({ userId }); const orders = await this.orderService.getOrders({ userId });
const fees = this.getFees(orders); const exchangeRates = await this.exchangeRateForOrders(orders);
const fees = this.getFees(orders, exchangeRates);
const firstOrderDate = orders[0]?.date; const firstOrderDate = orders[0]?.date;
const totalBuy = this.getTotalByType(orders, currency, TypeOfOrder.BUY); const totalBuy = this.getTotalByType(
const totalSell = this.getTotalByType(orders, currency, TypeOfOrder.SELL); orders,
TypeOfOrder.BUY,
exchangeRates
);
const totalSell = this.getTotalByType(
orders,
TypeOfOrder.SELL,
exchangeRates
);
const committedFunds = new Big(totalBuy).sub(totalSell); const committedFunds = new Big(totalBuy).sub(totalSell);
@ -865,14 +880,14 @@ export class PortfolioService {
return { return {
...performanceInformation.performance, ...performanceInformation.performance,
fees,
firstOrderDate, firstOrderDate,
netWorth, netWorth,
cash: balance, cash: balance,
committedFunds: committedFunds.toNumber(), committedFunds: committedFunds.toNumber(),
fees: fees.toNumber(),
ordersCount: orders.length, ordersCount: orders.length,
totalBuy: totalBuy, totalBuy: totalBuy.toNumber(),
totalSell: totalSell totalSell: totalSell.toNumber()
}; };
} }
@ -980,28 +995,25 @@ export class PortfolioService {
} }
const userCurrency = this.request.user?.Settings?.currency ?? baseCurrency; const userCurrency = this.request.user?.Settings?.currency ?? baseCurrency;
const exchangeRates = await this.exchangeRateForOrders(orders);
const portfolioOrders: PortfolioOrder[] = orders.map((order) => ({ const portfolioOrders: PortfolioOrder[] = orders.map((order) => ({
currency: order.currency, currency: order.currency,
dataSource: order.dataSource, dataSource: order.dataSource,
date: format(order.date, DATE_FORMAT), date: format(order.date, DATE_FORMAT),
fee: new Big( fee: this.convertCurrency({
this.exchangeRateDataService.toCurrency( exchangeRates,
order.fee, ...order,
order.currency, value: order.fee
userCurrency }),
)
),
name: order.SymbolProfile?.name, name: order.SymbolProfile?.name,
quantity: new Big(order.quantity), quantity: new Big(order.quantity),
symbol: order.symbol, symbol: order.symbol,
type: <OrderType>order.type, type: <OrderType>order.type,
unitPrice: new Big( unitPrice: this.convertCurrency({
this.exchangeRateDataService.toCurrency( exchangeRates,
order.unitPrice, ...order,
order.currency, value: order.unitPrice
userCurrency })
)
)
})); }));
const portfolioCalculator = new PortfolioCalculator( const portfolioCalculator = new PortfolioCalculator(
@ -1071,6 +1083,60 @@ export class PortfolioService {
return accounts; return accounts;
} }
private convertCurrency({
exchangeRates,
date,
currency,
value
}: {
exchangeRates: { [date: string]: { [currency: string]: Big } };
date: Date;
currency: string;
value: number | Big;
}): Big {
const exchangeRate = exchangeRates[format(date, DATE_FORMAT)]?.[currency];
if (exchangeRate) {
return exchangeRate?.mul(value);
}
const userCurrency = this.request.user?.Settings?.currency ?? baseCurrency;
if (!isToday(date)) {
Logger.error(
`Failed to convert value for date ${format(
date,
DATE_FORMAT
)} from ${currency} to ${userCurrency}`
);
}
return new Big(
this.exchangeRateDataService.toCurrency(
new Big(value).toNumber(),
currency,
userCurrency
)
);
}
private async exchangeRateForOrders(
orders: OrderWithAccount[]
): Promise<{ [date: string]: { [currency: string]: Big } }> {
const userCurrency = this.request.user?.Settings?.currency ?? baseCurrency;
const dates = orders.map((order) => resetHours(order.date));
const exchangeRates = await this.exchangeRateDataService.getExchangeRates({
dateQuery: {
in: dates
},
sourceCurrencies: orders.map((order) => order.currency),
destinationCurrency: userCurrency
});
const exchangeRateLookupMap = {};
for (const exchangeRate of exchangeRates) {
exchangeRateLookupMap[format(exchangeRate.date, DATE_FORMAT)] =
exchangeRate.exchangeRates;
}
return exchangeRateLookupMap;
}
private async getUserId(aImpersonationId: string, aUserId: string) { private async getUserId(aImpersonationId: string, aUserId: string) {
const impersonationUserId = const impersonationUserId =
await this.impersonationService.validateImpersonationId( await this.impersonationService.validateImpersonationId(
@ -1083,20 +1149,20 @@ export class PortfolioService {
private getTotalByType( private getTotalByType(
orders: OrderWithAccount[], orders: OrderWithAccount[],
currency: string, type: TypeOfOrder,
type: TypeOfOrder exchangeRates: { [date: string]: { [currency: string]: Big } }
) { ) {
return orders return orders
.filter( .filter(
(order) => !isAfter(order.date, endOfToday()) && order.type === type (order) => !isAfter(order.date, endOfToday()) && order.type === type
) )
.map((order) => { .map((order) =>
return this.exchangeRateDataService.toCurrency( this.convertCurrency({
order.quantity * order.unitPrice, exchangeRates,
order.currency, ...order,
currency value: order.quantity * order.unitPrice
); })
}) )
.reduce((previous, current) => previous + current, 0); .reduce((previous, current) => current.plus(previous), new Big(0));
} }
} }

@ -1,5 +1,6 @@
import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
import { MarketDataService } from '@ghostfolio/api/services/market-data.service';
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { PrismaModule } from './prisma.module'; import { PrismaModule } from './prisma.module';
@ -7,7 +8,7 @@ import { PropertyModule } from './property/property.module';
@Module({ @Module({
imports: [DataProviderModule, PrismaModule, PropertyModule], imports: [DataProviderModule, PrismaModule, PropertyModule],
providers: [ExchangeRateDataService], providers: [ExchangeRateDataService, MarketDataService],
exports: [ExchangeRateDataService] exports: [ExchangeRateDataService]
}) })
export class ExchangeRateDataModule {} export class ExchangeRateDataModule {}

@ -0,0 +1,201 @@
import { DateQuery } from '@ghostfolio/api/app/portfolio/interfaces/date-query.interface';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
import { MarketDataService } from '@ghostfolio/api/services/market-data.service';
import { MarketData } from '@prisma/client';
import { Big } from 'big.js';
import { addDays, endOfDay, isBefore } from 'date-fns';
jest.mock('@ghostfolio/api/services/market-data.service', () => {
return {
MarketDataService: jest.fn().mockImplementation(() => {
return {
getRange: ({
dateQuery,
symbols
}: {
dateQuery: DateQuery;
symbols: string[];
}) => {
const exchangeRateMap = {
USDEUR: 1,
USDCHF: 2,
USDUSD: 0
};
const result = [];
let j = 1;
for (
let i = dateQuery.gte;
isBefore(i, dateQuery.lt);
i = addDays(i, 1)
) {
const marketPrice = j++;
for (const symbol of symbols) {
result.push({
createdAt: i,
date: i,
id: '8fa48fde-f397-4b0d-adbc-fb940e830e6d',
marketPrice: marketPrice * exchangeRateMap[symbol] + 1,
symbol: symbol
});
}
}
return Promise.resolve<MarketData[]>(result);
}
};
})
};
});
describe('ExchangeRateDataService', () => {
let exchangeRateDataService: ExchangeRateDataService;
let marketDataService: MarketDataService;
beforeAll(async () => {
marketDataService = new MarketDataService(null);
exchangeRateDataService = new ExchangeRateDataService(
null,
marketDataService,
null
);
});
describe('getExchangeRates', () => {
it('source and destination USD', async () => {
const startDate = new Date(2021, 0, 1);
const exchangeRates = await exchangeRateDataService.getExchangeRates({
dateQuery: {
gte: startDate,
lt: endOfDay(startDate)
},
sourceCurrencies: ['USD'],
destinationCurrency: 'USD'
});
expect(exchangeRates).toEqual([
{
date: startDate,
exchangeRates: {
USD: new Big(1)
}
}
]);
});
it('source USD and destination CHF', async () => {
const startDate = new Date(2021, 0, 1);
const exchangeRates = await exchangeRateDataService.getExchangeRates({
dateQuery: {
gte: startDate,
lt: endOfDay(startDate)
},
sourceCurrencies: ['USD'],
destinationCurrency: 'CHF'
});
expect(exchangeRates).toEqual([
{
date: startDate,
exchangeRates: {
USD: new Big(3)
}
}
]);
});
it('source CHF and destination USD', async () => {
const startDate = new Date(2021, 0, 1);
const exchangeRates = await exchangeRateDataService.getExchangeRates({
dateQuery: {
gte: startDate,
lt: endOfDay(startDate)
},
sourceCurrencies: ['CHF'],
destinationCurrency: 'USD'
});
expect(exchangeRates).toEqual([
{
date: startDate,
exchangeRates: {
CHF: new Big(1).div(3)
}
}
]);
});
it('source CHF and destination EUR', async () => {
const startDate = new Date(2021, 0, 1);
const exchangeRates = await exchangeRateDataService.getExchangeRates({
dateQuery: {
gte: startDate,
lt: endOfDay(startDate)
},
sourceCurrencies: ['CHF'],
destinationCurrency: 'EUR'
});
expect(exchangeRates).toEqual([
{
date: startDate,
exchangeRates: {
CHF: new Big(2).div(3)
}
}
]);
});
it('source CHF,EUR,USD and destination EUR', async () => {
const startDate = new Date(2021, 0, 1);
const exchangeRates = await exchangeRateDataService.getExchangeRates({
dateQuery: {
gte: startDate,
lt: endOfDay(startDate)
},
sourceCurrencies: ['CHF', 'USD', 'EUR'],
destinationCurrency: 'EUR'
});
expect(exchangeRates).toEqual([
{
date: startDate,
exchangeRates: {
CHF: new Big(2).div(3),
USD: new Big(2),
EUR: new Big(1)
}
}
]);
});
it('with multiple days', async () => {
const startDate = new Date(2021, 0, 1);
const exchangeRates = await exchangeRateDataService.getExchangeRates({
dateQuery: {
gte: startDate,
lt: endOfDay(addDays(startDate, 1))
},
sourceCurrencies: ['CHF', 'USD', 'EUR'],
destinationCurrency: 'EUR'
});
expect(exchangeRates).toEqual([
{
date: startDate,
exchangeRates: {
CHF: new Big(2).div(3),
USD: new Big(2),
EUR: new Big(1)
}
},
{
date: addDays(startDate, 1),
exchangeRates: {
CHF: new Big(3).div(5),
USD: new Big(3),
EUR: new Big(1)
}
}
]);
});
});
});

@ -1,7 +1,11 @@
import { DateQuery } from '@ghostfolio/api/app/portfolio/interfaces/date-query.interface';
import { DateBasedExchangeRate } from '@ghostfolio/api/services/interfaces/date-based-exchange-rate.interface';
import { MarketDataService } from '@ghostfolio/api/services/market-data.service';
import { PROPERTY_CURRENCIES, baseCurrency } from '@ghostfolio/common/config'; import { PROPERTY_CURRENCIES, baseCurrency } from '@ghostfolio/common/config';
import { DATE_FORMAT, getYesterday } from '@ghostfolio/common/helper'; import { DATE_FORMAT, getYesterday } from '@ghostfolio/common/helper';
import { Injectable, Logger } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { format } from 'date-fns'; import Big from 'big.js';
import { format, isSameDay } from 'date-fns';
import { isEmpty, isNumber, uniq } from 'lodash'; import { isEmpty, isNumber, uniq } from 'lodash';
import { DataProviderService } from './data-provider/data-provider.service'; import { DataProviderService } from './data-provider/data-provider.service';
@ -17,6 +21,7 @@ export class ExchangeRateDataService {
public constructor( public constructor(
private readonly dataProviderService: DataProviderService, private readonly dataProviderService: DataProviderService,
private readonly marketDataService: MarketDataService,
private readonly prismaService: PrismaService, private readonly prismaService: PrismaService,
private readonly propertyService: PropertyService private readonly propertyService: PropertyService
) { ) {
@ -31,6 +36,55 @@ export class ExchangeRateDataService {
return this.currencyPairs; return this.currencyPairs;
} }
public async getExchangeRates({
dateQuery,
sourceCurrencies,
destinationCurrency
}: {
dateQuery: DateQuery;
sourceCurrencies: string[];
destinationCurrency: string;
}): Promise<DateBasedExchangeRate[]> {
const symbols = [...sourceCurrencies, destinationCurrency]
.map((currency) => `${baseCurrency}${currency}`)
.filter((v, i, a) => a.indexOf(v) === i);
const exchangeRates = await this.marketDataService.getRange({
dateQuery,
symbols
});
if (exchangeRates.length === 0) {
return [];
}
const results: DateBasedExchangeRate[] = [];
let currentDate = exchangeRates[0].date;
let currentRates: { [symbol: string]: Big } = {};
for (const exchangeRate of exchangeRates) {
if (!isSameDay(currentDate, exchangeRate.date)) {
results.push({
date: currentDate,
exchangeRates: this.getUserExchangeRates(
currentRates,
destinationCurrency,
sourceCurrencies
)
});
currentDate = exchangeRate.date;
currentRates = {};
}
currentRates[exchangeRate.symbol] = new Big(exchangeRate.marketPrice);
}
results.push({
date: currentDate,
exchangeRates: this.getUserExchangeRates(
currentRates,
destinationCurrency,
sourceCurrencies
)
});
return results;
}
public async initialize() { public async initialize() {
this.currencies = await this.prepareCurrencies(); this.currencies = await this.prepareCurrencies();
this.currencyPairs = []; this.currencyPairs = [];
@ -97,10 +151,10 @@ export class ExchangeRateDataService {
this.exchangeRates[symbol] = resultExtended[symbol]?.[date]?.marketPrice; this.exchangeRates[symbol] = resultExtended[symbol]?.[date]?.marketPrice;
if (!this.exchangeRates[symbol]) { if (!this.exchangeRates[symbol]) {
// Not found, calculate indirectly via USD // Not found, calculate indirectly via base currency
this.exchangeRates[symbol] = this.exchangeRates[symbol] =
resultExtended[`${currency1}${'USD'}`]?.[date]?.marketPrice * resultExtended[`${currency1}${baseCurrency}`]?.[date]?.marketPrice *
resultExtended[`${'USD'}${currency2}`]?.[date]?.marketPrice; resultExtended[`${baseCurrency}${currency2}`]?.[date]?.marketPrice;
// Calculate the opposite direction // Calculate the opposite direction
this.exchangeRates[`${currency2}${currency1}`] = this.exchangeRates[`${currency2}${currency1}`] =
@ -129,9 +183,9 @@ export class ExchangeRateDataService {
if (this.exchangeRates[`${aFromCurrency}${aToCurrency}`]) { if (this.exchangeRates[`${aFromCurrency}${aToCurrency}`]) {
factor = this.exchangeRates[`${aFromCurrency}${aToCurrency}`]; factor = this.exchangeRates[`${aFromCurrency}${aToCurrency}`];
} else { } else {
// Calculate indirectly via USD // Calculate indirectly via base currency
const factor1 = this.exchangeRates[`${aFromCurrency}${'USD'}`]; const factor1 = this.exchangeRates[`${aFromCurrency}${baseCurrency}`];
const factor2 = this.exchangeRates[`${'USD'}${aToCurrency}`]; const factor2 = this.exchangeRates[`${baseCurrency}${aToCurrency}`];
factor = factor1 * factor2; factor = factor1 * factor2;
@ -194,6 +248,46 @@ export class ExchangeRateDataService {
return uniq(currencies).sort(); return uniq(currencies).sort();
} }
private getUserExchangeRates(
currentRates: { [symbol: string]: Big },
destinationCurrency: string,
sourceCurrencies: string[]
): { [currency: string]: Big } {
const result: { [currency: string]: Big } = {};
for (const sourceCurrency of sourceCurrencies) {
let exchangeRate: Big;
if (sourceCurrency === destinationCurrency) {
exchangeRate = new Big(1);
} else if (
destinationCurrency === baseCurrency &&
currentRates[`${destinationCurrency}${sourceCurrency}`]
) {
exchangeRate = new Big(1).div(
currentRates[`${destinationCurrency}${sourceCurrency}`]
);
} else if (
sourceCurrency === baseCurrency &&
currentRates[`${sourceCurrency}${destinationCurrency}`]
) {
exchangeRate = currentRates[`${sourceCurrency}${destinationCurrency}`];
} else if (
currentRates[`${baseCurrency}${destinationCurrency}`] &&
currentRates[`${baseCurrency}${sourceCurrency}`]
) {
exchangeRate = currentRates[
`${baseCurrency}${destinationCurrency}`
].div(currentRates[`${baseCurrency}${sourceCurrency}`]);
}
if (exchangeRate) {
result[sourceCurrency] = exchangeRate;
}
}
return result;
}
private prepareCurrencyPairs(aCurrencies: string[]) { private prepareCurrencyPairs(aCurrencies: string[]) {
return aCurrencies return aCurrencies
.filter((currency) => { .filter((currency) => {

@ -0,0 +1,6 @@
import Big from 'big.js';
export interface DateBasedExchangeRate {
date: Date;
exchangeRates: { [currency: string]: Big };
}
Loading…
Cancel
Save