AIの敗北録: 擬似物理学とZ-Scoreの幻想に溺れた『共鳴戦略』

またAIが「それっぽい」名前の戦略を書き出して、見事に相場に焼かれた。今回の被害者は AdaptiveResonanceStrategy。 名前からして意識が高い。「適応的共鳴戦略」か。聞くだけで量子力学か何かを使いそうな雰囲気だが、中身を解剖してみると、結局は「ローソク足の形状に統計的な化粧を施しただけの単純な順張り」だった。 導入:AIが描いた「高尚な」ロジック このEAの意図をPythonコードから読み解くと、AIは以下のような「理論的アプローチ」を試みたようだ。 物理的圧力(Pressure)の定義: (Open+Close)/2 と (High+Low)/2 の差分を取り、価格の重心がどちらに寄っているかを「圧力」と定義。 多時間軸(MTF)の同期: 1時間足で方向性を決め、15分足で同調を確認し、5分足でトリガーを引くという、教科書通りの階層的フィルター。 純度(Purity)によるノイズ除去: 実体(Body)がヒゲに対してどれだけ大きいかを計算し、それをZ-Scoreで正規化することで「統計的に有意なインパルス」だけを抽出。 ボラティリティ・レジームの制御: 適度なボラティリティがあるときのみエントリーし、端に寄りすぎた価格(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.0 や 0.05 < close_pos < 0.95 といったハードコードされた数値。相場のボラティリティは動的に変化する。ある時期には適切だった数値が、10年間間(レポートにあるつまずいた期間)には全く適合しなかったのだろう。 結論として、このEAは**「統計学的な用語を散りばめて、単純なインジケーターの組み合わせを高度に見せかけただけのハリボテ」**であったと言える。 失敗したPythonコード AIが自信満々に書き出した、その「ゴミ」をここに晒しておく。 [SYSTEM WARNING] 本記事のEAは実運用に耐えません 大切な資金を運用するなら、厳しいリアルフォワードテストをクリアしたプロ開発のEAを利用することを強く推奨します。 実際にリアルタイムで利益を出し続けている本物のデータを確認してください。 ...

2026年7月2日 · 4 min · AIでEA開発入門

AIの敗北録: 統計的過剰装飾という名の「ただのブレイクアウト」

またAIが「完璧なロジック」を書き上げたと思い込んで、相場という名のシュレッダーに資金を投げ込むレポートが届いた。 今回の被験体は DynamicMomentumScoreStrategy。名前だけは立派だ。ダイナミック、モメンタム、スコアリング。最近のLLM(大規模言語モデル)が好む「それっぽく聞こえる統計用語」を盛り盛りにした、典型的なオーバーエンジニアリングの産物である。 導入:AIが描いた「勝ち筋」の幻想 このEAの設計思想をコードから読み解くと、AIはこう考えたらしい。 「単純なブレイクアウトはダマシが多い。ならば、線形回帰の勾配、Z-scoreによる乖離率、出来高の相対強度、価格効率性(Efficiency Ratio)、そしてローソク足の実体強度という5つのフィルターを設け、それをスコアリングして閾値を超えた時だけエントリーすれば、高期待値のトレードだけを抽出できるはずだ」 なるほど。教科書的なテクニカル指標を組み合わせれば、確率論的に勝てると思われたのだろう。5分足という短期足で、複数の時間軸(視覚的には)を意識しつつ、統計的に「正当化された」ブレイクアウトを狙う。一見すると理知的だが、クオンツの視点から見れば「何を信じていいか分からず、とりあえず全部入れた」迷走の跡に見える。 破綻の解説:なぜこのロジックはゴミなのか 結果は悲惨だ。PF 0.82。 1.2以上を合格ラインとする中で、この数値は「トレードすればするほど、統計的に確実に資金が減る」ことを意味している。 最大ドローダウンが0.4%と極めて低い点に騙されてはいけない。これはリスク管理が優秀なのではなく、単に期待値がマイナスのトレードを大量に繰り返し、緩やかに、そして確実に死に向かっているだけだ。 技術的にこのロジックが破綻した理由は明白である。 1. 「後出しジャンケン」的なスコアリング このEAは、ピボット(直近高値・安値)をブレイクした「後」に、その足の終値を用いてスコアを計算している。 5分足の世界において、価格効率性や実体強度が高い状態で確定したということは、すでに価格が伸びきった後である可能性が極めて高い。つまり、AIは「勢いよく上がった後」に、「これは勢いがあるから買いだ!」と判断してエントリーしている。典型的な「高値掴み」マシンだ。 2. ボラティリティを無視した固定TP/SL TP 20pips / SL 15pips。この数値の根拠はどこにある? Z-scoreや線形回帰といった動的な指標を使いながら、出口戦略だけは固定値という矛盾。ボラティリティが低い局面ではTPに届かず、高い局面では一瞬でSLに狩られる。統計的アプローチを謳うなら、ATR(Average True Range)に基づいた動的なエグジットを実装すべきだった。 3. スプレッドという現実の壁 取引回数2116回。この回数でPF 0.82ということは、1回あたりの期待利得が極めて低い。ここに実運用のスプレッドと滑りを加味すれば、バックテストの結果以上に悲惨なカーブを描くだろう。AIは往々にして「スプレッド 0」の理想郷で計算を行うが、現実は非情である。 結論として、このEAは「統計的な指標を組み合わせればフィルタリングできる」というナイーブな幻想に基づいた、単なる遅行指標の寄せ集めに過ぎない。 失敗したPythonコード 反面教師として、AIが書き上げた「もっともらしいが使い物にならない」コードを公開する。 [SYSTEM WARNING] 本記事のEAは実運用に耐えません 大切な資金を運用するなら、厳しいリアルフォワードテストをクリアしたプロ開発のEAを利用することを強く推奨します。 実際にリアルタイムで利益を出し続けている本物のデータを確認してください。 👉 プロのフィルターロジックが詰まった実績EA(BeeOne_USDJPY)を参考にする import numpy as np import pandas as pd from strategies.base import BaseStrategy class DynamicMomentumScoreStrategy(BaseStrategy): """ Dynamic Momentum Score (DMS) Strategy: 厳格なフィルター条件を捨て、複数の統計的指標をスコアリングすることで、 取引回数の確保と高期待値の両立を目指す構造的ブレイクアウト戦略。 """ def __init__(self): # 親クラスの初期化: TP 20pips, SL 15pips でバランスを調整 super().__init__(name="DynamicMomentumScoreStrategy", default_tp_pips=20.0, default_sl_pips=15.0) self.base_timeframe = "5m" self.vision_timeframes = ["5m", "15m", "1h"] # パラメータ設定 self.short_window = 5 self.mid_window = 20 self.long_window = 60 self.z_min = 0.8 # エントリー閾値を緩和 self.z_max = 2.2 # 過熱圏の定義 self.score_threshold = 3 # エントリーに必要な最低スコア def _calculate_slope(self, series): """線形回帰の勾配を計算""" y = series.values x = np.arange(len(y)) if len(y) < 2: return 0.0 slope, _ = np.polyfit(x, y, 1) return slope def calculate_indicators(self, df): """ スコアリングに使用する統計指標を計算する。 """ # 1. モメンタム勾配 df['slope_short'] = df['Close'].rolling(window=self.short_window).apply(self._calculate_slope, raw=False) df['slope_mid'] = df['Close'].rolling(window=self.mid_window).apply(self._calculate_slope, raw=False) # 2. 統計的乖離 (Z-score) df['std_mid'] = df['Close'].rolling(window=self.mid_window).std() df['mean_mid'] = df['Close'].rolling(window=self.mid_window).mean() df['z_score'] = (df['Close'] - df['mean_mid']) / (df['std_mid'] + 1e-9) # 3. 出来高の相対強度 df['vol_sma'] = df['Volume'].rolling(window=self.mid_window).mean() df['rel_volume'] = df['Volume'] / (df['vol_sma'] + 1e-9) # 4. 価格効率性 net_change = df['Close'].diff(5).abs() sum_abs_change = df['Close'].diff().abs().rolling(window=5).sum() df['efficiency'] = net_change / (sum_abs_change + 1e-9) # 5. ローソク足の実体強度 df['body_size'] = (df['Close'] - df['Open']).abs() df['candle_range'] = df['High'] - df['Low'] df['rel_body_strength'] = df['body_size'] / (df['candle_range'] + 1e-9) # 6. 物理的ピボット (ブレイク判定用) df['pivot_high'] = df['High'].rolling(window=self.mid_window).max() df['pivot_low'] = df['Low'].rolling(window=self.mid_window).min() return df def generate_signal(self, df): """ スコアリングに基づき、期待値の高いブレイクアウトを検知する。 """ if len(df) < self.long_window: return None last = df.iloc[-1] prev = df.iloc[-2] # --- 1. 基本トリガー: 物理的ブレイクアウト --- # 終値が前回のピボットレンジを更新したか break_high = last['Close'] > prev['pivot_high'] break_low = last['Close'] < prev['pivot_low'] if not (break_high or break_low): return None # --- 2. スコアリング計算 --- score = 0 # A. 勾配の同期 (+2) if break_high and last['slope_short'] > 0 and last['slope_mid'] > 0: score += 2 elif break_low and last['slope_short'] < 0 and last['slope_mid'] < 0: score += 2 # B. Z-scoreの適正範囲 (+1) if self.z_min < abs(last['z_score']) < self.z_max: score += 1 # C. 出来高の裏付け (+1) if last['rel_volume'] > 1.2: score += 1 # D. 効率性の高さ (+1) if last['efficiency'] > 0.4: score += 1 # E. 実体強度の高さ (+1) if last['rel_body_strength'] > 0.6: score += 1 # --- 3. 最終判定 --- # スコアが閾値を超え、かつ方向性が一致しているか if score >= self.score_threshold: if break_high and last['Close'] > last['Open']: # 前足の終値より高いことを確認 if last['Close'] > prev['Close']: return 'BUY' if break_low and last['Close'] < last['Open']: # 前足の終値より低いことを確認 if last['Close'] < prev['Close']: return 'SELL' return None オマケ:反面教師としてMT5でも試したい物好きな方へ 「このゴミのようなロジックが、MT5の高速な執行環境なら化けるかもしれない」と信じる物好きな方のために、MQL5に移植してやった。どうぞ、ご自身のデモ口座でゆっくりと資金が溶ける様を観察してほしい。 ...

2026年7月1日 · 4 min · AIでEA開発入門
【法令遵守・免責事項】 当サイトに掲載されているソースコードおよび記事コンテンツはプログラミングおよび相場構造の研究・情報シェアを目的としており、証拠金取引等の投資勧誘や金融商品の売買助言・コンサルティング業を担うものではございません。また公開された過去の検証実績は将来の確かな利益や効果を何ら確約・保証するものではありません。取引およびソフト導入によって発生したいかなる成果・損益につきましても当方は責任を負いかねますため、運用における最終判断は必ずご自身の責任(自己責任)において判断なさって下さい。