From 3165cd50ef22ba062e1c407d172da810ff69683c Mon Sep 17 00:00:00 2001 From: Andrea Bugeja <25248102+arogan178@users.noreply.github.com> Date: Wed, 21 Oct 2026 22:10:06 +0200 Subject: [PATCH] Accumulate the values of each holding right away in computeSnapshot() --- CHANGELOG.md | 6 ++ .../calculator/portfolio-calculator.ts | 82 +++++++------------ 2 files changed, 36 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f13393d1a..896843023b 100644 --- a/CHANGELOG.md +++ b/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/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Changed + +- Reduced the memory usage of the portfolio calculator for portfolios with many holdings + ## 3.90.0 - 2026-10-21 ### Changed diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts index 7d0c2ddda9..d4e9f44b4c 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts @@ -394,22 +394,6 @@ export abstract class PortfolioCalculator { [date: string]: AccumulatedValues; } = {}; - const valuesByAssetProfileIdentifier: { - [assetProfileIdentifier: string]: { - averageInvestmentValues: { [date: string]: Big }; - averageInvestmentValuesWithCurrencyEffect: { [date: string]: Big }; - currentValues: { [date: string]: Big }; - currentValuesWithCurrencyEffect: { [date: string]: Big }; - dividendValuesWithCurrencyEffect: { [date: string]: Big }; - investmentValuesAccumulated: { [date: string]: Big }; - investmentValuesAccumulatedWithCurrencyEffect: { [date: string]: Big }; - investmentValuesWithCurrencyEffect: { [date: string]: Big }; - netPerformanceValues: { [date: string]: Big }; - netPerformanceValuesWithCurrencyEffect: { [date: string]: Big }; - netWorthValuesWithCurrencyEffect: { [date: string]: Big }; - }; - } = {}; - for (const item of latestHoldingBalances.holdings) { const assetProfileIdentifier = getAssetProfileIdentifier(item); @@ -478,34 +462,33 @@ export abstract class PortfolioCalculator { // contributes nothing but its balance to the performance calculation. It // is therefore excluded from the value and the investment, while still // contributing to the net worth. - valuesByAssetProfileIdentifier[assetProfileIdentifier] = - isCashInBaseCurrency - ? { - averageInvestmentValues: {}, - averageInvestmentValuesWithCurrencyEffect: {}, - currentValues: {}, - currentValuesWithCurrencyEffect: {}, - dividendValuesWithCurrencyEffect: {}, - investmentValuesAccumulated: {}, - investmentValuesAccumulatedWithCurrencyEffect: {}, - investmentValuesWithCurrencyEffect: {}, - netPerformanceValues: {}, - netPerformanceValuesWithCurrencyEffect: {}, - netWorthValuesWithCurrencyEffect: currentValuesWithCurrencyEffect - } - : { - averageInvestmentValues, - averageInvestmentValuesWithCurrencyEffect, - currentValues, - currentValuesWithCurrencyEffect, - dividendValuesWithCurrencyEffect, - investmentValuesAccumulated, - investmentValuesAccumulatedWithCurrencyEffect, - investmentValuesWithCurrencyEffect, - netPerformanceValues, - netPerformanceValuesWithCurrencyEffect, - netWorthValuesWithCurrencyEffect: currentValuesWithCurrencyEffect - }; + const assetProfileValues = isCashInBaseCurrency + ? { + averageInvestmentValues: {}, + averageInvestmentValuesWithCurrencyEffect: {}, + currentValues: {}, + currentValuesWithCurrencyEffect: {}, + dividendValuesWithCurrencyEffect: {}, + investmentValuesAccumulated: {}, + investmentValuesAccumulatedWithCurrencyEffect: {}, + investmentValuesWithCurrencyEffect: {}, + netPerformanceValues: {}, + netPerformanceValuesWithCurrencyEffect: {}, + netWorthValuesWithCurrencyEffect: currentValuesWithCurrencyEffect + } + : { + averageInvestmentValues, + averageInvestmentValuesWithCurrencyEffect, + currentValues, + currentValuesWithCurrencyEffect, + dividendValuesWithCurrencyEffect, + investmentValuesAccumulated, + investmentValuesAccumulatedWithCurrencyEffect, + investmentValuesWithCurrencyEffect, + netPerformanceValues, + netPerformanceValuesWithCurrencyEffect, + netWorthValuesWithCurrencyEffect: currentValuesWithCurrencyEffect + }; positions.push({ averageInvestment, @@ -580,15 +563,10 @@ export abstract class PortfolioCalculator { ) { errors.push({ dataSource: item.dataSource, symbol: item.symbol }); } - } - - const assetProfileIdentifiers = Object.keys(valuesByAssetProfileIdentifier); - - for (const dateString of chartDates) { - for (const assetProfileIdentifier of assetProfileIdentifiers) { - const assetProfileValues = - valuesByAssetProfileIdentifier[assetProfileIdentifier]; + // Accumulate each holding right away, so that its values by date do not + // have to be kept for all holdings at once + for (const dateString of chartDates) { const currentValue = assetProfileValues.currentValues?.[dateString] ?? new Big(0);