refactor rule evaluation

pull/239/head
Valentin Zickner 3 years ago committed by Thomas
parent 04e6518226
commit 23b2e03923

@ -0,0 +1,11 @@
import { TimelinePosition } from '@ghostfolio/common/interfaces';
import Big from 'big.js';
export interface CurrentPositions {
hasErrors: boolean;
positions: TimelinePosition[];
grossPerformance: Big;
grossPerformancePercentage: Big;
currentValue: Big;
totalInvestment: Big;
}

@ -26,6 +26,7 @@ import {
subDays subDays
} from 'date-fns'; } from 'date-fns';
import { flatten } from 'lodash'; import { flatten } from 'lodash';
import { CurrentPositions } from '@ghostfolio/api/app/core/interfaces/current-positions.interface';
export class PortfolioCalculator { export class PortfolioCalculator {
private transactionPoints: TransactionPoint[]; private transactionPoints: TransactionPoint[];
@ -111,14 +112,7 @@ export class PortfolioCalculator {
this.transactionPoints = transactionPoints; this.transactionPoints = transactionPoints;
} }
public async getCurrentPositions(start: Date): Promise<{ public async getCurrentPositions(start: Date): Promise<CurrentPositions> {
hasErrors: boolean;
positions: TimelinePosition[];
grossPerformance: Big;
grossPerformancePercentage: Big;
currentValue: Big;
totalInvestment: Big;
}> {
if (!this.transactionPoints?.length) { if (!this.transactionPoints?.length) {
return { return {
hasErrors: false, hasErrors: false,

@ -4,18 +4,14 @@ import { PortfolioOrder } from '@ghostfolio/api/app/core/interfaces/portfolio-or
import { TimelineSpecification } from '@ghostfolio/api/app/core/interfaces/timeline-specification.interface'; import { TimelineSpecification } from '@ghostfolio/api/app/core/interfaces/timeline-specification.interface';
import { PortfolioCalculator } from '@ghostfolio/api/app/core/portfolio-calculator'; import { PortfolioCalculator } from '@ghostfolio/api/app/core/portfolio-calculator';
import { OrderService } from '@ghostfolio/api/app/order/order.service'; import { OrderService } from '@ghostfolio/api/app/order/order.service';
import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service';
import { UserService } from '@ghostfolio/api/app/user/user.service';
import { OrderType } from '@ghostfolio/api/models/order-type'; import { OrderType } from '@ghostfolio/api/models/order-type';
import { Portfolio } from '@ghostfolio/api/models/portfolio';
import { DataProviderService } from '@ghostfolio/api/services/data-provider.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service';
import { ImpersonationService } from '@ghostfolio/api/services/impersonation.service'; import { ImpersonationService } from '@ghostfolio/api/services/impersonation.service';
import { IOrder, Type } from '@ghostfolio/api/services/interfaces/interfaces'; import { Type } from '@ghostfolio/api/services/interfaces/interfaces';
import { RulesService } from '@ghostfolio/api/services/rules.service'; import { RulesService } from '@ghostfolio/api/services/rules.service';
import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper'; import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper';
import { import {
PortfolioItem,
PortfolioOverview, PortfolioOverview,
PortfolioPerformance, PortfolioPerformance,
PortfolioPosition, PortfolioPosition,
@ -30,11 +26,10 @@ import {
} from '@ghostfolio/common/types'; } from '@ghostfolio/common/types';
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { REQUEST } from '@nestjs/core'; import { REQUEST } from '@nestjs/core';
import { DataSource, Currency, Type as TypeOfOrder } from '@prisma/client'; import { Currency, DataSource, Type as TypeOfOrder } from '@prisma/client';
import Big from 'big.js'; import Big from 'big.js';
import { import {
add, add,
addMonths,
endOfToday, endOfToday,
format, format,
getDate, getDate,
@ -42,7 +37,6 @@ import {
getYear, getYear,
isAfter, isAfter,
isBefore, isBefore,
isSameDay,
max, max,
parse, parse,
parseISO, parseISO,
@ -54,7 +48,6 @@ import {
subYears subYears
} from 'date-fns'; } from 'date-fns';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import * as roundTo from 'round-to';
import { import {
HistoricalDataItem, HistoricalDataItem,
@ -83,69 +76,11 @@ export class PortfolioService {
private readonly exchangeRateDataService: ExchangeRateDataService, private readonly exchangeRateDataService: ExchangeRateDataService,
private readonly impersonationService: ImpersonationService, private readonly impersonationService: ImpersonationService,
private readonly orderService: OrderService, private readonly orderService: OrderService,
private readonly redisCacheService: RedisCacheService,
@Inject(REQUEST) private readonly request: RequestWithUser, @Inject(REQUEST) private readonly request: RequestWithUser,
private readonly rulesService: RulesService, private readonly rulesService: RulesService,
private readonly userService: UserService,
private readonly symbolProfileService: SymbolProfileService private readonly symbolProfileService: SymbolProfileService
) {} ) {}
public async createPortfolio(aUserId: string): Promise<Portfolio> {
let portfolio: Portfolio;
const stringifiedPortfolio = await this.redisCacheService.get(
`${aUserId}.portfolio`
);
const user = await this.userService.user({ id: aUserId });
if (stringifiedPortfolio) {
// Get portfolio from redis
const {
orders,
portfolioItems
}: { orders: IOrder[]; portfolioItems: PortfolioItem[] } =
JSON.parse(stringifiedPortfolio);
portfolio = new Portfolio(
this.accountService,
this.dataProviderService,
this.exchangeRateDataService,
this.rulesService
).createFromData({ orders, portfolioItems, user });
} else {
// Get portfolio from database
const orders = await this.getOrders(aUserId);
portfolio = new Portfolio(
this.accountService,
this.dataProviderService,
this.exchangeRateDataService,
this.rulesService
);
portfolio.setUser(user);
await portfolio.setOrders(orders);
// Cache data for the next time...
const portfolioData = {
orders: portfolio.getOrders(),
portfolioItems: portfolio.getPortfolioItems()
};
await this.redisCacheService.set(
`${aUserId}.portfolio`,
JSON.stringify(portfolioData)
);
}
// Enrich portfolio with current data
await portfolio.addCurrentPortfolioItems();
// Enrich portfolio with future data
await portfolio.addFuturePortfolioItems();
return portfolio;
}
public async getInvestments( public async getInvestments(
aImpersonationId: string aImpersonationId: string
): Promise<InvestmentItem[]> { ): Promise<InvestmentItem[]> {
@ -603,35 +538,49 @@ export class PortfolioService {
public async getReport(impersonationId: string): Promise<PortfolioReport> { public async getReport(impersonationId: string): Promise<PortfolioReport> {
const userId = await this.getUserId(impersonationId); const userId = await this.getUserId(impersonationId);
const portfolio = await this.createPortfolio(userId); const baseCurrency = this.request.user.Settings.currency;
const details = await portfolio.getDetails(); const { transactionPoints, orders } = await this.getTransactionPoints(
const { orders } = await this.getTransactionPoints(userId); userId
);
if (isEmpty(details)) { if (isEmpty(orders)) {
return { return {
rules: {} rules: {}
}; };
} }
const fees = this.getFees(orders); const portfolioCalculator = new PortfolioCalculator(
this.currentRateService,
this.request.user.Settings.currency
);
portfolioCalculator.setTransactionPoints(transactionPoints);
const baseCurrency = this.request.user.Settings.currency; const portfolioStart = parseDate(transactionPoints[0].date);
const currentPositions = await portfolioCalculator.getCurrentPositions(
portfolioStart
);
const portfolioItemsNow: { [symbol: string]: TimelinePosition } = {};
for (const position of currentPositions.positions) {
portfolioItemsNow[position.symbol] = position;
}
const accounts = this.getAccounts(orders, portfolioItemsNow, baseCurrency);
return { return {
rules: { rules: {
accountClusterRisk: await this.rulesService.evaluate( accountClusterRisk: await this.rulesService.evaluate(
[ [
new AccountClusterRiskInitialInvestment( new AccountClusterRiskInitialInvestment(
this.exchangeRateDataService, this.exchangeRateDataService,
details accounts
), ),
new AccountClusterRiskCurrentInvestment( new AccountClusterRiskCurrentInvestment(
this.exchangeRateDataService, this.exchangeRateDataService,
details accounts
), ),
new AccountClusterRiskSingleAccount( new AccountClusterRiskSingleAccount(
this.exchangeRateDataService, this.exchangeRateDataService,
details accounts
) )
], ],
{ baseCurrency } { baseCurrency }
@ -640,19 +589,19 @@ export class PortfolioService {
[ [
new CurrencyClusterRiskBaseCurrencyInitialInvestment( new CurrencyClusterRiskBaseCurrencyInitialInvestment(
this.exchangeRateDataService, this.exchangeRateDataService,
details currentPositions
), ),
new CurrencyClusterRiskBaseCurrencyCurrentInvestment( new CurrencyClusterRiskBaseCurrencyCurrentInvestment(
this.exchangeRateDataService, this.exchangeRateDataService,
details currentPositions
), ),
new CurrencyClusterRiskInitialInvestment( new CurrencyClusterRiskInitialInvestment(
this.exchangeRateDataService, this.exchangeRateDataService,
details currentPositions
), ),
new CurrencyClusterRiskCurrentInvestment( new CurrencyClusterRiskCurrentInvestment(
this.exchangeRateDataService, this.exchangeRateDataService,
details currentPositions
) )
], ],
{ baseCurrency } { baseCurrency }
@ -661,8 +610,8 @@ export class PortfolioService {
[ [
new FeeRatioInitialInvestment( new FeeRatioInitialInvestment(
this.exchangeRateDataService, this.exchangeRateDataService,
details, currentPositions.totalInvestment.toNumber(),
fees this.getFees(orders)
) )
], ],
{ baseCurrency } { baseCurrency }

@ -1,5 +1,8 @@
import { groupBy } from '@ghostfolio/common/helper'; import { groupBy } from '@ghostfolio/common/helper';
import { PortfolioPosition } from '@ghostfolio/common/interfaces'; import {
PortfolioPosition,
TimelinePosition
} from '@ghostfolio/common/interfaces';
import { Currency } from '@prisma/client'; import { Currency } from '@prisma/client';
import { ExchangeRateDataService } from '../services/exchange-rate-data.service'; import { ExchangeRateDataService } from '../services/exchange-rate-data.service';
@ -30,30 +33,30 @@ export abstract class Rule<T extends RuleSettings> implements RuleInterface<T> {
return this.name; return this.name;
} }
public groupPositionsByAttribute( public groupCurrentPositionsByAttribute(
aPositions: { [symbol: string]: PortfolioPosition }, positions: TimelinePosition[],
aAttribute: keyof PortfolioPosition, attribute: keyof TimelinePosition,
aBaseCurrency: Currency baseCurrency: Currency
) { ) {
return Array.from( return Array.from(groupBy(attribute, positions).entries()).map(
groupBy(aAttribute, Object.values(aPositions)).entries() ([attributeValue, objs]) => ({
).map(([attributeValue, objs]) => ({
groupKey: attributeValue, groupKey: attributeValue,
investment: objs.reduce( investment: objs.reduce(
(previousValue, currentValue) => (previousValue, currentValue) =>
previousValue + currentValue.investment, previousValue + currentValue.investment.toNumber(),
0 0
), ),
value: objs.reduce( value: objs.reduce(
(previousValue, currentValue) => (previousValue, currentValue) =>
previousValue + previousValue +
this.exchangeRateDataService.toCurrency( this.exchangeRateDataService.toCurrency(
currentValue.quantity * currentValue.marketPrice, currentValue.quantity.mul(currentValue.marketPrice).toNumber(),
currentValue.currency, currentValue.currency,
aBaseCurrency baseCurrency
), ),
0 0
) )
})); })
);
} }
} }

@ -8,7 +8,9 @@ import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.in
export class AccountClusterRiskCurrentInvestment extends Rule<Settings> { export class AccountClusterRiskCurrentInvestment extends Rule<Settings> {
public constructor( public constructor(
protected exchangeRateDataService: ExchangeRateDataService, protected exchangeRateDataService: ExchangeRateDataService,
private positions: { [symbol: string]: PortfolioPosition } private accounts: {
[account: string]: { current: number; original: number };
}
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
name: 'Current Investment' name: 'Current Investment'
@ -22,18 +24,12 @@ export class AccountClusterRiskCurrentInvestment extends Rule<Settings> {
}; };
} = {}; } = {};
Object.values(this.positions).forEach((position) => { for (const account of Object.keys(this.accounts)) {
for (const [account, { current }] of Object.entries(position.accounts)) {
if (accounts[account]?.investment) {
accounts[account].investment += current;
} else {
accounts[account] = { accounts[account] = {
investment: current, name: account,
name: account investment: this.accounts[account].current
}; };
} }
}
});
let maxItem; let maxItem;
let totalInvestment = 0; let totalInvestment = 0;

@ -8,7 +8,9 @@ import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.in
export class AccountClusterRiskInitialInvestment extends Rule<Settings> { export class AccountClusterRiskInitialInvestment extends Rule<Settings> {
public constructor( public constructor(
protected exchangeRateDataService: ExchangeRateDataService, protected exchangeRateDataService: ExchangeRateDataService,
private positions: { [symbol: string]: PortfolioPosition } private accounts: {
[account: string]: { current: number; original: number };
}
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
name: 'Initial Investment' name: 'Initial Investment'
@ -22,18 +24,12 @@ export class AccountClusterRiskInitialInvestment extends Rule<Settings> {
}; };
} = {}; } = {};
Object.values(this.positions).forEach((position) => { for (const account of Object.keys(this.accounts)) {
for (const [account, { original }] of Object.entries(position.accounts)) {
if (platforms[account]?.investment) {
platforms[account].investment += original;
} else {
platforms[account] = { platforms[account] = {
investment: original, name: account,
name: account investment: this.accounts[account].original
}; };
} }
}
});
let maxItem; let maxItem;
let totalInvestment = 0; let totalInvestment = 0;

@ -1,4 +1,3 @@
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';
@ -8,7 +7,9 @@ import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.in
export class AccountClusterRiskSingleAccount extends Rule<RuleSettings> { export class AccountClusterRiskSingleAccount extends Rule<RuleSettings> {
public constructor( public constructor(
protected exchangeRateDataService: ExchangeRateDataService, protected exchangeRateDataService: ExchangeRateDataService,
private positions: { [symbol: string]: PortfolioPosition } private accounts: {
[account: string]: { current: number; original: number };
}
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
name: 'Single Account' name: 'Single Account'
@ -16,15 +17,7 @@ export class AccountClusterRiskSingleAccount extends Rule<RuleSettings> {
} }
public evaluate() { public evaluate() {
const accounts: string[] = []; const accounts: string[] = Object.keys(this.accounts);
Object.values(this.positions).forEach((position) => {
for (const [account] of Object.entries(position.accounts)) {
if (!accounts.includes(account)) {
accounts.push(account);
}
}
});
if (accounts.length === 1) { if (accounts.length === 1) {
return { return {

@ -5,11 +5,12 @@ 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 { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.interface';
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
import { CurrentPositions } from '@ghostfolio/api/app/core/interfaces/current-positions.interface';
export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule<Settings> { export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule<Settings> {
public constructor( public constructor(
protected exchangeRateDataService: ExchangeRateDataService, protected exchangeRateDataService: ExchangeRateDataService,
private positions: { [symbol: string]: PortfolioPosition } private currentPositions: CurrentPositions
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
name: 'Current Investment: Base Currency' name: 'Current Investment: Base Currency'
@ -17,8 +18,8 @@ export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule<Setti
} }
public evaluate(ruleSettings: Settings) { public evaluate(ruleSettings: Settings) {
const positionsGroupedByCurrency = this.groupPositionsByAttribute( const positionsGroupedByCurrency = this.groupCurrentPositionsByAttribute(
this.positions, this.currentPositions.positions,
'currency', 'currency',
ruleSettings.baseCurrency ruleSettings.baseCurrency
); );

@ -1,15 +1,15 @@
import { Currency } from '@prisma/client'; import { Currency } from '@prisma/client';
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 { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.interface';
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
import { CurrentPositions } from '@ghostfolio/api/app/core/interfaces/current-positions.interface';
export class CurrencyClusterRiskBaseCurrencyInitialInvestment extends Rule<Settings> { export class CurrencyClusterRiskBaseCurrencyInitialInvestment extends Rule<Settings> {
public constructor( public constructor(
protected exchangeRateDataService: ExchangeRateDataService, protected exchangeRateDataService: ExchangeRateDataService,
private positions: { [symbol: string]: PortfolioPosition } private currentPositions: CurrentPositions
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
name: 'Initial Investment: Base Currency' name: 'Initial Investment: Base Currency'
@ -17,8 +17,8 @@ export class CurrencyClusterRiskBaseCurrencyInitialInvestment extends Rule<Setti
} }
public evaluate(ruleSettings: Settings) { public evaluate(ruleSettings: Settings) {
const positionsGroupedByCurrency = this.groupPositionsByAttribute( const positionsGroupedByCurrency = this.groupCurrentPositionsByAttribute(
this.positions, this.currentPositions.positions,
'currency', 'currency',
ruleSettings.baseCurrency ruleSettings.baseCurrency
); );

@ -1,15 +1,15 @@
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 { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.interface';
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';
import { CurrentPositions } from '@ghostfolio/api/app/core/interfaces/current-positions.interface';
export class CurrencyClusterRiskCurrentInvestment extends Rule<Settings> { export class CurrencyClusterRiskCurrentInvestment extends Rule<Settings> {
public constructor( public constructor(
public exchangeRateDataService: ExchangeRateDataService, public exchangeRateDataService: ExchangeRateDataService,
private positions: { [symbol: string]: PortfolioPosition } private currentPositions: CurrentPositions
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
name: 'Current Investment' name: 'Current Investment'
@ -17,8 +17,8 @@ export class CurrencyClusterRiskCurrentInvestment extends Rule<Settings> {
} }
public evaluate(ruleSettings: Settings) { public evaluate(ruleSettings: Settings) {
const positionsGroupedByCurrency = this.groupPositionsByAttribute( const positionsGroupedByCurrency = this.groupCurrentPositionsByAttribute(
this.positions, this.currentPositions.positions,
'currency', 'currency',
ruleSettings.baseCurrency ruleSettings.baseCurrency
); );

@ -1,15 +1,15 @@
import { Currency } from '@prisma/client'; import { Currency } from '@prisma/client';
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 { UserSettings } from '@ghostfolio/api/models/interfaces/user-settings.interface';
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface'; import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
import { CurrentPositions } from '@ghostfolio/api/app/core/interfaces/current-positions.interface';
export class CurrencyClusterRiskInitialInvestment extends Rule<Settings> { export class CurrencyClusterRiskInitialInvestment extends Rule<Settings> {
public constructor( public constructor(
protected exchangeRateDataService: ExchangeRateDataService, protected exchangeRateDataService: ExchangeRateDataService,
private positions: { [symbol: string]: PortfolioPosition } private currentPositions: CurrentPositions
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
name: 'Initial Investment' name: 'Initial Investment'
@ -17,8 +17,8 @@ export class CurrencyClusterRiskInitialInvestment extends Rule<Settings> {
} }
public evaluate(ruleSettings: Settings) { public evaluate(ruleSettings: Settings) {
const positionsGroupedByCurrency = this.groupPositionsByAttribute( const positionsGroupedByCurrency = this.groupCurrentPositionsByAttribute(
this.positions, this.currentPositions.positions,
'currency', 'currency',
ruleSettings.baseCurrency ruleSettings.baseCurrency
); );

@ -1,5 +1,4 @@
import { Currency } from '@prisma/client'; import { Currency } from '@prisma/client';
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';
@ -9,7 +8,7 @@ import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.in
export class FeeRatioInitialInvestment extends Rule<Settings> { export class FeeRatioInitialInvestment extends Rule<Settings> {
public constructor( public constructor(
protected exchangeRateDataService: ExchangeRateDataService, protected exchangeRateDataService: ExchangeRateDataService,
private positions: { [symbol: string]: PortfolioPosition }, private totalInvestment: number,
private fees: number private fees: number
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
@ -18,20 +17,7 @@ export class FeeRatioInitialInvestment extends Rule<Settings> {
} }
public evaluate(ruleSettings: Settings) { public evaluate(ruleSettings: Settings) {
const positionsGroupedByCurrency = this.groupPositionsByAttribute( const feeRatio = this.fees / this.totalInvestment;
this.positions,
'currency',
ruleSettings.baseCurrency
);
let totalInvestment = 0;
positionsGroupedByCurrency.forEach((groupItem) => {
// Calculate total investment
totalInvestment += groupItem.investment;
});
const feeRatio = this.fees / totalInvestment;
if (feeRatio > ruleSettings.threshold) { if (feeRatio > ruleSettings.threshold) {
return { return {

Loading…
Cancel
Save