change rule service interface
This commit is contained in:
parent
9834c52739
commit
72dbe00091
apps/api/src
@ -1,17 +1,9 @@
|
|||||||
import { PortfolioPosition } from '@ghostfolio/common/interfaces';
|
|
||||||
|
|
||||||
import { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.interface';
|
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';
|
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
|
||||||
|
|
||||||
export interface RuleInterface<T extends RuleSettings> {
|
export interface RuleInterface<T extends RuleSettings> {
|
||||||
evaluate(
|
evaluate(aRuleSettings: T): EvaluationResult;
|
||||||
aPortfolioPositionMap: {
|
|
||||||
[symbol: string]: PortfolioPosition;
|
|
||||||
},
|
|
||||||
aFees: number,
|
|
||||||
aRuleSettings: T
|
|
||||||
): EvaluationResult;
|
|
||||||
|
|
||||||
getSettings(aUserSettings: UserSettings): T;
|
getSettings(aUserSettings: UserSettings): T;
|
||||||
}
|
}
|
||||||
|
@ -450,42 +450,51 @@ export class Portfolio implements PortfolioInterface {
|
|||||||
return {
|
return {
|
||||||
rules: {
|
rules: {
|
||||||
accountClusterRisk: await this.rulesService.evaluate(
|
accountClusterRisk: await this.rulesService.evaluate(
|
||||||
details,
|
|
||||||
fees,
|
|
||||||
[
|
[
|
||||||
new AccountClusterRiskInitialInvestment(
|
new AccountClusterRiskInitialInvestment(
|
||||||
this.exchangeRateDataService
|
this.exchangeRateDataService,
|
||||||
|
details
|
||||||
),
|
),
|
||||||
new AccountClusterRiskCurrentInvestment(
|
new AccountClusterRiskCurrentInvestment(
|
||||||
this.exchangeRateDataService
|
this.exchangeRateDataService,
|
||||||
|
details
|
||||||
),
|
),
|
||||||
new AccountClusterRiskSingleAccount(this.exchangeRateDataService)
|
new AccountClusterRiskSingleAccount(
|
||||||
|
this.exchangeRateDataService,
|
||||||
|
details
|
||||||
|
)
|
||||||
],
|
],
|
||||||
{ baseCurrency: this.user.Settings.currency }
|
{ baseCurrency: this.user.Settings.currency }
|
||||||
),
|
),
|
||||||
currencyClusterRisk: await this.rulesService.evaluate(
|
currencyClusterRisk: await this.rulesService.evaluate(
|
||||||
details,
|
|
||||||
fees,
|
|
||||||
[
|
[
|
||||||
new CurrencyClusterRiskBaseCurrencyInitialInvestment(
|
new CurrencyClusterRiskBaseCurrencyInitialInvestment(
|
||||||
this.exchangeRateDataService
|
this.exchangeRateDataService,
|
||||||
|
details
|
||||||
),
|
),
|
||||||
new CurrencyClusterRiskBaseCurrencyCurrentInvestment(
|
new CurrencyClusterRiskBaseCurrencyCurrentInvestment(
|
||||||
this.exchangeRateDataService
|
this.exchangeRateDataService,
|
||||||
|
details
|
||||||
),
|
),
|
||||||
new CurrencyClusterRiskInitialInvestment(
|
new CurrencyClusterRiskInitialInvestment(
|
||||||
this.exchangeRateDataService
|
this.exchangeRateDataService,
|
||||||
|
details
|
||||||
),
|
),
|
||||||
new CurrencyClusterRiskCurrentInvestment(
|
new CurrencyClusterRiskCurrentInvestment(
|
||||||
this.exchangeRateDataService
|
this.exchangeRateDataService,
|
||||||
|
details
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
{ baseCurrency: this.user.Settings.currency }
|
{ baseCurrency: this.user.Settings.currency }
|
||||||
),
|
),
|
||||||
fees: await this.rulesService.evaluate(
|
fees: await this.rulesService.evaluate(
|
||||||
details,
|
[
|
||||||
fees,
|
new FeeRatioInitialInvestment(
|
||||||
[new FeeRatioInitialInvestment(this.exchangeRateDataService)],
|
this.exchangeRateDataService,
|
||||||
|
details,
|
||||||
|
fees
|
||||||
|
)
|
||||||
|
],
|
||||||
{ baseCurrency: this.user.Settings.currency }
|
{ baseCurrency: this.user.Settings.currency }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ export abstract class Rule<T extends RuleSettings> implements RuleInterface<T> {
|
|||||||
private name: string;
|
private name: string;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
public exchangeRateDataService: ExchangeRateDataService,
|
protected exchangeRateDataService: ExchangeRateDataService,
|
||||||
{
|
{
|
||||||
name
|
name
|
||||||
}: {
|
}: {
|
||||||
@ -22,13 +22,7 @@ export abstract class Rule<T extends RuleSettings> implements RuleInterface<T> {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract evaluate(
|
public abstract evaluate(aRuleSettings: T): EvaluationResult;
|
||||||
aPortfolioPositionMap: {
|
|
||||||
[symbol: string]: PortfolioPosition;
|
|
||||||
},
|
|
||||||
aFees: number,
|
|
||||||
aRuleSettings: T
|
|
||||||
): EvaluationResult;
|
|
||||||
|
|
||||||
public abstract getSettings(aUserSettings: UserSettings): T;
|
public abstract getSettings(aUserSettings: UserSettings): T;
|
||||||
|
|
||||||
|
@ -6,24 +6,23 @@ import { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.in
|
|||||||
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
|
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
|
||||||
|
|
||||||
export class AccountClusterRiskCurrentInvestment extends Rule<Settings> {
|
export class AccountClusterRiskCurrentInvestment extends Rule<Settings> {
|
||||||
public constructor(public exchangeRateDataService: ExchangeRateDataService) {
|
public constructor(
|
||||||
|
protected exchangeRateDataService: ExchangeRateDataService,
|
||||||
|
private positions: { [symbol: string]: PortfolioPosition }
|
||||||
|
) {
|
||||||
super(exchangeRateDataService, {
|
super(exchangeRateDataService, {
|
||||||
name: 'Current Investment'
|
name: 'Current Investment'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public evaluate(
|
public evaluate(ruleSettings: Settings) {
|
||||||
aPositions: { [symbol: string]: PortfolioPosition },
|
|
||||||
aFees: number,
|
|
||||||
ruleSettings?: Settings
|
|
||||||
) {
|
|
||||||
const accounts: {
|
const accounts: {
|
||||||
[symbol: string]: Pick<PortfolioPosition, 'name'> & {
|
[symbol: string]: Pick<PortfolioPosition, 'name'> & {
|
||||||
investment: number;
|
investment: number;
|
||||||
};
|
};
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
Object.values(aPositions).forEach((position) => {
|
Object.values(this.positions).forEach((position) => {
|
||||||
for (const [account, { current }] of Object.entries(position.accounts)) {
|
for (const [account, { current }] of Object.entries(position.accounts)) {
|
||||||
if (accounts[account]?.investment) {
|
if (accounts[account]?.investment) {
|
||||||
accounts[account].investment += current;
|
accounts[account].investment += current;
|
||||||
|
@ -6,24 +6,23 @@ import { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.in
|
|||||||
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
|
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
|
||||||
|
|
||||||
export class AccountClusterRiskInitialInvestment extends Rule<Settings> {
|
export class AccountClusterRiskInitialInvestment extends Rule<Settings> {
|
||||||
public constructor(public exchangeRateDataService: ExchangeRateDataService) {
|
public constructor(
|
||||||
|
protected exchangeRateDataService: ExchangeRateDataService,
|
||||||
|
private positions: { [symbol: string]: PortfolioPosition }
|
||||||
|
) {
|
||||||
super(exchangeRateDataService, {
|
super(exchangeRateDataService, {
|
||||||
name: 'Initial Investment'
|
name: 'Initial Investment'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public evaluate(
|
public evaluate(ruleSettings?: Settings) {
|
||||||
aPositions: { [symbol: string]: PortfolioPosition },
|
|
||||||
aFees: number,
|
|
||||||
ruleSettings?: Settings
|
|
||||||
) {
|
|
||||||
const platforms: {
|
const platforms: {
|
||||||
[symbol: string]: Pick<PortfolioPosition, 'name'> & {
|
[symbol: string]: Pick<PortfolioPosition, 'name'> & {
|
||||||
investment: number;
|
investment: number;
|
||||||
};
|
};
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
Object.values(aPositions).forEach((position) => {
|
Object.values(this.positions).forEach((position) => {
|
||||||
for (const [account, { original }] of Object.entries(position.accounts)) {
|
for (const [account, { original }] of Object.entries(position.accounts)) {
|
||||||
if (platforms[account]?.investment) {
|
if (platforms[account]?.investment) {
|
||||||
platforms[account].investment += original;
|
platforms[account].investment += original;
|
||||||
|
@ -6,16 +6,19 @@ import { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.in
|
|||||||
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
|
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
|
||||||
|
|
||||||
export class AccountClusterRiskSingleAccount extends Rule<RuleSettings> {
|
export class AccountClusterRiskSingleAccount extends Rule<RuleSettings> {
|
||||||
public constructor(public exchangeRateDataService: ExchangeRateDataService) {
|
public constructor(
|
||||||
|
protected exchangeRateDataService: ExchangeRateDataService,
|
||||||
|
private positions: { [symbol: string]: PortfolioPosition }
|
||||||
|
) {
|
||||||
super(exchangeRateDataService, {
|
super(exchangeRateDataService, {
|
||||||
name: 'Single Account'
|
name: 'Single Account'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public evaluate(positions: { [symbol: string]: PortfolioPosition }) {
|
public evaluate() {
|
||||||
const accounts: string[] = [];
|
const accounts: string[] = [];
|
||||||
|
|
||||||
Object.values(positions).forEach((position) => {
|
Object.values(this.positions).forEach((position) => {
|
||||||
for (const [account] of Object.entries(position.accounts)) {
|
for (const [account] of Object.entries(position.accounts)) {
|
||||||
if (!accounts.includes(account)) {
|
if (!accounts.includes(account)) {
|
||||||
accounts.push(account);
|
accounts.push(account);
|
||||||
|
@ -7,19 +7,18 @@ import { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.in
|
|||||||
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
|
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
|
||||||
|
|
||||||
export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule<Settings> {
|
export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule<Settings> {
|
||||||
public constructor(public exchangeRateDataService: ExchangeRateDataService) {
|
public constructor(
|
||||||
|
protected exchangeRateDataService: ExchangeRateDataService,
|
||||||
|
private positions: { [symbol: string]: PortfolioPosition }
|
||||||
|
) {
|
||||||
super(exchangeRateDataService, {
|
super(exchangeRateDataService, {
|
||||||
name: 'Current Investment: Base Currency'
|
name: 'Current Investment: Base Currency'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public evaluate(
|
public evaluate(ruleSettings: Settings) {
|
||||||
aPositions: { [symbol: string]: PortfolioPosition },
|
|
||||||
aFees: number,
|
|
||||||
ruleSettings: Settings
|
|
||||||
) {
|
|
||||||
const positionsGroupedByCurrency = this.groupPositionsByAttribute(
|
const positionsGroupedByCurrency = this.groupPositionsByAttribute(
|
||||||
aPositions,
|
this.positions,
|
||||||
'currency',
|
'currency',
|
||||||
ruleSettings.baseCurrency
|
ruleSettings.baseCurrency
|
||||||
);
|
);
|
||||||
|
@ -7,19 +7,18 @@ import { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.in
|
|||||||
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
|
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
|
||||||
|
|
||||||
export class CurrencyClusterRiskBaseCurrencyInitialInvestment extends Rule<Settings> {
|
export class CurrencyClusterRiskBaseCurrencyInitialInvestment extends Rule<Settings> {
|
||||||
public constructor(public exchangeRateDataService: ExchangeRateDataService) {
|
public constructor(
|
||||||
|
protected exchangeRateDataService: ExchangeRateDataService,
|
||||||
|
private positions: { [symbol: string]: PortfolioPosition }
|
||||||
|
) {
|
||||||
super(exchangeRateDataService, {
|
super(exchangeRateDataService, {
|
||||||
name: 'Initial Investment: Base Currency'
|
name: 'Initial Investment: Base Currency'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public evaluate(
|
public evaluate(ruleSettings: Settings) {
|
||||||
aPositions: { [symbol: string]: PortfolioPosition },
|
|
||||||
aFees: number,
|
|
||||||
ruleSettings: Settings
|
|
||||||
) {
|
|
||||||
const positionsGroupedByCurrency = this.groupPositionsByAttribute(
|
const positionsGroupedByCurrency = this.groupPositionsByAttribute(
|
||||||
aPositions,
|
this.positions,
|
||||||
'currency',
|
'currency',
|
||||||
ruleSettings.baseCurrency
|
ruleSettings.baseCurrency
|
||||||
);
|
);
|
||||||
|
@ -7,19 +7,18 @@ import { Currency } from '@prisma/client';
|
|||||||
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
|
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
|
||||||
|
|
||||||
export class CurrencyClusterRiskCurrentInvestment extends Rule<Settings> {
|
export class CurrencyClusterRiskCurrentInvestment extends Rule<Settings> {
|
||||||
public constructor(public exchangeRateDataService: ExchangeRateDataService) {
|
public constructor(
|
||||||
|
public exchangeRateDataService: ExchangeRateDataService,
|
||||||
|
private positions: { [symbol: string]: PortfolioPosition }
|
||||||
|
) {
|
||||||
super(exchangeRateDataService, {
|
super(exchangeRateDataService, {
|
||||||
name: 'Current Investment'
|
name: 'Current Investment'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public evaluate(
|
public evaluate(ruleSettings: Settings) {
|
||||||
aPositions: { [symbol: string]: PortfolioPosition },
|
|
||||||
aFees: number,
|
|
||||||
ruleSettings: Settings
|
|
||||||
) {
|
|
||||||
const positionsGroupedByCurrency = this.groupPositionsByAttribute(
|
const positionsGroupedByCurrency = this.groupPositionsByAttribute(
|
||||||
aPositions,
|
this.positions,
|
||||||
'currency',
|
'currency',
|
||||||
ruleSettings.baseCurrency
|
ruleSettings.baseCurrency
|
||||||
);
|
);
|
||||||
|
@ -7,19 +7,18 @@ import { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.in
|
|||||||
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
|
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
|
||||||
|
|
||||||
export class CurrencyClusterRiskInitialInvestment extends Rule<Settings> {
|
export class CurrencyClusterRiskInitialInvestment extends Rule<Settings> {
|
||||||
public constructor(public exchangeRateDataService: ExchangeRateDataService) {
|
public constructor(
|
||||||
|
protected exchangeRateDataService: ExchangeRateDataService,
|
||||||
|
private positions: { [symbol: string]: PortfolioPosition }
|
||||||
|
) {
|
||||||
super(exchangeRateDataService, {
|
super(exchangeRateDataService, {
|
||||||
name: 'Initial Investment'
|
name: 'Initial Investment'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public evaluate(
|
public evaluate(ruleSettings: Settings) {
|
||||||
aPositions: { [symbol: string]: PortfolioPosition },
|
|
||||||
aFees: number,
|
|
||||||
ruleSettings: Settings
|
|
||||||
) {
|
|
||||||
const positionsGroupedByCurrency = this.groupPositionsByAttribute(
|
const positionsGroupedByCurrency = this.groupPositionsByAttribute(
|
||||||
aPositions,
|
this.positions,
|
||||||
'currency',
|
'currency',
|
||||||
ruleSettings.baseCurrency
|
ruleSettings.baseCurrency
|
||||||
);
|
);
|
||||||
|
@ -7,19 +7,19 @@ import { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.in
|
|||||||
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
|
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
|
||||||
|
|
||||||
export class FeeRatioInitialInvestment extends Rule<Settings> {
|
export class FeeRatioInitialInvestment extends Rule<Settings> {
|
||||||
public constructor(public exchangeRateDataService: ExchangeRateDataService) {
|
public constructor(
|
||||||
|
protected exchangeRateDataService: ExchangeRateDataService,
|
||||||
|
private positions: { [symbol: string]: PortfolioPosition },
|
||||||
|
private fees: number
|
||||||
|
) {
|
||||||
super(exchangeRateDataService, {
|
super(exchangeRateDataService, {
|
||||||
name: 'Initial Investment'
|
name: 'Initial Investment'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public evaluate(
|
public evaluate(ruleSettings: Settings) {
|
||||||
aPositions: { [symbol: string]: PortfolioPosition },
|
|
||||||
aFees: number,
|
|
||||||
ruleSettings: Settings
|
|
||||||
) {
|
|
||||||
const positionsGroupedByCurrency = this.groupPositionsByAttribute(
|
const positionsGroupedByCurrency = this.groupPositionsByAttribute(
|
||||||
aPositions,
|
this.positions,
|
||||||
'currency',
|
'currency',
|
||||||
ruleSettings.baseCurrency
|
ruleSettings.baseCurrency
|
||||||
);
|
);
|
||||||
@ -31,7 +31,7 @@ export class FeeRatioInitialInvestment extends Rule<Settings> {
|
|||||||
totalInvestment += groupItem.investment;
|
totalInvestment += groupItem.investment;
|
||||||
});
|
});
|
||||||
|
|
||||||
const feeRatio = aFees / totalInvestment;
|
const feeRatio = this.fees / totalInvestment;
|
||||||
|
|
||||||
if (feeRatio > ruleSettings.threshold) {
|
if (feeRatio > ruleSettings.threshold) {
|
||||||
return {
|
return {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { Rule } from '../models/rule';
|
import { Rule } from '../models/rule';
|
||||||
import { PortfolioPosition } from '@ghostfolio/common/interfaces';
|
|
||||||
import { Currency } from '@prisma/client';
|
import { Currency } from '@prisma/client';
|
||||||
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
|
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
|
||||||
|
|
||||||
@ -9,8 +8,6 @@ export class RulesService {
|
|||||||
public constructor() {}
|
public constructor() {}
|
||||||
|
|
||||||
public async evaluate<T extends RuleSettings>(
|
public async evaluate<T extends RuleSettings>(
|
||||||
details: { [p: string]: PortfolioPosition },
|
|
||||||
fees: number,
|
|
||||||
aRules: Rule<T>[],
|
aRules: Rule<T>[],
|
||||||
aUserSettings: { baseCurrency: Currency }
|
aUserSettings: { baseCurrency: Currency }
|
||||||
) {
|
) {
|
||||||
@ -19,11 +16,7 @@ export class RulesService {
|
|||||||
return rule.getSettings(aUserSettings)?.isActive;
|
return rule.getSettings(aUserSettings)?.isActive;
|
||||||
})
|
})
|
||||||
.map((rule) => {
|
.map((rule) => {
|
||||||
const evaluationResult = rule.evaluate(
|
const evaluationResult = rule.evaluate(rule.getSettings(aUserSettings));
|
||||||
details,
|
|
||||||
fees,
|
|
||||||
rule.getSettings(aUserSettings)
|
|
||||||
);
|
|
||||||
return { ...evaluationResult, name: rule.getName() };
|
return { ...evaluationResult, name: rule.getName() };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user