Viewing the resource: Fair Value Gap (FVG) Smart Money Concept (SMC) MQL5 Expert Advisor (EA)

Fair Value Gap (FVG) Smart Money Concept (SMC) MQL5 Expert Advisor (EA)

Allan Munene Mutiiria 2025-06-03 23:27:04 549 Views
The "FVG SMC EA" is a beginner-friendly MQL5 strategy for MetaTrader 5, based on Fair Value Gaps (FV...

Introduction

Welcome, new traders, to the "FVG SMC EA"! This MQL5 strategy for MetaTrader 5 is a fantastic starting point for beginners exploring Smart Money Concepts (SMC). Fair Value Gaps (FVGs) are price zones where the market moved so quickly it left untested areas, which we expect price to revisit. This EA identifies these gaps, draws them as colored rectangles (green for bullish, red for bearish), and places trades when price enters these zones. With a customizable risk-to-reward ratio and robust validation checks, it automates SMC trading. Let’s dive into the code, explained step-by-step for newbies, to see how it brings this strategy to life.

Understanding the Strategy

Imagine you’re watching a price chart, and suddenly the market jumps, leaving a gap where no trading happened. That’s an FVG! In SMC, we believe price often returns to these gaps. A bullish FVG forms when a candle’s low is higher than the high of two candles ago, suggesting a strong upward move. A bearish FVG is the opposite: a candle’s high is below the low of two candles ago. We only consider gaps wider than "min_pts" (100 points) to ensure significance. When price re-enters a bullish FVG from above, we buy, expecting a bounce to the FVG’s high. For a bearish FVG, we sell when price enters from below, aiming for the low. Stop-losses are set beyond the gap, and take-profits are scaled by "r2r_ratio" (default 10). The EA checks lot sizes, margin, and broker rules to keep trades safe. Have a hint here.

Implementation Code

Below is the MQL5 code for the "FVG SMC EA". We’ll break it into large, logical sections, explaining each in detail to help beginners follow the flow from setup to trading.

//+------------------------------------------------------------------+
//|                                                   FVG SMC EA.mq5 |
//|      Copyright 2024, ALLAN MUNENE MUTIIRIA. #@Forex Algo-Trader. |
//|                           https://youtube.com/@ForexAlgo-Trader? |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, ALLAN MUNENE MUTIIRIA. #@Forex Algo-Trader"
#property link      "https://youtube.com/@ForexAlgo-Trader?"
#property description "======= IMPORTANT =======\n"
#property description "1. This is a FREE EA."
#property description "2. To get the source code of the EA, follow the Copyright link."
#property description "3. Incase of anything, contact developer via the link provided."
                      "Hope you will enjoy the EA Logic.\n"
#property description "*** HAPPY TRADING! ***"
#property version   "7.00"

#include <Trade/Trade.mqh>
CTrade obj_Trade;

sinput group "✔🔰 EA GENERAL SETTINGS 💱💲"
input double inpLot = 0.01; // Lotsize
input double r2r_ratio = 10; // Risk : Reward Ratio
sinput string def_prefix = "FVG REC "; // FVG Prefix
sinput color def_clr_up = clrLime; // BULL FVG Color
sinput color def_clr_down = clrRed; // BEAR FVG Color
input int min_pts = 100; // FVG Minimum Range Points
input int ext_bars = 10; // FVG Length in Bars
sinput bool prt = true; // Print Statements

sinput string src_code1 = "https://t.me/forexalgo_trading"; // Source Code HERE 👉
sinput string src_code2 = "https://youtube.com/@ForexAlgo-Trader?"; // Join Community HERE 👉

#define FVG_Prefix def_prefix
#define CLR_UP def_clr_up
#define CLR_DOWN def_clr_down

int minPts = min_pts;
int FVG_Rec_Ext_Bars = ext_bars;

string totalFVGs[];
int barINDICES[];
datetime barTIMEs[];
bool signalFVGs[];

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){

   int visibleBars = (int)ChartGetInteger(0,CHART_VISIBLE_BARS);
   if (prt){Print("Total visible bars on chart = ",visibleBars);}
   
   if (ObjectsTotal(0,0,OBJ_RECTANGLE)==0){
      if (prt){Print("No FVGs Found, Resizing storage arrays to 0 now!!!");}
      ArrayResize(totalFVGs,0);
      ArrayResize(barINDICES,0);
      ArrayResize(signalFVGs,0);
   }
   
   ObjectsDeleteAll(0,FVG_Prefix);
   
   for (int i=0; i<=visibleBars; i++){
      //Print("Bar Index = ",i);
      double low0 = iLow(_Symbol,_Period,i);
      double high2 = iHigh(_Symbol,_Period,i+2);
      double gap_L0_H2 = NormalizeDouble((low0 - high2)/_Point,_Digits);
      
      double high0 = iHigh(_Symbol,_Period,i);
      double low2 = iLow(_Symbol,_Period,i+2);
      double gap_H0_L2 = NormalizeDouble((low2 - high0)/_Point,_Digits);
      
      bool FVG_UP = low0 > high2 && gap_L0_H2 > minPts;
      bool FVG_DOWN = low2 > high0 && gap_H0_L2 > minPts;
      
      if (FVG_UP || FVG_DOWN){
         if (prt){Print("Bar Index with FVG = ",i+1);}
         datetime time1 = iTime(_Symbol,_Period,i+1);
         double price1 = FVG_UP ? high2 : high0;
         datetime time2 = time1 + PeriodSeconds(_Period)*FVG_Rec_Ext_Bars;
         double price2 = FVG_UP ? low0 : low2;
         string fvgNAME = FVG_Prefix+"("+TimeToString(time1)+")";
         color fvgClr = FVG_UP ? CLR_UP : CLR_DOWN;
         CreateRec(fvgNAME,time1,price1,time2,price2,fvgClr);
         if (prt){Print("Old ArraySize = ",ArraySize(totalFVGs));}
         ArrayResize(totalFVGs,ArraySize(totalFVGs)+1);
         ArrayResize(barINDICES,ArraySize(barINDICES)+1);
         if (prt){Print("New ArraySize = ",ArraySize(totalFVGs));}
         totalFVGs[ArraySize(totalFVGs)-1] = fvgNAME;
         barINDICES[ArraySize(barINDICES)-1] = i+1;
         if (prt){ArrayPrint(totalFVGs);}
         if (prt){ArrayPrint(barINDICES);}
      }
   }
   
   for (int i=ArraySize(totalFVGs)-1; i>=0; i--){
      string objName = totalFVGs[i];
      string fvgNAME = ObjectGetString(0,objName,OBJPROP_NAME);
      int barIndex = barINDICES[i];
      datetime timeSTART = (datetime)ObjectGetInteger(0,fvgNAME,OBJPROP_TIME,0);
      datetime timeEND = (datetime)ObjectGetInteger(0,fvgNAME,OBJPROP_TIME,1);
      double fvgLOW = ObjectGetDouble(0,fvgNAME,OBJPROP_PRICE,0);
      double fvgHIGH = ObjectGetDouble(0,fvgNAME,OBJPROP_PRICE,1);
      color fvgColor = (color)ObjectGetInteger(0,fvgNAME,OBJPROP_COLOR);
      
      if (prt){Print("FVG NAME = ",fvgNAME," >No: ",barIndex," TS: ",timeSTART," TE: ",
            timeEND," LOW: ",fvgLOW," HIGH: ",fvgHIGH," CLR = ",fvgColor);}
      for (int k=barIndex-1; k>=(barIndex-FVG_Rec_Ext_Bars); k--){
         datetime barTime = iTime(_Symbol,_Period,k);
         double barLow = iLow(_Symbol,_Period,k);
         double barHigh = iHigh(_Symbol,_Period,k);
         //Print("Bar No: ",k," >Time: ",barTime," >H: ",barHigh," >L: ",barLow);
         
         if (k==0){
            if (prt){Print("OverFlow Detected @ fvg ",fvgNAME);}
            UpdateRec(fvgNAME,timeSTART,fvgLOW,barTime,fvgHIGH);
            break;
         }
         
         if ((fvgColor == CLR_DOWN && barHigh > fvgHIGH) ||
            (fvgColor == CLR_UP && barLow < fvgLOW)
         ){
            if (prt){Print("Cut Off @ bar no: ",k," of Time: ",barTime);}
            UpdateRec(fvgNAME,timeSTART,fvgLOW,barTime,fvgHIGH);
            break;
         }
      }
      
   }
   
   ArrayResize(totalFVGs,0);
   ArrayResize(barINDICES,0);

   return(INIT_SUCCEEDED);
}

