How to build strategy to trade USD/JPY
Re: How to build strategy to trade USD/JPY
1. Core Architecture & State Machine Logic
Dynamic Cross-Currency Lot Sizing (SYMBOL_TRADE_TICK_VALUE_LOSS): Because USD/JPY quotes profit in JPY while your account base may be USD or EUR, the EA queries SYMBOL_TRADE_TICK_VALUE_LOSS and SYMBOL_TRADE_TICK_SIZE directly from the broker server before normalizing to SYMBOL_VOLUME_STEP.
Tick-Level Profit-Lock & House-Money Scaling (ManageOpenPositions): While entry signals evaluate once per closed M15 bar to prevent intrabar false triggers, position management runs on every tick:
As soon as Leg 1 reaches +20.0 pips (InpScaleTriggerPips), the EA modifies Leg 1's Stop Loss to +10.0 pips (InpLockedProfitPips) above/below entry.
Immediately after the Stop Loss modification succeeds, the EA calculates the exact dollar profit locked in by Leg 1 (applying a 0.95 buffer to cover Raw ECN round-turn commission) and uses that locked dollar amount as the risk budget to open Leg 2 with a 15.0-pip Stop Loss.
Daily Drawdown & MoF Intervention Circuit Breakers: The EA tracks daily starting balance at server reset and queries HistorySelect to sum realized intraday PnL alongside floating equity. If daily loss hits 1.50%, if 3 stop-outs occur in a single day, or if spot price breaches 159.20 (MoF intervention danger zone), new entries are blocked until the next daily reset.
Dynamic Cross-Currency Lot Sizing (SYMBOL_TRADE_TICK_VALUE_LOSS): Because USD/JPY quotes profit in JPY while your account base may be USD or EUR, the EA queries SYMBOL_TRADE_TICK_VALUE_LOSS and SYMBOL_TRADE_TICK_SIZE directly from the broker server before normalizing to SYMBOL_VOLUME_STEP.
Tick-Level Profit-Lock & House-Money Scaling (ManageOpenPositions): While entry signals evaluate once per closed M15 bar to prevent intrabar false triggers, position management runs on every tick:
As soon as Leg 1 reaches +20.0 pips (InpScaleTriggerPips), the EA modifies Leg 1's Stop Loss to +10.0 pips (InpLockedProfitPips) above/below entry.
Immediately after the Stop Loss modification succeeds, the EA calculates the exact dollar profit locked in by Leg 1 (applying a 0.95 buffer to cover Raw ECN round-turn commission) and uses that locked dollar amount as the risk budget to open Leg 2 with a 15.0-pip Stop Loss.
Daily Drawdown & MoF Intervention Circuit Breakers: The EA tracks daily starting balance at server reset and queries HistorySelect to sum realized intraday PnL alongside floating equity. If daily loss hits 1.50%, if 3 stop-outs occur in a single day, or if spot price breaches 159.20 (MoF intervention danger zone), new entries are blocked until the next daily reset.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: How to build strategy to trade USD/JPY
2. Complete MQL5 Expert Advisor Source Code
Save this file as USDJPY_LiquiditySweep_ProfitLock.mq5 inside your MQL5\Experts\ directory and compile it in MetaEditor (F7).
Save this file as USDJPY_LiquiditySweep_ProfitLock.mq5 inside your MQL5\Experts\ directory and compile it in MetaEditor (F7).
Code: Select all
//+------------------------------------------------------------------+
//| USDJPY_LiquiditySweep_ProfitLock.mq5 |
//| Top-Down H4/M15 Liquidity Sweep & House-Money Scaling EA |
//+------------------------------------------------------------------+
#property copyright "USD/JPY Automated Strategy (October 2026 Regime)"
#property version "1.00"
#property strict
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
//--- Input Parameters: H4 Structural Zones
input group "=== 1. H4 Structural Levels (JPY) ==="
input double InpDemandLower = 157.80; // Primary H4 Demand Lower Bound
input double InpDemandUpper = 158.00; // Primary H4 Demand Upper Bound (Sweep Level)
input double InpSupplyLower = 158.85; // Primary H4 Supply Lower Bound (Reject Close)
input double InpSupplyUpper = 158.95; // Primary H4 Supply Sweep Threshold
input double InpBearishBreakdownH4 = 157.60; // H4 Close Below Invalidates Longs
input double InpMoFCeilingCutoff = 159.20; // Hard Cutoff (MoF Intervention Danger Zone)
//--- Input Parameters: Timeframes & Entry Triggers
input group "=== 2. M15 Execution & Spread Filters ==="
input ENUM_TIMEFRAMES InpEntryTF = PERIOD_M15; // Execution Timeframe
input ENUM_TIMEFRAMES InpStructureTF = PERIOD_H4; // Structural Filter Timeframe
input double InpDisplacementRatio = 0.85; // Min Closed Body / Prior 3-Bar Avg Range Ratio
input double InpMaxSpreadPips = 1.2; // Max Allowed Spread (Pips) for Raw ECN
//--- Input Parameters: Risk & Exponential Profit-Lock Scaling
input group "=== 3. Risk & House-Money Pyramiding ==="
input double InpBaseRiskPercent = 0.50; // Leg 1 Base Risk (% of Account Balance)
input double InpBaseStopLossPips = 15.0; // Leg 1 Stop Loss Distance (Pips)
input double InpScaleTriggerPips = 20.0; // Profit Trigger to Lock SL & Open Leg 2 (Pips)
input double InpLockedProfitPips = 10.0; // Leg 1 Locked Profit SL Distance (+Pips)
input double InpScaleStopLossPips = 15.0; // Leg 2 Stop Loss Distance (Pips)
input double InpCommissionBuffer = 0.95; // Safety Factor on Locked Profit for ECN Fees
input double InpLongTargetPrice = 158.85; // Take Profit Price for Long Setups
input double InpShortTargetPrice = 158.00; // Take Profit Price for Short Setups
//--- Input Parameters: Circuit Breakers & Session Timing
input group "=== 4. Circuit Breakers & Session Windows (UTC) ==="
input double InpMaxDailyDrawdownPct = 1.50; // Daily Drawdown Circuit Breaker (% of Start Balance)
input int InpMaxDailyLosingTrades = 3; // Max Losing Trades Per Day Circuit Breaker
input bool InpCloseAllOnDailyLimit = true; // Flatten Open Positions if Daily DD Breached
input bool InpUseSessionFilter = true; // Restrict Entries to Tokyo Open & LDN-NY Overlap
input int InpTokyoStartHourUTC = 0; // Tokyo Window Start Hour (UTC)
input int InpTokyoEndHourUTC = 3; // Tokyo Window End Hour (UTC)
input int InpOverlapStartHourUTC = 12; // London-NY Overlap Start Hour (UTC)
input int InpOverlapEndHourUTC = 16; // London-NY Overlap End Hour (UTC)
//--- Input Parameters: System & Notifications
input group "=== 5. EA Identification & Mobile Alerts ==="
input ulong InpMagicNumber = 20261019; // EA Magic Number
input bool InpEnablePushNotify = true; // Send MT5 Mobile Push Notifications
//--- Constants for Leg Tracking
#define LEG1_TAG "USDJPY_LEG1_BASE"
#define LEG2_TAG "USDJPY_LEG2_SCALE"
//--- Global Variables
CTrade ExtTrade;
CPositionInfo ExtPosition;
double ExtPipSize = 0.01;
datetime ExtLastM15BarTime = 0;
int ExtCurrentDayOfYear = -1;
double ExtDayStartBalance = 0.0;
bool ExtDailyCircuitTripped = false;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- Configure CTrade instance
ExtTrade.SetExpertMagicNumber(InpMagicNumber);
ExtTrade.SetDeviationInPoints(15);
ExtTrade.SetTypeFillingBySymbol(_Symbol);
//--- Determine Pip Size (3-digit JPY brokers use 0.001 point -> 0.01 pip)
ExtPipSize = (_Digits == 3 || _Digits == 5) ? _Point * 10.0 : _Point;
//--- Initialize Daily Drawdown Baseline
UpdateDailyBaseline();
PrintFormat("USDJPY_LiquiditySweep_ProfitLock initialized on %s | PipSize: %.4f | StartBalance: %.2f",
_Symbol, ExtPipSize, ExtDayStartBalance);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Comment("");
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- 1. Refresh Daily Baseline & Check Circuit Breakers on Every Tick
UpdateDailyBaseline();
if(CheckDailyCircuitBreaker())
return;
//--- 2. Manage Open Positions on Every Tick (+20 Pip Lock & Leg 2 Scaling)
ManageOpenPositions();
//--- 3. Evaluate New Base Entries Only Once Per Closed M15 Bar
datetime currentBarTime = iTime(_Symbol, InpEntryTF, 0);
if(currentBarTime == ExtLastM15BarTime)
return;
//--- If a base position is already open, do not open another Leg 1
if(HasOpenPositionByTag(LEG1_TAG) || HasOpenPositionByTag(LEG2_TAG))
{
ExtLastM15BarTime = currentBarTime;
return;
}
//--- Verify Session Window & Spread Filter before Evaluating Signal
if(InpUseSessionFilter && !IsWithinPrimeSessionUTC())
return;
if(!IsSpreadAcceptable())
return;
//--- Mark bar as processed and check M15 Liquidity Sweep Signals
ExtLastM15BarTime = currentBarTime;
EvaluateM15EntrySignals();
}
//+------------------------------------------------------------------+
//| Reset daily starting balance at server midnight |
//+------------------------------------------------------------------+
void UpdateDailyBaseline()
{
MqlDateTime dt;
TimeCurrent(dt);
if(dt.day_of_year != ExtCurrentDayOfYear)
{
ExtCurrentDayOfYear = dt.day_of_year;
ExtDayStartBalance = AccountInfoDouble(ACCOUNT_BALANCE);
ExtDailyCircuitTripped = false;
}
}
//+------------------------------------------------------------------+
//| Verify 1.50% Daily Drawdown & Max Consecutive Stop-Outs |
//+------------------------------------------------------------------+
bool CheckDailyCircuitBreaker()
{
if(ExtDailyCircuitTripped)
return(true);
//--- Calculate today's realized PnL and losing trade count for this Magic Number
datetime dayStart = iTime(_Symbol, PERIOD_D1, 0);
double realizedToday = 0.0;
int losingTradesToday = 0;
if(HistorySelect(dayStart, TimeCurrent()))
{
int totalDeals = HistoryDealsTotal();
for(int i = 0; i < totalDeals; i++)
{
ulong dealTicket = HistoryDealGetTicket(i);
if(dealTicket == 0) continue;
if(HistoryDealGetInteger(dealTicket, DEAL_MAGIC) != (long)InpMagicNumber) continue;
if(HistoryDealGetString(dealTicket, DEAL_SYMBOL) != _Symbol) continue;
long dealEntry = HistoryDealGetInteger(dealTicket, DEAL_ENTRY);
if(dealEntry == DEAL_ENTRY_OUT || dealEntry == DEAL_ENTRY_INOUT)
{
double netDealProfit = HistoryDealGetDouble(dealTicket, DEAL_PROFIT)
+ HistoryDealGetDouble(dealTicket, DEAL_COMMISSION)
+ HistoryDealGetDouble(dealTicket, DEAL_SWAP);
realizedToday += netDealProfit;
if(netDealProfit < -1.0) // Ignore break-even / positive profit-lock exits
losingTradesToday++;
}
}
}
//--- Add current floating PnL for this EA
double floatingPnL = GetOpenFloatingPnL();
double totalDailyPnL = realizedToday + floatingPnL;
double maxAllowedLoss = -1.0 * ExtDayStartBalance * (InpMaxDailyDrawdownPct / 100.0);
if(totalDailyPnL <= maxAllowedLoss || losingTradesToday >= InpMaxDailyLosingTrades)
{
ExtDailyCircuitTripped = true;
string msg = StringFormat("Circuit Breaker Tripped on %s | Daily PnL: %.2f | Losses Today: %d",
_Symbol, totalDailyPnL, losingTradesToday);
Print(msg);
if(InpEnablePushNotify) SendNotification(msg);
if(InpCloseAllOnDailyLimit)
CloseAllEAPositions();
return(true);
}
return(false);
}
//+------------------------------------------------------------------+
//| Tick-level Position Management: +20 Pip Lock & Leg 2 Scale-In |
//+------------------------------------------------------------------+
void ManageOpenPositions()
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
//--- MoF Intervention Danger Zone Hard Cutoff Check (159.20+)
if(bid >= InpMoFCeilingCutoff)
{
if(HasOpenPositionByTag(LEG1_TAG) || HasOpenPositionByTag(LEG2_TAG))
{
Print("MoF Ceiling Cutoff breached (>= ", InpMoFCeilingCutoff, "). Flattening exposure.");
CloseAllEAPositions();
}
return;
}
//--- Find Open Leg 1 Position
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(!ExtPosition.SelectByIndex(i)) continue;
if(ExtPosition.Symbol() != _Symbol || ExtPosition.Magic() != InpMagicNumber) continue;
if(StringFind(ExtPosition.Comment(), LEG1_TAG) < 0) continue;
ulong leg1Ticket = ExtPosition.Ticket();
double leg1OpenPrice = ExtPosition.PriceOpen();
double leg1CurrentSL = ExtPosition.StopLoss();
double leg1CurrentTP = ExtPosition.TakeProfit();
double leg1Volume = ExtPosition.Volume();
ENUM_POSITION_TYPE posType = ExtPosition.PositionType();
double triggerDist = InpScaleTriggerPips * ExtPipSize;
double lockDist = InpLockedProfitPips * ExtPipSize;
//--- CASE A: LONG POSITION MANAGEMENT
if(posType == POSITION_TYPE_BUY)
{
double currentGainPrice = bid - leg1OpenPrice;
double targetLockSL = NormalizeDouble(leg1OpenPrice + lockDist, _Digits);
//--- Check if +20 Pips reached and SL is not yet locked at +10 Pips
if(currentGainPrice >= triggerDist && leg1CurrentSL < targetLockSL - (_Point * 0.5))
{
if(ExtTrade.PositionModify(leg1Ticket, targetLockSL, leg1CurrentTP))
{
PrintFormat("Leg 1 BUY #%I64u SL locked at +%.1f pips (%.3f).",
leg1Ticket, InpLockedProfitPips, targetLockSL);
//--- Open Leg 2 (House-Money Scale-In) if not already open
if(!HasOpenPositionByTag(LEG2_TAG))
ExecuteHouseMoneyScaleIn(ORDER_TYPE_BUY, leg1Volume, lockDist, ask, leg1CurrentTP);
}
}
}
//--- CASE B: SHORT POSITION MANAGEMENT
else if(posType == POSITION_TYPE_SELL)
{
double currentGainPrice = leg1OpenPrice - ask;
double targetLockSL = NormalizeDouble(leg1OpenPrice - lockDist, _Digits);
//--- Check if +20 Pips reached and SL is not yet locked at +10 Pips
if(currentGainPrice >= triggerDist && (leg1CurrentSL == 0.0 || leg1CurrentSL > targetLockSL + (_Point * 0.5)))
{
if(ExtTrade.PositionModify(leg1Ticket, targetLockSL, leg1CurrentTP))
{
PrintFormat("Leg 1 SELL #%I64u SL locked at +%.1f pips (%.3f).",
leg1Ticket, InpLockedProfitPips, targetLockSL);
//--- Open Leg 2 (House-Money Scale-In) if not already open
if(!HasOpenPositionByTag(LEG2_TAG))
ExecuteHouseMoneyScaleIn(ORDER_TYPE_SELL, leg1Volume, lockDist, bid, leg1CurrentTP);
}
}
}
}
}
//+------------------------------------------------------------------+
//| Open Leg 2 Funded Strictly by Locked-In Dollar Profit of Leg 1 |
//+------------------------------------------------------------------+
void ExecuteHouseMoneyScaleIn(ENUM_ORDER_TYPE orderType,
double leg1Volume,
double lockedPriceDistance,
double entryPrice,
double takeProfitPrice)
{
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE_LOSS);
if(tickValue <= 0.0)
tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
if(tickSize <= 0.0 || tickValue <= 0.0)
return;
//--- Calculate exact dollar profit locked in by Leg 1 (+10 pips)
double lockedProfitDollars = leg1Volume * (lockedPriceDistance / tickSize) * tickValue;
double riskBudgetForLeg2 = lockedProfitDollars * InpCommissionBuffer;
//--- Calculate Leg 2 Lot Size for a 15-pip Stop Loss
double leg2SLDistance = InpScaleStopLossPips * ExtPipSize;
double leg2Lots = CalculateLotSizeFromMoney(riskBudgetForLeg2, leg2SLDistance);
if(leg2Lots <= 0.0)
return;
double leg2SL = (orderType == ORDER_TYPE_BUY)
? NormalizeDouble(entryPrice - leg2SLDistance, _Digits)
: NormalizeDouble(entryPrice + leg2SLDistance, _Digits);
double leg2TP = NormalizeDouble(takeProfitPrice, _Digits);
bool res = false;
if(orderType == ORDER_TYPE_BUY)
res = ExtTrade.Buy(leg2Lots, _Symbol, entryPrice, leg2SL, leg2TP, LEG2_TAG);
else
res = ExtTrade.Sell(leg2Lots, _Symbol, entryPrice, leg2SL, leg2TP, LEG2_TAG);
if(res)
{
string msg = StringFormat("House-Money Leg 2 Opened (%s %.2f lots @ %.3f) | Risk Budget: $%.2f ($0 Principal Risk)",
(orderType == ORDER_TYPE_BUY ? "BUY" : "SELL"),
leg2Lots, entryPrice, riskBudgetForLeg2);
Print(msg);
if(InpEnablePushNotify) SendNotification(msg);
}
}
//+------------------------------------------------------------------+
//| Evaluate Closed M15 Candle for Demand/Supply Liquidity Sweeps |
//+------------------------------------------------------------------+
void EvaluateM15EntrySignals()
{
MqlRates rates[];
ArraySetAsSeries(rates, true);
//--- Copy last 4 closed M15 candles (indices 0..3 correspond to shifts 1..4)
if(CopyRates(_Symbol, InpEntryTF, 1, 4, rates) < 4)
return;
//--- Check H4 Bearish Breakdown Filter (Last closed H4 bar)
double h4Close = iClose(_Symbol, InpStructureTF, 1);
bool longsAllowedByH4 = (h4Close >= InpBearishBreakdownH4);
//--- Compute average full range of the prior 3 candles (shifts 2, 3, 4)
double avgPrior3Range = ((rates[1].high - rates[1].low) +
(rates[2].high - rates[2].low) +
(rates[3].high - rates[3].low)) / 3.0;
double signalOpen = rates[0].open;
double signalHigh = rates[0].high;
double signalLow = rates[0].low;
double signalClose = rates[0].close;
double signalBody = MathAbs(signalClose - signalOpen);
bool isImpulsiveBody = (signalBody >= avgPrior3Range * InpDisplacementRatio);
bool isBullishEngulf = (signalClose > signalOpen && signalClose > rates[1].high);
bool isBearishEngulf = (signalClose < signalOpen && signalClose < rates[1].low);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
//--- 1. LONG SETUP: H4 Demand Sweep (Wick <= 158.00 & >= 157.80, Close > 158.00)
bool sweptDemand = (signalLow <= InpDemandUpper && signalLow >= (InpDemandLower - 5.0 * ExtPipSize));
bool closedAboveDemand = (signalClose > InpDemandUpper && signalClose > signalOpen);
if(longsAllowedByH4 && sweptDemand && closedAboveDemand && (isImpulsiveBody || isBullishEngulf))
{
if(ask < InpLongTargetPrice - (25.0 * ExtPipSize))
{
ExecuteBaseEntry(ORDER_TYPE_BUY, ask, InpLongTargetPrice);
return;
}
}
//--- 2. SHORT SETUP: H4 Supply Rejection (Wick >= 158.95 & < 159.20, Close < 158.85)
bool sweptSupply = (signalHigh >= InpSupplyUpper && signalHigh < InpMoFCeilingCutoff);
bool closedBelowSupply = (signalClose <= InpSupplyLower && signalClose < signalOpen);
if(sweptSupply && closedBelowSupply && (isImpulsiveBody || isBearishEngulf))
{
if(bid > InpShortTargetPrice + (25.0 * ExtPipSize))
{
ExecuteBaseEntry(ORDER_TYPE_SELL, bid, InpShortTargetPrice);
return;
}
}
}
//+------------------------------------------------------------------+
//| Execute Leg 1 Base Entry (0.50% Risk) |
//+------------------------------------------------------------------+
void ExecuteBaseEntry(ENUM_ORDER_TYPE orderType, double entryPrice, double targetPrice)
{
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskMoney = balance * (InpBaseRiskPercent / 100.0);
double slDistance = InpBaseStopLossPips * ExtPipSize;
double lotSize = CalculateLotSizeFromMoney(riskMoney, slDistance);
if(lotSize <= 0.0)
return;
double slPrice = (orderType == ORDER_TYPE_BUY)
? NormalizeDouble(entryPrice - slDistance, _Digits)
: NormalizeDouble(entryPrice + slDistance, _Digits);
double tpPrice = NormalizeDouble(targetPrice, _Digits);
bool res = false;
if(orderType == ORDER_TYPE_BUY)
res = ExtTrade.Buy(lotSize, _Symbol, entryPrice, slPrice, tpPrice, LEG1_TAG);
else
res = ExtTrade.Sell(lotSize, _Symbol, entryPrice, slPrice, tpPrice, LEG1_TAG);
if(res)
{
string msg = StringFormat("Leg 1 Base %s Opened: %.2f lots @ %.3f | SL: %.3f | TP: %.3f | Risk: $%.2f (%.2f%%)",
(orderType == ORDER_TYPE_BUY ? "BUY" : "SELL"),
lotSize, entryPrice, slPrice, tpPrice, riskMoney, InpBaseRiskPercent);
Print(msg);
if(InpEnablePushNotify) SendNotification(msg);
}
}
//+------------------------------------------------------------------+
//| Calculate Normalized Lot Size from Dollar Risk & Price Distance |
//+------------------------------------------------------------------+
double CalculateLotSizeFromMoney(double riskMoney, double slPriceDistance)
{
if(riskMoney <= 0.0 || slPriceDistance <= 0.0)
return(0.0);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE_LOSS);
if(tickValue <= 0.0)
tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
if(tickSize <= 0.0 || tickValue <= 0.0)
return(0.0);
double lossPerLot = (slPriceDistance / tickSize) * tickValue;
if(lossPerLot <= 0.0)
return(0.0);
double rawLots = riskMoney / lossPerLot;
double minVol = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxVol = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double stepVol = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
//--- Floor to volume step so risk never exceeds target percentage
double normalizedLots = MathFloor(rawLots / stepVol) * stepVol;
if(normalizedLots < minVol)
return(0.0);
if(normalizedLots > maxVol)
normalizedLots = maxVol;
return(NormalizeDouble(normalizedLots, 2));
}
//+------------------------------------------------------------------+
//| Helper: Check if Prime Tokyo or London-NY Overlap Window (UTC) |
//+------------------------------------------------------------------+
bool IsWithinPrimeSessionUTC()
{
MqlDateTime utc;
TimeGMT(utc);
bool inTokyo = (utc.hour >= InpTokyoStartHourUTC && utc.hour < InpTokyoEndHourUTC);
bool inOverlap = (utc.hour >= InpOverlapStartHourUTC && utc.hour < InpOverlapEndHourUTC);
return(inTokyo || inOverlap);
}
//+------------------------------------------------------------------+
//| Helper: Verify Raw ECN Spread is Below Threshold |
//+------------------------------------------------------------------+
bool IsSpreadAcceptable()
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double currentSpreadPips = (ask - bid) / ExtPipSize;
return(currentSpreadPips <= InpMaxSpreadPips);
}
//+------------------------------------------------------------------+
//| Helper: Check if a Position with Specific Comment Tag Exists |
//+------------------------------------------------------------------+
bool HasOpenPositionByTag(string tag)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(!ExtPosition.SelectByIndex(i)) continue;
if(ExtPosition.Symbol() == _Symbol &&
ExtPosition.Magic() == InpMagicNumber &&
StringFind(ExtPosition.Comment(), tag) >= 0)
{
return(true);
}
}
return(false);
}
//+------------------------------------------------------------------+
//| Helper: Get Total Floating PnL of Open Positions for this EA |
//+------------------------------------------------------------------+
double GetOpenFloatingPnL()
{
double floating = 0.0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(!ExtPosition.SelectByIndex(i)) continue;
if(ExtPosition.Symbol() == _Symbol && ExtPosition.Magic() == InpMagicNumber)
{
floating += ExtPosition.Profit() + ExtPosition.Swap() + ExtPosition.Commission();
}
}
return(floating);
}
//+------------------------------------------------------------------+
//| Helper: Close All Open Positions Belonging to This EA |
//+------------------------------------------------------------------+
void CloseAllEAPositions()
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(!ExtPosition.SelectByIndex(i)) continue;
if(ExtPosition.Symbol() == _Symbol && ExtPosition.Magic() == InpMagicNumber)
{
ExtTrade.PositionClose(ExtPosition.Ticket());
}
}
}
//+------------------------------------------------------------------+Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: How to build strategy to trade USD/JPY
3.) Deployment & MT5 Strategy Tester Checklist
Compile in MetaEditor 5: Open MetaEditor (F4 in MT5), create a new Expert Advisor named USDJPY_LiquiditySweep_ProfitLock.mq5, paste the code above, and press F7 (0 errors, 0 warnings).
Backtest with Real Ticks: In the MT5 Strategy Tester (Ctrl+R), select USDJPY, set the timeframe to M15, and choose "Every tick based on real ticks" so the intrabar +20 pip profit-lock and Leg 2 trigger are simulated with exact ECN bid/ask spreads.
Enable Mobile Push Alerts (Optional): To receive instant iPhone MT5 notifications whenever Leg 1 enters or Leg 2 scales in on house money, enable Push Notifications under Tools -> Options -> Notifications in your desktop terminal and keep InpEnablePushNotify = true.
Compile in MetaEditor 5: Open MetaEditor (F4 in MT5), create a new Expert Advisor named USDJPY_LiquiditySweep_ProfitLock.mq5, paste the code above, and press F7 (0 errors, 0 warnings).
Backtest with Real Ticks: In the MT5 Strategy Tester (Ctrl+R), select USDJPY, set the timeframe to M15, and choose "Every tick based on real ticks" so the intrabar +20 pip profit-lock and Leg 2 trigger are simulated with exact ECN bid/ask spreads.
Enable Mobile Push Alerts (Optional): To receive instant iPhone MT5 notifications whenever Leg 1 enters or Leg 2 scales in on house money, enable Push Notifications under Tools -> Options -> Notifications in your desktop terminal and keep InpEnablePushNotify = true.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: How to build strategy to trade USD/JPY
Adding a cross-asset filter to the USD/JPY Expert Advisor eliminates false M15 liquidity sweeps caused by isolated Yen noise while USD real yields or safe-haven flows are moving in the opposite direction.
Because XAU/USD (Spot Gold) and US 10-Year Treasury Note futures/CFDs trade inversely to US real yields—while US10Y Yield indices trade directly with USD/JPY—the upgraded EA normalizes cross-asset momentum using each instrument's 14-period M15 ATR and 20-period EMA slope:
$$\text{Macro Impulse Score} = \frac{\text{Close}_{1} - \text{Close}_{1 + N}}{\text{ATR}_{14, 1}}$$
where $N$ is the M15 momentum lookback window (default 3 closed M15 bars, or 45 minutes of order flow).
Because XAU/USD (Spot Gold) and US 10-Year Treasury Note futures/CFDs trade inversely to US real yields—while US10Y Yield indices trade directly with USD/JPY—the upgraded EA normalizes cross-asset momentum using each instrument's 14-period M15 ATR and 20-period EMA slope:
$$\text{Macro Impulse Score} = \frac{\text{Close}_{1} - \text{Close}_{1 + N}}{\text{ATR}_{14, 1}}$$
where $N$ is the M15 momentum lookback window (default 3 closed M15 bars, or 45 minutes of order flow).
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: How to build strategy to trade USD/JPY
1. Multi-Symbol Confluence Matrix & Filter Modes
To accommodate different broker feeds (such as XAUUSD alongside either a direct yield ticker like US10Y or a Treasury Note price CFD like UST10Y / TNOTE), the EA includes two selectable strictness modes:
STRICT_DIRECTIONAL (Active Confirmation)
Requires XAU/USD Impulse $\le -0.20\text{ ATR}$ (and below M15 EMA-20) AND/OR US10Y Yield Impulse $\ge +0.20\text{ ATR}$ (Long Entry)
Requires XAU/USD Impulse $\ge +0.20\text{ ATR}$ (and above M15 EMA-20) AND/OR US10Y Yield Impulse $\le -0.20\text{ ATR}$ (Short Entry)
London–NY Overlap (12:00–16:00 UTC) when US yields and Gold actively drive USD legs (Best session use case)
To accommodate different broker feeds (such as XAUUSD alongside either a direct yield ticker like US10Y or a Treasury Note price CFD like UST10Y / TNOTE), the EA includes two selectable strictness modes:
STRICT_DIRECTIONAL (Active Confirmation)
Requires XAU/USD Impulse $\le -0.20\text{ ATR}$ (and below M15 EMA-20) AND/OR US10Y Yield Impulse $\ge +0.20\text{ ATR}$ (Long Entry)
Requires XAU/USD Impulse $\ge +0.20\text{ ATR}$ (and above M15 EMA-20) AND/OR US10Y Yield Impulse $\le -0.20\text{ ATR}$ (Short Entry)
London–NY Overlap (12:00–16:00 UTC) when US yields and Gold actively drive USD legs (Best session use case)
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: How to build strategy to trade USD/JPY
2. Upgraded MQL5 Expert Advisor (v2.00)
Save this updated code over USDJPY_LiquiditySweep_ProfitLock.mq5 in MQL5\Experts\ and compile with F7. In OnInit(), the EA automatically subscribes the secondary symbols in Market Watch via SymbolSelect() and initializes dedicated M15 iATR and iMA handles.
Save this updated code over USDJPY_LiquiditySweep_ProfitLock.mq5 in MQL5\Experts\ and compile with F7. In OnInit(), the EA automatically subscribes the secondary symbols in Market Watch via SymbolSelect() and initializes dedicated M15 iATR and iMA handles.
Code: Select all
//+------------------------------------------------------------------+
//| USDJPY_LiquiditySweep_ProfitLock.mq5 |
//| v2.00: Top-Down H4/M15 Sweep + XAUUSD/US10Y Confluence Filter |
//+------------------------------------------------------------------+
#property copyright "USD/JPY Automated Strategy (October 2026 Regime)"
#property version "2.00"
#property strict
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
//--- Enumerations for Multi-Symbol Macro Filter
enum ENUM_CORR_SOURCE
{
CORR_XAUUSD_ONLY = 0, // XAUUSD Only (Inverse to USD/JPY)
CORR_US10Y_ONLY = 1, // US10Y / Bond Symbol Only
CORR_EITHER_CONFIRMS = 2, // Either XAUUSD or US10Y Confirms
CORR_BOTH_REQUIRED = 3 // Both XAUUSD and US10Y Must Confirm
};
enum ENUM_FILTER_STRICTNESS
{
STRICT_DIRECTIONAL = 0, // Require Active Cross-Asset Momentum Alignment
VETO_OPPOSING_SPIKE = 1 // Only Block Entries When Cross-Asset Spikes Against Setup
};
//--- Input Parameters: H4 Structural Zones
input group "=== 1. H4 Structural Levels (JPY) ==="
input double InpDemandLower = 157.80; // Primary H4 Demand Lower Bound
input double InpDemandUpper = 158.00; // Primary H4 Demand Upper Bound (Sweep Level)
input double InpSupplyLower = 158.85; // Primary H4 Supply Lower Bound (Reject Close)
input double InpSupplyUpper = 158.95; // Primary H4 Supply Sweep Threshold
input double InpBearishBreakdownH4 = 157.60; // H4 Close Below Invalidates Longs
input double InpMoFCeilingCutoff = 159.20; // Hard Cutoff (MoF Intervention Danger Zone)
//--- Input Parameters: Timeframes & Entry Triggers
input group "=== 2. M15 Execution & Spread Filters ==="
input ENUM_TIMEFRAMES InpEntryTF = PERIOD_M15; // Execution Timeframe
input ENUM_TIMEFRAMES InpStructureTF = PERIOD_H4; // Structural Filter Timeframe
input double InpDisplacementRatio = 0.85; // Min Closed Body / Prior 3-Bar Avg Range Ratio
input double InpMaxSpreadPips = 1.2; // Max Allowed Spread (Pips) for Raw ECN
//--- Input Parameters: Multi-Symbol XAUUSD & US10Y Filter
input group "=== 3. Multi-Symbol Macro Filter (XAUUSD / US10Y) ==="
input bool InpUseMacroFilter = true; // Enable Cross-Asset Momentum Filter
input ENUM_CORR_SOURCE InpCorrSource = CORR_XAUUSD_ONLY; // Active Correlation Feed(s)
input ENUM_FILTER_STRICTNESS InpFilterStrictness = STRICT_DIRECTIONAL; // Filter Strictness Mode
input string InpGoldSymbol = "XAUUSD"; // Gold Symbol Name (Inverse Correlation)
input string InpBondSymbol = "US10Y"; // US10Y Yield or T-Note Symbol Name
input bool InpBondIsYieldDirect = true; // true = Yield (Direct), false = Bond Price (Inverse)
input int InpMomentumLookback = 3; // M15 Bar Lookback for Impulse ROC
input int InpMacroATRPeriod = 14; // ATR Period for Impulse Normalization
input int InpMacroEMAPeriod = 20; // EMA Period for Cross-Asset Trend Filter
input double InpMinConfirmATR = 0.20; // Min ATR Impulse for STRICT_DIRECTIONAL
input double InpMaxVetoSpikeATR = 0.45; // Max Opposing ATR Impulse for VETO Mode
//--- Input Parameters: Risk & Exponential Profit-Lock Scaling
input group "=== 4. Risk & House-Money Pyramiding ==="
input double InpBaseRiskPercent = 0.50; // Leg 1 Base Risk (% of Account Balance)
input double InpBaseStopLossPips = 15.0; // Leg 1 Stop Loss Distance (Pips)
input double InpScaleTriggerPips = 20.0; // Profit Trigger to Lock SL & Open Leg 2 (Pips)
input double InpLockedProfitPips = 10.0; // Leg 1 Locked Profit SL Distance (+Pips)
input double InpScaleStopLossPips = 15.0; // Leg 2 Stop Loss Distance (Pips)
input double InpCommissionBuffer = 0.95; // Safety Factor on Locked Profit for ECN Fees
input double InpLongTargetPrice = 158.85; // Take Profit Price for Long Setups
input double InpShortTargetPrice = 158.00; // Take Profit Price for Short Setups
//--- Input Parameters: Circuit Breakers & Session Timing
input group "=== 5. Circuit Breakers & Session Windows (UTC) ==="
input double InpMaxDailyDrawdownPct = 1.50; // Daily Drawdown Circuit Breaker (% of Start Balance)
input int InpMaxDailyLosingTrades = 3; // Max Losing Trades Per Day Circuit Breaker
input bool InpCloseAllOnDailyLimit = true; // Flatten Open Positions if Daily DD Breached
input bool InpUseSessionFilter = true; // Restrict Entries to Tokyo Open & LDN-NY Overlap
input int InpTokyoStartHourUTC = 0; // Tokyo Window Start Hour (UTC)
input int InpTokyoEndHourUTC = 3; // Tokyo Window End Hour (UTC)
input int InpOverlapStartHourUTC = 12; // London-NY Overlap Start Hour (UTC)
input int InpOverlapEndHourUTC = 16; // London-NY Overlap End Hour (UTC)
//--- Input Parameters: System & Notifications
input group "=== 6. EA Identification & Mobile Alerts ==="
input ulong InpMagicNumber = 20261019; // EA Magic Number
input bool InpEnablePushNotify = true; // Send MT5 Mobile Push Notifications
//--- Constants for Leg Tracking
#define LEG1_TAG "USDJPY_LEG1_BASE"
#define LEG2_TAG "USDJPY_LEG2_SCALE"
//--- Global Variables
CTrade ExtTrade;
CPositionInfo ExtPosition;
double ExtPipSize = 0.01;
datetime ExtLastM15BarTime = 0;
int ExtCurrentDayOfYear = -1;
double ExtDayStartBalance = 0.0;
bool ExtDailyCircuitTripped = false;
//--- Cross-Asset Indicator Handles
int ExtGoldATRHandle = INVALID_HANDLE;
int ExtGoldEMAHandle = INVALID_HANDLE;
int ExtBondATRHandle = INVALID_HANDLE;
int ExtBondEMAHandle = INVALID_HANDLE;
string ExtLastMacroDiagnostics = "";
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
ExtTrade.SetExpertMagicNumber(InpMagicNumber);
ExtTrade.SetDeviationInPoints(15);
ExtTrade.SetTypeFillingBySymbol(_Symbol);
ExtPipSize = (_Digits == 3 || _Digits == 5) ? _Point * 10.0 : _Point;
UpdateDailyBaseline();
//--- Initialize Cross-Asset Symbols & Indicator Handles
if(InpUseMacroFilter)
{
bool needGold = (InpCorrSource == CORR_XAUUSD_ONLY ||
InpCorrSource == CORR_EITHER_CONFIRMS ||
InpCorrSource == CORR_BOTH_REQUIRED);
bool needBond = (InpCorrSource == CORR_US10Y_ONLY ||
InpCorrSource == CORR_EITHER_CONFIRMS ||
InpCorrSource == CORR_BOTH_REQUIRED);
if(needGold)
{
if(!SymbolSelect(InpGoldSymbol, true))
{
PrintFormat("ERROR: Failed to select Gold symbol '%s' in Market Watch.", InpGoldSymbol);
return(INIT_FAILED);
}
ExtGoldATRHandle = iATR(InpGoldSymbol, InpEntryTF, InpMacroATRPeriod);
ExtGoldEMAHandle = iMA(InpGoldSymbol, InpEntryTF, InpMacroEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
if(ExtGoldATRHandle == INVALID_HANDLE || ExtGoldEMAHandle == INVALID_HANDLE)
{
PrintFormat("ERROR: Failed to create M15 ATR/EMA handles for %s.", InpGoldSymbol);
return(INIT_FAILED);
}
}
if(needBond)
{
if(!SymbolSelect(InpBondSymbol, true))
{
PrintFormat("ERROR: Failed to select Bond/Yield symbol '%s' in Market Watch.", InpBondSymbol);
return(INIT_FAILED);
}
ExtBondATRHandle = iATR(InpBondSymbol, InpEntryTF, InpMacroATRPeriod);
ExtBondEMAHandle = iMA(InpBondSymbol, InpEntryTF, InpMacroEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
if(ExtBondATRHandle == INVALID_HANDLE || ExtBondEMAHandle == INVALID_HANDLE)
{
PrintFormat("ERROR: Failed to create M15 ATR/EMA handles for %s.", InpBondSymbol);
return(INIT_FAILED);
}
}
}
PrintFormat("USDJPY_LiquiditySweep_ProfitLock v2.00 initialized on %s | MacroFilter: %s",
_Symbol, (InpUseMacroFilter ? "ENABLED" : "DISABLED"));
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(ExtGoldATRHandle != INVALID_HANDLE) IndicatorRelease(ExtGoldATRHandle);
if(ExtGoldEMAHandle != INVALID_HANDLE) IndicatorRelease(ExtGoldEMAHandle);
if(ExtBondATRHandle != INVALID_HANDLE) IndicatorRelease(ExtBondATRHandle);
if(ExtBondEMAHandle != INVALID_HANDLE) IndicatorRelease(ExtBondEMAHandle);
Comment("");
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- 1. Refresh Daily Baseline & Check Circuit Breakers on Every Tick
UpdateDailyBaseline();
if(CheckDailyCircuitBreaker())
return;
//--- 2. Manage Open Positions on Every Tick (+20 Pip Lock & Leg 2 Scaling)
ManageOpenPositions();
//--- 3. Evaluate New Base Entries Only Once Per Closed M15 Bar
datetime currentBarTime = iTime(_Symbol, InpEntryTF, 0);
if(currentBarTime == ExtLastM15BarTime)
return;
if(HasOpenPositionByTag(LEG1_TAG) || HasOpenPositionByTag(LEG2_TAG))
{
ExtLastM15BarTime = currentBarTime;
return;
}
if(InpUseSessionFilter && !IsWithinPrimeSessionUTC())
return;
if(!IsSpreadAcceptable())
return;
//--- Mark bar as processed and check M15 Liquidity Sweep + Macro Confluence
ExtLastM15BarTime = currentBarTime;
EvaluateM15EntrySignals();
}
//+------------------------------------------------------------------+
//| Evaluate Closed M15 Candle for Demand/Supply Sweeps + Macro |
//+------------------------------------------------------------------+
void EvaluateM15EntrySignals()
{
MqlRates rates[];
ArraySetAsSeries(rates, true);
if(CopyRates(_Symbol, InpEntryTF, 1, 4, rates) < 4)
return;
double h4Close = iClose(_Symbol, InpStructureTF, 1);
bool longsAllowedByH4 = (h4Close >= InpBearishBreakdownH4);
double avgPrior3Range = ((rates[1].high - rates[1].low) +
(rates[2].high - rates[2].low) +
(rates[3].high - rates[3].low)) / 3.0;
double signalOpen = rates[0].open;
double signalHigh = rates[0].high;
double signalLow = rates[0].low;
double signalClose = rates[0].close;
double signalBody = MathAbs(signalClose - signalOpen);
bool isImpulsiveBody = (signalBody >= avgPrior3Range * InpDisplacementRatio);
bool isBullishEngulf = (signalClose > signalOpen && signalClose > rates[1].high);
bool isBearishEngulf = (signalClose < signalOpen && signalClose < rates[1].low);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
//--- 1. LONG SETUP: H4 Demand Sweep (Wick <= 158.00 & >= 157.80, Close > 158.00)
bool sweptDemand = (signalLow <= InpDemandUpper && signalLow >= (InpDemandLower - 5.0 * ExtPipSize));
bool closedAboveDemand = (signalClose > InpDemandUpper && signalClose > signalOpen);
if(longsAllowedByH4 && sweptDemand && closedAboveDemand && (isImpulsiveBody || isBullishEngulf))
{
if(ask < InpLongTargetPrice - (25.0 * ExtPipSize))
{
if(!InpUseMacroFilter || CheckMacroCorrelationFilter(ORDER_TYPE_BUY))
ExecuteBaseEntry(ORDER_TYPE_BUY, ask, InpLongTargetPrice);
else
PrintFormat("M15 LONG Sweep filtered out by Macro Confluence: %s", ExtLastMacroDiagnostics);
return;
}
}
//--- 2. SHORT SETUP: H4 Supply Rejection (Wick >= 158.95 & < 159.20, Close < 158.85)
bool sweptSupply = (signalHigh >= InpSupplyUpper && signalHigh < InpMoFCeilingCutoff);
bool closedBelowSupply = (signalClose <= InpSupplyLower && signalClose < signalOpen);
if(sweptSupply && closedBelowSupply && (isImpulsiveBody || isBearishEngulf))
{
if(bid > InpShortTargetPrice + (25.0 * ExtPipSize))
{
if(!InpUseMacroFilter || CheckMacroCorrelationFilter(ORDER_TYPE_SELL))
ExecuteBaseEntry(ORDER_TYPE_SELL, bid, InpShortTargetPrice);
else
PrintFormat("M15 SHORT Sweep filtered out by Macro Confluence: %s", ExtLastMacroDiagnostics);
return;
}
}
}
//+------------------------------------------------------------------+
//| Multi-Symbol Macro Confluence Check (XAUUSD / US10Y) |
//+------------------------------------------------------------------+
bool CheckMacroCorrelationFilter(ENUM_ORDER_TYPE usdjpyOrderType)
{
ExtLastMacroDiagnostics = "";
bool goldConfirmed = false;
bool bondConfirmed = false;
string goldDiag = "N/A";
string bondDiag = "N/A";
bool evaluateGold = (InpCorrSource == CORR_XAUUSD_ONLY ||
InpCorrSource == CORR_EITHER_CONFIRMS ||
InpCorrSource == CORR_BOTH_REQUIRED);
bool evaluateBond = (InpCorrSource == CORR_US10Y_ONLY ||
InpCorrSource == CORR_EITHER_CONFIRMS ||
InpCorrSource == CORR_BOTH_REQUIRED);
//--- 1. Evaluate XAUUSD (Always Inverse to USD/JPY: Bearish Gold = Bullish USD/JPY)
if(evaluateGold)
{
goldConfirmed = EvaluateSingleSymbolMomentum(InpGoldSymbol,
ExtGoldATRHandle,
ExtGoldEMAHandle,
false, // Inverse correlation
usdjpyOrderType,
goldDiag);
}
//--- 2. Evaluate US10Y / T-Note (Direct if Yield, Inverse if Bond Price CFD)
if(evaluateBond)
{
bondConfirmed = EvaluateSingleSymbolMomentum(InpBondSymbol,
ExtBondATRHandle,
ExtBondEMAHandle,
InpBondIsYieldDirect,
usdjpyOrderType,
bondDiag);
}
ExtLastMacroDiagnostics = StringFormat("[Gold: %s | Bond: %s]", goldDiag, bondDiag);
//--- Combine according to selected correlation source rule
if(InpCorrSource == CORR_XAUUSD_ONLY)
return(goldConfirmed);
if(InpCorrSource == CORR_US10Y_ONLY)
return(bondConfirmed);
if(InpCorrSource == CORR_EITHER_CONFIRMS)
return(goldConfirmed || bondConfirmed);
if(InpCorrSource == CORR_BOTH_REQUIRED)
return(goldConfirmed && bondConfirmed);
return(false);
}
//+------------------------------------------------------------------+
//| Compute ATR-Normalized Impulse & EMA Slope for a Secondary Asset |
//+------------------------------------------------------------------+
bool EvaluateSingleSymbolMomentum(string corrSymbol,
int atrHandle,
int emaHandle,
bool isDirectCorrelation,
ENUM_ORDER_TYPE usdjpyOrderType,
string &outDiag)
{
//--- Trigger tick/series refresh on secondary symbol
MqlTick tick;
SymbolInfoTick(corrSymbol, tick);
int barsNeeded = InpMomentumLookback + 2;
double closeBuf[];
double atrBuf[];
double emaBuf[];
ArraySetAsSeries(closeBuf, true);
ArraySetAsSeries(atrBuf, true);
ArraySetAsSeries(emaBuf, true);
//--- Copy closed M15 bars (start at shift 1)
if(CopyClose(corrSymbol, InpEntryTF, 1, barsNeeded, closeBuf) < barsNeeded ||
CopyBuffer(atrHandle, 0, 1, 2, atrBuf) < 2 ||
CopyBuffer(emaHandle, 0, 1, 2, emaBuf) < 2)
{
outDiag = "SyncPending";
return(false);
}
double atr = atrBuf[0];
if(atr <= 0.0)
{
outDiag = "ZeroATR";
return(false);
}
//--- Raw ATR-normalized impulse over N closed M15 bars: (Close[1] - Close[1 + N]) / ATR[1]
double rawImpulseATR = (closeBuf[0] - closeBuf[InpMomentumLookback]) / atr;
double emaSlopeATR = (emaBuf[0] - emaBuf[1]) / atr;
bool aboveEMA = (closeBuf[0] > emaBuf[0]);
bool belowEMA = (closeBuf[0] < emaBuf[0]);
//--- Translate into "USD/JPY Bullish Alignment Score"
//--- If Direct (US10Y Yield): +Impulse = Bullish USD/JPY
//--- If Inverse (XAUUSD / T-Note Price): -Impulse = Bullish USD/JPY
double alignedBullishScore = isDirectCorrelation ? rawImpulseATR : -rawImpulseATR;
double alignedEMASlope = isDirectCorrelation ? emaSlopeATR : -emaSlopeATR;
bool alignedEMAPosition = isDirectCorrelation ? aboveEMA : belowEMA;
bool alignedEMAShortPos = isDirectCorrelation ? belowEMA : aboveEMA;
bool passed = false;
if(InpFilterStrictness == STRICT_DIRECTIONAL)
{
if(usdjpyOrderType == ORDER_TYPE_BUY)
{
//--- Require positive USD/JPY alignment >= InpMinConfirmATR OR firm EMA trend
passed = (alignedBullishScore >= InpMinConfirmATR ||
(alignedEMAPosition && alignedEMASlope > 0.0 && alignedBullishScore > -0.05));
}
else if(usdjpyOrderType == ORDER_TYPE_SELL)
{
//--- Require negative USD/JPY alignment <= -InpMinConfirmATR OR bearish EMA trend
passed = (alignedBullishScore <= -InpMinConfirmATR ||
(alignedEMAShortPos && alignedEMASlope < 0.0 && alignedBullishScore < 0.05));
}
}
else // VETO_OPPOSING_SPIKE
{
if(usdjpyOrderType == ORDER_TYPE_BUY)
{
//--- Allow USD/JPY Long unless secondary asset is spiking against USD/JPY
passed = (alignedBullishScore > -InpMaxVetoSpikeATR);
}
else if(usdjpyOrderType == ORDER_TYPE_SELL)
{
//--- Allow USD/JPY Short unless secondary asset is spiking in favor of USD/JPY
passed = (alignedBullishScore < InpMaxVetoSpikeATR);
}
}
outDiag = StringFormat("%s RawROC:%.2fATR Aligned:%.2fATR (%s)",
corrSymbol, rawImpulseATR, alignedBullishScore, (passed ? "OK" : "BLOCKED"));
return(passed);
}
//+------------------------------------------------------------------+
//| Reset daily starting balance at server midnight |
//+------------------------------------------------------------------+
void UpdateDailyBaseline()
{
MqlDateTime dt;
TimeCurrent(dt);
if(dt.day_of_year != ExtCurrentDayOfYear)
{
ExtCurrentDayOfYear = dt.day_of_year;
ExtDayStartBalance = AccountInfoDouble(ACCOUNT_BALANCE);
ExtDailyCircuitTripped = false;
}
}
//+------------------------------------------------------------------+
//| Verify 1.50% Daily Drawdown & Max Consecutive Stop-Outs |
//+------------------------------------------------------------------+
bool CheckDailyCircuitBreaker()
{
if(ExtDailyCircuitTripped)
return(true);
datetime dayStart = iTime(_Symbol, PERIOD_D1, 0);
double realizedToday = 0.0;
int losingTradesToday = 0;
if(HistorySelect(dayStart, TimeCurrent()))
{
int totalDeals = HistoryDealsTotal();
for(int i = 0; i < totalDeals; i++)
{
ulong dealTicket = HistoryDealGetTicket(i);
if(dealTicket == 0) continue;
if(HistoryDealGetInteger(dealTicket, DEAL_MAGIC) != (long)InpMagicNumber) continue;
if(HistoryDealGetString(dealTicket, DEAL_SYMBOL) != _Symbol) continue;
long dealEntry = HistoryDealGetInteger(dealTicket, DEAL_ENTRY);
if(dealEntry == DEAL_ENTRY_OUT || dealEntry == DEAL_ENTRY_INOUT)
{
double netDealProfit = HistoryDealGetDouble(dealTicket, DEAL_PROFIT)
+ HistoryDealGetDouble(dealTicket, DEAL_COMMISSION)
+ HistoryDealGetDouble(dealTicket, DEAL_SWAP);
realizedToday += netDealProfit;
if(netDealProfit < -1.0)
losingTradesToday++;
}
}
}
double floatingPnL = GetOpenFloatingPnL();
double totalDailyPnL = realizedToday + floatingPnL;
double maxAllowedLoss = -1.0 * ExtDayStartBalance * (InpMaxDailyDrawdownPct / 100.0);
if(totalDailyPnL <= maxAllowedLoss || losingTradesToday >= InpMaxDailyLosingTrades)
{
ExtDailyCircuitTripped = true;
string msg = StringFormat("Circuit Breaker Tripped on %s | Daily PnL: %.2f | Losses Today: %d",
_Symbol, totalDailyPnL, losingTradesToday);
Print(msg);
if(InpEnablePushNotify) SendNotification(msg);
if(InpCloseAllOnDailyLimit)
CloseAllEAPositions();
return(true);
}
return(false);
}
//+------------------------------------------------------------------+
//| Tick-level Position Management: +20 Pip Lock & Leg 2 Scale-In |
//+------------------------------------------------------------------+
void ManageOpenPositions()
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(bid >= InpMoFCeilingCutoff)
{
if(HasOpenPositionByTag(LEG1_TAG) || HasOpenPositionByTag(LEG2_TAG))
{
Print("MoF Ceiling Cutoff breached (>= ", InpMoFCeilingCutoff, "). Flattening exposure.");
CloseAllEAPositions();
}
return;
}
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(!ExtPosition.SelectByIndex(i)) continue;
if(ExtPosition.Symbol() != _Symbol || ExtPosition.Magic() != InpMagicNumber) continue;
if(StringFind(ExtPosition.Comment(), LEG1_TAG) < 0) continue;
ulong leg1Ticket = ExtPosition.Ticket();
double leg1OpenPrice = ExtPosition.PriceOpen();
double leg1CurrentSL = ExtPosition.StopLoss();
double leg1CurrentTP = ExtPosition.TakeProfit();
double leg1Volume = ExtPosition.Volume();
ENUM_POSITION_TYPE posType = ExtPosition.PositionType();
double triggerDist = InpScaleTriggerPips * ExtPipSize;
double lockDist = InpLockedProfitPips * ExtPipSize;
if(posType == POSITION_TYPE_BUY)
{
double currentGainPrice = bid - leg1OpenPrice;
double targetLockSL = NormalizeDouble(leg1OpenPrice + lockDist, _Digits);
if(currentGainPrice >= triggerDist && leg1CurrentSL < targetLockSL - (_Point * 0.5))
{
if(ExtTrade.PositionModify(leg1Ticket, targetLockSL, leg1CurrentTP))
{
PrintFormat("Leg 1 BUY #%I64u SL locked at +%.1f pips (%.3f).",
leg1Ticket, InpLockedProfitPips, targetLockSL);
if(!HasOpenPositionByTag(LEG2_TAG))
ExecuteHouseMoneyScaleIn(ORDER_TYPE_BUY, leg1Volume, lockDist, ask, leg1CurrentTP);
}
}
}
else if(posType == POSITION_TYPE_SELL)
{
double currentGainPrice = leg1OpenPrice - ask;
double targetLockSL = NormalizeDouble(leg1OpenPrice - lockDist, _Digits);
if(currentGainPrice >= triggerDist && (leg1CurrentSL == 0.0 || leg1CurrentSL > targetLockSL + (_Point * 0.5)))
{
if(ExtTrade.PositionModify(leg1Ticket, targetLockSL, leg1CurrentTP))
{
PrintFormat("Leg 1 SELL #%I64u SL locked at +%.1f pips (%.3f).",
leg1Ticket, InpLockedProfitPips, targetLockSL);
if(!HasOpenPositionByTag(LEG2_TAG))
ExecuteHouseMoneyScaleIn(ORDER_TYPE_SELL, leg1Volume, lockDist, bid, leg1CurrentTP);
}
}
}
}
}
//+------------------------------------------------------------------+
//| Open Leg 2 Funded Strictly by Locked-In Dollar Profit of Leg 1 |
//+------------------------------------------------------------------+
void ExecuteHouseMoneyScaleIn(ENUM_ORDER_TYPE orderType,
double leg1Volume,
double lockedPriceDistance,
double entryPrice,
double takeProfitPrice)
{
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE_LOSS);
if(tickValue <= 0.0)
tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
if(tickSize <= 0.0 || tickValue <= 0.0)
return;
double lockedProfitDollars = leg1Volume * (lockedPriceDistance / tickSize) * tickValue;
double riskBudgetForLeg2 = lockedProfitDollars * InpCommissionBuffer;
double leg2SLDistance = InpScaleStopLossPips * ExtPipSize;
double leg2Lots = CalculateLotSizeFromMoney(riskBudgetForLeg2, leg2SLDistance);
if(leg2Lots <= 0.0)
return;
double leg2SL = (orderType == ORDER_TYPE_BUY)
? NormalizeDouble(entryPrice - leg2SLDistance, _Digits)
: NormalizeDouble(entryPrice + leg2SLDistance, _Digits);
double leg2TP = NormalizeDouble(takeProfitPrice, _Digits);
bool res = (orderType == ORDER_TYPE_BUY)
? ExtTrade.Buy(leg2Lots, _Symbol, entryPrice, leg2SL, leg2TP, LEG2_TAG)
: ExtTrade.Sell(leg2Lots, _Symbol, entryPrice, leg2SL, leg2TP, LEG2_TAG);
if(res)
{
string msg = StringFormat("House-Money Leg 2 Opened (%s %.2f lots @ %.3f) | Risk Budget: $%.2f ($0 Principal Risk)",
(orderType == ORDER_TYPE_BUY ? "BUY" : "SELL"),
leg2Lots, entryPrice, riskBudgetForLeg2);
Print(msg);
if(InpEnablePushNotify) SendNotification(msg);
}
}
//+------------------------------------------------------------------+
//| Execute Leg 1 Base Entry (0.50% Risk) |
//+------------------------------------------------------------------+
void ExecuteBaseEntry(ENUM_ORDER_TYPE orderType, double entryPrice, double targetPrice)
{
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskMoney = balance * (InpBaseRiskPercent / 100.0);
double slDistance = InpBaseStopLossPips * ExtPipSize;
double lotSize = CalculateLotSizeFromMoney(riskMoney, slDistance);
if(lotSize <= 0.0)
return;
double slPrice = (orderType == ORDER_TYPE_BUY)
? NormalizeDouble(entryPrice - slDistance, _Digits)
: NormalizeDouble(entryPrice + slDistance, _Digits);
double tpPrice = NormalizeDouble(targetPrice, _Digits);
bool res = (orderType == ORDER_TYPE_BUY)
? ExtTrade.Buy(lotSize, _Symbol, entryPrice, slPrice, tpPrice, LEG1_TAG)
: ExtTrade.Sell(lotSize, _Symbol, entryPrice, slPrice, tpPrice, LEG1_TAG);
if(res)
{
string msg = StringFormat("Leg 1 %s %.2f lots @ %.3f | SL: %.3f | TP: %.3f | Macro: %s",
(orderType == ORDER_TYPE_BUY ? "BUY" : "SELL"),
lotSize, entryPrice, slPrice, tpPrice, ExtLastMacroDiagnostics);
Print(msg);
if(InpEnablePushNotify) SendNotification(msg);
}
}
//+------------------------------------------------------------------+
//| Calculate Normalized Lot Size from Dollar Risk & Price Distance |
//+------------------------------------------------------------------+
double CalculateLotSizeFromMoney(double riskMoney, double slPriceDistance)
{
if(riskMoney <= 0.0 || slPriceDistance <= 0.0)
return(0.0);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE_LOSS);
if(tickValue <= 0.0)
tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
if(tickSize <= 0.0 || tickValue <= 0.0)
return(0.0);
double lossPerLot = (slPriceDistance / tickSize) * tickValue;
if(lossPerLot <= 0.0)
return(0.0);
double rawLots = riskMoney / lossPerLot;
double minVol = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxVol = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double stepVol = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
double normalizedLots = MathFloor(rawLots / stepVol) * stepVol;
if(normalizedLots < minVol)
return(0.0);
if(normalizedLots > maxVol)
normalizedLots = maxVol;
return(NormalizeDouble(normalizedLots, 2));
}
//+------------------------------------------------------------------+
//| Helper: Check if Prime Tokyo or London-NY Overlap Window (UTC) |
//+------------------------------------------------------------------+
bool IsWithinPrimeSessionUTC()
{
MqlDateTime utc;
TimeGMT(utc);
bool inTokyo = (utc.hour >= InpTokyoStartHourUTC && utc.hour < InpTokyoEndHourUTC);
bool inOverlap = (utc.hour >= InpOverlapStartHourUTC && utc.hour < InpOverlapEndHourUTC);
return(inTokyo || inOverlap);
}
//+------------------------------------------------------------------+
//| Helper: Verify Raw ECN Spread is Below Threshold |
//+------------------------------------------------------------------+
bool IsSpreadAcceptable()
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double currentSpreadPips = (ask - bid) / ExtPipSize;
return(currentSpreadPips <= InpMaxSpreadPips);
}
//+------------------------------------------------------------------+
//| Helper: Check if a Position with Specific Comment Tag Exists |
//+------------------------------------------------------------------+
bool HasOpenPositionByTag(string tag)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(!ExtPosition.SelectByIndex(i)) continue;
if(ExtPosition.Symbol() == _Symbol &&
ExtPosition.Magic() == InpMagicNumber &&
StringFind(ExtPosition.Comment(), tag) >= 0)
{
return(true);
}
}
return(false);
}
//+------------------------------------------------------------------+
//| Helper: Get Total Floating PnL of Open Positions for this EA |
//+------------------------------------------------------------------+
double GetOpenFloatingPnL()
{
double floating = 0.0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(!ExtPosition.SelectByIndex(i)) continue;
if(ExtPosition.Symbol() == _Symbol && ExtPosition.Magic() == InpMagicNumber)
{
floating += ExtPosition.Profit() + ExtPosition.Swap() + ExtPosition.Commission();
}
}
return(floating);
}
//+------------------------------------------------------------------+
//| Helper: Close All Open Positions Belonging to This EA |
//+------------------------------------------------------------------+
void CloseAllEAPositions()
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(!ExtPosition.SelectByIndex(i)) continue;
if(ExtPosition.Symbol() == _Symbol && ExtPosition.Magic() == InpMagicNumber)
{
ExtTrade.PositionClose(ExtPosition.Ticket());
}
}
}
//+------------------------------------------------------------------+Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: How to build strategy to trade USD/JPY
3. Configuring the Multi-Symbol Inputs for Your Broker Feed
Using XAUUSD Only (Default & Universal ECN Setup):
Keep InpCorrSource = CORR_XAUUSD_ONLY and InpGoldSymbol = "XAUUSD".
Because Gold is priced against the USD and reacts instantaneously to US real-yield shifts, it works out of the box on every ECN feed without needing futures rollover adjustments.
Adding a 10-Year US Treasury Symbol (CORR_EITHER_CONFIRMS or CORR_BOTH_REQUIRED):
If your broker provides a Yield ticker (e.g., US10Y): Set InpBondSymbol = "US10Y" and InpBondIsYieldDirect = true (rising yield = bullish USD/JPY).
If your broker provides a 10-Year Treasury Note Futures/CFD price ticker (e.g., UST10Y_Z6 or TNOTE): Set InpBondSymbol to that exact Market Watch symbol and set InpBondIsYieldDirect = false (falling bond price = rising yield = bullish USD/JPY).
MT5 Strategy Tester Multi-Symbol Synchronization:
MetaTrader 5's Strategy Tester automatically downloads and synchronizes M15 tick history for XAUUSD (and US10Y) as soon as OnInit() calls SymbolSelect(). Run your backtest in "Every tick based on real ticks" mode so cross-symbol bid/ask timestamps align to the millisecond.
Using XAUUSD Only (Default & Universal ECN Setup):
Keep InpCorrSource = CORR_XAUUSD_ONLY and InpGoldSymbol = "XAUUSD".
Because Gold is priced against the USD and reacts instantaneously to US real-yield shifts, it works out of the box on every ECN feed without needing futures rollover adjustments.
Adding a 10-Year US Treasury Symbol (CORR_EITHER_CONFIRMS or CORR_BOTH_REQUIRED):
If your broker provides a Yield ticker (e.g., US10Y): Set InpBondSymbol = "US10Y" and InpBondIsYieldDirect = true (rising yield = bullish USD/JPY).
If your broker provides a 10-Year Treasury Note Futures/CFD price ticker (e.g., UST10Y_Z6 or TNOTE): Set InpBondSymbol to that exact Market Watch symbol and set InpBondIsYieldDirect = false (falling bond price = rising yield = bullish USD/JPY).
MT5 Strategy Tester Multi-Symbol Synchronization:
MetaTrader 5's Strategy Tester automatically downloads and synchronizes M15 tick history for XAUUSD (and US10Y) as soon as OnInit() calls SymbolSelect(). Run your backtest in "Every tick based on real ticks" mode so cross-symbol bid/ask timestamps align to the millisecond.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: How to build strategy to trade USD/JPY
Replacing static price inputs with auto-detected H4 Fractal + ATR Supply and Demand zones allows the Expert Advisor to adapt seamlessly as USD/JPY shifts regimes, while eliminating look-ahead bias in the MetaTrader 5 Strategy Tester.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: How to build strategy to trade USD/JPY
1. Mathematical & Algorithmic Specification (v3.00)
In MQL5, a Bill Williams 5-bar Fractal (iFractals) at bar index $k$ requires two closed bars to its right ($k-1$ and $k-2$) to confirm. Therefore, the earliest confirmed fractal without repainting is always at H4 shift $k \ge 3$.
To construct institutional-grade H4 liquidity zones from confirmed fractals:
Unmitigated Walk-Forward Validation:Scanning backward from H4 shift $k = 3$ up to InpFractalLookbackBars (default 120 H4 bars = 20 trading days), the EA locates the nearest confirmed Down Fractal below current price ($L_k$) and Up Fractal above current price ($H_k$) that have not been invalidated (broken) by any subsequent H4 candle close between bar $k-1$ and bar $1$:
Valid Demand Anchor ($L_k$): $\min_{j \in [1, k-1]} \text{Close}_{\text{H4}}[j] \ge L_k - \left(\text{ATR}_{\text{H4}, 14}[k] \times k_{\text{break}}\right)$
Valid Supply Anchor ($H_k$): $\max_{j \in [1, k-1]} \text{Close}_{\text{H4}}[j] \le H_k + \left(\text{ATR}_{\text{H4}, 14}[k] \times k_{\text{break}}\right)$
In MQL5, a Bill Williams 5-bar Fractal (iFractals) at bar index $k$ requires two closed bars to its right ($k-1$ and $k-2$) to confirm. Therefore, the earliest confirmed fractal without repainting is always at H4 shift $k \ge 3$.
To construct institutional-grade H4 liquidity zones from confirmed fractals:
Unmitigated Walk-Forward Validation:Scanning backward from H4 shift $k = 3$ up to InpFractalLookbackBars (default 120 H4 bars = 20 trading days), the EA locates the nearest confirmed Down Fractal below current price ($L_k$) and Up Fractal above current price ($H_k$) that have not been invalidated (broken) by any subsequent H4 candle close between bar $k-1$ and bar $1$:
Valid Demand Anchor ($L_k$): $\min_{j \in [1, k-1]} \text{Close}_{\text{H4}}[j] \ge L_k - \left(\text{ATR}_{\text{H4}, 14}[k] \times k_{\text{break}}\right)$
Valid Supply Anchor ($H_k$): $\max_{j \in [1, k-1]} \text{Close}_{\text{H4}}[j] \le H_k + \left(\text{ATR}_{\text{H4}, 14}[k] \times k_{\text{break}}\right)$
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: How to build strategy to trade USD/JPY
2.) ATR-Scaled Zone Geometry:
Using the H4 candle's body edge (MathMin(Open, Close) for Demand; MathMax(Open, Close) for Supply) clamped by $\text{ATR}_{\text{H4}, 14}$ multipliers (InpMinZoneWidthATR = 0.20, InpMaxZoneWidthATR = 0.45).
Using the H4 candle's body edge (MathMin(Open, Close) for Demand; MathMax(Open, Close) for Supply) clamped by $\text{ATR}_{\text{H4}, 14}$ multipliers (InpMinZoneWidthATR = 0.20, InpMaxZoneWidthATR = 0.45).
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.