//+------------------------------------------------------------------+ //| Urban Flux Research — 研究アーカイブ公開版(無償) | //| https://apex-predator-x.com/ | //| | //| 公開日 : 2026-08-22 | //| 配布区分 : 研究アーカイブ / 検証用ソースコード | //| 対応 : MetaTrader 5 (MQL5) | //| ライセンス : 検証・学習目的での閲覧・改変・実行を許諾します。 | //| 再配布する場合は本ヘッダを削除しないでください。 | //| | //| ── 免責 ────────────────────────────────────────────────────── | //| 本コードは研究記録の公開であり、投資助言ではありません。 | //| 過去の検証結果は将来の利益を一切保証しません。 | //| 実口座での使用により生じたいかなる損失についても、 | //| 当方は責任を負いません。必ずデモ口座で検証してください。 | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| ⚠⚠ 重要警告:本EAは実運用を推奨しません ⚠⚠ | //| | //| 本EAは絶対時刻(TimeGMT)を基準にエントリーします。 | //| しかし当社のその後の検証(TZ-2026-001 / RA-2026-002)により、 | //| 研究に用いた生データはブローカーサーバー時刻であり、 | //| 観測されたエッジの真の位置は | //| 「週次 market reopen の次の1時間」 | //| であることが判明しました。ライブで参照される真UTC01:00は | //| 別の時間帯であり、そこでの期待値は【負】と判定されています | //| (Trial Ledger T-0005)。 | //| | //| さらに、実測コスト控除後に生存する可能性があるのは USDJPY のみ | //| です。GBPJPY は再開直後のスプレッドが広く | //| (p50 3.2pip / p95 6.8pip)、悲観コストで期待値が負になります。 | //| EURJPY も同様に脆弱です。 | //| 本EAの対象ペアは、この「負」と判定されたペアを含みます。 | //| | //| → 本コードは「当時どう実装したか」を示す研究アーカイブです。 | //| 時刻基準を修正した実装例は TokyoMorningEA_v1.04.mq5 を | //| 参照してください(週次reopenアンカー・タイムゾーン非依存)。 | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| R135_ConnDOWN_G0pos_Portfolio_GateB.mq5 | //| RESEARCH-135 — ConnDOWN×g0pos GBPJPY+EURJPY Portfolio GATE B | //| | //| Recommended: GBPJPY or EURJPY | //| Signal: Mon GMT01:00 LONG when: | //| [1] ConnDOWN≥1: UTC木曜 H1が DOWN (close < open) | //| [2] g0_pos<40%: H1[1](GMT00バー) が低位引け | //| Exit: GMT02:00 (1時間保有) | //| | //| GBPJPY Python IS: avg=+27.4p PF=41.11 WR=89% N=19 (R086) | //| EURJPY Python IS: avg=+16.9p PF=31.10 WR=80% N=20 (R135) | //| Portfolio IS: avg=+21.9p PF=37.15 WR=85% N=40 | //| Portfolio Val: avg=+24.0p PF= 9.06 WR=85% N=13 | //| Portfolio OOS: avg=+20.1p PF=805 WR=92% N=12 | //| WFA 17/17=100% | Null Z=5.17 | 全6年黒字 | //| | //| Gate B合格基準: | //| GBPJPY: pip-PF ≥ 1.5 (Python 41.11 × 0.6 = 24.7) | //| EURJPY: pip-PF ≥ 1.5 (Python 31.10 × 0.6 = 18.7) | //| Portfolio: 両ペア合算 pip-PF ≥ 1.5 | //| | //| v1.00: R086 v1.01を基に作成 (ロジック同一・MAGIC更新) | //| v1.01: 複利ロット計算追加 (InpCompound / InpRiskPct / InpRiskPips)| //+------------------------------------------------------------------+ #property copyright "Urban Flux Research — RESEARCH-135" #property link "https://apex-predator-x.com/" #property version "1.01" #property description "[RESEARCH ARCHIVE / NOT FOR LIVE] Mon GMT01 LONG | Thu DOWN x GMT00 Low Close | R135 Portfolio Gate B" #include #include #define MAGIC_NUM 135001 #define TIMER_MS 10000 // 10秒ごとチェック #define MAX_RETRY 10 //============================================================ // 入力パラメータ //============================================================ input group "=== エントリー条件 ===" input double InpG0PosThr = 0.40; // GMT00バー低位引け閾値 (0.40 = 下位40%) input int InpRetryMaxMin = 10; // スプレッド待機上限 (分) input group "=== スプレッド ===" input double InpMaxSpreadPip = 5.0; // 最大スプレッド (pip) input group "=== ロット ===" input double InpLots = 0.01; input group "=== 複利ロット ===" input bool InpCompound = false; // true=複利ロット / false=固定ロット(InpLots) input double InpRiskPct = 2.0; // リスク (% of equity per trade) input double InpRiskPips = 50.0; // 想定最大損失 (pip) input group "=== ログ ===" input bool InpPrintLog = true; //============================================================ // グローバル変数 //============================================================ CTrade trade; CPositionInfo posInfo; bool g_entry_done = false; bool g_exit_done = false; bool g_filter_skip = false; datetime g_last_day = 0; int g_exit_retry = 0; //============================================================ // ユーティリティ //============================================================ double PipSize() { int digits = (int)SymbolInfoInteger(Symbol(), SYMBOL_DIGITS); double point = SymbolInfoDouble(Symbol(), SYMBOL_POINT); return (digits == 3 || digits == 5) ? point * 10.0 : point; } double GetSpreadPip() { return (SymbolInfoDouble(Symbol(), SYMBOL_ASK) - SymbolInfoDouble(Symbol(), SYMBOL_BID)) / PipSize(); } ENUM_ORDER_TYPE_FILLING DetectFilling() { long m = SymbolInfoInteger(Symbol(), SYMBOL_FILLING_MODE); if((m & SYMBOL_FILLING_IOC) != 0) return ORDER_FILLING_IOC; if((m & SYMBOL_FILLING_FOK) != 0) return ORDER_FILLING_FOK; return ORDER_FILLING_RETURN; } //============================================================ // 複利ロット計算 //============================================================ double CalcLots() { if(!InpCompound) return InpLots; double equity = AccountInfoDouble(ACCOUNT_EQUITY); double risk_usd = equity * InpRiskPct / 100.0; double pip_val = 0.0; double price = SymbolInfoDouble(Symbol(), SYMBOL_BID); if(!OrderCalcProfit(ORDER_TYPE_BUY, Symbol(), 1.0, price, price + PipSize(), pip_val) || pip_val <= 0) return InpLots; double raw_lots = risk_usd / (InpRiskPips * pip_val); double lot_step = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP); double lot_min = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN); double lot_max = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MAX); double lots = MathFloor(raw_lots / lot_step) * lot_step; return MathMax(lot_min, MathMin(lot_max, lots)); } void GetGmt(int &h, int &wd) { MqlDateTime dt; TimeToStruct(TimeGMT(), dt); h = dt.hour; wd = dt.day_of_week; } //============================================================ // 日次リセット //============================================================ void DailyReset() { datetime today = (datetime)(TimeCurrent() / 86400 * 86400); if(today == g_last_day) return; g_last_day = today; g_entry_done = false; g_exit_done = false; g_filter_skip = false; g_exit_retry = 0; if(InpPrintLog) PrintFormat("R135 日次リセット | %s | %s", Symbol(), TimeToString(today, TIME_DATE)); } //============================================================ // ポジション管理 //============================================================ bool HasPosition() { for(int i = PositionsTotal() - 1; i >= 0; i--) if(posInfo.SelectByIndex(i)) if(posInfo.Symbol() == Symbol() && posInfo.Magic() == MAGIC_NUM) return true; return false; } bool CloseAllPos() { bool ok = true; for(int i = PositionsTotal() - 1; i >= 0; i--) if(posInfo.SelectByIndex(i)) if(posInfo.Symbol() == Symbol() && posInfo.Magic() == MAGIC_NUM) if(!trade.PositionClose(posInfo.Ticket())) { PrintFormat("R135 ClosePos失敗 ticket=%d err=%d", posInfo.Ticket(), GetLastError()); ok = false; } return ok; } //============================================================ // シグナル1: GMT00バー低位引け (g0_pos < threshold) // H1[1] = GMT01時点での前バー = GMT00バー (UTC 00:00-00:59) //============================================================ bool CheckGMT00Shape() { double g00_h = iHigh (Symbol(), PERIOD_H1, 1); double g00_l = iLow (Symbol(), PERIOD_H1, 1); double g00_c = iClose(Symbol(), PERIOD_H1, 1); double range = g00_h - g00_l; if(range < PipSize() * 0.5) { if(InpPrintLog) PrintFormat("R135 GMT00バー レンジ不足スキップ | %s", Symbol()); return false; } double pos = (g00_c - g00_l) / range; bool pass = (pos < InpG0PosThr); if(InpPrintLog) PrintFormat("R135 GMT00フィルター | %s | H=%.3f L=%.3f C=%.3f range=%.1fpip | " "g0_pos=%.3f | %s", Symbol(), g00_h, g00_l, g00_c, range / PipSize(), pos, pass ? "PASS" : StringFormat("SKIP(%.3f>=%.2f)", pos, InpG0PosThr)); return pass; } //============================================================ // シグナル2: 木曜DOWN (ConnDOWN≥1) — UTC日付境界ベース // // UTC木曜 00:00 の H1 open と 23:00 の H1 close を直接比較。 // Python は UTC resample("1D") → 同一判定ロジック。 // // UTC月曜 00:00 を起点に: // UTC木曜 open = mon_utc - 4*86400 (Thu 00:00 UTC) // UTC木曜 close = mon_utc - 3*86400 - 3600 (Thu 23:00 UTC) //============================================================ bool CheckThursdayDown() { MqlDateTime mdt; TimeToStruct(TimeGMT(), mdt); datetime mon_gmt_midnight = TimeGMT() - (datetime)mdt.hour * 3600 - (datetime)mdt.min * 60 - (datetime)mdt.sec; datetime thu_gmt_open = mon_gmt_midnight - 4 * 86400; datetime thu_gmt_close = mon_gmt_midnight - 3 * 86400 - 3600; int broker_offset = (int)(TimeCurrent() - TimeGMT()); datetime thu_broker_open = thu_gmt_open + broker_offset; datetime thu_broker_close = thu_gmt_close + broker_offset; int shift_o = iBarShift(Symbol(), PERIOD_H1, thu_broker_open, false); int shift_c = iBarShift(Symbol(), PERIOD_H1, thu_broker_close, false); if(shift_o < 0 || shift_c < 0) { if(InpPrintLog) PrintFormat("R135 木曜UTC: iBarShift失敗 | %s", Symbol()); return false; } double thu_o = iOpen (Symbol(), PERIOD_H1, shift_o); double thu_c = iClose(Symbol(), PERIOD_H1, shift_c); bool pass = (thu_c < thu_o); double pip_m = (thu_c - thu_o) / PipSize(); if(InpPrintLog) PrintFormat("R135 木曜UTC | %s | OpenH1[%d]=%.3f CloseH1[%d]=%.3f Δ=%.1fpip | %s", Symbol(), shift_o, thu_o, shift_c, thu_c, pip_m, pass ? "PASS(DOWN)" : "SKIP(UP/FLAT)"); return pass; } //============================================================ // メイン処理 //============================================================ void ProcessLogic() { DailyReset(); int h, wd; GetGmt(h, wd); // ─── クローズ: GMT02 ─── if(!g_exit_done && h == 2 && HasPosition()) { if(CloseAllPos()) { g_exit_done = true; g_exit_retry = 0; if(InpPrintLog) PrintFormat("R135 EXIT完了 | %s GMT%02d", Symbol(), h); } else { g_exit_retry++; if(g_exit_retry >= MAX_RETRY) g_exit_done = true; } return; } // ─── エントリー判定 ─── if(g_entry_done || g_filter_skip) return; // 月曜GMT01のみ if(wd != 1) return; if(h != 1) return; MqlDateTime mdt; TimeToStruct(TimeGMT(), mdt); if(mdt.min > InpRetryMaxMin) return; // スプレッドチェック double spread = GetSpreadPip(); if(spread > InpMaxSpreadPip) { if(mdt.min <= InpRetryMaxMin) { if(InpPrintLog) PrintFormat("R135 スプレッド待機 | %s %.2fpip > %.1fpip | min=%d", Symbol(), spread, InpMaxSpreadPip, mdt.min); return; } if(InpPrintLog) PrintFormat("R135 スプレッドスキップ | %s %.2fpip", Symbol(), spread); g_entry_done = true; g_filter_skip = true; return; } // Axis-S: GMT00バー低位引けチェック if(!CheckGMT00Shape()) { g_filter_skip = true; g_entry_done = true; return; } // Axis-D: 木曜DOWNチェック if(!CheckThursdayDown()) { g_filter_skip = true; g_entry_done = true; return; } // ロット計算 double lots = CalcLots(); // 証拠金プリチェック double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK); double margin_req = 0.0; if(OrderCalcMargin(ORDER_TYPE_BUY, Symbol(), lots, ask, margin_req) && margin_req > AccountInfoDouble(ACCOUNT_FREEMARGIN)) { g_entry_done = true; if(InpPrintLog) PrintFormat("R135 SKIP(no margin) | %s | Need=%.2f Free=%.2f", Symbol(), margin_req, AccountInfoDouble(ACCOUNT_FREEMARGIN)); return; } // LONG エントリー trade.SetExpertMagicNumber(MAGIC_NUM); trade.SetDeviationInPoints(50); trade.SetTypeFilling(DetectFilling()); bool ok = trade.Buy(lots, Symbol(), ask, 0.0, 0.0, "R135_GateB"); if(ok) { g_entry_done = true; if(InpPrintLog) { double g0p = (iClose(Symbol(), PERIOD_H1, 1) - iLow(Symbol(), PERIOD_H1, 1)) / MathMax(PipSize() * 0.5, iHigh(Symbol(), PERIOD_H1, 1) - iLow(Symbol(), PERIOD_H1, 1)); PrintFormat("R135v1.01 ENTER LONG | %s | GMT%02d | " "Ask=%.3f Lot=%.2f Spread=%.2fpip | " "g0_pos=%.3f(<%.2f) ThuDOWN=UTC確認済", Symbol(), h, ask, lots, spread, g0p, InpG0PosThr); } } else PrintFormat("R135 エントリー失敗 | %s | err=%d %s", Symbol(), GetLastError(), trade.ResultComment()); } //============================================================ // 初期化 //============================================================ int OnInit() { // シンボル警告のみ(INIT_FAILED返さない → MQL5 Market対応) if(StringFind(Symbol(), "GBPJPY") < 0 && StringFind(Symbol(), "EURJPY") < 0) PrintFormat("R135 警告: 推奨シンボルはGBPJPYまたはEURJPYです (現在: %s)", Symbol()); trade.SetExpertMagicNumber(MAGIC_NUM); trade.SetDeviationInPoints(50); trade.SetTypeFilling(DetectFilling()); EventSetMillisecondTimer(TIMER_MS); if(InpPrintLog) PrintFormat("R135 Gate B 初期化 | %s | " "Signal: Mon GMT01 LONG when Thu-DOWN x g0_pos<%.2f | " "Exit: GMT02 | MaxSpread=%.1fpip | %s", Symbol(), InpG0PosThr, InpMaxSpreadPip, InpCompound ? StringFormat("複利 Risk=%.1f%% / %.0fpip", InpRiskPct, InpRiskPips) : StringFormat("固定 Lots=%.2f", InpLots)); return INIT_SUCCEEDED; } void OnDeinit(const int reason) { EventKillTimer(); } void OnTick() { ProcessLogic(); } void OnTimer() { ProcessLogic(); }