Advertisement IC Markets

Calibrating my invalidation list for gold impulses under prop constraints

Discuss 1-minute to 15-minute price action setups, fading intraday momentum, key support/resistance zones, and proven short-term trading methodologies.
LondonScalper
Posts: 701
Joined: Sat Sep 05, 2026 7:54 am

Calibrating my invalidation list for gold impulses under prop constraints

Post by LondonScalper »

Gold impulses under prop constraints need an invalidation list written before the move, not during it.

Prop trailing and daily loss maths punish the "give it room" instinct that personal accounts sometimes absorb. My invalidation list for XAU impulses is short: structure break, time stop, and spread blowout. If any trip, I am out — mentoring voice included.

List I calibrate
  • Price invalidation beyond the impulse origin / clear swing
  • Time invalidation if the move stalls and turns into a chop tax
  • Venue invalidation if prop spread makes the remaining R fictional
Calibrating means reviewing losers where I moved the line. If I keep moving it, the list is decoration.

How do you define gold impulse invalidation when a prop rulebook is watching the same chart?

Under prop I also treat daily-loss proximity as soft invalidation: if a gold impulse would need room that endangers the day, I pass. Personal accounts can be more patient; funded maths often cannot.

The list stays short enough to remember under stress. Long invalidation essays do not get read when gold is sprinting.
Recommended broker for automated trading & scalping IC Markets
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Calibrating my invalidation list for gold impulses under prop constraints

Post by PTScalper »

LondonScalper wrote: Tue Sep 22, 2026 1:09 pm Gold impulses under prop constraints need an invalidation list written before the move, not during it.

Prop trailing and daily loss maths punish the "give it room" instinct that personal accounts sometimes absorb. My invalidation list for XAU impulses is short: structure break, time stop, and spread blowout. If any trip, I am out — mentoring voice included.

List I calibrate
  • Price invalidation beyond the impulse origin / clear swing
  • Time invalidation if the move stalls and turns into a chop tax
  • Venue invalidation if prop spread makes the remaining R fictional
Calibrating means reviewing losers where I moved the line. If I keep moving it, the list is decoration.

How do you define gold impulse invalidation when a prop rulebook is watching the same chart?

Under prop I also treat daily-loss proximity as soft invalidation: if a gold impulse would need room that endangers the day, I pass. Personal accounts can be more patient; funded maths often cannot.

The list stays short enough to remember under stress. Long invalidation essays do not get read when gold is sprinting.
Hi LondonScalper,

Under proprietary trading rules, you are not managing the asset’s volatility; you are managing the firm’s math. Gold (XAU) is uniquely punishing here because its natural Average True Range (ATR) frequently conflicts with the rigid parameters of trailing drawdowns and daily loss limits.

When a rulebook is watching the chart, Gold impulse invalidation must shift from contextual ("let's see how it closes") to binary ("the threshold is breached, the thesis is dead").

Here is how to define and structure that invalidation.

1. The Meta-Invalidation: Daily Loss Proximity

Before the chart even matters, the trade must survive the risk-desk math. If a Gold impulse requires a 40-pip stop to clear the structural origin, but your daily loss limit only allows for a 20-pip stop at your minimum position size, the trade is automatically invalid.

The Rule: If the natural price invalidation point risks more than 25% of your remaining daily loss limit, pass. Forcing a tighter, unnatural stop just to take the trade is gambling on the venue, not trading the asset.

2. Price Invalidation: The Impulse Origin

Gold impulses are driven by acute liquidity voids. If price retraces entirely through the candle or swing that initiated the impulse, the liquidity void has been filled, and the momentum thesis is wrong.

The Rule: Hard stop placed 1-2 pips behind the wick of the impulse origin (or the immediate preceding swing low/high).

No Closes: Do not wait for a candle close. In Gold, a wick against a prop drawdown can cause a hard breach before the candle finishes painting.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Calibrating my invalidation list for gold impulses under prop constraints

Post by PTScalper »

3. Time Invalidation: The Momentum Tax

An impulse is a velocity setup. If Gold prints a massive 15-minute expansion candle, and then paints four consecutive inside-bar dojis, it is no longer an impulse—it is a consolidation. Prop maths cannot absorb the variance of consolidation chops.

The Rule: The 3-to-5 Bar Rule. If the asset has not achieved at least 1R in profit within 3 to 5 bars of the entry, cut the trade. You entered for a sprint; if it turns into a marathon, the original premise is invalidated.

