Browse Source

Clean up initial (original) values from X-Ray

pull/1914/head
Thomas 2 years ago
parent
commit
f21f98e509
  1. 3
      apps/api/src/app/portfolio/portfolio.controller.ts
  2. 37
      apps/api/src/app/portfolio/portfolio.service.ts
  3. 2
      apps/api/src/models/rules/account-cluster-risk/current-investment.ts
  4. 88
      apps/api/src/models/rules/account-cluster-risk/initial-investment.ts
  5. 2
      apps/api/src/models/rules/currency-cluster-risk/base-currency-current-investment.ts
  6. 71
      apps/api/src/models/rules/currency-cluster-risk/base-currency-initial-investment.ts
  7. 2
      apps/api/src/models/rules/currency-cluster-risk/current-investment.ts
  8. 72
      apps/api/src/models/rules/currency-cluster-risk/initial-investment.ts
  9. 2
      apps/api/src/models/rules/fees/fee-ratio-initial-investment.ts
  10. 1
      libs/common/src/lib/interfaces/portfolio-details.interface.ts

3
apps/api/src/app/portfolio/portfolio.controller.ts

@ -136,9 +136,8 @@ export class PortfolioController {
portfolioPosition.value / totalValue; portfolioPosition.value / totalValue;
} }
for (const [name, { current, original }] of Object.entries(accounts)) { for (const [name, { current }] of Object.entries(accounts)) {
accounts[name].current = current / totalValue; accounts[name].current = current / totalValue;
accounts[name].original = original / totalInvestment;
} }
} }

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

