add timeline time point calculation
This commit is contained in:
parent
88f0cb095d
commit
4f7628921d
@ -1,6 +1,8 @@
|
||||
import {
|
||||
PortfolioCalculator,
|
||||
PortfolioOrder
|
||||
PortfolioOrder,
|
||||
TimelinePeriod,
|
||||
TimelineSpecification
|
||||
} from '@ghostfolio/api/app/core/portfolio-calculator';
|
||||
import {
|
||||
CurrentRateService,
|
||||
@ -535,6 +537,690 @@ describe('PortfolioCalculator', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('calculate timeline', () => {
|
||||
it('with yearly', async () => {
|
||||
const portfolioCalculator = new PortfolioCalculator(
|
||||
currentRateService,
|
||||
Currency.USD
|
||||
);
|
||||
portfolioCalculator.setTransactionPoints(ordersVTITransactionPoints);
|
||||
const timelineSpecification: TimelineSpecification[] = [
|
||||
{
|
||||
start: '2019-01-01',
|
||||
accuracy: 'year'
|
||||
}
|
||||
];
|
||||
const timeline: TimelinePeriod[] =
|
||||
await portfolioCalculator.calculateTimeline(
|
||||
timelineSpecification,
|
||||
'2021-12-31'
|
||||
);
|
||||
|
||||
expect(timeline).toEqual([
|
||||
{
|
||||
date: '2019-01-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2020-01-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-01-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it('with monthly', async () => {
|
||||
const portfolioCalculator = new PortfolioCalculator(
|
||||
currentRateService,
|
||||
Currency.USD
|
||||
);
|
||||
portfolioCalculator.setTransactionPoints(ordersVTITransactionPoints);
|
||||
const timelineSpecification: TimelineSpecification[] = [
|
||||
{
|
||||
start: '2019-01-01',
|
||||
accuracy: 'month'
|
||||
}
|
||||
];
|
||||
const timeline: TimelinePeriod[] =
|
||||
await portfolioCalculator.calculateTimeline(
|
||||
timelineSpecification,
|
||||
'2021-12-31'
|
||||
);
|
||||
|
||||
expect(timeline).toEqual([
|
||||
{
|
||||
date: '2019-01-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2019-02-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2019-03-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2019-04-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2019-05-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2019-06-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2019-07-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2019-08-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2019-09-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2019-10-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2019-11-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2019-12-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2020-01-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2020-02-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2020-03-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2020-04-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2020-05-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2020-06-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2020-07-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2020-08-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2020-09-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2020-10-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2020-11-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2020-12-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-01-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-02-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-03-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-04-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-05-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-06-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-07-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-08-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-09-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-10-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-11-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it('with yearly and monthly mixed', async () => {
|
||||
const portfolioCalculator = new PortfolioCalculator(
|
||||
currentRateService,
|
||||
Currency.USD
|
||||
);
|
||||
portfolioCalculator.setTransactionPoints(ordersVTITransactionPoints);
|
||||
const timelineSpecification: TimelineSpecification[] = [
|
||||
{
|
||||
start: '2019-01-01',
|
||||
accuracy: 'year'
|
||||
},
|
||||
{
|
||||
start: '2021-01-01',
|
||||
accuracy: 'month'
|
||||
}
|
||||
];
|
||||
const timeline: TimelinePeriod[] =
|
||||
await portfolioCalculator.calculateTimeline(
|
||||
timelineSpecification,
|
||||
'2021-12-31'
|
||||
);
|
||||
|
||||
expect(timeline).toEqual([
|
||||
{
|
||||
date: '2019-01-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2020-01-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-01-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-02-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-03-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-04-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-05-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-06-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-07-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-08-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-09-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-10-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-11-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it('with all mixed', async () => {
|
||||
const portfolioCalculator = new PortfolioCalculator(
|
||||
currentRateService,
|
||||
Currency.USD
|
||||
);
|
||||
portfolioCalculator.setTransactionPoints(ordersVTITransactionPoints);
|
||||
const timelineSpecification: TimelineSpecification[] = [
|
||||
{
|
||||
start: '2019-01-01',
|
||||
accuracy: 'year'
|
||||
},
|
||||
{
|
||||
start: '2021-01-01',
|
||||
accuracy: 'month'
|
||||
},
|
||||
{
|
||||
start: '2021-12-01',
|
||||
accuracy: 'day'
|
||||
}
|
||||
];
|
||||
const timeline: TimelinePeriod[] =
|
||||
await portfolioCalculator.calculateTimeline(
|
||||
timelineSpecification,
|
||||
'2021-12-31'
|
||||
);
|
||||
|
||||
expect(timeline).toEqual([
|
||||
{
|
||||
date: '2019-01-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2020-01-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-01-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-02-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-03-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-04-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-05-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-06-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-07-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-08-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-09-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-10-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-11-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-01',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-02',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-03',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-04',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-05',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-06',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-07',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-08',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-09',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-10',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-11',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-12',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-13',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-14',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-15',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-16',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-17',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-18',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-19',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-20',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-21',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-22',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-23',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-24',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-25',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-26',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-27',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-28',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-29',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-30',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
date: '2021-12-31',
|
||||
grossPerformance: 0,
|
||||
investment: 0,
|
||||
value: 0
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
const ordersMixedSymbols: PortfolioOrder[] = [
|
||||
{
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user