refactor rule settings

pull/239/head
Valentin Zickner 3 years ago committed by Thomas
parent c47578bd3e
commit 9834c52739

@ -0,0 +1,3 @@
export interface RuleSettings {
isActive: boolean;
}

@ -1,15 +1,17 @@
import { PortfolioPosition } from '@ghostfolio/common/interfaces'; import { PortfolioPosition } from '@ghostfolio/common/interfaces';
import { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.interface';
import { EvaluationResult } from './evaluation-result.interface'; import { EvaluationResult } from './evaluation-result.interface';
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
export interface RuleInterface { export interface RuleInterface<T extends RuleSettings> {
evaluate( evaluate(
aPortfolioPositionMap: { aPortfolioPositionMap: {
[symbol: string]: PortfolioPosition; [symbol: string]: PortfolioPosition;
}, },
aFees: number, aFees: number,
aRuleSettingsMap: { aRuleSettings: T
[key: string]: any;
}
): EvaluationResult; ): EvaluationResult;
getSettings(aUserSettings: UserSettings): T;
} }

@ -0,0 +1,5 @@
import { Currency } from '@prisma/client';
export interface UserSettings {
baseCurrency: Currency;
}

@ -445,10 +445,13 @@ export class Portfolio implements PortfolioInterface {
}; };
} }
const fees = this.getFees();
return { return {
rules: { rules: {
accountClusterRisk: await this.rulesService.evaluate( accountClusterRisk: await this.rulesService.evaluate(
this, details,
fees,
[ [
new AccountClusterRiskInitialInvestment( new AccountClusterRiskInitialInvestment(
this.exchangeRateDataService this.exchangeRateDataService
@ -461,7 +464,8 @@ export class Portfolio implements PortfolioInterface {
{ baseCurrency: this.user.Settings.currency } { baseCurrency: this.user.Settings.currency }
), ),
currencyClusterRisk: await this.rulesService.evaluate( currencyClusterRisk: await this.rulesService.evaluate(
this, details,
fees,
[ [
new CurrencyClusterRiskBaseCurrencyInitialInvestment( new CurrencyClusterRiskBaseCurrencyInitialInvestment(
this.exchangeRateDataService this.exchangeRateDataService
@ -479,7 +483,8 @@ export class Portfolio implements PortfolioInterface {
{ baseCurrency: this.user.Settings.currency } { baseCurrency: this.user.Settings.currency }
), ),
fees: await this.rulesService.evaluate( fees: await this.rulesService.evaluate(
this, details,
fees,
[new FeeRatioInitialInvestment(this.exchangeRateDataService)], [new FeeRatioInitialInvestment(this.exchangeRateDataService)],
{ baseCurrency: this.user.Settings.currency } { baseCurrency: this.user.Settings.currency }
) )

@ -5,8 +5,10 @@ import { Currency } from '@prisma/client';
import { ExchangeRateDataService } from '../services/exchange-rate-data.service'; import { ExchangeRateDataService } from '../services/exchange-rate-data.service';
import { EvaluationResult } from './interfaces/evaluation-result.interface'; import { EvaluationResult } from './interfaces/evaluation-result.interface';
import { RuleInterface } from './interfaces/rule.interface'; import { RuleInterface } from './interfaces/rule.interface';
import { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.interface';
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
export abstract class Rule implements RuleInterface { export abstract class Rule<T extends RuleSettings> implements RuleInterface<T> {
private name: string; private name: string;
public constructor( public constructor(
@ -25,11 +27,11 @@ export abstract class Rule implements RuleInterface {
[symbol: string]: PortfolioPosition; [symbol: string]: PortfolioPosition;
}, },
aFees: number, aFees: number,
aRuleSettingsMap?: { aRuleSettings: T
[key: string]: any;
}
): EvaluationResult; ): EvaluationResult;
public abstract getSettings(aUserSettings: UserSettings): T;
public getName() { public getName() {
return this.name; return this.name;
} }

@ -2,8 +2,10 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-
import { PortfolioPosition } from '@ghostfolio/common/interfaces'; import { PortfolioPosition } from '@ghostfolio/common/interfaces';
import { Rule } from '../../rule'; import { Rule } from '../../rule';
import { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.interface';
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
export class AccountClusterRiskCurrentInvestment extends Rule { export class AccountClusterRiskCurrentInvestment extends Rule<Settings> {
public constructor(public exchangeRateDataService: ExchangeRateDataService) { public constructor(public exchangeRateDataService: ExchangeRateDataService) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
name: 'Current Investment' name: 'Current Investment'
@ -13,13 +15,8 @@ export class AccountClusterRiskCurrentInvestment extends Rule {
public evaluate( public evaluate(
aPositions: { [symbol: string]: PortfolioPosition }, aPositions: { [symbol: string]: PortfolioPosition },
aFees: number, aFees: number,
aRuleSettingsMap?: { ruleSettings?: Settings
[key: string]: any;
}
) { ) {
const ruleSettings =
aRuleSettingsMap[AccountClusterRiskCurrentInvestment.name];
const accounts: { const accounts: {
[symbol: string]: Pick<PortfolioPosition, 'name'> & { [symbol: string]: Pick<PortfolioPosition, 'name'> & {
investment: number; investment: number;
@ -78,4 +75,17 @@ export class AccountClusterRiskCurrentInvestment extends Rule {
value: true value: true
}; };
} }
public getSettings(aUserSettings: UserSettings): Settings {
return {
baseCurrency: aUserSettings.baseCurrency,
isActive: true,
threshold: 0.5
};
}
}
interface Settings extends RuleSettings {
baseCurrency: string;
threshold: number;
} }

@ -2,8 +2,10 @@ import { PortfolioPosition } from '@ghostfolio/common/interfaces';
import { ExchangeRateDataService } from 'apps/api/src/services/exchange-rate-data.service'; import { ExchangeRateDataService } from 'apps/api/src/services/exchange-rate-data.service';
import { Rule } from '../../rule'; import { Rule } from '../../rule';
import { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.interface';
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
export class AccountClusterRiskInitialInvestment extends Rule { export class AccountClusterRiskInitialInvestment extends Rule<Settings> {
public constructor(public exchangeRateDataService: ExchangeRateDataService) { public constructor(public exchangeRateDataService: ExchangeRateDataService) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
name: 'Initial Investment' name: 'Initial Investment'
@ -13,13 +15,8 @@ export class AccountClusterRiskInitialInvestment extends Rule {
public evaluate( public evaluate(
aPositions: { [symbol: string]: PortfolioPosition }, aPositions: { [symbol: string]: PortfolioPosition },
aFees: number, aFees: number,
aRuleSettingsMap?: { ruleSettings?: Settings
[key: string]: any;
}
) { ) {
const ruleSettings =
aRuleSettingsMap[AccountClusterRiskInitialInvestment.name];
const platforms: { const platforms: {
[symbol: string]: Pick<PortfolioPosition, 'name'> & { [symbol: string]: Pick<PortfolioPosition, 'name'> & {
investment: number; investment: number;
@ -78,4 +75,18 @@ export class AccountClusterRiskInitialInvestment extends Rule {
value: true value: true
}; };
} }
public getSettings(aUserSettings: UserSettings): Settings {
return {
baseCurrency: aUserSettings.baseCurrency,
isActive: true,
threshold: 0.5
};
}
}
interface Settings extends RuleSettings {
baseCurrency: string;
isActive: boolean;
threshold: number;
} }

@ -2,8 +2,10 @@ import { PortfolioPosition } from '@ghostfolio/common/interfaces';
import { ExchangeRateDataService } from 'apps/api/src/services/exchange-rate-data.service'; import { ExchangeRateDataService } from 'apps/api/src/services/exchange-rate-data.service';
import { Rule } from '../../rule'; import { Rule } from '../../rule';
import { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.interface';
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
export class AccountClusterRiskSingleAccount extends Rule { export class AccountClusterRiskSingleAccount extends Rule<RuleSettings> {
public constructor(public exchangeRateDataService: ExchangeRateDataService) { public constructor(public exchangeRateDataService: ExchangeRateDataService) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
name: 'Single Account' name: 'Single Account'
@ -33,4 +35,10 @@ export class AccountClusterRiskSingleAccount extends Rule {
value: true value: true
}; };
} }
public getSettings(aUserSettings: UserSettings): RuleSettings {
return {
isActive: true
};
}
} }

@ -1,9 +1,12 @@
import { Currency } from '@prisma/client';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
import { PortfolioPosition } from '@ghostfolio/common/interfaces'; import { PortfolioPosition } from '@ghostfolio/common/interfaces';
import { Rule } from '../../rule'; import { Rule } from '../../rule';
import { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.interface';
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule { export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule<Settings> {
public constructor(public exchangeRateDataService: ExchangeRateDataService) { public constructor(public exchangeRateDataService: ExchangeRateDataService) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
name: 'Current Investment: Base Currency' name: 'Current Investment: Base Currency'
@ -13,13 +16,8 @@ export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule {
public evaluate( public evaluate(
aPositions: { [symbol: string]: PortfolioPosition }, aPositions: { [symbol: string]: PortfolioPosition },
aFees: number, aFees: number,
aRuleSettingsMap?: { ruleSettings: Settings
[key: string]: any;
}
) { ) {
const ruleSettings =
aRuleSettingsMap[CurrencyClusterRiskBaseCurrencyCurrentInvestment.name];
const positionsGroupedByCurrency = this.groupPositionsByAttribute( const positionsGroupedByCurrency = this.groupPositionsByAttribute(
aPositions, aPositions,
'currency', 'currency',
@ -61,4 +59,15 @@ export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule {
value: true value: true
}; };
} }
public getSettings(aUserSettings: UserSettings): Settings {
return {
baseCurrency: aUserSettings.baseCurrency,
isActive: true
};
}
}
interface Settings extends RuleSettings {
baseCurrency: Currency;
} }

@ -1,9 +1,12 @@
import { Currency } from '@prisma/client';
import { PortfolioPosition } from '@ghostfolio/common/interfaces'; import { PortfolioPosition } from '@ghostfolio/common/interfaces';
import { ExchangeRateDataService } from 'apps/api/src/services/exchange-rate-data.service'; import { ExchangeRateDataService } from 'apps/api/src/services/exchange-rate-data.service';
import { Rule } from '../../rule'; import { Rule } from '../../rule';
import { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.interface';
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
export class CurrencyClusterRiskBaseCurrencyInitialInvestment extends Rule { export class CurrencyClusterRiskBaseCurrencyInitialInvestment extends Rule<Settings> {
public constructor(public exchangeRateDataService: ExchangeRateDataService) { public constructor(public exchangeRateDataService: ExchangeRateDataService) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
name: 'Initial Investment: Base Currency' name: 'Initial Investment: Base Currency'
@ -13,13 +16,8 @@ export class CurrencyClusterRiskBaseCurrencyInitialInvestment extends Rule {
public evaluate( public evaluate(
aPositions: { [symbol: string]: PortfolioPosition }, aPositions: { [symbol: string]: PortfolioPosition },
aFees: number, aFees: number,
aRuleSettingsMap?: { ruleSettings: Settings
[key: string]: any;
}
) { ) {
const ruleSettings =
aRuleSettingsMap[CurrencyClusterRiskBaseCurrencyInitialInvestment.name];
const positionsGroupedByCurrency = this.groupPositionsByAttribute( const positionsGroupedByCurrency = this.groupPositionsByAttribute(
aPositions, aPositions,
'currency', 'currency',
@ -62,4 +60,15 @@ export class CurrencyClusterRiskBaseCurrencyInitialInvestment extends Rule {
value: true value: true
}; };
} }
public getSettings(aUserSettings: UserSettings): Settings {
return {
baseCurrency: aUserSettings.baseCurrency,
isActive: true
};
}
}
interface Settings extends RuleSettings {
baseCurrency: Currency;
} }

@ -2,8 +2,11 @@ import { PortfolioPosition } from '@ghostfolio/common/interfaces';
import { ExchangeRateDataService } from 'apps/api/src/services/exchange-rate-data.service'; import { ExchangeRateDataService } from 'apps/api/src/services/exchange-rate-data.service';
import { Rule } from '../../rule'; import { Rule } from '../../rule';
import { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.interface';
import { Currency } from '@prisma/client';
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
export class CurrencyClusterRiskCurrentInvestment extends Rule { export class CurrencyClusterRiskCurrentInvestment extends Rule<Settings> {
public constructor(public exchangeRateDataService: ExchangeRateDataService) { public constructor(public exchangeRateDataService: ExchangeRateDataService) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
name: 'Current Investment' name: 'Current Investment'
@ -13,13 +16,8 @@ export class CurrencyClusterRiskCurrentInvestment extends Rule {
public evaluate( public evaluate(
aPositions: { [symbol: string]: PortfolioPosition }, aPositions: { [symbol: string]: PortfolioPosition },
aFees: number, aFees: number,
aRuleSettingsMap?: { ruleSettings: Settings
[key: string]: any;
}
) { ) {
const ruleSettings =
aRuleSettingsMap[CurrencyClusterRiskCurrentInvestment.name];
const positionsGroupedByCurrency = this.groupPositionsByAttribute( const positionsGroupedByCurrency = this.groupPositionsByAttribute(
aPositions, aPositions,
'currency', 'currency',
@ -61,4 +59,17 @@ export class CurrencyClusterRiskCurrentInvestment extends Rule {
value: true value: true
}; };
} }
public getSettings(aUserSettings: UserSettings): Settings {
return {
baseCurrency: aUserSettings.baseCurrency,
isActive: true,
threshold: 0.5
};
}
}
interface Settings extends RuleSettings {
baseCurrency: Currency;
threshold: number;
} }

@ -1,9 +1,12 @@
import { Currency } from '@prisma/client';
import { PortfolioPosition } from '@ghostfolio/common/interfaces'; import { PortfolioPosition } from '@ghostfolio/common/interfaces';
import { ExchangeRateDataService } from 'apps/api/src/services/exchange-rate-data.service'; import { ExchangeRateDataService } from 'apps/api/src/services/exchange-rate-data.service';
import { Rule } from '../../rule'; import { Rule } from '../../rule';
import { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.interface';
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
export class CurrencyClusterRiskInitialInvestment extends Rule { export class CurrencyClusterRiskInitialInvestment extends Rule<Settings> {
public constructor(public exchangeRateDataService: ExchangeRateDataService) { public constructor(public exchangeRateDataService: ExchangeRateDataService) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
name: 'Initial Investment' name: 'Initial Investment'
@ -13,13 +16,8 @@ export class CurrencyClusterRiskInitialInvestment extends Rule {
public evaluate( public evaluate(
aPositions: { [symbol: string]: PortfolioPosition }, aPositions: { [symbol: string]: PortfolioPosition },
aFees: number, aFees: number,
aRuleSettingsMap?: { ruleSettings: Settings
[key: string]: any;
}
) { ) {
const ruleSettings =
aRuleSettingsMap[CurrencyClusterRiskInitialInvestment.name];
const positionsGroupedByCurrency = this.groupPositionsByAttribute( const positionsGroupedByCurrency = this.groupPositionsByAttribute(
aPositions, aPositions,
'currency', 'currency',
@ -61,4 +59,17 @@ export class CurrencyClusterRiskInitialInvestment extends Rule {
value: true value: true
}; };
} }
public getSettings(aUserSettings: UserSettings): Settings {
return {
baseCurrency: aUserSettings.baseCurrency,
isActive: true,
threshold: 0.5
};
}
}
interface Settings extends RuleSettings {
baseCurrency: Currency;
threshold: number;
} }

@ -1,9 +1,12 @@
import { Currency } from '@prisma/client';
import { PortfolioPosition } from '@ghostfolio/common/interfaces'; import { PortfolioPosition } from '@ghostfolio/common/interfaces';
import { ExchangeRateDataService } from 'apps/api/src/services/exchange-rate-data.service'; import { ExchangeRateDataService } from 'apps/api/src/services/exchange-rate-data.service';
import { Rule } from '../../rule'; import { Rule } from '../../rule';
import { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.interface';
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
export class FeeRatioInitialInvestment extends Rule { export class FeeRatioInitialInvestment extends Rule<Settings> {
public constructor(public exchangeRateDataService: ExchangeRateDataService) { public constructor(public exchangeRateDataService: ExchangeRateDataService) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
name: 'Initial Investment' name: 'Initial Investment'
@ -13,12 +16,8 @@ export class FeeRatioInitialInvestment extends Rule {
public evaluate( public evaluate(
aPositions: { [symbol: string]: PortfolioPosition }, aPositions: { [symbol: string]: PortfolioPosition },
aFees: number, aFees: number,
aRuleSettingsMap?: { ruleSettings: Settings
[key: string]: any;
}
) { ) {
const ruleSettings = aRuleSettingsMap[FeeRatioInitialInvestment.name];
const positionsGroupedByCurrency = this.groupPositionsByAttribute( const positionsGroupedByCurrency = this.groupPositionsByAttribute(
aPositions, aPositions,
'currency', 'currency',
@ -50,4 +49,17 @@ export class FeeRatioInitialInvestment extends Rule {
value: true value: true
}; };
} }
public getSettings(aUserSettings: UserSettings): Settings {
return {
baseCurrency: aUserSettings.baseCurrency,
isActive: true,
threshold: 0.01
};
}
}
interface Settings extends RuleSettings {
baseCurrency: Currency;
threshold: number;
} }

@ -1,78 +1,30 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { Portfolio } from '../models/portfolio';
import { Rule } from '../models/rule'; import { Rule } from '../models/rule';
import { AccountClusterRiskCurrentInvestment } from '../models/rules/account-cluster-risk/current-investment'; import { PortfolioPosition } from '@ghostfolio/common/interfaces';
import { AccountClusterRiskInitialInvestment } from '../models/rules/account-cluster-risk/initial-investment'; import { Currency } from '@prisma/client';
import { AccountClusterRiskSingleAccount } from '../models/rules/account-cluster-risk/single-account'; import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
import { CurrencyClusterRiskBaseCurrencyCurrentInvestment } from '../models/rules/currency-cluster-risk/base-currency-current-investment';
import { CurrencyClusterRiskBaseCurrencyInitialInvestment } from '../models/rules/currency-cluster-risk/base-currency-initial-investment';
import { CurrencyClusterRiskCurrentInvestment } from '../models/rules/currency-cluster-risk/current-investment';
import { CurrencyClusterRiskInitialInvestment } from '../models/rules/currency-cluster-risk/initial-investment';
import { FeeRatioInitialInvestment } from '../models/rules/fees/fee-ratio-initial-investment';
@Injectable() @Injectable()
export class RulesService { export class RulesService {
public constructor() {} public constructor() {}
public async evaluate( public async evaluate<T extends RuleSettings>(
aPortfolio: Portfolio, details: { [p: string]: PortfolioPosition },
aRules: Rule[], fees: number,
aUserSettings: { baseCurrency: string } aRules: Rule<T>[],
aUserSettings: { baseCurrency: Currency }
) { ) {
const defaultSettings = this.getDefaultRuleSettings(aUserSettings);
const details = await aPortfolio.getDetails();
return aRules return aRules
.filter((rule) => { .filter((rule) => {
return defaultSettings[rule.constructor.name]?.isActive; return rule.getSettings(aUserSettings)?.isActive;
}) })
.map((rule) => { .map((rule) => {
const evaluationResult = rule.evaluate( const evaluationResult = rule.evaluate(
details, details,
aPortfolio.getFees(), fees,
defaultSettings rule.getSettings(aUserSettings)
); );
return { ...evaluationResult, name: rule.getName() }; return { ...evaluationResult, name: rule.getName() };
}); });
} }
private getDefaultRuleSettings(aUserSettings: { baseCurrency: string }) {
return {
[AccountClusterRiskCurrentInvestment.name]: {
baseCurrency: aUserSettings.baseCurrency,
isActive: true,
threshold: 0.5
},
[AccountClusterRiskInitialInvestment.name]: {
baseCurrency: aUserSettings.baseCurrency,
isActive: true,
threshold: 0.5
},
[AccountClusterRiskSingleAccount.name]: { isActive: true },
[CurrencyClusterRiskBaseCurrencyInitialInvestment.name]: {
baseCurrency: aUserSettings.baseCurrency,
isActive: true
},
[CurrencyClusterRiskBaseCurrencyCurrentInvestment.name]: {
baseCurrency: aUserSettings.baseCurrency,
isActive: true
},
[CurrencyClusterRiskCurrentInvestment.name]: {
baseCurrency: aUserSettings.baseCurrency,
isActive: true,
threshold: 0.5
},
[CurrencyClusterRiskInitialInvestment.name]: {
baseCurrency: aUserSettings.baseCurrency,
isActive: true,
threshold: 0.5
},
[FeeRatioInitialInvestment.name]: {
baseCurrency: aUserSettings.baseCurrency,
isActive: true,
threshold: 0.01
}
};
}
} }

Loading…
Cancel
Save