📥 ORB V6 Expert Advisor — MetaTrader 5

De complete V6-strategie (Opening Range Breakout + Filter B + ATR 1.5) als gratis MetaTrader 5 Expert Advisor. Backtest-gevalideerd op NQ 5-minuten data 2020–2026: PF 1.56 · WR 35% · MDD −5.8%.

📄 ORB_V6_EA.mq5

Het Expert Advisor bronbestand (MQL5). Kopieer naar MQL5/Experts/, compileer in MetaEditor (F7) en hang hem aan een US100/NAS100 M5-chart. v1.10: entries via pending BUY/SELL STOP-orders — dezelfde trigger als de live bot.

⬇ Download .mq5

📖 README

Volledige installatiegids: tijdzone-tabel (servertijd), VOLUME_SCALE voor CFD-puntwaarden, alle inputs, Telegram-setup en aanbevolen werkwijze.

⬇ Download README

📄 Volledige broncode (v1.14)

De complete Expert Advisor-broncode — kopieer hem direct, of download het .mq5-bestand hierboven.

//+------------------------------------------------------------------+
//|                                       ORB_V6_EA.mq5              |
//|  ORB Ares 2 V6 — Filter B (ORB-ligging) + ATR 1.5 momentum       |
//|                                                                  |
//|  Volledige V6-logica (gevalideerd op NQ 5m, 2020-2026):         |
//|    PF 1.56 · WR 35% · MDD -5.8% (futures, Ares-sizing)          |
//|                                                                  |
//|  v1.10 (28 aug 2026): entry via PENDING STOP-orders             |
//|    (BUY STP boven ORB-high / SELL STP onder ORB-low) — zelfde    |
//|    trigger als de live bot (25 aug). SL 45pt hangt aan de order. |
//|                                                                  |
//|  Tijden in SERVER-tijd. Default = EU-tijd (CET/CEST):            |
//|    ORB 09:00-09:30 ET = 15:00-15:30 EU                           |
//|    EOD 15:55 ET       = 21:55 EU                                 |
//|    (EU is altijd 6 uur vóór ET — zomer én winter)                |
//|                                                                  |
//|  Sizing: Ares = max(1, round(0.31 * equity / 10_000)) lots      |
//|  Alerts: Telegram (WebRequest) + MT5 Alert + Notifications       |
//+------------------------------------------------------------------+
#property copyright "Symtrade"
#property version   "1.14"
#property strict

#include <Trade/Trade.mqh>
CTrade trade;

//+------------------------------------------------------------------+
//| INPUTS                                                           |
//+------------------------------------------------------------------+
input group "=== Tijden (SERVER-tijd) ==="
input int    ORB_START_HOUR = 15;         // ORB start uur (default EU: 15 = 09:00 ET)
input int    ORB_START_MIN  = 0;          // ORB start minuut
input int    ORB_END_HOUR   = 15;         // ORB einde uur (default EU: 15 = 09:30 ET)
input int    ORB_END_MIN    = 30;         // ORB einde minuut
input int    EOD_HOUR       = 21;         // EOD exit uur (default EU: 21 = 15:55 ET)
input int    EOD_MIN        = 55;         // EOD exit minuut

input group "=== Strategie (V6) ==="
input double LONG_BUFFER    = 10.0;       // LONG buffer boven ORB-high (pt)
input double SHORT_BUFFER   = 75.0;       // SHORT buffer onder ORB-low (pt)
input double STOP_PTS       = 45.0;       // Stop loss (pt)
input double MIN_ORB_RANGE  = 20.0;       // Minimale ORB-range (pt)
input bool   FILTER_B       = true;       // Filter B: LONG alleen als ORB boven vorige close
input double ATR_MULT       = 1.5;        // ATR momentum multiplier
input int    ATR_LEN        = 14;         // ATR periode (bars)

input group "=== Sizing ==="
input string SIZING_MODE    = "ares";     // "ares" of "fixed"
input double FIXED_VOLUME   = 1.0;        // Volume bij SIZING_MODE=fixed
input double VOLUME_SCALE   = 1.0;        // Schaal op Ares-lots (CFD-puntwaarde-correctie)
input double MAX_VOLUME     = 5.0;        // Veiligheidslimiet: max lots per trade

input group "=== Alerts ==="
input bool   USE_TELEGRAM   = true;       // Telegram via WebRequest
input string TG_TOKEN       = "";         // Bot token
input string TG_CHAT_ID     = "";         // Chat ID
input bool   USE_ALERT      = true;       // MT5 Alert() popup
input bool   USE_NOTIFY     = false;      // MT5 push-notificatie

