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 { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.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(
aPortfolioPositionMap: {
[symbol: string]: PortfolioPosition;
},
aFees: number,
aRuleSettingsMap: {
[key: string]: any;
}
aRuleSettings: T
): 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 {
rules: {
accountClusterRisk: await this.rulesService.evaluate(
this,
details,
fees,
[
new AccountClusterRiskInitialInvestment(
this.exchangeRateDataService
@ -461,7 +464,8 @@ export class Portfolio implements PortfolioInterface {
{ baseCurrency: this.user.Settings.currency }
),
currencyClusterRisk: await this.rulesService.evaluate(
this,
details,
fees,
[
new CurrencyClusterRiskBaseCurrencyInitialInvestment(
this.exchangeRateDataService
@ -479,7 +483,8 @@ export class Portfolio implements PortfolioInterface {
{ baseCurrency: this.user.Settings.currency }
),
fees: await this.rulesService.evaluate(
this,
details,
fees,
[new FeeRatioInitialInvestment(this.exchangeRateDataService)],
{ baseCurrency: this.user.Settings.currency }
)

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

@ -2,8 +2,10 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-
import { PortfolioPosition } from '@ghostfolio/common/interfaces';
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) {
super(exchangeRateDataService, {
name: 'Current Investment'
@ -13,13 +15,8 @@ export class AccountClusterRiskCurrentInvestment extends Rule {
public evaluate(
aPositions: { [symbol: string]: PortfolioPosition },
aFees: number,
aRuleSettingsMap?: {
[key: string]: any;
}
ruleSettings?: Settings
) {
const ruleSettings =
aRuleSettingsMap[AccountClusterRiskCurrentInvestment.name];
const accounts: {
[symbol: string]: Pick<PortfolioPosition, 'name'> & {
investment: number;
@ -78,4 +75,17 @@ export class AccountClusterRiskCurrentInvestment extends Rule {
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 { 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) {
super(exchangeRateDataService, {
name: 'Initial Investment'
@ -13,13 +15,8 @@ export class AccountClusterRiskInitialInvestment extends Rule {
public evaluate(
aPositions: { [symbol: string]: PortfolioPosition },
aFees: number,
aRuleSettingsMap?: {
[key: string]: any;
}
ruleSettings?: Settings
) {
const ruleSettings =
aRuleSettingsMap[AccountClusterRiskInitialInvestment.name];
const platforms: {
[symbol: string]: Pick<PortfolioPosition, 'name'> & {
investment: number;
@ -78,4 +75,18 @@ export class AccountClusterRiskInitialInvestment extends Rule {
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 { 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) {
super(exchangeRateDataService, {
name: 'Single Account'
@ -33,4 +35,10 @@ export class AccountClusterRiskSingleAccount extends Rule {
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 { PortfolioPosition } from '@ghostfolio/common/interfaces';
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) {
super(exchangeRateDataService, {
name: 'Current Investment: Base Currency'
@ -13,13 +16,8 @@ export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule {
public evaluate(
aPositions: { [symbol: string]: PortfolioPosition },
aFees: number,
aRuleSettingsMap?: {
[key: string]: any;
}
ruleSettings: Settings
) {
const ruleSettings =
aRuleSettingsMap[CurrencyClusterRiskBaseCurrencyCurrentInvestment.name];
const positionsGroupedByCurrency = this.groupPositionsByAttribute(
aPositions,
'currency',
@ -61,4 +59,15 @@ export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule {
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 { ExchangeRateDataService } from 'apps/api/src/services/exchange-rate-data.service';
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) {
super(exchangeRateDataService, {
name: 'Initial Investment: Base Currency'
@ -13,13 +16,8 @@ export class CurrencyClusterRiskBaseCurrencyInitialInvestment extends Rule {
public evaluate(
aPositions: { [symbol: string]: PortfolioPosition },
aFees: number,
aRuleSettingsMap?: {
[key: string]: any;
}
ruleSettings: Settings
) {
const ruleSettings =
aRuleSettingsMap[CurrencyClusterRiskBaseCurrencyInitialInvestment.name];
const positionsGroupedByCurrency = this.groupPositionsByAttribute(
aPositions,
'currency',
@ -62,4 +60,15 @@ export class CurrencyClusterRiskBaseCurrencyInitialInvestment extends Rule {
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 { 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) {
super(exchangeRateDataService, {
name: 'Current Investment'
@ -13,13 +16,8 @@ export class CurrencyClusterRiskCurrentInvestment extends Rule {
public evaluate(
aPositions: { [symbol: string]: PortfolioPosition },
aFees: number,
aRuleSettingsMap?: {
[key: string]: any;
}
ruleSettings: Settings
) {
const ruleSettings =
aRuleSettingsMap[CurrencyClusterRiskCurrentInvestment.name];
const positionsGroupedByCurrency = this.groupPositionsByAttribute(
aPositions,
'currency',
@ -61,4 +59,17 @@ export class CurrencyClusterRiskCurrentInvestment extends Rule {
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 { ExchangeRateDataService } from 'apps/api/src/services/exchange-rate-data.service';
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) {
super(exchangeRateDataService, {
name: 'Initial Investment'
@ -13,13 +16,8 @@ export class CurrencyClusterRiskInitialInvestment extends Rule {
public evaluate(
aPositions: { [symbol: string]: PortfolioPosition },
aFees: number,
aRuleSettingsMap?: {
[key: string]: any;
}
ruleSettings: Settings
) {
const ruleSettings =
aRuleSettingsMap[CurrencyClusterRiskInitialInvestment.name];
const positionsGroupedByCurrency = this.groupPositionsByAttribute(
aPositions,
'currency',
@ -61,4 +59,17 @@ export class CurrencyClusterRiskInitialInvestment extends Rule {
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 { ExchangeRateDataService } from 'apps/api/src/services/exchange-rate-data.service';
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) {
super(exchangeRateDataService, {
name: 'Initial Investment'
@ -13,12 +16,8 @@ export class FeeRatioInitialInvestment extends Rule {
public evaluate(
aPositions: { [symbol: string]: PortfolioPosition },
aFees: number,
aRuleSettingsMap?: {
[key: string]: any;
}
ruleSettings: Settings
) {
const ruleSettings = aRuleSettingsMap[FeeRatioInitialInvestment.name];
const positionsGroupedByCurrency = this.groupPositionsByAttribute(
aPositions,
'currency',
@ -50,4 +49,17 @@ export class FeeRatioInitialInvestment extends Rule {
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 { Portfolio } from '../models/portfolio';
import { Rule } from '../models/rule';
import { AccountClusterRiskCurrentInvestment } from '../models/rules/account-cluster-risk/current-investment';
import { AccountClusterRiskInitialInvestment } from '../models/rules/account-cluster-risk/initial-investment';
import { AccountClusterRiskSingleAccount } from '../models/rules/account-cluster-risk/single-account';
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';
import { PortfolioPosition } from '@ghostfolio/common/interfaces';
import { Currency } from '@prisma/client';
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
@Injectable()
export class RulesService {
public constructor() {}
public async evaluate(
aPortfolio: Portfolio,
aRules: Rule[],
aUserSettings: { baseCurrency: string }
public async evaluate<T extends RuleSettings>(
details: { [p: string]: PortfolioPosition },
fees: number,
aRules: Rule<T>[],
aUserSettings: { baseCurrency: Currency }
) {
const defaultSettings = this.getDefaultRuleSettings(aUserSettings);
const details = await aPortfolio.getDetails();
return aRules
.filter((rule) => {
return defaultSettings[rule.constructor.name]?.isActive;
return rule.getSettings(aUserSettings)?.isActive;
})
.map((rule) => {
const evaluationResult = rule.evaluate(
details,
aPortfolio.getFees(),
defaultSettings
fees,
rule.getSettings(aUserSettings)
);
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