Building Momentum Systems Without Fooling Yourself: A Seven-Part Series

  • September 17, 2026
  • 5 min read
Introduction to the series. The two “Illusion of the Filter” posts ([Part 1](https://www.quanthedgeai.com/blog/the-illusion-of-the-filter-that-works-why-adding-conditions-sequentially-destroys-strategies/), [Part 2](https://www.quanthedgeai.com/blog/the-illusion-of-the-filter-that-works-part-2-counting-what-you-didnt-write-down/)) explained why the usual way of building strategies fails. This series shows the alternative, end to end.
The two previous posts made a negative argument: adding conditions one at a time, keeping what raises the metric, produces strategies that memorize noise, and the tools that expose this  trial counting, the deflated Sharpe ratio, purged cross-validation, are known and largely ignored. Several readers asked the obvious follow-up. Fine, but what does the “positive” version look like? Not the diagnosis, the construction.

That is what the next seven articles do. They walk through how I build systematic momentum strategies on a universe of twenty-five futures:  equity indices, rates, FX, energy, agriculturals, using a research framework designed so that the failure mode of the previous posts cannot happen by accident. This specific methodology owes most to Marcos López de Prado and David Bailey; the walk-forward discipline to Robert Pardo; the insistence on a random benchmark to Kevin Davey and Perry Kaufman. The implementation and some added features are mine: strategy logic written in C# as a library of primitive building blocks for MultiCharts .NET, and a separate Python layer whose only job is to judge what the C# layer produces. I also use a very specific execution and monitoring framework.

One thing the series will not do is hand over the operational details. You will not find parameter values, the exact composition of a signal, or code you can paste into a chart. Not because they are secret,  but because they are the least interesting part, and publishing them would invite exactly the copy-and-tweak behaviour the whole approach is built to prevent. What you will find is the architecture, the decision rules, and the reasons behind them, in enough detail to build your own version.
 
The organizing idea

Everything in the framework follows from a single constraint: the research process must be countable. If I cannot say how many hypotheses touched the data, I cannot correct for selection, and if I cannot correct for selection, every performance number I produce is meaningless. This sounds like a statistical nicety. In practice it dictates the software architecture, the file conventions, the order in which work is done, and even which questions are allowed to be asked.

Four consequences fall out of it, and they are the spine of the series.

Separation of concerns:  Entry signals, exit logic, regime filters, and position sizing are distinct, composable layers, each implemented as an independent primitive. A new idea for an entry becomes a new primitive, never a modification of an existing one. This is not software hygiene for its own sake: it is what makes it possible to test a signal in isolation, to know that a filter was evaluated as a feature and not smuggled in as a condition, and to count each of these as the separate trial it is.

One environment for logic, another for judgment. All strategy code lives in C#. Python never contains a strategy,  no “port” of the signal to test it faster, no twin implementation that drifts. Python consumes the trade list that MC.NET exports, reconstructs the equity path from raw prices and contract specifications, and applies the statistical tests. The two layers are joined by a canonical trade-list format and by an explicit parity test, because platform execution semantics, when a fill is timestamped, how a gap through a stop is handled, are non-obvious and will silently break any validation that assumes them away.

Decisions before data.The labelling scheme, the barrier geometry, the instrument universe, the primary metric, and the pass/fail thresholds are all fixed and registered with a timestamp before the first backtest runs. Labels are defined in volatility units, not price units, so that the same specification applies to every instrument without per-market calibration, which is itself a hidden degree of freedom I refuse to spend.

A register that only grows. Every hypothesis that touches data is an entry in an append-only register, admitted through a stage-gate funnel with a mechanical checklist and a human judgment step. Ideas that fail the checklist never reach the data and do not count; ideas that pass are trials whether they succeed or not. The register is what the deflated Sharpe ratio of Part 2 needs as input, and it is the one artefact I would keep if I had to throw everything else away.

Next article:

Architecture: primitives, layers, and the judge. How the C# primitive library is organized , signals, exits, filters, sizing  and why Python is deliberately kept incapable of expressing a strategy. The canonical trade list, the parity test, and the two platform behaviours that had to be discovered the hard way.
 

What to expect

The tone will be technical and the pace deliberate. Each article stands alone, but they are ordered the way the work is ordered: you cannot validate what you have not labelled, and you cannot label what you have not decided. Where the framework has open questions and it has several, including whether the horizon of the effects it targets is stable over twenty years of data I will say so rather than paper over them.

If the “Illusion” posts described the bill for noise you memorized, this series is about the accounting system that keeps the bill from arriving. It is slower than the alternative. It is supposed to be.

*quanthedgeai — the research arm of algosworksai. Systematic research on mid-frequency instruments.*
 
 

Get the monthly Market Regime Note

Regimes, volatility and correlations across major futures markets — with the code behind the charts. Free.

Subscribe →