Investment fee calculation testing verifies that a portfolio or investment account is charged the amount required by its approved fee rules for the correct assets, rate and billing period. It checks calculation inputs, effective dates, tiering, proration, rounding and resulting charges or postings. Expected results should come from an independently prepared calculation based on the documented fee policy, rather than from the application under test.
Define the fee rules and inputs
Before writing test cases, turn the fee schedule into explicit rules. Identify the charge type, eligible asset base, rate, billing frequency, day-count convention, rounding method and any minimum, maximum, tier or waiver. Confirm which valuation date supplies the asset value and how the system treats cash, accrued income, unsettled trades and assets excluded from the fee base.
For a simple annual percentage fee, a common calculation is:
fee = eligible asset value × annual rate × billing-period fraction
The fraction depends on the documented day-count convention. A tiered schedule may apply each rate to a separate band, or apply a single rate based on the total balance. Those methods produce different results, so test against the specific product rule. Record whether rounding happens per day, per asset, per tier or once at the final charge; the order can affect the amount.
Check dependencies as well as the arithmetic. Fee results may depend on valuation feeds, account classifications, product configuration, rate tables, business calendars and billing-period boundaries. A stale valuation or incorrect account segment can produce a plausible-looking charge from the wrong inputs.
Build test cases around rules and boundaries
Use calculation testing and boundary value analysis to cover each rule transition. Include values immediately below, at and above tier thresholds, minimum charges, fee caps and waiver thresholds. For a balance-based fee, include a zero balance and any permitted negative or unavailable valuation state. Test the first and last day of a billing period, leap-year dates where relevant, and a rate change effective part-way through a period.
Create controlled test data with known valuations, rates, dates, currencies and account attributes. Include cases for an account that qualifies for a waiver and one that is just outside its eligibility conditions. Keep input data stable between runs so a changed result points to a code or configuration change rather than environment drift.
Expected values should be calculated independently, for example in a reviewed spreadsheet or a small reference calculation. Avoid copying the system's formula into the test oracle without an independent review, as the same misunderstanding can then appear in both places.
Worked example: annual management fee
Assume the approved rule charges 0.75% per year on a constant eligible balance, uses an Actual/365 convention, bills for 30 days and rounds the final amount to the nearest penny. The balance is £120,000, with no minimum, cap, tax or waiver in scope.
£120,000 × 0.0075 × 30 ÷ 365 = £73.9726...
The expected fee is £73.97. Assert the calculated amount and the inputs used, including the £120,000 balance, 0.75% rate and 30-day period. Add separate cases for a different day-count convention and for a period that crosses a rate's effective date. Do not reuse £73.97 as the expected result if any of those assumptions changes.
Verify outputs and failure handling
Check the calculation result at the relevant interface, then follow the charge through any invoice, account transaction, subledger or general ledger posting in scope. Verify the currency, sign, effective date, account and fee description. Reconcile totals by account and billing run, and check that retries or reruns do not create duplicate charges where the process is expected to be idempotent.
Test how the system responds to missing or invalid inputs, such as an absent valuation, unsupported currency or missing fee schedule. The expected behaviour should be specified: for example, reject the calculation, hold it for review or return a controlled error. Confirm that the system does not silently substitute a stale value or charge a default rate unless the approved rule explicitly allows that behaviour.
Retain test inputs, rule version, independent expected calculations, outputs and posting references as evidence. This makes a failed assertion easier to trace to the valuation, configuration, calculation logic or downstream processing. Related coverage may sit within financial services testing, with specialist cases for Banking rounding and precision testing, Private bank portfolio fee tier testing and Banking test data management.
Frequently asked questions
How should performance fees be tested?
Test performance fees against the documented calculation method, including the measurement period, eligible gains, benchmark or hurdle, crystallisation date and any high-water mark. Prepare scenarios for gains below the hurdle, gains that cross it, losses and recovery after a loss, then verify both the fee and the state carried into the next period.
What should happen when a portfolio valuation is missing?
The system should follow an explicit control rule, such as holding the fee run for review or rejecting the affected calculation with a traceable error. Test that missing data does not silently become zero or trigger use of an old valuation unless that fallback is specifically authorised.
How should a corrected valuation affect a fee already charged?
Test the documented correction workflow from the revised valuation through recalculation and any adjustment transaction. Verify that the original charge remains traceable, the correction uses the applicable rule version, and rerunning the correction does not apply the adjustment twice.
Should taxes and transaction costs be included in an investment fee test?
Treat taxes and transaction costs as separate components unless the fee specification explicitly includes them in the calculation. Test their calculation, presentation and posting independently, then verify that account totals include each component once.
