Advertisement IC Markets

Revenge Trading Is the #1 Account Killer

Master exponential money management, position sizing calculators, strict daily stop-loss limits, and overcoming FOMO on micro-timeframes.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Revenge Trading Is the #1 Account Killer

Post by PTScalper »

Pro Features Explained

Escalating Penalties: A single process-break or standard loss triggers the base 5-minute timeout. If you take a second loss back-to-back, the script applies the escalationMult. By default (3x), your second timeout is 15 minutes. A third loss equals a 45-minute timeout. This mathematically matches the psychological escalation of tilt.

Consecutive Loss Hard-Stop: Prop firm daily limits are usually equity-based. This adds a behavioral stop. If your strategy is statistically sound, losing 3 or 4 trades in a row usually indicates market conditions have shifted, or you are forcing setups. Hitting this limit locks the system for the rest of the day, regardless of equity.

Telemetry HUD: Instead of guessing where your limits are, the bottom right of your chart now features a dynamic Heads-Up Display. It shows live PnL versus your soft stop limit, tracks your consecutive losses, and counts down your timeout minute-by-minute.

Session Fencing: The script defaults to regular market hours (0930-1600). A common revenge trading trap is migrating into low-liquidity aftermarket sessions to "make it back." The controller flatlines outside designated hours (chart background turns navy blue).
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Recommended broker for automated trading & scalping IC Markets
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Revenge Trading Is the #1 Account Killer

Post by PTScalper »

o elevate this from a behavioral safeguard to an institutional-grade risk engine, the system must handle the complexities of prop firm evaluations—specifically High-Water Mark (HWM) Trailing Drawdowns and Dynamic Position Sizing.

Furthermore, lagging technical indicators (like the SMA crossover used previously) have been stripped out. True algorithmic execution relies on market structure. The example entry logic now demonstrates a raw price action setup—specifically a Liquidity Sweep—calculating risk dynamically based on structural stop-loss placement.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Revenge Trading Is the #1 Account Killer

Post by PTScalper »

The Apex Risk Engine & Microstructure Controller (Pine Script v5)

Code: Select all

//@version=5
strategy("Apex Risk Engine & Microstructure Controller", overlay=true, initial_capital=100000, calc_on_every_tick=true, margin_long=100, margin_short=100)

// ==========================================
// ⚙️ INPUTS: PROP FIRM RISK LIMITS
// ==========================================
grp_firm = "Prop Firm Constraints"
dailyHardStopPct = input.float(4.0, "Daily Hard Stop (%)", step=0.5, group=grp_firm, tooltip="Absolute daily ruin limit.")
trailStopPct     = input.float(5.0, "Trailing Max DD (%)", step=0.5, group=grp_firm, tooltip="High-Water Mark trailing drawdown.")
maxRiskPerTrade  = input.float(0.5, "Max Risk Per Trade (%)", step=0.1, group=grp_firm, tooltip="Locks position sizing to this max equity percentage.")

grp_tilt = "Tilt & Time Mechanics"
baseCooldown     = input.int(5, "Base Cooldown (Min)", minval=1, group=grp_tilt)
escalationMult   = input.int(3, "Loss Multiplier", minval=1, group=grp_tilt, tooltip="Consecutive loss timeout multiplier.")
maxConsecLosses  = input.int(3, "Max Consecutive Losses", group=grp_tilt)
sessionStr       = input.session("0930-1600", "Execution Window", group=grp_tilt)
maxBarsInTrade   = input.int(10, "Time-In-Trade Abort (Bars)", group=grp_tilt, tooltip="Cuts a trade if it flatlines.")

// ==========================================
// 🧠 STATE TRACKING & HWM LOGIC
// ==========================================
var float startOfDayEquity = strategy.initial_capital
var float highWaterMark    = strategy.initial_capital
var int   consecLosses     = 0
var int   lastLossTime     = na
var int   closedTradeCount = 0

// Update High-Water Mark (HWM)
if strategy.equity > highWaterMark
    highWaterMark := strategy.equity

// Reset Daily Metrics
isNewDay = ta.change(time("D"))
if isNewDay
    startOfDayEquity := strategy.equity
    consecLosses     := 0
    lastLossTime     := na