4. Venue Invalidation: Spread & Slippage

Around major NY session opens or macro data, Gold spreads on prop firm feeds can widen dynamically, turning a 1:2 R:R setup into a 1:1 or worse.

The Rule: If the active spread consumes more than 15% of your projected take-profit distance, the R:R is mathematically compromised. Cancel the limit order or market execution.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Calibrating my invalidation list for gold impulses under prop constraints

Post by PTScalper »

Pine Script: XAU Prop Invalidation Tracker

This Pine Script (v5) directly maps your rules to the chart. It detects an "impulse" (based on ATR multiplier), draws your hard Price Invalidation line at the origin, and starts a Time Invalidation countdown. If price drops below the origin, it flags "PRICE INVALID". If momentum stalls for too many bars without advancing, it flags "TIME STOP".

Code: Select all

//@version=5
indicator("XAU Prop Invalidation Tracker", overlay=true, max_lines_count=100, max_labels_count=100)

// ================= Inputs =================
atrLength    = input.int(14, title="ATR Length", group="Impulse Settings")
impulseMult  = input.float(2.0, title="Impulse Multiplier (x ATR)", tooltip="Defines how big a candle must be to be considered an impulse.", group="Impulse Settings")
timeStopBars = input.int(4, title="Time Stop (Bars)", tooltip="Number of bars to hold before time invalidation triggers.", group="Invalidation Rules")
rrTarget     = input.float(1.5, title="Minimum R:R Expected", group="Invalidation Rules")

// ================= Logic =================
atr = ta.atr(atrLength)
body = math.abs(close - open)

// Detect Impulses
isBullImpulse = close > open and body > (atr * impulseMult)
isBearImpulse = open > close and body > (atr * impulseMult)

// State Tracking
var bool  inActiveSetup = false
var int   setupDir      = 0      // 1 for Bullish, -1 for Bearish
var float invalidPrice  = na
var float entryPrice    = na
var int   barsSince     = na

// Reset setup if not active
if not inActiveSetup
    if isBullImpulse
        inActiveSetup := true
        setupDir      := 1
        invalidPrice  := low     // Price invalidation at origin low
        entryPrice    := close
        barsSince     := 0
        
        // Draw the hard invalidation line
        line.new(bar_index, invalidPrice, bar_index + timeStopBars, invalidPrice, color=color.red, width=2, style=line.style_solid)
        label.new(bar_index, invalidPrice, "ORIGIN (STOP)", style=label.style_label_up, color=color.rgb(255, 82, 82, 30), textcolor=color.red, size=size.small)

    else if isBearImpulse
        inActiveSetup := true
        setupDir      := -1
        invalidPrice  := high    // Price invalidation at origin high
        entryPrice    := close
        barsSince     := 0
        
        line.new(bar_index, invalidPrice, bar_index + timeStopBars, invalidPrice, color=color.red, width=2, style=line.style_solid)
        label.new(bar_index, invalidPrice, "ORIGIN (STOP)", style=label.style_label_down, color=color.rgb(255, 82, 82, 30), textcolor=color.red, size=size.small)

// Manage Active Setup
if inActiveSetup
    barsSince += 1
    
    // Check 1: Price Invalidation (Structure Break)
    priceInvalidated = (setupDir == 1 and close < invalidPrice) or (setupDir == -1 and close > invalidPrice)
    
    // Check 2: Time Invalidation (Stalled Momentum)
    // If we haven't hit our 1R or expected move by 'timeStopBars', it's a chop tax.
    riskDistance = math.abs(entryPrice - invalidPrice)
    bullTarget = entryPrice + (riskDistance * rrTarget)
    bearTarget = entryPrice - (riskDistance * rrTarget)
    
    targetHit = (setupDir == 1 and high >= bullTarget) or (setupDir == -1 and low <= bearTarget)
    timeInvalidated = (barsSince >= timeStopBars) and not targetHit
    
    // Process Invalidations
    if priceInvalidated
        label.new(bar_index, close, "PRICE INVALID", style=label.style_label_left, color=color.red, textcolor=color.white, size=size.small)
        inActiveSetup := false // Reset for next setup
        
    else if timeInvalidated
        label.new(bar_index, close, "TIME STOP", style=label.style_label_left, color=color.orange, textcolor=color.white, size=size.small)
        inActiveSetup := false // Reset for next setup
        
    else if targetHit
        // Target hit, clear the active setup without an invalidation label
        inActiveSetup := false
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Calibrating my invalidation list for gold impulses under prop constraints

