Browse Source

Feature/add static portfolio analysis fee ratio rule based on total investment volume (#6332)

* Add fee ratio rule based on total investment volume

* Update changelog
pull/6333/head
Thomas Kaul 2 weeks ago
committed by GitHub
parent
commit
e59421f008
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      CHANGELOG.md
  2. 8
      apps/api/src/app/portfolio/portfolio.service.ts
  3. 8
      apps/api/src/app/user/user.service.ts
  4. 3
      apps/api/src/models/rules/fees/fee-ratio-initial-investment.ts
  5. 102
      apps/api/src/models/rules/fees/fee-ratio-total-investment-volume.ts
  6. 11
      apps/client/src/app/pages/i18n/i18n-page.html
  7. 1
      libs/common/src/lib/interfaces/x-ray-rules-settings.interface.ts

2
CHANGELOG.md

@ -9,10 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Added a new static portfolio analysis rule based on the total investment volume: _Fees_ (Fee Ratio)
- Extended the content of the _Self-Hosting_ section on the Frequently Asked Questions (FAQ) page with information on derived currencies
### Changed
- Deprecated the existing static portfolio analysis rule: _Fees_ (Fee Ratio)
- Ignored nested ETFs when fetching top holdings for ETF and mutual fund assets from _Yahoo Finance_
- Improved the scraper configuration with more detailed error messages
- Upgraded `cheerio` from version `1.0.0` to `1.2.0`

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

@ -14,6 +14,7 @@ import { EconomicMarketClusterRiskDevelopedMarkets } from '@ghostfolio/api/model
import { EconomicMarketClusterRiskEmergingMarkets } from '@ghostfolio/api/models/rules/economic-market-cluster-risk/emerging-markets';
import { EmergencyFundSetup } from '@ghostfolio/api/models/rules/emergency-fund/emergency-fund-setup';
import { FeeRatioInitialInvestment } from '@ghostfolio/api/models/rules/fees/fee-ratio-initial-investment';
import { FeeRatioTotalInvestmentVolume } from '@ghostfolio/api/models/rules/fees/fee-ratio-total-investment-volume';
import { BuyingPower } from '@ghostfolio/api/models/rules/liquidity/buying-power';
import { RegionalMarketClusterRiskAsiaPacific } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/asia-pacific';
import { RegionalMarketClusterRiskEmergingMarkets } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/emerging-markets';
@ -1315,6 +1316,13 @@ export class PortfolioService {
userSettings.language,
summary.committedFunds,
summary.fees
),
new FeeRatioTotalInvestmentVolume(
this.exchangeRateDataService,
this.i18nService,
userSettings.language,
summary.totalBuy + summary.totalSell,
summary.fees
)
],
userSettings

8
apps/api/src/app/user/user.service.ts

