From fa27a05bcf9b105ac614808537222e3182fcc7cf Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Tue, 28 Jan 2025 19:32:34 +0100 Subject: [PATCH 1/3] Feature/upgrade bull to version 4.16.5 (#4218) * Upgrade bull to version 4.16.5 * Update changelog --- CHANGELOG.md | 2 ++ package-lock.json | 10 +++++----- package.json | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b744e52d..2b659270 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Migrated the database seeding to _TypeScript_ - Upgraded `@trivago/prettier-plugin-sort-imports` from version `4.3.0` to `5.2.1` +- Upgraded `bull` from version `4.16.4` to `4.16.5` - Upgraded `ng-extract-i18n-merge` from version `2.13.1` to `2.14.1` ## 2.136.0 - 2025-01-24 @@ -40,6 +41,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Moved the language localization for Polski (`pl`) from experimental to general availability - Extended the _Financial Modeling Prep_ service - Switched to _ESLint_’s flat config format +- Upgraded `bull` from version `4.16.2` to `4.16.4` - Upgraded `chart.js` from version `4.2.0` to `4.4.7` - Upgraded `chartjs-chart-treemap` from version `2.3.1` to `3.1.0` - Upgraded `chartjs-plugin-annotation` from version `2.1.2` to `3.1.0` diff --git a/package-lock.json b/package-lock.json index 7176c0b7..087d945c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,7 +47,7 @@ "alphavantage": "2.2.0", "big.js": "6.2.2", "bootstrap": "4.6.0", - "bull": "4.16.4", + "bull": "4.16.5", "cache-manager": "5.7.6", "cache-manager-redis-yet": "5.1.4", "chart.js": "4.4.7", @@ -13379,12 +13379,12 @@ } }, "node_modules/bull": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/bull/-/bull-4.16.4.tgz", - "integrity": "sha512-CF+nGsJyfsCC9MJL8hFxqXzbwq+jGBXhaz1j15G+5N/XtKIPFUUy5O1mfWWKbKunfuH/x+UV4NYRQDHSkjCOgA==", + "version": "4.16.5", + "resolved": "https://registry.npmjs.org/bull/-/bull-4.16.5.tgz", + "integrity": "sha512-lDsx2BzkKe7gkCYiT5Acj02DpTwDznl/VNN7Psn7M3USPG7Vs/BaClZJJTAG+ufAR9++N1/NiUTdaFBWDIl5TQ==", "license": "MIT", "dependencies": { - "cron-parser": "^4.2.1", + "cron-parser": "^4.9.0", "get-port": "^5.1.1", "ioredis": "^5.3.2", "lodash": "^4.17.21", diff --git a/package.json b/package.json index b3612bed..e808eccd 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "alphavantage": "2.2.0", "big.js": "6.2.2", "bootstrap": "4.6.0", - "bull": "4.16.4", + "bull": "4.16.5", "cache-manager": "5.7.6", "cache-manager-redis-yet": "5.1.4", "chart.js": "4.4.7", From 954cf765b8fb6bb1000a2b5662795547b48ba8f6 Mon Sep 17 00:00:00 2001 From: Ken Tandrian <60643640+KenTandrian@users.noreply.github.com> Date: Thu, 30 Jan 2025 02:22:44 +0700 Subject: [PATCH 2/3] Bugfix/dynamic numerical precision for cryptocurrencies in holding detail dialog (#4255) * fix(ui): dynamic numerical precision of quantity for cryptocurrencies * Update changelog --- CHANGELOG.md | 4 ++++ .../holding-detail-dialog/holding-detail-dialog.component.ts | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b659270..10a290ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Upgraded `bull` from version `4.16.4` to `4.16.5` - Upgraded `ng-extract-i18n-merge` from version `2.13.1` to `2.14.1` +### Fixed + +- Fixed the dynamic numerical precision for cryptocurrencies in the holding detail dialog + ## 2.136.0 - 2025-01-24 ### Added diff --git a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts index 94d5bd91..d1315889 100644 --- a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts +++ b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts @@ -296,7 +296,7 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { if (Number.isInteger(this.quantity)) { this.quantityPrecision = 0; - } else if (this.SymbolProfile?.assetSubClass === 'CRYPTOCURRENCY') { + } else if (SymbolProfile?.assetSubClass === 'CRYPTOCURRENCY') { if (this.quantity < 1) { this.quantityPrecision = 7; } else if (this.quantity < 1000) { From 8bd869e1b2e127f7ad2d321704440bb81f440421 Mon Sep 17 00:00:00 2001 From: Shaunak Das <51281688+shaun-ak@users.noreply.github.com> Date: Fri, 31 Jan 2025 03:11:13 +0530 Subject: [PATCH 3/3] Feature/add regional market cluster risk for north america (#4240) * Add regional market cluster risk for north america * Update changelog --- CHANGELOG.md | 4 + .../src/app/portfolio/portfolio.service.ts | 35 ++++++-- apps/api/src/app/user/user.service.ts | 9 +- .../north-america.ts | 82 +++++++++++++++++++ .../portfolio/x-ray/x-ray-page.component.html | 24 ++++++ .../portfolio/x-ray/x-ray-page.component.ts | 6 ++ .../x-ray-rules-settings.interface.ts | 1 + 7 files changed, 153 insertions(+), 8 deletions(-) create mode 100644 apps/api/src/models/rules/regional-market-cluster-risk/north-america.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 10a290ae..e124c785 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Added a new static portfolio analysis rule: _Regional Market Cluster Risk_ (North America) + ### Changed - Migrated the database seeding to _TypeScript_ diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 8b295aad..a14f97d2 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -15,6 +15,7 @@ import { EconomicMarketClusterRiskDevelopedMarkets } from '@ghostfolio/api/model import { EconomicMarketClusterRiskEmergingMarkets } from '@ghostfolio/api/models/rules/economic-market-cluster-risk/emerging-markets'; import { EmergencyFundSetup } from '@ghostfolio/api/models/rules/emergency-fund/emergency-fund-setup'; import { FeeRatioInitialInvestment } from '@ghostfolio/api/models/rules/fees/fee-ratio-initial-investment'; +import { RegionalMarketClusterRiskNorthAmerica } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/north-america'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ImpersonationService } from '@ghostfolio/api/services/impersonation/impersonation.service'; @@ -1167,12 +1168,19 @@ export class PortfolioService { const userId = await this.getUserId(impersonationId, this.request.user.id); const userSettings = this.request.user.Settings.settings as UserSettings; - const { accounts, holdings, markets, summary } = await this.getDetails({ - impersonationId, - userId, - withMarkets: true, - withSummary: true - }); + const { accounts, holdings, markets, marketsAdvanced, summary } = + await this.getDetails({ + impersonationId, + userId, + withMarkets: true, + withSummary: true + }); + + const marketsAdvancedTotalInBaseCurrency = getSum( + Object.values(marketsAdvanced).map(({ valueInBaseCurrency }) => { + return new Big(valueInBaseCurrency); + }) + ).toNumber(); const marketsTotalInBaseCurrency = getSum( Object.values(markets).map(({ valueInBaseCurrency }) => { @@ -1265,7 +1273,20 @@ export class PortfolioService { ) ], userSettings - ) + ), + regionalMarketClusterRisk: + summary.ordersCount > 0 + ? await this.rulesService.evaluate( + [ + new RegionalMarketClusterRiskNorthAmerica( + this.exchangeRateDataService, + marketsAdvancedTotalInBaseCurrency, + marketsAdvanced.northAmerica.valueInBaseCurrency + ) + ], + userSettings + ) + : undefined }; return { rules, statistics: this.getReportStatistics(rules) }; diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index b5c71179..415cbc99 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -13,6 +13,7 @@ import { EconomicMarketClusterRiskDevelopedMarkets } from '@ghostfolio/api/model import { EconomicMarketClusterRiskEmergingMarkets } from '@ghostfolio/api/models/rules/economic-market-cluster-risk/emerging-markets'; import { EmergencyFundSetup } from '@ghostfolio/api/models/rules/emergency-fund/emergency-fund-setup'; import { FeeRatioInitialInvestment } from '@ghostfolio/api/models/rules/fees/fee-ratio-initial-investment'; +import { RegionalMarketClusterRiskNorthAmerica } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/north-america'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; @@ -268,7 +269,13 @@ export class UserService { undefined, undefined, undefined - ).getSettings(user.Settings.settings) + ).getSettings(user.Settings.settings), + RegionalMarketClusterRiskNorthAmerica: + new RegionalMarketClusterRiskNorthAmerica( + undefined, + undefined, + undefined + ).getSettings(user.Settings.settings) }; let currentPermissions = getPermissions(user.role); diff --git a/apps/api/src/models/rules/regional-market-cluster-risk/north-america.ts b/apps/api/src/models/rules/regional-market-cluster-risk/north-america.ts new file mode 100644 index 00000000..a137f7ab --- /dev/null +++ b/apps/api/src/models/rules/regional-market-cluster-risk/north-america.ts @@ -0,0 +1,82 @@ +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 { UserSettings } from '@ghostfolio/common/interfaces'; + +export class RegionalMarketClusterRiskNorthAmerica extends Rule { + private currentValueInBaseCurrency: number; + private northAmericaValueInBaseCurrency: number; + + public constructor( + protected exchangeRateDataService: ExchangeRateDataService, + currentValueInBaseCurrency: number, + valueInBaseCurrency + ) { + super(exchangeRateDataService, { + key: RegionalMarketClusterRiskNorthAmerica.name, + name: 'North America' + }); + + this.currentValueInBaseCurrency = currentValueInBaseCurrency; + this.northAmericaValueInBaseCurrency = valueInBaseCurrency; + } + + public evaluate(ruleSettings: Settings) { + const northAmericaMarketValueRatio = this.currentValueInBaseCurrency + ? this.northAmericaValueInBaseCurrency / this.currentValueInBaseCurrency + : 0; + + if (northAmericaMarketValueRatio > ruleSettings.thresholdMax) { + return { + evaluation: `The North America market contribution of your current investment (${(northAmericaMarketValueRatio * 100).toPrecision(3)}%) exceeds ${( + ruleSettings.thresholdMax * 100 + ).toPrecision(3)}%`, + value: false + }; + } else if (northAmericaMarketValueRatio < ruleSettings.thresholdMin) { + return { + evaluation: `The North America market contribution of your current investment (${(northAmericaMarketValueRatio * 100).toPrecision(3)}%) is below ${( + ruleSettings.thresholdMin * 100 + ).toPrecision(3)}%`, + value: false + }; + } + + return { + evaluation: `The North America market contribution of your current investment (${(northAmericaMarketValueRatio * 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.69, + thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0.65 + }; + } +} + +interface Settings extends RuleSettings { + baseCurrency: string; + thresholdMin: number; + thresholdMax: number; +} 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 ceba5f52..6ec5722b 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 @@ -144,6 +144,30 @@ (rulesUpdated)="onRulesUpdated($event)" /> +
+

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

+ +