Code: Select all
//+------------------------------------------------------------------+
//| LiquiditySweeps_PRO_EA.mq5 |
//| Institutional Auto-Exec EA |
//+------------------------------------------------------------------+
#property copyright "Forex, Stocks & Me"
#property link "https://forex-scalping.com"
#property version "2.00"
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\HistoryOrderInfo.mqh>
// === INPUTS ===
sinput string grp1 = "--- Structural Mapping ---";
input int InpSwingBars = 5; // Fractal Swing (Bars left & right)
input int InpHistory = 500; // HTF Bars to scan
input double InpTolerancePips = 15.0; // EQH/EQL Tolerance (Pips)
sinput string grp2 = "--- Exponential Risk Engine ---";
input bool InpUseExponential = true; // Use Accrued Profit Scaling?
input double InpStartingBalance= 1000.0; // Series Starting Balance
input double InpBaseRisk = 20.0; // Base Risk (Currency)
input double InpProfitScalePct = 50.0; // Profit Reinvestment (%)
input bool InpLockPrincipal = true; // Lock Principal?
input double InpStaticLots = 1.0; // Static Fallback Volume
sinput string grp3 = "--- Execution Rules ---";
input double InpStopLossPips = 10.0; // Stop Loss (Pips)
input double InpTakeProfitPips = 50.0; // Take Profit (Pips)
input double InpMaxSpreadPips = 1.5; // Max Spread (Pips)
input ulong InpMaxSlippagePts = 30; // Max Slippage (Points)
sinput string grp4 = "--- Pro Circuit Breakers & Sessions ---";
input double InpMaxDailyLossPct= 2.0; // Max Daily Loss (%) - Halts EA
input string InpSessionStart = "14:00"; // Trading Start (Broker Time)
input string InpSessionEnd = "18:00"; // Trading End (Broker Time)
sinput string grp5 = "--- Trade Management (BE, Trail, Partial) ---";
input double InpManagementRR = 1.0; // Trigger Management at R:R
input bool InpUsePartial = true; // Take Partial Profit?
input double InpPartialPct = 50.0; // Partial Profit (%)
input bool InpUseBreakeven = true; // Move to Breakeven?
input double InpCommBufferPips = 0.6; // Commission Buffer (Pips)
input bool InpUseTrailing = true; // Use Trailing Stop after BE?
input double InpTrailDistPips = 15.0; // Trailing Distance (Pips)
input double InpTrailStepPips = 2.0; // Trailing Step (Pips)
input ulong InpMagicNumber = 888899; // EA Magic Number
// === GLOBALS & CLASSES ===
CTrade trade;
CPositionInfo pos;
ulong partiallyClosedTickets[]; // State tracker
double startOfDayBalance = 0; // For Daily Drawdown Tracker
struct SwingPoint {
datetime time;
double price;
bool used;
};
//+------------------------------------------------------------------+
//| Initialization |
//+------------------------------------------------------------------+
int OnInit() {
trade.SetExpertMagicNumber(InpMagicNumber);
trade.SetDeviationInPoints(InpMaxSlippagePts); // Pro slippage control
startOfDayBalance = AccountInfoDouble(ACCOUNT_BALANCE);
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason) {
ObjectsDeleteAll(0, "EQH_EQL_");
Comment(""); // Clear Telemetry HUD
}
//+------------------------------------------------------------------+
//| Helper: Dynamic Pip Sizing |
//+------------------------------------------------------------------+
double GetPipSize() {
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
return (digits == 5 || digits == 3) ? _Point * 10.0 : _Point;
}
//+------------------------------------------------------------------+
//| Core Tick Loop |
//+------------------------------------------------------------------+
void OnTick() {
// 1. Check if a new day started to reset the Daily Drawdown balance
static int current_day = -1;
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
if(dt.day_of_year != current_day) {
current_day = dt.day_of_year;
startOfDayBalance = AccountInfoDouble(ACCOUNT_BALANCE);
ArrayResize(partiallyClosedTickets, 0); // Clear partial state tracker
}
// 2. Active Trade Management
ManageActivePositions();
// 3. Update HUD
UpdateTelemetryHUD();
// 4. M15 Bar Close Mapping & Entry
static datetime last_bar = 0;
datetime curr_bar = (datetime)SeriesInfoInteger(_Symbol, PERIOD_M15, SERIES_LASTBAR_DATE);
if(curr_bar != last_bar) {
last_bar = curr_bar;
ProcessM15BarClose();
}
}
//+------------------------------------------------------------------+
//| Pro Circuit Breaker: Max Daily Loss Tracker |
//+------------------------------------------------------------------+
bool IsDailyDrawdownHit() {
double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
double dailyPnL = currentEquity - startOfDayBalance;
double maxLossAllowed = startOfDayBalance * (InpMaxDailyLossPct / 100.0);
// If daily PnL is negative and exceeds our max loss threshold
if(dailyPnL < 0 && MathAbs(dailyPnL) >= maxLossAllowed) return true;
return false;
}
//+------------------------------------------------------------------+
//| Pro Circuit Breaker: Session Window |
//+------------------------------------------------------------------+
bool IsWithinTradingSession() {
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
int currentMins = dt.hour * 60 + dt.min;
string startArr[], endArr[];
StringSplit(InpSessionStart, ':', startArr);
StringSplit(InpSessionEnd, ':', endArr);
int startMins = (int)StringToInteger(startArr[0]) * 60 + (int)StringToInteger(startArr[1]);
int endMins = (int)StringToInteger(endArr[0]) * 60 + (int)StringToInteger(endArr[1]);
return (currentMins >= startMins && currentMins <= endMins);
}
//+------------------------------------------------------------------+
//| Telemetry HUD |
//+------------------------------------------------------------------+
void UpdateTelemetryHUD() {
double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
double dailyPnL = currentEquity - startOfDayBalance;
double spread = (SymbolInfoDouble(_Symbol, SYMBOL_ASK) - SymbolInfoDouble(_Symbol, SYMBOL_BID)) / GetPipSize();
string ddStatus = IsDailyDrawdownHit() ? "[HALTED: Daily Limit Hit]" : "[OK]";
string sessionStatus = IsWithinTradingSession() ? "ACTIVE" : "WAITING";
string riskMode = InpUseExponential ? (InpLockPrincipal ? "EXPONENTIAL (Locked)" : "EXPONENTIAL (Compounding)") : "STATIC";
string hud = "\n=== PRO TELEMETRY ===\n" +
"Risk Mode: " + riskMode + "\n" +
"Session: " + sessionStatus + "\n" +
"Spread: " + DoubleToString(spread, 1) + " pips\n" +
"Daily PnL: " + DoubleToString(dailyPnL, 2) + " " + AccountInfoString(ACCOUNT_CURRENCY) + " " + ddStatus + "\n" +
"Active Target R:R: 1:" + DoubleToString(InpTakeProfitPips/InpStopLossPips, 1);
Comment(hud);
}
//+------------------------------------------------------------------+
//| Trade Management Module (BE, Trail, Partial) |
//+------------------------------------------------------------------+
void ManageActivePositions() {
double pip = GetPipSize();
double trigger_dist = InpStopLossPips * InpManagementRR * pip;
for(int i = PositionsTotal() - 1; i >= 0; i--) {
if(pos.SelectByIndex(i) && pos.Symbol() == _Symbol && pos.Magic() == InpMagicNumber) {
double open_price = pos.PriceOpen();
double current_sl = pos.StopLoss();
ulong ticket = pos.Ticket();
double profit_dist = (pos.PositionType() == POSITION_TYPE_BUY)
? SymbolInfoDouble(_Symbol, SYMBOL_BID) - open_price
: open_price - SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(profit_dist >= trigger_dist) {
// 1. Partial Close
bool isPartiallyClosed = false;
for(int j=0; j<ArraySize(partiallyClosedTickets); j++) { if(partiallyClosedTickets[j]==ticket) isPartiallyClosed = true; }
if(InpUsePartial && !isPartiallyClosed) {
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
double vol_to_close = MathFloor((pos.Volume() * (InpPartialPct / 100.0)) / step) * step;
if(vol_to_close >= SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN) && pos.Volume() > vol_to_close) {
if(trade.PositionClosePartial(ticket, vol_to_close)) {
int s = ArraySize(partiallyClosedTickets);
ArrayResize(partiallyClosedTickets, s + 1);
partiallyClosedTickets[s] = ticket;
Print("PRO EXECUTION: Partial 50% closed at Target R:R.");
}
}
}
// 2. Breakeven & Trailing Stop
if(InpUseBreakeven) {
double comm_buffer = InpCommBufferPips * pip;
double be_sl = (pos.PositionType() == POSITION_TYPE_BUY) ? open_price + comm_buffer : open_price - comm_buffer;
double target_sl = be_sl;
if(InpUseTrailing) {
double trail_sl = (pos.PositionType() == POSITION_TYPE_BUY)
? SymbolInfoDouble(_Symbol, SYMBOL_BID) - (InpTrailDistPips * pip)
: SymbolInfoDouble(_Symbol, SYMBOL_ASK) + (InpTrailDistPips * pip);
target_sl = (pos.PositionType() == POSITION_TYPE_BUY) ? MathMax(target_sl, trail_sl) : MathMin(target_sl, trail_sl);
}
double step_buffer = InpTrailStepPips * pip;
bool requires_update = false;
if(pos.PositionType() == POSITION_TYPE_BUY && (current_sl == 0 || target_sl > current_sl + step_buffer)) requires_update = true;
if(pos.PositionType() == POSITION_TYPE_SELL && (current_sl == 0 || target_sl < current_sl - step_buffer)) requires_update = true;
if(requires_update) trade.PositionModify(ticket, NormalizeDouble(target_sl, _Digits), pos.TakeProfit());
}
}
}
}
}
//+------------------------------------------------------------------+
//| Structural Mapping & Entry Module (M15 Bar Close) |
//+------------------------------------------------------------------+
void ProcessM15BarClose() {
ObjectsDeleteAll(0, "EQH_EQL_");
double activeEQH[], activeEQL[];
MapEqualLiquidity(PERIOD_D1, "D1", clrCrimson, STYLE_SOLID, 2, activeEQH, activeEQL);
MapEqualLiquidity(PERIOD_H4, "H4", clrDarkOrange, STYLE_DASHDOT, 1, activeEQH, activeEQL);
ChartRedraw(0);
// --- PRO CIRCUIT BREAKERS ---
if(IsDailyDrawdownHit()) return;
if(!IsWithinTradingSession()) return;
double spread = (SymbolInfoDouble(_Symbol, SYMBOL_ASK) - SymbolInfoDouble(_Symbol, SYMBOL_BID)) / GetPipSize();
if(spread > InpMaxSpreadPips) return;
if(PositionsTotal() > 0) return;
double o = iOpen(_Symbol, PERIOD_M15, 1);
double h = iHigh(_Symbol, PERIOD_M15, 1);
double l = iLow(_Symbol, PERIOD_M15, 1);
double c = iClose(_Symbol, PERIOD_M15, 1);
double volume = GetDynamicVolume();
if(volume < SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN)) return;
double pip = GetPipSize();
for(int i = 0; i < ArraySize(activeEQH); i++) {
if(o < activeEQH[i] && h > activeEQH[i] && c < activeEQH[i]) {
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
trade.Sell(volume, _Symbol, bid, bid + (InpStopLossPips * pip), bid - (InpTakeProfitPips * pip), "PRO_Sweep");
return;
}
}
for(int i = 0; i < ArraySize(activeEQL); i++) {
if(o > activeEQL[i] && l < activeEQL[i] && c > activeEQL[i]) {
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
trade.Buy(volume, _Symbol, ask, ask - (InpStopLossPips * pip), ask + (InpTakeProfitPips * pip), "PRO_Sweep");
return;
}
}
}
//+------------------------------------------------------------------+
//| Exponential Risk Engine Calculation |
//+------------------------------------------------------------------+
double GetDynamicVolume() {
double risk_currency = InpBaseRisk;
if(InpUseExponential) {
double accrued_profit = AccountInfoDouble(ACCOUNT_EQUITY) - InpStartingBalance;
if(accrued_profit > 0) {
risk_currency = InpLockPrincipal
? (accrued_profit * (InpProfitScalePct / 100.0))
: InpBaseRisk + (accrued_profit * (InpProfitScalePct / 100.0));
}
} else return InpStaticLots;
double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tick_size = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double pip_val_per_lot = tick_value * (GetPipSize() / tick_size);
double calculated_vol = risk_currency / (InpStopLossPips * pip_val_per_lot);
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
return MathFloor(calculated_vol / step) * step;
}
//+------------------------------------------------------------------+
//| HTF Liquidity Mapper |
//+------------------------------------------------------------------+
void MapEqualLiquidity(ENUM_TIMEFRAMES tf, string prefix, color clr, int style, int width, double &outEQH[], double &outEQL[]) {
double h[], l[];
datetime t[];
ArraySetAsSeries(h, true); ArraySetAsSeries(l, true); ArraySetAsSeries(t, true);
if(CopyHigh(_Symbol, tf, 0, InpHistory, h) < InpHistory || CopyLow(_Symbol, tf, 0, InpHistory, l) < InpHistory || CopyTime(_Symbol, tf, 0, InpHistory, t) < InpHistory) return;
SwingPoint highs[], lows[];
for(int i = InpSwingBars; i < InpHistory - InpSwingBars; i++) {
bool isRes = true, isSup = true;
for(int j = 1; j <= InpSwingBars; j++) {
if(h[i] <= h[i-j] || h[i] <= h[i+j]) isRes = false;
if(l[i] >= l[i-j] || l[i] >= l[i+j]) isSup = false;
}
if(isRes) { int s = ArraySize(highs); ArrayResize(highs, s+1); highs[s].time = t[i]; highs[s].price = h[i]; highs[s].used = false; }
if(isSup) { int s = ArraySize(lows); ArrayResize(lows, s+1); lows[s].time = t[i]; lows[s].price = l[i]; lows[s].used = false; }
}
double tolerance = InpTolerancePips * GetPipSize();
for(int i = 0; i < ArraySize(highs); i++) {
if(highs[i].used) continue;
for(int j = i + 1; j < ArraySize(highs); j++) {
if(!highs[j].used && MathAbs(highs[i].price - highs[j].price) <= tolerance) {
DrawLine("EQH_" + prefix + "_" + TimeToString(highs[i].time), highs[j].time, highs[j].price, highs[i].time, highs[i].price, clr, style, width);
int s = ArraySize(outEQH); ArrayResize(outEQH, s+1); outEQH[s] = highs[i].price;
highs[i].used = true; highs[j].used = true; break;
}
}
}
for(int i = 0; i < ArraySize(lows); i++) {
if(lows[i].used) continue;
for(int j = i + 1; j < ArraySize(lows); j++) {
if(!lows[j].used && MathAbs(lows[i].price - lows[j].price) <= tolerance) {
DrawLine("EQL_" + prefix + "_" + TimeToString(lows[i].time), lows[j].time, lows[j].price, lows[i].time, lows[i].price, clr, style, width);
int s = ArraySize(outEQL); ArrayResize(outEQL, s+1); outEQL[s] = lows[i].price;
lows[i].used = true; lows[j].used = true; break;
}
}
}
}
void DrawLine(string name, datetime t1, double p1, datetime t2, double p2, color clr, int style, int width) {
if(ObjectCreate(0, name, OBJ_TREND, 0, t1, p1, t2, p2)) {
ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, true); ObjectSetInteger(0, name, OBJPROP_COLOR, clr); ObjectSetInteger(0, name, OBJPROP_STYLE, style);
ObjectSetInteger(0, name, OBJPROP_WIDTH, width); ObjectSetInteger(0, name, OBJPROP_BACK, true); ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
}
}
//+------------------------------------------------------------------+