Advertisement IC Markets

Scalping AUDNZD: A Cross Driven by Relative Rates and Quiet Ranges

Discuss 1-minute to 15-minute price action setups, fading intraday momentum, key support/resistance zones, and proven short-term trading methodologies.
FTtrader
Posts: 1008
Joined: Mon Aug 03, 2026 2:43 pm

Re: Scalping AUDNZD: A Cross Driven by Relative Rates and Quiet Ranges

Post by FTtrader »

AUDNZD Pro: Asian Liquidity Matrix

Code: Select all

//@version=5
indicator("AUDNZD Pro: Asian Liquidity Matrix", overlay=true, max_lines_count=500, max_boxes_count=500)

// =========================================================================
// INPUTS & SETTINGS
// =========================================================================
grp_time  = "Session & Broker Time"
sessTime  = input.session("2200-0600", title="Asian Session (Broker Time)", group=grp_time, tooltip="Set this to match your broker's Asian session hours.")
tz        = input.string("UTC", title="Timezone", group=grp_time, tooltip="Match to your broker (e.g., UTC+2 for standard NY Close charts).")

grp_ui    = "Visuals & UI"
colHigh   = input.color(color.new(color.red, 30), title="Buy-Side Liquidity (High)", group=grp_ui)
colLow    = input.color(color.new(color.teal, 30), title="Sell-Side Liquidity (Low)", group=grp_ui)
colBox    = input.color(color.new(color.blue, 90), title="Accumulation Box", group=grp_ui)
showDash  = input.bool(true, title="Show HUD Dashboard", group=grp_ui)

// =========================================================================
// SESSION LOGIC & STATE TRACKING
// =========================================================================
inSession    = time(timeframe.period, sessTime, tz)
sessionStart = inSession and not inSession[1]
sessionEnd   = not inSession and inSession[1]

var float asHigh = na
var float asLow  = na
var float asEq   = na // Equilibrium (50% of range)

// State flags for Alerts
var bool sweptHigh = false
var bool sweptLow  = false

// Drawing Objects
var box asBox = na
var line hLine = na
var line lLine = na
var line eqLine = na

// =========================================================================
// ACCUMULATION PHASE (ASIAN SESSION)
// =========================================================================
if sessionStart
    asHigh := high
    asLow  := low
    sweptHigh := false
    sweptLow  := false
    asBox  := box.new(left=bar_index, top=asHigh, right=bar_index, bottom=asLow, border_color=color.new(color.blue, 70), bgcolor=colBox)

if inSession
    asHigh := math.max(asHigh, high)
    asLow  := math.min(asLow, low)
    asEq   := (asHigh + asLow) / 2
    box.set_top(asBox, asHigh)
    box.set_bottom(asBox, asLow)
    box.set_right(asBox, bar_index)

// =========================================================================
// MANIPULATION / DISTRIBUTION PHASE (LONDON / NY)
// =========================================================================
if sessionEnd
    hLine  := line.new(x1=bar_index, y1=asHigh, x2=bar_index+1, y2=asHigh, color=colHigh, width=2, style=line.style_dashed)
    lLine  := line.new(x1=bar_index, y1=asLow, x2=bar_index+1, y2=asLow, color=colLow, width=2, style=line.style_dashed)
    eqLine := line.new(x1=bar_index, y1=asEq, x2=bar_index+1, y2=asEq, color=color.gray, width=1, style=line.style_dotted)

if not inSession and not na(hLine)
    line.set_x2(hLine, bar_index)
    line.set_x2(lLine, bar_index)
    line.set_x2(eqLine, bar_index)

// =========================================================================
// EXECUTION LOGIC & ALERTS (FADING THE SWEEP)
// =========================================================================
// Detect sweeps (Wick goes outside, but we track the state)
if not inSession
    if high > asHigh
        sweptHigh := true
    if low < asLow
        sweptLow := true

// Sweep Rejection (Price wicked out, but closed back inside the range)
validShortTrigger = sweptHigh and close < asHigh and high > asHigh and not inSession
validLongTrigger  = sweptLow and close > asLow and low < asLow and not inSession

// Alert Conditions for automation or notifications
alertcondition(validShortTrigger, title="AUDNZD: BSL Sweep & Reject", message="Buy-Side Liquidity swept. Short trigger formed. Target EQ.")
alertcondition(validLongTrigger, title="AUDNZD: SSL Sweep & Reject", message="Sell-Side Liquidity swept. Long trigger formed. Target EQ.")

// Visual cue on chart for valid triggers
plotshape(validShortTrigger, title="Short Trigger", style=shape.triangledown, location=location.abovebar, color=colHigh, size=size.small)
plotshape(validLongTrigger, title="Long Trigger", style=shape.triangleup, location=location.belowbar, color=colLow, size=size.small)

// =========================================================================
// PRO DASHBOARD (HUD)
// =========================================================================
adr14 = request.security(syminfo.tickerid, "D", ta.sma(high - low, 14))
pipMult = syminfo.mintick == 0.00001 ? 10000 : (syminfo.mintick == 0.001 ? 100 : 10000)

adrPips = adr14 * pipMult
asPips  = (asHigh - asLow) * pipMult
asPct   = (asPips / adrPips) * 100

// Dynamic Market State string
string mktState = "Accumulating"
color stateCol = color.gray
if not inSession
    if validShortTrigger or validLongTrigger
        mktState := "Sweep Rejected (Action)"
        stateCol := color.orange
    else if high > asHigh or low < asLow
        mktState := "Sweeping Liquidity"
        stateCol := color.red
    else
        mktState := "Inside Range"
        stateCol := color.teal

var table hud = table.new(position.top_right, 2, 4, border_width=1, border_color=color.new(color.gray, 80), bgcolor=color.new(color.black, 85))

if showDash and barstate.islast
    table.cell(hud, 0, 0, "14-Day ADR", text_color=color.white, text_size=size.small)
    table.cell(hud, 1, 0, str.tostring(adrPips, "#.0") + " pips", text_color=color.yellow, text_size=size.small)
    
    table.cell(hud, 0, 1, "Asian Range", text_color=color.white, text_size=size.small)
    table.cell(hud, 1, 1, str.tostring(asPips, "#.0") + " pips (" + str.tostring(asPct, "#.0") + "%)", text_color=color.aqua, text_size=size.small)

    table.cell(hud, 0, 2, "Session EQ", text_color=color.white, text_size=size.small)
    table.cell(hud, 1, 2, str.tostring(asEq, "#.00000"), text_color=color.gray, text_size=size.small)

    table.cell(hud, 0, 3, "State", text_color=color.white, text_size=size.small)
    table.cell(hud, 1, 3, mktState, text_color=stateCol, text_size=size.small)
Recommended broker for automated trading & scalping IC Markets
FTtrader
Posts: 1008
Joined: Mon Aug 03, 2026 2:43 pm

Re: Scalping AUDNZD: A Cross Driven by Relative Rates and Quiet Ranges

Post by FTtrader »

The Professional Upgrades

Sweep & Rejection Triggers: The script now features memory (sweptHigh and sweptLow). It tracks if the price breached the Asian high/low during London/NY. When a candle pierces that level to sweep liquidity but closes back inside the range, it plots a triangle entry signal directly on the chart.

Executable Alerts: Built-in alertcondition functions allow you to link this script to your TradingView alerts or a webhook to an execution algo. It will ping your phone exactly when a liquidity sweep rejection closes, saving you from staring at a ranging market.

Equilibrium (EQ) Mapping: A dotted grey line is now projected at exactly 50% of the Asian range. Institutional mean-reversion strategies dictate that if you enter a fade at the edge of the range (Premium or Discount), your primary Take Profit (TP1) or risk-reduction zone is the EQ line.

State-Aware HUD Dashboard: The dashboard now calculates what percentage of the Daily ADR has already been consumed by the Asian Range. (If the Asian range consumed 80% of the ADR, a breakout during London is highly improbable, making a mean-reversion trade highly probable). It also features a live "State" monitor telling you if the market is Accumulating, Sweeping, or signaling Action.

Timezone & Broker Adaptability: A major flaw in amateur scripts is hardcoding UTC. Depending on whether your broker uses UTC+2, UTC+3, or EST, the Asian session candles shift. The settings menu now allows you to specify your exact timezone so the box perfectly matches the true 22:00-06:00 window of your specific data feed.
FTtrader
Posts: 1008
Joined: Mon Aug 03, 2026 2:43 pm

Re: Scalping AUDNZD: A Cross Driven by Relative Rates and Quiet Ranges

Post by FTtrader »

MQL4 Implementation

This indicator builds the Asian session range box, projects the Buy-Side Liquidity (BSL), Sell-Side Liquidity (SSL), and Equilibrium (EQ 50%) levels through London and New York, plots non-repainting signal arrows on liquidity sweeps that reject back inside the range, and displays an on-chart institutional HUD.

Save as AUDNZD_Asian_Liquidity_Pro.mq4 in your MQL4/Indicators/ directory:

Code: Select all

//+------------------------------------------------------------------+
//|                                AUDNZD_Asian_Liquidity_Pro.mq4   |
//|                    Asian Range & Liquidity Rejection Matrix      |
//+------------------------------------------------------------------+
#property copyright "Forex Scalping Suite"
#property link      ""
#property version   "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_color1  clrCrimson
#property indicator_color2  clrMediumSeaGreen
#property indicator_width1  2
#property indicator_width2  2