Post by PTScalper »

How to use this for calibration

If you find yourself constantly being stopped out by wicks right before the move goes in your favor, do not move the price invalidation line. Instead, adjust the timeStopBars. If a move takes 8 bars to play out instead of 3, it wasn't an impulse setup—it was a standard mean-reversion or trend-continuation setup, which requires completely different sizing to respect the prop firm's daily loss limit. Keep the list short, trust the origin line, and respect the chop tax.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Calibrating my invalidation list for gold impulses under prop constraints

Post by PTScalper »

If you want it to make little bit more Pro, here is updated version:

Pro-Grade Pine Script: XAU Systematic Invalidation

This upgraded version acts as a visual execution engine. It introduces a Heads-Up Display (HUD) for real-time state tracking, explicit risk mapping (Entry, Stop, Target lines), and programmatic alerts for automated prop-firm copiers.

Code: Select all

//@version=5
indicator("XAU Institutional Invalidation Engine", overlay=true, max_lines_count=100, max_labels_count=100, max_boxes_count=50)

// ================= Core Parameters =================
atrLength    = input.int(14, title="ATR Period", group="1. Volatility Baseline")
impulseMult  = input.float(2.2, title="Displacement Threshold (x ATR)", step=0.1, group="1. Volatility Baseline")
volumeFilter = input.bool(true, title="Require Volume Expansion", group="1. Volatility Baseline")

timeStopBars = input.int(4, title="Velocity Decay Limit (Bars)", group="2. Invalidation Matrix")
rrTarget     = input.float(2.0, title="Expected R-Multiple", step=0.1, group="2. Invalidation Matrix")

// ================= Market Data =================
atr = ta.atr(atrLength)
body = math.abs(close - open)
volMA = ta.sma(volume, 20)
volValid = volumeFilter ? (volume > volMA) : true

// ================= Setup Detection =================
isBullImpulse = close > open and body > (atr * impulseMult) and volValid
isBearImpulse = open > close and body > (atr * impulseMult) and volValid

// ================= State Management =================
var bool  isActive    = false
var int   direction   = 0       // 1 = Long, -1 = Short
var float priceStop   = na
var float priceEntry  = na
var float priceTarget = na
var int   barCount    = na
var line  stopLine    = na
var line  targetLine  = na

// ================= Execution Engine =================
if not isActive
    if isBullImpulse or isBearImpulse
        isActive    := true
        direction   := isBullImpulse ? 1 : -1
        priceEntry  := close
        priceStop   := isBullImpulse ? low : high
        riskDistance = math.abs(priceEntry - priceStop)
        priceTarget := isBullImpulse ? (priceEntry + (riskDistance * rrTarget)) : (priceEntry - (riskDistance * rrTarget))
        barCount    := 0
        
        // Draw Risk Zones
        stopLine   := line.new(bar_index, priceStop, bar_index + timeStopBars, priceStop, color=color.red, width=2, style=line.style_dashed)
        targetLine := line.new(bar_index, priceTarget, bar_index + timeStopBars, priceTarget, color=color.new(color.teal, 0), width=2, style=line.style_dashed)
        
        // Box the impulse origin
        box.new(bar_index[1], priceEntry, bar_index, priceStop, color=color.new(direction == 1 ? color.green : color.red, 80), border_color=color.new(color.gray, 50))
        
        alert(direction == 1 ? "XAU LONG IMPULSE" : "XAU SHORT IMPULSE", alert.freq_once_per_bar)

