From 1fa96536e09f7a5d3b30ee93bd5c1718b1baa7cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20G=C3=BCnther?= Date: Tue, 16 Dec 2025 21:09:17 +0100 Subject: [PATCH] Task/include first and last date of each calendar year in getChartDateMap() (#6069) * Include first and last date of each calendar year in getChartDateMap() * Update changelog --- CHANGELOG.md | 4 ++++ .../calculator/portfolio-calculator.ts | 22 +++++++++++++++++++ .../portfolio-calculator-baln-buy.spec.ts | 10 +++++++++ ...ulator-btceur-in-base-currency-eur.spec.ts | 11 ++++++++++ .../roai/portfolio-calculator-btceur.spec.ts | 11 ++++++++++ .../roai/portfolio-calculator-btcusd.spec.ts | 11 ++++++++++ 6 files changed, 69 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5e803c65..5097993c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Included the calendar year boundaries in the portfolio calculations + ### Changed - Removed the deprecated _Angular CLI_ decorator (`decorate-angular-cli.js`) diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts index b3cedb00b..ee4219b58 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts @@ -44,11 +44,15 @@ import { plainToClass } from 'class-transformer'; import { differenceInDays, eachDayOfInterval, + eachYearOfInterval, endOfDay, + endOfYear, format, isAfter, isBefore, + isWithinInterval, min, + startOfYear, subDays } from 'date-fns'; import { isNumber, sortBy, sum, uniqBy } from 'lodash'; @@ -889,6 +893,24 @@ export abstract class PortfolioCalculator { } } + // Make sure the first and last date of each calendar year is present + const interval = { start: startDate, end: endDate }; + + for (const date of eachYearOfInterval(interval)) { + const yearStart = startOfYear(date); + const yearEnd = endOfYear(date); + + if (isWithinInterval(yearStart, interval)) { + // Add start of year (YYYY-01-01) + chartDateMap[format(yearStart, DATE_FORMAT)] = true; + } + + if (isWithinInterval(yearEnd, interval)) { + // Add end of year (YYYY-12-31) + chartDateMap[format(yearEnd, DATE_FORMAT)] = true; + } + } + return chartDateMap; } diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts index 84cab99e1..bfa4d06f3 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy.spec.ts @@ -109,6 +109,12 @@ describe('PortfolioCalculator', () => { const portfolioSnapshot = await portfolioCalculator.computeSnapshot(); + const historicalDataDates = portfolioSnapshot.historicalData.map( + ({ date }) => { + return date; + } + ); + const investments = portfolioCalculator.getInvestments(); const investmentsByMonth = portfolioCalculator.getInvestmentsByGroup({ @@ -170,8 +176,12 @@ describe('PortfolioCalculator', () => { totalLiabilitiesWithCurrencyEffect: new Big('0') }); + expect(historicalDataDates).not.toContain('2021-01-01'); + expect(historicalDataDates).not.toContain('2021-12-31'); + expect(portfolioSnapshot.historicalData.at(-1)).toMatchObject( expect.objectContaining({ + date: '2021-12-18', netPerformance: 23.05, netPerformanceInPercentage: 0.08437042459736457, netPerformanceInPercentageWithCurrencyEffect: 0.08437042459736457, diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur-in-base-currency-eur.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur-in-base-currency-eur.spec.ts index 1f64684a0..84ea6c251 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur-in-base-currency-eur.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur-in-base-currency-eur.spec.ts @@ -130,6 +130,17 @@ describe('PortfolioCalculator', () => { const portfolioSnapshot = await portfolioCalculator.computeSnapshot(); + const historicalDataDates = portfolioSnapshot.historicalData.map( + ({ date }) => { + return date; + } + ); + + expect(historicalDataDates).not.toContain('2021-01-01'); + expect(historicalDataDates).toContain('2021-12-31'); + expect(historicalDataDates).toContain('2022-01-01'); + expect(historicalDataDates).not.toContain('2022-12-31'); + expect(portfolioSnapshot.positions[0].fee).toEqual(new Big(4.46)); expect( portfolioSnapshot.positions[0].feeInBaseCurrency.toNumber() diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts index ce639b564..32b3f05c2 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btceur.spec.ts @@ -118,6 +118,12 @@ describe('PortfolioCalculator', () => { const portfolioSnapshot = await portfolioCalculator.computeSnapshot(); + const historicalDataDates = portfolioSnapshot.historicalData.map( + ({ date }) => { + return date; + } + ); + const investments = portfolioCalculator.getInvestments(); const investmentsByMonth = portfolioCalculator.getInvestmentsByGroup({ @@ -225,6 +231,11 @@ describe('PortfolioCalculator', () => { totalLiabilitiesWithCurrencyEffect: new Big('0') }); + expect(historicalDataDates).not.toContain('2021-01-01'); + expect(historicalDataDates).toContain('2021-12-31'); + expect(historicalDataDates).toContain('2022-01-01'); + expect(historicalDataDates).not.toContain('2022-12-31'); + expect(investments).toEqual([ { date: '2021-12-12', investment: new Big('44558.42') } ]); diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts index a7cbe746c..716ec7a59 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd.spec.ts @@ -118,6 +118,12 @@ describe('PortfolioCalculator', () => { const portfolioSnapshot = await portfolioCalculator.computeSnapshot(); + const historicalDataDates = portfolioSnapshot.historicalData.map( + ({ date }) => { + return date; + } + ); + const investments = portfolioCalculator.getInvestments(); const investmentsByMonth = portfolioCalculator.getInvestmentsByGroup({ @@ -225,6 +231,11 @@ describe('PortfolioCalculator', () => { totalLiabilitiesWithCurrencyEffect: new Big('0') }); + expect(historicalDataDates).not.toContain('2021-01-01'); + expect(historicalDataDates).toContain('2021-12-31'); + expect(historicalDataDates).toContain('2022-01-01'); + expect(historicalDataDates).not.toContain('2022-12-31'); + expect(investments).toEqual([ { date: '2021-12-12', investment: new Big('44558.42') } ]);