Quant500 Academy

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:

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.

The rest of Quant500

What you are learning, applied to the index every day:

The model portfolio, weekly in your inbox

Every Saturday, its week in one minute: how it went, the best and the worst, and which insiders bought with their own money. Plus, the day the model is rebuilt, which stocks come in and which go out. A simulation, not advice. At most one email a week, no ads, no account; one-click unsubscribe. Made by one person: Pol Gracia.

A model portfolio with simulated results, not a personalized recommendation. Your email is used for this only: privacy.

Behind the Academy and the rest of Quant500 there is one person: Pol Gracia Serrahima.