|
|
@ -22,7 +22,7 @@ import { |
|
|
|
import { cloneDeep, first, last, sortBy } from 'lodash'; |
|
|
|
|
|
|
|
export class TWRPortfolioCalculator extends PortfolioCalculator { |
|
|
|
private chartDatesDescending: string[]; |
|
|
|
private chartDates: string[]; |
|
|
|
|
|
|
|
protected calculateOverallPerformance( |
|
|
|
positions: TimelinePosition[] |
|
|
@ -228,11 +228,11 @@ export class TWRPortfolioCalculator extends PortfolioCalculator { |
|
|
|
|
|
|
|
const dateOfFirstTransaction = new Date(first(orders).date); |
|
|
|
|
|
|
|
const unitPriceAtStartDate = |
|
|
|
marketSymbolMap[format(start, DATE_FORMAT)]?.[symbol]; |
|
|
|
const endDateString = format(end, DATE_FORMAT); |
|
|
|
const startDateString = format(start, DATE_FORMAT); |
|
|
|
|
|
|
|
const unitPriceAtEndDate = |
|
|
|
marketSymbolMap[format(end, DATE_FORMAT)]?.[symbol]; |
|
|
|
const unitPriceAtStartDate = marketSymbolMap[startDateString]?.[symbol]; |
|
|
|
const unitPriceAtEndDate = marketSymbolMap[endDateString]?.[symbol]; |
|
|
|
|
|
|
|
if ( |
|
|
|
!unitPriceAtEndDate || |
|
|
@ -278,7 +278,7 @@ export class TWRPortfolioCalculator extends PortfolioCalculator { |
|
|
|
|
|
|
|
// Add a synthetic order at the start and the end date
|
|
|
|
orders.push({ |
|
|
|
date: format(start, DATE_FORMAT), |
|
|
|
date: startDateString, |
|
|
|
fee: new Big(0), |
|
|
|
feeInBaseCurrency: new Big(0), |
|
|
|
itemType: 'start', |
|
|
@ -292,7 +292,7 @@ export class TWRPortfolioCalculator extends PortfolioCalculator { |
|
|
|
}); |
|
|
|
|
|
|
|
orders.push({ |
|
|
|
date: format(end, DATE_FORMAT), |
|
|
|
date: endDateString, |
|
|
|
fee: new Big(0), |
|
|
|
feeInBaseCurrency: new Big(0), |
|
|
|
itemType: 'end', |
|
|
@ -305,7 +305,6 @@ export class TWRPortfolioCalculator extends PortfolioCalculator { |
|
|
|
unitPrice: unitPriceAtEndDate |
|
|
|
}); |
|
|
|
|
|
|
|
let day = start; |
|
|
|
let lastUnitPrice: Big; |
|
|
|
|
|
|
|
const ordersByDate: { [date: string]: PortfolioOrderItem[] } = {}; |
|
|
@ -315,15 +314,23 @@ export class TWRPortfolioCalculator extends PortfolioCalculator { |
|
|
|
ordersByDate[order.date].push(order); |
|
|
|
} |
|
|
|
|
|
|
|
while (isBefore(day, end)) { |
|
|
|
const dateString = format(day, DATE_FORMAT); |
|
|
|
if (!this.chartDates) { |
|
|
|
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) { |
|
|
|
for (let order of ordersByDate[dateString]) { |
|
|
|
order.unitPriceFromMarketData = |
|
|
|
marketSymbolMap[dateString]?.[symbol] ?? lastUnitPrice; |
|
|
|
} |
|
|
|
} else if (chartDateMap[dateString]) { |
|
|
|
} else { |
|
|
|
orders.push({ |
|
|
|
date: dateString, |
|
|
|
fee: new Big(0), |
|
|
@ -343,8 +350,6 @@ export class TWRPortfolioCalculator extends PortfolioCalculator { |
|
|
|
const lastOrder = last(orders); |
|
|
|
|
|
|
|
lastUnitPrice = lastOrder.unitPriceFromMarketData ?? lastOrder.unitPrice; |
|
|
|
|
|
|
|
day = addDays(day, 1); |
|
|
|
} |
|
|
|
|
|
|
|
// 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; |
|
|
|
} |
|
|
|
|
|
|
|
const endDateString = format(endDate, DATE_FORMAT); |
|
|
|
const startDateString = format(startDate, DATE_FORMAT); |
|
|
|
const rangeEndDateString = format(endDate, DATE_FORMAT); |
|
|
|
const rangeStartDateString = format(startDate, DATE_FORMAT); |
|
|
|
|
|
|
|
const currentValuesAtDateRangeStartWithCurrencyEffect = |
|
|
|
currentValuesWithCurrencyEffect[startDateString] ?? new Big(0); |
|
|
|
currentValuesWithCurrencyEffect[rangeStartDateString] ?? new Big(0); |
|
|
|
|
|
|
|
const investmentValuesAccumulatedAtStartDateWithCurrencyEffect = |
|
|
|
investmentValuesAccumulatedWithCurrencyEffect[startDateString] ?? |
|
|
|
investmentValuesAccumulatedWithCurrencyEffect[rangeStartDateString] ?? |
|
|
|
new Big(0); |
|
|
|
|
|
|
|
const grossPerformanceAtDateRangeStartWithCurrencyEffect = |
|
|
@ -839,14 +844,12 @@ export class TWRPortfolioCalculator extends PortfolioCalculator { |
|
|
|
let average = new Big(0); |
|
|
|
let dayCount = 0; |
|
|
|
|
|
|
|
if (!this.chartDatesDescending) { |
|
|
|
this.chartDatesDescending = Object.keys(chartDateMap).sort().reverse(); |
|
|
|
} |
|
|
|
for (let i = this.chartDates.length - 1; i >= 0; i -= 1) { |
|
|
|
const date = this.chartDates[i]; |
|
|
|
|
|
|
|
for (const date of this.chartDatesDescending) { |
|
|
|
if (date > endDateString) { |
|
|
|
if (date > rangeEndDateString) { |
|
|
|
continue; |
|
|
|
} else if (date < startDateString) { |
|
|
|
} else if (date < rangeStartDateString) { |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
@ -869,13 +872,13 @@ export class TWRPortfolioCalculator extends PortfolioCalculator { |
|
|
|
} |
|
|
|
|
|
|
|
netPerformanceWithCurrencyEffectMap[dateRange] = |
|
|
|
netPerformanceValuesWithCurrencyEffect[endDateString]?.minus( |
|
|
|
netPerformanceValuesWithCurrencyEffect[rangeEndDateString]?.minus( |
|
|
|
// 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
|
|
|
|
// differs from the buying price.
|
|
|
|
dateRange === 'max' |
|
|
|
? new Big(0) |
|
|
|
: (netPerformanceValuesWithCurrencyEffect[startDateString] ?? |
|
|
|
: (netPerformanceValuesWithCurrencyEffect[rangeStartDateString] ?? |
|
|
|
new Big(0)) |
|
|
|
) ?? new Big(0); |
|
|
|
|
|
|
|