|
|
|
@ -2,6 +2,17 @@ import { Currency } from '@prisma/client';
|
|
|
|
|
import { CurrentRateService } from '@ghostfolio/api/app/core/current-rate.service';
|
|
|
|
|
import { OrderType } from '@ghostfolio/api/models/order-type';
|
|
|
|
|
import Big from 'big.js';
|
|
|
|
|
import {
|
|
|
|
|
addDays,
|
|
|
|
|
addMonths,
|
|
|
|
|
addYears,
|
|
|
|
|
format,
|
|
|
|
|
isAfter,
|
|
|
|
|
isBefore,
|
|
|
|
|
parse
|
|
|
|
|
} from 'date-fns';
|
|
|
|
|
|
|
|
|
|
const DATE_FORMAT = 'yyyy-MM-dd';
|
|
|
|
|
|
|
|
|
|
export class PortfolioCalculator {
|
|
|
|
|
private transactionPoints: TransactionPoint[];
|
|
|
|
@ -11,10 +22,6 @@ export class PortfolioCalculator {
|
|
|
|
|
private currency: Currency
|
|
|
|
|
) {}
|
|
|
|
|
|
|
|
|
|
addOrder(order: PortfolioOrder): void {}
|
|
|
|
|
|
|
|
|
|
deleteOrder(order: PortfolioOrder): void {}
|
|
|
|
|
|
|
|
|
|
computeTransactionPoints(orders: PortfolioOrder[]) {
|
|
|
|
|
orders.sort((a, b) => a.date.localeCompare(b.date));
|
|
|
|
|
|
|
|
|
@ -121,9 +128,38 @@ export class PortfolioCalculator {
|
|
|
|
|
|
|
|
|
|
calculateTimeline(
|
|
|
|
|
timelineSpecification: TimelineSpecification[],
|
|
|
|
|
endDate: Date
|
|
|
|
|
endDate: string
|
|
|
|
|
): TimelinePeriod[] {
|
|
|
|
|
return null;
|
|
|
|
|
if (timelineSpecification.length === 0) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const startDate = timelineSpecification[0].start;
|
|
|
|
|
const start = parse(startDate, DATE_FORMAT, new Date());
|
|
|
|
|
const end = parse(endDate, DATE_FORMAT, new Date());
|
|
|
|
|
|
|
|
|
|
const timelinePeriod: TimelinePeriod[] = [];
|
|
|
|
|
let i = 0;
|
|
|
|
|
for (
|
|
|
|
|
let currentDate = start;
|
|
|
|
|
!isAfter(currentDate, end);
|
|
|
|
|
currentDate = this.addToDate(
|
|
|
|
|
currentDate,
|
|
|
|
|
timelineSpecification[i].accuracy
|
|
|
|
|
)
|
|
|
|
|
) {
|
|
|
|
|
if (this.isNextItemActive(timelineSpecification, currentDate, i)) {
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
timelinePeriod.push({
|
|
|
|
|
date: format(currentDate, DATE_FORMAT),
|
|
|
|
|
grossPerformance: 0,
|
|
|
|
|
investment: 0,
|
|
|
|
|
value: 0
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return timelinePeriod;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private getFactor(type: OrderType) {
|
|
|
|
@ -141,6 +177,31 @@ export class PortfolioCalculator {
|
|
|
|
|
}
|
|
|
|
|
return factor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private addToDate(date: Date, accurany: Accuracy): Date {
|
|
|
|
|
switch (accurany) {
|
|
|
|
|
case 'day':
|
|
|
|
|
return addDays(date, 1);
|
|
|
|
|
case 'month':
|
|
|
|
|
return addMonths(date, 1);
|
|
|
|
|
case 'year':
|
|
|
|
|
return addYears(date, 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private isNextItemActive(
|
|
|
|
|
timelineSpecification: TimelineSpecification[],
|
|
|
|
|
currentDate: Date,
|
|
|
|
|
i: number
|
|
|
|
|
) {
|
|
|
|
|
return (
|
|
|
|
|
i + 1 < timelineSpecification.length &&
|
|
|
|
|
!isBefore(
|
|
|
|
|
currentDate,
|
|
|
|
|
parse(timelineSpecification[i + 1].start, DATE_FORMAT, new Date())
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface TransactionPoint {
|
|
|
|
@ -169,13 +230,13 @@ interface TimelinePosition {
|
|
|
|
|
|
|
|
|
|
type Accuracy = 'year' | 'month' | 'day';
|
|
|
|
|
|
|
|
|
|
interface TimelineSpecification {
|
|
|
|
|
start: Date;
|
|
|
|
|
export interface TimelineSpecification {
|
|
|
|
|
start: string;
|
|
|
|
|
accuracy: Accuracy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface TimelinePeriod {
|
|
|
|
|
date: Date;
|
|
|
|
|
export interface TimelinePeriod {
|
|
|
|
|
date: string;
|
|
|
|
|
grossPerformance: number;
|
|
|
|
|
investment: number;
|
|
|
|
|
value: number;
|
|
|
|
|