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