Backtest Position Sizing Fix: Enforce Exposure Cap
Problem
Backtest position sizing could produce very large notionals when using risk-based sizing with tight stop losses because no exposure/leverage cap was enforced.
Example:
- Risk amount =
balance * riskPerTrade - Size =
riskAmount / (entryPrice * stopLossPct) - Notional =
size * entryPrice = riskAmount / stopLossPct
With small stop percentages this can exceed account balance by large multiples.
Fix Implemented
Added a hard exposure cap to the sizing engine:
- New config field:
maxLeverage - Default:
1(no leverage) - Applied to both:
- engine-computed size
- strategy-provided size overrides (
signal.size)
Capping rule:
maxNotional = balance * maxLeveragemaxSize = maxNotional / entryPricefinalSize = min(requestedSize, maxSize)
Files Changed
services/backend/src/lib/backtest-engine/runner.tsservices/backend/src/lib/backtest-engine/types.tsshared/types.ts
Runtime Metadata
When opening a position, signals now also include:
position.maxLeverageposition.cappedByExposureLimit
This makes it auditable when a trade size was reduced by the cap.
Validation
pnpm typecheckpassed.pnpm lintpassed with existing pre-existing warnings only.
Deployments
- Dev backend version:
555d882a-5b1b-41d9-ba90-f3418ec8eb76 - Production backend version:
5d4b860e-cae8-4b03-81b6-293b593b7ad2
Impact
- Existing completed backtests are unchanged (historical records).
- New backtests/reruns now enforce capped exposure by default.