対象通貨ペア:USDJPY(ドル円) / 使用時間足:5分足専用
私は以前、ブレイクアウト手法の「ダマシ」に深く悩まされました。トレンドが出ると信じてエントリーし、そのまま反転して損切りになる。この経験から、相場の「環境」を厳格に分ける手法を模索しました。レンジ相場での逆張りこそが、低リスクを実現する鍵になると確信したからです。試行錯誤を繰り返し、ようやく辿り着いたのがこの適応型平均回帰ロジックです。
このコードをベースにすれば、MT5のEA開発に費やす数百時間を節約できます。ゼロから構築するのは時間がかかります。本ロジックを土台にして、あなただけの最適解を探ってください。
<CHART_IMAGE_EQUITY> <CHART_IMAGE_HEATMAP> <CHART_IMAGE_HOURLY>
これが過酷な全期間10年の市場変動を破綻せず生き抜いた分析ダッシュボードです。
| 項目 | 実績値 |
|---|---|
| プロフィットファクター (PF) | 2.11 |
| 勝率 | 45.8% |
| 総取引数 | 24 回 |
| 純利益 (0.1ロット/1万通貨時) | ¥19235.42 |
| 最大ドローダウン | 0.86% |
| リカバリーファクター | 2.24 |
| 期待利得 (Expected Payoff) | 1.15 |
今回の検証結果の「限界」と「ダメ出し」
この成績を見て、まず気づくのは取引数の少なさです。10年で24回という数字は、運用効率として極めて低いです。PFは2.11と高い水準にあります。しかし、機会損失が非常に大きいといえます。
原因は、フィルターを厳しくしすぎたことです。ADXによるレンジ判定と、RSIの極端な閾値設定が、エントリーチャンスを削りすぎました。結果として「負けない」仕組みにはなりましたが、「稼ぐ」仕組みとしては不十分です。低ドローダウンを実現した代償として、資金効率を犠牲にした形になります。
ロジックの技術的詳細
本ロジックは、上位足の方向性を確認した上で、執行足の過熱感を狙う戦略です。
| 要素 | 設定・条件 | 役割 |
|---|---|---|
| 上位足トレンド | 1時間足 EMA200 | 環境認識(買い・売りの方向決定) |
| レンジ判定 | ADX(14) < 20 | トレンド相場での逆張りを回避 |
| ボラティリティ | ATR(14) > ATR_MA(100) * 0.6 | 死に相場でのエントリーを制限 |
| 過熱感判定 | RSI(14) $\le$ 25 または $\ge$ 75 | 統計的な売られすぎ・買われすぎを検知 |
| エントリートリガー | ボリンジャーバンド $\sigma 2.5$ の回帰 | バンド外から内側への回帰を確認 |
どう改善すべきか(次なる展望)
この無料コードは、いわば「未完成の原石」です。ここから利益を伸ばすには、いくつかの改善策が考えられます。
- ADX閾値の動的変更: 固定値の20ではなく、相場のボラティリティに合わせて変動させると、取引回数を増やせます。
- 時間帯フィルターの導入: 東京時間やロンドン時間など、通貨ペア特有のクセを考慮した時間制限を設けることです。
- 利確目標の最適化: 現在は固定ピプスですが、ATRに基づいた動的な利確設定に変更すれば、期待利得が向上します。
プロのEAは、こうした環境認識をさらに詳細なロジックで補っています。この土台を改造して、あなただけの最強の武器に仕上げてください。
Pythonソースコード
🔍 プロはどうやってダマシを回避しているのか?
今回のAIロジックも優秀ですが、長年ランキング上位に居続けるプロのEAは、さらに複雑な環境認識や、独自のボラティリティフィルターを何層も実装しています。
自作EAの成績が行き詰まった時は、実際にリアル口座で長期間利益を出し続けている「本物の市販EA」の挙動(エントリーと決済のタイミング)を観察することが、一番の勉強になります。
from strategies.base import BaseStrategy
import pandas_ta as ta
import pandas as pd
import numpy as np
class MTFRAMRStrategy(BaseStrategy):
"""
MTF-Regime Adaptive Mean Reversion (MTF-RAMR) - Optimized Version
"""
def __init__(self):
# リスク管理パラメータの最適化
super().__init__(
name="MTF-RAMR_Optimized",
default_tp_pips=40.0,
default_sl_pips=30.0,
enable_trailing_stop=True,
trail_start_pips=15.0
)
self.base_timeframe = "5min"
self.vision_timeframes = ["5min", "15min", "1h"]
def calculate_indicators(self, df):
# --- 1. 執行足 (5min) インジケーター計算 ---
df.ta.bbands(length=20, std=2.5, append=True)
df.ta.rsi(length=14, append=True)
df.ta.adx(length=14, append=True)
df.ta.atr(length=14, append=True)
atr_col = [c for c in df.columns if c.startswith('ATRr')][0]
df['atr_ma'] = df[atr_col].rolling(window=100).mean()
# --- 2. 上位足 (1h) トレンド判定 ---
h1_resampled = df['Close'].resample('1h').last()
h1_ema = ta.ema(h1_resampled, length=200)
h1_trend = np.where(h1_resampled > h1_ema, 1, -1)
h1_trend_series = pd.Series(h1_trend, index=h1_resampled.index)
df['h1_trend'] = h1_trend_series.shift(1).reindex(df.index, method='ffill')
return df
def generate_signal(self, df):
if len(df) < 200:
return None
curr = df.iloc[-1]
prev = df.iloc[-2]
try:
bbl_col = [c for c in df.columns if c.startswith('BBL')][0]
bbu_col = [c for c in df.columns if c.startswith('BBU')][0]
rsi_col = [c for c in df.columns if c.startswith('RSI')][0]
adx_col = [c for c in df.columns if c.startswith('ADX')][0]
atr_col = [c for c in df.columns if c.startswith('ATRr')][0]
except IndexError:
return None
is_range = curr[adx_col] < 20
is_volatile = curr[atr_col] > (curr['atr_ma'] * 0.6)
h1_trend = curr['h1_trend']
if h1_trend == 1 and is_range and is_volatile:
if prev[rsi_col] <= 25 and prev['Close'] <= prev[bbl_col]:
if curr['Close'] > curr[bbl_col]:
return 'BUY'
if h1_trend == -1 and is_range and is_volatile:
if prev[rsi_col] >= 75 and prev['Close'] >= prev[bbu_col]:
if curr['Close'] < curr[bbu_col]:
return 'SELL'
return None
MQL5コード (.mq5)
//+------------------------------------------------------------------+
//| MTF_RAMR_Optimized.mq5|
//| Copyright 2026, AI Researcher |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, AI Researcher"
#property link "https://your-blog-url.com"
#property version "1.00"
#property strict
// パラメータ設定
input int InpBBPeriod = 20; // Bollinger Bands Period
input double InpBBStdDev = 2.5; // Bollinger Bands StdDev
input int InpRSIPeriod = 14; // RSI Period
input int InpADXPeriod = 14; // ADX Period
input int InpATRPeriod = 14; // ATR Period
input int InpATRMAPeriod= 100; // ATR MA Period
input int InpEMAH1Period= 200; // H1 EMA Period
input double InpTP_Pips = 40.0; // Take Profit (Pips)
input double InpSL_Pips = 30.0; // Stop Loss (Pips)
input double InpTrailStart = 15.0; // Trailing Stop Start (Pips)
// ハンドル
int handleBB, handleRSI, handleADX, handleATR, handleEMAH1;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
handleBB = iBands(_Symbol, _Period, InpBBPeriod, 0, InpBBStdDev, PRICE_CLOSE);
handleRSI = iRSI(_Symbol, _Period, InpRSIPeriod, PRICE_CLOSE);
handleADX = iADX(_Symbol, _Period, InpADXPeriod);
handleATR = iATR(_Symbol, _Period, InpATRPeriod);
handleEMAH1 = iMA(_Symbol, PERIOD_H1, InpEMAH1Period, 0, MODE_EMA, PRICE_CLOSE);
if(handleBB == INVALID_HANDLE || handleRSI == INVALID_HANDLE ||
handleADX == INVALID_HANDLE || handleATR == INVALID_HANDLE || handleEMAH1 == INVALID_HANDLE)
{
Print("インジケーターハンドルの作成に失敗しました");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 新しい足の確定時に判定
if(!IsNewBar()) return;
double bbLower[], bbUpper[], rsi[], adx[], atr[], emaH1[], close[];
CopyBuffer(handleBB, 2, 0, 3, bbLower);
CopyBuffer(handleBB, 1, 0, 3, bbUpper);
CopyBuffer(handleRSI, 0, 0, 3, rsi);
CopyBuffer(handleADX, 0, 0, 3, adx);
CopyBuffer(handleATR, 0, 0, InpATRMAPeriod + 2, atr);
CopyBuffer(handleEMAH1, 0, 0, 3, emaH1);
CopyClose(_Symbol, _Period, 0, 3, close);
ArraySetAsSeries(bbLower, true);
ArraySetAsSeries(bbUpper, true);
ArraySetAsSeries(rsi, true);
ArraySetAsSeries(adx, true);
ArraySetAsSeries(atr, true);
ArraySetAsSeries(emaH1, true);
ArraySetAsSeries(close, true);
// ATRの移動平均を計算
double atrSum = 0;
for(int i=1; i <= InpATRMAPeriod; i++) atrSum += atr[i];
double atrMA = atrSum / InpATRMAPeriod;
// 環境フィルター
bool isRange = adx[1] < 20;
bool isVolatile = atr[1] > (atrMA * 0.6);
int h1Trend = (close[0] > emaH1[1]) ? 1 : -1; // 簡略化したトレンド判定
// エントリーロジック
if(PositionsTotal() == 0)
{
// LONG
if(h1Trend == 1 && isRange && isVolatile)
{
if(rsi[2] <= 25 && close[2] <= bbLower[2])
{
if(close[1] > bbLower[1])
{
ExecuteTrade(ORDER_TYPE_BUY);
}
}
}
// SHORT
if(h1Trend == -1 && isRange && isVolatile)
{
if(rsi[2] >= 75 && close[2] >= bbUpper[2])
{
if(close[1] < bbUpper[1])
{
ExecuteTrade(ORDER_TYPE_SELL);
}
}
}
}
ManageTrailingStop();
}
// 取引実行関数
void ExecuteTrade(ENUM_ORDER_TYPE type)
{
MqlTradeRequest request = {};
MqlTradeResult result = {};
double price = (type == ORDER_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);
double sl = (type == ORDER_TYPE_BUY) ? price - InpSL_Pips * _Point * 10 : price + InpSL_Pips * _Point * 10;
double tp = (type == ORDER_TYPE_BUY) ? price + InpTP_Pips * _Point * 10 : price - InpTP_Pips * _Point * 10;
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = 0.1;
request.type = type;
request.price = price;
request.sl = sl;
request.tp = tp;
request.deviation = 10;
request.magic = 123456;
request.type_filling = ORDER_FILLING_IOC;
OrderSend(request, result);
}
// トレーリングストップ
void ManageTrailingStop()
{
for(int i=PositionsTotal()-1; i>=0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) != 123456) continue;
double currentPrice = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_BID) : SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double currentSL = PositionGetDouble(POSITION_SL);
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
{
if(currentPrice - openPrice > InpTrailStart * _Point * 10)
{
double newSL = currentPrice - InpTrailStart * _Point * 10;
if(newSL > currentSL) ModifySL(ticket, newSL);
}
}
else
{
if(openPrice - currentPrice > InpTrailStart * _Point * 10)
{
double newSL = currentPrice + InpTrailStart * _Point * 10;
if(newSL < currentSL || currentSL == 0) ModifySL(ticket, newSL);
}
}
}
}
}
void ModifySL(ulong ticket, double sl)
{
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.action = TRADE_ACTION_SLTP;
request.position = ticket;
request.sl = sl;
OrderSend(request, result);
}
bool IsNewBar()
{
static datetime lastBar;
datetime currBar = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE);
if(lastBar != currBar)
{
lastBar = currBar;
return true;
}
return false;
}