MQL5's history architecture requires evaluating "Deals" rather than "Orders." We strictly filter for DEAL_ENTRY_OUT to ensure we are only counting closed trades, preventing partial fills or deposits/withdrawals from distorting your consecutive loss streak.
Code: Select all
//+------------------------------------------------------------------+
//| RevengeController_Pro.mq5 |
//+------------------------------------------------------------------+
#property strict
// ==========================================
// ⚙️ INPUTS: RISK & TILT MECHANICS
// ==========================================
input group "--- Risk Limits ---";
input double SoftStopPct = 2.0; // Daily Soft Stop (%)
input int MaxConsecLosses = 3; // Max Consecutive Losses Limit
input group "--- Tilt & Cooldown ---";
input int BaseCooldownMin = 5; // Base Cooldown (Min)
input int EscalationMult = 3; // Cooldown Escalation Multiplier
input group "--- Trading Session ---";
input int StartHour = 9; // Session Start Hour (Broker Time)
input int StartMinute = 30; // Session Start Minute
input int EndHour = 16; // Session End Hour
input int EndMinute = 0; // Session End Minute
input ulong MagicNumber = 12345; // EA Magic Number (0 for Manual Trades)
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 1. Calculate Daily PnL
double dailyPnLPct = GetDailyPnLPct();
bool hitSoftStop = (dailyPnLPct <= -SoftStopPct);
// 2. Fetch Tilt State
int consecLosses = 0;
datetime lastLossTime = 0;
GetTiltMetrics(consecLosses, lastLossTime);
bool hitConsecLossLimit = (consecLosses >= MaxConsecLosses);
// 3. Calculate Escalating Cooldown
int currentCooldownSecs = 0;
int secondsSinceLoss = (int)(TimeCurrent() - lastLossTime);
bool inCooldown = false;
if (consecLosses > 0)
{
// Math: Base * (Multiplier ^ (Losses - 1))
double multPower = MathPow(EscalationMult, MathMax(0, consecLosses - 1));
currentCooldownSecs = (int)(BaseCooldownMin * multPower * 60);
if (secondsSinceLoss < currentCooldownSecs) inCooldown = true;
}
// 4. Session Fencing
bool inSession = IsInSession();
// 5. Master Failsafe
bool isLockedOut = (hitSoftStop || hitConsecLossLimit);
// 6. Update HUD
UpdateHUD(isLockedOut, inCooldown, inSession, dailyPnLPct, consecLosses, currentCooldownSecs - secondsSinceLoss);
// 7. Execution Block
if (isLockedOut || inCooldown || !inSession)
{
return; // Engine locked. Abort tick.
}
// ==========================================
// 📉 YOUR A+ SETUP LOGIC GOES HERE
// ==========================================
}
//+------------------------------------------------------------------+
//| Engine Logic Functions |
//+------------------------------------------------------------------+
double GetDailyPnLPct()
{
double todayClosedProfit = 0.0;
datetime startOfDay = iTime(_Symbol, PERIOD_D1, 0);
HistorySelect(startOfDay, TimeCurrent());
int dealsTotal = HistoryDealsTotal();
for(int i = 0; i < dealsTotal; i++)
{
ulong ticket = HistoryDealGetTicket(i);
if(ticket > 0)
{
// Only count closing deals to calculate realized profit
long entryType = HistoryDealGetInteger(ticket, DEAL_ENTRY);
if(entryType == DEAL_ENTRY_OUT || entryType == DEAL_ENTRY_INOUT)
{
todayClosedProfit += HistoryDealGetDouble(ticket, DEAL_PROFIT) +
HistoryDealGetDouble(ticket, DEAL_COMMISSION) +
HistoryDealGetDouble(ticket, DEAL_SWAP);
}
}
}
double startOfDayBalance = AccountInfoDouble(ACCOUNT_BALANCE) - todayClosedProfit;
return ((AccountInfoDouble(ACCOUNT_EQUITY) - startOfDayBalance) / startOfDayBalance) * 100.0;
}
void GetTiltMetrics(int &consecLossesCount, datetime &lastTime)
{
consecLossesCount = 0;
lastTime = 0;
HistorySelect(0, TimeCurrent());
int dealsTotal = HistoryDealsTotal();
// Loop backwards from the most recent deal
for(int i = dealsTotal - 1; i >= 0; i--)
{
ulong ticket = HistoryDealGetTicket(i);
if(ticket > 0)
{
if(HistoryDealGetString(ticket, DEAL_SYMBOL) == _Symbol &&
(MagicNumber == 0 || HistoryDealGetInteger(ticket, DEAL_MAGIC) == MagicNumber))
{
long entryType = HistoryDealGetInteger(ticket, DEAL_ENTRY);
// We only care about exits
if(entryType == DEAL_ENTRY_OUT || entryType == DEAL_ENTRY_INOUT)
{
double netProfit = HistoryDealGetDouble(ticket, DEAL_PROFIT) +
HistoryDealGetDouble(ticket, DEAL_COMMISSION) +
HistoryDealGetDouble(ticket, DEAL_SWAP);
if(netProfit < 0)
{
consecLossesCount++;
if(lastTime == 0) lastTime = (datetime)HistoryDealGetInteger(ticket, DEAL_TIME);
}
else if(netProfit > 0)
{
break; // Streak broken by a win
}
}
}
}
}
}
bool IsInSession()
{
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
int currentMins = dt.hour * 60 + dt.min;
int startMins = StartHour * 60 + StartMinute;
int endMins = EndHour * 60 + EndMinute;
return (currentMins >= startMins && currentMins <= endMins);
}
void UpdateHUD(bool lockedOut, bool cooldown, bool inSession, double pnl, int consecLosses, int secsRemaining)
{
string status = "ARMED & CLEAR";
if (lockedOut) status = "FATAL LOCKOUT";
else if (cooldown) status = StringFormat("COOLDOWN (%d min left)", (secsRemaining / 60) + 1);
else if (!inSession) status = "OUT OF SESSION";
string hud = StringFormat(
"====== PRO RISK ENGINE ======\n" +
"Status: %s\n" +
"Daily PnL: %.2f%% / Limit: -%.2f%%\n" +
"Consec Losses: %d / Limit: %d",
status, pnl, SoftStopPct, consecLosses, MaxConsecLosses
);
Comment(hud);
}