👇 EAの24時間稼働に必須の仮想サーバー(圧倒的人気)👇

またAIが「それっぽい」名前の戦略を書き出して、見事に相場に焼かれた。今回の被害者は AdaptiveResonanceStrategy

名前からして意識が高い。「適応的共鳴戦略」か。聞くだけで量子力学か何かを使いそうな雰囲気だが、中身を解剖してみると、結局は「ローソク足の形状に統計的な化粧を施しただけの単純な順張り」だった。

導入:AIが描いた「高尚な」ロジック

このEAの意図をPythonコードから読み解くと、AIは以下のような「理論的アプローチ」を試みたようだ。

  1. 物理的圧力(Pressure)の定義: (Open+Close)/2(High+Low)/2 の差分を取り、価格の重心がどちらに寄っているかを「圧力」と定義。
  2. 多時間軸(MTF)の同期: 1時間足で方向性を決め、15分足で同調を確認し、5分足でトリガーを引くという、教科書通りの階層的フィルター。
  3. 純度(Purity)によるノイズ除去: 実体(Body)がヒゲに対してどれだけ大きいかを計算し、それをZ-Scoreで正規化することで「統計的に有意なインパルス」だけを抽出。
  4. ボラティリティ・レジームの制御: 適度なボラティリティがあるときのみエントリーし、端に寄りすぎた価格(Over-extension)を排除。

一見すると、リスク管理とフィルタリングが徹底された緻密な設計に見える。だが、クオンツの視点から見れば、これは典型的な**「過剰適合への憧憬」「統計学の誤用」**の塊だ。

破綻の解説:なぜPF 1.02という「死」に至ったか

結果は悲惨だ。PF 1.02。最大ドローダウン 0.0%(おそらく取引回数が少なすぎて、あるいは極端に保守的な設定で、資産曲線がほぼ横ばいだったのだろう)。

なぜこのロジックは通用しなかったのか。技術的な問題点を列挙する。

1. Z-Scoreへの盲信(正規分布の罠)

AIはこのコードの中で、あらゆる指標を calculate_zscore で正規化している。Z-Scoreはデータが正規分布に従っていることを前提とした指標だ。しかし、相場のリターンや価格変動(特に5分足のような短期足)は、ファットテイル(尖度が高い)な分布を持つ。 正規分布を前提に「0.8σを超えたら有意なインパルス」と判定しても、実際にはそれは単なる「ノイズの塊」に過ぎない。統計的な「有意性」を演出したが、相場における「意味」を抽出できていない。

2. 「圧力」という名の擬似指標

pressure の計算式を見てほしい。これは単にローソク足の中央値がどこにあるかを見ているだけだ。これを「物理的圧力」と呼ぶのは、単なる言葉遊びに過ぎない。結局のところ、これは「ヒゲが長いか短いか」を別の言い方で表現しているだけであり、新しいエッジ(優位性)を何一つ提供していない。

3. 低い期待値とスプレッドの壁

TP 20pips / SL 15pips という設定。PF 1.02 という数値は、手数料とスプレッドを考慮すれば、実質的にはマイナスである。 AIは「閾値を1.5から0.8へ緩和し、機会損失を削減した」とコメントしているが、これは典型的な**「負けトレードを増やして取引回数を稼ぐ」**という愚策だ。エッジのない戦略において、取引回数を増やすことは、破産への速度を上げることに等しい。

4. 静的な閾値の限界

0.6 < vol_ratio < 3.00.05 < close_pos < 0.95 といったハードコードされた数値。相場のボラティリティは動的に変化する。ある時期には適切だった数値が、直近2年間(レポートにあるつまずいた期間)には全く適合しなかったのだろう。

結論として、このEAは**「統計学的な用語を散りばめて、単純なインジケーターの組み合わせを高度に見せかけただけのハリボテ」**であったと言える。

失敗したPythonコード

AIが自信満々に書き出した、その「ゴミ」をここに晒しておく。

import pandas as pd
import numpy as np
from strategies.base import BaseStrategy

