# Opening range breakout (15 minutes) on the DAX - specification (RIP Backtest protocol v1.0 plus the 8 Oct 2026 rules)

Specification written and **frozen 2026-11-07 (draft 1 approved by the owner the same day, "Da, îngheață și rulează"; draft 2, written before freezing, changes only the reading of the clock, see the run log)**, before any opening range, signal or trade was computed on the DAX data. Once frozen, any change of a rule is documented and treated as a separate test; bugs may be fixed.

## Why this test, and what was looked at before
- **Chosen by the owner** on 2026-11-07 ("Breakout-ul primelor 15 minute de la 9:00"), the first DAX test on the site. It is the most searched DAX day-trading rule: take the high and low of the first 15 minutes after the Xetra open at 09:00 Frankfurt, then trade the first break of that range. It has no single source; we test our own exact definition below.
- **Earlier opening-range tests on the site:**
  - NQ, 5 minutes (two rules, 4 variants, all Buried);
  - gold, 30 minutes in three sessions (6 variants, Buried by costs).
  - **Not published, without a protocol:** opening range breakouts of 5, 15 and 30 minutes on ES (September 2026, no edge). The opening-range family has been tried in many variants, so the development bar is **t >= 2.5**.
- **What was looked at on the DAX file:** only its coverage and its clock (7 Nov 2026, see Data). No range, return or trade.

## Data
- **Source:** HistData.com, "GRX/EUR" (DAX index CFD quotes), ASCII 1-minute bars. 26 files, 2010 to 2025 by year and January to October 2026 by month, downloaded 7 Nov 2026.
  - Each zip's SHA-256 is recorded in `data_manifest.md` before the run. The files are not republished.
  - Prices are quotes from an undisclosed feed (most likely the bid), not Xetra or Eurex prices. The volume field is always 0.
- **Clock:** HistData documents its stamps as "EST without daylight saving". That is not what the files show. Checked on 7 Nov 2026 from the activity alone (the mean size of 1-minute moves by time of day), with no strategy number:
  - Read as fixed EST, the jump at the Xetra open falls at 10:00 Frankfurt in summer.
  - **Until 2018-12-31 the stamps are New York local time with daylight saving.** Read that way, the jump falls on the 09:00 minute every year, also in the weeks when the US and European clocks change on different dates.
  - **From 2019-01-01, Frankfurt time = stamp + 6 hours, all year.** Outside those weeks this equals New York time. Inside them the jump falls on 09:00 and the US 08:30 data on 13:30 only this way, and in 2020-2023 the data starts at 08:00 as on other days.
  - **2019 is unclear in those weeks**: March looks like New York time, October like Frankfurt minus 6 hours. The 20 weekdays of the 2019 mismatch weeks (11-29 March, 28 October-1 November) are **not traded**, and are counted.
  - Stamps mark the **start** of the minute. Times below are Frankfurt time (Europe/Berlin), converted with the real daylight-saving rules.
  - **Order:** rows are sorted by time. Of a stamp that occurs twice (an hour repeated on the Sunday evenings of 27 October 2019, 27 October 2024 and 26 October 2025, outside any trading window), the first row is kept.
- **Period used:** 2011-01-03 to 2026-10-30. November and December 2010 are partial and left out.
- **Coverage:** every day has 08:00 to 22:00 Frankfurt in 2011-2018, and longer from 2019. 2020 to 2023 have missing minutes inside the cash session; 2023 is the worst, with a median of 423 of 510 minutes. Prices are never filled.

## Trading days and session
- **Days:** Monday to Friday, except the days Xetra is closed: 1 January, Good Friday, Easter Monday, 1 May, 24, 25, 26 and 31 December.
- **Cash session:** 09:00 to 17:30 Frankfurt.
- **A day is traded only if all 15 minutes 09:00 to 09:14 exist.** Days that fail are counted and reported.