//--- Inputs
input string   InpAsianSession   = "22:00-06:00"; // Asian Session (Broker Server Time)
input color    InpBoxColor       = C'18,30,49';   // Range Box Background
input color    InpBSLColor       = clrCrimson;    // Buy-Side Liquidity (High)
input color    InpSSLColor       = clrMediumSeaGreen; // Sell-Side Liquidity (Low)
input color    InpEQColor        = clrDarkGray;   // Equilibrium (50%)
input bool     InpEnableAlerts   = true;          // Enable Pop-up & Sound Alerts
input bool     InpSendPush       = false;         // Send Mobile Notifications
input bool     InpShowHUD        = true;          // Show Range & ADR Dashboard

//--- Buffers
double ShortSignalBuffer[];
double LongSignalBuffer[];

//--- Global Variables
string  Prefix = "AZN_";
int     StartHour, StartMin, EndHour, EndMin;
double  PipPoint;
datetime LastAlertBar;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   IndicatorBuffers(2);
   SetIndexBuffer(0, ShortSignalBuffer);
   SetIndexStyle(0, DRAW_ARROW);
   SetIndexArrow(0, 234); // Down arrow
   
   SetIndexBuffer(1, LongSignalBuffer);
   SetIndexStyle(1, DRAW_ARROW);
   SetIndexArrow(1, 233); // Up arrow

   PipPoint = (Digits == 3 || Digits == 5) ? Point * 10.0 : Point;

   // Parse session string "HH:MM-HH:MM"
   string parts[];
   if(StringSplit(InpAsianSession, '-', parts) == 2)
   {
      string startParts[], endParts[];
      StringSplit(parts[0], ':', startParts);
      StringSplit(parts[1], ':', endParts);
      StartHour = (int)StringToInteger(startParts[0]);
      StartMin  = (ArraySize(startParts) > 1) ? (int)StringToInteger(startParts[1]) : 0;
      EndHour   = (int)StringToInteger(endParts[0]);
      EndMin    = (ArraySize(endParts) > 1) ? (int)StringToInteger(endParts[1]) : 0;
   }

   return(INIT_SUCCEEDED);
}

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

//+------------------------------------------------------------------+
//| Check if a bar falls inside the session                          |
//+------------------------------------------------------------------+
bool IsInSession(datetime t)
{
   MqlDateTime dt;
   TimeToStruct(t, dt);
   int barMinutes = dt.hour * 60 + dt.min;
   int startMinutes = StartHour * 60 + StartMin;
   int endMinutes = EndHour * 60 + EndMin;

   if(startMinutes <= endMinutes)
      return (barMinutes >= startMinutes && barMinutes < endMinutes);
   else // Overnight wrap
      return (barMinutes >= startMinutes || barMinutes < endMinutes);
}

//+------------------------------------------------------------------+
//| 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 < 50) return(0);

   int limit = rates_total - prev_calculated;
   if(limit <= 0) limit = 1;

   for(int i = limit; i >= 0; i--)
   {
      ShortSignalBuffer[i] = EMPTY_VALUE;
      LongSignalBuffer[i]  = EMPTY_VALUE;

      bool inSessNow  = IsInSession(time[i]);
      bool inSessPrev = (i + 1 < rates_total) ? IsInSession(time[i + 1]) : inSessNow;

      // Identify active or most recent session bounds
      datetime sessStartTime = 0;
      datetime sessEndTime   = 0;
      double   sessHigh       = -1.0;
      double   sessLow        = 99999.0;

      // Trace back to locate the session window
      int lookback = i;
      while(lookback < rates_total - 1 && !IsInSession(time[lookback]))
         lookback++;

      if(IsInSession(time[lookback]))
      {
         sessEndTime = time[lookback];
         while(lookback < rates_total - 1 && IsInSession(time[lookback]))
         {
            sessHigh = MathMax(sessHigh, high[lookback]);
            sessLow  = MathMin(sessLow, low[lookback]);
            sessStartTime = time[lookback];
            lookback++;
         }

         double sessEQ = (sessHigh + sessLow) / 2.0;
         string dayId = TimeToString(sessStartTime, TIME_DATE);

         // Render Asian Box & Projection Lines
         if(i == 0)
         {
            string boxName = Prefix + "Box_" + dayId;
            if(ObjectFind(0, boxName) < 0)
            {
               ObjectCreate(0, boxName, OBJ_RECTANGLE, 0, sessStartTime, sessHigh, sessEndTime, sessLow);
               ObjectSetInteger(0, boxName, OBJPROP_COLOR, InpBoxColor);
               ObjectSetInteger(0, boxName, OBJPROP_BGCOLOR, InpBoxColor);
               ObjectSetInteger(0, boxName, OBJPROP_BACK, true);
               ObjectSetInteger(0, boxName, OBJPROP_SELECTABLE, false);
            }
            else
            {
               ObjectSetDouble(0, boxName, OBJPROP_PRICE1, sessHigh);
               ObjectSetDouble(0, boxName, OBJPROP_PRICE2, sessLow);
               ObjectSetInteger(0, boxName, OBJPROP_TIME2, sessEndTime);
            }

            // Project Levels during London / NY
            if(!inSessNow)
            {
               DrawRay(Prefix + "BSL_" + dayId, sessEndTime, sessHigh, time[0], sessHigh, InpBSLColor, STYLE_DASH);
               DrawRay(Prefix + "SSL_" + dayId, sessEndTime, sessLow, time[0], sessLow, InpSSLColor, STYLE_DASH);
               DrawRay(Prefix + "EQ_"  + dayId, sessEndTime, sessEQ, time[0], sessEQ, InpEQColor, STYLE_DOT);
            }
         }

         // Trigger sweeps only after session close
         if(!inSessNow && sessHigh > 0 && sessLow < 99999.0)
         {
            // Bearish Rejection: High breached, closed back inside
            if(high[i] > sessHigh && close[i] < sessHigh && open[i] < sessHigh)
            {
               ShortSignalBuffer[i] = high[i] + (5 * Point);
               if(i == 1 && InpEnableAlerts && LastAlertBar != time[1])
               {
                  TriggerAlert("AUDNZD: BSL Swept & Rejected. Short Signal Formed.");
                  LastAlertBar = time[1];
               }
            }

            // Bullish Rejection: Low breached, closed back inside
            if(low[i] < sessLow && close[i] > sessLow && open[i] > sessLow)
            {
               LongSignalBuffer[i] = low[i] - (5 * Point);
               if(i == 1 && InpEnableAlerts && LastAlertBar != time[1])
               {
                  TriggerAlert("AUDNZD: SSL Swept & Rejected. Long Signal Formed.");
                  LastAlertBar = time[1];
               }
            }
         }
      }
   }

   if(InpShowHUD)
      RenderHUD();

   return(rates_total);
}

//+------------------------------------------------------------------+
//| Line Drawing Helper                                              |
//+------------------------------------------------------------------+
void DrawRay(string name, datetime t1, double p1, datetime t2, double p2, color clr, int style)
{
   if(ObjectFind(0, name) < 0)
   {
      ObjectCreate(0, name, OBJ_TREND, 0, t1, p1, t2, p2);
      ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, name, OBJPROP_STYLE, style);
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
      ObjectSetInteger(0, name, OBJPROP_RAY, false);
      ObjectSetInteger(0, name, OBJPROP_BACK, true);
   }
   else
   {
      ObjectSetInteger(0, name, OBJPROP_TIME2, t2);
      ObjectSetDouble(0, name, OBJPROP_PRICE2, p2);
   }
}

//+------------------------------------------------------------------+
//| Alert Handler                                                    |
//+------------------------------------------------------------------+
void TriggerAlert(string text)
{
   Alert(text);
   if(InpSendPush)
      SendNotification(text);
}

//+------------------------------------------------------------------+
//| On-Chart HUD Display                                             |
//+------------------------------------------------------------------+
void RenderHUD()
{
   double adr14 = 0;
   for(int d = 1; d <= 14; d++)
      adr14 += (iHigh(NULL, PERIOD_D1, d) - iLow(NULL, PERIOD_D1, d));
   adr14 /= 14.0;

   double adrPips = adr14 / PipPoint;

   string hudText = "\n  [ AUDNZD ASIAN MATRIX ]" +
                    "\n  -----------------------------------" +
                    "\n  14-Day ADR       : " + DoubleToString(adrPips, 1) + " pips" +
                    "\n  Broker Server    : " + TimeToString(TimeCurrent(), TIME_SECONDS) +
                    "\n  Session Window   : " + InpAsianSession +
                    "\n  -----------------------------------";

   Comment(hudText);
}
FTtrader
Posts: 1008
Joined: Mon Aug 03, 2026 2:43 pm

Re: Scalping AUDNZD: A Cross Driven by Relative Rates and Quiet Ranges

Post by FTtrader »

MQL5 Implementation

MQL5 processes rates using structure arrays, provides native drawing buffers without legacy leaks, and includes high-performance direct chart object rendering.

Save as AUDNZD_Asian_Liquidity_Pro.mq5 in your MQL5/Indicators/ directory:

Code: Select all

