diff --git a/CHANGELOG.md b/CHANGELOG.md index 4674e0bec..6ef7f85b5 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 benchmark detail dialog by the current market price +- Added the performance calculation type to the user settings (experimental) - Added `watchlist` to the `User` database schema as a preparation for watching assets ### Changed diff --git a/apps/api/src/app/portfolio/calculator/mwr/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/mwr/portfolio-calculator.ts index fab15e6e7..1460892fa 100644 --- a/apps/api/src/app/portfolio/calculator/mwr/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/mwr/portfolio-calculator.ts @@ -4,12 +4,17 @@ import { SymbolMetrics } from '@ghostfolio/common/interfaces'; import { PortfolioSnapshot } from '@ghostfolio/common/models'; +import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; export class MwrPortfolioCalculator extends PortfolioCalculator { protected calculateOverallPerformance(): PortfolioSnapshot { throw new Error('Method not implemented.'); } + protected getPerformanceCalculationType() { + return PerformanceCalculationType.MWR; + } + protected getSymbolMetrics({}: { end: Date; exchangeRates: { [dateString: string]: number }; diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts index 6cc5edeaf..24fe2b2f3 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.factory.ts @@ -5,20 +5,16 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration/con import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; import { Filter, HistoricalDataItem } from '@ghostfolio/common/interfaces'; +import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Injectable } from '@nestjs/common'; import { MwrPortfolioCalculator } from './mwr/portfolio-calculator'; import { PortfolioCalculator } from './portfolio-calculator'; import { RoaiPortfolioCalculator } from './roai/portfolio-calculator'; +import { RoiPortfolioCalculator } from './roi/portfolio-calculator'; import { TwrPortfolioCalculator } from './twr/portfolio-calculator'; -export enum PerformanceCalculationType { - MWR = 'MWR', // Money-Weighted Rate of Return - ROAI = 'ROAI', // Return on Average Investment - TWR = 'TWR' // Time-Weighted Rate of Return -} - @Injectable() export class PortfolioCalculatorFactory { public constructor( @@ -58,6 +54,7 @@ export class PortfolioCalculatorFactory { portfolioSnapshotService: this.portfolioSnapshotService, redisCacheService: this.redisCacheService }); + case PerformanceCalculationType.ROAI: return new RoaiPortfolioCalculator({ accountBalanceItems, @@ -71,6 +68,21 @@ export class PortfolioCalculatorFactory { portfolioSnapshotService: this.portfolioSnapshotService, redisCacheService: this.redisCacheService }); + + case PerformanceCalculationType.ROI: + return new RoiPortfolioCalculator({ + accountBalanceItems, + activities, + currency, + filters, + userId, + configurationService: this.configurationService, + currentRateService: this.currentRateService, + exchangeRateDataService: this.exchangeRateDataService, + portfolioSnapshotService: this.portfolioSnapshotService, + redisCacheService: this.redisCacheService + }); + case PerformanceCalculationType.TWR: return new TwrPortfolioCalculator({ accountBalanceItems, @@ -84,6 +96,7 @@ export class PortfolioCalculatorFactory { portfolioSnapshotService: this.portfolioSnapshotService, redisCacheService: this.redisCacheService }); + default: throw new Error('Invalid calculation type'); } diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts index 9698cb315..850b58113 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts @@ -35,6 +35,7 @@ import { } from '@ghostfolio/common/interfaces'; import { PortfolioSnapshot, TimelinePosition } from '@ghostfolio/common/models'; import { GroupBy } from '@ghostfolio/common/types'; +import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Logger } from '@nestjs/common'; import { Big } from 'big.js'; @@ -623,6 +624,8 @@ export abstract class PortfolioCalculator { }; } + protected abstract getPerformanceCalculationType(): PerformanceCalculationType; + public getDataProviderInfos() { return this.dataProviderInfos; } @@ -1073,6 +1076,7 @@ export abstract class PortfolioCalculator { // Compute in the background this.portfolioSnapshotService.addJobToQueue({ data: { + calculationType: this.getPerformanceCalculationType(), filters: this.filters, userCurrency: this.currency, userId: this.userId @@ -1089,6 +1093,7 @@ export abstract class PortfolioCalculator { // Wait for computation await this.portfolioSnapshotService.addJobToQueue({ data: { + calculationType: this.getPerformanceCalculationType(), filters: this.filters, userCurrency: this.currency, userId: this.userId 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 eed5a1e80..589c2dcee 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 @@ -4,10 +4,7 @@ import { symbolProfileDummyData, userDummyData } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; -import { - PortfolioCalculatorFactory, - PerformanceCalculationType -} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; +import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; @@ -17,6 +14,7 @@ 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 { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; 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 15f3983fe..033f2622d 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 @@ -4,10 +4,7 @@ import { symbolProfileDummyData, userDummyData } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; -import { - PerformanceCalculationType, - PortfolioCalculatorFactory -} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; +import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; @@ -17,6 +14,7 @@ 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 { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; 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 7a34bd114..b21192db1 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 @@ -4,10 +4,7 @@ import { symbolProfileDummyData, userDummyData } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; -import { - PortfolioCalculatorFactory, - PerformanceCalculationType -} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; +import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; @@ -17,6 +14,7 @@ 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 { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; 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 3158076cb..acd07344e 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 @@ -4,10 +4,7 @@ import { symbolProfileDummyData, userDummyData } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; -import { - PortfolioCalculatorFactory, - PerformanceCalculationType -} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; +import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; @@ -18,6 +15,7 @@ import { ExchangeRateDataServiceMock } from '@ghostfolio/api/services/exchange-r 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 { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts index 22a34af24..a8aea0841 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-fee.spec.ts @@ -4,10 +4,7 @@ import { symbolProfileDummyData, userDummyData } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; -import { - PortfolioCalculatorFactory, - PerformanceCalculationType -} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; +import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; @@ -17,6 +14,7 @@ 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 { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; 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 41299eb40..18e8dd5df 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 @@ -4,10 +4,7 @@ import { symbolProfileDummyData, userDummyData } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; -import { - PortfolioCalculatorFactory, - PerformanceCalculationType -} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; +import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; @@ -18,6 +15,7 @@ import { ExchangeRateDataServiceMock } from '@ghostfolio/api/services/exchange-r 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 { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-item.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-item.spec.ts index 721c9ae96..21d208d9f 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-item.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-item.spec.ts @@ -4,10 +4,7 @@ import { symbolProfileDummyData, userDummyData } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; -import { - PortfolioCalculatorFactory, - PerformanceCalculationType -} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; +import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; @@ -17,6 +14,7 @@ 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 { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts index a82e605d4..2cb3899e9 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-liability.spec.ts @@ -4,10 +4,7 @@ import { symbolProfileDummyData, userDummyData } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; -import { - PortfolioCalculatorFactory, - PerformanceCalculationType -} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; +import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; @@ -17,6 +14,7 @@ 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 { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; 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 d8e774639..8fc179879 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 @@ -4,10 +4,7 @@ import { symbolProfileDummyData, userDummyData } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; -import { - PerformanceCalculationType, - PortfolioCalculatorFactory -} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; +import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; @@ -18,6 +15,7 @@ import { ExchangeRateDataServiceMock } from '@ghostfolio/api/services/exchange-r 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 { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-no-orders.spec.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-no-orders.spec.ts index 77e3f6157..9d5b74bb2 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-no-orders.spec.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator-no-orders.spec.ts @@ -1,8 +1,5 @@ import { userDummyData } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; -import { - PerformanceCalculationType, - PortfolioCalculatorFactory -} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; +import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; @@ -12,6 +9,7 @@ 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 { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; 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 ffbfe7345..92a3e33ed 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 @@ -6,10 +6,7 @@ import { symbolProfileDummyData, userDummyData } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; -import { - PerformanceCalculationType, - PortfolioCalculatorFactory -} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; +import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; @@ -19,6 +16,7 @@ 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 { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; import { join } from 'path'; 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 0256a6a1e..b5d7d59f8 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 @@ -6,10 +6,7 @@ import { symbolProfileDummyData, userDummyData } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; -import { - PerformanceCalculationType, - PortfolioCalculatorFactory -} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; +import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; @@ -19,6 +16,7 @@ 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 { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Big } from 'big.js'; import { join } from 'path'; diff --git a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts index 5b918fa03..c22a101bd 100644 --- a/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts @@ -9,6 +9,7 @@ import { } from '@ghostfolio/common/interfaces'; import { PortfolioSnapshot, TimelinePosition } from '@ghostfolio/common/models'; import { DateRange } from '@ghostfolio/common/types'; +import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Logger } from '@nestjs/common'; import { Big } from 'big.js'; @@ -112,6 +113,10 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { }; } + protected getPerformanceCalculationType() { + return PerformanceCalculationType.ROAI; + } + protected getSymbolMetrics({ chartDateMap, dataSource, diff --git a/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator.ts new file mode 100644 index 000000000..b4929c570 --- /dev/null +++ b/apps/api/src/app/portfolio/calculator/roi/portfolio-calculator.ts @@ -0,0 +1,29 @@ +import { PortfolioCalculator } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator'; +import { + AssetProfileIdentifier, + SymbolMetrics +} from '@ghostfolio/common/interfaces'; +import { PortfolioSnapshot } from '@ghostfolio/common/models'; +import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; + +export class RoiPortfolioCalculator extends PortfolioCalculator { + protected calculateOverallPerformance(): PortfolioSnapshot { + throw new Error('Method not implemented.'); + } + + protected getPerformanceCalculationType() { + return PerformanceCalculationType.ROI; + } + + protected getSymbolMetrics({}: { + end: Date; + exchangeRates: { [dateString: string]: number }; + marketSymbolMap: { + [date: string]: { [symbol: string]: Big }; + }; + start: Date; + step?: number; + } & AssetProfileIdentifier): SymbolMetrics { + throw new Error('Method not implemented.'); + } +} diff --git a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts index 6499ca3db..8a58f816a 100644 --- a/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts @@ -4,12 +4,17 @@ import { SymbolMetrics } from '@ghostfolio/common/interfaces'; import { PortfolioSnapshot } from '@ghostfolio/common/models'; +import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; export class TwrPortfolioCalculator extends PortfolioCalculator { protected calculateOverallPerformance(): PortfolioSnapshot { throw new Error('Method not implemented.'); } + protected getPerformanceCalculationType() { + return PerformanceCalculationType.TWR; + } + protected getSymbolMetrics({}: { end: Date; exchangeRates: { [dateString: string]: number }; diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index d3bbc1e06..6d0cde40e 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -50,13 +50,14 @@ import { UserSettings } from '@ghostfolio/common/interfaces'; import { TimelinePosition } from '@ghostfolio/common/models'; -import type { +import { AccountWithValue, DateRange, GroupBy, RequestWithUser, UserWithSettings } from '@ghostfolio/common/types'; +import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Inject, Injectable } from '@nestjs/common'; import { REQUEST } from '@nestjs/core'; @@ -85,10 +86,7 @@ import { import { isEmpty } from 'lodash'; import { PortfolioCalculator } from './calculator/portfolio-calculator'; -import { - PerformanceCalculationType, - PortfolioCalculatorFactory -} from './calculator/portfolio-calculator.factory'; +import { PortfolioCalculatorFactory } from './calculator/portfolio-calculator.factory'; import { PortfolioHoldingDetail } from './interfaces/portfolio-holding-detail.interface'; import { RulesService } from './rules.service'; @@ -278,14 +276,16 @@ export class PortfolioService { savingsRate: number; }): Promise { const userId = await this.getUserId(impersonationId, this.request.user.id); + const user = await this.userService.user({ id: userId }); + const userCurrency = this.getUserCurrency(user); const { endDate, startDate } = getIntervalFromDateRange(dateRange); const { activities } = await this.orderService.getOrdersForPortfolioCalculator({ filters, - userId, - userCurrency: this.getUserCurrency() + userCurrency, + userId }); if (activities.length === 0) { @@ -299,8 +299,8 @@ export class PortfolioService { activities, filters, userId, - calculationType: PerformanceCalculationType.ROAI, - currency: this.request.user.Settings.settings.baseCurrency + calculationType: this.getUserPerformanceCalculationType(user), + currency: userCurrency }); const { historicalData } = await portfolioCalculator.getSnapshot(); @@ -376,7 +376,7 @@ export class PortfolioService { activities, filters, userId, - calculationType: PerformanceCalculationType.ROAI, + calculationType: this.getUserPerformanceCalculationType(user), currency: userCurrency }); @@ -684,7 +684,7 @@ export class PortfolioService { const portfolioCalculator = this.calculatorFactory.createCalculator({ activities, userId, - calculationType: PerformanceCalculationType.ROAI, + calculationType: this.getUserPerformanceCalculationType(user), currency: userCurrency }); @@ -935,12 +935,13 @@ export class PortfolioService { })?.id; const userId = await this.getUserId(impersonationId, this.request.user.id); const user = await this.userService.user({ id: userId }); + const userCurrency = this.getUserCurrency(user); const { activities } = await this.orderService.getOrdersForPortfolioCalculator({ filters, - userId, - userCurrency: this.getUserCurrency() + userCurrency, + userId }); if (activities.length === 0) { @@ -954,8 +955,8 @@ export class PortfolioService { activities, filters, userId, - calculationType: PerformanceCalculationType.ROAI, - currency: this.request.user.Settings.settings.baseCurrency + calculationType: this.getUserPerformanceCalculationType(user), + currency: userCurrency }); const portfolioSnapshot = await portfolioCalculator.getSnapshot(); @@ -1120,7 +1121,7 @@ export class PortfolioService { activities, filters, userId, - calculationType: PerformanceCalculationType.ROAI, + calculationType: this.getUserPerformanceCalculationType(user), currency: userCurrency }); @@ -2021,6 +2022,12 @@ export class PortfolioService { return impersonationUserId || aUserId; } + private getUserPerformanceCalculationType( + aUser: UserWithSettings + ): PerformanceCalculationType { + return aUser?.Settings?.settings.performanceCalculationType; + } + private async getValueOfAccountsAndPlatforms({ activities, filters = [], diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index ebdf09ba5..b6b31dada 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -41,6 +41,7 @@ import { permissions } from '@ghostfolio/common/permissions'; import { UserWithSettings } from '@ghostfolio/common/types'; +import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; import { Injectable } from '@nestjs/common'; import { EventEmitter2 } from '@nestjs/event-emitter'; @@ -246,6 +247,12 @@ export class UserService { ? 'max' : ((user.Settings.settings as UserSettings)?.dateRange ?? 'max'); + // Set default value for performance calculation type + if (!(user.Settings.settings as UserSettings)?.performanceCalculationType) { + (user.Settings.settings as UserSettings).performanceCalculationType = + PerformanceCalculationType.ROAI; + } + // Set default value for view mode if (!(user.Settings.settings as UserSettings).viewMode) { (user.Settings.settings as UserSettings).viewMode = 'DEFAULT'; diff --git a/apps/api/src/services/queues/portfolio-snapshot/interfaces/portfolio-snapshot-queue-job.interface.ts b/apps/api/src/services/queues/portfolio-snapshot/interfaces/portfolio-snapshot-queue-job.interface.ts index 24948e211..b9f315c5d 100644 --- a/apps/api/src/services/queues/portfolio-snapshot/interfaces/portfolio-snapshot-queue-job.interface.ts +++ b/apps/api/src/services/queues/portfolio-snapshot/interfaces/portfolio-snapshot-queue-job.interface.ts @@ -1,6 +1,8 @@ import { Filter } from '@ghostfolio/common/interfaces'; +import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; export interface IPortfolioSnapshotQueueJob { + calculationType: PerformanceCalculationType; filters: Filter[]; userCurrency: string; userId: string; diff --git a/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts b/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts index 60c3cf695..6a2a3114e 100644 --- a/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts +++ b/apps/api/src/services/queues/portfolio-snapshot/portfolio-snapshot.processor.ts @@ -1,9 +1,6 @@ import { AccountBalanceService } from '@ghostfolio/api/app/account-balance/account-balance.service'; import { OrderService } from '@ghostfolio/api/app/order/order.service'; -import { - PerformanceCalculationType, - PortfolioCalculatorFactory -} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; +import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; import { PortfolioSnapshotValue } from '@ghostfolio/api/app/portfolio/interfaces/snapshot-value.interface'; import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; @@ -68,7 +65,7 @@ export class PortfolioSnapshotProcessor { const portfolioCalculator = this.calculatorFactory.createCalculator({ accountBalanceItems, activities, - calculationType: PerformanceCalculationType.ROAI, + calculationType: job.data.calculationType, currency: job.data.userCurrency, filters: job.data.filters, userId: job.data.userId diff --git a/apps/client/src/app/components/user-account-settings/user-account-settings.html b/apps/client/src/app/components/user-account-settings/user-account-settings.html index 8bd2efd1f..72d5aa678 100644 --- a/apps/client/src/app/components/user-account-settings/user-account-settings.html +++ b/apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -2,25 +2,7 @@

