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

19 KiB

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
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