//+------------------------------------------------------------------+
//| GLOBALS                                                          |
//+------------------------------------------------------------------+
string  g_symbol;
double  g_orb_high = 0, g_orb_low = 0;
bool    g_orb_complete = false;
string  g_orb_date = "";
bool    g_trade_done = false;     // order geplaatst of dag geblokkeerd
double  g_prev_close = 0;
bool    g_prev_close_ok = false;
datetime g_last_bar_time = 0;
string  g_last_tg_day = "";
ulong   g_magic = 20260828;        // eigen magic (niet afhankelijk van CTrade-getter)
bool    g_order_placed = false;   // pending STOP-order staat open
bool    g_fill_notified = false;  // fill-melding verstuurd

//+------------------------------------------------------------------+
//| Helpers                                                          |
//+------------------------------------------------------------------+
bool IsNewBar()
{
   datetime t = iTime(g_symbol, PERIOD_M5, 0);
   if(t != g_last_bar_time)
   {
      g_last_bar_time = t;
      return true;
   }
   return false;
}

string TodayStr()
{
   MqlDateTime dt;
   TimeToStruct(TimeCurrent(), dt);
   return StringFormat("%04d-%02d-%02d", dt.year, dt.mon, dt.day);
}

double GetPrevDayClose()
{
   // Close van de vorige dag (D1 bar, shift 1) — voor Filter B
   return iClose(g_symbol, PERIOD_D1, 1);
}

bool HasPendingOrder()
{
   for(int i = 0; i < OrdersTotal(); i++)
   {
      ulong ticket = OrderGetTicket(i);
      if(ticket == 0) continue;
      if(OrderGetString(ORDER_SYMBOL) == g_symbol &&
         OrderGetInteger(ORDER_MAGIC) == g_magic)
         return true;
   }
   return false;
}

void DeletePendingOrders()
{
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      ulong ticket = OrderGetTicket(i);
      if(ticket == 0) continue;
      if(OrderGetString(ORDER_SYMBOL) == g_symbol &&
         OrderGetInteger(ORDER_MAGIC) == g_magic)
      {
         if(trade.OrderDelete(ticket))
            Print("🧹 Pending order verwijderd: #", ticket);
      }
   }
}

//+------------------------------------------------------------------+
//| Normaliseer volume binnen broker-limieten                        |
//+------------------------------------------------------------------+
double NormalizeVol(double vol)
{
   // Expliciete SymbolInfoDouble (waterdicht; geen afhankelijkheid van macro's)
   double minv = SymbolInfoDouble(g_symbol, SYMBOL_VOLUME_MIN);
   double maxv = SymbolInfoDouble(g_symbol, SYMBOL_VOLUME_MAX);
   double step = SymbolInfoDouble(g_symbol, SYMBOL_VOLUME_STEP);
   vol = MathMax(minv, MathMin(maxv, vol));
   if(step > 0)
      vol = MathRound(vol / step) * step;
   return vol;
}

//+------------------------------------------------------------------+
//| Ares-sizing: qty = max(1, round(0.31 * equity / 10_000))        |
//+------------------------------------------------------------------+
double AresVolume()
{
   double eq = AccountInfoDouble(ACCOUNT_EQUITY);
   int lots = (int)MathMax(1, MathRound(0.31 * eq / 10000.0));
   double v = NormalizeVol(lots * VOLUME_SCALE);
   Print("🔍 AresVolume: eq=", DoubleToString(eq, 2),
         " → lots=", lots, " scale=", DoubleToString(VOLUME_SCALE, 2),
         " → vol=", DoubleToString(v, 2));
   return v;
}

//+------------------------------------------------------------------+
//| ATR14: gemiddelde range van de ATR_LEN bars vóór shift           |
//+------------------------------------------------------------------+
double CalcATR(int shift)
{
   double sum = 0;
   for(int k = 1; k <= ATR_LEN; k++)
   {
      double h = iHigh(g_symbol, PERIOD_M5, shift + k);
      double l = iLow(g_symbol, PERIOD_M5, shift + k);
      if(h <= 0 || l <= 0) return -1;
      sum += (h - l);
   }
   return sum / ATR_LEN;
}

//+------------------------------------------------------------------+
//| URL-encode voor Telegram                                         |
//+------------------------------------------------------------------+
string UrlEncode(string s)
{
   string out = "";
   for(int i = 0; i < StringLen(s); i++)
   {
      ushort c = StringGetCharacter(s, i);
      if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')
         || c == '-' || c == '_' || c == '.' || c == '~')
         out += ShortToString(c);
      else
         out += StringFormat("%%%02X", c);
   }
   return out;
}

