Advertisement IC Markets

How to build strategy to trade USD/JPY

Share, develop, and backtest custom MQL4/MQL5 Expert Advisors, Python data-scraping scripts, trading bots, and automated market alert systems.
PTScalper
Site Admin
Posts: 5027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How to build strategy to trade USD/JPY

Post by PTScalper »

1. Core Architecture & State Machine Logic

Dynamic Cross-Currency Lot Sizing (SYMBOL_TRADE_TICK_VALUE_LOSS): Because USD/JPY quotes profit in JPY while your account base may be USD or EUR, the EA queries SYMBOL_TRADE_TICK_VALUE_LOSS and SYMBOL_TRADE_TICK_SIZE directly from the broker server before normalizing to SYMBOL_VOLUME_STEP.

Tick-Level Profit-Lock & House-Money Scaling (ManageOpenPositions): While entry signals evaluate once per closed M15 bar to prevent intrabar false triggers, position management runs on every tick:

As soon as Leg 1 reaches +20.0 pips (InpScaleTriggerPips), the EA modifies Leg 1's Stop Loss to +10.0 pips (InpLockedProfitPips) above/below entry.

Immediately after the Stop Loss modification succeeds, the EA calculates the exact dollar profit locked in by Leg 1 (applying a 0.95 buffer to cover Raw ECN round-turn commission) and uses that locked dollar amount as the risk budget to open Leg 2 with a 15.0-pip Stop Loss.

Daily Drawdown & MoF Intervention Circuit Breakers: The EA tracks daily starting balance at server reset and queries HistorySelect to sum realized intraday PnL alongside floating equity. If daily loss hits 1.50%, if 3 stop-outs occur in a single day, or if spot price breaches 159.20 (MoF intervention danger zone), new entries are blocked until the next daily reset.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Recommended broker for automated trading & scalping IC Markets
PTScalper
Site Admin
Posts: 5027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How to build strategy to trade USD/JPY

Post by PTScalper »

2. Complete MQL5 Expert Advisor Source Code

Save this file as USDJPY_LiquiditySweep_ProfitLock.mq5 inside your MQL5\Experts\ directory and compile it in MetaEditor (F7).

Code: Select all

//+------------------------------------------------------------------+
//|                         USDJPY_LiquiditySweep_ProfitLock.mq5     |
//|         Top-Down H4/M15 Liquidity Sweep & House-Money Scaling EA |
//+------------------------------------------------------------------+
#property copyright "USD/JPY Automated Strategy (October 2026 Regime)"
#property version   "1.00"
#property strict

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

//--- Input Parameters: H4 Structural Zones
input group "=== 1. H4 Structural Levels (JPY) ==="
input double   InpDemandLower          = 157.80;      // Primary H4 Demand Lower Bound
input double   InpDemandUpper          = 158.00;      // Primary H4 Demand Upper Bound (Sweep Level)
input double   InpSupplyLower          = 158.85;      // Primary H4 Supply Lower Bound (Reject Close)
input double   InpSupplyUpper          = 158.95;      // Primary H4 Supply Sweep Threshold
input double   InpBearishBreakdownH4   = 157.60;      // H4 Close Below Invalidates Longs
input double   InpMoFCeilingCutoff     = 159.20;      // Hard Cutoff (MoF Intervention Danger Zone)

//--- Input Parameters: Timeframes & Entry Triggers
input group "=== 2. M15 Execution & Spread Filters ==="
input ENUM_TIMEFRAMES InpEntryTF       = PERIOD_M15;  // Execution Timeframe
input ENUM_TIMEFRAMES InpStructureTF   = PERIOD_H4;   // Structural Filter Timeframe
input double   InpDisplacementRatio    = 0.85;        // Min Closed Body / Prior 3-Bar Avg Range Ratio
input double   InpMaxSpreadPips        = 1.2;         // Max Allowed Spread (Pips) for Raw ECN

//--- Input Parameters: Risk & Exponential Profit-Lock Scaling
input group "=== 3. Risk & House-Money Pyramiding ==="
input double   InpBaseRiskPercent      = 0.50;        // Leg 1 Base Risk (% of Account Balance)
input double   InpBaseStopLossPips     = 15.0;        // Leg 1 Stop Loss Distance (Pips)
input double   InpScaleTriggerPips     = 20.0;        // Profit Trigger to Lock SL & Open Leg 2 (Pips)
input double   InpLockedProfitPips     = 10.0;        // Leg 1 Locked Profit SL Distance (+Pips)
input double   InpScaleStopLossPips    = 15.0;        // Leg 2 Stop Loss Distance (Pips)
input double   InpCommissionBuffer     = 0.95;        // Safety Factor on Locked Profit for ECN Fees
input double   InpLongTargetPrice      = 158.85;      // Take Profit Price for Long Setups
input double   InpShortTargetPrice     = 158.00;      // Take Profit Price for Short Setups

//--- Input Parameters: Circuit Breakers & Session Timing
input group "=== 4. Circuit Breakers & Session Windows (UTC) ==="
input double   InpMaxDailyDrawdownPct  = 1.50;        // Daily Drawdown Circuit Breaker (% of Start Balance)
input int      InpMaxDailyLosingTrades = 3;           // Max Losing Trades Per Day Circuit Breaker
input bool     InpCloseAllOnDailyLimit = true;        // Flatten Open Positions if Daily DD Breached
input bool     InpUseSessionFilter     = true;        // Restrict Entries to Tokyo Open & LDN-NY Overlap
input int      InpTokyoStartHourUTC    = 0;           // Tokyo Window Start Hour (UTC)
input int      InpTokyoEndHourUTC      = 3;           // Tokyo Window End Hour (UTC)
input int      InpOverlapStartHourUTC  = 12;          // London-NY Overlap Start Hour (UTC)
input int      InpOverlapEndHourUTC    = 16;          // London-NY Overlap End Hour (UTC)

//--- Input Parameters: System & Notifications
input group "=== 5. EA Identification & Mobile Alerts ==="
input ulong    InpMagicNumber          = 20261019;    // EA Magic Number
input bool     InpEnablePushNotify     = true;        // Send MT5 Mobile Push Notifications

//--- Constants for Leg Tracking
#define LEG1_TAG "USDJPY_LEG1_BASE"
#define LEG2_TAG "USDJPY_LEG2_SCALE"

//--- Global Variables
CTrade         ExtTrade;
CPositionInfo  ExtPosition;
double         ExtPipSize              = 0.01;
datetime       ExtLastM15BarTime       = 0;
int            ExtCurrentDayOfYear     = -1;
double         ExtDayStartBalance      = 0.0;
bool           ExtDailyCircuitTripped  = false;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   //--- Configure CTrade instance
   ExtTrade.SetExpertMagicNumber(InpMagicNumber);
   ExtTrade.SetDeviationInPoints(15);
   ExtTrade.SetTypeFillingBySymbol(_Symbol);

   //--- Determine Pip Size (3-digit JPY brokers use 0.001 point -> 0.01 pip)
   ExtPipSize = (_Digits == 3 || _Digits == 5) ? _Point * 10.0 : _Point;

   //--- Initialize Daily Drawdown Baseline
   UpdateDailyBaseline();

   PrintFormat("USDJPY_LiquiditySweep_ProfitLock initialized on %s | PipSize: %.4f | StartBalance: %.2f",
               _Symbol, ExtPipSize, ExtDayStartBalance);
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   Comment("");
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   //--- 1. Refresh Daily Baseline & Check Circuit Breakers on Every Tick
   UpdateDailyBaseline();
   if(CheckDailyCircuitBreaker())
      return;

   //--- 2. Manage Open Positions on Every Tick (+20 Pip Lock & Leg 2 Scaling)
   ManageOpenPositions();

   //--- 3. Evaluate New Base Entries Only Once Per Closed M15 Bar
   datetime currentBarTime = iTime(_Symbol, InpEntryTF, 0);
   if(currentBarTime == ExtLastM15BarTime)
      return;

   //--- If a base position is already open, do not open another Leg 1
   if(HasOpenPositionByTag(LEG1_TAG) || HasOpenPositionByTag(LEG2_TAG))
   {
      ExtLastM15BarTime = currentBarTime;
      return;
   }

   //--- Verify Session Window & Spread Filter before Evaluating Signal
   if(InpUseSessionFilter && !IsWithinPrimeSessionUTC())
      return;

   if(!IsSpreadAcceptable())
      return;

   //--- Mark bar as processed and check M15 Liquidity Sweep Signals
   ExtLastM15BarTime = currentBarTime;
   EvaluateM15EntrySignals();
}