This section sets up the EA. The "#property" lines label it: "copyright" (Allan Munene, 2024), "link" (YouTube), and "description" notes it’s free with contact info, version 7.00. We include the "Trade.mqh" library for trading, creating "obj_Trade" as our trading tool. Inputs let you tweak settings: "inpLot" (0.01) sets trade size, "r2r_ratio" (10) scales profits, "def_prefix" ("FVG REC ") names rectangles, "def_clr_up" (lime) and "def_clr_down" (red) color FVGs, "min_pts" (100) sets minimum gap size, "ext_bars" (10) defines FVG duration, and "prt" (true) enables logging. We define constants like "FVG_Prefix" and set globals: "minPts", "FVG_Rec_Ext_Bars", and arrays "totalFVGs" (FVG names), "barINDICES" (bar numbers), "barTIMEs" (times), and "signalFVGs" (trade flags). In "OnInit", we count visible bars with "ChartGetInteger" and log them. If no rectangles exist ("ObjectsTotal"), we clear arrays with "ArrayResize". We delete old FVGs with "ObjectsDeleteAll". We loop through bars, using "iLow" and "iHigh" to find gaps. If a gap exceeds "minPts" ("NormalizeDouble"), we draw a rectangle with "CreateRec", set times via "iTime" and "PeriodSeconds", and store in "totalFVGs" and "barINDICES". We then adjust FVGs: for each, we get properties with "ObjectGetString", "ObjectGetInteger", and "ObjectGetDouble", checking if newer bars cross the FVG (e.g., high above bearish FVG) using "iLow" and "iHigh", updating with "UpdateRec". Finally, we clear arrays and return "INIT_SUCCEEDED".

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){
   
   for (int i=0; i<=FVG_Rec_Ext_Bars; i++){
      double low0 = iLow(_Symbol,_Period,i+1);
      double high2 = iHigh(_Symbol,_Period,i+2+1);
      double gap_L0_H2 = NormalizeDouble((low0 - high2)/_Point,_Digits);
      
      double high0 = iHigh(_Symbol,_Period,i+1);
      double low2 = iLow(_Symbol,_Period,i+2+1);
      double gap_H0_L2 = NormalizeDouble((low2 - high0)/_Point,_Digits);
      
      bool FVG_UP = low0 > high2 && gap_L0_H2 > minPts;
      bool FVG_DOWN = low2 > high0 && gap_H0_L2 > minPts;
      
      if (FVG_UP || FVG_DOWN){
         datetime time1 = iTime(_Symbol,_Period,i+1+1);
         double price1 = FVG_UP ? high2 : high0;
         datetime time2 = time1 + PeriodSeconds(_Period)*FVG_Rec_Ext_Bars;
         double price2 = FVG_UP ? low0 : low2;
         string fvgNAME = FVG_Prefix+"("+TimeToString(time1)+")";
         color fvgClr = FVG_UP ? CLR_UP : CLR_DOWN;
         
         if (ObjectFind(0,fvgNAME) < 0){
            CreateRec(fvgNAME,time1,price1,time2,price2,fvgClr);
            if (prt){Print("Old ArraySize = ",ArraySize(totalFVGs));}
            ArrayResize(totalFVGs,ArraySize(totalFVGs)+1);
            ArrayResize(barTIMEs,ArraySize(barTIMEs)+1);
            ArrayResize(signalFVGs,ArraySize(signalFVGs)+1);
            if (prt){Print("New ArraySize = ",ArraySize(totalFVGs));}
            totalFVGs[ArraySize(totalFVGs)-1] = fvgNAME;
            barTIMEs[ArraySize(barTIMEs)-1] = time1;
            signalFVGs[ArraySize(signalFVGs)-1] = false;
            if (prt){ArrayPrint(totalFVGs);}
            if (prt){ArrayPrint(barTIMEs);}
            if (prt){ArrayPrint(signalFVGs);}
         }
      }
   }
   
   for (int j=ArraySize(totalFVGs)-1; j>=0; j--){
      bool fvgExist = false;
      string objName = totalFVGs[j];
      string fvgNAME = ObjectGetString(0,objName,OBJPROP_NAME);
      double fvgLow = ObjectGetDouble(0,fvgNAME,OBJPROP_PRICE,0);
      double fvgHigh = ObjectGetDouble(0,fvgNAME,OBJPROP_PRICE,1);
      color fvgColor = (color)ObjectGetInteger(0,fvgNAME,OBJPROP_COLOR);
      
      for (int k=1; k<=FVG_Rec_Ext_Bars; k++){
         double barLow = iLow(_Symbol,_Period,k);
         double barHigh = iHigh(_Symbol,_Period,k);
         
         if (barHigh == fvgLow || barLow == fvgLow){
            //Print("Found: ",fvgNAME," @ bar ",k);
            fvgExist = true;
            break;
         }
      }
      
      //Print("Existence of ",fvgNAME," = ",fvgExist);
      
      double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
      double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
      
      if (fvgColor == CLR_DOWN && Bid > fvgHigh && !signalFVGs[j]){
         if (prt){Print("SELL SIGNAL For (",fvgNAME,") Now @ ",Bid);}
         double SL_sell = Ask + NormalizeDouble((((fvgHigh-fvgLow)/_Point)*r2r_ratio)*_Point,_Digits);
         double trade_lots = Check1_ValidateVolume_Lots(inpLot);
         
         if (Check2_Margin(ORDER_TYPE_SELL,trade_lots) &&
             Check3_VolumeLimit(trade_lots) &&
             Check4_TradeLevels(POSITION_TYPE_SELL,SL_sell,fvgLow)){
            obj_Trade.Sell(trade_lots,_Symbol,Bid,SL_sell,fvgLow);
            signalFVGs[j] = true;
         }
         if (prt){ArrayPrint(totalFVGs,_Digits," [< >] ");}
         if (prt){ArrayPrint(signalFVGs,_Digits," [< >] ");}
      }
      else if (fvgColor == CLR_UP && Ask < fvgLow && !signalFVGs[j]){
         if (prt){Print("BUY SIGNAL For (",fvgNAME,") Now @ ",Ask);}
         double SL_buy = Bid - NormalizeDouble((((fvgHigh-fvgLow)/_Point)*r2r_ratio)*_Point,_Digits);
         double trade_lots = Check1_ValidateVolume_Lots(inpLot);

         if (Check2_Margin(ORDER_TYPE_BUY,trade_lots) &&
             Check3_VolumeLimit(trade_lots) &&
             Check4_TradeLevels(POSITION_TYPE_BUY,SL_buy,fvgHigh)){
            obj_Trade.Buy(trade_lots,_Symbol,Ask,SL_buy,fvgHigh);
            signalFVGs[j] = true;
         }
         if (prt){ArrayPrint(totalFVGs,_Digits," [< >] ");}
         if (prt){ArrayPrint(signalFVGs,_Digits," [< >] ");}
      }
      
      if (fvgExist == false){
         bool removeName = ArrayRemove(totalFVGs,0,1);
         bool removeTime = ArrayRemove(barTIMEs,0,1);
         bool removeSignal = ArrayRemove(signalFVGs,0,1);
         if (removeName && removeTime && removeSignal){
            if (prt){Print("Success removing the FVG DATA from the arrays. New Data as Below:");}
            if (prt){Print("FVGs: ",ArraySize(totalFVGs)," TIMEs: ",ArraySize(barTIMEs),
                     " SIGNALs: ",ArraySize(signalFVGs));}
            if (prt){ArrayPrint(totalFVGs);}
            if (prt){ArrayPrint(barTIMEs);}
            if (prt){ArrayPrint(signalFVGs);}
         }
      }      
   }
   
}

