From f940eb67259c572174e0dfc7d6ba1c49b6d346aa Mon Sep 17 00:00:00 2001 From: Aman <142656811+Amanthisside@users.noreply.github.com> Date: Sat, 18 Oct 2025 12:56:39 +0530 Subject: [PATCH 1/6] Task/refactor PortfolioInvestments interface to PortfolioInvestmentsResponse interface (#5774) * Refactor PortfolioInvestments to PortfolioInvestmentsResponse --- apps/api/src/app/portfolio/portfolio.controller.ts | 4 ++-- apps/api/src/app/portfolio/portfolio.service.ts | 6 +++--- .../app/pages/portfolio/analysis/analysis-page.component.ts | 4 ++-- apps/client/src/app/services/data.service.ts | 4 ++-- libs/common/src/lib/interfaces/index.ts | 4 ++-- .../src/lib/interfaces/portfolio-investments.interface.ts | 6 ------ .../interfaces/responses/portfolio-investments.interface.ts | 6 ++++++ 7 files changed, 17 insertions(+), 17 deletions(-) delete mode 100644 libs/common/src/lib/interfaces/portfolio-investments.interface.ts create mode 100644 libs/common/src/lib/interfaces/responses/portfolio-investments.interface.ts diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts index 7d8ceecda..03796dad6 100644 --- a/apps/api/src/app/portfolio/portfolio.controller.ts +++ b/apps/api/src/app/portfolio/portfolio.controller.ts @@ -22,7 +22,7 @@ import { PortfolioDividendsResponse, PortfolioHoldingResponse, PortfolioHoldingsResponse, - PortfolioInvestments, + PortfolioInvestmentsResponse, PortfolioPerformanceResponse, PortfolioReportResponse } from '@ghostfolio/common/interfaces'; @@ -439,7 +439,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/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index cb6eba5be..b74b779f6 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -46,7 +46,7 @@ import { InvestmentItem, PortfolioDetails, PortfolioHoldingResponse, - PortfolioInvestments, + PortfolioInvestmentsResponse, PortfolioPerformanceResponse, PortfolioPosition, PortfolioReportResponse, @@ -397,7 +397,7 @@ export class PortfolioService { impersonationId: string; savingsRate: number; userId: string; - }): Promise { + }): Promise { userId = await this.getUserId(impersonationId, userId); const user = await this.userService.user({ id: userId }); const userCurrency = this.getUserCurrency(user); @@ -448,7 +448,7 @@ export class PortfolioService { }); } - let streaks: PortfolioInvestments['streaks']; + let streaks: PortfolioInvestmentsResponse['streaks']; if (savingsRate) { streaks = this.getStreaks({ 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 26d474f73..63ed3569c 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 @@ -6,7 +6,7 @@ import { UserService } from '@ghostfolio/client/services/user/user.service'; import { HistoricalDataItem, InvestmentItem, - PortfolioInvestments, + PortfolioInvestmentsResponse, PortfolioPerformance, PortfolioPosition, ToggleOption, @@ -94,7 +94,7 @@ export class GfAnalysisPageComponent implements OnDestroy, OnInit { public performanceDataItems: HistoricalDataItem[]; public performanceDataItemsInPercentage: HistoricalDataItem[]; public portfolioEvolutionDataLabel = $localize`Investment`; - public streaks: PortfolioInvestments['streaks']; + public streaks: PortfolioInvestmentsResponse['streaks']; public top3: PortfolioPosition[]; public unitCurrentStreak: string; public unitLongestStreak: string; diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index 0b5c4b253..b5f6d9e01 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -45,7 +45,7 @@ import { PortfolioDividendsResponse, PortfolioHoldingResponse, PortfolioHoldingsResponse, - PortfolioInvestments, + PortfolioInvestmentsResponse, PortfolioPerformanceResponse, PortfolioReportResponse, PublicPortfolioResponse, @@ -463,7 +463,7 @@ export class DataService { params = params.append('groupBy', groupBy); params = params.append('range', range); - return this.http.get( + return this.http.get( '/api/v1/portfolio/investments', { params } ); diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts index d38502945..8a7ef8544 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 { PortfolioInvestments } from './portfolio-investments.interface'; import type { PortfolioPerformance } from './portfolio-performance.interface'; import type { PortfolioPosition } from './portfolio-position.interface'; import type { PortfolioReportRule } from './portfolio-report-rule.interface'; @@ -58,6 +57,7 @@ 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 { PortfolioInvestmentsResponse } from './responses/portfolio-investments.interface'; import type { PortfolioPerformanceResponse } from './responses/portfolio-performance-response.interface'; import type { PortfolioReportResponse } from './responses/portfolio-report.interface'; import type { PublicPortfolioResponse } from './responses/public-portfolio-response.interface'; @@ -125,7 +125,7 @@ export { PortfolioDividendsResponse, PortfolioHoldingResponse, PortfolioHoldingsResponse, - PortfolioInvestments, + PortfolioInvestmentsResponse, PortfolioPerformance, PortfolioPerformanceResponse, PortfolioPosition, diff --git a/libs/common/src/lib/interfaces/portfolio-investments.interface.ts b/libs/common/src/lib/interfaces/portfolio-investments.interface.ts deleted file mode 100644 index 70de5c7d6..000000000 --- a/libs/common/src/lib/interfaces/portfolio-investments.interface.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { InvestmentItem } from './investment-item.interface'; - -export interface PortfolioInvestments { - investments: InvestmentItem[]; - streaks: { currentStreak: number; longestStreak: number }; -} diff --git a/libs/common/src/lib/interfaces/responses/portfolio-investments.interface.ts b/libs/common/src/lib/interfaces/responses/portfolio-investments.interface.ts new file mode 100644 index 000000000..6d0d60002 --- /dev/null +++ b/libs/common/src/lib/interfaces/responses/portfolio-investments.interface.ts @@ -0,0 +1,6 @@ +import { InvestmentItem } from '../investment-item.interface'; + +export interface PortfolioInvestmentsResponse { + investments: InvestmentItem[]; + streaks: { currentStreak: number; longestStreak: number }; +} From 71b375ea92706bde6fba83bdb9e62d13104041f1 Mon Sep 17 00:00:00 2001 From: Google <53668973+GooglyBlox@users.noreply.github.com> Date: Sat, 18 Oct 2025 01:14:24 -0700 Subject: [PATCH 2/6] Task/refactor BenchmarkMarketDataDetails to BenchmarkMarketDataDetailsResponse (#5771) * Refactor BenchmarkMarketDataDetails to BenchmarkMarketDataDetailsResponse --- .../src/app/endpoints/benchmarks/benchmarks.controller.ts | 4 ++-- apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts | 4 ++-- apps/client/src/app/services/data.service.ts | 6 +++--- libs/common/src/lib/interfaces/index.ts | 4 ++-- .../benchmark-market-data-details-response.interface.ts} | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) rename libs/common/src/lib/interfaces/{benchmark-market-data-details.interface.ts => responses/benchmark-market-data-details-response.interface.ts} (64%) diff --git a/apps/api/src/app/endpoints/benchmarks/benchmarks.controller.ts b/apps/api/src/app/endpoints/benchmarks/benchmarks.controller.ts index 69383a30d..629d90928 100644 --- a/apps/api/src/app/endpoints/benchmarks/benchmarks.controller.ts +++ b/apps/api/src/app/endpoints/benchmarks/benchmarks.controller.ts @@ -8,7 +8,7 @@ import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper' import { HEADER_KEY_IMPERSONATION } from '@ghostfolio/common/config'; import type { AssetProfileIdentifier, - BenchmarkMarketDataDetails, + BenchmarkMarketDataDetailsResponse, BenchmarkResponse } from '@ghostfolio/common/interfaces'; import { permissions } from '@ghostfolio/common/permissions'; @@ -125,7 +125,7 @@ export class BenchmarksController { @Query('symbol') filterBySymbol?: string, @Query('tags') filterByTags?: string, @Query('withExcludedAccounts') withExcludedAccountsParam = 'false' - ): Promise { + ): Promise { const { endDate, startDate } = getIntervalFromDateRange( dateRange, new Date(startDateString) diff --git a/apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts b/apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts index aa53564b7..03ff32c21 100644 --- a/apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts +++ b/apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts @@ -6,7 +6,7 @@ import { MarketDataService } from '@ghostfolio/api/services/market-data/market-d import { DATE_FORMAT, parseDate, resetHours } from '@ghostfolio/common/helper'; import { AssetProfileIdentifier, - BenchmarkMarketDataDetails, + BenchmarkMarketDataDetailsResponse, Filter } from '@ghostfolio/common/interfaces'; import { DateRange, UserWithSettings } from '@ghostfolio/common/types'; @@ -43,7 +43,7 @@ export class BenchmarksService { startDate: Date; user: UserWithSettings; withExcludedAccounts?: boolean; - } & AssetProfileIdentifier): Promise { + } & AssetProfileIdentifier): Promise { const marketData: { date: string; value: number }[] = []; const userCurrency = user.settings.settings.baseCurrency; const userId = user.id; diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index b5f6d9e01..3cb5a8c75 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -30,7 +30,7 @@ import { AiPromptResponse, ApiKeyResponse, AssetProfileIdentifier, - BenchmarkMarketDataDetails, + BenchmarkMarketDataDetailsResponse, BenchmarkResponse, DataProviderHealthResponse, Export, @@ -368,7 +368,7 @@ export class DataService { range: DateRange; startDate: Date; withExcludedAccounts?: boolean; - } & AssetProfileIdentifier): Observable { + } & AssetProfileIdentifier) { let params = this.buildFiltersAsQueryParams({ filters }); params = params.append('range', range); @@ -377,7 +377,7 @@ export class DataService { params = params.append('withExcludedAccounts', withExcludedAccounts); } - return this.http.get( + return this.http.get( `/api/v1/benchmarks/${dataSource}/${symbol}/${format(startDate, DATE_FORMAT, { in: utc })}`, { params } ); diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts index 8a7ef8544..578d9cec8 100644 --- a/libs/common/src/lib/interfaces/index.ts +++ b/libs/common/src/lib/interfaces/index.ts @@ -10,7 +10,6 @@ import type { import type { AdminUsers } from './admin-users.interface'; import type { AssetClassSelectorOption } from './asset-class-selector-option.interface'; import type { AssetProfileIdentifier } from './asset-profile-identifier.interface'; -import type { BenchmarkMarketDataDetails } from './benchmark-market-data-details.interface'; import type { BenchmarkProperty } from './benchmark-property.interface'; import type { Benchmark } from './benchmark.interface'; import type { Coupon } from './coupon.interface'; @@ -41,6 +40,7 @@ import type { AccountBalancesResponse } from './responses/account-balances-respo import type { AccountsResponse } from './responses/accounts-response.interface'; import type { AiPromptResponse } from './responses/ai-prompt-response.interface'; import type { ApiKeyResponse } from './responses/api-key-response.interface'; +import type { BenchmarkMarketDataDetailsResponse } from './responses/benchmark-market-data-details-response.interface'; import type { BenchmarkResponse } from './responses/benchmark-response.interface'; import type { DataEnhancerHealthResponse } from './responses/data-enhancer-health-response.interface'; import type { DataProviderGhostfolioAssetProfileResponse } from './responses/data-provider-ghostfolio-asset-profile-response.interface'; @@ -91,7 +91,7 @@ export { AssetClassSelectorOption, AssetProfileIdentifier, Benchmark, - BenchmarkMarketDataDetails, + BenchmarkMarketDataDetailsResponse, BenchmarkProperty, BenchmarkResponse, Coupon, diff --git a/libs/common/src/lib/interfaces/benchmark-market-data-details.interface.ts b/libs/common/src/lib/interfaces/responses/benchmark-market-data-details-response.interface.ts similarity index 64% rename from libs/common/src/lib/interfaces/benchmark-market-data-details.interface.ts rename to libs/common/src/lib/interfaces/responses/benchmark-market-data-details-response.interface.ts index ed693b9af..cdd63ff79 100644 --- a/libs/common/src/lib/interfaces/benchmark-market-data-details.interface.ts +++ b/libs/common/src/lib/interfaces/responses/benchmark-market-data-details-response.interface.ts @@ -1,5 +1,5 @@ import { LineChartItem } from '@ghostfolio/common/interfaces'; -export interface BenchmarkMarketDataDetails { +export interface BenchmarkMarketDataDetailsResponse { marketData: LineChartItem[]; } From 18d46cfa1eeeb8d9f6bacfade8fe21f1d6186a07 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 18 Oct 2025 11:28:47 +0200 Subject: [PATCH 3/6] Release 2.209.0 (#5780) --- CHANGELOG.md | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9478c563..48d622193 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## 2.209.0 - 2025-10-18 ### Added diff --git a/package-lock.json b/package-lock.json index e65c23ac7..bcd6300e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ghostfolio", - "version": "2.208.0", + "version": "2.209.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ghostfolio", - "version": "2.208.0", + "version": "2.209.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/package.json b/package.json index cb0ba6731..a7fc8728b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostfolio", - "version": "2.208.0", + "version": "2.209.0", "homepage": "https://ghostfol.io", "license": "AGPL-3.0", "repository": "https://github.com/ghostfolio/ghostfolio", From a65523fd043a1efb9f5990dc95c8946a99c45d98 Mon Sep 17 00:00:00 2001 From: s-vamshi <62489114+s-vamshi@users.noreply.github.com> Date: Sat, 18 Oct 2025 23:35:28 +0530 Subject: [PATCH 4/6] Feature/allow data gathering by date range (#5762) * Allow data gathering by date range * Update changelog --- CHANGELOG.md | 6 +++ apps/api/src/app/admin/admin.controller.ts | 2 +- .../asset-profile-dialog.component.ts | 37 ++++++++++++++++++- .../asset-profile-dialog.html | 18 +++++++++ apps/client/src/app/services/admin.service.ts | 18 ++++++++- 5 files changed, 76 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48d622193..66b47fc1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Added + +- Added support for data gathering by date range in the asset profile details dialog of the admin control panel + ## 2.209.0 - 2025-10-18 ### Added diff --git a/apps/api/src/app/admin/admin.controller.ts b/apps/api/src/app/admin/admin.controller.ts index 66f8483b4..d7c4c5d3d 100644 --- a/apps/api/src/app/admin/admin.controller.ts +++ b/apps/api/src/app/admin/admin.controller.ts @@ -169,7 +169,7 @@ export class AdminController { let date: Date; if (dateRange) { - const { startDate } = getIntervalFromDateRange(dateRange, new Date()); + const { startDate } = getIntervalFromDateRange(dateRange); date = startDate; } diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts index 3fd9e506f..a56f6dec5 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts @@ -18,6 +18,7 @@ import { ScraperConfiguration, User } from '@ghostfolio/common/interfaces'; +import { DateRange } from '@ghostfolio/common/types'; import { GfCurrencySelectorComponent } from '@ghostfolio/ui/currency-selector'; import { GfEntityLogoComponent } from '@ghostfolio/ui/entity-logo'; import { GfHistoricalMarketDataEditorComponent } from '@ghostfolio/ui/historical-market-data-editor'; @@ -190,6 +191,32 @@ export class GfAssetProfileDialogComponent implements OnDestroy, OnInit { }; public currencies: string[] = []; + public dateRangeOptions = [ + { + label: $localize`Current week` + ' (' + $localize`WTD` + ')', + value: 'wtd' + }, + { + label: $localize`Current month` + ' (' + $localize`MTD` + ')', + value: 'mtd' + }, + { + label: $localize`Current year` + ' (' + $localize`YTD` + ')', + value: 'ytd' + }, + { + label: '1 ' + $localize`year` + ' (' + $localize`1Y` + ')', + value: '1y' + }, + { + label: '5 ' + $localize`years` + ' (' + $localize`5Y` + ')', + value: '5y' + }, + { + label: $localize`Max`, + value: 'max' + } + ]; public historicalDataItems: LineChartItem[]; public isBenchmark = false; public isDataGatheringEnabled: boolean; @@ -405,9 +432,15 @@ export class GfAssetProfileDialogComponent implements OnDestroy, OnInit { .subscribe(); } - public onGatherSymbol({ dataSource, symbol }: AssetProfileIdentifier) { + public onGatherSymbol({ + dataSource, + range, + symbol + }: { + range?: DateRange; + } & AssetProfileIdentifier) { this.adminService - .gatherSymbol({ dataSource, symbol }) + .gatherSymbol({ dataSource, range, symbol }) .pipe(takeUntil(this.unsubscribeSubject)) .subscribe(); } diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html index 301287cf5..b2c063684 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -26,12 +26,30 @@ [disabled]=" assetProfileForm.dirty || !assetProfileForm.controls.isActive.value " + [matMenuTriggerFor]="gatherHistoricalMarketDataMenu" (click)=" onGatherSymbol({ dataSource: data.dataSource, symbol: data.symbol }) " > Gather Historical Market Data + + @for (dateRange of dateRangeOptions; track dateRange.value) { + + } +