//+------------------------------------------------------------------+
//| Reset daily starting balance at server midnight                  |
//+------------------------------------------------------------------+
void UpdateDailyBaseline()
{
   MqlDateTime dt;
   TimeCurrent(dt);
   if(dt.day_of_year != ExtCurrentDayOfYear)
   {
      ExtCurrentDayOfYear    = dt.day_of_year;
      ExtDayStartBalance     = AccountInfoDouble(ACCOUNT_BALANCE);
      ExtDailyCircuitTripped = false;
   }
}

//+------------------------------------------------------------------+
//| Verify 1.50% Daily Drawdown & Max Consecutive Stop-Outs          |
//+------------------------------------------------------------------+
bool CheckDailyCircuitBreaker()
{
   if(ExtDailyCircuitTripped)
      return(true);

   //--- Calculate today's realized PnL and losing trade count for this Magic Number
   datetime dayStart = iTime(_Symbol, PERIOD_D1, 0);
   double   realizedToday = 0.0;
   int      losingTradesToday = 0;

   if(HistorySelect(dayStart, TimeCurrent()))
   {
      int totalDeals = HistoryDealsTotal();
      for(int i = 0; i < totalDeals; i++)
      {
         ulong dealTicket = HistoryDealGetTicket(i);
         if(dealTicket == 0) continue;
         if(HistoryDealGetInteger(dealTicket, DEAL_MAGIC) != (long)InpMagicNumber) continue;
         if(HistoryDealGetString(dealTicket, DEAL_SYMBOL) != _Symbol) continue;

         long dealEntry = HistoryDealGetInteger(dealTicket, DEAL_ENTRY);
         if(dealEntry == DEAL_ENTRY_OUT || dealEntry == DEAL_ENTRY_INOUT)
         {
            double netDealProfit = HistoryDealGetDouble(dealTicket, DEAL_PROFIT)
                                 + HistoryDealGetDouble(dealTicket, DEAL_COMMISSION)
                                 + HistoryDealGetDouble(dealTicket, DEAL_SWAP);
            realizedToday += netDealProfit;
            if(netDealProfit < -1.0) // Ignore break-even / positive profit-lock exits
               losingTradesToday++;
         }
      }
   }

   //--- Add current floating PnL for this EA
   double floatingPnL = GetOpenFloatingPnL();
   double totalDailyPnL = realizedToday + floatingPnL;
   double maxAllowedLoss = -1.0 * ExtDayStartBalance * (InpMaxDailyDrawdownPct / 100.0);

   if(totalDailyPnL <= maxAllowedLoss || losingTradesToday >= InpMaxDailyLosingTrades)
   {
      ExtDailyCircuitTripped = true;
      string msg = StringFormat("Circuit Breaker Tripped on %s | Daily PnL: %.2f | Losses Today: %d",
                                _Symbol, totalDailyPnL, losingTradesToday);
      Print(msg);
      if(InpEnablePushNotify) SendNotification(msg);

      if(InpCloseAllOnDailyLimit)
         CloseAllEAPositions();

      return(true);
   }
   return(false);
}

//+------------------------------------------------------------------+
//| Tick-level Position Management: +20 Pip Lock & Leg 2 Scale-In    |
//+------------------------------------------------------------------+
void ManageOpenPositions()
{
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

   //--- MoF Intervention Danger Zone Hard Cutoff Check (159.20+)
   if(bid >= InpMoFCeilingCutoff)
   {
      if(HasOpenPositionByTag(LEG1_TAG) || HasOpenPositionByTag(LEG2_TAG))
      {
         Print("MoF Ceiling Cutoff breached (>= ", InpMoFCeilingCutoff, "). Flattening exposure.");
         CloseAllEAPositions();
      }
      return;
   }

   //--- Find Open Leg 1 Position
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(!ExtPosition.SelectByIndex(i)) continue;
      if(ExtPosition.Symbol() != _Symbol || ExtPosition.Magic() != InpMagicNumber) continue;
      if(StringFind(ExtPosition.Comment(), LEG1_TAG) < 0) continue;

      ulong  leg1Ticket    = ExtPosition.Ticket();
      double leg1OpenPrice = ExtPosition.PriceOpen();
      double leg1CurrentSL = ExtPosition.StopLoss();
      double leg1CurrentTP = ExtPosition.TakeProfit();
      double leg1Volume    = ExtPosition.Volume();
      ENUM_POSITION_TYPE posType = ExtPosition.PositionType();

      double triggerDist   = InpScaleTriggerPips * ExtPipSize;
      double lockDist      = InpLockedProfitPips * ExtPipSize;

      //--- CASE A: LONG POSITION MANAGEMENT
      if(posType == POSITION_TYPE_BUY)
      {
         double currentGainPrice = bid - leg1OpenPrice;
         double targetLockSL     = NormalizeDouble(leg1OpenPrice + lockDist, _Digits);

         //--- Check if +20 Pips reached and SL is not yet locked at +10 Pips
         if(currentGainPrice >= triggerDist && leg1CurrentSL < targetLockSL - (_Point * 0.5))
         {
            if(ExtTrade.PositionModify(leg1Ticket, targetLockSL, leg1CurrentTP))
            {
               PrintFormat("Leg 1 BUY #%I64u SL locked at +%.1f pips (%.3f).",
                           leg1Ticket, InpLockedProfitPips, targetLockSL);

               //--- Open Leg 2 (House-Money Scale-In) if not already open
               if(!HasOpenPositionByTag(LEG2_TAG))
                  ExecuteHouseMoneyScaleIn(ORDER_TYPE_BUY, leg1Volume, lockDist, ask, leg1CurrentTP);
            }
         }
      }
      //--- CASE B: SHORT POSITION MANAGEMENT
      else if(posType == POSITION_TYPE_SELL)
      {
         double currentGainPrice = leg1OpenPrice - ask;
         double targetLockSL     = NormalizeDouble(leg1OpenPrice - lockDist, _Digits);

         //--- Check if +20 Pips reached and SL is not yet locked at +10 Pips
         if(currentGainPrice >= triggerDist && (leg1CurrentSL == 0.0 || leg1CurrentSL > targetLockSL + (_Point * 0.5)))
         {
            if(ExtTrade.PositionModify(leg1Ticket, targetLockSL, leg1CurrentTP))
            {
               PrintFormat("Leg 1 SELL #%I64u SL locked at +%.1f pips (%.3f).",
                           leg1Ticket, InpLockedProfitPips, targetLockSL);

               //--- Open Leg 2 (House-Money Scale-In) if not already open
               if(!HasOpenPositionByTag(LEG2_TAG))
                  ExecuteHouseMoneyScaleIn(ORDER_TYPE_SELL, leg1Volume, lockDist, bid, leg1CurrentTP);
            }
         }
      }
   }
}

//+------------------------------------------------------------------+
//| Open Leg 2 Funded Strictly by Locked-In Dollar Profit of Leg 1   |
//+------------------------------------------------------------------+
void ExecuteHouseMoneyScaleIn(ENUM_ORDER_TYPE orderType,
                              double leg1Volume,
                              double lockedPriceDistance,
                              double entryPrice,
                              double takeProfitPrice)
{
   double tickSize  = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE_LOSS);
   if(tickValue <= 0.0)
      tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);

   if(tickSize <= 0.0 || tickValue <= 0.0)
      return;

   //--- Calculate exact dollar profit locked in by Leg 1 (+10 pips)
   double lockedProfitDollars = leg1Volume * (lockedPriceDistance / tickSize) * tickValue;
   double riskBudgetForLeg2   = lockedProfitDollars * InpCommissionBuffer;

   //--- Calculate Leg 2 Lot Size for a 15-pip Stop Loss
   double leg2SLDistance      = InpScaleStopLossPips * ExtPipSize;
   double leg2Lots            = CalculateLotSizeFromMoney(riskBudgetForLeg2, leg2SLDistance);

   if(leg2Lots <= 0.0)
      return;

   double leg2SL = (orderType == ORDER_TYPE_BUY)
                   ? NormalizeDouble(entryPrice - leg2SLDistance, _Digits)
                   : NormalizeDouble(entryPrice + leg2SLDistance, _Digits);

   double leg2TP = NormalizeDouble(takeProfitPrice, _Digits);

   bool res = false;
   if(orderType == ORDER_TYPE_BUY)
      res = ExtTrade.Buy(leg2Lots, _Symbol, entryPrice, leg2SL, leg2TP, LEG2_TAG);
   else
      res = ExtTrade.Sell(leg2Lots, _Symbol, entryPrice, leg2SL, leg2TP, LEG2_TAG);

   if(res)
   {
      string msg = StringFormat("House-Money Leg 2 Opened (%s %.2f lots @ %.3f) | Risk Budget: $%.2f ($0 Principal Risk)",
                                (orderType == ORDER_TYPE_BUY ? "BUY" : "SELL"),
                                leg2Lots, entryPrice, riskBudgetForLeg2);
      Print(msg);
      if(InpEnablePushNotify) SendNotification(msg);
   }
}

