Browse Source

Bugfix/date range performance percentage of portfolio (#7911)

* Fix net performance percentage of date ranges

* Update changelog
pull/7915/head^2
Thomas Kaul 1 week ago
committed by GitHub
parent
commit
214074a41f
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 6
      CHANGELOG.md
  2. 55
      apps/api/src/app/portfolio/calculator/portfolio-calculator.ts
  3. 18
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts
  4. 8
      apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts

6
CHANGELOG.md

@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Fixed
- Fixed the net performance percentage of date ranges in the portfolio performance calculation by including the gross performance at the start date
## 3.71.0 - 2026-09-19 ## 3.71.0 - 2026-09-19
### Added ### Added

55
apps/api/src/app/portfolio/calculator/portfolio-calculator.ts

@ -794,15 +794,25 @@ export abstract class PortfolioCalculator {
const chart: HistoricalDataItem[] = []; const chart: HistoricalDataItem[] = [];
let grossPerformanceAtStartDate: number;
let grossPerformanceWithCurrencyEffectAtStartDate: number;
let netPerformanceAtStartDate: number; let netPerformanceAtStartDate: number;
let netPerformanceWithCurrencyEffectAtStartDate: number; let netPerformanceWithCurrencyEffectAtStartDate: number;
const totalInvestmentValuesWithCurrencyEffect: number[] = []; const timeWeightedInvestmentValues: number[] = [];
const timeWeightedInvestmentValuesWithCurrencyEffect: number[] = [];
for (const historicalDataItem of historicalData) { for (const historicalDataItem of historicalData) {
const date = resetHours(parseDate(historicalDataItem.date)); const date = resetHours(parseDate(historicalDataItem.date));
if (!isBefore(date, start) && !isAfter(date, end)) { if (!isBefore(date, start) && !isAfter(date, end)) {
if (!isNumber(netPerformanceAtStartDate)) { if (!isNumber(netPerformanceAtStartDate)) {
grossPerformanceAtStartDate =
historicalDataItem.value - historicalDataItem.totalInvestment;
grossPerformanceWithCurrencyEffectAtStartDate =
historicalDataItem.valueWithCurrencyEffect -
historicalDataItem.totalInvestmentValueWithCurrencyEffect;
netPerformanceAtStartDate = historicalDataItem.netPerformance; netPerformanceAtStartDate = historicalDataItem.netPerformance;
netPerformanceWithCurrencyEffectAtStartDate = netPerformanceWithCurrencyEffectAtStartDate =
@ -816,33 +826,48 @@ export abstract class PortfolioCalculator {
historicalDataItem.netPerformanceWithCurrencyEffect - historicalDataItem.netPerformanceWithCurrencyEffect -
netPerformanceWithCurrencyEffectAtStartDate; netPerformanceWithCurrencyEffectAtStartDate;
// Add the gross performance at the start date of the range to the
// investment of each day. Thus the range starts with the value of its
// first day, and subsequent buy and sell activities stay included.
if (historicalDataItem.totalInvestment > 0) {
timeWeightedInvestmentValues.push(
historicalDataItem.totalInvestment + grossPerformanceAtStartDate
);
}
if (historicalDataItem.totalInvestmentValueWithCurrencyEffect > 0) { if (historicalDataItem.totalInvestmentValueWithCurrencyEffect > 0) {
totalInvestmentValuesWithCurrencyEffect.push( timeWeightedInvestmentValuesWithCurrencyEffect.push(
historicalDataItem.totalInvestmentValueWithCurrencyEffect historicalDataItem.totalInvestmentValueWithCurrencyEffect +
grossPerformanceWithCurrencyEffectAtStartDate
); );
} }
const timeWeightedInvestmentValue = const timeWeightedInvestmentValue =
totalInvestmentValuesWithCurrencyEffect.length > 0 timeWeightedInvestmentValues.length > 0
? sum(totalInvestmentValuesWithCurrencyEffect) / ? sum(timeWeightedInvestmentValues) /
totalInvestmentValuesWithCurrencyEffect.length timeWeightedInvestmentValues.length
: 0;
const timeWeightedInvestmentValueWithCurrencyEffect =
timeWeightedInvestmentValuesWithCurrencyEffect.length > 0
? sum(timeWeightedInvestmentValuesWithCurrencyEffect) /
timeWeightedInvestmentValuesWithCurrencyEffect.length
: 0; : 0;
chart.push({ chart.push({
...historicalDataItem, ...historicalDataItem,
netPerformance: netPerformance: netPerformanceSinceStartDate,
historicalDataItem.netPerformance - netPerformanceAtStartDate,
netPerformanceWithCurrencyEffect: netPerformanceWithCurrencyEffect:
netPerformanceWithCurrencyEffectSinceStartDate, netPerformanceWithCurrencyEffectSinceStartDate,
netPerformanceInPercentage: netPerformanceInPercentage:
timeWeightedInvestmentValue === 0 timeWeightedInvestmentValue > 0
? 0 ? netPerformanceSinceStartDate / timeWeightedInvestmentValue
: netPerformanceSinceStartDate / timeWeightedInvestmentValue, : 0,
netPerformanceInPercentageWithCurrencyEffect: netPerformanceInPercentageWithCurrencyEffect:
timeWeightedInvestmentValue === 0 timeWeightedInvestmentValueWithCurrencyEffect > 0
? 0 ? netPerformanceWithCurrencyEffectSinceStartDate /
: netPerformanceWithCurrencyEffectSinceStartDate / timeWeightedInvestmentValueWithCurrencyEffect
timeWeightedInvestmentValue : 0
}); });
} }
} }

18
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts

@ -190,10 +190,14 @@ describe('PortfolioCalculator', () => {
netPerformance: new Big('27172.74').mul(0.97373), netPerformance: new Big('27172.74').mul(0.97373),
netPerformancePercentage: new Big('42.41978276196153750666'), netPerformancePercentage: new Big('42.41978276196153750666'),
netPerformancePercentageWithCurrencyEffectMap: { netPerformancePercentageWithCurrencyEffectMap: {
max: new Big('41.72313811883729606471') '1d': new Big('-0.04016229506406263535'),
max: new Big('41.72313811883729606471'),
ytd: new Big('-0.04016229506406263535')
}, },
netPerformanceWithCurrencyEffectMap: { netPerformanceWithCurrencyEffectMap: {
max: new Big('26516.208701400000064086') '1d': new Big('-556.443324'),
max: new Big('26516.208701400000064086'),
ytd: new Big('-556.443324')
}, },
quantity: new Big('1'), quantity: new Big('1'),
symbol: 'BTCUSD', symbol: 'BTCUSD',
@ -281,8 +285,8 @@ describe('PortfolioCalculator', () => {
'1d': { '1d': {
date: '2018-01-01', date: '2018-01-01',
netPerformance: -486.0860160000011, netPerformance: -486.0860160000011,
netPerformanceInPercentage: -1.5259683109961866, netPerformanceInPercentage: -0.035263202509112565,
netPerformanceInPercentageWithCurrencyEffect: -1.7468407881319952, netPerformanceInPercentageWithCurrencyEffect: -0.040162295064062624,
netPerformanceWithCurrencyEffect: -556.4433239999998, netPerformanceWithCurrencyEffect: -556.4433239999998,
totalInvestmentValueWithCurrencyEffect: 318.54266729999995, totalInvestmentValueWithCurrencyEffect: 318.54266729999995,
valueWithCurrencyEffect: 13298.425356 valueWithCurrencyEffect: 13298.425356
@ -290,7 +294,7 @@ describe('PortfolioCalculator', () => {
max: { max: {
date: '2018-01-01', date: '2018-01-01',
netPerformance: 26458.9121202, netPerformance: 26458.9121202,
netPerformanceInPercentage: 41.63298219956282, netPerformanceInPercentage: 42.50435329547954,
netPerformanceInPercentageWithCurrencyEffect: 41.72313811883715, netPerformanceInPercentageWithCurrencyEffect: 41.72313811883715,
netPerformanceWithCurrencyEffect: 26516.2087014, netPerformanceWithCurrencyEffect: 26516.2087014,
totalInvestmentValueWithCurrencyEffect: 318.54266729999995, totalInvestmentValueWithCurrencyEffect: 318.54266729999995,
@ -299,8 +303,8 @@ describe('PortfolioCalculator', () => {
ytd: { ytd: {
date: '2018-01-01', date: '2018-01-01',
netPerformance: -486.0860160000011, netPerformance: -486.0860160000011,
netPerformanceInPercentage: -1.5259683109961866, netPerformanceInPercentage: -0.035263202509112565,
netPerformanceInPercentageWithCurrencyEffect: -1.7468407881319952, netPerformanceInPercentageWithCurrencyEffect: -0.040162295064062624,
netPerformanceWithCurrencyEffect: -556.4433239999998, netPerformanceWithCurrencyEffect: -556.4433239999998,
totalInvestmentValueWithCurrencyEffect: 318.54266729999995, totalInvestmentValueWithCurrencyEffect: 318.54266729999995,
valueWithCurrencyEffect: 13298.425356 valueWithCurrencyEffect: 13298.425356

8
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts

@ -193,8 +193,8 @@ describe('PortfolioCalculator', () => {
'1d': { '1d': {
date: '2023-07-10', date: '2023-07-10',
netPerformance: -5.390000000000001, netPerformance: -5.390000000000001,
netPerformanceInPercentage: -0.018052113336459244, netPerformanceInPercentage: -0.015983630864124312,
netPerformanceInPercentageWithCurrencyEffect: -0.018052113336459244, netPerformanceInPercentageWithCurrencyEffect: -0.015983630864124312,
netPerformanceWithCurrencyEffect: -5.390000000000001, netPerformanceWithCurrencyEffect: -5.390000000000001,
totalInvestmentValueWithCurrencyEffect: 298.58, totalInvestmentValueWithCurrencyEffect: 298.58,
valueWithCurrencyEffect: 331.83 valueWithCurrencyEffect: 331.83
@ -211,8 +211,8 @@ describe('PortfolioCalculator', () => {
ytd: { ytd: {
date: '2023-07-10', date: '2023-07-10',
netPerformance: -7.68, netPerformance: -7.68,
netPerformanceInPercentage: -0.025721749614843515, netPerformanceInPercentage: -0.022620835910577012,
netPerformanceInPercentageWithCurrencyEffect: -0.025721749614843515, netPerformanceInPercentageWithCurrencyEffect: -0.022620835910577012,
netPerformanceWithCurrencyEffect: -7.68, netPerformanceWithCurrencyEffect: -7.68,
totalInvestmentValueWithCurrencyEffect: 298.58, totalInvestmentValueWithCurrencyEffect: 298.58,
valueWithCurrencyEffect: 331.83 valueWithCurrencyEffect: 331.83

Loading…
Cancel
Save