## Rules
- **Opening range:** the highest high and the lowest low of the 15 one-minute bars stamped 09:00 to 09:14. **Range size** = high minus low.
- **Entry:** from the bar stamped 09:15 to the bar stamped 11:59, a buy stop **1 point above the range high** and a sell stop **1 point below the range low**.
  - The first one touched is the trade; at most one trade a day.
  - The fill is the trigger price, or the bar's open if the bar opens beyond it.
  - If both are touched inside the same 1-minute bar, the side nearer that bar's open is entered and the other side counts as its stop in the same bar (a full loss), as in our gold and NQ opening-range tests.
  - A missing minute is skipped: the levels are checked on the next bar that exists.
- **Stop:** 1 point beyond the opposite side of the range. **R** = the distance from the entry to the stop, in points.
- **Exit, two versions:**
  - **1R:** a target at the entry plus (minus, for a sell) 1 R, otherwise the session close;
  - **CL:** no target, held to the session close.
- **The session close** is the close of the bar stamped 17:29, or of the last bar before it if that one is missing.
- **Inside each bar** from the entry bar on: if the stop and the target are both touched, the stop is taken; a level the bar opens beyond fills at the open.
- **Variants:** four, **ORBD-1R-LS, ORBD-1R-L, ORBD-CL-LS, ORBD-CL-L**, for long and short / long only. The long-only variants take only the buy trades of the long-and-short rule: a day whose first break is downward has no trade.

## Costs
- **2.0 index points per round trip** (a spread of about 1 point plus 0.5 point of slippage on each fill), close to 0.01% of the price, as on our ES tests.
- Scenarios, reported but not used by the gates: 1.0 and 3.0 points.
- Results in **index points per trade** (a CFD of 1 euro per point) and in R. Financing is not charged: every position is closed the same day.

## Periods, gates, control
- **Periods (by trade date):**
  - development 2011 to 2018 (complete data);
  - validation 2019 to 2022;
  - holdout 2023-01 to 2026-10-30.
  - The holdout is **fresh**: no DAX data has been used on the site.
- **Six gates per variant**, in points per trade:
  1. validation profit factor >= 1.2;
  2. at least 50 trades in each period;
  3. development **t >= 2.5**;
  4. above the 95th percentile of the control;
  5. positive without the 10 best trades;
  6. holdout net positive.
  
  A variant with fewer than 30 trades is Inconclusive.
- **Control (shifted direction):** each traded day's direction (buy, sell or none) is moved, all together, by one random circular offset of at least 5% of the traded days. Only the moved side's stop order is placed on the real day, with the same range, window, stop, target, exit and costs. 500 runs, seeds 1000 + run. Statistic: the average net points per trade. It asks whether the direction of the first break says anything beyond how often and on which side the rule trades.
- **Missing minutes (rule 3):**
  - A day with any missing minute between 09:15 and 17:29, while a position is open or could be opened, is flagged.
  - Every gate and verdict is recomputed **without the flagged days** and reported next to the main result. If the verdict changes, the result is Inconclusive by rule 3.
- **Also reported:**
  - the share of trades reaching the target, stopped, or closed at 17:30;
  - the win rate with its Wilson interval;
  - points and R before and after costs, and the cost as a share of R;
  - longs and shorts separately, and results by year;
  - the two cost scenarios;
  - the days without a trade, by reason.

## Engine and self-test (before the run)
New code:
- a DAX loader (`dax_data.js`): the HistData files, the New York clock, Frankfurt days and the Xetra holidays;
- an engine (`orbd_lib.js`) and an independent re-implementation (`orbd_ref.js`);
- a self-test (`orbd_selftest.js`) that must pass before any result is printed. It covers:
  1. the clock: the Frankfurt 09:00 minute against the time-zone database on every weekday 2011-2026, including the weeks when the US and European clocks change on different dates;
  2. hand-worked days: a break up, a break down, both sides in one minute, no break before 12:00, a target, a stop, a target and a stop in the same minute (stop taken), a gap through a level, a missing minute after the entry, a day with an incomplete range (no trade), a holiday;
  3. the engine against the independent re-implementation, trade by trade, for all four variants on the real data;
  4. the control at offset 0 reproducing the real trades.

## Before the article
Results go to the owner first. One article, published only with the owner's agreement.

## Reading the result
"Buried" or "Inconclusive" mean a convincing edge was not demonstrated; neither is proof that none exists. One data source (CFD quotes, not the exchange), 2011 to 2026, the 15-minute range only.

### Run log
- 2026-11-07, before freezing: the owner approved draft 1, which read every stamp as New York time. Before any range or trade was computed, a check of the weeks when the US and European clocks change on different dates (mean size of 1-minute moves by time of day only) showed that from 2019 the stamps follow Frankfurt time minus 6 hours, and that the 2019 weeks of that kind are unclear. Draft 2 reads the clock in two eras and leaves the 20 unclear days of 2019 untraded; no rule of the strategy changed. This is reported to the owner with the results.
- 2026-11-07: frozen. sha256 of this file above the "### Run log" heading: 5beb130bc9723b8a3aac81e4b457c01bba4023ba6a3a156ab07db8aa4a5091ba.
- 2026-11-07: orbd_selftest.js PASSED on its first run (engine = independent re-implementation, 3,927 and 1,922 trades, 0 differences; the clock equal to the time-zone database on 86,730 stamps), and again on the final code. orbd_run.js -> orbd_out/: all four variants Buried (1R-LS 1/6, 1R-L 1/6, CL-LS 2/6, CL-L 2/6); rule 3 changes no verdict. No rule was changed.
- 2026-11-07: the owner asked for the article ("Da, scrie-l"). Written from the outputs of the single run, unchanged, with a new European Indices market page, to be published on 2026-11-08.
- 2026-11-07, after the run, DATA ERROR found while checking an anomaly of the Fibonacci test on the same file: the HistData GRX/EUR file holds prices at Euro Stoxx 50 level, not DAX, from 2020-06-15 to 2023-12-01 (891 trading days: 11,930 on 12 Jun 2020, then 3,138 on 15 Jun; 4,416 on 1 Dec 2023, then 16,412 on 4 Dec). The coverage and clock checks before the run did not look at price levels. Every result that uses those days is invalid as a DAX result; the 2011-2018 development period is not affected. Nothing of this test has been published. Next step, with the owner: a rerun on the DAX days only, documented here.
- 2026-11-07, RERUN after the data error (owner: "Da, rulează-le"): dax_data.js and orbd_ref.js mark the 891 weekdays 2020-06-15 to 2023-12-01 as "not DAX" and never use them; no rule changed and the period dates are kept, so validation now holds 2019-01-02 to 2020-06-12 and the holdout 2023-12-04 to 2026-10-30 (still fresh). The self-test gained a level check (891 days marked, every traded minute above 4,700) and PASSED with 0 differences. Results (orbd_out/): 1R-LS 3,082 trades, avg -1.38 points (gross +0.62), 1/6; 1R-L 1,490, avg -1.16, 1/6; CL-LS avg +0.75, 2/6; CL-L avg +1.65, 2/6; control percentiles 44.0, 18.0, 54.2 and 52.4; rule 3 changes no verdict. Every verdict is as in the first run. The first run's outputs, invalid as DAX, are kept in orbd_out_v1_invalid/.
- 2026-11-08: a review of the published article, the same day, found that it explained the long-only result held to the close as the DAX's rise. From 09:15 to 17:30 the DAX moved +158 points in total over the 3,098 traded days (-1.89 a day in 2011-2018, while CL-L made +3.52 a trade before costs there), and 1,683 of CL-L's 2,454 net points came from the 76 trades flagged by rule 3. The article was corrected, with a Corrections note (also: a stale trade count of the first run in its description, two missing losing years, the Euro Stoxx price range, the dates of the removed days, the cost as a share of the price, the control in the FAQ, a note that the invalid first run had shown the holdout days). orbd_intraday_extra.js (descriptive) writes orbd_out/orbd_intraday.json. No rule, trade, gate or verdict changed.