This section handles cleanup and live trading. The "OnDeinit" function is empty, meaning no resources need freeing when the EA stops—like closing a notebook. The "OnTick" function runs every price tick, keeping the EA active. We check recent bars (up to "FVG_Rec_Ext_Bars") for new FVGs using "iLow" and "iHigh" to compare prices, calculating gaps with "NormalizeDouble". If a valid FVG is found and not drawn ("ObjectFind"), we create a rectangle with "CreateRec", store it in "totalFVGs", "barTIMEs", and "signalFVGs" (set to false) using "ArrayResize", and log with "Print" and "ArrayPrint". Next, we monitor existing FVGs: for each in "totalFVGs", we fetch properties with "ObjectGetString", "ObjectGetDouble", and "ObjectGetInteger", and check if recent bars touch the FVG’s low using "iLow" and "iHigh". We get "Ask" and "Bid" with "SymbolInfoDouble" and "NormalizeDouble". For a bearish FVG ("CLR_DOWN"), if "Bid" is above "fvgHigh" and no trade ("signalFVGs[j]"), we sell at "Bid" with "obj_Trade.Sell", setting stop-loss above ("SL_sell") and take-profit at "fvgLow", scaled by "r2r_ratio", after passing "Check1_ValidateVolume_Lots", "Check2_Margin", "Check3_VolumeLimit", and "Check4_TradeLevels". For a bullish FVG, if "Ask" is below "fvgLow", we buy at "Ask" with "obj_Trade.Buy". We mark "signalFVGs[j]" true and log. If an FVG is no longer valid, we remove it with "ArrayRemove" and log.

