直近2年のインフレ相場は非常に困難でした。金利変動でトレンドが急変し、多くのEAが破綻しています。その中で生き残った優秀なロジックを公開します。

これが直近2年間を生き抜いた資産推移の証拠です。(※本ロジックは ユーロドルの5分足 専用です)
| 項目 | 実績値 |
|---|---|
| プロフィットファクター (PF) | 1.25 |
| 勝率 | 46.9% |
| 総取引数 | 113 回 |
| 純利益 (0.1ロット/1万通貨時) | $26428.83 |
| 最大ドローダウン | 1.82% |
なぜこのロジックは生き残れたのか
結論から述べます。相場環境を厳格に選別したからです。
理由は3点あります。1つ目はADXを用いたレンジ判定です。2つ目はアジア時間への限定です。3つ目は上位足の方向性一致です。
具体例を挙げます。1時間足が上昇中で、5分足がレンジのときだけ買いを狙います。強いトレンドに逆らわず、押し目での反発のみを拾う設計です。
結論として、無駄な取引を徹底的に省いたことが低いドローダウンに繋がりました。
勝ち続けることより、大きく負けない仕組み作りが重要です。
戦略の技術的詳細
| 項目 | 設定内容 | 役割 |
|---|---|---|
| メイン時間足 | 5分足 | エントリータイミングの判定 |
| 上位足フィルター | 1時間足 EMA200 | 全体のトレンド方向を決定 |
| レンジ判定 | ADX (14) < 25 | トレンドの欠如を確認 |
| 反転シグナル | RSI (14) 30/70 | 売られすぎ・買われすぎの判定 |
| 価格位置 | ボリンジャーバンド | バンド中央値との位置関係をチェック |
| 取引時間 | 0:00 〜 9:00 | レンジが発生しやすい時間帯に限定 |
このロジックの核心は「時間帯」と「ボラティリティ」の掛け合わせにあります。
公開コード (Python)
🔍 プロはどうやってダマシを回避しているのか?
今回のAIロジックも優秀ですが、長年ランキング上位に居続けるプロのEAは、さらに複雑な環境認識や、独自のボラティリティフィルターを何層も実装しています。
自作EAの成績が行き詰まった時は、実際にリアル口座で長期間利益を出し続けている「本物の市販EA」の挙動(エントリーと決済のタイミング)を観察することが、一番の勉強になります。
from strategies.base import BaseStrategy
import pandas_ta as ta
import pandas as pd
import numpy as np
class AMRFStrategy(BaseStrategy):
def __init__(self):
super().__init__(
name="AdaptiveMeanReversionFramework_Surgical_V5",
default_tp_pips=45.0, # 期待値向上のためTPを微調整
default_sl_pips=25.0, # Max DDを抑えつつ、勝率を確保するための適正値に設定
enable_trailing_stop=True,
trail_start_pips=15.0 # 利益確定を早めに行い、PFとリカバリーファクターを改善
)
self.base_timeframe = "5m"
self.vision_timeframes = ["5m", "15min", "1h"]
def calculate_indicators(self, df):
# --- Vectorized Indicator Calculation (5m) ---
# Bollinger Bands: std=2.0へ戻し、エントリー頻度を劇的に改善
bb = df.ta.bbands(length=20, std=2.0)
bb.columns = ['BBL', 'BBM', 'BBU', 'BBL_band', 'BBU_band']
df = pd.concat([df, bb], axis=1)
# RSI: 期間14。閾値を標準的な30/70へ緩和し、機会損失を防止
df.ta.rsi(length=14, append=True)
# ADX: 期間14。トレンド強度判定
df.ta.adx(length=14, append=True)
# ATR: 期間14。ボラティリティ測定
df.ta.atr(length=14, append=True)
# --- MTF Filter (1h EMA 200) ---
# 大方向のトレンドフィルター
df_1h_close = df['Close'].resample('1h').last().shift(1)
ema200_1h = ta.ema(df_1h_close, length=200)
df['ema200_1h'] = ema200_1h.reindex(df.index, method='ffill')
return df
def generate_signal(self, df):
if len(df) < 20:
return None
last = df.iloc[-1]
prev = df.iloc[-2]
# カラム名の確定
rsi_col = 'RSI_14'
adx_col = 'ADX_14'
# 1. Range Filter: ADX < 25 (厳格すぎたADX<20および下降条件を撤廃し、取引回数を確保)
is_range = last[adx_col] < 25
# 2. Time Filter: 0:00 - 9:00 (レンジ相場の優位性が高い時間帯)
is_time = 0 <= last.name.hour <= 9
# 3. HTF Trend Bias (1h EMA 200)
is_bull_bias = last['Close'] > last['ema200_1h']
is_bear_bias = last['Close'] < last['ema200_1h']
# --- Long Signal ---
# 条件:
# - HTF上昇トレンド中 + レンジ相場 + 指定時間帯
# - 価格がBB下限付近にあり、RSIが30を上抜ける反転シグナル
if is_range and is_time and is_bull_bias:
# 価格がBBLを割り込んでいるか、BBL付近にあり、RSIが30を上抜けた瞬間
if (last['Close'] < last['BBM']) and \
(prev[rsi_col] < 30) and (last[rsi_col] >= 30):
return 'BUY'
# --- Short Signal ---
# 条件:
# - HTF下降トレンド中 + レンジ相場 + 指定時間帯
# - 価格がBB上限付近にあり、RSIが70を下抜ける反転シグナル
if is_range and is_time and is_bear_bias:
# 価格がBBUを上回っているか、BBU付近にあり、RSIが70を下抜けた瞬間
if (last['Close'] > last['BBM']) and \
(prev[rsi_col] > 70) and (last[rsi_col] <= 70):
return 'SELL'
return None
MT5用コード (MQL5)
//+------------------------------------------------------------------+
//| AMRF_Surgical_V5.mq5 |
//| Copyright 2026, Developer |
//+------------------------------------------------------------------+
#property strict
input double InpTP = 45.0; // Take Profit (Pips)
input double InpSL = 25.0; // Stop Loss (Pips)
input double InpTrailStart = 15.0; // Trailing Start (Pips)
input int InpADXPeriod = 14; // ADX Period
input int InpRSIPeriod = 14; // RSI Period
input int InpBBPeriod = 20; // BB Period
input double InpBBStdDev = 2.0; // BB Standard Deviation
input int InpEMA_HTF = 200; // HTF EMA Period
int handleADX, handleRSI, handleBB, handleEMA_HTF;
int OnInit()
{
handleADX = iADX(_Symbol, _Period, InpADXPeriod);
handleRSI = iRSI(_Symbol, _Period, InpRSIPeriod, PRICE_CLOSE);
handleBB = iBands(_Symbol, _Period, InpBBPeriod, 0, InpBBStdDev, PRICE_CLOSE);
handleEMA_HTF = iMA(_Symbol, PERIOD_H1, InpEMA_HTF, 0, MODE_EMA, PRICE_CLOSE);
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
IndicatorRelease(handleADX);
IndicatorRelease(handleRSI);
IndicatorRelease(handleBB);
IndicatorRelease(handleEMA_HTF);
}
void OnTick()
{
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
if(dt.hour < 0 || dt.hour > 9) return;
double adx[], rsi[], bbUpper[], bbLower[], bbMain[], emaHTF[];
ArraySetAsSeries(adx, true);
ArraySetAsSeries(rsi, true);
ArraySetAsSeries(bbUpper, true);
ArraySetAsSeries(bbLower, true);
ArraySetAsSeries(bbMain, true);
ArraySetAsSeries(emaHTF, true);
if(CopyBuffer(handleADX, 0, 0, 2, adx) < 2) return;
if(CopyBuffer(handleRSI, 0, 0, 2, rsi) < 2) return;
if(CopyBuffer(handleBB, 1, 0, 2, bbUpper) < 2) return;
if(CopyBuffer(handleBB, 2, 0, 2, bbLower) < 2) return;
if(CopyBuffer(handleBB, 0, 0, 2, bbMain) < 2) return;
if(CopyBuffer(handleEMA_HTF, 0, 0, 2, emaHTF) < 2) return;
double close = iClose(_Symbol, _Period, 0);
bool isRange = adx[0] < 25;
bool isBullBias = close > emaHTF[0];
bool isBearBias = close < emaHTF[0];
if(PositionsTotal() == 0)
{
if(isRange && isBullBias && close < bbMain[0] && rsi[1] < 30 && rsi[0] >= 30)
{
ExecuteOrder(ORDER_TYPE_BUY);
}
else if(isRange && isBearBias && close > bbMain[0] && rsi[1] > 70 && rsi[0] <= 70)
{
ExecuteOrder(ORDER_TYPE_SELL);
}
}
ManageTrailingStop();
}
void ExecuteOrder(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 * _Point * 10 : price + InpSL * _Point * 10;
double tp = (type == ORDER_TYPE_BUY) ? price + InpTP * _Point * 10 : price - InpTP * _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))
{
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
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 newSL)
{
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.action = TRADE_ACTION_SLTP;
request.position = ticket;
request.sl = newSL;
OrderSend(request, result);
}