//+------------------------------------------------------------------+
//|                                AUDNZD_Asian_Liquidity_Pro.mq5   |
//|                    Asian Range & Liquidity Rejection Matrix      |
//+------------------------------------------------------------------+
#property copyright "Forex Scalping Suite"
#property link      ""
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots   2

//--- Plot Definitions
#property indicator_label1  "Short Rejection"
#property indicator_type1   DRAW_ARROW
#property indicator_color1  clrCrimson
#property indicator_width1  2

#property indicator_label2  "Long Rejection"
#property indicator_type2   DRAW_ARROW
#property indicator_color2  clrMediumSeaGreen
#property indicator_width2  2

//--- Inputs
input string   InpAsianSession   = "22:00-06:00"; // Asian Session (Broker Server Time)
input color    InpBoxColor       = C'22,35,55';   // Range Box Fill Color
input color    InpBSLColor       = clrCrimson;    // Buy-Side Liquidity (High)
input color    InpSSLColor       = clrMediumSeaGreen; // Sell-Side Liquidity (Low)
input color    InpEQColor        = clrDarkGray;   // Equilibrium (50%)
input bool     InpEnableAlerts   = true;          // Enable Pop-up & Sound Alerts
input bool     InpSendPush       = false;         // Send Mobile Push Alerts
input bool     InpShowHUD        = true;          // Show Range HUD

//--- Buffers
double ShortBuffer[];
double LongBuffer[];

//--- Global Tracking
string   Prefix = "AZN5_";
int      StartHour, StartMin, EndHour, EndMin;
double   PipPoint;
datetime LastAlertTime = 0;

//+------------------------------------------------------------------+
//| Indicator Initialization                                         |
//+------------------------------------------------------------------+
int OnInit()
{
   SetIndexBuffer(0, ShortBuffer, INDICATOR_DATA);
   PlotIndexSetInteger(0, PLOT_ARROW, 234); // Down arrow
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);

   SetIndexBuffer(1, LongBuffer, INDICATOR_DATA);
   PlotIndexSetInteger(1, PLOT_ARROW, 233); // Up arrow
   PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);

   PipPoint = (_Digits == 3 || _Digits == 5) ? _Point * 10.0 : _Point;

   // Parse "HH:MM-HH:MM"
   string parts[];
   if(StringSplit(InpAsianSession, '-', parts) == 2)
   {
      string startParts[], endParts[];
      StringSplit(parts[0], ':', startParts);
      StringSplit(parts[1], ':', endParts);
      StartHour = (int)StringToInteger(startParts[0]);
      StartMin  = (ArraySize(startParts) > 1) ? (int)StringToInteger(startParts[1]) : 0;
      EndHour   = (int)StringToInteger(endParts[0]);
      EndMin    = (ArraySize(endParts) > 1) ? (int)StringToInteger(endParts[1]) : 0;
   }

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Deinitialization                                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   ObjectsDeleteAll(0, Prefix);
   Comment("");
}

//+------------------------------------------------------------------+
//| Session Time Evaluator                                           |
//+------------------------------------------------------------------+
bool IsInSession(datetime t)
{
   MqlDateTime dt;
   TimeToStruct(t, dt);
   int barMinutes = dt.hour * 60 + dt.min;
   int startMinutes = StartHour * 60 + StartMin;
   int endMinutes = EndHour * 60 + EndMin;

   if(startMinutes <= endMinutes)
      return (barMinutes >= startMinutes && barMinutes < endMinutes);
   else
      return (barMinutes >= startMinutes || barMinutes < endMinutes);
}

//+------------------------------------------------------------------+
//| Indicator Calculation Core                                       |
//+------------------------------------------------------------------+
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 < 50) return(0);

   ArraySetAsSeries(time, true);
   ArraySetAsSeries(open, true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low, true);
   ArraySetAsSeries(close, true);
   ArraySetAsSeries(ShortBuffer, true);
   ArraySetAsSeries(LongBuffer, true);

   int limit = rates_total - prev_calculated;
   if(limit <= 0) limit = 1;

   for(int i = limit; i >= 0; i--)
   {
      ShortBuffer[i] = EMPTY_VALUE;
      LongBuffer[i]  = EMPTY_VALUE;

      bool inSessNow = IsInSession(time[i]);

      // Find session bounds
      datetime sessStart = 0;
      datetime sessEnd   = 0;
      double   sessH     = -1.0;
      double   sessL     = 999999.0;

      int idx = i;
      while(idx < rates_total - 1 && !IsInSession(time[idx]))
         idx++;

      if(IsInSession(time[idx]))
      {
         sessEnd = time[idx];
         while(idx < rates_total - 1 && IsInSession(time[idx]))
         {
            sessH = MathMax(sessH, high[idx]);
            sessL = MathMin(sessL, low[idx]);
            sessStart = time[idx];
            idx++;
         }

         double sessEQ = (sessH + sessL) / 2.0;
         string dayTag = TimeToString(sessStart, TIME_DATE);

         // Draw Box & Rays on the current/latest day
         if(i == 0)
         {
            string boxName = Prefix + "BOX_" + dayTag;
            if(ObjectFind(0, boxName) < 0)
            {
               ObjectCreate(0, boxName, OBJ_RECTANGLE, 0, sessStart, sessH, sessEnd, sessL);
               ObjectSetInteger(0, boxName, OBJPROP_COLOR, InpBoxColor);
               ObjectSetInteger(0, boxName, OBJPROP_BGCOLOR, InpBoxColor);
               ObjectSetInteger(0, boxName, OBJPROP_FILL, true);
               ObjectSetInteger(0, boxName, OBJPROP_BACK, true);
            }
            else
            {
               ObjectSetDouble(0, boxName, OBJPROP_PRICE1, sessH);
               ObjectSetDouble(0, boxName, OBJPROP_PRICE2, sessL);
               ObjectSetInteger(0, boxName, OBJPROP_TIME2, sessEnd);
            }

            if(!inSessNow)
            {
               UpdateRay(Prefix + "BSL_" + dayTag, sessEnd, sessH, time[0], sessH, InpBSLColor, STYLE_DASH);
               UpdateRay(Prefix + "SSL_" + dayTag, sessEnd, sessL, time[0], sessL, InpSSLColor, STYLE_DASH);
               UpdateRay(Prefix + "EQ_"  + dayTag, sessEnd, sessEQ, time[0], sessEQ, InpEQColor, STYLE_DOT);
            }
         }

         // Sweeps Outside Asian Session
         if(!inSessNow && sessH > 0 && sessL < 999999.0)
         {
            // Bearish Liquidity Rejection
            if(high[i] > sessH && close[i] < sessH && open[i] < sessH)
            {
               ShortBuffer[i] = high[i] + (8 * _Point);
               if(i == 1 && InpEnableAlerts && LastAlertTime != time[1])
               {
                  FireAlert("AUDNZD: BSL Liquidity Swept. Short Rejection Confirmed.");
                  LastAlertTime = time[1];
               }
            }

            // Bullish Liquidity Rejection
            if(low[i] < sessL && close[i] > sessL && open[i] > sessL)
            {
               LongBuffer[i] = low[i] - (8 * _Point);
               if(i == 1 && InpEnableAlerts && LastAlertTime != time[1])
               {
                  FireAlert("AUDNZD: SSL Liquidity Swept. Long Rejection Confirmed.");
                  LastAlertTime = time[1];
               }
            }
         }
      }
   }

   if(InpShowHUD)
      DisplayHUD();

   return(rates_total);
}

//+------------------------------------------------------------------+
//| Ray Creation and Update                                          |
//+------------------------------------------------------------------+
void UpdateRay(string name, datetime t1, double p1, datetime t2, double p2, color clr, ENUM_LINE_STYLE style)
{
   if(ObjectFind(0, name) < 0)
   {
      ObjectCreate(0, name, OBJ_TREND, 0, t1, p1, t2, p2);
      ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, name, OBJPROP_STYLE, style);
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
      ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, false);
      ObjectSetInteger(0, name, OBJPROP_BACK, true);
   }
   else
   {
      ObjectSetInteger(0, name, OBJPROP_TIME2, t2);
      ObjectSetDouble(0, name, OBJPROP_PRICE2, p2);
   }
}

//+------------------------------------------------------------------+
//| Notification Handler                                             |
//+------------------------------------------------------------------+
void FireAlert(string msg)
{
   Alert(msg);
   if(InpSendPush)
      SendNotification(msg);
}

//+------------------------------------------------------------------+
//| On-Chart HUD                                                     |
//+------------------------------------------------------------------+
void DisplayHUD()
{
   MqlRates dailyRates[];
   ArraySetAsSeries(dailyRates, true);
   int copied = CopyRates(_Symbol, PERIOD_D1, 1, 14, dailyRates);

   double adrPips = 0.0;
   if(copied > 0)
   {
      double sum = 0.0;
      for(int i = 0; i < copied; i++)
         sum += (dailyRates[i].high - dailyRates[i].low);
      adrPips = (sum / copied) / PipPoint;
   }

   string hud = "\n  [ AUDNZD ASIAN MATRIX MQL5 ]" +
                "\n  --------------------------------------" +
                "\n  14-Day ADR       : " + DoubleToString(adrPips, 1) + " pips" +
                "\n  Server Time      : " + TimeToString(TimeCurrent(), TIME_SECONDS) +
                "\n  Session Bounds   : " + InpAsianSession +
                "\n  --------------------------------------";

   Comment(hud);
}
FTtrader
Posts: 1008
Joined: Mon Aug 03, 2026 2:43 pm

