//+------------------------------------------------------------------+
//| Advanced SMC+FVG+Demand/Supply EA (cleaned & compilable) |
//+------------------------------------------------------------------+
#property copyright "Generated"
#property version "1.00"
#property strict
#include <Trade/[Link]>
CTrade trade;
//---- Inputs ----
input double RiskPercent = 0.75;
input int OB_Lookback_H4 = 50;
input int OB_Lookback_H1 = 50;
input int FVG_Lookback_M15 = 50;
input double MaxSpreadPoints = 50.0;
input int MagicNumber = 20250823;
input bool UseKillzones = true;
input string Killzone1 = "07:00-10:30";
input string Killzone2 = "13:00-16:00";
input int Volume_SMA = 20;
input double Volume_Mult = 1.2;
// ATR-based lot sizing fallback
input int ATR_Period_H1 = 14;
input double ATR_SL_Mult = 1.2; // multiplier to derive SL distance from
ATR when needed
// News filter (stub — returns false by default to avoid platform-dependent
API calls)
input bool UseNewsFilter = false;
input int NewsPauseBefore = 15; // minutes before news
input int NewsPauseAfter = 15; // minutes after news
input int MinNewsImportance = 2; // 1=Low,2=Medium,3=High
//---- Zone struct ----
struct Zone
double top;
double bottom;
int dir; // 1 bullish zone, -1 bearish zone
datetime time; // reference time (candle time)
bool valid;
bool sweepDetected;
double sweepHigh;
double sweepLow;
};
//---- Zones ----
Zone OB_H4, OB_H1, FVG_H4, FVG_H1, FVG_M15, SD_H4, SD_H1, SD_M15;
//---- Symbol info ----
double symbol_point_val = 0.0;
double symbol_tickval = 0.0;
double symbol_ticksize = 0.0;
double symbol_minlot = 0.0;
double symbol_lotstep = 0.0;
//---- Time track ----
static datetime prevH4 = 0;
static datetime prevH1 = 0;
static datetime prevM15 = 0;
//---- Visual colors (no OBJPROP_TRANSPARENCY used) ----
color OB_Color = clrTomato; // visually distinct
color FVG_Color = clrDodgerBlue;
color SD_Color = clrLimeGreen;
//---- ATR handle ----
int atrHandle = 0;
//---- Track created object names so we can safely delete them on deinit ----
string createdObjects[];
bool CreatedObjectExists(const string name)
int n = ArraySize(createdObjects);
for(int i=0;i<n;i++) if(createdObjects[i]==name) return(true);
return(false);
void AddCreatedObjectName(const string name)
{
if(CreatedObjectExists(name)) return;
int n = ArraySize(createdObjects);
ArrayResize(createdObjects,n+1);
createdObjects[n] = name;
void DeleteCreatedObjects()
int n = ArraySize(createdObjects);
for(int i=0;i<n;i++)
if(ObjectFind(0, createdObjects[i]) >= 0) ObjectDelete(0,
createdObjects[i]);
ArrayResize(createdObjects,0);
//+------------------------------------------------------------------+
//| Expert initialization |
//+------------------------------------------------------------------+
int OnInit()
[Link](MagicNumber);
[Link](100);
// safe retrievals using reference overloads for doubles
SymbolInfoDouble(_Symbol, SYMBOL_POINT, symbol_point_val);
SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE, symbol_tickval);
SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE, symbol_ticksize);
SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN, symbol_minlot);
SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP, symbol_lotstep);
// fallback defaults (if broker doesn't provide)
if(symbol_point_val <= 0.0) symbol_point_val = 0.00001;
if(symbol_minlot <= 0.0) symbol_minlot = 0.01;
if(symbol_lotstep <= 0.0) symbol_lotstep = 0.01;
// create ATR indicator handle for H1 (fallback used if fails)
atrHandle = iATR(_Symbol, PERIOD_H1, ATR_Period_H1);
if(atrHandle == INVALID_HANDLE) atrHandle = 0;
// initialize zones as invalid
OB_H4.valid = OB_H1.valid = FVG_H4.valid = FVG_H1.valid =
FVG_M15.valid = false;
SD_H4.valid = SD_H1.valid = SD_M15.valid = false;
ArrayResize(createdObjects,0);
return(INIT_SUCCEEDED);
//+------------------------------------------------------------------+
//| Expert deinitialization |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(atrHandle != 0 && atrHandle != INVALID_HANDLE)
IndicatorRelease(atrHandle);
// delete objects we created (if any)
DeleteCreatedObjects();
//+------------------------------------------------------------------+
//| Tick handler |
//+------------------------------------------------------------------+
void OnTick()
if(!PassBasicChecks()) return;
// update timeframe-based zones on new higher timeframe candles
datetime lastH4 = iTime(_Symbol, PERIOD_H4, 0);
datetime lastH1 = iTime(_Symbol, PERIOD_H1, 0);
datetime lastM15 = iTime(_Symbol, PERIOD_M15, 0);
if(lastH4 != prevH4) { UpdateOB_FVG_SD_H4(); prevH4 = lastH4; }
if(lastH1 != prevH1) { UpdateOB_FVG_SD_H1(); prevH1 = lastH1; }
if(lastM15 != prevM15) { UpdateFVG_SD_M15(); prevM15 = lastM15; }
// detect liquidity sweeps for every zone
DetectSweepAll();
// draw zones
DrawZone("OB_H4", OB_H4, OB_Color);
DrawZone("OB_H1", OB_H1, OB_Color);
DrawZone("FVG_H4", FVG_H4, FVG_Color);
DrawZone("FVG_H1", FVG_H1, FVG_Color);
DrawZone("FVG_M15", FVG_M15, FVG_Color);
DrawZone("SD_H4", SD_H4, SD_Color);
DrawZone("SD_H1", SD_H1, SD_Color);
DrawZone("SD_M15", SD_M15, SD_Color);
// entries
if(CanEnterLong()) PlaceLong();
if(CanEnterShort()) PlaceShort();
//+------------------------------------------------------------------+
//| Basic filters |
//+------------------------------------------------------------------+
bool PassBasicChecks()
// spread retrieval: use SymbolInfoInteger (spread in points) to avoid
overloads
long spread_int = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
double spread_points = (double)spread_int;
double sp = (symbol_point_val > 0.0) ? (spread_points / symbol_point_val) :
spread_points;
if(sp > MaxSpreadPoints) return(false);
if(UseKillzones && !IsInKillzone()) return(false);
if(UseNewsFilter && IsNewsTime()) return(false); // IsNewsTime() returns
false by default unless you implement a news source
return(true);
//+------------------------------------------------------------------+
bool IsInKillzone()
string kz[2] = { Killzone1, Killzone2 };
for(int i=0;i<2;i++)
int dash = StringFind(kz[i], "-");
if(dash > 0)
string s1 = StringSubstr(kz[i], 0, dash);
string s2 = StringSubstr(kz[i], dash+1);
datetime t1 = StringToTime(TimeToString(TimeCurrent(), TIME_DATE) + "
" + s1);
datetime t2 = StringToTime(TimeToString(TimeCurrent(), TIME_DATE) + "
" + s2);
if(TimeCurrent() >= t1 && TimeCurrent() <= t2) return(true);
}
return(false);
//+------------------------------------------------------------------+
//| News check stub - returns false to avoid platform-dependent API |
//+------------------------------------------------------------------+
bool IsNewsTime()
// If you have a broker/build that exposes EconomicNews* functions,
implement here.
// For portability and clean compilation we return false (no pause).
return(false);
//+------------------------------------------------------------------+
//| FVG rejection check (M5 recent candle) |
//+------------------------------------------------------------------+
bool IsFVGRejected(Zone &fvg)
if(![Link]) return(false);
double open = (double)iOpen(_Symbol, PERIOD_M5, 0);
double close = (double)iClose(_Symbol, PERIOD_M5, 0);
double high = (double)iHigh(_Symbol, PERIOD_M5, 0);
double low = (double)iLow(_Symbol, PERIOD_M5, 0);
double height = [Link] - [Link];
if(height <= 0.0) return(false);
// long rejection: price dips into FVG then closes above 30% of gap
if([Link] == 1 && low <= [Link] && close > ([Link] + 0.3 *
height)) return(true);
// short rejection: price rises into FVG then closes below 30% from top
if([Link] == -1 && high >= [Link] && close < ([Link] - 0.3 * height))
return(true);
return(false);
//+------------------------------------------------------------------+
//| Opposite candle 30% rule (previous M5 candle) |
//+------------------------------------------------------------------+
bool OppositeCandle30Percent(bool wantLong, double zoneBottom, double
zoneTop)
// previous candle is index 1
double o1 = (double)iOpen(_Symbol, PERIOD_M5, 1);
double c1 = (double)iClose(_Symbol, PERIOD_M5, 1);
double height = zoneTop - zoneBottom;
if(height <= 0.0) return(false);
if(wantLong)
{
// previous must be bearish and its close penetrate >=30% into zone from
bottom
if(c1 < o1)
double penetration = c1 - zoneBottom; // how deep close is into zone
from bottom
if(penetration >= 0.3 * height) return(true);
else
// previous must be bullish and its close penetrate >=30% into zone from
top downward
if(c1 > o1)
double penetration = zoneTop - c1;
if(penetration >= 0.3 * height) return(true);
return(false);
//+------------------------------------------------------------------+
//| CanEnterLong / CanEnterShort - combine checks |
//+------------------------------------------------------------------+
bool CanEnterLong()
if(!OB_H4.valid || !OB_H1.valid || !FVG_M15.valid) return(false);
if(!OB_H4.sweepDetected || !OB_H1.sweepDetected || !
FVG_M15.sweepDetected) return(false);
if(!SD_H4.valid || !SD_H1.valid || !SD_M15.valid) return(false);
if(!VolumeConfirm()) return(false);
if(!IsFVGRejected(FVG_M15)) return(false);
double zoneB = MathMax(MathMax(OB_H1.bottom, OB_H4.bottom),
MathMax(SD_H1.bottom, SD_H4.bottom));
double zoneT = MathMin(MathMin(OB_H1.top, OB_H4.top),
MathMin(SD_H1.top, SD_H4.top));
if(zoneT <= zoneB) return(false);
if(!OppositeCandle30Percent(true, zoneB, zoneT)) return(false);
double close = (double)iClose(_Symbol, PERIOD_M5, 0);
if(close <= zoneB + 0.3 * (zoneT - zoneB)) return(false);
return(true);
bool CanEnterShort()
if(!OB_H4.valid || !OB_H1.valid || !FVG_M15.valid) return(false);
if(!OB_H4.sweepDetected || !OB_H1.sweepDetected || !
FVG_M15.sweepDetected) return(false);
if(!SD_H4.valid || !SD_H1.valid || !SD_M15.valid) return(false);
if(!VolumeConfirm()) return(false);
if(!IsFVGRejected(FVG_M15)) return(false);
double zoneB = MathMax(MathMax(OB_H1.bottom, OB_H4.bottom),
MathMax(SD_H1.bottom, SD_H4.bottom));
double zoneT = MathMin(MathMin(OB_H1.top, OB_H4.top),
MathMin(SD_H1.top, SD_H4.top));
if(zoneT <= zoneB) return(false);
if(!OppositeCandle30Percent(false, zoneB, zoneT)) return(false);
double close = (double)iClose(_Symbol, PERIOD_M5, 0);
if(close >= zoneT - 0.3 * (zoneT - zoneB)) return(false);
return(true);
//+------------------------------------------------------------------+
//| Detect sweeps for all zones |
//+------------------------------------------------------------------+
void DetectSweepAll()
DetectSweep(OB_H4);
DetectSweep(OB_H1);
DetectSweep(FVG_H4);
DetectSweep(FVG_H1);
DetectSweep(FVG_M15);
DetectSweep(SD_H4);
DetectSweep(SD_H1);
DetectSweep(SD_M15);
//+------------------------------------------------------------------+
//| Detect liquidity sweep in last 3 M5 candles |
//+------------------------------------------------------------------+
void DetectSweep(Zone &z)
if(![Link]) return;
// search last 3 M5 candles (shift 1..3) for wick beyond zone boundary and
close back inside
for(int shift = 1; shift <= 3; shift++)
double h = (double)iHigh(_Symbol, PERIOD_M5, shift);
double l = (double)iLow(_Symbol, PERIOD_M5, shift);
double c = (double)iClose(_Symbol, PERIOD_M5, shift);
if([Link] == 1)
// bullish zone: look for spike above top then close back inside
if(h > [Link])
// same candle closed inside
if(c <= [Link])
[Link] = true;
[Link] = h;
[Link] = [Link];
return;
// next candle close back inside (shift-1)
if(shift - 1 >= 0)
double c2 = (double)iClose(_Symbol, PERIOD_M5, shift - 1);
if(c2 <= [Link])
[Link] = true;
[Link] = h;
[Link] = [Link];
return;
else if([Link] == -1)
// bearish zone: spike below bottom then close back inside
if(l < [Link])
if(c >= [Link])
[Link] = true;
[Link] = l;
[Link] = [Link];
return;
if(shift - 1 >= 0)
double c2 = (double)iClose(_Symbol, PERIOD_M5, shift - 1);
if(c2 >= [Link])
[Link] = true;
[Link] = l;
[Link] = [Link];
return;
// keep previous sweepDetected value (do not reset here)
//+------------------------------------------------------------------+
//| ATR helper (safe CopyBuffer) |
//+------------------------------------------------------------------+
double GetATR_H1()
if(atrHandle == 0 || atrHandle == INVALID_HANDLE) return(0.0);
double buf[];
int copied = CopyBuffer(atrHandle, 0, 0, 1, buf);
if(copied <= 0) return(0.0);
return(buf[0]);
//+------------------------------------------------------------------+
//| Lot sizing using sweep SL if available, otherwise ATR fallback |
//+------------------------------------------------------------------+
double CalcLot(bool useAskAsEntry=true)
double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
double risk = accountBalance * RiskPercent / 100.0;
double entryPrice = 0.0;
if(useAskAsEntry)
SymbolInfoDouble(_Symbol, SYMBOL_ASK, entryPrice);
else
SymbolInfoDouble(_Symbol, SYMBOL_BID, entryPrice);
double sl_dist = 0.0;
// prefer using OB_H1 sweep distance if present (use absolute)
if(OB_H1.sweepDetected)
sl_dist = MathAbs(entryPrice - OB_H1.sweepHigh);
if(sl_dist <= 0.0) sl_dist = 0.0;
}
// fallback to ATR
if(sl_dist <= 0.0)
double atr = GetATR_H1();
if(atr <= 0.0) atr = symbol_point_val * 10;
sl_dist = ATR_SL_Mult * atr;
// ensure tick value
if(symbol_tickval <= 0.0) SymbolInfoDouble(_Symbol,
SYMBOL_TRADE_TICK_VALUE, symbol_tickval);
if(symbol_tickval <= 0.0) symbol_tickval = 1.0;
// calculate lot such that risk = sl_dist * symbol_tickval * lots
double lot = risk / (sl_dist * symbol_tickval);
if(symbol_minlot <= 0.0) SymbolInfoDouble(_Symbol,
SYMBOL_VOLUME_MIN, symbol_minlot);
if(symbol_minlot <= 0.0) symbol_minlot = 0.01;
if(symbol_lotstep <= 0.0) SymbolInfoDouble(_Symbol,
SYMBOL_VOLUME_STEP, symbol_lotstep);
if(symbol_lotstep <= 0.0) symbol_lotstep = 0.01;
lot = MathMax(lot, symbol_minlot);
lot = MathMin(lot, 5.0);
int steps = (int)MathFloor(lot / symbol_lotstep);
double finalLot = (double)steps * symbol_lotstep;
if(finalLot < symbol_minlot) finalLot = symbol_minlot;
return(finalLot);
//+------------------------------------------------------------------+
//| Volume confirmation |
//+------------------------------------------------------------------+
int VolumeConfirm()
double sum = 0.0;
for(int i=1;i<=Volume_SMA;i++) sum += (double)iVolume(_Symbol,
PERIOD_M5, i);
double avg = sum / (double)Volume_SMA;
double vol = (double)iVolume(_Symbol, PERIOD_M5, 0);
return (vol > avg * Volume_Mult) ? 1 : 0;
//+------------------------------------------------------------------+
//| PlaceLong / PlaceShort |
//+------------------------------------------------------------------+
void PlaceLong()
double price = 0.0; SymbolInfoDouble(_Symbol, SYMBOL_ASK, price);
double sl = 0.0;
// use sweep high if available for OB_H1, else ATR fallback
if(OB_H1.sweepDetected && OB_H1.sweepHigh > 0.0)
sl = OB_H1.sweepHigh + symbol_point_val * 5; // small buffer
else
double atr = GetATR_H1();
if(atr <= 0.0) atr = symbol_point_val * 10;
sl = price - atr * ATR_SL_Mult;
double tp = price + MathAbs(price - sl) * 2.0; // 2R
double lot = CalcLot(true);
if(lot <= 0.0) return;
[Link](lot, _Symbol, price, sl, tp);
void PlaceShort()
double price = 0.0; SymbolInfoDouble(_Symbol, SYMBOL_BID, price);
double sl = 0.0;
if(OB_H1.sweepDetected && OB_H1.sweepLow > 0.0)
sl = OB_H1.sweepLow - symbol_point_val * 5;
else
double atr = GetATR_H1();
if(atr <= 0.0) atr = symbol_point_val * 10;
sl = price + atr * ATR_SL_Mult;
}
double tp = price - MathAbs(sl - price) * 2.0;
double lot = CalcLot(false);
if(lot <= 0.0) return;
[Link](lot, _Symbol, price, sl, tp);
//+------------------------------------------------------------------+
//| Zone update placeholders (simple: last closed candle extremes) |
//| Replace these with your advanced OB/FVG/SD detection logic |
//+------------------------------------------------------------------+
void UpdateOB_FVG_SD_H4()
int bars = iBars(_Symbol, PERIOD_H4);
if(bars < 2) return;
double high = (double)iHigh(_Symbol, PERIOD_H4, 1);
double low = (double)iLow(_Symbol, PERIOD_H4, 1);
OB_H4.top = high; OB_H4.bottom = low; OB_H4.dir = 1; OB_H4.time =
iTime(_Symbol, PERIOD_H4, 1); OB_H4.valid = true; OB_H4.sweepDetected =
false;
FVG_H4.top = high; FVG_H4.bottom = low; FVG_H4.dir = 1; FVG_H4.time =
iTime(_Symbol, PERIOD_H4, 1); FVG_H4.valid = true; FVG_H4.sweepDetected
= false;
SD_H4.top = high; SD_H4.bottom = low; SD_H4.dir = 1; SD_H4.time =
iTime(_Symbol, PERIOD_H4, 1); SD_H4.valid = true; SD_H4.sweepDetected =
false;
void UpdateOB_FVG_SD_H1()
{
int bars = iBars(_Symbol, PERIOD_H1);
if(bars < 2) return;
double high = (double)iHigh(_Symbol, PERIOD_H1, 1);
double low = (double)iLow(_Symbol, PERIOD_H1, 1);
OB_H1.top = high; OB_H1.bottom = low; OB_H1.dir = 1; OB_H1.time =
iTime(_Symbol, PERIOD_H1, 1); OB_H1.valid = true; OB_H1.sweepDetected =
false;
FVG_H1.top = high; FVG_H1.bottom = low; FVG_H1.dir = 1; FVG_H1.time =
iTime(_Symbol, PERIOD_H1, 1); FVG_H1.valid = true; FVG_H1.sweepDetected
= false;
SD_H1.top = high; SD_H1.bottom = low; SD_H1.dir = 1; SD_H1.time =
iTime(_Symbol, PERIOD_H1, 1); SD_H1.valid = true; SD_H1.sweepDetected =
false;
void UpdateFVG_SD_M15()
int bars = iBars(_Symbol, PERIOD_M15);
if(bars < 2) return;
double high = (double)iHigh(_Symbol, PERIOD_M15, 1);
double low = (double)iLow(_Symbol, PERIOD_M15, 1);
FVG_M15.top = high; FVG_M15.bottom = low; FVG_M15.dir = 1;
FVG_M15.time = iTime(_Symbol, PERIOD_M15, 1); FVG_M15.valid = true;
FVG_M15.sweepDetected = false;
SD_M15.top = high; SD_M15.bottom = low; SD_M15.dir = 1; SD_M15.time =
iTime(_Symbol, PERIOD_M15, 1); SD_M15.valid = true;
SD_M15.sweepDetected = false;
}
//+------------------------------------------------------------------+
//| Draw dim rectangle zone |
//+------------------------------------------------------------------+
void DrawZone(string name, Zone &z, color c)
if(![Link]) return;
// use zone time as suffix to make object stable per zone
string objName = name + "_" + IntegerToString((int)[Link]);
// delete existing object if present
if(ObjectFind(0, objName) >= 0)
ObjectDelete(0, objName);
// create rectangle
ObjectCreate(0, objName, OBJ_RECTANGLE, 0, [Link], [Link], [Link],
[Link]);
uint dimColor = ColorToARGB(c, 80); // alpha 0..255
ObjectSetInteger(0, objName, OBJPROP_COLOR, (long)dimColor);
ObjectSetInteger(0, objName, OBJPROP_BACK, 1);
ObjectSetInteger(0, objName, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(0, objName, OBJPROP_WIDTH, 1);
ObjectSetInteger(0, objName, OBJPROP_FILL, 1);
// remember object for cleanup
AddCreatedObjectName(objName);
}
//+------------------------------------------------------------------+