2025-10-31 23:54:15 +00:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| LiquidityFibConfluenceEA_PRO.mq5 |
|
|
|
|
|
//| 3-4AM & 10-11AM EST | 5M Sweep/FVG → 1M Fib 0.62/0.79 Entry |
|
|
|
|
|
//| NAS100 ONLY | Visuals | Push | Screenshot | SL 100 pips |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#property copyright "Houssam El Fayda"
|
|
|
|
|
#property link "https://x.ai"
|
|
|
|
|
#property version "2.00"
|
|
|
|
|
#property strict
|
|
|
|
|
#property script_show_inputs
|
2025-10-31 23:53:21 +00:00
|
|
|
|
2025-10-31 23:54:15 +00:00
|
|
|
input double LotSize = 0.1;
|
|
|
|
|
input int Slippage = 3;
|
|
|
|
|
input int MagicNumber = 987654;
|
|
|
|
|
input int StopLossPips = 100;
|
|
|
|
|
input bool EnableDebug = true;
|
|
|
|
|
input bool EnablePush = true;
|
|
|
|
|
input bool EnableScreenshot = true;
|
|
|
|
|
input color ArrowColorBuy = clrLime;
|
|
|
|
|
input color ArrowColorSell = clrRed;
|
|
|
|
|
|
|
|
|
|
//--- Allowed symbols (NAS100 variants)
|
|
|
|
|
string AllowedSymbols[] = {"NAS100", "US100", "USTEC", "NAS100.cash"};
|
|
|
|
|
|
|
|
|
|
//--- Time Zones
|
|
|
|
|
datetime est_offset = -5 * 3600; // EST = UTC-5
|
|
|
|
|
|
|
|
|
|
//--- Global
|
|
|
|
|
datetime last_bar_time = 0;
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Expert initialization |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
int OnInit()
|
|
|
|
|
{
|
|
|
|
|
if(Period() != PERIOD_M1)
|
|
|
|
|
{
|
|
|
|
|
Alert("Attach this EA to a 1-MINUTE chart!");
|
|
|
|
|
return(INIT_FAILED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!IsNas100Symbol())
|
|
|
|
|
{
|
|
|
|
|
Alert("This EA only works on NAS100 (US100, USTEC, NAS100.cash)");
|
|
|
|
|
return(INIT_FAILED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Print("LiquidityFibConfluenceEA_PRO initialized on ", _Symbol, " M1");
|
|
|
|
|
return(INIT_SUCCEEDED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Expert tick function |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void OnTick()
|
|
|
|
|
{
|
|
|
|
|
if(!IsTradeTime()) return;
|
|
|
|
|
if(!IsNewBar(PERIOD_M5)) return;
|
|
|
|
|
|
|
|
|
|
if(DetectLiquiditySweep() || DetectFVGHits())
|
|
|
|
|
{
|
|
|
|
|
double fib_062, fib_079;
|
|
|
|
|
if(GetFibLevels(fib_062, fib_079))
|
|
|
|
|
{
|
|
|
|
|
double entry = 0;
|
|
|
|
|
bool use_062 = CheckConfluence(fib_062);
|
|
|
|
|
bool use_079 = CheckConfluence(fib_079);
|
|
|
|
|
|
|
|
|
|
if(use_062) entry = fib_062;
|
|
|
|
|
else if(use_079) entry = fib_079;
|
|
|
|
|
else return;
|
|
|
|
|
|
|
|
|
|
ENUM_ORDER_TYPE type = (Close[0] > entry) ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;
|
|
|
|
|
double sl = (type == ORDER_TYPE_BUY) ? entry - StopLossPips * _Point : entry + StopLossPips * _Point;
|
|
|
|
|
double tp = GetNextStructureLevel(type);
|
|
|
|
|
|
|
|
|
|
if(tp == 0) return;
|
|
|
|
|
|
|
|
|
|
OpenPosition(type, entry, sl, tp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Check if symbol is NAS100 |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool IsNas100Symbol()
|
|
|
|
|
{
|
|
|
|
|
for(int i = 0; i < ArraySize(AllowedSymbols); i++)
|
|
|
|
|
if(StringFind(_Symbol, AllowedSymbols[i], 0) != -1)
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Trade Time: 3-4 AM & 10-11 AM EST |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool IsTradeTime()
|
|
|
|
|
{
|
|
|
|
|
datetime now = TimeCurrent();
|
|
|
|
|
datetime est = now + est_offset;
|
|
|
|
|
int hour = TimeHour(est);
|
|
|
|
|
return (hour >= 3 && hour < 4) || (hour >= 10 && hour < 11);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Detect 5M Liquidity Sweep |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool DetectLiquiditySweep()
|
|
|
|
|
{
|
|
|
|
|
double high[], low[], close[];
|
|
|
|
|
if(CopyHigh(_Symbol, PERIOD_M5, 1, 3, high) <= 0 ||
|
|
|
|
|
CopyLow(_Symbol, PERIOD_M5, 1, 3, low) <= 0 ||
|
|
|
|
|
CopyClose(_Symbol, PERIOD_M5, 1, 3, close) <= 0)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if(high[0] > high[1] && close[0] < high[1]) return true; // High sweep
|
|
|
|
|
if(low[0] < low[1] && close[0] > low[1]) return true; // Low sweep
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Detect FVG Hit on 5M |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool DetectFVGHits()
|
|
|
|
|
{
|
|
|
|
|
double high[], low[];
|
|
|
|
|
if(CopyHigh(_Symbol, PERIOD_M5, 1, 3, high) <= 0 ||
|
|
|
|
|
CopyLow(_Symbol, PERIOD_M5, 1, 3, low) <= 0)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if(low[2] > high[0] || high[2] < low[0]) return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Get Fib 0.62 & 0.79 from last 5M swing |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool GetFibLevels(double &fib_062, double &fib_079)
|
|
|
|
|
{
|
|
|
|
|
double high[], low[];
|
|
|
|
|
if(CopyHigh(_Symbol, PERIOD_M5, 1, 25, high) <= 0 ||
|
|
|
|
|
CopyLow(_Symbol, PERIOD_M5, 1, 25, low) <= 0)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
int swing_i = -1;
|
|
|
|
|
double swing_p = 0;
|
|
|
|
|
bool is_high = true;
|
|
|
|
|
|
|
|
|
|
for(int i = 3; i < 20; i++)
|
|
|
|
|
{
|
|
|
|
|
if(high[i] > high[i-1] && high[i] > high[i+1] && high[i] > high[i-2] && high[i] > high[i+2])
|
|
|
|
|
{
|
|
|
|
|
swing_i = i; swing_p = high[i]; is_high = true; break;
|
|
|
|
|
}
|
|
|
|
|
if(low[i] < low[i-1] && low[i] < low[i+1] && low[i] < low[i-2] && low[i] < low[i+2])
|
|
|
|
|
{
|
|
|
|
|
swing_i = i; swing_p = low[i]; is_high = false; break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(swing_i == -1) return false;
|
|
|
|
|
|
|
|
|
|
double base = is_high ? ArrayMinimum(low, swing_i, 12) : ArrayMaximum(high, swing_i, 12);
|
|
|
|
|
if(base <= 0) return false;
|
|
|
|
|
|
|
|
|
|
double range = MathAbs(swing_p - base);
|
|
|
|
|
fib_062 = is_high ? swing_p - 0.618 * range : swing_p + 0.618 * range;
|
|
|
|
|
fib_079 = is_high ? swing_p - 0.786 * range : swing_p + 0.786 * range;
|
|
|
|
|
|
|
|
|
|
DrawFibLines(swing_p, base, fib_062, fib_079);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Draw Fib Lines on Chart |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void DrawFibLines(double high, double low, double f062, double f079)
|
|
|
|
|
{
|
|
|
|
|
string name = "Fib_" + IntegerToString(TimeCurrent());
|
|
|
|
|
ObjectCreate(0, name, OBJ_TREND, 0, Time[10], high, Time[0], low);
|
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_COLOR, clrGray);
|
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DOT);
|
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
|
|
|
|
|
|
|
|
|
|
string l062 = name + "_062";
|
|
|
|
|
string l079 = name + "_079";
|
|
|
|
|
ObjectCreate(0, l062, OBJ_HLINE, 0, 0, f062);
|
|
|
|
|
ObjectCreate(0, l079, OBJ_HLINE, 0, 0, f079);
|
|
|
|
|
ObjectSetInteger(0, l062, OBJPROP_COLOR, clrYellow);
|
|
|
|
|
ObjectSetInteger(0, l079, OBJPROP_COLOR, clrOrange);
|
|
|
|
|
ObjectSetString(0, l062, OBJPROP_TEXT, "Fib 0.62");
|
|
|
|
|
ObjectSetString(0, l079, OBJPROP_TEXT, "Fib 0.79");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Check Confluence with PD Arrays |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CheckConfluence(double level)
|
|
|
|
|
{
|
|
|
|
|
double tol = 15 * _Point;
|
|
|
|
|
double fvg = GetLastFVG();
|
|
|
|
|
double ob = GetLastOrderBlock();
|
|
|
|
|
double rb = GetLastRejectionBlock();
|
|
|
|
|
|
|
|
|
|
if(fvg > 0 && MathAbs(level - fvg) < tol) { DrawLabel("FVG", level, clrAqua); return true; }
|
|
|
|
|
if(ob > 0 && MathAbs(level - ob) < tol) { DrawLabel("OB", level, clrGold); return true; }
|
|
|
|
|
if(rb > 0 && MathAbs(level - rb) < tol) { DrawLabel("RB", level, clrPink); return true; }
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Get Last FVG |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
double GetLastFVG()
|
|
|
|
|
{
|
|
|
|
|
double high[], low[];
|
|
|
|
|
if(CopyHigh(_Symbol, PERIOD_M5, 1, 5, high) <= 0 || CopyLow(_Symbol, PERIOD_M5, 1, 5, low) <= 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
for(int i = 2; i < 4; i++)
|
|
|
|
|
{
|
|
|
|
|
if(low[i] > high[i-2]) return (low[i] + high[i-2]) / 2.0;
|
|
|
|
|
if(high[i] < low[i-2]) return (high[i] + low[i-2]) / 2.0;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Get Last Order Block |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
double GetLastOrderBlock()
|
|
|
|
|
{
|
|
|
|
|
double o[], c[], h[], l[];
|
|
|
|
|
if(CopyOpen(_Symbol, PERIOD_M5, 1, 10, o) <= 0 || CopyClose(_Symbol, PERIOD_M5, 1, 10, c) <= 0 ||
|
|
|
|
|
CopyHigh(_Symbol, PERIOD_M5, 1, 10, h) <= 0 || CopyLow(_Symbol, PERIOD_M5, 1, 10, l) <= 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
for(int i = 1; i < 8; i++)
|
|
|
|
|
{
|
|
|
|
|
double body = MathAbs(c[i] - o[i]);
|
|
|
|
|
double range = h[i] - l[i];
|
|
|
|
|
if(body > 0.7 * range && range > 0)
|
|
|
|
|
return (o[i] + c[i]) / 2.0;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Get Last Rejection Block |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
double GetLastRejectionBlock()
|
|
|
|
|
{
|
|
|
|
|
double o[], c[], h[], l[];
|
|
|
|
|
if(CopyOpen(_Symbol, PERIOD_M5, 1, 10, o) <= 0 || CopyClose(_Symbol, PERIOD_M5, 1, 10, c) <= 0 ||
|
|
|
|
|
CopyHigh(_Symbol, PERIOD_M5, 1, 10, h) <= 0 || CopyLow(_Symbol, PERIOD_M5, 1, 10, l) <= 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
for(int i = 1; i < 8; i++)
|
|
|
|
|
{
|
|
|
|
|
double body = MathAbs(c[i] - o[i]);
|
|
|
|
|
double uw = h[i] - MathMax(o[i], c[i]);
|
|
|
|
|
double lw = MathMin(o[i], c[i]) - l[i];
|
|
|
|
|
if(uw > 2 * body || lw > 2 * body)
|
|
|
|
|
return (o[i] + c[i]) / 2.0;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Get Next 15M Structure (TP) |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
double GetNextStructureLevel(ENUM_ORDER_TYPE type)
|
|
|
|
|
{
|
|
|
|
|
double high[], low[];
|
|
|
|
|
if(CopyHigh(_Symbol, PERIOD_M15, 0, 25, high) <= 0 ||
|
|
|
|
|
CopyLow(_Symbol, PERIOD_M15, 0, 25, low) <= 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
double last_high = high[ArrayMaximum(high, 15)];
|
|
|
|
|
double last_low = low[ArrayMinimum(low, 15)];
|
|
|
|
|
|
|
|
|
|
return (type == ORDER_TYPE_BUY) ? last_low : last_high;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Open Position with Visuals, Push, Screenshot |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void OpenPosition(ENUM_ORDER_TYPE type, double entry, double sl, double tp)
|
|
|
|
|
{
|
|
|
|
|
if(PositionExists()) return;
|
|
|
|
|
|
|
|
|
|
MqlTradeRequest req; MqlTradeResult res;
|
|
|
|
|
ZeroMemory(req); ZeroMemory(res);
|
|
|
|
|
|
|
|
|
|
req.action = TRADE_ACTION_DEAL;
|
|
|
|
|
req.symbol = _Symbol;
|
|
|
|
|
req.volume = LotSize;
|
|
|
|
|
req.type = type;
|
|
|
|
|
req.price = (type == ORDER_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK)
|
|
|
|
|
: SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
|
|
|
req.sl = NormalizeDouble(sl, _Digits);
|
|
|
|
|
req.tp = NormalizeDouble(tp, _Digits);
|
|
|
|
|
req.deviation= Slippage;
|
|
|
|
|
req.magic = MagicNumber;
|
|
|
|
|
req.comment = "FibConfluencePRO";
|
|
|
|
|
|
|
|
|
|
if(!OrderSend(req, res))
|
|
|
|
|
{
|
|
|
|
|
Print("OrderSend failed: ", GetLastError());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(res.retcode == TRADE_RETCODE_DONE)
|
|
|
|
|
{
|
|
|
|
|
// === Visual Arrow ===
|
|
|
|
|
string arrow_name = "Entry_" + IntegerToString(res.order);
|
|
|
|
|
ObjectCreate(0, arrow_name, OBJ_ARROW, 0, Time[0], req.price);
|
|
|
|
|
ObjectSetInteger(0, arrow_name, OBJPROP_ARROWCODE, type == ORDER_TYPE_BUY ? 233 : 234);
|
|
|
|
|
ObjectSetInteger(0, arrow_name, OBJPROP_COLOR, type == ORDER_TYPE_BUY ? ArrowColorBuy : ArrowColorSell);
|
|
|
|
|
ObjectSetInteger(0, arrow_name, OBJPROP_WIDTH, 3);
|
|
|
|
|
|
|
|
|
|
// === Label ===
|
|
|
|
|
string label = "Trade_" + IntegerToString(res.order);
|
|
|
|
|
ObjectCreate(0, label, OBJ_TEXT, 0, Time[0], req.price);
|
|
|
|
|
ObjectSetString(0, label, OBJPROP_TEXT, (type == ORDER_TYPE_BUY ? "BUY" : "SELL") +
|
|
|
|
|
" @ " + DoubleToString(req.price, _Digits));
|
|
|
|
|
ObjectSetInteger(0, label, OBJPROP_COLOR, clrWhite);
|
|
|
|
|
ObjectSetInteger(0, label, OBJPROP_FONTSIZE, 10);
|
|
|
|
|
|
|
|
|
|
// === Push Notification ===
|
|
|
|
|
if(EnablePush)
|
|
|
|
|
SendNotification("NAS100 " + (type == ORDER_TYPE_BUY ? "BUY" : "SELL") +
|
|
|
|
|
" @ " + DoubleToString(req.price, _Digits) +
|
|
|
|
|
" | SL: " + DoubleToString(sl, _Digits) +
|
|
|
|
|
" | TP: " + DoubleToString(tp, _Digits));
|
|
|
|
|
|
|
|
|
|
// === Screenshot ===
|
|
|
|
|
if(EnableScreenshot)
|
|
|
|
|
{
|
|
|
|
|
string filename = "NAS100_" + (type == ORDER_TYPE_BUY ? "BUY" : "SELL") +
|
|
|
|
|
"_" + TimeToString(TimeCurrent(), TIME_DATE|TIME_MINUTES) + ".png";
|
|
|
|
|
StringReplace(filename, ":", "");
|
|
|
|
|
ChartScreenShot(0, filename, 1920, 1080, ALIGN_CENTER);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Print("TRADE OPENED: ", EnumToString(type), " @ ", req.price, " | TP: ", tp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Draw Label for PD Array |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void DrawLabel(string text, double price, color col)
|
|
|
|
|
{
|
|
|
|
|
string name = text + "_" + IntegerToString(Time[0]);
|
|
|
|
|
ObjectCreate(0, name, OBJ_TEXT, 0, Time[5], price);
|
|
|
|
|
ObjectSetString(0, name, OBJPROP_TEXT, text);
|
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_COLOR, col);
|
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 9);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Check if position exists |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool PositionExists()
|
|
|
|
|
{
|
|
|
|
|
for(int i = 0; i < PositionsTotal(); i++)
|
|
|
|
|
{
|
|
|
|
|
if(PositionGetSymbol(i) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| New Bar Detection |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool IsNewBar(ENUM_TIMEFRAMES tf)
|
|
|
|
|
{
|
|
|
|
|
datetime curr = iTime(_Symbol, tf, 0);
|
|
|
|
|
if(curr != last_bar_time)
|
|
|
|
|
{
|
|
|
|
|
last_bar_time = curr;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|