fix: InpWaitForRetest = false (default): The EA was entering immediately on the breakout bar, not waiting for any retest, Failed-retest threshold killed the setup early, No mid-level retest detection, The EA had zero logic to enter on a standalone OB boundary retest.
This commit is contained in:
+150
-32
@@ -5,7 +5,7 @@
|
||||
//| Based on: ORB-All-Sessions.pine + LuxAlgo Order Block Detector |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "NANDR"
|
||||
#property version "1.20"
|
||||
#property version "1.30"
|
||||
#property strict
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
@@ -68,11 +68,11 @@ input int InpBreakoutConfBars = 0; // Breakout Confirm Bars
|
||||
|
||||
// --- Sessions ---
|
||||
input group "═══ Session Settings ═══"
|
||||
input bool InpUseDailyOpen = false; // Daily Open Session (00:00 UTC)
|
||||
input bool InpUseTokyoSession = false; // Tokyo Session (00:00 UTC)
|
||||
input bool InpUseLondonSession = false; // London Session (07:00 UTC)
|
||||
input bool InpUseNYSession = false; // NY Session (12:00 UTC)
|
||||
input bool InpUseNYOrbSession = false; // NY ORB Session (13:30 UTC)
|
||||
input bool InpUseDailyOpen = true; // Daily Open Session (00:00 UTC)
|
||||
input bool InpUseTokyoSession = true; // Tokyo Session (00:00 UTC)
|
||||
input bool InpUseLondonSession = true; // London Session (07:00 UTC)
|
||||
input bool InpUseNYSession = true; // NY Session (12:00 UTC)
|
||||
input bool InpUseNYOrbSession = true; // NY ORB Session (13:30 UTC)
|
||||
|
||||
// --- Order Block Settings ---
|
||||
input group "═══ Order Block Settings ═══"
|
||||
@@ -86,40 +86,41 @@ input double InpOBProximityPips = 50.0; // OB Proximity (pips)
|
||||
input group "═══ Entry Settings ═══"
|
||||
input ENUM_ENTRY_MODE InpEntryMode = ENTRY_MARKET; // Entry Mode
|
||||
input bool InpUseStrictFilter = false; // Use Strict Breakout Filter (false=simple close-cross)
|
||||
input bool InpWaitForRetest = false; // Wait for ORB Retest Before Entry
|
||||
input int InpBreakoutExpireBars = 8; // Bars Before Breakout Expires (0=never)
|
||||
input bool InpWaitForRetest = true; // Wait for ORB Retest Before Entry
|
||||
input int InpBreakoutExpireBars = 20; // Bars Before Breakout Expires (0=never)
|
||||
input bool InpUseOBRetestEntry = true; // OB Retest Entry (enter on OB boundary retest)
|
||||
|
||||
// --- Lot Size & Risk ---
|
||||
input group "═══ Lot Size & Risk ═══"
|
||||
input ENUM_LOT_MODE InpLotMode = LOT_RISK_PERCENT; // Lot Mode
|
||||
input double InpFixedLotSize = 0.01; // Fixed Lot Size
|
||||
input double InpFixedLotSize = 0.05; // Fixed Lot Size
|
||||
input double InpRiskPercent = 1.0; // Risk % per Trade
|
||||
|
||||
// --- Stop Loss ---
|
||||
input group "═══ Stop Loss ═══"
|
||||
input ENUM_SL_MODE InpSLMode = SL_OB_BOUNDARY; // SL Mode
|
||||
input double InpSLPips = 20.0; // SL Fixed Pips
|
||||
input double InpSLPips = 100.0; // SL Fixed Pips
|
||||
input double InpATRMultiplier = 1.5; // ATR Multiplier (for SL_ATR)
|
||||
input int InpATRPeriod = 14; // ATR Period
|
||||
|
||||
// --- Take Profit ---
|
||||
input group "═══ Take Profit ═══"
|
||||
input ENUM_TP_MODE InpTPMode = TP_RR_RATIO; // TP Mode
|
||||
input double InpTPPips = 40.0; // TP Fixed Pips
|
||||
input double InpRRRatio = 2.0; // Risk:Reward Ratio
|
||||
input double InpTPPips = 100.0; // TP Fixed Pips
|
||||
input double InpRRRatio = 1.0; // Risk:Reward Ratio
|
||||
|
||||
// --- Trade Management ---
|
||||
input group "═══ Trade Management ═══"
|
||||
input double InpBreakevenTrigPips = 15.0; // Breakeven Trigger (pips)
|
||||
input double InpBreakevenTrigPips = 50.0; // Breakeven Trigger (pips)
|
||||
input double InpBreakevenOffPips = 2.0; // Breakeven Offset (pips)
|
||||
input double InpTrailingStopPips = 0.0; // Trailing Stop Pips (0=off)
|
||||
input int InpMaxTradesPerDay = 3; // Max Trades per Day
|
||||
input int InpMaxPosPerSession = 1; // Max Positions per Session
|
||||
input int InpMaxTradesPerDay = 8; // Max Trades per Day
|
||||
input int InpMaxPosPerSession = 2; // Max Positions per Session
|
||||
|
||||
// --- Risk Management ---
|
||||
input group "═══ Risk Management ═══"
|
||||
input double InpMaxDailyLossUSD = 0.0; // Max Daily Loss USD (0=off)
|
||||
input double InpMaxDailyLossPct = 0.0; // Max Daily Loss % (0=off)
|
||||
input double InpMaxDailyLossUSD = 150.0; // Max Daily Loss USD (0=off)
|
||||
input double InpMaxDailyLossPct = 5.0; // Max Daily Loss % (0=off)
|
||||
|
||||
// --- Display ---
|
||||
input group "═══ Display Settings ═══"
|
||||
@@ -179,6 +180,7 @@ struct SOrderBlock
|
||||
double mid;
|
||||
datetime time;
|
||||
bool active;
|
||||
bool traded; // Prevents re-entering the same OB zone twice in one day
|
||||
string objName;
|
||||
};
|
||||
|
||||
@@ -605,35 +607,51 @@ bool DetectRetest(int sessIdx, int &dir)
|
||||
double h0 = iHigh(g_Symbol, PERIOD_CURRENT, 1);
|
||||
double l0 = iLow(g_Symbol, PERIOD_CURRENT, 1);
|
||||
|
||||
// Bullish retest: bar[2] confirmed above ORB high, bar[1] dipped to ORB and closed back above
|
||||
double orbMid = (g_Sessions[sessIdx].orbHigh + g_Sessions[sessIdx].orbLow) / 2.0;
|
||||
|
||||
// Bullish retest: standard ORB high retest OR mid-level retest
|
||||
if(g_Sessions[sessIdx].breakoutDir == 1)
|
||||
{
|
||||
bool retest = (c1 > g_Sessions[sessIdx].orbHigh)
|
||||
&& (l0 <= g_Sessions[sessIdx].orbHigh)
|
||||
&& (c0 >= g_Sessions[sessIdx].orbHigh);
|
||||
if(retest) { dir = 1; return true; }
|
||||
// Standard: previous bar above ORB high, current bar wick dips to ORB high, closes back above
|
||||
bool retestHigh = (c1 > g_Sessions[sessIdx].orbHigh)
|
||||
&& (l0 <= g_Sessions[sessIdx].orbHigh)
|
||||
&& (c0 >= g_Sessions[sessIdx].orbHigh);
|
||||
// Mid retest: price bounced back down to ORB mid after breakout, closes back above mid
|
||||
bool retestMid = (c1 > orbMid)
|
||||
&& (l0 <= orbMid)
|
||||
&& (c0 > orbMid);
|
||||
if(retestHigh || retestMid) { dir = 1; return true; }
|
||||
}
|
||||
// Bearish retest: bar[2] confirmed below ORB low, bar[1] ticked back to ORB and closed back below
|
||||
// Bearish retest: standard ORB low retest OR mid-level retest
|
||||
else if(g_Sessions[sessIdx].breakoutDir == -1)
|
||||
{
|
||||
bool retest = (c1 < g_Sessions[sessIdx].orbLow)
|
||||
&& (h0 >= g_Sessions[sessIdx].orbLow)
|
||||
&& (c0 <= g_Sessions[sessIdx].orbLow);
|
||||
if(retest) { dir = -1; return true; }
|
||||
// Standard: previous bar below ORB low, current bar wick ticks back to ORB low, closes back below
|
||||
bool retestLow = (c1 < g_Sessions[sessIdx].orbLow)
|
||||
&& (h0 >= g_Sessions[sessIdx].orbLow)
|
||||
&& (c0 <= g_Sessions[sessIdx].orbLow);
|
||||
// Mid retest: price bounced above ORB low up to the mid, closes back below mid
|
||||
// This captures entries like a 14:45 retest of the opening-day ORB mid after the break
|
||||
bool retestMid = (c1 < orbMid)
|
||||
&& (h0 >= orbMid)
|
||||
&& (c0 < orbMid);
|
||||
if(retestLow || retestMid) { dir = -1; return true; }
|
||||
}
|
||||
|
||||
// Failed retest — only on a COMPLETED bar, never on bar open
|
||||
// Failed retest — threshold raised to orbMid so a simple bounce above orbLow/below orbHigh
|
||||
// does NOT cancel the breakout prematurely; only a close through the mid invalidates it.
|
||||
if(g_Sessions[sessIdx].breakoutDir == 1
|
||||
&& c0 < g_Sessions[sessIdx].orbHigh && c1 > g_Sessions[sessIdx].orbHigh)
|
||||
&& c0 < orbMid && c1 > orbMid)
|
||||
{
|
||||
PrintFormat("NANDR EA: [%s] Failed bullish retest", g_Sessions[sessIdx].name);
|
||||
PrintFormat("NANDR EA: [%s] Failed bullish retest (closed below mid %.2f)",
|
||||
g_Sessions[sessIdx].name, orbMid);
|
||||
g_Sessions[sessIdx].inBreakout = false;
|
||||
g_Sessions[sessIdx].breakoutDir = 0;
|
||||
}
|
||||
else if(g_Sessions[sessIdx].breakoutDir == -1
|
||||
&& c0 > g_Sessions[sessIdx].orbLow && c1 < g_Sessions[sessIdx].orbLow)
|
||||
&& c0 > orbMid && c1 < orbMid)
|
||||
{
|
||||
PrintFormat("NANDR EA: [%s] Failed bearish retest", g_Sessions[sessIdx].name);
|
||||
PrintFormat("NANDR EA: [%s] Failed bearish retest (closed above mid %.2f)",
|
||||
g_Sessions[sessIdx].name, orbMid);
|
||||
g_Sessions[sessIdx].inBreakout = false;
|
||||
g_Sessions[sessIdx].breakoutDir = 0;
|
||||
}
|
||||
@@ -701,6 +719,7 @@ void ScanOrderBlocks()
|
||||
g_BullOBs[g_BullOBCount].mid = (hl2 + pivotLow) / 2.0;
|
||||
g_BullOBs[g_BullOBCount].time = obTime;
|
||||
g_BullOBs[g_BullOBCount].active = true;
|
||||
g_BullOBs[g_BullOBCount].traded = false;
|
||||
g_BullOBs[g_BullOBCount].objName= "NANDR_BullOB_" + IntegerToString(obTime);
|
||||
g_BullOBCount++;
|
||||
if(InpShowOBZones) DrawOBZone(g_BullOBCount - 1, true);
|
||||
@@ -719,6 +738,7 @@ void ScanOrderBlocks()
|
||||
g_BearOBs[g_BearOBCount].mid = (pivotHigh + hl2) / 2.0;
|
||||
g_BearOBs[g_BearOBCount].time = obTime;
|
||||
g_BearOBs[g_BearOBCount].active = true;
|
||||
g_BearOBs[g_BearOBCount].traded = false;
|
||||
g_BearOBs[g_BearOBCount].objName= "NANDR_BearOB_" + IntegerToString(obTime);
|
||||
g_BearOBCount++;
|
||||
if(InpShowOBZones) DrawOBZone(g_BearOBCount - 1, false);
|
||||
@@ -922,6 +942,99 @@ void OpenTrade(int dir, int sessIdx, double orbLevel, double obTop, double obBot
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Standalone OB retest entries (Entry 3 type: 18:00 Bear OB low) |
|
||||
//| Fires when price retests the boundary of an active OB zone and |
|
||||
//| closes back on the rejection side — independent of ORB sessions.|
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckOBRetestEntries()
|
||||
{
|
||||
if(!InpUseOBRetestEntry) return;
|
||||
if(g_TradingHalted) return;
|
||||
if(g_TodayTrades >= InpMaxTradesPerDay) return;
|
||||
if(CountOpenPositions() + CountPendingOrders() >= InpMaxPosPerSession) return;
|
||||
|
||||
double c0 = iClose(g_Symbol, PERIOD_CURRENT, 1); // just-closed bar
|
||||
double c1 = iClose(g_Symbol, PERIOD_CURRENT, 2); // previous closed bar
|
||||
double h0 = iHigh(g_Symbol, PERIOD_CURRENT, 1);
|
||||
double l0 = iLow(g_Symbol, PERIOD_CURRENT, 1);
|
||||
|
||||
// --- Bearish OB retest → SELL ---
|
||||
// Setup: price was below OB bottom (already rejected), wick retraces back up
|
||||
// to OB bottom, candle closes back below it → confirmed rejection sell
|
||||
for(int i = 0; i < g_BearOBCount; i++)
|
||||
{
|
||||
if(!g_BearOBs[i].active || g_BearOBs[i].traded) continue;
|
||||
|
||||
double obBottom = g_BearOBs[i].bottom;
|
||||
double obTop = g_BearOBs[i].top;
|
||||
|
||||
bool retest = (c1 < obBottom) // prev bar was below OB bottom (breakdown confirmed)
|
||||
&& (h0 >= obBottom) // this bar's wick touched the OB bottom
|
||||
&& (c0 < obBottom); // closed back below OB bottom (rejection confirmed)
|
||||
if(!retest) continue;
|
||||
|
||||
double entry = SymbolInfoDouble(g_Symbol, SYMBOL_BID);
|
||||
double sl = NormalizeDouble(obTop + PipsToPrice(2.0),
|
||||
(int)SymbolInfoInteger(g_Symbol, SYMBOL_DIGITS));
|
||||
double slPips = PriceToPips(MathAbs(entry - sl));
|
||||
double tp = CalcTP(-1, entry, sl);
|
||||
double lots = CalcLotSize(slPips);
|
||||
|
||||
bool result = g_Trade.Sell(lots, g_Symbol, 0, sl, tp,
|
||||
StringFormat("NANDR|OBRetest|SELL|%.2f", obBottom));
|
||||
if(result)
|
||||
{
|
||||
g_TodayTrades++;
|
||||
g_BearOBs[i].traded = true;
|
||||
PrintFormat("NANDR EA: OB Retest SELL. Entry=%.2f SL=%.2f TP=%.2f OB=[%.2f-%.2f]",
|
||||
entry, sl, tp, obBottom, obTop);
|
||||
if(InpShowTradeLabels) DrawTradeLabel(-1, entry, sl, tp);
|
||||
}
|
||||
else
|
||||
PrintFormat("NANDR EA: OB Retest SELL failed. Error=%d", GetLastError());
|
||||
break; // One OB entry per bar
|
||||
}
|
||||
|
||||
if(CountOpenPositions() + CountPendingOrders() >= InpMaxPosPerSession) return;
|
||||
|
||||
// --- Bullish OB retest → BUY ---
|
||||
// Setup: price was above OB top, wick dips into OB top zone, closes back above
|
||||
for(int i = 0; i < g_BullOBCount; i++)
|
||||
{
|
||||
if(!g_BullOBs[i].active || g_BullOBs[i].traded) continue;
|
||||
|
||||
double obTop = g_BullOBs[i].top;
|
||||
double obBottom = g_BullOBs[i].bottom;
|
||||
|
||||
bool retest = (c1 > obTop) // prev bar was above OB top (breakout confirmed)
|
||||
&& (l0 <= obTop) // this bar's wick touched the OB top
|
||||
&& (c0 > obTop); // closed back above OB top (rejection confirmed)
|
||||
if(!retest) continue;
|
||||
|
||||
double entry = SymbolInfoDouble(g_Symbol, SYMBOL_ASK);
|
||||
double sl = NormalizeDouble(obBottom - PipsToPrice(2.0),
|
||||
(int)SymbolInfoInteger(g_Symbol, SYMBOL_DIGITS));
|
||||
double slPips = PriceToPips(MathAbs(entry - sl));
|
||||
double tp = CalcTP(1, entry, sl);
|
||||
double lots = CalcLotSize(slPips);
|
||||
|
||||
bool result = g_Trade.Buy(lots, g_Symbol, 0, sl, tp,
|
||||
StringFormat("NANDR|OBRetest|BUY|%.2f", obTop));
|
||||
if(result)
|
||||
{
|
||||
g_TodayTrades++;
|
||||
g_BullOBs[i].traded = true;
|
||||
PrintFormat("NANDR EA: OB Retest BUY. Entry=%.2f SL=%.2f TP=%.2f OB=[%.2f-%.2f]",
|
||||
entry, sl, tp, obBottom, obTop);
|
||||
if(InpShowTradeLabels) DrawTradeLabel(1, entry, sl, tp);
|
||||
}
|
||||
else
|
||||
PrintFormat("NANDR EA: OB Retest BUY failed. Error=%d", GetLastError());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check all sessions for entry signals |
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -1064,6 +1177,10 @@ void CheckDailyReset()
|
||||
for(int s = 0; s < g_SessionCount; s++)
|
||||
g_Sessions[s].Reset();
|
||||
|
||||
// Reset OB traded flags so the same zones can re-trigger on the new day
|
||||
for(int i = 0; i < g_BullOBCount; i++) g_BullOBs[i].traded = false;
|
||||
for(int i = 0; i < g_BearOBCount; i++) g_BearOBs[i].traded = false;
|
||||
|
||||
Print("NANDR EA: Daily reset. Equity=", g_DayStartEquity);
|
||||
}
|
||||
}
|
||||
@@ -1517,6 +1634,7 @@ void OnTick()
|
||||
|
||||
// Entry signals
|
||||
CheckEntrySignals();
|
||||
CheckOBRetestEntries();
|
||||
|
||||
// Update closed trade stats & dashboard
|
||||
UpdateClosedTrades();
|
||||
|
||||
Reference in New Issue
Block a user