void CreateRec(string objName,datetime time1,double price1,
               datetime time2, double price2,color clr){
   if (ObjectFind(0,objName) < 0){
      ObjectCreate(0,objName,OBJ_RECTANGLE,0,time1,price1,time2,price2);
      
      ObjectSetInteger(0,objName,OBJPROP_TIME,0,time1);
      ObjectSetDouble(0,objName,OBJPROP_PRICE,0,price1);
      ObjectSetInteger(0,objName,OBJPROP_TIME,1,time2);
      ObjectSetDouble(0,objName,OBJPROP_PRICE,1,price2);
      ObjectSetInteger(0,objName,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,objName,OBJPROP_FILL,true);
      ObjectSetInteger(0,objName,OBJPROP_BACK,false);
      
      ChartRedraw(0);
   }
}

void UpdateRec(string objName,datetime time1,double price1,
               datetime time2, double price2){
   if (ObjectFind(0,objName) >= 0){
      ObjectSetInteger(0,objName,OBJPROP_TIME,0,time1);
      ObjectSetDouble(0,objName,OBJPROP_PRICE,0,price1);
      ObjectSetInteger(0,objName,OBJPROP_TIME,1,time2);
      ObjectSetDouble(0,objName,OBJPROP_PRICE,1,price2);
      
      ChartRedraw(0);
   }
}