@ -13,6 +13,7 @@ import { EconomicMarketClusterRiskDevelopedMarkets } from '@ghostfolio/api/model
import { EconomicMarketClusterRiskEmergingMarkets } from '@ghostfolio/api/models/rules/economic-market-cluster-risk/emerging-markets';
import { EmergencyFundSetup } from '@ghostfolio/api/models/rules/emergency-fund/emergency-fund-setup';
import { FeeRatioInitialInvestment } from '@ghostfolio/api/models/rules/fees/fee-ratio-initial-investment';
import { FeeRatioTotalInvestmentVolume } from '@ghostfolio/api/models/rules/fees/fee-ratio-total-investment-volume';
import { BuyingPower } from '@ghostfolio/api/models/rules/liquidity/buying-power';
import { RegionalMarketClusterRiskAsiaPacific } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/asia-pacific';
import { RegionalMarketClusterRiskEmergingMarkets } from '@ghostfolio/api/models/rules/regional-market-cluster-risk/emerging-markets';
@ -383,6 +384,13 @@ export class UserService {
undefined,
undefined
).getSettings(user.settings.settings),
FeeRatioTotalInvestmentVolume: new FeeRatioTotalInvestmentVolume(
undefined,
undefined,
undefined,
undefined,
undefined
).getSettings(user.settings.settings),
RegionalMarketClusterRiskAsiaPacific:
new RegionalMarketClusterRiskAsiaPacific(
undefined,

3
apps/api/src/models/rules/fees/fee-ratio-initial-investment.ts

@ -3,6 +3,9 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-
import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service';
import { RuleSettings, UserSettings } from '@ghostfolio/common/interfaces';
/**
* @deprecated This rule is deprecated in favor of FeeRatioTotalInvestmentVolume
*/
export class FeeRatioInitialInvestment extends Rule<Settings> {
private fees: number;
private totalInvestment: number;

102
apps/api/src/models/rules/fees/fee-ratio-total-investment-volume.ts

@ -0,0 +1,102 @@
import { Rule } from '@ghostfolio/api/models/rule';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service';
import { RuleSettings, UserSettings } from '@ghostfolio/common/interfaces';
export class FeeRatioTotalInvestmentVolume extends Rule<Settings> {
private fees: number;
private totalInvestmentVolumeInBaseCurrency: number;
public constructor(
protected exchangeRateDataService: ExchangeRateDataService,
private i18nService: I18nService,
languageCode: string,
totalInvestmentVolumeInBaseCurrency: number,
fees: number
) {
super(exchangeRateDataService, {
languageCode,
key: FeeRatioTotalInvestmentVolume.name
});
this.fees = fees;
this.totalInvestmentVolumeInBaseCurrency =
totalInvestmentVolumeInBaseCurrency;
}
public evaluate(ruleSettings: Settings) {
const feeRatio = this.totalInvestmentVolumeInBaseCurrency
? this.fees / this.totalInvestmentVolumeInBaseCurrency
: 0;
if (feeRatio > ruleSettings.thresholdMax) {
return {
evaluation: this.i18nService.getTranslation({
id: 'rule.feeRatioTotalInvestmentVolume.false',
languageCode: this.getLanguageCode(),
placeholders: {
feeRatio: (ruleSettings.thresholdMax * 100).toFixed(2),
thresholdMax: (feeRatio * 100).toPrecision(3)
}
}),
value: false
};
}
return {
evaluation: this.i18nService.getTranslation({
id: 'rule.feeRatioTotalInvestmentVolume.true',
languageCode: this.getLanguageCode(),
placeholders: {
feeRatio: (feeRatio * 100).toPrecision(3),
thresholdMax: (ruleSettings.thresholdMax * 100).toFixed(2)
}
}),
value: true
};
}
public getCategoryName() {
return this.i18nService.getTranslation({
id: 'rule.fees.category',
languageCode: this.getLanguageCode()
});
}
public getConfiguration() {
return {
threshold: {
max: 0.1,
min: 0,
step: 0.0025,
unit: '%'
},
thresholdMax: true
};
}
public getName() {
return this.i18nService.getTranslation({
id: 'rule.feeRatioTotalInvestmentVolume',
languageCode: this.getLanguageCode()
});
}
public getSettings({
baseCurrency,
locale,
xRayRules
}: UserSettings): Settings {
return {
baseCurrency,
locale,
isActive: xRayRules?.[this.getKey()]?.isActive ?? true,
thresholdMax: xRayRules?.[this.getKey()]?.thresholdMax ?? 0.01
};
}
}
interface Settings extends RuleSettings {
baseCurrency: string;
thresholdMax: number;
}

11
apps/client/src/app/pages/i18n/i18n-page.html

@ -149,7 +149,7 @@
<li i18n="@@rule.emergencyFundSetup.true">
An emergency fund has been set up
</li>
<li i18n="@@rule.feeRatioInitialInvestment">Fee Ratio</li>
<li i18n="@@rule.feeRatioInitialInvestment">Fee Ratio (lecacy)</li>
<li i18n="@@rule.feeRatioInitialInvestment.false">
The fees do exceed $&#123;thresholdMax&#125;% of your initial investment
($&#123;feeRatio&#125;%)
@ -158,6 +158,15 @@
The fees do not exceed $&#123;thresholdMax&#125;% of your initial
investment ($&#123;feeRatio&#125;%)
</li>
<li i18n="@@rule.feeRatioTotalInvestmentVolume">Fee Ratio</li>
<li i18n="@@rule.feeRatioTotalInvestmentVolume.false">
The fees do exceed $&#123;thresholdMax&#125;% of your total investment
volume ($&#123;feeRatio&#125;%)
</li>
<li i18n="@@rule.feeRatioTotalInvestmentVolume.true">
The fees do not exceed $&#123;thresholdMax&#125;% of your total
investment volume ($&#123;feeRatio&#125;%)
</li>
<li i18n="@@rule.fees.category">Fees</li>
<li i18n="@@rule.regionalMarketClusterRisk.category">
Regional Market Cluster Risks

1
libs/common/src/lib/interfaces/x-ray-rules-settings.interface.ts

@ -10,6 +10,7 @@ export interface XRayRulesSettings {
EconomicMarketClusterRiskEmergingMarkets?: RuleSettings;
EmergencyFundSetup?: RuleSettings;
FeeRatioInitialInvestment?: RuleSettings;
FeeRatioTotalInvestmentVolume?: RuleSettings;
RegionalMarketClusterRiskAsiaPacific?: RuleSettings;
RegionalMarketClusterRiskEmergingMarkets?: RuleSettings;
RegionalMarketClusterRiskEurope?: RuleSettings;

Loading…
Cancel
Save