Position sizing: fixed-fractional risk
Risk a fixed percentage of equity per trade and let the stop distance decide your size. The workhorse method that anchors everything else.
The single most important number in your trading is not the entry price — it is the position size. Get it wrong and a good strategy still blows up (last lesson); get it right and a mediocre strategy survives long enough to earn. The workhorse method used by most professional discretionary and systematic traders is fixed-fractional sizing, also called "percent-risk" sizing.
The formula
You need three inputs: your equity, the risk fraction (how much of that equity you will lose if stopped out), and the stop distance (how far, in price, from entry to stop). The size falls straight out:
def position_size(equity, risk_pct, entry, stop):
class="s">""class="s">"
equity : account value in currency
risk_pct : fraction of equity to risk, e.g. class="n">0.01 for class="n">1%
entry : planned entry price
stop : stop-loss price
returns : number of units (shares / contracts / coins)
"class="s">""
risk_amount = equity * risk_pct class="c"># currency you'll lose if stopped
stop_distance = abs(entry - stop) class="c"># loss per unit
if stop_distance == class="n">0:
raise ValueError(class="s">"stop must differ from entry")
return risk_amount / stop_distance
size = position_size(equity=25_000, risk_pct=class="n">0.01,
entry=class="n">100.0, stop=class="n">96.0)
print(fclass="s">"Buy {size:.0f} units") class="c"># Buy class="n">62 units
print(fclass="s">"Capital deployed: {size * class="n">100:,.0f}") class="c"># class="n">6,class="n">250
print(fclass="s">"Risk if stopped: {size * class="n">4:,.0f}") class="c"># class="n">250 == class="n">1% of class="n">25,class="n">000Read the last two comments carefully. We deployed 6,250 of capital — a quarter of the account — but the *actual risk* is only 250, exactly 1% of equity, because the stop is just 4 points away. Capital deployed and capital at risk are completely different quantities. Fixed-fractional sizing controls the one that matters: the loss you take when you are wrong.
Why let the stop set the size?
Because it makes every trade an equal-risk bet. A wide-stop trade and a tight-stop trade both lose the same 1% if they fail. This decouples "how confident is the stop placement" from "how much am I risking" — you place the stop where the chart says it belongs, and the formula shrinks or grows the size to keep the risk constant.
equity, risk_pct = 25_000, class="n">0.01
trades = [
class="c"># (name, entry, stop)
(class="s">"Tight stop", class="n">100.0, class="n">99.0), class="c"># class="n">1-point stop
(class="s">"Medium stop", class="n">100.0, class="n">96.0), class="c"># class="n">4-point stop
(class="s">"Wide stop", class="n">100.0, class="n">90.0), class="c"># class="n">10-point stop
(class="s">"Cheap stock", class="n">20.0, class="n">19.5), class="c"># class="n">0.5-point stop
]
for name, entry, stop in trades:
size = position_size(equity, risk_pct, entry, stop)
deployed = size * entry
risk = size * abs(entry - stop)
print(fclass="s">"{name:12s} {size:class="n">6.0f} units "
fclass="s">"deployed {deployed:class="n">8,.0f} risk {risk:class="n">6,.0f}")
class="c"># Tight stop class="n">250 units deployed class="n">25,class="n">000 risk class="n">250
class="c"># Medium stop class="n">62 units deployed class="n">6,class="n">250 risk class="n">250
class="c"># Wide stop class="n">25 units deployed class="n">2,class="n">500 risk class="n">250
class="c"># Cheap stock class="n">500 units deployed class="n">10,class="n">000 risk class="n">250Four completely different position sizes — from 25 units to 500 — yet the risk column is a flat 250 every time. That is the point of the method: you normalise risk, not size. Note the tight-stop trade deploys the full account; a sensible system adds a cap so a single position cannot use more than, say, 30% of capital even when the stop is very close.
Choosing the risk fraction
How big should risk_pct be? This is a judgement about survival, informed directly by the risk-of-ruin maths from the last lesson. Common practice:
- 0.25%–0.5% per trade — conservative; for larger accounts, higher trade frequency, or strategies you do not fully trust yet.
- 1% per trade — the classic default. A run of 10 straight losses costs roughly 10% — painful but easily recoverable.
- 2% per trade — aggressive; the upper bound most professionals will accept. Ten straight losses now costs ~18%.
- Above 2% — you are gambling on not hitting a losing streak. The maths says you eventually will.
Putting it in a trade loop
class FixedFractionalSizer:
def __init__(self, risk_pct=class="n">0.01, max_deploy_pct=class="n">0.30):
self.risk_pct = risk_pct
self.max_deploy_pct = max_deploy_pct
def size(self, equity, entry, stop):
raw = position_size(equity, self.risk_pct, entry, stop)
class="c"># cap capital deployed so a tight stop can't use the whole account
cap = (equity * self.max_deploy_pct) / entry
return min(raw, cap)
sizer = FixedFractionalSizer(risk_pct=class="n">0.01)
class="c"># on a signal:
equity = 25_000
units = sizer.size(equity, entry=class="n">100.0, stop=class="n">99.0)
print(fclass="s">"{units:.0f} units") class="c"># class="n">75 units (capped at class="n">30% = class="n">7,class="n">500), not class="n">250Fixed-fractional sizing is the foundation. But it has a blind spot: it treats a 4-point stop the same whether the market is dead calm or violently volatile. In a quiet market 4 points might be enormous; in a wild one it might be nothing. The next lesson fixes that by sizing off volatility itself.