// ================= Trade Management =================
if isActive
    barCount += 1
    
    // Extend lines for active visuals
    line.set_x2(stopLine, bar_index + 1)
    line.set_x2(targetLine, bar_index + 1)
    
    // Invalidation Conditions
    hitStop   = (direction == 1 and low <= priceStop) or (direction == -1 and high >= priceStop)
    hitTarget = (direction == 1 and high >= priceTarget) or (direction == -1 and low <= priceTarget)
    hitTime   = (barCount >= timeStopBars) and not hitTarget and not hitStop
    
    if hitStop
        label.new(bar_index, priceStop, "STRUCTURAL INVALIDATION", style=direction == 1 ? label.style_label_up : label.style_label_down, color=color.red, textcolor=color.white, size=size.small)
        alert("XAU STRUCTURAL STOP", alert.freq_once_per_bar)
        isActive := false
        
    else if hitTime
        label.new(bar_index, close, "VELOCITY DECAY (TIME STOP)", style=label.style_label_left, color=color.gray, textcolor=color.white, size=size.small)
        alert("XAU TIME STOP", alert.freq_once_per_bar)
        isActive := false
        
    else if hitTarget
        label.new(bar_index, priceTarget, "TARGET REALIZED", style=direction == 1 ? label.style_label_down : label.style_label_up, color=color.teal, textcolor=color.white, size=size.small)
        isActive := false

// ================= HUD Dashboard =================
var table display = table.new(position.top_right, 2, 4, border_width = 1, border_color = color.new(color.gray, 80))
if barstate.islast
    table.cell(display, 0, 0, "SYSTEM STATE", text_color=color.white, bgcolor=color.new(color.gray, 80))
    table.cell(display, 1, 0, isActive ? "ACTIVE" : "SCANNING", text_color=isActive ? color.yellow : color.gray, bgcolor=color.new(color.black, 0))
    
    table.cell(display, 0, 1, "T-MINUS (TIME STOP)", text_color=color.white, bgcolor=color.new(color.gray, 80))
    table.cell(display, 1, 1, isActive ? str.tostring(timeStopBars - barCount) : "-", text_color=color.white, bgcolor=color.new(color.black, 0))
    
    table.cell(display, 0, 2, "CURRENT SPREAD RISK", text_color=color.white, bgcolor=color.new(color.gray, 80))
    // Approximate spread monitoring (Requires real-time tick data; simulated visually via difference in high/close proxy if needed, but best left as manual execution check in Pine)
    table.cell(display, 1, 2, "CHECK B-BOOK", text_color=color.red, bgcolor=color.new(color.black, 0))
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Calibrating my invalidation list for gold impulses under prop constraints

Post by PTScalper »

Calibration Protocol

Do not manipulate the price invalidation line to optimize win rates on historical data. If you are experiencing high structural invalidation frequencies, the error is not your stop placement; the error is your displacement threshold. Increase the impulseMult to filter out algorithmic noise and isolate true liquidity injections. Prop survival is dictated by frequency suppression, not spread tolerance.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Calibrating my invalidation list for gold impulses under prop constraints

Post by PTScalper »

Execution Architecture (MQL4 vs. MQL5)

Unlike TradingView’s closed environment, MetaTrader interacts directly with your broker/prop firm’s terminal bridge. This introduces two critical execution realities:

Dynamic Spread Exposure: Pine Script cannot natively read raw tick book spreads on historical bars. MQL can inspect the live bridge spread (Ask - Bid) on every incoming tick to immediately flag spread blowout.

Object Lifecycles: Both indicators use explicit object prefixes (XAU_PI_). When the indicator is removed or reloaded, it cleans up only its own lines, boxes, and labels without touching your manual chart markups.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Calibrating my invalidation list for gold impulses under prop constraints

Post by PTScalper »

1. MQL4 Indicator (XAU_Prop_Invalidation.mq4)

Save this inside your terminal's MQL4/Indicators/ folder and compile via MetaEditor.

Code: Select all

//+------------------------------------------------------------------+
//|                                  XAU_Prop_Invalidation_MT4.mq4   |
//|                                  Institutional Invalidation Track |
//+------------------------------------------------------------------+
#property copyright "Prop Risk Engine"
#property link      ""
#property version   "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots   0

// ================= INPUT PARAMETERS =================
input string   sep0              = "--- 1. Volatility Baseline ---";
input int      InpATRPeriod      = 14;      // ATR Period
input double   InpImpulseMult    = 2.2;     // Displacement Multiplier (x ATR)
input bool     InpVolumeFilter   = true;    // Require Volume Expansion (> 20 SMA)

input string   sep1              = "--- 2. Invalidation Matrix ---";
input int      InpTimeStopBars   = 4;       // Velocity Decay Limit (Bars)
input double   InpRRTarget       = 2.0;     // Target R-Multiple
input int      InpMaxSpreadPts   = 35;      // Max Allowable Spread (Points)

