Published 2026-10-14 · Updated 2026-10-25
Test a Trading Rule Without Fooling Yourself
A backtest is the standard way to evaluate a rule — and the standard way to fool yourself. Three checks that separate a result from a screenshot: look-ahead protection, costs, and survivorship.
On this page
What this is about
The first guide closed the loop: an agent pulls a number, code computes, the model explains. This one is about the step after that — you have a rule, now how do you know if it works?
Most backtest problems are not statistical. They are three specific mistakes, and all three are checkable in minutes.
A backtest that fails any of these three is not a result. It is a screenshot.
Check 1 — Look-ahead protection
Look-ahead bias is when the rule uses information that would not have been available at the time of the trade. It is the single most common reason a backtest looks excellent and then fails.
AI makes this worse, not better. A model trained on historical data has, in effect, already seen the future.
The common forms:
| Form | Looks like |
|---|---|
| Training-data leakage | Asking an LLM to “analyse” a period it was trained on |
| Restated fundamentals | Using a figure as revised later, not as first published |
| Index membership | Testing on today’s constituents, which already exclude the failures |
| Same-bar execution | Deciding on a close and executing at that same close |
The check: for every input your rule uses, ask at the moment of this decision, was this number actually available? If you cannot answer with a source, the result is not a result.
Check 2 — Costs
A rule that trades often looks free when it is not.
The failure mode is almost always the same: a backtest assumes thin costs — say 0.1% slippage — and the live market gives you 3% in the moments you need liquidity most.
The check is to state the assumption out loud:
# Every result must declare its cost model.
COST = {
"commission": 0.0003, # per side
"slippage": 0.0010, # assumed, per side
"note": "slippage is an assumption — state it, do not hide it",
}
If costs are not in the backtest, the backtest is not measuring the rule. It is measuring the rule as if trading were free.
One more thing the model above hides: in A-shares, stamp duty is charged on the sell side only. A single symmetric commission number is therefore already wrong — every exit costs more than every entry.
Check 3 — Survivorship
Survivorship bias is the distortion that appears when a dataset only contains the things that survived.
In markets: delisted companies vanish from history. A backtest over “all stocks” that quietly excludes the ones that went to zero is testing a market that never existed.
The check: ask whether the dataset includes delisted instruments. If the answer is no, or if nobody has asked, the result carries an unquantified upward bias — and the failures are exactly the point.
Putting it together
A rule is worth taking seriously when you can say, about all three:
| Question | Passes when |
|---|---|
| Look-ahead | Every input was available at decision time, and you can name the source |
| Costs | The cost model is stated, including slippage as an assumption |
| Survivorship | The universe includes the instruments that failed |
Failing all three does not mean the rule is wrong. It means you do not yet know whether it is right — and those are very different states.
Where the data comes from
All three checks require data you can re-query with the parameters stated: the window, the universe, the adjustment convention.
If you cannot re-run the data pull and get the same series, you cannot run any of these checks. That is the practical reason to use a source with a stable, documented shape rather than a scraped page.
# Reproducible pull: same request, same series, any time.
curl "https://api.ashareapi.com/v1/kline?code=sh600519&period=day&count=250"
Next
- Connect an agent to data → the first guide, if you have not done it
- The six rules → where these three checks come from