Extend current rate service with getRange()
This commit is contained in:
parent
7dac059a55
commit
099571437e
@ -17,6 +17,28 @@ jest.mock('./market-data.service', () => {
|
||||
id: 'aefcbe3a-ee10-4c4f-9f2d-8ffad7b05584',
|
||||
marketPrice: 1847.839966
|
||||
});
|
||||
},
|
||||
getRange: (
|
||||
dateRangeEnd: Date,
|
||||
dateRangeStart: Date,
|
||||
symbol: string
|
||||
) => {
|
||||
return Promise.resolve<MarketData[]>([
|
||||
{
|
||||
date: dateRangeStart,
|
||||
symbol,
|
||||
createdAt: dateRangeStart,
|
||||
id: '8fa48fde-f397-4b0d-adbc-fb940e830e6d',
|
||||
marketPrice: 1841.823902
|
||||
},
|
||||
{
|
||||
date: dateRangeEnd,
|
||||
symbol,
|
||||
createdAt: dateRangeEnd,
|
||||
id: '082d6893-df27-4c91-8a5d-092e84315b56',
|
||||
marketPrice: 1847.839966
|
||||
}
|
||||
]);
|
||||
}
|
||||
};
|
||||
})
|
||||
@ -71,6 +93,29 @@ describe('CurrentRateService', () => {
|
||||
symbol: 'AMZN',
|
||||
userCurrency: Currency.CHF
|
||||
})
|
||||
).toEqual(1847.839966);
|
||||
).toMatchObject({
|
||||
marketPrice: 1847.839966
|
||||
});
|
||||
});
|
||||
|
||||
it('getValues', async () => {
|
||||
expect(
|
||||
await currentRateService.getValues({
|
||||
currency: Currency.USD,
|
||||
dateRangeEnd: new Date(Date.UTC(2020, 0, 2, 0, 0, 0)),
|
||||
dateRangeStart: new Date(Date.UTC(2020, 0, 1, 0, 0, 0)),
|
||||
symbol: 'AMZN',
|
||||
userCurrency: Currency.CHF
|
||||
})
|
||||
).toMatchObject([
|
||||
{
|
||||
// date
|
||||
marketPrice: 1841.823902
|
||||
},
|
||||
{
|
||||
// date
|
||||
marketPrice: 1847.839966
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { DataProviderService } from '@ghostfolio/api/services/data-provider.service';
|
||||
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
|
||||
import { resetHours } from '@ghostfolio/common/helper';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Currency } from '@prisma/client';
|
||||
import { isToday } from 'date-fns';
|
||||
@ -19,10 +20,13 @@ export class CurrentRateService {
|
||||
date,
|
||||
symbol,
|
||||
userCurrency
|
||||
}: GetValueParams): Promise<number> {
|
||||
}: GetValueParams): Promise<GetValueObject> {
|
||||
if (isToday(date)) {
|
||||
const dataProviderResult = await this.dataProviderService.get([symbol]);
|
||||
return dataProviderResult?.[symbol]?.marketPrice ?? 0;
|
||||
return {
|
||||
date: resetHours(date),
|
||||
marketPrice: dataProviderResult?.[symbol]?.marketPrice ?? 0
|
||||
};
|
||||
}
|
||||
|
||||
const marketData = await this.marketDataService.get({
|
||||
@ -31,14 +35,50 @@ export class CurrentRateService {
|
||||
});
|
||||
|
||||
if (marketData) {
|
||||
return this.exchangeRateDataService.toCurrency(
|
||||
marketData.marketPrice,
|
||||
currency,
|
||||
userCurrency
|
||||
);
|
||||
return {
|
||||
date: marketData.date,
|
||||
marketPrice: this.exchangeRateDataService.toCurrency(
|
||||
marketData.marketPrice,
|
||||
currency,
|
||||
userCurrency
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error(`Value not found for ${symbol} at ${date}`);
|
||||
throw new Error(`Value not found for ${symbol} at ${resetHours(date)}`);
|
||||
}
|
||||
|
||||
public async getValues({
|
||||
currency,
|
||||
dateRangeEnd,
|
||||
dateRangeStart,
|
||||
symbol,
|
||||
userCurrency
|
||||
}: GetValuesParams): Promise<GetValueObject[]> {
|
||||
const marketData = await this.marketDataService.getRange({
|
||||
dateRangeEnd,
|
||||
dateRangeStart,
|
||||
symbol
|
||||
});
|
||||
|
||||
if (marketData) {
|
||||
return marketData.map((marketDataItem) => {
|
||||
return {
|
||||
date: marketDataItem.date,
|
||||
marketPrice: this.exchangeRateDataService.toCurrency(
|
||||
marketDataItem.marketPrice,
|
||||
currency,
|
||||
userCurrency
|
||||
)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Values not found for ${symbol} from ${resetHours(
|
||||
dateRangeStart
|
||||
)} to ${resetHours(dateRangeEnd)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,3 +88,16 @@ export interface GetValueParams {
|
||||
currency: Currency;
|
||||
userCurrency: Currency;
|
||||
}
|
||||
|
||||
export interface GetValuesParams {
|
||||
dateRangeEnd: Date;
|
||||
dateRangeStart: Date;
|
||||
symbol: string;
|
||||
currency: Currency;
|
||||
userCurrency: Currency;
|
||||
}
|
||||
|
||||
export interface GetValueObject {
|
||||
date: Date;
|
||||
marketPrice: number;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import { PrismaService } from '@ghostfolio/api/services/prisma.service';
|
||||
import { resetHours } from '@ghostfolio/common/helper';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { MarketData } from '@prisma/client';
|
||||
import { endOfDay } from 'date-fns';
|
||||
|
||||
@Injectable()
|
||||
export class MarketDataService {
|
||||
@ -21,4 +22,24 @@ export class MarketDataService {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async getRange({
|
||||
dateRangeEnd,
|
||||
dateRangeStart,
|
||||
symbol
|
||||
}: {
|
||||
dateRangeEnd: Date;
|
||||
dateRangeStart: Date;
|
||||
symbol: string;
|
||||
}): Promise<MarketData[]> {
|
||||
return await this.prisma.marketData.findMany({
|
||||
where: {
|
||||
date: {
|
||||
gte: dateRangeStart,
|
||||
lt: endOfDay(dateRangeEnd)
|
||||
},
|
||||
symbol
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,15 @@
|
||||
import {
|
||||
CurrentRateService,
|
||||
GetValueParams
|
||||
} from '@ghostfolio/api/app/core/current-rate.service';
|
||||
import {
|
||||
PortfolioCalculator,
|
||||
PortfolioOrder,
|
||||
TimelinePeriod,
|
||||
TimelineSpecification
|
||||
} from '@ghostfolio/api/app/core/portfolio-calculator';
|
||||
import {
|
||||
CurrentRateService,
|
||||
GetValueParams
|
||||
} from '@ghostfolio/api/app/core/current-rate.service';
|
||||
import { Currency } from '@prisma/client';
|
||||
import { OrderType } from '@ghostfolio/api/models/order-type';
|
||||
import { Currency } from '@prisma/client';
|
||||
import Big from 'big.js';
|
||||
import { differenceInCalendarDays, parse } from 'date-fns';
|
||||
|
||||
@ -46,21 +46,21 @@ jest.mock('./current-rate.service.ts', () => {
|
||||
const today = new Date();
|
||||
if (symbol === 'VTI') {
|
||||
if (dateEqual(today, date)) {
|
||||
return Promise.resolve(new Big('213.32'));
|
||||
return Promise.resolve({ marketPrice: new Big('213.32') });
|
||||
} else {
|
||||
const startDate = parse('2019-02-01', 'yyyy-MM-dd', new Date());
|
||||
const daysInBetween = differenceInCalendarDays(date, startDate);
|
||||
|
||||
const result = new Big('144.38').plus(
|
||||
const marketPrice = new Big('144.38').plus(
|
||||
new Big('0.08').mul(daysInBetween)
|
||||
);
|
||||
return Promise.resolve(result);
|
||||
return Promise.resolve({ marketPrice });
|
||||
}
|
||||
} else if (symbol === 'AMZN') {
|
||||
return Promise.resolve(2021.99);
|
||||
return Promise.resolve({ marketPrice: new Big('2021.99') });
|
||||
}
|
||||
|
||||
return Promise.resolve(new Big('0'));
|
||||
return Promise.resolve({ marketPrice: new Big('0') });
|
||||
}
|
||||
};
|
||||
})
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Currency } from '@prisma/client';
|
||||
import { CurrentRateService } from '@ghostfolio/api/app/core/current-rate.service';
|
||||
import { OrderType } from '@ghostfolio/api/models/order-type';
|
||||
import { Currency } from '@prisma/client';
|
||||
import Big from 'big.js';
|
||||
import {
|
||||
addDays,
|
||||
@ -110,7 +110,7 @@ export class PortfolioCalculator {
|
||||
|
||||
const result: { [symbol: string]: TimelinePosition } = {};
|
||||
for (const item of lastTransactionPoint.items) {
|
||||
const marketPrice = await this.currentRateService.getValue({
|
||||
const marketValue = await this.currentRateService.getValue({
|
||||
date: new Date(),
|
||||
symbol: item.symbol,
|
||||
currency: item.currency,
|
||||
@ -122,7 +122,7 @@ export class PortfolioCalculator {
|
||||
quantity: item.quantity,
|
||||
symbol: item.symbol,
|
||||
investment: item.investment,
|
||||
marketPrice: marketPrice,
|
||||
marketPrice: marketValue.marketPrice,
|
||||
transactionCount: item.transactionCount
|
||||
};
|
||||
}
|
||||
@ -186,7 +186,7 @@ export class PortfolioCalculator {
|
||||
currency: item.currency,
|
||||
userCurrency: this.currency
|
||||
})
|
||||
.then((v) => new Big(v).mul(item.quantity))
|
||||
.then(({ marketPrice }) => new Big(marketPrice).mul(item.quantity))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user