Exercise · m03_sizing.py
Position sizing, R-multiples, expectancy and SQN
not run yet
Not saved: sign up to keep your code"""Exercise: position sizing, R-multiples, expectancy and SQN (notes §3.1, §3.9).
Implement every function, then run from the repo root:
cd exercises && .venv/bin/python -m pytest modules/M03-betting-kelly-ruin/tests/test_m03_sizing.py
numpy and math only. Provided: toolkit.stats.max_drawdown(equity), the most negative
E_t / running max - 1 (a fraction <= 0, the M02 convention); it takes one curve per row.
"""
from __future__ import annotations
import math # noqa: F401
import numpy as np
from toolkit.stats import max_drawdown # noqa: F401 (provided)
def position_size(equity: float, risk_frac: float, entry: float, stop: float, lot_size: int = 1) -> int:
"""Fixed-fractional ("percent risk") sizing (notes eq. 3.23):
units = floor(equity * risk_frac / |entry - stop|),
then rounded DOWN to a whole number of lots (a multiple of lot_size).
Works for longs (stop < entry) and shorts (stop > entry).
Raise ValueError if entry == stop. Guard the floor against float error
(e.g. add 1e-9 before flooring).
"""
raise NotImplementedError
def r_multiples(pnl, initial_risk) -> np.ndarray:
"""R_i = pnl_i / risk_i where risk_i = |entry - stop| * quantity (1R) for trade i.
initial_risk may be a scalar or an array. Raise ValueError if any risk <= 0.
"""
raise NotImplementedError
def expectancy(R) -> float:
"""Expectancy in R units: the mean R-multiple (notes eq. 3.1)."""
raise NotImplementedError
def expectancy_from_stats(win_rate: float, avg_win_R: float, avg_loss_R: float) -> float:
"""E = p W - (1 - p) L, with W and L the average win and average loss in R (both > 0)."""
raise NotImplementedError
def sqn(R, cap: int | None = 100) -> float:
"""Tharp's System Quality Number: sqrt(N) * mean(R) / sd(R), sd with ddof=1,
N = min(len(R), cap) (no cap if cap is None). It is a t-statistic for the
mean R with the sample size capped (notes §3.1)."""
raise NotImplementedError
def equity_curve_fixed_fraction(R, risk_frac: float, equity0: float = 1.0) -> np.ndarray:
"""Equity when each trade risks risk_frac of current equity:
E_0 = equity0, E_i = E_{i-1} (1 + risk_frac R_i). Return length len(R) + 1."""
raise NotImplementedError
def bootstrap_mdd(R, risk_frac: float, n_trades: int, n_paths: int, seed: int) -> np.ndarray:
"""Distribution of max drawdown by resampling trades with replacement:
rng = np.random.default_rng(seed); draws = R[rng.integers(0, len(R), (n_paths, n_trades))];
build each equity curve (starting at 1) and return the n_paths max drawdowns in the
toolkit convention (<= 0; -0.11 is an 11% drawdown). toolkit.stats.max_drawdown(curves)
takes one curve per row."""
raise NotImplementedError
def kelly_from_R(R) -> float:
"""Empirical Kelly risk fraction: the f maximising mean(ln(1 + f R_i)).
The derivative mean(R_i / (1 + f R_i)) is decreasing in f; bisect for its root
on [0, 1/|min R|). Return 0.0 if mean(R) <= 0. For a two-outcome R list
(+W w.p. p, -1 w.p. q) this equals p - q/W."""
raise NotImplementedError
No runs yet
The stub's functions raise
NotImplementedError until you write them. Fill in one, press
Run tests, and watch its tests turn green.Reference solution
Available after your first run, with an account approved for code runs.