Forward-Simulation/Article-22927-For-Sim-V2/README.md

373 lines
No EOL
19 KiB
Markdown

# For_Sim.mq5 — Culmination Version
## EMA Crossover Forward Simulation: Interactive Synthetic Candles
This repository contains the final version of the Forward Simulation Engine,
a custom MQL5 indicator that projects synthetic OHLC candles ahead of an EMA
crossover signal and advances that projection in real time as new bars close.
It does not claim to reproduce any third-party source code.
All logic is original and derived from the accompanying two-part article series.
---
## Overview
The culmination version extends the original Forward Simulation Engine across
three dimensions that were absent in part one:
- **Instrument-calibrated sizing** — body heights and wick lengths are derived
from the measured averages of recent closed bars on the active symbol and
timeframe, not from the EMA slope alone
- **Organic visual variation** — body sizes follow a sine-wave envelope over
an exponential decay, and counter-trend candles are injected periodically to
simulate realistic pullbacks within the dominant direction
- **Live interactivity** — as each new real candle closes, the leading projected
candle is removed, keeping the simulation perpetually one bar ahead of the
chart without any manual intervention
The indicator renders two visible EMA lines via indicator buffers, projects up
to `FutureBars` synthetic candles into future time slots using chart objects,
and supports both automatic and manual anchor placement.
Designed for **M15** but adapts to any timeframe via `PeriodSeconds()`.
---
## Original Article Series
| Field | Value |
|-------------------|-----------------------------------------------------------------------------------------------|
| Part 1 Title | Creating an EMA Crossover Forward Simulation Indicator in MQL5 |
| Part 2 Title | Creating an EMA Crossover Forward Simulation (Culmination): Interactive Synthetic Candles |
| Author | johnhlomohang |
| Publication Date | <!-- ADD DATE --> |
| Category | Indicators |
| Part 1 URL | https://www.mql5.com/en/articles/22323 |
| Part 2 URL | https://www.mql5.com/en/articles/22927 |
---
## Repository Purpose
This repository accompanies the published article series. Its purpose is to:
- preserve the complete culmination-version source for study and extension
- document every input parameter, internal function, and design decision
- demonstrate the calibration, prediction, rendering, and interactive-removal
patterns described across both articles
- serve as a clean base for replacing the EMA crossover with structure-based,
ATR-driven, or machine-learning signal triggers without touching the rendering
or invalidation layers
---
## What Changed from Part 1
| Feature | Part 1 | Culmination (this version) |
|--------------------------------|---------------------------------|-----------------------------------------------------|
| Body size source | Raw EMA slope | Measured `g_AvgBody` × momentum scale factor |
| Wick size source | Fixed pip constant | Measured `g_AvgUpperWick` / `g_AvgLowerWick` ratios |
| Body variation across run | Exponential decay only | Sine-wave envelope × exponential decay |
| Counter-trend candles | None | Injected every `CounterCandleFreq` bars at 25–45% |
| Inter-candle spacing | None | Time-inset `OBJ_RECTANGLE` via `CandleGapFraction` |
| Projection advancement | Static — never moves | Leading candle removed as each real bar closes |
| Calibration refresh | None | `CalcAvgCandleMetrics` at init and on each signal |
| New inputs | — | `CandleGapFraction`, `CounterCandleFreq`, `AvgLookback` |
---
## Key Concepts
- fast and slow EMA crossover detection on fully closed bars only
- automatic anchor placement at the crossover bar (`AutoAnchor = true`)
- `CalcAvgCandleMetrics` — measures `g_AvgBody`, `g_AvgUpperWick`, `g_AvgLowerWick`, `g_AvgRange` from `AvgLookback` closed bars
- EMA slope normalised against `g_AvgBody` to produce a momentum scale factor in `[0.5, 1.5]`
- `sin²(phase)` envelope pulsing body sizes between 40% and 130% of `baseStep`
- `0.93^i` exponential decay flattening the projection toward the tail
- counter-trend candle injection every N bars retracing 25–45% of the prior body
- wick lengths = `bodyHeight × (avgWick / avgBody)` ± 25% jitter, floored at 20% of body
- `CandleGapFraction` insets the body rectangle inside each bar slot; wicks stay centred on the full slot
- pip-based invalidation: projection is cleared when live price moves `InvalidationPips` against the signal price
- interactive removal: `FutureCandle_0` objects are deleted each time `rates_total` increments
- dual-mode anchor: auto (tracks crossover bar) or manual (user-placed `OBJ_VLINE`)
- `ArraySize()` guard on indicator buffers prevents out-of-range writes during early ticks
---
## Architecture Summary
### Module 1 — Signal and Calibration (`RunEngine`, `CalcAvgCandleMetrics`)
`OnCalculate` fires on every tick. `RunEngine` reads `tmpFast[]` and `tmpSlow[]`
(non-series, index 0 = oldest) and compares the two most recently closed bars
(`rates_total-2` vs `rates_total-3`) for a crossover condition.
On a new cross, the signal state is latched and `CalcAvgCandleMetrics` is called.
That function uses `CopyOpen`, `CopyHigh`, `CopyLow`, `CopyClose` from shift 1
(skipping the live bar) to compute four averages over `AvgLookback` bars.
All four values are floored at 1 pip to handle flat markets.
`CalcAvgCandleMetrics` also runs once in `OnInit` so the first projection is
calibrated immediately on indicator attach.
### Module 2 — Prediction Engine (`GeneratePrediction`)
Accepts `startPrice` and `emaSlope`, builds a `PredictedCandle[]` of length
`FutureBars`. For each bar `i`:
```
momentumScale = 0.5 + min(|emaSlope| / g_AvgBody, 1.0) // range [0.5, 1.5]
baseStep = g_AvgBody × momentumScale
phase = i / FutureBars × 2π
envelope = 0.40 + 0.90 × sin²(phase) // range [0.40, 1.30]
decay = 0.93^i
jitter = 1.0 ± 0.08
bodySize = baseStep × envelope × decay × jitter
```
Floor applied: `bodySize` cannot fall below `0.5 × g_AvgBody`.
Every `CounterCandleFreq` bars (starting at bar 4, 8, 12, …), direction is
reversed and body size is reduced to `bodySize × retracePct` where
`retracePct` ∈ [0.25, 0.45].
Wick construction:
```
upWick = bodyHeight × (g_AvgUpperWick / g_AvgBody) × uJitter × 1.20 (if bullish)
dnWick = bodyHeight × (g_AvgLowerWick / g_AvgBody) × lJitter × 1.20 (if bearish)
```
Both wicks floored at `0.20 × bodyHeight`.
### Module 3 — Renderer (`DrawAllCandles`, `DrawSingleCandle`)
Each projected candle occupies one full bar-width time slot starting one bar
after the anchor. The body `OBJ_RECTANGLE` is inset by `gapSec` seconds on
each side of the slot. The wick `OBJ_TREND` lines are anchored to `tMid`
(centre of the full slot) so they remain visually centred despite the inset.
Every object is created with `OBJPROP_HIDDEN = false` and `OBJPROP_SELECTABLE = false`.
Supporting objects: `FSE_Sep` (dashed separator), `FSE_Label` (zone text),
`FSE_Invalid` (invalidation notice), `FSE_Anchor` (gold dash-dot vline).
Cleanup uses prefix-based `ObjectsDeleteAll(0, "FutureCandle_")` and
`ObjectsDeleteAll(0, "FSE_")` to remove all objects atomically.
### Interactive Removal
On each `OnCalculate` call, if `rates_total > prev_calculated` by exactly one,
a new bar has closed. The engine deletes the `FutureCandle_0_*` group and
renumbers the remaining candles from index 1 downward, keeping the sequence
continuously aligned one bar ahead of the live bar.
### Invalidation
On each tick the live bar's close (`close[rates_total-1]`) is compared to
`g_SignalPrice`. The threshold is `InvalidationPips × _Point × 10.0`.
On breach: all objects are deleted, `FSE_Invalid` is drawn, signal state
is cleared, and the engine waits silently for the next valid cross.
### `OnTimer` (manual anchor mode only)
When `AutoAnchor = false`, a 3-second timer polls `GetAnchorTime()`. If the
user has moved the `FSE_Anchor` vline, `g_DrawnAnchor` is reset to force a
full redraw on the next tick.
### Buffer Safety Pattern
MT5 indicator buffers (`FastEMABuffer`, `SlowEMABuffer`) are auto-sized by
the terminal and are not guaranteed to equal `rates_total` in early ticks.
All writes are gated by `MathMin(copiedFast, ArraySize(FastEMABuffer))` to
prevent out-of-range crashes.
---
## Attached Files
| File | Description |
|-----------------------------|--------------------------------|
| `For Sim.mq5` | Complete MQL5 custom indicator |
---
## Input Parameters
| Parameter | Default | Range / Notes |
|---------------------|----------------|------------------------------------------------------------------------|
| `FastEMA_Period` | 9 | Period of the fast EMA (close price) |
| `SlowEMA_Period` | 21 | Period of the slow EMA (close price) |
| `FutureBars` | 30 | Projected candles; keep ≤ 60 for visible tail bodies |
| `SpreadMultiplier` | 2.0 | Legacy floor multiplier; superseded by calibrated wick ratios |
| `AutoAnchor` | true | `true` = anchor tracks crossover bar; `false` = manual `OBJ_VLINE` |
| `AnchorLineName` | "FSE_Anchor" | Chart object name for the anchor vertical line |
| `BullishColor` | DodgerBlue | Fill color for trend-direction bullish projected candles |
| `BearishColor` | Crimson | Fill color for trend-direction bearish projected candles |
| `WickColor` | DimGray | Color for all upper and lower wick trend lines |
| `ShowZoneLabel` | true | Toggle `[ BULLISH PROJECTION ]` / `[ BEARISH PROJECTION ]` label |
| `ShowSeparatorLine` | true | Toggle dashed vertical separator at the anchor bar |
| `InvalidationPips` | 10 | Pip distance from signal price that triggers full projection removal |
| `CandleGapFraction` | 0.08 | Body inset as fraction of bar width; clamped to [0.01, 0.45] |
| `CounterCandleFreq` | 4 | Counter-trend candle every N bars; hard minimum enforced at 3 |
| `AvgLookback` | 50 | Closed bars sampled for calibration; hard minimum enforced at 10 |
---
## Chart Object Naming Convention
| Name pattern | Type | Role |
|-------------------------|-------------------|---------------------------------------|
| `FutureCandle_N_body` | `OBJ_RECTANGLE` | Filled candle body |
| `FutureCandle_N_bord` | `OBJ_RECTANGLE` | Unfilled body outline |
| `FutureCandle_N_wU` | `OBJ_TREND` | Upper wick |
| `FutureCandle_N_wD` | `OBJ_TREND` | Lower wick |
| `FSE_Sep` | `OBJ_VLINE` | Dashed silver separator at anchor |
| `FSE_Label` | `OBJ_TEXT` | Projection zone label |
| `FSE_Invalid` | `OBJ_TEXT` | Invalidation notice (orange) |
| `FSE_Anchor` | `OBJ_VLINE` | Gold dash-dot anchor line |
All prefixes are chosen so `ObjectsDeleteAll(0, "FutureCandle_")` and
`ObjectsDeleteAll(0, "FSE_")` remove every drawn object in two calls.
---
## Global State Variables
| Variable | Type | Description |
|-------------------|------------|----------------------------------------------------------|
| `g_FastHandle` | `int` | `iMA` handle for the fast EMA calculation |
| `g_SlowHandle` | `int` | `iMA` handle for the slow EMA calculation |
| `g_LastSignal` | `int` | +1 bullish, -1 bearish, 0 none |
| `g_SignalActive` | `bool` | Whether a valid, non-invalidated signal is live |
| `g_SignalPrice` | `double` | Close price of the bar where the cross occurred |
| `g_SignalBarTime` | `datetime` | Time of the crossover bar |
| `g_DrawnAnchor` | `datetime` | Anchor time of the last completed draw (redraw gate) |
| `g_AvgBody` | `double` | Mean `\|close - open\|` over `AvgLookback` bars |
| `g_AvgUpperWick` | `double` | Mean `high - max(open, close)` over `AvgLookback` bars |
| `g_AvgLowerWick` | `double` | Mean `min(open, close) - low` over `AvgLookback` bars |
| `g_AvgRange` | `double` | Mean `high - low` over `AvgLookback` bars |
---
## Statistics
| Metric | Value |
|---------------------------------|-----------------------------|
| Indicator type | Chart window overlay |
| Plot buffers (visible EMA lines)| 2 |
| Crossover evaluation window | 2 closed bars |
| Default projection length | 30 bars |
| Default calibration lookback | 50 bars (min 10) |
| Momentum scale range | 0.5× – 1.5× `g_AvgBody` |
| Sine envelope range | 0.40× – 1.30× `baseStep` |
| Exponential decay base | 0.93 per bar |
| Body size floor | 0.5 × `g_AvgBody` |
| Counter-trend retrace range | 25% – 45% of body |
| Wick minimum floor | 20% of body height |
| Gap clamp range | 1% – 45% of bar width |
| Chart objects per candle | 4 |
| Timer interval | 3 seconds (manual mode) |
| Invalidation unit | Pips (`_Point × 10`) |
---
## Installation
1. Copy `ForwardSimEngine.mq5` to your MetaTrader 5 data folder:
```
MQL5/Indicators/ForwardSimEngine.mq5
```
2. Open MetaEditor (F4 in MT5) and compile the file (F7). Zero errors expected.
3. In the MT5 Navigator panel under Custom Indicators, drag the indicator onto a chart.
4. Recommended timeframe: **M15**.
5. Click OK. Open the **Experts** tab and confirm the following log lines appear:
```
ForwardSimEngine [INIT]: OK FastEMA=9 SlowEMA=21 ...
ForwardSimEngine [AVG]: lookback=50 AvgBody=...
```
6. On the first EMA crossover, a `[SIGNAL]` and `[DRAWN]` log line will appear
and the projection will render to the right of the anchor.
---
## Experts Log Reference
| Tag | When it appears | What to check |
|-----------------|------------------------------------------------------|-------------------------------------------|
| `[INIT]` | On indicator attach | All input values confirmed correct |
| `[AVG]` | At init and on each new signal | AvgBody in points looks realistic for TF |
| `[TICK #1-3]` | First three ticks after attach | `fCur` and `sCur` are non-zero |
| `[SIGNAL]` | On EMA crossover | Direction, bar time, and price are right |
| `[DRAWN]` | After each successful render | Anchor time and bar count are correct |
| `[INVALIDATED]` | When live price breaches invalidation threshold | signalPx and livePx delta makes sense |
| `[WARN]` | If CopyBuffer returns ≤ 0 or bar count too low | Enough history loaded on that symbol/TF |
---
## Tuning Notes
- **`AvgLookback`** — use 80–100 on instruments with regime changes (e.g. XAUUSD around news). Use 20–30 to react faster to recent short-term volatility.
- **`InvalidationPips`** — start at 15–20 on XAUUSD and GBPJPY. Use 5–8 on tight instruments like EURUSD on M15.
- **`CandleGapFraction`** — 0.08 is well-suited for M15. Increase to 0.12 on H1 or H4 where bar slots are visually wider.
- **`CounterCandleFreq`** — 3 gives a more choppy, ranging look. 5–7 gives a cleaner impulsive projection.
- **`FutureBars`** — above 60, the `0.93^i` decay reduces tail bodies to the 0.5× floor and they all look the same size. 20–40 is the sweet spot.
- **Manual anchor replay** — set `AutoAnchor = false`, draw an `OBJ_VLINE` named `FSE_Anchor` on any historical bar, and the engine projects forward from that bar using the candle averages measured at that moment.
---
## Extension Points
The three modules are fully independent. Common extensions that require no changes
to the rendering or invalidation layers:
- Replace `DetectCrossover` in `RunEngine` with a structure-based (BOS/CHoCH) trigger
- Replace the EMA slope scale factor in `GeneratePrediction` with an ATR-based step size
- Add a confidence score input that reduces `FutureBars` or applies opacity graduation
- Feed `g_AvgBody` and `g_AvgRange` into a volatility-regime classifier to switch between
projection styles (trending vs ranging candle shapes)
---
## Limitations
- The projection is a structured visual scenario. It is not a price forecast and
makes no statistical claims about future price direction.
- Crossover detection uses only two closed bars. Whipsaw conditions on ranging
markets will produce frequent signal resets.
- The invalidation threshold is fixed in pips and does not adapt dynamically to
ATR or volatility regime.
- Calibration quality depends on the quantity and quality of history available.
Symbols with thin history on a given timeframe may produce unrealistic averages.
- The indicator does not place, manage, or close any trades. It is a visualization
tool only.
---
## Tags
`MQL5` `MetaTrader5` `CustomIndicator` `ForwardSimulation` `EMA` `Crossover`
`SyntheticCandles` `ChartObjects` `InteractiveProjection` `Calibration`
`SineEnvelope` `CounterTrend` `AlgorithmicTrading` `Visualization`
---
## Difficulty
**Intermediate — Advanced**
Requires familiarity with:
- MQL5 indicator development (`OnCalculate`, `SetIndexBuffer`, `CopyBuffer`)
- MT5 chart object API (`ObjectCreate`, `ObjectSetInteger`, `ObjectsDeleteAll`)
- MQL5 array indexing: series vs non-series layout, `prev_calculated` gating
- `ArraySize()` vs `rates_total` distinction for indicator buffer writes
- Basic EMA crossover concepts and pip arithmetic
- MetaTrader 5 Strategy Tester for visual and log validation
---
## Reference
Part 1 article: https://www.mql5.com/en/articles/22323
Part 2 article: https://www.mql5.com/en/articles/22927