//+------------------------------------------------------------------+
//| Telegram versturen via WebRequest                                |
//+------------------------------------------------------------------+
bool SendTelegram(string msg)
{
   if(!USE_TELEGRAM || StringLen(TG_TOKEN) == 0 || StringLen(TG_CHAT_ID) == 0)
      return false;
   string url = "https://api.telegram.org/bot" + TG_TOKEN + "/sendMessage";
   string body = "chat_id=" + TG_CHAT_ID + "&text=" + UrlEncode(msg) + "&parse_mode=HTML";
   uchar post[];
   uchar result[];
   string headers;
   StringToCharArray(body, post, 0, StringLen(body));
   ResetLastError();
   int code = WebRequest("POST", url, "", NULL, 5000, post, ArraySize(post), result, headers);
   if(code == -1)
   {
      Print("⚠️ Telegram WebRequest fout (", GetLastError(),
            ") — zet api.telegram.org in Tools > Options > Expert Advisors > Allow WebRequest");
      return false;
   }
   return true;
}

//+------------------------------------------------------------------+
//| Alert versturen (Telegram + MT5 popup + push)                    |
//+------------------------------------------------------------------+
void Notify(string msg)
{
   Print(msg);
   if(USE_ALERT) Alert(msg);
   if(USE_NOTIFY) SendNotification(msg);
   SendTelegram(msg);
}

//+------------------------------------------------------------------+
//| ORB-ligging t.o.v. vorige close ("boven"/"rond"/"onder")         |
//+------------------------------------------------------------------+
string OrbPos()
{
   if(!g_prev_close_ok || g_orb_high <= 0 || g_orb_low <= 0) return "";
   if(g_orb_low > g_prev_close) return "boven";
   if(g_orb_high < g_prev_close) return "onder";
   return "rond";
}

//+------------------------------------------------------------------+
//| OnInit                                                           |
//+------------------------------------------------------------------+
int OnInit()
{
   g_symbol = _Symbol;
   trade.SetExpertMagicNumber(g_magic);

   // Controleer minimum timeframe-data
   if(iTime(g_symbol, PERIOD_M5, 0) == 0)
   {
      Print("❌ Geen M5-data voor ", g_symbol);
      return INIT_FAILED;
   }

   Print("🚀 ORB V6 EA v1.13 gestart op ", g_symbol);
   Print("   ORB venster: ", ORB_START_HOUR, ":", StringFormat("%02d", ORB_START_MIN),
         " - ", ORB_END_HOUR, ":", StringFormat("%02d", ORB_END_MIN),
         " server-tijd (default EU = 09:00-09:30 ET)");
   Print("   EOD exit: ", EOD_HOUR, ":", StringFormat("%02d", EOD_MIN), " server-tijd");
   Print("   Filter B: ", (FILTER_B ? "AAN" : "UIT"),
         " | ATR ", ATR_MULT, "x", ATR_LEN,
         " | Stop ", STOP_PTS, "pt | Min range ", MIN_ORB_RANGE, "pt");
   Print("   Entry: pending STOP-orders (BUY STP / SELL STP)");

   return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| OnDeinit                                                         |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   Print("ORB V6 EA gestopt (reason=", reason, ")");
}