//+------------------------------------------------------------------+
//|      1. CHECK TRADING VOLUME                                     |
//+------------------------------------------------------------------+

double Check1_ValidateVolume_Lots(double lots){
   double symbolVol_Min = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
   double symbolVol_Max = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
   double symbolVol_STEP = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
      
   double accepted_Lots;
   double CurrentLots = lots;
   accepted_Lots = MathMax(MathMin(CurrentLots,symbolVol_Max),symbolVol_Min);
   
   int lotDigits = 0;
   if (symbolVol_Min == 1) lotDigits = 0;
   if (symbolVol_Min == 0.1) lotDigits = 1;
   if (symbolVol_Min == 0.01) lotDigits = 2;
   if (symbolVol_Min == 0.001) lotDigits = 3;

   double normalized_lots = NormalizeDouble(accepted_Lots,lotDigits);
   //Print("MIN LOTS = ",symbolVol_Min,", NORMALIZED LOTS = ",normalized_lots);
   
   return (normalized_lots);
}

These functions draw FVGs and check lot sizes. The "CreateRec" function makes a rectangle for an FVG if it’s not already drawn ("ObjectFind"). We use "ObjectCreate" to draw an "OBJ_RECTANGLE", set its times and prices with "ObjectSetInteger" and "ObjectSetDouble", apply color ("OBJPROP_COLOR"), fill it ("OBJPROP_FILL"), and keep it non-background ("OBJPROP_BACK"). We update the chart with "ChartRedraw". The "UpdateRec" function adjusts an existing FVG’s boundaries if found, updating times and prices and redrawing. The "Check1_ValidateVolume_Lots" function ensures valid trade sizes: we get minimum and maximum volumes with "SymbolInfoDouble" ("SYMBOL_VOLUME_MIN", "SYMBOL_VOLUME_MAX"), limit "lots" with "MathMax" and "MathMin", set decimal places ("lotDigits") based on "symbolVol_Min", and return a precise "normalized_lots" with "NormalizeDouble".

//+------------------------------------------------------------------+
//|      2. CHECK MONEY/MARGIN TO OPEN POSITION                      |
//+------------------------------------------------------------------+

bool Check2_Margin(ENUM_ORDER_TYPE Order_Type,double lot_Vol){
   double margin;
   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
   
   double openPrice = (Order_Type == ORDER_TYPE_BUY) ? Ask : Bid;
   
   bool result = OrderCalcMargin(Order_Type,_Symbol,lot_Vol,openPrice,margin);
   if (result == false){
      Print("ERROR: Something Unexpected Happened While Calculating Margin");
      return (false);
   }
   if (margin > AccountInfoDouble(ACCOUNT_MARGIN_FREE)){
      Print("WARNING! NOT ENOUGH MARGIN TO OPEN THE POSITION. NEEDED = ",margin);
      return (false);
   }
   return (true);
}

//+------------------------------------------------------------------+
//|      3. CHECK VOLUME LIMIT                                       |
//+------------------------------------------------------------------+