//+------------------------------------------------------------------+
//| Evaluate Closed M15 Candle for Demand/Supply Liquidity Sweeps    |
//+------------------------------------------------------------------+
void EvaluateM15EntrySignals()
{
   MqlRates rates[];
   ArraySetAsSeries(rates, true);
   //--- Copy last 4 closed M15 candles (indices 0..3 correspond to shifts 1..4)
   if(CopyRates(_Symbol, InpEntryTF, 1, 4, rates) < 4)
      return;

   //--- Check H4 Bearish Breakdown Filter (Last closed H4 bar)
   double h4Close = iClose(_Symbol, InpStructureTF, 1);
   bool   longsAllowedByH4 = (h4Close >= InpBearishBreakdownH4);

   //--- Compute average full range of the prior 3 candles (shifts 2, 3, 4)
   double avgPrior3Range = ((rates[1].high - rates[1].low) +
                            (rates[2].high - rates[2].low) +
                            (rates[3].high - rates[3].low)) / 3.0;

   double signalOpen  = rates[0].open;
   double signalHigh  = rates[0].high;
   double signalLow   = rates[0].low;
   double signalClose = rates[0].close;
   double signalBody  = MathAbs(signalClose - signalOpen);

   bool isImpulsiveBody = (signalBody >= avgPrior3Range * InpDisplacementRatio);
   bool isBullishEngulf = (signalClose > signalOpen && signalClose > rates[1].high);
   bool isBearishEngulf = (signalClose < signalOpen && signalClose < rates[1].low);

   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

   //--- 1. LONG SETUP: H4 Demand Sweep (Wick <= 158.00 & >= 157.80, Close > 158.00)
   bool sweptDemand = (signalLow <= InpDemandUpper && signalLow >= (InpDemandLower - 5.0 * ExtPipSize));
   bool closedAboveDemand = (signalClose > InpDemandUpper && signalClose > signalOpen);

   if(longsAllowedByH4 && sweptDemand && closedAboveDemand && (isImpulsiveBody || isBullishEngulf))
   {
      if(ask < InpLongTargetPrice - (25.0 * ExtPipSize))
      {
         ExecuteBaseEntry(ORDER_TYPE_BUY, ask, InpLongTargetPrice);
         return;
      }
   }

   //--- 2. SHORT SETUP: H4 Supply Rejection (Wick >= 158.95 & < 159.20, Close < 158.85)
   bool sweptSupply = (signalHigh >= InpSupplyUpper && signalHigh < InpMoFCeilingCutoff);
   bool closedBelowSupply = (signalClose <= InpSupplyLower && signalClose < signalOpen);

   if(sweptSupply && closedBelowSupply && (isImpulsiveBody || isBearishEngulf))
   {
      if(bid > InpShortTargetPrice + (25.0 * ExtPipSize))
      {
         ExecuteBaseEntry(ORDER_TYPE_SELL, bid, InpShortTargetPrice);
         return;
      }
   }
}

//+------------------------------------------------------------------+
//| Execute Leg 1 Base Entry (0.50% Risk)                            |
//+------------------------------------------------------------------+
void ExecuteBaseEntry(ENUM_ORDER_TYPE orderType, double entryPrice, double targetPrice)
{
   double balance    = AccountInfoDouble(ACCOUNT_BALANCE);
   double riskMoney  = balance * (InpBaseRiskPercent / 100.0);
   double slDistance = InpBaseStopLossPips * ExtPipSize;

   double lotSize    = CalculateLotSizeFromMoney(riskMoney, slDistance);
   if(lotSize <= 0.0)
      return;

   double slPrice = (orderType == ORDER_TYPE_BUY)
                    ? NormalizeDouble(entryPrice - slDistance, _Digits)
                    : NormalizeDouble(entryPrice + slDistance, _Digits);

   double tpPrice = NormalizeDouble(targetPrice, _Digits);

   bool res = false;
   if(orderType == ORDER_TYPE_BUY)
      res = ExtTrade.Buy(lotSize, _Symbol, entryPrice, slPrice, tpPrice, LEG1_TAG);
   else
      res = ExtTrade.Sell(lotSize, _Symbol, entryPrice, slPrice, tpPrice, LEG1_TAG);

   if(res)
   {
      string msg = StringFormat("Leg 1 Base %s Opened: %.2f lots @ %.3f | SL: %.3f | TP: %.3f | Risk: $%.2f (%.2f%%)",
                                (orderType == ORDER_TYPE_BUY ? "BUY" : "SELL"),
                                lotSize, entryPrice, slPrice, tpPrice, riskMoney, InpBaseRiskPercent);
      Print(msg);
      if(InpEnablePushNotify) SendNotification(msg);
   }
}

//+------------------------------------------------------------------+
//| Calculate Normalized Lot Size from Dollar Risk & Price Distance  |
//+------------------------------------------------------------------+
double CalculateLotSizeFromMoney(double riskMoney, double slPriceDistance)
{
   if(riskMoney <= 0.0 || slPriceDistance <= 0.0)
      return(0.0);

   double tickSize  = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE_LOSS);
   if(tickValue <= 0.0)
      tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);

   if(tickSize <= 0.0 || tickValue <= 0.0)
      return(0.0);

   double lossPerLot = (slPriceDistance / tickSize) * tickValue;
   if(lossPerLot <= 0.0)
      return(0.0);

   double rawLots = riskMoney / lossPerLot;

   double minVol  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double maxVol  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   double stepVol = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);

   //--- Floor to volume step so risk never exceeds target percentage
   double normalizedLots = MathFloor(rawLots / stepVol) * stepVol;
   if(normalizedLots < minVol)
      return(0.0);
   if(normalizedLots > maxVol)
      normalizedLots = maxVol;

   return(NormalizeDouble(normalizedLots, 2));
}

//+------------------------------------------------------------------+
//| Helper: Check if Prime Tokyo or London-NY Overlap Window (UTC)   |
//+------------------------------------------------------------------+
bool IsWithinPrimeSessionUTC()
{
   MqlDateTime utc;
   TimeGMT(utc);

   bool inTokyo   = (utc.hour >= InpTokyoStartHourUTC && utc.hour < InpTokyoEndHourUTC);
   bool inOverlap = (utc.hour >= InpOverlapStartHourUTC && utc.hour < InpOverlapEndHourUTC);

   return(inTokyo || inOverlap);
}

//+------------------------------------------------------------------+
//| Helper: Verify Raw ECN Spread is Below Threshold                 |
//+------------------------------------------------------------------+
bool IsSpreadAcceptable()
{
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double currentSpreadPips = (ask - bid) / ExtPipSize;

   return(currentSpreadPips <= InpMaxSpreadPips);
}

//+------------------------------------------------------------------+
//| Helper: Check if a Position with Specific Comment Tag Exists     |
//+------------------------------------------------------------------+
bool HasOpenPositionByTag(string tag)
{
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(!ExtPosition.SelectByIndex(i)) continue;
      if(ExtPosition.Symbol() == _Symbol &&
         ExtPosition.Magic() == InpMagicNumber &&
         StringFind(ExtPosition.Comment(), tag) >= 0)
      {
         return(true);
      }
   }
   return(false);
}

