//+------------------------------------------------------------------+ //| 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.10" #property strict #include 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 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 = ""; 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) == trade.ExpertMagicNumber()) 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) == trade.ExpertMagicNumber()) { if(trade.OrderDelete(ticket)) Print("🧹 Pending order verwijderd: #", ticket); } } } //+------------------------------------------------------------------+ //| Normaliseer volume binnen broker-limieten | //+------------------------------------------------------------------+ double NormalizeVol(double vol) { double minv = SYMBOL_VOLUME_MIN, maxv = SYMBOL_VOLUME_MAX, step = 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)); return NormalizeVol(lots * VOLUME_SCALE); } //+------------------------------------------------------------------+ //| 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"; char post[]; char result[]; string headers; StringToCharArray(body, post, 0, StringLen(body)); ResetLastError(); int code = WebRequest("POST", url, "", NULL, 5000, 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(20260828); // 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.10 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 = "📊 ORB V6 — " + g_symbol + "\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 double vol = (SIZING_MODE == "ares") ? AresVolume() : NormalizeVol(FIXED_VOLUME); // Pending STOP-order op het level (zelfde trigger als live bot, 25 aug) double sl = (direction == 1) ? (long_level - STOP_PTS) : (short_level + STOP_PTS); double tp = 0; // geen TP — EOD/stop bool ok = false; if(direction == 1) ok = trade.BuyStop(vol, long_level, g_symbol, sl, tp, ORDER_TIME_GTC, 0, "ORB-V6 LONG STP"); else ok = trade.SellStop(vol, short_level, g_symbol, sl, tp, ORDER_TIME_GTC, 0, "ORB-V6 SHORT STP"); if(ok) { g_trade_done = true; g_order_placed = true; Notify("📤 ORB V6 " + (direction == 1 ? "LONG" : "SHORT") + " entry geplaatst (STP) @ " + DoubleToString((direction == 1 ? long_level : short_level), _Digits) + " vol=" + DoubleToString(vol, 2) + " · SL=" + DoubleToString(sl, _Digits) + " — wacht op trigger" + (FILTER_B && opos != "" ? " (Filter B: " + opos + ")" : "")); } else { Print("❌ STOP-order mislukt: ", trade.ResultRetcode(), " ", trade.ResultRetcodeDescription()); } } //+------------------------------------------------------------------+