Home / Blog / Backtest contamination
Your backtest Sharpe is contaminated.
If a research agent with persistent memory helped produce a backtest, there's a question you can't currently answer: did it use anything it couldn't have known at simulation time? For one of the two contamination classes, your vector store can't even represent the question.
Contamination class 1
FUTURE_EVENT: the one you already check for.
The obvious form of lookahead: a memory whose underlying event happens after the simulated decision date. Your backtest runs at January 15; the agent recalls a March earnings surprise. Every quant knows to guard against this, and any store with an event timestamp can filter it. If this were the whole problem, a WHERE event_time <= sim_date clause would end the discussion.
Contamination class 2
LATE_REVISION: the one your vector store can't see.
Financial data gets revised. Guidance is raised, GDP prints are corrected, filings are amended. A revision carries the original event's date, but it arrived months later.
Q3 revenue, event time September 30. In February, the figure is restated. Your agent's memory now holds the corrected number, stamped with its September event time.
Simulation checkpoint: January 15. The filter says September < January, so it looks safely historical. The agent recalls the corrected figure and trades on it.
On January 15, the corrected number did not exist. The agent used information from three weeks in its future while every timestamp it can see says "past." That's lookahead bias wearing a historical date.
A store with one clock cannot represent this, let alone filter it. You need two: event time (when the fact was true in the world) and ingestion time (when your system learned it). Honest recall at simulated time T requires both to precede T. This is the bitemporal property, and it's why bolting a date filter onto a vector database doesn't fix the problem.
Why it inflates results
Revisions are free alpha, in simulation only.
Revisions are systematically informative: figures get corrected toward the truth, and the truth moves prices. An agent that recalls post-revision numbers at pre-revision checkpoints is trading on corrections before they were published. In simulation, that edge is real and shows up directly in the Sharpe ratio. Live, the revision hasn't landed yet, so the edge evaporates, and the strategy you funded is not the strategy you tested. The more your agent's memory accumulates revised data over time, the wider the gap between the contaminated backtest and live performance. The failure mode isn't a crash; it's a quiet overstatement you discover with real money.
The check
Audit the memory, not just the strategy.
Lians ships a contamination detector as an API primitive. Point it at an agent and a simulation checkpoint; it scans every memory the agent possessed and flags both classes:
$ curl -X POST $LIANS/v1/backtest/check \ -d '{"agent_id":"desk","simulation_as_of":"2026-01-15T00:00:00Z"}' { "memories_checked": 1204, "flags": [{ "contamination_type": "late_revision", "event_time": "2025-09-30", "ingestion_time": "2026-02-06", ← arrived 22 days after the checkpoint "delta_days": 22.4 }], "contamination_rate": 0.0008, "is_clean": false }
A report with is_clean: true is the artifact a research lead can attach to a backtest before capital moves. And because recall itself can be gated the same way: recall_at(query, as_of=T) refuses anything either clock places after T, so the honest path is to prevent contamination at recall time and use the detector as the independent audit.
Try it on your own agent
Two commands to a verdict.
pip install lians-sdk[local]mem.backtest_check(agent_id="desk", simulation_as_of="2026-01-15")