Settings

-
-
-
Presenter View
-
- Protection for sensitive information like absolute performances and - quantity values -
-
-
- -
-
-
+
@@ -43,6 +25,32 @@
+ @if (user?.settings?.isExperimentalFeatures && !user?.subscription) { +
+
+ Performance Calculation +
+
+ + + Return on Average Investment (ROAI) + + +
+
+ }
Language
@@ -172,6 +180,24 @@
+
+
+
Presenter View
+
+ Protection for sensitive information like absolute performances and + quantity values +
+
+
+ +
+
Zen Mode
diff --git a/libs/common/src/lib/interfaces/user-settings.interface.ts b/libs/common/src/lib/interfaces/user-settings.interface.ts index d72be7c7c..942f6e616 100644 --- a/libs/common/src/lib/interfaces/user-settings.interface.ts +++ b/libs/common/src/lib/interfaces/user-settings.interface.ts @@ -5,6 +5,7 @@ import { HoldingsViewMode, ViewMode } from '@ghostfolio/common/types'; +import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; export interface UserSettings { annualInterestRate?: number; @@ -22,6 +23,7 @@ export interface UserSettings { isRestrictedView?: boolean; language?: string; locale?: string; + performanceCalculationType?: PerformanceCalculationType; projectedTotalAmount?: number; retirementDate?: string; savingsRate?: number; diff --git a/libs/common/src/lib/types/performance-calculation-type.type.ts b/libs/common/src/lib/types/performance-calculation-type.type.ts new file mode 100644 index 000000000..a970636b6 --- /dev/null +++ b/libs/common/src/lib/types/performance-calculation-type.type.ts @@ -0,0 +1,6 @@ +export enum PerformanceCalculationType { + MWR = 'MWR', // Money-Weighted Rate of Return + ROAI = 'ROAI', // Return on Average Investment + ROI = 'ROI', // Return on Investment + TWR = 'TWR' // Time-Weighted Rate of Return +}