// Trade Evaluation (Executes on trade close)
if strategy.closedtrades > closedTradeCount
    lastProfit = strategy.closedtrades.profit(strategy.closedtrades - 1)
    if lastProfit < 0
        consecLosses += 1
        lastLossTime := time
    else
        consecLosses := 0
    closedTradeCount := strategy.closedtrades

// Calculate Drawdowns
dailyPnL_Pct = ((strategy.equity - startOfDayEquity) / startOfDayEquity) * 100
trailDD_Pct  = ((strategy.equity - highWaterMark) / highWaterMark) * 100

// Limit Triggers
hitDailyStop       = dailyPnL_Pct <= -dailyHardStopPct
hitTrailStop       = trailDD_Pct <= -trailStopPct
hitConsecLossLimit = consecLosses >= maxConsecLosses
inSession          = not na(time(timeframe.period, sessionStr))

// Escalating Cooldown Math
msPerMinute       = 60 * 1000
currentCooldownMs = baseCooldown * math.pow(escalationMult, math.max(0, consecLosses - 1)) * msPerMinute
timeRemainingMs   = not na(lastLossTime) ? (lastLossTime + currentCooldownMs) - time : 0
inCooldown        = consecLosses > 0 and timeRemainingMs > 0

// Master Failsafe
isLockedOut = hitDailyStop or hitTrailStop or hitConsecLossLimit
masterClear = not isLockedOut and not inCooldown and inSession

// ==========================================
// 🖥️ TELEMETRY HUD
// ==========================================
bgColor = isLockedOut ? color.new(color.red, 85) : inCooldown ? color.new(color.orange, 85) : not inSession ? color.new(color.navy, 90) : na
bgcolor(bgColor, title="Controller State")

c_dark = color.rgb(15, 15, 15, 10), c_green = color.rgb(38, 166, 154), c_red = color.rgb(239, 83, 80), c_orange = color.rgb(255, 167, 38)
var table hud = table.new(position.bottom_right, 2, 6, bgcolor=c_dark, border_color=color.new(color.gray, 60), border_width=1)

if barstate.islast
    table.cell(hud, 0, 0, "APEX RISK ENGINE", text_color=color.gray, text_size=size.small, text_halign=text.align_left)
    table.cell(hud, 1, 0, "V2.0", text_color=color.gray, text_size=size.small, text_halign=text.align_right)

    statusText  = isLockedOut ? "FATAL LOCKOUT" : inCooldown ? "COOLDOWN (" + str.tostring(math.ceil(timeRemainingMs / msPerMinute)) + "m)" : inSession ? "ARMED & CLEAR" : "OUT OF SESSION"
    table.cell(hud, 0, 1, "Status", text_color=color.white, text_size=size.normal, text_halign=text.align_left)
    table.cell(hud, 1, 1, statusText, text_color=isLockedOut ? c_red : inCooldown ? c_orange : inSession ? c_green : color.gray, text_size=size.normal, text_halign=text.align_right)

    table.cell(hud, 0, 2, "Daily PnL / Limit", text_color=color.white, text_size=size.normal, text_halign=text.align_left)
    table.cell(hud, 1, 2, str.tostring(dailyPnL_Pct, "#.##") + "% / " + str.tostring(-dailyHardStopPct) + "%", text_color=dailyPnL_Pct < 0 ? c_red : c_green, text_halign=text.align_right)

    table.cell(hud, 0, 3, "Trailing DD", text_color=color.white, text_size=size.normal, text_halign=text.align_left)
    table.cell(hud, 1, 3, str.tostring(trailDD_Pct, "#.##") + "% / " + str.tostring(-trailStopPct) + "%", text_color=trailDD_Pct < -2.0 ? c_orange : c_green, text_halign=text.align_right)

    table.cell(hud, 0, 4, "Consec. Losses", text_color=color.white, text_size=size.normal, text_halign=text.align_left)
    table.cell(hud, 1, 4, str.tostring(consecLosses) + " / " + str.tostring(maxConsecLosses), text_color=consecLosses > 0 ? c_orange : c_green, text_halign=text.align_right)