Re: Scalping AUDNZD: A Cross Driven by Relative Rates and Quiet Ranges

Post by FTtrader »

Key Operational Notes for MT4 & MT5

Broker Server Time Alignment: MetaTrader runs strictly on your broker's server time (most commonly UTC+2 during winter and UTC+3 during US daylight saving time). Adjust the InpAsianSession input string (default "22:00-06:00") to match your broker's platform clock for the Sydney/Tokyo liquidity block.

Execution Strategy: The signals fire on bar close (i = 1), confirming the candlestick breached the high/low and closed back inside the range before printing an alert or arrow. This eliminates intrabar repainting. Use the middle dotted gray line (Equilibrium / 50%) as your primary Take Profit 1 target.
FTtrader
Posts: 1008
Joined: Mon Aug 03, 2026 2:43 pm

Re: Scalping AUDNZD: A Cross Driven by Relative Rates and Quiet Ranges

Post by FTtrader »

Buffer Integration with EA Bots: The signal arrows are exposed directly via indicator buffers (Buffer 0 for Short, Buffer 1 for Long). You can reference them via iCustom() in an automated Expert Advisor to execute bracket orders automatically when a rejection closes:

Code: Select all

// Reading signals in an EA
double shortSig = iCustom(Symbol(), PERIOD_M15, "AUDNZD_Asian_Liquidity_Pro", ..., 0, 1);
double longSig  = iCustom(Symbol(), PERIOD_M15, "AUDNZD_Asian_Liquidity_Pro", ..., 1, 1);
FTtrader
Posts: 1008
Joined: Mon Aug 03, 2026 2:43 pm

Re: Scalping AUDNZD: A Cross Driven by Relative Rates and Quiet Ranges

Post by FTtrader »

Here are the institutional-grade MQL5 and MQL4 implementations.

This upgraded architecture moves away from naive high/low touches by introducing four professional trading filters:

Minimum Sweep Depth & Rejection Anatomy: Verifies that price didn't just tick past the level by 0.1 pip; it requires an intentional liquidity sweep ($X$ pips past the level) and validates candle anatomy (the rejection wick must comprise at least 35% of the total bar range).

Volatility & Range Expansion Gates: Suppresses trades if the Asian session was too tight (where broker spreads destroy expectancy) or blown out by overnight central bank releases (> 1.5x ADR, indicating trend continuation rather than mean reversion).

Spread-to-Target Friction Guard: Calculates the real-time spread as a percentage of the distance to the 50% Equilibrium target. If transaction costs consume more than 20% of the potential gain, the setup is invalidated.

EA-Ready Multi-Buffer Architecture: Exposes Entry, Stop Loss (wick tip + spread buffer), TP1 (50% Equilibrium), and TP2 (Opposite Range Extreme) across 5 separate indicator buffers for automated trading via iCustom().
FTtrader
Posts: 1008
Joined: Mon Aug 03, 2026 2:43 pm

Re: Scalping AUDNZD: A Cross Driven by Relative Rates and Quiet Ranges

Post by FTtrader »

MQL5 Institutional Version

Save as AUDNZD_Asian_Matrix_Pro.mq5 in MQL5/Indicators/.

Code: Select all

//+------------------------------------------------------------------+
//|                                     AUDNZD_Asian_Matrix_Pro.mq5  |
//|                 Institutional Liquidity & Volatility Engine      |
//+------------------------------------------------------------------+
#property copyright "Institutional Microstructure Engine"
#property link      ""
#property version   "2.00"
#property indicator_chart_window
#property indicator_buffers 5
#property indicator_plots   2

#property indicator_label1  "Short Sweep Entry"
#property indicator_type1   DRAW_ARROW
#property indicator_color1  clrCrimson
#property indicator_width1  2

#property indicator_label2  "Long Sweep Entry"
#property indicator_type2   DRAW_ARROW
#property indicator_color2  clrMediumSeaGreen
#property indicator_width2  2

//--- Inputs
input group "=== Session Parameters ==="
input string   InpSessionTime       = "22:00-06:00"; // Asian Range (Server Time)
input int      InpAdrPeriod         = 14;            // ADR Baseline Period (Days)

input group "=== Microstructure & Sweep Filters ==="
input double   InpMinSweepPips      = 1.5;           // Minimum Liquidity Penetration (Pips)
input double   InpMinWickPercent    = 35.0;          // Min Rejection Wick (% of Bar)
input double   InpMinRangePips      = 15.0;          // Min Asian Range Size (Pips)
input double   InpMaxAdrPercent     = 85.0;          // Max Asian Range (% of ADR)
input double   InpMaxSpreadCostPct  = 20.0;          // Max Spread as % of Distance to EQ

input group "=== Visuals & Levels ==="
input color    InpBoxColor          = C'16,26,42';   // Session Box Color
input color    InpBSLColor          = clrCrimson;    // BSL Level Color
input color    InpSSLColor          = clrMediumSeaGreen; // SSL Level Color
input color    InpEQColor           = clrSlateGray;  // Equilibrium (50%) Color
input bool     InpDrawProjections   = true;          // Extend Target Rays

input group "=== Alert Notification ==="
input bool     InpPopupAlerts       = true;          // Pop-up Alert
input bool     InpPushAlerts        = false;         // Mobile Push Notification

//--- Buffers (Exposed to EAs)
double BufferShortEntry[]; // Index 0
double BufferLongEntry[];  // Index 1
double BufferStopLoss[];   // Index 2 (Calculated SL level)
double BufferTP1[];        // Index 3 (Equilibrium level)
double BufferTP2[];        // Index 4 (Opposite Bound)

//--- Engine Globals
string   Prefix = "AZN_PRO_";
int      StartHour, StartMin, EndHour, EndMin;
double   PipMultiplier;
datetime LastAlertTime = 0;

//+------------------------------------------------------------------+
//| Initialization                                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   SetIndexBuffer(0, BufferShortEntry, INDICATOR_DATA);
   PlotIndexSetInteger(0, PLOT_ARROW, 234);
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);

   SetIndexBuffer(1, BufferLongEntry, INDICATOR_DATA);
   PlotIndexSetInteger(1, PLOT_ARROW, 233);
   PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);

   SetIndexBuffer(2, BufferStopLoss, INDICATOR_CALCULATIONS);
   SetIndexBuffer(3, BufferTP1, INDICATOR_CALCULATIONS);
   SetIndexBuffer(4, BufferTP2, INDICATOR_CALCULATIONS);

   PipMultiplier = (_Digits == 3 || _Digits == 5) ? _Point * 10.0 : _Point;

   string parts[];
   if(StringSplit(InpSessionTime, '-', parts) == 2)
   {
      string startParts[], endParts[];
      StringSplit(parts[0], ':', startParts);
      StringSplit(parts[1], ':', endParts);
      StartHour = (int)StringToInteger(startParts[0]);
      StartMin  = (ArraySize(startParts) > 1) ? (int)StringToInteger(startParts[1]) : 0;
      EndHour   = (int)StringToInteger(endParts[0]);
      EndMin    = (ArraySize(endParts) > 1) ? (int)StringToInteger(endParts[1]) : 0;
   }

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Deinitialization                                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   ObjectsDeleteAll(0, Prefix);
   ChartRedraw(0);
}

//+------------------------------------------------------------------+
//| Session Time Check                                               |
//+------------------------------------------------------------------+
bool IsInSession(datetime t)
{
   MqlDateTime dt;
   TimeToStruct(t, dt);
   int barM = dt.hour * 60 + dt.min;
   int sM   = StartHour * 60 + StartMin;
   int eM   = EndHour * 60 + EndMin;

   return (sM <= eM) ? (barM >= sM && barM < eM) : (barM >= sM || barM < eM);
}

//+------------------------------------------------------------------+
//| Calculation Engine                                               |
//+------------------------------------------------------------------+
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 < 100) return(0);

   ArraySetAsSeries(time, true);
   ArraySetAsSeries(open, true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low, true);
   ArraySetAsSeries(close, true);
   ArraySetAsSeries(BufferShortEntry, true);
   ArraySetAsSeries(BufferLongEntry, true);
   ArraySetAsSeries(BufferStopLoss, true);
   ArraySetAsSeries(BufferTP1, true);
   ArraySetAsSeries(BufferTP2, true);

   // Get Daily ADR
   MqlRates dailyRates[];
   ArraySetAsSeries(dailyRates, true);
   int copied = CopyRates(_Symbol, PERIOD_D1, 1, InpAdrPeriod, dailyRates);
   double adrPips = 50.0; // fallback baseline
   if(copied > 0)
   {
      double sum = 0.0;
      for(int d = 0; d < copied; d++)
         sum += (dailyRates[d].high - dailyRates[d].low);
      adrPips = (sum / copied) / PipMultiplier;
   }

   int limit = rates_total - prev_calculated;
   if(limit <= 0) limit = 1;

   for(int i = limit; i >= 0; i--)
   {
      BufferShortEntry[i] = EMPTY_VALUE;
      BufferLongEntry[i]  = EMPTY_VALUE;
      BufferStopLoss[i]   = EMPTY_VALUE;
      BufferTP1[i]        = EMPTY_VALUE;
      BufferTP2[i]        = EMPTY_VALUE;

      bool inSess = IsInSession(time[i]);

      // Locate enclosing/previous Asian session
      int idx = i;
      while(idx < rates_total - 1 && !IsInSession(time[idx])) idx++;

      if(IsInSession(time[idx]))
      {
         datetime sessEnd = time[idx];
         double sessH = -1.0;
         double sessL = 999999.0;
         datetime sessStart = time[idx];

         while(idx < rates_total - 1 && IsInSession(time[idx]))
         {
            sessH = MathMax(sessH, high[idx]);
            sessL = MathMin(sessL, low[idx]);
            sessStart = time[idx];
            idx++;
         }

         double rangePips = (sessH - sessL) / PipMultiplier;
         double sessEQ    = (sessH + sessL) / 2.0;
         string dayId     = TimeToString(sessStart, TIME_DATE);

         // Render UI Elements for current day
         if(i == 0)
         {
            RenderSessionGeometry(dayId, sessStart, sessEnd, sessH, sessL, sessEQ, inSess, time[0]);
            RenderHUD(rangePips, adrPips, sessEQ, sessH, sessL);
         }

         // Trigger Evaluation: Only outside the Asian session
         if(!inSess && sessH > 0 && sessL < 999999.0)
         {
            // Gate 1: Volatility checks
            bool validRange = (rangePips >= InpMinRangePips) && 
                              (rangePips <= (adrPips * (InpMaxAdrPercent / 100.0)));

            if(validRange)
            {
               double candleRange = high[i] - low[i];
               if(candleRange <= 0) continue;

               double currentSpreadPips = (double)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * _Point / PipMultiplier;

               // --- SELL SETUP: Liquidity Sweep of Asian High ---
               double upperWick = high[i] - MathMax(open[i], close[i]);
               double upperWickPct = (upperWick / candleRange) * 100.0;
               double sweepPipsAbove = (high[i] - sessH) / PipMultiplier;

               if(sweepPipsAbove >= InpMinSweepPips && close[i] < sessH && upperWickPct >= InpMinWickPercent)
               {
                  double distToEQ = (close[i] - sessEQ) / PipMultiplier;
                  double spreadCostRatio = (distToEQ > 0) ? (currentSpreadPips / distToEQ) * 100.0 : 100.0;

                  if(spreadCostRatio <= InpMaxSpreadCostPct)
                  {
                     BufferShortEntry[i] = high[i] + (5 * _Point);
                     BufferStopLoss[i]   = high[i] + (2 * PipMultiplier); // Wick + 2 pip buffer
                     BufferTP1[i]        = sessEQ;
                     BufferTP2[i]        = sessL;

                     if(i == 1 && LastAlertTime != time[1])
                     {
                        SendExecutionAlert("SELL", high[i], BufferStopLoss[i], sessEQ, rangePips);
                        LastAlertTime = time[1];
                     }
                  }
               }

               // --- BUY SETUP: Liquidity Sweep of Asian Low ---
               double lowerWick = MathMin(open[i], close[i]) - low[i];
               double lowerWickPct = (lowerWick / candleRange) * 100.0;
               double sweepPipsBelow = (sessL - low[i]) / PipMultiplier;

               if(sweepPipsBelow >= InpMinSweepPips && close[i] > sessL && lowerWickPct >= InpMinWickPercent)
               {
                  double distToEQ = (sessEQ - close[i]) / PipMultiplier;
                  double spreadCostRatio = (distToEQ > 0) ? (currentSpreadPips / distToEQ) * 100.0 : 100.0;

                  if(spreadCostRatio <= InpMaxSpreadCostPct)
                  {
                     BufferLongEntry[i] = low[i] - (5 * _Point);
                     BufferStopLoss[i]  = low[i] - (2 * PipMultiplier);
                     BufferTP1[i]       = sessEQ;
                     BufferTP2[i]       = sessH;

                     if(i == 1 && LastAlertTime != time[1])
                     {
                        SendExecutionAlert("BUY", low[i], BufferStopLoss[i], sessEQ, rangePips);
                        LastAlertTime = time[1];
                     }
                  }
               }
            }
         }
      }
   }

   return(rates_total);
}

//+------------------------------------------------------------------+
//| On-Chart HUD Implementation                                      |
//+------------------------------------------------------------------+
void RenderHUD(double rangePips, double adrPips, double eq, double high, double low)
{
   double spreadPips = (double)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * _Point / PipMultiplier;
   double rangeAdrPct = (adrPips > 0) ? (rangePips / adrPips) * 100.0 : 0.0;

   CreateLabel(Prefix + "HUD_BG", 15, 25, " ", clrWhite, 9, C'10,16,26', 220, 105);
   CreateLabel(Prefix + "HUD_T1", 25, 32, "AUDNZD LIQUIDITY DESK", clrGold, 9, clrNONE);
   CreateLabel(Prefix + "HUD_T2", 25, 50, StringFormat("Asian Range : %.1f p (%.0f%% ADR)", rangePips, rangeAdrPct), clrWhite, 8, clrNONE);
   CreateLabel(Prefix + "HUD_T3", 25, 66, StringFormat("14D ADR     : %.1f pips", adrPips), clrSilver, 8, clrNONE);
   CreateLabel(Prefix + "HUD_T4", 25, 82, StringFormat("Live Spread : %.1f pips", spreadPips), (spreadPips > 1.5 ? clrTomato : clrLimeGreen), 8, clrNONE);
   CreateLabel(Prefix + "HUD_T5", 25, 98, StringFormat("Equilibrium : %.5f", eq), clrLightSkyBlue, 8, clrNONE);
}

void CreateLabel(string name, int x, int y, string text, color clr, int fontSize, color bg, int w = 0, int h = 0)
{
   if(bg != clrNONE)
   {
      if(ObjectFind(0, name) < 0)
      {
         ObjectCreate(0, name, OBJ_RECTANGLE_LABEL, 0, 0, 0);
         ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
         ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
         ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
         ObjectSetInteger(0, name, OBJPROP_XSIZE, w);
         ObjectSetInteger(0, name, OBJPROP_YSIZE, h);
         ObjectSetInteger(0, name, OBJPROP_BGCOLOR, bg);
         ObjectSetInteger(0, name, OBJPROP_BORDER_TYPE, BORDER_FLAT);
         ObjectSetInteger(0, name, OBJPROP_COLOR, C'35,45,60');
      }
   }
   else
   {
      if(ObjectFind(0, name) < 0)
      {
         ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
         ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
         ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
         ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
      }
      ObjectSetString(0, name, OBJPROP_TEXT, text);
      ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontSize);
      ObjectSetString(0, name, OBJPROP_FONT, "Lucida Console");
   }
}

//+------------------------------------------------------------------+
//| Chart Projections Helper                                         |
//+------------------------------------------------------------------+
void RenderSessionGeometry(string dayId, datetime sStart, datetime sEnd, double sH, double sL, double eq, bool inSess, datetime currT)
{
   string boxName = Prefix + "BOX_" + dayId;
   if(ObjectFind(0, boxName) < 0)
   {
      ObjectCreate(0, boxName, OBJ_RECTANGLE, 0, sStart, sH, sEnd, sL);
      ObjectSetInteger(0, boxName, OBJPROP_COLOR, InpBoxColor);
      ObjectSetInteger(0, boxName, OBJPROP_BGCOLOR, InpBoxColor);
      ObjectSetInteger(0, boxName, OBJPROP_FILL, true);
      ObjectSetInteger(0, boxName, OBJPROP_BACK, true);
   }
   else
   {
      ObjectSetDouble(0, boxName, OBJPROP_PRICE1, sH);
      ObjectSetDouble(0, boxName, OBJPROP_PRICE2, sL);
      ObjectSetInteger(0, boxName, OBJPROP_TIME2, sEnd);
   }

   if(InpDrawProjections && !inSess)
   {
      SetRay(Prefix + "BSL_" + dayId, sEnd, sH, currT, sH, InpBSLColor, STYLE_DASH);
      SetRay(Prefix + "SSL_" + dayId, sEnd, sL, currT, sL, InpSSLColor, STYLE_DASH);
      SetRay(Prefix + "EQ_"  + dayId, sEnd, eq, currT, eq, InpEQColor, STYLE_DOT);
   }
}

void SetRay(string name, datetime t1, double p1, datetime t2, double p2, color clr, ENUM_LINE_STYLE style)
{
   if(ObjectFind(0, name) < 0)
   {
      ObjectCreate(0, name, OBJ_TREND, 0, t1, p1, t2, p2);
      ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, name, OBJPROP_STYLE, style);
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
      ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, false);
      ObjectSetInteger(0, name, OBJPROP_BACK, true);
   }
   else
   {
      ObjectSetInteger(0, name, OBJPROP_TIME2, t2);
      ObjectSetDouble(0, name, OBJPROP_PRICE2, p2);
   }
}