bool Check3_VolumeLimit(double lots_Vol_Limit){
   double volumeLimit = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_LIMIT);
   double symb_Vol_Max40 = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
   double allowed_Vol_Lim = (volumeLimit == 0) ? symb_Vol_Max40 : volumeLimit;
   if (getAllVolume()+lots_Vol_Limit > allowed_Vol_Lim){
      Print("WARNING! VOLUME LIMIT REACHED: LIMIT = ",allowed_Vol_Lim);
      return (false);
   }
   return (true);
}

double getAllVolume(){
   ulong ticket=0;
   double Volume=0;
   
   for (int i=PositionsTotal()-1 ;i>=0 ;i--){
      ticket = PositionGetTicket(i);
      if (PositionSelectByTicket(ticket)){
         if (PositionGetString(POSITION_SYMBOL)==_Symbol){
            Volume += PositionGetDouble(POSITION_VOLUME);
         }
      }
   }
   
   for (int i=OrdersTotal()-1 ;i>=0 ;i--){
      ticket = OrderGetTicket(i);
      if (OrderSelect(ticket)){
         if (OrderGetString(ORDER_SYMBOL)==_Symbol){
            Volume += OrderGetDouble(ORDER_VOLUME_CURRENT);
         }
      }
   }
   return (Volume);
}

These functions validate funds and volume limits. The "Check2_Margin" function checks if you have enough money: we get "Ask" and "Bid" with "SymbolInfoDouble", pick "openPrice" based on "Order_Type" ("ORDER_TYPE_BUY" or "ORDER_TYPE_SELL"), and calculate margin with "OrderCalcMargin". If it fails or exceeds free margin ("AccountInfoDouble"), we log with "Print" and return false; else, true. The "Check3_VolumeLimit" function ensures we don’t exceed broker limits: we get "volumeLimit" and "SYMBOL_VOLUME_MAX" with "SymbolInfoDouble", use the stricter limit, and check if "getAllVolume" plus "lots_Vol_Limit" is too high, logging with "Print" if so. The "getAllVolume" function totals volumes: it loops through positions ("PositionsTotal", "PositionGetTicket") and orders ("OrdersTotal", "OrderGetTicket"), adding "POSITION_VOLUME" or "ORDER_VOLUME_CURRENT" with "PositionGetDouble" and "OrderGetDouble" for the symbol, returning "Volume".

//+------------------------------------------------------------------+
//|      4. CHECK TRADE LEVELS                                       |
//+------------------------------------------------------------------+

