feat: ORB-native SL modes added (boundary stays default), All session start times are now configurable, Optional breakout volume filter (default OFF)

This commit is contained in:
Naji El Chemaly
2026-06-11 12:18:03 +03:00
parent 392b7769e4
commit 338eead522
2 changed files with 107 additions and 26 deletions
+92 -23
View File
@@ -5,7 +5,7 @@
//| Based on: ORB-All-Sessions.pine + LuxAlgo Order Block Detector |
//+------------------------------------------------------------------+
#property copyright "NANDR"
#property version "1.395"
#property version "1.396"
#property strict
#include <Trade\Trade.mqh>
@@ -26,7 +26,9 @@ enum ENUM_SL_MODE
{
SL_OB_BOUNDARY, // OB Boundary
SL_FIXED_PIPS, // Fixed Pips
SL_ATR_BASED // ATR-Based
SL_ATR_BASED, // ATR-Based
SL_ORB_OPPOSITE, // ORB Opposite Boundary (buy=ORL, sell=ORH)
SL_ORB_MID // ORB Range Midpoint
};
enum ENUM_TP_MODE
@@ -76,9 +78,20 @@ input int InpBreakoutConfBars = 0; // Breakout Confirm Bars
input group "═══ Session Settings ═══"
input bool InpUseDailyOpen = true; // Daily Open Session (22:00 UTC, 01:00 Kuwait)
input bool InpUseTokyoSession = true; // Tokyo Session (00:00 UTC)
input bool InpUseLondonSession = true; // London Session (07:00 UTC)
input bool InpUseLondonSession = true; // London Session (08:00 UTC = 8AM GMT)
input bool InpUseNYSession = true; // NY Session (12:00 UTC)
input bool InpUseNYOrbSession = true; // NY ORB Session (13:30 UTC)
input bool InpUseNYOrbSession = true; // NY ORB Session (13:30 UTC = 9:30 EDT)
// Session start times are UTC-based (converted to broker server time internally).
input int InpDailyOpenHour = 22; // Daily Open Start Hour (UTC)
input int InpDailyOpenMin = 0; // Daily Open Start Minute
input int InpTokyoHour = 0; // Tokyo Start Hour (UTC)
input int InpTokyoMin = 0; // Tokyo Start Minute
input int InpLondonHour = 8; // London Start Hour (UTC, 8=8AM GMT)
input int InpLondonMin = 0; // London Start Minute
input int InpNYHour = 12; // NY Start Hour (UTC)
input int InpNYMin = 0; // NY Start Minute
input int InpNYOrbHour = 13; // NY ORB Start Hour (UTC)
input int InpNYOrbMin = 30; // NY ORB Start Minute
// --- Order Block Settings ---
input group "═══ Order Block Settings ═══"
@@ -97,6 +110,9 @@ input int InpBreakoutExpireBars = 20; // Bars Before Breakout
input bool InpUseOBRetestEntry = true; // OB Retest Entry (enter on OB boundary retest)
input int InpMaxRetestsPerSession = 3; // Max Retest Entries Per Session (per breakout)
input bool InpUseSessionBiasFilter = true; // Session Bias Filter: block signals against day bias
input bool InpUseBreakoutVolFilter = false; // Require Volume Spike on Breakout Bar (fakeout filter)
input double InpBreakoutVolMult = 1.5; // Breakout Volume >= X * Average Volume
input int InpBreakoutVolAvgBars = 20; // Bars to Average Volume Over
// --- Session Level Ladder ---
input group "═══ Session Level Ladder ═══"
@@ -389,13 +405,15 @@ double CalcLotSize(double slPips)
//+------------------------------------------------------------------+
//| Calculate SL price |
//+------------------------------------------------------------------+
double CalcSL(int dir, double entry, double obBottom, double obTop)
double CalcSL(int dir, double entry, double obBottom, double obTop, double orbHigh, double orbLow)
{
double sl = 0;
double fixedFallback = (dir > 0) ? entry - PipsToPrice(InpSLPips)
: entry + PipsToPrice(InpSLPips);
if(InpSLMode == SL_FIXED_PIPS)
{
sl = (dir > 0) ? entry - PipsToPrice(InpSLPips)
: entry + PipsToPrice(InpSLPips);
sl = fixedFallback;
}
else if(InpSLMode == SL_OB_BOUNDARY)
{
@@ -404,18 +422,31 @@ double CalcSL(int dir, double entry, double obBottom, double obTop)
else if(dir < 0 && obTop > 0)
sl = obTop + PipsToPrice(2.0); // 2 pip buffer above OB top
else
sl = (dir > 0) ? entry - PipsToPrice(InpSLPips)
: entry + PipsToPrice(InpSLPips);
sl = fixedFallback;
}
else // ATR
else if(InpSLMode == SL_ORB_OPPOSITE)
{
// Buy → SL at ORB Low; Sell → SL at ORB High (2 pip buffer beyond).
double raw = (dir > 0) ? orbLow - PipsToPrice(2.0)
: orbHigh + PipsToPrice(2.0);
bool valid = (dir > 0) ? (raw < entry) : (raw > entry);
sl = valid ? raw : fixedFallback;
}
else if(InpSLMode == SL_ORB_MID)
{
// SL at the midpoint of the opening range.
double mid = (orbHigh + orbLow) / 2.0;
bool valid = (dir > 0) ? (mid < entry) : (mid > entry);
sl = valid ? mid : fixedFallback;
}
else // SL_ATR_BASED
{
double atr[1];
if(CopyBuffer(g_ATRHandle, 0, 0, 1, atr) > 0)
sl = (dir > 0) ? entry - atr[0] * InpATRMultiplier
: entry + atr[0] * InpATRMultiplier;
else
sl = (dir > 0) ? entry - PipsToPrice(InpSLPips)
: entry + PipsToPrice(InpSLPips);
sl = fixedFallback;
}
return NormalizeDouble(sl, (int)SymbolInfoInteger(g_Symbol, SYMBOL_DIGITS));
}
@@ -451,8 +482,8 @@ void InitSessions()
if(InpUseDailyOpen)
{
g_Sessions[g_SessionCount].name = "DailyOpen";
g_Sessions[g_SessionCount].startHour = 22;
g_Sessions[g_SessionCount].startMin = 0;
g_Sessions[g_SessionCount].startHour = InpDailyOpenHour;
g_Sessions[g_SessionCount].startMin = InpDailyOpenMin;
g_Sessions[g_SessionCount].enabled = true;
g_Sessions[g_SessionCount].Reset();
g_SessionCount++;
@@ -461,8 +492,8 @@ void InitSessions()
if(InpUseTokyoSession)
{
g_Sessions[g_SessionCount].name = "Tokyo";
g_Sessions[g_SessionCount].startHour = 0;
g_Sessions[g_SessionCount].startMin = 0;
g_Sessions[g_SessionCount].startHour = InpTokyoHour;
g_Sessions[g_SessionCount].startMin = InpTokyoMin;
g_Sessions[g_SessionCount].enabled = true;
g_Sessions[g_SessionCount].Reset();
g_SessionCount++;
@@ -471,8 +502,8 @@ void InitSessions()
if(InpUseLondonSession)
{
g_Sessions[g_SessionCount].name = "London";
g_Sessions[g_SessionCount].startHour = 7;
g_Sessions[g_SessionCount].startMin = 0;
g_Sessions[g_SessionCount].startHour = InpLondonHour;
g_Sessions[g_SessionCount].startMin = InpLondonMin;
g_Sessions[g_SessionCount].enabled = true;
g_Sessions[g_SessionCount].Reset();
g_SessionCount++;
@@ -481,8 +512,8 @@ void InitSessions()
if(InpUseNYSession)
{
g_Sessions[g_SessionCount].name = "NY";
g_Sessions[g_SessionCount].startHour = 12;
g_Sessions[g_SessionCount].startMin = 0;
g_Sessions[g_SessionCount].startHour = InpNYHour;
g_Sessions[g_SessionCount].startMin = InpNYMin;
g_Sessions[g_SessionCount].enabled = true;
g_Sessions[g_SessionCount].Reset();
g_SessionCount++;
@@ -491,8 +522,8 @@ void InitSessions()
if(InpUseNYOrbSession)
{
g_Sessions[g_SessionCount].name = "NYORB";
g_Sessions[g_SessionCount].startHour = 13;
g_Sessions[g_SessionCount].startMin = 30;
g_Sessions[g_SessionCount].startHour = InpNYOrbHour;
g_Sessions[g_SessionCount].startMin = InpNYOrbMin;
g_Sessions[g_SessionCount].enabled = true;
g_Sessions[g_SessionCount].Reset();
g_SessionCount++;
@@ -640,6 +671,33 @@ bool CheckSimpleBreakout(int dir, double orbLevel)
else return (c1 >= orbLevel && c0 < orbLevel);
}
//+------------------------------------------------------------------+
//| Volume-spike confirmation for the breakout bar (bar[1]) |
//| Optional fakeout filter: requires the just-closed breakout bar's |
//| tick volume to exceed a multiple of the recent average volume. |
//| Returns true (pass) when the filter is disabled or data is thin. |
//+------------------------------------------------------------------+
bool IsBreakoutVolumeConfirmed()
{
if(!InpUseBreakoutVolFilter) return true;
int n = InpBreakoutVolAvgBars;
if(n < 1) n = 1;
int bars = Bars(g_Symbol, PERIOD_CURRENT);
if(bars < n + 2) return true; // not enough history — don't block
double sum = 0;
for(int i = 2; i <= n + 1; i++)
sum += (double)iVolume(g_Symbol, PERIOD_CURRENT, i);
double avg = sum / n;
if(avg <= 0) return true;
double breakoutVol = (double)iVolume(g_Symbol, PERIOD_CURRENT, 1);
return (breakoutVol >= avg * InpBreakoutVolMult);
}
//+------------------------------------------------------------------+
//| Detect breakout for all sessions |
//+------------------------------------------------------------------+
@@ -680,6 +738,16 @@ void DetectBreakouts()
}
}
// Optional volume-spike confirmation (fakeout filter). Applies to whichever
// direction is breaking out; a weak-volume breakout bar is rejected.
if((bullBO || bearBO) && !IsBreakoutVolumeConfirmed())
{
PrintFormat("NANDR EA: [%s] Breakout rejected — volume on breakout bar below %.2fx average.",
g_Sessions[s].name, InpBreakoutVolMult);
bullBO = false;
bearBO = false;
}
if(bullBO)
{
// Bias filter note: do not suppress ORB breakout detection.
@@ -1328,7 +1396,8 @@ void OpenTrade(int dir, int sessIdx, double orbLevel, double obTop, double obBot
if(InpEntryMode == ENTRY_LIMIT)
entry = orbLevel;
double sl = CalcSL(dir, entry, obBottom, obTop);
double sl = CalcSL(dir, entry, obBottom, obTop,
g_Sessions[sessIdx].orbHigh, g_Sessions[sessIdx].orbLow);
double tp = CalcTP(dir, entry, sl);
double slPips = PriceToPips(MathAbs(entry - sl));
double lots = CalcLotSize(slPips);
+15 -3
View File
@@ -58,9 +58,13 @@ All sessions are **disabled by default** — enable the ones you want to trade:
|---------|-----------|-----------------|
| Daily Open | 22:00 | Asian range setup |
| Tokyo | 00:00 | Low volume, range-bound |
| London | 07:00 | High volatility ✓ |
| London | 08:00 | High volatility ✓ (8AM GMT) |
| NY | 12:00 | High volatility ✓ |
| NY ORB (Stock open) | 13:30 | Strong momentum ✓ |
| NY ORB (Stock open) | 13:30 | Strong momentum ✓ (9:30 EDT) |
All session start times are **UTC-based** and individually configurable via the
`Inp<Session>Hour` / `Inp<Session>Min` inputs. The EA converts them to broker
server time internally.
---
@@ -85,6 +89,11 @@ All sessions are **disabled by default** — enable the ones you want to trade:
| `InpUseLondonSession` | `false` | Enable London session |
| `InpUseNYSession` | `false` | Enable NY session |
| `InpUseNYOrbSession` | `false` | Enable NY ORB session (13:30 UTC) |
| `InpDailyOpenHour` / `InpDailyOpenMin` | `22` / `0` | Daily Open start (UTC) |
| `InpTokyoHour` / `InpTokyoMin` | `0` / `0` | Tokyo start (UTC) |
| `InpLondonHour` / `InpLondonMin` | `8` / `0` | London start (UTC = 8AM GMT) |
| `InpNYHour` / `InpNYMin` | `12` / `0` | NY start (UTC) |
| `InpNYOrbHour` / `InpNYOrbMin` | `13` / `30` | NY ORB start (UTC = 9:30 EDT) |
### Order Block Settings
| Parameter | Default | Description |
@@ -100,6 +109,9 @@ All sessions are **disabled by default** — enable the ones you want to trade:
|-----------|---------|-------------|
| `InpEntryMode` | `Market` | Market order on retest close, or Limit at ORB level |
| `InpUseStrictFilter` | `true` | Strict N-bar breakout filter (recommended) |
| `InpUseBreakoutVolFilter` | `false` | Require a volume spike on the breakout bar (fakeout filter) |
| `InpBreakoutVolMult` | `1.5` | Breakout bar volume must be ≥ this × average volume |
| `InpBreakoutVolAvgBars` | `20` | Number of bars used for the volume average |
### Session Level Ladder Settings
| Parameter | Default | Description |
@@ -132,7 +144,7 @@ All sessions are **disabled by default** — enable the ones you want to trade:
### Stop Loss
| Parameter | Default | Description |
|-----------|---------|-------------|
| `InpSLMode` | `OB Boundary` | SL placement: OB Boundary / Fixed Pips / ATR-based |
| `InpSLMode` | `OB Boundary` | SL placement: OB Boundary / Fixed Pips / ATR-based / ORB Opposite Boundary (buy=ORL, sell=ORH) / ORB Range Midpoint |
| `InpSLPips` | `20.0` | SL in pips (Fixed mode) |
| `InpATRMultiplier` | `1.5` | ATR multiplier for SL (ATR mode) |
| `InpATRPeriod` | `14` | ATR period |