//+------------------------------------------------------------------+
//| OnTick                                                           |
//+------------------------------------------------------------------+
void OnTick()
{
   // Alleen handelen op nieuwe M5-bar (lookahead-vrij)
   if(!IsNewBar()) return;

   datetime now = TimeCurrent();
   MqlDateTime dt;
   TimeToStruct(now, dt);
   int cur_min = dt.hour * 60 + dt.min;
   int orb_start = ORB_START_HOUR * 60 + ORB_START_MIN;
   int orb_end   = ORB_END_HOUR * 60 + ORB_END_MIN;
   int eod       = EOD_HOUR * 60 + EOD_MIN;
   string today = TodayStr();

   // ─── Dagelijkse reset ──────────────────────────────────────────
   if(g_orb_date != today)
   {
      g_orb_date = today;
      g_orb_high = 0;
      g_orb_low = 0;
      g_orb_complete = false;
      g_trade_done = false;
      g_order_placed = false;
      g_fill_notified = false;
      g_prev_close = GetPrevDayClose();
      g_prev_close_ok = (g_prev_close > 0);
      DeletePendingOrders();   // veiligheid: restanten van gisteren opruimen
      Print("📅 ", today, " | prev close = ", (g_prev_close_ok ? DoubleToString(g_prev_close, _Digits) : "n/a"));
   }

   // ─── ORB accumuleren binnen venster ────────────────────────────
   if(!g_orb_complete && cur_min >= orb_start && cur_min < orb_end)
   {
      double h = iHigh(g_symbol, PERIOD_M5, 1);  // vorige gesloten bar
      double l = iLow(g_symbol, PERIOD_M5, 1);
      if(h > 0 && l > 0)
      {
         if(g_orb_high == 0 || h > g_orb_high) g_orb_high = h;
         if(g_orb_low == 0 || l < g_orb_low) g_orb_low = l;
      }
   }

   // ─── ORB compleet melden ───────────────────────────────────────
   if(!g_orb_complete && cur_min >= orb_end && g_orb_high > 0 && g_orb_low > 0)
   {
      g_orb_complete = true;
      double range = g_orb_high - g_orb_low;
      string opos = OrbPos();
      string fb = (FILTER_B && opos != "") ? (" · Filter B: " + opos) : "";
      Print("📊 ORB compleet: H=", g_orb_high, " L=", g_orb_low,
            " range=", range, "pt", fb);
      if(g_last_tg_day != today)
      {
         g_last_tg_day = today;
         string msg = "📊 <b>ORB V6 — " + g_symbol + "</b>\n"
                    + "ORB: H=" + DoubleToString(g_orb_high, _Digits)
                    + " · L=" + DoubleToString(g_orb_low, _Digits)
                    + " · range=" + DoubleToString(range, 1) + "pt"
                    + (fb != "" ? "\n" + fb : "")
                    + "\nLONG ≥ " + DoubleToString(g_orb_high + LONG_BUFFER, _Digits)
                    + " · SHORT ≤ " + DoubleToString(g_orb_low - SHORT_BUFFER, _Digits);
         SendTelegram(msg);
      }
      if(range < MIN_ORB_RANGE)
      {
         Notify("⚪ ORB V6: range " + DoubleToString(range, 1) + "pt < " +
                DoubleToString(MIN_ORB_RANGE, 1) + "pt → geen trade vandaag");
         g_trade_done = true;  // blokkeer entries
      }
   }

   // ─── EOD: positie sluiten + pending orders opruimen ────────────
   if(cur_min >= eod)
   {
      if(PositionSelect(g_symbol))
      {
         double close = iClose(g_symbol, PERIOD_M5, 1);
         if(close > 0)
         {
            trade.PositionClose(g_symbol);
            Notify("🕓 ORB V6 EOD: positie gesloten @ " + DoubleToString(close, _Digits));
         }
         g_trade_done = true;
      }
      DeletePendingOrders();
      g_order_placed = false;
      return;
   }

   // ─── Fill-detectie: pending STOP werd gevuld ───────────────────
   if(g_order_placed && !g_fill_notified && PositionSelect(g_symbol))
   {
      g_fill_notified = true;
      Notify("✅ ORB V6: STOP-entry GEVULD — positie open (" + g_symbol + ")");
   }

   // ─── Entry-check ───────────────────────────────────────────────
   if(g_trade_done || !g_orb_complete || g_orb_high <= 0)
      return;
   if(PositionSelect(g_symbol) || HasPendingOrder())
      return;

   // ATR-momentum check op de vorige gesloten bar (shift 1)
   double atr14 = CalcATR(1);
   if(atr14 < 0)
      return;
   double bar_range = iHigh(g_symbol, PERIOD_M5, 1) - iLow(g_symbol, PERIOD_M5, 1);
   if(bar_range < ATR_MULT * atr14)
      return;  // zwakke bar: geen momentum

   double close1 = iClose(g_symbol, PERIOD_M5, 1);
   double long_level  = g_orb_high + LONG_BUFFER;
   double short_level = g_orb_low  - SHORT_BUFFER;
   string opos = OrbPos();

   int direction = 0;
   if(close1 >= long_level)
   {
      if(FILTER_B && opos != "" && opos != "boven")
         return;  // Filter B: LONG alleen als ORB boven
      direction = 1;   // LONG
   }
   else if(close1 <= short_level)
   {
      if(FILTER_B && opos == "rond")
         return;  // Filter B: SHORT niet als ORB rond
      direction = -1;  // SHORT
   }
   else
      return;

   // Sizing (met MAX_VOLUME veiligheidslimiet)
   double vol = (SIZING_MODE == "ares") ? AresVolume() : NormalizeVol(FIXED_VOLUME);
   vol = NormalizeVol(MathMin(vol, MAX_VOLUME));
   Print("🔍 Entry vol=", DoubleToString(vol, 2), " (MAX_VOLUME=", DoubleToString(MAX_VOLUME, 2), ")");

   // Pending STOP-order op het level (zelfde trigger als live bot, 25 aug).
   // MT5-eis: BUY STOP > Ask, SELL STOP < Bid. Als de close het level al
   // gepasseerd heeft, is de pending order ongeldig ([Invalid price]) →
   // direct markt-order (zelfde effect als een STP die direct triggert).
   double tp = 0;  // geen TP — EOD/stop
   bool ok = false;
   bool as_pending = false;
   double entry_ref = 0;
   double sl_used = 0;
   if(direction == 1)
   {
      entry_ref = long_level;
      sl_used = long_level - STOP_PTS;
      ok = trade.BuyStop(vol, long_level, g_symbol, sl_used, tp, ORDER_TIME_GTC, 0, "ORB-V6 LONG STP");
      if(ok)
         as_pending = true;
      else
      {
         double ask = SymbolInfoDouble(g_symbol, SYMBOL_ASK);
         entry_ref = ask;
         sl_used = ask - STOP_PTS;
         ok = trade.Buy(vol, g_symbol, 0.0, sl_used, tp, "ORB-V6 LONG");
      }
   }
   else
   {
      entry_ref = short_level;
      sl_used = short_level + STOP_PTS;
      ok = trade.SellStop(vol, short_level, g_symbol, sl_used, tp, ORDER_TIME_GTC, 0, "ORB-V6 SHORT STP");
      if(ok)
         as_pending = true;
      else
      {
         double bid = SymbolInfoDouble(g_symbol, SYMBOL_BID);
         entry_ref = bid;
         sl_used = bid + STOP_PTS;
         ok = trade.Sell(vol, g_symbol, 0.0, sl_used, tp, "ORB-V6 SHORT");
      }
   }

   if(ok)
   {
      g_trade_done = true;
      g_order_placed = as_pending;
      if(as_pending)
         Notify("📤 ORB V6 " + (direction == 1 ? "LONG" : "SHORT") +
                " entry geplaatst (STP) @ " + DoubleToString(entry_ref, _Digits) +
                " vol=" + DoubleToString(vol, 2) + " · SL=" + DoubleToString(sl_used, _Digits) +
                " — wacht op trigger" +
                (FILTER_B && opos != "" ? " (Filter B: " + opos + ")" : ""));
      else
         Notify("⚡ ORB V6 " + (direction == 1 ? "LONG" : "SHORT") +
                " entry DIRECT (MKT — level al gepasseerd) @ " + DoubleToString(entry_ref, _Digits) +
                " · SL=" + DoubleToString(sl_used, _Digits) +
                (FILTER_B && opos != "" ? " (Filter B: " + opos + ")" : ""));
   }
   else
   {
      Print("❌ Order mislukt: ", trade.ResultRetcode(), " ", trade.ResultRetcodeDescription());
   }
}
//+------------------------------------------------------------------+

