- ) {}
+ ) {
+ addIcons({
+ ellipsisVertical
+ });
+ }
public ngOnInit() {
this.adminService
@@ -66,6 +72,13 @@ export class GfUserDetailDialogComponent implements OnDestroy, OnInit {
});
}
+ public deleteUser() {
+ this.dialogRef.close({
+ action: 'delete',
+ userId: this.data.userId
+ });
+ }
+
public onClose() {
this.dialogRef.close();
}
diff --git a/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html b/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html
index 60f6a2585..570dcf4d6 100644
--- a/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html
+++ b/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html
@@ -1,9 +1,28 @@
-
-
+
+
+
+
+
+
-
+
+
+
From c6d1260e07f64ba894cbd73b2d06bf95a6904b0c Mon Sep 17 00:00:00 2001
From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
Date: Tue, 3 Feb 2026 20:25:42 +0100
Subject: [PATCH 21/29] Release 2.235.0 (#6275)
---
CHANGELOG.md | 2 +-
package-lock.json | 4 ++--
package.json | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 30cc42eda..0883d78b6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,7 @@ 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
+## 2.235.0 - 2026-02-03
### Added
diff --git a/package-lock.json b/package-lock.json
index d5e3c39fa..7c472abc9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "ghostfolio",
- "version": "2.234.0",
+ "version": "2.235.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ghostfolio",
- "version": "2.234.0",
+ "version": "2.235.0",
"hasInstallScript": true,
"license": "AGPL-3.0",
"dependencies": {
diff --git a/package.json b/package.json
index 5452f3e95..e379b0f32 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "ghostfolio",
- "version": "2.234.0",
+ "version": "2.235.0",
"homepage": "https://ghostfol.io",
"license": "AGPL-3.0",
"repository": "https://github.com/ghostfolio/ghostfolio",
From 0365b9b614c89d0d3dacc6a6282f82d3a610a1e1 Mon Sep 17 00:00:00 2001
From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
Date: Wed, 4 Feb 2026 17:27:38 +0100
Subject: [PATCH 22/29] Task/upgrade stripe to version 20.3.0 (#6273)
* Upgrade stripe to version 20.3.0
* Update changelog
---
CHANGELOG.md | 6 ++++++
apps/api/src/app/subscription/subscription.service.ts | 2 +-
package-lock.json | 11 ++++-------
package.json | 2 +-
4 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0883d78b6..f64e6dc12 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
+
+- Upgraded `stripe` from version `20.1.0` to `20.3.0`
+
## 2.235.0 - 2026-02-03
### Added
diff --git a/apps/api/src/app/subscription/subscription.service.ts b/apps/api/src/app/subscription/subscription.service.ts
index b38b07bb4..2c0226937 100644
--- a/apps/api/src/app/subscription/subscription.service.ts
+++ b/apps/api/src/app/subscription/subscription.service.ts
@@ -35,7 +35,7 @@ export class SubscriptionService {
this.stripe = new Stripe(
this.configurationService.get('STRIPE_SECRET_KEY'),
{
- apiVersion: '2025-12-15.clover'
+ apiVersion: '2026-01-28.clover'
}
);
}
diff --git a/package-lock.json b/package-lock.json
index 7c472abc9..25cee635d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -85,7 +85,7 @@
"passport-openidconnect": "0.1.2",
"reflect-metadata": "0.2.2",
"rxjs": "7.8.1",
- "stripe": "20.1.0",
+ "stripe": "20.3.0",
"svgmap": "2.14.0",
"tablemark": "4.1.0",
"twitter-api-v2": "1.27.0",
@@ -32105,13 +32105,10 @@
}
},
"node_modules/stripe": {
- "version": "20.1.0",
- "resolved": "https://registry.npmjs.org/stripe/-/stripe-20.1.0.tgz",
- "integrity": "sha512-o1VNRuMkY76ZCq92U3EH3/XHm/WHp7AerpzDs4Zyo8uE5mFL4QUcv/2SudWsSnhBSp4moO2+ZoGCZ7mT8crPmQ==",
+ "version": "20.3.0",
+ "resolved": "https://registry.npmjs.org/stripe/-/stripe-20.3.0.tgz",
+ "integrity": "sha512-DYzcmV1MfYhycr1GwjCjeQVYk9Gu8dpxyTlu7qeDCsuguug7oUTxPsUQuZeSf/OPzK7pofqobvOKVqAwlpgf/Q==",
"license": "MIT",
- "dependencies": {
- "qs": "^6.11.0"
- },
"engines": {
"node": ">=16"
},
diff --git a/package.json b/package.json
index e379b0f32..9d7a37979 100644
--- a/package.json
+++ b/package.json
@@ -129,7 +129,7 @@
"passport-openidconnect": "0.1.2",
"reflect-metadata": "0.2.2",
"rxjs": "7.8.1",
- "stripe": "20.1.0",
+ "stripe": "20.3.0",
"svgmap": "2.14.0",
"tablemark": "4.1.0",
"twitter-api-v2": "1.27.0",
From af034e87c991bf9bfa9701e128a6a3718975481b Mon Sep 17 00:00:00 2001
From: Kenrick Tandrian <60643640+KenTandrian@users.noreply.github.com>
Date: Thu, 5 Feb 2026 23:25:20 +0700
Subject: [PATCH 23/29] Task/improve chart type safety (#6277)
* Improve chart type safety
---
.../benchmark-comparator.component.ts | 20 ++++---
.../investment-chart.component.ts | 50 ++++++++--------
libs/common/src/lib/chart-helper.ts | 55 +++++++++++-------
libs/ui/src/lib/chart/chart.registry.ts | 29 ++++++++++
libs/ui/src/lib/chart/index.ts | 1 +
.../fire-calculator.component.ts | 18 +++---
.../lib/line-chart/line-chart.component.ts | 58 ++++++++++---------
.../portfolio-proportion-chart.component.ts | 56 +++++++++++-------
.../treemap-chart/interfaces/interfaces.ts | 16 +++++
.../treemap-chart/treemap-chart.component.ts | 49 +++++++++-------
10 files changed, 221 insertions(+), 131 deletions(-)
create mode 100644 libs/ui/src/lib/chart/chart.registry.ts
create mode 100644 libs/ui/src/lib/chart/index.ts
diff --git a/apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts b/apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts
index 7f03ea57f..2ecefc311 100644
--- a/apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts
+++ b/apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts
@@ -1,6 +1,5 @@
import {
getTooltipOptions,
- getTooltipPositionerMapTop,
getVerticalHoverLinePlugin
} from '@ghostfolio/common/chart-helper';
import { primaryColorRgb, secondaryColorRgb } from '@ghostfolio/common/config';
@@ -15,12 +14,14 @@ import { LineChartItem, User } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { internalRoutes } from '@ghostfolio/common/routes/routes';
import { ColorScheme } from '@ghostfolio/common/types';
+import { registerChartConfiguration } from '@ghostfolio/ui/chart';
import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator';
import { CommonModule } from '@angular/common';
import {
ChangeDetectionStrategy,
Component,
+ type ElementRef,
EventEmitter,
Input,
OnChanges,
@@ -42,7 +43,7 @@ import {
PointElement,
TimeScale,
Tooltip,
- TooltipPosition
+ type TooltipOptions
} from 'chart.js';
import 'chartjs-adapter-date-fns';
import annotationPlugin from 'chartjs-plugin-annotation';
@@ -78,7 +79,7 @@ export class GfBenchmarkComparatorComponent implements OnChanges, OnDestroy {
@Output() benchmarkChanged = new EventEmitter
();
- @ViewChild('chartCanvas') chartCanvas;
+ @ViewChild('chartCanvas') chartCanvas: ElementRef;
public chart: Chart<'line'>;
public hasPermissionToAccessAdminControl: boolean;
@@ -96,8 +97,7 @@ export class GfBenchmarkComparatorComponent implements OnChanges, OnDestroy {
Tooltip
);
- Tooltip.positioners['top'] = (_elements, position: TooltipPosition) =>
- getTooltipPositionerMapTop(this.chart, position);
+ registerChartConfiguration();
addIcons({ arrowForwardOutline });
}
@@ -157,8 +157,10 @@ export class GfBenchmarkComparatorComponent implements OnChanges, OnDestroy {
if (this.chartCanvas) {
if (this.chart) {
this.chart.data = data;
+ this.chart.options.plugins ??= {};
this.chart.options.plugins.tooltip =
- this.getTooltipPluginConfiguration() as unknown;
+ this.getTooltipPluginConfiguration();
+
this.chart.update();
} else {
this.chart = new Chart(this.chartCanvas.nativeElement, {
@@ -196,7 +198,7 @@ export class GfBenchmarkComparatorComponent implements OnChanges, OnDestroy {
verticalHoverLine: {
color: `rgba(${getTextColor(this.colorScheme)}, 0.1)`
}
- } as unknown,
+ },
responsive: true,
scales: {
x: {
@@ -253,7 +255,7 @@ export class GfBenchmarkComparatorComponent implements OnChanges, OnDestroy {
}
}
- private getTooltipPluginConfiguration() {
+ private getTooltipPluginConfiguration(): Partial> {
return {
...getTooltipOptions({
colorScheme: this.colorScheme,
@@ -261,7 +263,7 @@ export class GfBenchmarkComparatorComponent implements OnChanges, OnDestroy {
unit: '%'
}),
mode: 'index',
- position: 'top' as unknown,
+ position: 'top',
xAlign: 'center',
yAlign: 'bottom'
};
diff --git a/apps/client/src/app/components/investment-chart/investment-chart.component.ts b/apps/client/src/app/components/investment-chart/investment-chart.component.ts
index 5492ddd4c..53d4f5693 100644
--- a/apps/client/src/app/components/investment-chart/investment-chart.component.ts
+++ b/apps/client/src/app/components/investment-chart/investment-chart.component.ts
@@ -1,6 +1,5 @@
import {
getTooltipOptions,
- getTooltipPositionerMapTop,
getVerticalHoverLinePlugin,
transformTickToAbbreviation
} from '@ghostfolio/common/chart-helper';
@@ -15,11 +14,13 @@ import {
import { LineChartItem } from '@ghostfolio/common/interfaces';
import { InvestmentItem } from '@ghostfolio/common/interfaces/investment-item.interface';
import { ColorScheme, GroupBy } from '@ghostfolio/common/types';
+import { registerChartConfiguration } from '@ghostfolio/ui/chart';
import { CommonModule } from '@angular/common';
import {
ChangeDetectionStrategy,
Component,
+ type ElementRef,
Input,
OnChanges,
OnDestroy,
@@ -34,12 +35,15 @@ import {
LineController,
LineElement,
PointElement,
+ type ScriptableLineSegmentContext,
TimeScale,
Tooltip,
- TooltipPosition
+ type TooltipOptions
} from 'chart.js';
import 'chartjs-adapter-date-fns';
-import annotationPlugin from 'chartjs-plugin-annotation';
+import annotationPlugin, {
+ type AnnotationOptions
+} from 'chartjs-plugin-annotation';
import { isAfter } from 'date-fns';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
@@ -62,7 +66,7 @@ export class GfInvestmentChartComponent implements OnChanges, OnDestroy {
@Input() locale = getLocale();
@Input() savingsRate = 0;
- @ViewChild('chartCanvas') chartCanvas;
+ @ViewChild('chartCanvas') chartCanvas: ElementRef;
public chart: Chart<'bar' | 'line'>;
private investments: InvestmentItem[];
@@ -81,8 +85,7 @@ export class GfInvestmentChartComponent implements OnChanges, OnDestroy {
Tooltip
);
- Tooltip.positioners['top'] = (_elements, position: TooltipPosition) =>
- getTooltipPositionerMapTop(this.chart, position);
+ registerChartConfiguration();
}
public ngOnChanges() {
@@ -121,12 +124,12 @@ export class GfInvestmentChartComponent implements OnChanges, OnDestroy {
}),
label: this.benchmarkDataLabel,
segment: {
- borderColor: (context: unknown) =>
+ borderColor: (context) =>
this.isInFuture(
context,
`rgba(${secondaryColorRgb.r}, ${secondaryColorRgb.g}, ${secondaryColorRgb.b}, 0.67)`
),
- borderDash: (context: unknown) => this.isInFuture(context, [2, 2])
+ borderDash: (context) => this.isInFuture(context, [2, 2])
},
stepped: true
},
@@ -143,12 +146,12 @@ export class GfInvestmentChartComponent implements OnChanges, OnDestroy {
label: $localize`Total Amount`,
pointRadius: 0,
segment: {
- borderColor: (context: unknown) =>
+ borderColor: (context) =>
this.isInFuture(
context,
`rgba(${primaryColorRgb.r}, ${primaryColorRgb.g}, ${primaryColorRgb.b}, 0.67)`
),
- borderDash: (context: unknown) => this.isInFuture(context, [2, 2])
+ borderDash: (context) => this.isInFuture(context, [2, 2])
}
}
]
@@ -157,17 +160,14 @@ export class GfInvestmentChartComponent implements OnChanges, OnDestroy {
if (this.chartCanvas) {
if (this.chart) {
this.chart.data = chartData;
+ this.chart.options.plugins ??= {};
this.chart.options.plugins.tooltip =
- this.getTooltipPluginConfiguration() as unknown;
+ this.getTooltipPluginConfiguration();
- if (
- this.savingsRate &&
- // @ts-ignore
- this.chart.options.plugins.annotation.annotations.savingsRate
- ) {
- // @ts-ignore
- this.chart.options.plugins.annotation.annotations.savingsRate.value =
- this.savingsRate;
+ const annotations = this.chart.options.plugins.annotation
+ .annotations as Record>;
+ if (this.savingsRate && annotations.savingsRate) {
+ annotations.savingsRate.value = this.savingsRate;
}
this.chart.update();
@@ -201,7 +201,7 @@ export class GfInvestmentChartComponent implements OnChanges, OnDestroy {
color: 'white',
content: $localize`Savings Rate`,
display: true,
- font: { size: '10px', weight: 'normal' },
+ font: { size: 10, weight: 'normal' },
padding: {
x: 4,
y: 2
@@ -229,7 +229,7 @@ export class GfInvestmentChartComponent implements OnChanges, OnDestroy {
verticalHoverLine: {
color: `rgba(${getTextColor(this.colorScheme)}, 0.1)`
}
- } as unknown,
+ },
responsive: true,
scales: {
x: {
@@ -286,7 +286,9 @@ export class GfInvestmentChartComponent implements OnChanges, OnDestroy {
}
}
- private getTooltipPluginConfiguration() {
+ private getTooltipPluginConfiguration(): Partial<
+ TooltipOptions<'bar' | 'line'>
+ > {
return {
...getTooltipOptions({
colorScheme: this.colorScheme,
@@ -296,13 +298,13 @@ export class GfInvestmentChartComponent implements OnChanges, OnDestroy {
unit: this.isInPercent ? '%' : undefined
}),
mode: 'index',
- position: 'top' as unknown,
+ position: 'top',
xAlign: 'center',
yAlign: 'bottom'
};
}
- private isInFuture(aContext: any, aValue: T) {
+ private isInFuture(aContext: ScriptableLineSegmentContext, aValue: T) {
return isAfter(new Date(aContext?.p1?.parsed?.x), new Date())
? aValue
: undefined;
diff --git a/libs/common/src/lib/chart-helper.ts b/libs/common/src/lib/chart-helper.ts
index da6473645..1f385e901 100644
--- a/libs/common/src/lib/chart-helper.ts
+++ b/libs/common/src/lib/chart-helper.ts
@@ -1,8 +1,11 @@
import type { ElementRef } from '@angular/core';
import type {
Chart,
- ChartTypeRegistry,
+ ChartType,
+ ControllerDatasetOptions,
Plugin,
+ Point,
+ TooltipOptions,
TooltipPosition
} from 'chart.js';
import { format } from 'date-fns';
@@ -21,7 +24,7 @@ export function formatGroupedDate({
date,
groupBy
}: {
- date: Date;
+ date: number;
groupBy: GroupBy;
}) {
if (groupBy === 'month') {
@@ -33,7 +36,7 @@ export function formatGroupedDate({
return format(date, DATE_FORMAT);
}
-export function getTooltipOptions({
+export function getTooltipOptions({
colorScheme,
currency = '',
groupBy,
@@ -45,35 +48,43 @@ export function getTooltipOptions({
groupBy?: GroupBy;
locale?: string;
unit?: string;
-}) {
+}): Partial> {
return {
backgroundColor: getBackgroundColor(colorScheme),
bodyColor: `rgb(${getTextColor(colorScheme)})`,
borderWidth: 1,
borderColor: `rgba(${getTextColor(colorScheme)}, 0.1)`,
+ // @ts-expect-error: no need to set all attributes in callbacks
callbacks: {
label: (context) => {
- let label = context.dataset.label ?? '';
+ let label = (context.dataset as ControllerDatasetOptions).label ?? '';
+
if (label) {
label += ': ';
}
- if (context.parsed.y !== null) {
+
+ const yPoint = (context.parsed as Point).y;
+
+ if (yPoint !== null) {
if (currency) {
- label += `${context.parsed.y.toLocaleString(locale, {
+ label += `${yPoint.toLocaleString(locale, {
maximumFractionDigits: 2,
minimumFractionDigits: 2
})} ${currency}`;
} else if (unit) {
- label += `${context.parsed.y.toFixed(2)} ${unit}`;
+ label += `${yPoint.toFixed(2)} ${unit}`;
} else {
- label += context.parsed.y.toFixed(2);
+ label += yPoint.toFixed(2);
}
}
+
return label;
},
title: (contexts) => {
- if (groupBy) {
- return formatGroupedDate({ groupBy, date: contexts[0].parsed.x });
+ const xPoint = (contexts[0].parsed as Point).x;
+
+ if (groupBy && xPoint !== null) {
+ return formatGroupedDate({ groupBy, date: xPoint });
}
return contexts[0].label;
@@ -98,16 +109,17 @@ export function getTooltipPositionerMapTop(
if (!position || !chart?.chartArea) {
return false;
}
+
return {
x: position.x,
y: chart.chartArea.top
};
}
-export function getVerticalHoverLinePlugin(
- chartCanvas: ElementRef,
+export function getVerticalHoverLinePlugin(
+ chartCanvas: ElementRef,
colorScheme: ColorScheme
-): Plugin {
+): Plugin {
return {
afterDatasetsDraw: (chart, _, options) => {
const active = chart.getActiveElements();
@@ -125,13 +137,16 @@ export function getVerticalHoverLinePlugin(
const xValue = active[0].element.x;
const context = chartCanvas.nativeElement.getContext('2d');
- context.lineWidth = width;
- context.strokeStyle = color;
- context.beginPath();
- context.moveTo(xValue, top);
- context.lineTo(xValue, bottom);
- context.stroke();
+ if (context) {
+ context.lineWidth = width;
+ context.strokeStyle = color;
+
+ context.beginPath();
+ context.moveTo(xValue, top);
+ context.lineTo(xValue, bottom);
+ context.stroke();
+ }
},
id: 'verticalHoverLine'
};
diff --git a/libs/ui/src/lib/chart/chart.registry.ts b/libs/ui/src/lib/chart/chart.registry.ts
new file mode 100644
index 000000000..465d6e716
--- /dev/null
+++ b/libs/ui/src/lib/chart/chart.registry.ts
@@ -0,0 +1,29 @@
+import { getTooltipPositionerMapTop } from '@ghostfolio/common/chart-helper';
+
+import { Tooltip, TooltipPositionerFunction, ChartType } from 'chart.js';
+
+interface VerticalHoverLinePluginOptions {
+ color?: string;
+ width?: number;
+}
+
+declare module 'chart.js' {
+ interface PluginOptionsByType {
+ verticalHoverLine: TType extends 'line' | 'bar'
+ ? VerticalHoverLinePluginOptions
+ : never;
+ }
+ interface TooltipPositionerMap {
+ top: TooltipPositionerFunction;
+ }
+}
+
+export function registerChartConfiguration() {
+ if (Tooltip.positioners['top']) {
+ return;
+ }
+
+ Tooltip.positioners.top = function (_elements, eventPosition) {
+ return getTooltipPositionerMapTop(this.chart, eventPosition);
+ };
+}
diff --git a/libs/ui/src/lib/chart/index.ts b/libs/ui/src/lib/chart/index.ts
new file mode 100644
index 000000000..2a3d3b358
--- /dev/null
+++ b/libs/ui/src/lib/chart/index.ts
@@ -0,0 +1 @@
+export * from './chart.registry';
diff --git a/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts b/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts
index 6b0bc8dcb..7461f6729 100644
--- a/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts
+++ b/libs/ui/src/lib/fire-calculator/fire-calculator.component.ts
@@ -38,6 +38,8 @@ import {
BarElement,
CategoryScale,
Chart,
+ type ChartData,
+ type ChartDataset,
LinearScale,
Tooltip
} from 'chart.js';
@@ -270,7 +272,7 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy {
this.chart.update();
} else {
- this.chart = new Chart(this.chartCanvas.nativeElement, {
+ this.chart = new Chart<'bar'>(this.chartCanvas.nativeElement, {
data: chartData,
options: {
plugins: {
@@ -280,7 +282,7 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy {
callbacks: {
footer: (items) => {
const totalAmount = items.reduce(
- (a, b) => a + b.parsed.y,
+ (a, b) => a + (b.parsed.y ?? 0),
0
);
@@ -302,8 +304,6 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy {
if (context.parsed.y !== null) {
label += new Intl.NumberFormat(this.locale, {
currency: this.currency,
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore: Only supported from ES2020 or later
currencyDisplay: 'code',
style: 'currency'
}).format(context.parsed.y);
@@ -345,9 +345,9 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy {
this.isLoading = false;
}
- private getChartData() {
+ private getChartData(): ChartData<'bar'> {
const currentYear = new Date().getFullYear();
- const labels = [];
+ const labels: number[] = [];
// Principal investment amount
const P: number = this.getP();
@@ -371,13 +371,13 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy {
labels.push(year);
}
- const datasetDeposit = {
+ const datasetDeposit: ChartDataset<'bar'> = {
backgroundColor: `rgb(${primaryColorRgb.r}, ${primaryColorRgb.g}, ${primaryColorRgb.b})`,
data: [],
label: $localize`Deposit`
};
- const datasetInterest = {
+ const datasetInterest: ChartDataset<'bar'> = {
backgroundColor: Color(
`rgb(${primaryColorRgb.r}, ${primaryColorRgb.g}, ${primaryColorRgb.b})`
)
@@ -387,7 +387,7 @@ export class GfFireCalculatorComponent implements OnChanges, OnDestroy {
label: $localize`Interest`
};
- const datasetSavings = {
+ const datasetSavings: ChartDataset<'bar'> = {
backgroundColor: Color(
`rgb(${primaryColorRgb.r}, ${primaryColorRgb.g}, ${primaryColorRgb.b})`
)
diff --git a/libs/ui/src/lib/line-chart/line-chart.component.ts b/libs/ui/src/lib/line-chart/line-chart.component.ts
index 0afef5959..dd972bc5a 100644
--- a/libs/ui/src/lib/line-chart/line-chart.component.ts
+++ b/libs/ui/src/lib/line-chart/line-chart.component.ts
@@ -1,6 +1,5 @@
import {
getTooltipOptions,
- getTooltipPositionerMapTop,
getVerticalHoverLinePlugin
} from '@ghostfolio/common/chart-helper';
import { primaryColorRgb, secondaryColorRgb } from '@ghostfolio/common/config';
@@ -19,12 +18,14 @@ import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
+ type ElementRef,
Input,
OnChanges,
OnDestroy,
ViewChild
} from '@angular/core';
import {
+ type AnimationsSpec,
Chart,
Filler,
LinearScale,
@@ -33,11 +34,13 @@ import {
PointElement,
TimeScale,
Tooltip,
- TooltipPosition
+ type TooltipOptions
} from 'chart.js';
import 'chartjs-adapter-date-fns';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
+import { registerChartConfiguration } from '../chart';
+
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [CommonModule, NgxSkeletonLoaderModule],
@@ -67,7 +70,7 @@ export class GfLineChartComponent
@Input() yMin: number;
@Input() yMinLabel: string;
- @ViewChild('chartCanvas') chartCanvas;
+ @ViewChild('chartCanvas') chartCanvas: ElementRef;
public chart: Chart<'line'>;
public isLoading = true;
@@ -85,8 +88,7 @@ export class GfLineChartComponent
Tooltip
);
- Tooltip.positioners['top'] = (_elements, position: TooltipPosition) =>
- getTooltipPositionerMapTop(this.chart, position);
+ registerChartConfiguration();
}
public ngAfterViewInit() {
@@ -117,9 +119,9 @@ export class GfLineChartComponent
private initialize() {
this.isLoading = true;
- const benchmarkPrices = [];
+ const benchmarkPrices: number[] = [];
const labels: string[] = [];
- const marketPrices = [];
+ const marketPrices: number[] = [];
this.historicalDataItems?.forEach((historicalDataItem, index) => {
benchmarkPrices.push(this.benchmarkDataItems?.[index]?.value);
@@ -129,11 +131,14 @@ export class GfLineChartComponent
const gradient = this.chartCanvas?.nativeElement
?.getContext('2d')
- .createLinearGradient(
+ ?.createLinearGradient(
0,
0,
0,
- (this.chartCanvas.nativeElement.parentNode.offsetHeight * 4) / 5
+ ((this.chartCanvas.nativeElement.parentNode as HTMLElement)
+ .offsetHeight *
+ 4) /
+ 5
);
if (gradient && this.showGradient) {
@@ -169,27 +174,26 @@ export class GfLineChartComponent
};
if (this.chartCanvas) {
+ const animations = {
+ x: this.getAnimationConfigurationForAxis({ labels, axis: 'x' }),
+ y: this.getAnimationConfigurationForAxis({ labels, axis: 'y' })
+ };
+
if (this.chart) {
this.chart.data = data;
+ this.chart.options.plugins ??= {};
this.chart.options.plugins.tooltip =
- this.getTooltipPluginConfiguration() as unknown;
- this.chart.options.animation =
- this.isAnimated &&
- ({
- x: this.getAnimationConfigurationForAxis({ labels, axis: 'x' }),
- y: this.getAnimationConfigurationForAxis({ labels, axis: 'y' })
- } as unknown);
+ this.getTooltipPluginConfiguration();
+ this.chart.options.animations = this.isAnimated
+ ? animations
+ : undefined;
+
this.chart.update();
} else {
this.chart = new Chart(this.chartCanvas.nativeElement, {
data,
options: {
- animation:
- this.isAnimated &&
- ({
- x: this.getAnimationConfigurationForAxis({ labels, axis: 'x' }),
- y: this.getAnimationConfigurationForAxis({ labels, axis: 'y' })
- } as unknown),
+ animations: this.isAnimated ? animations : undefined,
aspectRatio: 16 / 9,
elements: {
point: {
@@ -208,7 +212,7 @@ export class GfLineChartComponent
verticalHoverLine: {
color: `rgba(${getTextColor(this.colorScheme)}, 0.1)`
}
- } as unknown,
+ },
scales: {
x: {
border: {
@@ -298,7 +302,7 @@ export class GfLineChartComponent
}: {
axis: 'x' | 'y';
labels: string[];
- }) {
+ }): Partial[string]> {
const delayBetweenPoints = this.ANIMATION_DURATION / labels.length;
return {
@@ -308,7 +312,7 @@ export class GfLineChartComponent
}
context[`${axis}Started`] = true;
- return context.index * delayBetweenPoints;
+ return context.dataIndex * delayBetweenPoints;
},
duration: delayBetweenPoints,
easing: 'linear',
@@ -317,7 +321,7 @@ export class GfLineChartComponent
};
}
- private getTooltipPluginConfiguration() {
+ private getTooltipPluginConfiguration(): Partial> {
return {
...getTooltipOptions({
colorScheme: this.colorScheme,
@@ -326,7 +330,7 @@ export class GfLineChartComponent
unit: this.unit
}),
mode: 'index',
- position: 'top' as unknown,
+ position: 'top',
xAlign: 'center',
yAlign: 'bottom'
};
diff --git a/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts b/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts
index fb11897eb..7d0203e9c 100644
--- a/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts
+++ b/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts
@@ -22,11 +22,16 @@ import {
} from '@angular/core';
import { DataSource } from '@prisma/client';
import { Big } from 'big.js';
-import { ChartConfiguration, Tooltip } from 'chart.js';
-import { LinearScale } from 'chart.js';
-import { ArcElement } from 'chart.js';
-import { DoughnutController } from 'chart.js';
-import { Chart } from 'chart.js';
+import {
+ ArcElement,
+ Chart,
+ type ChartData,
+ type ChartDataset,
+ DoughnutController,
+ LinearScale,
+ Tooltip,
+ type TooltipOptions
+} from 'chart.js';
import ChartDataLabels from 'chartjs-plugin-datalabels';
import { isUUID } from 'class-validator';
import Color from 'color';
@@ -286,7 +291,7 @@ export class GfPortfolioProportionChartComponent
});
});
- const datasets: ChartConfiguration<'doughnut'>['data']['datasets'] = [
+ const datasets: ChartDataset<'doughnut'>[] = [
{
backgroundColor: chartDataSorted.map(([, item]) => {
return item.color;
@@ -324,7 +329,7 @@ export class GfPortfolioProportionChartComponent
datasets[1].data[1] = Number.MAX_SAFE_INTEGER;
}
- const data: ChartConfiguration<'doughnut'>['data'] = {
+ const data: ChartData<'doughnut'> = {
datasets,
labels
};
@@ -332,9 +337,10 @@ export class GfPortfolioProportionChartComponent
if (this.chartCanvas) {
if (this.chart) {
this.chart.data = data;
- this.chart.options.plugins.tooltip = this.getTooltipPluginConfiguration(
- data
- ) as unknown;
+ this.chart.options.plugins ??= {};
+ this.chart.options.plugins.tooltip =
+ this.getTooltipPluginConfiguration(data);
+
this.chart.update();
} else {
this.chart = new Chart<'doughnut'>(this.chartCanvas.nativeElement, {
@@ -345,21 +351,22 @@ export class GfPortfolioProportionChartComponent
layout: {
padding: this.showLabels === true ? 100 : 0
},
- onClick: (event, activeElements) => {
+ onClick: (_, activeElements, chart) => {
try {
const dataIndex = activeElements[0].index;
- const symbol: string = event.chart.data.labels[dataIndex];
+ const symbol = chart.data.labels?.[dataIndex] as string;
- const dataSource = this.data[symbol]?.dataSource;
+ const dataSource = this.data[symbol].dataSource;
- this.proportionChartClicked.emit({ dataSource, symbol });
+ if (dataSource) {
+ this.proportionChartClicked.emit({ dataSource, symbol });
+ }
} catch {}
},
onHover: (event, chartElement) => {
if (this.cursor) {
- event.native.target.style.cursor = chartElement[0]
- ? this.cursor
- : 'default';
+ (event.native?.target as HTMLElement).style.cursor =
+ chartElement[0] ? this.cursor : 'default';
}
},
plugins: {
@@ -392,7 +399,7 @@ export class GfPortfolioProportionChartComponent
legend: { display: false },
tooltip: this.getTooltipPluginConfiguration(data)
}
- } as unknown,
+ },
plugins: [ChartDataLabels],
type: 'doughnut'
});
@@ -419,19 +426,24 @@ export class GfPortfolioProportionChartComponent
];
}
- private getTooltipPluginConfiguration(data: ChartConfiguration['data']) {
+ private getTooltipPluginConfiguration(
+ data: ChartData<'doughnut'>
+ ): Partial> {
return {
...getTooltipOptions({
colorScheme: this.colorScheme,
currency: this.baseCurrency,
locale: this.locale
}),
+ // @ts-expect-error: no need to set all attributes in callbacks
callbacks: {
label: (context) => {
const labelIndex =
(data.datasets[context.datasetIndex - 1]?.data?.length ?? 0) +
context.dataIndex;
- let symbol = context.chart.data.labels?.[labelIndex] ?? '';
+
+ let symbol =
+ (context.chart.data.labels?.[labelIndex] as string) ?? '';
if (symbol === this.OTHER_KEY) {
symbol = $localize`Other`;
@@ -439,9 +451,10 @@ export class GfPortfolioProportionChartComponent
symbol = $localize`No data available`;
}
- const name = translate(this.data[symbol as string]?.name);
+ const name = translate(this.data[symbol]?.name);
let sum = 0;
+
for (const item of context.dataset.data) {
sum += item;
}
@@ -454,6 +467,7 @@ export class GfPortfolioProportionChartComponent
return [`${name ?? symbol}`, `${percentage.toFixed(2)}%`];
} else {
const value = context.raw as number;
+
return [
`${name ?? symbol}`,
`${value.toLocaleString(this.locale, {
diff --git a/libs/ui/src/lib/treemap-chart/interfaces/interfaces.ts b/libs/ui/src/lib/treemap-chart/interfaces/interfaces.ts
index bb673ed64..e8d182adb 100644
--- a/libs/ui/src/lib/treemap-chart/interfaces/interfaces.ts
+++ b/libs/ui/src/lib/treemap-chart/interfaces/interfaces.ts
@@ -1,5 +1,21 @@
+import { PortfolioPosition } from '@ghostfolio/common/interfaces';
+
+import { ScriptableContext, TooltipItem } from 'chart.js';
+import { TreemapDataPoint } from 'chartjs-chart-treemap';
+
export interface GetColorParams {
annualizedNetPerformancePercent: number;
negativeNetPerformancePercentsRange: { max: number; min: number };
positiveNetPerformancePercentsRange: { max: number; min: number };
}
+
+interface GfTreemapDataPoint extends TreemapDataPoint {
+ _data: PortfolioPosition;
+}
+
+export interface GfTreemapScriptableContext extends ScriptableContext<'treemap'> {
+ raw: GfTreemapDataPoint;
+}
+export interface GfTreemapTooltipItem extends TooltipItem<'treemap'> {
+ raw: GfTreemapDataPoint;
+}
diff --git a/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts b/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts
index 6ae958b83..ce85c300e 100644
--- a/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts
+++ b/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts
@@ -25,7 +25,7 @@ import {
} from '@angular/core';
import { DataSource } from '@prisma/client';
import { Big } from 'big.js';
-import { ChartConfiguration } from 'chart.js';
+import type { TooltipOptions, ChartData } from 'chart.js';
import { LinearScale } from 'chart.js';
import { Chart, Tooltip } from 'chart.js';
import { TreemapController, TreemapElement } from 'chartjs-chart-treemap';
@@ -35,7 +35,11 @@ import { orderBy } from 'lodash';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import OpenColor from 'open-color';
-import { GetColorParams } from './interfaces/interfaces';
+import type {
+ GetColorParams,
+ GfTreemapScriptableContext,
+ GfTreemapTooltipItem
+} from './interfaces/interfaces';
const { gray, green, red } = OpenColor;
@@ -198,10 +202,10 @@ export class GfTreemapChartComponent
min: Math.min(...negativeNetPerformancePercents)
};
- const data: ChartConfiguration<'treemap'>['data'] = {
+ const data: ChartData<'treemap'> = {
datasets: [
{
- backgroundColor: (context) => {
+ backgroundColor: (context: GfTreemapScriptableContext) => {
let annualizedNetPerformancePercent =
getAnnualizedPerformancePercent({
daysInMarket: differenceInDays(
@@ -232,7 +236,7 @@ export class GfTreemapChartComponent
key: 'allocationInPercentage',
labels: {
align: 'left',
- color: (context) => {
+ color: (context: GfTreemapScriptableContext) => {
let annualizedNetPerformancePercent =
getAnnualizedPerformancePercent({
daysInMarket: differenceInDays(
@@ -261,7 +265,7 @@ export class GfTreemapChartComponent
},
display: true,
font: [{ size: 16 }, { lineHeight: 1.5, size: 14 }],
- formatter: ({ raw }) => {
+ formatter: ({ raw }: GfTreemapScriptableContext) => {
// Round to 4 decimal places
let netPerformancePercentWithCurrencyEffect =
Math.round(
@@ -286,32 +290,35 @@ export class GfTreemapChartComponent
position: 'top'
},
spacing: 1,
+ // @ts-expect-error: should be PortfolioPosition[]
tree: this.holdings
}
]
- } as any;
+ };
if (this.chartCanvas) {
if (this.chart) {
this.chart.data = data;
+ this.chart.options.plugins ??= {};
this.chart.options.plugins.tooltip =
- this.getTooltipPluginConfiguration() as unknown;
+ this.getTooltipPluginConfiguration();
+
this.chart.update();
} else {
- this.chart = new Chart(this.chartCanvas.nativeElement, {
+ this.chart = new Chart<'treemap'>(this.chartCanvas.nativeElement, {
data,
options: {
animation: false,
- onClick: (event, activeElements) => {
+ onClick: (_, activeElements, chart: Chart<'treemap'>) => {
try {
const dataIndex = activeElements[0].index;
const datasetIndex = activeElements[0].datasetIndex;
const dataset = orderBy(
- event.chart.data.datasets[datasetIndex].tree,
+ chart.data.datasets[datasetIndex].tree,
['allocationInPercentage'],
['desc']
- );
+ ) as PortfolioPosition[];
const dataSource: DataSource = dataset[dataIndex].dataSource;
const symbol: string = dataset[dataIndex].symbol;
@@ -321,15 +328,14 @@ export class GfTreemapChartComponent
},
onHover: (event, chartElement) => {
if (this.cursor) {
- event.native.target.style.cursor = chartElement[0]
- ? this.cursor
- : 'default';
+ (event.native?.target as HTMLElement).style.cursor =
+ chartElement[0] ? this.cursor : 'default';
}
},
plugins: {
tooltip: this.getTooltipPluginConfiguration()
}
- } as unknown,
+ },
type: 'treemap'
});
}
@@ -338,16 +344,17 @@ export class GfTreemapChartComponent
this.isLoading = false;
}
- private getTooltipPluginConfiguration() {
+ private getTooltipPluginConfiguration(): Partial> {
return {
...getTooltipOptions({
colorScheme: this.colorScheme,
currency: this.baseCurrency,
locale: this.locale
}),
+ // @ts-expect-error: no need to set all attributes in callbacks
callbacks: {
- label: ({ raw }) => {
- const allocationInPercentage = `${((raw._data.allocationInPercentage as number) * 100).toFixed(2)}%`;
+ label: ({ raw }: GfTreemapTooltipItem) => {
+ const allocationInPercentage = `${(raw._data.allocationInPercentage * 100).toFixed(2)}%`;
const name = raw._data.name;
const sign =
raw._data.netPerformancePercentWithCurrencyEffect > 0 ? '+' : '';
@@ -356,11 +363,11 @@ export class GfTreemapChartComponent
const netPerformanceInPercentageWithSign = `${sign}${(raw._data.netPerformancePercentWithCurrencyEffect * 100).toFixed(2)}%`;
if (raw._data.valueInBaseCurrency !== null) {
- const value = raw._data.valueInBaseCurrency as number;
+ const value = raw._data.valueInBaseCurrency;
return [
`${name ?? symbol} (${allocationInPercentage})`,
- `${value.toLocaleString(this.locale, {
+ `${value?.toLocaleString(this.locale, {
maximumFractionDigits: 2,
minimumFractionDigits: 2
})} ${this.baseCurrency}`,
From eb6177d2e56aebaed2b35e2811f5d944c22667c4 Mon Sep 17 00:00:00 2001
From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
Date: Thu, 5 Feb 2026 19:40:41 +0100
Subject: [PATCH 24/29] Bugfix/fix exception when fetching top holdings in
Yahoo Finance service (#6279)
* Add missing guard
* Update changelog
---
CHANGELOG.md | 4 +++
.../yahoo-finance/yahoo-finance.service.ts | 34 +++++++++----------
2 files changed, 21 insertions(+), 17 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f64e6dc12..e4d88d75f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Upgraded `stripe` from version `20.1.0` to `20.3.0`
+### Fixed
+
+- Fixed an exception when fetching the top holdings for ETF and mutual fund assets from _Yahoo Finance_
+
## 2.235.0 - 2026-02-03
### Added
diff --git a/apps/api/src/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service.ts b/apps/api/src/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service.ts
index 97c875360..c83e35503 100644
--- a/apps/api/src/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service.ts
+++ b/apps/api/src/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service.ts
@@ -206,26 +206,26 @@ export class YahooFinanceDataEnhancerService implements DataEnhancerInterface {
);
if (['ETF', 'MUTUALFUND'].includes(assetSubClass)) {
- response.sectors = [];
-
- for (const sectorWeighting of assetProfile.topHoldings
- ?.sectorWeightings ?? []) {
- for (const [sector, weight] of Object.entries(sectorWeighting)) {
- response.sectors.push({
+ response.holdings =
+ assetProfile.topHoldings?.holdings?.map(
+ ({ holdingName, holdingPercent }) => {
+ return {
+ name: this.formatName({ longName: holdingName }),
+ weight: holdingPercent
+ };
+ }
+ ) ?? [];
+
+ response.sectors = (
+ assetProfile.topHoldings?.sectorWeightings ?? []
+ ).flatMap((sectorWeighting) => {
+ return Object.entries(sectorWeighting).map(([sector, weight]) => {
+ return {
name: this.parseSector(sector),
weight: weight as number
- });
- }
- }
-
- response.holdings = assetProfile.topHoldings.holdings.map(
- ({ holdingName, holdingPercent }) => {
- return {
- name: this.formatName({ longName: holdingName }),
- weight: holdingPercent
};
- }
- );
+ });
+ });
} else if (
assetSubClass === 'STOCK' &&
assetProfile.summaryProfile?.country
From 4ff372f0205366e1afdb36c8c8ade4dbb491878b Mon Sep 17 00:00:00 2001
From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
Date: Thu, 5 Feb 2026 20:24:04 +0100
Subject: [PATCH 25/29] Task/remove deprecated transaction count in get admin
endpoint (#6281)
* Remove deprecated transaction count
* Update changelog
---
CHANGELOG.md | 1 +
apps/api/src/app/admin/admin.service.ts | 1 -
libs/common/src/lib/interfaces/admin-data.interface.ts | 4 ----
3 files changed, 1 insertion(+), 5 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e4d88d75f..5e1c8b91a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
+- Removed the deprecated `transactionCount` in the endpoint `GET api/v1/admin`
- Upgraded `stripe` from version `20.1.0` to `20.3.0`
### Fixed
diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts
index cd18eb239..2cc8bbfb8 100644
--- a/apps/api/src/app/admin/admin.service.ts
+++ b/apps/api/src/app/admin/admin.service.ts
@@ -186,7 +186,6 @@ export class AdminService {
dataProviders,
settings,
userCount,
- transactionCount: activitiesCount,
version: environment.version
};
}
diff --git a/libs/common/src/lib/interfaces/admin-data.interface.ts b/libs/common/src/lib/interfaces/admin-data.interface.ts
index 63588300c..dd25b516d 100644
--- a/libs/common/src/lib/interfaces/admin-data.interface.ts
+++ b/libs/common/src/lib/interfaces/admin-data.interface.ts
@@ -7,10 +7,6 @@ export interface AdminData {
useForExchangeRates: boolean;
})[];
settings: { [key: string]: boolean | object | string | string[] };
-
- /** @deprecated use activitiesCount instead */
- transactionCount: number;
-
userCount: number;
version: string;
}
From bf9f8d49e9301c676cb2195f8950812ad4b77e82 Mon Sep 17 00:00:00 2001
From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
Date: Thu, 5 Feb 2026 20:28:23 +0100
Subject: [PATCH 26/29] Release 2.236.0 (#6282)
---
CHANGELOG.md | 2 +-
package-lock.json | 4 ++--
package.json | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5e1c8b91a..53c9b42d8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,7 @@ 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
+## 2.236.0 - 2026-02-05
### Changed
diff --git a/package-lock.json b/package-lock.json
index 25cee635d..b90f9050f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "ghostfolio",
- "version": "2.235.0",
+ "version": "2.236.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ghostfolio",
- "version": "2.235.0",
+ "version": "2.236.0",
"hasInstallScript": true,
"license": "AGPL-3.0",
"dependencies": {
diff --git a/package.json b/package.json
index 9d7a37979..0aa520beb 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "ghostfolio",
- "version": "2.235.0",
+ "version": "2.236.0",
"homepage": "https://ghostfol.io",
"license": "AGPL-3.0",
"repository": "https://github.com/ghostfolio/ghostfolio",
From 5adacda88fd4010ef217409ab635eb6c2aed0625 Mon Sep 17 00:00:00 2001
From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
Date: Fri, 6 Feb 2026 12:22:39 +0100
Subject: [PATCH 27/29] Task/remove deprecated session id from create stripe
checkout session response (#6280)
* Remove deprecated sessionId
---
apps/api/src/app/subscription/subscription.service.ts | 1 -
.../create-stripe-checkout-session-response.interface.ts | 3 ---
2 files changed, 4 deletions(-)
diff --git a/apps/api/src/app/subscription/subscription.service.ts b/apps/api/src/app/subscription/subscription.service.ts
index 2c0226937..689ee3e6a 100644
--- a/apps/api/src/app/subscription/subscription.service.ts
+++ b/apps/api/src/app/subscription/subscription.service.ts
@@ -100,7 +100,6 @@ export class SubscriptionService {
);
return {
- sessionId: session.id,
sessionUrl: session.url
};
}
diff --git a/libs/common/src/lib/interfaces/responses/create-stripe-checkout-session-response.interface.ts b/libs/common/src/lib/interfaces/responses/create-stripe-checkout-session-response.interface.ts
index 1222ac6e9..8ac1a8279 100644
--- a/libs/common/src/lib/interfaces/responses/create-stripe-checkout-session-response.interface.ts
+++ b/libs/common/src/lib/interfaces/responses/create-stripe-checkout-session-response.interface.ts
@@ -1,6 +1,3 @@
export interface CreateStripeCheckoutSessionResponse {
- /** @deprecated */
- sessionId: string;
-
sessionUrl: string;
}
From 348ee5de8d6e62f3271327090d9765633b4bae13 Mon Sep 17 00:00:00 2001
From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
Date: Sat, 7 Feb 2026 14:04:10 +0100
Subject: [PATCH 28/29] Task/add missing transform data source interceptors in
market data controller (#6287)
* Add missing transform data source interceptors
---
.../app/endpoints/market-data/market-data.controller.ts | 7 ++++++-
.../src/app/endpoints/market-data/market-data.module.ts | 6 +++++-
2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/apps/api/src/app/endpoints/market-data/market-data.controller.ts b/apps/api/src/app/endpoints/market-data/market-data.controller.ts
index 987d34918..0dae82d2c 100644
--- a/apps/api/src/app/endpoints/market-data/market-data.controller.ts
+++ b/apps/api/src/app/endpoints/market-data/market-data.controller.ts
@@ -2,6 +2,8 @@ import { AdminService } from '@ghostfolio/api/app/admin/admin.service';
import { SymbolService } from '@ghostfolio/api/app/symbol/symbol.service';
import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator';
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
+import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor';
+import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor';
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
import {
@@ -28,7 +30,8 @@ import {
Param,
Post,
Query,
- UseGuards
+ UseGuards,
+ UseInterceptors
} from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
@@ -86,6 +89,8 @@ export class MarketDataController {
@Get(':dataSource/:symbol')
@UseGuards(AuthGuard('jwt'))
+ @UseInterceptors(TransformDataSourceInRequestInterceptor)
+ @UseInterceptors(TransformDataSourceInResponseInterceptor)
public async getMarketDataBySymbol(
@Param('dataSource') dataSource: DataSource,
@Param('symbol') symbol: string
diff --git a/apps/api/src/app/endpoints/market-data/market-data.module.ts b/apps/api/src/app/endpoints/market-data/market-data.module.ts
index a8b355de3..d5d64673d 100644
--- a/apps/api/src/app/endpoints/market-data/market-data.module.ts
+++ b/apps/api/src/app/endpoints/market-data/market-data.module.ts
@@ -1,5 +1,7 @@
import { AdminModule } from '@ghostfolio/api/app/admin/admin.module';
import { SymbolModule } from '@ghostfolio/api/app/symbol/symbol.module';
+import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module';
+import { TransformDataSourceInResponseModule } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.module';
import { MarketDataModule as MarketDataServiceModule } from '@ghostfolio/api/services/market-data/market-data.module';
import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module';
@@ -13,7 +15,9 @@ import { MarketDataController } from './market-data.controller';
AdminModule,
MarketDataServiceModule,
SymbolModule,
- SymbolProfileModule
+ SymbolProfileModule,
+ TransformDataSourceInRequestModule,
+ TransformDataSourceInResponseModule
]
})
export class MarketDataModule {}
From 63e75942a8e227dff18fe7667bc0cea30fab58e4 Mon Sep 17 00:00:00 2001
From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
Date: Sat, 7 Feb 2026 14:51:09 +0100
Subject: [PATCH 29/29] Task/remove deprecated transaction count in portfolio
calculator and service (#6288)
* Remove deprecated transactionCount in portfolio calculator and service
* Update changelog
---
CHANGELOG.md | 6 +++
apps/api/src/app/account/account.service.ts | 6 +--
.../calculator/portfolio-calculator.ts | 7 +--
...tfolio-calculator-baln-buy-and-buy.spec.ts | 1 -
...aln-buy-and-sell-in-two-activities.spec.ts | 1 -
...folio-calculator-baln-buy-and-sell.spec.ts | 1 -
.../portfolio-calculator-baln-buy.spec.ts | 1 -
.../roai/portfolio-calculator-btceur.spec.ts | 1 -
...ator-btcusd-buy-and-sell-partially.spec.ts | 1 -
.../roai/portfolio-calculator-btcusd.spec.ts | 1 -
.../roai/portfolio-calculator-cash.spec.ts | 1 -
.../portfolio-calculator-googl-buy.spec.ts | 1 -
...-calculator-msft-buy-with-dividend.spec.ts | 3 +-
...ulator-novn-buy-and-sell-partially.spec.ts | 1 -
...folio-calculator-novn-buy-and-sell.spec.ts | 1 -
.../portfolio-calculator-valuable.spec.ts | 1 -
.../transaction-point-symbol.interface.ts | 3 --
.../src/app/portfolio/portfolio.service.ts | 8 ----
apps/api/src/helper/object.helper.spec.ts | 48 +++++++++----------
.../account-detail-dialog.component.ts | 6 +--
.../account-detail-dialog.html | 2 +-
.../portfolio-position.interface.ts | 4 --
.../responses/accounts-response.interface.ts | 3 --
.../src/lib/models/timeline-position.ts | 3 --
.../src/lib/types/account-with-value.type.ts | 4 --
.../accounts-table.component.html | 6 +--
.../accounts-table.component.stories.ts | 6 +--
libs/ui/src/lib/mocks/holdings.ts | 7 ---
28 files changed, 46 insertions(+), 88 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 53c9b42d8..094266869 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
+
+- Removed the `transactionCount` in the portfolio calculator and service
+
## 2.236.0 - 2026-02-05
### Changed
diff --git a/apps/api/src/app/account/account.service.ts b/apps/api/src/app/account/account.service.ts
index 398a89bb9..e1b01a6ed 100644
--- a/apps/api/src/app/account/account.service.ts
+++ b/apps/api/src/app/account/account.service.ts
@@ -150,15 +150,15 @@ export class AccountService {
});
return accounts.map((account) => {
- let transactionCount = 0;
+ let activitiesCount = 0;
for (const { isDraft } of account.activities) {
if (!isDraft) {
- transactionCount += 1;
+ activitiesCount += 1;
}
}
- const result = { ...account, transactionCount };
+ const result = { ...account, activitiesCount };
delete result.activities;
diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts
index 9612ad1c4..2e58a4ef5 100644
--- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts
+++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts
@@ -445,7 +445,6 @@ export abstract class PortfolioCalculator {
quantity: item.quantity,
symbol: item.symbol,
tags: item.tags,
- transactionCount: item.transactionCount,
valueInBaseCurrency: new Big(marketPriceInBaseCurrency).mul(
item.quantity
)
@@ -1005,8 +1004,7 @@ export abstract class PortfolioCalculator {
oldAccumulatedSymbol.feeInBaseCurrency.plus(feeInBaseCurrency),
includeInHoldings: oldAccumulatedSymbol.includeInHoldings,
quantity: newQuantity,
- tags: oldAccumulatedSymbol.tags.concat(tags),
- transactionCount: oldAccumulatedSymbol.transactionCount + 1
+ tags: oldAccumulatedSymbol.tags.concat(tags)
};
} else {
currentTransactionPointItem = {
@@ -1024,8 +1022,7 @@ export abstract class PortfolioCalculator {
dividend: new Big(0),
includeInHoldings: INVESTMENT_ACTIVITY_TYPES.includes(type),
investment: unitPrice.mul(quantity).mul(factor),
- quantity: quantity.mul(factor),
- transactionCount: 1
+ quantity: quantity.mul(factor)
};
}
diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-buy.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-buy.spec.ts
index 7858d7546..52c8489dd 100644
--- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-buy.spec.ts
+++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-buy.spec.ts
@@ -178,7 +178,6 @@ describe('PortfolioCalculator', () => {
timeWeightedInvestmentWithCurrencyEffect: new Big(
'474.93846153846153846154'
),
- transactionCount: 2,
valueInBaseCurrency: new Big('595.6')
}
],
diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts
index 8b40c7b70..3998b081d 100644
--- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts
+++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts
@@ -192,7 +192,6 @@ describe('PortfolioCalculator', () => {
timeWeightedInvestmentWithCurrencyEffect: new Big(
'285.80000000000000396627'
),
- transactionCount: 3,
valueInBaseCurrency: new Big('0')
}
],
diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts
index fc372f68f..acd0d0b2e 100644
--- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts
+++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-baln-buy-and-sell.spec.ts
@@ -176,7 +176,6 @@ describe('PortfolioCalculator', () => {
tags: [],
timeWeightedInvestment: new Big('285.8'),
timeWeightedInvestmentWithCurrencyEffect: new Big('285.8'),
- transactionCount: 2,
valueInBaseCurrency: new Big('0')
}
],
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 926fae6dc..652e72db0 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
@@ -172,7 +172,6 @@ describe('PortfolioCalculator', () => {
tags: [],
timeWeightedInvestment: new Big('273.2'),
timeWeightedInvestmentWithCurrencyEffect: new Big('273.2'),
- transactionCount: 1,
valueInBaseCurrency: new Big('297.8')
}
],
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 b216438b8..055356325 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
@@ -227,7 +227,6 @@ describe('PortfolioCalculator', () => {
tags: [],
timeWeightedInvestment: new Big('44558.42'),
timeWeightedInvestmentWithCurrencyEffect: new Big('44558.42'),
- transactionCount: 1,
valueInBaseCurrency: new Big('43099.7')
}
],
diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts
index 14cd4f217..a70cc2986 100644
--- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts
+++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts
@@ -194,7 +194,6 @@ describe('PortfolioCalculator', () => {
timeWeightedInvestmentWithCurrencyEffect: new Big(
'636.79389574611155533947'
),
- transactionCount: 2,
valueInBaseCurrency: new Big('13298.425356')
}
],
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 066f33ea3..64882061f 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
@@ -227,7 +227,6 @@ describe('PortfolioCalculator', () => {
tags: [],
timeWeightedInvestment: new Big('44558.42'),
timeWeightedInvestmentWithCurrencyEffect: new Big('44558.42'),
- transactionCount: 1,
valueInBaseCurrency: new Big('43099.7')
}
],
diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts
index bd8afddd7..a53ebcf05 100644
--- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts
+++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-cash.spec.ts
@@ -276,7 +276,6 @@ describe('PortfolioCalculator', () => {
timeWeightedInvestmentWithCurrencyEffect: new Big(
'852.45231607629427792916'
),
- transactionCount: 2,
valueInBaseCurrency: new Big(1820)
});
diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts
index 28b44e159..9b48a1324 100644
--- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts
+++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-googl-buy.spec.ts
@@ -172,7 +172,6 @@ describe('PortfolioCalculator', () => {
tags: [],
timeWeightedInvestment: new Big('89.12').mul(0.8854),
timeWeightedInvestmentWithCurrencyEffect: new Big('82.329056'),
- transactionCount: 1,
valueInBaseCurrency: new Big('103.10483')
}
],
diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts
index 87ef9ed8b..b19adb642 100644
--- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts
+++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-msft-buy-with-dividend.spec.ts
@@ -162,8 +162,7 @@ describe('PortfolioCalculator', () => {
},
quantity: new Big('1'),
symbol: 'MSFT',
- tags: [],
- transactionCount: 2
+ tags: []
}
],
totalFeesWithCurrencyEffect: new Big('19'),
diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts
index 7a8dc010a..fecf17011 100644
--- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts
+++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell-partially.spec.ts
@@ -174,7 +174,6 @@ describe('PortfolioCalculator', () => {
timeWeightedInvestmentWithCurrencyEffect: new Big(
'145.10285714285714285714'
),
- transactionCount: 2,
valueInBaseCurrency: new Big('87.8')
}
],
diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts
index 02a4e80d8..adbb5c3ff 100644
--- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts
+++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-novn-buy-and-sell.spec.ts
@@ -225,7 +225,6 @@ describe('PortfolioCalculator', () => {
tags: [],
timeWeightedInvestment: new Big('151.6'),
timeWeightedInvestmentWithCurrencyEffect: new Big('151.6'),
- transactionCount: 2,
valueInBaseCurrency: new Big('0')
}
],
diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-valuable.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-valuable.spec.ts
index 610a52c06..6fc94622f 100644
--- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-valuable.spec.ts
+++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-valuable.spec.ts
@@ -146,7 +146,6 @@ describe('PortfolioCalculator', () => {
tags: [],
timeWeightedInvestment: new Big('500000'),
timeWeightedInvestmentWithCurrencyEffect: new Big('500000'),
- transactionCount: 1,
valueInBaseCurrency: new Big('500000')
}
],
diff --git a/apps/api/src/app/portfolio/interfaces/transaction-point-symbol.interface.ts b/apps/api/src/app/portfolio/interfaces/transaction-point-symbol.interface.ts
index 7e7d741ea..7f3f54ff5 100644
--- a/apps/api/src/app/portfolio/interfaces/transaction-point-symbol.interface.ts
+++ b/apps/api/src/app/portfolio/interfaces/transaction-point-symbol.interface.ts
@@ -17,7 +17,4 @@ export interface TransactionPointSymbol {
skipErrors: boolean;
symbol: string;
tags?: Tag[];
-
- /** @deprecated use activitiesCount instead */
- transactionCount: number;
}
diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts
index 05df6a8fc..7be375473 100644
--- a/apps/api/src/app/portfolio/portfolio.service.ts
+++ b/apps/api/src/app/portfolio/portfolio.service.ts
@@ -233,7 +233,6 @@ export class PortfolioService {
account.currency,
userCurrency
),
- transactionCount: activitiesCount,
value: this.exchangeRateDataService.toCurrency(
valueInBaseCurrency,
userCurrency,
@@ -284,7 +283,6 @@ export class PortfolioService {
let totalDividendInBaseCurrency = new Big(0);
let totalInterestInBaseCurrency = new Big(0);
let totalValueInBaseCurrency = new Big(0);
- let transactionCount = 0;
for (const account of accounts) {
activitiesCount += account.activitiesCount;
@@ -301,8 +299,6 @@ export class PortfolioService {
totalValueInBaseCurrency = totalValueInBaseCurrency.plus(
account.valueInBaseCurrency
);
-
- transactionCount += account.transactionCount;
}
for (const account of accounts) {
@@ -317,7 +313,6 @@ export class PortfolioService {
return {
accounts,
activitiesCount,
- transactionCount,
totalBalanceInBaseCurrency: totalBalanceInBaseCurrency.toNumber(),
totalDividendInBaseCurrency: totalDividendInBaseCurrency.toNumber(),
totalInterestInBaseCurrency: totalInterestInBaseCurrency.toNumber(),
@@ -591,7 +586,6 @@ export class PortfolioService {
quantity,
symbol,
tags,
- transactionCount,
valueInBaseCurrency
} of positions) {
if (isFilteredByClosedHoldings === true) {
@@ -625,7 +619,6 @@ export class PortfolioService {
marketPrice,
symbol,
tags,
- transactionCount,
allocationInPercentage: filteredValueInBaseCurrency.eq(0)
? 0
: valueInBaseCurrency.div(filteredValueInBaseCurrency).toNumber(),
@@ -1696,7 +1689,6 @@ export class PortfolioService {
sectors: [],
symbol: currency,
tags: [],
- transactionCount: 0,
valueInBaseCurrency: balance
};
}
diff --git a/apps/api/src/helper/object.helper.spec.ts b/apps/api/src/helper/object.helper.spec.ts
index 5ddff164b..ed821390f 100644
--- a/apps/api/src/helper/object.helper.spec.ts
+++ b/apps/api/src/helper/object.helper.spec.ts
@@ -111,6 +111,7 @@ describe('redactAttributes', () => {
hasError: false,
holdings: {
'AAPL.US': {
+ activitiesCount: 1,
currency: 'USD',
markets: {
UNKNOWN: 0,
@@ -130,7 +131,6 @@ describe('redactAttributes', () => {
marketPrice: 220.79,
symbol: 'AAPL.US',
tags: [],
- transactionCount: 1,
allocationInPercentage: 0.044900865255793135,
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
@@ -163,6 +163,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.0694356974830054
},
'ALV.DE': {
+ activitiesCount: 2,
currency: 'EUR',
markets: {
UNKNOWN: 0,
@@ -182,7 +183,6 @@ describe('redactAttributes', () => {
marketPrice: 296.5,
symbol: 'ALV.DE',
tags: [],
- transactionCount: 2,
allocationInPercentage: 0.026912563036519527,
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
@@ -210,6 +210,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.04161818652826481
},
AMZN: {
+ activitiesCount: 1,
currency: 'USD',
markets: {
UNKNOWN: 0,
@@ -229,7 +230,6 @@ describe('redactAttributes', () => {
marketPrice: 187.99,
symbol: 'AMZN',
tags: [],
- transactionCount: 1,
allocationInPercentage: 0.07646101417126275,
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
@@ -262,6 +262,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.11824101426541227
},
bitcoin: {
+ activitiesCount: 1,
currency: 'USD',
markets: {
UNKNOWN: 36985.0332704,
@@ -287,7 +288,6 @@ describe('redactAttributes', () => {
userId: null
}
],
- transactionCount: 1,
allocationInPercentage: 0.15042891393226654,
assetClass: 'LIQUIDITY',
assetSubClass: 'CRYPTOCURRENCY',
@@ -313,6 +313,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.232626620912395
},
BONDORA_GO_AND_GROW: {
+ activitiesCount: 5,
currency: 'EUR',
markets: {
UNKNOWN: 2231.644722160232,
@@ -338,7 +339,6 @@ describe('redactAttributes', () => {
userId: null
}
],
- transactionCount: 5,
allocationInPercentage: 0.009076749759365777,
assetClass: 'FIXED_INCOME',
assetSubClass: 'BOND',
@@ -364,6 +364,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.014036487867880205
},
FRANKLY95P: {
+ activitiesCount: 6,
currency: 'CHF',
markets: {
UNKNOWN: 0,
@@ -389,7 +390,6 @@ describe('redactAttributes', () => {
userId: null
}
],
- transactionCount: 6,
allocationInPercentage: 0.09095764645669335,
assetClass: 'EQUITY',
assetSubClass: 'ETF',
@@ -488,6 +488,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.14065892911313693
},
MSFT: {
+ activitiesCount: 1,
currency: 'USD',
markets: {
UNKNOWN: 0,
@@ -507,7 +508,6 @@ describe('redactAttributes', () => {
marketPrice: 428.02,
symbol: 'MSFT',
tags: [],
- transactionCount: 1,
allocationInPercentage: 0.05222646409742627,
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
@@ -540,6 +540,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.08076416659271518
},
TSLA: {
+ activitiesCount: 1,
currency: 'USD',
markets: {
UNKNOWN: 0,
@@ -559,7 +560,6 @@ describe('redactAttributes', () => {
marketPrice: 260.46,
symbol: 'TSLA',
tags: [],
- transactionCount: 1,
allocationInPercentage: 0.1589050142378352,
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
@@ -592,6 +592,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.2457342510950259
},
VTI: {
+ activitiesCount: 5,
currency: 'USD',
markets: {
UNKNOWN: 0,
@@ -611,7 +612,6 @@ describe('redactAttributes', () => {
marketPrice: 282.05,
symbol: 'VTI',
tags: [],
- transactionCount: 5,
allocationInPercentage: 0.057358979326040366,
assetClass: 'EQUITY',
assetSubClass: 'ETF',
@@ -764,6 +764,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.08870120238725339
},
'VWRL.SW': {
+ activitiesCount: 5,
currency: 'CHF',
markets: {
UNKNOWN: 0,
@@ -783,7 +784,6 @@ describe('redactAttributes', () => {
marketPrice: 117.62,
symbol: 'VWRL.SW',
tags: [],
- transactionCount: 5,
allocationInPercentage: 0.09386983901959013,
assetClass: 'EQUITY',
assetSubClass: 'ETF',
@@ -1172,6 +1172,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.145162408515095
},
'XDWD.DE': {
+ activitiesCount: 1,
currency: 'EUR',
markets: {
UNKNOWN: 0,
@@ -1191,7 +1192,6 @@ describe('redactAttributes', () => {
marketPrice: 105.72,
symbol: 'XDWD.DE',
tags: [],
- transactionCount: 1,
allocationInPercentage: 0.03598477442100562,
assetClass: 'EQUITY',
assetSubClass: 'ETF',
@@ -1450,6 +1450,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.055647656152211074
},
USD: {
+ activitiesCount: 0,
currency: 'USD',
allocationInPercentage: 0.20291717628620132,
assetClass: 'LIQUIDITY',
@@ -1472,7 +1473,6 @@ describe('redactAttributes', () => {
sectors: [],
symbol: 'USD',
tags: [],
- transactionCount: 0,
valueInBaseCurrency: 49890,
valueInPercentage: 0.3137956381563603
}
@@ -1615,6 +1615,7 @@ describe('redactAttributes', () => {
hasError: false,
holdings: {
'AAPL.US': {
+ activitiesCount: 1,
currency: 'USD',
markets: {
UNKNOWN: 0,
@@ -1634,7 +1635,6 @@ describe('redactAttributes', () => {
marketPrice: 220.79,
symbol: 'AAPL.US',
tags: [],
- transactionCount: 1,
allocationInPercentage: 0.044900865255793135,
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
@@ -1667,6 +1667,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.0694356974830054
},
'ALV.DE': {
+ activitiesCount: 2,
currency: 'EUR',
markets: {
UNKNOWN: 0,
@@ -1686,7 +1687,6 @@ describe('redactAttributes', () => {
marketPrice: 296.5,
symbol: 'ALV.DE',
tags: [],
- transactionCount: 2,
allocationInPercentage: 0.026912563036519527,
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
@@ -1714,6 +1714,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.04161818652826481
},
AMZN: {
+ activitiesCount: 1,
currency: 'USD',
markets: {
UNKNOWN: 0,
@@ -1733,7 +1734,6 @@ describe('redactAttributes', () => {
marketPrice: 187.99,
symbol: 'AMZN',
tags: [],
- transactionCount: 1,
allocationInPercentage: 0.07646101417126275,
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
@@ -1766,6 +1766,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.11824101426541227
},
bitcoin: {
+ activitiesCount: 1,
currency: 'USD',
markets: {
UNKNOWN: 36985.0332704,
@@ -1791,7 +1792,6 @@ describe('redactAttributes', () => {
userId: null
}
],
- transactionCount: 1,
allocationInPercentage: 0.15042891393226654,
assetClass: 'LIQUIDITY',
assetSubClass: 'CRYPTOCURRENCY',
@@ -1817,6 +1817,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.232626620912395
},
BONDORA_GO_AND_GROW: {
+ activitiesCount: 5,
currency: 'EUR',
markets: {
UNKNOWN: 2231.644722160232,
@@ -1842,7 +1843,6 @@ describe('redactAttributes', () => {
userId: null
}
],
- transactionCount: 5,
allocationInPercentage: 0.009076749759365777,
assetClass: 'FIXED_INCOME',
assetSubClass: 'BOND',
@@ -1868,6 +1868,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.014036487867880205
},
FRANKLY95P: {
+ activitiesCount: 6,
currency: 'CHF',
markets: {
UNKNOWN: 0,
@@ -1893,7 +1894,6 @@ describe('redactAttributes', () => {
userId: null
}
],
- transactionCount: 6,
allocationInPercentage: 0.09095764645669335,
assetClass: 'EQUITY',
assetSubClass: 'ETF',
@@ -1972,6 +1972,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.14065892911313693
},
MSFT: {
+ activitiesCount: 1,
currency: 'USD',
markets: {
UNKNOWN: 0,
@@ -1991,7 +1992,6 @@ describe('redactAttributes', () => {
marketPrice: 428.02,
symbol: 'MSFT',
tags: [],
- transactionCount: 1,
allocationInPercentage: 0.05222646409742627,
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
@@ -2024,6 +2024,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.08076416659271518
},
TSLA: {
+ activitiesCount: 1,
currency: 'USD',
markets: {
UNKNOWN: 0,
@@ -2043,7 +2044,6 @@ describe('redactAttributes', () => {
marketPrice: 260.46,
symbol: 'TSLA',
tags: [],
- transactionCount: 1,
allocationInPercentage: 0.1589050142378352,
assetClass: 'EQUITY',
assetSubClass: 'STOCK',
@@ -2076,6 +2076,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.2457342510950259
},
VTI: {
+ activitiesCount: 5,
currency: 'USD',
markets: {
UNKNOWN: 0,
@@ -2095,7 +2096,6 @@ describe('redactAttributes', () => {
marketPrice: 282.05,
symbol: 'VTI',
tags: [],
- transactionCount: 5,
allocationInPercentage: 0.057358979326040366,
assetClass: 'EQUITY',
assetSubClass: 'ETF',
@@ -2248,6 +2248,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.08870120238725339
},
'VWRL.SW': {
+ activitiesCount: 5,
currency: 'CHF',
markets: {
UNKNOWN: 0,
@@ -2267,7 +2268,6 @@ describe('redactAttributes', () => {
marketPrice: 117.62,
symbol: 'VWRL.SW',
tags: [],
- transactionCount: 5,
allocationInPercentage: 0.09386983901959013,
assetClass: 'EQUITY',
assetSubClass: 'ETF',
@@ -2648,6 +2648,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.145162408515095
},
'XDWD.DE': {
+ activitiesCount: 1,
currency: 'EUR',
markets: {
UNKNOWN: 0,
@@ -2667,7 +2668,6 @@ describe('redactAttributes', () => {
marketPrice: 105.72,
symbol: 'XDWD.DE',
tags: [],
- transactionCount: 1,
allocationInPercentage: 0.03598477442100562,
assetClass: 'EQUITY',
assetSubClass: 'ETF',
@@ -2926,6 +2926,7 @@ describe('redactAttributes', () => {
valueInPercentage: 0.055647656152211074
},
USD: {
+ activitiesCount: 0,
currency: 'USD',
allocationInPercentage: 0.20291717628620132,
assetClass: 'LIQUIDITY',
@@ -2948,7 +2949,6 @@ describe('redactAttributes', () => {
sectors: [],
symbol: 'USD',
tags: [],
- transactionCount: 0,
valueInBaseCurrency: null,
valueInPercentage: 0.3137956381563603
}
diff --git a/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts b/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts
index b40043cc8..380fb69cb 100644
--- a/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts
+++ b/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts
@@ -80,6 +80,7 @@ import { AccountDetailDialogParams } from './interfaces/interfaces';
export class GfAccountDetailDialogComponent implements OnDestroy, OnInit {
public accountBalances: AccountBalancesResponse['balances'];
public activities: OrderWithAccount[];
+ public activitiesCount: number;
public balance: number;
public balancePrecision = 2;
public currency: string;
@@ -100,7 +101,6 @@ export class GfAccountDetailDialogComponent implements OnDestroy, OnInit {
public sortColumn = 'date';
public sortDirection: SortDirection = 'desc';
public totalItems: number;
- public transactionCount: number;
public user: User;
public valueInBaseCurrency: number;
@@ -215,16 +215,17 @@ export class GfAccountDetailDialogComponent implements OnDestroy, OnInit {
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(
({
+ activitiesCount,
balance,
currency,
dividendInBaseCurrency,
interestInBaseCurrency,
name,
platform,
- transactionCount,
value,
valueInBaseCurrency
}) => {
+ this.activitiesCount = activitiesCount;
this.balance = balance;
if (
@@ -270,7 +271,6 @@ export class GfAccountDetailDialogComponent implements OnDestroy, OnInit {
this.name = name;
this.platformName = platform?.name ?? '-';
- this.transactionCount = transactionCount;
this.valueInBaseCurrency = valueInBaseCurrency;
this.changeDetectorRef.markForCheck();
diff --git a/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.html b/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.html
index 07ea17038..15dd8f13a 100644
--- a/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.html
+++ b/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.html
@@ -82,7 +82,7 @@
>
- Activities
diff --git a/libs/common/src/lib/interfaces/portfolio-position.interface.ts b/libs/common/src/lib/interfaces/portfolio-position.interface.ts
index 67a2f3e77..620cc00e9 100644
--- a/libs/common/src/lib/interfaces/portfolio-position.interface.ts
+++ b/libs/common/src/lib/interfaces/portfolio-position.interface.ts
@@ -39,10 +39,6 @@ export interface PortfolioPosition {
sectors: Sector[];
symbol: string;
tags?: Tag[];
-
- /** @deprecated use activitiesCount instead */
- transactionCount: number;
-
type?: string;
url?: string;
valueInBaseCurrency?: number;
diff --git a/libs/common/src/lib/interfaces/responses/accounts-response.interface.ts b/libs/common/src/lib/interfaces/responses/accounts-response.interface.ts
index 1891b9cbb..90f1303e0 100644
--- a/libs/common/src/lib/interfaces/responses/accounts-response.interface.ts
+++ b/libs/common/src/lib/interfaces/responses/accounts-response.interface.ts
@@ -7,7 +7,4 @@ export interface AccountsResponse {
totalDividendInBaseCurrency: number;
totalInterestInBaseCurrency: number;
totalValueInBaseCurrency: number;
-
- /** @deprecated use activitiesCount instead */
- transactionCount: number;
}
diff --git a/libs/common/src/lib/models/timeline-position.ts b/libs/common/src/lib/models/timeline-position.ts
index 9cfb2df04..13f9001d5 100644
--- a/libs/common/src/lib/models/timeline-position.ts
+++ b/libs/common/src/lib/models/timeline-position.ts
@@ -93,9 +93,6 @@ export class TimelinePosition {
@Type(() => Big)
timeWeightedInvestmentWithCurrencyEffect: Big;
- /** @deprecated use activitiesCount instead */
- transactionCount: number;
-
@Transform(transformToBig, { toClassOnly: true })
@Type(() => Big)
valueInBaseCurrency: Big;
diff --git a/libs/common/src/lib/types/account-with-value.type.ts b/libs/common/src/lib/types/account-with-value.type.ts
index 7f5fe79ba..23cb14749 100644
--- a/libs/common/src/lib/types/account-with-value.type.ts
+++ b/libs/common/src/lib/types/account-with-value.type.ts
@@ -7,10 +7,6 @@ export type AccountWithValue = AccountModel & {
dividendInBaseCurrency: number;
interestInBaseCurrency: number;
platform?: Platform;
-
- /** @deprecated use activitiesCount instead */
- transactionCount: number;
-
value: number;
valueInBaseCurrency: number;
};
diff --git a/libs/ui/src/lib/accounts-table/accounts-table.component.html b/libs/ui/src/lib/accounts-table/accounts-table.component.html
index c9124820c..68ae78474 100644
--- a/libs/ui/src/lib/accounts-table/accounts-table.component.html
+++ b/libs/ui/src/lib/accounts-table/accounts-table.component.html
@@ -120,13 +120,13 @@
*matHeaderCellDef
class="justify-content-end px-1"
mat-header-cell
- mat-sort-header="transactionCount"
+ mat-sort-header="activitiesCount"
>
#
Activities
- {{ element.transactionCount }}
+ {{ element.activitiesCount }}
|
{{ activitiesCount }}
@@ -323,7 +323,7 @@
|