//+------------------------------------------------------------------+
//| Standardized Alert Payload                                       |
//+------------------------------------------------------------------+
void SendExecutionAlert(string dir, double sweepLevel, double sl, double tp1, double rangePips)
{
   string payload = StringFormat("[AUDNZD MATRIX] %s Trigger\nLevel: %.5f\nSL: %.5f\nTP1 (EQ): %.5f\nRange: %.1f pips", 
                                 dir, sweepLevel, sl, tp1, rangePips);
   if(InpPopupAlerts) Alert(payload);
   if(InpPushAlerts)  SendNotification(payload);
}
FTtrader
Posts: 1008
Joined: Mon Aug 03, 2026 2:43 pm

Re: Scalping AUDNZD: A Cross Driven by Relative Rates and Quiet Ranges

Post by FTtrader »

MQL4 Institutional Version

Save as AUDNZD_Asian_Matrix_Pro.mq4 in MQL4/Indicators/.

Code: Select all

//+------------------------------------------------------------------+
//|                                     AUDNZD_Asian_Matrix_Pro.mq4  |
//|                 Institutional Liquidity & Volatility Engine      |
//+------------------------------------------------------------------+
#property copyright "Institutional Microstructure Engine"
#property link      ""
#property version   "2.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 5
#property indicator_color1  clrCrimson
#property indicator_color2  clrMediumSeaGreen
#property indicator_width1  2
#property indicator_width2  2

//--- Inputs
input string   InpSessionTime       = "22:00-06:00"; // Asian Range (Server Time)
input int      InpAdrPeriod         = 14;            // ADR Period (Days)
input double   InpMinSweepPips      = 1.5;           // Min Liquidity Sweep (Pips)
input double   InpMinWickPercent    = 35.0;          // Min Rejection Wick (% of Bar)
input double   InpMinRangePips      = 15.0;          // Min Session Range (Pips)
input double   InpMaxAdrPercent     = 85.0;          // Max Session Range (% of ADR)
input double   InpMaxSpreadCostPct  = 20.0;          // Max Spread as % of Distance to EQ

input color    InpBoxColor          = C'16,26,42';   // Session Box Color
input color    InpBSLColor          = clrCrimson;    // BSL Level Color
input color    InpSSLColor          = clrMediumSeaGreen; // SSL Level Color
input color    InpEQColor           = clrSlateGray;  // Equilibrium (50%) Color

input bool     InpPopupAlerts       = true;          // Pop-up Alert
input bool     InpPushAlerts        = false;         // Mobile Push Notification

//--- Buffers
double BufferShortEntry[]; // 0
double BufferLongEntry[];  // 1
double BufferStopLoss[];   // 2
double BufferTP1[];        // 3
double BufferTP2[];        // 4

string   Prefix = "AZN4_";
int      StartHour, StartMin, EndHour, EndMin;
double   PipMultiplier;
datetime LastAlertTime = 0;

//+------------------------------------------------------------------+
//| Init                                                             |
//+------------------------------------------------------------------+
int OnInit()
{
   IndicatorBuffers(5);
   SetIndexBuffer(0, BufferShortEntry);
   SetIndexStyle(0, DRAW_ARROW);
   SetIndexArrow(0, 234);

   SetIndexBuffer(1, BufferLongEntry);
   SetIndexStyle(1, DRAW_ARROW);
   SetIndexArrow(1, 233);

   SetIndexBuffer(2, BufferStopLoss);
   SetIndexBuffer(3, BufferTP1);
   SetIndexBuffer(4, BufferTP2);

   PipMultiplier = (Digits == 3 || Digits == 5) ? Point * 10.0 : Point;

   string parts[];
   if(StringSplit(InpSessionTime, '-', parts) == 2)
   {
      string startParts[], endParts[];
      StringSplit(parts[0], ':', startParts);
      StringSplit(parts[1], ':', endParts);
      StartHour = (int)StringToInteger(startParts[0]);
      StartMin  = (ArraySize(startParts) > 1) ? (int)StringToInteger(startParts[1]) : 0;
      EndHour   = (int)StringToInteger(endParts[0]);
      EndMin    = (ArraySize(endParts) > 1) ? (int)StringToInteger(endParts[1]) : 0;
   }

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Deinit                                                           |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   ObjectsDeleteAll(0, Prefix);
}

//+------------------------------------------------------------------+
//| Session Filter                                                   |
//+------------------------------------------------------------------+
bool IsInSession(datetime t)
{
   MqlDateTime dt;
   TimeToStruct(t, dt);
   int barM = dt.hour * 60 + dt.min;
   int sM   = StartHour * 60 + StartMin;
   int eM   = EndHour * 60 + EndMin;

   return (sM <= eM) ? (barM >= sM && barM < eM) : (barM >= sM || barM < eM);
}

//+------------------------------------------------------------------+
//| Main Iteration                                                   |
//+------------------------------------------------------------------+
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 < 100) return(0);

   double adr14 = 0;
   for(int d = 1; d <= InpAdrPeriod; d++)
      adr14 += (iHigh(NULL, PERIOD_D1, d) - iLow(NULL, PERIOD_D1, d));
   double adrPips = (adr14 / InpAdrPeriod) / PipMultiplier;

   int limit = rates_total - prev_calculated;
   if(limit <= 0) limit = 1;

   for(int i = limit; i >= 0; i--)
   {
      BufferShortEntry[i] = EMPTY_VALUE;
      BufferLongEntry[i]  = EMPTY_VALUE;
      BufferStopLoss[i]   = EMPTY_VALUE;
      BufferTP1[i]        = EMPTY_VALUE;
      BufferTP2[i]        = EMPTY_VALUE;

      bool inSess = IsInSession(time[i]);

      int idx = i;
      while(idx < rates_total - 1 && !IsInSession(time[idx])) idx++;

      if(IsInSession(time[idx]))
      {
         datetime sEnd = time[idx];
         double sH = -1.0;
         double sL = 999999.0;
         datetime sStart = time[idx];

         while(idx < rates_total - 1 && IsInSession(time[idx]))
         {
            sH = MathMax(sH, high[idx]);
            sL = MathMin(sL, low[idx]);
            sStart = time[idx];
            idx++;
         }

         double rangePips = (sH - sL) / PipMultiplier;
         double eq = (sH + sL) / 2.0;
         string dayId = TimeToString(sStart, TIME_DATE);

         if(i == 0)
         {
            RenderGeometry(dayId, sStart, sEnd, sH, sL, eq, inSess, time[0]);
            RenderHUD(rangePips, adrPips, eq);
         }

         if(!inSess && sH > 0 && sL < 999999.0)
         {
            bool validRange = (rangePips >= InpMinRangePips) && 
                              (rangePips <= (adrPips * (InpMaxAdrPercent / 100.0)));

            if(validRange)
            {
               double candleRange = high[i] - low[i];
               if(candleRange <= 0) continue;

               double currentSpreadPips = (double)MarketInfo(_Symbol, MODE_SPREAD) * Point / PipMultiplier;

               // Bearish Liquidity Rejection
               double upperWick = high[i] - MathMax(open[i], close[i]);
               double upperWickPct = (upperWick / candleRange) * 100.0;
               double sweepAbove = (high[i] - sH) / PipMultiplier;

               if(sweepAbove >= InpMinSweepPips && close[i] < sH && upperWickPct >= InpMinWickPercent)
               {
                  double distToEQ = (close[i] - eq) / PipMultiplier;
                  double spreadRatio = (distToEQ > 0) ? (currentSpreadPips / distToEQ) * 100.0 : 100.0;

                  if(spreadRatio <= InpMaxSpreadCostPct)
                  {
                     BufferShortEntry[i] = high[i] + (5 * Point);
                     BufferStopLoss[i]   = high[i] + (2 * PipMultiplier);
                     BufferTP1[i]        = eq;
                     BufferTP2[i]        = sL;

                     if(i == 1 && LastAlertTime != time[1])
                     {
                        SendAlert("SELL", high[i], BufferStopLoss[i], eq, rangePips);
                        LastAlertTime = time[1];
                     }
                  }
               }

               // Bullish Liquidity Rejection
               double lowerWick = MathMin(open[i], close[i]) - low[i];
               double lowerWickPct = (lowerWick / candleRange) * 100.0;
               double sweepBelow = (sL - low[i]) / PipMultiplier;

               if(sweepBelow >= InpMinSweepPips && close[i] > sL && lowerWickPct >= InpMinWickPercent)
               {
                  double distToEQ = (eq - close[i]) / PipMultiplier;
                  double spreadRatio = (distToEQ > 0) ? (currentSpreadPips / distToEQ) * 100.0 : 100.0;

                  if(spreadRatio <= InpMaxSpreadCostPct)
                  {
                     BufferLongEntry[i] = low[i] - (5 * Point);
                     BufferStopLoss[i]  = low[i] - (2 * PipMultiplier);
                     BufferTP1[i]       = eq;
                     BufferTP2[i]       = sH;

                     if(i == 1 && LastAlertTime != time[1])
                     {
                        SendAlert("BUY", low[i], BufferStopLoss[i], eq, rangePips);
                        LastAlertTime = time[1];
                     }
                  }
               }
            }
         }
      }
   }

   return(rates_total);
}

