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:
+92
-23
@@ -5,7 +5,7 @@
|
|||||||
//| Based on: ORB-All-Sessions.pine + LuxAlgo Order Block Detector |
|
//| Based on: ORB-All-Sessions.pine + LuxAlgo Order Block Detector |
|
||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
#property copyright "NANDR"
|
#property copyright "NANDR"
|
||||||
#property version "1.395"
|
#property version "1.396"
|
||||||
#property strict
|
#property strict
|
||||||
|
|
||||||
#include <Trade\Trade.mqh>
|
#include <Trade\Trade.mqh>
|
||||||
@@ -26,7 +26,9 @@ enum ENUM_SL_MODE
|
|||||||
{
|
{
|
||||||
SL_OB_BOUNDARY, // OB Boundary
|
SL_OB_BOUNDARY, // OB Boundary
|
||||||
SL_FIXED_PIPS, // Fixed Pips
|
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
|
enum ENUM_TP_MODE
|
||||||
@@ -76,9 +78,20 @@ input int InpBreakoutConfBars = 0; // Breakout Confirm Bars
|
|||||||
input group "═══ Session Settings ═══"
|
input group "═══ Session Settings ═══"
|
||||||
input bool InpUseDailyOpen = true; // Daily Open Session (22:00 UTC, 01:00 Kuwait)
|
input bool InpUseDailyOpen = true; // Daily Open Session (22:00 UTC, 01:00 Kuwait)
|
||||||
input bool InpUseTokyoSession = true; // Tokyo Session (00:00 UTC)
|
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 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 ---
|
// --- Order Block Settings ---
|
||||||
input group "═══ 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 bool InpUseOBRetestEntry = true; // OB Retest Entry (enter on OB boundary retest)
|
||||||
input int InpMaxRetestsPerSession = 3; // Max Retest Entries Per Session (per breakout)
|
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 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 ---
|
// --- Session Level Ladder ---
|
||||||
input group "═══ Session Level Ladder ═══"
|
input group "═══ Session Level Ladder ═══"
|
||||||
@@ -389,13 +405,15 @@ double CalcLotSize(double slPips)
|
|||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
//| Calculate SL price |
|
//| 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 sl = 0;
|
||||||
|
double fixedFallback = (dir > 0) ? entry - PipsToPrice(InpSLPips)
|
||||||
|
: entry + PipsToPrice(InpSLPips);
|
||||||
|
|
||||||
if(InpSLMode == SL_FIXED_PIPS)
|
if(InpSLMode == SL_FIXED_PIPS)
|
||||||
{
|
{
|
||||||
sl = (dir > 0) ? entry - PipsToPrice(InpSLPips)
|
sl = fixedFallback;
|
||||||
: entry + PipsToPrice(InpSLPips);
|
|
||||||
}
|
}
|
||||||
else if(InpSLMode == SL_OB_BOUNDARY)
|
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)
|
else if(dir < 0 && obTop > 0)
|
||||||
sl = obTop + PipsToPrice(2.0); // 2 pip buffer above OB top
|
sl = obTop + PipsToPrice(2.0); // 2 pip buffer above OB top
|
||||||
else
|
else
|
||||||
sl = (dir > 0) ? entry - PipsToPrice(InpSLPips)
|
sl = fixedFallback;
|
||||||
: entry + PipsToPrice(InpSLPips);
|
|
||||||
}
|
}
|
||||||
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];
|
double atr[1];
|
||||||
if(CopyBuffer(g_ATRHandle, 0, 0, 1, atr) > 0)
|
if(CopyBuffer(g_ATRHandle, 0, 0, 1, atr) > 0)
|
||||||
sl = (dir > 0) ? entry - atr[0] * InpATRMultiplier
|
sl = (dir > 0) ? entry - atr[0] * InpATRMultiplier
|
||||||
: entry + atr[0] * InpATRMultiplier;
|
: entry + atr[0] * InpATRMultiplier;
|
||||||
else
|
else
|
||||||
sl = (dir > 0) ? entry - PipsToPrice(InpSLPips)
|
sl = fixedFallback;
|
||||||
: entry + PipsToPrice(InpSLPips);
|
|
||||||
}
|
}
|
||||||
return NormalizeDouble(sl, (int)SymbolInfoInteger(g_Symbol, SYMBOL_DIGITS));
|
return NormalizeDouble(sl, (int)SymbolInfoInteger(g_Symbol, SYMBOL_DIGITS));
|
||||||
}
|
}
|
||||||
@@ -451,8 +482,8 @@ void InitSessions()
|
|||||||
if(InpUseDailyOpen)
|
if(InpUseDailyOpen)
|
||||||
{
|
{
|
||||||
g_Sessions[g_SessionCount].name = "DailyOpen";
|
g_Sessions[g_SessionCount].name = "DailyOpen";
|
||||||
g_Sessions[g_SessionCount].startHour = 22;
|
g_Sessions[g_SessionCount].startHour = InpDailyOpenHour;
|
||||||
g_Sessions[g_SessionCount].startMin = 0;
|
g_Sessions[g_SessionCount].startMin = InpDailyOpenMin;
|
||||||
g_Sessions[g_SessionCount].enabled = true;
|
g_Sessions[g_SessionCount].enabled = true;
|
||||||
g_Sessions[g_SessionCount].Reset();
|
g_Sessions[g_SessionCount].Reset();
|
||||||
g_SessionCount++;
|
g_SessionCount++;
|
||||||
@@ -461,8 +492,8 @@ void InitSessions()
|
|||||||
if(InpUseTokyoSession)
|
if(InpUseTokyoSession)
|
||||||
{
|
{
|
||||||
g_Sessions[g_SessionCount].name = "Tokyo";
|
g_Sessions[g_SessionCount].name = "Tokyo";
|
||||||
g_Sessions[g_SessionCount].startHour = 0;
|
g_Sessions[g_SessionCount].startHour = InpTokyoHour;
|
||||||
g_Sessions[g_SessionCount].startMin = 0;
|
g_Sessions[g_SessionCount].startMin = InpTokyoMin;
|
||||||
g_Sessions[g_SessionCount].enabled = true;
|
g_Sessions[g_SessionCount].enabled = true;
|
||||||
g_Sessions[g_SessionCount].Reset();
|
g_Sessions[g_SessionCount].Reset();
|
||||||
g_SessionCount++;
|
g_SessionCount++;
|
||||||
@@ -471,8 +502,8 @@ void InitSessions()
|
|||||||
if(InpUseLondonSession)
|
if(InpUseLondonSession)
|
||||||
{
|
{
|
||||||
g_Sessions[g_SessionCount].name = "London";
|
g_Sessions[g_SessionCount].name = "London";
|
||||||
g_Sessions[g_SessionCount].startHour = 7;
|
g_Sessions[g_SessionCount].startHour = InpLondonHour;
|
||||||
g_Sessions[g_SessionCount].startMin = 0;
|
g_Sessions[g_SessionCount].startMin = InpLondonMin;
|
||||||
g_Sessions[g_SessionCount].enabled = true;
|
g_Sessions[g_SessionCount].enabled = true;
|
||||||
g_Sessions[g_SessionCount].Reset();
|
g_Sessions[g_SessionCount].Reset();
|
||||||
g_SessionCount++;
|
g_SessionCount++;
|
||||||
@@ -481,8 +512,8 @@ void InitSessions()
|
|||||||
if(InpUseNYSession)
|
if(InpUseNYSession)
|
||||||
{
|
{
|
||||||
g_Sessions[g_SessionCount].name = "NY";
|
g_Sessions[g_SessionCount].name = "NY";
|
||||||
g_Sessions[g_SessionCount].startHour = 12;
|
g_Sessions[g_SessionCount].startHour = InpNYHour;
|
||||||
g_Sessions[g_SessionCount].startMin = 0;
|
g_Sessions[g_SessionCount].startMin = InpNYMin;
|
||||||
g_Sessions[g_SessionCount].enabled = true;
|
g_Sessions[g_SessionCount].enabled = true;
|
||||||
g_Sessions[g_SessionCount].Reset();
|
g_Sessions[g_SessionCount].Reset();
|
||||||
g_SessionCount++;
|
g_SessionCount++;
|
||||||
@@ -491,8 +522,8 @@ void InitSessions()
|
|||||||
if(InpUseNYOrbSession)
|
if(InpUseNYOrbSession)
|
||||||
{
|
{
|
||||||
g_Sessions[g_SessionCount].name = "NYORB";
|
g_Sessions[g_SessionCount].name = "NYORB";
|
||||||
g_Sessions[g_SessionCount].startHour = 13;
|
g_Sessions[g_SessionCount].startHour = InpNYOrbHour;
|
||||||
g_Sessions[g_SessionCount].startMin = 30;
|
g_Sessions[g_SessionCount].startMin = InpNYOrbMin;
|
||||||
g_Sessions[g_SessionCount].enabled = true;
|
g_Sessions[g_SessionCount].enabled = true;
|
||||||
g_Sessions[g_SessionCount].Reset();
|
g_Sessions[g_SessionCount].Reset();
|
||||||
g_SessionCount++;
|
g_SessionCount++;
|
||||||
@@ -640,6 +671,33 @@ bool CheckSimpleBreakout(int dir, double orbLevel)
|
|||||||
else return (c1 >= orbLevel && c0 < 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 |
|
//| 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)
|
if(bullBO)
|
||||||
{
|
{
|
||||||
// Bias filter note: do not suppress ORB breakout detection.
|
// 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)
|
if(InpEntryMode == ENTRY_LIMIT)
|
||||||
entry = orbLevel;
|
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 tp = CalcTP(dir, entry, sl);
|
||||||
double slPips = PriceToPips(MathAbs(entry - sl));
|
double slPips = PriceToPips(MathAbs(entry - sl));
|
||||||
double lots = CalcLotSize(slPips);
|
double lots = CalcLotSize(slPips);
|
||||||
|
|||||||
@@ -58,9 +58,13 @@ All sessions are **disabled by default** — enable the ones you want to trade:
|
|||||||
|---------|-----------|-----------------|
|
|---------|-----------|-----------------|
|
||||||
| Daily Open | 22:00 | Asian range setup |
|
| Daily Open | 22:00 | Asian range setup |
|
||||||
| Tokyo | 00:00 | Low volume, range-bound |
|
| 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 | 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 |
|
| `InpUseLondonSession` | `false` | Enable London session |
|
||||||
| `InpUseNYSession` | `false` | Enable NY session |
|
| `InpUseNYSession` | `false` | Enable NY session |
|
||||||
| `InpUseNYOrbSession` | `false` | Enable NY ORB session (13:30 UTC) |
|
| `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
|
### Order Block Settings
|
||||||
| Parameter | Default | Description |
|
| 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 |
|
| `InpEntryMode` | `Market` | Market order on retest close, or Limit at ORB level |
|
||||||
| `InpUseStrictFilter` | `true` | Strict N-bar breakout filter (recommended) |
|
| `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
|
### Session Level Ladder Settings
|
||||||
| Parameter | Default | Description |
|
| Parameter | Default | Description |
|
||||||
@@ -132,7 +144,7 @@ All sessions are **disabled by default** — enable the ones you want to trade:
|
|||||||
### Stop Loss
|
### Stop Loss
|
||||||
| Parameter | Default | Description |
|
| 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) |
|
| `InpSLPips` | `20.0` | SL in pips (Fixed mode) |
|
||||||
| `InpATRMultiplier` | `1.5` | ATR multiplier for SL (ATR mode) |
|
| `InpATRMultiplier` | `1.5` | ATR multiplier for SL (ATR mode) |
|
||||||
| `InpATRPeriod` | `14` | ATR period |
|
| `InpATRPeriod` | `14` | ATR period |
|
||||||
|
|||||||
Reference in New Issue
Block a user