input string   sep2              = "--- 3. Visuals & Alerts ---";
input color    InpStopColor      = clrCrimson;
input color    InpTargetColor    = clrTeal;
input color    InpTextColor      = clrWhite;
input bool     InpEnableAlerts   = true;

// Prefix for chart cleanup
#define PREFIX "XAU_PI_"

// Global tracking
datetime g_lastAlertTime = 0;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   IndicatorShortName("XAU Prop Invalidation [MT4]");
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   ObjectsDeleteAll(0, PREFIX);
   Comment("");
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
   if(rates_total < InpATRPeriod + 25) return(0);

   int limit = rates_total - prev_calculated;
   if(limit > 250) limit = 250; // Scan the last 250 bars to preserve memory

   // Loop through historical context up to bar 1
   for(int i = limit; i >= 1; i--)
   {
      double atr = iATR(NULL, 0, InpATRPeriod, i);
      double body = MathAbs(close[i] - open[i]);
      
      // Volume Filter: calculate 20-period SMA of tick volume
      bool volPass = true;
      if(InpVolumeFilter)
      {
         double sumVol = 0;
         for(int v = 0; v < 20; v++) sumVol += (double)tick_volume[i + v];
         double avgVol = sumVol / 20.0;
         volPass = ((double)tick_volume[i] > avgVol);
      }

      bool isBullImpulse = (close[i] > open[i]) && (body > (atr * InpImpulseMult)) && volPass;
      bool isBearImpulse = (open[i] > close[i]) && (body > (atr * InpImpulseMult)) && volPass;

      if(isBullImpulse || isBearImpulse)
      {
         int dir = isBullImpulse ? 1 : -1;
         datetime impulseTime = time[i];
         string baseId = PREFIX + TimeToString(impulseTime);

         // Avoid re-processing already drawn structures
         if(ObjectFind(0, baseId + "_Stop") != -1) continue;

         double entryPrice  = close[i];
         double stopPrice   = (dir == 1) ? low[i] : high[i];
         double riskDist    = MathAbs(entryPrice - stopPrice);
         double targetPrice = (dir == 1) ? (entryPrice + riskDist * InpRRTarget) 
                                         : (entryPrice - riskDist * InpRRTarget);

         // Draw Structural Stop Line
         DrawHLine(baseId + "_Stop", impulseTime, time[MathMax(0, i - InpTimeStopBars)], stopPrice, InpStopColor, STYLE_DASH);
         DrawHLine(baseId + "_Target", impulseTime, time[MathMax(0, i - InpTimeStopBars)], targetPrice, InpTargetColor, STYLE_DASH);

         // Evaluate forward bars from the impulse for immediate invalidation state
         int outcomeBar = -1;
         string outcomeText = "";
         color outcomeColor = clrWhite;

         for(int f = i - 1; f >= 0; f--)
         {
            int barsElapsed = (i - f);

            // Condition 1: Structural Invalidation
            if((dir == 1 && low[f] <= stopPrice) || (dir == -1 && high[f] >= stopPrice))
            {
               outcomeBar = f;
               outcomeText = "STRUCTURAL INVALIDATION";
               outcomeColor = InpStopColor;
               break;
            }
            // Condition 2: Target Realized
            if((dir == 1 && high[f] >= targetPrice) || (dir == -1 && low[f] <= targetPrice))
            {
               outcomeBar = f;
               outcomeText = "TARGET HIT";
               outcomeColor = InpTargetColor;
               break;
            }
            // Condition 3: Velocity Decay (Time Stop)
            if(barsElapsed >= InpTimeStopBars)
            {
               outcomeBar = f;
               outcomeText = "VELOCITY DECAY (TIME STOP)";
               outcomeColor = clrSilver;
               break;
            }
         }

         if(outcomeBar != -1)
         {
            DrawLabel(baseId + "_Outcome", time[outcomeBar], (dir == 1 ? high[outcomeBar] : low[outcomeBar]), outcomeText, outcomeColor);
         }
      }
   }

   // Live Bar 0 & Microstructure Evaluation
   UpdateTerminalHUD();

   return(rates_total);
}