//+------------------------------------------------------------------+
//| Helper: Get Total Floating PnL of Open Positions for this EA     |
//+------------------------------------------------------------------+
double GetOpenFloatingPnL()
{
   double floating = 0.0;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(!ExtPosition.SelectByIndex(i)) continue;
      if(ExtPosition.Symbol() == _Symbol && ExtPosition.Magic() == InpMagicNumber)
      {
         floating += ExtPosition.Profit() + ExtPosition.Swap() + ExtPosition.Commission();
      }
   }
   return(floating);
}

//+------------------------------------------------------------------+
//| Helper: Close All Open Positions Belonging to This EA            |
//+------------------------------------------------------------------+
void CloseAllEAPositions()
{
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(!ExtPosition.SelectByIndex(i)) continue;
      if(ExtPosition.Symbol() == _Symbol && ExtPosition.Magic() == InpMagicNumber)
      {
         ExtTrade.PositionClose(ExtPosition.Ticket());
      }
   }
}
//+------------------------------------------------------------------+
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 5027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How to build strategy to trade USD/JPY

Post by PTScalper »

3.) Deployment & MT5 Strategy Tester Checklist

Compile in MetaEditor 5: Open MetaEditor (F4 in MT5), create a new Expert Advisor named USDJPY_LiquiditySweep_ProfitLock.mq5, paste the code above, and press F7 (0 errors, 0 warnings).

Backtest with Real Ticks: In the MT5 Strategy Tester (Ctrl+R), select USDJPY, set the timeframe to M15, and choose "Every tick based on real ticks" so the intrabar +20 pip profit-lock and Leg 2 trigger are simulated with exact ECN bid/ask spreads.

Enable Mobile Push Alerts (Optional): To receive instant iPhone MT5 notifications whenever Leg 1 enters or Leg 2 scales in on house money, enable Push Notifications under Tools -> Options -> Notifications in your desktop terminal and keep InpEnablePushNotify = true.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 5027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How to build strategy to trade USD/JPY

Post by PTScalper »

Adding a cross-asset filter to the USD/JPY Expert Advisor eliminates false M15 liquidity sweeps caused by isolated Yen noise while USD real yields or safe-haven flows are moving in the opposite direction.

Because XAU/USD (Spot Gold) and US 10-Year Treasury Note futures/CFDs trade inversely to US real yields—while US10Y Yield indices trade directly with USD/JPY—the upgraded EA normalizes cross-asset momentum using each instrument's 14-period M15 ATR and 20-period EMA slope:

$$\text{Macro Impulse Score} = \frac{\text{Close}_{1} - \text{Close}_{1 + N}}{\text{ATR}_{14, 1}}$$

where $N$ is the M15 momentum lookback window (default 3 closed M15 bars, or 45 minutes of order flow).
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 5027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How to build strategy to trade USD/JPY

Post by PTScalper »

1. Multi-Symbol Confluence Matrix & Filter Modes

To accommodate different broker feeds (such as XAUUSD alongside either a direct yield ticker like US10Y or a Treasury Note price CFD like UST10Y / TNOTE), the EA includes two selectable strictness modes:

STRICT_DIRECTIONAL (Active Confirmation)

Requires XAU/USD Impulse $\le -0.20\text{ ATR}$ (and below M15 EMA-20) AND/OR US10Y Yield Impulse $\ge +0.20\text{ ATR}$ (Long Entry)

Requires XAU/USD Impulse $\ge +0.20\text{ ATR}$ (and above M15 EMA-20) AND/OR US10Y Yield Impulse $\le -0.20\text{ ATR}$ (Short Entry)

London–NY Overlap (12:00–16:00 UTC) when US yields and Gold actively drive USD legs (Best session use case)
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 5027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How to build strategy to trade USD/JPY

Post by PTScalper »

2. Upgraded MQL5 Expert Advisor (v2.00)

Save this updated code over USDJPY_LiquiditySweep_ProfitLock.mq5 in MQL5\Experts\ and compile with F7. In OnInit(), the EA automatically subscribes the secondary symbols in Market Watch via SymbolSelect() and initializes dedicated M15 iATR and iMA handles.

Code: Select all

//+------------------------------------------------------------------+
//|                         USDJPY_LiquiditySweep_ProfitLock.mq5     |
//|   v2.00: Top-Down H4/M15 Sweep + XAUUSD/US10Y Confluence Filter  |
//+------------------------------------------------------------------+
#property copyright "USD/JPY Automated Strategy (October 2026 Regime)"
#property version   "2.00"
#property strict

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

//--- Enumerations for Multi-Symbol Macro Filter
enum ENUM_CORR_SOURCE
{
   CORR_XAUUSD_ONLY        = 0,  // XAUUSD Only (Inverse to USD/JPY)
   CORR_US10Y_ONLY         = 1,  // US10Y / Bond Symbol Only
   CORR_EITHER_CONFIRMS    = 2,  // Either XAUUSD or US10Y Confirms
   CORR_BOTH_REQUIRED      = 3   // Both XAUUSD and US10Y Must Confirm
};

enum ENUM_FILTER_STRICTNESS
{
   STRICT_DIRECTIONAL      = 0,  // Require Active Cross-Asset Momentum Alignment
   VETO_OPPOSING_SPIKE     = 1   // Only Block Entries When Cross-Asset Spikes Against Setup
};

//--- Input Parameters: H4 Structural Zones
input group "=== 1. H4 Structural Levels (JPY) ==="
input double   InpDemandLower          = 157.80;      // Primary H4 Demand Lower Bound
input double   InpDemandUpper          = 158.00;      // Primary H4 Demand Upper Bound (Sweep Level)
input double   InpSupplyLower          = 158.85;      // Primary H4 Supply Lower Bound (Reject Close)
input double   InpSupplyUpper          = 158.95;      // Primary H4 Supply Sweep Threshold
input double   InpBearishBreakdownH4   = 157.60;      // H4 Close Below Invalidates Longs
input double   InpMoFCeilingCutoff     = 159.20;      // Hard Cutoff (MoF Intervention Danger Zone)

//--- Input Parameters: Timeframes & Entry Triggers
input group "=== 2. M15 Execution & Spread Filters ==="
input ENUM_TIMEFRAMES InpEntryTF       = PERIOD_M15;  // Execution Timeframe
input ENUM_TIMEFRAMES InpStructureTF   = PERIOD_H4;   // Structural Filter Timeframe
input double   InpDisplacementRatio    = 0.85;        // Min Closed Body / Prior 3-Bar Avg Range Ratio
input double   InpMaxSpreadPips        = 1.2;         // Max Allowed Spread (Pips) for Raw ECN

//--- Input Parameters: Multi-Symbol XAUUSD & US10Y Filter
input group "=== 3. Multi-Symbol Macro Filter (XAUUSD / US10Y) ==="
input bool                   InpUseMacroFilter     = true;                 // Enable Cross-Asset Momentum Filter
input ENUM_CORR_SOURCE       InpCorrSource         = CORR_XAUUSD_ONLY;     // Active Correlation Feed(s)
input ENUM_FILTER_STRICTNESS InpFilterStrictness   = STRICT_DIRECTIONAL;   // Filter Strictness Mode
input string                 InpGoldSymbol         = "XAUUSD";             // Gold Symbol Name (Inverse Correlation)
input string                 InpBondSymbol         = "US10Y";              // US10Y Yield or T-Note Symbol Name
input bool                   InpBondIsYieldDirect  = true;                 // true = Yield (Direct), false = Bond Price (Inverse)
input int                    InpMomentumLookback   = 3;                    // M15 Bar Lookback for Impulse ROC
input int                    InpMacroATRPeriod     = 14;                   // ATR Period for Impulse Normalization
input int                    InpMacroEMAPeriod     = 20;                   // EMA Period for Cross-Asset Trend Filter
input double                 InpMinConfirmATR      = 0.20;                 // Min ATR Impulse for STRICT_DIRECTIONAL
input double                 InpMaxVetoSpikeATR    = 0.45;                 // Max Opposing ATR Impulse for VETO Mode

