From 7dacc10946cc51a048d6db76d4ad32427cc0db96 Mon Sep 17 00:00:00 2001
From: Mariam Saeed <69825646+Mariam-Saeed@users.noreply.github.com>
Date: Wed, 15 Oct 2025 21:10:19 +0300
Subject: [PATCH 01/10] Bugfix/reset scroll position on page change (#5753)
* Reset scroll position on page change
* Update changelog
---
CHANGELOG.md | 1 +
apps/client/src/app/app-routing.module.ts | 5 +++--
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 31d691c87..fff28224a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Respected the include indices flag in the search functionality of the _Financial Modeling Prep_ service
+- Fixed an issue where the scroll position was not restored when changing pages
## 2.208.0 - 2025-10-11
diff --git a/apps/client/src/app/app-routing.module.ts b/apps/client/src/app/app-routing.module.ts
index 0e5a2dead..0ceee3725 100644
--- a/apps/client/src/app/app-routing.module.ts
+++ b/apps/client/src/app/app-routing.module.ts
@@ -155,8 +155,9 @@ const routes: Routes = [
// Preload all lazy loaded modules with the attribute preload === true
{
anchorScrolling: 'enabled',
- preloadingStrategy: ModulePreloadService
- // enableTracing: true // <-- debugging purposes only
+ // enableTracing: true, // <-- debugging purposes only
+ preloadingStrategy: ModulePreloadService,
+ scrollPositionRestoration: 'top'
}
)
],
From 33d9ba0063d8e9b95b3df7bf7f26fe65b464ffc9 Mon Sep 17 00:00:00 2001
From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
Date: Thu, 16 Oct 2025 17:45:43 +0200
Subject: [PATCH 02/10] Feature/add Stealth Wealth to glossary (#5754)
* Add Stealth Wealth
* Update changelog
---
CHANGELOG.md | 1 +
.../glossary/resources-glossary.component.html | 17 +++++++++++++++++
2 files changed, 18 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fff28224a..5024d0541 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
### Added
+- Extended the glossary of the resources page by _Stealth Wealth_
- Added a _Storybook_ story for the holdings table component
### Changed
diff --git a/apps/client/src/app/pages/resources/glossary/resources-glossary.component.html b/apps/client/src/app/pages/resources/glossary/resources-glossary.component.html
index 123b4dac9..b028734a7 100644
--- a/apps/client/src/app/pages/resources/glossary/resources-glossary.component.html
+++ b/apps/client/src/app/pages/resources/glossary/resources-glossary.component.html
@@ -132,6 +132,23 @@
+
From 3caa3c010efc289d1b13a76349110de34f782f86 Mon Sep 17 00:00:00 2001
From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
Date: Thu, 16 Oct 2025 17:46:26 +0200
Subject: [PATCH 03/10] Bugfix/dark mode in logo carousel component (#5758)
* Fix dark mode
* Update changelog
---
CHANGELOG.md | 1 +
.../logo-carousel.component.scss | 20 +++++++------------
2 files changed, 8 insertions(+), 13 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5024d0541..adc9dbf53 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Respected the include indices flag in the search functionality of the _Financial Modeling Prep_ service
- Fixed an issue where the scroll position was not restored when changing pages
+- Fixed the dark mode in the _As seen in_ section on the landing page
## 2.208.0 - 2025-10-11
diff --git a/libs/ui/src/lib/logo-carousel/logo-carousel.component.scss b/libs/ui/src/lib/logo-carousel/logo-carousel.component.scss
index d8a8865f7..18c3a26cb 100644
--- a/libs/ui/src/lib/logo-carousel/logo-carousel.component.scss
+++ b/libs/ui/src/lib/logo-carousel/logo-carousel.component.scss
@@ -194,19 +194,13 @@
);
}
- .logo {
- &.logo-alternative-to,
- &.logo-dev-community,
- &.logo-hacker-news,
- &.logo-openalternative,
- &.logo-privacy-tools,
- &.logo-reddit,
- &.logo-sackgeld,
- &.logo-selfh-st,
- &.logo-sourceforge,
- &.logo-umbrel,
- &.logo-unraid {
- background-color: rgba(var(--light-primary-text));
+ .logo-carousel-track {
+ .logo-carousel-item {
+ .logo {
+ &.mask {
+ background-color: rgba(var(--light-secondary-text));
+ }
+ }
}
}
}
From db2c2426c648ec39e5d9393c3b0e80c63dad506c Mon Sep 17 00:00:00 2001
From: Dibyendu Sahoo
Date: Fri, 17 Oct 2025 00:16:24 +0530
Subject: [PATCH 04/10] Task/refactor interest to interestInBaseCurrency in
portfolio summary interface (#5763)
* Refactor interest to interestInBaseCurrency
---
apps/api/src/app/portfolio/portfolio.controller.ts | 2 +-
apps/api/src/app/portfolio/portfolio.service.ts | 2 +-
apps/api/src/helper/object.helper.spec.ts | 4 ++--
.../portfolio-summary/portfolio-summary.component.html | 2 +-
libs/common/src/lib/interfaces/portfolio-summary.interface.ts | 2 +-
5 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts
index 19b0636c7..f6f8e3d80 100644
--- a/apps/api/src/app/portfolio/portfolio.controller.ts
+++ b/apps/api/src/app/portfolio/portfolio.controller.ts
@@ -197,7 +197,7 @@ export class PortfolioController {
'filteredValueInBaseCurrency',
'grossPerformance',
'grossPerformanceWithCurrencyEffect',
- 'interest',
+ 'interestInBaseCurrency',
'items',
'liabilities',
'netPerformance',
diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts
index a5bc10fbd..bbfb31b79 100644
--- a/apps/api/src/app/portfolio/portfolio.service.ts
+++ b/apps/api/src/app/portfolio/portfolio.service.ts
@@ -2105,7 +2105,7 @@ export class PortfolioService {
)
.plus(fees)
.toNumber(),
- interest: interest.toNumber(),
+ interestInBaseCurrency: interest.toNumber(),
liabilitiesInBaseCurrency: liabilities.toNumber(),
totalInvestment: totalInvestment.toNumber(),
totalValueInBaseCurrency: netWorth
diff --git a/apps/api/src/helper/object.helper.spec.ts b/apps/api/src/helper/object.helper.spec.ts
index d7caf9bc9..433490325 100644
--- a/apps/api/src/helper/object.helper.spec.ts
+++ b/apps/api/src/helper/object.helper.spec.ts
@@ -1536,7 +1536,7 @@ describe('redactAttributes', () => {
fireWealth: null,
grossPerformance: null,
grossPerformanceWithCurrencyEffect: null,
- interest: null,
+ interestInBaseCurrency: null,
items: null,
liabilities: null,
totalInvestment: null,
@@ -3039,7 +3039,7 @@ describe('redactAttributes', () => {
fireWealth: null,
grossPerformance: null,
grossPerformanceWithCurrencyEffect: null,
- interest: null,
+ interestInBaseCurrency: null,
items: null,
liabilities: null,
totalInvestment: null,
diff --git a/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html b/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html
index c8d710019..b20b6b263 100644
--- a/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html
+++ b/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html
@@ -302,7 +302,7 @@
[isCurrency]="true"
[locale]="locale"
[unit]="baseCurrency"
- [value]="isLoading ? undefined : summary?.interest"
+ [value]="isLoading ? undefined : summary?.interestInBaseCurrency"
/>
diff --git a/libs/common/src/lib/interfaces/portfolio-summary.interface.ts b/libs/common/src/lib/interfaces/portfolio-summary.interface.ts
index 092a4bb97..f08eb61b8 100644
--- a/libs/common/src/lib/interfaces/portfolio-summary.interface.ts
+++ b/libs/common/src/lib/interfaces/portfolio-summary.interface.ts
@@ -20,7 +20,7 @@ export interface PortfolioSummary extends PortfolioPerformance {
fireWealth: FireWealth;
grossPerformance: number;
grossPerformanceWithCurrencyEffect: number;
- interest: number;
+ interestInBaseCurrency: number;
liabilitiesInBaseCurrency: number;
totalBuy: number;
totalSell: number;
From 835bde6662c2aa5118fc0dccb38aa972caffb640 Mon Sep 17 00:00:00 2001
From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
Date: Thu, 16 Oct 2025 20:51:14 +0200
Subject: [PATCH 05/10] Feature/extend pricing page (#5761)
* Extend pricing page
* Update changelog
---
CHANGELOG.md | 1 +
.../pages/pricing/pricing-page.component.ts | 10 +++++
.../src/app/pages/pricing/pricing-page.html | 43 +++++++++++++++----
3 files changed, 46 insertions(+), 8 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index adc9dbf53..0fb55409a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Extended the glossary of the resources page by _Stealth Wealth_
+- Extended the content of the pricing page
- Added a _Storybook_ story for the holdings table component
### Changed
diff --git a/apps/client/src/app/pages/pricing/pricing-page.component.ts b/apps/client/src/app/pages/pricing/pricing-page.component.ts
index 170d70914..8bc3e3a67 100644
--- a/apps/client/src/app/pages/pricing/pricing-page.component.ts
+++ b/apps/client/src/app/pages/pricing/pricing-page.component.ts
@@ -69,6 +69,16 @@ export class GfPricingPageComponent implements OnDestroy, OnInit {
public professionalDataProviderTooltipPremium = translate(
'PROFESSIONAL_DATA_PROVIDER_TOOLTIP_PREMIUM'
);
+ public referralBrokers = [
+ 'DEGIRO',
+ 'finpension',
+ 'frankly',
+ 'Interactive Brokers',
+ 'Mintos',
+ 'Swissquote',
+ 'VIAC',
+ 'Zak'
+ ];
public routerLinkFeatures = publicRoutes.features.routerLink;
public routerLinkRegister = publicRoutes.register.routerLink;
public user: User;
diff --git a/apps/client/src/app/pages/pricing/pricing-page.html b/apps/client/src/app/pages/pricing/pricing-page.html
index ea68b74eb..ee006b2d6 100644
--- a/apps/client/src/app/pages/pricing/pricing-page.html
+++ b/apps/client/src/app/pages/pricing/pricing-page.html
@@ -326,16 +326,43 @@
From ba1ee013d7868f549427df92bc379ab072bf372a Mon Sep 17 00:00:00 2001
From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
Date: Thu, 16 Oct 2025 20:51:39 +0200
Subject: [PATCH 06/10] Bugfix/fix word wrap in menus of activities table
(#5764)
* Fix word wrap
* Update changelog
---
CHANGELOG.md | 1 +
.../activities-table/activities-table.component.html | 12 ++++++++++--
2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0fb55409a..a024cc722 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Respected the include indices flag in the search functionality of the _Financial Modeling Prep_ service
- Fixed an issue where the scroll position was not restored when changing pages
+- Fixed the word wrap in the menus of the activities table component
- Fixed the dark mode in the _As seen in_ section on the landing page
## 2.208.0 - 2025-10-11
diff --git a/libs/ui/src/lib/activities-table/activities-table.component.html b/libs/ui/src/lib/activities-table/activities-table.component.html
index 472c24e2b..8079a6258 100644
--- a/libs/ui/src/lib/activities-table/activities-table.component.html
+++ b/libs/ui/src/lib/activities-table/activities-table.component.html
@@ -361,7 +361,11 @@
}
-
+
@if (hasPermissionToCreateActivity) {
}
-
+
From 103c15ca31bea67fcd2d0108a69b5b15c4811700 Mon Sep 17 00:00:00 2001
From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
Date: Thu, 16 Oct 2025 20:52:20 +0200
Subject: [PATCH 07/10] Feature/improve portfolio calculator unit tests by
loading currency from user settings (#5765)
* Use currency from user settings
* Update changelog
---
CHANGELOG.md | 1 +
.../portfolio-calculator-test-utils.ts | 6 ++-
.../roai/portfolio-calculator-btceur.spec.ts | 44 +++++++++----------
.../portfolio-calculator-btcusd-short.spec.ts | 44 +++++++++----------
.../roai/portfolio-calculator-btcusd.spec.ts | 44 +++++++++----------
...ulator-novn-buy-and-sell-partially.spec.ts | 44 +++++++++----------
...folio-calculator-novn-buy-and-sell.spec.ts | 44 +++++++++----------
.../ok/novn-buy-and-sell-partially.json | 7 ++-
test/import/ok/novn-buy-and-sell.json | 7 ++-
9 files changed, 122 insertions(+), 119 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a024cc722..758898cee 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Improved the currency validation in the search functionality of the data provider service
- Optimized the get quotes functionality by utilizing the asset profile resolutions in the _Financial Modeling Prep_ service
- Extracted the footer to a component
+- Improved the portfolio calculator unit tests to load the user currency from the exported file
### Fixed
diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator-test-utils.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator-test-utils.ts
index 8850a6874..ccdbafac8 100644
--- a/apps/api/src/app/portfolio/calculator/portfolio-calculator-test-utils.ts
+++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator-test-utils.ts
@@ -1,3 +1,5 @@
+import { Export } from '@ghostfolio/common/interfaces';
+
import { readFileSync } from 'node:fs';
export const activityDummyData = {
@@ -37,6 +39,6 @@ export const userDummyData = {
id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
};
-export function loadActivityExportFile(filePath: string) {
- return JSON.parse(readFileSync(filePath, 'utf8')).activities;
+export function loadExportFile(filePath: string): Export {
+ return JSON.parse(readFileSync(filePath, 'utf8'));
}
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 1f6f9dc2a..1ac0dcd16 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
@@ -1,8 +1,7 @@
-import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import {
activityDummyData,
- loadActivityExportFile,
+ loadExportFile,
symbolProfileDummyData,
userDummyData
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils';
@@ -16,9 +15,9 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-
import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service';
import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock';
import { parseDate } from '@ghostfolio/common/helper';
+import { Export } from '@ghostfolio/common/interfaces';
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type';
-import { Tag } from '@prisma/client';
import { Big } from 'big.js';
import { join } from 'node:path';
@@ -53,7 +52,7 @@ jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => {
});
describe('PortfolioCalculator', () => {
- let activityDtos: CreateOrderDto[];
+ let exportResponse: Export;
let configurationService: ConfigurationService;
let currentRateService: CurrentRateService;
@@ -63,7 +62,7 @@ describe('PortfolioCalculator', () => {
let redisCacheService: RedisCacheService;
beforeAll(() => {
- activityDtos = loadActivityExportFile(
+ exportResponse = loadExportFile(
join(__dirname, '../../../../../../../test/import/ok/btceur.json')
);
});
@@ -97,28 +96,27 @@ describe('PortfolioCalculator', () => {
it.only('with BTCUSD buy (in EUR)', async () => {
jest.useFakeTimers().setSystemTime(parseDate('2022-01-14').getTime());
- const activities: Activity[] = activityDtos.map((activity) => ({
- ...activityDummyData,
- ...activity,
- date: parseDate(activity.date),
- feeInAssetProfileCurrency: 4.46,
- SymbolProfile: {
- ...symbolProfileDummyData,
- currency: 'USD',
- dataSource: activity.dataSource,
- name: 'Bitcoin',
- symbol: activity.symbol
- },
- tags: activity.tags?.map((id) => {
- return { id } as Tag;
- }),
- unitPriceInAssetProfileCurrency: 44558.42
- }));
+ const activities: Activity[] = exportResponse.activities.map(
+ (activity) => ({
+ ...activityDummyData,
+ ...activity,
+ date: parseDate(activity.date),
+ feeInAssetProfileCurrency: 4.46,
+ SymbolProfile: {
+ ...symbolProfileDummyData,
+ currency: 'USD',
+ dataSource: activity.dataSource,
+ name: 'Bitcoin',
+ symbol: activity.symbol
+ },
+ unitPriceInAssetProfileCurrency: 44558.42
+ })
+ );
const portfolioCalculator = portfolioCalculatorFactory.createCalculator({
activities,
calculationType: PerformanceCalculationType.ROAI,
- currency: 'USD',
+ currency: exportResponse.user.settings.currency,
userId: userDummyData.id
});
diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-short.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-short.spec.ts
index a2d7e60d3..29413c6ad 100644
--- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-short.spec.ts
+++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-btcusd-short.spec.ts
@@ -1,8 +1,7 @@
-import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import {
activityDummyData,
- loadActivityExportFile,
+ loadExportFile,
symbolProfileDummyData,
userDummyData
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils';
@@ -16,9 +15,9 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-
import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service';
import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock';
import { parseDate } from '@ghostfolio/common/helper';
+import { Export } from '@ghostfolio/common/interfaces';
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type';
-import { Tag } from '@prisma/client';
import { Big } from 'big.js';
import { join } from 'node:path';
@@ -53,7 +52,7 @@ jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => {
});
describe('PortfolioCalculator', () => {
- let activityDtos: CreateOrderDto[];
+ let exportResponse: Export;
let configurationService: ConfigurationService;
let currentRateService: CurrentRateService;
@@ -63,7 +62,7 @@ describe('PortfolioCalculator', () => {
let redisCacheService: RedisCacheService;
beforeAll(() => {
- activityDtos = loadActivityExportFile(
+ exportResponse = loadExportFile(
join(__dirname, '../../../../../../../test/import/ok/btcusd-short.json')
);
});
@@ -97,28 +96,27 @@ describe('PortfolioCalculator', () => {
it.only('with BTCUSD short sell (in USD)', async () => {
jest.useFakeTimers().setSystemTime(parseDate('2022-01-14').getTime());
- const activities: Activity[] = activityDtos.map((activity) => ({
- ...activityDummyData,
- ...activity,
- date: parseDate(activity.date),
- feeInAssetProfileCurrency: activity.fee,
- SymbolProfile: {
- ...symbolProfileDummyData,
- currency: 'USD',
- dataSource: activity.dataSource,
- name: 'Bitcoin',
- symbol: activity.symbol
- },
- tags: activity.tags?.map((id) => {
- return { id } as Tag;
- }),
- unitPriceInAssetProfileCurrency: activity.unitPrice
- }));
+ const activities: Activity[] = exportResponse.activities.map(
+ (activity) => ({
+ ...activityDummyData,
+ ...activity,
+ date: parseDate(activity.date),
+ feeInAssetProfileCurrency: activity.fee,
+ SymbolProfile: {
+ ...symbolProfileDummyData,
+ currency: 'USD',
+ dataSource: activity.dataSource,
+ name: 'Bitcoin',
+ symbol: activity.symbol
+ },
+ unitPriceInAssetProfileCurrency: activity.unitPrice
+ })
+ );
const portfolioCalculator = portfolioCalculatorFactory.createCalculator({
activities,
calculationType: PerformanceCalculationType.ROAI,
- currency: 'USD',
+ currency: exportResponse.user.settings.currency,
userId: userDummyData.id
});
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 bdccb23e0..26b3325c2 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
@@ -1,8 +1,7 @@
-import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import {
activityDummyData,
- loadActivityExportFile,
+ loadExportFile,
symbolProfileDummyData,
userDummyData
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils';
@@ -16,9 +15,9 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-
import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service';
import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock';
import { parseDate } from '@ghostfolio/common/helper';
+import { Export } from '@ghostfolio/common/interfaces';
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type';
-import { Tag } from '@prisma/client';
import { Big } from 'big.js';
import { join } from 'node:path';
@@ -53,7 +52,7 @@ jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => {
});
describe('PortfolioCalculator', () => {
- let activityDtos: CreateOrderDto[];
+ let exportResponse: Export;
let configurationService: ConfigurationService;
let currentRateService: CurrentRateService;
@@ -63,7 +62,7 @@ describe('PortfolioCalculator', () => {
let redisCacheService: RedisCacheService;
beforeAll(() => {
- activityDtos = loadActivityExportFile(
+ exportResponse = loadExportFile(
join(__dirname, '../../../../../../../test/import/ok/btcusd.json')
);
});
@@ -97,28 +96,27 @@ describe('PortfolioCalculator', () => {
it.only('with BTCUSD buy (in USD)', async () => {
jest.useFakeTimers().setSystemTime(parseDate('2022-01-14').getTime());
- const activities: Activity[] = activityDtos.map((activity) => ({
- ...activityDummyData,
- ...activity,
- date: parseDate(activity.date),
- feeInAssetProfileCurrency: 4.46,
- SymbolProfile: {
- ...symbolProfileDummyData,
- currency: 'USD',
- dataSource: activity.dataSource,
- name: 'Bitcoin',
- symbol: activity.symbol
- },
- tags: activity.tags?.map((id) => {
- return { id } as Tag;
- }),
- unitPriceInAssetProfileCurrency: 44558.42
- }));
+ const activities: Activity[] = exportResponse.activities.map(
+ (activity) => ({
+ ...activityDummyData,
+ ...activity,
+ date: parseDate(activity.date),
+ feeInAssetProfileCurrency: 4.46,
+ SymbolProfile: {
+ ...symbolProfileDummyData,
+ currency: 'USD',
+ dataSource: activity.dataSource,
+ name: 'Bitcoin',
+ symbol: activity.symbol
+ },
+ unitPriceInAssetProfileCurrency: 44558.42
+ })
+ );
const portfolioCalculator = portfolioCalculatorFactory.createCalculator({
activities,
calculationType: PerformanceCalculationType.ROAI,
- currency: 'USD',
+ currency: exportResponse.user.settings.currency,
userId: userDummyData.id
});
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 4872a1004..0f1cdfff7 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
@@ -1,8 +1,7 @@
-import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import {
activityDummyData,
- loadActivityExportFile,
+ loadExportFile,
symbolProfileDummyData,
userDummyData
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils';
@@ -16,9 +15,9 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-
import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service';
import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock';
import { parseDate } from '@ghostfolio/common/helper';
+import { Export } from '@ghostfolio/common/interfaces';
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type';
-import { Tag } from '@prisma/client';
import { Big } from 'big.js';
import { join } from 'node:path';
@@ -53,7 +52,7 @@ jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => {
});
describe('PortfolioCalculator', () => {
- let activityDtos: CreateOrderDto[];
+ let exportResponse: Export;
let configurationService: ConfigurationService;
let currentRateService: CurrentRateService;
@@ -63,7 +62,7 @@ describe('PortfolioCalculator', () => {
let redisCacheService: RedisCacheService;
beforeAll(() => {
- activityDtos = loadActivityExportFile(
+ exportResponse = loadExportFile(
join(
__dirname,
'../../../../../../../test/import/ok/novn-buy-and-sell-partially.json'
@@ -100,28 +99,27 @@ describe('PortfolioCalculator', () => {
it.only('with NOVN.SW buy and sell partially', async () => {
jest.useFakeTimers().setSystemTime(parseDate('2022-04-11').getTime());
- const activities: Activity[] = activityDtos.map((activity) => ({
- ...activityDummyData,
- ...activity,
- date: parseDate(activity.date),
- feeInAssetProfileCurrency: activity.fee,
- SymbolProfile: {
- ...symbolProfileDummyData,
- currency: activity.currency,
- dataSource: activity.dataSource,
- name: 'Novartis AG',
- symbol: activity.symbol
- },
- tags: activity.tags?.map((id) => {
- return { id } as Tag;
- }),
- unitPriceInAssetProfileCurrency: activity.unitPrice
- }));
+ const activities: Activity[] = exportResponse.activities.map(
+ (activity) => ({
+ ...activityDummyData,
+ ...activity,
+ date: parseDate(activity.date),
+ feeInAssetProfileCurrency: activity.fee,
+ SymbolProfile: {
+ ...symbolProfileDummyData,
+ currency: activity.currency,
+ dataSource: activity.dataSource,
+ name: 'Novartis AG',
+ symbol: activity.symbol
+ },
+ unitPriceInAssetProfileCurrency: activity.unitPrice
+ })
+ );
const portfolioCalculator = portfolioCalculatorFactory.createCalculator({
activities,
calculationType: PerformanceCalculationType.ROAI,
- currency: 'CHF',
+ currency: exportResponse.user.settings.currency,
userId: userDummyData.id
});
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 e6c71230b..e426a68fa 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
@@ -1,8 +1,7 @@
-import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import {
activityDummyData,
- loadActivityExportFile,
+ loadExportFile,
symbolProfileDummyData,
userDummyData
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils';
@@ -16,9 +15,9 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-
import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service';
import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock';
import { parseDate } from '@ghostfolio/common/helper';
+import { Export } from '@ghostfolio/common/interfaces';
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type';
-import { Tag } from '@prisma/client';
import { Big } from 'big.js';
import { join } from 'node:path';
@@ -53,7 +52,7 @@ jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => {
});
describe('PortfolioCalculator', () => {
- let activityDtos: CreateOrderDto[];
+ let exportResponse: Export;
let configurationService: ConfigurationService;
let currentRateService: CurrentRateService;
@@ -63,7 +62,7 @@ describe('PortfolioCalculator', () => {
let redisCacheService: RedisCacheService;
beforeAll(() => {
- activityDtos = loadActivityExportFile(
+ exportResponse = loadExportFile(
join(
__dirname,
'../../../../../../../test/import/ok/novn-buy-and-sell.json'
@@ -100,28 +99,27 @@ describe('PortfolioCalculator', () => {
it.only('with NOVN.SW buy and sell', async () => {
jest.useFakeTimers().setSystemTime(parseDate('2022-04-11').getTime());
- const activities: Activity[] = activityDtos.map((activity) => ({
- ...activityDummyData,
- ...activity,
- date: parseDate(activity.date),
- feeInAssetProfileCurrency: activity.fee,
- SymbolProfile: {
- ...symbolProfileDummyData,
- currency: activity.currency,
- dataSource: activity.dataSource,
- name: 'Novartis AG',
- symbol: activity.symbol
- },
- tags: activity.tags?.map((id) => {
- return { id } as Tag;
- }),
- unitPriceInAssetProfileCurrency: activity.unitPrice
- }));
+ const activities: Activity[] = exportResponse.activities.map(
+ (activity) => ({
+ ...activityDummyData,
+ ...activity,
+ date: parseDate(activity.date),
+ feeInAssetProfileCurrency: activity.fee,
+ SymbolProfile: {
+ ...symbolProfileDummyData,
+ currency: activity.currency,
+ dataSource: activity.dataSource,
+ name: 'Novartis AG',
+ symbol: activity.symbol
+ },
+ unitPriceInAssetProfileCurrency: activity.unitPrice
+ })
+ );
const portfolioCalculator = portfolioCalculatorFactory.createCalculator({
activities,
calculationType: PerformanceCalculationType.ROAI,
- currency: 'CHF',
+ currency: exportResponse.user.settings.currency,
userId: userDummyData.id
});
diff --git a/test/import/ok/novn-buy-and-sell-partially.json b/test/import/ok/novn-buy-and-sell-partially.json
index 06cbc75ea..8c5778566 100644
--- a/test/import/ok/novn-buy-and-sell-partially.json
+++ b/test/import/ok/novn-buy-and-sell-partially.json
@@ -24,5 +24,10 @@
"date": "2022-03-07T00:00:00.000Z",
"symbol": "NOVN.SW"
}
- ]
+ ],
+ "user": {
+ "settings": {
+ "currency": "CHF"
+ }
+ }
}
diff --git a/test/import/ok/novn-buy-and-sell.json b/test/import/ok/novn-buy-and-sell.json
index b7ab6aee1..71ee9b7a9 100644
--- a/test/import/ok/novn-buy-and-sell.json
+++ b/test/import/ok/novn-buy-and-sell.json
@@ -24,5 +24,10 @@
"date": "2022-03-07T00:00:00.000Z",
"symbol": "NOVN.SW"
}
- ]
+ ],
+ "user": {
+ "settings": {
+ "currency": "CHF"
+ }
+ }
}
From 5188544c9b52bb170e5cd91c5aac6abf72916e41 Mon Sep 17 00:00:00 2001
From: H_S <81474463+HarjobandeepSingh@users.noreply.github.com>
Date: Fri, 17 Oct 2025 23:27:41 +0530
Subject: [PATCH 08/10] Task/migrate blog page component to standalone (#5742)
* Migrate blog page component to standalone
* Update changelog
---
CHANGELOG.md | 1 +
apps/client/src/app/app-routing.module.ts | 2 +-
.../src/app/pages/blog/blog-page.component.ts | 13 +++++++++----
.../client/src/app/pages/blog/blog-page.module.ts | 14 --------------
...page-routing.module.ts => blog-page.routes.ts} | 15 ++++-----------
5 files changed, 15 insertions(+), 30 deletions(-)
delete mode 100644 apps/client/src/app/pages/blog/blog-page.module.ts
rename apps/client/src/app/pages/blog/{blog-page-routing.module.ts => blog-page.routes.ts} (95%)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 758898cee..b97b37ae2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Improved the currency validation in the search functionality of the data provider service
- Optimized the get quotes functionality by utilizing the asset profile resolutions in the _Financial Modeling Prep_ service
- Extracted the footer to a component
+- Refactored the blog page component to standalone
- Improved the portfolio calculator unit tests to load the user currency from the exported file
### Fixed
diff --git a/apps/client/src/app/app-routing.module.ts b/apps/client/src/app/app-routing.module.ts
index 0ceee3725..fb045a174 100644
--- a/apps/client/src/app/app-routing.module.ts
+++ b/apps/client/src/app/app-routing.module.ts
@@ -48,7 +48,7 @@ const routes: Routes = [
{
path: publicRoutes.blog.path,
loadChildren: () =>
- import('./pages/blog/blog-page.module').then((m) => m.BlogPageModule)
+ import('./pages/blog/blog-page.routes').then((m) => m.routes)
},
{
canActivate: [AuthGuard],
diff --git a/apps/client/src/app/pages/blog/blog-page.component.ts b/apps/client/src/app/pages/blog/blog-page.component.ts
index 65a867f65..7599a3358 100644
--- a/apps/client/src/app/pages/blog/blog-page.component.ts
+++ b/apps/client/src/app/pages/blog/blog-page.component.ts
@@ -1,19 +1,24 @@
import { DataService } from '@ghostfolio/client/services/data.service';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
-import { Component, OnDestroy } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Component, CUSTOM_ELEMENTS_SCHEMA, OnDestroy } from '@angular/core';
+import { MatCardModule } from '@angular/material/card';
+import { RouterModule } from '@angular/router';
+import { IonIcon } from '@ionic/angular/standalone';
import { addIcons } from 'ionicons';
import { chevronForwardOutline } from 'ionicons/icons';
import { Subject } from 'rxjs';
@Component({
host: { class: 'page' },
+ imports: [CommonModule, IonIcon, MatCardModule, RouterModule],
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
selector: 'gf-blog-page',
styleUrls: ['./blog-page.scss'],
- templateUrl: './blog-page.html',
- standalone: false
+ templateUrl: './blog-page.html'
})
-export class BlogPageComponent implements OnDestroy {
+export class GfBlogPageComponent implements OnDestroy {
public hasPermissionForSubscription: boolean;
private unsubscribeSubject = new Subject();
diff --git a/apps/client/src/app/pages/blog/blog-page.module.ts b/apps/client/src/app/pages/blog/blog-page.module.ts
deleted file mode 100644
index 37925e494..000000000
--- a/apps/client/src/app/pages/blog/blog-page.module.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { CommonModule } from '@angular/common';
-import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
-import { MatCardModule } from '@angular/material/card';
-import { IonIcon } from '@ionic/angular/standalone';
-
-import { BlogPageRoutingModule } from './blog-page-routing.module';
-import { BlogPageComponent } from './blog-page.component';
-
-@NgModule({
- declarations: [BlogPageComponent],
- imports: [BlogPageRoutingModule, CommonModule, IonIcon, MatCardModule],
- schemas: [CUSTOM_ELEMENTS_SCHEMA]
-})
-export class BlogPageModule {}
diff --git a/apps/client/src/app/pages/blog/blog-page-routing.module.ts b/apps/client/src/app/pages/blog/blog-page.routes.ts
similarity index 95%
rename from apps/client/src/app/pages/blog/blog-page-routing.module.ts
rename to apps/client/src/app/pages/blog/blog-page.routes.ts
index 9b352b7a8..2b5a4be64 100644
--- a/apps/client/src/app/pages/blog/blog-page-routing.module.ts
+++ b/apps/client/src/app/pages/blog/blog-page.routes.ts
@@ -1,15 +1,14 @@
import { AuthGuard } from '@ghostfolio/client/core/auth.guard';
import { publicRoutes } from '@ghostfolio/common/routes/routes';
-import { NgModule } from '@angular/core';
-import { RouterModule, Routes } from '@angular/router';
+import { Routes } from '@angular/router';
-import { BlogPageComponent } from './blog-page.component';
+import { GfBlogPageComponent } from './blog-page.component';
-const routes: Routes = [
+export const routes: Routes = [
{
canActivate: [AuthGuard],
- component: BlogPageComponent,
+ component: GfBlogPageComponent,
path: '',
title: publicRoutes.blog.title
},
@@ -212,9 +211,3 @@ const routes: Routes = [
title: 'Hacktoberfest 2025'
}
];
-
-@NgModule({
- imports: [RouterModule.forChild(routes)],
- exports: [RouterModule]
-})
-export class BlogPageRoutingModule {}
From 5bcc2e9648c86109cac7b5a35e0625971e263b6a Mon Sep 17 00:00:00 2001
From: Umesh Pal <127204670+72umesh@users.noreply.github.com>
Date: Fri, 17 Oct 2025 23:33:08 +0530
Subject: [PATCH 09/10] Task/refactor PortfolioDividends interface to
PortfolioDividendsResponse interface (#5773)
* Refactor PortfolioDividends to PortfolioDividendsResponse
---
apps/api/src/app/portfolio/portfolio.controller.ts | 4 ++--
apps/client/src/app/services/data.service.ts | 11 +++++++----
libs/common/src/lib/interfaces/index.ts | 4 ++--
.../lib/interfaces/portfolio-dividends.interface.ts | 5 -----
.../portfolio-dividends-response.interface.ts | 5 +++++
5 files changed, 16 insertions(+), 13 deletions(-)
delete mode 100644 libs/common/src/lib/interfaces/portfolio-dividends.interface.ts
create mode 100644 libs/common/src/lib/interfaces/responses/portfolio-dividends-response.interface.ts
diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts
index f6f8e3d80..7d8ceecda 100644
--- a/apps/api/src/app/portfolio/portfolio.controller.ts
+++ b/apps/api/src/app/portfolio/portfolio.controller.ts
@@ -19,7 +19,7 @@ import {
} from '@ghostfolio/common/config';
import {
PortfolioDetails,
- PortfolioDividends,
+ PortfolioDividendsResponse,
PortfolioHoldingResponse,
PortfolioHoldingsResponse,
PortfolioInvestments,
@@ -305,7 +305,7 @@ export class PortfolioController {
@Query('range') dateRange: DateRange = 'max',
@Query('symbol') filterBySymbol?: string,
@Query('tags') filterByTags?: string
- ): Promise {
+ ): Promise {
const filters = this.apiService.buildFiltersFromQueryParams({
filterByAccounts,
filterByAssetClasses,
diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts
index c2678924b..0b5c4b253 100644
--- a/apps/client/src/app/services/data.service.ts
+++ b/apps/client/src/app/services/data.service.ts
@@ -42,7 +42,7 @@ import {
MarketDataOfMarketsResponse,
OAuthResponse,
PortfolioDetails,
- PortfolioDividends,
+ PortfolioDividendsResponse,
PortfolioHoldingResponse,
PortfolioHoldingsResponse,
PortfolioInvestments,
@@ -270,9 +270,12 @@ export class DataService {
params = params.append('groupBy', groupBy);
params = params.append('range', range);
- return this.http.get('/api/v1/portfolio/dividends', {
- params
- });
+ return this.http.get(
+ '/api/v1/portfolio/dividends',
+ {
+ params
+ }
+ );
}
public fetchDividendsImport({ dataSource, symbol }: AssetProfileIdentifier) {
diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts
index 1da2236e8..d38502945 100644
--- a/libs/common/src/lib/interfaces/index.ts
+++ b/libs/common/src/lib/interfaces/index.ts
@@ -30,7 +30,6 @@ import type { LookupItem } from './lookup-item.interface';
import type { MarketData } from './market-data.interface';
import type { PortfolioChart } from './portfolio-chart.interface';
import type { PortfolioDetails } from './portfolio-details.interface';
-import type { PortfolioDividends } from './portfolio-dividends.interface';
import type { PortfolioInvestments } from './portfolio-investments.interface';
import type { PortfolioPerformance } from './portfolio-performance.interface';
import type { PortfolioPosition } from './portfolio-position.interface';
@@ -56,6 +55,7 @@ import type { LookupResponse } from './responses/lookup-response.interface';
import type { MarketDataDetailsResponse } from './responses/market-data-details-response.interface';
import type { MarketDataOfMarketsResponse } from './responses/market-data-of-markets-response.interface';
import type { OAuthResponse } from './responses/oauth-response.interface';
+import type { PortfolioDividendsResponse } from './responses/portfolio-dividends-response.interface';
import { PortfolioHoldingResponse } from './responses/portfolio-holding-response.interface';
import type { PortfolioHoldingsResponse } from './responses/portfolio-holdings-response.interface';
import type { PortfolioPerformanceResponse } from './responses/portfolio-performance-response.interface';
@@ -122,7 +122,7 @@ export {
OAuthResponse,
PortfolioChart,
PortfolioDetails,
- PortfolioDividends,
+ PortfolioDividendsResponse,
PortfolioHoldingResponse,
PortfolioHoldingsResponse,
PortfolioInvestments,
diff --git a/libs/common/src/lib/interfaces/portfolio-dividends.interface.ts b/libs/common/src/lib/interfaces/portfolio-dividends.interface.ts
deleted file mode 100644
index 585c46bb7..000000000
--- a/libs/common/src/lib/interfaces/portfolio-dividends.interface.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { InvestmentItem } from './investment-item.interface';
-
-export interface PortfolioDividends {
- dividends: InvestmentItem[];
-}
diff --git a/libs/common/src/lib/interfaces/responses/portfolio-dividends-response.interface.ts b/libs/common/src/lib/interfaces/responses/portfolio-dividends-response.interface.ts
new file mode 100644
index 000000000..bd33dbccb
--- /dev/null
+++ b/libs/common/src/lib/interfaces/responses/portfolio-dividends-response.interface.ts
@@ -0,0 +1,5 @@
+import { InvestmentItem } from '../investment-item.interface';
+
+export interface PortfolioDividendsResponse {
+ dividends: InvestmentItem[];
+}
From 04d6d9cfa93ca20b527799e15cb272d684479ef5 Mon Sep 17 00:00:00 2001
From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
Date: Fri, 17 Oct 2025 20:04:27 +0200
Subject: [PATCH 10/10] Bugfix/total buy and sell calculation in summary
(#5759)
* Fix total buy and sell calculation related to activities in custom currency
* Update changelog
---
CHANGELOG.md | 1 +
apps/api/src/app/portfolio/portfolio.service.ts | 4 ++--
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b97b37ae2..c0f6bd0e1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
+- Fixed an issue with the total buy and sell calculation in the summary related to activities in a custom currency
- Respected the include indices flag in the search functionality of the _Financial Modeling Prep_ service
- Fixed an issue where the scroll position was not restored when changing pages
- Fixed the word wrap in the menus of the activities table component
diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts
index bbfb31b79..cb6eba5be 100644
--- a/apps/api/src/app/portfolio/portfolio.service.ts
+++ b/apps/api/src/app/portfolio/portfolio.service.ts
@@ -2126,11 +2126,11 @@ export class PortfolioService {
.filter(({ isDraft, type }) => {
return isDraft === false && type === activityType;
})
- .map(({ quantity, SymbolProfile, unitPrice }) => {
+ .map(({ currency, quantity, SymbolProfile, unitPrice }) => {
return new Big(
this.exchangeRateDataService.toCurrency(
new Big(quantity).mul(unitPrice).toNumber(),
- SymbolProfile.currency,
+ currency ?? SymbolProfile.currency,
userCurrency
)
);