Browse Source

Task/centralize X-ray rule settings (#7887)

Centralize X-ray rule settings
pull/7908/head
Thomas Kaul 1 week ago
committed by GitHub
parent
commit
2f30b11cff
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 69
      apps/api/src/app/portfolio/portfolio.service.ts
  2. 8
      apps/api/src/app/portfolio/rules.service.ts
  3. 143
      apps/api/src/app/user/user.service.ts
  4. 5
      apps/api/src/models/interfaces/rule-settings-configuration.interface.ts
  5. 4
      apps/api/src/models/interfaces/rule.interface.ts
  6. 15
      apps/api/src/models/rule.ts
  7. 22
      apps/api/src/models/rules/account-cluster-risk/current-investment.ts
  8. 19
      apps/api/src/models/rules/account-cluster-risk/single-account.ts
  9. 23
      apps/api/src/models/rules/asset-class-cluster-risk/equity.ts
  10. 23
      apps/api/src/models/rules/asset-class-cluster-risk/fixed-income.ts
  11. 23
      apps/api/src/models/rules/currency-cluster-risk/base-currency-current-investment.ts
  12. 24
      apps/api/src/models/rules/currency-cluster-risk/current-investment.ts
  13. 19
      apps/api/src/models/rules/economic-market-cluster-risk/developed-markets.ts
  14. 19
      apps/api/src/models/rules/economic-market-cluster-risk/emerging-markets.ts
  15. 17
      apps/api/src/models/rules/emergency-fund/emergency-fund-coverage.ts
  16. 17
      apps/api/src/models/rules/emergency-fund/emergency-fund-setup.ts
  17. 18
      apps/api/src/models/rules/fees/fee-ratio-total-investment-volume.ts
  18. 18
      apps/api/src/models/rules/liquidity/buying-power.ts
  19. 18
      apps/api/src/models/rules/regional-market-cluster-risk/asia-pacific.ts
  20. 18
      apps/api/src/models/rules/regional-market-cluster-risk/emerging-markets.ts
  21. 18
      apps/api/src/models/rules/regional-market-cluster-risk/europe.ts
  22. 18
      apps/api/src/models/rules/regional-market-cluster-risk/japan.ts
  23. 18
      apps/api/src/models/rules/regional-market-cluster-risk/north-america.ts
  24. 129
      apps/api/src/models/rules/rule-settings.spec.ts
  25. 98
      apps/api/src/models/rules/rule-settings.ts
  26. 3
      apps/api/src/models/types/x-ray-rule-key.type.ts

69
apps/api/src/app/portfolio/portfolio.service.ts

@ -33,6 +33,7 @@ import {
import { import {
DEFAULT_CURRENCY, DEFAULT_CURRENCY,
DEFAULT_DATE_RANGE, DEFAULT_DATE_RANGE,
DEFAULT_LANGUAGE_CODE,
TAG_ID_DRAFT, TAG_ID_DRAFT,
TAG_ID_EMERGENCY_FUND, TAG_ID_EMERGENCY_FUND,
TAG_ID_EXCLUDE_FROM_ANALYSIS, TAG_ID_EXCLUDE_FROM_ANALYSIS,
@ -1288,6 +1289,8 @@ export class PortfolioService {
userSettings: this.request.user.settings.settings as UserSettings userSettings: this.request.user.settings.settings as UserSettings
}); });
const languageCode = userSettings.language ?? DEFAULT_LANGUAGE_CODE;
const { accounts, holdings, markets, marketsAdvanced, summary } = const { accounts, holdings, markets, marketsAdvanced, summary } =
await this.getDetails({ await this.getDetails({
user, user,
@ -1330,8 +1333,8 @@ export class PortfolioService {
{ {
key: 'liquidity', key: 'liquidity',
name: this.i18nService.getTranslation({ name: this.i18nService.getTranslation({
id: 'rule.liquidity.category', languageCode,
languageCode: userSettings.language id: 'rule.liquidity.category'
}), }),
rules: await this.rulesService.evaluate( rules: await this.rulesService.evaluate(
[ [
@ -1339,7 +1342,7 @@ export class PortfolioService {
this.exchangeRateDataService, this.exchangeRateDataService,
this.i18nService, this.i18nService,
summary.cash, summary.cash,
userSettings.language languageCode
) )
], ],
userSettings userSettings
@ -1348,15 +1351,15 @@ export class PortfolioService {
{ {
key: 'emergencyFund', key: 'emergencyFund',
name: this.i18nService.getTranslation({ name: this.i18nService.getTranslation({
id: 'rule.emergencyFund.category', languageCode,
languageCode: userSettings.language id: 'rule.emergencyFund.category'
}), }),
rules: await this.rulesService.evaluate( rules: await this.rulesService.evaluate(
[ [
new EmergencyFundSetup( new EmergencyFundSetup(
this.exchangeRateDataService, this.exchangeRateDataService,
this.i18nService, this.i18nService,
userSettings.language, languageCode,
totalEmergencyFundInBaseCurrency totalEmergencyFundInBaseCurrency
), ),
// The coverage is only meaningful once an emergency fund has been // The coverage is only meaningful once an emergency fund has been
@ -1366,7 +1369,7 @@ export class PortfolioService {
new EmergencyFundCoverage( new EmergencyFundCoverage(
this.exchangeRateDataService, this.exchangeRateDataService,
this.i18nService, this.i18nService,
userSettings.language, languageCode,
emergencyFundInBaseCurrency, emergencyFundInBaseCurrency,
emergencyFundHoldingsValueInBaseCurrency, emergencyFundHoldingsValueInBaseCurrency,
cashBalanceInBaseCurrency cashBalanceInBaseCurrency
@ -1380,8 +1383,8 @@ export class PortfolioService {
{ {
key: 'currencyClusterRisk', key: 'currencyClusterRisk',
name: this.i18nService.getTranslation({ name: this.i18nService.getTranslation({
id: 'rule.currencyClusterRisk.category', languageCode,
languageCode: userSettings.language id: 'rule.currencyClusterRisk.category'
}), }),
rules: hasOpenHoldings rules: hasOpenHoldings
? await this.rulesService.evaluate( ? await this.rulesService.evaluate(
@ -1390,13 +1393,13 @@ export class PortfolioService {
this.exchangeRateDataService, this.exchangeRateDataService,
this.i18nService, this.i18nService,
holdings, holdings,
userSettings.language languageCode
), ),
new CurrencyClusterRiskCurrentInvestment( new CurrencyClusterRiskCurrentInvestment(
this.exchangeRateDataService, this.exchangeRateDataService,
this.i18nService, this.i18nService,
holdings, holdings,
userSettings.language languageCode
) )
], ],
userSettings userSettings
@ -1406,8 +1409,8 @@ export class PortfolioService {
{ {
key: 'assetClassClusterRisk', key: 'assetClassClusterRisk',
name: this.i18nService.getTranslation({ name: this.i18nService.getTranslation({
id: 'rule.assetClassClusterRisk.category', languageCode,
languageCode: userSettings.language id: 'rule.assetClassClusterRisk.category'
}), }),
rules: hasOpenHoldings rules: hasOpenHoldings
? await this.rulesService.evaluate( ? await this.rulesService.evaluate(
@ -1415,13 +1418,13 @@ export class PortfolioService {
new AssetClassClusterRiskEquity( new AssetClassClusterRiskEquity(
this.exchangeRateDataService, this.exchangeRateDataService,
this.i18nService, this.i18nService,
userSettings.language, languageCode,
holdings holdings
), ),
new AssetClassClusterRiskFixedIncome( new AssetClassClusterRiskFixedIncome(
this.exchangeRateDataService, this.exchangeRateDataService,
this.i18nService, this.i18nService,
userSettings.language, languageCode,
holdings holdings
) )
], ],
@ -1432,8 +1435,8 @@ export class PortfolioService {
{ {
key: 'accountClusterRisk', key: 'accountClusterRisk',
name: this.i18nService.getTranslation({ name: this.i18nService.getTranslation({
id: 'rule.accountClusterRisk.category', languageCode,
languageCode: userSettings.language id: 'rule.accountClusterRisk.category'
}), }),
rules: rules:
summary.activityCount > 0 summary.activityCount > 0
@ -1442,13 +1445,13 @@ export class PortfolioService {
new AccountClusterRiskCurrentInvestment( new AccountClusterRiskCurrentInvestment(
this.exchangeRateDataService, this.exchangeRateDataService,
this.i18nService, this.i18nService,
userSettings.language, languageCode,
accounts accounts
), ),
new AccountClusterRiskSingleAccount( new AccountClusterRiskSingleAccount(
this.exchangeRateDataService, this.exchangeRateDataService,
this.i18nService, this.i18nService,
userSettings.language, languageCode,
accounts accounts
) )
], ],
@ -1459,8 +1462,8 @@ export class PortfolioService {
{ {
key: 'economicMarketClusterRisk', key: 'economicMarketClusterRisk',
name: this.i18nService.getTranslation({ name: this.i18nService.getTranslation({
id: 'rule.economicMarketClusterRisk.category', languageCode,
languageCode: userSettings.language id: 'rule.economicMarketClusterRisk.category'
}), }),
rules: hasOpenHoldings rules: hasOpenHoldings
? await this.rulesService.evaluate( ? await this.rulesService.evaluate(
@ -1470,14 +1473,14 @@ export class PortfolioService {
this.i18nService, this.i18nService,
marketsTotalInBaseCurrency, marketsTotalInBaseCurrency,
markets.developedMarkets.valueInBaseCurrency, markets.developedMarkets.valueInBaseCurrency,
userSettings.language languageCode
), ),
new EconomicMarketClusterRiskEmergingMarkets( new EconomicMarketClusterRiskEmergingMarkets(
this.exchangeRateDataService, this.exchangeRateDataService,
this.i18nService, this.i18nService,
marketsTotalInBaseCurrency, marketsTotalInBaseCurrency,
markets.emergingMarkets.valueInBaseCurrency, markets.emergingMarkets.valueInBaseCurrency,
userSettings.language languageCode
) )
], ],
userSettings userSettings
@ -1487,8 +1490,8 @@ export class PortfolioService {
{ {
key: 'regionalMarketClusterRisk', key: 'regionalMarketClusterRisk',
name: this.i18nService.getTranslation({ name: this.i18nService.getTranslation({
id: 'rule.regionalMarketClusterRisk.category', languageCode,
languageCode: userSettings.language id: 'rule.regionalMarketClusterRisk.category'
}), }),
rules: hasOpenHoldings rules: hasOpenHoldings
? await this.rulesService.evaluate( ? await this.rulesService.evaluate(
@ -1496,35 +1499,35 @@ export class PortfolioService {
new RegionalMarketClusterRiskAsiaPacific( new RegionalMarketClusterRiskAsiaPacific(
this.exchangeRateDataService, this.exchangeRateDataService,
this.i18nService, this.i18nService,
userSettings.language, languageCode,
marketsAdvancedTotalInBaseCurrency, marketsAdvancedTotalInBaseCurrency,
marketsAdvanced.asiaPacific.valueInBaseCurrency marketsAdvanced.asiaPacific.valueInBaseCurrency
), ),
new RegionalMarketClusterRiskEmergingMarkets( new RegionalMarketClusterRiskEmergingMarkets(
this.exchangeRateDataService, this.exchangeRateDataService,
this.i18nService, this.i18nService,
userSettings.language, languageCode,
marketsAdvancedTotalInBaseCurrency, marketsAdvancedTotalInBaseCurrency,
marketsAdvanced.emergingMarkets.valueInBaseCurrency marketsAdvanced.emergingMarkets.valueInBaseCurrency
), ),
new RegionalMarketClusterRiskEurope( new RegionalMarketClusterRiskEurope(
this.exchangeRateDataService, this.exchangeRateDataService,
this.i18nService, this.i18nService,
userSettings.language, languageCode,
marketsAdvancedTotalInBaseCurrency, marketsAdvancedTotalInBaseCurrency,
marketsAdvanced.europe.valueInBaseCurrency marketsAdvanced.europe.valueInBaseCurrency
), ),
new RegionalMarketClusterRiskJapan( new RegionalMarketClusterRiskJapan(
this.exchangeRateDataService, this.exchangeRateDataService,
this.i18nService, this.i18nService,
userSettings.language, languageCode,
marketsAdvancedTotalInBaseCurrency, marketsAdvancedTotalInBaseCurrency,
marketsAdvanced.japan.valueInBaseCurrency marketsAdvanced.japan.valueInBaseCurrency
), ),
new RegionalMarketClusterRiskNorthAmerica( new RegionalMarketClusterRiskNorthAmerica(
this.exchangeRateDataService, this.exchangeRateDataService,
this.i18nService, this.i18nService,
userSettings.language, languageCode,
marketsAdvancedTotalInBaseCurrency, marketsAdvancedTotalInBaseCurrency,
marketsAdvanced.northAmerica.valueInBaseCurrency marketsAdvanced.northAmerica.valueInBaseCurrency
) )
@ -1536,15 +1539,15 @@ export class PortfolioService {
{ {
key: 'fees', key: 'fees',
name: this.i18nService.getTranslation({ name: this.i18nService.getTranslation({
id: 'rule.fees.category', languageCode,
languageCode: userSettings.language id: 'rule.fees.category'
}), }),
rules: await this.rulesService.evaluate( rules: await this.rulesService.evaluate(
[ [
new FeeRatioTotalInvestmentVolume( new FeeRatioTotalInvestmentVolume(
this.exchangeRateDataService, this.exchangeRateDataService,
this.i18nService, this.i18nService,
userSettings.language, languageCode,
summary.totalBuy + summary.totalSell, summary.totalBuy + summary.totalSell,
summary.fees summary.fees
) )

8
apps/api/src/app/portfolio/rules.service.ts

@ -1,4 +1,5 @@
import { Rule } from '@ghostfolio/api/models/rule'; import { Rule } from '@ghostfolio/api/models/rule';
import { getRuleSettings } from '@ghostfolio/api/models/rules/rule-settings';
import { import {
PortfolioReportRule, PortfolioReportRule,
RuleSettings, RuleSettings,
@ -14,9 +15,12 @@ export class RulesService {
aUserSettings: UserSettings aUserSettings: UserSettings
): Promise<PortfolioReportRule[]> { ): Promise<PortfolioReportRule[]> {
return aRules.map((rule) => { return aRules.map((rule) => {
const settings = rule.getSettings(aUserSettings); const settings = getRuleSettings<T>({
key: rule.getKey(),
userSettings: aUserSettings
});
if (settings?.isActive) { if (settings.isActive) {
const { evaluation, value } = rule.evaluate(settings); const { evaluation, value } = rule.evaluate(settings);
return { return {

143
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 { PortfolioChangedEvent } from '@ghostfolio/api/events/portfolio-changed.event';
import { getSupportedLanguageCode } from '@ghostfolio/api/helper/language.helper'; import { getSupportedLanguageCode } from '@ghostfolio/api/helper/language.helper';
import { getRandomString } from '@ghostfolio/api/helper/string.helper'; import { getRandomString } from '@ghostfolio/api/helper/string.helper';
import { AccountClusterRiskCurrentInvestment } from '@ghostfolio/api/models/rules/account-cluster-risk/current-investment'; import { getXRayRulesSettings } from '@ghostfolio/api/models/rules/rule-settings';
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 { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.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).viewMode = 'DEFAULT';
} }
(user.settings.settings as UserSettings).xRayRules = { (user.settings.settings as UserSettings).xRayRules = getXRayRulesSettings(
AccountClusterRiskCurrentInvestment: user.settings.settings
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)
};
let currentPermissions = getPermissions(user.role); let currentPermissions = getPermissions(user.role);

5
apps/api/src/models/interfaces/rule-settings-configuration.interface.ts

@ -0,0 +1,5 @@
export interface RuleSettingsConfiguration {
thresholdMax?: number;
thresholdMin?: number;
withBaseCurrency?: boolean;
}

4
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'; import { EvaluationResult } from './evaluation-result.interface';
export interface RuleInterface<T extends RuleSettings> { export interface RuleInterface<T extends RuleSettings> {
evaluate(aRuleSettings: T): EvaluationResult; evaluate(aRuleSettings: T): EvaluationResult;
getSettings(aUserSettings: UserSettings): T;
} }

15
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 { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config';
import { import {
PortfolioPosition, PortfolioPosition,
PortfolioReportRule, PortfolioReportRule,
RuleSettings, RuleSettings
UserSettings
} from '@ghostfolio/common/interfaces'; } from '@ghostfolio/common/interfaces';
import { Big } from 'big.js'; import { Big } from 'big.js';
@ -14,17 +13,17 @@ import { EvaluationResult } from './interfaces/evaluation-result.interface';
import { RuleInterface } from './interfaces/rule.interface'; import { RuleInterface } from './interfaces/rule.interface';
export abstract class Rule<T extends RuleSettings> implements RuleInterface<T> { export abstract class Rule<T extends RuleSettings> implements RuleInterface<T> {
private key: string; private key: XRayRuleKey;
private languageCode: string; private languageCode: string;
public constructor( public constructor(
protected exchangeRateDataService: ExchangeRateDataService, protected exchangeRateDataService: ExchangeRateDataService,
{ {
key, key,
languageCode = DEFAULT_LANGUAGE_CODE languageCode
}: { }: {
key: string; key: XRayRuleKey;
languageCode?: string; // TODO: Make mandatory languageCode: string;
} }
) { ) {
this.key = key; this.key = key;
@ -75,6 +74,4 @@ export abstract class Rule<T extends RuleSettings> implements RuleInterface<T> {
>; >;
public abstract getName(): string; public abstract getName(): string;
public abstract getSettings(aUserSettings: UserSettings): T;
} }

22
apps/api/src/models/rules/account-cluster-risk/current-investment.ts

@ -1,12 +1,7 @@
import { Rule } from '@ghostfolio/api/models/rule'; import { Rule } from '@ghostfolio/api/models/rule';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service';
import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; import { PortfolioDetails, RuleSettings } from '@ghostfolio/common/interfaces';
import {
PortfolioDetails,
RuleSettings,
UserSettings
} from '@ghostfolio/common/interfaces';
import { Account } from '@prisma/client'; import { Account } from '@prisma/client';
@ -21,7 +16,7 @@ export class AccountClusterRiskCurrentInvestment extends Rule<Settings> {
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
languageCode, languageCode,
key: AccountClusterRiskCurrentInvestment.name key: 'AccountClusterRiskCurrentInvestment'
}); });
this.accounts = accounts; this.accounts = accounts;
@ -118,19 +113,6 @@ export class AccountClusterRiskCurrentInvestment extends Rule<Settings> {
languageCode: this.getLanguageCode() 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 { interface Settings extends RuleSettings {

19
apps/api/src/models/rules/account-cluster-risk/single-account.ts

@ -1,12 +1,7 @@
import { Rule } from '@ghostfolio/api/models/rule'; import { Rule } from '@ghostfolio/api/models/rule';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service';
import { DEFAULT_LOCALE } from '@ghostfolio/common/config'; import { PortfolioDetails, RuleSettings } from '@ghostfolio/common/interfaces';
import {
PortfolioDetails,
RuleSettings,
UserSettings
} from '@ghostfolio/common/interfaces';
export class AccountClusterRiskSingleAccount extends Rule<RuleSettings> { export class AccountClusterRiskSingleAccount extends Rule<RuleSettings> {
private accounts: PortfolioDetails['accounts']; private accounts: PortfolioDetails['accounts'];
@ -19,7 +14,7 @@ export class AccountClusterRiskSingleAccount extends Rule<RuleSettings> {
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
languageCode, languageCode,
key: AccountClusterRiskSingleAccount.name key: 'AccountClusterRiskSingleAccount'
}); });
this.accounts = accounts; this.accounts = accounts;
@ -68,14 +63,4 @@ export class AccountClusterRiskSingleAccount extends Rule<RuleSettings> {
languageCode: this.getLanguageCode() languageCode: this.getLanguageCode()
}); });
} }
public getSettings({
locale = DEFAULT_LOCALE,
xRayRules
}: UserSettings): RuleSettings {
return {
locale,
isActive: xRayRules?.[this.getKey()]?.isActive ?? true
};
}
} }

23
apps/api/src/models/rules/asset-class-cluster-risk/equity.ts

@ -1,12 +1,7 @@
import { Rule } from '@ghostfolio/api/models/rule'; import { Rule } from '@ghostfolio/api/models/rule';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service';
import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; import { PortfolioPosition, RuleSettings } from '@ghostfolio/common/interfaces';
import {
PortfolioPosition,
RuleSettings,
UserSettings
} from '@ghostfolio/common/interfaces';
export class AssetClassClusterRiskEquity extends Rule<Settings> { export class AssetClassClusterRiskEquity extends Rule<Settings> {
private holdings: PortfolioPosition[]; private holdings: PortfolioPosition[];
@ -19,7 +14,7 @@ export class AssetClassClusterRiskEquity extends Rule<Settings> {
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
languageCode, languageCode,
key: AssetClassClusterRiskEquity.name key: 'AssetClassClusterRiskEquity'
}); });
this.holdings = holdings; this.holdings = holdings;
@ -106,20 +101,6 @@ export class AssetClassClusterRiskEquity extends Rule<Settings> {
languageCode: this.getLanguageCode() 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 { interface Settings extends RuleSettings {

23
apps/api/src/models/rules/asset-class-cluster-risk/fixed-income.ts

@ -1,12 +1,7 @@
import { Rule } from '@ghostfolio/api/models/rule'; import { Rule } from '@ghostfolio/api/models/rule';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service';
import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; import { PortfolioPosition, RuleSettings } from '@ghostfolio/common/interfaces';
import {
PortfolioPosition,
RuleSettings,
UserSettings
} from '@ghostfolio/common/interfaces';
export class AssetClassClusterRiskFixedIncome extends Rule<Settings> { export class AssetClassClusterRiskFixedIncome extends Rule<Settings> {
private holdings: PortfolioPosition[]; private holdings: PortfolioPosition[];
@ -19,7 +14,7 @@ export class AssetClassClusterRiskFixedIncome extends Rule<Settings> {
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
languageCode, languageCode,
key: AssetClassClusterRiskFixedIncome.name key: 'AssetClassClusterRiskFixedIncome'
}); });
this.holdings = holdings; this.holdings = holdings;
@ -106,20 +101,6 @@ export class AssetClassClusterRiskFixedIncome extends Rule<Settings> {
languageCode: this.getLanguageCode() 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 { interface Settings extends RuleSettings {

23
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 { Rule } from '@ghostfolio/api/models/rule';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service';
import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; import { PortfolioPosition, RuleSettings } from '@ghostfolio/common/interfaces';
import {
PortfolioPosition,
RuleSettings,
UserSettings
} from '@ghostfolio/common/interfaces';
export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule<Settings> { export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule<Settings> {
private holdings: PortfolioPosition[]; private holdings: PortfolioPosition[];
@ -18,8 +13,8 @@ export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule<Setti
languageCode: string languageCode: string
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
key: CurrencyClusterRiskBaseCurrencyCurrentInvestment.name, languageCode,
languageCode key: 'CurrencyClusterRiskBaseCurrencyCurrentInvestment'
}); });
this.holdings = holdings; this.holdings = holdings;
@ -93,18 +88,6 @@ export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule<Setti
languageCode: this.getLanguageCode() 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 { interface Settings extends RuleSettings {

24
apps/api/src/models/rules/currency-cluster-risk/current-investment.ts

@ -1,12 +1,7 @@
import { Rule } from '@ghostfolio/api/models/rule'; import { Rule } from '@ghostfolio/api/models/rule';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service';
import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; import { PortfolioPosition, RuleSettings } from '@ghostfolio/common/interfaces';
import {
PortfolioPosition,
RuleSettings,
UserSettings
} from '@ghostfolio/common/interfaces';
export class CurrencyClusterRiskCurrentInvestment extends Rule<Settings> { export class CurrencyClusterRiskCurrentInvestment extends Rule<Settings> {
private holdings: PortfolioPosition[]; private holdings: PortfolioPosition[];
@ -18,8 +13,8 @@ export class CurrencyClusterRiskCurrentInvestment extends Rule<Settings> {
languageCode: string languageCode: string
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
key: CurrencyClusterRiskCurrentInvestment.name, languageCode,
languageCode key: 'CurrencyClusterRiskCurrentInvestment'
}); });
this.holdings = holdings; this.holdings = holdings;
@ -94,19 +89,6 @@ export class CurrencyClusterRiskCurrentInvestment extends Rule<Settings> {
languageCode: this.getLanguageCode() 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 { interface Settings extends RuleSettings {

19
apps/api/src/models/rules/economic-market-cluster-risk/developed-markets.ts

@ -1,8 +1,7 @@
import { Rule } from '@ghostfolio/api/models/rule'; import { Rule } from '@ghostfolio/api/models/rule';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service';
import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; import { RuleSettings } from '@ghostfolio/common/interfaces';
import { RuleSettings, UserSettings } from '@ghostfolio/common/interfaces';
export class EconomicMarketClusterRiskDevelopedMarkets extends Rule<Settings> { export class EconomicMarketClusterRiskDevelopedMarkets extends Rule<Settings> {
private currentValueInBaseCurrency: number; private currentValueInBaseCurrency: number;
@ -17,7 +16,7 @@ export class EconomicMarketClusterRiskDevelopedMarkets extends Rule<Settings> {
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
languageCode, languageCode,
key: EconomicMarketClusterRiskDevelopedMarkets.name key: 'EconomicMarketClusterRiskDevelopedMarkets'
}); });
this.currentValueInBaseCurrency = currentValueInBaseCurrency; this.currentValueInBaseCurrency = currentValueInBaseCurrency;
@ -96,20 +95,6 @@ export class EconomicMarketClusterRiskDevelopedMarkets extends Rule<Settings> {
languageCode: this.getLanguageCode() 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 { interface Settings extends RuleSettings {

19
apps/api/src/models/rules/economic-market-cluster-risk/emerging-markets.ts

@ -1,8 +1,7 @@
import { Rule } from '@ghostfolio/api/models/rule'; import { Rule } from '@ghostfolio/api/models/rule';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service';
import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; import { RuleSettings } from '@ghostfolio/common/interfaces';
import { RuleSettings, UserSettings } from '@ghostfolio/common/interfaces';
export class EconomicMarketClusterRiskEmergingMarkets extends Rule<Settings> { export class EconomicMarketClusterRiskEmergingMarkets extends Rule<Settings> {
private currentValueInBaseCurrency: number; private currentValueInBaseCurrency: number;
@ -17,7 +16,7 @@ export class EconomicMarketClusterRiskEmergingMarkets extends Rule<Settings> {
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
languageCode, languageCode,
key: EconomicMarketClusterRiskEmergingMarkets.name key: 'EconomicMarketClusterRiskEmergingMarkets'
}); });
this.currentValueInBaseCurrency = currentValueInBaseCurrency; this.currentValueInBaseCurrency = currentValueInBaseCurrency;
@ -96,20 +95,6 @@ export class EconomicMarketClusterRiskEmergingMarkets extends Rule<Settings> {
languageCode: this.getLanguageCode() 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 { interface Settings extends RuleSettings {

17
apps/api/src/models/rules/emergency-fund/emergency-fund-coverage.ts

@ -1,8 +1,7 @@
import { Rule } from '@ghostfolio/api/models/rule'; import { Rule } from '@ghostfolio/api/models/rule';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service';
import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; import { RuleSettings } from '@ghostfolio/common/interfaces';
import { RuleSettings, UserSettings } from '@ghostfolio/common/interfaces';
import { Big } from 'big.js'; import { Big } from 'big.js';
@ -17,7 +16,7 @@ export class EmergencyFundCoverage extends Rule<Settings> {
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
languageCode, languageCode,
key: EmergencyFundCoverage.name key: 'EmergencyFundCoverage'
}); });
} }
@ -91,18 +90,6 @@ export class EmergencyFundCoverage extends Rule<Settings> {
languageCode: this.getLanguageCode() 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 { interface Settings extends RuleSettings {

17
apps/api/src/models/rules/emergency-fund/emergency-fund-setup.ts

@ -1,8 +1,7 @@
import { Rule } from '@ghostfolio/api/models/rule'; import { Rule } from '@ghostfolio/api/models/rule';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service';
import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; import { RuleSettings } from '@ghostfolio/common/interfaces';
import { RuleSettings, UserSettings } from '@ghostfolio/common/interfaces';
export class EmergencyFundSetup extends Rule<Settings> { export class EmergencyFundSetup extends Rule<Settings> {
private emergencyFund: number; private emergencyFund: number;
@ -15,7 +14,7 @@ export class EmergencyFundSetup extends Rule<Settings> {
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
languageCode, languageCode,
key: EmergencyFundSetup.name key: 'EmergencyFundSetup'
}); });
this.emergencyFund = emergencyFund; this.emergencyFund = emergencyFund;
@ -51,18 +50,6 @@ export class EmergencyFundSetup extends Rule<Settings> {
languageCode: this.getLanguageCode() 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 { interface Settings extends RuleSettings {

18
apps/api/src/models/rules/fees/fee-ratio-total-investment-volume.ts

@ -1,8 +1,7 @@
import { Rule } from '@ghostfolio/api/models/rule'; import { Rule } from '@ghostfolio/api/models/rule';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service';
import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; import { RuleSettings } from '@ghostfolio/common/interfaces';
import { RuleSettings, UserSettings } from '@ghostfolio/common/interfaces';
export class FeeRatioTotalInvestmentVolume extends Rule<Settings> { export class FeeRatioTotalInvestmentVolume extends Rule<Settings> {
private fees: number; private fees: number;
@ -17,7 +16,7 @@ export class FeeRatioTotalInvestmentVolume extends Rule<Settings> {
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
languageCode, languageCode,
key: FeeRatioTotalInvestmentVolume.name key: 'FeeRatioTotalInvestmentVolume'
}); });
this.fees = fees; this.fees = fees;
@ -75,19 +74,6 @@ export class FeeRatioTotalInvestmentVolume extends Rule<Settings> {
languageCode: this.getLanguageCode() 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 { interface Settings extends RuleSettings {

18
apps/api/src/models/rules/liquidity/buying-power.ts

@ -1,8 +1,7 @@
import { Rule } from '@ghostfolio/api/models/rule'; import { Rule } from '@ghostfolio/api/models/rule';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service';
import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from '@ghostfolio/common/config'; import { RuleSettings } from '@ghostfolio/common/interfaces';
import { RuleSettings, UserSettings } from '@ghostfolio/common/interfaces';
export class BuyingPower extends Rule<Settings> { export class BuyingPower extends Rule<Settings> {
private buyingPower: number; private buyingPower: number;
@ -15,7 +14,7 @@ export class BuyingPower extends Rule<Settings> {
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
languageCode, languageCode,
key: BuyingPower.name key: 'BuyingPower'
}); });
this.buyingPower = buyingPower; this.buyingPower = buyingPower;
@ -82,19 +81,6 @@ export class BuyingPower extends Rule<Settings> {
languageCode: this.getLanguageCode() 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 { interface Settings extends RuleSettings {

18
apps/api/src/models/rules/regional-market-cluster-risk/asia-pacific.ts

@ -1,8 +1,6 @@
import { Rule } from '@ghostfolio/api/models/rule'; import { Rule } from '@ghostfolio/api/models/rule';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { I18nService } from '@ghostfolio/api/services/i18n/i18n.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'; import { Settings } from './interfaces/rule-settings.interface';
@ -19,7 +17,7 @@ export class RegionalMarketClusterRiskAsiaPacific extends Rule<Settings> {
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
languageCode, languageCode,
key: RegionalMarketClusterRiskAsiaPacific.name key: 'RegionalMarketClusterRiskAsiaPacific'
}); });
this.asiaPacificValueInBaseCurrency = asiaPacificValueInBaseCurrency; this.asiaPacificValueInBaseCurrency = asiaPacificValueInBaseCurrency;
@ -90,18 +88,4 @@ export class RegionalMarketClusterRiskAsiaPacific extends Rule<Settings> {
languageCode: this.getLanguageCode() 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
};
}
} }

18
apps/api/src/models/rules/regional-market-cluster-risk/emerging-markets.ts

@ -1,8 +1,6 @@
import { Rule } from '@ghostfolio/api/models/rule'; import { Rule } from '@ghostfolio/api/models/rule';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { I18nService } from '@ghostfolio/api/services/i18n/i18n.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'; import { Settings } from './interfaces/rule-settings.interface';
@ -19,7 +17,7 @@ export class RegionalMarketClusterRiskEmergingMarkets extends Rule<Settings> {
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
languageCode, languageCode,
key: RegionalMarketClusterRiskEmergingMarkets.name key: 'RegionalMarketClusterRiskEmergingMarkets'
}); });
this.currentValueInBaseCurrency = currentValueInBaseCurrency; this.currentValueInBaseCurrency = currentValueInBaseCurrency;
@ -92,18 +90,4 @@ export class RegionalMarketClusterRiskEmergingMarkets extends Rule<Settings> {
languageCode: this.getLanguageCode() 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
};
}
} }

18
apps/api/src/models/rules/regional-market-cluster-risk/europe.ts

@ -1,8 +1,6 @@
import { Rule } from '@ghostfolio/api/models/rule'; import { Rule } from '@ghostfolio/api/models/rule';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { I18nService } from '@ghostfolio/api/services/i18n/i18n.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'; import { Settings } from './interfaces/rule-settings.interface';
@ -19,7 +17,7 @@ export class RegionalMarketClusterRiskEurope extends Rule<Settings> {
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
languageCode, languageCode,
key: RegionalMarketClusterRiskEurope.name key: 'RegionalMarketClusterRiskEurope'
}); });
this.currentValueInBaseCurrency = currentValueInBaseCurrency; this.currentValueInBaseCurrency = currentValueInBaseCurrency;
@ -90,18 +88,4 @@ export class RegionalMarketClusterRiskEurope extends Rule<Settings> {
languageCode: this.getLanguageCode() 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
};
}
} }

18
apps/api/src/models/rules/regional-market-cluster-risk/japan.ts

@ -1,8 +1,6 @@
import { Rule } from '@ghostfolio/api/models/rule'; import { Rule } from '@ghostfolio/api/models/rule';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { I18nService } from '@ghostfolio/api/services/i18n/i18n.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'; import { Settings } from './interfaces/rule-settings.interface';
@ -19,7 +17,7 @@ export class RegionalMarketClusterRiskJapan extends Rule<Settings> {
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
languageCode, languageCode,
key: RegionalMarketClusterRiskJapan.name key: 'RegionalMarketClusterRiskJapan'
}); });
this.currentValueInBaseCurrency = currentValueInBaseCurrency; this.currentValueInBaseCurrency = currentValueInBaseCurrency;
@ -90,18 +88,4 @@ export class RegionalMarketClusterRiskJapan extends Rule<Settings> {
languageCode: this.getLanguageCode() 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
};
}
} }

18
apps/api/src/models/rules/regional-market-cluster-risk/north-america.ts

@ -1,8 +1,6 @@
import { Rule } from '@ghostfolio/api/models/rule'; import { Rule } from '@ghostfolio/api/models/rule';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { I18nService } from '@ghostfolio/api/services/i18n/i18n.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'; import { Settings } from './interfaces/rule-settings.interface';
@ -19,7 +17,7 @@ export class RegionalMarketClusterRiskNorthAmerica extends Rule<Settings> {
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
languageCode, languageCode,
key: RegionalMarketClusterRiskNorthAmerica.name key: 'RegionalMarketClusterRiskNorthAmerica'
}); });
this.currentValueInBaseCurrency = currentValueInBaseCurrency; this.currentValueInBaseCurrency = currentValueInBaseCurrency;
@ -90,18 +88,4 @@ export class RegionalMarketClusterRiskNorthAmerica extends Rule<Settings> {
languageCode: this.getLanguageCode() 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
};
}
} }

129
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'
]);
});
});

98
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<XRayRuleKey, RuleSettingsConfiguration>;
export function getRuleSettings<T extends RuleSettings>({
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;
}

3
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;
Loading…
Cancel
Save