make it pretty
This commit is contained in:
parent
7538133d09
commit
88f0cb095d
@ -1,5 +1,11 @@
|
|||||||
import { PortfolioCalculator, PortfolioOrder } from '@ghostfolio/api/app/core/portfolio-calculator';
|
import {
|
||||||
import { CurrentRateService, GetValueParams } from '@ghostfolio/api/app/core/current-rate.service';
|
PortfolioCalculator,
|
||||||
|
PortfolioOrder
|
||||||
|
} from '@ghostfolio/api/app/core/portfolio-calculator';
|
||||||
|
import {
|
||||||
|
CurrentRateService,
|
||||||
|
GetValueParams
|
||||||
|
} from '@ghostfolio/api/app/core/current-rate.service';
|
||||||
import { Currency } from '@prisma/client';
|
import { Currency } from '@prisma/client';
|
||||||
import { OrderType } from '@ghostfolio/api/models/order-type';
|
import { OrderType } from '@ghostfolio/api/models/order-type';
|
||||||
import Big from 'big.js';
|
import Big from 'big.js';
|
||||||
@ -16,9 +22,11 @@ function dateEqual(date1: Date, date2: Date) {
|
|||||||
const date1Converted = toYearMonthDay(date1);
|
const date1Converted = toYearMonthDay(date1);
|
||||||
const date2Converted = toYearMonthDay(date2);
|
const date2Converted = toYearMonthDay(date2);
|
||||||
|
|
||||||
return date1Converted[0] === date2Converted[0]&&
|
return (
|
||||||
|
date1Converted[0] === date2Converted[0] &&
|
||||||
date1Converted[1] === date2Converted[1] &&
|
date1Converted[1] === date2Converted[1] &&
|
||||||
date1Converted[2] === date2Converted[2]
|
date1Converted[2] === date2Converted[2]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
jest.mock('./current-rate.service.ts', () => {
|
jest.mock('./current-rate.service.ts', () => {
|
||||||
@ -26,13 +34,17 @@ jest.mock('./current-rate.service.ts', () => {
|
|||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
CurrentRateService: jest.fn().mockImplementation(() => {
|
CurrentRateService: jest.fn().mockImplementation(() => {
|
||||||
return {
|
return {
|
||||||
getValue: ({date, symbol, currency, userCurrency}: GetValueParams) => {
|
getValue: ({
|
||||||
|
date,
|
||||||
|
symbol,
|
||||||
|
currency,
|
||||||
|
userCurrency
|
||||||
|
}: GetValueParams) => {
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
if (dateEqual(today, date) && symbol === 'VTI') {
|
if (dateEqual(today, date) && symbol === 'VTI') {
|
||||||
return Promise.resolve(new Big('213.32'));
|
return Promise.resolve(new Big('213.32'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return Promise.resolve(new Big('0'));
|
return Promise.resolve(new Big('0'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -41,7 +53,6 @@ jest.mock('./current-rate.service.ts', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('PortfolioCalculator', () => {
|
describe('PortfolioCalculator', () => {
|
||||||
|
|
||||||
let currentRateService: CurrentRateService;
|
let currentRateService: CurrentRateService;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
currentRateService = new CurrentRateService(null, null);
|
currentRateService = new CurrentRateService(null, null);
|
||||||
@ -49,11 +60,17 @@ describe('PortfolioCalculator', () => {
|
|||||||
|
|
||||||
describe('calculate transaction points', () => {
|
describe('calculate transaction points', () => {
|
||||||
it('with orders of only one symbol', () => {
|
it('with orders of only one symbol', () => {
|
||||||
const portfolioCalculator = new PortfolioCalculator(currentRateService, Currency.USD);
|
const portfolioCalculator = new PortfolioCalculator(
|
||||||
|
currentRateService,
|
||||||
|
Currency.USD
|
||||||
|
);
|
||||||
portfolioCalculator.computeTransactionPoints(ordersVTI);
|
portfolioCalculator.computeTransactionPoints(ordersVTI);
|
||||||
const portfolioItemsAtTransactionPoints = portfolioCalculator.getTransactionPoints();
|
const portfolioItemsAtTransactionPoints =
|
||||||
|
portfolioCalculator.getTransactionPoints();
|
||||||
|
|
||||||
expect(portfolioItemsAtTransactionPoints).toEqual(ordersVTITransactionPoints);
|
expect(portfolioItemsAtTransactionPoints).toEqual(
|
||||||
|
ordersVTITransactionPoints
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('with two orders at the same day of the same type', () => {
|
it('with two orders at the same day of the same type', () => {
|
||||||
@ -68,65 +85,79 @@ describe('PortfolioCalculator', () => {
|
|||||||
currency: Currency.USD
|
currency: Currency.USD
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
const portfolioCalculator = new PortfolioCalculator(currentRateService, Currency.USD);
|
const portfolioCalculator = new PortfolioCalculator(
|
||||||
|
currentRateService,
|
||||||
|
Currency.USD
|
||||||
|
);
|
||||||
portfolioCalculator.computeTransactionPoints(orders);
|
portfolioCalculator.computeTransactionPoints(orders);
|
||||||
const portfolioItemsAtTransactionPoints = portfolioCalculator.getTransactionPoints();
|
const portfolioItemsAtTransactionPoints =
|
||||||
|
portfolioCalculator.getTransactionPoints();
|
||||||
|
|
||||||
expect(portfolioItemsAtTransactionPoints).toEqual([
|
expect(portfolioItemsAtTransactionPoints).toEqual([
|
||||||
{
|
{
|
||||||
date: '2019-02-01',
|
date: '2019-02-01',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('10'),
|
{
|
||||||
symbol: 'VTI',
|
quantity: new Big('10'),
|
||||||
investment: new Big('1443.8'),
|
symbol: 'VTI',
|
||||||
currency: Currency.USD,
|
investment: new Big('1443.8'),
|
||||||
firstBuyDate: '2019-02-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 1
|
firstBuyDate: '2019-02-01',
|
||||||
}]
|
transactionCount: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
date: '2019-08-03',
|
date: '2019-08-03',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('20'),
|
{
|
||||||
symbol: 'VTI',
|
quantity: new Big('20'),
|
||||||
investment: new Big('2923.7'),
|
symbol: 'VTI',
|
||||||
currency: Currency.USD,
|
investment: new Big('2923.7'),
|
||||||
firstBuyDate: '2019-02-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 2
|
firstBuyDate: '2019-02-01',
|
||||||
}]
|
transactionCount: 2
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
date: '2020-02-02',
|
date: '2020-02-02',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('5'),
|
{
|
||||||
symbol: 'VTI',
|
quantity: new Big('5'),
|
||||||
investment: new Big('652.55'),
|
symbol: 'VTI',
|
||||||
currency: Currency.USD,
|
investment: new Big('652.55'),
|
||||||
firstBuyDate: '2019-02-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 3
|
firstBuyDate: '2019-02-01',
|
||||||
}]
|
transactionCount: 3
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
date: '2021-02-01',
|
date: '2021-02-01',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('35'),
|
{
|
||||||
symbol: 'VTI',
|
quantity: new Big('35'),
|
||||||
investment: new Big('6627.05'),
|
symbol: 'VTI',
|
||||||
currency: Currency.USD,
|
investment: new Big('6627.05'),
|
||||||
firstBuyDate: '2019-02-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 5
|
firstBuyDate: '2019-02-01',
|
||||||
}]
|
transactionCount: 5
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
date: '2021-08-01',
|
date: '2021-08-01',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('45'),
|
{
|
||||||
symbol: 'VTI',
|
quantity: new Big('45'),
|
||||||
investment: new Big('8403.95'),
|
symbol: 'VTI',
|
||||||
currency: Currency.USD,
|
investment: new Big('8403.95'),
|
||||||
firstBuyDate: '2019-02-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 6
|
firstBuyDate: '2019-02-01',
|
||||||
}]
|
transactionCount: 6
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
@ -143,104 +174,124 @@ describe('PortfolioCalculator', () => {
|
|||||||
currency: Currency.USD
|
currency: Currency.USD
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
const portfolioCalculator = new PortfolioCalculator(currentRateService, Currency.USD);
|
const portfolioCalculator = new PortfolioCalculator(
|
||||||
|
currentRateService,
|
||||||
|
Currency.USD
|
||||||
|
);
|
||||||
portfolioCalculator.computeTransactionPoints(orders);
|
portfolioCalculator.computeTransactionPoints(orders);
|
||||||
const portfolioItemsAtTransactionPoints = portfolioCalculator.getTransactionPoints();
|
const portfolioItemsAtTransactionPoints =
|
||||||
|
portfolioCalculator.getTransactionPoints();
|
||||||
|
|
||||||
expect(portfolioItemsAtTransactionPoints).toEqual([
|
expect(portfolioItemsAtTransactionPoints).toEqual([
|
||||||
{
|
{
|
||||||
date: '2019-02-01',
|
date: '2019-02-01',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('10'),
|
{
|
||||||
symbol: 'VTI',
|
quantity: new Big('10'),
|
||||||
investment: new Big('1443.8'),
|
symbol: 'VTI',
|
||||||
currency: Currency.USD,
|
investment: new Big('1443.8'),
|
||||||
firstBuyDate: '2019-02-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 1
|
firstBuyDate: '2019-02-01',
|
||||||
}]
|
transactionCount: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
date: '2019-08-03',
|
date: '2019-08-03',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('20'),
|
{
|
||||||
symbol: 'VTI',
|
quantity: new Big('20'),
|
||||||
investment: new Big('2923.7'),
|
symbol: 'VTI',
|
||||||
currency: Currency.USD,
|
investment: new Big('2923.7'),
|
||||||
firstBuyDate: '2019-02-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 2
|
firstBuyDate: '2019-02-01',
|
||||||
}]
|
transactionCount: 2
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
date: '2019-09-01',
|
date: '2019-09-01',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('5'),
|
{
|
||||||
symbol: 'AMZN',
|
quantity: new Big('5'),
|
||||||
investment: new Big('10109.95'),
|
symbol: 'AMZN',
|
||||||
currency: Currency.USD,
|
investment: new Big('10109.95'),
|
||||||
firstBuyDate: '2019-09-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 1
|
firstBuyDate: '2019-09-01',
|
||||||
}, {
|
transactionCount: 1
|
||||||
quantity: new Big('20'),
|
},
|
||||||
symbol: 'VTI',
|
{
|
||||||
investment: new Big('2923.7'),
|
quantity: new Big('20'),
|
||||||
currency: Currency.USD,
|
symbol: 'VTI',
|
||||||
firstBuyDate: '2019-02-01',
|
investment: new Big('2923.7'),
|
||||||
transactionCount: 2
|
currency: Currency.USD,
|
||||||
}]
|
firstBuyDate: '2019-02-01',
|
||||||
|
transactionCount: 2
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
date: '2020-02-02',
|
date: '2020-02-02',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('5'),
|
{
|
||||||
symbol: 'AMZN',
|
quantity: new Big('5'),
|
||||||
investment: new Big('10109.95'),
|
symbol: 'AMZN',
|
||||||
currency: Currency.USD,
|
investment: new Big('10109.95'),
|
||||||
firstBuyDate: '2019-09-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 1
|
firstBuyDate: '2019-09-01',
|
||||||
}, {
|
transactionCount: 1
|
||||||
quantity: new Big('5'),
|
},
|
||||||
symbol: 'VTI',
|
{
|
||||||
investment: new Big('652.55'),
|
quantity: new Big('5'),
|
||||||
currency: Currency.USD,
|
symbol: 'VTI',
|
||||||
firstBuyDate: '2019-02-01',
|
investment: new Big('652.55'),
|
||||||
transactionCount: 3
|
currency: Currency.USD,
|
||||||
}]
|
firstBuyDate: '2019-02-01',
|
||||||
|
transactionCount: 3
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
date: '2021-02-01',
|
date: '2021-02-01',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('5'),
|
{
|
||||||
symbol: 'AMZN',
|
quantity: new Big('5'),
|
||||||
investment: new Big('10109.95'),
|
symbol: 'AMZN',
|
||||||
currency: Currency.USD,
|
investment: new Big('10109.95'),
|
||||||
firstBuyDate: '2019-09-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 1
|
firstBuyDate: '2019-09-01',
|
||||||
}, {
|
transactionCount: 1
|
||||||
quantity: new Big('15'),
|
},
|
||||||
symbol: 'VTI',
|
{
|
||||||
investment: new Big('2684.05'),
|
quantity: new Big('15'),
|
||||||
currency: Currency.USD,
|
symbol: 'VTI',
|
||||||
firstBuyDate: '2019-02-01',
|
investment: new Big('2684.05'),
|
||||||
transactionCount: 4
|
currency: Currency.USD,
|
||||||
}]
|
firstBuyDate: '2019-02-01',
|
||||||
|
transactionCount: 4
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
date: '2021-08-01',
|
date: '2021-08-01',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('5'),
|
{
|
||||||
symbol: 'AMZN',
|
quantity: new Big('5'),
|
||||||
investment: new Big('10109.95'),
|
symbol: 'AMZN',
|
||||||
currency: Currency.USD,
|
investment: new Big('10109.95'),
|
||||||
firstBuyDate: '2019-09-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 1
|
firstBuyDate: '2019-09-01',
|
||||||
}, {
|
transactionCount: 1
|
||||||
quantity: new Big('25'),
|
},
|
||||||
symbol: 'VTI',
|
{
|
||||||
investment: new Big('4460.95'),
|
quantity: new Big('25'),
|
||||||
currency: Currency.USD,
|
symbol: 'VTI',
|
||||||
firstBuyDate: '2019-02-01',
|
investment: new Big('4460.95'),
|
||||||
transactionCount: 5
|
currency: Currency.USD,
|
||||||
}]
|
firstBuyDate: '2019-02-01',
|
||||||
|
transactionCount: 5
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
@ -265,173 +316,208 @@ describe('PortfolioCalculator', () => {
|
|||||||
currency: Currency.USD
|
currency: Currency.USD
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
const portfolioCalculator = new PortfolioCalculator(currentRateService, Currency.USD);
|
const portfolioCalculator = new PortfolioCalculator(
|
||||||
|
currentRateService,
|
||||||
|
Currency.USD
|
||||||
|
);
|
||||||
portfolioCalculator.computeTransactionPoints(orders);
|
portfolioCalculator.computeTransactionPoints(orders);
|
||||||
const portfolioItemsAtTransactionPoints = portfolioCalculator.getTransactionPoints();
|
const portfolioItemsAtTransactionPoints =
|
||||||
|
portfolioCalculator.getTransactionPoints();
|
||||||
|
|
||||||
expect(portfolioItemsAtTransactionPoints).toEqual([
|
expect(portfolioItemsAtTransactionPoints).toEqual([
|
||||||
{
|
{
|
||||||
date: '2019-02-01',
|
date: '2019-02-01',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('10'),
|
{
|
||||||
symbol: 'VTI',
|
quantity: new Big('10'),
|
||||||
investment: new Big('1443.8'),
|
symbol: 'VTI',
|
||||||
currency: Currency.USD,
|
investment: new Big('1443.8'),
|
||||||
firstBuyDate: '2019-02-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 1
|
firstBuyDate: '2019-02-01',
|
||||||
}]
|
transactionCount: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
date: '2019-08-03',
|
date: '2019-08-03',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('20'),
|
{
|
||||||
symbol: 'VTI',
|
quantity: new Big('20'),
|
||||||
investment: new Big('2923.7'),
|
symbol: 'VTI',
|
||||||
currency: Currency.USD,
|
investment: new Big('2923.7'),
|
||||||
firstBuyDate: '2019-02-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 2
|
firstBuyDate: '2019-02-01',
|
||||||
}]
|
transactionCount: 2
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
date: '2019-09-01',
|
date: '2019-09-01',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('5'),
|
{
|
||||||
symbol: 'AMZN',
|
quantity: new Big('5'),
|
||||||
investment: new Big('10109.95'),
|
symbol: 'AMZN',
|
||||||
currency: Currency.USD,
|
investment: new Big('10109.95'),
|
||||||
firstBuyDate: '2019-09-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 1
|
firstBuyDate: '2019-09-01',
|
||||||
}, {
|
transactionCount: 1
|
||||||
quantity: new Big('20'),
|
},
|
||||||
symbol: 'VTI',
|
{
|
||||||
investment: new Big('2923.7'),
|
quantity: new Big('20'),
|
||||||
currency: Currency.USD,
|
symbol: 'VTI',
|
||||||
firstBuyDate: '2019-02-01',
|
investment: new Big('2923.7'),
|
||||||
transactionCount: 2
|
currency: Currency.USD,
|
||||||
}]
|
firstBuyDate: '2019-02-01',
|
||||||
|
transactionCount: 2
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
date: '2020-02-02',
|
date: '2020-02-02',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('5'),
|
{
|
||||||
symbol: 'AMZN',
|
quantity: new Big('5'),
|
||||||
investment: new Big('10109.95'),
|
symbol: 'AMZN',
|
||||||
currency: Currency.USD,
|
investment: new Big('10109.95'),
|
||||||
firstBuyDate: '2019-09-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 1
|
firstBuyDate: '2019-09-01',
|
||||||
}, {
|
transactionCount: 1
|
||||||
quantity: new Big('5'),
|
},
|
||||||
symbol: 'VTI',
|
{
|
||||||
investment: new Big('652.55'),
|
quantity: new Big('5'),
|
||||||
currency: Currency.USD,
|
symbol: 'VTI',
|
||||||
firstBuyDate: '2019-02-01',
|
investment: new Big('652.55'),
|
||||||
transactionCount: 3
|
currency: Currency.USD,
|
||||||
}]
|
firstBuyDate: '2019-02-01',
|
||||||
|
transactionCount: 3
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
date: '2020-08-02',
|
date: '2020-08-02',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('5'),
|
{
|
||||||
symbol: 'VTI',
|
quantity: new Big('5'),
|
||||||
investment: new Big('652.55'),
|
symbol: 'VTI',
|
||||||
currency: Currency.USD,
|
investment: new Big('652.55'),
|
||||||
firstBuyDate: '2019-02-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 3
|
firstBuyDate: '2019-02-01',
|
||||||
}]
|
transactionCount: 3
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
date: '2021-02-01',
|
date: '2021-02-01',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('15'),
|
{
|
||||||
symbol: 'VTI',
|
quantity: new Big('15'),
|
||||||
investment: new Big('2684.05'),
|
symbol: 'VTI',
|
||||||
currency: Currency.USD,
|
investment: new Big('2684.05'),
|
||||||
firstBuyDate: '2019-02-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 4
|
firstBuyDate: '2019-02-01',
|
||||||
}]
|
transactionCount: 4
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
date: '2021-08-01',
|
date: '2021-08-01',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('25'),
|
{
|
||||||
symbol: 'VTI',
|
quantity: new Big('25'),
|
||||||
investment: new Big('4460.95'),
|
symbol: 'VTI',
|
||||||
currency: Currency.USD,
|
investment: new Big('4460.95'),
|
||||||
firstBuyDate: '2019-02-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 5
|
firstBuyDate: '2019-02-01',
|
||||||
}]
|
transactionCount: 5
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('with mixed symbols', () => {
|
it('with mixed symbols', () => {
|
||||||
const portfolioCalculator = new PortfolioCalculator(currentRateService, Currency.USD);
|
const portfolioCalculator = new PortfolioCalculator(
|
||||||
|
currentRateService,
|
||||||
|
Currency.USD
|
||||||
|
);
|
||||||
portfolioCalculator.computeTransactionPoints(ordersMixedSymbols);
|
portfolioCalculator.computeTransactionPoints(ordersMixedSymbols);
|
||||||
const portfolioItemsAtTransactionPoints = portfolioCalculator.getTransactionPoints();
|
const portfolioItemsAtTransactionPoints =
|
||||||
|
portfolioCalculator.getTransactionPoints();
|
||||||
|
|
||||||
expect(portfolioItemsAtTransactionPoints).toEqual([
|
expect(portfolioItemsAtTransactionPoints).toEqual([
|
||||||
{
|
{
|
||||||
date: '2017-01-03',
|
date: '2017-01-03',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('50'),
|
{
|
||||||
symbol: 'TSLA',
|
quantity: new Big('50'),
|
||||||
investment: new Big('2148.5'),
|
symbol: 'TSLA',
|
||||||
currency: Currency.USD,
|
investment: new Big('2148.5'),
|
||||||
firstBuyDate: '2017-01-03',
|
currency: Currency.USD,
|
||||||
transactionCount: 1
|
firstBuyDate: '2017-01-03',
|
||||||
}]
|
transactionCount: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
date: '2017-07-01',
|
date: '2017-07-01',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('0.5614682'),
|
{
|
||||||
symbol: 'BTCUSD',
|
quantity: new Big('0.5614682'),
|
||||||
investment: new Big('1999.9999999999998659756'),
|
symbol: 'BTCUSD',
|
||||||
currency: Currency.USD,
|
investment: new Big('1999.9999999999998659756'),
|
||||||
firstBuyDate: '2017-07-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 1
|
firstBuyDate: '2017-07-01',
|
||||||
}, {
|
transactionCount: 1
|
||||||
quantity: new Big('50'),
|
},
|
||||||
symbol: 'TSLA',
|
{
|
||||||
investment: new Big('2148.5'),
|
quantity: new Big('50'),
|
||||||
currency: Currency.USD,
|
symbol: 'TSLA',
|
||||||
firstBuyDate: '2017-01-03',
|
investment: new Big('2148.5'),
|
||||||
transactionCount: 1
|
currency: Currency.USD,
|
||||||
}]
|
firstBuyDate: '2017-01-03',
|
||||||
|
transactionCount: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
date: '2018-09-01',
|
date: '2018-09-01',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('5'),
|
{
|
||||||
symbol: 'AMZN',
|
quantity: new Big('5'),
|
||||||
investment: new Big('10109.95'),
|
symbol: 'AMZN',
|
||||||
currency: Currency.USD,
|
investment: new Big('10109.95'),
|
||||||
firstBuyDate: '2018-09-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 1
|
firstBuyDate: '2018-09-01',
|
||||||
}, {
|
transactionCount: 1
|
||||||
quantity: new Big('0.5614682'),
|
},
|
||||||
symbol: 'BTCUSD',
|
{
|
||||||
investment: new Big('1999.9999999999998659756'),
|
quantity: new Big('0.5614682'),
|
||||||
currency: Currency.USD,
|
symbol: 'BTCUSD',
|
||||||
firstBuyDate: '2017-07-01',
|
investment: new Big('1999.9999999999998659756'),
|
||||||
transactionCount: 1
|
currency: Currency.USD,
|
||||||
}, {
|
firstBuyDate: '2017-07-01',
|
||||||
quantity: new Big('50'),
|
transactionCount: 1
|
||||||
symbol: 'TSLA',
|
},
|
||||||
investment: new Big('2148.5'),
|
{
|
||||||
currency: Currency.USD,
|
quantity: new Big('50'),
|
||||||
firstBuyDate: '2017-01-03',
|
symbol: 'TSLA',
|
||||||
transactionCount: 1
|
investment: new Big('2148.5'),
|
||||||
}]
|
currency: Currency.USD,
|
||||||
|
firstBuyDate: '2017-01-03',
|
||||||
|
transactionCount: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('get current positions', () => {
|
describe('get current positions', () => {
|
||||||
|
|
||||||
it('with just VTI', async () => {
|
it('with just VTI', async () => {
|
||||||
const portfolioCalculator = new PortfolioCalculator(currentRateService, Currency.USD);
|
const portfolioCalculator = new PortfolioCalculator(
|
||||||
|
currentRateService,
|
||||||
|
Currency.USD
|
||||||
|
);
|
||||||
portfolioCalculator.setTransactionPoints(ordersVTITransactionPoints);
|
portfolioCalculator.setTransactionPoints(ordersVTITransactionPoints);
|
||||||
const currentPositions = await portfolioCalculator.getCurrentPositions();
|
const currentPositions = await portfolioCalculator.getCurrentPositions();
|
||||||
|
|
||||||
@ -447,12 +533,8 @@ describe('PortfolioCalculator', () => {
|
|||||||
transactionCount: 5
|
transactionCount: 5
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
});
|
});
|
||||||
const ordersMixedSymbols: PortfolioOrder[] = [
|
const ordersMixedSymbols: PortfolioOrder[] = [
|
||||||
{
|
{
|
||||||
@ -527,57 +609,67 @@ const ordersVTI: PortfolioOrder[] = [
|
|||||||
const ordersVTITransactionPoints = [
|
const ordersVTITransactionPoints = [
|
||||||
{
|
{
|
||||||
date: '2019-02-01',
|
date: '2019-02-01',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('10'),
|
{
|
||||||
symbol: 'VTI',
|
quantity: new Big('10'),
|
||||||
investment: new Big('1443.8'),
|
symbol: 'VTI',
|
||||||
currency: Currency.USD,
|
investment: new Big('1443.8'),
|
||||||
firstBuyDate: '2019-02-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 1
|
firstBuyDate: '2019-02-01',
|
||||||
}]
|
transactionCount: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
date: '2019-08-03',
|
date: '2019-08-03',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('20'),
|
{
|
||||||
symbol: 'VTI',
|
quantity: new Big('20'),
|
||||||
investment: new Big('2923.7'),
|
symbol: 'VTI',
|
||||||
currency: Currency.USD,
|
investment: new Big('2923.7'),
|
||||||
firstBuyDate: '2019-02-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 2
|
firstBuyDate: '2019-02-01',
|
||||||
}]
|
transactionCount: 2
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
date: '2020-02-02',
|
date: '2020-02-02',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('5'),
|
{
|
||||||
symbol: 'VTI',
|
quantity: new Big('5'),
|
||||||
investment: new Big('652.55'),
|
symbol: 'VTI',
|
||||||
currency: Currency.USD,
|
investment: new Big('652.55'),
|
||||||
firstBuyDate: '2019-02-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 3
|
firstBuyDate: '2019-02-01',
|
||||||
}]
|
transactionCount: 3
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
date: '2021-02-01',
|
date: '2021-02-01',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('15'),
|
{
|
||||||
symbol: 'VTI',
|
quantity: new Big('15'),
|
||||||
investment: new Big('2684.05'),
|
symbol: 'VTI',
|
||||||
currency: Currency.USD,
|
investment: new Big('2684.05'),
|
||||||
firstBuyDate: '2019-02-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 4
|
firstBuyDate: '2019-02-01',
|
||||||
}]
|
transactionCount: 4
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
date: '2021-08-01',
|
date: '2021-08-01',
|
||||||
items: [{
|
items: [
|
||||||
quantity: new Big('25'),
|
{
|
||||||
symbol: 'VTI',
|
quantity: new Big('25'),
|
||||||
investment: new Big('4460.95'),
|
symbol: 'VTI',
|
||||||
currency: Currency.USD,
|
investment: new Big('4460.95'),
|
||||||
firstBuyDate: '2019-02-01',
|
currency: Currency.USD,
|
||||||
transactionCount: 5
|
firstBuyDate: '2019-02-01',
|
||||||
}]
|
transactionCount: 5
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@ -4,22 +4,16 @@ import { OrderType } from '@ghostfolio/api/models/order-type';
|
|||||||
import Big from 'big.js';
|
import Big from 'big.js';
|
||||||
|
|
||||||
export class PortfolioCalculator {
|
export class PortfolioCalculator {
|
||||||
|
|
||||||
private transactionPoints: TransactionPoint[];
|
private transactionPoints: TransactionPoint[];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private currentRateService: CurrentRateService,
|
private currentRateService: CurrentRateService,
|
||||||
private currency: Currency
|
private currency: Currency
|
||||||
) {
|
) {}
|
||||||
}
|
|
||||||
|
|
||||||
addOrder(order: PortfolioOrder): void {
|
addOrder(order: PortfolioOrder): void {}
|
||||||
|
|
||||||
}
|
deleteOrder(order: PortfolioOrder): void {}
|
||||||
|
|
||||||
deleteOrder(order: PortfolioOrder): void {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
computeTransactionPoints(orders: PortfolioOrder[]) {
|
computeTransactionPoints(orders: PortfolioOrder[]) {
|
||||||
orders.sort((a, b) => a.date.localeCompare(b.date));
|
orders.sort((a, b) => a.date.localeCompare(b.date));
|
||||||
@ -39,9 +33,14 @@ export class PortfolioCalculator {
|
|||||||
const unitPrice = new Big(order.unitPrice);
|
const unitPrice = new Big(order.unitPrice);
|
||||||
if (oldAccumulatedSymbol) {
|
if (oldAccumulatedSymbol) {
|
||||||
currentTransactionPointItem = {
|
currentTransactionPointItem = {
|
||||||
quantity: order.quantity.mul(factor).plus(oldAccumulatedSymbol.quantity),
|
quantity: order.quantity
|
||||||
|
.mul(factor)
|
||||||
|
.plus(oldAccumulatedSymbol.quantity),
|
||||||
symbol: order.symbol,
|
symbol: order.symbol,
|
||||||
investment: unitPrice.mul(order.quantity).mul(factor).add(oldAccumulatedSymbol.investment),
|
investment: unitPrice
|
||||||
|
.mul(order.quantity)
|
||||||
|
.mul(factor)
|
||||||
|
.add(oldAccumulatedSymbol.investment),
|
||||||
currency: order.currency,
|
currency: order.currency,
|
||||||
firstBuyDate: oldAccumulatedSymbol.firstBuyDate,
|
firstBuyDate: oldAccumulatedSymbol.firstBuyDate,
|
||||||
transactionCount: oldAccumulatedSymbol.transactionCount + 1
|
transactionCount: oldAccumulatedSymbol.transactionCount + 1
|
||||||
@ -60,7 +59,9 @@ export class PortfolioCalculator {
|
|||||||
symbols[order.symbol] = currentTransactionPointItem;
|
symbols[order.symbol] = currentTransactionPointItem;
|
||||||
|
|
||||||
const items = lastTransactionPoint?.items ?? [];
|
const items = lastTransactionPoint?.items ?? [];
|
||||||
const newItems = items.filter(transactionPointItem => transactionPointItem.symbol !== order.symbol);
|
const newItems = items.filter(
|
||||||
|
(transactionPointItem) => transactionPointItem.symbol !== order.symbol
|
||||||
|
);
|
||||||
if (!currentTransactionPointItem.quantity.eq(0)) {
|
if (!currentTransactionPointItem.quantity.eq(0)) {
|
||||||
newItems.push(currentTransactionPointItem);
|
newItems.push(currentTransactionPointItem);
|
||||||
} else {
|
} else {
|
||||||
@ -93,7 +94,8 @@ export class PortfolioCalculator {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const lastTransactionPoint = this.transactionPoints[this.transactionPoints.length - 1];
|
const lastTransactionPoint =
|
||||||
|
this.transactionPoints[this.transactionPoints.length - 1];
|
||||||
|
|
||||||
const result: { [symbol: string]: TimelinePosition } = {};
|
const result: { [symbol: string]: TimelinePosition } = {};
|
||||||
for (const item of lastTransactionPoint.items) {
|
for (const item of lastTransactionPoint.items) {
|
||||||
@ -117,7 +119,10 @@ export class PortfolioCalculator {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateTimeline(timelineSpecification: TimelineSpecification[], endDate: Date): TimelinePeriod[] {
|
calculateTimeline(
|
||||||
|
timelineSpecification: TimelineSpecification[],
|
||||||
|
endDate: Date
|
||||||
|
): TimelinePeriod[] {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,7 +141,6 @@ export class PortfolioCalculator {
|
|||||||
}
|
}
|
||||||
return factor;
|
return factor;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TransactionPoint {
|
interface TransactionPoint {
|
||||||
@ -167,7 +171,7 @@ type Accuracy = 'year' | 'month' | 'day';
|
|||||||
|
|
||||||
interface TimelineSpecification {
|
interface TimelineSpecification {
|
||||||
start: Date;
|
start: Date;
|
||||||
accuracy: Accuracy
|
accuracy: Accuracy;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TimelinePeriod {
|
interface TimelinePeriod {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user