# EMA 9/21 crossover on 1-hour ES, NQ and GC bars - frozen specification (RIP Backtest protocol v1.0 plus the 8 Oct 2026 rules)

Specification written **2026-10-08, before any EMA 9/21 value, signal or trade was computed on any of the three markets.** What was looked at before writing it: the data audit of the three files (`data_manifest.md`: holes, back-adjustment, hashes), the earlier unprotocolled tests of the author's own EMA 9/20 + TDI system on ES 1-minute bars (a different rule with filters and discretionary exits; all failed), the engine and results of the 1-hour golden cross test (SMA 50/200, which is a different pair of averages), and the contract calendar of GC. No EMA 9/21 number was looked at.
Bugs may be fixed after the run; rules may not. If the engine fails its self-test (below), nothing is run.

## What is tested
The most common short-term moving-average rule: **EMA 9 crossing EMA 21** on **1-hour bars**, all sessions, with and without a stop and a target, on **E-mini S&P 500 (ES), E-mini Nasdaq-100 (NQ) and gold (GC) futures**. One pair of periods, one timeframe: 20/50, 12/26 and other timeframes are not tested here and will not be tried after seeing the result.

## Rules
- **Bars:** 1-hour bars built from the 1-minute files exactly as in every hourly test (bar time = close time, New York clock), 24 hours a day, no session filter, positions held over weekends. Files: `ES_1min.csv`, `NQ_1min.csv`, `GC_1min.csv` (NinjaTrader, back-adjusted, hashes in `data_manifest.md`), **cut at 2026-09-25 17:00 New York** for all three markets; 2020-01-01 to that date. $50 a point (ES), $20 (NQ), $100 (GC).
- **Averages:** EMA(n) of the 1-hour closes, seeded with the simple average of the first n closes, then ema[k] = a x close[k] + (1 - a) x ema[k-1] with a = 2 / (n + 1). The first signal is possible once both averages exist (bar 21).
- **Signals** at the close of bar k: **golden** if EMA9[k] > EMA21[k] and EMA9[k-1] <= EMA21[k-1]; **death** if EMA9[k] < EMA21[k] and EMA9[k-1] >= EMA21[k-1]; differences below 1e-10 count as exact ties (as in the golden cross test).
- **Entries only on a signal**, filled at the open of the first 1-minute bar after the signal bar, plus slippage: a golden cross opens a long; in the long-short versions a death cross opens a short. After a stop or a target the position stays flat until the next signal. The first signal is an entry.
- **Exit:** the opposite signal at the next 1-minute open (a golden cross closes a short, a death cross closes a long; in the long-short versions it also opens the new position). A position still open on the last bar is closed at its close and flagged.
- **R = 1.5 x ATR(14)** of the 1-hour bars at the signal bar, in points. Stop 1 R; target 2 R; the stop wins a tie; a level gapped through fills at the open; slippage on every fill. (R is defined for the no-stop version too, as a unit only.)
- **4 variants per market, 12 results in all (the only ones tested):** long only (L) and long and short (LS), each with two exit rules: **N** (no stop, no target, exit on the opposite signal) and **T2** (stop 1 R, target 2 R). Named `EM-L-N`, `EM-LS-N`, `EM-L-T2`, `EM-LS-T2`, prefixed `ES-`, `NQ-`, `GC-` in the files. No other settings will be tried after seeing results.

## Costs, rolls, periods, units
- **Costs:** commission $5.76 per round trip plus one tick of slippage on every fill, as in all the ES tests. ES: tick 0.25 = $12.50, so **$30.76 a trade**; NQ: tick 0.25 = $5.00, **$15.76 a trade**; GC: tick 0.1 = $10.00, **$25.76 a trade**. The same amount again for each roll a position is held through. The same commission for the three products is an assumption, not a quote from a broker. A signal every day or two makes costs a visible share of R: that is part of the test.
- **Rolls:** ES and NQ: second Thursday of March, June, September and December, at 17:00 New York (the engine's list). GC: the first session date of each new contract in NinjaTrader's merge (contracts Feb, Apr, Jun, Aug, Dec; 34 dates from 2020-01-29 to 2026-07-24, in `gc_rolls.json`), at 17:00 New York.
- **Periods (by entry date):** development 2020-01-01 .. 2023-12-31; validation 2024-01-01 .. 2025-03-31; holdout 2025-04-01 .. 2026-09-25. **The holdout is not clean:** the same ES data was used by earlier tests on this site, and the three markets move together, so NQ and GC are not independent of ES.
- **Units:** net R per trade (net points divided by R in points), non-compounded; dollars per contract next to it.

## Gates and verdicts (as in the 1-hour golden cross test)
Six gates per variant: validation profit factor >= 1.2; at least 50 trades in each period; development t >= **2.5** (more than four variants are tried: 12 results); above the 95th percentile of 500 runs of the matched control; positive without the 10 best trades; holdout net positive (flagged used). **A variant with fewer than 30 trades is Inconclusive**; otherwise Buried or Survivor.

## Matched control (entry state)
A **circularly shifted state-series control**. The state is +1 while EMA9 is above EMA21 and -1 while it is below (on an exact tie the previous state is kept), carried bar by bar. The real state series is shifted circularly by a random offset of at least 5% of its length and applied to the real prices with the same entry, stop, target and exit rules and the same costs; **the ATR-based R (and so every stop and target distance) is recomputed at the shifted signal time from the real prices**. 500 runs, fixed seeds (1000 + run), runs with no trade excluded, the same shifts for every variant of a market. The statistic is the average net R per trade over the full sample.

## Data holes (rule 3 of `data_manifest.md`)
The files have known holes (listed per market in `data_holes.json`). The data is used as it is, nothing is filled. After the run the study reports how many trades were open across a hole and recomputes gates 1, 2, 3, 5 and 6 and the verdict without them (gate 4 is taken from the run, it is not recomputed). If a verdict changes, it is reported as Inconclusive with the reason.

## Always reported
Win rate with its 95% Wilson interval, how the trades ended (stop, target, opposite signal), average hours held, long and short sides separately, the cost per trade in R next to the gross result per trade, drawdown on closed trades and including open losses, buy and hold in the same dollars, and the sign of every result in both R and dollars.

## Before anything is run (self-test)
`em_selftest.js` must pass first: (1) a hand-worked example of the EMA and of the cross signals; (2) the engine's EMA9, EMA21 and signals match an independent naive recomputation to 1e-9 on the real bars of each market; (3) an independent re-implementation of the trade simulation, with each market's costs and roll dates, run on the same signals, matches the engine trade by trade for all four variants on all three markets; (4) the shifted-state control with shift 0 reproduces the real signals; (5) the data cut and the cost per trade of each market equal the numbers above. The engine of the golden cross test is reused with three additive options (another moving average, a market's own roll dates, a narrowed variant list) and passed its own self-test again after the change. No result is printed. (The first draft of the one-position check used a strict inequality and failed on every long-short variant, because a reversal closes one position and opens the next at the same 1-minute open; the check was corrected. No rule was changed and no result had been computed.)

## Reading the result
"Inconclusive" and "no proven edge" mean a statistically convincing edge was not demonstrated; neither is proof that none exists. One pair of periods, one timeframe, three correlated markets, one stop size, one target.
