fix: ORB range = opening price only, Strict filter structurally impossible for ORB breakouts
This commit is contained in:
+76
-30
@@ -84,8 +84,10 @@ input double InpOBProximityPips = 50.0; // OB Proximity (pips)
|
|||||||
|
|
||||||
// --- Entry Settings ---
|
// --- Entry Settings ---
|
||||||
input group "═══ Entry Settings ═══"
|
input group "═══ Entry Settings ═══"
|
||||||
input ENUM_ENTRY_MODE InpEntryMode = ENTRY_MARKET; // Entry Mode
|
input ENUM_ENTRY_MODE InpEntryMode = ENTRY_MARKET; // Entry Mode
|
||||||
input bool InpUseStrictFilter = true; // Use Strict Breakout Filter
|
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)
|
||||||
|
|
||||||
// --- Lot Size & Risk ---
|
// --- Lot Size & Risk ---
|
||||||
input group "═══ Lot Size & Risk ═══"
|
input group "═══ Lot Size & Risk ═══"
|
||||||
@@ -438,28 +440,26 @@ bool IsWithinOrbWindow(const SSession &sess, datetime barTime)
|
|||||||
|
|
||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
//| Update ORB for all sessions on new bar |
|
//| Update ORB for all sessions on new bar |
|
||||||
|
//| IMPORTANT: always reads bar[1] (the just-CLOSED candle) so the |
|
||||||
|
//| true high/low is captured. bar[0] at new-bar time = open only. |
|
||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
void UpdateORBSessions()
|
void UpdateORBSessions()
|
||||||
{
|
{
|
||||||
datetime barTime = iTime(g_Symbol, PERIOD_CURRENT, 0);
|
datetime bar1Time = iTime(g_Symbol, PERIOD_CURRENT, 1);
|
||||||
double barHigh = iHigh(g_Symbol, PERIOD_CURRENT, 0);
|
double bar1High = iHigh(g_Symbol, PERIOD_CURRENT, 1);
|
||||||
double barLow = iLow(g_Symbol, PERIOD_CURRENT, 0);
|
double bar1Low = iLow(g_Symbol, PERIOD_CURRENT, 1);
|
||||||
|
|
||||||
MqlDateTime dt;
|
|
||||||
TimeToStruct(barTime, dt);
|
|
||||||
|
|
||||||
for(int s = 0; s < g_SessionCount; s++)
|
for(int s = 0; s < g_SessionCount; s++)
|
||||||
{
|
{
|
||||||
if(!g_Sessions[s].enabled) continue;
|
if(!g_Sessions[s].enabled) continue;
|
||||||
|
|
||||||
// Session open bar starts accumulation
|
// Session open bar just closed: bar[1] time matches session start
|
||||||
if(IsSessionOpenBar(g_Sessions[s], barTime))
|
if(IsSessionOpenBar(g_Sessions[s], bar1Time))
|
||||||
{
|
{
|
||||||
// Reset session state for new day's ORB
|
g_Sessions[s].orbHigh = bar1High;
|
||||||
g_Sessions[s].orbHigh = barHigh;
|
g_Sessions[s].orbLow = bar1Low;
|
||||||
g_Sessions[s].orbLow = barLow;
|
|
||||||
g_Sessions[s].orbBarCount = 1;
|
g_Sessions[s].orbBarCount = 1;
|
||||||
g_Sessions[s].orbStartTime = barTime;
|
g_Sessions[s].orbStartTime = bar1Time;
|
||||||
g_Sessions[s].orbComplete = (g_OrbBarsNeeded == 1);
|
g_Sessions[s].orbComplete = (g_OrbBarsNeeded == 1);
|
||||||
g_Sessions[s].breakoutDir = 0;
|
g_Sessions[s].breakoutDir = 0;
|
||||||
g_Sessions[s].inBreakout = false;
|
g_Sessions[s].inBreakout = false;
|
||||||
@@ -467,17 +467,20 @@ void UpdateORBSessions()
|
|||||||
g_Sessions[s].breakoutBarsAgo = 0;
|
g_Sessions[s].breakoutBarsAgo = 0;
|
||||||
g_Sessions[s].tradesThisSession= 0;
|
g_Sessions[s].tradesThisSession= 0;
|
||||||
|
|
||||||
|
if(g_Sessions[s].orbComplete)
|
||||||
|
PrintFormat("NANDR EA: [%s] ORB complete. High=%.2f Low=%.2f",
|
||||||
|
g_Sessions[s].name, bar1High, bar1Low);
|
||||||
if(InpShowORBLines)
|
if(InpShowORBLines)
|
||||||
DrawORBLines(s);
|
DrawORBLines(s);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Still accumulating (M5 or M1)
|
// Still accumulating (M5 or M1): append bar[1] closed data to range
|
||||||
if(!g_Sessions[s].orbComplete && g_Sessions[s].orbBarCount > 0
|
if(!g_Sessions[s].orbComplete && g_Sessions[s].orbBarCount > 0
|
||||||
&& IsWithinOrbWindow(g_Sessions[s], barTime))
|
&& IsWithinOrbWindow(g_Sessions[s], bar1Time))
|
||||||
{
|
{
|
||||||
g_Sessions[s].orbHigh = MathMax(g_Sessions[s].orbHigh, barHigh);
|
g_Sessions[s].orbHigh = MathMax(g_Sessions[s].orbHigh, bar1High);
|
||||||
g_Sessions[s].orbLow = MathMin(g_Sessions[s].orbLow, barLow);
|
g_Sessions[s].orbLow = MathMin(g_Sessions[s].orbLow, bar1Low);
|
||||||
g_Sessions[s].orbBarCount++;
|
g_Sessions[s].orbBarCount++;
|
||||||
if(g_Sessions[s].orbBarCount >= g_OrbBarsNeeded)
|
if(g_Sessions[s].orbBarCount >= g_OrbBarsNeeded)
|
||||||
{
|
{
|
||||||
@@ -489,7 +492,11 @@ void UpdateORBSessions()
|
|||||||
DrawORBLines(s);
|
DrawORBLines(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Track breakout bar age
|
// Track breakout bar age (only while breakout is active)
|
||||||
|
// NOTE: incremented here before DetectBreakouts, so newly-detected
|
||||||
|
// breakouts start at 0 here but will be seen as 0 in CheckEntrySignals
|
||||||
|
// because DetectBreakouts resets it to 0 AFTER this runs.
|
||||||
|
// The entry guard uses <=1 to handle both cases safely.
|
||||||
if(g_Sessions[s].inBreakout)
|
if(g_Sessions[s].inBreakout)
|
||||||
g_Sessions[s].breakoutBarsAgo++;
|
g_Sessions[s].breakoutBarsAgo++;
|
||||||
}
|
}
|
||||||
@@ -924,20 +931,59 @@ void CheckEntrySignals()
|
|||||||
{
|
{
|
||||||
if(!g_Sessions[s].enabled || !g_Sessions[s].orbComplete) continue;
|
if(!g_Sessions[s].enabled || !g_Sessions[s].orbComplete) continue;
|
||||||
if(g_Sessions[s].tradesThisSession >= InpMaxPosPerSession) continue;
|
if(g_Sessions[s].tradesThisSession >= InpMaxPosPerSession) continue;
|
||||||
|
if(!g_Sessions[s].inBreakout) continue;
|
||||||
|
|
||||||
int dir = 0;
|
// --- Breakout expiry ---
|
||||||
if(!DetectRetest(s, dir)) continue;
|
if(InpBreakoutExpireBars > 0
|
||||||
|
&& g_Sessions[s].breakoutBarsAgo >= InpBreakoutExpireBars)
|
||||||
double obTop = 0, obBottom = 0;
|
{
|
||||||
bool obFound = IsOBNearLevel(
|
PrintFormat("NANDR EA: [%s] Breakout expired after %d bars. Resetting.",
|
||||||
(dir > 0) ? g_Sessions[s].orbHigh : g_Sessions[s].orbLow,
|
g_Sessions[s].name, g_Sessions[s].breakoutBarsAgo);
|
||||||
dir, obTop, obBottom);
|
g_Sessions[s].inBreakout = false;
|
||||||
|
g_Sessions[s].breakoutDir = 0;
|
||||||
// If OB required and not found, skip
|
g_Sessions[s].inRetest = false;
|
||||||
if(InpOBRequireConf && !obFound) continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dir = g_Sessions[s].breakoutDir;
|
||||||
double orbLevel = (dir > 0) ? g_Sessions[s].orbHigh : g_Sessions[s].orbLow;
|
double orbLevel = (dir > 0) ? g_Sessions[s].orbHigh : g_Sessions[s].orbLow;
|
||||||
OpenTrade(dir, s, orbLevel, obTop, obBottom);
|
|
||||||
|
// --- LIMIT mode: place order immediately on breakout bar ---
|
||||||
|
if(InpEntryMode == ENTRY_LIMIT)
|
||||||
|
{
|
||||||
|
// Place once — on the bar breakout is first detected (age 0 or 1 due to update order)
|
||||||
|
if(g_Sessions[s].breakoutBarsAgo <= 1)
|
||||||
|
{
|
||||||
|
double obTop = 0, obBottom = 0;
|
||||||
|
IsOBNearLevel(orbLevel, dir, obTop, obBottom);
|
||||||
|
if(InpOBRequireConf && obTop == 0) continue;
|
||||||
|
OpenTrade(dir, s, orbLevel, obTop, obBottom);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- MARKET mode ---
|
||||||
|
if(!InpWaitForRetest)
|
||||||
|
{
|
||||||
|
// Direct entry on the bar the breakout is confirmed (age 0 or 1)
|
||||||
|
if(g_Sessions[s].breakoutBarsAgo <= 1)
|
||||||
|
{
|
||||||
|
double obTop = 0, obBottom = 0;
|
||||||
|
IsOBNearLevel(orbLevel, dir, obTop, obBottom);
|
||||||
|
if(InpOBRequireConf && obTop == 0) continue;
|
||||||
|
OpenTrade(dir, s, orbLevel, obTop, obBottom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Retest-based entry: wait for price to pull back to ORB level
|
||||||
|
int retestDir = 0;
|
||||||
|
if(!DetectRetest(s, retestDir)) continue;
|
||||||
|
double obTop = 0, obBottom = 0;
|
||||||
|
IsOBNearLevel(orbLevel, retestDir, obTop, obBottom);
|
||||||
|
if(InpOBRequireConf && obTop == 0) continue;
|
||||||
|
OpenTrade(retestDir, s, orbLevel, obTop, obBottom);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user