// ==========================================
// 📉 RAW PRICE ACTION LOGIC (LIQUIDITY SWEEPS)
// ==========================================
// Identify market structure (15-bar swing lows/highs)
swingLow  = ta.lowest(low, 15)[1]
swingHigh = ta.highest(high, 15)[1]

// Bullish Sweep: Price breaks below recent swing low, but closes above it (rejection)
bullishSweep = low < swingLow and close > swingLow and close > open
// Bearish Sweep: Price breaks above recent swing high, but closes below it (rejection)
bearishSweep = high > swingHigh and close < swingHigh and close < open

// Dynamic Position Sizing (Risk / Stop Distance)
riskAmount = strategy.equity * (maxRiskPerTrade / 100)
longStop   = low - ta.atr(14) // Structural stop with ATR buffer
shortStop  = high + ta.atr(14)

longQty  = riskAmount / (close - longStop)
shortQty = riskAmount / (shortStop - close)

// Execution
if bullishSweep and masterClear and strategy.position_size == 0
    strategy.entry("Sweep Long", strategy.long, qty=longQty)
    strategy.exit("Exit Long", "Sweep Long", stop=longStop, limit=close + ((close - longStop) * 2)) // 2R Target

if bearishSweep and masterClear and strategy.position_size == 0
    strategy.entry("Sweep Short", strategy.short, qty=shortQty)
    strategy.exit("Exit Short", "Sweep Short", stop=shortStop, limit=close - ((shortStop - close) * 2)) // 2R Target

// Time-in-Trade Abort (Cut dead trades)
barsInTrade = bar_index - strategy.opentrades.entry_bar_index(0)
if strategy.position_size != 0 and barsInTrade >= maxBarsInTrade
    strategy.close_all(comment="Time Abort")
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Revenge Trading Is the #1 Account Killer

Post by PTScalper »

Institutional-Grade Additions

High-Water Mark (HWM) Trailing Drawdown: Prop firms do not track limits based strictly on your daily starting balance; they track them from your highest intra-day or overall equity peak. This script continually logs your highWaterMark and cuts the system if you slip below the trailing allowed percentage, preventing you from giving back massive unrealized gains.

Raw Price Action Triggers: The logic now mirrors real market microstructure. Instead of waiting for a lagging moving average crossover, it identifies swingLow and swingHigh liquidity pools. An entry only triggers when price sweeps that liquidity (spikes past the level) but sharply rejects and closes back inside the structure.

Dynamic Fractional Position Sizing: Rather than buying fixed lots, the engine calculates the exact dollar risk authorized per trade (e.g., 0.5% of current equity). It measures the distance between your entry and the structural stop-loss (with an ATR buffer), and calculates the precise qty required to risk exactly that percentage—no more, no less. This mathematically prevents sizing-up during tilt.

Time-In-Trade Abort: Scalping setups rely on immediate momentum. If a trade sits flat for maxBarsInTrade (default 10 bars) without hitting the 2R target or the stop loss, the controller automatically flattens the position, freeing up capital and mental bandwidth rather than letting you sit in chop.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Revenge Trading Is the #1 Account Killer

Post by PTScalper »

Translating this logic from Pine Script to MetaQuotes Language (MQL) requires shifting from Pine’s bar-by-bar evaluation to MQL’s event-driven OnTick() architecture.

Since you work with MQL4, MQL5, and C# cAlgo, you know that MetaTrader handles history very differently than TradingView. Instead of a simple strategy.closedtrades array, we have to query the Order History (MT4) or Deal History (MT5) to find the most recent exit and calculate the daily floating PnL dynamically.

Here is the "first level" logic translated into both MT4 and MT5 as a modular Expert Advisor (EA) template. You can wrap this around your existing price action execution logic.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Revenge Trading Is the #1 Account Killer

Post by PTScalper »

1. MT4 / MQL4: Revenge Controller Template

In MQL4, we loop backward through OrdersHistoryTotal() to find the last closed ticket for the current symbol and magic number to determine if a cooldown is required.

Code: Select all

//+------------------------------------------------------------------+
//|                                     RevengeController_Basic.mq4  |
//+------------------------------------------------------------------+
#property strict

