diff --git a/CHANGELOG.md b/CHANGELOG.md index 480f75a0..67e133ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added a new static portfolio analysis rule: _Regional Market Cluster Risk_ (Japan) - Extended the tags selector component by a `readonly` attribute - Extended the tags selector component to support creating custom tags - Added global styles to the _Storybook_ setup diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 72568f37..5fdb1003 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -17,6 +17,7 @@ import { EmergencyFundSetup } from '@ghostfolio/api/models/rules/emergency-fund/ import { FeeRatioInitialInvestment } from '@ghostfolio/api/models/rules/fees/fee-ratio-initial-investment'; import { RegionalMarketClusterRiskEmergingMarkets } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/emerging-markets'; import { RegionalMarketClusterRiskEurope } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/europe'; +import { RegionalMarketClusterRiskJapan } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/japan'; 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'; @@ -1290,6 +1291,11 @@ export class PortfolioService { marketsAdvancedTotalInBaseCurrency, marketsAdvanced.europe.valueInBaseCurrency ), + new RegionalMarketClusterRiskJapan( + this.exchangeRateDataService, + marketsAdvancedTotalInBaseCurrency, + marketsAdvanced.japan.valueInBaseCurrency + ), new RegionalMarketClusterRiskNorthAmerica( this.exchangeRateDataService, marketsAdvancedTotalInBaseCurrency, diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index 1dae9d45..13b067cf 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -15,6 +15,7 @@ import { EmergencyFundSetup } from '@ghostfolio/api/models/rules/emergency-fund/ import { FeeRatioInitialInvestment } from '@ghostfolio/api/models/rules/fees/fee-ratio-initial-investment'; import { RegionalMarketClusterRiskEmergingMarkets } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/emerging-markets'; import { RegionalMarketClusterRiskEurope } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/europe'; +import { RegionalMarketClusterRiskJapan } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/japan'; 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'; @@ -283,6 +284,11 @@ export class UserService { undefined, undefined ).getSettings(user.Settings.settings), + RegionalMarketClusterRiskJapan: new RegionalMarketClusterRiskJapan( + undefined, + undefined, + undefined + ).getSettings(user.Settings.settings), RegionalMarketClusterRiskNorthAmerica: new RegionalMarketClusterRiskNorthAmerica( undefined, diff --git a/apps/api/src/models/rules/regional-market-cluster-risk/japan.ts b/apps/api/src/models/rules/regional-market-cluster-risk/japan.ts new file mode 100644 index 00000000..4694b000 --- /dev/null +++ b/apps/api/src/models/rules/regional-market-cluster-risk/japan.ts @@ -0,0 +1,77 @@ +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'; + +import { Settings } from './interfaces/rule-settings.interface'; + +export class RegionalMarketClusterRiskJapan extends Rule { + private currentValueInBaseCurrency: number; + private japanValueInBaseCurrency: number; + + public constructor( + protected exchangeRateDataService: ExchangeRateDataService, + currentValueInBaseCurrency: number, + japanValueInBaseCurrency: number + ) { + super(exchangeRateDataService, { + key: RegionalMarketClusterRiskJapan.name, + name: 'Japan' + }); + + this.currentValueInBaseCurrency = currentValueInBaseCurrency; + this.japanValueInBaseCurrency = japanValueInBaseCurrency; + } + + public evaluate(ruleSettings: Settings) { + const japanMarketValueRatio = this.currentValueInBaseCurrency + ? this.japanValueInBaseCurrency / this.currentValueInBaseCurrency + : 0; + + if (japanMarketValueRatio > ruleSettings.thresholdMax) { + return { + evaluation: `The Japan market contribution of your current investment (${(japanMarketValueRatio * 100).toPrecision(3)}%) exceeds ${( + ruleSettings.thresholdMax * 100 + ).toPrecision(3)}%`, + value: false + }; + } else if (japanMarketValueRatio < ruleSettings.thresholdMin) { + return { + evaluation: `The Japan market contribution of your current investment (${(japanMarketValueRatio * 100).toPrecision(3)}%) is below ${( + ruleSettings.thresholdMin * 100 + ).toPrecision(3)}%`, + value: false + }; + } + + return { + evaluation: `The Japan market contribution of your current investment (${(japanMarketValueRatio * 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.06, + thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0.04 + }; + } +} diff --git a/libs/common/src/lib/interfaces/x-ray-rules-settings.interface.ts b/libs/common/src/lib/interfaces/x-ray-rules-settings.interface.ts index 4a31ef7b..e81256ed 100644 --- a/libs/common/src/lib/interfaces/x-ray-rules-settings.interface.ts +++ b/libs/common/src/lib/interfaces/x-ray-rules-settings.interface.ts @@ -11,6 +11,7 @@ export interface XRayRulesSettings { FeeRatioInitialInvestment?: RuleSettings; RegionalMarketClusterRiskEmergingMarkets?: RuleSettings; RegionalMarketClusterRiskEurope?: RuleSettings; + RegionalMarketClusterRiskJapan?: RuleSettings; RegionalMarketClusterRiskNorthAmerica?: RuleSettings; }