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 »

3.) Dynamic Opposite-Zone Targeting:

Long entries automatically place their Take-Profit at $\text{SupplyLower}$ (the front edge of the active H4 Supply zone), while Short entries target $\text{DemandUpper}$ (the front edge of the active H4 Demand zone), falling back to a configurable InpFallbackTargetATR multiple ($2.0 \times \text{ATR}_{\text{H4}}$) if no opposite fractal exists within the lookback window.
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 »

4. Complete MQL5 Expert Advisor Source Code (v3.00)

Save this over USDJPY_LiquiditySweep_ProfitLock.mq5 in MQL5\Experts\ and compile with F7. It also draws live semi-transparent H4 Demand and Supply rectangles directly on your chart so you can visually verify zone transitions in real time or in Visual Strategy Tester mode.

Code: Select all

//+------------------------------------------------------------------+
//|                         USDJPY_LiquiditySweep_ProfitLock.mq5     |
//|   v3.00: Auto H4 Fractal+ATR Zones + XAUUSD/US10Y + Profit-Lock  |
//+------------------------------------------------------------------+
#property copyright "USD/JPY Automated Strategy (October 2026 Regime)"
#property version   "3.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: Auto-Detected H4 Fractal & ATR Zones
input group "=== 1. Dynamic H4 Fractal + ATR Zone Engine ==="
input ENUM_TIMEFRAMES InpStructureTF       = PERIOD_H4;  // Structural Zone Timeframe
input int             InpFractalLookback   = 120;        // H4 Bars Lookback for Unmitigated Fractals
input int             InpStructureATRPeriod= 14;         // H4 ATR Period for Zone Width Scaling
input double          InpMinZoneWidthATR   = 0.20;       // Min H4 Zone Width (Multiple of H4 ATR)
input double          InpMaxZoneWidthATR   = 0.45;       // Max H4 Zone Width (Multiple of H4 ATR)
input double          InpSweepToleranceATR = 0.08;       // Extra Wick Overshoot Allowed Beyond Fractal (H4 ATR)
input double          InpBreakInvalidATR   = 0.15;       // H4 Close Beyond Zone to Invalidate (H4 ATR)
input double          InpFallbackTargetATR = 2.00;       // Fallback TP Distance if Opposite Zone Missing (H4 ATR)
input double          InpMoFCeilingCutoff  = 159.20;     // Hard MoF Intervention Ceiling Cutoff (JPY)
input bool            InpDrawZonesOnChart  = true;       // Render Active H4 Supply & Demand Boxes on Chart

//--- Input Parameters: Timeframes & Entry Triggers
input group "=== 2. M15 Execution & Spread Filters ==="
input ENUM_TIMEFRAMES InpEntryTF           = PERIOD_M15; // Execution Timeframe
input double          InpDisplacementRatio = 0.85;       // Min Closed Body / Prior 3-Bar Avg Range Ratio
input double          InpMinRewardToRisk   = 1.60;       // Min Required Distance to Opposite Zone (R-Multiple)
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 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 & Chart Objects
#define LEG1_TAG          "USDJPY_LEG1_BASE"
#define LEG2_TAG          "USDJPY_LEG2_SCALE"
#define OBJ_DEMAND_RECT   "USDJPY_H4_DEMAND_ZONE"
#define OBJ_SUPPLY_RECT   "USDJPY_H4_SUPPLY_ZONE"

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

//--- Dynamic H4 Zone State Variables
int            ExtH4FractalsHandle     = INVALID_HANDLE;
int            ExtH4ATRHandle          = INVALID_HANDLE;
bool           ExtHasValidDemand       = false;
bool           ExtHasValidSupply       = false;
double         ExtDemandLower          = 0.0;
double         ExtDemandUpper          = 0.0;
double         ExtDemandInvalidLevel   = 0.0;
datetime       ExtDemandAnchorTime     = 0;
double         ExtSupplyLower          = 0.0;
double         ExtSupplyUpper          = 0.0;
double         ExtSupplyInvalidLevel   = 0.0;
datetime       ExtSupplyAnchorTime     = 0;
double         ExtCurrentH4ATR         = 0.0;

//--- 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 H4 Fractals & H4 ATR Handles for Primary Symbol
   ExtH4FractalsHandle = iFractals(_Symbol, InpStructureTF);
   ExtH4ATRHandle      = iATR(_Symbol, InpStructureTF, InpStructureATRPeriod);
   if(ExtH4FractalsHandle == INVALID_HANDLE || ExtH4ATRHandle == INVALID_HANDLE)
   {
      Print("ERROR: Failed to create H4 iFractals or iATR handles for ", _Symbol);
      return(INIT_FAILED);
   }

   //--- 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)
            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)
            return(INIT_FAILED);
      }
   }

   //--- Perform Initial H4 Zone Calculation
   UpdateDynamicH4Zones();

   PrintFormat("USDJPY_LiquiditySweep_ProfitLock v3.00 initialized | Auto H4 Zones: Demand [%.3f - %.3f] Supply [%.3f - %.3f]",
               ExtDemandLower, ExtDemandUpper, ExtSupplyLower, ExtSupplyUpper);
   return(INIT_SUCCEEDED);
}

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

   ObjectDelete(0, OBJ_DEMAND_RECT);
   ObjectDelete(0, OBJ_SUPPLY_RECT);
   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. Recalculate Dynamic H4 Fractal + ATR Zones on New H4 Bar (or if unset)
   datetime currentH4BarTime = iTime(_Symbol, InpStructureTF, 0);
   if(currentH4BarTime != ExtLastH4BarTime || (!ExtHasValidDemand && !ExtHasValidSupply))
   {
      if(UpdateDynamicH4Zones())
         ExtLastH4BarTime = currentH4BarTime;
   }

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

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

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

   if(!IsSpreadAcceptable())
      return;

   //--- Mark M15 bar as processed and evaluate sweep signals against dynamic H4 zones
   ExtLastM15BarTime = currentM15BarTime;
   EvaluateM15EntrySignals();
}

//+------------------------------------------------------------------+
//| Scan Confirmed H4 Fractals (Shift >= 3) & Build ATR Supply/Demand|
//+------------------------------------------------------------------+
bool UpdateDynamicH4Zones()
{
   int lookback = InpFractalLookback;
   MqlRates h4Rates[];
   double   upFractals[];
   double   downFractals[];
   double   h4ATR[];

   ArraySetAsSeries(h4Rates, true);
   ArraySetAsSeries(upFractals, true);
   ArraySetAsSeries(downFractals, true);
   ArraySetAsSeries(h4ATR, true);

   if(CopyRates(_Symbol, InpStructureTF, 0, lookback, h4Rates) < lookback ||
      CopyBuffer(ExtH4FractalsHandle, UPPER_LINE, 0, lookback, upFractals) < lookback ||
      CopyBuffer(ExtH4FractalsHandle, LOWER_LINE, 0, lookback, downFractals) < lookback ||
      CopyBuffer(ExtH4ATRHandle, 0, 0, lookback, h4ATR) < lookback)
   {
      return(false);
   }

   ExtCurrentH4ATR   = h4ATR[1];
   if(ExtCurrentH4ATR <= 0.0)
      return(false);

   double refPrice   = h4Rates[1].close;
   ExtHasValidDemand = false;
   ExtHasValidSupply = false;

   //--- 1. Locate Nearest Unmitigated H4 Down Fractal (Demand Zone)
   //--- Start at shift 3 because a 5-bar fractal at shift k needs bars k-1 and k-2 closed to confirm
   for(int k = 3; k < lookback - 3; k++)
   {
      double fracLow = downFractals[k];
      if(fracLow == 0.0 || fracLow == EMPTY_VALUE)
         continue;

      double atrAtK       = (h4ATR[k] > 0.0) ? h4ATR[k] : ExtCurrentH4ATR;
      double invalidLevel = fracLow - (atrAtK * InpBreakInvalidATR);

      //--- Current H4 close must still be above the invalidation threshold
      if(refPrice < invalidLevel)
         continue;

      //--- Walk forward from bar k-1 down to bar 1 to verify no H4 candle closed below invalidLevel
      bool brokenBySubsequentClose = false;
      for(int j = k - 1; j >= 1; j--)
      {
         if(h4Rates[j].close < invalidLevel)
         {
            brokenBySubsequentClose = true;
            break;
         }
      }

      if(!brokenBySubsequentClose)
      {
         double rawBodyDist   = MathMin(h4Rates[k].open, h4Rates[k].close) - fracLow;
         double minWidth      = atrAtK * InpMinZoneWidthATR;
         double maxWidth      = atrAtK * InpMaxZoneWidthATR;
         double zoneWidth     = MathMax(minWidth, MathMin(maxWidth, rawBodyDist));

         ExtDemandLower        = NormalizeDouble(fracLow, _Digits);
         ExtDemandUpper        = NormalizeDouble(fracLow + zoneWidth, _Digits);
         ExtDemandInvalidLevel = NormalizeDouble(invalidLevel, _Digits);
         ExtDemandAnchorTime   = h4Rates[k].time;
         ExtHasValidDemand     = true;
         break; // Nearest unmitigated Demand zone found
      }
   }

   //--- 2. Locate Nearest Unmitigated H4 Up Fractal (Supply Zone)
   for(int k = 3; k < lookback - 3; k++)
   {
      double fracHigh = upFractals[k];
      if(fracHigh == 0.0 || fracHigh == EMPTY_VALUE)
         continue;

      //--- Cap Supply zone below hard MoF intervention ceiling
      if(fracHigh >= InpMoFCeilingCutoff)
         continue;

      double atrAtK       = (h4ATR[k] > 0.0) ? h4ATR[k] : ExtCurrentH4ATR;
      double invalidLevel = fracHigh + (atrAtK * InpBreakInvalidATR);

      if(refPrice > invalidLevel)
         continue;

      //--- Verify no subsequent H4 candle closed above invalidLevel
      bool brokenBySubsequentClose = false;
      for(int j = k - 1; j >= 1; j--)
      {
         if(h4Rates[j].close > invalidLevel)
         {
            brokenBySubsequentClose = true;
            break;
         }
      }

      if(!brokenBySubsequentClose)
      {
         double rawBodyDist   = fracHigh - MathMax(h4Rates[k].open, h4Rates[k].close);
         double minWidth      = atrAtK * InpMinZoneWidthATR;
         double maxWidth      = atrAtK * InpMaxZoneWidthATR;
         double zoneWidth     = MathMax(minWidth, MathMin(maxWidth, rawBodyDist));

         ExtSupplyUpper        = NormalizeDouble(fracHigh, _Digits);
         ExtSupplyLower        = NormalizeDouble(fracHigh - zoneWidth, _Digits);
         ExtSupplyInvalidLevel = NormalizeDouble(invalidLevel, _Digits);
         ExtSupplyAnchorTime   = h4Rates[k].time;
         ExtHasValidSupply     = true;
         break; // Nearest unmitigated Supply zone found
      }
   }

   if(InpDrawZonesOnChart)
      RenderZonesOnChart(h4Rates[0].time);

   return(true);
}

//+------------------------------------------------------------------+
//| Render Active H4 Demand & Supply Rectangles on Chart             |
//+------------------------------------------------------------------+
void RenderZonesOnChart(datetime currentBarTime)
{
   datetime rightEdge = currentBarTime + PeriodSeconds(InpStructureTF) * 6;

   if(ExtHasValidDemand)
   {
      if(ObjectFind(0, OBJ_DEMAND_RECT) < 0)
         ObjectCreate(0, OBJ_DEMAND_RECT, OBJ_RECTANGLE, 0, ExtDemandAnchorTime, ExtDemandUpper, rightEdge, ExtDemandLower);
      else
      {
         ObjectMove(0, OBJ_DEMAND_RECT, 0, ExtDemandAnchorTime, ExtDemandUpper);
         ObjectMove(0, OBJ_DEMAND_RECT, 1, rightEdge, ExtDemandLower);
      }
      ObjectSetInteger(0, OBJ_DEMAND_RECT, OBJPROP_COLOR, clrDodgerBlue);
      ObjectSetInteger(0, OBJ_DEMAND_RECT, OBJPROP_FILL, true);
      ObjectSetInteger(0, OBJ_DEMAND_RECT, OBJPROP_BACK, true);
      ObjectSetString(0, OBJ_DEMAND_RECT, OBJPROP_TOOLTIP,
                      StringFormat("H4 Demand: %.3f - %.3f (Invalid < %.3f)",
                                   ExtDemandLower, ExtDemandUpper, ExtDemandInvalidLevel));
   }
   else
      ObjectDelete(0, OBJ_DEMAND_RECT);

   if(ExtHasValidSupply)
   {
      if(ObjectFind(0, OBJ_SUPPLY_RECT) < 0)
         ObjectCreate(0, OBJ_SUPPLY_RECT, OBJ_RECTANGLE, 0, ExtSupplyAnchorTime, ExtSupplyUpper, rightEdge, ExtSupplyLower);
      else
      {
         ObjectMove(0, OBJ_SUPPLY_RECT, 0, ExtSupplyAnchorTime, ExtSupplyUpper);
         ObjectMove(0, OBJ_SUPPLY_RECT, 1, rightEdge, ExtSupplyLower);
      }
      ObjectSetInteger(0, OBJ_SUPPLY_RECT, OBJPROP_COLOR, clrCrimson);
      ObjectSetInteger(0, OBJ_SUPPLY_RECT, OBJPROP_FILL, true);
      ObjectSetInteger(0, OBJ_SUPPLY_RECT, OBJPROP_BACK, true);
      ObjectSetString(0, OBJ_SUPPLY_RECT, OBJPROP_TOOLTIP,
                      StringFormat("H4 Supply: %.3f - %.3f (Invalid > %.3f)",
                                   ExtSupplyLower, ExtSupplyUpper, ExtSupplyInvalidLevel));
   }
   else
      ObjectDelete(0, OBJ_SUPPLY_RECT);
}

//+------------------------------------------------------------------+
//| Evaluate Closed M15 Candle Against Auto-Detected H4 Zones        |
//+------------------------------------------------------------------+
void EvaluateM15EntrySignals()
{
   MqlRates rates[];
   ArraySetAsSeries(rates, true);
   if(CopyRates(_Symbol, InpEntryTF, 1, 4, rates) < 4)
      return;

   double h4Close = iClose(_Symbol, InpStructureTF, 1);

   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);
   double slDistance    = InpBaseStopLossPips * ExtPipSize;
   double minTargetDist = slDistance * InpMinRewardToRisk;
   double sweepBuffer   = (ExtCurrentH4ATR > 0.0) ? (ExtCurrentH4ATR * InpSweepToleranceATR) : (5.0 * ExtPipSize);

   //--- 1. LONG SETUP: Dynamic H4 Demand Liquidity Sweep
   if(ExtHasValidDemand && h4Close >= ExtDemandInvalidLevel)
   {
      bool sweptDemand       = (signalLow <= ExtDemandUpper && signalLow >= (ExtDemandLower - sweepBuffer));
      bool closedAboveDemand = (signalClose > ExtDemandUpper && signalClose > signalOpen);

      if(sweptDemand && closedAboveDemand && (isImpulsiveBody || isBullishEngulf))
      {
         //--- Target opposite H4 Supply front edge, or fallback ATR multiple if Supply is missing
         double targetTP = (ExtHasValidSupply && ExtSupplyLower > ask + minTargetDist)
                           ? ExtSupplyLower
                           : NormalizeDouble(ask + MathMax(minTargetDist, ExtCurrentH4ATR * InpFallbackTargetATR), _Digits);

         //--- Cap Long TP safely below MoF Intervention Ceiling
         targetTP = MathMin(targetTP, InpMoFCeilingCutoff - (10.0 * ExtPipSize));

         if(targetTP - ask >= minTargetDist)
         {
            if(!InpUseMacroFilter || CheckMacroCorrelationFilter(ORDER_TYPE_BUY))
               ExecuteBaseEntry(ORDER_TYPE_BUY, ask, targetTP);
            else
               PrintFormat("M15 Dynamic LONG Sweep [%.3f-%.3f] filtered out by Macro: %s",
                           ExtDemandLower, ExtDemandUpper, ExtLastMacroDiagnostics);
            return;
         }
      }
   }

   //--- 2. SHORT SETUP: Dynamic H4 Supply Liquidity Sweep
   if(ExtHasValidSupply && h4Close <= ExtSupplyInvalidLevel)
   {
      bool sweptSupply       = (signalHigh >= ExtSupplyLower &&
                                signalHigh <= (ExtSupplyUpper + sweepBuffer) &&
                                signalHigh < InpMoFCeilingCutoff);
      bool closedBelowSupply = (signalClose < ExtSupplyLower && signalClose < signalOpen);

      if(sweptSupply && closedBelowSupply && (isImpulsiveBody || isBearishEngulf))
      {
         //--- Target opposite H4 Demand upper edge, or fallback ATR multiple if Demand is missing
         double targetTP = (ExtHasValidDemand && ExtDemandUpper < bid - minTargetDist)
                           ? ExtDemandUpper
                           : NormalizeDouble(bid - MathMax(minTargetDist, ExtCurrentH4ATR * InpFallbackTargetATR), _Digits);

         if(bid - targetTP >= minTargetDist)
         {
            if(!InpUseMacroFilter || CheckMacroCorrelationFilter(ORDER_TYPE_SELL))
               ExecuteBaseEntry(ORDER_TYPE_SELL, bid, targetTP);
            else
               PrintFormat("M15 Dynamic SHORT Sweep [%.3f-%.3f] filtered out by Macro: %s",
                           ExtSupplyLower, ExtSupplyUpper, 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);

   if(evaluateGold)
   {
      goldConfirmed = EvaluateSingleSymbolMomentum(InpGoldSymbol,
                                                   ExtGoldATRHandle,
                                                   ExtGoldEMAHandle,
                                                   false,
                                                   usdjpyOrderType,
                                                   goldDiag);
   }

   if(evaluateBond)
   {
      bondConfirmed = EvaluateSingleSymbolMomentum(InpBondSymbol,
                                                   ExtBondATRHandle,
                                                   ExtBondEMAHandle,
                                                   InpBondIsYieldDirect,
                                                   usdjpyOrderType,
                                                   bondDiag);
   }

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

   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)
{
   MqlTick tick;
   SymbolInfoTick(corrSymbol, tick);

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

   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);
   }

   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]);

   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)
         passed = (alignedBullishScore >= InpMinConfirmATR ||
                  (alignedEMAPosition && alignedEMASlope > 0.0 && alignedBullishScore > -0.05));
      else if(usdjpyOrderType == ORDER_TYPE_SELL)
         passed = (alignedBullishScore <= -InpMinConfirmATR ||
                  (alignedEMAShortPos && alignedEMASlope < 0.0 && alignedBullishScore < 0.05));
   }
   else
   {
      if(usdjpyOrderType == ORDER_TYPE_BUY)
         passed = (alignedBullishScore > -InpMaxVetoSpikeATR);
      else if(usdjpyOrderType == ORDER_TYPE_SELL)
         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 zoneInfo = (orderType == ORDER_TYPE_BUY)
                        ? StringFormat("H4 Demand [%.3f-%.3f]", ExtDemandLower, ExtDemandUpper)
                        : StringFormat("H4 Supply [%.3f-%.3f]", ExtSupplyLower, ExtSupplyUpper);

      string msg = StringFormat("Leg 1 %s %.2f lots @ %.3f | SL: %.3f | TP: %.3f | %s | Macro: %s",
                                (orderType == ORDER_TYPE_BUY ? "BUY" : "SELL"),
                                lotSize, entryPrice, slPrice, tpPrice, zoneInfo, 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 »

To give you complete visibility into the Expert Advisor's internal state machine, macro filters, and risk controls without cluttering your MetaTrader 5 terminal, this upgrade adds a clean, real-time on-chart MQL5 telemetry dashboard.

The panel renders as a fixed HUD overlay in the top-left corner of the chart, updating on every tick to display active H4 zone boundaries, cross-asset momentum scores, live profit-lock status, and intraday drawdown meters.
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. Upgraded MQL5 Expert Advisor (v4.00)

Save this updated code over USDJPY_LiquiditySweep_ProfitLock.mq5 in your MQL5\Experts\ folder and compile with F7.

Code: Select all

//+------------------------------------------------------------------+
//|                         USDJPY_LiquiditySweep_ProfitLock.mq5     |
//|   v4.00: Auto H4 Fractal+ATR Zones + Macro Filter + HUD Dashboard|
//+------------------------------------------------------------------+
#property copyright "USD/JPY Automated Strategy (October 2026 Regime)"
#property version   "4.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: Auto-Detected H4 Fractal & ATR Zones
input group "=== 1. Dynamic H4 Fractal + ATR Zone Engine ==="
input ENUM_TIMEFRAMES InpStructureTF       = PERIOD_H4;  // Structural Zone Timeframe
input int             InpFractalLookback   = 120;        // H4 Bars Lookback for Unmitigated Fractals
input int             InpStructureATRPeriod= 14;         // H4 ATR Period for Zone Width Scaling
input double          InpMinZoneWidthATR   = 0.20;       // Min H4 Zone Width (Multiple of H4 ATR)
input double          InpMaxZoneWidthATR   = 0.45;       // Max H4 Zone Width (Multiple of H4 ATR)
input double          InpSweepToleranceATR = 0.08;       // Extra Wick Overshoot Allowed Beyond Fractal (H4 ATR)
input double          InpBreakInvalidATR   = 0.15;       // H4 Close Beyond Zone to Invalidate (H4 ATR)
input double          InpFallbackTargetATR = 2.00;       // Fallback TP Distance if Opposite Zone Missing (H4 ATR)
input double          InpMoFCeilingCutoff  = 159.20;     // Hard MoF Intervention Ceiling Cutoff (JPY)
input bool            InpDrawZonesOnChart  = true;       // Render Active H4 Supply & Demand Boxes on Chart

//--- Input Parameters: Timeframes & Entry Triggers
input group "=== 2. M15 Execution & Spread Filters ==="
input ENUM_TIMEFRAMES InpEntryTF           = PERIOD_M15; // Execution Timeframe
input double          InpDisplacementRatio = 0.85;       // Min Closed Body / Prior 3-Bar Avg Range Ratio
input double          InpMinRewardToRisk   = 1.60;       // Min Required Distance to Opposite Zone (R-Multiple)
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 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 & Telemetry Dashboard
input group "=== 6. Telemetry Dashboard & Notifications ==="
input bool     InpShowDashboard        = true;        // Render On-Chart Telemetry HUD Panel
input ulong    InpMagicNumber          = 20261019;    // EA Magic Number
input bool     InpEnablePushNotify     = true;        // Send MT5 Mobile Push Notifications

//--- Constants for Leg Tracking & Chart Objects
#define LEG1_TAG          "USDJPY_LEG1_BASE"
#define LEG2_TAG          "USDJPY_LEG2_SCALE"
#define OBJ_DEMAND_RECT   "USDJPY_H4_DEMAND_ZONE"
#define OBJ_SUPPLY_RECT   "USDJPY_H4_SUPPLY_ZONE"
#define DASH_PREFIX       "USDJPY_HUD_"

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

//--- Dynamic H4 Zone State Variables
int            ExtH4FractalsHandle     = INVALID_HANDLE;
int            ExtH4ATRHandle          = INVALID_HANDLE;
bool           ExtHasValidDemand       = false;
bool           ExtHasValidSupply       = false;
double         ExtDemandLower          = 0.0;
double         ExtDemandUpper          = 0.0;
double         ExtDemandInvalidLevel   = 0.0;
datetime       ExtDemandAnchorTime     = 0;
double         ExtSupplyLower          = 0.0;
double         ExtSupplyUpper          = 0.0;
double         ExtSupplyInvalidLevel   = 0.0;
datetime       ExtSupplyAnchorTime     = 0;
double         ExtCurrentH4ATR         = 0.0;

//--- Cross-Asset Indicator Handles & Telemetry
int            ExtGoldATRHandle        = INVALID_HANDLE;
int            ExtGoldEMAHandle        = INVALID_HANDLE;
int            ExtBondATRHandle        = INVALID_HANDLE;
int            ExtBondEMAHandle        = INVALID_HANDLE;
string         ExtLastMacroDiagnostics = "Waiting...";
string         ExtGoldScoreDiag        = "N/A";
string         ExtBondScoreDiag        = "N/A";
double         ExtLockedProfitDollars  = 0.0;

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

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

   ExtH4FractalsHandle = iFractals(_Symbol, InpStructureTF);
   ExtH4ATRHandle      = iATR(_Symbol, InpStructureTF, InpStructureATRPeriod);
   if(ExtH4FractalsHandle == INVALID_HANDLE || ExtH4ATRHandle == INVALID_HANDLE)
      return(INIT_FAILED);

   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)) return(INIT_FAILED);
         ExtGoldATRHandle = iATR(InpGoldSymbol, InpEntryTF, InpMacroATRPeriod);
         ExtGoldEMAHandle = iMA(InpGoldSymbol, InpEntryTF, InpMacroEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
      }
      if(needBond)
      {
         if(!SymbolSelect(InpBondSymbol, true)) return(INIT_FAILED);
         ExtBondATRHandle = iATR(InpBondSymbol, InpEntryTF, InpMacroATRPeriod);
         ExtBondEMAHandle = iMA(InpBondSymbol, InpEntryTF, InpMacroEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
      }
   }

   UpdateDynamicH4Zones();
   return(INIT_SUCCEEDED);
}

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

   ObjectDelete(0, OBJ_DEMAND_RECT);
   ObjectDelete(0, OBJ_SUPPLY_RECT);
   RemoveDashboardObjects();
   Comment("");
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   UpdateDailyBaseline();
   if(CheckDailyCircuitBreaker())
   {
      if(InpShowDashboard) RenderDashboard();
      return;
   }

   ManageOpenPositions();

   datetime currentH4BarTime = iTime(_Symbol, InpStructureTF, 0);
   if(currentH4BarTime != ExtLastH4BarTime || (!ExtHasValidDemand && !ExtHasValidSupply))
   {
      if(UpdateDynamicH4Zones())
         ExtLastH4BarTime = currentH4BarTime;
   }

   //--- Render On-Chart HUD Dashboard on Every Tick
   if(InpShowDashboard)
      RenderDashboard();

   datetime currentM15BarTime = iTime(_Symbol, InpEntryTF, 0);
   if(currentM15BarTime == ExtLastM15BarTime)
      return;

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

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

   if(!IsSpreadAcceptable())
      return;

   ExtLastM15BarTime = currentM15BarTime;
   EvaluateM15EntrySignals();
}