// --- User Inputs ---
input double   SoftStopPct     = 2.0;       // Daily Soft Stop (%)
input int      CooldownMinutes = 5;         // Post-Loss Cooldown (Min)
input int      MagicNumber     = 12345;     // EA Magic Number

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    // 1. Calculate Daily PnL (Closed today + Floating)
    double dailyPnLPct = GetDailyPnLPct();
    bool hitSoftStop = (dailyPnLPct <= -SoftStopPct);

    // 2. Check Cooldown Status
    datetime lastLossTime = GetLastLossTime();
    int secondsSinceLoss = (int)(TimeCurrent() - lastLossTime);
    bool inCooldown = (lastLossTime > 0 && secondsSinceLoss < (CooldownMinutes * 60));

    // 3. Update HUD (Telemetry on chart)
    UpdateHUD(hitSoftStop, inCooldown, dailyPnLPct, secondsSinceLoss);

    // 4. Master Filter
    if (hitSoftStop || inCooldown) 
    {
        return; // Abort further execution. System is locked.
    }

    // ==========================================
    // 📉 YOUR ENTRY LOGIC GOES HERE
    // ==========================================
    // Example: if(BullishSetup) OrderSend(...);
}

//+------------------------------------------------------------------+
//| Engine Logic Functions                                           |
//+------------------------------------------------------------------+
double GetDailyPnLPct()
{
    double todayProfit = 0.0;
    datetime startOfDay = iTime(Symbol(), PERIOD_D1, 0);

    // Add today's closed profit
    for(int i = OrdersHistoryTotal() - 1; i >= 0; i--)
    {
        if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
        {
            if(OrderCloseTime() >= startOfDay)
                todayProfit += (OrderProfit() + OrderSwap() + OrderCommission());
        }
    }

    // Add currently floating profit
    for(int i = OrdersTotal() - 1; i >= 0; i--)
    {
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
            todayProfit += (OrderProfit() + OrderSwap() + OrderCommission());
        }
    }

    return (todayProfit / AccountBalance()) * 100.0;
}

datetime GetLastLossTime()
{
    for(int i = OrdersHistoryTotal() - 1; i >= 0; i--)
    {
        if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
        {
            if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
            {
                double totalProfit = OrderProfit() + OrderSwap() + OrderCommission();
                if(totalProfit < 0) 
                    return OrderCloseTime(); // Last trade was a loss
                else 
                    return 0; // Last trade was a win, no cooldown needed
            }
        }
    }
    return 0;
}

void UpdateHUD(bool softStop, bool cooldown, double pnl, int elapsedSecs)
{
    string status = "SYSTEM CLEAR";
    if (softStop) status = "SOFT STOP HIT: SESSION OVER";
    else if (cooldown) status = StringFormat("COOLDOWN: %d seconds remaining", (CooldownMinutes * 60) - elapsedSecs);

    string hud = StringFormat("--- REVENGE CONTROLLER ---\nStatus: %s\nDaily PnL: %.2f%% / -%.2f%%", 
                              status, pnl, SoftStopPct);
    Comment(hud);
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Revenge Trading Is the #1 Account Killer

Post by PTScalper »

2. MT5 / MQL5: Revenge Controller Template

MQL5's architecture separates Orders, Deals, and Positions. To find the last loss, we must query the HistorySelect pool and look specifically for DEAL_ENTRY_OUT (or INOUT) types to evaluate the net profit of the closing deal.

Code: Select all

//+------------------------------------------------------------------+
//|                                     RevengeController_Basic.mq5  |
//+------------------------------------------------------------------+
#property strict

// --- User Inputs ---
input double   SoftStopPct     = 2.0;       // Daily Soft Stop (%)
input int      CooldownMinutes = 5;         // Post-Loss Cooldown (Min)
input ulong    MagicNumber     = 12345;     // EA Magic Number

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    // 1. Calculate Daily PnL (Closed today + Floating)
    double dailyPnLPct = GetDailyPnLPct();
    bool hitSoftStop = (dailyPnLPct <= -SoftStopPct);

    // 2. Check Cooldown Status
    datetime lastLossTime = GetLastLossTime();
    int secondsSinceLoss = (int)(TimeCurrent() - lastLossTime);
    bool inCooldown = (lastLossTime > 0 && secondsSinceLoss < (CooldownMinutes * 60));

    // 3. Update HUD (Telemetry on chart)
    UpdateHUD(hitSoftStop, inCooldown, dailyPnLPct, secondsSinceLoss);

    // 4. Master Filter
    if (hitSoftStop || inCooldown) 
    {
        return; // Abort further execution. System is locked.
    }

    // ==========================================
    // 📉 YOUR ENTRY LOGIC GOES HERE
    // ==========================================
    // Example: if(BullishSetup) OrderSend(...);
}