//+------------------------------------------------------------------+
//| Drawing Helpers                                                  |
//+------------------------------------------------------------------+
void RenderGeometry(string dayId, datetime sStart, datetime sEnd, double sH, double sL, double eq, bool inSess, datetime currT)
{
   string boxName = Prefix + "BOX_" + dayId;
   if(ObjectFind(0, boxName) < 0)
   {
      ObjectCreate(0, boxName, OBJ_RECTANGLE, 0, sStart, sH, sEnd, sL);
      ObjectSetInteger(0, boxName, OBJPROP_COLOR, InpBoxColor);
      ObjectSetInteger(0, boxName, OBJPROP_BGCOLOR, InpBoxColor);
      ObjectSetInteger(0, boxName, OBJPROP_BACK, true);
   }
   else
   {
      ObjectSetDouble(0, boxName, OBJPROP_PRICE1, sH);
      ObjectSetDouble(0, boxName, OBJPROP_PRICE2, sL);
      ObjectSetInteger(0, boxName, OBJPROP_TIME2, sEnd);
   }

   if(!inSess)
   {
      SetRay(Prefix + "BSL_" + dayId, sEnd, sH, currT, sH, InpBSLColor, STYLE_DASH);
      SetRay(Prefix + "SSL_" + dayId, sEnd, sL, currT, sL, InpSSLColor, STYLE_DASH);
      SetRay(Prefix + "EQ_"  + dayId, sEnd, eq, currT, eq, InpEQColor, STYLE_DOT);
   }
}

