E-->>I: line, side, pivots, arrows (drawn, no alert)
T->>I: OnCalculate (new bar closes, prev_calculated > 0)
I->>E: feed the one bar that just closed
E-->>I: did a new trend start?
alt new trend and bar time is newer than the last alert and not in the tester
I->>U: pop-up (optional sound, push, e-mail)
else no new trend, same bar again or tester run
I-->>T: nothing sent
end
```
### How often a new trend starts (measured)
New trends per 1,000 closed bars, default inputs (period 3, swing confirmation on), on broker history of EURUSD, GBPJPY,
XAUUSD and BTCUSD (H1: 284,859 bars; H4: 72,039; M15: 19,112 because the M15 history in the tester was shorter). It counts
trend changes only, not what price does afterwards. Source: [`docs/data/trend-frequency.json`](docs/data/trend-frequency.json),
made by [`tests/frequency.py`](tests/frequency.py).
```mermaid
xychart-beta
title "New trends per 1,000 closed bars (4 symbols, defaults)"
x-axis ["M15", "H1", "H4"]
y-axis "new trends per 1,000 bars" 0 --> 140
bar [125.84, 115.23, 111.34]
```
About one new trend every 8 to 9 bars on every timeframe. This is why the arrows should be read as the trend state, not as
a stream of entries; a longer period gives fewer changes.
## Non-repainting: the proof and how it was tested
"Non-repainting" is easy to claim. Here is what was actually run. Raw result files are in [`tests/results/`](tests/results).
**1. Implementations compared bar by bar.** The Python reference ([`tests/reference.py`](tests/reference.py)) holds the rules, operation for operation. The MT5 and MT4 indicators were dumped bar by bar (every buffer) in the MetaTrader Strategy Tester (MT4) or by a script (MT5), and [`tests/check_parity.py`](tests/check_parity.py) compared each dump with the reference: line, side, trend, arrows and swing pivots must be identical on every closed bar.
Run on this exact code: compile 0 errors, 0 warnings (both editions); MT5 24 dumps and MT4 25 dumps (EURUSD, GBPJPY, XAUUSD, BTCUSD on M15, H1 and H4 with the repository's input sets; MT4 adds an every-tick run on EURUSD H1), all identical to the reference on every closed bar; the repaint probes found 0 closed bars that changed afterwards.
**The wider matrix**, made with the Market builds before this repository was cut from them (the same calculation; four symbols EURUSD, GBPJPY, XAUUSD, BTCUSD, M15, H1 and H4, the repository's input sets), checked here against the same reference:
| | MT5 | MT4 |
|---|---|---|
| dumps compared | 24 | 25 |
| bars compared | 72,000 | 509,548 |
| failures | 0 | 0 |
| result | ALL PASS | ALL PASS |
Sources: [`tests/results/parity-wide-run-mt5.txt`](tests/results/parity-wide-run-mt5.txt), [`tests/results/parity-wide-run-mt4.txt`](tests/results/parity-wide-run-mt4.txt). MT5 history starts later than MT4's test history on some pairs, which is why the bar counts differ.
**2. Repaint probe.** An EA records which signal buffers are set on each bar *at the moment it closes*, then reads the same bars again at the end and counts any value that changed. It also counts any signal ever shown on the still-forming bar.
| Run | Closed bars checked | Signals seen at close | Changed afterwards | Signals on the live bar |
Source: [`tests/results/repaint-summary.md`](tests/results/repaint-summary.md). The MT5 probe was run once per build on one symbol and timeframe; the MT4 probe ran on every dump run.
**3. Market validation.** Both Market builds passed the MQL5 Market's automated product validation before publication (this is the Market's own check, not part of this repository).
```mermaid
flowchart LR
R[reference.py<br/>Python rules] --> P
S5[MT5 indicator] --> T5[Tester script<br/>DumpIndicator<br/>every bar to CSV]
**Install from source.** Copy `mql5/Indicators/GannHiLoSwing/` into `<terminal data folder>/MQL5/Indicators/` (MT5) or
`mql4/Indicators/GannHiLoSwing/` into `<data folder>/MQL4/Indicators/` (MT4), open the `.mq5` or `.mq4` in MetaEditor and
press Compile (F7). Both compile with 0 errors and 0 warnings (checked on a Windows machine with MetaEditor, from a clean scratch folder that holds only these files; the compiled files are not in the repository).
Keep the three files of each edition together: the indicator includes `Gann.mqh` and `SignalAlerts.mqh` from its own folder. The debug log needs the Chart Kit file as well: copy `mql5/Include/ChartKit/` (MT5) or `mql4/Include/ChartKit/` (MT4) into the `Include` folder of the data folder.
# h, l, c are float lists of closed bars, oldest first
rows,pivots=run(h,l,c,hilo=3,swing_confirm=True)# rows[k]: line, trend, side, sd, ev (+1/-1 new trend)
```
To repeat the parity check on your own data:
1. Compile the indicator and the test tool for your platform (`tests/mql5/Scripts/DumpIndicator`, or
`tests/mql4/GannDump.mq4` with `tests/mql4/Include/IndTest4/Dump4.mqh` in your `MQL4/Include/IndTest4/`; MT5 also needs
`tests/mql5/Include/IndTest/Params.mqh`).
2. Run it in the Strategy Tester (MT4) or as a script (MT5) for each symbol and timeframe, with the input sets in
`tests/configs.tsv` (MT5; the exact `InpParams` strings are in `tests/check_parity.py`) and `tests/mql4/configs.tsv`
(MT4). The tool writes one CSV per symbol and timeframe into a `dump` folder.
3.`python3 tests/check_parity.py <folder that contains dump>` (add `--mt4` for MT4 dumps). It prints PASS or FAIL per file
and an overall result.
4. Repaint: run `tests/mql5/Experts/RepaintProbe` in the MT5 Strategy Tester (every tick); the MT4 test EA writes its
repaint log with the dump.
The price dumps used for the published numbers are not in this repository (they are broker price data).
## Honest limits
- **A trend line is not a strategy.** No stop-loss, position sizing or exit rules are provided or implied, and the rules
were not optimised or back-tested for profit. An expert advisor built on these rules did not pass out-of-sample testing.
- **No profit or accuracy claim is made.** The only numbers here are about *consistency* (the implementations agree, lines
and arrows do not move) and *frequency* (new trends per 1,000 bars).
- **Fast by design.** Period 3 follows price closely: about one new trend every 8 to 9 bars. Raise the period or the
timeframe for fewer changes; sideways markets produce many.
- **Lag.** A trend change is confirmed by a close beyond an average, and a swing pivot is known only when the swing turns.
- **One reading of Gann.** Gann's tools have several modern interpretations; these are the rules stated above.
- **Scope of the tests.** Four symbols on M15, H1 and H4 for the Market builds (the same calculation), the repository's input sets, one MT5 repaint run; the code in this repository has so far been compile-checked only, and the MT5 build was not run on a live chart as part of these numbers. Other symbols and timeframes are not covered by the published numbers.
## Licence
Copyright 2026 Allan Niñal (<https://www.allanninal.dev/mql5>). Licensed 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 HiLo Activator and the 2-bar swing chart are classic, publicly described methods; the code is
original. The licence applies to this source, not to anything you build or trade with the indicator. Trading involves risk;