🎯 Wat is V6?

⚙️ Snelle installatie

  1. Download het .mq5-bestand en zet het in je MT5-datamap → MQL5/Experts/
  2. Open MetaEditor (F4), open het bestand, druk op Compile (F7)
  3. Sta WebRequest toe naar https://api.telegram.org (Extra → Opties → Expert Advisors)
  4. Hang de EA aan een US100 / NAS100 M5-chart en controleer de servertijd-inputs

⏰ Tijdzone — cruciaal

De EA werkt in de servertijd van je broker. Defaults zijn EU-tijd (CET/CEST). EU is altijd 6 uur vóór ET:

Broker-servertijdORB (09:00–09:30 ET)EOD (15:55 ET)
EU (CET/CEST)15:00–15:3021:55
GMT+3 (veel CFD-brokers)16:00–16:3022:55
GMT+215:00–15:3021:55

Controleer je servertijd in MT5 (Marktkoersen → server-kolom). Verkeerde tijden = verkeerde ORB = verkeerde trades. Zie de README voor VOLUME_SCALE (CFD-puntwaarde).

⚠️ Disclaimer

Deze Expert Advisor wordt uitsluitend voor educatieve doeleinden aangeboden. Handelen brengt aanzienlijk risico op verlies met zich mee; historische resultaten garanderen geen toekomstige prestaties. Test altijd eerst op een demo-account. Zie onze volledige disclaimer.