From 2f30b11cff7b8522ee07f4cd63b601151a012cbf Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 19 Sep 2026 10:24:03 +0200 Subject: [PATCH] Task/centralize X-ray rule settings (#7887) Centralize X-ray rule settings --- .../src/app/portfolio/portfolio.service.ts | 69 +++++---- apps/api/src/app/portfolio/rules.service.ts | 8 +- apps/api/src/app/user/user.service.ts | 143 +----------------- .../rule-settings-configuration.interface.ts | 5 + .../src/models/interfaces/rule.interface.ts | 4 +- apps/api/src/models/rule.ts | 15 +- .../current-investment.ts | 22 +-- .../account-cluster-risk/single-account.ts | 19 +-- .../rules/asset-class-cluster-risk/equity.ts | 23 +-- .../asset-class-cluster-risk/fixed-income.ts | 23 +-- .../base-currency-current-investment.ts | 23 +-- .../current-investment.ts | 24 +-- .../developed-markets.ts | 19 +-- .../emerging-markets.ts | 19 +-- .../emergency-fund/emergency-fund-coverage.ts | 17 +-- .../emergency-fund/emergency-fund-setup.ts | 17 +-- .../fees/fee-ratio-total-investment-volume.ts | 18 +-- .../models/rules/liquidity/buying-power.ts | 18 +-- .../asia-pacific.ts | 18 +-- .../emerging-markets.ts | 18 +-- .../regional-market-cluster-risk/europe.ts | 18 +-- .../regional-market-cluster-risk/japan.ts | 18 +-- .../north-america.ts | 18 +-- .../src/models/rules/rule-settings.spec.ts | 129 ++++++++++++++++ apps/api/src/models/rules/rule-settings.ts | 98 ++++++++++++ .../src/models/types/x-ray-rule-key.type.ts | 3 + 26 files changed, 319 insertions(+), 487 deletions(-) create mode 100644 apps/api/src/models/interfaces/rule-settings-configuration.interface.ts create mode 100644 apps/api/src/models/rules/rule-settings.spec.ts create mode 100644 apps/api/src/models/rules/rule-settings.ts create mode 100644 apps/api/src/models/types/x-ray-rule-key.type.ts diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index fd941286c2..f58fd1eaaa 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -33,6 +33,7 @@ import { import { DEFAULT_CURRENCY, DEFAULT_DATE_RANGE, + DEFAULT_LANGUAGE_CODE, TAG_ID_DRAFT, TAG_ID_EMERGENCY_FUND, TAG_ID_EXCLUDE_FROM_ANALYSIS, @@ -1288,6 +1289,8 @@ export class PortfolioService { userSettings: this.request.user.settings.settings as UserSettings }); + const languageCode = userSettings.language ?? DEFAULT_LANGUAGE_CODE; + const { accounts, holdings, markets, marketsAdvanced, summary } = await this.getDetails({ user, @@ -1330,8 +1333,8 @@ export class PortfolioService { { key: 'liquidity', name: this.i18nService.getTranslation({ - id: 'rule.liquidity.category', - languageCode: userSettings.language + languageCode, + id: 'rule.liquidity.category' }), rules: await this.rulesService.evaluate( [ @@ -1339,7 +1342,7 @@ export class PortfolioService { this.exchangeRateDataService, this.i18nService, summary.cash, - userSettings.language + languageCode ) ], userSettings @@ -1348,15 +1351,15 @@ export class PortfolioService { { key: 'emergencyFund', name: this.i18nService.getTranslation({ - id: 'rule.emergencyFund.category', - languageCode: userSettings.language + languageCode, + id: 'rule.emergencyFund.category' }), rules: await this.rulesService.evaluate( [ new EmergencyFundSetup( this.exchangeRateDataService, this.i18nService, - userSettings.language, + languageCode, totalEmergencyFundInBaseCurrency ), // The coverage is only meaningful once an emergency fund has been @@ -1366,7 +1369,7 @@ export class PortfolioService { new EmergencyFundCoverage( this.exchangeRateDataService, this.i18nService, - userSettings.language, + languageCode, emergencyFundInBaseCurrency, emergencyFundHoldingsValueInBaseCurrency, cashBalanceInBaseCurrency @@ -1380,8 +1383,8 @@ export class PortfolioService { { key: 'currencyClusterRisk', name: this.i18nService.getTranslation({ - id: 'rule.currencyClusterRisk.category', - languageCode: userSettings.language + languageCode, + id: 'rule.currencyClusterRisk.category' }), rules: hasOpenHoldings ? await this.rulesService.evaluate( @@ -1390,13 +1393,13 @@ export class PortfolioService { this.exchangeRateDataService, this.i18nService, holdings, - userSettings.language + languageCode ), new CurrencyClusterRiskCurrentInvestment( this.exchangeRateDataService, this.i18nService, holdings, - userSettings.language + languageCode ) ], userSettings @@ -1406,8 +1409,8 @@ export class PortfolioService { { key: 'assetClassClusterRisk', name: this.i18nService.getTranslation({ - id: 'rule.assetClassClusterRisk.category', - languageCode: userSettings.language + languageCode, + id: 'rule.assetClassClusterRisk.category' }), rules: hasOpenHoldings ? await this.rulesService.evaluate( @@ -1415,13 +1418,13 @@ export class PortfolioService { new AssetClassClusterRiskEquity( this.exchangeRateDataService, this.i18nService, - userSettings.language, + languageCode, holdings ), new AssetClassClusterRiskFixedIncome( this.exchangeRateDataService, this.i18nService, - userSettings.language, + languageCode, holdings ) ], @@ -1432,8 +1435,8 @@ export class PortfolioService { { key: 'accountClusterRisk', name: this.i18nService.getTranslation({ - id: 'rule.accountClusterRisk.category', - languageCode: userSettings.language + languageCode, + id: 'rule.accountClusterRisk.category' }), rules: summary.activityCount > 0 @@ -1442,13 +1445,13 @@ export class PortfolioService { new AccountClusterRiskCurrentInvestment( this.exchangeRateDataService, this.i18nService, - userSettings.language, + languageCode, accounts ), new AccountClusterRiskSingleAccount( this.exchangeRateDataService, this.i18nService, - userSettings.language, + languageCode, accounts ) ], @@ -1459,8 +1462,8 @@ export class PortfolioService { { key: 'economicMarketClusterRisk', name: this.i18nService.getTranslation({ - id: 'rule.economicMarketClusterRisk.category', - languageCode: userSettings.language + languageCode, + id: 'rule.economicMarketClusterRisk.category' }), rules: hasOpenHoldings ? await this.rulesService.evaluate( @@ -1470,14 +1473,14 @@ export class PortfolioService { this.i18nService, marketsTotalInBaseCurrency, markets.developedMarkets.valueInBaseCurrency, - userSettings.language + languageCode ), new EconomicMarketClusterRiskEmergingMarkets( this.exchangeRateDataService, this.i18nService, marketsTotalInBaseCurrency, markets.emergingMarkets.valueInBaseCurrency, - userSettings.language + languageCode ) ], userSettings @@ -1487,8 +1490,8 @@ export class PortfolioService { { key: 'regionalMarketClusterRisk', name: this.i18nService.getTranslation({ - id: 'rule.regionalMarketClusterRisk.category', - languageCode: userSettings.language + languageCode, + id: 'rule.regionalMarketClusterRisk.category' }), rules: hasOpenHoldings ? await this.rulesService.evaluate( @@ -1496,35 +1499,35 @@ export class PortfolioService { new RegionalMarketClusterRiskAsiaPacific( this.exchangeRateDataService, this.i18nService, - userSettings.language, + languageCode, marketsAdvancedTotalInBaseCurrency, marketsAdvanced.asiaPacific.valueInBaseCurrency ), new RegionalMarketClusterRiskEmergingMarkets( this.exchangeRateDataService, this.i18nService, - userSettings.language, + languageCode, marketsAdvancedTotalInBaseCurrency, marketsAdvanced.emergingMarkets.valueInBaseCurrency ), new RegionalMarketClusterRiskEurope( this.exchangeRateDataService, this.i18nService, - userSettings.language, + languageCode, marketsAdvancedTotalInBaseCurrency, marketsAdvanced.europe.valueInBaseCurrency ), new RegionalMarketClusterRiskJapan( this.exchangeRateDataService, this.i18nService, - userSettings.language, + languageCode, marketsAdvancedTotalInBaseCurrency, marketsAdvanced.japan.valueInBaseCurrency ), new RegionalMarketClusterRiskNorthAmerica( this.exchangeRateDataService, this.i18nService, - userSettings.language, + languageCode, marketsAdvancedTotalInBaseCurrency, marketsAdvanced.northAmerica.valueInBaseCurrency ) @@ -1536,15 +1539,15 @@ export class PortfolioService { { key: 'fees', name: this.i18nService.getTranslation({ - id: 'rule.fees.category', - languageCode: userSettings.language + languageCode, + id: 'rule.fees.category' }), rules: await this.rulesService.evaluate( [ new FeeRatioTotalInvestmentVolume( this.exchangeRateDataService, this.i18nService, - userSettings.language, + languageCode, summary.totalBuy + summary.totalSell, summary.fees ) diff --git a/apps/api/src/app/portfolio/rules.service.ts b/apps/api/src/app/portfolio/rules.service.ts index 5bfb116e0d..404e843919 100644 --- a/apps/api/src/app/portfolio/rules.service.ts +++ b/apps/api/src/app/portfolio/rules.service.ts @@ -1,4 +1,5 @@ import { Rule } from '@ghostfolio/api/models/rule'; +import { getRuleSettings } from '@ghostfolio/api/models/rules/rule-settings'; import { PortfolioReportRule, RuleSettings, @@ -14,9 +15,12 @@ export class RulesService { aUserSettings: UserSettings ): Promise { return aRules.map((rule) => { - const settings = rule.getSettings(aUserSettings); + const settings = getRuleSettings({ + key: rule.getKey(), + userSettings: aUserSettings + }); - if (settings?.isActive) { + if (settings.isActive) { const { evaluation, value } = rule.evaluate(settings); return { diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index dae4463d23..a74e32ca93 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -4,23 +4,7 @@ import { environment } from '@ghostfolio/api/environments/environment'; import { PortfolioChangedEvent } from '@ghostfolio/api/events/portfolio-changed.event'; import { getSupportedLanguageCode } from '@ghostfolio/api/helper/language.helper'; 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'; -import { EconomicMarketClusterRiskEmergingMarkets } from '@ghostfolio/api/models/rules/economic-market-cluster-risk/emerging-markets'; -import { EmergencyFundCoverage } from '@ghostfolio/api/models/rules/emergency-fund/emergency-fund-coverage'; -import { EmergencyFundSetup } from '@ghostfolio/api/models/rules/emergency-fund/emergency-fund-setup'; -import { FeeRatioTotalInvestmentVolume } from '@ghostfolio/api/models/rules/fees/fee-ratio-total-investment-volume'; -import { BuyingPower } from '@ghostfolio/api/models/rules/liquidity/buying-power'; -import { RegionalMarketClusterRiskAsiaPacific } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/asia-pacific'; -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 { getXRayRulesSettings } from '@ghostfolio/api/models/rules/rule-settings'; 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'; @@ -405,128 +389,9 @@ export class UserService { (user.settings.settings as UserSettings).viewMode = 'DEFAULT'; } - (user.settings.settings as UserSettings).xRayRules = { - AccountClusterRiskCurrentInvestment: - new AccountClusterRiskCurrentInvestment( - undefined, - undefined, - undefined, - {} - ).getSettings(user.settings.settings), - AccountClusterRiskSingleAccount: new AccountClusterRiskSingleAccount( - undefined, - undefined, - undefined, - {} - ).getSettings(user.settings.settings), - AssetClassClusterRiskEquity: new AssetClassClusterRiskEquity( - undefined, - undefined, - undefined, - undefined - ).getSettings(user.settings.settings), - AssetClassClusterRiskFixedIncome: new AssetClassClusterRiskFixedIncome( - undefined, - undefined, - undefined, - undefined - ).getSettings(user.settings.settings), - BuyingPower: new BuyingPower( - undefined, - undefined, - undefined, - undefined - ).getSettings(user.settings.settings), - CurrencyClusterRiskBaseCurrencyCurrentInvestment: - new CurrencyClusterRiskBaseCurrencyCurrentInvestment( - undefined, - undefined, - undefined, - undefined - ).getSettings(user.settings.settings), - CurrencyClusterRiskCurrentInvestment: - new CurrencyClusterRiskCurrentInvestment( - undefined, - undefined, - undefined, - undefined - ).getSettings(user.settings.settings), - EconomicMarketClusterRiskDevelopedMarkets: - new EconomicMarketClusterRiskDevelopedMarkets( - undefined, - undefined, - undefined, - undefined, - undefined - ).getSettings(user.settings.settings), - EconomicMarketClusterRiskEmergingMarkets: - new EconomicMarketClusterRiskEmergingMarkets( - undefined, - undefined, - undefined, - undefined, - undefined - ).getSettings(user.settings.settings), - EmergencyFundCoverage: new EmergencyFundCoverage( - undefined, - undefined, - undefined, - undefined, - undefined, - undefined - ).getSettings(user.settings.settings), - EmergencyFundSetup: new EmergencyFundSetup( - undefined, - undefined, - undefined, - undefined - ).getSettings(user.settings.settings), - FeeRatioTotalInvestmentVolume: new FeeRatioTotalInvestmentVolume( - undefined, - undefined, - undefined, - undefined, - undefined - ).getSettings(user.settings.settings), - RegionalMarketClusterRiskAsiaPacific: - new RegionalMarketClusterRiskAsiaPacific( - undefined, - undefined, - undefined, - undefined, - undefined - ).getSettings(user.settings.settings), - RegionalMarketClusterRiskEmergingMarkets: - new RegionalMarketClusterRiskEmergingMarkets( - undefined, - undefined, - undefined, - undefined, - undefined - ).getSettings(user.settings.settings), - RegionalMarketClusterRiskEurope: new RegionalMarketClusterRiskEurope( - undefined, - undefined, - undefined, - undefined, - undefined - ).getSettings(user.settings.settings), - RegionalMarketClusterRiskJapan: new RegionalMarketClusterRiskJapan( - undefined, - undefined, - undefined, - undefined, - undefined - ).getSettings(user.settings.settings), - RegionalMarketClusterRiskNorthAmerica: - new RegionalMarketClusterRiskNorthAmerica( - undefined, - undefined, - undefined, - undefined, - undefined - ).getSettings(user.settings.settings) - }; + (user.settings.settings as UserSettings).xRayRules = getXRayRulesSettings( + user.settings.settings + ); let currentPermissions = getPermissions(user.role); diff --git a/apps/api/src/models/interfaces/rule-settings-configuration.interface.ts b/apps/api/src/models/interfaces/rule-settings-configuration.interface.ts new file mode 100644 index 0000000000..75a69626b2 --- /dev/null +++ b/apps/api/src/models/interfaces/rule-settings-configuration.interface.ts @@ -0,0 +1,5 @@ +export interface RuleSettingsConfiguration { + thresholdMax?: number; + thresholdMin?: number; + withBaseCurrency?: boolean; +} diff --git a/apps/api/src/models/interfaces/rule.interface.ts b/apps/api/src/models/interfaces/rule.interface.ts index 7c794614eb..8c9a838045 100644 --- a/apps/api/src/models/interfaces/rule.interface.ts +++ b/apps/api/src/models/interfaces/rule.interface.ts @@ -1,9 +1,7 @@ -import { RuleSettings, UserSettings } from '@ghostfolio/common/interfaces'; +import { RuleSettings } from '@ghostfolio/common/interfaces'; import { EvaluationResult } from './evaluation-result.interface'; export interface RuleInterface { evaluate(aRuleSettings: T): EvaluationResult; - - getSettings(aUserSettings: UserSettings): T; } diff --git a/apps/api/src/models/rule.ts b/apps/api/src/models/rule.ts index d47ea30f20..f935d2ad31 100644 --- a/apps/api/src/models/rule.ts +++ b/apps/api/src/models/rule.ts @@ -1,10 +1,9 @@ +import { XRayRuleKey } from '@ghostfolio/api/models/types/x-ray-rule-key.type'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; -import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config'; import { PortfolioPosition, PortfolioReportRule, - RuleSettings, - UserSettings + RuleSettings } from '@ghostfolio/common/interfaces'; import { Big } from 'big.js'; @@ -14,17 +13,17 @@ import { EvaluationResult } from './interfaces/evaluation-result.interface'; import { RuleInterface } from './interfaces/rule.interface'; export abstract class Rule implements RuleInterface { - private key: string; + private key: XRayRuleKey; private languageCode: string; public constructor( protected exchangeRateDataService: ExchangeRateDataService, { key, - languageCode = DEFAULT_LANGUAGE_CODE + languageCode }: { - key: string; - languageCode?: string; // TODO: Make mandatory + key: XRayRuleKey; + languageCode: string; } ) { this.key = key; @@ -75,6 +74,4 @@ export abstract class Rule implements RuleInterface { >; public abstract getName(): string; - - public abstract getSettings(aUserSettings: UserSettings): T; } diff --git a/apps/api/src/models/rules/account-cluster-risk/current-investment.ts b/apps/api/src/models/rules/account-cluster-risk/current-investment.ts index 1b967c5dbe..8c3818c9a7 100644 --- a/apps/api/src/models/rules/account-cluster-risk/current-investment.ts +++ b/apps/api/src/models/rules/account-cluster-risk/current-investment.ts @@ -1,12 +1,7 @@ import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; -import { - PortfolioDetails, - RuleSettings, - UserSettings -} from '@ghostfolio/common/interfaces'; +import { PortfolioDetails, RuleSettings } from '@ghostfolio/common/interfaces'; import { Account } from '@prisma/client'; @@ -21,7 +16,7 @@ export class AccountClusterRiskCurrentInvestment extends Rule { ) { super(exchangeRateDataService, { languageCode, - key: AccountClusterRiskCurrentInvestment.name + key: 'AccountClusterRiskCurrentInvestment' }); this.accounts = accounts; @@ -118,19 +113,6 @@ export class AccountClusterRiskCurrentInvestment extends Rule { languageCode: this.getLanguageCode() }); } - - public getSettings({ - baseCurrency = DEFAULT_CURRENCY, - locale = DEFAULT_LOCALE, - xRayRules - }: UserSettings): Settings { - return { - baseCurrency, - locale, - isActive: xRayRules?.[this.getKey()]?.isActive ?? true, - thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.5 - }; - } } interface Settings extends RuleSettings { diff --git a/apps/api/src/models/rules/account-cluster-risk/single-account.ts b/apps/api/src/models/rules/account-cluster-risk/single-account.ts index 9a6224ac29..17c60e5772 100644 --- a/apps/api/src/models/rules/account-cluster-risk/single-account.ts +++ b/apps/api/src/models/rules/account-cluster-risk/single-account.ts @@ -1,12 +1,7 @@ import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { DEFAULT_LOCALE } from '@ghostfolio/common/config'; -import { - PortfolioDetails, - RuleSettings, - UserSettings -} from '@ghostfolio/common/interfaces'; +import { PortfolioDetails, RuleSettings } from '@ghostfolio/common/interfaces'; export class AccountClusterRiskSingleAccount extends Rule { private accounts: PortfolioDetails['accounts']; @@ -19,7 +14,7 @@ export class AccountClusterRiskSingleAccount extends Rule { ) { super(exchangeRateDataService, { languageCode, - key: AccountClusterRiskSingleAccount.name + key: 'AccountClusterRiskSingleAccount' }); this.accounts = accounts; @@ -68,14 +63,4 @@ export class AccountClusterRiskSingleAccount extends Rule { languageCode: this.getLanguageCode() }); } - - public getSettings({ - locale = DEFAULT_LOCALE, - xRayRules - }: UserSettings): RuleSettings { - return { - locale, - isActive: xRayRules?.[this.getKey()]?.isActive ?? true - }; - } } 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 index 0e054f1846..8a29ef0dd5 100644 --- a/apps/api/src/models/rules/asset-class-cluster-risk/equity.ts +++ b/apps/api/src/models/rules/asset-class-cluster-risk/equity.ts @@ -1,12 +1,7 @@ import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; -import { - PortfolioPosition, - RuleSettings, - UserSettings -} from '@ghostfolio/common/interfaces'; +import { PortfolioPosition, RuleSettings } from '@ghostfolio/common/interfaces'; export class AssetClassClusterRiskEquity extends Rule { private holdings: PortfolioPosition[]; @@ -19,7 +14,7 @@ export class AssetClassClusterRiskEquity extends Rule { ) { super(exchangeRateDataService, { languageCode, - key: AssetClassClusterRiskEquity.name + key: 'AssetClassClusterRiskEquity' }); this.holdings = holdings; @@ -106,20 +101,6 @@ export class AssetClassClusterRiskEquity extends Rule { languageCode: this.getLanguageCode() }); } - - public getSettings({ - baseCurrency = DEFAULT_CURRENCY, - locale = DEFAULT_LOCALE, - xRayRules - }: UserSettings): Settings { - return { - baseCurrency, - locale, - isActive: xRayRules?.[this.getKey()]?.isActive ?? true, - thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.82, - thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0.78 - }; - } } interface Settings extends RuleSettings { 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 index 94f3598091..c9479e2ba6 100644 --- 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 @@ -1,12 +1,7 @@ import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; -import { - PortfolioPosition, - RuleSettings, - UserSettings -} from '@ghostfolio/common/interfaces'; +import { PortfolioPosition, RuleSettings } from '@ghostfolio/common/interfaces'; export class AssetClassClusterRiskFixedIncome extends Rule { private holdings: PortfolioPosition[]; @@ -19,7 +14,7 @@ export class AssetClassClusterRiskFixedIncome extends Rule { ) { super(exchangeRateDataService, { languageCode, - key: AssetClassClusterRiskFixedIncome.name + key: 'AssetClassClusterRiskFixedIncome' }); this.holdings = holdings; @@ -106,20 +101,6 @@ export class AssetClassClusterRiskFixedIncome extends Rule { languageCode: this.getLanguageCode() }); } - - public getSettings({ - baseCurrency = DEFAULT_CURRENCY, - locale = DEFAULT_LOCALE, - xRayRules - }: UserSettings): Settings { - return { - baseCurrency, - locale, - isActive: xRayRules?.[this.getKey()]?.isActive ?? true, - thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.22, - thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0.18 - }; - } } interface Settings extends RuleSettings { 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 588c50ca11..6044c0a250 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 @@ -1,12 +1,7 @@ import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; -import { - PortfolioPosition, - RuleSettings, - UserSettings -} from '@ghostfolio/common/interfaces'; +import { PortfolioPosition, RuleSettings } from '@ghostfolio/common/interfaces'; export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule { private holdings: PortfolioPosition[]; @@ -18,8 +13,8 @@ export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule { private holdings: PortfolioPosition[]; @@ -18,8 +13,8 @@ export class CurrencyClusterRiskCurrentInvestment extends Rule { languageCode: string ) { super(exchangeRateDataService, { - key: CurrencyClusterRiskCurrentInvestment.name, - languageCode + languageCode, + key: 'CurrencyClusterRiskCurrentInvestment' }); this.holdings = holdings; @@ -94,19 +89,6 @@ export class CurrencyClusterRiskCurrentInvestment extends Rule { languageCode: this.getLanguageCode() }); } - - public getSettings({ - baseCurrency = DEFAULT_CURRENCY, - locale = DEFAULT_LOCALE, - xRayRules - }: UserSettings): Settings { - return { - baseCurrency, - locale, - isActive: xRayRules?.[this.getKey()]?.isActive ?? true, - thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.5 - }; - } } interface Settings extends RuleSettings { diff --git a/apps/api/src/models/rules/economic-market-cluster-risk/developed-markets.ts b/apps/api/src/models/rules/economic-market-cluster-risk/developed-markets.ts index c3b8e618c9..769d56c4a6 100644 --- a/apps/api/src/models/rules/economic-market-cluster-risk/developed-markets.ts +++ b/apps/api/src/models/rules/economic-market-cluster-risk/developed-markets.ts @@ -1,8 +1,7 @@ import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; -import { RuleSettings, UserSettings } from '@ghostfolio/common/interfaces'; +import { RuleSettings } from '@ghostfolio/common/interfaces'; export class EconomicMarketClusterRiskDevelopedMarkets extends Rule { private currentValueInBaseCurrency: number; @@ -17,7 +16,7 @@ export class EconomicMarketClusterRiskDevelopedMarkets extends Rule { ) { super(exchangeRateDataService, { languageCode, - key: EconomicMarketClusterRiskDevelopedMarkets.name + key: 'EconomicMarketClusterRiskDevelopedMarkets' }); this.currentValueInBaseCurrency = currentValueInBaseCurrency; @@ -96,20 +95,6 @@ export class EconomicMarketClusterRiskDevelopedMarkets extends Rule { languageCode: this.getLanguageCode() }); } - - public getSettings({ - baseCurrency = DEFAULT_CURRENCY, - locale = DEFAULT_LOCALE, - xRayRules - }: UserSettings): Settings { - return { - baseCurrency, - locale, - isActive: xRayRules?.[this.getKey()]?.isActive ?? true, - thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.72, - thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0.68 - }; - } } interface Settings extends RuleSettings { diff --git a/apps/api/src/models/rules/economic-market-cluster-risk/emerging-markets.ts b/apps/api/src/models/rules/economic-market-cluster-risk/emerging-markets.ts index ad91bc554c..0a15f12ec2 100644 --- a/apps/api/src/models/rules/economic-market-cluster-risk/emerging-markets.ts +++ b/apps/api/src/models/rules/economic-market-cluster-risk/emerging-markets.ts @@ -1,8 +1,7 @@ import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; -import { RuleSettings, UserSettings } from '@ghostfolio/common/interfaces'; +import { RuleSettings } from '@ghostfolio/common/interfaces'; export class EconomicMarketClusterRiskEmergingMarkets extends Rule { private currentValueInBaseCurrency: number; @@ -17,7 +16,7 @@ export class EconomicMarketClusterRiskEmergingMarkets extends Rule { ) { super(exchangeRateDataService, { languageCode, - key: EconomicMarketClusterRiskEmergingMarkets.name + key: 'EconomicMarketClusterRiskEmergingMarkets' }); this.currentValueInBaseCurrency = currentValueInBaseCurrency; @@ -96,20 +95,6 @@ export class EconomicMarketClusterRiskEmergingMarkets extends Rule { languageCode: this.getLanguageCode() }); } - - public getSettings({ - baseCurrency = DEFAULT_CURRENCY, - locale = DEFAULT_LOCALE, - xRayRules - }: UserSettings): Settings { - return { - baseCurrency, - locale, - isActive: xRayRules?.[this.getKey()]?.isActive ?? true, - thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.32, - thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0.28 - }; - } } interface Settings extends RuleSettings { diff --git a/apps/api/src/models/rules/emergency-fund/emergency-fund-coverage.ts b/apps/api/src/models/rules/emergency-fund/emergency-fund-coverage.ts index 7f9bbba09b..c3513284f6 100644 --- a/apps/api/src/models/rules/emergency-fund/emergency-fund-coverage.ts +++ b/apps/api/src/models/rules/emergency-fund/emergency-fund-coverage.ts @@ -1,8 +1,7 @@ import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; -import { RuleSettings, UserSettings } from '@ghostfolio/common/interfaces'; +import { RuleSettings } from '@ghostfolio/common/interfaces'; import { Big } from 'big.js'; @@ -17,7 +16,7 @@ export class EmergencyFundCoverage extends Rule { ) { super(exchangeRateDataService, { languageCode, - key: EmergencyFundCoverage.name + key: 'EmergencyFundCoverage' }); } @@ -91,18 +90,6 @@ export class EmergencyFundCoverage extends Rule { languageCode: this.getLanguageCode() }); } - - public getSettings({ - baseCurrency = DEFAULT_CURRENCY, - locale = DEFAULT_LOCALE, - xRayRules - }: UserSettings): Settings { - return { - baseCurrency, - locale, - isActive: xRayRules?.[this.getKey()]?.isActive ?? true - }; - } } interface Settings extends RuleSettings { diff --git a/apps/api/src/models/rules/emergency-fund/emergency-fund-setup.ts b/apps/api/src/models/rules/emergency-fund/emergency-fund-setup.ts index 1efd224efb..5cc9bff1ec 100644 --- a/apps/api/src/models/rules/emergency-fund/emergency-fund-setup.ts +++ b/apps/api/src/models/rules/emergency-fund/emergency-fund-setup.ts @@ -1,8 +1,7 @@ import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; -import { RuleSettings, UserSettings } from '@ghostfolio/common/interfaces'; +import { RuleSettings } from '@ghostfolio/common/interfaces'; export class EmergencyFundSetup extends Rule { private emergencyFund: number; @@ -15,7 +14,7 @@ export class EmergencyFundSetup extends Rule { ) { super(exchangeRateDataService, { languageCode, - key: EmergencyFundSetup.name + key: 'EmergencyFundSetup' }); this.emergencyFund = emergencyFund; @@ -51,18 +50,6 @@ export class EmergencyFundSetup extends Rule { languageCode: this.getLanguageCode() }); } - - public getSettings({ - baseCurrency = DEFAULT_CURRENCY, - locale = DEFAULT_LOCALE, - xRayRules - }: UserSettings): Settings { - return { - baseCurrency, - locale, - isActive: xRayRules?.[this.getKey()]?.isActive ?? true - }; - } } interface Settings extends RuleSettings { diff --git a/apps/api/src/models/rules/fees/fee-ratio-total-investment-volume.ts b/apps/api/src/models/rules/fees/fee-ratio-total-investment-volume.ts index 1786c2f8e5..49c36ec461 100644 --- a/apps/api/src/models/rules/fees/fee-ratio-total-investment-volume.ts +++ b/apps/api/src/models/rules/fees/fee-ratio-total-investment-volume.ts @@ -1,8 +1,7 @@ import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; -import { RuleSettings, UserSettings } from '@ghostfolio/common/interfaces'; +import { RuleSettings } from '@ghostfolio/common/interfaces'; export class FeeRatioTotalInvestmentVolume extends Rule { private fees: number; @@ -17,7 +16,7 @@ export class FeeRatioTotalInvestmentVolume extends Rule { ) { super(exchangeRateDataService, { languageCode, - key: FeeRatioTotalInvestmentVolume.name + key: 'FeeRatioTotalInvestmentVolume' }); this.fees = fees; @@ -75,19 +74,6 @@ export class FeeRatioTotalInvestmentVolume extends Rule { languageCode: this.getLanguageCode() }); } - - public getSettings({ - baseCurrency = DEFAULT_CURRENCY, - locale = DEFAULT_LOCALE, - xRayRules - }: UserSettings): Settings { - return { - baseCurrency, - locale, - isActive: xRayRules?.[this.getKey()]?.isActive ?? true, - thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.01 - }; - } } interface Settings extends RuleSettings { diff --git a/apps/api/src/models/rules/liquidity/buying-power.ts b/apps/api/src/models/rules/liquidity/buying-power.ts index 4017516cff..832f58f0b7 100644 --- a/apps/api/src/models/rules/liquidity/buying-power.ts +++ b/apps/api/src/models/rules/liquidity/buying-power.ts @@ -1,8 +1,7 @@ import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; -import { RuleSettings, UserSettings } from '@ghostfolio/common/interfaces'; +import { RuleSettings } from '@ghostfolio/common/interfaces'; export class BuyingPower extends Rule { private buyingPower: number; @@ -15,7 +14,7 @@ export class BuyingPower extends Rule { ) { super(exchangeRateDataService, { languageCode, - key: BuyingPower.name + key: 'BuyingPower' }); this.buyingPower = buyingPower; @@ -82,19 +81,6 @@ export class BuyingPower extends Rule { languageCode: this.getLanguageCode() }); } - - public getSettings({ - baseCurrency = DEFAULT_CURRENCY, - locale = DEFAULT_LOCALE, - xRayRules - }: UserSettings): Settings { - return { - baseCurrency, - locale, - isActive: xRayRules?.[this.getKey()]?.isActive ?? true, - thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0 - }; - } } interface Settings extends RuleSettings { diff --git a/apps/api/src/models/rules/regional-market-cluster-risk/asia-pacific.ts b/apps/api/src/models/rules/regional-market-cluster-risk/asia-pacific.ts index b244641aeb..8e821634e9 100644 --- a/apps/api/src/models/rules/regional-market-cluster-risk/asia-pacific.ts +++ b/apps/api/src/models/rules/regional-market-cluster-risk/asia-pacific.ts @@ -1,8 +1,6 @@ import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; -import { UserSettings } from '@ghostfolio/common/interfaces'; import { Settings } from './interfaces/rule-settings.interface'; @@ -19,7 +17,7 @@ export class RegionalMarketClusterRiskAsiaPacific extends Rule { ) { super(exchangeRateDataService, { languageCode, - key: RegionalMarketClusterRiskAsiaPacific.name + key: 'RegionalMarketClusterRiskAsiaPacific' }); this.asiaPacificValueInBaseCurrency = asiaPacificValueInBaseCurrency; @@ -90,18 +88,4 @@ export class RegionalMarketClusterRiskAsiaPacific extends Rule { languageCode: this.getLanguageCode() }); } - - public getSettings({ - baseCurrency = DEFAULT_CURRENCY, - locale = DEFAULT_LOCALE, - xRayRules - }: UserSettings): Settings { - return { - baseCurrency, - locale, - isActive: xRayRules?.[this.getKey()]?.isActive ?? true, - thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.03, - thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0.02 - }; - } } diff --git a/apps/api/src/models/rules/regional-market-cluster-risk/emerging-markets.ts b/apps/api/src/models/rules/regional-market-cluster-risk/emerging-markets.ts index eb7ed7815f..d923aed18e 100644 --- a/apps/api/src/models/rules/regional-market-cluster-risk/emerging-markets.ts +++ b/apps/api/src/models/rules/regional-market-cluster-risk/emerging-markets.ts @@ -1,8 +1,6 @@ import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; -import { UserSettings } from '@ghostfolio/common/interfaces'; import { Settings } from './interfaces/rule-settings.interface'; @@ -19,7 +17,7 @@ export class RegionalMarketClusterRiskEmergingMarkets extends Rule { ) { super(exchangeRateDataService, { languageCode, - key: RegionalMarketClusterRiskEmergingMarkets.name + key: 'RegionalMarketClusterRiskEmergingMarkets' }); this.currentValueInBaseCurrency = currentValueInBaseCurrency; @@ -92,18 +90,4 @@ export class RegionalMarketClusterRiskEmergingMarkets extends Rule { languageCode: this.getLanguageCode() }); } - - public getSettings({ - baseCurrency = DEFAULT_CURRENCY, - locale = DEFAULT_LOCALE, - xRayRules - }: UserSettings): Settings { - return { - baseCurrency, - locale, - isActive: xRayRules?.[this.getKey()]?.isActive ?? true, - thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.12, - thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0.08 - }; - } } diff --git a/apps/api/src/models/rules/regional-market-cluster-risk/europe.ts b/apps/api/src/models/rules/regional-market-cluster-risk/europe.ts index 24e0825a3f..6b02c422fd 100644 --- a/apps/api/src/models/rules/regional-market-cluster-risk/europe.ts +++ b/apps/api/src/models/rules/regional-market-cluster-risk/europe.ts @@ -1,8 +1,6 @@ import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; -import { UserSettings } from '@ghostfolio/common/interfaces'; import { Settings } from './interfaces/rule-settings.interface'; @@ -19,7 +17,7 @@ export class RegionalMarketClusterRiskEurope extends Rule { ) { super(exchangeRateDataService, { languageCode, - key: RegionalMarketClusterRiskEurope.name + key: 'RegionalMarketClusterRiskEurope' }); this.currentValueInBaseCurrency = currentValueInBaseCurrency; @@ -90,18 +88,4 @@ export class RegionalMarketClusterRiskEurope extends Rule { languageCode: this.getLanguageCode() }); } - - public getSettings({ - baseCurrency = DEFAULT_CURRENCY, - locale = DEFAULT_LOCALE, - xRayRules - }: UserSettings): Settings { - return { - baseCurrency, - locale, - isActive: xRayRules?.[this.getKey()]?.isActive ?? true, - thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.15, - thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0.11 - }; - } } 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 index 301cebf23c..4e7262a84c 100644 --- a/apps/api/src/models/rules/regional-market-cluster-risk/japan.ts +++ b/apps/api/src/models/rules/regional-market-cluster-risk/japan.ts @@ -1,8 +1,6 @@ import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; -import { UserSettings } from '@ghostfolio/common/interfaces'; import { Settings } from './interfaces/rule-settings.interface'; @@ -19,7 +17,7 @@ export class RegionalMarketClusterRiskJapan extends Rule { ) { super(exchangeRateDataService, { languageCode, - key: RegionalMarketClusterRiskJapan.name + key: 'RegionalMarketClusterRiskJapan' }); this.currentValueInBaseCurrency = currentValueInBaseCurrency; @@ -90,18 +88,4 @@ export class RegionalMarketClusterRiskJapan extends Rule { languageCode: this.getLanguageCode() }); } - - public getSettings({ - baseCurrency = DEFAULT_CURRENCY, - locale = DEFAULT_LOCALE, - xRayRules - }: UserSettings): Settings { - return { - baseCurrency, - locale, - isActive: xRayRules?.[this.getKey()]?.isActive ?? true, - thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.06, - thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0.04 - }; - } } 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 index eda446f9aa..3fd66a927c 100644 --- 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 @@ -1,8 +1,6 @@ import { Rule } from '@ghostfolio/api/models/rule'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; -import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; -import { UserSettings } from '@ghostfolio/common/interfaces'; import { Settings } from './interfaces/rule-settings.interface'; @@ -19,7 +17,7 @@ export class RegionalMarketClusterRiskNorthAmerica extends Rule { ) { super(exchangeRateDataService, { languageCode, - key: RegionalMarketClusterRiskNorthAmerica.name + key: 'RegionalMarketClusterRiskNorthAmerica' }); this.currentValueInBaseCurrency = currentValueInBaseCurrency; @@ -90,18 +88,4 @@ export class RegionalMarketClusterRiskNorthAmerica extends Rule { languageCode: this.getLanguageCode() }); } - - public getSettings({ - baseCurrency = DEFAULT_CURRENCY, - locale = DEFAULT_LOCALE, - xRayRules - }: UserSettings): Settings { - return { - baseCurrency, - locale, - isActive: xRayRules?.[this.getKey()]?.isActive ?? true, - thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.69, - thresholdMin: xRayRules?.[this.getKey()]?.thresholdMin ?? 0.65 - }; - } } diff --git a/apps/api/src/models/rules/rule-settings.spec.ts b/apps/api/src/models/rules/rule-settings.spec.ts new file mode 100644 index 0000000000..8b49f4b4fe --- /dev/null +++ b/apps/api/src/models/rules/rule-settings.spec.ts @@ -0,0 +1,129 @@ +import { getRuleSettings, getXRayRulesSettings } from './rule-settings'; + +describe('Rule settings', () => { + it('gets default settings for a rule', () => { + expect( + getRuleSettings({ + key: 'AssetClassClusterRiskEquity', + userSettings: {} + }) + ).toEqual({ + baseCurrency: 'USD', + isActive: true, + locale: 'en-US', + thresholdMax: 0.82, + thresholdMin: 0.78 + }); + }); + + it('gets a default threshold of zero for a rule', () => { + expect( + getRuleSettings({ + key: 'BuyingPower', + userSettings: {} + }) + ).toEqual({ + baseCurrency: 'USD', + isActive: true, + locale: 'en-US', + thresholdMin: 0 + }); + }); + + it('applies the configured settings for a rule', () => { + expect( + getRuleSettings({ + key: 'AssetClassClusterRiskEquity', + userSettings: { + baseCurrency: 'CHF', + locale: 'de-CH', + xRayRules: { + AssetClassClusterRiskEquity: { + isActive: false, + thresholdMax: 0.9, + thresholdMin: 0.7 + } + } + } + }) + ).toEqual({ + baseCurrency: 'CHF', + isActive: false, + locale: 'de-CH', + thresholdMax: 0.9, + thresholdMin: 0.7 + }); + }); + + it('applies the default for a threshold which is not configured', () => { + expect( + getRuleSettings({ + key: 'AssetClassClusterRiskEquity', + userSettings: { + xRayRules: { + AssetClassClusterRiskEquity: { isActive: true, thresholdMax: 0.9 } + } + } + }) + ).toEqual({ + baseCurrency: 'USD', + isActive: true, + locale: 'en-US', + thresholdMax: 0.9, + thresholdMin: 0.78 + }); + }); + + it('applies a configured threshold of zero', () => { + expect( + getRuleSettings({ + key: 'AssetClassClusterRiskEquity', + userSettings: { + xRayRules: { + AssetClassClusterRiskEquity: { isActive: true, thresholdMin: 0 } + } + } + }) + ).toEqual({ + baseCurrency: 'USD', + isActive: true, + locale: 'en-US', + thresholdMax: 0.82, + thresholdMin: 0 + }); + }); + + it('omits the base currency when it is not used by a rule', () => { + expect( + getRuleSettings({ + key: 'AccountClusterRiskSingleAccount', + userSettings: {} + }) + ).toEqual({ + isActive: true, + locale: 'en-US' + }); + }); + + it('gets settings for all rules', () => { + expect(Object.keys(getXRayRulesSettings({})).sort()).toEqual([ + 'AccountClusterRiskCurrentInvestment', + 'AccountClusterRiskSingleAccount', + 'AssetClassClusterRiskEquity', + 'AssetClassClusterRiskFixedIncome', + 'BuyingPower', + 'CurrencyClusterRiskBaseCurrencyCurrentInvestment', + 'CurrencyClusterRiskCurrentInvestment', + 'EconomicMarketClusterRiskDevelopedMarkets', + 'EconomicMarketClusterRiskEmergingMarkets', + 'EmergencyFundCoverage', + 'EmergencyFundSetup', + 'FeeRatioTotalInvestmentVolume', + 'RegionalMarketClusterRiskAsiaPacific', + 'RegionalMarketClusterRiskEmergingMarkets', + 'RegionalMarketClusterRiskEurope', + 'RegionalMarketClusterRiskJapan', + 'RegionalMarketClusterRiskNorthAmerica' + ]); + }); +}); diff --git a/apps/api/src/models/rules/rule-settings.ts b/apps/api/src/models/rules/rule-settings.ts new file mode 100644 index 0000000000..de0fb20425 --- /dev/null +++ b/apps/api/src/models/rules/rule-settings.ts @@ -0,0 +1,98 @@ +import { RuleSettingsConfiguration } from '@ghostfolio/api/models/interfaces/rule-settings-configuration.interface'; +import { XRayRuleKey } from '@ghostfolio/api/models/types/x-ray-rule-key.type'; +import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; +import { + RuleSettings, + UserSettings, + XRayRulesSettings +} from '@ghostfolio/common/interfaces'; + +const RULE_SETTINGS_CONFIGURATIONS = { + AccountClusterRiskCurrentInvestment: { thresholdMax: 0.5 }, + AccountClusterRiskSingleAccount: { withBaseCurrency: false }, + AssetClassClusterRiskEquity: { + thresholdMax: 0.82, + thresholdMin: 0.78 + }, + AssetClassClusterRiskFixedIncome: { + thresholdMax: 0.22, + thresholdMin: 0.18 + }, + BuyingPower: { thresholdMin: 0 }, + CurrencyClusterRiskBaseCurrencyCurrentInvestment: {}, + CurrencyClusterRiskCurrentInvestment: { thresholdMax: 0.5 }, + EconomicMarketClusterRiskDevelopedMarkets: { + thresholdMax: 0.72, + thresholdMin: 0.68 + }, + EconomicMarketClusterRiskEmergingMarkets: { + thresholdMax: 0.32, + thresholdMin: 0.28 + }, + EmergencyFundCoverage: {}, + EmergencyFundSetup: {}, + FeeRatioTotalInvestmentVolume: { thresholdMax: 0.01 }, + RegionalMarketClusterRiskAsiaPacific: { + thresholdMax: 0.03, + thresholdMin: 0.02 + }, + RegionalMarketClusterRiskEmergingMarkets: { + thresholdMax: 0.12, + thresholdMin: 0.08 + }, + RegionalMarketClusterRiskEurope: { + thresholdMax: 0.15, + thresholdMin: 0.11 + }, + RegionalMarketClusterRiskJapan: { + thresholdMax: 0.06, + thresholdMin: 0.04 + }, + RegionalMarketClusterRiskNorthAmerica: { + thresholdMax: 0.69, + thresholdMin: 0.65 + } +} as const satisfies Record; + +export function getRuleSettings({ + key, + userSettings +}: { + key: XRayRuleKey; + userSettings: UserSettings; +}): T { + const configuration: RuleSettingsConfiguration = + RULE_SETTINGS_CONFIGURATIONS[key] ?? {}; + const configuredSettings = userSettings.xRayRules?.[key]; + + return { + ...(configuration.withBaseCurrency === false + ? {} + : { baseCurrency: userSettings.baseCurrency ?? DEFAULT_CURRENCY }), + isActive: configuredSettings?.isActive ?? true, + locale: userSettings.locale ?? DEFAULT_LOCALE, + ...(configuration.thresholdMax === undefined + ? {} + : { + thresholdMax: + configuredSettings?.thresholdMax ?? configuration.thresholdMax + }), + ...(configuration.thresholdMin === undefined + ? {} + : { + thresholdMin: + configuredSettings?.thresholdMin ?? configuration.thresholdMin + }) + } as T; +} + +export function getXRayRulesSettings( + userSettings: UserSettings +): XRayRulesSettings { + return Object.fromEntries( + (Object.keys(RULE_SETTINGS_CONFIGURATIONS) as XRayRuleKey[]).map((key) => [ + key, + getRuleSettings({ key, userSettings }) + ]) + ) as XRayRulesSettings; +} diff --git a/apps/api/src/models/types/x-ray-rule-key.type.ts b/apps/api/src/models/types/x-ray-rule-key.type.ts new file mode 100644 index 0000000000..1d7c97bec1 --- /dev/null +++ b/apps/api/src/models/types/x-ray-rule-key.type.ts @@ -0,0 +1,3 @@ +import { XRayRulesSettings } from '@ghostfolio/common/interfaces'; + +export type XRayRuleKey = keyof XRayRulesSettings;