From c59488d1b75935972a3ea49c00d087ccff13d066 Mon Sep 17 00:00:00 2001 From: minhal128 Date: Sun, 27 Sep 2026 21:58:01 +0500 Subject: [PATCH] Fix swapped fee ratio and threshold in Fee Ratio rule message The violation branch of FeeRatioTotalInvestmentVolume fed the threshold into the feeRatio placeholder and the actual ratio into thresholdMax, so the X-ray message reported the two values the wrong way round. The passing branch of the same method already had them correct. With fees of 100 on an investment volume of 1000 and the default 1% threshold, the message read: The fees do exceed 10.0% of the total investment volume (1.00%) instead of: The fees do exceed 1.00% of the total investment volume (10.0%) Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 6 ++ .../fee-ratio-total-investment-volume.spec.ts | 68 +++++++++++++++++++ .../fees/fee-ratio-total-investment-volume.ts | 4 +- 3 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 apps/api/src/models/rules/fees/fee-ratio-total-investment-volume.spec.ts 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