diff --git a/CHANGELOG.md b/CHANGELOG.md index 03d762319..0366c3950 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 ### Changed - Reworked the portfolio calculator +- Exposed the maximum of chart data items as an environment variable (`MAX_CHART_ITEMS`) ### Fixed diff --git a/apps/api/src/app/benchmark/benchmark.module.ts b/apps/api/src/app/benchmark/benchmark.module.ts index e7d1e9435..4c5f4d58e 100644 --- a/apps/api/src/app/benchmark/benchmark.module.ts +++ b/apps/api/src/app/benchmark/benchmark.module.ts @@ -2,6 +2,7 @@ import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.mo import { SymbolModule } from '@ghostfolio/api/app/symbol/symbol.module'; import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module'; import { TransformDataSourceInResponseModule } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.module'; +import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module'; @@ -18,6 +19,7 @@ import { BenchmarkService } from './benchmark.service'; controllers: [BenchmarkController], exports: [BenchmarkService], imports: [ + ConfigurationModule, DataProviderModule, ExchangeRateDataModule, MarketDataModule, diff --git a/apps/api/src/app/benchmark/benchmark.service.spec.ts b/apps/api/src/app/benchmark/benchmark.service.spec.ts index 42a29e6d1..5371fcdc0 100644 --- a/apps/api/src/app/benchmark/benchmark.service.spec.ts +++ b/apps/api/src/app/benchmark/benchmark.service.spec.ts @@ -12,6 +12,7 @@ describe('BenchmarkService', () => { null, null, null, + null, null ); }); diff --git a/apps/api/src/app/benchmark/benchmark.service.ts b/apps/api/src/app/benchmark/benchmark.service.ts index 710eb0212..169ea8cad 100644 --- a/apps/api/src/app/benchmark/benchmark.service.ts +++ b/apps/api/src/app/benchmark/benchmark.service.ts @@ -1,15 +1,13 @@ import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; import { SymbolService } from '@ghostfolio/api/app/symbol/symbol.service'; +import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; -import { - MAX_CHART_ITEMS, - PROPERTY_BENCHMARKS -} from '@ghostfolio/common/config'; +import { PROPERTY_BENCHMARKS } from '@ghostfolio/common/config'; import { DATE_FORMAT, calculateBenchmarkTrend, @@ -47,6 +45,7 @@ export class BenchmarkService { private readonly CACHE_KEY_BENCHMARKS = 'BENCHMARKS'; public constructor( + private readonly configurationService: ConfigurationService, private readonly dataProviderService: DataProviderService, private readonly exchangeRateDataService: ExchangeRateDataService, private readonly marketDataService: MarketDataService, @@ -173,7 +172,12 @@ export class BenchmarkService { start: startDate, end: endDate }, - { step: Math.round(days / Math.min(days, MAX_CHART_ITEMS)) } + { + step: Math.round( + days / + Math.min(days, this.configurationService.get('MAX_CHART_ITEMS')) + ) + } ).map((date) => { return resetHours(date); }); diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts index 74d1bd458..99f71ef0e 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts @@ -9,7 +9,6 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration/con import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper'; -import { MAX_CHART_ITEMS } from '@ghostfolio/common/config'; import { DATE_FORMAT, getSum, @@ -247,7 +246,13 @@ export abstract class PortfolioCalculator { let chartDateMap = this.getChartDateMap({ endDate: this.endDate, startDate: this.startDate, - step: Math.round(daysInMarket / Math.min(daysInMarket, MAX_CHART_ITEMS)) + step: Math.round( + daysInMarket / + Math.min( + daysInMarket, + this.configurationService.get('MAX_CHART_ITEMS') + ) + ) }); const chartDates = sortBy(Object.keys(chartDateMap), (chartDate) => { @@ -659,12 +664,6 @@ export abstract class PortfolioCalculator { return this.snapshot.totalLiabilitiesWithCurrencyEffect; } - public async getSnapshot() { - await this.snapshotPromise; - - return this.snapshot; - } - public async getPerformance({ end, start }) { await this.snapshotPromise; @@ -733,6 +732,12 @@ export abstract class PortfolioCalculator { return { chart }; } + public async getSnapshot() { + await this.snapshotPromise; + + return this.snapshot; + } + public getStartDate() { let firstAccountBalanceDate: Date; let firstActivityDate: Date; @@ -813,9 +818,17 @@ export abstract class PortfolioCalculator { } if (step > 1) { - // Reduce the step size of recent dates + // Reduce the step size of last 90 days for (let date of eachDayOfInterval( { end: endDate, start: subDays(endDate, 90) }, + { step: 3 } + )) { + chartDateMap[format(date, DATE_FORMAT)] = true; + } + + // Reduce the step size of last 30 days + for (let date of eachDayOfInterval( + { end: endDate, start: subDays(endDate, 30) }, { step: 1 } )) { chartDateMap[format(date, DATE_FORMAT)] = true; diff --git a/apps/api/src/services/configuration/configuration.service.ts b/apps/api/src/services/configuration/configuration.service.ts index 9e3f7fdbc..6a50766d2 100644 --- a/apps/api/src/services/configuration/configuration.service.ts +++ b/apps/api/src/services/configuration/configuration.service.ts @@ -42,6 +42,7 @@ export class ConfigurationService { HOST: host({ default: '0.0.0.0' }), JWT_SECRET_KEY: str({}), MAX_ACTIVITIES_TO_IMPORT: num({ default: Number.MAX_SAFE_INTEGER }), + MAX_CHART_ITEMS: num({ default: 365 }), MAX_ITEM_IN_CACHE: num({ default: 9999 }), PORT: port({ default: 3333 }), REDIS_DB: num({ default: 0 }), diff --git a/apps/api/src/services/interfaces/environment.interface.ts b/apps/api/src/services/interfaces/environment.interface.ts index 5d3145a28..c0dfb1806 100644 --- a/apps/api/src/services/interfaces/environment.interface.ts +++ b/apps/api/src/services/interfaces/environment.interface.ts @@ -28,6 +28,7 @@ export interface Environment extends CleanedEnvAccessors { GOOGLE_SHEETS_PRIVATE_KEY: string; JWT_SECRET_KEY: string; MAX_ACTIVITIES_TO_IMPORT: number; + MAX_CHART_ITEMS: number; MAX_ITEM_IN_CACHE: number; PORT: number; REDIS_DB: number; diff --git a/libs/common/src/lib/config.ts b/libs/common/src/lib/config.ts index 9c08b8253..50c02ae20 100644 --- a/libs/common/src/lib/config.ts +++ b/libs/common/src/lib/config.ts @@ -88,7 +88,6 @@ export const HEADER_KEY_IMPERSONATION = 'Impersonation-Id'; export const HEADER_KEY_TIMEZONE = 'Timezone'; export const HEADER_KEY_TOKEN = 'Authorization'; -export const MAX_CHART_ITEMS = 365; export const MAX_TOP_HOLDINGS = 50; export const NUMERICAL_PRECISION_THRESHOLD = 100000;