diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e705c788e..5c28a30af0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Fixed + +- Fixed the swapped fee ratio and threshold in the message of the _Fee Ratio_ rule in the _X-ray_ section + ## 3.74.0 - 2026-09-27 ### Added diff --git a/apps/api/src/models/rules/fees/fee-ratio-total-investment-volume.spec.ts b/apps/api/src/models/rules/fees/fee-ratio-total-investment-volume.spec.ts new file mode 100644 index 0000000000..7d95d0cb3a --- /dev/null +++ b/apps/api/src/models/rules/fees/fee-ratio-total-investment-volume.spec.ts @@ -0,0 +1,68 @@ +import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; +import { I18nService } from '@ghostfolio/api/services/i18n/i18n.service'; + +import { FeeRatioTotalInvestmentVolume } from './fee-ratio-total-investment-volume'; + +describe('FeeRatioTotalInvestmentVolume', () => { + let getTranslation: jest.Mock; + let i18nService: I18nService; + + beforeEach(() => { + getTranslation = jest.fn().mockReturnValue(''); + i18nService = { getTranslation } as unknown as I18nService; + }); + + function evaluate({ + fees, + thresholdMax, + totalInvestmentVolumeInBaseCurrency + }: { + fees: number; + thresholdMax: number; + totalInvestmentVolumeInBaseCurrency: number; + }) { + const rule = new FeeRatioTotalInvestmentVolume({ + fees, + i18nService, + totalInvestmentVolumeInBaseCurrency, + exchangeRateDataService: null as unknown as ExchangeRateDataService, + languageCode: 'en' + }); + + const { value } = rule.evaluate({ + thresholdMax, + baseCurrency: 'USD', + isActive: true, + locale: 'en-US' + }); + + return { + placeholders: getTranslation.mock.calls[0][0].placeholders, + value + }; + } + + it('reports the fee ratio and the threshold when the threshold is exceeded', () => { + // The fees are 10% of the investment volume, against a 1% threshold. + const { placeholders, value } = evaluate({ + fees: 100, + thresholdMax: 0.01, + totalInvestmentVolumeInBaseCurrency: 1000 + }); + + expect(value).toBe(false); + expect(placeholders).toEqual({ feeRatio: '10.0', thresholdMax: '1.00' }); + }); + + it('reports the fee ratio and the threshold when the threshold is not exceeded', () => { + // The fees are 1% of the investment volume, against a 10% threshold. + const { placeholders, value } = evaluate({ + fees: 10, + thresholdMax: 0.1, + totalInvestmentVolumeInBaseCurrency: 1000 + }); + + expect(value).toBe(true); + expect(placeholders).toEqual({ feeRatio: '1.00', thresholdMax: '10.00' }); + }); +}); diff --git a/apps/api/src/models/rules/fees/fee-ratio-total-investment-volume.ts b/apps/api/src/models/rules/fees/fee-ratio-total-investment-volume.ts index 551ad9cbf2..91db46d830 100644 --- a/apps/api/src/models/rules/fees/fee-ratio-total-investment-volume.ts +++ b/apps/api/src/models/rules/fees/fee-ratio-total-investment-volume.ts @@ -44,8 +44,8 @@ export class FeeRatioTotalInvestmentVolume extends Rule { id: 'rule.feeRatioTotalInvestmentVolume.false', languageCode: this.getLanguageCode(), placeholders: { - feeRatio: (ruleSettings.thresholdMax * 100).toFixed(2), - thresholdMax: (feeRatio * 100).toPrecision(3) + feeRatio: (feeRatio * 100).toPrecision(3), + thresholdMax: (ruleSettings.thresholdMax * 100).toFixed(2) } }), value: false