How to Backtest a Trading Strategy: Rules, Data, Costs, and Validation
Backtesting applies a defined decision process to historical data under an execution model. It can reveal implementation mistakes and estimate historical outcomes. It cannot establish a future return or make a strategy safe to deploy. A reproducible test records the rules, data, costs, and model alongside the result.
Step 1: Turn the Idea Into a Complete Rule Sheet
An entry signal is only one part of a strategy. Before opening the test period, specify:
- Universe: eligible instruments and when each becomes eligible.
- Information: data fields, bar interval, timezone, and when values become available.
- Orders: trigger, order type, submission time, expiry, and handling of unfilled orders.
- Exits: stop, target, time exit, and precedence if several conditions occur together.
- Sizing: units or capital allocation, leverage limit, and aggregate exposure.
- Exceptions: missing bars, halts, corporate actions, contract rolls, and unavailable borrow.
For a hypothetical daily moving-average strategy, “buy on a crossover” leaves the fill time unspecified. A more testable rule is: evaluate the crossover after the daily close, submit a market order for the following session, and close after the reverse crossover using the same timing. Add an explicit position size and any exit or exposure limits before testing. These rules illustrate a specification; they are not a recommendation.
Distinguish Position Allocation From Risk
Putting 2% of equity into a position is different from risking 2% of equity at a stop. Under a simplified example, $10,000 of equity, a $100 planned loss budget, and a $2 entry-to-stop distance imply 50 shares before costs. At a $50 entry, that uses $2,500, or 25% of equity. A gap through the stop can exceed the planned $100 loss. Use the actual contract multiplier and currency conversion for other instruments.
Step 2: Audit Historical Data
A longer history is not automatically a better sample. Coverage must match the mechanism being tested, and old data may reflect different market structure. Check these properties before running the engine:
| Check | What to record |
|---|---|
| Timestamp meaning | Bar start, bar end, or publication time; timezone and daylight saving rules |
| Adjustments | How splits, dividends, and futures rolls affect prices and returns |
| Missing observations | Gaps, duplicate timestamps, and stale or filled-forward values |
| Universe history | Whether delisted securities and past constituents are represented |
| Resolution | Whether the data can establish the sequence of orders and exits |
| Availability | When each fundamental or alternative-data value became public |
QuantConnect's custom-data timing documentation illustrates a general issue: assigning a completed bar to its opening time exposes information too early. A financial statement's period-end date similarly differs from its publication time.
Daily OHLC data cannot establish which came first when both a stop and target are touched during one bar. Use suitable finer data or show the sensitivity to alternative fill sequences. Do not silently assume the favorable outcome.
Step 3: Choose an Engine That Represents the Question
Start from requirements rather than a platform ranking.
| Requirement | What to verify in a candidate engine |
|---|---|
| Chart-based rules | Signal timing, available history, and order-fill assumptions |
| A multi-asset portfolio | Shared cash, simultaneous positions, rebalancing, and point-in-time universes |
| Futures or options | Contract selection, expiry, rolls, multipliers, and instrument-specific costs |
| Intraday execution | Quote or trade data, session boundaries, partial fills, and liquidity assumptions |
| Manual decisions | Hidden future bars, decision-time journaling, and a reproducible rule sheet |
For example, TradingView strategies use Pine Script order commands and a broker emulator. A Python engine can make custom calculations easier to inspect, but its defaults still need review. Open-source software does not make market data, infrastructure, or every execution model free.
If you use a generator such as Pineify, compare the generated entries and exits with the written rules. Compiling successfully is a syntax check, not a validation of strategy behavior.
Step 4: Make the Cost Model Explicit
Separate commissions, bid-ask spread, slippage, financing, and borrow costs. They arise through different mechanisms, so replacing all of them with one fee can hide sensitivity to holding time or order size.
As a hypothetical accounting example, a 100-share trade buys at $50 and sells at $51. Its gross gain is $100. If adverse execution reduces the result by $0.02 per share on each side, that costs $4. A $1 commission per order adds $2, leaving $94 before other charges. This calculation is not an estimate of typical market costs.
Use documented units. TradingView's properties guide expresses slippage in ticks; the backtesting.py API accepts relative spread and commission parameters. The same numeric input can mean different things in different tools.
Run a baseline and less favorable cost assumptions. If a modest change removes the result, report that dependence instead of searching for parameters that conceal it.
Step 5: Audit Trades Before Evaluating Performance
Inspect the first trade, a losing trade, the largest winner, a gap, and a case where several exits could trigger. Reconstruct each from the source bars. Verify signal time, order submission, fill, size, and costs.
Then reconcile beginning equity, realized results, costs, and the value of open positions at the end. State whether the engine liquidates remaining positions or marks them to market. An unresolved position can affect account equity while being excluded from closed-trade statistics.
Save a reproducibility bundle: data source and retrieval date, tested dates, code version, parameter file, environment version, execution settings, and exported trades. A screenshot of the final profit number is insufficient to repeat the calculation.
Step 6: Read Results Without Universal Pass Marks
| Measure | What to examine beyond the headline |
|---|---|
| Return | Same-date benchmark, leverage, cash exposure, and cost assumptions |
| Win rate | Average win, average loss, and dependence between trades |
| Profit factor | Whether a few outsized wins or very few losses dominate |
| Maximum drawdown | Duration, recovery time, and concentration of exposure |
| Sharpe / Sortino | Return frequency, annualization, and sensitivity to unusual periods |
| Trade count | Independent opportunities and market coverage, not just a total |
A strategy with a 60% win rate can lose money if losses are larger than wins. A high Sharpe ratio over a short or selected window can be unstable. There is no universal minimum of 100 trades, acceptable drawdown, or annual return that establishes a valid result.
Inspect the sequence of returns as well as totals. Correlated positions can make many trades behave like one concentrated bet. Comparing time-aligned portfolio returns is more informative than adding isolated strategy profit numbers.
Step 7: Separate Development From Validation
A chronological holdout reserves a later period before any rule or parameter selection. Develop on earlier data, freeze the complete strategy, and evaluate it on the holdout. A 70/30 allocation is an example, not a required ratio.
Walk-forward analysis repeats development on a window and evaluation on the following window, then advances through history. The combined evaluation windows represent the chosen retuning policy. Specify the window lengths, optimization objective, and treatment of positions crossing a boundary before seeing the results.
Keep a separate final holdout if the walk-forward design itself is tuned. Repeatedly inspecting a test period and changing rules makes it part of development. A disappointing holdout can result from selection bias, changed conditions, execution assumptions, or uncertainty; the outcome alone does not identify the cause.
The backtesting.py walk-forward example demonstrates training on earlier observations and advancing through later data. Its example output is specific to that tutorial, not a target for another strategy.
Step 8: Forward-Test the Operational Process
Forward simulation checks whether new-data signals, order timing, logging, and state recovery behave as specified. Define the operational checks and observation period in advance. A month with no signals tells you little about the order path, while frequent fills may still omit rare failures.
Paper fills do not establish market impact, borrow availability, or behavior under real financial loss. If live evaluation is considered, its capital limits and stopping rules need a separate decision. Passing a backtest does not imply an obligation to trade or scale up.
Common Questions
How much data is enough? Enough to examine the intended mechanism across relevant conditions, with usable later data left for validation. Report uncertainty and dependence; elapsed years or trade count alone cannot certify the sample.
What is a good Sharpe ratio? Interpret it in context: sampling frequency, exposure, costs, number of trials, and uncertainty matter. Use it alongside the equity path and a comparable benchmark, not as a standalone acceptance threshold.
Can backtesting predict future profits? It estimates historical outcomes under stated assumptions. Changed markets, data errors, and execution differences can invalidate the inference. The deliverable is a testable record of evidence and limitations.