bool Check4_TradeLevels(ENUM_POSITION_TYPE pos_Type,double sl=0,double tp=0,ulong tkt=0){
   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
   
   int stopLevel = (int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);
   int freezeLevel = (int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_FREEZE_LEVEL);
   int spread = (int)SymbolInfoInteger(_Symbol,SYMBOL_SPREAD);
   
   double stopLevel_Pts = stopLevel*_Point;
   double freezeLevel_Pts = freezeLevel*_Point;
   
   if (pos_Type == POSITION_TYPE_BUY){
      // STOP LEVELS CHECK
      if (tp > 0 && tp - Bid < stopLevel_Pts){
         Print("WARNING! BUY TP ",tp,", Bid ",Bid," (TP-Bid = ",NormalizeDouble((tp-Bid)/_Point,_Digits),") WITHIN STOP LEVEL OF ",stopLevel);
         return (false);
      }
      if (sl > 0 && Bid - sl < stopLevel_Pts){
         Print("WARNING! BUY SL ",sl,", Bid ",Bid," (Bid-SL = ",NormalizeDouble((Bid-sl)/_Point,_Digits),") WITHIN STOP LEVEL OF ",stopLevel);
         return (false);
      }
      // FREEZE LEVELS CHECK
      if (tp > 0 && tp - Bid < freezeLevel_Pts){
         Print("WARNING! BUY TP ",tp,", Bid ",Bid," (TP-Bid = ",NormalizeDouble((tp-Bid)/_Point,_Digits),") WITHIN FREEZE LEVEL OF ",freezeLevel);
         return (false);
      }
      if (sl > 0 && Bid - sl < freezeLevel_Pts){
         Print("WARNING! BUY SL ",sl,", Bid ",Bid," (Bid-SL = ",NormalizeDouble((Bid-sl)/_Point,_Digits),") WITHIN FREEZE LEVEL OF ",freezeLevel);
         return (false);
      }
   }
   if (pos_Type == POSITION_TYPE_SELL){
      // STOP LEVELS CHECK
      if (tp > 0 && Ask - tp < stopLevel_Pts){
         Print("WARNING! SELL TP ",tp,", Ask ",Ask," (Ask-TP = ",NormalizeDouble((Ask-tp)/_Point,_Digits),") WITHIN STOP LEVEL OF ",stopLevel);
         return (false);
      }
      if (sl > 0 && sl - Ask < stopLevel_Pts){
         Print("WARNING! SELL SL ",sl,", Ask ",Ask," (SL-Ask = ",NormalizeDouble((sl-Ask)/_Point,_Digits),") WITHIN STOP LEVEL OF ",stopLevel);
         return (false);
      }
      
      // FREEZE LEVELS CHECK
      if (tp > 0 && Ask - tp < freezeLevel_Pts){
         Print("WARNING! SELL TP ",tp,", Ask ",Ask," (Ask-TP = ",NormalizeDouble((Ask-tp)/_Point,_Digits),") WITHIN FREEZE LEVEL OF ",freezeLevel);
         return (false);
      }
      if (sl > 0 && sl - Ask < freezeLevel_Pts){
         Print("WARNING! SELL SL ",sl,", Ask ",Ask," (SL-Ask = ",NormalizeDouble((sl-Ask)/_Point,_Digits),") WITHIN FREEZE LEVEL OF ",freezeLevel);
         return (false);
      }
   }
   
   if (tkt > 0){
      bool result = PositionSelectByTicket(tkt);
      if (result == false){
         Print("ERROR Selecting The Position (CHECK) With Ticket # ",tkt);
         return (false);
      }
      double point = SymbolInfoDouble(_Symbol,SYMBOL_POINT);
      double pos_SL = PositionGetDouble(POSITION_SL);
      double pos_TP = PositionGetDouble(POSITION_TP);
      
      bool slChanged = MathAbs(pos_SL - sl) > point;
      bool tpChanged = MathAbs(pos_TP - tp) > point;

      if (!slChanged && !tpChanged){
         Print("ERROR. Pos # ",tkt," Already has Levels of SL: ",pos_SL,
               ", TP: ",pos_TP," NEW[SL = ",sl," | TP = ",tp,"]. NO POINT IN MODIFYING!!!");
         return (false);
      }
   }
   
   return (true);
}

//+------------------------------------------------------------------+
//|      5. CHECK & CORRECT TRADE LEVELS                             |
//+------------------------------------------------------------------+