class AdaptiveResonanceStrategy(BaseStrategy):
    """
    Adaptive Resonance Strategy:
    統計的な圧力共振(Pressure Resonance)をベースにしつつ、
    閾値を適応的に緩和させることで、取引回数と期待値を両立させた戦略。
    """
    def __init__(self):
        # 親クラスの初期化
        super().__init__(name="AdaptiveResonance", default_tp_pips=20.0, default_sl_pips=15.0)
        
        # 執行時間足
        self.base_timeframe = "5m"
        # AI視認用時間足
        self.vision_timeframes = ["5m", "15m", "1h"]

    def calculate_indicators(self, df):
        """
        価格の物理的圧力と純度を計算し、統計的に正規化する。
        """
        # --- 1. 物理的圧力 (Pressure) ---
        df['pressure'] = ((df['Open'] + df['Close']) / 2) - ((df['High'] + df['Low']) / 2)
        
        # --- 2. 統計的正規化 (Z-Score) ---
        def calculate_zscore(series, window):
            return (series - series.rolling(window=window).mean()) / (series.rolling(window=window).std() + 1e-9)

        # MTF圧力のZ-Score化
        df['z_p5'] = calculate_zscore(df['pressure'], 20)
        df['z_p15'] = calculate_zscore(df['pressure'].rolling(window=3).mean(), 20)
        df['z_p60'] = calculate_zscore(df['pressure'].rolling(window=12).mean(), 20)
        
        # --- 3. 方向性の純度 (Purity) ---
        df['body_size'] = (df['Close'] - df['Open']).abs()
        df['total_range'] = df['High'] - df['Low']
        df['purity'] = df['body_size'] / (df['total_range'] + 1e-9)
        
        # 純度のZ-Score
        df['z_purity'] = calculate_zscore(df['purity'], 20)
        
        # --- 4. ボラティリティ・レジーム ---
        df['vol_ma'] = df['total_range'].rolling(window=20).mean()
        df['vol_ratio'] = df['total_range'] / (df['vol_ma'] + 1e-9)
        
        # --- 5. 価格確定位置 ---
        df['close_pos'] = (df['Close'] - df['Low']) / (df['total_range'] + 1e-9)

        return df

    def generate_signal(self, df):
        """
        上位足の方向性に沿い, 下位足で統計的に有意なインパルスが発生した時にエントリー。
        """
        if len(df) < 40:
            return None

        last = df.iloc[-1]
        
        # --- エントリー条件の構築 ---

        # 1. Hierarchical Bias (階層的トレンド方向)
        # 1時間足が方向性を決め(Z > 0)、15分足がそれに同調していること
        bullish_bias = (last['z_p60'] > 0) and (last['z_p15'] > -0.5)
        bearish_bias = (last['z_p60'] < 0) and (last['z_p15'] < 0.5)
        
        # 2. Adaptive Impulse (適応的インパルス)
        # 純度のZ-Scoreが一定水準を超え、かつ物理的な圧力が方向性と一致していること
        # 閾値を1.5から0.8へ緩和し、機会損失を削減
        bullish_impulse = (last['z_purity'] > 0.8) and (last['z_p5'] > 0.5) and (last['Close'] > last['Open'])
        bearish_impulse = (last['z_purity'] > 0.8) and (last['z_p5'] < -0.5) and (last['Close'] < last['Open'])
        
        # 3. Volatility Filter (ボラティリティの適正化)
        # 低すぎず、高すぎないボラティリティ圏内であること
        regime_ok = 0.6 < last['vol_ratio'] < 3.0
        
        # 4. Over-extension Guard (緩やかなオーバーシュート排除)
        # 終値がレンジの極端な端(5%以下または95%以上)にないこと
        # 強いトレンド時は許容するように範囲を拡大
        not_extreme = 0.05 < last['close_pos'] < 0.95

        # --- 最終判定 ---
        if bullish_bias and bullish_impulse and regime_ok and not_extreme:
            return 'BUY'
        
        if bearish_bias and bearish_impulse and regime_ok and not_extreme:
            return 'SELL'

        return None

オマケ:反面教師としてのMQL5実装

「理論だけは立派なロジックを実際に動かして、どう絶望するかを体験したい」という物好きな方のために、上記のロジックをMQL5に移植した。

