My Model Was Reading Tomorrow's Newspaper
For a while, one of my models looked like a genius. Its backtest was clean, confident, almost too smooth. I wanted to believe I had finally built something that worked.
It had not learned anything worth keeping. It had learned to cheat — quietly, through a leak I had built myself, that let it read information from the future before deciding what to do in the past.
The suspiciously good student
A model that performs a little better than you expected is exciting. A model that performs a lot better than it has any right to should frighten you. Mine was the second kind: smooth where real markets are jagged, confident where it should have been uncertain.
In an earlier post I wrote that the most dangerous result is the one that flatters you. This was that lesson again, in a subtler costume. The numbers were not absurd this time — they were just too good, and “too good” is its own kind of red flag, because genuine edge in markets is small, noisy, and hard-won. A model that looks like it has solved the market has almost always found a crack in your simulation instead.
Where the future got in
The crack was in normalization — the unglamorous step where you rescale raw inputs so a model can learn from them. To normalize, you need statistics: a center, a spread. And I computed those statistics over the entire dataset at once.
That single choice is where the future leaked in. When you compute statistics across all of history and then apply them to a point in the past, every “normalized” value at that past moment quietly encodes information about data that had not happened yet. The model, looking at any given instant, was implicitly being told something about the shape of the whole timeline — including its future. It never saw tomorrow’s prices directly. It did not need to. It was breathing air that had tomorrow mixed into it.
Why look-ahead is so easy to build
What makes look-ahead bias dangerous is not that it is hard to create. It is that it slips in through otherwise ordinary preprocessing. The most natural, readable way to write the code — fit your scaler on the data, then transform the data — is already wrong for time series, and nothing complains. No exception is thrown. No test turns red. The numbers look completely reasonable. The only symptom is that your model is better than it should be, which is the single symptom you are least motivated to investigate.
That is the trap of leakage: it does not look like a bug. It looks like success.
The tell: brilliant in here, dead out there
There is one telling signature, though it is not proof on its own. A model corrupted by look-ahead tends to be brilliant on the data it was tuned against and to fall apart the instant it meets data it cannot peek at. A large gap between in-sample glory and out-of-sample collapse is a warning sign of leakage — though it can also come from overfitting, a shift in regime, or costs you underestimated. In my case, the leak was the culprit.
So the test that mattered was never “how good is the backtest?” It was “how much worse does it get when I make the simulation honest?” When I closed the leak, the performance dropped — and that drop, painful as it was, was the first trustworthy number the system had ever shown me.
The fix: never use anything you could not have known yet
The repair is a principle, not a line of code: at any moment in the simulation, the model may only use information that existed at or before that moment. For normalization, that means deriving statistics from a trailing window of the past — fitting on what was available up to a given time, never on the whole timeline, and never on a single point of the future.
It is slower. It is fiddlier. It produces worse-looking backtests. All three of those are features, not costs, because each one is the simulation refusing to let the model time-travel. An honest pipeline pays a tax in apparent performance, and that tax is the price of numbers you can actually trust.
Leakage is a family, not a single bug
The hardest part of fixing this was not the normalizer. It was realizing the normalizer was just one door, and the future could walk in through many. The same mistake hides in how labels are built, in features that secretly summarize a whole series, in any preprocessing step that touches past and future together. Close one leak and you have not closed leakage; you have only learned what it looks like, so you can go find the others.
I stopped thinking of look-ahead as a bug to fix and started treating it as a property to audit — something I now hunt for deliberately, on the assumption that if a result looks too clean, the future is getting in somewhere I have not checked yet.
Why backtests reward cheating
Underneath all of this is an uncomfortable truth about backtests: they cannot tell skill from hindsight. A backtest pays out exactly the same for a model that genuinely anticipates the market and a model that is quietly reading the answer key. Both produce a beautiful curve. Strictly forward, unseen data is the stronger test that begins to separate them — though even that cannot, on its own, prove a pipeline is leak-free.
That is why “it did well in backtest” is the start of an investigation, not the end of one. A good-looking equity curve is a question: did this model earn this, or did I accidentally let it cheat? The whole discipline of honest research is just learning to ask that question before the market asks it for you.
A model reading tomorrow’s newspaper will always look like a genius. The genius is yours, not its — and it lives entirely in the leak you forgot to close.
— No signals, no returns, not investment advice.