@ -7,12 +7,9 @@ import { PortfolioOrder } from '@ghostfolio/api/app/portfolio/interfaces/portfol
import { TransactionPoint } from '@ghostfolio/api/app/portfolio/interfaces/transaction-point.interface'; import { TransactionPoint } from '@ghostfolio/api/app/portfolio/interfaces/transaction-point.interface';
import { UserService } from '@ghostfolio/api/app/user/user.service'; import { UserService } from '@ghostfolio/api/app/user/user.service';
import { AccountClusterRiskCurrentInvestment } from '@ghostfolio/api/models/rules/account-cluster-risk/current-investment'; import { AccountClusterRiskCurrentInvestment } from '@ghostfolio/api/models/rules/account-cluster-risk/current-investment';
import { AccountClusterRiskInitialInvestment } from '@ghostfolio/api/models/rules/account-cluster-risk/initial-investment';
import { AccountClusterRiskSingleAccount } from '@ghostfolio/api/models/rules/account-cluster-risk/single-account'; import { AccountClusterRiskSingleAccount } from '@ghostfolio/api/models/rules/account-cluster-risk/single-account';
import { CurrencyClusterRiskBaseCurrencyCurrentInvestment } from '@ghostfolio/api/models/rules/currency-cluster-risk/base-currency-current-investment'; import { CurrencyClusterRiskBaseCurrencyCurrentInvestment } from '@ghostfolio/api/models/rules/currency-cluster-risk/base-currency-current-investment';
import { CurrencyClusterRiskBaseCurrencyInitialInvestment } from '@ghostfolio/api/models/rules/currency-cluster-risk/base-currency-initial-investment';
import { CurrencyClusterRiskCurrentInvestment } from '@ghostfolio/api/models/rules/currency-cluster-risk/current-investment'; import { CurrencyClusterRiskCurrentInvestment } from '@ghostfolio/api/models/rules/currency-cluster-risk/current-investment';
import { CurrencyClusterRiskInitialInvestment } from '@ghostfolio/api/models/rules/currency-cluster-risk/initial-investment';
import { FeeRatioInitialInvestment } from '@ghostfolio/api/models/rules/fees/fee-ratio-initial-investment'; import { FeeRatioInitialInvestment } from '@ghostfolio/api/models/rules/fees/fee-ratio-initial-investment';
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
@ -619,8 +616,7 @@ export class PortfolioService {
balance: 0, balance: 0,
currency: userCurrency, currency: userCurrency,
current: emergencyFundInCash, current: emergencyFundInCash,
name: UNKNOWN_KEY, name: UNKNOWN_KEY
original: emergencyFundInCash
}; };
holdings[userCurrency] = { holdings[userCurrency] = {
@ -1185,10 +1181,6 @@ export class PortfolioService {
rules: { rules: {
accountClusterRisk: await this.rulesService.evaluate( accountClusterRisk: await this.rulesService.evaluate(
[ [
new AccountClusterRiskInitialInvestment(
this.exchangeRateDataService,
accounts
),
new AccountClusterRiskCurrentInvestment( new AccountClusterRiskCurrentInvestment(
this.exchangeRateDataService, this.exchangeRateDataService,
accounts accounts
@ -1202,18 +1194,10 @@ export class PortfolioService {
), ),
currencyClusterRisk: await this.rulesService.evaluate( currencyClusterRisk: await this.rulesService.evaluate(
[ [
new CurrencyClusterRiskBaseCurrencyInitialInvestment(
this.exchangeRateDataService,
positions
),
new CurrencyClusterRiskBaseCurrencyCurrentInvestment( new CurrencyClusterRiskBaseCurrencyCurrentInvestment(
this.exchangeRateDataService, this.exchangeRateDataService,
positions positions
), ),
new CurrencyClusterRiskInitialInvestment(
this.exchangeRateDataService,
positions
),
new CurrencyClusterRiskCurrentInvestment( new CurrencyClusterRiskCurrentInvestment(
this.exchangeRateDataService, this.exchangeRateDataService,
positions positions
@ -1779,12 +1763,7 @@ export class PortfolioService {
account.currency, account.currency,
userCurrency userCurrency
), ),
name: account.name, name: account.name
original: this.exchangeRateDataService.toCurrency(
account.balance,
account.currency,
userCurrency
)
}; };
for (const order of ordersByAccount) { for (const order of ordersByAccount) {
@ -1793,30 +1772,20 @@ export class PortfolioService {
(portfolioItemsNow[order.SymbolProfile.symbol]?.marketPrice ?? (portfolioItemsNow[order.SymbolProfile.symbol]?.marketPrice ??
order.unitPrice ?? order.unitPrice ??
0); 0);
let originalValueOfSymbolInBaseCurrency =
this.exchangeRateDataService.toCurrency(
order.quantity * order.unitPrice,
order.SymbolProfile.currency,
userCurrency
);
if (order.type === 'SELL') { if (order.type === 'SELL') {
currentValueOfSymbolInBaseCurrency *= -1; currentValueOfSymbolInBaseCurrency *= -1;
originalValueOfSymbolInBaseCurrency *= -1;
} }
if (accounts[order.Account?.id || UNKNOWN_KEY]?.current) { if (accounts[order.Account?.id || UNKNOWN_KEY]?.current) {
accounts[order.Account?.id || UNKNOWN_KEY].current += accounts[order.Account?.id || UNKNOWN_KEY].current +=
currentValueOfSymbolInBaseCurrency; currentValueOfSymbolInBaseCurrency;
accounts[order.Account?.id || UNKNOWN_KEY].original +=
originalValueOfSymbolInBaseCurrency;
} else { } else {
accounts[order.Account?.id || UNKNOWN_KEY] = { accounts[order.Account?.id || UNKNOWN_KEY] = {
balance: 0, balance: 0,
currency: order.Account?.currency, currency: order.Account?.currency,
current: currentValueOfSymbolInBaseCurrency, current: currentValueOfSymbolInBaseCurrency,
name: account.name, name: account.name
original: originalValueOfSymbolInBaseCurrency
}; };
} }
} }

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

@ -14,7 +14,7 @@ export class AccountClusterRiskCurrentInvestment extends Rule<Settings> {
private accounts: PortfolioDetails['accounts'] private accounts: PortfolioDetails['accounts']
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
name: 'Current Investment' name: 'Investment'
}); });
} }

88
apps/api/src/models/rules/account-cluster-risk/initial-investment.ts

@ -1,88 +0,0 @@
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import {
PortfolioDetails,
PortfolioPosition,
UserSettings
} from '@ghostfolio/common/interfaces';
import { Rule } from '../../rule';
export class AccountClusterRiskInitialInvestment extends Rule<Settings> {
public constructor(
protected exchangeRateDataService: ExchangeRateDataService,
private accounts: PortfolioDetails['accounts']
) {
super(exchangeRateDataService, {
name: 'Initial Investment'
});
}
public evaluate(ruleSettings?: Settings) {
const accounts: {
[symbol: string]: Pick<PortfolioPosition, 'name'> & {
investment: number;
};
} = {};
for (const [accountId, account] of Object.entries(this.accounts)) {
accounts[accountId] = {
name: account.name,
investment: account.original
};
}
let maxItem;
let totalInvestment = 0;
for (const account of Object.values(accounts)) {
if (!maxItem) {
maxItem = account;
}
// Calculate total investment
totalInvestment += account.investment;
// Find maximum
if (account.investment > maxItem?.investment) {
maxItem = account;
}
}
const maxInvestmentRatio = maxItem.investment / totalInvestment;
if (maxInvestmentRatio > ruleSettings.threshold) {
return {
evaluation: `Over ${
ruleSettings.threshold * 100
}% of your initial investment is at ${maxItem.name} (${(
maxInvestmentRatio * 100
).toPrecision(3)}%)`,
value: false
};
}
return {
evaluation: `The major part of your initial investment is at ${
maxItem.name
} (${(maxInvestmentRatio * 100).toPrecision(3)}%) and does not exceed ${
ruleSettings.threshold * 100
}%`,
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
apps/api/src/models/rules/currency-cluster-risk/base-currency-current-investment.ts

@ -10,7 +10,7 @@ export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule<Setti
private positions: TimelinePosition[] private positions: TimelinePosition[]
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
name: 'Current Investment: Base Currency' name: 'Investment: Base Currency'
}); });
} }

71
apps/api/src/models/rules/currency-cluster-risk/base-currency-initial-investment.ts

@ -1,71 +0,0 @@
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { TimelinePosition, UserSettings } from '@ghostfolio/common/interfaces';
import { Rule } from '../../rule';
export class CurrencyClusterRiskBaseCurrencyInitialInvestment extends Rule<Settings> {
public constructor(
protected exchangeRateDataService: ExchangeRateDataService,
private positions: TimelinePosition[]
) {
super(exchangeRateDataService, {
name: 'Initial Investment: Base Currency'
});
}
public evaluate(ruleSettings: Settings) {
const positionsGroupedByCurrency = this.groupCurrentPositionsByAttribute(
this.positions,
'currency',
ruleSettings.baseCurrency
);
let maxItem = positionsGroupedByCurrency[0];
let totalInvestment = 0;
positionsGroupedByCurrency.forEach((groupItem) => {
// Calculate total investment
totalInvestment += groupItem.investment;
// Find maximum
if (groupItem.investment > maxItem.investment) {
maxItem = groupItem;
}
});
const baseCurrencyItem = positionsGroupedByCurrency.find((item) => {
return item.groupKey === ruleSettings.baseCurrency;
});
const baseCurrencyInvestmentRatio =
baseCurrencyItem?.investment / totalInvestment || 0;
if (maxItem.groupKey !== ruleSettings.baseCurrency) {
return {
evaluation: `The major part of your initial investment is not in your base currency (${(
baseCurrencyInvestmentRatio * 100
).toPrecision(3)}% in ${ruleSettings.baseCurrency})`,
value: false
};
}
return {
evaluation: `The major part of your initial investment is in your base currency (${(
baseCurrencyInvestmentRatio * 100
).toPrecision(3)}% in ${ruleSettings.baseCurrency})`,
value: true
};
}
public getSettings(aUserSettings: UserSettings): Settings {
return {
baseCurrency: aUserSettings.baseCurrency,
isActive: true
};
}
}
interface Settings extends RuleSettings {
baseCurrency: string;
}

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

@ -10,7 +10,7 @@ export class CurrencyClusterRiskCurrentInvestment extends Rule<Settings> {
private positions: TimelinePosition[] private positions: TimelinePosition[]
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
name: 'Current Investment' name: 'Investment'
}); });
} }

72
apps/api/src/models/rules/currency-cluster-risk/initial-investment.ts

@ -1,72 +0,0 @@
import { RuleSettings } from '@ghostfolio/api/models/interfaces/rule-settings.interface';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { TimelinePosition, UserSettings } from '@ghostfolio/common/interfaces';
import { Rule } from '../../rule';
export class CurrencyClusterRiskInitialInvestment extends Rule<Settings> {
public constructor(
protected exchangeRateDataService: ExchangeRateDataService,
private positions: TimelinePosition[]
) {
super(exchangeRateDataService, {
name: 'Initial Investment'
});
}
public evaluate(ruleSettings: Settings) {
const positionsGroupedByCurrency = this.groupCurrentPositionsByAttribute(
this.positions,
'currency',
ruleSettings.baseCurrency
);
let maxItem = positionsGroupedByCurrency[0];
let totalInvestment = 0;
positionsGroupedByCurrency.forEach((groupItem) => {
// Calculate total investment
totalInvestment += groupItem.investment;
// Find maximum
if (groupItem.investment > maxItem.investment) {
maxItem = groupItem;
}
});
const maxInvestmentRatio = maxItem.investment / totalInvestment;
if (maxInvestmentRatio > ruleSettings.threshold) {
return {
evaluation: `Over ${
ruleSettings.threshold * 100
}% of your initial investment is in ${maxItem.groupKey} (${(
maxInvestmentRatio * 100
).toPrecision(3)}%)`,
value: false
};
}
return {
evaluation: `The major part of your initial investment is in ${
maxItem.groupKey
} (${(maxInvestmentRatio * 100).toPrecision(3)}%) and does not exceed ${
ruleSettings.threshold * 100
}%`,
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
apps/api/src/models/rules/fees/fee-ratio-initial-investment.ts

@ -11,7 +11,7 @@ export class FeeRatioInitialInvestment extends Rule<Settings> {
private fees: number private fees: number
) { ) {
super(exchangeRateDataService, { super(exchangeRateDataService, {
name: 'Initial Investment' name: 'Investment'
}); });
} }

1
libs/common/src/lib/interfaces/portfolio-details.interface.ts

@ -10,7 +10,6 @@ export interface PortfolioDetails {
currency: string; currency: string;
current: number; current: number;
name: string; name: string;
original: number;
}; };
}; };
filteredValueInBaseCurrency?: number; filteredValueInBaseCurrency?: number;

Loading…
Cancel
Save