2021-07-01 23:11:41 +02:00
|
|
|
import { OrderType } from '@ghostfolio/api/models/order-type';
|
2021-07-31 18:17:50 +02:00
|
|
|
import { parseDate, resetHours } from '@ghostfolio/common/helper';
|
2021-07-09 21:35:54 +02:00
|
|
|
import { Currency } from '@prisma/client';
|
2021-07-01 23:11:41 +02:00
|
|
|
import Big from 'big.js';
|
2021-07-20 22:17:37 +02:00
|
|
|
import {
|
|
|
|
addDays,
|
|
|
|
differenceInCalendarDays,
|
|
|
|
endOfDay,
|
|
|
|
isBefore,
|
2021-07-31 18:17:50 +02:00
|
|
|
isSameDay
|
2021-07-20 22:17:37 +02:00
|
|
|
} from 'date-fns';
|
2021-07-01 23:11:41 +02:00
|
|
|
|
2021-08-14 16:55:40 +02:00
|
|
|
import { CurrentRateService } from './current-rate.service';
|
|
|
|
import { GetValueParams } from './interfaces/get-value-params.interface';
|
|
|
|
import { GetValuesParams } from './interfaces/get-values-params.interface';
|
|
|
|
import { PortfolioOrder } from './interfaces/portfolio-order.interface';
|
|
|
|
import { TimelinePeriod } from './interfaces/timeline-period.interface';
|
|
|
|
import { TimelineSpecification } from './interfaces/timeline-specification.interface';
|
|
|
|
import { TransactionPoint } from './interfaces/transaction-point.interface';
|
|
|
|
import { PortfolioCalculator } from './portfolio-calculator';
|
|
|
|
|
2021-07-20 22:17:37 +02:00
|
|
|
function mockGetValue(symbol: string, date: Date) {
|
2021-08-01 00:29:46 +02:00
|
|
|
switch (symbol) {
|
|
|
|
case 'AMZN':
|
|
|
|
return { marketPrice: 2021.99 };
|
|
|
|
case 'MFA':
|
|
|
|
if (isSameDay(parseDate('2010-12-31'), date)) {
|
|
|
|
return { marketPrice: 1 };
|
|
|
|
} else if (isSameDay(parseDate('2011-08-15'), date)) {
|
|
|
|
return { marketPrice: 1.162484 }; // 1162484 / 1000000
|
|
|
|
} else if (isSameDay(parseDate('2011-12-31'), date)) {
|
|
|
|
return { marketPrice: 1.097884981 }; // 1192328 / 1086022.689344541
|
|
|
|
}
|
2021-07-20 22:17:37 +02:00
|
|
|
|
2021-08-01 00:29:46 +02:00
|
|
|
return { marketPrice: 0 };
|
|
|
|
case 'SPA':
|
|
|
|
if (isSameDay(parseDate('2013-12-31'), date)) {
|
|
|
|
return { marketPrice: 1.025 }; // 205 / 200
|
|
|
|
}
|
2021-07-31 09:10:27 +02:00
|
|
|
|
2021-08-01 00:29:46 +02:00
|
|
|
return { marketPrice: 0 };
|
|
|
|
case 'SPB':
|
|
|
|
if (isSameDay(parseDate('2013-12-31'), date)) {
|
|
|
|
return { marketPrice: 1.04 }; // 312 / 300
|
|
|
|
}
|
2021-07-31 18:17:50 +02:00
|
|
|
|
2021-08-01 00:29:46 +02:00
|
|
|
return { marketPrice: 0 };
|
|
|
|
case 'TSLA':
|
|
|
|
if (isSameDay(parseDate('2021-07-26'), date)) {
|
|
|
|
return { marketPrice: 657.62 };
|
|
|
|
} else if (isSameDay(parseDate('2021-01-02'), date)) {
|
|
|
|
return { marketPrice: 666.66 };
|
|
|
|
}
|
2021-07-31 18:17:50 +02:00
|
|
|
|
2021-08-01 00:29:46 +02:00
|
|
|
return { marketPrice: 0 };
|
|
|
|
case 'VTI':
|
|
|
|
return {
|
|
|
|
marketPrice: new Big('144.38')
|
|
|
|
.plus(
|
|
|
|
new Big('0.08').mul(
|
|
|
|
differenceInCalendarDays(date, parseDate('2019-02-01'))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.toNumber()
|
|
|
|
};
|
|
|
|
default:
|
|
|
|
return { marketPrice: 0 };
|
2021-07-20 22:17:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-14 16:55:40 +02:00
|
|
|
jest.mock('./current-rate.service', () => {
|
2021-07-01 23:11:41 +02:00
|
|
|
return {
|
|
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
|
|
CurrentRateService: jest.fn().mockImplementation(() => {
|
|
|
|
return {
|
2021-07-06 19:40:37 +02:00
|
|
|
getValue: ({
|
2021-07-28 15:54:37 +02:00
|
|
|
currency,
|
2021-07-06 19:40:37 +02:00
|
|
|
date,
|
|
|
|
symbol,
|
|
|
|
userCurrency
|
|
|
|
}: GetValueParams) => {
|
2021-07-20 22:17:37 +02:00
|
|
|
return Promise.resolve(mockGetValue(symbol, date));
|
|
|
|
},
|
|
|
|
getValues: ({
|
|
|
|
currencies,
|
2021-07-26 22:13:09 +02:00
|
|
|
dateQuery,
|
2021-07-20 22:17:37 +02:00
|
|
|
symbols,
|
|
|
|
userCurrency
|
|
|
|
}: GetValuesParams) => {
|
|
|
|
const result = [];
|
2021-07-26 22:13:09 +02:00
|
|
|
if (dateQuery.lt) {
|
|
|
|
for (
|
|
|
|
let date = resetHours(dateQuery.gte);
|
|
|
|
isBefore(date, endOfDay(dateQuery.lt));
|
|
|
|
date = addDays(date, 1)
|
|
|
|
) {
|
|
|
|
for (const symbol of symbols) {
|
|
|
|
result.push({
|
|
|
|
date,
|
|
|
|
symbol,
|
|
|
|
marketPrice: mockGetValue(symbol, date).marketPrice
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (const date of dateQuery.in) {
|
|
|
|
for (const symbol of symbols) {
|
|
|
|
result.push({
|
|
|
|
date,
|
|
|
|
symbol,
|
|
|
|
marketPrice: mockGetValue(symbol, date).marketPrice
|
|
|
|
});
|
|
|
|
}
|
2021-07-06 22:41:56 +02:00
|
|
|
}
|
2021-07-06 19:34:37 +02:00
|
|
|
}
|
2021-07-20 22:17:37 +02:00
|
|
|
return Promise.resolve(result);
|
2021-07-01 23:11:41 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
})
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('PortfolioCalculator', () => {
|
|
|
|
let currentRateService: CurrentRateService;
|
2021-07-08 22:42:19 +02:00
|
|
|
|
2021-07-01 23:11:41 +02:00
|
|
|
beforeEach(() => {
|
2021-07-08 22:42:19 +02:00
|
|
|
currentRateService = new CurrentRateService(null, null, null);
|
2021-07-01 23:11:41 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
describe('calculate transaction points', () => {
|
|
|
|
it('with orders of only one symbol', () => {
|
2021-07-06 19:40:37 +02:00
|
|
|
const portfolioCalculator = new PortfolioCalculator(
|
|
|
|
currentRateService,
|
|
|
|
Currency.USD
|
|
|
|
);
|
2021-07-06 19:34:37 +02:00
|
|
|
portfolioCalculator.computeTransactionPoints(ordersVTI);
|
2021-07-06 19:40:37 +02:00
|
|
|
const portfolioItemsAtTransactionPoints =
|
|
|
|
portfolioCalculator.getTransactionPoints();
|
2021-07-01 23:11:41 +02:00
|
|
|
|
2021-07-06 19:40:37 +02:00
|
|
|
expect(portfolioItemsAtTransactionPoints).toEqual(
|
|
|
|
ordersVTITransactionPoints
|
|
|
|
);
|
2021-07-01 23:11:41 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
it('with two orders at the same day of the same type', () => {
|
|
|
|
const orders = [
|
|
|
|
...ordersVTI,
|
|
|
|
{
|
2021-07-28 15:54:37 +02:00
|
|
|
currency: Currency.USD,
|
2021-07-01 23:11:41 +02:00
|
|
|
date: '2021-02-01',
|
2021-07-25 13:31:44 +02:00
|
|
|
name: 'Vanguard Total Stock Market Index Fund ETF Shares',
|
2021-07-01 23:11:41 +02:00
|
|
|
quantity: new Big('20'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
type: OrderType.Buy,
|
2021-07-28 15:54:37 +02:00
|
|
|
unitPrice: new Big('197.15')
|
2021-07-01 23:11:41 +02:00
|
|
|
}
|
|
|
|
];
|
2021-07-06 19:40:37 +02:00
|
|
|
const portfolioCalculator = new PortfolioCalculator(
|
|
|
|
currentRateService,
|
|
|
|
Currency.USD
|
|
|
|
);
|
2021-07-06 19:34:37 +02:00
|
|
|
portfolioCalculator.computeTransactionPoints(orders);
|
2021-07-06 19:40:37 +02:00
|
|
|
const portfolioItemsAtTransactionPoints =
|
|
|
|
portfolioCalculator.getTransactionPoints();
|
2021-07-01 23:11:41 +02:00
|
|
|
|
|
|
|
expect(portfolioItemsAtTransactionPoints).toEqual([
|
|
|
|
{
|
|
|
|
date: '2019-02-01',
|
2021-07-06 19:40:37 +02:00
|
|
|
items: [
|
|
|
|
{
|
2021-07-28 15:54:37 +02:00
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
investment: new Big('1443.8'),
|
2021-07-06 19:40:37 +02:00
|
|
|
quantity: new Big('10'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
transactionCount: 1
|
|
|
|
}
|
|
|
|
]
|
2021-07-01 23:11:41 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2019-08-03',
|
2021-07-06 19:40:37 +02:00
|
|
|
items: [
|
|
|
|
{
|
2021-07-28 15:54:37 +02:00
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
investment: new Big('2923.7'),
|
2021-07-06 19:40:37 +02:00
|
|
|
quantity: new Big('20'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
transactionCount: 2
|
|
|
|
}
|
|
|
|
]
|
2021-07-01 23:11:41 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2020-02-02',
|
2021-07-06 19:40:37 +02:00
|
|
|
items: [
|
|
|
|
{
|
2021-07-28 15:54:37 +02:00
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
investment: new Big('652.55'),
|
2021-07-06 19:40:37 +02:00
|
|
|
quantity: new Big('5'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
transactionCount: 3
|
|
|
|
}
|
|
|
|
]
|
2021-07-01 23:11:41 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-02-01',
|
2021-07-06 19:40:37 +02:00
|
|
|
items: [
|
|
|
|
{
|
2021-07-28 15:54:37 +02:00
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
investment: new Big('6627.05'),
|
2021-07-06 19:40:37 +02:00
|
|
|
quantity: new Big('35'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
transactionCount: 5
|
|
|
|
}
|
|
|
|
]
|
2021-07-01 23:11:41 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-08-01',
|
2021-07-06 19:40:37 +02:00
|
|
|
items: [
|
|
|
|
{
|
2021-07-28 15:54:37 +02:00
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
investment: new Big('8403.95'),
|
2021-07-06 19:40:37 +02:00
|
|
|
quantity: new Big('45'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
transactionCount: 6
|
|
|
|
}
|
|
|
|
]
|
2021-07-01 23:11:41 +02:00
|
|
|
}
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('with additional order', () => {
|
|
|
|
const orders = [
|
|
|
|
...ordersVTI,
|
|
|
|
{
|
2021-07-28 15:54:37 +02:00
|
|
|
currency: Currency.USD,
|
2021-07-01 23:11:41 +02:00
|
|
|
date: '2019-09-01',
|
2021-07-25 13:31:44 +02:00
|
|
|
name: 'Amazon.com, Inc.',
|
2021-07-01 23:11:41 +02:00
|
|
|
quantity: new Big('5'),
|
|
|
|
symbol: 'AMZN',
|
|
|
|
type: OrderType.Buy,
|
2021-07-28 15:54:37 +02:00
|
|
|
unitPrice: new Big('2021.99')
|
2021-07-01 23:11:41 +02:00
|
|
|
}
|
|
|
|
];
|
2021-07-06 19:40:37 +02:00
|
|
|
const portfolioCalculator = new PortfolioCalculator(
|
|
|
|
currentRateService,
|
|
|
|
Currency.USD
|
|
|
|
);
|
2021-07-06 19:34:37 +02:00
|
|
|
portfolioCalculator.computeTransactionPoints(orders);
|
2021-07-06 19:40:37 +02:00
|
|
|
const portfolioItemsAtTransactionPoints =
|
|
|
|
portfolioCalculator.getTransactionPoints();
|
2021-07-01 23:11:41 +02:00
|
|
|
|
|
|
|
expect(portfolioItemsAtTransactionPoints).toEqual([
|
|
|
|
{
|
|
|
|
date: '2019-02-01',
|
2021-07-06 19:40:37 +02:00
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('10'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('1443.8'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
transactionCount: 1
|
|
|
|
}
|
|
|
|
]
|
2021-07-01 23:11:41 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2019-08-03',
|
2021-07-06 19:40:37 +02:00
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('20'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('2923.7'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
transactionCount: 2
|
|
|
|
}
|
|
|
|
]
|
2021-07-01 23:11:41 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2019-09-01',
|
2021-07-06 19:40:37 +02:00
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('5'),
|
|
|
|
symbol: 'AMZN',
|
|
|
|
investment: new Big('10109.95'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-09-01',
|
|
|
|
transactionCount: 1
|
|
|
|
},
|
|
|
|
{
|
|
|
|
quantity: new Big('20'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('2923.7'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
transactionCount: 2
|
|
|
|
}
|
|
|
|
]
|
2021-07-01 23:11:41 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2020-02-02',
|
2021-07-06 19:40:37 +02:00
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('5'),
|
|
|
|
symbol: 'AMZN',
|
|
|
|
investment: new Big('10109.95'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-09-01',
|
|
|
|
transactionCount: 1
|
|
|
|
},
|
|
|
|
{
|
|
|
|
quantity: new Big('5'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('652.55'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
transactionCount: 3
|
|
|
|
}
|
|
|
|
]
|
2021-07-01 23:11:41 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-02-01',
|
2021-07-06 19:40:37 +02:00
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('5'),
|
|
|
|
symbol: 'AMZN',
|
|
|
|
investment: new Big('10109.95'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-09-01',
|
|
|
|
transactionCount: 1
|
|
|
|
},
|
|
|
|
{
|
|
|
|
quantity: new Big('15'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
transactionCount: 4
|
|
|
|
}
|
|
|
|
]
|
2021-07-01 23:11:41 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-08-01',
|
2021-07-06 19:40:37 +02:00
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('5'),
|
|
|
|
symbol: 'AMZN',
|
|
|
|
investment: new Big('10109.95'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-09-01',
|
|
|
|
transactionCount: 1
|
|
|
|
},
|
|
|
|
{
|
|
|
|
quantity: new Big('25'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('4460.95'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
transactionCount: 5
|
|
|
|
}
|
|
|
|
]
|
2021-07-01 23:11:41 +02:00
|
|
|
}
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('with additional buy & sell', () => {
|
|
|
|
const orders = [
|
|
|
|
...ordersVTI,
|
|
|
|
{
|
|
|
|
date: '2019-09-01',
|
2021-07-25 13:31:44 +02:00
|
|
|
name: 'Amazon.com, Inc.',
|
2021-07-01 23:11:41 +02:00
|
|
|
quantity: new Big('5'),
|
|
|
|
symbol: 'AMZN',
|
|
|
|
type: OrderType.Buy,
|
|
|
|
unitPrice: new Big('2021.99'),
|
|
|
|
currency: Currency.USD
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2020-08-02',
|
2021-07-25 13:31:44 +02:00
|
|
|
name: 'Amazon.com, Inc.',
|
2021-07-01 23:11:41 +02:00
|
|
|
quantity: new Big('5'),
|
|
|
|
symbol: 'AMZN',
|
|
|
|
type: OrderType.Sell,
|
|
|
|
unitPrice: new Big('2412.23'),
|
|
|
|
currency: Currency.USD
|
|
|
|
}
|
|
|
|
];
|
2021-07-06 19:40:37 +02:00
|
|
|
const portfolioCalculator = new PortfolioCalculator(
|
|
|
|
currentRateService,
|
|
|
|
Currency.USD
|
|
|
|
);
|
2021-07-06 19:34:37 +02:00
|
|
|
portfolioCalculator.computeTransactionPoints(orders);
|
2021-07-06 19:40:37 +02:00
|
|
|
const portfolioItemsAtTransactionPoints =
|
|
|
|
portfolioCalculator.getTransactionPoints();
|
2021-07-01 23:11:41 +02:00
|
|
|
|
2021-08-07 07:07:12 +02:00
|
|
|
expect(portfolioItemsAtTransactionPoints).toEqual(
|
|
|
|
transactionPointsBuyAndSell
|
|
|
|
);
|
2021-07-01 23:11:41 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
it('with mixed symbols', () => {
|
2021-07-06 19:40:37 +02:00
|
|
|
const portfolioCalculator = new PortfolioCalculator(
|
|
|
|
currentRateService,
|
|
|
|
Currency.USD
|
|
|
|
);
|
2021-07-06 19:34:37 +02:00
|
|
|
portfolioCalculator.computeTransactionPoints(ordersMixedSymbols);
|
2021-07-06 19:40:37 +02:00
|
|
|
const portfolioItemsAtTransactionPoints =
|
|
|
|
portfolioCalculator.getTransactionPoints();
|
2021-07-01 23:11:41 +02:00
|
|
|
|
|
|
|
expect(portfolioItemsAtTransactionPoints).toEqual([
|
|
|
|
{
|
|
|
|
date: '2017-01-03',
|
2021-07-06 19:40:37 +02:00
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('50'),
|
|
|
|
symbol: 'TSLA',
|
|
|
|
investment: new Big('2148.5'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2017-01-03',
|
|
|
|
transactionCount: 1
|
|
|
|
}
|
|
|
|
]
|
2021-07-01 23:11:41 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2017-07-01',
|
2021-07-06 19:40:37 +02:00
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('0.5614682'),
|
|
|
|
symbol: 'BTCUSD',
|
|
|
|
investment: new Big('1999.9999999999998659756'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2017-07-01',
|
|
|
|
transactionCount: 1
|
|
|
|
},
|
|
|
|
{
|
|
|
|
quantity: new Big('50'),
|
|
|
|
symbol: 'TSLA',
|
|
|
|
investment: new Big('2148.5'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2017-01-03',
|
|
|
|
transactionCount: 1
|
|
|
|
}
|
|
|
|
]
|
2021-07-01 23:11:41 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2018-09-01',
|
2021-07-06 19:40:37 +02:00
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('5'),
|
|
|
|
symbol: 'AMZN',
|
|
|
|
investment: new Big('10109.95'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2018-09-01',
|
|
|
|
transactionCount: 1
|
|
|
|
},
|
|
|
|
{
|
|
|
|
quantity: new Big('0.5614682'),
|
|
|
|
symbol: 'BTCUSD',
|
|
|
|
investment: new Big('1999.9999999999998659756'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2017-07-01',
|
|
|
|
transactionCount: 1
|
|
|
|
},
|
|
|
|
{
|
|
|
|
quantity: new Big('50'),
|
|
|
|
symbol: 'TSLA',
|
|
|
|
investment: new Big('2148.5'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2017-01-03',
|
|
|
|
transactionCount: 1
|
|
|
|
}
|
|
|
|
]
|
2021-07-01 23:11:41 +02:00
|
|
|
}
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2021-07-06 19:34:37 +02:00
|
|
|
describe('get current positions', () => {
|
2021-07-27 20:49:04 +02:00
|
|
|
it('with single TSLA and early start', async () => {
|
2021-07-27 13:36:38 +02:00
|
|
|
const portfolioCalculator = new PortfolioCalculator(
|
|
|
|
currentRateService,
|
|
|
|
Currency.USD
|
|
|
|
);
|
2021-07-27 17:48:49 +02:00
|
|
|
portfolioCalculator.setTransactionPoints(orderTslaTransactionPoint);
|
2021-07-27 13:36:38 +02:00
|
|
|
|
|
|
|
const spy = jest
|
|
|
|
.spyOn(Date, 'now')
|
|
|
|
.mockImplementation(() => new Date(Date.UTC(2021, 6, 26)).getTime()); // 2021-07-26
|
|
|
|
const currentPositions = await portfolioCalculator.getCurrentPositions(
|
2021-07-31 18:17:50 +02:00
|
|
|
parseDate('2020-01-21')
|
2021-07-27 13:36:38 +02:00
|
|
|
);
|
|
|
|
spy.mockRestore();
|
|
|
|
|
|
|
|
expect(currentPositions).toEqual({
|
|
|
|
hasErrors: false,
|
2021-07-31 10:08:05 +02:00
|
|
|
currentValue: new Big('657.62'),
|
|
|
|
grossPerformance: new Big('-61.84'),
|
2021-07-31 18:17:50 +02:00
|
|
|
grossPerformancePercentage: new Big('-0.08595335390431712673'),
|
2021-07-31 20:45:12 +02:00
|
|
|
totalInvestment: new Big('719.46'),
|
2021-07-27 13:36:38 +02:00
|
|
|
positions: [
|
|
|
|
{
|
2021-07-27 17:48:49 +02:00
|
|
|
averagePrice: new Big('719.46'),
|
2021-07-27 13:36:38 +02:00
|
|
|
currency: 'USD',
|
|
|
|
firstBuyDate: '2021-01-01',
|
2021-07-27 17:48:49 +02:00
|
|
|
grossPerformance: new Big('-61.84'), // 657.62-719.46=-61.84
|
2021-07-27 20:56:40 +02:00
|
|
|
grossPerformancePercentage: new Big('-0.08595335390431712673'), // (657.62-719.46)/719.46=-0.08595335390431712673
|
2021-07-27 17:48:49 +02:00
|
|
|
investment: new Big('719.46'),
|
|
|
|
marketPrice: 657.62,
|
2021-07-27 13:36:38 +02:00
|
|
|
quantity: new Big('1'),
|
2021-07-27 17:48:49 +02:00
|
|
|
symbol: 'TSLA',
|
2021-07-27 13:36:38 +02:00
|
|
|
transactionCount: 1
|
|
|
|
}
|
|
|
|
]
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2021-07-27 20:49:04 +02:00
|
|
|
it('with single TSLA and buy day start', async () => {
|
|
|
|
const portfolioCalculator = new PortfolioCalculator(
|
|
|
|
currentRateService,
|
|
|
|
Currency.USD
|
|
|
|
);
|
|
|
|
portfolioCalculator.setTransactionPoints(orderTslaTransactionPoint);
|
|
|
|
|
|
|
|
const spy = jest
|
|
|
|
.spyOn(Date, 'now')
|
|
|
|
.mockImplementation(() => new Date(Date.UTC(2021, 6, 26)).getTime()); // 2021-07-26
|
|
|
|
const currentPositions = await portfolioCalculator.getCurrentPositions(
|
2021-07-31 18:17:50 +02:00
|
|
|
parseDate('2021-01-01')
|
2021-07-27 20:49:04 +02:00
|
|
|
);
|
|
|
|
spy.mockRestore();
|
|
|
|
|
|
|
|
expect(currentPositions).toEqual({
|
|
|
|
hasErrors: false,
|
2021-07-31 10:08:05 +02:00
|
|
|
currentValue: new Big('657.62'),
|
|
|
|
grossPerformance: new Big('-61.84'),
|
2021-07-31 18:17:50 +02:00
|
|
|
grossPerformancePercentage: new Big('-0.08595335390431712673'),
|
2021-07-31 20:45:12 +02:00
|
|
|
totalInvestment: new Big('719.46'),
|
2021-07-27 20:49:04 +02:00
|
|
|
positions: [
|
|
|
|
{
|
|
|
|
averagePrice: new Big('719.46'),
|
|
|
|
currency: 'USD',
|
|
|
|
firstBuyDate: '2021-01-01',
|
|
|
|
grossPerformance: new Big('-61.84'), // 657.62-719.46=-61.84
|
2021-07-27 20:56:40 +02:00
|
|
|
grossPerformancePercentage: new Big('-0.08595335390431712673'), // (657.62-719.46)/719.46=-0.08595335390431712673
|
2021-07-27 20:49:04 +02:00
|
|
|
investment: new Big('719.46'),
|
|
|
|
marketPrice: 657.62,
|
|
|
|
quantity: new Big('1'),
|
|
|
|
symbol: 'TSLA',
|
|
|
|
transactionCount: 1
|
|
|
|
}
|
|
|
|
]
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('with single TSLA and late start', async () => {
|
|
|
|
const portfolioCalculator = new PortfolioCalculator(
|
|
|
|
currentRateService,
|
|
|
|
Currency.USD
|
|
|
|
);
|
|
|
|
portfolioCalculator.setTransactionPoints(orderTslaTransactionPoint);
|
|
|
|
|
|
|
|
const spy = jest
|
|
|
|
.spyOn(Date, 'now')
|
|
|
|
.mockImplementation(() => new Date(Date.UTC(2021, 6, 26)).getTime()); // 2021-07-26
|
|
|
|
const currentPositions = await portfolioCalculator.getCurrentPositions(
|
2021-07-31 18:17:50 +02:00
|
|
|
parseDate('2021-01-02')
|
2021-07-27 20:49:04 +02:00
|
|
|
);
|
|
|
|
spy.mockRestore();
|
|
|
|
|
|
|
|
expect(currentPositions).toEqual({
|
|
|
|
hasErrors: false,
|
2021-07-31 10:08:05 +02:00
|
|
|
currentValue: new Big('657.62'),
|
|
|
|
grossPerformance: new Big('-9.04'),
|
2021-07-31 18:17:50 +02:00
|
|
|
grossPerformancePercentage: new Big('-0.01356013560135601356'),
|
2021-07-31 20:45:12 +02:00
|
|
|
totalInvestment: new Big('719.46'),
|
2021-07-27 20:49:04 +02:00
|
|
|
positions: [
|
|
|
|
{
|
|
|
|
averagePrice: new Big('719.46'),
|
|
|
|
currency: 'USD',
|
|
|
|
firstBuyDate: '2021-01-01',
|
|
|
|
grossPerformance: new Big('-9.04'), // 657.62-666.66=-9.04
|
|
|
|
grossPerformancePercentage: new Big('-0.01356013560135601356'), // 657.62/666.66-1=-0.013560136
|
|
|
|
investment: new Big('719.46'),
|
|
|
|
marketPrice: 657.62,
|
|
|
|
quantity: new Big('1'),
|
|
|
|
symbol: 'TSLA',
|
|
|
|
transactionCount: 1
|
|
|
|
}
|
|
|
|
]
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2021-07-27 13:36:38 +02:00
|
|
|
it('with VTI only', async () => {
|
2021-07-06 19:40:37 +02:00
|
|
|
const portfolioCalculator = new PortfolioCalculator(
|
|
|
|
currentRateService,
|
|
|
|
Currency.USD
|
|
|
|
);
|
2021-07-06 19:34:37 +02:00
|
|
|
portfolioCalculator.setTransactionPoints(ordersVTITransactionPoints);
|
2021-07-26 22:13:09 +02:00
|
|
|
|
|
|
|
const spy = jest
|
|
|
|
.spyOn(Date, 'now')
|
2021-07-26 22:23:32 +02:00
|
|
|
.mockImplementation(() => new Date(Date.UTC(2020, 9, 24)).getTime()); // 2020-10-24
|
2021-07-26 22:13:09 +02:00
|
|
|
const currentPositions = await portfolioCalculator.getCurrentPositions(
|
2021-07-31 18:17:50 +02:00
|
|
|
parseDate('2019-01-01')
|
2021-07-26 22:13:09 +02:00
|
|
|
);
|
|
|
|
spy.mockRestore();
|
2021-07-06 19:34:37 +02:00
|
|
|
|
|
|
|
expect(currentPositions).toEqual({
|
2021-07-26 23:03:22 +02:00
|
|
|
hasErrors: false,
|
2021-07-31 10:08:05 +02:00
|
|
|
currentValue: new Big('4871.5'),
|
|
|
|
grossPerformance: new Big('240.4'),
|
2021-07-31 18:17:50 +02:00
|
|
|
grossPerformancePercentage: new Big('0.08839407904876477102'),
|
2021-07-31 20:45:12 +02:00
|
|
|
totalInvestment: new Big('4460.95'),
|
2021-07-26 23:03:22 +02:00
|
|
|
positions: [
|
|
|
|
{
|
|
|
|
averagePrice: new Big('178.438'),
|
|
|
|
currency: 'USD',
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
// see next test for details about how to calculate this
|
2021-07-27 20:37:46 +02:00
|
|
|
grossPerformance: new Big('240.4'),
|
2021-07-26 23:03:22 +02:00
|
|
|
grossPerformancePercentage: new Big(
|
2021-07-31 09:10:27 +02:00
|
|
|
'0.0883940790487647710162214425767848424215253864940558186258745429269647266073266478435285352186572448'
|
2021-07-26 23:03:22 +02:00
|
|
|
),
|
|
|
|
investment: new Big('4460.95'),
|
|
|
|
marketPrice: 194.86,
|
|
|
|
quantity: new Big('25'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
transactionCount: 5
|
|
|
|
}
|
|
|
|
]
|
2021-07-06 19:34:37 +02:00
|
|
|
});
|
2021-07-06 19:40:37 +02:00
|
|
|
});
|
2021-07-26 22:13:09 +02:00
|
|
|
|
2021-08-07 07:07:12 +02:00
|
|
|
it('with buy and sell', async () => {
|
|
|
|
const portfolioCalculator = new PortfolioCalculator(
|
|
|
|
currentRateService,
|
|
|
|
Currency.USD
|
|
|
|
);
|
|
|
|
portfolioCalculator.setTransactionPoints(transactionPointsBuyAndSell);
|
|
|
|
|
|
|
|
const spy = jest
|
|
|
|
.spyOn(Date, 'now')
|
|
|
|
.mockImplementation(() => new Date(Date.UTC(2020, 9, 24)).getTime()); // 2020-10-24
|
|
|
|
const currentPositions = await portfolioCalculator.getCurrentPositions(
|
|
|
|
parseDate('2019-01-01')
|
|
|
|
);
|
|
|
|
spy.mockRestore();
|
|
|
|
|
|
|
|
expect(currentPositions).toEqual({
|
|
|
|
hasErrors: false,
|
|
|
|
currentValue: new Big('4871.5'),
|
|
|
|
grossPerformance: new Big('240.4'),
|
|
|
|
grossPerformancePercentage: new Big('0.01104605615757711361'),
|
|
|
|
totalInvestment: new Big('4460.95'),
|
|
|
|
positions: [
|
|
|
|
{
|
|
|
|
averagePrice: new Big('0'),
|
|
|
|
currency: 'USD',
|
|
|
|
firstBuyDate: '2019-09-01',
|
|
|
|
grossPerformance: new Big('0'),
|
|
|
|
grossPerformancePercentage: new Big('0'),
|
|
|
|
investment: new Big('0'),
|
|
|
|
marketPrice: 2021.99,
|
|
|
|
quantity: new Big('0'),
|
|
|
|
symbol: 'AMZN',
|
|
|
|
transactionCount: 2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
averagePrice: new Big('178.438'),
|
|
|
|
currency: 'USD',
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
grossPerformance: new Big('240.4'),
|
|
|
|
grossPerformancePercentage: new Big(
|
|
|
|
'0.08839407904876477101219019935616297754969945667391763908415656216989674494965785538864363782688167989866968512455219637257546280462751601552'
|
|
|
|
),
|
|
|
|
investment: new Big('4460.95'),
|
|
|
|
marketPrice: 194.86,
|
|
|
|
quantity: new Big('25'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
transactionCount: 5
|
|
|
|
}
|
|
|
|
]
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('with buy, sell, buy', async () => {
|
|
|
|
const portfolioCalculator = new PortfolioCalculator(
|
|
|
|
currentRateService,
|
|
|
|
Currency.USD
|
|
|
|
);
|
|
|
|
portfolioCalculator.setTransactionPoints([
|
|
|
|
{
|
|
|
|
date: '2019-09-01',
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('5'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('805.9'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-09-01',
|
|
|
|
transactionCount: 1
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2020-08-02',
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('0'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('0'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-09-01',
|
|
|
|
transactionCount: 2
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-02-01',
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('5'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('1013.9'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-09-01',
|
|
|
|
transactionCount: 3
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
]);
|
|
|
|
|
|
|
|
const spy = jest
|
|
|
|
.spyOn(Date, 'now')
|
|
|
|
.mockImplementation(() => new Date(Date.UTC(2021, 7, 1)).getTime()); // 2021-08-01
|
|
|
|
const currentPositions = await portfolioCalculator.getCurrentPositions(
|
|
|
|
parseDate('2019-02-01')
|
|
|
|
);
|
|
|
|
spy.mockRestore();
|
|
|
|
|
|
|
|
expect(currentPositions).toEqual({
|
|
|
|
hasErrors: false,
|
|
|
|
currentValue: new Big('1086.7'),
|
|
|
|
grossPerformance: new Big('207.6'),
|
|
|
|
grossPerformancePercentage: new Big('0.2516103956224511062'),
|
|
|
|
totalInvestment: new Big('1013.9'),
|
|
|
|
positions: [
|
|
|
|
{
|
|
|
|
averagePrice: new Big('202.78'),
|
|
|
|
currency: 'USD',
|
|
|
|
firstBuyDate: '2019-09-01',
|
|
|
|
grossPerformance: new Big('207.6'),
|
|
|
|
grossPerformancePercentage: new Big(
|
|
|
|
'0.2516103956224511061954915466429950404846'
|
|
|
|
),
|
|
|
|
investment: new Big('1013.9'),
|
|
|
|
marketPrice: 217.34,
|
|
|
|
quantity: new Big('5'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
transactionCount: 3
|
|
|
|
}
|
|
|
|
]
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2021-07-26 22:13:09 +02:00
|
|
|
it('with performance since Jan 1st, 2020', async () => {
|
|
|
|
const portfolioCalculator = new PortfolioCalculator(
|
|
|
|
currentRateService,
|
|
|
|
Currency.USD
|
|
|
|
);
|
|
|
|
const transactionPoints = [
|
|
|
|
{
|
|
|
|
date: '2019-02-01',
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('10'),
|
|
|
|
name: 'Vanguard Total Stock Market Index Fund ETF Shares',
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('1443.8'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
transactionCount: 1
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2020-08-03',
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('20'),
|
|
|
|
name: 'Vanguard Total Stock Market Index Fund ETF Shares',
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('2923.7'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
transactionCount: 2
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
|
|
|
portfolioCalculator.setTransactionPoints(transactionPoints);
|
|
|
|
const spy = jest
|
|
|
|
.spyOn(Date, 'now')
|
2021-07-26 22:23:32 +02:00
|
|
|
.mockImplementation(() => new Date(Date.UTC(2020, 9, 24)).getTime()); // 2020-10-24
|
2021-07-26 22:13:09 +02:00
|
|
|
|
|
|
|
// 2020-01-01 -> days 334 => value: VTI: 144.38+334*0.08=171.1 => 10*171.10=1711
|
2021-07-31 09:10:27 +02:00
|
|
|
// 2020-08-03 -> days 549 => value: VTI: 144.38+549*0.08=188.3 => 10*188.30=1883 => 1883/1711 = 1.100526008
|
2021-07-26 22:13:09 +02:00
|
|
|
// 2020-08-03 -> days 549 => value: VTI: 144.38+549*0.08=188.3 => 20*188.30=3766
|
2021-07-31 09:10:27 +02:00
|
|
|
// cash flow: 2923.7-1443.8=1479.9
|
|
|
|
// 2020-10-24 [today] -> days 631 => value: VTI: 144.38+631*0.08=194.86 => 20*194.86=3897.2 => 3897.2/(1883+1479.9) = 1.158880728
|
2021-07-26 22:13:09 +02:00
|
|
|
// gross performance: 1883-1711 + 3897.2-3766 = 303.2
|
2021-07-31 09:10:27 +02:00
|
|
|
// gross performance percentage: 1.100526008 * 1.158880728 = 1.275378381 => 27.5378381 %
|
2021-07-26 22:13:09 +02:00
|
|
|
|
|
|
|
const currentPositions = await portfolioCalculator.getCurrentPositions(
|
2021-07-31 18:17:50 +02:00
|
|
|
parseDate('2020-01-01')
|
2021-07-26 22:13:09 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
spy.mockRestore();
|
|
|
|
expect(currentPositions).toEqual({
|
2021-07-26 23:03:22 +02:00
|
|
|
hasErrors: false,
|
2021-07-31 10:08:05 +02:00
|
|
|
currentValue: new Big('3897.2'),
|
|
|
|
grossPerformance: new Big('303.2'),
|
2021-07-31 18:17:50 +02:00
|
|
|
grossPerformancePercentage: new Big('0.27537838148272398344'),
|
2021-07-31 20:45:12 +02:00
|
|
|
totalInvestment: new Big('2923.7'),
|
2021-07-26 23:03:22 +02:00
|
|
|
positions: [
|
|
|
|
{
|
|
|
|
averagePrice: new Big('146.185'),
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
quantity: new Big('20'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('2923.7'),
|
|
|
|
marketPrice: 194.86,
|
|
|
|
transactionCount: 2,
|
|
|
|
grossPerformance: new Big('303.2'),
|
|
|
|
grossPerformancePercentage: new Big(
|
2021-07-31 09:10:27 +02:00
|
|
|
'0.2753783814827239834392742298083677500037'
|
2021-07-26 23:03:22 +02:00
|
|
|
),
|
|
|
|
currency: 'USD'
|
|
|
|
}
|
|
|
|
]
|
2021-07-26 22:13:09 +02:00
|
|
|
});
|
|
|
|
});
|
2021-07-31 09:10:27 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Source: https://www.investopedia.com/terms/t/time-weightedror.asp
|
|
|
|
*/
|
|
|
|
it('with TWR example from Investopedia: Scenario 1', async () => {
|
|
|
|
const portfolioCalculator = new PortfolioCalculator(
|
|
|
|
currentRateService,
|
|
|
|
Currency.USD
|
|
|
|
);
|
|
|
|
portfolioCalculator.setTransactionPoints([
|
|
|
|
{
|
|
|
|
date: '2010-12-31',
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('1000000'), // 1 million
|
2021-08-01 20:43:12 +02:00
|
|
|
symbol: 'MFA', // Mutual Fund A
|
2021-07-31 09:10:27 +02:00
|
|
|
investment: new Big('1000000'), // 1 million
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2010-12-31',
|
|
|
|
transactionCount: 1
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2011-08-15',
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('1086022.689344541'), // 1,000,000 + 100,000 / 1.162484
|
2021-08-01 20:43:12 +02:00
|
|
|
symbol: 'MFA', // Mutual Fund A
|
2021-07-31 09:10:27 +02:00
|
|
|
investment: new Big('1100000'), // 1,000,000 + 100,000
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2010-12-31',
|
|
|
|
transactionCount: 2
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
]);
|
|
|
|
|
|
|
|
const spy = jest
|
|
|
|
.spyOn(Date, 'now')
|
|
|
|
.mockImplementation(() => new Date(Date.UTC(2011, 11, 31)).getTime()); // 2011-12-31
|
|
|
|
|
|
|
|
const currentPositions = await portfolioCalculator.getCurrentPositions(
|
|
|
|
parseDate('2010-12-31')
|
|
|
|
);
|
|
|
|
spy.mockRestore();
|
|
|
|
|
|
|
|
expect(currentPositions).toEqual({
|
|
|
|
hasErrors: false,
|
2021-07-31 10:08:05 +02:00
|
|
|
currentValue: new Big('1192327.999656600298238721'),
|
|
|
|
grossPerformance: new Big('92327.999656600898394721'),
|
2021-07-31 18:17:50 +02:00
|
|
|
grossPerformancePercentage: new Big('0.09788498099999947809'),
|
2021-07-31 20:45:12 +02:00
|
|
|
totalInvestment: new Big('1100000'),
|
2021-07-31 09:10:27 +02:00
|
|
|
positions: [
|
|
|
|
{
|
|
|
|
averagePrice: new Big('1.01287018290924923237'), // 1'100'000 / 1'086'022.689344542
|
|
|
|
firstBuyDate: '2010-12-31',
|
|
|
|
quantity: new Big('1086022.689344541'),
|
|
|
|
symbol: 'MFA',
|
|
|
|
investment: new Big('1100000'),
|
|
|
|
marketPrice: 1.097884981,
|
|
|
|
transactionCount: 2,
|
|
|
|
grossPerformance: new Big('92327.999656600898394721'), // 1'192'328 - 1'100'000 = 92'328
|
|
|
|
grossPerformancePercentage: new Big('0.09788498099999947808927632'), // 9.79 %
|
|
|
|
currency: 'USD'
|
|
|
|
}
|
|
|
|
]
|
|
|
|
});
|
|
|
|
});
|
2021-07-31 18:17:50 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Source: https://www.chsoft.ch/en/assets/Dateien/files/PDF/ePoca/en/Practical%20Performance%20Calculation.pdf
|
|
|
|
*/
|
|
|
|
it('with example from chsoft.ch: Performance of a Combination of Investments', async () => {
|
|
|
|
const portfolioCalculator = new PortfolioCalculator(
|
|
|
|
currentRateService,
|
|
|
|
Currency.CHF
|
|
|
|
);
|
|
|
|
portfolioCalculator.setTransactionPoints([
|
|
|
|
{
|
|
|
|
date: '2012-12-31',
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('200'),
|
2021-08-01 20:43:12 +02:00
|
|
|
symbol: 'SPA', // Sub Portfolio A
|
2021-07-31 18:17:50 +02:00
|
|
|
investment: new Big('200'),
|
|
|
|
currency: Currency.CHF,
|
|
|
|
firstBuyDate: '2012-12-31',
|
|
|
|
transactionCount: 1
|
|
|
|
},
|
|
|
|
{
|
|
|
|
quantity: new Big('300'),
|
2021-08-01 20:43:12 +02:00
|
|
|
symbol: 'SPB', // Sub Portfolio B
|
2021-07-31 18:17:50 +02:00
|
|
|
investment: new Big('300'),
|
|
|
|
currency: Currency.CHF,
|
|
|
|
firstBuyDate: '2012-12-31',
|
|
|
|
transactionCount: 1
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2013-12-31',
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('200'),
|
2021-08-01 20:43:12 +02:00
|
|
|
symbol: 'SPA', // Sub Portfolio A
|
2021-07-31 18:17:50 +02:00
|
|
|
investment: new Big('200'),
|
|
|
|
currency: Currency.CHF,
|
|
|
|
firstBuyDate: '2012-12-31',
|
|
|
|
transactionCount: 1
|
|
|
|
},
|
|
|
|
{
|
|
|
|
quantity: new Big('300'),
|
2021-08-01 20:43:12 +02:00
|
|
|
symbol: 'SPB', // Sub Portfolio B
|
2021-07-31 18:17:50 +02:00
|
|
|
investment: new Big('300'),
|
|
|
|
currency: Currency.CHF,
|
|
|
|
firstBuyDate: '2012-12-31',
|
|
|
|
transactionCount: 1
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
]);
|
|
|
|
|
|
|
|
const spy = jest
|
|
|
|
.spyOn(Date, 'now')
|
|
|
|
.mockImplementation(() => new Date(Date.UTC(2013, 11, 31)).getTime()); // 2013-12-31
|
|
|
|
|
|
|
|
const currentPositions = await portfolioCalculator.getCurrentPositions(
|
|
|
|
parseDate('2012-12-31')
|
|
|
|
);
|
|
|
|
spy.mockRestore();
|
|
|
|
|
|
|
|
expect(currentPositions).toEqual({
|
|
|
|
currentValue: new Big('517'),
|
|
|
|
grossPerformance: new Big('17'), // 517 - 500
|
|
|
|
grossPerformancePercentage: new Big('0.034'), // ((200 * 0.025) + (300 * 0.04)) / (200 + 300) = 3.4%
|
2021-07-31 20:45:12 +02:00
|
|
|
totalInvestment: new Big('500'),
|
2021-07-31 18:17:50 +02:00
|
|
|
hasErrors: false,
|
|
|
|
positions: [
|
|
|
|
{
|
|
|
|
averagePrice: new Big('1'),
|
|
|
|
firstBuyDate: '2012-12-31',
|
|
|
|
quantity: new Big('200'),
|
|
|
|
symbol: 'SPA',
|
|
|
|
investment: new Big('200'),
|
|
|
|
marketPrice: 1.025, // 205 / 200
|
|
|
|
transactionCount: 1,
|
|
|
|
grossPerformance: new Big('5'), // 205 - 200
|
|
|
|
grossPerformancePercentage: new Big('0.025'),
|
|
|
|
currency: 'CHF'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
averagePrice: new Big('1'),
|
|
|
|
firstBuyDate: '2012-12-31',
|
|
|
|
quantity: new Big('300'),
|
|
|
|
symbol: 'SPB',
|
|
|
|
investment: new Big('300'),
|
|
|
|
marketPrice: 1.04, // 312 / 300
|
|
|
|
transactionCount: 1,
|
|
|
|
grossPerformance: new Big('12'), // 312 - 300
|
|
|
|
grossPerformancePercentage: new Big('0.04'),
|
|
|
|
currency: 'CHF'
|
|
|
|
}
|
|
|
|
]
|
|
|
|
});
|
|
|
|
});
|
2021-07-06 19:40:37 +02:00
|
|
|
});
|
2021-07-06 20:54:51 +02:00
|
|
|
|
|
|
|
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-07-06 22:41:56 +02:00
|
|
|
'2021-06-30'
|
2021-07-06 20:54:51 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
expect(timeline).toEqual([
|
|
|
|
{
|
|
|
|
date: '2019-01-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('0'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('0'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('0')
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2020-01-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('498.3'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('2923.7'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('3422') // 20 * (144.38 + days=335 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-01-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('349.35'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('652.55'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('1001.9') // 5 * (144.38 + days=700 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
}
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
|
|
|
|
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-07-06 22:41:56 +02:00
|
|
|
'2021-06-30'
|
2021-07-06 20:54:51 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
expect(timeline).toEqual([
|
|
|
|
{
|
|
|
|
date: '2019-01-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('0'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('0'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('0')
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2019-02-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('0'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('1443.8'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('1443.8') // 10 * (144.38 + days=0 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2019-03-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('22.4'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('1443.8'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('1466.2') // 10 * (144.38 + days=28 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2019-04-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('47.2'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('1443.8'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('1491') // 10 * (144.38 + days=59 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2019-05-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('71.2'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('1443.8'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('1515') // 10 * (144.38 + days=89 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2019-06-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('96'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('1443.8'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('1539.8') // 10 * (144.38 + days=120 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2019-07-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('120'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('1443.8'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('1563.8') // 10 * (144.38 + days=150 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2019-08-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('144.8'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('1443.8'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('1588.6') // 10 * (144.38 + days=181 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2019-09-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('303.1'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('2923.7'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('3226.8') // 20 * (144.38 + days=212 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2019-10-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('351.1'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('2923.7'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('3274.8') // 20 * (144.38 + days=242 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2019-11-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('400.7'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('2923.7'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('3324.4') // 20 * (144.38 + days=273 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2019-12-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('448.7'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('2923.7'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('3372.4') // 20 * (144.38 + days=303 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2020-01-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('498.3'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('2923.7'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('3422') // 20 * (144.38 + days=335 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2020-02-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('547.9'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('2923.7'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('3471.6') // 20 * (144.38 + days=365 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2020-03-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('226.95'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('652.55'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('879.5') // 5 * (144.38 + days=394 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2020-04-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('239.35'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('652.55'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('891.9') // 5 * (144.38 + days=425 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2020-05-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('251.35'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('652.55'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('903.9') // 5 * (144.38 + days=455 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2020-06-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('263.75'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('652.55'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('916.3') // 5 * (144.38 + days=486 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2020-07-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('275.75'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('652.55'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('928.3') // 5 * (144.38 + days=516 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2020-08-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('288.15'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('652.55'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('940.7') // 5 * (144.38 + days=547 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2020-09-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('300.55'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('652.55'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('953.1') // 5 * (144.38 + days=578 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2020-10-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('312.55'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('652.55'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('965.1') // 5 * (144.38 + days=608 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2020-11-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('324.95'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('652.55'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('977.5') // 5 * (144.38 + days=639 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2020-12-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('336.95'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('652.55'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('989.5') // 5 * (144.38 + days=669 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-01-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('349.35'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('652.55'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('1001.9') // 5 * (144.38 + days=700 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-02-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('358.85'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('2684.05'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('3042.9') // 15 * (144.38 + days=731 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-03-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('392.45'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('2684.05'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('3076.5') // 15 * (144.38 + days=759 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-04-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('429.65'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('2684.05'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('3113.7') // 15 * (144.38 + days=790 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-05-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('465.65'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('2684.05'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('3149.7') // 15 * (144.38 + days=820 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('502.85'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('2684.05'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('3186.9') // 15 * (144.38 + days=851 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
}
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
|
|
|
|
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-07-06 22:41:56 +02:00
|
|
|
'2021-06-30'
|
2021-07-06 20:54:51 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
expect(timeline).toEqual([
|
|
|
|
{
|
|
|
|
date: '2019-01-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('0'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('0'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('0')
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2020-01-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('498.3'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('2923.7'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('3422') // 20 * (144.38 + days=335 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-01-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('349.35'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('652.55'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('1001.9') // 5 * (144.38 + days=700 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-02-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('358.85'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('2684.05'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('3042.9') // 15 * (144.38 + days=731 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-03-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('392.45'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('2684.05'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('3076.5') // 15 * (144.38 + days=759 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-04-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('429.65'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('2684.05'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('3113.7') // 15 * (144.38 + days=790 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-05-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('465.65'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('2684.05'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('3149.7') // 15 * (144.38 + days=820 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('502.85'),
|
2021-07-06 21:19:41 +02:00
|
|
|
investment: new Big('2684.05'),
|
2021-07-06 22:41:56 +02:00
|
|
|
value: new Big('3186.9') // 15 * (144.38 + days=851 * 0.08)
|
2021-07-06 20:54:51 +02:00
|
|
|
}
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
|
|
|
|
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'
|
|
|
|
},
|
|
|
|
{
|
2021-07-06 22:41:56 +02:00
|
|
|
start: '2021-06-01',
|
2021-07-06 20:54:51 +02:00
|
|
|
accuracy: 'day'
|
|
|
|
}
|
|
|
|
];
|
|
|
|
const timeline: TimelinePeriod[] =
|
|
|
|
await portfolioCalculator.calculateTimeline(
|
|
|
|
timelineSpecification,
|
2021-07-06 22:41:56 +02:00
|
|
|
'2021-06-30'
|
2021-07-06 20:54:51 +02:00
|
|
|
);
|
|
|
|
|
2021-07-20 23:05:12 +02:00
|
|
|
expect(timeline).toEqual(
|
|
|
|
expect.objectContaining([
|
|
|
|
{
|
|
|
|
date: '2019-01-01',
|
|
|
|
grossPerformance: new Big('0'),
|
|
|
|
investment: new Big('0'),
|
|
|
|
value: new Big('0')
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2020-01-01',
|
|
|
|
grossPerformance: new Big('498.3'),
|
|
|
|
investment: new Big('2923.7'),
|
|
|
|
value: new Big('3422') // 20 * (144.38 + days=335 * 0.08)
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-01-01',
|
|
|
|
grossPerformance: new Big('349.35'),
|
|
|
|
investment: new Big('652.55'),
|
|
|
|
value: new Big('1001.9') // 5 * (144.38 + days=700 * 0.08)
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-02-01',
|
|
|
|
grossPerformance: new Big('358.85'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3042.9') // 15 * (144.38 + days=731 * 0.08)
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-03-01',
|
|
|
|
grossPerformance: new Big('392.45'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3076.5') // 15 * (144.38 + days=759 * 0.08)
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-04-01',
|
|
|
|
grossPerformance: new Big('429.65'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3113.7') // 15 * (144.38 + days=790 * 0.08)
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-05-01',
|
|
|
|
grossPerformance: new Big('465.65'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3149.7') // 15 * (144.38 + days=820 * 0.08)
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-01',
|
|
|
|
grossPerformance: new Big('502.85'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3186.9') // 15 * (144.38 + days=851 * 0.08)
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-02',
|
|
|
|
grossPerformance: new Big('504.05'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3188.1') // 15 * (144.38 + days=852 * 0.08) / +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-03',
|
|
|
|
grossPerformance: new Big('505.25'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3189.3') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-04',
|
|
|
|
grossPerformance: new Big('506.45'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3190.5') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-05',
|
|
|
|
grossPerformance: new Big('507.65'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3191.7') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-06',
|
|
|
|
grossPerformance: new Big('508.85'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3192.9') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-07',
|
|
|
|
grossPerformance: new Big('510.05'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3194.1') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-08',
|
|
|
|
grossPerformance: new Big('511.25'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3195.3') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-09',
|
|
|
|
grossPerformance: new Big('512.45'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3196.5') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-10',
|
|
|
|
grossPerformance: new Big('513.65'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3197.7') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-11',
|
|
|
|
grossPerformance: new Big('514.85'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3198.9') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-12',
|
|
|
|
grossPerformance: new Big('516.05'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3200.1') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-13',
|
|
|
|
grossPerformance: new Big('517.25'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3201.3') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-14',
|
|
|
|
grossPerformance: new Big('518.45'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3202.5') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-15',
|
|
|
|
grossPerformance: new Big('519.65'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3203.7') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-16',
|
|
|
|
grossPerformance: new Big('520.85'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3204.9') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-17',
|
|
|
|
grossPerformance: new Big('522.05'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3206.1') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-18',
|
|
|
|
grossPerformance: new Big('523.25'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3207.3') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-19',
|
|
|
|
grossPerformance: new Big('524.45'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3208.5') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-20',
|
|
|
|
grossPerformance: new Big('525.65'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3209.7') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-21',
|
|
|
|
grossPerformance: new Big('526.85'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3210.9') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-22',
|
|
|
|
grossPerformance: new Big('528.05'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3212.1') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-23',
|
|
|
|
grossPerformance: new Big('529.25'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3213.3') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-24',
|
|
|
|
grossPerformance: new Big('530.45'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3214.5') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-25',
|
|
|
|
grossPerformance: new Big('531.65'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3215.7') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-26',
|
|
|
|
grossPerformance: new Big('532.85'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3216.9') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-27',
|
|
|
|
grossPerformance: new Big('534.05'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3218.1') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-28',
|
|
|
|
grossPerformance: new Big('535.25'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3219.3') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-29',
|
|
|
|
grossPerformance: new Big('536.45'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3220.5') // +1.2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-06-30',
|
|
|
|
grossPerformance: new Big('537.65'),
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
value: new Big('3221.7') // +1.2
|
|
|
|
}
|
|
|
|
])
|
|
|
|
);
|
2021-07-06 22:41:56 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
it('with mixed portfolio', async () => {
|
|
|
|
const portfolioCalculator = new PortfolioCalculator(
|
|
|
|
currentRateService,
|
|
|
|
Currency.USD
|
|
|
|
);
|
|
|
|
portfolioCalculator.setTransactionPoints([
|
2021-07-06 20:54:51 +02:00
|
|
|
{
|
2021-07-06 22:41:56 +02:00
|
|
|
date: '2019-02-01',
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('5'),
|
|
|
|
symbol: 'AMZN',
|
|
|
|
investment: new Big('10109.95'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
transactionCount: 1
|
|
|
|
},
|
|
|
|
{
|
|
|
|
quantity: new Big('10'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('1443.8'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
transactionCount: 1
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
]);
|
|
|
|
const timelineSpecification: TimelineSpecification[] = [
|
2021-07-06 20:54:51 +02:00
|
|
|
{
|
2021-07-06 22:41:56 +02:00
|
|
|
start: '2019-01-01',
|
|
|
|
accuracy: 'year'
|
|
|
|
}
|
|
|
|
];
|
|
|
|
const timeline: TimelinePeriod[] =
|
|
|
|
await portfolioCalculator.calculateTimeline(
|
|
|
|
timelineSpecification,
|
|
|
|
'2020-01-01'
|
|
|
|
);
|
|
|
|
|
|
|
|
expect(timeline).toEqual([
|
2021-07-06 20:54:51 +02:00
|
|
|
{
|
2021-07-06 22:41:56 +02:00
|
|
|
date: '2019-01-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('0'),
|
2021-07-06 22:41:56 +02:00
|
|
|
investment: new Big('0'),
|
|
|
|
value: new Big('0')
|
2021-07-06 20:54:51 +02:00
|
|
|
},
|
|
|
|
{
|
2021-07-06 22:41:56 +02:00
|
|
|
date: '2020-01-01',
|
2021-07-07 22:34:41 +02:00
|
|
|
grossPerformance: new Big('267.2'),
|
2021-07-06 22:41:56 +02:00
|
|
|
investment: new Big('11553.75'),
|
|
|
|
value: new Big('11820.95') // 10 * (144.38 + days=334 * 0.08) + 5 * 2021.99
|
2021-07-06 20:54:51 +02:00
|
|
|
}
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
});
|
2021-07-06 19:34:37 +02:00
|
|
|
});
|
2021-07-28 15:54:37 +02:00
|
|
|
|
2021-07-01 23:11:41 +02:00
|
|
|
const ordersMixedSymbols: PortfolioOrder[] = [
|
|
|
|
{
|
|
|
|
date: '2017-01-03',
|
2021-07-25 13:31:44 +02:00
|
|
|
name: 'Tesla, Inc.',
|
2021-07-01 23:11:41 +02:00
|
|
|
quantity: new Big('50'),
|
|
|
|
symbol: 'TSLA',
|
|
|
|
type: OrderType.Buy,
|
|
|
|
unitPrice: new Big('42.97'),
|
|
|
|
currency: Currency.USD
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2017-07-01',
|
2021-07-25 13:31:44 +02:00
|
|
|
name: 'Bitcoin USD',
|
2021-07-01 23:11:41 +02:00
|
|
|
quantity: new Big('0.5614682'),
|
|
|
|
symbol: 'BTCUSD',
|
|
|
|
type: OrderType.Buy,
|
|
|
|
unitPrice: new Big('3562.089535970158'),
|
|
|
|
currency: Currency.USD
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2018-09-01',
|
2021-07-25 13:31:44 +02:00
|
|
|
name: 'Amazon.com, Inc.',
|
2021-07-01 23:11:41 +02:00
|
|
|
quantity: new Big('5'),
|
|
|
|
symbol: 'AMZN',
|
|
|
|
type: OrderType.Buy,
|
|
|
|
unitPrice: new Big('2021.99'),
|
|
|
|
currency: Currency.USD
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
|
|
|
const ordersVTI: PortfolioOrder[] = [
|
|
|
|
{
|
|
|
|
date: '2019-02-01',
|
2021-07-25 13:31:44 +02:00
|
|
|
name: 'Vanguard Total Stock Market Index Fund ETF Shares',
|
2021-07-01 23:11:41 +02:00
|
|
|
quantity: new Big('10'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
type: OrderType.Buy,
|
|
|
|
unitPrice: new Big('144.38'),
|
|
|
|
currency: Currency.USD
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2019-08-03',
|
2021-07-25 13:31:44 +02:00
|
|
|
name: 'Vanguard Total Stock Market Index Fund ETF Shares',
|
2021-07-01 23:11:41 +02:00
|
|
|
quantity: new Big('10'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
type: OrderType.Buy,
|
|
|
|
unitPrice: new Big('147.99'),
|
|
|
|
currency: Currency.USD
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2020-02-02',
|
2021-07-25 13:31:44 +02:00
|
|
|
name: 'Vanguard Total Stock Market Index Fund ETF Shares',
|
2021-07-01 23:11:41 +02:00
|
|
|
quantity: new Big('15'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
type: OrderType.Sell,
|
|
|
|
unitPrice: new Big('151.41'),
|
|
|
|
currency: Currency.USD
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-08-01',
|
2021-07-25 13:31:44 +02:00
|
|
|
name: 'Vanguard Total Stock Market Index Fund ETF Shares',
|
2021-07-01 23:11:41 +02:00
|
|
|
quantity: new Big('10'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
type: OrderType.Buy,
|
|
|
|
unitPrice: new Big('177.69'),
|
|
|
|
currency: Currency.USD
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-02-01',
|
2021-07-25 13:31:44 +02:00
|
|
|
name: 'Vanguard Total Stock Market Index Fund ETF Shares',
|
2021-07-01 23:11:41 +02:00
|
|
|
quantity: new Big('10'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
type: OrderType.Buy,
|
|
|
|
unitPrice: new Big('203.15'),
|
|
|
|
currency: Currency.USD
|
|
|
|
}
|
|
|
|
];
|
2021-07-06 19:34:37 +02:00
|
|
|
|
2021-07-27 17:48:49 +02:00
|
|
|
const orderTslaTransactionPoint: TransactionPoint[] = [
|
2021-07-27 13:36:38 +02:00
|
|
|
{
|
|
|
|
date: '2021-01-01',
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('1'),
|
2021-07-27 17:48:49 +02:00
|
|
|
symbol: 'TSLA',
|
|
|
|
investment: new Big('719.46'),
|
2021-07-27 13:36:38 +02:00
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2021-01-01',
|
|
|
|
transactionCount: 1
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
2021-07-25 13:31:44 +02:00
|
|
|
const ordersVTITransactionPoints: TransactionPoint[] = [
|
2021-07-06 19:34:37 +02:00
|
|
|
{
|
|
|
|
date: '2019-02-01',
|
2021-07-06 19:40:37 +02:00
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('10'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('1443.8'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
transactionCount: 1
|
|
|
|
}
|
|
|
|
]
|
2021-07-06 19:34:37 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2019-08-03',
|
2021-07-06 19:40:37 +02:00
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('20'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('2923.7'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
transactionCount: 2
|
|
|
|
}
|
|
|
|
]
|
2021-07-06 19:34:37 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2020-02-02',
|
2021-07-06 19:40:37 +02:00
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('5'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('652.55'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
transactionCount: 3
|
|
|
|
}
|
|
|
|
]
|
2021-07-06 19:34:37 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-02-01',
|
2021-07-06 19:40:37 +02:00
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('15'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
transactionCount: 4
|
|
|
|
}
|
|
|
|
]
|
2021-07-06 19:34:37 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-08-01',
|
2021-07-06 19:40:37 +02:00
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('25'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('4460.95'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
transactionCount: 5
|
|
|
|
}
|
|
|
|
]
|
2021-07-06 19:34:37 +02:00
|
|
|
}
|
|
|
|
];
|
2021-08-07 07:07:12 +02:00
|
|
|
|
|
|
|
const transactionPointsBuyAndSell = [
|
|
|
|
{
|
|
|
|
date: '2019-02-01',
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('10'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('1443.8'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
transactionCount: 1
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2019-08-03',
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('20'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('2923.7'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
transactionCount: 2
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2019-09-01',
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('5'),
|
|
|
|
symbol: 'AMZN',
|
|
|
|
investment: new Big('10109.95'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-09-01',
|
|
|
|
transactionCount: 1
|
|
|
|
},
|
|
|
|
{
|
|
|
|
quantity: new Big('20'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('2923.7'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
transactionCount: 2
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2020-02-02',
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('5'),
|
|
|
|
symbol: 'AMZN',
|
|
|
|
investment: new Big('10109.95'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-09-01',
|
|
|
|
transactionCount: 1
|
|
|
|
},
|
|
|
|
{
|
|
|
|
quantity: new Big('5'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('652.55'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
transactionCount: 3
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2020-08-02',
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('0'),
|
|
|
|
symbol: 'AMZN',
|
|
|
|
investment: new Big('0'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-09-01',
|
|
|
|
transactionCount: 2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
quantity: new Big('5'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('652.55'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
transactionCount: 3
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-02-01',
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('0'),
|
|
|
|
symbol: 'AMZN',
|
|
|
|
investment: new Big('0'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-09-01',
|
|
|
|
transactionCount: 2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
quantity: new Big('15'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('2684.05'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
transactionCount: 4
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: '2021-08-01',
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
quantity: new Big('0'),
|
|
|
|
symbol: 'AMZN',
|
|
|
|
investment: new Big('0'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-09-01',
|
|
|
|
transactionCount: 2
|
|
|
|
},
|
|
|
|
{
|
|
|
|
quantity: new Big('25'),
|
|
|
|
symbol: 'VTI',
|
|
|
|
investment: new Big('4460.95'),
|
|
|
|
currency: Currency.USD,
|
|
|
|
firstBuyDate: '2019-02-01',
|
|
|
|
transactionCount: 5
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
];
|