//--- Input Parameters: Risk & Exponential Profit-Lock Scaling
input group "=== 4. Risk & House-Money Pyramiding ==="
input double   InpBaseRiskPercent      = 0.50;        // Leg 1 Base Risk (% of Account Balance)
input double   InpBaseStopLossPips     = 15.0;        // Leg 1 Stop Loss Distance (Pips)
input double   InpScaleTriggerPips     = 20.0;        // Profit Trigger to Lock SL & Open Leg 2 (Pips)
input double   InpLockedProfitPips     = 10.0;        // Leg 1 Locked Profit SL Distance (+Pips)
input double   InpScaleStopLossPips    = 15.0;        // Leg 2 Stop Loss Distance (Pips)
input double   InpCommissionBuffer     = 0.95;        // Safety Factor on Locked Profit for ECN Fees
input double   InpLongTargetPrice      = 158.85;      // Take Profit Price for Long Setups
input double   InpShortTargetPrice     = 158.00;      // Take Profit Price for Short Setups

//--- Input Parameters: Circuit Breakers & Session Timing
input group "=== 5. Circuit Breakers & Session Windows (UTC) ==="
input double   InpMaxDailyDrawdownPct  = 1.50;        // Daily Drawdown Circuit Breaker (% of Start Balance)
input int      InpMaxDailyLosingTrades = 3;           // Max Losing Trades Per Day Circuit Breaker
input bool     InpCloseAllOnDailyLimit = true;        // Flatten Open Positions if Daily DD Breached
input bool     InpUseSessionFilter     = true;        // Restrict Entries to Tokyo Open & LDN-NY Overlap
input int      InpTokyoStartHourUTC    = 0;           // Tokyo Window Start Hour (UTC)
input int      InpTokyoEndHourUTC      = 3;           // Tokyo Window End Hour (UTC)
input int      InpOverlapStartHourUTC  = 12;          // London-NY Overlap Start Hour (UTC)
input int      InpOverlapEndHourUTC    = 16;          // London-NY Overlap End Hour (UTC)

//--- Input Parameters: System & Notifications
input group "=== 6. EA Identification & Mobile Alerts ==="
input ulong    InpMagicNumber          = 20261019;    // EA Magic Number
input bool     InpEnablePushNotify     = true;        // Send MT5 Mobile Push Notifications

//--- Constants for Leg Tracking
#define LEG1_TAG "USDJPY_LEG1_BASE"
#define LEG2_TAG "USDJPY_LEG2_SCALE"

//--- Global Variables
CTrade         ExtTrade;
CPositionInfo  ExtPosition;
double         ExtPipSize              = 0.01;
datetime       ExtLastM15BarTime       = 0;
int            ExtCurrentDayOfYear     = -1;
double         ExtDayStartBalance      = 0.0;
bool           ExtDailyCircuitTripped  = false;

