1
3 Comments

Your Backtest Isn't Broken — It's Probably Looking Into the Future

One of the most frustrating experiences in algorithmic trading is seeing a strategy produce incredible backtest results...

…and then watching it perform completely differently in live trading.

Maybe the backtest showed an 85% win rate.

Maybe the equity curve looked almost perfect.

Then you deploy it.

Suddenly, the results disappear.

It's easy to blame latency, slippage, execution, or changing market conditions.

But sometimes the problem is much simpler:

Your backtest was using information from the future.

This is called look-ahead bias.

I've encountered this while building and testing Polymarket trading bots, and it's one of those problems that can make a bad backtest look extremely convincing.


What Is Look-Ahead Bias?

The basic rule is simple:

A strategy can only use information that was available at the exact moment it made a decision.

Nothing from the future.

Sounds obvious, right?

But when you're working with historical data, the entire future is already sitting inside your dataset.

Your program can see everything.

A live trading bot can't.

That's where the problem starts.


A Simple Example

Imagine your strategy enters a trade at the beginning of a candle.

This looks reasonable at first:

signal = df["close"][i] > df["open"][i]
enter_at = df["open"][i]

But there's a problem.

You're using the candle's closing price to make a decision at its opening.

That closing price doesn't exist yet.

A more realistic version would be:

signal = df["close"][i - 1] > df["open"][i - 1]
enter_at = df["open"][i]

Now the strategy uses the previous completed candle.

It's a tiny difference in code.

It can be a huge difference in results.


The Dataset Already Knows the Future

This is what makes backtesting dangerous.

Suppose you're testing a strategy from January through June.

Your dataset already contains:

January
February
March
April
May
June

But when your strategy makes a decision in January, it should only know what was available in January.

It shouldn't have access to information from March or June.

The historical dataset doesn't enforce this automatically.

You have to enforce it yourself.


Future Daily Statistics

Here's another common example.

Your strategy trades at 9:00 AM and uses:

  • today's high
  • today's low
  • today's volume
  • today's closing price

But how much of that information was actually known at 9:00 AM?

The complete day's high and low obviously aren't known yet.

Neither is the final volume.

Yet when you load historical data, those values are already present.

That's why every feature should have an important property:

A known timestamp.

You need to know not just what the value is, but when your strategy could have known it.


Even Data Preprocessing Can Cheat

Look-ahead bias isn't limited to trading signals.

It can also appear during machine-learning preprocessing.

For example:

# WRONG
scaler.fit(all_data)
X = scaler.transform(all_data)

The scaler has now seen the entire dataset.

That means information from future observations can influence how historical observations are transformed.

A better approach is to fit preprocessing only on information available at that point:

# CORRECT
scaler.fit(data[:t])
X_now = scaler.transform(data[t])

This is one reason walk-forward testing is so important.


Watch Out for Centered Indicators

Rolling calculations can also introduce future information.

For example:

df["ma"] = df["close"].rolling(
    20,
    center=True
).mean()

A centered moving average can use values from both sides of the current timestamp.

That means it can include future prices.

A normal trailing window is safer:

df["ma"] = df["close"].rolling(20).mean()

The indicator should only use information that has already happened.


The Worst One: Leaked Labels

This is especially dangerous when building predictive models.

Imagine you're trying to predict the outcome of a prediction market.

If your features accidentally contain:

  • settlement information
  • resolution prices
  • future returns
  • post-resolution data
  • future market statistics

your model isn't really predicting the outcome.

It's seeing the answer.

That's how you can end up with a model showing 99% accuracy in testing while failing badly in live trading.


How I Think About It

Whenever I build a feature, I try to ask one question:

Would my live bot actually know this value at this exact moment?

If the answer is no, I shouldn't use it.

I also prefer backtests where the historical boundary is explicit:

for t in range(start, end):

    history = data[:t]

    decision = strategy(history)

    outcome = data[t]

    record(decision, outcome)

The strategy receives the past.

The backtest evaluates what happens next.

That separation is important.


This Became Important in My Polymarket Research

When I started working more seriously on Polymarket strategies, I realized that having good historical data was just as important as having a good strategy.

About three months ago, I started recording historical data directly from on-chain sources and the Polymarket API instead of relying only on reconstructed datasets.

I archive 5-minute cryptocurrency market data locally and use it for strategy research and backtesting.

This gives me much better control over:

  • timestamps
  • market state
  • price history
  • market lifecycle
  • missing data
  • available information
  • backtesting assumptions

I've used this data and infrastructure while developing and testing several Polymarket strategies, including an end-cycle sniper and a BTC/ETH hedge bot.

The goal isn't to create a beautiful equity curve.

The goal is to create a simulation that is as close as possible to what the bot could actually have experienced.


A Good Backtest Still Isn't a Guarantee

Removing look-ahead bias doesn't magically make a strategy profitable.

There are still many real-world problems:

Latency

Your historical model may assume an order executes instantly.

Slippage

The price you see isn't always the price you receive.

Liquidity

A strategy may work with 10 shares but behave very differently with 1,000.

Order-book depth

The best quote isn't necessarily enough to fill your entire order.

API delays

Market data and order responses aren't instantaneous.

Partial fills

Your backtest may assume complete fills when real trading doesn't.

Changing market conditions

A strategy that worked six months ago may behave differently today.

A backtest is a simulation.

It isn't a guarantee.


The Question I Think Matters Most

Don't just ask:

"Did this strategy make money historically?"

Ask:

"Could my bot actually have made this decision with the information available at that moment?"

That question changes how you build a backtesting system.

You're no longer trying to reproduce a historical chart.

You're trying to reproduce the information environment that existed when the trade happened.

And that's a much harder — but much more useful — problem.


Final Thoughts

Look-ahead bias is one of those bugs that can hide in plain sight.

Your code works.

Your backtest runs.

Your charts look great.

And yet the strategy may be using information that a real trader or bot could never have known.

So whenever a backtest looks unusually perfect, don't immediately celebrate.

Check the timeline.

For every feature, indicator, and input, ask:

When did this information become available?

If the answer is after the decision, your backtest is looking into the future.


Building Polymarket Trading Bots

I'm continuing to work on Polymarket trading infrastructure, historical data collection, automated execution, and realistic backtesting.

You can explore some of my work here:

GitHub:
https://github.com/Benjam1nCup/Polymarket-trading-bot-python-V2

Telegram:
https://t.me/BenjaminCup

If you're building trading bots, prediction-market strategies, or quantitative research infrastructure, feel free to connect.

on September 25, 2026
  1. 1

    Live agents fail the same way pretty backtests do: the dataset already contains the future, so the decision looks brilliant until you freeze the clock. I’d pin every retrieval to an as-of timestamp, refuse to let a later note rewrite why an earlier action was allowed, and treat “we know now” as a different claim from “we knew then.” Soft “use the vault” with no observation time is look-ahead dressed as memory. Curious which single leak you’d ban first in an agent log: reading a doc dated after the decision, scoring a plan with metrics collected after the change, or letting a compaction summary invent outcomes that hadn’t landed yet.

  2. 1

    Good write-up. What would you do differently if you started again?

  3. 1

    Thanks for writing this up. Bookmarking it for later.