Describe the bug
backend_api_python/app/services/strategy_v2/runtime.py _lot_size returns 1e-8 for every Crypto: symbol. Real Binance perps use integer coin lots: TUT/USDT:USDT LOT_SIZE.stepSize = "1" (and MIN_NOTIONAL.notional = "5").
Because the per-bar liquidity cap (volume * 0.1) splits large fills across bars, a close can leave a floating-point remainder below 1e-8 coins. The remaining close order is then rejected with minimum_trade_unit, and since position.amount > 0, a strategy that gates on amount > 0 treats the symbol as still held and never re-enters it. The 1e-8-coin "dust" does not exist in production (real lots are integer coins), so it is purely an engine artifact. Net effect: backtest execution results depend on initial capital, because larger positions hit the liquidity cap more often and leave more dust.
Steps to reproduce
- Run any Strategy V2 backtest with a fixed-percentage-per-symbol strategy at
initial_capital=300 vs initial_capital=10000 over the same window.
- Inspect
result['orderLedger'] at the larger capital: a large share of orders are rejected with statusReason="minimum_trade_unit" at requestedQuantity≈9.99e-09 — repeatedly trying and failing to close a ~1e-8 residual.
- Observe trade count is materially lower at higher capital (e.g. 480 vs 212 trades on the same strategy/params).
Expected behavior
The engine should use the real per-symbol stepSize (integer coins for these perps), model MIN_NOTIONAL, and force sub-lot residuals to zero so results are independent of initial capital and match exchange behavior.
Deployment method
Docker Compose
Relevant logs
orderLedger: 269604 entries, 269252 rejected
rejected: reason=minimum_trade_unit requestedQty=9.989548743760679e-09 kind=target_percent value=0.0
Describe the bug
backend_api_python/app/services/strategy_v2/runtime.py_lot_sizereturns1e-8for everyCrypto:symbol. Real Binance perps use integer coin lots:TUT/USDT:USDTLOT_SIZE.stepSize = "1"(andMIN_NOTIONAL.notional = "5").Because the per-bar liquidity cap (
volume * 0.1) splits large fills across bars, a close can leave a floating-point remainder below 1e-8 coins. The remaining close order is then rejected withminimum_trade_unit, and sinceposition.amount > 0, a strategy that gates onamount > 0treats the symbol as still held and never re-enters it. The 1e-8-coin "dust" does not exist in production (real lots are integer coins), so it is purely an engine artifact. Net effect: backtest execution results depend on initial capital, because larger positions hit the liquidity cap more often and leave more dust.Steps to reproduce
initial_capital=300vsinitial_capital=10000over the same window.result['orderLedger']at the larger capital: a large share of orders are rejected withstatusReason="minimum_trade_unit"atrequestedQuantity≈9.99e-09— repeatedly trying and failing to close a ~1e-8 residual.Expected behavior
The engine should use the real per-symbol
stepSize(integer coins for these perps), modelMIN_NOTIONAL, and force sub-lot residuals to zero so results are independent of initial capital and match exchange behavior.Deployment method
Docker Compose
Relevant logs