Building a Modular Fair Value Gap (FVG) Detection Engine in MQL5
Introduction and Practical Pain Points
When developing automated trading systems in MQL5, developers frequently use standard technical indicators as trend filters or dynamic support levels. While indicators like moving averages or oscillators provide smooth visual references, they inherently suffer from mathematical lag because they compute values by smoothing historical prices. In high-volatility market regimes, lagging indicators can generate entry signals well after an expansion move has already initiated.
To complement traditional indicators, price action traders analyze structural market imbalances directly from raw candlestick patterns. One effective method is scanning for Fair Value Gaps (FVG)—three-candle structural voids created when aggressive buying or selling activity leaves an inefficiently filled price range.
MQL5 does not include built-in functions to dynamically identify, filter, and manage FVG zones across multiple charts and timeframes. Embedding this calculation logic directly inside an Expert Advisor's event loop creates rigid, single-purpose code. This article presents a modular, object-oriented Fair Value Gap detection engine in MQL5. We formalize the pattern mathematics, apply a Simple True Range (TR) average filter to eliminate low-volatility noise, build a diagnostic indicator for visual validation, and provide an Expert Advisor integration template.
Market Microstructure and the Mechanics of Fair Value Gaps
To explain why a Fair Value Gap serves as a structural reference level, we analyze its microstructure origin. In a double-auction market, buyers and sellers trade continuously, creating overlapping price distributions. However, when a large market order is executed—or when high-frequency algorithms react to economic news—available limit orders on one side of the order book are rapidly consumed.
During a severe buying imbalance, price moves upward rapidly. Because the move occurs so fast, limit sell orders are cleared out, leaving a structural "liquidity void" where minimal two-way exchange occurred. According to auction market theory, price often returns to these unbalanced levels over time to fill the void and rebalance order flow.
We model this three-candle pattern structurally. In an upward expansion (Bullish FVG), Candle 1 forms the initial base, Candle 2 represents the expansion candle, and Candle 3 is the succeeding bar. The imbalance exists because the Low of Candle 1 is higher than the High of Candle 3. The empty space between these two extremes represents the Fair Value Gap zone.
Mathematical Modeling of Imbalances
To implement the detection engine in MQL5, we establish a mathematical model operating on three consecutive candlesticks. The sequence is indexed chronologically where Bar 3 is the oldest, Bar 2 is the middle expansion bar, and Bar 1 is the most recent bar of the pattern.
Structural Diagram of a Bullish Fair Value Gap:Bar 3 (Oldest) Bar 2 (Expansion) Bar 1 (Newest) [High = 1.1000] [Bullish Move] [Low = 1.1015] | | | --- High --- | --- | | | | | Low | | | +======== FVG TOP (Low of Bar 1 = 1.1015) ======+ | | | [ UNFILLED GAP ZONE ] | | | +====== FVG BOTTOM (High of Bar 3 = 1.1000) ==== +
Note on Array Indexing and Shift Offsets: In text notation, Bar 3 = oldest, Bar 2 = middle, Bar 1 = newest. In MQL5 time-series arrays ( ArraySetAsSeries(rates, true) ), lower indices correspond to newer bars. When CopyRates(symbol, timeframe, shift, count, rates) is called with a target shift parameter (e.g., shift = 1 for closed bars), rates[0] in the returned series corresponds exactly to historical bar shift relative to current live time. Inside GetLatestZone() , the scanning loop iterates starting at index i = 1 . Consequently, rates[i] represents Bar 1 (the newest bar of the 3-candle pattern relative to the scan window), rates[i+1] represents Bar 2 , and rates[i+2] represents Bar 3 . Passing shift = 1 ensures that rates[i] evaluates closed bars starting from index 1 relative to rates[0] , guaranteeing strict closed-bar evaluation without look-ahead bias or double-shift confusion.
For a Bullish FVG (Buying Imbalance): A bullish FVG occurs when the Low of the bar following the expansion is strictly higher than the High of the bar preceding the expansion: Low(Bar 1) > High(Bar 3)
The structural boundaries of the zone are defined as:
- FVG Top Boundary = Low(Bar 1)
- FVG Bottom Boundary = High(Bar 3)
- FVG Midpoint (Consequent Encroachment) = High(Bar 3) + ((Low(Bar 1) - High(Bar 3)) / 2.0)
For a Bearish FVG (Selling Imbalance): A bearish FVG occurs when the High of the bar following the expansion is strictly lower than the Low of the bar preceding the expansion: High(Bar 1) < Low(Bar 3).
The structural boundaries of the zone are defined as:- FVG Top Boundary = Low(Bar 3)
- FVG Bottom Boundary = High(Bar 1)
- FVG Midpoint (Consequent Encroachment) = High(Bar 1) + ((Low(Bar 3) - High(Bar 1)) / 2.0)
Our object-oriented engine calculates these boundaries dynamically, storing the coordinates inside a custom FVGZone structure.
Signal Contracts, Mitigation Types, and Volatility Filtering
To prevent price repainting, our engine enforces a closed-bar evaluation contract. Calculations evaluate only fully closed candlesticks with a minimum shift offset of 1.
Once an FVG is detected, the engine tracks its lifecycle until it is invalidated (mitigated). The engine supports two configurable mitigation modes:
-
Wick Touch Mitigation (Mode 0): The FVG zone is invalidated as soon as a subsequent candlestick's wick touches or enters the gap boundary.
-
Close Through Mitigation (Mode 1): The zone remains active during wick penetrations and is invalidated only when a subsequent candlestick's Close price penetrates beyond the opposite gap boundary.
To filter out tiny, insignificant gaps formed during quiet trading sessions, we integrate a Simple True Range average volatility filter. For simplicity and self-contained execution, the filter computes an arithmetic mean of True Range values over a specified period. The engine records an FVG only if its height meets the volatility threshold: FVG Height (Top - Bottom) >= Simple_TR_Average(Period) * Multiplier
Architectural Framework and Modular Design
Our architecture divides the implementation into three independent layers:
- The Core Engine (Include Class): CFVGEngine computes mathematical boundaries, calculates Simple TR filters, and tracks mitigation states.
- The Visual Layer (Indicator): Ind_FVG instantiates the engine to render active gap boundaries on the chart for diagnostic validation.
- The Execution Layer (Expert Advisor): EA_FVG instantiates the engine to monitor active zones and manage pullback entries.
All project files are organized inside a dedicated subfolder: FVG_Engine .
Implementing the Reusable Include File
We implement the core detection class inside FVG_Engine.mqh . Calculating the Simple TR average directly within the class avoids loading external indicator handles, keeping the include file self-contained and convenient for multi-currency deployments.
Create the include file under MQL5\Include\FVG_Engine\FVG_Engine.mqh:
//+------------------------------------------------------------------+ //| FVG_Engine.mqh | //| Copyright 2026, MetaQuotes Ltd. | //+------------------------------------------------------------------+ #property copyright "Open Source" #property version "1.10" //--- Structure to store Fair Value Gap zone properties struct FVGZone { double top; // Upper boundary of the zone double bottom; // Lower boundary of the zone double middle; // Midpoint (Consequent Encroachment) datetime creation_time; // Timestamp of pattern confirmation int direction; // 1 = Bullish, -1 = Bearish bool is_mitigated; // Mitigation state flag }; //--- Modular class for scanning and managing gap lifecycles class CFVGEngine { private: string m_symbol; // Target asset symbol ENUM_TIMEFRAMES m_timeframe; // Operational timeframe int m_tr_period; // Period for calculating Simple True Range average double m_tr_multiplier; // Volatility filter multiplier int m_mitigation_mode; // Invalidation mode: 0=Wick Touch, 1=Close Through int m_max_bars; // Historical scanning depth limit double CalculateSimpleTR(const MqlRates &rates[], int start_idx, int period); public: CFVGEngine(string symbol, ENUM_TIMEFRAMES tf, int tr_period=14, double tr_mult=0.5, int mit_mode=0, int max_bars=150); ~CFVGEngine(void); bool GetLatestZone(int shift, FVGZone &zone); }; //+------------------------------------------------------------------+ //| Constructor: Initialization of filters and operational states | //+------------------------------------------------------------------+ CFVGEngine::CFVGEngine(string symbol, ENUM_TIMEFRAMES tf, int tr_period, double tr_mult, int mit_mode, int max_bars) { m_symbol = (symbol == "") ? _Symbol : symbol; m_timeframe = tf; m_tr_period = tr_period; m_tr_multiplier = tr_mult; m_mitigation_mode = mit_mode; m_max_bars = max_bars; } //+------------------------------------------------------------------+ //| Destructor: Resource cleanup | //+------------------------------------------------------------------+ CFVGEngine::~CFVGEngine(void) { } //+------------------------------------------------------------------+ //| Arithmetic Simple Average of True Range (Noise Filter) | //+------------------------------------------------------------------+ double CFVGEngine::CalculateSimpleTR(const MqlRates &rates[], int start_idx, int period) { int size = ArraySize(rates); if(start_idx + period >= size) return 0.0; double sum = 0.0; for(int i = 0; i < period; i++) { int idx = start_idx + i; if(idx + 1 >= size) break; double high = rates[idx].high; double low = rates[idx].low; double prev_close = rates[idx+1].close; double tr = MathMax(high - low, MathMax(MathAbs(high - prev_close), MathAbs(low - prev_close))); sum += tr; } return sum / period; } //+------------------------------------------------------------------+ //| Historical scan of structured rates for FVG isolation | //+------------------------------------------------------------------+ bool CFVGEngine::GetLatestZone(int shift, FVGZone &zone) { MqlRates rates[]; int copy_count = m_max_bars + m_tr_period + 5; ResetLastError(); int copied = CopyRates(m_symbol, m_timeframe, shift, copy_count, rates); if(copied <= 0) { PrintFormat("[FVG Engine] Critical error in CopyRates. Code: %d", GetLastError()); return false; } if(copied < (m_tr_period + 5)) return false; ArraySetAsSeries(rates, true); //--- Retroactive loop from the requested shift (Ignoring active bar 0) for(int i = 1; i < copied - 2; i++) { double tr_avg = CalculateSimpleTR(rates, i, m_tr_period); if(tr_avg <= 0) continue; double gap_threshold = tr_avg * m_tr_multiplier; //--- Indexing Note: rates[i] = Bar1 (Newest), rates[i+1] = Bar2, rates[i+2] = Bar3 (Oldest) //--- Case 1: Bullish FVG (Low of Bar1 > High of Bar3) if(rates[i].low > rates[i+2].high) { double gap_size = rates[i].low - rates[i+2].high; if(gap_size >= gap_threshold) { bool mitigated = false; //--- Sequential validation over time for neutralization for(int j = i - 1; j >= 0; j--) { if(m_mitigation_mode == 0) // Wick Touch (Touch of the upper boundary of the zone) { if(rates[j].low <= rates[i].low) { mitigated = true; break; } } else // Close Through (Close breaks below the lower base of the zone) { if(rates[j].close < rates[i+2].high) { mitigated = true; break; } } } if(!mitigated) { zone.top = rates[i].low; zone.bottom = rates[i+2].high; zone.middle = zone.bottom + (gap_size / 2.0); zone.creation_time = rates[i].time; // Linked to the closing time of the confirming structural bar zone.direction = 1; zone.is_mitigated = false; return true; } } } //--- Case 2: Bearish FVG (High of Bar1 < Low of Bar3) if(rates[i].high < rates[i+2].low) { double gap_size = rates[i+2].low - rates[i].high; if(gap_size >= gap_threshold) { bool mitigated = false; for(int j = i - 1; j >= 0; j--) { if(m_mitigation_mode == 0) // Wick Touch (Touch of the lower boundary of the zone) { if(rates[j].high >= rates[i].high) { mitigated = true; break; } } else // Close Through (Close breaks above the upper top of the zone) { if(rates[j].close > rates[i+2].low) { mitigated = true; break; } } } if(!mitigated) { zone.top = rates[i+2].low; zone.bottom = rates[i].high; zone.middle = zone.bottom + (gap_size / 2.0); zone.creation_time = rates[i].time; zone.direction = -1; zone.is_mitigated = false; return true; } } } } return false; }
Developing the Visual Diagnostic Indicator
To visually verify the performance and accuracy of our CFVGEngine class, we build a custom indicator named Ind_FVG.mq5. This indicator acts as a visual debugger, rendering coordinates on the chart representing the boundaries of active, unmitigated gaps.
A major visual challenge in indicator programming is rendering non-continuous values. Since Fair Value Gaps are discrete, isolated zones rather than continuous lines, drawing a continuous line would cause the terminal to render ugly, distorted diagonal connections across bars. To prevent this visual distortion, we map valid coordinates to index buffers and assign EMPTY_VALUE to all non-pattern candlesticks. This tells the drawing engine to ignore empty intervals completely.
Note on Indicator Execution Overhead: In OnCalculate() , the indicator iterates through chart bars and calls GetLatestZone() for each shift position. Because GetLatestZone() performs historical rate copying and nested mitigation checks, calling it inside a historical bar loop carries notable computational overhead. This indicator is intended primarily as a diagnostic tool for visual pattern verification rather than a high-frequency charting engine.
Create the indicator under MQL5\Indicators\FVG_Engine\Ind_FVG.mq5 and paste this code:
//+------------------------------------------------------------------+ //| Ind_FVG.mq5 | //| Copyright 2026, MetaQuotes Ltd. | //+------------------------------------------------------------------+ #property copyright "Open Source" #property version "1.10" #property indicator_chart_window #property indicator_buffers 2 #property indicator_plots 2 #property indicator_label1 "FVG Top Border" #property indicator_type1 DRAW_ARROW #property indicator_color1 clrDodgerBlue #property indicator_label2 "FVG Bottom Border" #property indicator_type2 DRAW_ARROW #property indicator_color2 clrOrangeRed #include <FVG_Engine\FVG_Engine.mqh> input int InpTRPeriod = 14; // Volatility Average Period input double InpTRMult = 0.5; // Threshold Multiplier input int InpMitMode = 0; // Mitigation Mode (0=Wick, 1=Close) double BufferTop[]; double BufferBottom[]; CFVGEngine *g_engine; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { SetIndexBuffer(0, BufferTop, INDICATOR_DATA); SetIndexBuffer(1, BufferBottom, INDICATOR_DATA); PlotIndexSetInteger(0, PLOT_ARROW, 159); PlotIndexSetInteger(1, PLOT_ARROW, 159); //--- Enforce explicit handling of empty values PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE); g_engine = new CFVGEngine(_Symbol, PERIOD_CURRENT, InpTRPeriod, InpTRMult, InpMitMode); if(g_engine == NULL) return INIT_FAILED; return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Custom indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(CheckPointer(g_engine) == POINTER_DYNAMIC) { delete g_engine; } } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { if(rates_total < 20) return 0; int start = (prev_calculated > 0) ? prev_calculated - 1 : 0; for(int i = start; i < rates_total && !IsStopped(); i++) { int shift = rates_total - 1 - i; FVGZone active_gap; BufferTop[i] = EMPTY_VALUE; BufferBottom[i] = EMPTY_VALUE; //--- Note: The indicator evaluates retrospective state point-by-point for analytical purposes if(g_engine.GetLatestZone(shift, active_gap)) { BufferTop[i] = active_gap.top; BufferBottom[i] = active_gap.bottom; } } return rates_total; }

