floor(risk / stop / step) gave 0.28 lots instead of 0.29: the float trap in sizing
Posted: Thu Oct 29, 2026 11:08 am
floor(risk / stop / step) gave 0.28 lots instead of 0.29: the float trap in sizing
My lot size function rounded down to the broker's lot step, and for a $29 risk with a 10 pip stop it gave 0.28 instead of 0.29. Here is the bug in Python, which is the same in most languages.
0.29 can't be represented exactly in binary, so dividing by 0.01 lands a hair under 29, and floor takes it to 28. The size is then 0.28 lots, so the risk is $28 instead of $29, about 3.4% under plan. I counted the sizes from 0.01 to 1.99 in steps of 0.01 and 14 of the 199 hit this, including 0.29, 0.47, 0.57 and 1.13.
Two fixes that work. One is to add a small tolerance before flooring, with math.floor(raw / step + 1e-9) * step. The other is to use Decimal for money and sizes.
I now have a unit test that loops over every size from 0.01 to 5.00 and checks that sizing back from the risk it implies returns the same lots.
My lot size function rounded down to the broker's lot step, and for a $29 risk with a 10 pip stop it gave 0.28 instead of 0.29. Here is the bug in Python, which is the same in most languages.
Code: Select all
import math
risk, stop_pips, pip_value, step = 29.0, 10, 10.0, 0.01
raw = risk / (stop_pips * pip_value) # 0.29
print(raw / step) # 28.999999999999996
print(math.floor(raw / step) * step) # 0.28
Two fixes that work. One is to add a small tolerance before flooring, with math.floor(raw / step + 1e-9) * step. The other is to use Decimal for money and sizes.
Code: Select all
from decimal import Decimal, ROUND_DOWN
lots = Decimal(str(risk)) / (Decimal(stop_pips) * Decimal(str(pip_value)))
lots = lots.quantize(Decimal("0.01"), rounding=ROUND_DOWN) # 0.29