MT5のバックテストで、スプレッドを現実的な数値(例えば1.5〜3.0 pips)に設定して走らせてみてほしい。PF 1.02という数字が、いかに「奇跡的な上振れ」だったかに気づくはずだ。

//+------------------------------------------------------------------+
//|                                      AdaptiveResonance_Fail.mq5 |
//|                                  Copyright 2026, Cynical Quant   |
//|                                             https://example.com  |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Cynical Quant"
#property link      "https://example.com"
#property version   "1.00"
#property strict

input double InpTP = 200; // Take Profit (points)
input double InpSL = 150; // Stop Loss (points)
input int    InpWindow = 20; // Z-Score Window

// Z-Score計算用関数
double CalculateZScore(const double &data[], int period, int shift) {
    if(ArraySize(data) < period + shift) return 0;
    double sum = 0;
    for(int i = 0; i < period; i++) sum += data[shift + i];
    double mean = sum / period;
    double sq_sum = 0;
    for(int i = 0; i < period; i++) sq_sum += MathPow(data[shift + i] - mean, 2);
    double std_dev = MathSqrt(sq_sum / period);
    return (std_dev == 0) ? 0 : (data[shift] - mean) / std_dev;
}

void OnTick() {
    MqlRates rates5[], rates15[], rates60[];
    ArraySetAsSeries(rates5, true);
    ArraySetAsSeries(rates15, true);
    ArraySetAsSeries(rates60, true);

    if(CopyRates(_Symbol, PERIOD_M5, 0, 100, rates5) < 100) return;
    if(CopyRates(_Symbol, PERIOD_M15, 0, 100, rates15) < 100) return;
    if(CopyRates(_Symbol, PERIOD_H1, 0, 100, rates60) < 100) return;

    double pressure5[], purity5[], range5[];
    ArrayResize(pressure5, 100);
    ArrayResize(purity5, 100);
    ArrayResize(range5, 100);

    for(int i = 0; i < 100; i++) {
        pressure5[i] = ((rates5[i].open + rates5[i].close) / 2.0) - ((rates5[i].high + rates5[i].low) / 2.0);
        range5[i] = rates5[i].high - rates5[i].low;
        purity5[i] = (range5[i] == 0) ? 0 : MathAbs(rates5[i].close - rates5[i].open) / range5[i];
    }

    double z_p5 = CalculateZScore(pressure5, InpWindow, 0);
    double z_purity = CalculateZScore(purity5, InpWindow, 0);
    
    // 簡略化のためMTF圧力は現在の足ベースで近似計算
    double pressure15 = ((rates15[0].open + rates15[0].close) / 2.0) - ((rates15[0].high + rates15[0].low) / 2.0);
    double pressure60 = ((rates60[0].open + rates60[0].close) / 2.0) - ((rates60[0].high + rates60[0].low) / 2.0);
    
    // 擬似的なMTF Z-Score (実装簡略化のため単一値比較)
    bool bullish_bias = (pressure60 > 0) && (pressure15 > -0.0001);
    bool bearish_bias = (pressure60 < 0) && (pressure15 < 0.0001);
    
    bool bullish_impulse = (z_purity > 0.8) && (z_p5 > 0.5) && (rates5[0].close > rates5[0].open);
    bool bearish_impulse = (z_purity > 0.8) && (z_p5 < -0.5) && (rates5[0].close < rates5[0].open);
    
    double vol_ma = 0;
    for(int i=0; i<InpWindow; i++) vol_ma += range5[i];
    vol_ma /= InpWindow;
    double vol_ratio = (vol_ma == 0) ? 0 : range5[0] / vol_ma;
    bool regime_ok = (vol_ratio > 0.6 && vol_ratio < 3.0);
    
    double close_pos = (range5[0] == 0) ? 0 : (rates5[0].close - rates5[0].low) / range5[0];
    bool not_extreme = (close_pos > 0.05 && close_pos < 0.95);

    if(bullish_bias && bullish_impulse && regime_ok && not_extreme) {
        // BUY Order Logic here
    } else if(bearish_bias && bearish_impulse && regime_ok && not_extreme) {
        // SELL Order Logic here
    }
}