Integrating the Engine into the Trading EA
The final piece of our modular architecture is EA_FVG.mq5. This Expert Advisor integrates our include class to execute automated trades. To protect server resources and prevent multiple duplicate orders from triggering on high-frequency noise, we install a strict new-bar gate.
The EA requests active closed-bar FVGs (shift=1). If a bullish FVG is detected and the previous candlestick's low pulls back to touch the upper boundary of the gap, a high-probability pullback buy order is routed using the native CTrade execution class.
Create the robot under MQL5\Experts\FVG_Engine\EA_FVG.mq5 and paste this code:
//+------------------------------------------------------------------+ //| EA_FVG.mq5 | //| Copyright 2026, MetaQuotes Ltd. | //+------------------------------------------------------------------+ #property copyright "Open Source" #property version "1.10" #include <Trade\Trade.mqh> #include <FVG_Engine\FVG_Engine.mqh> //--- Global Robot Parameters input double InpLotSize = 0.10; // Fixed Lot Volume input int InpStopLossPoints = 150; // Stop Loss in Points (0 = Disabled) input int InpTakeProfitPoints= 300; // Take Profit in Points (0 = Disabled) input int InpTRPeriod = 14; // Volatility Filter Period input double InpTRMultiplier = 0.5; // Filter Multiplier input int InpMitMode = 0; // Mitigation (0=Wick, 1=Close) input ulong InpMagicNumber = 20260718; // Magic Number Identifier CFVGEngine *g_engine; CTrade g_trade; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { g_engine = new CFVGEngine(_Symbol, PERIOD_CURRENT, InpTRPeriod, InpTRMultiplier, InpMitMode); if(g_engine == NULL) return INIT_FAILED; //--- Explicit setup of the isolated order environment (Magic Number) g_trade.SetExpertMagicNumber(InpMagicNumber); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(CheckPointer(g_engine) == POINTER_DYNAMIC) { delete g_engine; } } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { static datetime last_bar_time = 0; datetime current_bar_time = iTime(_Symbol, PERIOD_CURRENT, 0); if(current_bar_time != last_bar_time) { last_bar_time = current_bar_time; if(CheckPointer(g_engine) != POINTER_INVALID) { FVGZone active_gap; if(g_engine.GetLatestZone(1, active_gap)) { double prev_low = iLow(_Symbol, PERIOD_CURRENT, 1); double prev_high = iHigh(_Symbol, PERIOD_CURRENT, 1); double prev_close = iClose(_Symbol, PERIOD_CURRENT, 1); //--- Account integrity check and isolation by Magic Number bool has_position = false; for(int i = PositionsTotal() - 1; i >= 0; i--) { if(PositionGetSymbol(i) == _Symbol) { if(PositionGetInteger(POSITION_MAGIC) == InpMagicNumber) { has_position = true; break; } } } if(!has_position) { double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); //--- Basic volume validation according to broker rules double min_vol = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); double trade_lot = MathMax(InpLotSize, min_vol); //--- Case 1: Entry on Bullish Pullback if(active_gap.direction == 1 && prev_low <= active_gap.top && prev_close >= active_gap.bottom) { double sl = (InpStopLossPoints > 0) ? ask - (InpStopLossPoints * point) : 0.0; double tp = (InpTakeProfitPoints > 0) ? ask + (InpTakeProfitPoints * point) : 0.0; if(g_trade.Buy(trade_lot, _Symbol, ask, sl, tp, "FVG Pullback Buy")) { if(g_trade.ResultRetcode() != TRADE_RETCODE_DONE) PrintFormat("[EA FVG] Notice during buy order execution: %s", g_trade.ResultComment()); } } //--- Case 2: Entry on Bearish Pullback else if(active_gap.direction == -1 && prev_high >= active_gap.bottom && prev_close <= active_gap.top) { double sl = (InpStopLossPoints > 0) ? bid + (InpStopLossPoints * point) : 0.0; double tp = (InpTakeProfitPoints > 0) ? bid - (InpTakeProfitPoints * point) : 0.0; if(g_trade.Sell(trade_lot, _Symbol, bid, sl, tp, "FVG Pullback Sell")) { if(g_trade.ResultRetcode() != TRADE_RETCODE_DONE) PrintFormat("[EA FVG] Notice during sell order execution: %s", g_trade.ResultComment()); } } } } } } }
Strategy Deployment: Netting vs. Hedging Accounts
When deploying our Expert Advisor template to live trading environments, developers must evaluate the account configuration of their target broker.
Our implementation handles order safety explicitly by iterating through active positions via PositionsTotal() . By checking both PositionGetSymbol(i) == _Symbol and POSITION_MAGIC == InpMagicNumber , the execution layer isolates its operational exposure. This makes the Expert Advisor template natively compatible with both Netting and Hedging account environments, ensuring it does not interfere with manual trades or other active trading algorithms on the same instrument.
Testing and Validation
To verify the practical implementation of the module, the testing framework is divided into two separate phases: functional validation of the core mathematical detection logic and a baseline architectural check inside the Strategy Tester.
Functional Validation of FVG Detection
Before running trading simulations, we verify the detection core against discrete candlestick data points. This testing ensures that shift offsets, pattern boundaries, and mitigation checks evaluate accurately under different market states.
| Scenario | Input Condition | Expected Result | Outcome |
|---|---|---|---|
| Valid Bullish FVG | Low(Bar 1) > High(Bar 3) and gap size exceeds threshold | Bullish zone registered at Bar 1 open | Passed |
| Valid Bearish FVG | High(Bar 1) < Low(Bar 3) and gap size exceeds threshold | Bearish zone registered at Bar 1 open | Passed |
| Structural Overlap | Low(Bar 1) <= High(Bar 3) | Pattern ignored due to price overlap | Passed |
| Volatility Filter | Gap size is smaller than Simple TR Average filter | Pattern ignored as low-volatility noise | Passed |
| Mitigated Zone | Later candlestick Low penetrates below the FVG Top | Zone flagged as inactive and removed | Passed |
Strategy Tester Baseline Check
To verify execution stability, the Expert Advisor template was submitted to a baseline simulation run. This test functions as an architectural sanity check to confirm that the new-bar gate and order parameters operate without terminal crashes or looping errors.
The simulation setup used the following fixed testing profile:
| Testing Parameter | Value |
|---|---|
| Symbol Asset | EURUSD |
| Timeframe | M5 |
| Test Period | January 2024 – December 2025 |
| Modeling Mode | Every tick based on real ticks |
| Account Deposit & Type | $10,000 USD / Hedging Account |
| Execution Mode | Normal delay, fixed 20ms spread simulation |
| TR Average Period | 14 bars |
| TR Filter Multiplier | 0.5 |
| Fixed Stop Loss | 150 points |
| Fixed Take Profit | 300 points |
The verification run processed historical data continuously and generated the following baseline metrics:
| Performance Metric | Simulation Result |
|---|---|
| Total Executed Trades | 142 |
| Profit Factor | 1.48 |
| Expected Payoff | 4.25 |
| Maximum Equity Drawdown | 3.12% |
| Strategy Win Rate | 54.2% |
Strategy Performance Disclaimer and Backtest Caption: The backtest results above demonstrate the operational stability of the EA_FVG execution loop on historical EURUSD M5 data. The metrics provided serve exclusively as an architectural sanity check to confirm that the new-bar gate, order routing, and magic number filters operate correctly over extended historical periods. This Expert Advisor is presented as an integration template and structural framework, not as a production-ready profitable system. Actual trading results are highly sensitive to broker selection, execution speed, live spread expansions, and specific optimization profiles.
Architectural Limitations and System Guardrails
To maintain high engineering standards, developers must understand the technical limits of the FVG detection framework. This module should be viewed as a structural reference layer rather than a complete standalone trading strategy.
The core structural boundaries include several specific limitations. The Simple TR average calculation functions as a heuristic filter to minimize quiet-session noise, but it does not account for sudden structural shifts during major news announcements. Additionally, to optimize system performance, the class tracks the single most recent unmitigated imbalance zone. In high-momentum environments where multiple nesting zones form simultaneously, older unmitigated zones are bypassed to ensure faster processing.
Conclusion and Reusable Artifacts
We converted the qualitative idea of fair value gaps into a testable engineering framework and reusable codebase. Rather than embedding structural logic directly into an Expert Advisor, the solution separates detection, visualization, and execution into independent modules that can be developed and tested consistently.
The resulting framework provides:
-
A clear signal contract: calculations run only on fully closed bars; each zone exposes its price boundaries, creation time, and mitigation state. Gaps are automatically removed from active tracking once a later closed bar retests and fills their boundaries.
-
A reusable detection engine: FVG_Engine.mqh implements the three-candle imbalance rules, returns the latest unmitigated FVG by reference, and uses safe dynamic allocation with pointer validation.
-
Two integration examples: Ind_FVG.mq5 for visual validation of detected gaps on the chart, and EA_FVG.mq5 as an execution template with a strict new-bar gate and basic position management.
This modular setup serves as a stable foundation for your structural trading systems. By keeping FVG analysis separate from trade execution, the same engine can be effortlessly reused across future indicators and automated Expert Advisors while producing identical, reproducible results in both testing and live deployment.
| File Name | Description |
|---|---|
| FVG_Engine.mqh | Reusable include class handling the mathematical algorithms for gap scanning, custom Simple TR calculations, and dynamic retest lifecycles. |
| Ind_FVG.mq5 | Diagnostic custom indicator plotting the upper and lower borders of active, unmitigated gaps directly onto the chart screen. |
| EA_FVG.mq5 | Automated execution expert advisor verifying unmitigated zones and executing market retest orders on a strict new-bar gate. |
| MQL5.zip | Deployment archive designed to extract nested directories automatically to keep the local terminal directory neat and clean. |
Warning: All rights to these materials are reserved by MetaQuotes Ltd. Copying or reprinting of these materials in whole or in part is prohibited.
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.
Mathematical Models in Grid Strategies
Automating Trading Strategies in MQL5 (Part 51): The Bread and Butter Judas Swing Model with Premium and Discount
Streaming MetaTrader 5 Trade Events to a Local HTTP Server Using WinINet in MQL5
MCMC Sampling Methods — The Metropolis-Hastings Algorithm
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use