2. The ladder: simulation, paper and real capital
Level 12: Testing, deploying and taking the step
You already know how to test the system against a double. That proves the code does what you said, not that the strategy makes money or that the whole thing survives contact with a real market. Between those two things there is a four-rung ladder, and the most common way of losing money with an automated system is not a programming bug: it is skipping rungs because the backtest looked so pretty.
The rule of the ladder
Each rung validates one specific thing and lies about the rest. Standing on a rung without knowing what it validates is as useless as never having stepped on it. And you do not climb on time served or on impatience: you climb when the difference between what you expected and what has happened is measured, and explained.
Rung 1: simulation validates the logic, not the execution
This is the backtest and the replay of historical data against the complete system, with the same scheduler and the same calendar. It validates the handling of gaps, idempotency, state transitions and behavior on the calendar's odd days. It validates absolutely nothing about execution: not the price you get filled at, not the latency, not the impact, not the rejections.
Its characteristic trap is subtle: a simulator that executes at the price you used to decide turns any intraday mean-reversion strategy into a money-printing machine. Reasonable duration: as long as it takes, with the out-of-sample stretch untouched.
Rung 2: the paper account validates the plumbing and lies about the price
Here you validate everything between your decision and the market: credentials, order format, time zones, order types the broker really accepts, out-of-session behavior, reconnection, and that your reconciliation squares against a real account. It is the rung that discovers your broker rounds the size differently from how you thought.
It lies in two very specific places:
- The execution price. Many paper accounts fill at the best visible price, instantly and in full. Live, you compete for that price with people faster than you.
- Market impact. Your paper orders do not exist for anybody: they consume no liquidity and do not move the book. At small size it makes no difference, and that is why you do not notice it until the size stops being small.
Reasonable duration: weeks, not days. At least one complete cycle of the strategy, enough trades for a rare failure to have a chance to show up, and a period that includes a holiday, a clock change or an expiry.
Rung 3: small capital is the first honest data
Here at last you get what no simulator was going to give you: real slippage and commissions, with your broker, your order type and your schedule. Small means two things at once: that losing it all would not change your life, and that the order size is still representative. If the minimum commission eats 3% of every trade, the result does not measure the strategy, it measures the minimum commission.
What has to be measured is already recorded by Level 10: for each order, the reference price at the moment of deciding and the average execution price.
import numpy as np, pandas as pd
ops = pd.read_parquet("ejecuciones.parquet")
# fecha, lado, precio_decision, precio_medio_ejecucion, comision, nominal
signo = np.where(ops["lado"] == "compra", 1.0, -1.0)
ops["deslizamiento_pb"] = 1e4 * signo * (
ops["precio_medio_ejecucion"] - ops["precio_decision"]
) / ops["precio_decision"]
ops["coste_total_pb"] = ops["deslizamiento_pb"] + 1e4 * ops["comision"] / ops["nominal"]
n = len(ops)
media = ops["coste_total_pb"].mean()
error_tipico = ops["coste_total_pb"].std(ddof=1) / np.sqrt(n)
print(n, round(media, 2), round(error_tipico, 2))With twenty trades you know nothing: the standard error of that mean is comparable to the mean itself. The criterion for climbing is not "the average cost looks low", but that the average cost plus two standard errors still leaves margin, and that usually requires on the order of a hundred trades.
Rung 4: real capital, in tranches and with a written criterion
import math
def puede_subir(ops, coste_supuesto_pb, margen_bruto_pb, minimo=100):
if len(ops) < minimo:
return False, "faltan operaciones para medir con sentido"
media = ops["coste_total_pb"].mean()
et = ops["coste_total_pb"].std(ddof=1) / math.sqrt(len(ops))
if media - 2 * et > coste_supuesto_pb:
return False, "el coste real supera al supuesto del backtest"
if margen_bruto_pb - (media + 2 * et) <= 0:
return False, "no queda margen despues de costes"
return True, "adelante, y por tramos"You do not go from small capital to real capital in one leap: you multiply by two or three, let enough trades go by to measure again at the new size and only then repeat. Slippage is not a constant, it grows with size relative to available liquidity. And one condition that is not statistical: you do not climb a rung and touch the strategy in the same week, because then no difference in result has a single explanation any more.
Worked scenario
An example built with figures, not a historical case. The backtest assumed 5 basis points of cost per trade and an expected gross margin of 18. The paper account confirmed the plumbing over six weeks with no surprises. With small capital, 120 trades gave an average cost of 11 basis points with a standard deviation of 9: a standard error of 0.82, so the cost lies between 9.4 and 12.6 with room to spare. The margin is still positive, but it has eaten two thirds of what was expected. On multiplying the size by five, the average cost rose to 19 basis points and the margin turned negative. None of this was visible on the first two rungs, and skipping them would have taken that size straight to the real account.