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 { PortfolioController } from './portfolio.controller';
|
||||||
import { PortfolioService } from './portfolio.service';
|
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({
|
@Module({
|
||||||
imports: [RedisCacheModule],
|
imports: [RedisCacheModule],
|
||||||
@ -26,6 +28,7 @@ import { PortfolioService } from './portfolio.service';
|
|||||||
AccountService,
|
AccountService,
|
||||||
AlphaVantageService,
|
AlphaVantageService,
|
||||||
CacheService,
|
CacheService,
|
||||||
|
CurrentRateService,
|
||||||
ConfigurationService,
|
ConfigurationService,
|
||||||
DataGatheringService,
|
DataGatheringService,
|
||||||
DataProviderService,
|
DataProviderService,
|
||||||
@ -37,6 +40,7 @@ import { PortfolioService } from './portfolio.service';
|
|||||||
PrismaService,
|
PrismaService,
|
||||||
RakutenRapidApiService,
|
RakutenRapidApiService,
|
||||||
RulesService,
|
RulesService,
|
||||||
|
MarketDataService,
|
||||||
UserService,
|
UserService,
|
||||||
YahooFinanceService
|
YahooFinanceService
|
||||||
]
|
]
|
||||||
|
@ -26,11 +26,15 @@ import {
|
|||||||
getYear,
|
getYear,
|
||||||
isAfter,
|
isAfter,
|
||||||
isSameDay,
|
isSameDay,
|
||||||
|
max,
|
||||||
parse,
|
parse,
|
||||||
parseISO,
|
parseISO,
|
||||||
setDate,
|
setDate,
|
||||||
|
setDayOfYear,
|
||||||
setMonth,
|
setMonth,
|
||||||
sub
|
sub,
|
||||||
|
subDays,
|
||||||
|
subYears
|
||||||
} from 'date-fns';
|
} from 'date-fns';
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import * as roundTo from 'round-to';
|
import * as roundTo from 'round-to';
|
||||||
@ -39,6 +43,14 @@ import {
|
|||||||
HistoricalDataItem,
|
HistoricalDataItem,
|
||||||
PortfolioPositionDetail
|
PortfolioPositionDetail
|
||||||
} from './interfaces/portfolio-position-detail.interface';
|
} 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()
|
@Injectable()
|
||||||
export class PortfolioService {
|
export class PortfolioService {
|
||||||
@ -51,7 +63,8 @@ export class PortfolioService {
|
|||||||
private readonly redisCacheService: RedisCacheService,
|
private readonly redisCacheService: RedisCacheService,
|
||||||
@Inject(REQUEST) private readonly request: RequestWithUser,
|
@Inject(REQUEST) private readonly request: RequestWithUser,
|
||||||
private readonly rulesService: RulesService,
|
private readonly rulesService: RulesService,
|
||||||
private readonly userService: UserService
|
private readonly userService: UserService,
|
||||||
|
private readonly currentRateService: CurrentRateService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public async createPortfolio(aUserId: string): Promise<Portfolio> {
|
public async createPortfolio(aUserId: string): Promise<Portfolio> {
|
||||||
@ -148,7 +161,8 @@ export class PortfolioService {
|
|||||||
impersonationUserId || this.request.user.id
|
impersonationUserId || this.request.user.id
|
||||||
);
|
);
|
||||||
|
|
||||||
if (portfolio.getOrders().length <= 0) {
|
const orders = portfolio.getOrders();
|
||||||
|
if (orders.length <= 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,10 +171,14 @@ export class PortfolioService {
|
|||||||
portfolio.getMinDate()
|
portfolio.getMinDate()
|
||||||
);
|
);
|
||||||
|
|
||||||
return portfolio
|
const portfolioCalculator = new PortfolioCalculator(
|
||||||
.get()
|
this.currentRateService,
|
||||||
|
this.request.user.Settings.currency
|
||||||
|
);
|
||||||
|
|
||||||
|
const portfolioOrders: PortfolioOrder[] = orders
|
||||||
.filter((portfolioItem) => {
|
.filter((portfolioItem) => {
|
||||||
if (isAfter(parseISO(portfolioItem.date), endOfToday())) {
|
if (isAfter(parseISO(portfolioItem.getDate()), endOfToday())) {
|
||||||
// Filter out future dates
|
// Filter out future dates
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -170,18 +188,70 @@ export class PortfolioService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
isSameDay(parseISO(portfolioItem.date), dateRangeDate) ||
|
isSameDay(parseISO(portfolioItem.getDate()), dateRangeDate) ||
|
||||||
isAfter(parseISO(portfolioItem.date), dateRangeDate)
|
isAfter(parseISO(portfolioItem.getDate()), dateRangeDate)
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.map((portfolioItem) => {
|
.map((order) => ({
|
||||||
return {
|
date: order.getDate().substr(0, 10),
|
||||||
date: format(parseISO(portfolioItem.date), 'yyyy-MM-dd'),
|
quantity: new Big(order.getQuantity()),
|
||||||
grossPerformancePercent: portfolioItem.grossPerformancePercent,
|
symbol: order.getSymbol(),
|
||||||
marketPrice: portfolioItem.value ?? null,
|
type: order.getType(),
|
||||||
value: portfolioItem.value - portfolioItem.investment ?? null
|
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(
|
public async getOverview(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user