void SetRay(string name, datetime t1, double p1, datetime t2, double p2, color clr, int style)
{
   if(ObjectFind(0, name) < 0)
   {
      ObjectCreate(0, name, OBJ_TREND, 0, t1, p1, t2, p2);
      ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, name, OBJPROP_STYLE, styleTo bring this system to an institutional and prop-firm standard, we eliminate manual session guesswork, filter out low-expectancy market noise, and structure the data so any automated execution engine (EA) can read entries, stops, and targets directly off the chart buffers.

### Core Institutional Upgrades

1. **Microstructure & Spread Gatekeeper:** The indicator continuously monitors real-time broker spread against both a hard limit (e.g., maximum 1.8 pips) and the session range. If the spread-to-range ratio erodes expected value, signals are suppressed.
2. **Tick Volume Absorption Filter:** A true institutional liquidity sweep requires absorption. The script verifies that the sweep candle’s tick volume exceeds its rolling volume moving average ($V > \text{SMA}(V, 20) \times \text{Multiplier}$), filtering out low-volume drift.
3. **Execution-Ready Buffer Matrix (5 Buffers):** Rather than just plotting visual arrows, the indicator exposes five discrete buffers via `iCustom()`:
   * **Buffer 0:** Short Signal Price
   * **Buffer 1:** Long Signal Price
   * **Buffer 2:** Calculated Stop Loss (Sweep extreme + buffer)
   * **Buffer 3:** Take Profit 1 (Range Equilibrium / 50%)
   * **Buffer 4:** Take Profit 2 (Opposite Session Extreme)
4. **Structured JSON Alert Payloads:** Alerts are formatted as JSON strings, allowing seamless passthrough to webhooks, Telegram, Discord, or external Python execution servers.

---

### MQL5 Production Implementation

Save as **`AUDNZD_Asian_Liquidity_Institutional.mq5`** in `MQL5/Indicators/`.

```mql5
//+------------------------------------------------------------------+
//|                        AUDNZD_Asian_Liquidity_Institutional.mq5  |
//|               Institutional Asian Range & Sweep Execution Engine |
//+------------------------------------------------------------------+
#property copyright   "Quant Architecture"
#property version     "2.00"
#property indicator_chart_window
#property indicator_buffers 5
#property indicator_plots   2

//--- Plot Declarations
#property indicator_label1  "Short Execution"
#property indicator_type1   DRAW_ARROW
#property indicator_color1  clrCrimson
#property indicator_width1  2

#property indicator_label2  "Long Execution"
#property indicator_type2   DRAW_ARROW
#property indicator_color2  clrMediumSeaGreen
#property indicator_width2  2

//--- Input Parameters: Session Timing
input group "=== Session Timing (Broker Server Time) ==="
input string   InpSessionWindow     = "22:00-06:00"; // Asian Accumulation Window
input int      InpTradeWindowEndHour= 17;            // Disable signals after London/NY close hour (Server Time)

//--- Input Parameters: Microstructure & Trade Quality Filters
input group "=== Institutional Quality Filters ==="
input double   InpMaxSpreadPips     = 2.0;           // Max Allowable Spread (Pips)
input double   InpMinRangePips      = 15.0;          // Min Asian Range Size (Pips)
input double   InpMaxRangePips      = 60.0;          // Max Asian Range Size (Avoid Overexpanded Days)
input bool     InpRequireVolSpike   = true;          // Require Tick Volume Spike on Sweep Bar
input double   InpVolMultiplier     = 1.15;          // Volume Multiplier vs SMA(20)
input double   InpSLBufferPips      = 2.5;           // Stop Loss Buffer Beyond Sweep Wick (Pips)

//--- Input Parameters: Visuals & Notifications
input group "=== UI & Telemetry ==="
input color    InpBoxColor          = C'15,25,40';   // Asian Range Shading
input color    InpBSLColor          = clrCrimson;    // Buy-Side Liquidity Ray
input color    InpSSLColor          = clrMediumSeaGreen; // Sell-Side Liquidity Ray
input color    InpEQColor           = clrDarkGray;   // Equilibrium Ray (50%)
input bool     InpEmitJsonAlerts    = true;          // Emit Structured JSON Webhook Alerts

//--- Output Buffers for EAs
double BufferShortSignal[];
double BufferLongSignal[];
double BufferCalculatedSL[];
double BufferTargetTP1[];
double BufferTargetTP2[];

//--- Internal State
string   Prefix = "ALI_";
int      StartH, StartM, EndH, EndM;
double   PipSize;
datetime LastProcessedBar = 0;

//+------------------------------------------------------------------+
//| Custom Indicator Initialization                                  |
//+------------------------------------------------------------------+
int OnInit()
{
   SetIndexBuffer(0, BufferShortSignal, INDICATOR_DATA);
   PlotIndexSetInteger(0, PLOT_ARROW, 234);
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);

   SetIndexBuffer(1, BufferLongSignal, INDICATOR_DATA);
   PlotIndexSetInteger(1, PLOT_ARROW, 233);
   PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);

   // EA Trade Management Buffers
   SetIndexBuffer(2, BufferCalculatedSL, INDICATOR_CALCULATIONS);
   SetIndexBuffer(3, BufferTargetTP1,    INDICATOR_CALCULATIONS);
   SetIndexBuffer(4, BufferTargetTP2,    INDICATOR_CALCULATIONS);

   PipSize = (_Digits == 3 || _Digits == 5) ? _Point * 10.0 : _Point;

   // Parse Session Range
   string parts[];
   if(StringSplit(InpSessionWindow, '-', parts) == 2)
   {
      string startParts[], endParts[];
      StringSplit(parts[0], ':', startParts);
      StringSplit(parts[1], ':', endParts);
      StartH = (int)StringToInteger(startParts[0]);
      StartM = (ArraySize(startParts) > 1) ? (int)StringToInteger(startParts[1]) : 0;
      EndH   = (int)StringToInteger(endParts[0]);
      EndM   = (ArraySize(endParts) > 1) ? (int)StringToInteger(endParts[1]) : 0;
   }

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom Indicator Deinitialization                                |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   ObjectsDeleteAll(0, Prefix);
   Comment("");
}

//+------------------------------------------------------------------+
//| Session Time Boundary Verifier                                   |
//+------------------------------------------------------------------+
bool IsInAccumulation(datetime t)
{
   MqlDateTime dt;
   TimeToStruct(t, dt);
   int barM = dt.hour * 60 + dt.min;
   int startM = StartH * 60 + StartM;
   int endM   = EndH * 60 + EndM;

   return (startM <= endM) ? (barM >= startM && barM < endM) : (barM >= startM || barM < endM);
}

//+------------------------------------------------------------------+
//| Execution Calculation Loop                                       |
//+------------------------------------------------------------------+
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 < 100) return(0);

   ArraySetAsSeries(time, true);
   ArraySetAsSeries(open, true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low, true);
   ArraySetAsSeries(close, true);
   ArraySetAsSeries(tick_volume, true);
   ArraySetAsSeries(spread, true);

   ArraySetAsSeries(BufferShortSignal, true);
   ArraySetAsSeries(BufferLongSignal, true);
   ArraySetAsSeries(BufferCalculatedSL, true);
   ArraySetAsSeries(BufferTargetTP1, true);
   ArraySetAsSeries(BufferTargetTP2, true);

   int limit = rates_total - prev_calculated;
   if(limit <= 0) limit = 1;

   for(int i = limit; i >= 0; i--)
   {
      BufferShortSignal[i] = EMPTY_VALUE;
      BufferLongSignal[i]  = EMPTY_VALUE;
      BufferCalculatedSL[i]= EMPTY_VALUE;
      BufferTargetTP1[i]   = EMPTY_VALUE;
      BufferTargetTP2[i]   = EMPTY_VALUE;

      bool inSession = IsInAccumulation(time[i]);

      // Scan backward to isolate the active/prior Asian session
      int cur = i;
      while(cur < rates_total - 1 && !IsInAccumulation(time[cur])) cur++;

      if(IsInAccumulation(time[cur]))
      {
         datetime sEnd = time[cur];
         double sHigh = -1.0;
         double sLow  = 999999.0;
         datetime sStart = 0;

         while(cur < rates_total - 1 && IsInAccumulation(time[cur]))
         {
            sHigh  = MathMax(sHigh, high[cur]);
            sLow   = MathMin(sLow, low[cur]);
            sStart = time[cur];
            cur++;
         }

         double sessionRangePips = (sHigh - sLow) / PipSize;
         double sessionEQ        = (sHigh + sLow) / 2.0;
         string dayKey           = TimeToString(sStart, TIME_DATE);

         // Render UI Elements for current day
         if(i == 0)
         {
            ManageChartGraphics(dayKey, sStart, sEnd, sHigh, sLow, sessionEQ, inSession, time[0]);
         }

         // Signal Detection: Closed candle during European/US hours
         MqlDateTime dt;
         TimeToStruct(time[i], dt);

         if(!inSession && dt.hour < InpTradeWindowEndHour && sessionRangePips >= InpMinRangePips && sessionRangePips <= InpMaxRangePips)
         {
            // Spread Filter
            double currentSpreadPips = (double)spread[i] * _Point / PipSize;
            if(currentSpreadPips <= InpMaxSpreadPips)
            {
               // Volume Spike Verification
               bool volumeConfirmed = true;
               if(InpRequireVolSpike)
               {
                  long avgVol = 0;
                  for(int v = 1; v <= 20; v++) avgVol += tick_volume[i + v];
                  avgVol /= 20;
                  volumeConfirmed = (tick_volume[i] >= (avgVol * InpVolMultiplier));
               }

               // Bearish Liquidity Rejection (BSL Swept)
               if(high[i] > sHigh && close[i] < sHigh && open[i] < sHigh && volumeConfirmed)
               {
                  BufferShortSignal[i] = high[i] + (5 * _Point);
                  BufferCalculatedSL[i]= high[i] + (InpSLBufferPips * PipSize);
                  BufferTargetTP1[i]   = sessionEQ;
                  BufferTargetTP2[i]   = sLow;

                  if(i == 1 && LastProcessedBar != time[1])
                  {
                     DispatchTelemetry("SELL", close[1], BufferCalculatedSL[1], BufferTargetTP1[1], BufferTargetTP2[1], currentSpreadPips);
                     LastProcessedBar = time[1];
                  }
               }

               // Bullish Liquidity Rejection (SSL Swept)
               if(low[i] < sLow && close[i] > sLow && open[i] > sLow && volumeConfirmed)
               {
                  BufferLongSignal[i]  = low[i] - (5 * _Point);
                  BufferCalculatedSL[i]= low[i] - (InpSLBufferPips * PipSize);
                  BufferTargetTP1[i]   = sessionEQ;
                  BufferTargetTP2[i]   = sHigh;

                  if(i == 1 && LastProcessedBar != time[1])
                  {
                     DispatchTelemetry("BUY", close[1], BufferCalculatedSL[1], BufferTargetTP1[1], BufferTargetTP2[1], currentSpreadPips);
                     LastProcessedBar = time[1];
                  }
               }
            }
         }
      }
   }

   RenderTelemetryDashboard();
   return(rates_total);
}

//+------------------------------------------------------------------+
//| Graphical Object Manager                                         |
//+------------------------------------------------------------------+
void ManageChartGraphics(string dayKey, datetime sStart, datetime sEnd, double sH, double sL, double sEQ, bool inSess, datetime curTime)
{
   string boxName = Prefix + "BOX_" + dayKey;
   if(ObjectFind(0, boxName) < 0)
   {
      ObjectCreate(0, boxName, OBJ_RECTANGLE, 0, sStart, sH, sEnd, sL);
      ObjectSetInteger(0, boxName, OBJPROP_COLOR, InpBoxColor);
      ObjectSetInteger(0, boxName, OBJPROP_BGCOLOR, InpBoxColor);
      ObjectSetInteger(0, boxName, OBJPROP_FILL, true);
      ObjectSetInteger(0, boxName, OBJPROP_BACK, true);
   }
   else
   {
      ObjectSetDouble(0, boxName, OBJPROP_PRICE1, sH);
      ObjectSetDouble(0, boxName, OBJPROP_PRICE2, sL);
      ObjectSetInteger(0, boxName, OBJPROP_TIME2, sEnd);
   }

   if(!inSess)
   {
      SyncRay(Prefix + "BSL_" + dayKey, sEnd, sH, curTime, sH, InpBSLColor, STYLE_DASH);
      SyncRay(Prefix + "SSL_" + dayKey, sEnd, sL, curTime, sL, InpSSLColor, STYLE_DASH);
      SyncRay(Prefix + "EQ_"  + dayKey, sEnd, sEQ, curTime, sEQ, InpEQColor, STYLE_DOT);
   }
}

void SyncRay(string name, datetime t1, double p1, datetime t2, double p2, color clr, ENUM_LINE_STYLE style)
{
   if(ObjectFind(0, name) < 0)
   {
      ObjectCreate(0, name, OBJ_TREND, 0, t1, p1, t2, p2);
      ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, name, OBJPROP_STYLE, style);
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
      ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, false);
      ObjectSetInteger(0, name, OBJPROP_BACK, true);
   }
   else
   {
      ObjectSetInteger(0, name, OBJPROP_TIME2, t2);
      ObjectSetDouble(0, name, OBJPROP_PRICE2, p2);
   }
}

//+------------------------------------------------------------------+
//| Telemetry & Webhook Dispatcher                                   |
//+------------------------------------------------------------------+
void DispatchTelemetry(string side, double entry, double sl, double tp1, double tp2, double sprd)
{
   if(!InpEmitJsonAlerts) return;

   string payload = StringFormat(
      "{\"event\":\"LIQUIDITY_SWEEP\",\"pair\":\"%s\",\"side\":\"%s\",\"entry\":%.5f,\"sl\":%.5f,\"tp1\":%.5f,\"tp2\":%.5f,\"spread\":%.1f,\"time\":\"%s\"}",
      _Symbol, side, entry, sl, tp1, tp2, sprd, TimeToString(TimeCurrent(), TIME_SECONDS)
   );

   Alert(payload);
}

//+------------------------------------------------------------------+
//| Heads-Up Execution Dashboard                                     |
//+------------------------------------------------------------------+
void RenderTelemetryDashboard()
{
   MqlRates daily[];
   ArraySetAsSeries(daily, true);
   int copied = CopyRates(_Symbol, PERIOD_D1, 1, 14, daily);

   double adrPips = 0.0;
   if(copied > 0)
   {
      double acc = 0;
      for(int i = 0; i < copied; i++) acc += (daily[i].high - daily[i].low);
      adrPips = (acc / copied) / PipSize;
   }

   double curSpread = (double)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * _Point / PipSize;

   string hud = "\n  == AUDNZD INSTITUTIONAL MATRIX ==" +
                "\n  --------------------------------------" +
                "\n  Real-time Spread : " + DoubleToString(curSpread, 1) + " pips" + (curSpread > InpMaxSpreadPips ? " [FILTERED]" : " [NOMINAL]") +
                "\n  14-Day ADR       : " + DoubleToString(adrPips, 1) + " pips" +
                "\n  Broker Time      : " + TimeToString(TimeCurrent(), TIME_SECONDS) +
                "\n  Engine Status    : Online (Volume & Spread Gated)" +
                "\n  --------------------------------------";
   Comment(hud);
}
FTtrader
Posts: 1008
Joined: Mon Aug 03, 2026 2:43 pm

Re: Scalping AUDNZD: A Cross Driven by Relative Rates and Quiet Ranges

Post by FTtrader »

Quantitative Trade Consumption Blueprint

To automate position sizing and bracket execution inside an EA, reference the indicator's calculation buffers:

Code: Select all

// MQL5 EA Execution Excerpt
double signalShort = iCustom(_Symbol, PERIOD_M15, "AUDNZD_Asian_Liquidity_Institutional", ..., 0, 1);
double signalLong  = iCustom(_Symbol, PERIOD_M15, "AUDNZD_Asian_Liquidity_Institutional", ..., 1, 1);
double stopLoss    = iCustom(_Symbol, PERIOD_M15, "AUDNZD_Asian_Liquidity_Institutional", ..., 2, 1);
double takeProfit1 = iCustom(_Symbol, PERIOD_M15, "AUDNZD_Asian_Liquidity_Institutional", ..., 3, 1);
double takeProfit2 = iCustom(_Symbol, PERIOD_M15, "AUDNZD_Asian_Liquidity_Institutional", ..., 4, 1);

if(signalShort != EMPTY_VALUE && signalShort > 0)
{
    // 1. Calculate dynamic risk-based lot sizing: (AccountBalance * RiskPct) / (Entry - stopLoss)
    // 2. Transmit split order:
    //    Position A: 50% Volume -> SL: stopLoss, TP: takeProfit1 (Equilibrium)
    //    Position B: 50% Volume -> SL: stopLoss, TP: takeProfit2 (Opposite Extreme)
}
This ensures zero manual trade intervention, complete immunity to broker spread blowouts, and algorithmic execution directly against liquidity traps.
Post Reply