また出たな。AIが書いた「完璧に見えるゴミ」が。
今回の犠牲者は NYTrendScalper_v9。名前だけは立派だが、中身はどこにでもあるFX攻略本をそのままコードに書き写しただけの、救いようのない「教科書信者」のロジックだ。
このAIが何をしようとしたか。推測するまでもない。 「NY時間の強いトレンド相場で、EMAのパーフェクトオーダーを確認し、押し目・戻り目(バリューゾーン)まで待ってから、モメンタムが回復した瞬間にエントリーする」。
……ふん。笑わせるな。 そんなものは、10年前のネット掲示板に転がっていた「必勝法」だ。
破綻の理由:なぜこのロジックは「ゴミ」なのか
バックテストの結果を見てみろ。PF 0.94。 つまり、手数料とスプレッドを考慮せずとも、期待値がマイナスだということだ。
特筆すべきは、最大ドローダウン 0.1% という数値だ。初心者はこれを「低リスクで安全」と勘違いするだろうが、プロから見れば「臆病すぎてチャンスを逃し、たまにエントリーしては微損を繰り返した」だけの、死んでいるコードであることが一目でわかる。
このロジックが現実の相場で通用しない理由は、主に3つだ。
1. インジケーターの「遅行性」の積み上げ
EMA20 > 50 > 200というパーフェクトオーダーを確認し、さらにADXが25を超えるまで待つ。いいか、5分足でここまで条件を揃えたときには、トレンドの美味しい部分はすでに終わっている。 AIは「確実性」を求めてフィルターを重ねたが、結果として「トレンドの終焉」でエントリーする装置を作り上げたわけだ。
2. 「バリューゾーン」という幻想
EMA20から50の間を「反発ポイント」と定義しているが、相場にそんな固定された境界線があると思うな。 大口のトレーダーは、まさにその「教科書通りの反発ポイント」に溜まったストップロスを狩りに来る。このEAが「買い」を入れた瞬間、それは単なる「流動性の提供」となり、ヒゲで刈り取られる運命にある。
3. リスクリワードの不整合
TP 150pipsに対し、SL 45pips。一見、リスクリワード比はいい。だが、5分足のスキャルピングで150pipsを狙うなど、相場のボラティリティを完全に無視した設定だ。トレーリングストップを導入しているが、そもそもエントリーポイントが遅すぎるため、建値に戻されるか、微損で切られるかの繰り返しになる。
結論。インジケーターを組み合わせれば「正解」に辿り着けると思うな。相場は数式ではなく、人間の心理と資金のぶつかり合いだ。
敗北の記録(Pythonコード)
AIが自信満々に書き上げた、絶望のコードがこちらだ。
from strategies.base import BaseStrategy
import pandas_ta as ta
import pandas as pd
import numpy as np
class NYTrendScalper(BaseStrategy):
"""
NY Trend Scalper v9:
バリューゾーン・リバウンド戦略。
EMA20-50のゾーン反発とADX強トレンドを組み合わせ、低DDを維持しつつPFの最大化を追求する。
"""
def __init__(self):
# DD 0.2%という余裕を最大限に活用し、リスクリワード比を向上させる。
# TPを大幅に伸ばし、トレーリングストップでトレンドの終焉まで追随する。
super().__init__(
name="NYTrendScalper_v9",
default_tp_pips=150.0,
default_sl_pips=45.0,
enable_trailing_stop=True,
trail_start_pips=30.0
)
self.base_timeframe = "5m"
self.vision_timeframes = ["5m", "15m", "1h"]
def calculate_indicators(self, df):
# --- 5分足 (Base Timeframe) 指標 ---
# ADX: トレンド強度の判定 (14)
adx_df = df.ta.adx(length=14)
if adx_df is not None:
df['ADX'] = adx_df.iloc[:, 0]
# ATR: ボラティリティ (14)
df['ATR'] = df.ta.atr(length=14)
df['ATR_MA'] = df['ATR'].rolling(window=100).mean()
# MACD: モメンタム (12, 26, 9)
macd_df = df.ta.macd(fast=12, slow=26, signal=9)
if macd_df is not None:
df['MACD_hist'] = macd_df.iloc[:, 2]
# RSI: 強弱判定 (14)
df['RSI'] = df.ta.rsi(length=14)
# --- Trend Filters (MTF) ---
# EMA 20: 短期トレンド / EMA 50: 中期トレンド / EMA 200: 長期トレンド
df['EMA_20'] = df.ta.ema(length=20)
df['EMA_50'] = df.ta.ema(length=50)
df['EMA_200'] = df.ta.ema(length=200)
return df
def generate_signal(self, df):
if len(df) < 200:
return None
curr = df.iloc[-1]
prev = df.iloc[-2]
# 必須カラムチェック
required_cols = ['ADX', 'ATR', 'MACD_hist', 'RSI', 'EMA_20', 'EMA_50', 'EMA_200', 'ATR_MA']
if not all(col in df.columns for col in required_cols):
return None
# 【エントリー時間帯の限定】
# NY市場のメイントレンド時間帯 (21:00〜01:00 JST)
current_hour = df.index[-1].hour
if not (21 <= current_hour <= 23 or 0 <= current_hour <= 1):
return None
# 【相場環境フィルター】
# 1. トレンド強度の厳格化: ADX > 25 (強いトレンドのみを抽出)
if curr['ADX'] < 25:
return None
# 2. ボラティリティ: ATRが平均的に安定していること
if curr['ATR'] < curr['ATR_MA'] * 0.6:
return None
# 【MTFトレンド判定: パーフェクトオーダーの確認】
# 強気環境: 価格 > EMA 20 > EMA 50 > EMA 200
is_bullish_env = (curr['Close'] > curr['EMA_20']) and (curr['EMA_20'] > curr['EMA_50']) and (curr['EMA_50'] > curr['EMA_200'])
# 弱気環境: 価格 < EMA 20 < EMA 50 < EMA 200
is_bearish_env = (curr['Close'] < curr['EMA_20']) and (curr['EMA_20'] < curr['EMA_50']) and (curr['EMA_50'] < curr['EMA_200'])
# 【エントリートリガー: バリューゾーンからの反発】
# 買いシグナル:
if is_bullish_env:
# 1. バリューゾーン判定: 直近の安値がEMA 20〜EMA 50の間に位置していたか
in_value_zone = (prev['Low'] <= curr['EMA_20'] * 1.001) and (prev['Low'] >= curr['EMA_50'] * 0.999)
# 2. プライスアクション: 直近高値を更新する強い陽線
is_strong_bull = (curr['Close'] > curr['Open']) and (curr['Close'] > prev['High'])
# 3. モメンタム: RSIが50以上、かつMACDヒストグラムが上昇
momentum_ok = (curr['RSI'] > 50) and (curr['MACD_hist'] > prev['MACD_hist'])
if in_value_zone and is_strong_bull and momentum_ok:
return 'BUY'
# 売りシグナル:
if is_bearish_env:
# 1. バリューゾーン判定: 直近の高値がEMA 20〜EMA 50の間に位置していたか
in_value_zone = (prev['High'] >= curr['EMA_20'] * 0.999) and (prev['High'] <= curr['EMA_50'] * 1.001)
# 2. プライスアクション: 直近安値を更新する強い陰線
is_strong_bear = (curr['Close'] < curr['Open']) and (curr['Close'] < prev['Low'])
# 3. モメンタム: RSIが50以下、かつMACDヒストグラムが下降
momentum_ok = (curr['RSI'] < 50) and (curr['MACD_hist'] < prev['MACD_hist'])
if in_value_zone and is_strong_bear and momentum_ok:
return 'SELL'
return None
オマケ:反面教師用 MQL5コード
「この程度のロジックで本当に負けるのか?」と、まだ夢を見ている物好きな方へ。MT5でその絶望を体験するためのコードを書いておいた。
//+------------------------------------------------------------------+
//| NYTrendScalper_Fail.mq5 |
//| Copyright 2026, Legendary Trader|
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Legendary Trader"
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
// Inputs
input int InpEMA_Short = 20;
input int InpEMA_Medium = 50;
input int InpEMA_Long = 200;
input int InpADX_Period = 14;
input int InpRSI_Period = 14;
input double InpTP_Pips = 1500; // Points
input double InpSL_Pips = 450; // Points
// Handles
int hEMA20, hEMA50, hEMA200, hADX, hRSI, hMACD;
int OnInit() {
hEMA20 = iMA(_Symbol, _Period, InpEMA_Short, 0, MODE_EMA, PRICE_CLOSE);
hEMA50 = iMA(_Symbol, _Period, InpEMA_Medium, 0, MODE_EMA, PRICE_CLOSE);
hEMA200 = iMA(_Symbol, _Period, InpEMA_Long, 0, MODE_EMA, PRICE_CLOSE);
hADX = iADX(_Symbol, _Period, InpADX_Period);
hRSI = iRSI(_Symbol, _Period, InpRSI_Period, PRICE_CLOSE);
hMACD = iMACD(_Symbol, _Period, 12, 26, 9, PRICE_CLOSE);
return(INIT_SUCCEEDED);
}
void OnTick() {
MqlDateTime dt;
TimeCurrent(dt);
if(!(dt.hour >= 21 || dt.hour <= 1)) return;
double ema20[], ema50[], ema200[], adx[], rsi[], macd_main[], macd_sig[];
CopyBuffer(hEMA20, 0, 0, 2, ema20);
CopyBuffer(hEMA50, 0, 0, 2, ema50);
CopyBuffer(hEMA200, 0, 0, 2, ema200);
CopyBuffer(hADX, 0, 0, 2, adx);
CopyBuffer(hRSI, 0, 0, 2, rsi);
CopyBuffer(hMACD, 0, 0, 2, macd_main);
CopyBuffer(hMACD, 1, 0, 2, macd_sig);
MqlRates rates[];
CopyRates(_Symbol, _Period, 0, 2, rates);
bool bullish_env = (rates[1].close > ema20[1]) && (ema20[1] > ema50[1]) && (ema50[1] > ema200[1]);
bool bearish_env = (rates[1].close < ema20[1]) && (ema20[1] < ema50[1]) && (ema50[1] < ema200[1]);
if(adx[1] < 25) return;
if(bullish_env) {
bool in_zone = (rates[0].low <= ema20[1] * 1.001) && (rates[0].low >= ema50[1] * 0.999);
bool strong_bull = (rates[1].close > rates[1].open) && (rates[1].close > rates[0].high);
bool momentum = (rsi[1] > 50) && ((macd_main[1]-macd_sig[1]) > (macd_main[0]-macd_sig[0]));
if(in_zone && strong_bull && momentum) {
// Execute BUY
}
}
if(bearish_env) {
bool in_zone = (rates[0].high >= ema20[1] * 0.999) && (rates[0].high <= ema50[1] * 1.001);
bool strong_bear = (rates[1].close < rates[1].open) && (rates[1].close < rates[0].low);
bool momentum = (rsi[1] < 50) && ((macd_main[1]-macd_sig[1]) < (macd_main[0]-macd_sig[0]));
if(in_zone && strong_bear && momentum) {
// Execute SELL
}
}
}