Feature/optimize portfolio snapshot computation by reusing date intervals (#3855)
* Optimize portfolio snapshot computation by reusing date intervals * Update changelog
This commit is contained in:
parent
e715ce14e5
commit
ecd75b5d8a
@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Set up a git-hook via `husky` to lint and format the changes before a commit
|
- Set up a git-hook via `husky` to lint and format the changes before a commit
|
||||||
- Added the `typescript-eslint/recommended-type-checked` rule to the `eslint` configuration
|
- Added the `typescript-eslint/recommended-type-checked` rule to the `eslint` configuration
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Optimized the portfolio calculations by reusing date intervals
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Handled an exception in the historical market data gathering of derived currencies
|
- Handled an exception in the historical market data gathering of derived currencies
|
||||||
|
@ -22,7 +22,7 @@ import {
|
|||||||
import { cloneDeep, first, last, sortBy } from 'lodash';
|
import { cloneDeep, first, last, sortBy } from 'lodash';
|
||||||
|
|
||||||
export class TWRPortfolioCalculator extends PortfolioCalculator {
|
export class TWRPortfolioCalculator extends PortfolioCalculator {
|
||||||
private chartDatesDescending: string[];
|
private chartDates: string[];
|
||||||
|
|
||||||
protected calculateOverallPerformance(
|
protected calculateOverallPerformance(
|
||||||
positions: TimelinePosition[]
|
positions: TimelinePosition[]
|
||||||
@ -228,11 +228,11 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
|
|||||||
|
|
||||||
const dateOfFirstTransaction = new Date(first(orders).date);
|
const dateOfFirstTransaction = new Date(first(orders).date);
|
||||||
|
|
||||||
const unitPriceAtStartDate =
|
const endDateString = format(end, DATE_FORMAT);
|
||||||
marketSymbolMap[format(start, DATE_FORMAT)]?.[symbol];
|
const startDateString = format(start, DATE_FORMAT);
|
||||||
|
|
||||||
const unitPriceAtEndDate =
|
const unitPriceAtStartDate = marketSymbolMap[startDateString]?.[symbol];
|
||||||
marketSymbolMap[format(end, DATE_FORMAT)]?.[symbol];
|
const unitPriceAtEndDate = marketSymbolMap[endDateString]?.[symbol];
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!unitPriceAtEndDate ||
|
!unitPriceAtEndDate ||
|
||||||
@ -278,7 +278,7 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
|
|||||||
|
|
||||||
// Add a synthetic order at the start and the end date
|
// Add a synthetic order at the start and the end date
|
||||||
orders.push({
|
orders.push({
|
||||||
date: format(start, DATE_FORMAT),
|
date: startDateString,
|
||||||
fee: new Big(0),
|
fee: new Big(0),
|
||||||
feeInBaseCurrency: new Big(0),
|
feeInBaseCurrency: new Big(0),
|
||||||
itemType: 'start',
|
itemType: 'start',
|
||||||
@ -292,7 +292,7 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
|
|||||||
});
|
});
|
||||||
|
|
||||||
orders.push({
|
orders.push({
|
||||||
date: format(end, DATE_FORMAT),
|
date: endDateString,
|
||||||
fee: new Big(0),
|
fee: new Big(0),
|
||||||
feeInBaseCurrency: new Big(0),
|
feeInBaseCurrency: new Big(0),
|
||||||
itemType: 'end',
|
itemType: 'end',
|
||||||
@ -305,7 +305,6 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
|
|||||||
unitPrice: unitPriceAtEndDate
|
unitPrice: unitPriceAtEndDate
|
||||||
});
|
});
|
||||||
|
|
||||||
let day = start;
|
|
||||||
let lastUnitPrice: Big;
|
let lastUnitPrice: Big;
|
||||||
|
|
||||||
const ordersByDate: { [date: string]: PortfolioOrderItem[] } = {};
|
const ordersByDate: { [date: string]: PortfolioOrderItem[] } = {};
|
||||||
@ -315,15 +314,23 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
|
|||||||
ordersByDate[order.date].push(order);
|
ordersByDate[order.date].push(order);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (isBefore(day, end)) {
|
if (!this.chartDates) {
|
||||||
const dateString = format(day, DATE_FORMAT);
|
this.chartDates = Object.keys(chartDateMap).sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const dateString of this.chartDates) {
|
||||||
|
if (dateString < startDateString) {
|
||||||
|
continue;
|
||||||
|
} else if (dateString > endDateString) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (ordersByDate[dateString]?.length > 0) {
|
if (ordersByDate[dateString]?.length > 0) {
|
||||||
for (let order of ordersByDate[dateString]) {
|
for (let order of ordersByDate[dateString]) {
|
||||||
order.unitPriceFromMarketData =
|
order.unitPriceFromMarketData =
|
||||||
marketSymbolMap[dateString]?.[symbol] ?? lastUnitPrice;
|
marketSymbolMap[dateString]?.[symbol] ?? lastUnitPrice;
|
||||||
}
|
}
|
||||||
} else if (chartDateMap[dateString]) {
|
} else {
|
||||||
orders.push({
|
orders.push({
|
||||||
date: dateString,
|
date: dateString,
|
||||||
fee: new Big(0),
|
fee: new Big(0),
|
||||||
@ -343,8 +350,6 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
|
|||||||
const lastOrder = last(orders);
|
const lastOrder = last(orders);
|
||||||
|
|
||||||
lastUnitPrice = lastOrder.unitPriceFromMarketData ?? lastOrder.unitPrice;
|
lastUnitPrice = lastOrder.unitPriceFromMarketData ?? lastOrder.unitPrice;
|
||||||
|
|
||||||
day = addDays(day, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort orders so that the start and end placeholder order are at the correct
|
// Sort orders so that the start and end placeholder order are at the correct
|
||||||
@ -821,14 +826,14 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
|
|||||||
startDate = start;
|
startDate = start;
|
||||||
}
|
}
|
||||||
|
|
||||||
const endDateString = format(endDate, DATE_FORMAT);
|
const rangeEndDateString = format(endDate, DATE_FORMAT);
|
||||||
const startDateString = format(startDate, DATE_FORMAT);
|
const rangeStartDateString = format(startDate, DATE_FORMAT);
|
||||||
|
|
||||||
const currentValuesAtDateRangeStartWithCurrencyEffect =
|
const currentValuesAtDateRangeStartWithCurrencyEffect =
|
||||||
currentValuesWithCurrencyEffect[startDateString] ?? new Big(0);
|
currentValuesWithCurrencyEffect[rangeStartDateString] ?? new Big(0);
|
||||||
|
|
||||||
const investmentValuesAccumulatedAtStartDateWithCurrencyEffect =
|
const investmentValuesAccumulatedAtStartDateWithCurrencyEffect =
|
||||||
investmentValuesAccumulatedWithCurrencyEffect[startDateString] ??
|
investmentValuesAccumulatedWithCurrencyEffect[rangeStartDateString] ??
|
||||||
new Big(0);
|
new Big(0);
|
||||||
|
|
||||||
const grossPerformanceAtDateRangeStartWithCurrencyEffect =
|
const grossPerformanceAtDateRangeStartWithCurrencyEffect =
|
||||||
@ -839,14 +844,12 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
|
|||||||
let average = new Big(0);
|
let average = new Big(0);
|
||||||
let dayCount = 0;
|
let dayCount = 0;
|
||||||
|
|
||||||
if (!this.chartDatesDescending) {
|
for (let i = this.chartDates.length - 1; i >= 0; i -= 1) {
|
||||||
this.chartDatesDescending = Object.keys(chartDateMap).sort().reverse();
|
const date = this.chartDates[i];
|
||||||
}
|
|
||||||
|
|
||||||
for (const date of this.chartDatesDescending) {
|
if (date > rangeEndDateString) {
|
||||||
if (date > endDateString) {
|
|
||||||
continue;
|
continue;
|
||||||
} else if (date < startDateString) {
|
} else if (date < rangeStartDateString) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -869,13 +872,13 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
netPerformanceWithCurrencyEffectMap[dateRange] =
|
netPerformanceWithCurrencyEffectMap[dateRange] =
|
||||||
netPerformanceValuesWithCurrencyEffect[endDateString]?.minus(
|
netPerformanceValuesWithCurrencyEffect[rangeEndDateString]?.minus(
|
||||||
// If the date range is 'max', take 0 as a start value. Otherwise,
|
// If the date range is 'max', take 0 as a start value. Otherwise,
|
||||||
// the value of the end of the day of the start date is taken which
|
// the value of the end of the day of the start date is taken which
|
||||||
// differs from the buying price.
|
// differs from the buying price.
|
||||||
dateRange === 'max'
|
dateRange === 'max'
|
||||||
? new Big(0)
|
? new Big(0)
|
||||||
: (netPerformanceValuesWithCurrencyEffect[startDateString] ??
|
: (netPerformanceValuesWithCurrencyEffect[rangeStartDateString] ??
|
||||||
new Big(0))
|
new Big(0))
|
||||||
) ?? new Big(0);
|
) ?? new Big(0);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user