【PF1.86】MT5無料EA: 10年間の激動相場を制した「東京時間特化型」ミーンリバージョン戦略を公開
導入:なぜこのロジックは「激動の2年」を生き抜いたのか AI研究者の視点から見て、このロジックが10年間間のインフレ局面や金利変動という極めてボラティリティの高い相場でPF 1.86という高水準な数値を叩き出した理由は、**「徹底した環境認識によるエントリー制限」**にあります。 多くのEAが陥る罠は、トレンド相場で逆張りを仕掛け、踏み上げられることです。しかし、本ロジックは以下の3重のフィルターによって、極めて期待値の高い局面のみを抽出しています。 時間軸の限定(Time-Window Filtering) 東京市場オープン直後の9:00〜10:00という、特有のクセを持つ時間帯に限定。ノイズの多い時間帯を排除し、短期的なリバウンドが発生しやすい時間枠に絞り込んでいます。 ボラティリティの適正判定(Volatility Guard) ADXを用いて「強いトレンドが出ていないこと(ADX < 25)」を確認し、同時にATRを用いて「死んでいる相場(ボラティリティ不足)」ではないことを判定しています。これにより、トレンド追随相場での大損失を回避しつつ、効率的なリバウンドを狙う設計となっています。 マルチタイムフレーム(MTF)による方向性合致 5分足のシグナルだけでなく、15分足のSMAをフィルターとして採用。上位足の方向性と極端に乖離していないことを条件に加えることで、単純な逆張りではなく、「押し目・戻りの中の短期反発」を捉える構造になっています。 結論として、このEAは「勝つこと」よりも「負ける局面を徹底的に避けること」に特化した、非常に冷静な設計と言えます。 公開:ベースとなったPythonコード 本ロジックの根幹となるPythonコードを公開します。データ分析およびバックテストに使用された実装です。 🔍 プロはどうやってダマシを回避しているのか? 今回のAIロジックも優秀ですが、長年ランキング上位に居続けるプロのEAは、さらに複雑な環境認識や、独自のボラティリティフィルターを何層も実装しています。 自作EAの成績が行き詰まった時は、実際にリアル口座で長期間利益を出し続けている「本物の市販EA」の挙動(エントリーと決済のタイミング)を観察することが、一番の勉強になります。 👉 プロのフィルターロジックが詰まった実績EA(一本勝ち)を参考にする from strategies.base import BaseStrategy import pandas_ta as ta import pandas as pd class MeanReversionTokyoStrategy(BaseStrategy): def __init__(self): # トレーリングストップを有効化し、利益を最大化させる設計 super().__init__( name="MeanReversion_Tokyo_Quant_v2", default_tp_pips=30.0, default_sl_pips=15.0, enable_trailing_stop=True, trail_start_pips=10.0 ) # 5分足ベースのデイトレ・スキャルピング設定 self.base_timeframe = "5m" # AI画像認識および分析用のマルチタイムフレーム定義 self.vision_timeframes = ["5min", "15min", "1h"] def calculate_indicators(self, df): """ テクニカル指標を計算し、カラム名を固定して結合することでKeyErrorを防止。 """ # --- 1. ボラティリティ & トレンドフィルター --- # ADX: トレンドの強さを判定 adx_df = df.ta.adx(high='High', low='Low', close='Close', length=14) df['ADX'] = adx_df.iloc[:, 0] # 1番目のカラム(ADX_14)をADXとして固定 # ATR: ボラティリティの絶対値を判定 df['ATR'] = df.ta.atr(length=14) # --- 2. 過剰伸長判定インジケーター --- # ボリンジャーバンド (20期間, 2標準偏差) bbands_df = df.ta.bbands(length=20, std=2.0) # カラム名が動的なため、接頭辞で抽出して固定名に割り当て df['BBL'] = bbands_df.filter(like='BBL').iloc[:, 0] df['BBM'] = bbands_df.filter(like='BBM').iloc[:, 0] df['BBU'] = bbands_df.filter(like='BBU').iloc[:, 0] # RSI (14期間) df['RSI'] = df.ta.rsi(length=14) # ストキャスティクス (14, 3, 3) stoch_df = df.ta.stoch(high='High', low='Low', close='Close', length=14, k=3, d=3) df['STOCHk'] = stoch_df.filter(like='STOCHk').iloc[:, 0] # --- 3. マルチタイムフレーム (MTF) 分析 --- # 未来のカンニングを防ぐため、resample直後に .shift(1) を適用 # 15分足の終値を5分足にリインデックス df['close_15min'] = df['Close'].resample('15min').last().shift(1).reindex(df.index, method='ffill') # 1時間足の終値を5分足にリインデックス df['close_1h'] = df['Close'].resample('1h').last().shift(1).reindex(df.index, method='ffill') # 上位足の方向性判定 df['sma_15min'] = df['close_15min'].rolling(window=12).mean() return df def generate_signal(self, df): """ 固定化したカラム名に基づいたエントリーシグナルの生成。 """ if len(df) < 30: return None last = df.iloc[-1] # --- [フィルター1] 時間帯制限 (東京オープン 9:00〜10:00 JST) --- current_hour = df.index[-1].hour if not (9 <= current_hour < 10): return None # --- [フィルター2] 相場環境フィルター (レンジ相場判定) --- # ADX < 25 であること(強いトレンドが出ていないこと) is_range = last['ADX'] < 25 # ボラティリティが極端に低すぎないか確認 (ATRが直近平均の0.5倍以上) atr_ma = df['ATR'].rolling(window=100).mean().iloc[-1] is_volatile_enough = last['ATR'] > (atr_ma * 0.5) if not pd.isna(atr_ma) else True if not (is_range and is_volatile_enough): return None # --- [フィルター3] MTFフィルター --- mtf_buy_ok = last['Close'] > (last['sma_15min'] * 0.998) mtf_sell_ok = last['Close'] < (last['sma_15min'] * 1.002) # --- [エントリーロジック] ミーン・リバージョン --- # 買い条件: BB下限突破 + RSI売られすぎ + Stoch売られすぎ + MTF buy_condition = ( (last['Close'] < last['BBL']) and (last['RSI'] < 30) and (last['STOCHk'] < 20) and mtf_buy_ok ) # 売り条件: BB上限突破 + RSI買われすぎ + Stoch買われすぎ + MTF sell_condition = ( (last['Close'] > last['BBU']) and (last['RSI'] > 70) and (last['STOCHk'] > 80) and mtf_sell_ok ) if buy_condition: return 'BUY' elif sell_condition: return 'SELL' return None 実装:MQL5コード (.mq5) への翻訳 上記のPythonロジックをMT5でそのまま動作するように翻訳したコードです。 時間設定(InpStartHour / InpEndHour)は、ご利用のブローカーのサーバー時間に合わせて調整してください。 ...