//+------------------------------------------------------------------+ //| Urban Flux Research — 研究アーカイブ公開版(無償) | //| https://apex-predator-x.com/ | //| | //| 公開日 : 2026-08-25 | //| 配布区分 : 研究アーカイブ / 検証用ソースコード | //| 対応 : MetaTrader 5 (MQL5) | //| ライセンス : 検証・学習目的での閲覧・改変・実行を許諾します。 | //| 再配布する場合は本ヘッダを削除しないでください。 | //| | //| ── 免責 ────────────────────────────────────────────────────── | //| 本コードは研究記録の公開であり、投資助言ではありません。 | //| 過去の検証結果は将来の利益を一切保証しません。 | //| 実口座での使用により生じたいかなる損失についても、 | //| 当方は責任を負いません。必ずデモ口座で検証してください。 | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| ✓ 本EAは絶対時刻(TimeGMT)基準ではありません。 | //| 判定に使うのはブローカーのサーバー時刻(TimeCurrent)であり、 | //| 「サーバー0時=ロールオーバー」というアンカーに紐づきます。 | //| ロールオーバー自体がサーバー時刻で定義されるため、この設計は | //| 機序と時刻基準が一致しています。 | //| | //| ⚠ ただしサーバー時刻の異なるブローカーでは基準がずれます。 | //| 検証は IC Markets 系(GMT+2/+3・ロールオーバー=サーバー0時) | //| で実施しました。他社では必ず再検証してください。 | //| ⚠ バックテストは「Every tick based on real ticks」必須です。 | //| OHLC on M1 等では指値の約定判定が再現できません。 | //| ⚠ 2025年単年は PF 1.03(ほぼ損益分岐)でした。好調年と軟調年の | //| 差が大きい戦略です。検証記事の「弱点」節を必ずお読みください。| //| ⚠ 本EAは研究・検証用プロトタイプです。 | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| R107_TokyoLimit_GateB.mq5 v1.01 | //| RESEARCH-107: Tokyo Open Carry 指値版 (R082 Revival) GATE B EA | //| Recommended: GBPJPY | //| | //| v1.01 修正 (2026-06-10): | //| - v1.00はOnTimerで「23:59」を待ったが、テスターの時刻はティック | //| 駆動であり、ICMarketsは23:58-00:02にティックが無いため | //| 23:59が一度も観測されず取引ゼロだった | //| - OnTick駆動に変更。23:50-23:58で発注試行し、未発注なら | //| 日付変更後の最初のティック(00:00-00:10)で前夜ref価格を使い発注 | //| | //| ロジック: | //| シグナル: 前日D1 close_pos < 0.60 かつ エントリー日が木曜以外 | //| BUY LIMIT = ref(前夜最終Bid) - InpDeltaPips、01:00失効 | //| 約定したら 02:00 クローズ | //| | //| GATE B 合格基準: MT5 PF >= 1.51 (Python現実約定PF 2.52 x 0.6) | //| テスター: GBPJPY / M1 / Every tick (based on real ticks 推奨) | //+------------------------------------------------------------------+ #property copyright "Urban Flux Research — RESEARCH-107" #property link "https://apex-predator-x.com/" #property description "Rollover-anchored BUY LIMIT dip entry (server-time based)" #property version "1.01" #include input double InpDeltaPips = 10.0; // BUY LIMIT depth (pips below ref) input double InpLots = 0.10; // lots input double InpMaxClosePos = 0.60; // prev D1 close_pos threshold input bool InpSkipThursday = true; // skip Thursday entries input bool InpPrintLog = true; // verbose log CTrade g_trade; long g_magic = 107001; double g_ref_bid = 0.0; // last bid seen in hour 23 (pre-rollover ref) datetime g_ref_day = 0; // date(00:00) of the day whose 23h produced ref datetime g_placed_entry = 0; // entry-day date an order was placed for datetime g_done_entry = 0; // entry-day date already handled (skip re-try) double PipSize() { int digits = (int)SymbolInfoInteger(Symbol(), SYMBOL_DIGITS); double point = SymbolInfoDouble(Symbol(), SYMBOL_POINT); return (digits == 3 || digits == 5) ? point * 10.0 : point; } datetime DateOf(datetime t) { return t - (t % 86400); } int OnInit() { g_trade.SetExpertMagicNumber(g_magic); PrintFormat("R107 GateB v1.01 init | %s delta=%.1f pos<%.2f noThu=%d", Symbol(), InpDeltaPips, InpMaxClosePos, InpSkipThursday); return INIT_SUCCEEDED; } void OnDeinit(const int reason) {} bool HasPending() { for(int i = OrdersTotal() - 1; i >= 0; i--) { ulong tk = OrderGetTicket(i); if(tk > 0 && OrderGetInteger(ORDER_MAGIC) == g_magic && OrderGetString(ORDER_SYMBOL) == Symbol()) return true; } return false; } void DeletePendings() { for(int i = OrdersTotal() - 1; i >= 0; i--) { ulong tk = OrderGetTicket(i); if(tk > 0 && OrderGetInteger(ORDER_MAGIC) == g_magic && OrderGetString(ORDER_SYMBOL) == Symbol()) g_trade.OrderDelete(tk); } } bool HasMyPosition() { return PositionSelect(Symbol()) && PositionGetInteger(POSITION_MAGIC) == g_magic; } // signal check; shift=0 (forming bar, used at 23:5x) / shift=1 (closed bar, used at 00:0x) bool SignalOK(int shift, double &cp_out) { double dh = iHigh(Symbol(), PERIOD_D1, shift); double dl = iLow(Symbol(), PERIOD_D1, shift); double dc = iClose(Symbol(), PERIOD_D1, shift); cp_out = -1.0; if(dh - dl <= 0) return false; cp_out = (dc - dl) / (dh - dl); return (cp_out < InpMaxClosePos); } void TryPlace(double ref, datetime entry_day, int cp_shift) { if(g_done_entry == entry_day || g_placed_entry == entry_day) return; if(HasPending() || HasMyPosition()) return; MqlDateTime ed; TimeToStruct(entry_day, ed); if(InpSkipThursday && ed.day_of_week == 4) { g_done_entry = entry_day; return; } double cp; if(!SignalOK(cp_shift, cp)) { if(InpPrintLog && cp >= 0) PrintFormat("R107 skip %s: close_pos=%.2f >= %.2f", TimeToString(entry_day, TIME_DATE), cp, InpMaxClosePos); g_done_entry = entry_day; return; } int digits = (int)SymbolInfoInteger(Symbol(), SYMBOL_DIGITS); double price = NormalizeDouble(ref - InpDeltaPips * PipSize(), digits); double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID); if(price >= bid) // limit must be below current market { if(InpPrintLog) PrintFormat("R107 skip %s: market already below limit (bid=%.3f limit=%.3f)", TimeToString(entry_day, TIME_DATE), bid, price); g_done_entry = entry_day; return; } // margin pre-check (MQL5 Market validator [M-Fix 4]) double margin_req = 0.0; if(OrderCalcMargin(ORDER_TYPE_BUY_LIMIT, Symbol(), InpLots, price, margin_req) && margin_req > AccountInfoDouble(ACCOUNT_FREEMARGIN)) { if(InpPrintLog) PrintFormat("R107 SKIP(no margin) Need=%.2f Free=%.2f", margin_req, AccountInfoDouble(ACCOUNT_FREEMARGIN)); g_done_entry = entry_day; return; } datetime expire = entry_day + 3600; // entry day 01:00 bool ok = g_trade.BuyLimit(InpLots, price, Symbol(), 0, 0, ORDER_TIME_SPECIFIED, expire, "R107"); uint rc = g_trade.ResultRetcode(); if(InpPrintLog) PrintFormat("R107 BuyLimit %.3f (ref=%.3f cp=%.2f entry=%s) ok=%d rc=%u", price, ref, cp, TimeToString(entry_day, TIME_DATE), ok, rc); if(ok && (rc == TRADE_RETCODE_DONE || rc == TRADE_RETCODE_PLACED)) g_placed_entry = entry_day; // 失敗時(市場閉鎖等)は marked しない → 00:0x の最初のティックで再試行される } void OnTick() { datetime now = TimeCurrent(); MqlDateTime t; TimeToStruct(now, t); datetime today = DateOf(now); // ── hour 23: ref追跡 + 発注試行 (23:50以降) ── if(t.hour == 23) { g_ref_bid = SymbolInfoDouble(Symbol(), SYMBOL_BID); g_ref_day = today; if(t.min >= 50) { // entry day = 翌取引日 (金曜23時→月曜) datetime entry_day = today + 86400; MqlDateTime ed; TimeToStruct(entry_day, ed); while(ed.day_of_week == 0 || ed.day_of_week == 6) { entry_day += 86400; TimeToStruct(entry_day, ed); } TryPlace(g_ref_bid, entry_day, 0); // forming D1 bar } return; } // ── hour 0 (00:00-00:10): 前夜発注に失敗していた場合のフォールバック ── if(t.hour == 0 && t.min <= 10) { // ref は前夜23時台の最終Bid。直近3日以内のもののみ有効(週末スパン許容) if(g_ref_bid > 0 && g_ref_day > 0 && (today - g_ref_day) <= 3 * 86400) TryPlace(g_ref_bid, today, 1); // closed D1 bar (shift=1) return; } // ── hour 1: 未約定ペンディング掃除 (expiration のバックアップ) ── if(t.hour == 1) { DeletePendings(); // fall through: ポジションは02:00まで保有 } // ── hour 2-22: ポジションクローズ ── if(t.hour >= 2 && t.hour < 23 && HasMyPosition()) { bool ok = g_trade.PositionClose(Symbol()); if(InpPrintLog) PrintFormat("R107 close @%02d:%02d ok=%d", t.hour, t.min, ok); } } //+------------------------------------------------------------------+