//+------------------------------------------------------------------+
//| Real-time Head-Up Display                                        |
//+------------------------------------------------------------------+
void UpdateTerminalHUD()
{
   int liveSpread = (int)MarketInfo(Symbol(), MODE_SPREAD);
   string spreadStatus = (liveSpread > InpMaxSpreadPts) ? "BLOWOUT RISK (PASS)" : "EXECUTION PERMITTED";

   string hud = "\n=== XAU PROP INVALIDATION ENGINE [MT4] ===\n" +
                "Live Spread: " + IntegerToString(liveSpread) + " pts (" + spreadStatus + ")\n" +
                "Impulse Trigger: > " + DoubleToString(iATR(NULL, 0, InpATRPeriod, 0) * InpImpulseMult, Digits) + " distance\n" +
                "Time Stop Policy: Strict " + IntegerToString(InpTimeStopBars) + " Bars\n" +
                "===========================================";
   Comment(hud);
}

//+------------------------------------------------------------------+
//| Drawing Utilities                                                |
//+------------------------------------------------------------------+
void DrawHLine(string name, datetime t1, datetime t2, double price, color clr, ENUM_LINE_STYLE style)
{
   ObjectDelete(0, name);
   ObjectCreate(0, name, OBJ_TREND, 0, t1, price, t2, price);
   ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, name, OBJPROP_STYLE, style);
   ObjectSetInteger(0, name, OBJPROP_WIDTH, 2);
   ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, false);
}

void DrawLabel(string name, datetime t, double price, string text, color clr)
{
   ObjectDelete(0, name);
   ObjectCreate(0, name, OBJ_TEXT, 0, t, price);
   ObjectSetString(0, name, OBJPROP_TEXT, text);
   ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 8);
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: Calibrating my invalidation list for gold impulses under prop constraints

Post by PTScalper »

2. MQL5 Indicator (XAU_Prop_Invalidation.mq5)

Save this inside your terminal's MQL5/Indicators/ folder and compile via MetaEditor.

Code: Select all

//+------------------------------------------------------------------+
//|                                  XAU_Prop_Invalidation_MT5.mq5   |
//|                                  Institutional Invalidation Track |
//+------------------------------------------------------------------+
#property copyright "Prop Risk Engine"
#property link      ""
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots   0

// ================= INPUT PARAMETERS =================
input group "--- 1. Volatility Baseline ---"
input int      InpATRPeriod      = 14;      // ATR Period
input double   InpImpulseMult    = 2.2;     // Displacement Multiplier (x ATR)
input bool     InpVolumeFilter   = true;    // Require Volume Expansion (> 20 SMA)

input group "--- 2. Invalidation Matrix ---"
input int      InpTimeStopBars   = 4;       // Velocity Decay Limit (Bars)
input double   InpRRTarget       = 2.0;     // Target R-Multiple
input int      InpMaxSpreadPts   = 35;      // Max Allowable Spread (Points)

input group "--- 3. Visuals & Alerts ---"
input color    InpStopColor      = clrCrimson;
input color    InpTargetColor    = clrTeal;
input color    InpTextColor      = clrWhite;
input bool     InpEnableAlerts   = true;

#define PREFIX "XAU_PI5_"