//--- Cross-Asset Indicator Handles
int            ExtGoldATRHandle        = INVALID_HANDLE;
int            ExtGoldEMAHandle        = INVALID_HANDLE;
int            ExtBondATRHandle        = INVALID_HANDLE;
int            ExtBondEMAHandle        = INVALID_HANDLE;
string         ExtLastMacroDiagnostics = "";

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   ExtTrade.SetExpertMagicNumber(InpMagicNumber);
   ExtTrade.SetDeviationInPoints(15);
   ExtTrade.SetTypeFillingBySymbol(_Symbol);

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

   //--- Initialize Cross-Asset Symbols & Indicator Handles
   if(InpUseMacroFilter)
   {
      bool needGold = (InpCorrSource == CORR_XAUUSD_ONLY ||
                       InpCorrSource == CORR_EITHER_CONFIRMS ||
                       InpCorrSource == CORR_BOTH_REQUIRED);

      bool needBond = (InpCorrSource == CORR_US10Y_ONLY ||
                       InpCorrSource == CORR_EITHER_CONFIRMS ||
                       InpCorrSource == CORR_BOTH_REQUIRED);

      if(needGold)
      {
         if(!SymbolSelect(InpGoldSymbol, true))
         {
            PrintFormat("ERROR: Failed to select Gold symbol '%s' in Market Watch.", InpGoldSymbol);
            return(INIT_FAILED);
         }
         ExtGoldATRHandle = iATR(InpGoldSymbol, InpEntryTF, InpMacroATRPeriod);
         ExtGoldEMAHandle = iMA(InpGoldSymbol, InpEntryTF, InpMacroEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
         if(ExtGoldATRHandle == INVALID_HANDLE || ExtGoldEMAHandle == INVALID_HANDLE)
         {
            PrintFormat("ERROR: Failed to create M15 ATR/EMA handles for %s.", InpGoldSymbol);
            return(INIT_FAILED);
         }
      }

      if(needBond)
      {
         if(!SymbolSelect(InpBondSymbol, true))
         {
            PrintFormat("ERROR: Failed to select Bond/Yield symbol '%s' in Market Watch.", InpBondSymbol);
            return(INIT_FAILED);
         }
         ExtBondATRHandle = iATR(InpBondSymbol, InpEntryTF, InpMacroATRPeriod);
         ExtBondEMAHandle = iMA(InpBondSymbol, InpEntryTF, InpMacroEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
         if(ExtBondATRHandle == INVALID_HANDLE || ExtBondEMAHandle == INVALID_HANDLE)
         {
            PrintFormat("ERROR: Failed to create M15 ATR/EMA handles for %s.", InpBondSymbol);
            return(INIT_FAILED);
         }
      }
   }

   PrintFormat("USDJPY_LiquiditySweep_ProfitLock v2.00 initialized on %s | MacroFilter: %s",
               _Symbol, (InpUseMacroFilter ? "ENABLED" : "DISABLED"));
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   if(ExtGoldATRHandle != INVALID_HANDLE) IndicatorRelease(ExtGoldATRHandle);
   if(ExtGoldEMAHandle != INVALID_HANDLE) IndicatorRelease(ExtGoldEMAHandle);
   if(ExtBondATRHandle != INVALID_HANDLE) IndicatorRelease(ExtBondATRHandle);
   if(ExtBondEMAHandle != INVALID_HANDLE) IndicatorRelease(ExtBondEMAHandle);
   Comment("");
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   //--- 1. Refresh Daily Baseline & Check Circuit Breakers on Every Tick
   UpdateDailyBaseline();
   if(CheckDailyCircuitBreaker())
      return;

   //--- 2. Manage Open Positions on Every Tick (+20 Pip Lock & Leg 2 Scaling)
   ManageOpenPositions();

   //--- 3. Evaluate New Base Entries Only Once Per Closed M15 Bar
   datetime currentBarTime = iTime(_Symbol, InpEntryTF, 0);
   if(currentBarTime == ExtLastM15BarTime)
      return;

   if(HasOpenPositionByTag(LEG1_TAG) || HasOpenPositionByTag(LEG2_TAG))
   {
      ExtLastM15BarTime = currentBarTime;
      return;
   }

   if(InpUseSessionFilter && !IsWithinPrimeSessionUTC())
      return;

   if(!IsSpreadAcceptable())
      return;

   //--- Mark bar as processed and check M15 Liquidity Sweep + Macro Confluence
   ExtLastM15BarTime = currentBarTime;
   EvaluateM15EntrySignals();
}

//+------------------------------------------------------------------+
//| Evaluate Closed M15 Candle for Demand/Supply Sweeps + Macro      |
//+------------------------------------------------------------------+
void EvaluateM15EntrySignals()
{
   MqlRates rates[];
   ArraySetAsSeries(rates, true);
   if(CopyRates(_Symbol, InpEntryTF, 1, 4, rates) < 4)
      return;

   double h4Close = iClose(_Symbol, InpStructureTF, 1);
   bool   longsAllowedByH4 = (h4Close >= InpBearishBreakdownH4);

   double avgPrior3Range = ((rates[1].high - rates[1].low) +
                            (rates[2].high - rates[2].low) +
                            (rates[3].high - rates[3].low)) / 3.0;

   double signalOpen  = rates[0].open;
   double signalHigh  = rates[0].high;
   double signalLow   = rates[0].low;
   double signalClose = rates[0].close;
   double signalBody  = MathAbs(signalClose - signalOpen);

   bool isImpulsiveBody = (signalBody >= avgPrior3Range * InpDisplacementRatio);
   bool isBullishEngulf = (signalClose > signalOpen && signalClose > rates[1].high);
   bool isBearishEngulf = (signalClose < signalOpen && signalClose < rates[1].low);

   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

   //--- 1. LONG SETUP: H4 Demand Sweep (Wick <= 158.00 & >= 157.80, Close > 158.00)
   bool sweptDemand       = (signalLow <= InpDemandUpper && signalLow >= (InpDemandLower - 5.0 * ExtPipSize));
   bool closedAboveDemand = (signalClose > InpDemandUpper && signalClose > signalOpen);

   if(longsAllowedByH4 && sweptDemand && closedAboveDemand && (isImpulsiveBody || isBullishEngulf))
   {
      if(ask < InpLongTargetPrice - (25.0 * ExtPipSize))
      {
         if(!InpUseMacroFilter || CheckMacroCorrelationFilter(ORDER_TYPE_BUY))
            ExecuteBaseEntry(ORDER_TYPE_BUY, ask, InpLongTargetPrice);
         else
            PrintFormat("M15 LONG Sweep filtered out by Macro Confluence: %s", ExtLastMacroDiagnostics);
         return;
      }
   }

   //--- 2. SHORT SETUP: H4 Supply Rejection (Wick >= 158.95 & < 159.20, Close < 158.85)
   bool sweptSupply       = (signalHigh >= InpSupplyUpper && signalHigh < InpMoFCeilingCutoff);
   bool closedBelowSupply = (signalClose <= InpSupplyLower && signalClose < signalOpen);

   if(sweptSupply && closedBelowSupply && (isImpulsiveBody || isBearishEngulf))
   {
      if(bid > InpShortTargetPrice + (25.0 * ExtPipSize))
      {
         if(!InpUseMacroFilter || CheckMacroCorrelationFilter(ORDER_TYPE_SELL))
            ExecuteBaseEntry(ORDER_TYPE_SELL, bid, InpShortTargetPrice);
         else
            PrintFormat("M15 SHORT Sweep filtered out by Macro Confluence: %s", ExtLastMacroDiagnostics);
         return;
      }
   }
}

//+------------------------------------------------------------------+
//| Multi-Symbol Macro Confluence Check (XAUUSD / US10Y)             |
//+------------------------------------------------------------------+
bool CheckMacroCorrelationFilter(ENUM_ORDER_TYPE usdjpyOrderType)
{
   ExtLastMacroDiagnostics = "";
   bool goldConfirmed = false;
   bool bondConfirmed = false;
   string goldDiag = "N/A";
   string bondDiag = "N/A";

   bool evaluateGold = (InpCorrSource == CORR_XAUUSD_ONLY ||
                        InpCorrSource == CORR_EITHER_CONFIRMS ||
                        InpCorrSource == CORR_BOTH_REQUIRED);

   bool evaluateBond = (InpCorrSource == CORR_US10Y_ONLY ||
                        InpCorrSource == CORR_EITHER_CONFIRMS ||
                        InpCorrSource == CORR_BOTH_REQUIRED);

   //--- 1. Evaluate XAUUSD (Always Inverse to USD/JPY: Bearish Gold = Bullish USD/JPY)
   if(evaluateGold)
   {
      goldConfirmed = EvaluateSingleSymbolMomentum(InpGoldSymbol,
                                                   ExtGoldATRHandle,
                                                   ExtGoldEMAHandle,
                                                   false, // Inverse correlation
                                                   usdjpyOrderType,
                                                   goldDiag);
   }

   //--- 2. Evaluate US10Y / T-Note (Direct if Yield, Inverse if Bond Price CFD)
   if(evaluateBond)
   {
      bondConfirmed = EvaluateSingleSymbolMomentum(InpBondSymbol,
                                                   ExtBondATRHandle,
                                                   ExtBondEMAHandle,
                                                   InpBondIsYieldDirect,
                                                   usdjpyOrderType,
                                                   bondDiag);
   }

   ExtLastMacroDiagnostics = StringFormat("[Gold: %s | Bond: %s]", goldDiag, bondDiag);

   //--- Combine according to selected correlation source rule
   if(InpCorrSource == CORR_XAUUSD_ONLY)
      return(goldConfirmed);
   if(InpCorrSource == CORR_US10Y_ONLY)
      return(bondConfirmed);
   if(InpCorrSource == CORR_EITHER_CONFIRMS)
      return(goldConfirmed || bondConfirmed);
   if(InpCorrSource == CORR_BOTH_REQUIRED)
      return(goldConfirmed && bondConfirmed);

   return(false);
}

//+------------------------------------------------------------------+
//| Compute ATR-Normalized Impulse & EMA Slope for a Secondary Asset |
//+------------------------------------------------------------------+
bool EvaluateSingleSymbolMomentum(string corrSymbol,
                                  int atrHandle,
                                  int emaHandle,
                                  bool isDirectCorrelation,
                                  ENUM_ORDER_TYPE usdjpyOrderType,
                                  string &outDiag)
{
   //--- Trigger tick/series refresh on secondary symbol
   MqlTick tick;
   SymbolInfoTick(corrSymbol, tick);

   int barsNeeded = InpMomentumLookback + 2;
   double closeBuf[];
   double atrBuf[];
   double emaBuf[];
   ArraySetAsSeries(closeBuf, true);
   ArraySetAsSeries(atrBuf, true);
   ArraySetAsSeries(emaBuf, true);

   //--- Copy closed M15 bars (start at shift 1)
   if(CopyClose(corrSymbol, InpEntryTF, 1, barsNeeded, closeBuf) < barsNeeded ||
      CopyBuffer(atrHandle, 0, 1, 2, atrBuf) < 2 ||
      CopyBuffer(emaHandle, 0, 1, 2, emaBuf) < 2)
   {
      outDiag = "SyncPending";
      return(false);
   }

   double atr = atrBuf[0];
   if(atr <= 0.0)
   {
      outDiag = "ZeroATR";
      return(false);
   }

   //--- Raw ATR-normalized impulse over N closed M15 bars: (Close[1] - Close[1 + N]) / ATR[1]
   double rawImpulseATR = (closeBuf[0] - closeBuf[InpMomentumLookback]) / atr;
   double emaSlopeATR   = (emaBuf[0] - emaBuf[1]) / atr;
   bool   aboveEMA      = (closeBuf[0] > emaBuf[0]);
   bool   belowEMA      = (closeBuf[0] < emaBuf[0]);

   //--- Translate into "USD/JPY Bullish Alignment Score"
   //--- If Direct (US10Y Yield): +Impulse = Bullish USD/JPY
   //--- If Inverse (XAUUSD / T-Note Price): -Impulse = Bullish USD/JPY
   double alignedBullishScore = isDirectCorrelation ? rawImpulseATR : -rawImpulseATR;
   double alignedEMASlope     = isDirectCorrelation ? emaSlopeATR   : -emaSlopeATR;
   bool   alignedEMAPosition  = isDirectCorrelation ? aboveEMA      : belowEMA;
   bool   alignedEMAShortPos  = isDirectCorrelation ? belowEMA      : aboveEMA;

   bool passed = false;

   if(InpFilterStrictness == STRICT_DIRECTIONAL)
   {
      if(usdjpyOrderType == ORDER_TYPE_BUY)
      {
         //--- Require positive USD/JPY alignment >= InpMinConfirmATR OR firm EMA trend
         passed = (alignedBullishScore >= InpMinConfirmATR ||
                  (alignedEMAPosition && alignedEMASlope > 0.0 && alignedBullishScore > -0.05));
      }
      else if(usdjpyOrderType == ORDER_TYPE_SELL)
      {
         //--- Require negative USD/JPY alignment <= -InpMinConfirmATR OR bearish EMA trend
         passed = (alignedBullishScore <= -InpMinConfirmATR ||
                  (alignedEMAShortPos && alignedEMASlope < 0.0 && alignedBullishScore < 0.05));
      }
   }
   else // VETO_OPPOSING_SPIKE
   {
      if(usdjpyOrderType == ORDER_TYPE_BUY)
      {
         //--- Allow USD/JPY Long unless secondary asset is spiking against USD/JPY
         passed = (alignedBullishScore > -InpMaxVetoSpikeATR);
      }
      else if(usdjpyOrderType == ORDER_TYPE_SELL)
      {
         //--- Allow USD/JPY Short unless secondary asset is spiking in favor of USD/JPY
         passed = (alignedBullishScore < InpMaxVetoSpikeATR);
      }
   }

   outDiag = StringFormat("%s RawROC:%.2fATR Aligned:%.2fATR (%s)",
                          corrSymbol, rawImpulseATR, alignedBullishScore, (passed ? "OK" : "BLOCKED"));
   return(passed);
}

//+------------------------------------------------------------------+
//| Reset daily starting balance at server midnight                  |
//+------------------------------------------------------------------+
void UpdateDailyBaseline()
{
   MqlDateTime dt;
   TimeCurrent(dt);
   if(dt.day_of_year != ExtCurrentDayOfYear)
   {
      ExtCurrentDayOfYear    = dt.day_of_year;
      ExtDayStartBalance     = AccountInfoDouble(ACCOUNT_BALANCE);
      ExtDailyCircuitTripped = false;
   }
}

//+------------------------------------------------------------------+
//| Verify 1.50% Daily Drawdown & Max Consecutive Stop-Outs          |
//+------------------------------------------------------------------+
bool CheckDailyCircuitBreaker()
{
   if(ExtDailyCircuitTripped)
      return(true);

   datetime dayStart = iTime(_Symbol, PERIOD_D1, 0);
   double   realizedToday = 0.0;
   int      losingTradesToday = 0;

   if(HistorySelect(dayStart, TimeCurrent()))
   {
      int totalDeals = HistoryDealsTotal();
      for(int i = 0; i < totalDeals; i++)
      {
         ulong dealTicket = HistoryDealGetTicket(i);
         if(dealTicket == 0) continue;
         if(HistoryDealGetInteger(dealTicket, DEAL_MAGIC) != (long)InpMagicNumber) continue;
         if(HistoryDealGetString(dealTicket, DEAL_SYMBOL) != _Symbol) continue;

         long dealEntry = HistoryDealGetInteger(dealTicket, DEAL_ENTRY);
         if(dealEntry == DEAL_ENTRY_OUT || dealEntry == DEAL_ENTRY_INOUT)
         {
            double netDealProfit = HistoryDealGetDouble(dealTicket, DEAL_PROFIT)
                                 + HistoryDealGetDouble(dealTicket, DEAL_COMMISSION)
                                 + HistoryDealGetDouble(dealTicket, DEAL_SWAP);
            realizedToday += netDealProfit;
            if(netDealProfit < -1.0)
               losingTradesToday++;
         }
      }
   }

   double floatingPnL = GetOpenFloatingPnL();
   double totalDailyPnL = realizedToday + floatingPnL;
   double maxAllowedLoss = -1.0 * ExtDayStartBalance * (InpMaxDailyDrawdownPct / 100.0);

   if(totalDailyPnL <= maxAllowedLoss || losingTradesToday >= InpMaxDailyLosingTrades)
   {
      ExtDailyCircuitTripped = true;
      string msg = StringFormat("Circuit Breaker Tripped on %s | Daily PnL: %.2f | Losses Today: %d",
                                _Symbol, totalDailyPnL, losingTradesToday);
      Print(msg);
      if(InpEnablePushNotify) SendNotification(msg);

      if(InpCloseAllOnDailyLimit)
         CloseAllEAPositions();

      return(true);
   }
   return(false);
}

//+------------------------------------------------------------------+
//| Tick-level Position Management: +20 Pip Lock & Leg 2 Scale-In    |
//+------------------------------------------------------------------+
void ManageOpenPositions()
{
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

   if(bid >= InpMoFCeilingCutoff)
   {
      if(HasOpenPositionByTag(LEG1_TAG) || HasOpenPositionByTag(LEG2_TAG))
      {
         Print("MoF Ceiling Cutoff breached (>= ", InpMoFCeilingCutoff, "). Flattening exposure.");
         CloseAllEAPositions();
      }
      return;
   }

   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(!ExtPosition.SelectByIndex(i)) continue;
      if(ExtPosition.Symbol() != _Symbol || ExtPosition.Magic() != InpMagicNumber) continue;
      if(StringFind(ExtPosition.Comment(), LEG1_TAG) < 0) continue;

      ulong  leg1Ticket    = ExtPosition.Ticket();
      double leg1OpenPrice = ExtPosition.PriceOpen();
      double leg1CurrentSL = ExtPosition.StopLoss();
      double leg1CurrentTP = ExtPosition.TakeProfit();
      double leg1Volume    = ExtPosition.Volume();
      ENUM_POSITION_TYPE posType = ExtPosition.PositionType();

      double triggerDist   = InpScaleTriggerPips * ExtPipSize;
      double lockDist      = InpLockedProfitPips * ExtPipSize;

      if(posType == POSITION_TYPE_BUY)
      {
         double currentGainPrice = bid - leg1OpenPrice;
         double targetLockSL     = NormalizeDouble(leg1OpenPrice + lockDist, _Digits);

         if(currentGainPrice >= triggerDist && leg1CurrentSL < targetLockSL - (_Point * 0.5))
         {
            if(ExtTrade.PositionModify(leg1Ticket, targetLockSL, leg1CurrentTP))
            {
               PrintFormat("Leg 1 BUY #%I64u SL locked at +%.1f pips (%.3f).",
                           leg1Ticket, InpLockedProfitPips, targetLockSL);

               if(!HasOpenPositionByTag(LEG2_TAG))
                  ExecuteHouseMoneyScaleIn(ORDER_TYPE_BUY, leg1Volume, lockDist, ask, leg1CurrentTP);
            }
         }
      }
      else if(posType == POSITION_TYPE_SELL)
      {
         double currentGainPrice = leg1OpenPrice - ask;
         double targetLockSL     = NormalizeDouble(leg1OpenPrice - lockDist, _Digits);

         if(currentGainPrice >= triggerDist && (leg1CurrentSL == 0.0 || leg1CurrentSL > targetLockSL + (_Point * 0.5)))
         {
            if(ExtTrade.PositionModify(leg1Ticket, targetLockSL, leg1CurrentTP))
            {
               PrintFormat("Leg 1 SELL #%I64u SL locked at +%.1f pips (%.3f).",
                           leg1Ticket, InpLockedProfitPips, targetLockSL);

               if(!HasOpenPositionByTag(LEG2_TAG))
                  ExecuteHouseMoneyScaleIn(ORDER_TYPE_SELL, leg1Volume, lockDist, bid, leg1CurrentTP);
            }
         }
      }
   }
}

//+------------------------------------------------------------------+
//| Open Leg 2 Funded Strictly by Locked-In Dollar Profit of Leg 1   |
//+------------------------------------------------------------------+
void ExecuteHouseMoneyScaleIn(ENUM_ORDER_TYPE orderType,
                              double leg1Volume,
                              double lockedPriceDistance,
                              double entryPrice,
                              double takeProfitPrice)
{
   double tickSize  = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE_LOSS);
   if(tickValue <= 0.0)
      tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);

   if(tickSize <= 0.0 || tickValue <= 0.0)
      return;

   double lockedProfitDollars = leg1Volume * (lockedPriceDistance / tickSize) * tickValue;
   double riskBudgetForLeg2   = lockedProfitDollars * InpCommissionBuffer;
   double leg2SLDistance      = InpScaleStopLossPips * ExtPipSize;
   double leg2Lots            = CalculateLotSizeFromMoney(riskBudgetForLeg2, leg2SLDistance);

   if(leg2Lots <= 0.0)
      return;

   double leg2SL = (orderType == ORDER_TYPE_BUY)
                   ? NormalizeDouble(entryPrice - leg2SLDistance, _Digits)
                   : NormalizeDouble(entryPrice + leg2SLDistance, _Digits);

   double leg2TP = NormalizeDouble(takeProfitPrice, _Digits);

   bool res = (orderType == ORDER_TYPE_BUY)
              ? ExtTrade.Buy(leg2Lots, _Symbol, entryPrice, leg2SL, leg2TP, LEG2_TAG)
              : ExtTrade.Sell(leg2Lots, _Symbol, entryPrice, leg2SL, leg2TP, LEG2_TAG);

   if(res)
   {
      string msg = StringFormat("House-Money Leg 2 Opened (%s %.2f lots @ %.3f) | Risk Budget: $%.2f ($0 Principal Risk)",
                                (orderType == ORDER_TYPE_BUY ? "BUY" : "SELL"),
                                leg2Lots, entryPrice, riskBudgetForLeg2);
      Print(msg);
      if(InpEnablePushNotify) SendNotification(msg);
   }
}

//+------------------------------------------------------------------+
//| Execute Leg 1 Base Entry (0.50% Risk)                            |
//+------------------------------------------------------------------+
void ExecuteBaseEntry(ENUM_ORDER_TYPE orderType, double entryPrice, double targetPrice)
{
   double balance    = AccountInfoDouble(ACCOUNT_BALANCE);
   double riskMoney  = balance * (InpBaseRiskPercent / 100.0);
   double slDistance = InpBaseStopLossPips * ExtPipSize;

   double lotSize    = CalculateLotSizeFromMoney(riskMoney, slDistance);
   if(lotSize <= 0.0)
      return;

   double slPrice = (orderType == ORDER_TYPE_BUY)
                    ? NormalizeDouble(entryPrice - slDistance, _Digits)
                    : NormalizeDouble(entryPrice + slDistance, _Digits);

   double tpPrice = NormalizeDouble(targetPrice, _Digits);

   bool res = (orderType == ORDER_TYPE_BUY)
              ? ExtTrade.Buy(lotSize, _Symbol, entryPrice, slPrice, tpPrice, LEG1_TAG)
              : ExtTrade.Sell(lotSize, _Symbol, entryPrice, slPrice, tpPrice, LEG1_TAG);

   if(res)
   {
      string msg = StringFormat("Leg 1 %s %.2f lots @ %.3f | SL: %.3f | TP: %.3f | Macro: %s",
                                (orderType == ORDER_TYPE_BUY ? "BUY" : "SELL"),
                                lotSize, entryPrice, slPrice, tpPrice, ExtLastMacroDiagnostics);
      Print(msg);
      if(InpEnablePushNotify) SendNotification(msg);
   }
}

//+------------------------------------------------------------------+
//| Calculate Normalized Lot Size from Dollar Risk & Price Distance  |
//+------------------------------------------------------------------+
double CalculateLotSizeFromMoney(double riskMoney, double slPriceDistance)
{
   if(riskMoney <= 0.0 || slPriceDistance <= 0.0)
      return(0.0);

   double tickSize  = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE_LOSS);
   if(tickValue <= 0.0)
      tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);

   if(tickSize <= 0.0 || tickValue <= 0.0)
      return(0.0);

   double lossPerLot = (slPriceDistance / tickSize) * tickValue;
   if(lossPerLot <= 0.0)
      return(0.0);

   double rawLots = riskMoney / lossPerLot;

   double minVol  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double maxVol  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   double stepVol = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);

   double normalizedLots = MathFloor(rawLots / stepVol) * stepVol;
   if(normalizedLots < minVol)
      return(0.0);
   if(normalizedLots > maxVol)
      normalizedLots = maxVol;

   return(NormalizeDouble(normalizedLots, 2));
}

//+------------------------------------------------------------------+
//| Helper: Check if Prime Tokyo or London-NY Overlap Window (UTC)   |
//+------------------------------------------------------------------+
bool IsWithinPrimeSessionUTC()
{
   MqlDateTime utc;
   TimeGMT(utc);

   bool inTokyo   = (utc.hour >= InpTokyoStartHourUTC && utc.hour < InpTokyoEndHourUTC);
   bool inOverlap = (utc.hour >= InpOverlapStartHourUTC && utc.hour < InpOverlapEndHourUTC);

   return(inTokyo || inOverlap);
}

//+------------------------------------------------------------------+
//| Helper: Verify Raw ECN Spread is Below Threshold                 |
//+------------------------------------------------------------------+
bool IsSpreadAcceptable()
{
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double currentSpreadPips = (ask - bid) / ExtPipSize;

   return(currentSpreadPips <= InpMaxSpreadPips);
}

//+------------------------------------------------------------------+
//| Helper: Check if a Position with Specific Comment Tag Exists     |
//+------------------------------------------------------------------+
bool HasOpenPositionByTag(string tag)
{
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(!ExtPosition.SelectByIndex(i)) continue;
      if(ExtPosition.Symbol() == _Symbol &&
         ExtPosition.Magic() == InpMagicNumber &&
         StringFind(ExtPosition.Comment(), tag) >= 0)
      {
         return(true);
      }
   }
   return(false);
}

//+------------------------------------------------------------------+
//| Helper: Get Total Floating PnL of Open Positions for this EA     |
//+------------------------------------------------------------------+
double GetOpenFloatingPnL()
{
   double floating = 0.0;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(!ExtPosition.SelectByIndex(i)) continue;
      if(ExtPosition.Symbol() == _Symbol && ExtPosition.Magic() == InpMagicNumber)
      {
         floating += ExtPosition.Profit() + ExtPosition.Swap() + ExtPosition.Commission();
      }
   }
   return(floating);
}

//+------------------------------------------------------------------+
//| Helper: Close All Open Positions Belonging to This EA            |
//+------------------------------------------------------------------+
void CloseAllEAPositions()
{
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(!ExtPosition.SelectByIndex(i)) continue;
      if(ExtPosition.Symbol() == _Symbol && ExtPosition.Magic() == InpMagicNumber)
      {
         ExtTrade.PositionClose(ExtPosition.Ticket());
      }
   }
}
//+------------------------------------------------------------------+
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 5027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How to build strategy to trade USD/JPY

Post by PTScalper »

3. Configuring the Multi-Symbol Inputs for Your Broker Feed

Using XAUUSD Only (Default & Universal ECN Setup):

Keep InpCorrSource = CORR_XAUUSD_ONLY and InpGoldSymbol = "XAUUSD".

Because Gold is priced against the USD and reacts instantaneously to US real-yield shifts, it works out of the box on every ECN feed without needing futures rollover adjustments.

Adding a 10-Year US Treasury Symbol (CORR_EITHER_CONFIRMS or CORR_BOTH_REQUIRED):

If your broker provides a Yield ticker (e.g., US10Y): Set InpBondSymbol = "US10Y" and InpBondIsYieldDirect = true (rising yield = bullish USD/JPY).

If your broker provides a 10-Year Treasury Note Futures/CFD price ticker (e.g., UST10Y_Z6 or TNOTE): Set InpBondSymbol to that exact Market Watch symbol and set InpBondIsYieldDirect = false (falling bond price = rising yield = bullish USD/JPY).

MT5 Strategy Tester Multi-Symbol Synchronization:

MetaTrader 5's Strategy Tester automatically downloads and synchronizes M15 tick history for XAUUSD (and US10Y) as soon as OnInit() calls SymbolSelect(). Run your backtest in "Every tick based on real ticks" mode so cross-symbol bid/ask timestamps align to the millisecond.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 5027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How to build strategy to trade USD/JPY

Post by PTScalper »

Replacing static price inputs with auto-detected H4 Fractal + ATR Supply and Demand zones allows the Expert Advisor to adapt seamlessly as USD/JPY shifts regimes, while eliminating look-ahead bias in the MetaTrader 5 Strategy Tester.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 5027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How to build strategy to trade USD/JPY

Post by PTScalper »

1. Mathematical & Algorithmic Specification (v3.00)

In MQL5, a Bill Williams 5-bar Fractal (iFractals) at bar index $k$ requires two closed bars to its right ($k-1$ and $k-2$) to confirm. Therefore, the earliest confirmed fractal without repainting is always at H4 shift $k \ge 3$.

To construct institutional-grade H4 liquidity zones from confirmed fractals:

Unmitigated Walk-Forward Validation:Scanning backward from H4 shift $k = 3$ up to InpFractalLookbackBars (default 120 H4 bars = 20 trading days), the EA locates the nearest confirmed Down Fractal below current price ($L_k$) and Up Fractal above current price ($H_k$) that have not been invalidated (broken) by any subsequent H4 candle close between bar $k-1$ and bar $1$:

Valid Demand Anchor ($L_k$): $\min_{j \in [1, k-1]} \text{Close}_{\text{H4}}[j] \ge L_k - \left(\text{ATR}_{\text{H4}, 14}[k] \times k_{\text{break}}\right)$

Valid Supply Anchor ($H_k$): $\max_{j \in [1, k-1]} \text{Close}_{\text{H4}}[j] \le H_k + \left(\text{ATR}_{\text{H4}, 14}[k] \times k_{\text{break}}\right)$
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 5027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How to build strategy to trade USD/JPY

Post by PTScalper »

2.) ATR-Scaled Zone Geometry:

Using the H4 candle's body edge (MathMin(Open, Close) for Demand; MathMax(Open, Close) for Supply) clamped by $\text{ATR}_{\text{H4}, 14}$ multipliers (InpMinZoneWidthATR = 0.20, InpMaxZoneWidthATR = 0.45).
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply