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.
How to build strategy to trade USD/JPY
Re: How to build strategy to trade USD/JPY
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: How to build strategy to trade USD/JPY
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.
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.
Re: How to build strategy to trade USD/JPY
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.
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.
Re: How to build strategy to trade USD/JPY
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.
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.
Re: How to build strategy to trade USD/JPY
2. What the Telemetry Dashboard Displays
When attached to your USD/JPY M15 chart, the HUD panel presents live diagnostics:
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.