diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts
index 31dd6cd73..772ee3068 100644
--- a/apps/api/src/app/portfolio/portfolio.controller.ts
+++ b/apps/api/src/app/portfolio/portfolio.controller.ts
@@ -117,17 +117,18 @@ export class PortfolioController {
hasReadRestrictedAccessPermission ||
this.userService.isRestrictedView(this.request.user)
) {
+ // TODO: Introduce calculations with currency effect
const totalInvestment = Object.values(holdings)
- .map((portfolioPosition) => {
- return portfolioPosition.investment;
+ .map(({ investment }) => {
+ return investment;
})
.reduce((a, b) => a + b, 0);
const totalValue = Object.values(holdings)
- .map((portfolioPosition) => {
+ .map(({ currency, marketPrice, quantity }) => {
return this.exchangeRateDataService.toCurrency(
- portfolioPosition.quantity * portfolioPosition.marketPrice,
- portfolioPosition.currency,
+ quantity * marketPrice,
+ currency,
this.request.user.Settings.settings.baseCurrency
);
})
@@ -135,9 +136,11 @@ export class PortfolioController {
for (const [symbol, portfolioPosition] of Object.entries(holdings)) {
portfolioPosition.grossPerformance = null;
+ portfolioPosition.grossPerformanceWithCurrencyEffect = null;
portfolioPosition.investment =
portfolioPosition.investment / totalInvestment;
portfolioPosition.netPerformance = null;
+ portfolioPosition.netPerformanceWithCurrencyEffect = null;
portfolioPosition.quantity = null;
portfolioPosition.valueInPercentage =
portfolioPosition.valueInBaseCurrency / totalValue;
diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts
index 9c70c404b..3fafa6795 100644
--- a/apps/api/src/app/portfolio/portfolio.service.ts
+++ b/apps/api/src/app/portfolio/portfolio.service.ts
@@ -529,12 +529,20 @@ export class PortfolioService {
grossPerformance: item.grossPerformance?.toNumber() ?? 0,
grossPerformancePercent:
item.grossPerformancePercentage?.toNumber() ?? 0,
+ grossPerformancePercentWithCurrencyEffect:
+ item.grossPerformancePercentageWithCurrencyEffect?.toNumber() ?? 0,
+ grossPerformanceWithCurrencyEffect:
+ item.grossPerformanceWithCurrencyEffect?.toNumber() ?? 0,
investment: item.investment.toNumber(),
marketPrice: item.marketPrice,
marketState: dataProviderResponse?.marketState ?? 'delayed',
name: symbolProfile.name,
netPerformance: item.netPerformance?.toNumber() ?? 0,
netPerformancePercent: item.netPerformancePercentage?.toNumber() ?? 0,
+ netPerformancePercentWithCurrencyEffect:
+ item.netPerformancePercentageWithCurrencyEffect?.toNumber() ?? 0,
+ netPerformanceWithCurrencyEffect:
+ item.netPerformanceWithCurrencyEffect?.toNumber() ?? 0,
quantity: item.quantity.toNumber(),
sectors: symbolProfile.sectors,
symbol: item.symbol,
@@ -1600,12 +1608,16 @@ export class PortfolioService {
dateOfFirstActivity: undefined,
grossPerformance: 0,
grossPerformancePercent: 0,
+ grossPerformancePercentWithCurrencyEffect: 0,
+ grossPerformanceWithCurrencyEffect: 0,
investment: balance,
marketPrice: 0,
marketState: 'open',
name: currency,
netPerformance: 0,
netPerformancePercent: 0,
+ netPerformancePercentWithCurrencyEffect: 0,
+ netPerformanceWithCurrencyEffect: 0,
quantity: 0,
sectors: [],
symbol: currency,
@@ -1814,9 +1826,25 @@ export class PortfolioService {
})
?.toNumber();
+ const annualizedPerformancePercentWithCurrencyEffect =
+ new PortfolioCalculator({
+ currency: userCurrency,
+ currentRateService: this.currentRateService,
+ exchangeRateDataService: this.exchangeRateDataService,
+ orders: []
+ })
+ .getAnnualizedPerformancePercent({
+ daysInMarket,
+ netPerformancePercent: new Big(
+ performanceInformation.performance.currentNetPerformancePercentWithCurrencyEffect
+ )
+ })
+ ?.toNumber();
+
return {
...performanceInformation.performance,
annualizedPerformancePercent,
+ annualizedPerformancePercentWithCurrencyEffect,
cash,
dividend,
excludedAccountsAndActivities,
diff --git a/apps/client/src/app/components/home-holdings/home-holdings.component.ts b/apps/client/src/app/components/home-holdings/home-holdings.component.ts
index aeb531379..9dbf9d9bf 100644
--- a/apps/client/src/app/components/home-holdings/home-holdings.component.ts
+++ b/apps/client/src/app/components/home-holdings/home-holdings.component.ts
@@ -154,8 +154,8 @@ export class HomeHoldingsComponent implements OnDestroy, OnInit {
this.dataService
.fetchPositions({ range: this.user?.settings?.dateRange })
.pipe(takeUntil(this.unsubscribeSubject))
- .subscribe((response) => {
- this.positions = response.positions;
+ .subscribe(({ positions }) => {
+ this.positions = positions;
this.changeDetectorRef.markForCheck();
});
diff --git a/apps/client/src/app/components/home-overview/home-overview.component.ts b/apps/client/src/app/components/home-overview/home-overview.component.ts
index 75fd21eeb..7beb322a2 100644
--- a/apps/client/src/app/components/home-overview/home-overview.component.ts
+++ b/apps/client/src/app/components/home-overview/home-overview.component.ts
@@ -127,10 +127,10 @@ export class HomeOverviewComponent implements OnDestroy, OnInit {
this.isLoadingPerformance = false;
this.historicalDataItems = chart.map(
- ({ date, netPerformanceInPercentage }) => {
+ ({ date, netPerformanceInPercentageWithCurrencyEffect }) => {
return {
date,
- value: netPerformanceInPercentage
+ value: netPerformanceInPercentageWithCurrencyEffect
};
}
);
diff --git a/apps/client/src/app/components/portfolio-performance/portfolio-performance.component.html b/apps/client/src/app/components/portfolio-performance/portfolio-performance.component.html
index fdc231579..68d191b5c 100644
--- a/apps/client/src/app/components/portfolio-performance/portfolio-performance.component.html
+++ b/apps/client/src/app/components/portfolio-performance/portfolio-performance.component.html
@@ -40,7 +40,11 @@
[colorizeSign]="true"
[isCurrency]="true"
[locale]="locale"
- [value]="isLoading ? undefined : performance?.currentNetPerformance"
+ [value]="
+ isLoading
+ ? undefined
+ : performance?.currentNetPerformanceWithCurrencyEffect
+ "
/>
@@ -49,7 +53,9 @@
[isPercent]="true"
[locale]="locale"
[value]="
- isLoading ? undefined : performance?.currentNetPerformancePercent
+ isLoading
+ ? undefined
+ : performance?.currentNetPerformancePercentWithCurrencyEffect
"
/>
diff --git a/apps/client/src/app/components/portfolio-performance/portfolio-performance.component.ts b/apps/client/src/app/components/portfolio-performance/portfolio-performance.component.ts
index 720adb3f6..b76ecb004 100644
--- a/apps/client/src/app/components/portfolio-performance/portfolio-performance.component.ts
+++ b/apps/client/src/app/components/portfolio-performance/portfolio-performance.component.ts
@@ -63,7 +63,8 @@ export class PortfolioPerformanceComponent implements OnChanges, OnInit {
} else if (this.showDetails === false) {
new CountUp(
'value',
- this.performance?.currentNetPerformancePercent * 100,
+ this.performance?.currentNetPerformancePercentWithCurrencyEffect *
+ 100,
{
decimal: getNumberFormatDecimal(this.locale),
decimalPlaces: 2,
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 6adaca943..c4aea8891 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
@@ -64,7 +64,11 @@
[isCurrency]="true"
[locale]="locale"
[unit]="baseCurrency"
- [value]="isLoading ? undefined : summary?.currentGrossPerformance"
+ [value]="
+ isLoading
+ ? undefined
+ : summary?.currentGrossPerformanceWithCurrencyEffect
+ "
/>
@@ -85,7 +89,9 @@
[isPercent]="true"
[locale]="locale"
[value]="
- isLoading ? undefined : summary?.currentGrossPerformancePercent
+ isLoading
+ ? undefined
+ : summary?.currentGrossPerformancePercentWithCurrencyEffect
"
/>
@@ -114,7 +120,11 @@
[isCurrency]="true"
[locale]="locale"
[unit]="baseCurrency"
- [value]="isLoading ? undefined : summary?.currentNetPerformance"
+ [value]="
+ isLoading
+ ? undefined
+ : summary?.currentNetPerformanceWithCurrencyEffect
+ "
/>
@@ -134,7 +144,11 @@
[colorizeSign]="true"
[isPercent]="true"
[locale]="locale"
- [value]="isLoading ? undefined : summary?.currentNetPerformancePercent"
+ [value]="
+ isLoading
+ ? undefined
+ : summary?.currentNetPerformancePercentWithCurrencyEffect
+ "
/>
@@ -283,7 +297,11 @@
[colorizeSign]="true"
[isPercent]="true"
[locale]="locale"
- [value]="isLoading ? undefined : summary?.annualizedPerformancePercent"
+ [value]="
+ isLoading
+ ? undefined
+ : summary?.annualizedPerformancePercentWithCurrencyEffect
+ "
/>
diff --git a/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts b/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts
index f44d41e82..2e3f8f1d3 100644
--- a/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts
+++ b/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts
@@ -50,15 +50,13 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
public dividendInBaseCurrency: number;
public feeInBaseCurrency: number;
public firstBuyDate: string;
- public grossPerformance: number;
- public grossPerformancePercent: number;
public historicalDataItems: LineChartItem[];
public investment: number;
public marketPrice: number;
public maxPrice: number;
public minPrice: number;
- public netPerformance: number;
- public netPerformancePercent: number;
+ public netPerformancePercentWithCurrencyEffect: number;
+ public netPerformanceWithCurrencyEffect: number;
public quantity: number;
public quantityPrecision = 2;
public reportDataGlitchMail: string;
@@ -99,15 +97,13 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
dividendInBaseCurrency,
feeInBaseCurrency,
firstBuyDate,
- grossPerformance,
- grossPerformancePercent,
historicalData,
investment,
marketPrice,
maxPrice,
minPrice,
- netPerformance,
- netPerformancePercent,
+ netPerformancePercentWithCurrencyEffect,
+ netPerformanceWithCurrencyEffect,
orders,
quantity,
SymbolProfile,
@@ -125,8 +121,6 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
this.dividendInBaseCurrency = dividendInBaseCurrency;
this.feeInBaseCurrency = feeInBaseCurrency;
this.firstBuyDate = firstBuyDate;
- this.grossPerformance = grossPerformance;
- this.grossPerformancePercent = grossPerformancePercent;
this.historicalDataItems = historicalData.map(
(historicalDataItem) => {
this.benchmarkDataItems.push({
@@ -144,8 +138,10 @@ export class PositionDetailDialog implements OnDestroy, OnInit {
this.marketPrice = marketPrice;
this.maxPrice = maxPrice;
this.minPrice = minPrice;
- this.netPerformance = netPerformance;
- this.netPerformancePercent = netPerformancePercent;
+ this.netPerformancePercentWithCurrencyEffect =
+ netPerformancePercentWithCurrencyEffect;
+ this.netPerformanceWithCurrencyEffect =
+ netPerformanceWithCurrencyEffect;
this.quantity = quantity;
this.reportDataGlitchMail = `mailto:hi@ghostfol.io?Subject=Ghostfolio Data Glitch Report&body=Hello%0D%0DI would like to report a data glitch for%0D%0DSymbol: ${SymbolProfile?.symbol}%0DData Source: ${SymbolProfile?.dataSource}%0D%0DAdditional notes:%0D%0DCan you please take a look?%0D%0DKind regards`;
this.sectors = {};
diff --git a/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html b/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html
index 3c6c12d80..f87d11e04 100644
--- a/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html
+++ b/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.html
@@ -44,7 +44,7 @@
[isCurrency]="true"
[locale]="data.locale"
[unit]="data.baseCurrency"
- [value]="netPerformance"
+ [value]="netPerformanceWithCurrencyEffect"
>Change
@@ -55,7 +55,7 @@
[colorizeSign]="true"
[isPercent]="true"
[locale]="data.locale"
- [value]="netPerformancePercent"
+ [value]="netPerformancePercentWithCurrencyEffect"
>Performance
diff --git a/apps/client/src/app/components/position/position.component.html b/apps/client/src/app/components/position/position.component.html
index 007ccc70f..4a5ed6f9a 100644
--- a/apps/client/src/app/components/position/position.component.html
+++ b/apps/client/src/app/components/position/position.component.html
@@ -17,7 +17,7 @@
[isLoading]="isLoading"
[marketState]="position?.marketState"
[range]="range"
- [value]="position?.netPerformancePercentage"
+ [value]="position?.netPerformancePercentageWithCurrencyEffect"
/>
@@ -49,13 +49,13 @@
[isCurrency]="true"
[locale]="locale"
[unit]="baseCurrency"
- [value]="position?.netPerformance"
+ [value]="position?.netPerformanceWithCurrencyEffect"
/>
diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts
index d2f8487ad..96c7c01dc 100644
--- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts
+++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts
@@ -270,23 +270,28 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
index,
{
date,
- netPerformanceInPercentage,
- totalInvestment,
- value,
- valueInPercentage
+ netPerformanceInPercentageWithCurrencyEffect,
+ totalInvestmentValueWithCurrencyEffect,
+ valueInPercentage,
+ valueWithCurrencyEffect
}
] of chart.entries()) {
if (index > 0 || this.user?.settings?.dateRange === 'max') {
// Ignore first item where value is 0
- this.investments.push({ date, investment: totalInvestment });
+ this.investments.push({
+ date,
+ investment: totalInvestmentValueWithCurrencyEffect
+ });
this.performanceDataItems.push({
date,
- value: isNumber(value) ? value : valueInPercentage
+ value: isNumber(valueWithCurrencyEffect)
+ ? valueWithCurrencyEffect
+ : valueInPercentage
});
}
this.performanceDataItemsInPercentage.push({
date,
- value: netPerformanceInPercentage
+ value: netPerformanceInPercentageWithCurrencyEffect
});
}
@@ -305,10 +310,10 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ positions }) => {
const positionsSorted = sortBy(
- positions.filter(({ netPerformancePercentage }) => {
- return isNumber(netPerformancePercentage);
+ positions.filter(({ netPerformancePercentageWithCurrencyEffect }) => {
+ return isNumber(netPerformancePercentageWithCurrencyEffect);
}),
- 'netPerformancePercentage'
+ 'netPerformancePercentageWithCurrencyEffect'
).reverse();
this.top3 = positionsSorted.slice(0, 3);
diff --git a/libs/common/src/lib/interfaces/portfolio-position.interface.ts b/libs/common/src/lib/interfaces/portfolio-position.interface.ts
index 0eebb2975..87db0117d 100644
--- a/libs/common/src/lib/interfaces/portfolio-position.interface.ts
+++ b/libs/common/src/lib/interfaces/portfolio-position.interface.ts
@@ -17,6 +17,8 @@ export interface PortfolioPosition {
exchange?: string;
grossPerformance: number;
grossPerformancePercent: number;
+ grossPerformancePercentWithCurrencyEffect: number;
+ grossPerformanceWithCurrencyEffect: number;
investment: number;
marketChange?: number;
marketChangePercent?: number;
@@ -27,6 +29,8 @@ export interface PortfolioPosition {
name: string;
netPerformance: number;
netPerformancePercent: number;
+ netPerformancePercentWithCurrencyEffect: number;
+ netPerformanceWithCurrencyEffect: number;
quantity: number;
sectors: Sector[];
symbol: string;
diff --git a/libs/common/src/lib/interfaces/portfolio-summary.interface.ts b/libs/common/src/lib/interfaces/portfolio-summary.interface.ts
index 4ebe9afa4..f0bb4c3b1 100644
--- a/libs/common/src/lib/interfaces/portfolio-summary.interface.ts
+++ b/libs/common/src/lib/interfaces/portfolio-summary.interface.ts
@@ -2,6 +2,7 @@ import { PortfolioPerformance } from './portfolio-performance.interface';
export interface PortfolioSummary extends PortfolioPerformance {
annualizedPerformancePercent: number;
+ annualizedPerformancePercentWithCurrencyEffect: number;
cash: number;
committedFunds: number;
dividend: number;
diff --git a/libs/common/src/lib/interfaces/position.interface.ts b/libs/common/src/lib/interfaces/position.interface.ts
index 2a7c4a3c0..d1f74380b 100644
--- a/libs/common/src/lib/interfaces/position.interface.ts
+++ b/libs/common/src/lib/interfaces/position.interface.ts
@@ -18,6 +18,8 @@ export interface Position {
name?: string;
netPerformance?: number;
netPerformancePercentage?: number;
+ netPerformancePercentageWithCurrencyEffect?: number;
+ netPerformanceWithCurrencyEffect?: number;
quantity: number;
symbol: string;
transactionCount: number;
diff --git a/libs/ui/src/lib/holdings-table/holdings-table.component.html b/libs/ui/src/lib/holdings-table/holdings-table.component.html
index 164c5e74b..17e7c9f36 100644
--- a/libs/ui/src/lib/holdings-table/holdings-table.component.html
+++ b/libs/ui/src/lib/holdings-table/holdings-table.component.html
@@ -114,7 +114,7 @@
*matHeaderCellDef
class="justify-content-end px-1"
mat-header-cell
- mat-sort-header="netPerformancePercent"
+ mat-sort-header="netPerformancePercentWithCurrencyEffect"
>
Performance
±
@@ -125,7 +125,11 @@
[colorizeSign]="true"
[isPercent]="true"
[locale]="locale"
- [value]="isLoading ? undefined : element.netPerformancePercent"
+ [value]="
+ isLoading
+ ? undefined
+ : element.netPerformancePercentWithCurrencyEffect
+ "
/>