Browse Source

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 <noreply@anthropic.com>
pull/7960/head
minhal128 23 hours ago
parent
commit
c59488d1b7
  1. 6
      CHANGELOG.md
  2. 68
      apps/api/src/models/rules/fees/fee-ratio-total-investment-volume.spec.ts
  3. 4
      apps/api/src/models/rules/fees/fee-ratio-total-investment-volume.ts

6
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/), 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). 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 ## 3.74.0 - 2026-09-27
### Added ### Added

68
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' });
});
});

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

@ -44,8 +44,8 @@ export class FeeRatioTotalInvestmentVolume extends Rule<Settings> {
id: 'rule.feeRatioTotalInvestmentVolume.false', id: 'rule.feeRatioTotalInvestmentVolume.false',
languageCode: this.getLanguageCode(), languageCode: this.getLanguageCode(),
placeholders: { placeholders: {
feeRatio: (ruleSettings.thresholdMax * 100).toFixed(2), feeRatio: (feeRatio * 100).toPrecision(3),
thresholdMax: (feeRatio * 100).toPrecision(3) thresholdMax: (ruleSettings.thresholdMax * 100).toFixed(2)
} }
}), }),
value: false value: false

Loading…
Cancel
Save