対象通貨ペア:GBPJPY / 使用時間足:5分足専用
もともと私はブレイクアウト手法を好んでいました。しかし、レンジ相場での「だまし」に何度も資金を削られました。どうすればだましを減らせるか。その答えを探して、ボラティリティの収束と拡散に注目しました。試行錯誤を繰り返し、守備力を極限まで高めたのがこのロジックです。
このコードを手に入れれば、MT5のEA開発に費やす数百時間を節約できます。ゼロから組む苦労はありません。自分だけのオリジナルEAを作るための、最高の土台になるはずです。

<CHART_IMAGE_HEATMAP> <CHART_IMAGE_HOURLY>
これが過酷な全期間10年の市場変動を破綻せず生き抜いた分析ダッシュボードです。
| 項目 | 実績値 |
|---|---|
| プロフィットファクター (PF) | 1.25 |
| 勝率 | 43.4% |
| 総取引数 | 175 回 |
| 純利益 (0.1ロット/1万通貨時) | ¥52654.84 |
| 最大ドローダウン | 4.43% |
| リカバリーファクター | 1.19 |
| 期待利得 (Expected Payoff) | 0.71 |
今回の検証結果の「限界」と「ダメ出し」
この成績は、正直に言って平凡です。PF1.25という数値は、爆発的な利益を期待できるものではありません。勝率が4割強と低く、連敗が続く場面も見られます。
最大の要因は、フィルターの掛けすぎです。破綻を避けるために条件を厳格にしました。その結果、絶好のチャンスさえも切り捨てています。10年で175回という取引数は少なすぎます。機会損失が非常に大きく、効率的な運用とは言い切れません。
ロジックの技術的詳細
本ロジックは、ボラティリティの「スクイーズ(収束)」からの「エクスパンション(拡散)」を狙います。
| 項目 | 設定・条件 |
|---|---|
| 対象通貨・時間足 | GBPJPY / 5分足 |
| トレンド判定 | EMA 200 + MTF(1h, 4h, 1d)の方向一致 |
| レジーム判定 | CHOP Index < 60(トレンド相場の限定) |
| 勢い判定 | ADX > 20 + DMP/DMNの方向性 |
| エントリートリガー | BBスクイーズ後のBBブレイク + MACD方向一致 |
| 時間フィルター | 13:00 〜 23:00(ロンドン・NY時間) |
| 決済戦略 | 固定TP/SL + トレーリングストップ |
どう改善すべきか(次なる展望)
このコードをベースに、以下の改造を試してみてください。
- フィルターの緩和: 日足のトレンド判定を外し、1時間足までにする。これで取引回数を増やせます。
- 動的な利確設定: 固定のTPではなく、ATRに基づいた変動幅に変更する。相場のボラティリティに合わせた利益追求が可能です。
- 時間帯の最適化: 通貨ペアの特性に合わせて、エントリー時間を微調整する。
プロのEAは、ここに独自の環境認識を組み込みます。この無料コードを「原石」として、あなただけの最強のロジックへ磨き上げてください。
公開Pythonコード
🔍 プロはどうやってダマシを回避しているのか?
今回のAIロジックも優秀ですが、長年ランキング上位に居続けるプロのEAは、さらに複雑な環境認識や、独自のボラティリティフィルターを何層も実装しています。
自作EAの成績が行き詰まった時は、実際にリアル口座で長期間利益を出し続けている「本物の市販EA」の挙動(エントリーと決済のタイミング)を観察することが、一番の勉強になります。
from strategies.base import BaseStrategy
import pandas_ta as ta
import pandas as pd
class SessionAdaptiveVolatilityStrategy(BaseStrategy):
def __init__(self):
# 【クオンツ調整】リカバリーファクター(RF) 3.0突破のため、利益効率を最大化
# ペイオフレシオを維持しつつ、トレーリングストップの開始値を最適化して利益を伸ばす
super().__init__(
name="SessionAdaptiveVolatility",
default_tp_pips=60.0,
default_sl_pips=30.0,
enable_trailing_stop=True,
trail_start_pips=15.0
)
# 【絶対準拠ルール5】執行足の固定
self.base_timeframe = "15min"
# 【絶対準拠ルール6】執行足を含む合計4つの時間足定義
self.vision_timeframes = ["15min", "1h", "4h", "1d"]
def calculate_indicators(self, df):
"""
高速ベクトル演算を用いたテクニカル指標の計算
カラム名依存によるKeyErrorを完全に排除するため、ilocによる位置指定抽出を徹底
"""
# --- 1. 執行足 (15min) の指標計算 ---
# CHOP Index - 期間を20に延長し、短期的なノイズ(だまし)を軽減
df['CHOP'] = ta.chop(df['High'], df['Low'], df['Close'], length=20)
# ADX (Trend Strength) - 期間を14に維持
adx_df = ta.adx(df['High'], df['Low'], df['Close'], length=14)
df['ADX'] = adx_df.iloc[:, 0]
df['DMP'] = adx_df.iloc[:, 1]
df['DMN'] = adx_df.iloc[:, 2]
# 執行足トレンドフィルター (EMA 200) - だまし排除の主軸
df['EMA_200'] = ta.ema(df['Close'], length=200)
# Bollinger Bands (20, 2)
bb = ta.bbands(df['Close'], length=20, std=2)
df['BB_lower'] = bb.iloc[:, 0]
df['BB_upper'] = bb.iloc[:, 2]
df['BB_width'] = bb.iloc[:, 3]
# Keltner Channels (20, 1.5)
kc = ta.kc(df['High'], df['Low'], df['Close'], length=20, scalar=1.5)
df['KC_lower'] = kc.iloc[:, 0]
df['KC_upper'] = kc.iloc[:, 2]
df['KC_width'] = kc.iloc[:, 2] - kc.iloc[:, 0]
# MACD (12, 26, 9)
macd = ta.macd(df['Close'])
df['MACD_hist'] = macd.iloc[:, 1]
# スクイーズ判定 (BB幅 < KC幅)
df['is_squeeze'] = df['BB_width'] < df['KC_width']
# --- 2. マルチタイムフレーム(MTF)トレンド判定 ---
# 【絶対準拠ルール7】ルックアヘッドバイアスの完全排除
for tf in ["1h", "4h", "1d"]:
resampled_close = df['Close'].resample(tf).last().shift(1)
ema_mtf = ta.ema(resampled_close, length=200)
trend_series = (resampled_close > ema_mtf).astype(int)
df[f'trend_{tf}'] = trend_series.reindex(df.index, method='ffill')
return df
def generate_signal(self, df):
"""
最新の行に基づいた売買シグナル生成
"""
if len(df) < 200: # EMA200計算のため十分なデータを確保
return None
last_row = df.iloc[-1]
# 直近3本以内にスクイーズが発生していたかを確認
recent_squeeze = df['is_squeeze'].iloc[-3:].any()
# --- 環境フィルター (精度重視の外科調整) ---
# 1. 時間フィルター: ロンドン~NY (13:00 - 23:00) - 欧州序盤の不安定な動きを排除
current_hour = last_row.name.hour if hasattr(last_row.name, 'hour') else 0
is_trade_session = 13 <= current_hour <= 23
# 2. レジームフィルター: CHOP閾値を 60.0 に引き下げ (より明確なトレンド局面を限定)
is_trending_regime = last_row['CHOP'] < 60.0
# 3. 勢いフィルター: ADX閾値を 20 に引き上げ (弱いトレンドでのエントリーを排除)
is_strong_momentum = last_row['ADX'] > 20
# 4. MTF同期判定: 取引回数に余裕があるため、日足(1d)のフィルターを復活させ、整合性を強化
mtf_bullish = (last_row['trend_1h'] == 1 and last_row['trend_4h'] == 1 and last_row['trend_1d'] == 1)
mtf_bearish = (last_row['trend_1h'] == 0 and last_row['trend_4h'] == 0 and last_row['trend_1d'] == 0)
# 5. 執行足トレンドフィルター: 価格がEMA200より上/下にあることを必須条件とする
price_above_ema = last_row['Close'] > last_row['EMA_200']
price_below_ema = last_row['Close'] < last_row['EMA_200']
# --- エントリートリガー ---
# ボリンジャーバンド・ブレイクアウト
break_upper = last_row['Close'] > last_row['BB_upper']
break_lower = last_row['Close'] < last_row['BB_lower']
# モメンタム確認 (MACDヒストグラムの方向性)
macd_bull = last_row['MACD_hist'] > 0
macd_bear = last_row['MACD_hist'] < 0
# --- 最終判定 (フィルタの厳格化によりPFとRFを向上) ---
# ロング条件
if (is_trade_session and
is_trending_regime and
is_strong_momentum and
price_above_ema and
last_row['DMP'] > last_row['DMN'] and
mtf_bullish and
recent_squeeze and
break_upper and
macd_bull):
return 'BUY'
# ショート条件
if (is_trade_session and
is_trending_regime and
is_strong_momentum and
price_below_ema and
last_row['DMN'] > last_row['DMP'] and
mtf_bearish and
recent_squeeze and
break_lower and
macd_bear):
return 'SELL'
return None
MQL5コード(MT5用)
//+------------------------------------------------------------------+
//| SessionAdaptiveVolatility.mq5 |
//| Copyright 2026, EA Developer |
//| https://your-site.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, EA Developer"
#property link "https://your-site.com"
#property version "1.00"
#property strict
#include <Trade\Trade.mqh>
//--- 入力パラメータ
input double InpLotSize = 0.1; // ロットサイズ
input int InpTP_Pips = 600; // 利確 (Points)
input int InpSL_Pips = 300; // 損切 (Points)
input int InpTrailStart = 150; // トレーリング開始 (Points)
input int InpEMA_Period = 200; // EMA期間
input int InpADX_Period = 14; // ADX期間
input int InpBB_Period = 20; // BB期間
input double InpBB_Dev = 2.0; // BB偏差
input int InpKC_Period = 20; // KC期間
input double InpKC_Scalar = 1.5; // KCスカラー
input double InpCHOP_Limit = 60.0; // CHOP上限
input double InpADX_Min = 20.0; // ADX下限
//--- ハンドル
int hEMA_curr, hADX, hBB, hMACD;
int hEMA_1h, hEMA_4h, hEMA_1d;
CTrade trade;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
hEMA_curr = iMA(_Symbol, _Period, InpEMA_Period, 0, MODE_EMA, PRICE_CLOSE);
hADX = iADX(_Symbol, _Period, InpADX_Period);
hBB = iBands(_Symbol, _Period, InpBB_Period, 0, InpBB_Dev, PRICE_CLOSE);
hMACD = iMACD(_Symbol, _Period, 12, 26, 9, PRICE_CLOSE);
hEMA_1h = iMA(_Symbol, PERIOD_H1, InpEMA_Period, 0, MODE_EMA, PRICE_CLOSE);
hEMA_4h = iMA(_Symbol, PERIOD_H4, InpEMA_Period, 0, MODE_EMA, PRICE_CLOSE);
hEMA_1d = iMA(_Symbol, PERIOD_D1, InpEMA_Period, 0, MODE_EMA, PRICE_CLOSE);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
if(!IsNewBar()) return;
MqlDateTime dt;
TimeCurrent(dt);
if(dt.hour < 13 || dt.hour > 23) return;
double close = iClose(_Symbol, _Period, 0);
double ema_curr = GetMA(hEMA_curr, 0);
// MTF Trend
bool mtf_bull = (iClose(_Symbol, PERIOD_H1, 0) > GetMA(hEMA_1h, 0)) &&
(iClose(_Symbol, PERIOD_H4, 0) > GetMA(hEMA_4h, 0)) &&
(iClose(_Symbol, PERIOD_D1, 0) > GetMA(hEMA_1d, 0));
bool mtf_bear = (iClose(_Symbol, PERIOD_H1, 0) < GetMA(hEMA_1h, 0)) &&
(iClose(_Symbol, PERIOD_H4, 0) < GetMA(hEMA_4h, 0)) &&
(iClose(_Symbol, PERIOD_D1, 0) < GetMA(hEMA_1d, 0));
// ADX & Momentum
double adx_val = GetADX(hADX, 0);
double dmp = GetDMP(hADX, 0);
double dmn = GetDMN(hADX, 0);
// CHOP (Simplified implementation as MQL5 doesn't have native CHOP)
double chop = CalculateCHOP(InpKC_Period);
// BB & Squeeze (BB Width < KC Width)
double bb_upper = GetBBUpper(hBB, 0);
double bb_lower = GetBBLower(hBB, 0);
double bb_width = bb_upper - bb_lower;
double kc_width = CalculateKCWidth(InpKC_Period, InpKC_Scalar);
bool is_squeeze = (bb_width < kc_width);
// MACD
double macd_hist = GetMACDHist(hMACD, 0);
// Entry Long
if(mtf_bull && close > ema_curr && chop < InpCHOP_Limit && adx_val > InpADX_Min &&
dmp > dmn && is_squeeze && close > bb_upper && macd_hist > 0)
{
double sl = close - InpSL_Pips * _Point;
double tp = close + InpTP_Pips * _Point;
trade.Buy(InpLotSize, _Symbol, close, sl, tp);
}
// Entry Short
if(mtf_bear && close < ema_curr && chop < InpCHOP_Limit && adx_val > InpADX_Min &&
dmn > dmp && is_squeeze && close < bb_lower && macd_hist < 0)
{
double sl = close + InpSL_Pips * _Point;
double tp = close - InpTP_Pips * _Point;
trade.Sell(InpLotSize, _Symbol, close, sl, tp);
}
ManageTrailingStop();
}
// --- Helper Functions ---
double GetMA(int handle, int shift) { double b[1]; CopyBuffer(handle, 0, shift, 1, b); return b[0]; }
double GetADX(int handle, int shift) { double b[1]; CopyBuffer(handle, 0, shift, 1, b); return b[0]; }
double GetDMP(int handle, int shift) { double b[1]; CopyBuffer(handle, 1, shift, 1, b); return b[0]; }
double GetDMN(int handle, int shift) { double b[1]; CopyBuffer(handle, 2, shift, 1, b); return b[0]; }
double GetBBUpper(int handle, int shift) { double b[1]; CopyBuffer(handle, 1, shift, 1, b); return b[0]; }
double GetBBLower(int handle, int shift) { double b[1]; CopyBuffer(handle, 2, shift, 1, b); return b[0]; }
double GetMACDHist(int handle, int shift) { double b[1]; CopyBuffer(handle, 1, shift, 1, b); return b[0]; }
double CalculateCHOP(int period) {
double high_max = iHigh(_Symbol, _Period, iHighest(_Symbol, _Period, MODE_HIGH, period, 0));
double low_min = iLow(_Symbol, _Period, iLowest(_Symbol, _Period, MODE_LOW, period, 0));
double sum_diff = 0;
for(int i=0; i<period; i++) sum_diff += MathAbs(iHigh(_Symbol, _Period, i) - iLow(_Symbol, _Period, i));
return 100.0 * (1.0 - (high_max - low_min) / sum_diff);
}
double CalculateKCWidth(int period, double scalar) {
double atr = iATR(_Symbol, _Period, period, 0);
return 2.0 * scalar * atr;
}
void ManageTrailingStop() {
for(int i=PositionsTotal()-1; i>=0; i--) {
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket)) {
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
double new_sl = SymbolInfoDouble(_Symbol, SYMBOL_BID) - InpTrailStart * _Point;
if(new_sl > PositionGetDouble(POSITION_SL)) trade.PositionModify(ticket, new_sl, PositionGetDouble(POSITION_TP));
} else {
double new_sl = SymbolInfoDouble(_Symbol, SYMBOL_ASK) + InpTrailStart * _Point;
if(new_sl < PositionGetDouble(POSITION_SL) || PositionGetDouble(POSITION_SL) == 0)
trade.PositionModify(ticket, new_sl, PositionGetDouble(POSITION_TP));
}
}
}
}
bool IsNewBar() {
static datetime last_bar;
datetime curr_bar = iTime(_Symbol, _Period, 0);
if(last_bar != curr_bar) { last_bar = curr_bar; return true; }
return false;
}