What paper trading is genuinely good for
- Proving the plumbing works — orders route, brackets attach, positions reconcile, the journal populates.
- Catching bugs that a backtest hides, such as duplicate orders, timezone errors, and stale quotes.
- Rehearsing the routine so the mechanical steps are automatic before money is involved.
- Sanity-checking that live signal frequency roughly matches what the backtest predicted.
That is a real list and it is worth doing. What it is not is evidence that the strategy makes money.
What paper does not simulate
| Friction | What paper assumes | What real money does |
| Slippage | Fills at the quoted price | Fills a few ticks worse, much worse on gaps and thin names |
| Partial fills | The whole order fills | Limit orders fill partially or not at all, changing the risk you actually took |
| Market impact | Your order does not exist | Size moves the book in illiquid names, and you trade against your own footprint |
| Short borrow | Shorts are always available | Locate may be unavailable, expensive, or recalled mid-trade |
| Halts and gaps | Orders behave normally | A stop through a gap fills far below the stop price, so the loss exceeds 1R |
| Fees and financing | Often ignored entirely | Commission, regulatory fees, and margin interest apply to every round trip |
| Queue position | Instant fill at the touch | A resting limit sits behind everyone who got there first, and only fills when the price is going against you |
| Emotion | Every signal is taken | You skip the uncomfortable ones, which are frequently the good ones |
The last row is the largest by a distance. A backtest and a paper account both take 100% of signals. In real money you will hesitate after two losses, size down after a drawdown, and take a discretionary exit at the worst moment. A strategy's realised expectancy is the strategy's expectancy multiplied by your compliance with it, and compliance is never 100%.
The queue-position row deserves a note too, because it produces a specific and nasty bias. A paper engine fills your resting buy limit the moment the price prints there. A real one fills it only if enough sellers arrive to clear the book ahead of you — which happens reliably when the market is about to keep falling, and unreliably when it is about to bounce. Paper gives you the good fills for free and charges nothing for the bad ones.
How much does friction cost?
Run the arithmetic on the only setup my walk-forward testing did not eliminate. The anchored-VWAP reclaim came out at +0.117R over 4,933 trades on a 129-symbol, 10-year universe, 95% CI +0.057 to +0.174. It is not a validated edge: a risk-matched random entry captured +0.086R on the same data, so the signal's own contribution is about +0.030R with a confidence interval crossing zero. The roughly double figure I first reported, on barely a hundred hand-picked trades, failed my own adversarial re-test and is retired. Take the headline 0.117R anyway, as the friendliest possible case, and assume a 1R risk of $300 per trade on 100 shares of a $48 stock.
modelled edge = 0.117R × $300 = $35.10 per trade
slippage 2 ticks × 2 sides on 100 shares = $4.00
commission + fees = $2.00
───────
net edge = $29.10 per trade
= 0.097R
friction = $6.00 = 0.020R = 17% of the modelled edge
Two ticks a side and two dollars of fees — a genuinely benign assumption — takes 17% of the edge before anything goes wrong. That version still survives, barely, at 0.097R. Now assume slippage of 0.05R per side rather than a few ticks, which is realistic on gappy names and small caps, and you lose 0.10R of the 0.117R headline — over 85% of the entire result, gone to execution quality alone.
And remember what the headline number actually was. If the signal's own contribution over a risk-matched random entry is about +0.030R, then $6.00 of friction on a $300 risk unit is 0.020R, which is two thirds of the residual. On that reading the setup does not survive its own costs. A small edge is not robust to sloppy fills, which is the practical reason execution discipline matters more than signal hunting.
The full friction budget
Worth building once, for your own instrument and holding period, because the items people forget are the ones that dominate. All figures below are on the same 100 shares of a $48 stock with a $300 risk unit, so 1R is $3.00 per share.
| Cost | Illustrative amount | As R | Notes |
| Slippage, entry | 2 ticks × 100 sh = $2.00 | 0.007R | Worse on gaps, thin names, and market-on-open orders |
| Slippage, exit | 2 ticks × 100 sh = $2.00 | 0.007R | Stop-outs slip more than take-profits, because they fill into momentum |
| Commission | $0 to $1.00 per side | 0 to 0.003R | Zero at most US equity brokers, per-share at institutional ones |
| Regulatory fees | Cents to about $1.00 on the sale | under 0.004R | Sell-side only, rate set annually — check the current schedule |
| Margin financing | $4,800 at 8% for 5 days = $5.26 | 0.018R | Only if on margin. Scales with notional and days, not with risk |
| Short borrow | $4,800 at 25% for 5 days = $16.44 | 0.055R | Hard-to-borrow names can run far higher, and the rate changes daily |
| Total, long, cash, 5 days | $6.00 | 0.020R | The benign case in the formula above |
| Total, short, HTB, 5 days | $22.44 | 0.075R | 64% of a 0.117R headline edge, and more than double the residual |
The bottom row is the one that ends short-side systems. A borrow rate is not a fee you pay once, it is a rate you pay per day on notional, and it has no relationship to your risk sizing. Two things follow: never quote a strategy's expectancy without saying which side it trades, and never assume the short leg of a long-short idea costs the same as the long leg.
Why a promotion gate
A promotion gate is a pre-committed, written rule that decides when a system is allowed to trade real money, evaluated by something other than your enthusiasm. Without one, the promotion decision gets made on a good week, which is precisely the wrong sample.
A gate needs three properties to be worth anything:
- Written before the paper run starts. A threshold invented afterwards is a rationalisation with a number attached.
- Sample-size based, not time based. "Two months of paper" can mean four trades. Trades are the unit that carries information.
- Enforced by something that is not you. A rule you can quietly waive at 9:28am is not a rule.
The gate I actually enforce
My execution engine refuses to connect a live broker until the paper record shows 60 closed paper trades with positive realised expectancy. It is a hard check in the promotion path, not a note in a document, and the current record against it is zero closed paper trades, so the live path stays shut. That is not modesty, it is the actual state: the gate has never been approached, let alone passed.
The two numbers are chosen for different reasons. Sixty closed trades is a compromise: it is not enough for statistical confidence, which needs a few hundred, but it is enough to expose an engine that double-fires orders, mishandles a halt, or quietly loses on fees. Requiring realised expectancy rather than modelled expectancy is the important half, because realised includes the fills you actually got.
Written as something you could implement, it is about ten lines:
def guard_promotion_gate(journal, env):
closed = journal.closed_trades()
n = len(closed)
exp_r = sum(t.realised_r for t in closed) / n if n else 0.0
if n >= 60 and exp_r > 0:
return ALLOW
if env.get("OVERRIDE_PROMOTION_GATE") == "I_ACCEPT_UNVALIDATED_RISK":
log.warning("promotion gate overridden: n=%d expectancy=%+.3fR", n, exp_r)
return ALLOW
raise PromotionBlocked(
f"gate: {n}/60 closed trades, realised expectancy {exp_r:+.3f}R")
Three design details in that block matter more than the threshold. It reads closed trades, because open positions have unrealised results you would be tempted to count. It divides by n rather than summing, so a run of tiny wins cannot substitute for a real sample. And it raises rather than returning a flag, so a caller that forgets to check the return value still cannot trade.
An override exists in the code because a gate with no escape hatch gets bypassed by editing the source, which is worse. It is named to be uncomfortable to type, it must be set as an explicit environment variable, and it logs a warning with the actual trade count and expectancy every time it is used. That is the design intent: make the unsafe path possible, obvious, and recorded, rather than impossible and therefore routed around.
The failure mode I hit: the gate and the journal disagreed
Worth reporting because it is the kind of bug that renders a gate decorative. My promotion check reads a SQLite journal maintained by the execution engine. My screener's paper runner writes a CSV journal somewhere else. Nothing bridges them — so sixty closed trades in the screener's journal would have advanced the gate by exactly zero, while looking on a dashboard like progress.
I deliberately did not auto-bridge them. Feeding a real-money gate from a system it was never written to validate defeats the purpose of the gate: the point is to prove the executor that will actually place the orders, not some other program that produced similar-looking rows. The correct fix is to run the paper phase through the executor being validated. If your gate reads from a different source than your paper engine writes to, you do not have a gate. Check that specific thing before you trust yours.
Reconciling the journal against broker fills
A promotion gate is only as good as the numbers feeding it, and the numbers are wrong more often than people assume. Reconciliation means comparing, per order, what your system thinks happened against what the broker says happened.
- Pull the broker's fill records for the day through the API, not the web UI. You want fill price, fill quantity, timestamp, and order ID.
- Match on your own client order ID, not on symbol and time. Symbol-and-time matching silently merges two orders in the same name.
- Compare intended price against fill price and record the difference as realised slippage per trade. This is the single most valuable field you will collect during paper.
- Compare intended quantity against filled quantity. Any partial fill means the R you actually took was not the R you planned, and every downstream statistic is off.
- Compare your computed position against the broker's reported position at the close, every day. A mismatch is a bug, not a rounding issue.
- Alert on any unmatched order in either direction. An order the broker has that you do not know about is the worst possible category, and it is exactly what a duplicate-fire bug produces.
Reconciliation is also what turns the friction table above from a guess into a measurement. After sixty paper trades you should be able to state your own median slippage per side in cents and in R, rather than assuming two ticks because a guide said so. The reading a backtest report guide covers the equivalent discipline for backtest output, and the journal guide covers the field list.
What to log from the very first paper fill
- Planned entry, stop, 1R per share, share count, and risk percentage — recorded before the order goes out, never reconstructed after.
- Realised entry fill, exit fill, exit reason (stop, target, trail, time stop, discretionary), and realised R.
- Slippage per side, in cents and in R, from the reconciliation step above.
- Setup tag and any sub-tags you might later want expectancy for. Retrofitting tags is guesswork; adding one costs nothing now.
- Market context at entry — index trend, the instrument's ATR — so you can later ask whether the edge was concentrated in one regime.
- Mistake tags: moved stop, skipped signal, oversized, early exit. These are the fields that eventually explain the gap between backtest and reality.
- Timestamps for signal generation, order submission, and fill. Latency between the first two is a bug; between the last two is a market condition.
A staged promotion, not a switch
| Stage | Size | Sample | What it is testing | Advance when |
| 1. Paper | Normal risk, simulated | 60 closed trades | Plumbing, order lifecycle, journal accuracy | Gate passes on closed trades and the journal reconciles to broker records |
| 2. Live minimum | 1 share, or the smallest tradeable unit | 20 trades | Real fills, real fees, and your own behaviour. Not profitability | 20 trades closed with zero reconciliation errors |
| 3. Live quarter risk | 0.25% instead of 1% | 30 trades | Whether realised R tracks paper R per setup | Realised and paper expectancy agree within a stated margin |
| 4. Live half risk | 0.5% | 30 trades | Whether stage 3 held up as size began to matter | No regime of systematic slippage growth with size |
| 5. Full risk | The planned risk percentage | Ongoing | The actual strategy | Never stop measuring |
Stage 2 is the one people skip and it is the one that earns its keep. A single share costs almost nothing and tests everything that paper cannot: whether your order actually routes, whether the fee schedule is what you assumed, whether the broker's stop order behaves the way its documentation claims, and whether you personally can watch a real loss without touching anything. None of those are strategy questions and all of them can end a strategy.
Stage 4 exists because slippage is not constant in size. A one-share order fills at the touch; a five-hundred-share order in a thin name does not. If your realised slippage per side grows meaningfully between stage 3 and stage 4, you have found the capacity limit of the strategy, and that is a fact about the strategy rather than a fact about a bad week.
Halt and demotion rules
Promotion rules without demotion rules are a ratchet. Write both, before you start, and code both.
- Drawdown halt. My engine halts at a 10% account drawdown. It works only because it is coded rather than intended.
- Consecutive-loss halt. A pause after some number of losses in a row — not because streaks predict anything, but because they reliably degrade the human in the loop.
- Demotion on divergence. If realised expectancy falls a stated distance below paper expectancy over a stated sample, drop back a stage rather than pushing through.
- Immediate halt on any reconciliation error. An unexplained fill is a code problem until proven otherwise, and code problems compound.
- Halt on infrastructure fault. Stale market data, a failed heartbeat, or an unreachable broker endpoint should stop new orders rather than fall back to guessing.
- A written restart condition for each. "Halted until I feel better" is not a rule, and neither is "halted until it comes back".
The drawdown number is not arbitrary either. Recovery is asymmetric — 10% down needs 11.1% back, 20% needs 25%, 50% needs 100% — and the curve steepens exactly where discipline is weakest. The drawdown math is here, and it is the reason a halt threshold should be well inside the drawdown you think you could tolerate.
Broker choice is part of the gate
- Prefer a broker with a real paper environment on the same API, so promotion is a credential change rather than a rewrite. If paper and live use different code paths, paper validates the wrong program.
- Avoid reverse-engineered private APIs. They violate the platform's terms of service, get accounts restricted or closed, usually offer no paper mode at all, break without notice when the vendor changes an endpoint, and often require storing a password and a second-factor secret in plain text on your machine. There is no version of that trade-off that is worth it for a retail account.
- Check that the API exposes fills, not just orders. You cannot reconcile or measure slippage without fill-level data.
- Verify the system fails closed: if configuration is missing or ambiguous, it should place no orders rather than default to live.
- Test concurrency explicitly. Twelve simultaneous signal evaluations must produce exactly one order, not twelve. Idempotency keys on order submission are the standard fix.
- Confirm bracket and stop order semantics in the sandbox. "Stop" means different things at different venues, and discovering that live is expensive.
- Read the rate limits before you write the polling loop, not after you are throttled during a stop-out.
A side-by-side on paper environments, API quality, and the terms-of-service question is in the broker comparison guide. The short version: official paper trading on the same API is worth more than commission savings, because it is the only thing that lets a promotion gate mean anything.
The pre-live checklist
- The strategy has a walk-forward result, not an in-sample fit, and you can state its confidence interval out loud.
- You have checked the result against a naive benchmark. A risk-matched random entry is the honest one, and if your signal does not clearly beat it you do not have a signal.
- Costs are modelled in the backtest at a level you can defend, and the edge still exists afterwards.
- The promotion gate is written down, coded, and reads from the same journal the paper engine writes to.
- The halt and demotion rules are written down and coded.
- The journal captures planned and realised R, slippage per side, exit reason, setup tag, and mistake tags.
- Reconciliation against broker fills runs automatically and alerts on mismatch.
- The system fails closed and has been tested concurrently.
- You have traded stage 2 at minimum size and watched a real loss without intervening.
- Your risk per trade is set from a tolerable drawdown, not from ambition — see position sizing per trade.
If any line in that list is unchecked, the honest answer is that you are not ready, and the gate exists precisely so that answer does not have to be re-litigated on a good morning. The measurement layer has to exist before any of it is meaningful, which means logging planned 1R, realised R, and setup tag on every trade from the first paper fill onward. Journal fields that make this work → · TradeLog does it offline and locally · why R rather than dollars.
Educational content, not financial advice. No live profit-and-loss figures are claimed anywhere on this site; backtest and walk-forward results are always labelled as such. Full terms: /terms.html
Tools referenced in this guide
- Trading bot — the execution engine with the 60-closed-paper-trade promotion gate enforced in code.
- TradeLog — logs planned and realised R per setup so paper and live can be compared directly.
- Walk-forward testing guide — the stage before paper, and the costs a backtest leaves out.