int      g_atrHandle;
datetime g_lastAlertTime = 0;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   g_atrHandle = iATR(_Symbol, _Period, InpATRPeriod);
   if(g_atrHandle == INVALID_HANDLE)
   {
      Print("Failed to initialize ATR handle.");
      return(INIT_FAILED);
   }

   IndicatorSetString(INDICATOR_SHORTNAME, "XAU Prop Invalidation [MT5]");
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   ObjectsDeleteAll(0, PREFIX);
   Comment("");
   IndicatorRelease(g_atrHandle);
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
   if(rates_total < InpATRPeriod + 25) return(0);

   // Map series arrays (0 = Current Bar, 1 = Previous Bar)
   ArraySetAsSeries(time, true);
   ArraySetAsSeries(open, true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low, true);
   ArraySetAsSeries(close, true);
   ArraySetAsSeries(tick_volume, true);

   double atrValues[];
   ArraySetAsSeries(atrValues, true);
   if(CopyBuffer(g_atrHandle, 0, 0, 250, atrValues) <= 0) return(0);

   int limit = (prev_calculated == 0) ? 250 : (rates_total - prev_calculated);
   if(limit > 250) limit = 250;

   for(int i = limit; i >= 1; i--)
   {
      double atr = atrValues[i];
      double body = MathAbs(close[i] - open[i]);

      bool volPass = true;
      if(InpVolumeFilter)
      {
         double sumVol = 0;
         for(int v = 0; v < 20; v++) sumVol += (double)tick_volume[i + v];
         double avgVol = sumVol / 20.0;
         volPass = ((double)tick_volume[i] > avgVol);
      }

      bool isBullImpulse = (close[i] > open[i]) && (body > (atr * InpImpulseMult)) && volPass;
      bool isBearImpulse = (open[i] > close[i]) && (body > (atr * InpImpulseMult)) && volPass;

      if(isBullImpulse || isBearImpulse)
      {
         int dir = isBullImpulse ? 1 : -1;
         datetime impulseTime = time[i];
         string baseId = PREFIX + TimeToString(impulseTime);

         if(ObjectFind(0, baseId + "_Stop") != -1) continue;

         double entryPrice  = close[i];
         double stopPrice   = (dir == 1) ? low[i] : high[i];
         double riskDist    = MathAbs(entryPrice - stopPrice);
         double targetPrice = (dir == 1) ? (entryPrice + riskDist * InpRRTarget) 
                                         : (entryPrice - riskDist * InpRRTarget);

         datetime endTime = time[MathMax(0, i - InpTimeStopBars)];

         DrawHLine(baseId + "_Stop", impulseTime, endTime, stopPrice, InpStopColor, STYLE_DASH);
         DrawHLine(baseId + "_Target", impulseTime, endTime, targetPrice, InpTargetColor, STYLE_DASH);

         // Forward Bar Evaluation
         int outcomeBar = -1;
         string outcomeText = "";
         color outcomeColor = clrWhite;

         for(int f = i - 1; f >= 0; f--)
         {
            int barsElapsed = (i - f);

            if((dir == 1 && low[f] <= stopPrice) || (dir == -1 && high[f] >= stopPrice))
            {
               outcomeBar = f;
               outcomeText = "STRUCTURAL INVALIDATION";
               outcomeColor = InpStopColor;
               break;
            }
            if((dir == 1 && high[f] >= targetPrice) || (dir == -1 && low[f] <= targetPrice))
            {
               outcomeBar = f;
               outcomeText = "TARGET HIT";
               outcomeColor = InpTargetColor;
               break;
            }
            if(barsElapsed >= InpTimeStopBars)
            {
               outcomeBar = f;
               outcomeText = "VELOCITY DECAY (TIME STOP)";
               outcomeColor = clrSilver;
               break;
            }
         }

         if(outcomeBar != -1)
         {
            DrawLabel(baseId + "_Outcome", time[outcomeBar], (dir == 1 ? high[outcomeBar] : low[outcomeBar]), outcomeText, outcomeColor);
         }
      }
   }

   UpdateHUD(atrValues[0]);

   return(rates_total);
}

//+------------------------------------------------------------------+
//| Heads-Up Dashboard                                               |
//+------------------------------------------------------------------+
void UpdateHUD(double currentAtr)
{
   long spread = 0;
   SymbolInfoInteger(_Symbol, SYMBOL_SPREAD, spread);

   string spreadStatus = (spread > InpMaxSpreadPts) ? "BLOWOUT RISK (NO TRADES)" : "NORMAL";

   string hud = "\n=== XAU PROP INVALIDATION ENGINE [MT5] ===\n" +
                "Current Live Spread: " + IntegerToString(spread) + " pts (" + spreadStatus + ")\n" +
                "Impulse Velocity Bar Min: > " + DoubleToString(currentAtr * InpImpulseMult, _Digits) + "\n" +
                "Decay Cutoff: " + IntegerToString(InpTimeStopBars) + " Bars\n" +
                "===========================================";
   Comment(hud);
}

//+------------------------------------------------------------------+
//| Visual Helpers                                                   |
//+------------------------------------------------------------------+
void DrawHLine(string name, datetime t1, datetime t2, double price, color clr, ENUM_LINE_STYLE style)
{
   ObjectDelete(0, name);
   ObjectCreate(0, name, OBJ_TREND, 0, t1, price, t2, price);
   ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, name, OBJPROP_STYLE, style);
   ObjectSetInteger(0, name, OBJPROP_WIDTH, 2);
   ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, false);
}

void DrawLabel(string name, datetime t, double price, string text, color clr)
{
   ObjectDelete(0, name);
   ObjectCreate(0, name, OBJ_TEXT, 0, t, price);
   ObjectSetString(0, name, OBJPROP_TEXT, text);
   ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 8);
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply