| <p align="center"><img src="docs/images/logo.png" alt="Prior Day Breakout logo" width="120"></p>
|
|
|
| # Prior Day Breakout (MetaTrader 5 and MetaTrader 4)
|
|
|
| A free Expert Advisor that trades the **previous-day high/low breakout** on **XAUEUR**: on a closed bar, the first close
|
| above yesterday's high is a buy, the first close below yesterday's low is a sell, once per side per day, with the stop at
|
| the other side of yesterday's range and a fixed target. Fixed 0.01 lot. Source for both editions, a Python statement of
|
| the rule, the tests, and the honest backtest evidence, which is **thin**.
|
|
|
| **Website:** [allanninal.dev/mql5 (Expert Advisors)](https://www.allanninal.dev/mql5/#eas) · [MT4 editions](https://www.allanninal.dev/mql5/#mt4) · [allanninal.dev](https://www.allanninal.dev)
|
| · **Contact:** message me on [LinkedIn](https://www.linkedin.com/in/allanninal/).
|
|
|
| 
|
|
|
| > This EA promises no profit. Every number here is a backtest on broker history, not live trading, and the evidence is
|
| > weak (see [What the backtest does and does not show](#what-the-backtest-does-and-does-not-show)).
|
|
|
| ## Get it on the MQL5 Market
|
|
|
| | Edition | Platform | Price | Link |
|
| |---|---|---|---|
|
| | Prior Day Breakout | MT5 | free | <https://www.mql5.com/en/market/product/201959> |
|
| | Prior Day Breakout MT4 | MT4 | free | MT4 edition: coming soon on the MQL5 Market |
|
| | Prior Day Breakout Pro | MT5 | paid | <https://www.mql5.com/en/market/product/201963> |
|
| | Prior Day Breakout Pro MT4 | MT4 | paid | MT4 edition: coming soon on the MQL5 Market |
|
|
|
| The Market builds are the same code as the source in this repository. The Pro edition is a separate, paid product that is
|
| **not** part of this repository (see [Free vs Pro](#free-vs-pro)).
|
|
|
| ## Contents
|
|
|
| 1. [What it is and who it is for](#what-it-is-and-who-it-is-for)
|
| 2. [Why it was built](#why-it-was-built)
|
| 3. [How it works](#how-it-works)
|
| 4. [What the backtest does and does not show](#what-the-backtest-does-and-does-not-show)
|
| 5. [How it was tested](#how-it-was-tested)
|
| 6. [Architecture](#architecture)
|
| 7. [Inputs, presets, install and compile](#inputs-presets-install-and-compile)
|
| 8. [Run the Python reference and the tests](#run-the-python-reference-and-the-tests)
|
| 9. [Honest limits](#honest-limits)
|
| 10. [Free vs Pro](#free-vs-pro)
|
| 11. [Licence](#licence)
|
|
|
| ## What it is and who it is for
|
|
|
| **What:** a mechanical, once-a-day breakout rule. Levels are the high and low of the previous trading day. A buy needs a
|
| bar to close above the high, a sell a close below the low. The EA sends a market order at the next bar's open, puts the
|
| stop at the other side of yesterday's range, the target at a fixed multiple of that distance (R), and closes whatever is
|
| open at 23:00 server time. One position at a time. The panel shows the rule, the preset status, the levels, the last signal
|
| and the open position.
|
|
|
| **Live safety gate:** live, the EA opens orders only on the two **validated presets** (XAUEUR H1 with target 2 R, XAUEUR M15
|
| with target 1 R). On any other symbol, timeframe or target the panel reads "not validated" and no orders are sent. The
|
| Strategy Tester accepts any symbol, so the MQL5 Market validator can run it.
|
|
|
| **For:** traders who want this exact rule automated and are willing to read the evidence below first. Also for developers
|
| who want a small, tested EA example with a Python statement of its rule.
|
|
|
| **Not for:** anyone looking for a proven edge. There is none shown here.
|
|
|
| | | |
|
| |---|---|
|
| |  |  |
|
| |  |  |
|
|
|
| Image notes: the charts and equity curves are drawn from broker history and the Strategy Tester trade lists by a script, and
|
| the input screens are generated from the `.set` values; they are illustrations, not terminal captures. MT4 versions are in
|
| [`docs/images/mt4/`](docs/images/mt4).
|
|
|
| ## Why it was built
|
|
|
| The motivations, stated with the evidence we actually have:
|
|
|
| - **It came out of a screen, not out of a belief.** The rule was one of the combinations in a pre-registered strategy
|
| screen (37 symbols, several timeframes, in-sample 2020-01 to 2023-06, out-of-sample 2023-07 to 2026-10). It was the only
|
| combination that passed both the Python backtest and the MetaTrader 5 Strategy Tester, so the EA ships for that symbol
|
| and those timeframes only.
|
| - **Weak demand, stated plainly.** No demand research backs this specific rule. Breakout EAs are plentiful on the MQL5
|
| Market, and this one exists because it passed the gates, not because buyers asked for it. It is a free, transparent
|
| reference implementation.
|
| - **Provable behaviour.** The rule is written down in [`tests/reference.py`](tests/reference.py) and compared with the EA's
|
| own trades, entry for entry.
|
|
|
| ```mermaid
|
| mindmap
|
| root((Prior Day Breakout))
|
| Origin
|
| Pre-registered screen, 37 symbols
|
| One combination passed both gates
|
| XAUEUR only
|
| Design rules
|
| Closed bars only, no repaint
|
| Once per side per day
|
| Flat at 23:00
|
| Live only on validated presets
|
| Proof
|
| Python statement of the rule
|
| Same entries as the EA, 612 of 612 and 690 of 690
|
| Evidence is thin, stated plainly
|
| ```
|
|
|
| ## How it works
|
|
|
| ### The rule, step by step
|
|
|
| Times are the broker's server clock (the tests used UTC+2, UTC+3 in US summer time). `H` and `L` are the previous trading day's
|
| high and low, taken from 1-minute bars of the latest earlier day that has bars (up to 7 days back).
|
|
|
| 1. **Levels.** At the start of the day, `H = max(high)` and `L = min(low)` of the previous trading day.
|
| 2. **Signal.** On each closed chart bar that opened at or after 01:00: a **buy** if `close > H` and no buy has fired today; a
|
| **sell** if `close < L` and no sell has fired today. The side is used up even if the entry turns out to be disallowed.
|
| 3. **Entry window.** The order is sent at the open of the next bar, only if that bar opens before 20:00 of the same day and
|
| no position is open.
|
| 4. **Fill and stop.** A buy fills at the ask, a sell at the bid. The stop is the other side of the range: `L` for a buy, `H`
|
| for a sell. The stop distance is `sd = (fill - stop) x side`.
|
| 5. **Skip.** The trade is skipped if `sd` is not positive or is under 8 times the entry spread, or if the stop would lose more
|
| than 50% of equity at 0.01 lot, or if free margin is short.
|
| 6. **Target.** `fill + side x R x sd`, with `R` the preset's target (2 on H1, 1 on M15).
|
| 7. **Exit.** Target, stop, or flat at 23:00 server time (at the first tick after 23:00 if the market is closed then).
|
|
|
| The lot is fixed: 0.01, or the broker's minimum lot when that is larger. After a restart the EA replays today's closed bars,
|
| so a side that already fired today is not taken again.
|
|
|
| ### Per-bar algorithm
|
|
|
| ```mermaid
|
| flowchart TD
|
| A[New bar: look at the bar that just closed] --> B{Bar opened at or after 01:00?}
|
| B -- no --> Z[Nothing]
|
| B -- yes --> C[Levels: previous trading day high H and low L]
|
| C --> D{Close above H and no buy yet today?}
|
| D -- yes --> S1[Signal BUY, buy side used up]
|
| D -- no --> E{Close below L and no sell yet today?}
|
| E -- yes --> S2[Signal SELL, sell side used up]
|
| E -- no --> Z
|
| S1 --> F{Now before 20:00, same day, no position open?}
|
| S2 --> F
|
| F -- no --> Z
|
| F -- yes --> G{Algo trading on, market open, preset validated?}
|
| G -- no --> Z2[Skipped, reason on the panel]
|
| G -- yes --> H2{Stop at least 8 x spread, loss under 50% of equity, margin ok?}
|
| H2 -- no --> Z2
|
| H2 -- yes --> I[Market order: stop at the other side, target R x stop]
|
| ```
|
|
|
| ### Day cycle
|
|
|
| ```mermaid
|
| stateDiagram-v2
|
| [*] --> WaitingForSignal: new server day, levels read, sides reset
|
| WaitingForSignal --> InTrade: signal and order sent at the next open
|
| WaitingForSignal --> WaitingForSignal: bar closes inside the range
|
| InTrade --> Flat: target or stop hit
|
| InTrade --> Flat: 23:00 server time
|
| Flat --> WaitingForSignal: the other side may still fire once
|
| Flat --> [*]: day ends
|
| WaitingForSignal --> [*]: day ends
|
| ```
|
|
|
| ### One trading day
|
|
|
| ```mermaid
|
| sequenceDiagram
|
| participant D as New server day
|
| participant E as EA
|
| participant T as Terminal
|
| participant B as Broker
|
| D->>E: first bar of the day
|
| E->>T: M1 history of the previous trading day
|
| T-->>E: high H and low L
|
| loop every closed bar from 01:00
|
| E->>E: close vs H and L, once per side
|
| end
|
| E->>B: market order at the next open, stop at the far side, target R x stop
|
| B-->>E: filled
|
| alt target or stop first
|
| B-->>E: position closed by the broker
|
| else 23:00 server time
|
| E->>B: close the position (waits for an open market)
|
| end
|
| ```
|
|
|
| ## What the backtest does and does not show
|
|
|
| All numbers are the **Strategy Tester run of the EA itself** (1-minute OHLC, XAUEUR, 0.01 lot, results in R where R is the
|
| stop distance of each trade, spread and swap included, no commission), out of sample 2023-07-01 to 2026-10-09, settings
|
| frozen on 2020 to mid-2023 data first. Source: [`tests/results/oos-summary.md`](tests/results/oos-summary.md).
|
|
|
| | Preset | Target | OOS trades | Profit factor | net R | t | max drawdown (R) |
|
| |---|---:|---:|---:|---:|---:|---:|
|
| | XAUEUR H1 | 2 R | 612 | 1.37 | 39.3 | 2.91 | 6.9 |
|
| | XAUEUR M15 | 1 R | 690 | 1.27 | 36.6 | 2.47 | 6.7 |
|
|
|
| Reading them with care, as the screen's own write-up does:
|
|
|
| - **One idea in two forms.** The two presets share most of their trades.
|
| - **Chance-level evidence.** Of 392 judged units over 37 symbols, 8 passed nominally against 8.3 expected from chance alone,
|
| and none survives the Holm multiple-testing correction (smallest adjusted p 0.33). XAUUSD with the same rule is just under
|
| the bar; the FX pairs and crypto fail.
|
| - **Mostly buys, in a gold rally.** Gold rose about 110% over the out-of-sample period. The buy side earned 35 to 37 R, the
|
| sell side 1 to 2 R.
|
| - **Small per-trade edge.** In-sample profit factors were only 1.05 to 1.09; win rate about 52%, average win 0.46 R,
|
| average loss 0.36 R, about +0.06 R per trade, so costs matter and commission was not modelled.
|
|
|
| ```mermaid
|
| xychart-beta
|
| title "Out-of-sample net R by side (backtest, gross of swap)"
|
| x-axis ["H1 buys", "H1 sells", "M15 buys", "M15 sells"]
|
| y-axis "R" 0 --> 40
|
| bar [36.98, 3.93, 34.57, 2.82]
|
| ```
|
|
|
| Source: [`tests/results/quarterly-gross-r.json`](tests/results/quarterly-gross-r.json), the reference run (gross R, before swap, so
|
| slightly above the net R in the table).
|
|
|
| ```mermaid
|
| xychart-beta
|
| title "XAUEUR H1, target 2 R: gross R per quarter, out of sample"
|
| x-axis ["23Q3", "23Q4", "24Q1", "24Q2", "24Q3", "24Q4", "25Q1", "25Q2", "25Q3", "25Q4", "26Q1", "26Q2", "26Q3"]
|
| y-axis "R" -2 --> 10
|
| bar [1.92, 2.05, 0.51, 3.04, 7.07, 4.82, 9.02, -1.48, 3.83, 2.31, 7.12, 0.53, 0.85]
|
| ```
|
|
|
| (26Q4 holds 3 trades, -0.68 R, and is left out of the chart.)
|
|
|
| ## How it was tested
|
|
|
| | Check | Result |
|
| |---|---|
|
| | Rule invariants on synthetic data, 60 runs, 1,931 trades ([`tests/check_reference.py`](tests/check_reference.py)) | PASS, 0 violations |
|
| | Reference vs the EA in the MT5 Strategy Tester, XAUEUR H1, 2023-07 to 2026-10 | 612 of 612 entries identical (time and side) |
|
| | Reference vs the EA in the MT5 Strategy Tester, XAUEUR M15 | 690 of 690 entries identical |
|
| | Reference vs the EA, same exit minute | 521 of 612 (H1), 502 of 690 (M15): the tester builds its own tick path from M1 bars |
|
| | Reference vs the research simulation the presets were selected with | 612 of 612 and 690 of 690 trades identical, exits and exit prices too (largest difference 0.0) |
|
| | MT4 edition vs the simulation (MT4 Strategy Tester, every tick, fixed 25-point spread) | same trade counts (612 and 690); entries match on 99.84% and 99.86% |
|
| | MT5 release check (memory, speed, non-repainting, validator-style runs) | PASS: 364 MB, 1 min 21 s for 52 M ticks, 142 signals identical on every tick and on 1-minute OHLC, 0 error lines |
|
| | MT5 and MT4 editions compiled with MetaEditor (clean scratch folder, only this repository's files) | 0 errors, 0 warnings each |
|
| | MT5 Market build | passed the MQL5 Market's automated product validation before publication (the Market's own check) |
|
|
|
| Sources: [`tests/results/`](tests/results). Full tables, including the MT4 gate and the release check, are in
|
| [`tests/results/oos-summary.md`](tests/results/oos-summary.md).
|
|
|
| ```mermaid
|
| xychart-beta
|
| title "Entries compared: reference vs the EA (all identical)"
|
| x-axis ["XAUEUR H1", "XAUEUR M15"]
|
| y-axis "entries" 0 --> 800
|
| bar [612, 690]
|
| ```
|
|
|
| ```mermaid
|
| flowchart LR
|
| M[M1 bars of the symbol<br/>server time, bid, spread] --> R[reference.py<br/>the rule in Python]
|
| M --> TST[Strategy Tester<br/>EA with the debug log on]
|
| TST --> L[TRD lines in the log<br/>entry time, exit time, side, prices]
|
| L --> C{compare_trades.py<br/>same entry time and side?}
|
| R --> C
|
| C -- yes --> P[PASS]
|
| C -- no --> F[list the differences, fix, rerun]
|
| SY[synthetic random walks] --> K[check_reference.py<br/>rule invariants]
|
| R --> K
|
| K --> P2[PASS]
|
| S5[PriorDayBreakout.mq5] --> MC5[MetaEditor 0 errors, 0 warnings]
|
| S4[PriorDayBreakout.mq4] --> MC4[MetaEditor 4 0 errors, 0 warnings]
|
| ```
|
|
|
| **Not covered:** forward (live) trading, commission and slippage, and tick-level fills (the tests use 1-minute OHLC for MT5
|
| and a fixed 25-point spread for MT4). The tester and live behaviour of the preset refusal on other symbols is by design and
|
| was read, not run live. The reference is not a bit-for-bit copy of the tester's fills: only entries are compared exactly.
|
|
|
| ## Architecture
|
|
|
| ```mermaid
|
| flowchart TB
|
| subgraph MT5["mql5/Experts/PriorDayBreakout"]
|
| E5[PriorDayBreakout.mq5<br/>the program]
|
| C5[PriorDayBreakout.mqh<br/>rule, orders, panel, events]
|
| P5[DbPanel.mqh<br/>cached labels, boxes, rows]
|
| R5[DbPresets.mqh<br/>the two validated presets]
|
| E5 --> C5
|
| C5 --> P5
|
| C5 --> R5
|
| end
|
| subgraph MT4["mql4/Experts/PriorDayBreakout"]
|
| E4[PriorDayBreakout.mq4]
|
| C4[PriorDayBreakout.mqh<br/>MT4 orders and clock]
|
| PR4[DbPanel.mqh, DbPresets.mqh<br/>same text as MT5]
|
| E4 --> C4
|
| C4 --> PR4
|
| end
|
| subgraph TESTS[tests]
|
| RF[reference.py]
|
| CK[check_reference.py]
|
| CT[compare_trades.py]
|
| CK --> RF
|
| CT --> RF
|
| end
|
| RF -. same rule .-> C5
|
| ```
|
|
|
| ```mermaid
|
| classDiagram
|
| class PriorDayBreakout {
|
| +InpTargetR double
|
| +InpMagic long
|
| +InpSlippage int
|
| +InpPanel bool
|
| +InpLevels bool
|
| +InpDebug bool
|
| +OnInit()
|
| +OnTick()
|
| +OnTimer()
|
| +OnTradeTransaction()
|
| }
|
| class Rule {
|
| +PrevDay()
|
| +Decide()
|
| +ReplayToday()
|
| +OnNewBar()
|
| +ManageTimed()
|
| }
|
| class Orders {
|
| +EntryLots()
|
| +EntryBlocked()
|
| +OpenTrade()
|
| +MarketOpen()
|
| }
|
| class DbPresets {
|
| +g_db_presets
|
| +DbPresetValidated()
|
| }
|
| class DbPanel {
|
| +Text()
|
| +Box()
|
| +Row()
|
| +Section()
|
| }
|
| class reference {
|
| +run()
|
| }
|
| PriorDayBreakout --> Rule
|
| PriorDayBreakout --> Orders
|
| PriorDayBreakout --> DbPresets
|
| PriorDayBreakout --> DbPanel
|
| reference ..> Rule : same rule, compared entry by entry
|
| ```
|
|
|
| ## Inputs, presets, install and compile
|
|
|
| | Input | Default | Meaning |
|
| |---|---|---|
|
| | Target, x the stop distance (R) | 2.0 (1.0 on M15) | the preset's own value; live, other values are refused |
|
| | Magic number | 26101801 | identifies this EA's position |
|
| | Maximum slippage, points | 30 | order deviation |
|
| | Show the panel | true | rule, preset status, levels, signal, position |
|
| | Draw the previous-day high and low | true | dotted lines across the current day |
|
| | Log every signal and trade (Experts tab) | false | prints signals and one `TRD` line per closed trade |
|
|
|
| Preset files (inputs only): [`presets/`](presets) (MT5) and [`presets/mt4/`](presets/mt4). Load the file that matches the
|
| chart's timeframe.
|
|
|
| **Install from source.** Copy the `mql5/Experts/PriorDayBreakout` folder into `<terminal data folder>/MQL5/Experts/` (MT5) or
|
| the `mql4/Experts/PriorDayBreakout` folder into `<data folder>/MQL4/Experts/` (MT4). Open `PriorDayBreakout.mq5` or `.mq4`
|
| in MetaEditor and press Compile (F7). Both compile with 0 errors and 0 warnings. Keep the files of each edition together:
|
| the program includes `PriorDayBreakout.mqh`, which includes `DbPanel.mqh` and `DbPresets.mqh` from its own folder.
|
|
|
| **Run it.** Open an XAUEUR chart (a prefixed or suffixed name is fine) on H1 or M15, attach the EA, keep Algo Trading (MT5) or
|
| AutoTrading (MT4) on. The tests used a server clock of UTC+2 (UTC+3 in US summer time); the 01:00 to 20:00 window follows
|
| your server clock, so check that it matches. Minimum balance suggested by the Market listing: 1,500 USD at 0.01 lot
|
| (recommended 3,000 USD); the out-of-sample backtest's largest drawdown at 0.01 lot was 673 USD and the largest single loss
|
| 373 USD. These are backtest figures, not a limit on future losses.
|
|
|
| ## Run the Python reference and the tests
|
|
|
| ```bash
|
| python3 -m venv .venv && . .venv/bin/activate && pip install -r requirements.txt
|
| python3 tests/check_reference.py # synthetic data, no broker history needed; prints PASS or FAIL
|
| ```
|
|
|
| ```python
|
| import sys; sys.path.insert(0, "tests")
|
| from reference import run
|
| # m1: pandas DataFrame indexed by server time with open, high, low, close (bid) and spread (price units)
|
| trades = run(m1, tf_minutes=60, target_r=2.0) # one row per trade, gross of swap
|
| ```
|
|
|
| To repeat the entry comparison on your own data:
|
|
|
| 1. Run the EA in the Strategy Tester with "Log every signal and trade" on, on your XAUEUR history.
|
| 2. Export the symbol's M1 bars to a CSV with the header `time,open,high,low,close,spread` (server time, bid, spread in price units).
|
| 3. `python3 tests/compare_trades.py <log file> <m1.csv> --tf 60 --target 2.0 --from 2023-07-01` lists the entries both share and
|
| those only one of them has.
|
|
|
| The broker price history and the EA's trade lists used for the published numbers are not in this repository.
|
|
|
| ## Honest limits
|
|
|
| - **No edge is shown.** The evidence is a single 3.3-year window in one gold regime, with 8 nominal passes where 8.3 were
|
| expected by chance. Treat the EA as unproven automation.
|
| - **Mostly buys.** The result depends on the buy side in a period of rising gold.
|
| - **Costs.** Commission and slippage are not modelled; the swap is from the broker's sheet at the time; the test ticks are
|
| 1-minute OHLC.
|
| - **XAUEUR is offered by few brokers.** The EA refuses live orders on other symbols and timeframes (the tester accepts any).
|
| - **Server clock.** The entry window and the 23:00 exit follow the broker's server time, not UTC.
|
| - **Settings were chosen by in-sample profit factor only,** and no walk-forward test was run.
|
| - **MetaTrader 4 has no economic calendar and a different tick model**; the MT4 edition's numbers come from its own tester
|
| run on a fixed spread.
|
| - No profit or accuracy claim is made.
|
|
|
| ## Free vs Pro
|
|
|
| | | Free: Prior Day Breakout | Paid: Prior Day Breakout Pro |
|
| |---|---|---|
|
| | Platforms | MT5 [201959](https://www.mql5.com/en/market/product/201959), MT4 coming soon | MT5 [201963](https://www.mql5.com/en/market/product/201963), MT4 coming soon |
|
| | Price | free | paid (see the Market page) |
|
| | What you get | the rule above, fixed 0.01 lot, XAUEUR H1 and M15 presets | the same rule with risk-based sizing, news modes, spread and Friday filters, as described on its Market listing |
|
| | Source | this repository | not published |
|
|
|
| No need to buy the paid product to use the free one: the free EA is complete on its own.
|
| Website: [allanninal.dev/mql5](https://www.allanninal.dev/mql5/#eas) ([MT4](https://www.allanninal.dev/mql5/#mt4)).
|
|
|
| ## Licence
|
|
|
| Everything in this repository is © 2026 Allan Niñal under the
|
| [PolyForm Noncommercial License 1.0.0](LICENSES/PolyForm-Noncommercial-1.0.0.md): source-available, free for personal and
|
| other noncommercial use, not for commercial use. Each source file has an `SPDX-License-Identifier` line. Text:
|
| [`LICENSE`](LICENSE). The previous-day high/low breakout is a public idea; this is an independent implementation. The licence
|
| applies to this source, not to anything you build or trade with the EA. Trading involves risk; the software is provided as is.
|
|
|
| ---
|
|
|
| **Website:** [allanninal.dev/mql5](https://www.allanninal.dev/mql5/#eas) · [MT4](https://www.allanninal.dev/mql5/#mt4) · [allanninal.dev](https://www.allanninal.dev)
|
| · Questions: message me on [LinkedIn](https://www.linkedin.com/in/allanninal/).
|