add draft integration of new portfolio calculator to chart
This commit is contained in:
parent
19bcd601d1
commit
cfee6c1ddd
@ -18,6 +18,8 @@ import { Module } from '@nestjs/common';
|
||||
|
||||
import { PortfolioController } from './portfolio.controller';
|
||||
import { PortfolioService } from './portfolio.service';
|
||||
import { CurrentRateService } from '@ghostfolio/api/app/core/current-rate.service';
|
||||
import { MarketDataService } from '@ghostfolio/api/app/core/market-data.service';
|
||||
|
||||
@Module({
|
||||
imports: [RedisCacheModule],
|
||||
@ -26,6 +28,7 @@ import { PortfolioService } from './portfolio.service';
|
||||
AccountService,
|
||||
AlphaVantageService,
|
||||
CacheService,
|
||||
CurrentRateService,
|
||||
ConfigurationService,
|
||||
DataGatheringService,
|
||||
DataProviderService,
|
||||
@ -37,6 +40,7 @@ import { PortfolioService } from './portfolio.service';
|
||||
PrismaService,
|
||||
RakutenRapidApiService,
|
||||
RulesService,
|
||||
MarketDataService,
|
||||
UserService,
|
||||
YahooFinanceService
|
||||
]
|
||||
|
@ -26,11 +26,15 @@ import {
|
||||
getYear,
|
||||
isAfter,
|
||||
isSameDay,
|
||||
max,
|
||||
parse,
|
||||
parseISO,
|
||||
setDate,
|
||||
setDayOfYear,
|
||||
setMonth,
|
||||
sub
|
||||
sub,
|
||||
subDays,
|
||||
subYears
|
||||
} from 'date-fns';
|
||||
import { isEmpty } from 'lodash';
|
||||
import * as roundTo from 'round-to';
|
||||
@ -39,6 +43,14 @@ import {
|
||||
HistoricalDataItem,
|
||||
PortfolioPositionDetail
|
||||
} from './interfaces/portfolio-position-detail.interface';
|
||||
import {
|
||||
PortfolioCalculator,
|
||||
PortfolioOrder,
|
||||
TimelineSpecification
|
||||
} from '@ghostfolio/api/app/core/portfolio-calculator';
|
||||
import { CurrentRateService } from '@ghostfolio/api/app/core/current-rate.service';
|
||||
import Big from 'big.js';
|
||||
import { port } from 'envalid';
|
||||
|
||||
@Injectable()
|
||||
export class PortfolioService {
|
||||
@ -51,7 +63,8 @@ export class PortfolioService {
|
||||
private readonly redisCacheService: RedisCacheService,
|
||||
@Inject(REQUEST) private readonly request: RequestWithUser,
|
||||
private readonly rulesService: RulesService,
|
||||
private readonly userService: UserService
|
||||
private readonly userService: UserService,
|
||||
private readonly currentRateService: CurrentRateService
|
||||
) {}
|
||||
|
||||
public async createPortfolio(aUserId: string): Promise<Portfolio> {
|
||||
@ -148,7 +161,8 @@ export class PortfolioService {
|
||||
impersonationUserId || this.request.user.id
|
||||
);
|
||||
|
||||
if (portfolio.getOrders().length <= 0) {
|
||||
const orders = portfolio.getOrders();
|
||||
if (orders.length <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -157,10 +171,14 @@ export class PortfolioService {
|
||||
portfolio.getMinDate()
|
||||
);
|
||||
|
||||
return portfolio
|
||||
.get()
|
||||
const portfolioCalculator = new PortfolioCalculator(
|
||||
this.currentRateService,
|
||||
this.request.user.Settings.currency
|
||||
);
|
||||
|
||||
const portfolioOrders: PortfolioOrder[] = orders
|
||||
.filter((portfolioItem) => {
|
||||
if (isAfter(parseISO(portfolioItem.date), endOfToday())) {
|
||||
if (isAfter(parseISO(portfolioItem.getDate()), endOfToday())) {
|
||||
// Filter out future dates
|
||||
return false;
|
||||
}
|
||||
@ -170,18 +188,70 @@ export class PortfolioService {
|
||||
}
|
||||
|
||||
return (
|
||||
isSameDay(parseISO(portfolioItem.date), dateRangeDate) ||
|
||||
isAfter(parseISO(portfolioItem.date), dateRangeDate)
|
||||
isSameDay(parseISO(portfolioItem.getDate()), dateRangeDate) ||
|
||||
isAfter(parseISO(portfolioItem.getDate()), dateRangeDate)
|
||||
);
|
||||
})
|
||||
.map((portfolioItem) => {
|
||||
return {
|
||||
date: format(parseISO(portfolioItem.date), 'yyyy-MM-dd'),
|
||||
grossPerformancePercent: portfolioItem.grossPerformancePercent,
|
||||
marketPrice: portfolioItem.value ?? null,
|
||||
value: portfolioItem.value - portfolioItem.investment ?? null
|
||||
};
|
||||
});
|
||||
.map((order) => ({
|
||||
date: order.getDate().substr(0, 10),
|
||||
quantity: new Big(order.getQuantity()),
|
||||
symbol: order.getSymbol(),
|
||||
type: order.getType(),
|
||||
unitPrice: new Big(order.getUnitPrice()),
|
||||
currency: order.getCurrency()
|
||||
}));
|
||||
portfolioCalculator.computeTransactionPoints(portfolioOrders);
|
||||
const transactionPoints = portfolioCalculator.getTransactionPoints();
|
||||
if (transactionPoints.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const dateFormat = 'yyyy-MM-dd';
|
||||
let portfolioStart = parse(
|
||||
transactionPoints[0].date,
|
||||
dateFormat,
|
||||
new Date()
|
||||
);
|
||||
portfolioStart = this.getStartDate(aDateRange, portfolioStart);
|
||||
|
||||
const timelineSpecification: TimelineSpecification[] = [
|
||||
{
|
||||
start: format(portfolioStart, dateFormat),
|
||||
accuracy: 'month'
|
||||
},
|
||||
{
|
||||
start: format(subYears(new Date(), 1), dateFormat),
|
||||
accuracy: 'day'
|
||||
}
|
||||
];
|
||||
|
||||
const timeline = await portfolioCalculator.calculateTimeline(
|
||||
timelineSpecification,
|
||||
format(new Date(), dateFormat)
|
||||
);
|
||||
|
||||
return timeline.map((timelineItem) => ({
|
||||
date: timelineItem.date,
|
||||
value: timelineItem.grossPerformance,
|
||||
marketPrice: timelineItem.value
|
||||
}));
|
||||
}
|
||||
|
||||
private getStartDate(aDateRange: DateRange, portfolioStart: Date) {
|
||||
switch (aDateRange) {
|
||||
case '1d':
|
||||
portfolioStart = max([portfolioStart, subDays(new Date(), 1)]);
|
||||
break;
|
||||
case 'ytd':
|
||||
portfolioStart = max([portfolioStart, setDayOfYear(new Date(), 1)]);
|
||||
break;
|
||||
case '1y':
|
||||
portfolioStart = max([portfolioStart, subYears(new Date(), 1)]);
|
||||
break;
|
||||
case '5y':
|
||||
portfolioStart = max([portfolioStart, subYears(new Date(), 5)]);
|
||||
break;
|
||||
}
|
||||
return portfolioStart;
|
||||
}
|
||||
|
||||
public async getOverview(
|
||||
|
Loading…
x
Reference in New Issue
Block a user