When I started training a machine-learned trailing stop model, I assumed the hard part was the model.
I was wrong.
The hard part was futures contracts.
The Quarterly Amnesia of Futures Data
Every few months, futures contracts roll:
- ES 12-25
- ES 03-26
- ES 06-26
- ES 09-26
Traders switch to the new contract. Liquidity moves. The old contract fades into history.
But machine learning models don’t know that unless you tell them.
To a model, ES 12-25 and ES 03-26 are just rows in a dataset.
That’s the trap.
What I Thought Was Happening
My mental model was simple:
“Price is price. The contract changed, but the market didn’t.”
So I trained a Random Forest on ES 12-25 data and deployed it on ES 03-26.
It worked.
Then it quietly degraded.
What Was Actually Happening
NinjaTrader exports raw contract prices.
There is no back-adjustment.
No continuous series.
No hidden stitching.
That means rollover introduces:
- Price level jumps
- ATR regime shifts
- EMA discontinuities
- Structural breaks in features
To a machine learning model, that looks like a regime change.
But it’s not market behavior.
It’s a data artifact.
The Indicator Distortion Problem
I was feeding the model features like:
- Distance to EMA
- EMA slope
- ATR
- Bar range
These are scale-sensitive.
At rollover:
- EMA resets to a new price regime
- ATR can spike or compress
- Distance-to-EMA distributions shift
So the model learned patterns that didn’t exist anymore.
It wasn’t overfitting.
It was being fed a different world.
Why This Breaks Trailing Stops First
Entries are often robust to small distribution shifts.
Trailing stops are not.
Trailing stops depend on:
- Microstructure volatility
- Local noise patterns
- Path behavior after entry
Rollover changes all of that subtly.
The model starts tightening too aggressively.
Or not aggressively enough.
Or at the wrong times.
The equity curve doesn’t explode.
It quietly decays.
That’s the dangerous part.
Continuous Contracts vs Real Contracts
Data vendors offer continuous futures contracts that stitch rolls together.
They look clean.
They are useful for research.
But they are synthetic.
Back-adjusted prices change history.
Indicators computed on them are not what traders actually traded.
For execution models—especially stops—I decided:
Train on the contract I actually traded.
My Production Rule
This became a hard rule in my system:
Retrain Machine B at every contract roll.
In practice:
- Export ES 03-26 data when ES 03-26 becomes front contract
- Train on recent weeks of ES 03-26 only
- Validate on recent ES 03-26 only
- Never mix contracts blindly
It’s boring.
It’s manual.
It works.
A Subtle but Important Fix
To make models more stable across contracts, I normalized key features:
- Distance-to-EMA / ATR
- EMA slope / ATR
- Bar range / ATR
This makes the model care about structure, not absolute price levels.
The Engineering Lesson
Futures rollovers are not a trading problem.
They are a data engineering problem.
If you ignore them:
- Your model performance decays
- Your backtests lie
- Your production system diverges from research
If you engineer around them:
- Your models survive contract boundaries
- Your research matches live trading
- Your equity curve becomes explainable
What Comes Next
Rollover was only half the data problem.
The next trap was sessions:
- RTH vs ETH
- Liquidity regimes
- Volatility regimes
- Distribution mismatch
In the next post, I’ll explain why training on overnight data when you only trade RTH quietly sabotages your model—and how I fixed it.
Previous: Random Forests for Trailing Stops: Labels Without Lookahead Bias →
Next: RTH vs ETH: Data Distribution Mismatch in Trading ML →