//+------------------------------------------------------------------+
//| Engine Logic Functions                                           |
//+------------------------------------------------------------------+
double GetDailyPnLPct()
{
    double todayProfit = 0.0;
    datetime startOfDay = iTime(_Symbol, PERIOD_D1, 0);

    // Request history for today
    HistorySelect(startOfDay, TimeCurrent());
    int dealsTotal = HistoryDealsTotal();

    // Add today's closed profit
    for(int i = 0; i < dealsTotal; i++)
    {
        ulong ticket = HistoryDealGetTicket(i);
        if(ticket > 0)
        {
            todayProfit += HistoryDealGetDouble(ticket, DEAL_PROFIT) + 
                           HistoryDealGetDouble(ticket, DEAL_COMMISSION) + 
                           HistoryDealGetDouble(ticket, DEAL_SWAP);
        }
    }

    // Add currently floating profit
    for(int i = PositionsTotal() - 1; i >= 0; i--)
    {
        ulong ticket = PositionGetTicket(i);
        if(ticket > 0)
        {
            todayProfit += PositionGetDouble(POSITION_PROFIT) + 
                           PositionGetDouble(POSITION_SWAP);
        }
    }

    return (todayProfit / AccountInfoDouble(ACCOUNT_BALANCE)) * 100.0;
}

datetime GetLastLossTime()
{
    // Select the entire history to find the absolute last closed deal
    HistorySelect(0, TimeCurrent());
    int dealsTotal = HistoryDealsTotal();

    for(int i = dealsTotal - 1; i >= 0; i--)
    {
        ulong ticket = HistoryDealGetTicket(i);
        if(ticket > 0)
        {
            // Only evaluate deals belonging to this EA and Symbol
            if(HistoryDealGetString(ticket, DEAL_SYMBOL) == _Symbol && 
               HistoryDealGetInteger(ticket, DEAL_MAGIC) == MagicNumber)
            {
                // Ensure this deal was an EXIT deal (closing a position)
                long entryType = HistoryDealGetInteger(ticket, DEAL_ENTRY);
                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)
                        return (datetime)HistoryDealGetInteger(ticket, DEAL_TIME); // Was a loss
                    else
                        return 0; // Last trade was a win, no cooldown
                }
            }
        }
    }
    return 0;
}

void UpdateHUD(bool softStop, bool cooldown, double pnl, int elapsedSecs)
{
    string status = "SYSTEM CLEAR";
    if (softStop) status = "SOFT STOP HIT: SESSION OVER";
    else if (cooldown) status = StringFormat("COOLDOWN: %d seconds remaining", (CooldownMinutes * 60) - elapsedSecs);

    string hud = StringFormat("--- REVENGE CONTROLLER ---\nStatus: %s\nDaily PnL: %.2f%% / -%.2f%%", 
                              status, pnl, SoftStopPct);
    Comment(hud); // Displays the text in the top-left corner of the chart
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Revenge Trading Is the #1 Account Killer

Post by PTScalper »

Key Differences from Pine Script

The return Statement (Master Filter): Instead of wrapping your execution block in an if (masterClear) check like in Pine, MQL executes via OnTick(). If the soft stop is hit or the cooldown is active, we just call return;. This kills the tick instantly, blocking all downstream calculation and saving CPU cycles.

Global Account Context: GetDailyPnLPct() iterates over all trades for the day on the account to calculate the true daily drawdown against the account balance, mapping perfectly to prop firm rules.

The Chart HUD: MT4/MT5 don't color chart backgrounds easily without creating overlapping GUI objects. Instead, Comment(hud) paints a lightweight text block in the top-left corner of the terminal window showing the system state in real-time.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Revenge Trading Is the #1 Account Killer

Post by PTScalper »

Translating the "Pro" level system into MQL4 and MQL5 requires a significant architectural shift. Pine Script calculates state bar-by-bar, holding variables in memory. If MetaTrader restarts, loses connection, or you recompile the Expert Advisor, memory variables reset.

To make this truly professional and robust, the MQL versions dynamically recalculate your tilt state directly from the terminal's order history on every tick. If you crash and reboot, the EA instantly rebuilds your consecutive loss count, cooldown timers, and daily drawdown by reading the hard data.

Here is the Pro-Level Risk Engine for both platforms, featuring escalating penalties, session fencing, and consecutive loss hard-stops.

(Note: Set the MagicNumber input to 0 if you want the controller to monitor and restrict your manual trades on the chart, or set it to your EA's magic number to monitor algorithmic executions.)
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Revenge Trading Is the #1 Account Killer

Post by PTScalper »

1. MT4 / MQL4: Pro Risk Engine

Code: Select all

//+------------------------------------------------------------------+
//|                                       RevengeController_Pro.mq4  |
//+------------------------------------------------------------------+
#property strict

// ==========================================
// ⚙️ INPUTS: RISK & TILT MECHANICS
// ==========================================
input string   ___RiskLimits___   = "--- Risk Limits ---";
input double   SoftStopPct        = 2.0;       // Daily Soft Stop (%)
input int      MaxConsecLosses    = 3;         // Max Consecutive Losses Limit

input string   ___TiltMechanics___= "--- Tilt & Cooldown ---";
input int      BaseCooldownMin    = 5;         // Base Cooldown (Min)
input int      EscalationMult     = 3;         // Cooldown Escalation Multiplier

input string   ___Session___      = "--- 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 int      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 (Consecutive losses & time of last loss)
    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 Logic
    bool isLockedOut = (hitSoftStop || hitConsecLossLimit);
    
    // 6. Update HUD
    UpdateHUD(isLockedOut, inCooldown, inSession, dailyPnLPct, consecLosses, currentCooldownSecs - secondsSinceLoss);

    // 7. Execution Block
    if (isLockedOut || inCooldown || !inSession) 
    {
        return; // Engine is hard-locked. Abort tick.
    }

    // ==========================================
    // 📉 YOUR A+ SETUP LOGIC GOES HERE
    // ==========================================
    // if(BullishSweep) OrderSend(...);
}

//+------------------------------------------------------------------+
//| Engine Logic Functions                                           |
//+------------------------------------------------------------------+
double GetDailyPnLPct()
{
    double todayClosedProfit = 0.0;
    datetime startOfDay = iTime(Symbol(), PERIOD_D1, 0);

    // Sum closed profit for today
    for(int i = OrdersHistoryTotal() - 1; i >= 0; i--)
    {
        if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
        {
            if(OrderCloseTime() >= startOfDay)
                todayClosedProfit += (OrderProfit() + OrderSwap() + OrderCommission());
        }
    }

    // Determine Starting Equity for the day
    double startOfDayBalance = AccountBalance() - todayClosedProfit;
    
    // Calculate floating drawdown natively using AccountEquity()
    return ((AccountEquity() - startOfDayBalance) / startOfDayBalance) * 100.0;
}

void GetTiltMetrics(int &consecLossesCount, datetime &lastTime)
{
    consecLossesCount = 0;
    lastTime = 0;

    // Read backwards from the most recent trade
    for(int i = OrdersHistoryTotal() - 1; i >= 0; i--)
    {
        if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
        {
            if(OrderSymbol() == Symbol() && (MagicNumber == 0 || OrderMagicNumber() == MagicNumber))
            {
                double netProfit = OrderProfit() + OrderSwap() + OrderCommission();
                
                if(netProfit < 0) 
                {
                    consecLossesCount++;
                    if(lastTime == 0) lastTime = OrderCloseTime(); // Capture the exact time of the most recent loss
                }
                else if(netProfit > 0) 
                {
                    break; // Streak broken by a win
                }
            }
        }
    }
}

bool IsInSession()
{
    int currentMins = Hour() * 60 + Minute();
    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);
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply