What Is Lookahead Bias?
Lookahead bias occurs when a trading strategy uses information that would not have been available at the time a trade decision was made. It’s the single most common cause of backtests that look profitable but fail in live trading.How ClyptQ Prevents It
ClyptQ prevents lookahead bias through four structural mechanisms — not through code reviews or best practices, but through architecture that makes lookahead physically impossible.1. RollingBuffer: Fixed-Size Circular Buffer
Every operator input is delivered through aRollingBuffer — a pre-allocated circular buffer that contains only the declared lookback number of past ticks:
lookback slots. There is no array of future prices. There is no array at all — just a fixed window of past data that overwrites itself circularly.
An operator requesting lookback=20 receives exactly 20 ticks of historical data. Not 21. Not the entire dataset. Twenty ticks, ordered oldest-to-newest, with no possibility of accessing tick 21.
2. Input Declarations: Explicit Lookback Contracts
Every operator must declare exactly how much history it needs throughInput objects:
lookback parameter determines the size of the RollingBuffer allocated for that specific consumer. The operator’s compute() method receives a TaggedArray of shape (lookback, n_symbols) — no more, no less.
This is fundamentally different from vectorized frameworks where every operation has access to the full price array:
3. Automatic Warmup Calculation
Before the backtest starts, ClyptQ automatically computes how many ticks are needed to fill allRollingBuffers. This warmup phase runs the graph without executing any trades:
The algorithm works by:
- Tracing backward from every node in
execution_orderthrough itsInputdependencies - Accumulating lookback values along each path (adjusting for overlaps:
total = node_lookback + accumulated - 1) - Converting to source ticks (accounting for timeframe differences: a 20-bar 1h lookback = 1,200 1m source ticks)
- Taking the maximum across all paths to each FIELD source
- Adding a 5% safety buffer (
warmup = int(max_warmup * 1.05))
- Operators execute normally (buffers fill up)
- STATE is extracted (portfolio state available)
- No trading orders are executed (
extra_context={"is_warmup": True}) - Intention operators produce no output
Warmup ticks are automatically computed — you never need to manually set warmup. The graph traces its own dependency tree to determine the exact number of pre-run ticks needed. See Warmup Calculation and Lookback Buffers for the full algorithm.
4. Topological Execution Order
The graph executes operators in dependency order (Kahn’s algorithm), ensuring that every operator’s inputs are computed before the operator runs: Each operator sees only:- Its declared inputs (through
RollingBuffer) - The current tick’s FIELD data (through the graph’s on_tick dispatch)
- STATE data extracted from the executor (cash, positions, margin)
The Complete Picture
These four mechanisms work together to create an environment where lookahead is structurally impossible:Comparison with Vectorized Frameworks
Pandas / NumPy (Used by Freqtrade, bt, Moonshot)
.shift() call correct.
Backtrader / Zipline (Event-Driven)
These frameworks process data bar-by-bar, which is better than pure vectorized. But:ClyptQ (RollingBuffer)
lookback window. This isn’t a convention — it’s a constraint enforced by the RollingBuffer allocation.
Summary
The result: you cannot introduce lookahead bias in ClyptQ, even if you try. The architecture doesn’t rely on developer discipline — it makes the wrong thing impossible.

