From 57659d2c04b7f8d9bf529ea2390c0e95a9c42201 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Fri, 13 Dec 2024 17:13:51 +0100 Subject: [PATCH 1/8] Feature/add missing public keywords (#4102) * Add public keyword --- .../create-or-update-access-dialog.component.ts | 2 +- apps/client/src/app/core/auth.guard.ts | 2 +- apps/client/src/app/pages/webauthn/webauthn-page.component.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts b/apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts index 5bb6ca3e5..727263718 100644 --- a/apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts +++ b/apps/client/src/app/components/user-account-access/create-or-update-access-dialog/create-or-update-access-dialog.component.ts @@ -38,7 +38,7 @@ export class CreateOrUpdateAccessDialog implements OnDestroy { private notificationService: NotificationService ) {} - ngOnInit() { + public ngOnInit() { this.accessForm = this.formBuilder.group({ alias: [this.data.access.alias], permissions: [this.data.access.permissions[0], Validators.required], diff --git a/apps/client/src/app/core/auth.guard.ts b/apps/client/src/app/core/auth.guard.ts index 548d36473..7a75728ca 100644 --- a/apps/client/src/app/core/auth.guard.ts +++ b/apps/client/src/app/core/auth.guard.ts @@ -29,7 +29,7 @@ export class AuthGuard { `/${paths.resources}` ]; - constructor( + public constructor( private dataService: DataService, private router: Router, private settingsStorageService: SettingsStorageService, diff --git a/apps/client/src/app/pages/webauthn/webauthn-page.component.ts b/apps/client/src/app/pages/webauthn/webauthn-page.component.ts index 41860014a..77a053258 100644 --- a/apps/client/src/app/pages/webauthn/webauthn-page.component.ts +++ b/apps/client/src/app/pages/webauthn/webauthn-page.component.ts @@ -22,7 +22,7 @@ export class GfWebauthnPageComponent implements OnDestroy, OnInit { private unsubscribeSubject = new Subject(); - constructor( + public constructor( private changeDetectorRef: ChangeDetectorRef, private router: Router, private tokenStorageService: TokenStorageService, From de6884184388c352450f102957e3340f679b3f3e Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 14 Dec 2024 10:43:35 +0100 Subject: [PATCH 2/8] Feature/add user id to symbol profile database schema (#4122) * Add userId to SymbolProfile database schema * Update changelog --- CHANGELOG.md | 6 ++++++ apps/api/src/app/import/import.service.ts | 10 ++++++---- apps/api/src/app/order/order.service.ts | 3 ++- .../migration.sql | 15 +++++++++++++++ prisma/schema.prisma | 17 ++++++++++------- 5 files changed, 39 insertions(+), 12 deletions(-) create mode 100644 prisma/migrations/20241214091912_added_user_to_symbol_profile/migration.sql diff --git a/CHANGELOG.md b/CHANGELOG.md index 42f400bcc..9aebc76a4 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 `userId` to the `SymbolProfile` database schema + ## 2.128.0 - 2024-12-12 ### Changed diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts index e51696b56..3b7290b43 100644 --- a/apps/api/src/app/import/import.service.ts +++ b/apps/api/src/app/import/import.service.ts @@ -224,7 +224,7 @@ export class ImportService { for (const activity of activitiesDto) { if (!activity.dataSource) { - if (activity.type === 'ITEM' || activity.type === 'LIABILITY') { + if (['FEE', 'INTEREST', 'ITEM', 'LIABILITY'].includes(activity.type)) { activity.dataSource = DataSource.MANUAL; } else { activity.dataSource = @@ -356,6 +356,7 @@ export class ImportService { quantity, type, unitPrice, + Account: validatedAccount, accountId: validatedAccount?.id, accountUserId: undefined, createdAt: new Date(), @@ -380,10 +381,10 @@ export class ImportService { symbolMapping, updatedAt, url, + comment: assetProfile.comment, currency: assetProfile.currency, - comment: assetProfile.comment + userId: dataSource === 'MANUAL' ? user.id : undefined }, - Account: validatedAccount, symbolProfileId: undefined, updatedAt: new Date(), userId: user.id @@ -406,7 +407,8 @@ export class ImportService { create: { dataSource, symbol, - currency: assetProfile.currency + currency: assetProfile.currency, + userId: dataSource === 'MANUAL' ? user.id : undefined }, where: { dataSource_symbol: { diff --git a/apps/api/src/app/order/order.service.ts b/apps/api/src/app/order/order.service.ts index 5613adc5c..129f3d8a9 100644 --- a/apps/api/src/app/order/order.service.ts +++ b/apps/api/src/app/order/order.service.ts @@ -93,7 +93,7 @@ export class OrderService { userId: string; } ): Promise { - let Account; + let Account: Prisma.AccountCreateNestedOneWithoutOrderInput; if (data.accountId) { Account = { @@ -124,6 +124,7 @@ export class OrderService { data.SymbolProfile.connectOrCreate.create.dataSource = dataSource; data.SymbolProfile.connectOrCreate.create.name = name; data.SymbolProfile.connectOrCreate.create.symbol = id; + data.SymbolProfile.connectOrCreate.create.userId = userId; data.SymbolProfile.connectOrCreate.where.dataSource_symbol = { dataSource, symbol: id diff --git a/prisma/migrations/20241214091912_added_user_to_symbol_profile/migration.sql b/prisma/migrations/20241214091912_added_user_to_symbol_profile/migration.sql new file mode 100644 index 000000000..cbacd405a --- /dev/null +++ b/prisma/migrations/20241214091912_added_user_to_symbol_profile/migration.sql @@ -0,0 +1,15 @@ +-- AlterTable +ALTER TABLE "SymbolProfile" ADD COLUMN "userId" TEXT; + +-- AddForeignKey +ALTER TABLE "SymbolProfile" ADD CONSTRAINT "SymbolProfile_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- Set userIds in SymbolProfile for 'MANUAL' data source +UPDATE "SymbolProfile" +SET "userId" = ( + SELECT "userId" + FROM "Order" + WHERE "Order"."symbolProfileId" = "SymbolProfile"."id" + LIMIT 1 +) +WHERE "dataSource" = 'MANUAL'; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 7df28d694..af9acebaf 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -188,8 +188,10 @@ model SymbolProfile { symbol String symbolMapping Json? url String? + userId String? Order Order[] SymbolProfileOverrides SymbolProfileOverrides? + User User? @relation(fields: [userId], onDelete: Cascade, references: [id]) @@unique([dataSource, symbol]) @@index([assetClass]) @@ -239,14 +241,14 @@ model Tag { model User { accessToken String? authChallenge String? - createdAt DateTime @default(now()) - id String @id @default(uuid()) - provider Provider @default(ANONYMOUS) - role Role @default(USER) + createdAt DateTime @default(now()) + id String @id @default(uuid()) + provider Provider @default(ANONYMOUS) + role Role @default(USER) thirdPartyId String? - updatedAt DateTime @updatedAt - Access Access[] @relation("accessGet") - AccessGive Access[] @relation("accessGive") + updatedAt DateTime @updatedAt + Access Access[] @relation("accessGet") + AccessGive Access[] @relation("accessGive") Account Account[] Analytics Analytics? ApiKey ApiKey[] @@ -254,6 +256,7 @@ model User { Order Order[] Settings Settings? Subscription Subscription[] + SymbolProfile SymbolProfile[] Tag Tag[] @@index([accessToken]) From 6ad9661d7faa542bbe6db0ac6cc018a96bf119b8 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 14 Dec 2024 11:19:15 +0100 Subject: [PATCH 3/8] Feature/improve language localization for de 20241214 (#4123) * Update translations * Update changelog --- CHANGELOG.md | 4 ++ .../src/app/portfolio/portfolio.controller.ts | 5 ++ apps/client/project.json | 2 +- .../portfolio/x-ray/x-ray-page.component.html | 4 +- apps/client/src/locales/messages.ca.xlf | 52 ++++++++++++------- apps/client/src/locales/messages.de.xlf | 52 ++++++++++++------- apps/client/src/locales/messages.es.xlf | 52 ++++++++++++------- apps/client/src/locales/messages.fr.xlf | 52 ++++++++++++------- apps/client/src/locales/messages.it.xlf | 52 ++++++++++++------- apps/client/src/locales/messages.nl.xlf | 52 ++++++++++++------- apps/client/src/locales/messages.pl.xlf | 52 ++++++++++++------- apps/client/src/locales/messages.pt.xlf | 52 ++++++++++++------- apps/client/src/locales/messages.tr.xlf | 52 ++++++++++++------- apps/client/src/locales/messages.xlf | 50 +++++++++++------- apps/client/src/locales/messages.zh.xlf | 52 ++++++++++++------- 15 files changed, 384 insertions(+), 201 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9aebc76a4..349e62646 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `userId` to the `SymbolProfile` database schema +### Changed + +- Improved the language localization for German (`de`) + ## 2.128.0 - 2024-12-12 ### Changed diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts index b15d22268..0e4022969 100644 --- a/apps/api/src/app/portfolio/portfolio.controller.ts +++ b/apps/api/src/app/portfolio/portfolio.controller.ts @@ -623,6 +623,11 @@ export class PortfolioController { report.rules[rule] = []; } } + + report.statistics = { + rulesActiveCount: 0, + rulesFulfilledCount: 0 + }; } return report; diff --git a/apps/client/project.json b/apps/client/project.json index dd644f8c1..7fd28d5ca 100644 --- a/apps/client/project.json +++ b/apps/client/project.json @@ -212,7 +212,7 @@ "extract-i18n": { "executor": "ng-extract-i18n-merge:ng-extract-i18n-merge", "options": { - "browserTarget": "client:build", + "buildTarget": "client:build", "includeContext": true, "outputPath": "src/locales", "targetFiles": [ diff --git a/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html b/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html index 7a0a3512c..37d9c89c4 100644 --- a/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html +++ b/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html @@ -19,9 +19,9 @@ /> } @else { {{ statistics?.rulesFulfilledCount }} - of + out of {{ statistics?.rulesActiveCount }} - rules are currently fulfilled. + rules align with your portfolio. }

diff --git a/apps/client/src/locales/messages.ca.xlf b/apps/client/src/locales/messages.ca.xlf index d32f8c1ee..6d9fe62e6 100644 --- a/apps/client/src/locales/messages.ca.xlf +++ b/apps/client/src/locales/messages.ca.xlf @@ -2619,7 +2619,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 88 + 105 @@ -2991,7 +2991,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 12 + 29 @@ -3207,7 +3207,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 223 + 221 @@ -3219,7 +3219,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 233 + 231 @@ -3231,7 +3231,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 237 + 235 @@ -3243,7 +3243,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 259 + 257 @@ -3255,7 +3255,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 262 + 260 @@ -5255,7 +5255,7 @@ Currency Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 31 + 48 @@ -5263,7 +5263,7 @@ Account Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 50 + 67 @@ -6059,7 +6059,7 @@ Week to date libs/ui/src/lib/assistant/assistant.component.ts - 225 + 223 @@ -6067,7 +6067,7 @@ WTD libs/ui/src/lib/assistant/assistant.component.ts - 225 + 223 @@ -6075,7 +6075,7 @@ Month to date libs/ui/src/lib/assistant/assistant.component.ts - 229 + 227 @@ -6083,7 +6083,7 @@ MTD libs/ui/src/lib/assistant/assistant.component.ts - 229 + 227 @@ -6091,7 +6091,7 @@ Year to date libs/ui/src/lib/assistant/assistant.component.ts - 233 + 231 @@ -6099,7 +6099,7 @@ year libs/ui/src/lib/assistant/assistant.component.ts - 237 + 235 @@ -6107,7 +6107,7 @@ years libs/ui/src/lib/assistant/assistant.component.ts - 259 + 257 @@ -7047,7 +7047,7 @@ Inactive apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 107 + 124 @@ -7457,7 +7457,7 @@ Economic Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 69 + 86 @@ -7584,6 +7584,22 @@ 26 + + out of + out of + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 22 + + + + rules align with your portfolio. + rules align with your portfolio. + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 24 + + diff --git a/apps/client/src/locales/messages.de.xlf b/apps/client/src/locales/messages.de.xlf index 897453767..b4b8857a5 100644 --- a/apps/client/src/locales/messages.de.xlf +++ b/apps/client/src/locales/messages.de.xlf @@ -1254,7 +1254,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 12 + 29 @@ -1422,7 +1422,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 223 + 221 @@ -1434,7 +1434,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 233 + 231 @@ -1446,7 +1446,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 237 + 235 @@ -1458,7 +1458,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 259 + 257 @@ -1470,7 +1470,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 262 + 260 @@ -3766,7 +3766,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 88 + 105 @@ -5907,7 +5907,7 @@ Währungsklumpenrisiken apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 31 + 48 @@ -5915,7 +5915,7 @@ Kontoklumpenrisiken apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 50 + 67 @@ -6375,7 +6375,7 @@ Seit Wochenbeginn libs/ui/src/lib/assistant/assistant.component.ts - 225 + 223 @@ -6383,7 +6383,7 @@ WTD libs/ui/src/lib/assistant/assistant.component.ts - 225 + 223 @@ -6391,7 +6391,7 @@ Seit Monatsbeginn libs/ui/src/lib/assistant/assistant.component.ts - 229 + 227 @@ -6399,7 +6399,7 @@ MTD libs/ui/src/lib/assistant/assistant.component.ts - 229 + 227 @@ -6407,7 +6407,7 @@ Seit Jahresbeginn libs/ui/src/lib/assistant/assistant.component.ts - 233 + 231 @@ -6451,7 +6451,7 @@ Jahr libs/ui/src/lib/assistant/assistant.component.ts - 237 + 235 @@ -6459,7 +6459,7 @@ Jahre libs/ui/src/lib/assistant/assistant.component.ts - 259 + 257 @@ -7047,7 +7047,7 @@ Inaktiv apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 107 + 124 @@ -7457,7 +7457,7 @@ Wirtschaftsraumklumpenrisiken apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 69 + 86 @@ -7584,6 +7584,22 @@ 26 + + out of + von + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 22 + + + + rules align with your portfolio. + Regeln werden von Ihrem Portfolio erfüllt. + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 24 + + diff --git a/apps/client/src/locales/messages.es.xlf b/apps/client/src/locales/messages.es.xlf index b2dcc61cc..d52ea0470 100644 --- a/apps/client/src/locales/messages.es.xlf +++ b/apps/client/src/locales/messages.es.xlf @@ -1255,7 +1255,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 12 + 29 @@ -1423,7 +1423,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 223 + 221 @@ -1435,7 +1435,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 233 + 231 @@ -1447,7 +1447,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 237 + 235 @@ -1459,7 +1459,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 259 + 257 @@ -1471,7 +1471,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 262 + 260 @@ -3767,7 +3767,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 88 + 105 @@ -5908,7 +5908,7 @@ Currency Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 31 + 48 @@ -5916,7 +5916,7 @@ Account Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 50 + 67 @@ -6376,7 +6376,7 @@ Week to date libs/ui/src/lib/assistant/assistant.component.ts - 225 + 223 @@ -6384,7 +6384,7 @@ WTD libs/ui/src/lib/assistant/assistant.component.ts - 225 + 223 @@ -6392,7 +6392,7 @@ Month to date libs/ui/src/lib/assistant/assistant.component.ts - 229 + 227 @@ -6400,7 +6400,7 @@ MTD libs/ui/src/lib/assistant/assistant.component.ts - 229 + 227 @@ -6408,7 +6408,7 @@ Year to date libs/ui/src/lib/assistant/assistant.component.ts - 233 + 231 @@ -6452,7 +6452,7 @@ año libs/ui/src/lib/assistant/assistant.component.ts - 237 + 235 @@ -6460,7 +6460,7 @@ años libs/ui/src/lib/assistant/assistant.component.ts - 259 + 257 @@ -7048,7 +7048,7 @@ Inactive apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 107 + 124 @@ -7458,7 +7458,7 @@ Economic Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 69 + 86 @@ -7585,6 +7585,22 @@ 26 + + out of + out of + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 22 + + + + rules align with your portfolio. + rules align with your portfolio. + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 24 + + diff --git a/apps/client/src/locales/messages.fr.xlf b/apps/client/src/locales/messages.fr.xlf index fd29241b2..84ec59dda 100644 --- a/apps/client/src/locales/messages.fr.xlf +++ b/apps/client/src/locales/messages.fr.xlf @@ -1602,7 +1602,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 12 + 29 @@ -1734,7 +1734,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 223 + 221 @@ -1746,7 +1746,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 233 + 231 @@ -1758,7 +1758,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 237 + 235 @@ -1770,7 +1770,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 259 + 257 @@ -1782,7 +1782,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 262 + 260 @@ -3766,7 +3766,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 88 + 105 @@ -5907,7 +5907,7 @@ Risques de change apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 31 + 48 @@ -5915,7 +5915,7 @@ Risques liés aux regroupements de comptes apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 50 + 67 @@ -6375,7 +6375,7 @@ Week to date libs/ui/src/lib/assistant/assistant.component.ts - 225 + 223 @@ -6383,7 +6383,7 @@ WTD libs/ui/src/lib/assistant/assistant.component.ts - 225 + 223 @@ -6391,7 +6391,7 @@ Month to date libs/ui/src/lib/assistant/assistant.component.ts - 229 + 227 @@ -6399,7 +6399,7 @@ MTD libs/ui/src/lib/assistant/assistant.component.ts - 229 + 227 @@ -6407,7 +6407,7 @@ Year to date libs/ui/src/lib/assistant/assistant.component.ts - 233 + 231 @@ -6451,7 +6451,7 @@ année libs/ui/src/lib/assistant/assistant.component.ts - 237 + 235 @@ -6459,7 +6459,7 @@ années libs/ui/src/lib/assistant/assistant.component.ts - 259 + 257 @@ -7047,7 +7047,7 @@ Inactif apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 107 + 124 @@ -7457,7 +7457,7 @@ Economic Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 69 + 86 @@ -7584,6 +7584,22 @@ 26 + + out of + out of + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 22 + + + + rules align with your portfolio. + rules align with your portfolio. + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 24 + + diff --git a/apps/client/src/locales/messages.it.xlf b/apps/client/src/locales/messages.it.xlf index 00eb42277..9a9e8b06b 100644 --- a/apps/client/src/locales/messages.it.xlf +++ b/apps/client/src/locales/messages.it.xlf @@ -1255,7 +1255,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 12 + 29 @@ -1423,7 +1423,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 223 + 221 @@ -1435,7 +1435,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 233 + 231 @@ -1447,7 +1447,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 237 + 235 @@ -1459,7 +1459,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 259 + 257 @@ -1471,7 +1471,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 262 + 260 @@ -3767,7 +3767,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 88 + 105 @@ -5908,7 +5908,7 @@ Rischio di Concentrazione Valutario apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 31 + 48 @@ -5916,7 +5916,7 @@ Rischi di Concentrazione dei Conti apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 50 + 67 @@ -6376,7 +6376,7 @@ Da inizio settimana libs/ui/src/lib/assistant/assistant.component.ts - 225 + 223 @@ -6384,7 +6384,7 @@ Settimana corrente libs/ui/src/lib/assistant/assistant.component.ts - 225 + 223 @@ -6392,7 +6392,7 @@ Da inizio mese libs/ui/src/lib/assistant/assistant.component.ts - 229 + 227 @@ -6400,7 +6400,7 @@ Mese corrente libs/ui/src/lib/assistant/assistant.component.ts - 229 + 227 @@ -6408,7 +6408,7 @@ Da inizio anno libs/ui/src/lib/assistant/assistant.component.ts - 233 + 231 @@ -6452,7 +6452,7 @@ anno libs/ui/src/lib/assistant/assistant.component.ts - 237 + 235 @@ -6460,7 +6460,7 @@ anni libs/ui/src/lib/assistant/assistant.component.ts - 259 + 257 @@ -7048,7 +7048,7 @@ Inattivo apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 107 + 124 @@ -7458,7 +7458,7 @@ Economic Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 69 + 86 @@ -7585,6 +7585,22 @@ 26 + + out of + out of + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 22 + + + + rules align with your portfolio. + rules align with your portfolio. + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 24 + + diff --git a/apps/client/src/locales/messages.nl.xlf b/apps/client/src/locales/messages.nl.xlf index a2c287c2e..3af261ded 100644 --- a/apps/client/src/locales/messages.nl.xlf +++ b/apps/client/src/locales/messages.nl.xlf @@ -1254,7 +1254,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 12 + 29 @@ -1422,7 +1422,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 223 + 221 @@ -1434,7 +1434,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 233 + 231 @@ -1446,7 +1446,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 237 + 235 @@ -1458,7 +1458,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 259 + 257 @@ -1470,7 +1470,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 262 + 260 @@ -3766,7 +3766,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 88 + 105 @@ -5907,7 +5907,7 @@ Currency Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 31 + 48 @@ -5915,7 +5915,7 @@ Account Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 50 + 67 @@ -6375,7 +6375,7 @@ Week to date libs/ui/src/lib/assistant/assistant.component.ts - 225 + 223 @@ -6383,7 +6383,7 @@ WTD libs/ui/src/lib/assistant/assistant.component.ts - 225 + 223 @@ -6391,7 +6391,7 @@ Month to date libs/ui/src/lib/assistant/assistant.component.ts - 229 + 227 @@ -6399,7 +6399,7 @@ MTD libs/ui/src/lib/assistant/assistant.component.ts - 229 + 227 @@ -6407,7 +6407,7 @@ Year to date libs/ui/src/lib/assistant/assistant.component.ts - 233 + 231 @@ -6451,7 +6451,7 @@ year libs/ui/src/lib/assistant/assistant.component.ts - 237 + 235 @@ -6459,7 +6459,7 @@ years libs/ui/src/lib/assistant/assistant.component.ts - 259 + 257 @@ -7047,7 +7047,7 @@ Inactive apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 107 + 124 @@ -7457,7 +7457,7 @@ Economic Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 69 + 86 @@ -7584,6 +7584,22 @@ 26 + + out of + out of + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 22 + + + + rules align with your portfolio. + rules align with your portfolio. + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 24 + + diff --git a/apps/client/src/locales/messages.pl.xlf b/apps/client/src/locales/messages.pl.xlf index c5439f695..12a51af3d 100644 --- a/apps/client/src/locales/messages.pl.xlf +++ b/apps/client/src/locales/messages.pl.xlf @@ -2595,7 +2595,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 88 + 105 @@ -2643,7 +2643,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 12 + 29 @@ -2959,7 +2959,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 223 + 221 @@ -2971,7 +2971,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 233 + 231 @@ -2983,7 +2983,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 237 + 235 @@ -2995,7 +2995,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 259 + 257 @@ -3007,7 +3007,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 262 + 260 @@ -4783,7 +4783,7 @@ Currency Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 31 + 48 @@ -4791,7 +4791,7 @@ Account Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 50 + 67 @@ -6375,7 +6375,7 @@ Week to date libs/ui/src/lib/assistant/assistant.component.ts - 225 + 223 @@ -6383,7 +6383,7 @@ WTD libs/ui/src/lib/assistant/assistant.component.ts - 225 + 223 @@ -6391,7 +6391,7 @@ Month to date libs/ui/src/lib/assistant/assistant.component.ts - 229 + 227 @@ -6399,7 +6399,7 @@ MTD libs/ui/src/lib/assistant/assistant.component.ts - 229 + 227 @@ -6407,7 +6407,7 @@ Year to date libs/ui/src/lib/assistant/assistant.component.ts - 233 + 231 @@ -6451,7 +6451,7 @@ year libs/ui/src/lib/assistant/assistant.component.ts - 237 + 235 @@ -6459,7 +6459,7 @@ years libs/ui/src/lib/assistant/assistant.component.ts - 259 + 257 @@ -7047,7 +7047,7 @@ Nieaktywny apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 107 + 124 @@ -7457,7 +7457,7 @@ Economic Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 69 + 86 @@ -7584,6 +7584,22 @@ 26 + + out of + out of + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 22 + + + + rules align with your portfolio. + rules align with your portfolio. + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 24 + + diff --git a/apps/client/src/locales/messages.pt.xlf b/apps/client/src/locales/messages.pt.xlf index e472770f8..7d2d5e3c3 100644 --- a/apps/client/src/locales/messages.pt.xlf +++ b/apps/client/src/locales/messages.pt.xlf @@ -1478,7 +1478,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 12 + 29 @@ -1718,7 +1718,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 223 + 221 @@ -1730,7 +1730,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 233 + 231 @@ -1742,7 +1742,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 237 + 235 @@ -1754,7 +1754,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 259 + 257 @@ -1766,7 +1766,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 262 + 260 @@ -3766,7 +3766,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 88 + 105 @@ -5907,7 +5907,7 @@ Currency Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 31 + 48 @@ -5915,7 +5915,7 @@ Account Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 50 + 67 @@ -6375,7 +6375,7 @@ Week to date libs/ui/src/lib/assistant/assistant.component.ts - 225 + 223 @@ -6383,7 +6383,7 @@ WTD libs/ui/src/lib/assistant/assistant.component.ts - 225 + 223 @@ -6391,7 +6391,7 @@ Month to date libs/ui/src/lib/assistant/assistant.component.ts - 229 + 227 @@ -6399,7 +6399,7 @@ MTD libs/ui/src/lib/assistant/assistant.component.ts - 229 + 227 @@ -6407,7 +6407,7 @@ Year to date libs/ui/src/lib/assistant/assistant.component.ts - 233 + 231 @@ -6451,7 +6451,7 @@ year libs/ui/src/lib/assistant/assistant.component.ts - 237 + 235 @@ -6459,7 +6459,7 @@ years libs/ui/src/lib/assistant/assistant.component.ts - 259 + 257 @@ -7047,7 +7047,7 @@ Inactive apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 107 + 124 @@ -7457,7 +7457,7 @@ Economic Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 69 + 86 @@ -7584,6 +7584,22 @@ 26 + + out of + out of + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 22 + + + + rules align with your portfolio. + rules align with your portfolio. + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 24 + + diff --git a/apps/client/src/locales/messages.tr.xlf b/apps/client/src/locales/messages.tr.xlf index 355488ea9..7752f1339 100644 --- a/apps/client/src/locales/messages.tr.xlf +++ b/apps/client/src/locales/messages.tr.xlf @@ -2479,7 +2479,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 12 + 29 @@ -2635,7 +2635,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 88 + 105 @@ -2803,7 +2803,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 223 + 221 @@ -2815,7 +2815,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 233 + 231 @@ -2827,7 +2827,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 237 + 235 @@ -2839,7 +2839,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 259 + 257 @@ -2851,7 +2851,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 262 + 260 @@ -5907,7 +5907,7 @@ Kur Kümelenme Riskleri (Currency Cluster Risks) apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 31 + 48 @@ -5915,7 +5915,7 @@ Hesap Kümelenme Riski (Account Cluster Risks) apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 50 + 67 @@ -6375,7 +6375,7 @@ Week to date libs/ui/src/lib/assistant/assistant.component.ts - 225 + 223 @@ -6383,7 +6383,7 @@ WTD libs/ui/src/lib/assistant/assistant.component.ts - 225 + 223 @@ -6391,7 +6391,7 @@ Month to date libs/ui/src/lib/assistant/assistant.component.ts - 229 + 227 @@ -6399,7 +6399,7 @@ MTD libs/ui/src/lib/assistant/assistant.component.ts - 229 + 227 @@ -6407,7 +6407,7 @@ Year to date libs/ui/src/lib/assistant/assistant.component.ts - 233 + 231 @@ -6451,7 +6451,7 @@ year libs/ui/src/lib/assistant/assistant.component.ts - 237 + 235 @@ -6459,7 +6459,7 @@ years libs/ui/src/lib/assistant/assistant.component.ts - 259 + 257 @@ -7047,7 +7047,7 @@ Inactive apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 107 + 124 @@ -7457,7 +7457,7 @@ Economic Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 69 + 86 @@ -7584,6 +7584,22 @@ 26 + + out of + out of + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 22 + + + + rules align with your portfolio. + rules align with your portfolio. + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 24 + + diff --git a/apps/client/src/locales/messages.xlf b/apps/client/src/locales/messages.xlf index 0e1c16eb9..5f15aafde 100644 --- a/apps/client/src/locales/messages.xlf +++ b/apps/client/src/locales/messages.xlf @@ -2436,7 +2436,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 88 + 105 @@ -2479,7 +2479,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 12 + 29 @@ -2767,7 +2767,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 223 + 221 @@ -2778,7 +2778,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 233 + 231 @@ -2789,7 +2789,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 237 + 235 @@ -2800,7 +2800,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 259 + 257 @@ -2811,7 +2811,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 262 + 260 @@ -4395,14 +4395,14 @@ Currency Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 31 + 48 Account Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 50 + 67 @@ -5800,35 +5800,35 @@ Year to date libs/ui/src/lib/assistant/assistant.component.ts - 233 + 231 Week to date libs/ui/src/lib/assistant/assistant.component.ts - 225 + 223 Month to date libs/ui/src/lib/assistant/assistant.component.ts - 229 + 227 MTD libs/ui/src/lib/assistant/assistant.component.ts - 229 + 227 WTD libs/ui/src/lib/assistant/assistant.component.ts - 225 + 223 @@ -5867,14 +5867,14 @@ year libs/ui/src/lib/assistant/assistant.component.ts - 237 + 235 years libs/ui/src/lib/assistant/assistant.component.ts - 259 + 257 @@ -6390,7 +6390,7 @@ Inactive apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 107 + 124 @@ -6744,7 +6744,7 @@ Economic Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 69 + 86 @@ -6863,6 +6863,20 @@ 153 + + rules align with your portfolio. + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 24 + + + + out of + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 22 + + diff --git a/apps/client/src/locales/messages.zh.xlf b/apps/client/src/locales/messages.zh.xlf index 31c897c54..f33c6342a 100644 --- a/apps/client/src/locales/messages.zh.xlf +++ b/apps/client/src/locales/messages.zh.xlf @@ -2612,7 +2612,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 88 + 105 @@ -2660,7 +2660,7 @@ apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 12 + 29 @@ -2976,7 +2976,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 223 + 221 @@ -2988,7 +2988,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 233 + 231 @@ -3000,7 +3000,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 237 + 235 @@ -3012,7 +3012,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 259 + 257 @@ -3024,7 +3024,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 262 + 260 @@ -4800,7 +4800,7 @@ 货币集群风险 apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 31 + 48 @@ -4808,7 +4808,7 @@ 账户集群风险 apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 50 + 67 @@ -6376,7 +6376,7 @@ 今年迄今为止 libs/ui/src/lib/assistant/assistant.component.ts - 233 + 231 @@ -6384,7 +6384,7 @@ 本周至今 libs/ui/src/lib/assistant/assistant.component.ts - 225 + 223 @@ -6392,7 +6392,7 @@ 本月至今 libs/ui/src/lib/assistant/assistant.component.ts - 229 + 227 @@ -6400,7 +6400,7 @@ 最大输运量 libs/ui/src/lib/assistant/assistant.component.ts - 229 + 227 @@ -6408,7 +6408,7 @@ 世界贸易组织 libs/ui/src/lib/assistant/assistant.component.ts - 225 + 223 @@ -6452,7 +6452,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 237 + 235 @@ -6460,7 +6460,7 @@ libs/ui/src/lib/assistant/assistant.component.ts - 259 + 257 @@ -7048,7 +7048,7 @@ Inactive apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 107 + 124 @@ -7458,7 +7458,7 @@ Economic Market Cluster Risks apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html - 69 + 86 @@ -7585,6 +7585,22 @@ 26 + + out of + out of + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 22 + + + + rules align with your portfolio. + rules align with your portfolio. + + apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html + 24 + + From 943ff2f0dcd2c84360537992ad59ba421846d6e7 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 14 Dec 2024 11:37:39 +0100 Subject: [PATCH 4/8] Feature/improve usability of X-ray page by hiding empty rule categories (#4108) * Hide empty rule categories * Update changelog --- CHANGELOG.md | 1 + .../src/app/portfolio/portfolio.controller.ts | 4 +-- .../portfolio/x-ray/x-ray-page.component.html | 35 ++++++++++++++++--- .../portfolio/x-ray/x-ray-page.component.ts | 2 +- 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 349e62646..449b610f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Improved the usability of the _X-ray_ page by hiding empty rule categories - Improved the language localization for German (`de`) ## 2.128.0 - 2024-12-12 diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts index 0e4022969..797d0449a 100644 --- a/apps/api/src/app/portfolio/portfolio.controller.ts +++ b/apps/api/src/app/portfolio/portfolio.controller.ts @@ -619,9 +619,7 @@ export class PortfolioController { this.request.user.subscription.type === 'Basic' ) { for (const rule in report.rules) { - if (report.rules[rule]) { - report.rules[rule] = []; - } + report.rules[rule] = null; } report.statistics = { diff --git a/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html b/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html index 37d9c89c4..4af225c49 100644 --- a/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html +++ b/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html @@ -24,7 +24,12 @@ rules align with your portfolio. }

-
+

Emergency Fund @if (user?.subscription?.type === 'Basic') { @@ -43,7 +48,12 @@ (rulesUpdated)="onRulesUpdated($event)" />

-
+

Currency Cluster Risks @if (user?.subscription?.type === 'Basic') { @@ -62,7 +72,12 @@ (rulesUpdated)="onRulesUpdated($event)" />

-
+

Account Cluster Risks @if (user?.subscription?.type === 'Basic') { @@ -81,7 +96,12 @@ (rulesUpdated)="onRulesUpdated($event)" />

-
+

Economic Market Cluster Risks @if (user?.subscription?.type === 'Basic') { @@ -100,7 +120,12 @@ (rulesUpdated)="onRulesUpdated($event)" />

-
+

Fees @if (user?.subscription?.type === 'Basic') { diff --git a/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.ts b/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.ts index 86bc37737..9bfe30fb6 100644 --- a/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.ts +++ b/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.ts @@ -134,7 +134,7 @@ export class XRayPageComponent { let inactiveRules: PortfolioReportRule[] = []; for (const category in rules) { - const rulesArray = rules[category]; + const rulesArray = rules[category] || []; inactiveRules = inactiveRules.concat( rulesArray.filter(({ isActive }) => { From a776ea8864aaea7cacb4c243e01ad72dba485738 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 14 Dec 2024 11:40:17 +0100 Subject: [PATCH 5/8] Release 2.129.0 (#4124) --- 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 449b610f5..6dbe1cace 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.129.0 - 2024-12-14 ### Added diff --git a/package-lock.json b/package-lock.json index 3069b24af..6352c17dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ghostfolio", - "version": "2.128.0", + "version": "2.129.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ghostfolio", - "version": "2.128.0", + "version": "2.129.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/package.json b/package.json index 4feecab8f..d0ab4f289 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostfolio", - "version": "2.128.0", + "version": "2.129.0", "homepage": "https://ghostfol.io", "license": "AGPL-3.0", "repository": "https://github.com/ghostfolio/ghostfolio", From c3bd433ac9ae6676dcebae37993aae632580e82c Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sun, 15 Dec 2024 20:18:42 +0100 Subject: [PATCH 6/8] Feature/move market data management from admin to dedicated endpoint (#4125) * Move market data management from admin to dedicated endpoint * Update changelog --- CHANGELOG.md | 6 + apps/api/src/app/admin/admin.controller.ts | 6 + apps/api/src/app/app.module.ts | 2 + .../market-data/market-data.controller.ts | 136 ++++++++++++++++++ .../market-data/market-data.module.ts | 13 ++ .../update-bulk-market-data.dto.ts | 24 ++++ .../asset-profile-dialog.component.ts | 4 +- apps/client/src/app/services/admin.service.ts | 39 +---- apps/client/src/app/services/data.service.ts | 39 ++++- .../enhanced-symbol-profile.interface.ts | 1 + libs/common/src/lib/interfaces/index.ts | 2 + .../market-data-details-response.interface.ts | 8 ++ libs/common/src/lib/permissions.ts | 15 ++ ...cal-market-data-editor-dialog.component.ts | 4 +- ...historical-market-data-editor.component.ts | 6 +- 15 files changed, 260 insertions(+), 45 deletions(-) create mode 100644 apps/api/src/app/endpoints/market-data/market-data.controller.ts create mode 100644 apps/api/src/app/endpoints/market-data/market-data.module.ts create mode 100644 apps/api/src/app/endpoints/market-data/update-bulk-market-data.dto.ts create mode 100644 libs/common/src/lib/interfaces/responses/market-data-details-response.interface.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 6dbe1cace..44fd0c3a8 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 + +### Changed + +- Extracted the market data management from the admin control panel endpoint to a dedicated endpoint + ## 2.129.0 - 2024-12-14 ### Added diff --git a/apps/api/src/app/admin/admin.controller.ts b/apps/api/src/app/admin/admin.controller.ts index 4cc4d467e..a761bbbae 100644 --- a/apps/api/src/app/admin/admin.controller.ts +++ b/apps/api/src/app/admin/admin.controller.ts @@ -214,6 +214,9 @@ export class AdminController { }); } + /** + * @deprecated + */ @Get('market-data/:dataSource/:symbol') @HasPermission(permissions.accessAdminControl) @UseGuards(AuthGuard('jwt'), HasPermissionGuard) @@ -250,6 +253,9 @@ export class AdminController { } } + /** + * @deprecated + */ @HasPermission(permissions.accessAdminControl) @Post('market-data/:dataSource/:symbol') @UseGuards(AuthGuard('jwt'), HasPermissionGuard) diff --git a/apps/api/src/app/app.module.ts b/apps/api/src/app/app.module.ts index b1a240235..7ac2c5915 100644 --- a/apps/api/src/app/app.module.ts +++ b/apps/api/src/app/app.module.ts @@ -33,6 +33,7 @@ import { BenchmarkModule } from './benchmark/benchmark.module'; import { CacheModule } from './cache/cache.module'; import { ApiKeysModule } from './endpoints/api-keys/api-keys.module'; import { GhostfolioModule } from './endpoints/data-providers/ghostfolio/ghostfolio.module'; +import { MarketDataModule } from './endpoints/market-data/market-data.module'; import { PublicModule } from './endpoints/public/public.module'; import { ExchangeRateModule } from './exchange-rate/exchange-rate.module'; import { ExportModule } from './export/export.module'; @@ -84,6 +85,7 @@ import { UserModule } from './user/user.module'; ImportModule, InfoModule, LogoModule, + MarketDataModule, OrderModule, PlatformModule, PortfolioModule, diff --git a/apps/api/src/app/endpoints/market-data/market-data.controller.ts b/apps/api/src/app/endpoints/market-data/market-data.controller.ts new file mode 100644 index 000000000..b4aef807a --- /dev/null +++ b/apps/api/src/app/endpoints/market-data/market-data.controller.ts @@ -0,0 +1,136 @@ +import { AdminService } from '@ghostfolio/api/app/admin/admin.service'; +import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; +import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; +import { MarketDataDetailsResponse } from '@ghostfolio/common/interfaces'; +import { hasPermission, permissions } from '@ghostfolio/common/permissions'; +import { RequestWithUser } from '@ghostfolio/common/types'; + +import { + Body, + Controller, + Get, + HttpException, + Inject, + Param, + Post, + UseGuards +} from '@nestjs/common'; +import { REQUEST } from '@nestjs/core'; +import { AuthGuard } from '@nestjs/passport'; +import { DataSource, Prisma } from '@prisma/client'; +import { parseISO } from 'date-fns'; +import { getReasonPhrase, StatusCodes } from 'http-status-codes'; + +import { UpdateBulkMarketDataDto } from './update-bulk-market-data.dto'; + +@Controller('market-data') +export class MarketDataController { + public constructor( + private readonly adminService: AdminService, + private readonly marketDataService: MarketDataService, + @Inject(REQUEST) private readonly request: RequestWithUser, + private readonly symbolProfileService: SymbolProfileService + ) {} + + @Get(':dataSource/:symbol') + @UseGuards(AuthGuard('jwt')) + public async getMarketDataBySymbol( + @Param('dataSource') dataSource: DataSource, + @Param('symbol') symbol: string + ): Promise { + const [assetProfile] = await this.symbolProfileService.getSymbolProfiles([ + { dataSource, symbol } + ]); + + if (!assetProfile) { + throw new HttpException( + getReasonPhrase(StatusCodes.NOT_FOUND), + StatusCodes.NOT_FOUND + ); + } + + const canReadAllAssetProfiles = hasPermission( + this.request.user.permissions, + permissions.readMarketData + ); + + const canReadOwnAssetProfile = + assetProfile.userId === this.request.user.id && + hasPermission( + this.request.user.permissions, + permissions.readMarketDataOfOwnAssetProfile + ); + + if (!canReadAllAssetProfiles && !canReadOwnAssetProfile) { + throw new HttpException( + assetProfile.userId + ? getReasonPhrase(StatusCodes.NOT_FOUND) + : getReasonPhrase(StatusCodes.FORBIDDEN), + assetProfile.userId ? StatusCodes.NOT_FOUND : StatusCodes.FORBIDDEN + ); + } + + return this.adminService.getMarketDataBySymbol({ dataSource, symbol }); + } + + @Post(':dataSource/:symbol') + @UseGuards(AuthGuard('jwt')) + public async updateMarketData( + @Body() data: UpdateBulkMarketDataDto, + @Param('dataSource') dataSource: DataSource, + @Param('symbol') symbol: string + ) { + const [assetProfile] = await this.symbolProfileService.getSymbolProfiles([ + { dataSource, symbol } + ]); + + if (!assetProfile) { + throw new HttpException( + getReasonPhrase(StatusCodes.NOT_FOUND), + StatusCodes.NOT_FOUND + ); + } + + const canUpsertAllAssetProfiles = + hasPermission( + this.request.user.permissions, + permissions.createMarketData + ) && + hasPermission( + this.request.user.permissions, + permissions.updateMarketData + ); + + const canUpsertOwnAssetProfile = + assetProfile.userId === this.request.user.id && + hasPermission( + this.request.user.permissions, + permissions.createMarketDataOfOwnAssetProfile + ) && + hasPermission( + this.request.user.permissions, + permissions.updateMarketDataOfOwnAssetProfile + ); + + if (!canUpsertAllAssetProfiles && !canUpsertOwnAssetProfile) { + throw new HttpException( + getReasonPhrase(StatusCodes.FORBIDDEN), + StatusCodes.FORBIDDEN + ); + } + + const dataBulkUpdate: Prisma.MarketDataUpdateInput[] = data.marketData.map( + ({ date, marketPrice }) => ({ + dataSource, + marketPrice, + symbol, + date: parseISO(date), + state: 'CLOSE' + }) + ); + + return this.marketDataService.updateMany({ + data: dataBulkUpdate + }); + } +} diff --git a/apps/api/src/app/endpoints/market-data/market-data.module.ts b/apps/api/src/app/endpoints/market-data/market-data.module.ts new file mode 100644 index 000000000..2050889fd --- /dev/null +++ b/apps/api/src/app/endpoints/market-data/market-data.module.ts @@ -0,0 +1,13 @@ +import { AdminModule } from '@ghostfolio/api/app/admin/admin.module'; +import { MarketDataModule as MarketDataServiceModule } from '@ghostfolio/api/services/market-data/market-data.module'; +import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module'; + +import { Module } from '@nestjs/common'; + +import { MarketDataController } from './market-data.controller'; + +@Module({ + controllers: [MarketDataController], + imports: [AdminModule, MarketDataServiceModule, SymbolProfileModule] +}) +export class MarketDataModule {} diff --git a/apps/api/src/app/endpoints/market-data/update-bulk-market-data.dto.ts b/apps/api/src/app/endpoints/market-data/update-bulk-market-data.dto.ts new file mode 100644 index 000000000..d07b189b2 --- /dev/null +++ b/apps/api/src/app/endpoints/market-data/update-bulk-market-data.dto.ts @@ -0,0 +1,24 @@ +import { Type } from 'class-transformer'; +import { + ArrayNotEmpty, + IsArray, + IsISO8601, + IsNumber, + IsOptional +} from 'class-validator'; + +export class UpdateBulkMarketDataDto { + @ArrayNotEmpty() + @IsArray() + @Type(() => UpdateMarketDataDto) + marketData: UpdateMarketDataDto[]; +} + +class UpdateMarketDataDto { + @IsISO8601() + @IsOptional() + date?: string; + + @IsNumber() + marketPrice: number; +} 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 4fdc22986..a271915f3 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 @@ -121,8 +121,8 @@ export class AssetProfileDialog implements OnDestroy, OnInit { } }); - this.adminService - .fetchAdminMarketDataBySymbol({ + this.dataService + .fetchMarketDataBySymbol({ dataSource: this.data.dataSource, symbol: this.data.symbol }) diff --git a/apps/client/src/app/services/admin.service.ts b/apps/client/src/app/services/admin.service.ts index 77d135f57..ec0605bee 100644 --- a/apps/client/src/app/services/admin.service.ts +++ b/apps/client/src/app/services/admin.service.ts @@ -1,5 +1,4 @@ import { UpdateAssetProfileDto } from '@ghostfolio/api/app/admin/update-asset-profile.dto'; -import { UpdateBulkMarketDataDto } from '@ghostfolio/api/app/admin/update-bulk-market-data.dto'; import { CreatePlatformDto } from '@ghostfolio/api/app/platform/create-platform.dto'; import { UpdatePlatformDto } from '@ghostfolio/api/app/platform/update-platform.dto'; import { CreateTagDto } from '@ghostfolio/api/app/tag/create-tag.dto'; @@ -17,7 +16,6 @@ import { AdminData, AdminJobs, AdminMarketData, - AdminMarketDataDetails, AdminUsers, DataProviderGhostfolioStatusResponse, EnhancedSymbolProfile, @@ -29,8 +27,8 @@ import { Injectable } from '@angular/core'; import { SortDirection } from '@angular/material/sort'; import { DataSource, MarketData, Platform, Tag } from '@prisma/client'; import { JobStatus } from 'bull'; -import { format, parseISO } from 'date-fns'; -import { Observable, map, switchMap } from 'rxjs'; +import { format } from 'date-fns'; +import { switchMap } from 'rxjs'; import { environment } from '../../environments/environment'; import { DataService } from './data.service'; @@ -125,25 +123,6 @@ export class AdminService { }); } - public fetchAdminMarketDataBySymbol({ - dataSource, - symbol - }: { - dataSource: DataSource; - symbol: string; - }): Observable { - return this.http - .get(`/api/v1/admin/market-data/${dataSource}/${symbol}`) - .pipe( - map((data) => { - for (const item of data.marketData) { - item.date = parseISO(item.date); - } - return data; - }) - ); - } - public fetchGhostfolioDataProviderStatus() { return this.fetchAdminData().pipe( switchMap(({ settings }) => { @@ -278,20 +257,6 @@ export class AdminService { ); } - public postMarketData({ - dataSource, - marketData, - symbol - }: { - dataSource: DataSource; - marketData: UpdateBulkMarketDataDto; - symbol: string; - }) { - const url = `/api/v1/admin/market-data/${dataSource}/${symbol}`; - - return this.http.post(url, marketData); - } - public postPlatform(aPlatform: CreatePlatformDto) { return this.http.post(`/api/v1/platform`, aPlatform); } diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index eef258a5c..269a03e31 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -3,6 +3,7 @@ import { CreateAccountBalanceDto } from '@ghostfolio/api/app/account-balance/cre import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto'; import { TransferBalanceDto } from '@ghostfolio/api/app/account/transfer-balance.dto'; import { UpdateAccountDto } from '@ghostfolio/api/app/account/update-account.dto'; +import { UpdateBulkMarketDataDto } from '@ghostfolio/api/app/admin/update-bulk-market-data.dto'; import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto'; import { Activities, @@ -21,7 +22,6 @@ import { Access, AccountBalancesResponse, Accounts, - AdminMarketDataDetails, ApiKeyResponse, AssetProfileIdentifier, BenchmarkMarketDataDetails, @@ -31,6 +31,7 @@ import { ImportResponse, InfoItem, LookupResponse, + MarketDataDetailsResponse, OAuthResponse, PortfolioDetails, PortfolioDividends, @@ -51,6 +52,7 @@ import { SortDirection } from '@angular/material/sort'; import { AccountBalance, DataSource, + MarketData, Order as OrderModel, Tag } from '@prisma/client'; @@ -316,7 +318,7 @@ export class DataService { public fetchAsset({ dataSource, symbol - }: AssetProfileIdentifier): Observable { + }: AssetProfileIdentifier): Observable { return this.http.get(`/api/v1/asset/${dataSource}/${symbol}`).pipe( map((data) => { for (const item of data.marketData) { @@ -431,6 +433,25 @@ export class DataService { ); } + public fetchMarketDataBySymbol({ + dataSource, + symbol + }: { + dataSource: DataSource; + symbol: string; + }): Observable { + return this.http + .get(`/api/v1/market-data/${dataSource}/${symbol}`) + .pipe( + map((data) => { + for (const item of data.marketData) { + item.date = parseISO(item.date); + } + return data; + }) + ); + } + public fetchSymbolItem({ dataSource, includeHistoricalData, @@ -665,6 +686,20 @@ export class DataService { return this.http.post('/api/v1/benchmark', benchmark); } + public postMarketData({ + dataSource, + marketData, + symbol + }: { + dataSource: DataSource; + marketData: UpdateBulkMarketDataDto; + symbol: string; + }) { + const url = `/api/v1/market-data/${dataSource}/${symbol}`; + + return this.http.post(url, marketData); + } + public postOrder(aOrder: CreateOrderDto) { return this.http.post('/api/v1/order', aOrder); } diff --git a/libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts b/libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts index e7fc4c5b5..ce585c6a3 100644 --- a/libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts +++ b/libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts @@ -30,4 +30,5 @@ export interface EnhancedSymbolProfile { symbolMapping?: { [key: string]: string }; updatedAt: Date; url?: string; + userId?: string; } diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts index ed8fd4f2a..fa5eb25a5 100644 --- a/libs/common/src/lib/interfaces/index.ts +++ b/libs/common/src/lib/interfaces/index.ts @@ -46,6 +46,7 @@ import type { ResponseError } from './responses/errors.interface'; import type { HistoricalResponse } from './responses/historical-response.interface'; import type { ImportResponse } from './responses/import-response.interface'; import type { LookupResponse } from './responses/lookup-response.interface'; +import type { MarketDataDetailsResponse } from './responses/market-data-details-response.interface'; import type { OAuthResponse } from './responses/oauth-response.interface'; import type { PortfolioHoldingsResponse } from './responses/portfolio-holdings-response.interface'; import type { PortfolioPerformanceResponse } from './responses/portfolio-performance-response.interface'; @@ -97,6 +98,7 @@ export { LineChartItem, LookupItem, LookupResponse, + MarketDataDetailsResponse, OAuthResponse, PortfolioChart, PortfolioDetails, diff --git a/libs/common/src/lib/interfaces/responses/market-data-details-response.interface.ts b/libs/common/src/lib/interfaces/responses/market-data-details-response.interface.ts new file mode 100644 index 000000000..bbf947301 --- /dev/null +++ b/libs/common/src/lib/interfaces/responses/market-data-details-response.interface.ts @@ -0,0 +1,8 @@ +import { MarketData } from '@prisma/client'; + +import { EnhancedSymbolProfile } from '../enhanced-symbol-profile.interface'; + +export interface MarketDataDetailsResponse { + assetProfile: Partial; + marketData: MarketData[]; +} diff --git a/libs/common/src/lib/permissions.ts b/libs/common/src/lib/permissions.ts index cfee1c9e8..d6676ec4e 100644 --- a/libs/common/src/lib/permissions.ts +++ b/libs/common/src/lib/permissions.ts @@ -10,6 +10,8 @@ export const permissions = { createAccount: 'createAccount', createAccountBalance: 'createAccountBalance', createApiKey: 'createApiKey', + createMarketData: 'createMarketData', + createMarketDataOfOwnAssetProfile: 'createMarketDataOfOwnAssetProfile', createOrder: 'createOrder', createPlatform: 'createPlatform', createTag: 'createTag', @@ -33,12 +35,16 @@ export const permissions = { enableSubscriptionInterstitial: 'enableSubscriptionInterstitial', enableSystemMessage: 'enableSystemMessage', impersonateAllUsers: 'impersonateAllUsers', + readMarketData: 'readMarketData', + readMarketDataOfOwnAssetProfile: 'readMarketDataOfOwnAssetProfile', readPlatforms: 'readPlatforms', readTags: 'readTags', reportDataGlitch: 'reportDataGlitch', toggleReadOnlyMode: 'toggleReadOnlyMode', updateAccount: 'updateAccount', updateAuthDevice: 'updateAuthDevice', + updateMarketData: 'updateMarketData', + updateMarketDataOfOwnAssetProfile: 'updateMarketDataOfOwnAssetProfile', updateOrder: 'updateOrder', updatePlatform: 'updatePlatform', updateTag: 'updateTag', @@ -57,6 +63,8 @@ export function getPermissions(aRole: Role): string[] { permissions.createAccount, permissions.createAccountBalance, permissions.deleteAccountBalance, + permissions.createMarketData, + permissions.createMarketDataOfOwnAssetProfile, permissions.createOrder, permissions.createPlatform, permissions.createTag, @@ -68,10 +76,14 @@ export function getPermissions(aRole: Role): string[] { permissions.deletePlatform, permissions.deleteTag, permissions.deleteUser, + permissions.readMarketData, + permissions.readMarketDataOfOwnAssetProfile, permissions.readPlatforms, permissions.readTags, permissions.updateAccount, permissions.updateAuthDevice, + permissions.updateMarketData, + permissions.updateMarketDataOfOwnAssetProfile, permissions.updateOrder, permissions.updatePlatform, permissions.updateTag, @@ -93,6 +105,7 @@ export function getPermissions(aRole: Role): string[] { permissions.createAccess, permissions.createAccount, permissions.createAccountBalance, + permissions.createMarketDataOfOwnAssetProfile, permissions.createOrder, permissions.deleteAccess, permissions.deleteAccount, @@ -100,8 +113,10 @@ export function getPermissions(aRole: Role): string[] { permissions.deleteAuthDevice, permissions.deleteOrder, permissions.deleteOwnUser, + permissions.readMarketDataOfOwnAssetProfile, permissions.updateAccount, permissions.updateAuthDevice, + permissions.updateMarketDataOfOwnAssetProfile, permissions.updateOrder, permissions.updateUserSettings, permissions.updateViewMode diff --git a/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.component.ts b/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.component.ts index 434266e1e..69105ac94 100644 --- a/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.component.ts +++ b/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.component.ts @@ -1,4 +1,5 @@ import { AdminService } from '@ghostfolio/client/services/admin.service'; +import { DataService } from '@ghostfolio/client/services/data.service'; import { CommonModule } from '@angular/common'; import { @@ -51,6 +52,7 @@ export class GfHistoricalMarketDataEditorDialogComponent implements OnDestroy { private changeDetectorRef: ChangeDetectorRef, @Inject(MAT_DIALOG_DATA) public data: HistoricalMarketDataEditorDialogParams, + private dataService: DataService, private dateAdapter: DateAdapter, public dialogRef: MatDialogRef, @Inject(MAT_DATE_LOCALE) private locale: string @@ -81,7 +83,7 @@ export class GfHistoricalMarketDataEditorDialogComponent implements OnDestroy { } public onUpdate() { - this.adminService + this.dataService .postMarketData({ dataSource: this.data.dataSource, marketData: { diff --git a/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.ts b/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.ts index 0fce78621..c9725e3ee 100644 --- a/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.ts +++ b/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.ts @@ -1,5 +1,5 @@ import { UpdateMarketDataDto } from '@ghostfolio/api/app/admin/update-market-data.dto'; -import { AdminService } from '@ghostfolio/client/services/admin.service'; +import { DataService } from '@ghostfolio/client/services/data.service'; import { DATE_FORMAT, getDateFormatString, @@ -90,7 +90,7 @@ export class GfHistoricalMarketDataEditorComponent private unsubscribeSubject = new Subject(); public constructor( - private adminService: AdminService, + private dataService: DataService, private deviceService: DeviceDetectorService, private dialog: MatDialog, private formBuilder: FormBuilder, @@ -236,7 +236,7 @@ export class GfHistoricalMarketDataEditorComponent } ).data as UpdateMarketDataDto[]; - this.adminService + this.dataService .postMarketData({ dataSource: this.dataSource, marketData: { From 6ee61213172a90b4afaa85c91538902b3b8481f6 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Tue, 17 Dec 2024 20:33:57 +0100 Subject: [PATCH 7/8] Feature/set up asset class cluster risk x-ray rule (#4128) * Set up Asset Class Cluster Risk rule (equity) * Set up Asset Class Cluster Risk rule (fixed income) * Update changelog --- CHANGELOG.md | 5 + .../src/app/portfolio/portfolio.service.ts | 32 +++++-- apps/api/src/app/user/user.service.ts | 30 ++++-- .../rules/asset-class-cluster-risk/equity.ts | 95 +++++++++++++++++++ .../asset-class-cluster-risk/fixed-income.ts | 95 +++++++++++++++++++ .../base-currency-current-investment.ts | 17 ++-- .../portfolio/x-ray/x-ray-page.component.html | 24 +++++ .../portfolio/x-ray/x-ray-page.component.ts | 6 ++ .../x-ray-rules-settings.interface.ts | 2 + 9 files changed, 282 insertions(+), 24 deletions(-) create mode 100644 apps/api/src/models/rules/asset-class-cluster-risk/equity.ts create mode 100644 apps/api/src/models/rules/asset-class-cluster-risk/fixed-income.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 44fd0c3a8..35c78e959 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Added a new static portfolio analysis rule: _Asset Class Cluster Risk_ (Equity) +- Added a new static portfolio analysis rule: _Asset Class Cluster Risk_ (Fixed Income) + ### Changed - Extracted the market data management from the admin control panel endpoint to a dedicated endpoint diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 8ead98a58..400b0c3a9 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -7,6 +7,8 @@ import { UserService } from '@ghostfolio/api/app/user/user.service'; import { getFactor } from '@ghostfolio/api/helper/portfolio.helper'; import { AccountClusterRiskCurrentInvestment } from '@ghostfolio/api/models/rules/account-cluster-risk/current-investment'; import { AccountClusterRiskSingleAccount } from '@ghostfolio/api/models/rules/account-cluster-risk/single-account'; +import { AssetClassClusterRiskEquity } from '@ghostfolio/api/models/rules/asset-class-cluster-risk/equity'; +import { AssetClassClusterRiskFixedIncome } from '@ghostfolio/api/models/rules/asset-class-cluster-risk/fixed-income'; import { CurrencyClusterRiskBaseCurrencyCurrentInvestment } from '@ghostfolio/api/models/rules/currency-cluster-risk/base-currency-current-investment'; import { CurrencyClusterRiskCurrentInvestment } from '@ghostfolio/api/models/rules/currency-cluster-risk/current-investment'; import { EconomicMarketClusterRiskDevelopedMarkets } from '@ghostfolio/api/models/rules/economic-market-cluster-risk/developed-markets'; @@ -1198,19 +1200,17 @@ export class PortfolioService { userSettings ) : undefined, - economicMarketClusterRisk: + assetClassClusterRisk: summary.ordersCount > 0 ? await this.rulesService.evaluate( [ - new EconomicMarketClusterRiskDevelopedMarkets( + new AssetClassClusterRiskEquity( this.exchangeRateDataService, - marketsTotalInBaseCurrency, - markets.developedMarkets.valueInBaseCurrency + Object.values(holdings) ), - new EconomicMarketClusterRiskEmergingMarkets( + new AssetClassClusterRiskFixedIncome( this.exchangeRateDataService, - marketsTotalInBaseCurrency, - markets.emergingMarkets.valueInBaseCurrency + Object.values(holdings) ) ], userSettings @@ -1232,6 +1232,24 @@ export class PortfolioService { userSettings ) : undefined, + economicMarketClusterRisk: + summary.ordersCount > 0 + ? await this.rulesService.evaluate( + [ + new EconomicMarketClusterRiskDevelopedMarkets( + this.exchangeRateDataService, + marketsTotalInBaseCurrency, + markets.developedMarkets.valueInBaseCurrency + ), + new EconomicMarketClusterRiskEmergingMarkets( + this.exchangeRateDataService, + marketsTotalInBaseCurrency, + markets.emergingMarkets.valueInBaseCurrency + ) + ], + userSettings + ) + : undefined, emergencyFund: await this.rulesService.evaluate( [ new EmergencyFundSetup( diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index 6676a00b6..bac6ed19b 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -5,6 +5,8 @@ import { PortfolioChangedEvent } from '@ghostfolio/api/events/portfolio-changed. import { getRandomString } from '@ghostfolio/api/helper/string.helper'; import { AccountClusterRiskCurrentInvestment } from '@ghostfolio/api/models/rules/account-cluster-risk/current-investment'; import { AccountClusterRiskSingleAccount } from '@ghostfolio/api/models/rules/account-cluster-risk/single-account'; +import { AssetClassClusterRiskEquity } from '@ghostfolio/api/models/rules/asset-class-cluster-risk/equity'; +import { AssetClassClusterRiskFixedIncome } from '@ghostfolio/api/models/rules/asset-class-cluster-risk/fixed-income'; import { CurrencyClusterRiskBaseCurrencyCurrentInvestment } from '@ghostfolio/api/models/rules/currency-cluster-risk/base-currency-current-investment'; import { CurrencyClusterRiskCurrentInvestment } from '@ghostfolio/api/models/rules/currency-cluster-risk/current-investment'; import { EconomicMarketClusterRiskDevelopedMarkets } from '@ghostfolio/api/models/rules/economic-market-cluster-risk/developed-markets'; @@ -226,25 +228,33 @@ export class UserService { undefined, {} ).getSettings(user.Settings.settings), - EconomicMarketClusterRiskDevelopedMarkets: - new EconomicMarketClusterRiskDevelopedMarkets( - undefined, + AssetClassClusterRiskEquity: new AssetClassClusterRiskEquity( + undefined, + undefined + ).getSettings(user.Settings.settings), + AssetClassClusterRiskFixedIncome: new AssetClassClusterRiskFixedIncome( + undefined, + undefined + ).getSettings(user.Settings.settings), + CurrencyClusterRiskBaseCurrencyCurrentInvestment: + new CurrencyClusterRiskBaseCurrencyCurrentInvestment( undefined, undefined ).getSettings(user.Settings.settings), - EconomicMarketClusterRiskEmergingMarkets: - new EconomicMarketClusterRiskEmergingMarkets( - undefined, + CurrencyClusterRiskCurrentInvestment: + new CurrencyClusterRiskCurrentInvestment( undefined, undefined ).getSettings(user.Settings.settings), - CurrencyClusterRiskBaseCurrencyCurrentInvestment: - new CurrencyClusterRiskBaseCurrencyCurrentInvestment( + EconomicMarketClusterRiskDevelopedMarkets: + new EconomicMarketClusterRiskDevelopedMarkets( + undefined, undefined, undefined ).getSettings(user.Settings.settings), - CurrencyClusterRiskCurrentInvestment: - new CurrencyClusterRiskCurrentInvestment( + EconomicMarketClusterRiskEmergingMarkets: + new EconomicMarketClusterRiskEmergingMarkets( + undefined, undefined, undefined ).getSettings(user.Settings.settings), diff --git a/apps/api/src/models/rules/asset-class-cluster-risk/equity.ts b/apps/api/src/models/rules/asset-class-cluster-risk/equity.ts new file mode 100644 index 000000000..b67e01e61 --- /dev/null +++ b/apps/api/src/models/rules/asset-class-cluster-risk/equity.ts @@ -0,0 +1,95 @@ +import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; +import { Rule } from '@ghostfolio/api/models/rule'; +import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; +import { PortfolioPosition, UserSettings } from '@ghostfolio/common/interfaces'; + +export class AssetClassClusterRiskEquity extends Rule { + private holdings: PortfolioPosition[]; + + public constructor( + protected exchangeRateDataService: ExchangeRateDataService, + holdings: PortfolioPosition[] + ) { + super(exchangeRateDataService, { + key: AssetClassClusterRiskEquity.name, + name: 'Equity' + }); + + this.holdings = holdings; + } + + public evaluate(ruleSettings: Settings) { + const holdingsGroupedByAssetClass = this.groupCurrentHoldingsByAttribute( + this.holdings, + 'assetClass', + ruleSettings.baseCurrency + ); + let totalValue = 0; + + const equityValueInBaseCurrency = + holdingsGroupedByAssetClass.find(({ groupKey }) => { + return groupKey === 'EQUITY'; + })?.value ?? 0; + + for (const { value } of holdingsGroupedByAssetClass) { + totalValue += value; + } + + const equityValueRatio = totalValue + ? equityValueInBaseCurrency / totalValue + : 0; + + if (equityValueRatio > ruleSettings.thresholdMax) { + return { + evaluation: `The equity contribution of your current investment (${(equityValueRatio * 100).toPrecision(3)}%) exceeds ${( + ruleSettings.thresholdMax * 100 + ).toPrecision(3)}%`, + value: false + }; + } else if (equityValueRatio < ruleSettings.thresholdMin) { + return { + evaluation: `The equity contribution of your current investment (${(equityValueRatio * 100).toPrecision(3)}%) is below ${( + ruleSettings.thresholdMin * 100 + ).toPrecision(3)}%`, + value: false + }; + } + + return { + evaluation: `The equity contribution of your current investment (${(equityValueRatio * 100).toPrecision(3)}%) is within the range of ${( + ruleSettings.thresholdMin * 100 + ).toPrecision( + 3 + )}% and ${(ruleSettings.thresholdMax * 100).toPrecision(3)}%`, + value: true + }; + } + + public getConfiguration() { + return { + threshold: { + max: 1, + min: 0, + step: 0.01, + unit: '%' + }, + thresholdMax: true, + thresholdMin: true + }; + } + + public getSettings({ baseCurrency, xRayRules }: UserSettings): Settings { + return { + baseCurrency, + isActive: xRayRules?.[this.getKey()]?.isActive ?? true, + thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.82, + thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0.78 + }; + } +} + +interface Settings extends RuleSettings { + baseCurrency: string; + thresholdMin: number; + thresholdMax: number; +} diff --git a/apps/api/src/models/rules/asset-class-cluster-risk/fixed-income.ts b/apps/api/src/models/rules/asset-class-cluster-risk/fixed-income.ts new file mode 100644 index 000000000..eb744a143 --- /dev/null +++ b/apps/api/src/models/rules/asset-class-cluster-risk/fixed-income.ts @@ -0,0 +1,95 @@ +import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; +import { Rule } from '@ghostfolio/api/models/rule'; +import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; +import { PortfolioPosition, UserSettings } from '@ghostfolio/common/interfaces'; + +export class AssetClassClusterRiskFixedIncome extends Rule { + private holdings: PortfolioPosition[]; + + public constructor( + protected exchangeRateDataService: ExchangeRateDataService, + holdings: PortfolioPosition[] + ) { + super(exchangeRateDataService, { + key: AssetClassClusterRiskFixedIncome.name, + name: 'Fixed Income' + }); + + this.holdings = holdings; + } + + public evaluate(ruleSettings: Settings) { + const holdingsGroupedByAssetClass = this.groupCurrentHoldingsByAttribute( + this.holdings, + 'assetClass', + ruleSettings.baseCurrency + ); + let totalValue = 0; + + const fixedIncomeValueInBaseCurrency = + holdingsGroupedByAssetClass.find(({ groupKey }) => { + return groupKey === 'FIXED_INCOME'; + })?.value ?? 0; + + for (const { value } of holdingsGroupedByAssetClass) { + totalValue += value; + } + + const fixedIncomeValueRatio = totalValue + ? fixedIncomeValueInBaseCurrency / totalValue + : 0; + + if (fixedIncomeValueRatio > ruleSettings.thresholdMax) { + return { + evaluation: `The fixed income contribution of your current investment (${(fixedIncomeValueRatio * 100).toPrecision(3)}%) exceeds ${( + ruleSettings.thresholdMax * 100 + ).toPrecision(3)}%`, + value: false + }; + } else if (fixedIncomeValueRatio < ruleSettings.thresholdMin) { + return { + evaluation: `The fixed income contribution of your current investment (${(fixedIncomeValueRatio * 100).toPrecision(3)}%) is below ${( + ruleSettings.thresholdMin * 100 + ).toPrecision(3)}%`, + value: false + }; + } + + return { + evaluation: `The fixed income contribution of your current investment (${(fixedIncomeValueRatio * 100).toPrecision(3)}%) is within the range of ${( + ruleSettings.thresholdMin * 100 + ).toPrecision( + 3 + )}% and ${(ruleSettings.thresholdMax * 100).toPrecision(3)}%`, + value: true + }; + } + + public getConfiguration() { + return { + threshold: { + max: 1, + min: 0, + step: 0.01, + unit: '%' + }, + thresholdMax: true, + thresholdMin: true + }; + } + + public getSettings({ baseCurrency, xRayRules }: UserSettings): Settings { + return { + baseCurrency, + isActive: xRayRules?.[this.getKey()]?.isActive ?? true, + thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.22, + thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0.18 + }; + } +} + +interface Settings extends RuleSettings { + baseCurrency: string; + thresholdMin: number; + thresholdMax: number; +} diff --git a/apps/api/src/models/rules/currency-cluster-risk/base-currency-current-investment.ts b/apps/api/src/models/rules/currency-cluster-risk/base-currency-current-investment.ts index 573795799..90ee29c54 100644 --- a/apps/api/src/models/rules/currency-cluster-risk/base-currency-current-investment.ts +++ b/apps/api/src/models/rules/currency-cluster-risk/base-currency-current-investment.ts @@ -28,7 +28,12 @@ export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule { + const baseCurrencyValue = + holdingsGroupedByCurrency.find(({ groupKey }) => { + return groupKey === ruleSettings.baseCurrency; + })?.value ?? 0; + + for (const groupItem of holdingsGroupedByCurrency) { // Calculate total value totalValue += groupItem.value; @@ -36,13 +41,11 @@ export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule maxItem.investment) { maxItem = groupItem; } - }); - - const baseCurrencyItem = holdingsGroupedByCurrency.find((item) => { - return item.groupKey === ruleSettings.baseCurrency; - }); + } - const baseCurrencyValueRatio = baseCurrencyItem?.value / totalValue || 0; + const baseCurrencyValueRatio = totalValue + ? baseCurrencyValue / totalValue + : 0; if (maxItem?.groupKey !== ruleSettings.baseCurrency) { return { diff --git a/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html b/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html index 4af225c49..ceba5f52c 100644 --- a/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html +++ b/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html @@ -72,6 +72,30 @@ (rulesUpdated)="onRulesUpdated($event)" />

+
+

+ Asset Class Cluster Risks + @if (user?.subscription?.type === 'Basic') { + + } +

+ +