Advertisement IC Markets

AUDUSD and the overlap continuation: does it still work after costs?

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

Re: AUDUSD and the overlap continuation: does it still work after costs?

Post by FTtrader »

Here are the complete, production-ready Expert Advisors (EAs) for both MetaTrader 4 (MQL4) and MetaTrader 5 (MQL5).

Both implementations use server time for session tracking, check tick-volume RVOL on closed candles to avoid intrabar repainting, place resting limit orders on the retest, enforce live spread checks, and continuously poll OnTick() to cancel pending orders the instant price violates the structural midpoint (asiaMid).
Recommended broker for automated trading & scalping IC Markets
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: AUDUSD and the overlap continuation: does it still work after costs?

Post by FTtrader »

1. MetaTrader 4 (MQL4)

Save this file as CostAwareAsiaBreakout.mq4 in your MQL4/Experts folder.

Code: Select all

//+------------------------------------------------------------------+
//|                                  CostAwareAsiaBreakout_MT4.mq4   |
//|                        Cost-Aware Asia Breakout + RVOL (AUDUSD)  |
//+------------------------------------------------------------------+
#property strict

//--- Inputs
input string   InpTradeSettings    = "--- Trade Settings ---";
input double   InpLotSize          = 0.1;           // Lot Size
input int      InpMagicNumber      = 101202;        // Magic Number

input string   InpSessionSettings  = "--- Session Settings (Server Time) ---";
input int      InpAsiaStartHour    = 0;             // Asia Start Hour
input int      InpAsiaEndHour      = 6;             // Asia End Hour

input string   InpCostSettings     = "--- Cost & Risk Mechanics ---";
input double   InpMaxCostPips      = 1.5;           // Expected Cost (Spread + Slippage in Pips)
input double   InpMinCostMargin    = 3.0;           // Min Target-to-Cost Margin (Multiple)
input double   InpRiskReward       = 1.5;           // Risk/Reward Ratio
input double   InpMaxSpreadPips    = 2.0;           // Max Live Spread Allowed (Pips)

input string   InpVolSettings      = "--- Volume & Momentum ---";
input int      InpRvolLength       = 20;            // RVOL Lookback Length
input double   InpRvolThreshold    = 1.5;           // Minimum RVOL (e.g., 1.5 = 150%)

//--- Internal State Variables
double   pipPoint;
double   asiaHigh        = 0.0;
double   asiaLow         = 999999.0;
double   asiaMid         = 0.0;
bool     wasInSession    = false;
datetime lastBarTime     = 0;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   pipPoint = (_Digits == 3 || _Digits == 5) ? _Point * 10 : _Point;
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Session Helper                                                   |
//+------------------------------------------------------------------+
bool IsInSession(datetime timeVal)
{
   MqlDateTime dt;
   TimeToStruct(timeVal, dt);
   
   if (InpAsiaStartHour < InpAsiaEndHour)
      return (dt.hour >= InpAsiaStartHour && dt.hour < InpAsiaEndHour);
   else
      return (dt.hour >= InpAsiaStartHour || dt.hour < InpAsiaEndHour);
}

//+------------------------------------------------------------------+
//| Session Reset & Cleanup                                          |
//+------------------------------------------------------------------+
void ResetSession()
{
   asiaHigh = 0.0;
   asiaLow  = 999999.0;
   asiaMid  = 0.0;

   // Purge stale pending orders and positions from the previous cycle
   for (int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if (OrderSymbol() == _Symbol && OrderMagicNumber() == InpMagicNumber)
         {
            int type = OrderType();
            if (type == OP_BUYLIMIT || type == OP_SELLLIMIT)
               bool res = OrderDelete(OrderTicket());
            else if (type == OP_BUY)
               bool res = OrderClose(OrderTicket(), OrderLots(), Bid, 3, clrRed);
            else if (type == OP_SELL)
               bool res = OrderClose(OrderTicket(), OrderLots(), Ask, 3, clrRed);
         }
      }
   }
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   datetime currentTime = TimeCurrent();
   bool inSession = IsInSession(currentTime);

   // 1. Session boundary detection
   if (inSession && !wasInSession)
   {
      ResetSession();
   }
   wasInSession = inSession;

   // 2. Track range during Asian session
   if (inSession)
   {
      if (High[0] > asiaHigh) asiaHigh = High[0];
      if (Low[0] < asiaLow)   asiaLow  = Low[0];
      asiaMid = (asiaHigh + asiaLow) / 2.0;
      return;
   }

   // 3. Structural Invalidation: Cancel limit orders if price re-crosses asiaMid
   if (!inSession && asiaHigh > 0.0)
   {
      for (int i = OrdersTotal() - 1; i >= 0; i--)
      {
         if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
         {
            if (OrderSymbol() == _Symbol && OrderMagicNumber() == InpMagicNumber)
            {
               if (OrderType() == OP_BUYLIMIT && Bid < asiaMid)
               {
                  Print("Long limit canceled: Price crossed structural invalidation (Asia Mid).");
                  bool res = OrderDelete(OrderTicket());
               }
               else if (OrderType() == OP_SELLLIMIT && Ask > asiaMid)
               {
                  Print("Short limit canceled: Price crossed structural invalidation (Asia Mid).");
                  bool res = OrderDelete(OrderTicket());
               }
            }
         }
      }
   }

   // 4. Bar-Close Evaluation for Breakout & RVOL
   if (Time[0] == lastBarTime || inSession || asiaHigh <= 0.0) return;
   lastBarTime = Time[0];

   // Calculate RVOL using Tick Volume
   double volSum = 0;
   for (int j = 1; j <= InpRvolLength; j++)
      volSum += (double)Volume[j];
   
   double avgVol = volSum / InpRvolLength;
   double currentRvol = (avgVol > 0) ? ((double)Volume[1] / avgVol) : 0.0;

   // Breakout confirmation on Bar 1
   bool breakoutLong  = (Close[1] > asiaHigh) && (Close[2] <= asiaHigh) && (currentRvol >= InpRvolThreshold);
   bool breakoutShort = (Close[1] < asiaLow)  && (Close[2] >= asiaLow)  && (currentRvol >= InpRvolThreshold);

   if (!breakoutLong && !breakoutShort) return;

   // Live Spread Check
   double currentSpreadPips = (Ask - Bid) / pipPoint;
   if (currentSpreadPips > InpMaxSpreadPips)
   {
      PrintFormat("Breakout ignored: Spread too high (%.1f pips).", currentSpreadPips);
      return;
   }

   // Calculate Risk, Targets, and Costs
   if (breakoutLong)
   {
      double riskPips   = (asiaHigh - asiaMid) / pipPoint;
      double targetPips = riskPips * InpRiskReward;

      if (targetPips >= (InpMaxCostPips * InpMinCostMargin))
      {
         double entry = NormalizeDouble(asiaHigh, _Digits);
         double sl    = NormalizeDouble(asiaMid, _Digits);
         double tp    = NormalizeDouble(asiaHigh + (targetPips * pipPoint), _Digits);

         int ticket = OrderSend(_Symbol, OP_BUYLIMIT, InpLotSize, entry, 3, sl, tp, "Asia Breakout", InpMagicNumber, 0, clrGreen);
         if (ticket > 0)
            PrintFormat("Long limit placed at %.5f (Target: %.1f pips, RVOL: %.2f)", entry, targetPips, currentRvol);
      }
      else
      {
         PrintFormat("Long breakout skipped: Target (%.1f pips) fails cost-to-margin threshold.", targetPips);
      }
   }
   else if (breakoutShort)
   {
      double riskPips   = (asiaMid - asiaLow) / pipPoint;
      double targetPips = riskPips * InpRiskReward;

      if (targetPips >= (InpMaxCostPips * InpMinCostMargin))
      {
         double entry = NormalizeDouble(asiaLow, _Digits);
         double sl    = NormalizeDouble(asiaMid, _Digits);
         double tp    = NormalizeDouble(asiaLow - (targetPips * pipPoint), _Digits);

         int ticket = OrderSend(_Symbol, OP_SELLLIMIT, InpLotSize, entry, 3, sl, tp, "Asia Breakout", InpMagicNumber, 0, clrRed);
         if (ticket > 0)
            PrintFormat("Short limit placed at %.5f (Target: %.1f pips, RVOL: %.2f)", entry, targetPips, currentRvol);
      }
      else
      {
         PrintFormat("Short breakout skipped: Target (%.1f pips) fails cost-to-margin threshold.", targetPips);
      }
   }
}
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: AUDUSD and the overlap continuation: does it still work after costs?

Post by FTtrader »

2. MetaTrader 5 (MQL5)

Save this file as CostAwareAsiaBreakout.mq5 in your MQL5/Experts folder. It uses the native standard library trade wrapper (CTrade) for order management and asynchronous tick processing.

Code: Select all

//+------------------------------------------------------------------+
//|                                  CostAwareAsiaBreakout_MT5.mq5   |
//|                        Cost-Aware Asia Breakout + RVOL (AUDUSD)  |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026"
#property link      ""
#property version   "1.00"

#include <Trade\Trade.mqh>
#include <Trade\OrderInfo.mqh>

//--- Inputs
input group "--- Trade Settings ---"
input double   InpLotSize          = 0.1;           // Lot Size
input ulong    InpMagicNumber      = 101202;        // Magic Number

input group "--- Session Settings (Server Time) ---"
input int      InpAsiaStartHour    = 0;             // Asia Start Hour
input int      InpAsiaEndHour      = 6;             // Asia End Hour

input group "--- Cost & Risk Mechanics ---"
input double   InpMaxCostPips      = 1.5;           // Expected Cost (Spread + Slippage in Pips)
input double   InpMinCostMargin    = 3.0;           // Min Target-to-Cost Margin (Multiple)
input double   InpRiskReward       = 1.5;           // Risk/Reward Ratio
input double   InpMaxSpreadPips    = 2.0;           // Max Live Spread Allowed (Pips)

input group "--- Volume & Momentum ---"
input int      InpRvolLength       = 20;            // RVOL Lookback Length
input double   InpRvolThreshold    = 1.5;           // Minimum RVOL (e.g., 1.5 = 150%)

//--- Objects & State Variables
CTrade         trade;
COrderInfo     orderInfo;

double         pipPoint;
double         asiaHigh        = 0.0;
double         asiaLow         = 999999.0;
double         asiaMid         = 0.0;
bool           wasInSession    = false;
datetime       lastBarTime     = 0;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   pipPoint = (_Digits == 3 || _Digits == 5) ? _Point * 10 : _Point;
   trade.SetExpertMagicNumber(InpMagicNumber);
   trade.SetTypeFillingBySymbol(_Symbol);
   
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Session Helper                                                   |
//+------------------------------------------------------------------+
bool IsInSession(datetime timeVal)
{
   MqlDateTime dt;
   TimeToStruct(timeVal, dt);
   
   if (InpAsiaStartHour < InpAsiaEndHour)
      return (dt.hour >= InpAsiaStartHour && dt.hour < InpAsiaEndHour);
   else
      return (dt.hour >= InpAsiaStartHour || dt.hour < InpAsiaEndHour);
}

//+------------------------------------------------------------------+
//| Session Reset & Cleanup                                          |
//+------------------------------------------------------------------+
void ResetSession()
{
   asiaHigh = 0.0;
   asiaLow  = 999999.0;
   asiaMid  = 0.0;

   // Purge pending orders
   for (int i = OrdersTotal() - 1; i >= 0; i--)
   {
      ulong ticket = OrderGetTicket(i);
      if (ticket > 0 && orderInfo.Select(ticket))
      {
         if (orderInfo.Symbol() == _Symbol && orderInfo.Magic() == InpMagicNumber)
         {
            trade.OrderDelete(ticket);
         }
      }
   }

   // Close open positions from previous session
   for (int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong ticket = PositionGetTicket(i);
      if (ticket > 0 && PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == InpMagicNumber)
      {
         trade.PositionClose(ticket);
      }
   }
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   MqlTick lastTick;
   if (!SymbolInfoTick(_Symbol, lastTick)) return;

   datetime currentTime = lastTick.time;
   bool inSession = IsInSession(currentTime);

   // 1. Session boundary detection
   if (inSession && !wasInSession)
   {
      ResetSession();
   }
   wasInSession = inSession;

   // 2. Track range during Asian session
   if (inSession)
   {
      MqlRates currentRates[];
      if (CopyRates(_Symbol, _Period, 0, 1, currentRates) > 0)
      {
         if (currentRates[0].high > asiaHigh) asiaHigh = currentRates[0].high;
         if (currentRates[0].low  < asiaLow)  asiaLow  = currentRates[0].low;
         asiaMid = (asiaHigh + asiaLow) / 2.0;
      }
      return;
   }

   // 3. Structural Invalidation: Cancel limit orders if price re-crosses asiaMid
   if (!inSession && asiaHigh > 0.0)
   {
      for (int i = OrdersTotal() - 1; i >= 0; i--)
      {
         ulong ticket = OrderGetTicket(i);
         if (ticket > 0 && orderInfo.Select(ticket))
         {
            if (orderInfo.Symbol() == _Symbol && orderInfo.Magic() == InpMagicNumber)
            {
               if (orderInfo.OrderType() == ORDER_TYPE_BUY_LIMIT && lastTick.bid < asiaMid)
               {
                  Print("Long limit canceled: Price crossed structural invalidation (Asia Mid).");
                  trade.OrderDelete(ticket);
               }
               else if (orderInfo.OrderType() == ORDER_TYPE_SELL_LIMIT && lastTick.ask > asiaMid)
               {
                  Print("Short limit canceled: Price crossed structural invalidation (Asia Mid).");
                  trade.OrderDelete(ticket);
               }
            }
         }
      }
   }

   // 4. Bar-Close Evaluation for Breakout & RVOL
   datetime barTime = iTime(_Symbol, _Period, 0);
   if (barTime == lastBarTime || inSession || asiaHigh <= 0.0) return;
   lastBarTime = barTime;

   // Copy rates to check Breakout (need bar 1 and bar 2)
   MqlRates rates[];
   ArraySetAsSeries(rates, true);
   if (CopyRates(_Symbol, _Period, 1, 2, rates) < 2) return;

   // Calculate RVOL using Tick Volume
   long tickVolumes[];
   ArraySetAsSeries(tickVolumes, true);
   if (CopyTickVolume(_Symbol, _Period, 1, InpRvolLength, tickVolumes) < InpRvolLength) return;

   double volSum = 0;
   for (int j = 0; j < InpRvolLength; j++)
      volSum += (double)tickVolumes[j];

   double avgVol = volSum / InpRvolLength;
   double currentRvol = (avgVol > 0) ? ((double)tickVolumes[0] / avgVol) : 0.0;

   // Breakout confirmation on Bar 1
   bool breakoutLong  = (rates[0].close > asiaHigh) && (rates[1].close <= asiaHigh) && (currentRvol >= InpRvolThreshold);
   bool breakoutShort = (rates[0].close < asiaLow)  && (rates[1].close >= asiaLow)  && (currentRvol >= InpRvolThreshold);

   if (!breakoutLong && !breakoutShort) return;

   // Live Spread Check
   double currentSpreadPips = (lastTick.ask - lastTick.bid) / pipPoint;
   if (currentSpreadPips > InpMaxSpreadPips)
   {
      PrintFormat("Breakout ignored: Spread too high (%.1f pips).", currentSpreadPips);
      return;
   }

   // Calculate Risk, Targets, and Costs
   if (breakoutLong)
   {
      double riskPips   = (asiaHigh - asiaMid) / pipPoint;
      double targetPips = riskPips * InpRiskReward;

      if (targetPips >= (InpMaxCostPips * InpMinCostMargin))
      {
         double entry = NormalizeDouble(asiaHigh, _Digits);
         double sl    = NormalizeDouble(asiaMid, _Digits);
         double tp    = NormalizeDouble(asiaHigh + (targetPips * pipPoint), _Digits);

         if (trade.BuyLimit(InpLotSize, entry, _Symbol, sl, tp, ORDER_TIME_GTC, 0, "Asia Breakout"))
            PrintFormat("Long limit placed at %.5f (Target: %.1f pips, RVOL: %.2f)", entry, targetPips, currentRvol);
      }
      else
      {
         PrintFormat("Long breakout skipped: Target (%.1f pips) fails cost-to-margin threshold.", targetPips);
      }
   }
   else if (breakoutShort)
   {
      double riskPips   = (asiaMid - asiaLow) / pipPoint;
      double targetPips = riskPips * InpRiskReward;

      if (targetPips >= (InpMaxCostPips * InpMinCostMargin))
      {
         double entry = NormalizeDouble(asiaLow, _Digits);
         double sl    = NormalizeDouble(asiaMid, _Digits);
         double tp    = NormalizeDouble(asiaLow - (targetPips * pipPoint), _Digits);

         if (trade.SellLimit(InpLotSize, entry, _Symbol, sl, tp, ORDER_TIME_GTC, 0, "Asia Breakout"))
            PrintFormat("Short limit placed at %.5f (Target: %.1f pips, RVOL: %.2f)", entry, targetPips, currentRvol);
      }
      else
      {
         PrintFormat("Short breakout skipped: Target (%.1f pips) fails cost-to-margin threshold.", targetPips);
      }
   }
}
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: AUDUSD and the overlap continuation: does it still work after costs?

Post by FTtrader »

Implementation & Setup Details

Broker Timezone Alignment: Both MetaTrader platforms evaluate hours based on the Broker's Server Time (often UTC+2 or UTC+3, depending on daylight saving). Double-check your broker's market watch clock and configure InpAsiaStartHour and InpAsiaEndHour to match the 00:00–06:00 UTC Asian session.

Tick Volume Compatibility: Forex brokers do not provide centralized contract volume, so both scripts read tick frequency through Volume[] (MT4) and CopyTickVolume() (MT5), which serves as the industry-standard institutional liquidity proxy.

Execution Routing: The EAs place standard Buy Limit and Sell Limit orders at the exact breakout boundaries. If the market continues running without retracing, no fills occur and no spread or commission is paid. If price drifts back through the range midpoint (asiaMid), the orders are pulled instantly on the nearest incoming tick.
Post Reply