double Check5_TradeLevels_Rectify(ENUM_POSITION_TYPE pos_Type,double sl=0,double tp=0){
   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
   
   int stopLevel = (int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);
   int freezeLevel = (int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_FREEZE_LEVEL);
   int spread = (int)SymbolInfoInteger(_Symbol,SYMBOL_SPREAD);
   
   double stopLevel_Pts = stopLevel*_Point;
   double freezeLevel_Pts = freezeLevel*_Point;
   
   double accepted_price = 0.0;
   
   if (pos_Type == POSITION_TYPE_BUY){
      // STOP LEVELS CHECK
      if (tp > 0 && tp - Bid < stopLevel_Pts){
         accepted_price = Bid+stopLevel_Pts;
         Print("WARNING! BUY TP ",tp,", Bid ",Bid," (TP-Bid = ",NormalizeDouble((tp-Bid)/_Point,_Digits),") WITHIN STOP LEVEL OF ",stopLevel);
         Print("PRICE MODIFIED TO: ",accepted_price);
         return (accepted_price);
      }
      if (sl > 0 && Bid - sl < stopLevel_Pts){
         accepted_price = Bid-stopLevel_Pts;
         Print("WARNING! BUY SL ",sl,", Bid ",Bid," (Bid-SL = ",NormalizeDouble((Bid-sl)/_Point,_Digits),") WITHIN STOP LEVEL OF ",stopLevel);
         Print("PRICE MODIFIED TO: ",accepted_price);
         return (accepted_price);
      }
      // FREEZE LEVELS CHECK
      if (tp > 0 && tp - Bid < freezeLevel_Pts){
         accepted_price = Bid+freezeLevel_Pts;
         Print("WARNING! BUY TP ",tp,", Bid ",Bid," (TP-Bid = ",NormalizeDouble((tp-Bid)/_Point,_Digits),") WITHIN FREEZE LEVEL OF ",freezeLevel);
         Print("PRICE MODIFIED TO: ",accepted_price);
         return (accepted_price);
      }
      if (sl > 0 && Bid - sl < freezeLevel_Pts){
         accepted_price = Bid-freezeLevel_Pts;
         Print("WARNING! BUY SL ",sl,", Bid ",Bid," (Bid-SL = ",NormalizeDouble((Bid-sl)/_Point,_Digits),") WITHIN FREEZE LEVEL OF ",freezeLevel);
         Print("PRICE MODIFIED TO: ",accepted_price);
         return (accepted_price);
      }
   }
   if (pos_Type == POSITION_TYPE_SELL){
      // STOP LEVELS CHECK
      if (tp > 0 && Ask - tp < stopLevel_Pts){
         accepted_price = Ask-stopLevel_Pts;
         Print("WARNING! SELL TP ",tp,", Ask ",Ask," (Ask-TP = ",NormalizeDouble((Ask-tp)/_Point,_Digits),") WITHIN STOP LEVEL OF ",stopLevel);
         Print("PRICE MODIFIED TO: ",accepted_price);
         return (accepted_price);
      }
      if (sl > 0 && sl - Ask < stopLevel_Pts){
         accepted_price = Ask+stopLevel_Pts;
         Print("WARNING! SELL SL ",sl,", Ask ",Ask," (SL-Ask = ",NormalizeDouble((sl-Ask)/_Point,_Digits),") WITHIN STOP LEVEL OF ",stopLevel);
         Print("PRICE MODIFIED TO: ",accepted_price);
         return (accepted_price);
      }
      
      // FREEZE LEVELS CHECK
      if (tp > 0 && Ask - tp < freezeLevel_Pts){
         accepted_price = Ask-freezeLevel_Pts;
         Print("WARNING! SELL TP ",tp,", Ask ",Ask," (Ask-TP = ",NormalizeDouble((Ask-tp)/_Point,_Digits),") WITHIN FREEZE LEVEL OF ",freezeLevel);
         Print("PRICE MODIFIED TO: ",accepted_price);
         return (accepted_price);
      }
      if (sl > 0 && sl - Ask < freezeLevel_Pts){
         accepted_price = Ask+freezeLevel_Pts;
         Print("WARNING! SELL SL ",sl,", Ask ",Ask," (SL-Ask = ",NormalizeDouble((sl-Ask)/_Point,_Digits),") WITHIN FREEZE LEVEL OF ",freezeLevel);
         Print("PRICE MODIFIED TO: ",accepted_price);
         return (accepted_price);
      }
   }
   return (accepted_price);
}

These functions ensure trades meet broker rules. The "Check4_TradeLevels" function verifies stop-loss and take-profit levels: we get "Ask" and "Bid" with "SymbolInfoDouble", and "stopLevel" and "freezeLevel" with "SymbolInfoInteger". For buys ("POSITION_TYPE_BUY"), we check if "tp" or "sl" are too close to "Bid" ("stopLevel_Pts" or "freezeLevel_Pts"), logging with "Print" and returning false if so. For sells, we check against "Ask". If a ticket ("tkt") is provided, we use "PositionSelectByTicket" to compare current "POSITION_SL" and "POSITION_TP" with "PositionGetDouble", returning false if unchanged ("MathAbs"). The "Check5_TradeLevels_Rectify" function fixes invalid levels: for buys, if "tp" or "sl" is too close, we adjust to "Bid" plus/minus "stopLevel_Pts" or "freezeLevel_Pts", logging with "Print" and returning "accepted_price". For sells, we adjust relative to "Ask". This ensures trades comply with broker restrictions.

Conclusion

The "FVG SMC EA" is a powerful tool for new traders to explore SMC trading. We set up inputs, draw FVGs, trade gap revisits, and validate trades with checks for volume, margin, and levels. Test it on MetaTrader 5!

Disclaimer: For education only. Trading is risky—test on a demo account first.

Disclaimer: The ideas and strategies presented in this resource are solely those of the author and are intended for informational and educational purposes only. They do not constitute financial advice, and past performance is not indicative of future results. All materials, including but not limited to text, images, files, and any downloadable content, are protected by copyright and intellectual property laws and are the exclusive property of Forex Algo-Trader or its licensors. Reproduction, distribution, modification, or commercial use of these materials without prior written consent from Forex Algo-Trader is strictly prohibited and may result in legal action. Users are advised to exercise extreme caution, perform thorough independent research, and consult with qualified financial professionals before implementing any trading strategies or decisions based on this resource, as trading in financial markets involves significant risk of loss.

Recent Comments

Go to discussion to Comment or View other Comments