//+------------------------------------------------------------------+
//| Scan Confirmed H4 Fractals & Build ATR Supply/Demand Zones       |
//+------------------------------------------------------------------+
bool UpdateDynamicH4Zones()
{
   int lookback = InpFractalLookback;
   MqlRates h4Rates[];
   double   upFractals[];
   double   downFractals[];
   double   h4ATR[];

   ArraySetAsSeries(h4Rates, true);
   ArraySetAsSeries(upFractals, true);
   ArraySetAsSeries(downFractals, true);
   ArraySetAsSeries(h4ATR, true);

   if(CopyRates(_Symbol, InpStructureTF, 0, lookback, h4Rates) < lookback ||
      CopyBuffer(ExtH4FractalsHandle, UPPER_LINE, 0, lookback, upFractals) < lookback ||
      CopyBuffer(ExtH4FractalsHandle, LOWER_LINE, 0, lookback, downFractals) < lookback ||
      CopyBuffer(ExtH4ATRHandle, 0, 0, lookback, h4ATR) < lookback)
   {
      return(false);
   }

   ExtCurrentH4ATR   = h4ATR[1];
   if(ExtCurrentH4ATR <= 0.0) return(false);

   double refPrice   = h4Rates[1].close;
   ExtHasValidDemand = false;
   ExtHasValidSupply = false;

   for(int k = 3; k < lookback - 3; k++)
   {
      double fracLow = downFractals[k];
      if(fracLow == 0.0 || fracLow == EMPTY_VALUE) continue;

      double atrAtK       = (h4ATR[k] > 0.0) ? h4ATR[k] : ExtCurrentH4ATR;
      double invalidLevel = fracLow - (atrAtK * InpBreakInvalidATR);
      if(refPrice < invalidLevel) continue;

      bool broken = false;
      for(int j = k - 1; j >= 1; j--)
      {
         if(h4Rates[j].close < invalidLevel) { broken = true; break; }
      }

      if(!broken)
      {
         double rawBodyDist = MathMin(h4Rates[k].open, h4Rates[k].close) - fracLow;
         double zoneWidth   = MathMax(atrAtK * InpMinZoneWidthATR, MathMin(atrAtK * InpMaxZoneWidthATR, rawBodyDist));

         ExtDemandLower        = NormalizeDouble(fracLow, _Digits);
         ExtDemandUpper        = NormalizeDouble(fracLow + zoneWidth, _Digits);
         ExtDemandInvalidLevel = NormalizeDouble(invalidLevel, _Digits);
         ExtDemandAnchorTime   = h4Rates[k].time;
         ExtHasValidDemand     = true;
         break;
      }
   }

   for(int k = 3; k < lookback - 3; k++)
   {
      double fracHigh = upFractals[k];
      if(fracHigh == 0.0 || fracHigh == EMPTY_VALUE) continue;
      if(fracHigh >= InpMoFCeilingCutoff) continue;

      double atrAtK       = (h4ATR[k] > 0.0) ? h4ATR[k] : ExtCurrentH4ATR;
      double invalidLevel = fracHigh + (atrAtK * InpBreakInvalidATR);
      if(refPrice > invalidLevel) continue;

      bool broken = false;
      for(int j = k - 1; j >= 1; j--)
      {
         if(h4Rates[j].close > invalidLevel) { broken = true; break; }
      }

      if(!broken)
      {
         double rawBodyDist = fracHigh - MathMax(h4Rates[k].open, h4Rates[k].close);
         double zoneWidth   = MathMax(atrAtK * InpMinZoneWidthATR, MathMin(atrAtK * InpMaxZoneWidthATR, rawBodyDist));

         ExtSupplyUpper        = NormalizeDouble(fracHigh, _Digits);
         ExtSupplyLower        = NormalizeDouble(fracHigh - zoneWidth, _Digits);
         ExtSupplyInvalidLevel = NormalizeDouble(invalidLevel, _Digits);
         ExtSupplyAnchorTime   = h4Rates[k].time;
         ExtHasValidSupply     = true;
         break;
      }
   }

   if(InpDrawZonesOnChart)
      RenderZonesOnChart(h4Rates[0].time);

   return(true);
}

//+------------------------------------------------------------------+
//| Render Active H4 Demand & Supply Rectangles on Chart             |
//+------------------------------------------------------------------+
void RenderZonesOnChart(datetime currentBarTime)
{
   datetime rightEdge = currentBarTime + PeriodSeconds(InpStructureTF) * 6;

   if(ExtHasValidDemand)
   {
      if(ObjectFind(0, OBJ_DEMAND_RECT) < 0)
         ObjectCreate(0, OBJ_DEMAND_RECT, OBJ_RECTANGLE, 0, ExtDemandAnchorTime, ExtDemandUpper, rightEdge, ExtDemandLower);
      else
      {
         ObjectMove(0, OBJ_DEMAND_RECT, 0, ExtDemandAnchorTime, ExtDemandUpper);
         ObjectMove(0, OBJ_DEMAND_RECT, 1, rightEdge, ExtDemandLower);
      }
      ObjectSetInteger(0, OBJ_DEMAND_RECT, OBJPROP_COLOR, clrDodgerBlue);
      ObjectSetInteger(0, OBJ_DEMAND_RECT, OBJPROP_FILL, true);
      ObjectSetInteger(0, OBJ_DEMAND_RECT, OBJPROP_BACK, true);
   }
   else ObjectDelete(0, OBJ_DEMAND_RECT);

   if(ExtHasValidSupply)
   {
      if(ObjectFind(0, OBJ_SUPPLY_RECT) < 0)
         ObjectCreate(0, OBJ_SUPPLY_RECT, OBJ_RECTANGLE, 0, ExtSupplyAnchorTime, ExtSupplyUpper, rightEdge, ExtSupplyLower);
      else
      {
         ObjectMove(0, OBJ_SUPPLY_RECT, 0, ExtSupplyAnchorTime, ExtSupplyUpper);
         ObjectMove(0, OBJ_SUPPLY_RECT, 1, rightEdge, ExtSupplyLower);
      }
      ObjectSetInteger(0, OBJ_SUPPLY_RECT, OBJPROP_COLOR, clrCrimson);
      ObjectSetInteger(0, OBJ_SUPPLY_RECT, OBJPROP_FILL, true);
      ObjectSetInteger(0, OBJ_SUPPLY_RECT, OBJPROP_BACK, true);
   }
   else ObjectDelete(0, OBJ_SUPPLY_RECT);
}

//+------------------------------------------------------------------+
//| Evaluate M15 Entry Signals Against Dynamic H4 Zones              |
//+------------------------------------------------------------------+
void EvaluateM15EntrySignals()
{
   MqlRates rates[];
   ArraySetAsSeries(rates, true);
   if(CopyRates(_Symbol, InpEntryTF, 1, 4, rates) < 4) return;

   double h4Close = iClose(_Symbol, InpStructureTF, 1);
   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);
   double slDistance    = InpBaseStopLossPips * ExtPipSize;
   double minTargetDist = slDistance * InpMinRewardToRisk;
   double sweepBuffer   = (ExtCurrentH4ATR > 0.0) ? (ExtCurrentH4ATR * InpSweepToleranceATR) : (5.0 * ExtPipSize);

   if(ExtHasValidDemand && h4Close >= ExtDemandInvalidLevel)
   {
      bool sweptDemand       = (signalLow <= ExtDemandUpper && signalLow >= (ExtDemandLower - sweepBuffer));
      bool closedAboveDemand = (signalClose > ExtDemandUpper && signalClose > signalOpen);

      if(sweptDemand && closedAboveDemand && (isImpulsiveBody || isBullishEngulf))
      {
         double targetTP = (ExtHasValidSupply && ExtSupplyLower > ask + minTargetDist)
                           ? ExtSupplyLower
                           : NormalizeDouble(ask + MathMax(minTargetDist, ExtCurrentH4ATR * InpFallbackTargetATR), _Digits);

         targetTP = MathMin(targetTP, InpMoFCeilingCutoff - (10.0 * ExtPipSize));

         if(targetTP - ask >= minTargetDist)
         {
            if(!InpUseMacroFilter || CheckMacroCorrelationFilter(ORDER_TYPE_BUY))
               ExecuteBaseEntry(ORDER_TYPE_BUY, ask, targetTP);
            return;
         }
      }
   }

   if(ExtHasValidSupply && h4Close <= ExtSupplyInvalidLevel)
   {
      bool sweptSupply       = (signalHigh >= ExtSupplyLower && signalHigh <= (ExtSupplyUpper + sweepBuffer) && signalHigh < InpMoFCeilingCutoff);
      bool closedBelowSupply = (signalClose < ExtSupplyLower && signalClose < signalOpen);

      if(sweptSupply && closedBelowSupply && (isImpulsiveBody || isBearishEngulf))
      {
         double targetTP = (ExtHasValidDemand && ExtDemandUpper < bid - minTargetDist)
                           ? ExtDemandUpper
                           : NormalizeDouble(bid - MathMax(minTargetDist, ExtCurrentH4ATR * InpFallbackTargetATR), _Digits);

         if(bid - targetTP >= minTargetDist)
         {
            if(!InpUseMacroFilter || CheckMacroCorrelationFilter(ORDER_TYPE_SELL))
               ExecuteBaseEntry(ORDER_TYPE_SELL, bid, targetTP);
            return;
         }
      }
   }
}

//+------------------------------------------------------------------+
//| Multi-Symbol Macro Confluence Check                              |
//+------------------------------------------------------------------+
bool CheckMacroCorrelationFilter(ENUM_ORDER_TYPE usdjpyOrderType)
{
   ExtLastMacroDiagnostics = "";
   bool goldConfirmed = false;
   bool bondConfirmed = false;

   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);

   if(evaluateGold)
      goldConfirmed = EvaluateSingleSymbolMomentum(InpGoldSymbol, ExtGoldATRHandle, ExtGoldEMAHandle, false, usdjpyOrderType, ExtGoldScoreDiag);
   if(evaluateBond)
      bondConfirmed = EvaluateSingleSymbolMomentum(InpBondSymbol, ExtBondATRHandle, ExtBondEMAHandle, InpBondIsYieldDirect, usdjpyOrderType, ExtBondScoreDiag);

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

   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                       |
//+------------------------------------------------------------------+
bool EvaluateSingleSymbolMomentum(string corrSymbol, int atrHandle, int emaHandle, bool isDirect, ENUM_ORDER_TYPE orderType, string &outDiag)
{
   MqlTick tick;
   SymbolInfoTick(corrSymbol, tick);

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

   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); }

   double rawImpulseATR = (closeBuf[0] - closeBuf[InpMomentumLookback]) / atr;
   double alignedScore  = isDirect ? rawImpulseATR : -rawImpulseATR;
   bool   passed        = false;

   if(InpFilterStrictness == STRICT_DIRECTIONAL)
   {
      if(orderType == ORDER_TYPE_BUY)  passed = (alignedScore >= InpMinConfirmATR);
      else                             passed = (alignedScore <= -InpMinConfirmATR);
   }
   else
   {
      if(orderType == ORDER_TYPE_BUY)  passed = (alignedScore > -InpMaxVetoSpikeATR);
      else                             passed = (alignedScore < InpMaxVetoSpikeATR);
   }

   outDiag = StringFormat("%.2fATR (%s)", alignedScore, (passed ? "OK" : "BLOCKED"));
   return(passed);
}

//+------------------------------------------------------------------+
//| Daily Baseline & Circuit Breakers                                |
//+------------------------------------------------------------------+
void UpdateDailyBaseline()
{
   MqlDateTime dt;
   TimeCurrent(dt);
   if(dt.day_of_year != ExtCurrentDayOfYear)
   {
      ExtCurrentDayOfYear    = dt.day_of_year;
      ExtDayStartBalance     = AccountInfoDouble(ACCOUNT_BALANCE);
      ExtDailyCircuitTripped = false;
   }
}

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 ticket = HistoryDealGetTicket(i);
         if(ticket == 0) continue;
         if(HistoryDealGetInteger(ticket, DEAL_MAGIC) != (long)InpMagicNumber) continue;
         if(HistoryDealGetString(ticket, DEAL_SYMBOL) != _Symbol) continue;

         if(HistoryDealGetInteger(ticket, DEAL_ENTRY) == DEAL_ENTRY_OUT)
         {
            double net = HistoryDealGetDouble(ticket, DEAL_PROFIT) + HistoryDealGetDouble(ticket, DEAL_COMMISSION) + HistoryDealGetDouble(ticket, DEAL_SWAP);
            realizedToday += net;
            if(net < -1.0) losingTradesToday++;
         }
      }
   }

   double floating = GetOpenFloatingPnL();
   double totalDaily = realizedToday + floating;
   double maxLoss    = -1.0 * ExtDayStartBalance * (InpMaxDailyDrawdownPct / 100.0);

   if(totalDaily <= maxLoss || losingTradesToday >= InpMaxDailyLosingTrades)
   {
      ExtDailyCircuitTripped = true;
      if(InpCloseAllOnDailyLimit) CloseAllEAPositions();
      return(true);
   }
   return(false);
}

//+------------------------------------------------------------------+
//| Position Management (+20 Pip Lock & Leg 2 Scaling)               |
//+------------------------------------------------------------------+
void ManageOpenPositions()
{
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   ExtLockedProfitDollars = 0.0;

   if(bid >= InpMoFCeilingCutoff)
   {
      if(HasOpenPositionByTag(LEG1_TAG) || HasOpenPositionByTag(LEG2_TAG))
         CloseAllEAPositions();
      return;
   }

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

      // Track locked profit for dashboard
      if(StringFind(ExtPosition.Comment(), LEG1_TAG) >= 0)
      {
         double openPrice = ExtPosition.PriceOpen();
         double vol       = ExtPosition.Volume();
         ENUM_POSITION_TYPE pType = ExtPosition.PositionType();
         double lockedDist = InpLockedProfitPips * ExtPipSize;
         double tickSize   = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
         double tickVal    = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE_LOSS);
         if(tickVal <= 0.0) tickVal = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);

         if((pType == POSITION_TYPE_BUY && ExtPosition.StopLoss() >= openPrice + lockedDist - _Point) ||
            (pType == POSITION_TYPE_SELL && ExtPosition.StopLoss() <= openPrice - lockedDist + _Point))
         {
            if(tickSize > 0 && tickVal > 0)
               ExtLockedProfitDollars = vol * (lockedDist / tickSize) * tickVal * InpCommissionBuffer;
         }

         double triggerDist = InpScaleTriggerPips * ExtPipSize;
         double targetLockSL = NormalizeDouble(openPrice + (pType == POSITION_TYPE_BUY ? lockedDist : -lockedDist), _Digits);

         if(pType == POSITION_TYPE_BUY && (bid - openPrice) >= triggerDist && ExtPosition.StopLoss() < targetLockSL - _Point)
         {
            if(ExtTrade.PositionModify(ExtPosition.Ticket(), targetLockSL, ExtPosition.TakeProfit()))
            {
               if(!HasOpenPositionByTag(LEG2_TAG))
                  ExecuteHouseMoneyScaleIn(ORDER_TYPE_BUY, vol, lockedDist, ask, ExtPosition.TakeProfit());
            }
         }
         else if(pType == POSITION_TYPE_SELL && (openPrice - ask) >= triggerDist && (ExtPosition.StopLoss() == 0 || ExtPosition.StopLoss() > targetLockSL + _Point))
         {
            if(ExtTrade.PositionModify(ExtPosition.Ticket(), targetLockSL, ExtPosition.TakeProfit()))
            {
               if(!HasOpenPositionByTag(LEG2_TAG))
                  ExecuteHouseMoneyScaleIn(ORDER_TYPE_SELL, vol, lockedDist, bid, ExtPosition.TakeProfit());
            }
         }
      }
   }
}

void ExecuteHouseMoneyScaleIn(ENUM_ORDER_TYPE orderType, double leg1Vol, double lockedDist, double entryPrice, double tpPrice)
{
   double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   double tickVal  = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE_LOSS);
   if(tickVal <= 0.0) tickVal = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);

   double lockedDollars = leg1Vol * (lockedDist / tickSize) * tickVal * InpCommissionBuffer;
   double leg2Lots      = CalculateLotSizeFromMoney(lockedDollars, InpScaleStopLossPips * ExtPipSize);
   if(leg2Lots <= 0.0) return;

   double sl = NormalizeDouble(entryPrice + (orderType == ORDER_TYPE_BUY ? -1.5 : 1.5) * InpScaleStopLossPips * ExtPipSize, _Digits);
   if(orderType == ORDER_TYPE_BUY) ExtTrade.Buy(leg2Lots, _Symbol, entryPrice, sl, tpPrice, LEG2_TAG);
   else                            ExtTrade.Sell(leg2Lots, _Symbol, entryPrice, sl, tpPrice, LEG2_TAG);
}

void ExecuteBaseEntry(ENUM_ORDER_TYPE orderType, double entryPrice, double targetPrice)
{
   double balance   = AccountInfoDouble(ACCOUNT_BALANCE);
   double riskMoney = balance * (InpBaseRiskPercent / 100.0);
   double slDist    = InpBaseStopLossPips * ExtPipSize;
   double lotSize   = CalculateLotSizeFromMoney(riskMoney, slDist);
   if(lotSize <= 0.0) return;

   double sl = NormalizeDouble(entryPrice + (orderType == ORDER_TYPE_BUY ? -1.0 : 1.0) * slDist, _Digits);
   if(orderType == ORDER_TYPE_BUY) ExtTrade.Buy(lotSize, _Symbol, entryPrice, sl, targetPrice, LEG1_TAG);
   else                            ExtTrade.Sell(lotSize, _Symbol, entryPrice, sl, targetPrice, LEG1_TAG);
}

double CalculateLotSizeFromMoney(double riskMoney, double slDist)
{
   double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   double tickVal  = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE_LOSS);
   if(tickVal <= 0.0) tickVal = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);

   double lossPerLot = (slDist / tickSize) * tickVal;
   if(lossPerLot <= 0.0) return(0.0);

   double raw = riskMoney / lossPerLot;
   double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   double norm = MathFloor(raw / step) * step;
   return(NormalizeDouble(MathMax(SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN), MathMin(SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX), norm)), 2));
}

//+------------------------------------------------------------------+
//| On-Chart HUD Telemetry Dashboard Renderer                        |
//+------------------------------------------------------------------+
void RenderDashboard()
{
   int x = 15;
   int y = 30;
   int rowH = 18;

   CreateLabel("Title", "--- USD/JPY TELEMETRY HUD (v4.00) ---", x, y, 10, clrGold, true);
   y += rowH + 4;

   // Session & Circuit Status
   string sessionStr = (IsWithinPrimeSessionUTC() ? "ACTIVE" : "OUT-OF-SESSION");
   color  sessionCol = (IsWithinPrimeSessionUTC() ? clrLimeGreen : clrOrange);
   CreateLabel("Session", "Session Window: " + sessionStr, x, y, 9, sessionCol, false);
   y += rowH;

   string ddStr = StringFormat("Daily DD Limit: %.2f%% / %.2f%%", (GetDailyPnL() < 0 ? -GetDailyPnL() / ExtDayStartBalance * 100.0 : 0.0), InpMaxDailyDrawdownPct);
   color  ddCol = (ExtDailyCircuitTripped ? clrRed : clrWhite);
   CreateLabel("DD", ddStr, x, y, 9, ddCol, false);
   y += rowH;

   // H4 Zones
   CreateLabel("H4Head", "[Active H4 Fractal Zones]", x, y, 9, clrCyan, true);
   y += rowH;

   string demStr = ExtHasValidDemand ? StringFormat("Demand: %.3f - %.3f", ExtDemandLower, ExtDemandUpper) : "Demand: Scanning...";
   CreateLabel("Demand", demStr, x, y, 9, clrDodgerBlue, false);
   y += rowH;

   string supStr = ExtHasValidSupply ? StringFormat("Supply: %.3f - %.3f", ExtSupplyLower, ExtSupplyUpper) : "Supply: Scanning...";
   CreateLabel("Supply", supStr, x, y, 9, clrCrimson, false);
   y += rowH;

   // Macro Confluence
   CreateLabel("MacHead", "[Cross-Asset Macro Filter]", x, y, 9, clrCyan, true);
   y += rowH;

   CreateLabel("GoldDiag", "Gold (" + InpGoldSymbol + "): " + ExtGoldScoreDiag, x, y, 9, clrYellow, false);
   y += rowH;

   CreateLabel("BondDiag", "Bond (" + InpBondSymbol + "): " + ExtBondScoreDiag, x, y, 9, clrYellow, false);
   y += rowH;

   // Position & Profit Lock
   CreateLabel("PosHead", "[Position & Profit-Lock State]", x, y, 9, clrCyan, true);
   y += rowH;

   string leg1Status = HasOpenPositionByTag(LEG1_TAG) ? "Active (0.50% Risk)" : "Flat";
   CreateLabel("Leg1", "Leg 1 Base: " + leg1Status, x, y, 9, clrWhite, false);
   y += rowH;

   string leg2Status = HasOpenPositionByTag(LEG2_TAG) ? "Active ($0 Principal Risk)" : "Standby";
   CreateLabel("Leg2", "Leg 2 Scale: " + leg2Status, x, y, 9, clrWhite, false);
   y += rowH;

   string lockStr = StringFormat("Locked House-Profit: $%.2f", ExtLockedProfitDollars);
   CreateLabel("Lock", lockStr, x, y, 9, (ExtLockedProfitDollars > 0 ? clrLime : clrGray), false);
}

void CreateLabel(string name, string text, int x, int y, int fontSize, color col, bool bold)
{
   string objName = DASH_PREFIX + name;
   if(ObjectFind(0, objName) < 0)
   {
      ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0);
      ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
      ObjectSetInteger(0, objName, OBJPROP_X_DISTANCE, x);
      ObjectSetInteger(0, objName, OBJPROP_Y_DISTANCE, y);
      ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false);
   }
   ObjectSetString(0, objName, OBJPROP_TEXT, text);
   ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize);
   ObjectSetString(0, objName, OBJPROP_FONT, "Consolas");
   ObjectSetInteger(0, objName, OBJPROP_COLOR, col);
}

void RemoveDashboardObjects()
{
   int total = ObjectsTotal(0, 0, -1);
   for(int i = total - 1; i >= 0; i--)
   {
      string name = ObjectName(0, i, 0, -1);
      if(StringFind(name, DASH_PREFIX) == 0)
         ObjectDelete(0, name);
   }
}

bool IsWithinPrimeSessionUTC()
{
   MqlDateTime utc; TimeGMT(utc);
   return ((utc.hour >= InpTokyoStartHourUTC && utc.hour < InpTokyoEndHourUTC) ||
           (utc.hour >= InpOverlapStartHourUTC && utc.hour < InpOverlapEndHourUTC));
}

bool IsSpreadAcceptable()
{
   return (((SymbolInfoDouble(_Symbol, SYMBOL_ASK) - SymbolInfoDouble(_Symbol, SYMBOL_BID)) / ExtPipSize) <= InpMaxSpreadPips);
}

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);
}

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);
}

double GetDailyPnL()
{
   datetime dayStart = iTime(_Symbol, PERIOD_D1, 0);
   double realized = 0.0;
   if(HistorySelect(dayStart, TimeCurrent()))
   {
      for(int i = 0; i < HistoryDealsTotal(); i++)
      {
         ulong t = HistoryDealGetTicket(i);
         if(t != 0 && HistoryDealGetInteger(t, DEAL_MAGIC) == (long)InpMagicNumber && HistoryDealGetString(t, DEAL_SYMBOL) == _Symbol)
            realized += HistoryDealGetDouble(t, DEAL_PROFIT) + HistoryDealGetDouble(t, DEAL_COMMISSION) + HistoryDealGetDouble(t, DEAL_SWAP);
      }
   }
   return(realized + GetOpenFloatingPnL());
}

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 »

2. What the Telemetry Dashboard Displays

When attached to your USD/JPY M15 chart, the HUD panel presents live diagnostics:

Code: Select all

--- USD/JPY TELEMETRY HUD (v4.00) ---
Session Window: ACTIVE (Lime Green)
Daily DD Limit: 0.00% / 1.50%
[Active H4 Fractal Zones]
Demand: 157.812 - 158.045 (DodgerBlue)
Supply: 158.890 - 159.120 (Crimson)
[Cross-Asset Macro Filter]
Gold (XAUUSD): -0.42ATR (OK)
Bond (US10Y): +0.31ATR (OK)
[Position & Profit-Lock State]
Leg 1 Base: Active (0.50% Risk)
Leg 2 Scale: Standby
Locked House-Profit: $33.44 (Lime)
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply