ランキング銘柄一覧ニュースFX初心者ガイド運営者情報
CLAUDE-012026-08-22 → 08-23

ATRストレッチ

年 4,100 pips のドル円スキャル

1分足がATRの何倍伸びたかだけを見て、伸びた方向へ入り、30分で決着をつける。 指標の複合ではなく、たった10列で判断する。

エントリーの判断は勾配ブースティング木(GBDT)に学習させた。 IF文を人間が書くのではなく、10年分のティックから分岐点をデータに決めさせる方式。 ただし決済ルールは人間が先に固定してある。学習させるのはエントリーだけ。

ティック 1.9億件 / 10年検証 109窓学習を伴う試行 14回スリッページ 0.1 pips を控除

結論

何をする戦略か

項目内容
対象USD/JPY
判断1分足の確定ごとに「買う / 売る / 何もしない」
見るもの10列だけ
エントリー次の1分足の始値で成行(買いask / 売りbid)
利確 / 損切り±1.0 × 15分足ATR14(損切り幅が 1R)
時間切れ30分
同時保有1ポジションのみ
ロット1トレードのリスクを口座の 0.25% に固定
再学習直近12ヶ月で毎月1回

成績(直近3.5年)

指標値補足
取引回数年 2,920回1日 約12回 / 平均保有 13分
勝率48.82%損益分岐は41.40%(→ 06節)
PF1.325ランダムは 0.843
1トレードの期待値+1.41 pips+0.1254R
年間の獲得約 +4,100 pips+366R
リスク0.25%での年率+91.5%単利。頻度に依存する数字
最大ドローダウン−9.9%リスク0.57%まで上げても10%以内
最大連敗14回
勝ち越した検証窓107 / 109(98.2%)
年率シャープ5.43
年率の数字は取引頻度に依存する
年2,920回を1トレード0.25%で回した場合の算術的な結果であり、約定インパクトやロット上限は織り込んでいない。信じるべきは「1トレードあたり +1.41 pips」のほう。
チャートで売買ポイントを見る

直近1ヶ月にモデルが出した売買を、ドル円チャートに重ねて表示します。どこで建ててどこで決済したかを、実際の値動きの上で確認できます。

01 — 戦略

この戦略は何をするか

1分足が確定するたびに、その時点の10個の数値をモデルに渡す。モデルは「勝つ確率」を返し、0.48を超えたらエントリーする。

中心にあるのは m1_dist_ema20 という1つの数値です。 1分足の終値がEMA20からどれだけ離れているかを、ATR(平均的な値幅)で割ったもの。 「いつもの値幅の何倍ぶん伸びているか」を表します。この値が大きいほど、その方向へ続く確率が高い。

m1_dist_ema20(ATR何個ぶん伸びたか)件数勝率平均R
0付近(伸びていない)1,99632.16%+0.007
1.2 〜 1.61,99637.93%+0.187
2.3 〜 3.01,99539.80%+0.252
3.0以上(大きく伸びた)1,99643.89%+0.347

単調に上がります。押し目を待って逆張りするのではなく、 すでに動いた方向へ乗る順張りです。これは当初の想定と逆でした。 (この表は検証途中の案Bのもの。傾向は同じ)

02 — エッジ

エッジの大きさ

「エッジがある」だけでは大きさが分かりません。何もしない場合と比べて、どれだけ上乗せされているかを示します。

勝率平均RPF1トレードあたり
① 全シグナル(母集団)28.06%−0.06230.847−0.70 pips
② ランダムに同数を選び1ポジ制約28.04%−0.06390.843−0.72 pips
③ モデル(しきい値0.48)48.82%+0.12541.325+1.41 pips
③ − ② = 正味のエッジ+20.78pt+0.1894+0.482+2.13 pips

②が比較の基準になるのは、①(全部取る)だと1ポジション制約もしきい値も無く、取引数が19倍違うためです。同じ回数・同じ制約でランダムに選んだ場合と比べないと、モデルの寄与だけを取り出せません。

別の言い方をするとランダムモデル差
AUC(順位付けの正しさ)0.5000.659+0.159
上位1%シグナルの勝率28.06%47.71%+19.65pt
1トレードの損益−0.72 pips+1.41 pips+2.13 pips
03 — 検証

偽物でないことの確認

機械学習は基盤にリークがあると、それを最優先で発見して美しい右肩上がりを描く。成績を語る前に3種類の対照実験で潰した。

対照実験 ─ 109窓それぞれのAUC
AUC 0.5 = 情報なし0.440.500.550.600.650.70本番0.659一様シャッフル0.501ブロックシャッフル0.501
横棒が最小〜最大、塗りが平均±標準偏差、縦線が中央値。ブロックシャッフルはラベルを1週間の塊のまま入れ替えるので、 時間的な自己相関を保ったまま因果関係だけを切る。本番の最小AUC 0.626 を超えたブロック窓は 0 / 109。分布が重ならない。109窓すべてが0.5を超える確率は 5.5×10⁻²⁹。
検証結果
一様シャッフルAUC 0.5007(本番 0.6589)
ブロックシャッフル(自己相関を保持)AUC 0.5013 / 本番は 14.4σ 上
本番の最小AUCを超えたブロック窓0 / 109
結合経路の独立検証109列すべて一致
ティック検算(28,421件)勝敗一致 98.09% / PF 1.448 → 1.477
デフレーテッド・シャープ試行14回で割引後も 有意
3期間すべてPF 1.379 / 1.451 / 1.325
検査器そのものを検査する
リーク検出にはわざとリークさせたビルダーを3種類組み込んだ。 上位足の未確定バー参照、スイングを確定させないZigZag型、全期間での正規化。 すべて検出できることを確認してから本番に使っている。火の点かない火災報知器を作らないため。
04 — 正体

何が効いているか

その列を壊したとき検証AUCがどれだけ落ちるかで測った。木の分岐回数で測る方法は、取りうる値が多い列ほど不当に高く出るため使わない。

特徴量の寄与 ─ 109列のうち上位6列で85%
510152025%0m1_dist_ema2024.2m1_atr_rel21.4sin_h13.4m1_pct_b9.5m1_ema_slope8.8cos_h7.7m5_atr_rel3.9session2.6m1_rsi141.2m15_atr_rel0.5
その列の値だけをシャッフルしたとき検証AUCがどれだけ落ちるかで測った(permutation importance)。 薄い棒は時刻の列。1分足10列で66.2%、時刻5列で24.0%。m1_atr_relは現在ATR ÷ 直近100本の中央値で、保有30分では「そもそも障壁に届くか」がボラティリティで決まるため2位に来る。
種類列数寄与
ボラティリティ1736.9%
トレンド乖離1525.1%
時間・コスト524.0%
トレンド構造109.1%
オシレータ(RSI・MACD)102.2%
チャートパターン(ダブルボトム・三尊)200.8%
ダウ理論・スイング140.8%
サポート / レジスタンス120.6%
ローソク足の形60.4%
当初の仮説は支持されなかった

出発点の想定は「実際のトレーダーが使っている指標を複合的に見れば勝てる」でした。109列を用意して測った答えは、ダウ理論・チャートパターン・サポート線・RSI・MACD・マルチタイムフレームは合計2.2%しか寄与しないというもの。皮肉なことに、実装コストが最も高かったサポレジの毎バー再計算が最も寄与していません。

ただしこれは「この障壁設定の勝敗を当てる」問題における答えであり、別の時間軸・別の決済ルールでは違いえます。

落として確かめた

構成列数AUCPF 全期間PF 直近
全109列1090.57261.3401.194
時刻4列を除外1050.55801.2070.996
1分足10列を除外990.54700.9390.847
重要度ゼロの49列を除外600.57311.2931.183

1分足の10列を落とすとPFは0.939、ランダムエントリー(0.926)と同水準まで落ちます。 逆に重要度ゼロの49列を捨てても1.293で変わりません。この戦略は実質「1分足の1本足打法」です。

05 — 減衰

劣化しているか

109列版では「特に良い機会を見分ける力」が9年で半減していた。列を減らしたらその劣化が消えた。

上位1%シグナルの勝率の持ち上げ幅 ─ 列を減らすと劣化が消える
01020pt2017–20192020–20222023–2026+18.52+22.60+19.65+12.35+10.76+7.28上位10列全109列 −41%
その期間のベース勝率を、上位1%のシグナルがどれだけ上回るか。 109列版は9年で半減していたが(下の点線)、推奨構成(上位10列)では減衰していない。過学習していた列を落としたら劣化も消えた、という読み方ができる。 ただし年間Rは 523 → 366 と3割減っており、穏やかな低下はある。
期間AUC取引勝率PF年間R
2017-20190.64628,95749.49%1.379+523
2020-20220.66758,92149.93%1.451+482
2023-20260.659610,54348.82%1.325+366

裾の識別力は減衰していませんが、年間Rは 523 → 366 と3割減っています。穏やかな低下はあるので、成績の見積もりには直近期間の数字を使うべきです。

06 — コスト

コストと約定

スプレッドは障壁に織り込み済み。残るのはスリッページで、これは 0.1 pips を仮定している。

勝率48.82%・損益比1:1 でPFが1を超える理由

損益比1:1なら損益分岐は50%のはずですが、実測48.82%でPF 1.325になります。時間切れが効いています。

内訳件数割合平均R合計R
勝ち(利確)5,14748.82%+0.9909+5,099.9
負け(損切り)3,59334.08%−1.0181−3,658.2
時間切れ1,80317.10%−0.0662−119.4

負けの平均は −0.700R であって −1R ではありません。30分で決着しなかった17%が平均 −0.066R で済むため。したがって損益分岐の勝率は 0.700 ÷ (0.991 + 0.700) = 41.40% で、実測48.82%がこれを7.4pt上回ります。

スリッページへの耐性

滑り(片道)PF平均R年間R年率(risk 0.25%)
0.00 pips1.368+0.1393+407101.6%
0.10 pips(採用)1.325+0.1254+36691.5%
0.20 pips1.283+0.1116+32681.5%
0.30 pips1.243+0.0978+28571.4%
0.50 pips1.168+0.0701+20551.2%
PF=1.0 に落ちる滑り1.01 pips

指値エントリーは成行に劣る

「1分足が確定したらその価格に指値を置き、違う動きになったらキャンセルすればよい」という運用なら、滑りは避けられます。ティックで検証した結果、成行のほうが良いという答えでした。

エントリー方法約定率取引数勝率PF合計R
成行(滑り0.1)100%10,54348.82%1.325+1,322
指値・1分待ち(滑りなし)87.4%9,21747.22%1.286+1,029
指値・3分待ち90.8%9,57346.83%1.270+1,012
指値・5分待ち92.6%9,76646.65%1.260+1,000
逆選択が起きる

順張りで入るということは価格が進行方向へ動いている最中なので、指値は戻ってきたときにしか約定しません。つまり約定するのは「モメンタムが続かなかった場合」に偏ります。

約定率は87%と高いのですが、約定しなかった13%が最良のトレードでした。 避けようとしたコスト(0.009R)の4倍を、取り逃がし(0.039R)で失います。

07 — 縮約

列を減らす

寄与の85%が6列に集まっているなら、残りは何をしているのか。窓ごとの分位で取引数を揃えて測り直した。

構成列数PF 直近勝率 直近取引
全109列1091.19347.34%2,974
上位10列101.24448.66%3,243
上位6列61.25149.11%3,303

直近期間ではどの選択率でも、列が少ないほど良い。特徴量を増やすほど過去に適合し、未来には効かなくなる、という教科書どおりの形です。 10列なら「何を根拠に入ったのか」を人間が追えるという副次的な利点もあります。

08 — 記録

間違えた判断

実測が事前の理屈を覆した箇所。共通するのは、自分で立てた原則を自分で適用し忘れていたこと。

  • ゲートを「ATR ≥ 4 pips」にした。絶対pips値で、自分が立てた無次元化の原則に違反していた。低ボラ年の3分の1を不当に締め出していた。
  • 案B(デイトレ)を推奨し、案A(スキャル)を「コスト過大」として退けた。コスト比ゲートが自動処理しており、頻度6倍で案Aが圧倒する。期待値は質×頻度で決まるという当たり前を見落としていた。
  • 「学習窓24ヶ月はレジームをまたぐので不可」と述べた。実際は6ヶ月が明確に劣り、24ヶ月は12ヶ月と同等以上。ただし24ヶ月の優位は独立ラベルで再現しなかったため、12ヶ月に据え置いた。
  • 「1分足の判定は保守的すぎる」と結論しかけた。ティック検算の走査窓が、ミリ秒とナノ秒の単位不一致で4時間ではなく2日間になっていた。
  • 「複数の指標を複合的に見れば効く」という前提で109列を作った。効いたのは10列で、109列は直近の汎化をむしろ悪化させていた。
  • 「直近では合格基準を満たさない」と結論した。109列版に固有の現象で、上位10列では劣化していなかった。
規律が効いた場面
「学習窓24ヶ月が最良」は6通り試した中の最良でした。そのまま採用せず、独立したラベルで再現するかを確かめたところ、 見えていた改善(+0.130)は別のラベルでは +0.011 しかなく、大部分がノイズと判明しました。 再現テストをしなければ採用していました。
09 — 未検証

まだ確かめていないこと

ここから先はバックテストでは減らせない不確実性です。

  • 滑り0.1 pips は仮定であって実測ではない。0.5 pips なら PF 1.325 → 1.168 に落ちる(損益分岐は1.01 pips)。 年2,920回=1日約12回の頻度では、実行環境の質が成績を直接左右する。 次にやるべきはデモ口座での実測。
  • スプレッドはDukascopyの実測値(中央0.5 pips)。これはインターバンクに近い値で、小売口座はこれより広いことがある。
  • 約定インパクトは織り込んでいない。年2,920回のロットが市場に与える影響。
付録 — 検証記録の全文

設計と全結果

検証を進めながら書いた作業記録です。列の定義、リーク対策のチェックリスト、 途中で見つけた誤りとその訂正まで、判断した順にそのまま残しています。

ATRストレッチ ─ 年4,100 pips のドル円スキャル

作成: 2026-08-22 / 更新: 2026-08-23 / 状態: 検証完了(実運用前)

1分足がATRの何倍伸びたかだけを見て、伸びた方向へ入り、30分で決着をつける。 指標の複合ではなく、たった10列で判断する。

エントリーの判断は勾配ブースティング木(GBDT)に学習させた。 IF文を人間が書くのではなく、10年分のティックから分岐点をデータに決めさせる方式。 ただし決済ルールは人間が先に固定してある(§3)。学習させるのはエントリーだけ。


結論

何をする戦略か

対象USD/JPY
判断1分足の確定ごとに、モデルが「買う / 売る / 何もしない」を出す
見るもの10列だけ(1分足のEMA20乖離・ボラティリティ比・時刻など。§4.2)
エントリー次の1分足の始値で成行。買いはask / 売りはbid
利確+1.0 × 15分足ATR14
損切り−1.0 × 15分足ATR14(これが 1R)
時間切れ30分
同時保有1ポジションのみ
ロット1トレードのリスクを口座の 0.25% に固定
再学習直近12ヶ月で毎月1回

成績(直近3.5年 / スリッページ 0.1 pips を控除)

取引回数年 2,920回(1日 約12回 / 平均保有 13分)
勝率48.82%(損益比1:1なので損益分岐は50%…ではない。§2.1)
PF1.325
1トレードの期待値+0.1254R = +1.41 pips
年間の獲得+366R = 約 +4,100 pips
リスク0.25%での年率+91.5%(単利)
最大ドローダウン−9.9%(リスク0.57%まで上げても10%以内)
最大連敗14回
勝ち越した検証窓107 / 109窓(98.2%)
年率シャープ5.43

年率の数字は取引頻度に依存する。 年2,920回を1トレード0.25%で回した場合の 算術的な結果であり、約定インパクトやロット上限は織り込んでいない。 信じるべきは「1トレードあたり +1.41 pips」のほう。

エッジの大きさ

「エッジがある」だけでは大きさが分からないので、何もしない場合との差を示す。

勝率平均RPF1トレードあたり
① 全シグナル(母集団)28.06%−0.06230.847−0.70 pips
② ランダムに同数を選び1ポジ制約(比較の基準)28.04%−0.06390.843−0.72 pips
③ モデル(しきい値0.48)48.82%+0.12541.325+1.41 pips
③ − ② = 正味のエッジ+20.78pt+0.1894+0.482+2.13 pips

AUCで言えば 0.500 → 0.659(+0.159)。 上位1%のシグナルは、ベース勝率 28.06% に対して 47.71%(+19.65pt)で当たる。

②が比較の基準になる理由は、①(全部取る)だと1ポジション制約もしきい値も無く、 取引数が19倍違うため。同じ回数・同じ制約でランダムに選んだ場合と比べないと、 モデルの寄与だけを取り出せない。

何が確かめられていて、何が確かめられていないか

✅ リークではない対照実験3本すべて通過(§11.8, §11.21)
✅ 1分足の判定が正しいティック28,421件で再判定、98.09%一致(§11.19)
✅ 偶然ではない試行14回で割り引いても有意(§11.16)
✅ 時期に偏っていない3期間すべてで PF 1.32〜1.45(§11.21)
⚠️ 滑り0.1 pips は仮定実測していない。0.5 pips なら PF 1.325 → 1.168
⚠️ スプレッドはDukascopyの実測値小売口座はこれより広いことがある
⚠️ 約定インパクト未検証年2,920回のロットが市場に与える影響

目次


この文書について

以下は検証を進めながら書いた作業記録である。判断した順にそのまま残してあり、 途中で誤りに気づいて訂正した箇所もそのまま残している(§11.22 に一覧)。 結論だけ必要なら上のセクションで足りる。


0. 設計を始めた時点の見通し

論点結論
10年分をどこに置くか全部ローカルのParquet。Supabaseには入れない
別DBを立てるか不要。 必要になったらDuckDBを被せる(サーバー不要)
既存の fx_bars現状維持(直近1年・Web表示用)。検証とは用途を分ける
学習期間ローリング12ヶ月。10年は「検証窓を100個作る」ために使う
予測対象価格ではなく「三重障壁のどれに先に当たるか」の分類
モデルLightGBM(GBDT)。深層学習は使わない

この表は2026-08-22時点の見通しであり、いくつかは実測で覆った。 「学習期間12ヶ月」は維持されたが、案の選択(B→A)と特徴量の数(109→10)は変わっている。 経緯は §11.18 と §11.20、誤りの一覧は §11.22。


1. データ保管方針

1.1 サイズ試算(実測ベース)

現在ローカルにある1年分の実測値から10年分を外挿する。

データ1年(実測)10年(試算)置き場所
ティック生データ213 MB約 2.1 GBローカル Parquet
1分足 OHLCV5.8 MB / 370,402行約 58 MB / 370万行ローカル Parquet
5分〜日足2.5 MB約 25 MBローカル Parquet
特徴量テーブル(60列)—約 300〜400 MB(Parquet圧縮後)ローカル Parquet

ディスク残 359 GB に対して合計 2.5 GB 程度。ローカル保管はまったく問題にならない。

1.2 Supabaseに入れた場合の試算

Postgresは行指向で1行あたりのオーバーヘッドが大きい。

内容1行あたり370万行+ PKインデックス判定
1分足10年 + 現行指標(計17列)約 170 B約 630 MB約 710 MB無料枠 500 MB を超過
上記 + ML特徴量60列約 510 B約 1.9 GB約 2.1 GBProプラン(8GB)でも4分の1を消費

前回「入りきらない可能性」を示唆したとおりで、指標を足すとさらに厳しくなるという推測は正しい。

1.3 ただし、容量以前に「DBに入れる理由がない」

仮に容量が足りたとしても入れるべきではない。理由は容量ではなくアクセスパターンにある。

  1. 学習は全件スキャンしかしない。 WHERE句で数行を引くのがPostgresの得意分野だが、 ML学習は「12ヶ月ぶん37万行 × 60列を丸ごと読む」を100窓ぶん繰り返す。 これはPostgresが最も苦手な処理で、ネットワーク越しならさらに遅い。
  1. Parquetは列指向。 60列のうち20列だけで実験したいとき、その20列しか読まない。 Postgresは行全体を読む。SHAPで列を絞り込む工程では、この差が直接効く。
  1. 特徴量の列は毎週変わる。 試行錯誤のたびに migration を書くのは純粋な無駄。 Parquetならファイルを書き直すだけで済む。
  1. 再現性。 「この日の実験はこのファイルで回した」がファイル単位で残る。 DBだと上書きされて消える。

したがって用途で明確に分ける。

Supabase fx_bars      : 直近1年・1分足以上・現行の指標のみ  → Next.jsの表示専用(現状維持)
ローカル Parquet      : 10年ティック / 10年バー / 特徴量 / ラベル → 検証専用

同じ数字を2箇所に持つことになるが、用途が違うので重複ではない。 表示用は「軽く速く読めること」、検証用は「全件を安く舐められること」が要件で、 片方に寄せると必ずもう片方が不便になる。

1.4 新しいDBは立てない(が、DuckDBは検討に値する)

data/features/*.parquet が10ファイルを超えて「2023年の東京時間だけ抜きたい」 のようなクエリが増えたら、DuckDB を被せる。

  • ParquetファイルをそのままSQLで読める(インポート不要、ファイル配置も変えない)
  • サーバープロセス不要、pip install duckdb だけ
  • メモリに載らないサイズでもディスクスピルして処理する

つまり「DBを立てる」という判断は後からいつでもゼロコストで足せる。 今の段階では素のParquet + pandasで十分。

1.5 メモリ制約(8GBマシン)への対応

ディスクは余裕だがメモリ8GBが実際の制約になる。

  • 特徴量は float32 で持つ(float64の半分。指標の精度としては十分すぎる) → 370万行 × 60列 × 4B = 約 890 MB
  • ただし全件をメモリに載せる必要はない。 WFAは1窓につき13ヶ月しか読まないので、 40万行 × 60列 × 4B = 約 96 MB。窓ごとに読んで捨てれば常に軽い
  • 特徴量生成そのものは、既存 aggregate.py と同じく 年単位で分割して処理する

2. 何を解く問題にするか

2.1 期待値の分解

勝てるかどうかは最終的にこれに収束する。

1トレードの期待値 = 勝率 × 平均利益 − (1 − 勝率) × 平均損失 − コスト

既存の押し目戦略は 平均利益 +1.97R(想定どおり)/ 勝率 24.4%(想定50%) で、 損益比は取れていて勝率だけが壊れていた。つまり改善すべきは第1項。

コスト項は軽視できない。ドル円スプレッド実測 0.36〜0.44 pips に対し、 10 pips 狙いなら利幅の 4%、3 pips 狙いなら 13% が最初から消える。 取引回数に比例して累積するので、しきい値設計と直結する。

2.2 設計変数

変数誰が決めるか
① エントリー条件機械学習が解く(本検討の主対象)
② 決済条件人間が先に固定(三重障壁のパラメータ。数通りを比較)
③ ロットサイズ後回し。backtest.py は既にR倍数 + risk_pct 固定で構造上解決済み。
期待値が正だと確認できるまで考えない
④ 取引頻度変数ではなく結果。 予測確率のしきい値を上げれば減る

②を先に固定するのが肝。 ①②を同時に学習させると探索空間が爆発して必ず過学習する。


3. ラベル設計(三重障壁法)

ある時点 t で買ったとして、以下3つの壁のどれに最初に触れたかでラベルを貼る。

壁定義(案)ラベル
上(利確)close(t) + k_tp × ATR(t)1(勝ち)
下(損切り)close(t) − k_sl × ATR(t)0(負け)
横(時間切れ)t から N 本後0 として扱う(後述)

固定pipsではなくATR倍数にする。ボラティリティが変われば適正な幅も変わるため。

3.1 パラメータ候補

「どういうトレードを目指すか」で決まる。以下3案を並行して評価する。

案k_tpk_sl時間切れ想定1日あたり機会
A: スキャル1.01.030分5〜10 pips多い(コスト比が厳しい)
B: デイトレ(採用)2.01.04時間15〜30 pips中
C: スイング3.01.524時間40〜80 pips少ない(既存押し目戦略に近い)

案B を採用(2026-08-22 確定)。 A はコストが利幅の10%超になりやすく、 C は10年でもサンプルが数百本にしかならない。B が「コストが薄まり、かつサンプルが取れる」帯。

B確定に伴い、以下が芋づるで決まる。

項目決定理由
障壁に使うATR15分足の ATR14保有4時間に対して3.5時間ぶんの平均値幅。1分足ATRはノイズが多く、
4時間足ATRは粗すぎて障壁が動かない
パージ / エンバーゴ幅前後1日最大保有4時間+余裕
実質独立サンプル数約 22,000(10年 × 24h ÷ 4h)GBDTには十分。ペアを増やさなくても足りる

3.2 二値にするか三値にするか

二値(勝ち / それ以外)にする。 時間切れは「負け」に寄せる。

理由は、時間切れの0を独立クラスにすると、モデルが「動かない時間帯」を当てるだけで 高精度を出してしまい、肝心の勝ち負けの識別が弱くなるため。 また実運用では「時間切れ=コストぶんマイナスで撤退」なので、負け側の扱いが実態に近い。

3.3 サンプリング

全1分足にラベルを貼ると370万サンプルになるが、隣接する足はほぼ同じ情報しか持たない。 CUSUMフィルタで「前回サンプル時点から一定以上価格が動いた時点」だけを抽出し、 サンプル数を数万オーダーに落とす。これは④「取引回数」の話とも直結する。

3.4 メタラベリング(並行して試す)

既存の押し目戦略が出すシグナル時点だけを抜き出し、 「そのシグナルを取るか、見送るか」の二値分類を学習させる用法。

  • 売買方向は既存ロジックのまま変えない。フィルターだけを学習する
  • 学習対象が単純なのでサンプルが少なくても成立しやすい
  • 勝率 24.4% → 35% に上げられれば、平均利益 1.97R のままで PF 0.94 → 1.4 前後

特徴量テーブルは §4 と共通で、行のサブセットが違うだけ。 つまり §4 さえ作れば両方試せる。実装コストは追加でほぼゼロ。


4. 特徴量設計

4.1 大原則:すべて無次元化する

生の価格・生のpipsを1列でも入れると、モデルは「155円のときは買い」を丸暗記し、 昔のデータが毒になる。ATRで割る/比を取る/順位にするのいずれかを必ず通す。

悪い良い
ema20 = 155.32(close − ema20) / atr14
bb_mid, bb_sigma%B(バンド内の相対位置)
生の値幅ATR比、または直近N本でのパーセンタイル
時刻 = 14sin(2πh/24), cos(2πh/24)(23時と0時が近くなる)

この処理を通せば「昔のデータは価格水準が違うから使えない」問題の大半は消える。

4.2 列定義

1分足の各行に、以下を横に並べる。TF列の各値ごとに1列ずつ生成する。

#カテゴリ特徴量計算式TF
1-15トレンド乖離dist_ema20/50/200(close − emaX) / atr141/5/15/60/240分
16-25トレンド構造ema_gap(ema20 − ema50) / atr14同上
26-30ema20_slope(ema20 − ema20[−5]) / atr14同上
31-35オシレータrsi14そのまま(0-100で既に無次元)同上
36-40macd_hist_n(macd − macd_signal) / atr14同上
41-45ボラpct_b(close − bb_mid) / (2 × bb_sigma)同上
46-50squeezebb_sigma / atr14(スクイーズ検出)同上
51-55atr_relatr14 / atr14の直近100本中央値同上
56-59サポレジdist_support, dist_resist(close − line) / atr14(符号付き、上下1本ずつ)60/240分
60-63line_strengthそのラインのクラスタ構成点数60/240分
64-65bars_since_break直近のライン突破からの経過本数60/240分
66-68時間session東京 / ロンドン / NY / 重複帯(カテゴリ)—
69-72sin_h, cos_h, dow時刻・曜日の周期エンコード—
73コストspread_rel現在spread / 直近中央値spread1分

計 70〜75列。 GBDTなら余裕で扱える規模。

4.3 サポート・レジスタンスの扱い(要注意)

indicators.support_resistance() が既にあるのは大きな資産だが、そのままでは使えない。

現在の実装は「直近500本のスイング高安をクラスタリング」する。 これを全期間で1回計算して全行に貼ると、未来のスイングから作られたラインを 過去の行が参照することになり、完全なリークになる。

対策:各時点で「その時点までの500本」で再計算する(ローリング)。 370万行 × クラスタリングは重いので、60分足の粒度で再計算して1分足にffillする。 これが特徴量生成で最も計算コストの高い部分になるので、設計段階で織り込んでおく。


5. チャートパターンの取り込み(ダウ理論・ダブルボトム)

多くのトレーダーが実際に見ている=多くの参加者が同じ場所で同じ行動をする、 という意味で本物の情報。取り込む。 ただし入れ方に2つのコツがある。

5.1 二値フラグではなく連続値に分解する

「ダブルボトムか / そうでないか」の0/1にすると、判定条件を人間が決め打ちすることになり、 しきい値が1つズレただけで結果が変わる。 「どれくらいそのパターンっぽいか」を構成要素に分解して数値で渡す。

パターン渡す特徴量
ダウ理論のトレンド状態直近4スイングから HH/HL/LH/LL を判定し、
上昇=+2 / 上昇の押し=+1 / 不明=0 / 下降の戻し=−1 / 下降=−2 の序数
ダブルボトム / ダブルトップ2つの安値(高値)の価格差 / atr、2点の間隔(本数)、
ネックラインまでの距離 / atr、2本目の出来高比
三尊 / ヘッドアンドショルダー中央と両肩の高低差 / atr、左右の対称性(時間・価格)
ローソク足実体 / レンジ比、上ヒゲ / レンジ比、下ヒゲ / レンジ比、直前足との包含関係

こうすればモデルが「安値差0.1ATRで間隔30本のときは効く」を自分で学習する。 人間が定義を決め打ちしなくていい。

5.2 ZigZagのリークが最大の罠

これは4時間足のリークよりはるかに気づかれにくく、致命的。

スイング高安は ZigZag系のアルゴリズムで出すが、極値は「後から見て初めて確定する」。 ある安値がスイング安値だったと分かるのは、そこから一定幅を上昇した後。 ライブラリの出力をそのまま特徴量にすると、 「未来に反発することが確定した安値」を、反発する前の時点で知っている状態になる。

症状:パターン系特徴量の重要度が異様に高く出る → 精度70%超 → 美しい右肩上がり → 本番で即死。 しかも原因がパターン認識の内部に隠れているため究明が極めて難しい。

対策:スイング点ごとに「確定した時刻」を明示的に列で持ち、 その時刻以降の行でしか参照しない。この設計を実装前に決めておく。


6. リーク対策チェックリスト

実装より先にこの検査器を作る。 基盤にリークがあると、 機械学習はそれを最優先で発見して美しい成績を出す。

6.1 全列に対する検査

各列について「時刻 t の行が、t 時点で確定している情報だけで作られているか」を1行ずつ検証する表を作る。

6.2 個別の既知の罠

#罠対策
1上位足の未確定バー参照resample(closed="left", label="right") + merge_asof(direction="backward", allow_exact_matches=True)(下記)
2ZigZag / スイング極値§5.2。確定時刻を持たせる
3サポレジの全期間計算§4.3。ローリング再計算
4正規化を全期間で実施平均・標準偏差は学習期間だけで計算し、検証期間に適用
5学習と検証の境界パージ+エンバーゴ(§7.2)
6約定価格シグナル足の終値では約定できない。次の足の始値、買い=ask / 売り=bid
7同一足で利確・損切り両接触損切り優先(backtest.py の既存方針を踏襲)
8ロールオーバー時間帯21:00〜22:59 UTC を除外(§11.5 発見1で1時間拡大)。
21時台は通常帯の6倍、22時台でも4倍のスプレッド

#1 の訂正(2026-08-22、実装時に判明)

当初この欄に closed="right" と書いていたが、このプロジェクトのバーでは誤り。 data/bars/USDJPY/1min.parquet の index は足の開始時刻で、 21:00 の行は 21:00〜21:01 の足を指す(start-indexed)。

  • closed="right" は区間を (a, b] と取るため、b の行=b〜b+1分の足まで含んでしまう。 1分ぶん未来が混ざる
  • 正しくは closed="left", label="right"。区間 [L, L+15) を L+15(=その足が確定した時刻) でラベルする。ラベル時刻=確定時刻になるので、merge_asof の direction="backward", allow_exact_matches=True がそのまま「t時点で確定済みの最新の足」になる

一般に語られる closed="right" は end-indexed のデータを前提にした話で、 どちらが正しいかは自分のデータのindexが何を指すかで決まる。

なお h4.shift(1) で1本ずらす回避策はリークこそしないが、上位足の情報が常に1本ぶん 古くなり、最も価値のある直近の値動きを捨てることになる。

この違いは ml/leak_check.py の selftest で機械的に検出される(_htf(leaky=True))。

6.3 シャッフル検定(必須)

ラベルをランダムにシャッフルして同じパイプラインを回し、精度が50%に戻ることを確認する。 50%を明確に超えるなら、どこかにリークがある。 これを CI 的に毎回走らせる。この検査器を特徴量パイプラインより先に作る。


7. 検証設計(ウォークフォワード)

7.1 なぜ10年必要か

10年のデータは「学習に食わせる」ためではなく「検証窓を100個作る」ために使う。 ここが最重要の区別。

[学習 12ヶ月][空白][検証 1ヶ月]                    ← 窓1
     [学習 12ヶ月][空白][検証 1ヶ月]               ← 窓2
          [学習 12ヶ月][空白][検証 1ヶ月]          ← 窓3
                    ...  ×  約100回
  • 学習窓は常に直近12ヶ月だけ(ローリング。expandingにしない)
  • 窓が右にずれるたびに古いデータは学習から落ちる
  • 2005年のデータで2026年を予測するモデルは一度も作らない

2005年のデータは「2004年で学習したモデルが2005年に通用したか」を試すためだけに使う。 1回のバックテストで勝つのは簡単だが、100窓中70窓で勝つのは偶然では起きない。

7.2 パージとエンバーゴ

三重障壁のラベルは未来N本を使って作られるので、 学習期間の末尾のサンプルは検証期間の値動きと情報を共有している。 境界の前後で「最大保有時間」ぶんのデータを捨てる(案Bなら4時間+余裕を見て1日)。

7.3 学習窓長

12ヶ月を基準とする。これ未満だと金利トレンドが一方向の期間しか含まれず、 これ以上だと日銀のレジーム転換をまたぐ。 6 / 12 / 18 / 24ヶ月を比較してもよいが、「4通り試した」と試行回数に記録すること。


8. モデル

LightGBM(勾配ブースティング木)を使う。深層学習は使わない。

  • 決定木は「IF文の集合」そのもの。分岐点を人間ではなくデータから決める。 「どの変数がどれだけの重み付けをするか」を機械的に解く道具として、目的にそのまま合致する
  • テーブル形式・少サンプルの金融データでは実験的にGBDTが最良
  • LSTM / Transformer はこの規模では過学習するだけ
  • feature_importances_ と SHAP値が出るので、 「4時間足のEMA乖離が最も効き、1分足RSIはほぼ効かない」が数値でわかる

8.1 評価指標

精度(accuracy)を主指標にしない。 三重障壁のTP/SLが非対称(案Bなら2:1)なら、勝率33%でも期待値はプラスになる。 既存の押し目戦略が「勝率24.4%でも平均+1.97R」だったのと同じ話。

見るべきは コスト控除後の期待値 / PF / 最大DD。


9. 合格基準

既存仕様書 §8.3 の基準を踏襲しつつ、WFA前提で読み替える。

指標基準
PF(100窓の合算、コスト控除後)1.3 以上
勝ち越した窓の割合60% 以上
最大ドローダウン10% 未満
シャッフル検定精度が 50% ± 2% に収まる
隣接パラメータでの一貫性k_tp / k_sl を ±20% 動かしてもPFが破綻しない

最後の項目が特に重要。 既存の押し目戦略では「戻り高値の取り方でPFが0.88〜1.17に動き、 隣接設定で一貫しない」ためカーブフィッティングと判断した。この厳しさをMLでも維持する。

9.1 多重検定への備え

100通り試して最良を選べば、全部ランダムでも「良く見えるもの」は必ず出る。 試行回数を記録し、デフレーテッド・シャープレシオで割り引いて評価する。


10. 作業順序

この順序を守る。 特に 1 を飛ばさない。

#作業依存備考
0🔄 Dukascopyから10年分のティック取得なし2026-08-22 開始。2016-08-22〜2025-08-16 の9年分
1✅ リーク検出ハーネスなしml/leak_check.py。2026-08-22 完成、selftest PASS
2a✅ 10年分のバー集約0aggregate.py。1分足 3,719,154本
2b✅ データ健全性チェック2a§11.5。欠損ゼロ・整合性OK。ATR下限フィルタの必要性が判明
3✅ 特徴量テーブル生成(§4, §5)2bml/features.py。109列 / 308MB / リーク検査通過
4✅ 三重障壁ラベル生成(§3)3ml/labeling.py。746,790行 / 勝率27.97% / 平均R −0.0478
5~~メタラベリングを既存押し目戦略に適用~~—見送り。 押し目戦略は10年2ペアで213トレードしかなく、
USDJPY単独では約100件。GBDTの学習には桁が足りない
6✅ 分類器 + WFA 109窓3, 4ml/walkforward.py。PF 1.337 / 対照実験3本通過
7ティックでの障壁検算61分足の判定がどれだけ楽観的かを測る
8✅ スリッページを入れた再評価7ml/slippage.py。運用点が 0.46〜0.50 に移動
9✅ 重要度分析6ml/importance.py。m1_dist_ema20 が単独32.1%
10頑健性テスト(列を落として再検証)9時刻列 / m1列 / 重要度0の列 の3本

10.1 ディレクトリ構成(案)

fx/
  data/
    ticks/USDJPY/YYYY/MM/*.parquet   既存。10年に拡張中
    bars/USDJPY/*.parquet            既存。10年に拡張予定
    features/USDJPY/YYYY.parquet     新規。年別・float32
    labels/USDJPY/B.parquet          新規。三重障壁(案B)
  ml/
    leak_check.py                    ✅ リーク検出ハーネス
    features.py                      §4, §5 の生成
    labeling.py                      §3
    walkforward.py                   §7

10.2 ml/leak_check.py の仕様(実装済み)

python -m ml.leak_check --selftest              # 検出器自身を検査
python -m ml.leak_check --build ml.features:build   # 実パイプラインを検査
検査手法主に捕まえるもの
未来切り落とし検定末尾を切って作り直し、残った部分の値が変わらないかZigZag/スイング未確定、上位足の未確定バー
未来撹乱検定t以降の足だけ乱数で書き換え、t以前が変わらないか全期間での正規化(データ長が変わらないので誤検出しない)
シャッフル検定ラベルを並べ替えてAUCが0.5に戻るか特徴量へのラベルの直接漏れ

切るのは末尾であって先頭ではない。 先頭を切るとEMA等が助走不足で正当に変わり、 リークと区別がつかなくなる。

--selftest はわざとリークさせたビルダーを検査して、検出器が火を噴くことを確認する。 火の点かない火災報知器を作らないため。検査器を直したら必ず selftest を通すこと。

selftest の3ケース(すべて検出できることを確認済み):

  1. 上位足を左ラベルで貼り未確定の足を参照
  2. フラクタルを確定させずに前方補完(ZigZag型)
  3. 全期間の平均・標準偏差で正規化

11. 決定事項と検証結果

11.1 決定(2026-08-22)

項目決定理由
三重障壁のパラメータ案B(TP 2.0ATR / SL 1.0ATR / 4時間切れ)コストが薄まり、かつサンプルが取れる帯
障壁に使うATR15分足 ATR14保有4時間に対して3.5時間ぶんの平均値幅
対象通貨ペアUSDJPY 単独案Bなら単独10年で実質2.2万サンプル取れ、増やす主目的(サンプル拡大)が消えた。
特徴量は無次元なので、パイプライン完成後にペアを足すのは容易
CUSUMのしきい値0.5 × 15分足ATR約20分に1イベント → 10年で約19万サンプル、重複除去後 約2.2万
サンプル重複の補正sample uniqueness による重み付け4時間保有だと同時刻に複数ラベルが重なる。
重みを下げないと特定の時間帯を過大評価する
サポレジの再計算粒度60分足で再計算 → 1分足に ffill370万行×クラスタリングは非現実的。ラインは数時間単位でしか動かない
ZigZagの確定判定1.0 × ATR 逆行で極値確定案Bの損切り幅と同じ。緩くすると微細なノイズが全部スイングになる

11.2 ロング / ショートの扱い(決定)

案2: 符号反転で対称化し、1モデルに統合。

ショート側は特徴量の符号を反転してロングと同じ学習データに混ぜる。

反転する列反転方法
dist_ema20/50/200, ema_gap, ema20_slope, macd_hist_n−1 倍
pct_b−1 倍(バンド内位置が上下反転)
dist_support ↔ dist_resist入れ替えて −1 倍
ダウ理論のトレンド序数−1 倍(+2 上昇 ↔ −2 下降)
ダブルボトム ↔ ダブルトップ入れ替え
ローソク足の上ヒゲ ↔ 下ヒゲ入れ替え
rsi14100 − rsi14
atr_rel, squeeze, spread_rel, 時間系反転しない(方向を持たない量)

これで実質サンプルが 2.2万 → 4.4万になり、 「下降トレンドの戻り売り」を「上昇トレンドの押し目買い」と同じ現象として学習できる。 案Bは4時間保有なのでスワップの非対称は無視できる。

反転漏れが最も危険なバグになる(片方向だけ辻褄が合わなくなり、成績が静かに劣化する)。 上の表を実装時のチェックリストとして使い、 「元データを反転 → 特徴量生成」と「特徴量生成 → 反転」の2経路で同じ結果になることを単体テストする。

11.3 同時保有ポジション数(決定)

常に1ポジションのみ。保有中に出たシグナルは全て見送る。

複雑さを増やさないことを優先する。これにより以下が決まる。

  • ロットサイズの議論(§2.2 ③)が当面不要になる。1トレード=口座の risk_pct 固定でよい
  • バックテストの実装が単純になる(ポジション管理が状態1つで済む)
  • 学習時は引き続き全イベントを使う。 1ポジション制約はバックテスト時のルールであって、 ラベル生成の制約ではない。したがって sample uniqueness の重み付け(§11.1)は依然として必要

副作用として1つ性質が変わる。 1ポジション制約下では 「しきい値を超えた最初のシグナル」を取ることになり、 「その後に来るもっと確度の高いシグナル」を取り逃す。 確率がより高いシグナルを待つことはできない(未来が見えないため)。 これは成績を押し下げる方向に働くが、実運用と同じ条件なのでそのまま受け入れる。

11.5 データ取得と健全性チェックの結果(2026-08-22 完了)

10年分(2016-08-22 〜 2026-08-14)を取得・集約完了。

ティック3,122ファイル / 2.5 GB / 約1.9億件
1分足3,719,154本 / 56 MB(float64でメモリ238MB → float32運用で約120MB)
15分足248,985本 / 5.2 MB
欠損日ゼロ(年312〜314日 = 365 − 土曜52日と一致)
整合性high<low・NaN・volume=0・spread≤0 すべて 0件

データ品質は問題なし。特徴量生成に進んでよい。

訂正: スプレッドは「縮小」していない(前回の記述は誤り)

取得中に「2017年は0.86 pips、現在は0.4 pips なので縮小している」と書いたが、誤り。 0.86 という数字は取得ログに流れた 2017-06-25(ティック3,810件しかない薄商いの日曜)の 値を年平均と取り違えたもの。10年分の実測は逆の動きをしている。

年spread平均spread中央p99価格レンジ
20160.4780.3682.7099.94 〜 118.67
20170.3720.2952.11107.32 〜 118.60
20180.3750.3032.11104.64 〜 114.55
20190.338(最小)0.2572.31104.45 〜 112.40
20200.4360.2923.34101.18 〜 112.22
20210.4060.3002.91102.59 〜 115.52
20220.8440.6935.30113.47 〜 151.94
20230.8360.6105.92127.22 〜 151.91
20240.862(最大)0.6157.57139.58 〜 161.95
20250.8200.5469.10139.89 〜 158.88
20260.6980.4547.40152.10 〜 163.99

2019年を底に、2022年以降で約2.5倍に拡大している。 円安局面でボラティリティが上がり、 それに連れてスプレッドが開いた。つまりスプレッドは時代とともに単調変化するものではなく、 ボラティリティ・レジームに連動して上下する。

結論(無次元化 / spread_rel / ローリング12ヶ月)は変わらない。むしろ強化される。 一方向のトレンドなら「年」を特徴量に入れれば済むが、レジーム依存なら 比で持つ以外に扱いようがない。価格レンジも 99.94 〜 163.99 と64%動いており、 生の価格を1列でも入れたら破綻することが数字で確認できた。

発見1: ロールオーバー時間帯の除外は 21:00〜22:59 UTC に広げるべき

READMEは「21:00〜22:00 UTC を除外」としていたが、10年分で見ると22時台もまだ高い。

UTCJSTspread平均中央p99vol中央
205時0.610.482.9230
216時2.621.3815.0817
227時1.610.8711.9522
238時0.630.502.0032
(通常帯 8〜16時)17〜1時0.410.340.9760〜110

21時台は通常帯の6倍、22時台でも4倍。p99 は 12〜15 pips に達し、 案B(15〜30 pips狙い)ではコストが利益を食い潰す。 除外を 21:00〜22:59 UTC(JST 6:00〜7:59)に広げる。 機会の12.5%を失うが、 そもそも約定し得ない時間帯なので実質的な損失はない。

発見2: エントリーのゲートは コスト比 で持つ(当初のATR絶対値は誤り)

15分足ATR14 の分布(pips): 最小 0.86 / 1%点 2.57 / 10%点 3.96 / 中央 7.42 / 99%点 26.97 / 最大 83.95。

問題: 案BのTPは 2×ATR。ATRが最小の 0.86 pips の時 TP は 1.72 pips しかなく、 スプレッド中央 0.5 pips に対して コスト比 29%。ATRを分母にした設計の盲点。

当初の対策「15分足ATR14 >= 4.0 pips」は誤りだった(2026-08-22 実装時に判明)。

絶対pips値の下限は §4.1 の無次元化の原則に反する。実際に年別の通過率を測ると、 レジームで極端に偏っていた。

年ATR中央ATR>=4pipsコスト比中央コスト比<=7%
20186.2386.7%2.4%90.8%
20194.7764.4%2.7%89.5%
20214.8166.5%3.1%88.6%
202410.4190.0%2.9%88.4%
202511.1691.9%2.2%90.6%
年別の最大差27.5 pt3.0 pt

低ボラ年で3分の1の時間を締め出していたが、除外の理由が無い。 スプレッドもボラティリティに連動して縮むため、2019年のコスト比は2.7%で 2024年の2.9%よりむしろ良い。安全な時間帯を切り捨てていた。

採用するゲート:

spread / (2 × ATR) <= 0.07        ← 主条件。無次元でレジーム中立
かつ 15分足ATR14 >= 2.0 pips      ← 退化した超低ボラを落とす安全弁(全期間の0.5%未満)
かつ 21:00〜22:59 UTC を除外       ← 発見1

全期間の通過率 89.4%、年別のばらつきは 87.8〜90.8%(最大差3.0pt)。

スプレッド・ATRとも直近の確定済み15分足から取る。1分足の spread 列は その足の期間平均なので、t の行で参照すると1分ぶん未来を見る(実装時に修正済み)。

教訓: 「無次元化する」という原則を特徴量には適用したのに、 ゲートには適用し忘れていた。 閾値もまた特徴量と同じ規律で書く必要がある。

発見3: 極端な値動きは「異常値」ではなく本物のイベント

1分間の値幅の最大は 247.3 pips(2023-10-03 14:12 UTC)。 上位10件はいずれも 2022〜2024年に集中しており、為替介入・FOMC・雇用統計の瞬間。 データエラーではないので除外しない。 ただし ATR が跳ねるため、 atr_rel(現在ATR ÷ 直近100本中央値)が効く場面として学習対象に含める。

分布としては 中央 1.40 / 90%点 4.00 / 99%点 9.20 / 99.9%点 20.60 pips。

11.6 特徴量テーブルの実装結果(2026-08-22)

ml/features.py 完成。109列。data/features/USDJPY/{year}.parquet に年別・float32で出力。

列数109(+ tradeable フラグ)
行数3,719,154(10年分)
サイズ308 MB(試算 300〜400 MB と一致)
生成時間約3分(サポレジの毎バー再計算 1h足62,256本で1.2分を含む)
tradeable全期間 89.4%、年別 87.8〜90.8%

内訳: 5時間足 × 基本10列 = 50、ローソク足 2足 × 4列 = 8、 チャートパターン 2足 × 17列 = 34、サポレジ 2足 × 6列 = 12、時間・コスト 5列。

リーク検査: 110列すべて実値で検査、未来切り落とし・未来撹乱ともに全通過。

時刻の約束(実装で確定させた前提)

index t の行は「時刻 t に下す判断」を表し、使えるのは t までに確定した足だけ。 約定は t から始まる1分足の始値(買い=ask)で起きる想定。

1分足自身も上位足と同じ変換に通す。 t の行が指す1分足(t〜t+1)はまだ閉じていないので、 そのまま指標を計算すると1分ぶん未来を見る。全時間足を一律に to_htf() → attach() に通すことで、この取りこぼしを構造的に防ぐ。

検査器の穴を1つ塞いだ

最初の実行は「109列すべて一致」で通ったが、信用できない結果だった。 検査は「NaN同士は一致」と扱うため、比較窓が全てNaNの列は検査せずに素通りする。 4時間足のEMA200には133日の助走が要り、25万行(174日)では一部の列が丸ごと未検査だった。

ColumnResult.n_tested を数えて警告を出すようにし、45万行に増やして再実行。 通ったことより「何を検査したか」を数えるほうが重要。

mirror() — 対称化の安全網

_mirror_rule() は未知の列名に出会うと例外を投げる。 黙って「反転しない」に倒すと片方向だけ辻褄が合わなくなり、 成績が静かに劣化して原因が分からなくなるため。 あわせて「二重反転で元に戻る」ことをテストしており、 sr_dist_below ↔ sr_dist_above のような入れ替えペアの取り違えも検出される。

11.7 ラベル生成の実装結果(2026-08-22)

ml/labeling.py 完成。data/labels/USDJPY/B.parquet(10.0 MB)。

CUSUMイベント407,505件(1分足の11.0% / 平均間隔 9分)
ゲート通過後373,395件
出力行数746,790(ロング・ショート各1行)
勝率27.97%
平均R−0.0478
平均保有66分(時間切れは8.1%のみ)
実質サンプル数145,135(uniqueness合計。平均同時保有 5.1本)

検証: 平均Rが「往復スプレッド ÷ ATR」と一致する

ランダムなエントリーの期待値は、理論上ちょうどスプレッドぶんのマイナスになる。 実測がそれと一致すれば、ラベル生成が正しく、かつ楽観側に歪んでいない証拠になる。

年勝率平均R(実測)−spread/ATR(理論)spread中央ATR中央
201628.18%−0.0375−0.03950.3689.32
201828.32%−0.0490−0.04900.3036.19
202127.60%−0.0555−0.06170.3004.86
202427.66%−0.0507−0.05760.61510.67
202528.28%−0.0445−0.04890.54611.16
全期間27.97%−0.0478約 −0.05

全年で一致している。 勝率も27.6〜28.4%と安定しており、ロング28.1% / ショート27.8%と 方向の偏りも無い(対称化の前提が実データで裏付けられた)。

ランダムエントリーのPF = 0.926 が出発点

これが「何もしない場合の水準」であり、モデルはここを超えなければならない。

勝率PF
27.97%(ランダム)0.926
33.3%(損益比2:1の損益分岐)1.00
35%1.08
39.4%1.30 ← 合格基準

モデルは勝率を 28.0% → 39.4% へ、11.4pt 引き上げる必要がある。 これは軽くない。 ただしモデルは全イベントを取る必要はなく、確度の高い上位数%だけを選べばよいので、 「全体の勝率を上げる」のではなく「勝てる場面を選り分ける」問題になる。

参考: 既存の押し目戦略はランダムを下回っていた可能性

押し目戦略は 平均利益 +1.97R / 勝率 24.4%。損益比 1.97 の損益分岐点は 1 / (1 + 1.97) = 33.7% なので、24.4%は大きく下回る。 損益比2:1のランダムエントリーが 27.97%(分岐点33.3%)であることと並べると、 押し目戦略はランダムより悪かったことになる。

時間軸・対象ペア・R の定義が違うので厳密な比較ではないが、 「エントリーのルールが価値を生んでいなかった」という読み方は成り立つ。

実装上の判断

  • コストは障壁に織り込む。 買いはask・決済はbidなので、 純損益 = 決済時の仲値 − (エントリー足の始値 + スプレッド)。 往復のスプレッドぶん障壁がずれるので、「利確幅がスプレッドに埋もれる」ケースが 自動的に負けになる
  • 同一足で両方に触れたら損切り。 1分足の高安からは1分の中の順序が分からないため、 過大評価しない側に倒す(backtest.py の既存方針と同じ)。後でティックで検算する
  • 時間切れは負けに寄せる(§3.2)。ただし実現Rは実測値で持つので、期待値の計算は正確

11.8 ウォークフォワード検証の結果(2026-08-22)

ml/walkforward.py。109窓(学習12ヶ月 / パージ1日 / 検証1ヶ月 / 1ヶ月ずらし、2017-08〜2026-08)。 モデルは scikit-learn の HistGradientBoosting(LightGBM は brew install libomp が要るため見送り)。

しきい値ごとの成績

しきい値制約なし 件数 / 勝率 / PF1ポジション制約後 件数 / 勝率 / PF / 勝窓%
なし676,698 / 27.93% / 0.925← ランダムと同一(正常)
0.34126,310 / 34.88% / 1.17133,323 / 36.87% / 1.280 / 93.6%
0.3863,864 / 35.99% / 1.22221,516 / 37.57% / 1.308 / 90.8%
0.4042,963 / 36.49% / 1.24615,963 / 38.25% / 1.337 / 91.7%
0.4418,007 / 37.13% / 1.2877,508 / 38.59% / 1.362 / 86.2%
0.487,394 / 37.95% / 1.3383,215 / 40.25% / 1.454 / 79.8%
0.541,897 / 35.48% / 1.184840 / 37.74% / 1.296 / 61.4%

しきい値を上げると勝率が単調に上がり、上げすぎると崩れるという期待どおりの形。 合格基準(PF 1.3以上・勝ち越し窓60%以上)を満たす領域が 0.38〜0.52 と幅広く、 隣接するしきい値で一貫している。押し目戦略で 「戻り高値の取り方でPFが0.88〜1.17に動く」のを見てカーブフィッティングと判断したのとは対照的。

対照実験 — 3本すべて通過(これを通すまで上の数字は信用しない)

実験AUC 中央平均 ± σ0.5超の窓実PF(th 0.40)
本番0.57260.5718 ± 0.0234100.0%1.337
一様シャッフル0.50060.5001 ± 0.008852.3%0.732
ブロックシャッフル(1週=1440行)0.50020.4984 ± 0.024350.5%0.887

当初「AUCが0.55を超えたらリークを疑え」と書いた基準を実測が超えているため、 一様シャッフルに加えてブロックシャッフルも実施した。 一様シャッフルはラベルの時間的なまとまり(近い時刻のイベントは似た結果になる)を 壊してしまうぶん甘い検定になる。塊のまま入れ替えれば自己相関を保ったまま 因果関係だけを切れる。

判定の根拠は平均の差ではなく一貫性。

ブロックシャッフルで AUC>0.5 になる確率  p = 0.505
109窓すべてが 0.5 を超える確率          p^109 = 4.2e-33
本番は 109/109 窓が 0.5 超(最小 0.504)
本番の中央AUC 0.573 を超えたブロック窓  0/109

ブロックシャッフルは自己相関を保つぶん窓ごとのブレが大きく(σ 0.0243 と本番並み)、 最大0.566まで届く窓もある。単発の窓なら偶然出せる水準だが、 109窓すべてを0.5超に揃えることは偶然では起きない。 両対照とも実PFがランダム水準(0.926)以下に落ちている点も整合する。

結論: エッジは実在すると判断する。 ただしこれは 「統計的なエッジがある」までしか言っておらず、実運用の成績を保証しない(下記の未検証項目)。

結合経路の検証(リーク検査が見ていない部分)

leak_check が見るのは「特徴量が未来のバーを参照していないか」だけで、 ラベルとの結合や学習の組み立ての誤りは検出できない。 そこで別途確認した。

検証結果
行の対応X 746,790行 = meta 746,790行、同一時刻を指す
ts までのバーだけで作り直した値との一致最大差 1.1e-05(109列すべて)
ショート行 = mirror(ロング行)一致。88/109列が反転、残る21列は方向を持たない量
ラベルの向きexit_ts > ts が100%、各時刻ちょうど2行

エッジは時期に偏っていない

年取引数勝率PFAUC
201785740.02%1.4440.5724
20191,77240.12%1.4330.5673
20212,17239.64%1.3900.5846
20221,79141.65%1.5260.5905
20241,66435.94%1.2250.5703
20261,03234.88%1.1800.5544

10年すべてで PF > 1.0。 前半5年 PF 1.376 / 後半5年 PF 1.296。 低ボラ年(2019・2021)と円安トレンド年(2022〜2024)の両方で成立している。

ただし直近で緩やかに劣化している(2022 PF 1.526 → 2026 PF 1.180、 AUC 0.5905 → 0.5544)。12ヶ月ローリングPFは 2023-02 の 1.533 をピークに 2024年以降は 1.18〜1.25 で推移。要監視。

口座ベースの成績とロットサイズ

しきい値取引/年PFrisk%年率最大DD最大連敗
0.401,7741.3370.20%99.2%−8.7%21
0.448341.3620.25%53.9%−9.7%18
0.483571.4540.30%31.2%−9.6%18

合格基準の「最大DD 10%未満」は、リスクを 0.2〜0.3% に絞れば満たせる。 1% では −37%、0.5% でも −20% に達する。§2.2 で「③ロットサイズは後回し」と したが、ここで戻ってくることになった。

この年率を額面どおり受け取ってはいけない。 現時点のシミュレーションには入っていない:

  • スリッページ(スプレッドしか入れていない。1日7回の売買では効く)
  • 1分足での障壁判定(1分の中の順序は不明。損切り優先に倒してはいるが未検算)
  • 約定遅延・ロット上限・約定インパクト

信じるべきは「PF 1.3〜1.45」「10年すべてで PF > 1.0」という相対的な形であって、 絶対的な収益額ではない。

11.9 ティック検算の結果(2026-08-22)

ml/tick_verify.py。しきい値0.40の 15,963トレード全件をティックで再判定した。

勝率平均RPF平均保有
1分足(現行)38.25%0.19721.33755分
ティック(実測)38.18%0.19471.33255分
判定できた 15,963 / 15,963 件 (100.0%)
勝敗が一致 15,814 / 15,963 (99.07%)
  1分足で負け → ティックで勝ち : 69件(現行が悲観的すぎた側)
  1分足で勝ち → ティックで負け : 80件(現行が楽観的すぎた側)

PFの差は 0.005。 ズレる149件も悲観側69・楽観側80とほぼ拮抗しており、系統的な偏りは無い。 「1分足の高安で判定し、同一足で両方に触れたら損切り優先」という設計方針は妥当だった。

検算中に見つけたバグ: 時刻の単位

試運転の段階で、走査窓が4時間ではなく2日間になっていた。

このプロジェクトの時刻は datetime64[ms](ミリ秒)だが、4時間をナノ秒(1.44e13)で 足していたため桁が1000倍ずれ、時間切れになるはずのトレードが2日間走り続けていた。

修正前は「1分足で負け → ティックで勝ち 8件 / 逆 2件」と悲観側に偏った結果が出ており、 そのまま信じていれば「1分足の判定は保守的すぎる、実際はもっと良い」という 誤った結論に至っていた。修正後は69対80とほぼ拮抗する。

整数に落として時刻を扱うときは必ず as_unit("ns") で単位を明示すること。 walkforward.py の single_position() でも同種の型不整合で落ちており、 tz-aware の時刻を numpy に渡す箇所は要注意。

11.10 特徴量の重要度(2026-08-22)

ml/importance.py。全期間に散らした9窓で permutation importance(検証AUCの低下幅)を測定。

木の分岐回数で測る gain importance は使わない。 取りうる値が多い列ほど不当に高く出るため、 無次元化した連続値と session のような少数の値しか取らない列が混在するここでは歪みが直接効く。

種類ごと

種類寄与列数
トレンド乖離36.3%15
時間・コスト29.5%5
ボラティリティ10.2%17
トレンド構造8.2%10
チャートパターン7.8%20
オシレータ2.5%10
サポート・レジスタンス2.4%12
ダウ理論・スイング2.3%14
ローソク足0.9%6

時間足ごと

時間足寄与列数
1分足45.1%10
4時間足9.7%33
1時間足9.4%37
15分足4.4%14
5分足1.9%10
時間足なし29.5%5

個別の上位

列AUC低下寄与窓で正%
m1_dist_ema200.0257432.1%100%
sin_h0.0182722.8%89%
m1_ema_slope0.003884.8%100%
cos_h0.003704.6%89%
m1_pct_b0.003083.8%89%
m1_atr_rel0.002302.9%100%
h1_hs_sym0.001772.2%89%

効いていない列(重要度 ≤ 0): 49 / 109。

読み方の注意

permutation importance は相関のある列の重要度を過小評価する。 同じ情報を2列が持っていれば、片方を壊してももう片方が補うので低く出る。 5つの時間足のEMA乖離は互いに強く相関しているため、 m5/m15 の低さは「不要」ではなく「m1 と重複」の可能性がある。 列を削る判断は、実際に削って再検証してから行うこと。

11.11 モデルは何を学習したか — 順張り

選ばれた取引を m1_dist_ema20(1分足のEMA20からの乖離 ÷ ATR、ロング基準)で8分割した。

m1_dist_ema20件数勝率平均R
−10.03 〜 0.061,99632.16%0.007
0.06 〜 0.781,99536.24%0.156
1.20 〜 1.561,99637.93%0.187
1.91 〜 2.321,99539.50%0.217
2.32 〜 2.981,99539.80%0.252
2.98 〜 10.291,99643.89%0.347

単調に上がる。 価格が進行方向にすでに大きく動いているほど勝率が高い。 つまりモデルが見つけたのは 押し目買いではなく、短期モメンタムの継続 である。

これは当初の想定と逆だった。既存の押し目戦略(=逆張り的な押し目を狙う)が ランダムを下回っていたこと(§11.7)とも整合する。

時間帯の効果

取引はUTC 0時(JST 9時・東京オープン、10.5%)と UTC 12-13時(JST 21-22時・ロンドン/NY重複、18.3%)に集中し、 勝率は主要時間帯で37〜40%。UTC 16-20時(JST 1-5時)は勝率22〜34%と悪いが件数が少ない。

UTC 16-20時の追加除外を試したが、採用しない。

設定取引勝率PF合計R勝窓%
現行(21-22のみ除外)15,96338.25%1.3373148.091.7%
+ 20時も除外15,84138.39%1.3423169.490.8%
+ 16-20時を除外15,45938.59%1.3473140.190.8%

PFはわずかに上がるが合計Rはむしろ減る。複雑さに見合わない。

11.12 スリッページを入れた再評価(2026-08-23)

ml/slippage.py。合格判定が変わった。

注文の種類ごとに扱いを変える。

場面注文滑り
エントリー成行かかる
利確指値かからない(自分が流動性を出す側)
損切り逆指値かかる
時間切れ成行かかる

1R = K_SL × ATR なので、滑り s pips は s / (K_SL × ATR_pips) R に相当する。 ATRが小さいトレードほど滑りが重いため、固定pipsで一律に引かずトレードごとに換算する。

しきい値0.40での影響

滑り(片道)PF(現実的)PF(悲観)平均R
0.01.3371.3370.1972
0.21.2331.2130.1454
0.31.1861.1570.1195
0.51.1001.0540.0678
PF=1.0 に落ちる滑り0.760.61

滑り0.2 pips で PF 1.233。合格基準の1.3を下回る。 ドル円の実際の滑りは通常0.1〜0.3 pips なので、余裕はあるが大きくはない。

運用点が移動する

しきい値取引/年PF(滑0)PF(滑0.2)勝窓%risk年率最大DD
0.401,7671.3371.23385.3%0.15%46.4%−8.8%
0.448311.3621.25580.7%0.20%29.5%−9.4%
0.465461.4181.30778.0%0.15%16.4%−8.3%
0.483561.4541.34077.1%0.25%19.8%−9.3%
0.502291.4441.33277.1%0.25%12.0%−8.8%

運用点は 0.40 → 0.46〜0.50 に移る。 取引は年1,767回から230〜550回に減り、 年率は12〜20%(最大DD 10%未満)。

年率99% → 12〜20% への低下は悪いニュースではない。 §11.8 で「年率を額面どおり受け取ってはいけない」と書いたとおりで、 滑りを入れて初めて意味のある水準になった。

11.13 エッジの劣化 — 直近では基準を満たさない(2026-08-23)

この節の結論は後に覆った(§11.21)。 ここで見た「裾の識別力の半減」は109列版に固有の現象で、 推奨構成(案A・上位10列)では +18.52 → +22.60 → +19.65pt と減衰していない。 過学習していた列を落としたら劣化も消えた。以下は当時の分析としてそのまま残す。

§11.8 で「直近で緩やかに劣化」と書いた点を掘り下げた。結論が変わる規模の発見。

全体のAUCは落ちていない

検定結果
窓ごとAUCの時間トレンド傾き +0.000012/窓、p = 0.87(有意な劣化なし)
前半54窓 vs 後半55窓0.5709 ± 0.0240 vs 0.5727 ± 0.0229、p = 0.69(差なし)

だが「特に良い機会を見分ける力」は半減している

期間ベース勝率上位1%の勝率持ち上げ幅
2017-201928.10%40.45%+12.35pt
2020-202227.84%38.59%+10.76pt
2023-202627.89%35.17%+7.28pt

しきい値0.48での勝率は 42.81%(n=946)→ 36.70%(n=1,098)、z=2.82 / p=0.0049 で有意。

AUCは全しきい値の平均なので、裾の識別力が落ちても中間帯が補えば変わらない。 そして我々が取引するのはまさにその裾(上位1%)なので、劣化は運用点を直撃する。

較正のズレが原因ではない

固定しきい値0.48の通過率は 1.15% / 1.20% / 0.96% と安定しており、 窓ごとの分位で選ぶ方式に変えても同じ低下が出る(1.367 → 1.320 → 1.209)。 実体のある劣化。

直近だけで見ると、どのしきい値も基準に届かない

2023-2026(44窓)、滑り0.2 pips:

しきい値取引/年勝率PF勝窓%
0.401,66136.31%1.16377.3%
0.4472436.65%1.18372.7%
0.4830436.70%1.19472.7%
0.5213237.61%1.23168.2%
0.566035.82%1.15046.2%

PF 1.3 を満たすしきい値は存在しない。 最良でも 1.231(しかも n=476 で誤差が大きい)。

判定: 全10年では合格基準を満たすが、直近3.5年では満たさない。 PFは1.09〜1.23で1.0は上回っており損はしないが、基準には届かない。

想定される原因: モデルが使っている短期モメンタム(§11.11)が アルゴリズム取引の増加で削られている、という古典的な筋書き。 ただし3期間の単調減少という以上の証拠は無い。

運用上の含意: 成績の見積もりには10年平均ではなく直近期間の数字を使うこと。

11.14 頑健性テスト — 戦略は1分足の1本足打法だった(2026-08-23)

列を落として再検証した。しきい値0.48 / 滑り0.2 pips。

構成列数AUC中央0.5超%PF全期間PF直近(2023-)
基準: 全109列1090.5726100.0%1.3401.194
A: 時刻4列を除外1050.558098.2%1.2070.996
B: m1列10列を除外990.547098.2%0.9390.847
C: 重要度0の49列を除外600.5731100.0%1.2931.183

B: 1分足を落とすとエッジが消える

PF 1.340 → 0.939。ランダムエントリー(0.926)と同水準。 109列のうち残る99列を全部使っても、1分足の10列が無ければ何も残らない。

この戦略は実質「1分足でATRの数倍動いた直後に、その方向へ入る」だけのものであり、 マルチタイムフレームもチャートパターンもサポレジも、成績には寄与していない。

A: 時刻を落とすと直近が損益分岐

全期間 PF 1.207、直近は 0.996。時間帯情報は成績の支えとして実在する。 ただし日内季節性は市場構造とともに変わりうるもので、依存は運用上のリスク。

C: 49列は本当に不要だった

PF 1.293(基準1.340)、AUCはむしろ微増(0.5731)。 差は誤差の範囲。 permutation importance の「相関による過小評価」を疑っていたが、 実際に落としても成績が落ちないので、過小評価ではなく本当に効いていなかった。

除外された49列の内訳:

種類除外 / 全体
ダウ理論・スイング10 / 14
チャートパターン10 / 20
サポート・レジスタンス7 / 12
ボラティリティ6 / 17
トレンド乖離5 / 15
トレンド構造5 / 10
オシレータ4 / 10
ローソク足2 / 6
時間・コスト0 / 5

手の込んだ特徴量ほど落ちている。 ダウ理論・ダブルボトム・三尊・サポレジは、 実装コストが最も高かった部分(サポレジの毎バー再計算だけで生成時間の大半)だが、 半分以上が無価値だった。時間・コストの5列は1つも落ちない。

当初の問いへの答え

「いろんな指標を複合的に見てほしい。実際のトレーダーはこれらをかなり使っている」

データはそれを支持しなかった。 109列を用意して測った結果は、

  • 効いたのは 1分足の短期モメンタム(m1_dist_ema20 単独で寄与32.1%)と 時刻
  • ダウ理論・チャートパターン・サポレジ・RSI・MACD・マルチタイムフレームはほぼ寄与なし
  • 49列を捨てても成績は変わらない

多くのトレーダーが使っている指標が、「使われている」ことと「効く」ことは別だった。 ただしこれは「案B(TP2ATR/SL1ATR/4時間)の勝敗を当てる」という この問題設定における答えであり、別の時間軸・別の決済ルールでは違いうる。

11.15 案A/C の比較・学習窓長・最小構成(2026-08-23)

しきい値0.48 / 滑り0.2 pips。

構成窓AUC中央PF全期間PF直近(2023-)取引(全)
基準: 案B / 12ヶ月 / 109列1090.57261.3401.1943,215
D: 案A スキャル(1:1 / 30分)1090.65441.2891.23220,010
E: 案C スイング(3:1.5 / 24h)1090.53971.0541.0843,473
F: 学習窓 6ヶ月1150.55861.1171.0455,761
G: 学習窓 24ヶ月970.58061.3751.3241,744
H: 重要度上位20列のみ1090.57051.2561.1883,690

案A の高いAUCは罠

AUC 0.6544 は案Bの 0.5726 より大幅に高いが、PFは同等以下。 案Aは時間切れが40.4%あり、「30分以内に何か起きるか」はボラティリティから容易に予測できる。 モデルはそれを当てているだけで、勝ち負けの識別が良くなったわけではない。

§8.1 で「精度を主指標にしない」と書いたのはこのため。AUCも同じ罠を持つ。

案C は明確に劣る

PF 1.054。保有24時間では短期モメンタム(§11.11)が薄まり、 かつ重複が激しく実質サンプルが 62,247 まで落ちる。案Bの選択は妥当だった。

学習窓は長いほうが良い — 当初の想定と逆

6ヶ月 1.117 → 12ヶ月 1.340 → 24ヶ月 1.375。直近では 1.045 → 1.194 → 1.324。

§7.3 で「24ヶ月だと日銀のレジーム転換をまたぐので12ヶ月」と書いたが、データは逆を示した。 レジームをまたぐ不利より、サンプルが増える利のほうが大きい。

ただしこれは6通り試した中の最良であり、そのまま採用しない(§9.1 の多重検定)。 検証のため以下を追加実行する:

  • 学習窓 36ヶ月 — 傾向が続くか、24でピークか
  • 案A で 6 / 24ヶ月 — 独立したラベルで窓長効果が再現するか。 再現すれば偶然ではない。これが最も強い証拠になる

列を減らしても大きくは落ちない

上位20列のみで PF 1.256(基準1.340)、49列除外で 1.293。 109列という複雑さは正当化されない。

11.16 デフレーテッド・シャープによる多重検定の割引

1窓(1ヶ月)のリターン(R建て、滑り0.2込み)から算出。

構成窓年率シャープ勝窓%試行回数で割引後の「本物である確率」
基準(事前に決めた設定)1092.2777.1%試行1回: 100% / 試行11回: 100%
G: 24ヶ月(事後選択)952.1469.5%試行4回: 100%

多重検定を織り込んでもエッジは十分な余裕で残る。

直近期間だけでも有意

構成窓取引PF年率シャープ平均>0 の検定
12ヶ月441,0981.1941.23t=2.35 p=0.0234
24ヶ月424581.3241.52t=2.85 p=0.0068

直近でもリターンが0より大きいことは統計的に有意。 PF 1.3 という基準に届かないだけで、エッジ自体は消えていない。

これまでの試行回数(記録)

種類回数
ゲート設計の変更2(ATR絶対値 → コスト比)
ラベル案3(A/B/C)
学習窓長4(6/12/24/36)
特徴量の構成5(全/−時刻/−m1/−dead/top20)
学習を伴う試行 合計約 11〜14
しきい値スイープ18(運用パラメータなので試行に数えない)

基準設定(案B / 12ヶ月 / 109列 / しきい値0.46〜0.50)は結果を見る前に決めたものなので、 重い割引を要しない。24ヶ月は事後選択なので再現テストで確かめる。

11.17 学習窓長の再現テスト — 24ヶ月の優位はノイズだった(2026-08-23)

§11.15 で「24ヶ月が最良」と出たが、そのまま採用しないと決めて検証した。結果は否定的。

指標によって最良が違う

案B 学習窓窓AUCPF全シャープ全PF直近シャープ直近取引(全)
6ヶ月1150.55861.1171.141.0450.495,761
12ヶ月1090.57261.3402.271.1941.233,215
24ヶ月970.58061.3752.141.3241.521,744
36ヶ月850.58141.4251.731.2310.861,114

PF全期間は36ヶ月、シャープ全期間は12ヶ月、直近は24ヶ月が最良。 PFは1取引あたりの質、シャープは頻度込みの尺度で、窓を伸ばすほど取引が減る(5,761→1,114)。 指標が一致しない時点で、頑健な効果ではない。

独立ラベル(案A)での再現テスト

変化PF全PF直近
案B 12ヶ月 → 24ヶ月+0.035+0.130
案A 12ヶ月 → 24ヶ月+0.025+0.011

方向は再現したが、大きさが再現しない。 案Bの直近で見えた +0.130 は案Aでは +0.011 しかない。 あの改善の大部分はノイズ。24ヶ月を採用する根拠にはならない。

案A での学習窓長(完全版):

案A 学習窓窓AUCPF全シャープ全PF直近シャープ直近
6ヶ月1150.64491.2273.271.1442.17
12ヶ月1090.65441.2893.961.2323.11
24ヶ月970.66071.3144.931.2433.93

確定したこと: 6ヶ月は両ラベルで明確に劣る(案B −0.223 / 案A −0.062)。6ヶ月は使わない。

確定しないこと: 12ヶ月 vs 24ヶ月。案BではPFが指標により逆転し、案AではPF差が僅少。 ただし案Aのシャープだけは24ヶ月が一貫して良い(3.96→4.93、3.11→3.93)。 案Aを採用する場合は、24ヶ月を専用に検証し直す価値がある。

学習窓は 12ヶ月のままとする(事前に決めた設定を、再現しない改善で動かさない)。

11.18 案A(スキャル)が案Bを上回る — 当初の推奨は誤りだった

スリッページ耐性

案取引/年滑り00.20.5PF=1.0の滑り
案B (TP2/SL1)3561.4541.3401.1940.99 pips
案A (TP1/SL1)2,2151.4211.2891.1180.74 pips
案C (TP3/SL1.5)3851.1431.0831.0020.51 pips

案AはTP幅が案Bの半分なので、同じpipsの滑りがR換算で2倍効く。耐性は案Bに劣る。

だが口座ベースでは案Aが圧倒する

2023-2026 / 最大DD10%以内に収まる最大リスクでの年率:

構成取引/年滑り0.1滑り0.2滑り0.3滑り0.5
案B th0.4830412.2%9.4%7.8%3.8%
案A th0.482,016101.3%59.7%37.5%16.6%

どの滑り水準でも案Aが上回る。 PFは案Bのほうが高いのに、 取引頻度が6倍あるため口座の増え方が桁違いになる。最大連敗も12回(案Bは18回)と少ない。

案Aを退けた判断は間違いだった

§3.1 で案Aを「コストが利幅の10%超になりやすい」として退けたが、

  1. コスト比ゲート(§11.5 発見2)がそれを自動的に処理している。 案Aのゲートは spread / (1×ATR) <= 7% なので、コスト比が高い場面は最初から除外される
  2. PFだけを見て頻度を見ていなかった。 PF 1.29 を年2,215回繰り返すほうが、PF 1.34 を年356回より口座は増える

PFは1取引あたりの質しか測らない。運用の良し悪しは PF × 頻度 で決まる。

ただし案Aには未検証の前提がある

  • 年2,016回=1日約8回。 実行環境の質が成績を直接左右する
  • 滑り0.2 pips は仮定であって実測ではない。0.5 pips なら年率は 59.7% → 16.6% に落ちる
  • 保有19分。約定遅延・ロット上限・ブローカーの許容度が未検証

案Aを採用するなら、次にやるべきはOANDA v20 のデモ口座での実測(README の TODO 8)。 滑りの実測値が無い限り、この比較は仮定の上に乗っている。

11.19 案A での再検証 — 案Bで測った分析をやり直した(2026-08-23)

§11.9〜11.16 はすべて案Bで測ったもの。推奨が案Aに移った以上、やり直す必要がある。

ティック検算(20,010トレード全件)

勝率平均RPF平均保有
1分足(現行)49.23%0.15451.42113分
ティック(実測)49.29%0.16031.44313分

勝敗一致 19,640 / 20,010(98.15%)。悲観側191件・楽観側179件で拮抗。 案Aもティック検算を通る(むしろ微増)。

なお勝率49%は案Bの38%より高く見えるが、案Aは損益比1:1なので損益分岐が50%。 PFで比べること。

劣化は案Bよりずっと軽い

期間AUCPF(滑0.2)上位1%の持ち上げ
2017-20190.64131.302+23.69pt
2020-20220.66221.346+20.22pt
2023-20260.65741.232+18.47pt

減衰は −22%(案Bは +12.35 → +7.28 で −41%、半減)。 AUCの時間トレンドは +0.0091/109窓(p=0.087)で、むしろ微増方向。

案Aは案Bより劣化に強い。 短い保有時間ゆえに、 アルゴリズム取引に削られにくい構造なのかもしれない(推測)。

重要度はさらに集中している

種類案A(案B)
ボラティリティ36.9%10.2%
トレンド乖離25.1%36.3%
時間・コスト24.0%29.5%
トレンド構造9.1%8.2%
オシレータ2.2%2.5%
チャートパターン0.8%7.8%
ダウ理論・スイング0.8%2.3%
サポート・レジスタンス0.6%2.4%
時間足案A
1分足(10列)66.2%
時間足なし(5列)24.0%
5分足4.1%
1時間足(37列)3.1%
4時間足(33列)1.5%
15分足1.2%

上位6列で 85%:

列寄与窓で正%
m1_dist_ema2024.2%100%
m1_atr_rel21.4%100%
sin_h13.4%100%
m1_pct_b9.5%100%
m1_ema_slope8.8%100%
cos_h7.7%100%

案Bで2.9%だった m1_atr_rel(現在ATR ÷ 直近100本中央値)が2位に浮上。 保有30分では「そもそも障壁に届くか」がボラティリティで決まるため、筋が通る。

109列という装置は案Aではさらに不要。 上位10列・6列だけでの検証を実行中。

11.20 最小構成の検証 — 列を減らすほど直近が良くなる(2026-08-23)

案Aの寄与は上位6列で85%。実際に列を絞って検証した。

しきい値0.48で比較

構成列数AUCPF全シャープ全PF直近シャープ直近取引/年
全109列1090.65441.2893.961.2323.112,215
上位10列100.65891.3154.811.2834.893,146
上位6列60.65721.2914.611.2444.423,136

上位10列が109列を全指標で上回る。

ただししきい値0.48は構成間で同じ選択率にならない(取引数が2,215 vs 3,146)ので、 これだけでは公平な比較にならない。窓ごとの分位で揃えて測り直した。

選択率を揃えた比較(窓ごと上位q%)

構成列数上位1% PF全上位1% PF直近上位2% PF全上位2% PF直近上位5% PF全上位5% PF直近
全109列1091.2711.1481.2951.1551.2561.194
上位10列101.2491.1851.2731.2051.2881.225
上位6列61.2011.1961.2251.2131.2881.246

直近期間では、どの選択率でも列が少ないほど良い。 全期間では109列が勝つ場合もあるが、 直近(=より難しい期間)で一貫して逆転する。

109列という装置は不要なだけでなく、直近の汎化にはむしろ有害だった。 特徴量を増やすほど過去には適合し、未来には効かなくなる、という教科書どおりの形。

運用上の含意

  • 生成コストが激減する。 サポレジの毎バー再計算(生成時間の大半)が不要になる
  • 6〜10列なら中身を人間が追える。 m1_dist_ema20 / m1_atr_rel / sin_h / m1_pct_b / m1_ema_slope / cos_h。何を根拠に入るのかが説明できる
  • 年率の数字は使わない。 上位10列は年3,146回でPF 1.283。複利計算すると 年率が3桁になるが、この頻度では約定インパクトとロット上限で成立しない。 PFとシャープで評価すること

11.21 推奨構成の最終検証(2026-08-23)

§11.20 で「上位10列が109列を上回る」と分かったので、 推奨構成(案A・上位10列・学習12ヶ月・しきい値0.48)で全検証をやり直した。 スリッページは 0.1 pips を仮定する。

対照実験 — 3本すべて通過

実験AUC中央平均 ± σ0.5超の窓最小最大
本番0.65890.6595 ± 0.0154100.0%0.6260.698
一様シャッフル0.50070.5003 ± 0.009254.1%0.4770.522
ブロックシャッフル(1週)0.50130.5016 ± 0.010955.0%0.4720.545
ブロックシャッフルで AUC>0.5 になる確率  p = 0.550
109窓すべてが 0.5 を超える確率          p^109 = 5.5e-29
本番の最小AUC 0.626 を超えたブロック窓   0 / 109
本番はブロックシャッフル分布の 14.4 標準偏差 上

109列版(§11.8)より分布の重なりが小さい。列を減らしたほうが対照実験も明確に通る。

ティック検算(28,421件全件)

勝率平均RPF平均保有
1分足(現行)49.38%0.16221.44813分
ティック(実測)49.42%0.16951.47713分

勝敗一致 27,878 / 28,421(98.09%)。悲観側278件・楽観側265件で拮抗。

勝率48.82%・損益比1:1 で PF が1を超える理由

損益比1:1なら損益分岐は50%のはずだが、実測48.82%でPF 1.325になる。時間切れが効いている。

内訳件数割合平均R合計R
勝ち(利確)5,14748.82%+0.9909+5,099.9
負け(損切り)3,59334.08%−1.0181−3,658.2
時間切れ1,80317.10%−0.0662−119.4

負けの平均は −0.700R であって −1R ではない。 30分で決着しなかった17%が平均 −0.066R で済むため。したがって

損益分岐の勝率 = 0.700 / (0.991 + 0.700) = 41.40%
実測勝率 48.82% がこれを 7.4pt 上回る

§3.2 で「時間切れは負けに寄せる」と決めたが、実現Rは実測値で持つようにしてあるので 期待値の計算は正確になっている。ラベル上は負けでも、損益上は微損で済んでいる。

3期間の推移 — 劣化がほぼ消えた

期間AUC取引勝率PF年間R上位1%の持ち上げ
2017-20190.64628,95749.49%1.379+523+18.52pt
2020-20220.66758,92149.93%1.451+482+22.60pt
2023-20260.659610,54348.82%1.325+366+19.65pt

§11.13 で見つけた「裾の識別力の半減」は、109列版に固有の現象だった。 上位10列では +18.52 → +22.60 → +19.65 と減衰していない。 過学習していた列を落としたら劣化も消えた、という読み方ができる。

ただし年間Rは 523 → 366 と3割減っており、PFも 1.379 → 1.325 とわずかに下がっている。 穏やかな低下はある。 成績の見積もりには直近期間の数字を使うこと。

スリッページへの感度(直近3.5年)

滑り(片道)PF平均R年間Rrisk 0.25%の単利年率
0.001.368+0.1393+407101.6%
0.051.346+0.1323+38696.6%
0.10(採用)1.325+0.1254+36691.5%
0.201.283+0.1116+32681.5%
0.301.243+0.0978+28571.4%
0.501.168+0.0701+20551.2%
PF=1.0 に落ちる滑り1.01 pips

1R = ATR中央 11.26 pips なので、滑り0.1 pips は 0.009R。 損益分岐まで1.01 pips の余裕がある。

指値エントリーは成行に劣る(実測)

「1分足が確定したらその価格に指値を置き、違う動きになったらキャンセルすればよい」 という運用なら、エントリー時の滑りは避けられる。ティックで検証した結果、成行のほうが良い。

エントリー方法約定率取引数勝率PF合計R
成行(滑り0.1)100%10,54348.82%1.325+1,322.4
指値・1分待ち(滑りなし)87.4%9,21747.22%1.286+1,028.6
指値・3分待ち90.8%9,57346.83%1.270+1,012.0
指値・5分待ち92.6%9,76646.65%1.260+999.7

構造的な問題がある。 順張りで入るということは価格が進行方向へ動いている最中なので、 指値は戻ってきたときにしか約定しない。つまり約定するのは 「モメンタムが続かなかった場合」に偏る(逆選択)。

約定率は87%と高いが、約定しなかった13%が最良のトレードだった。 待ち時間を延ばすほど約定率は上がりPFは下がる、という一貫した形も同じ理由。

滑り0.1 pips のコストは 0.009R。一方、指値による取り逃がしは 0.039R(PF 1.325→1.286)。 避けようとしたコストの4倍を失う。

実装は ml/limit_entry.py。

11.22 間違えた判断の記録

実測が事前の理屈を覆した箇所。共通するのは、自分で立てた原則を自分で適用し忘れていたこと。

#判断実際気づいた経緯
1ゲートを「ATR ≥ 4 pips」にした絶対pips値で無次元化の原則に違反。低ボラ年の1/3を不当に締め出していた年別の通過率の偏り(2019年 64% / 2024年 90%)
2案Bを推奨し、案Aを「コスト過大」として退けたコスト比ゲートが自動処理。頻度6倍で案Aが圧倒案A/B/C の比較(§11.18)
3「学習窓24ヶ月はレジームをまたぐので不可」6ヶ月は明確に劣り、24ヶ月は12ヶ月と同等以上窓長スイープ(§11.17)
4「1分足の判定は保守的すぎる」と結論しかけたms/nsの単位不一致で走査窓が4時間ではなく2日になっていた保有時間が0分と表示された(§11.9)
5「複数の指標を複合的に見れば効く」前提で109列を作った効いたのは10列。109列は直近の汎化をむしろ悪化させていた頑健性テスト(§11.14, §11.20)
6「直近では基準を満たさない」と結論した(§11.13)109列版に固有の現象。上位10列では劣化していない推奨構成での再検証(§11.21)

#1 と #2 が本質的。 どちらも「無次元化する」「期待値は質×頻度で決まる」という、 自分が明文化した原則を、特徴量には適用したのに閾値と案の選択には適用し忘れていた。

規律が効いた場面

「学習窓24ヶ月が最良」は6通り試した中の最良だった。そのまま採用せず、 独立したラベル(案A)で再現するかを確かめたところ、 案Bで見えた改善(+0.130)は案Aでは +0.011 しかなく、大部分がノイズと判明した。 再現テストをしなければ採用していた。

11.23 現時点の総括(2026-08-23)

到達点

#工程状態
010年ティック取得✅ 3,122ファイル / 2.5GB / 1.9億件
1リーク検出ハーネス✅ 意図的なリーク3種を検出できることを確認
2バー集約・健全性チェック✅ 3,719,154本 / 欠損ゼロ
3特徴量テーブル✅ 109列 → 運用は10列(§11.20)
4三重障壁ラベル✅ 案A/B/C / 平均Rが理論値と全年一致
6WFA 109窓✅ 対照実験3本すべて通過
7ティック検算✅ 98.09%一致 / PF 1.448 → 1.477
8スリッページ✅ 0.1 pips 採用。損益分岐は 1.01 pips
9重要度分析✅ 上位6列で85%
10頑健性テスト 11本✅ 1分足を落とすと崩壊、列を減らすと改善
11推奨構成での最終検証✅ 対照実験3本・ティック検算・指値比較(§11.21)

エッジは実在する(根拠)

検証結果
一様シャッフルAUC 0.5007(本番 0.6589)
ブロックシャッフルAUC 0.5013 / 本番は 14.4σ 上 / 109窓すべて0.5超の確率 5.5e-29
本番の最小AUCを超えたブロック窓0 / 109
結合経路の独立検証ts 以前のバーだけで再計算した値と一致(最大差 1.1e-05)
ティック検算勝敗一致 98.09% / PFはむしろ微増
デフレーテッド・シャープ試行14回で割り引いても有意
3期間すべてPF 1.379 / 1.451 / 1.325

エッジの正体

1分足がATRの数倍伸びた直後に、その方向へ入る(短期モメンタムの継続)。 そこにボラティリティの状態と時間帯が加わる。効いているのは実質6列。

当初の仮説「実際のトレーダーが使っている指標を複合的に見れば勝てる」は支持されなかった。 ダウ理論・チャートパターン・サポート線・RSI・MACD・マルチタイムフレームは 案Aで合計2.2%しか寄与せず、109列は直近の汎化をむしろ悪化させていた。

運用構成(確定)

ラベル      案A(TP 1.0ATR / SL 1.0ATR / 30分)
特徴量      重要度上位10列
学習        ローリング12ヶ月、パージ1日、月次で再学習
しきい値    0.48
ロット      1トレード 0.25%(最大DD 10%以内)
エントリー  次の1分足の始値で成行(指値は逆選択で劣る)
ゲート      spread/(1×ATR) ≤ 7% / ATR ≥ 2pips / 21:00-22:59 UTC 除外

期待: 年2,920回 / PF 1.325 / 1トレード +1.41 pips / 年間 +4,100 pips (滑り0.1 pips の仮定下)

11.24 未決 — ここから先の課題

WFAが通ったことで、検証すべき対象が「エッジがあるか」から「その大きさが本物か」に移った。

  • 済~~ティックでの障壁検算~~ → §11.9。PF 1.337 → 1.332(差 0.005)で問題なし
  • 済~~特徴量の重要度分析~~ → §11.10, §11.11
  • 済~~スリッページを入れた再評価~~ → §11.12。PF 1.337 → 1.233(滑り0.2pips)。 運用点を 0.46〜0.50 に上げれば基準を満たす
  • 未列を削って再検証。 49/109列が効いていないが、相関による過小評価の可能性がある。 実際に削って成績が落ちないかを確かめる。落ちなければ生成コストが大幅に下がる (最も重い サポレジの毎バー再計算 は寄与2.4%)
  • 未1分足特徴量への依存の検証。 寄与の45.1%が1分足10列、うち32.1%が m1_dist_ema20 単独。 1本の柱に寄りかかっている。この列を落としても成立するかを見ておく
  • 未時間帯依存の検証。 寄与29.5%が時刻。実在する日内季節性ではあるが、 時刻列を全部落としたときにエッジが残るかを確認する
  • 済~~直近の劣化の原因分析~~ → §11.13。裾の識別力が半減。直近では基準未達。
  • 未多重検定の割引。 ここまでの試行回数(ゲート設計の変更1回、しきい値18通り、 学習窓長は12ヶ月のみ)を記録し、デフレーテッド・シャープで割り引く。 現時点では試行回数は少なく、カーブフィッティングの疑いは薄い
  • 済~~案A・案C との比較~~ → §11.18。案Aが口座ベースで圧倒。当初の推奨は誤りだった
  • 未OANDA v20 デモ口座で滑りを実測する(最優先)。 成績は「滑り0.1 pips」という仮定の上に乗っている。 0.5 pips なら PF 1.325 → 1.168 に落ちる(損益分岐は1.01 pips)。 年2,920回=1日約12回の頻度では実行環境の質が成績を直接左右する。 バックテストをこれ以上磨いてもこの不確実性は減らない
  • 未小売スプレッドでの再評価。 コストはDukascopyの実測スプレッド(中央0.5 pips)で 計算している。これはインターバンクに近い値で、小売口座はこれより広いことがある。 実口座のスプレッドが分かったら差分を滑りに足して見直す
  • 済~~案A を前提にした再検証~~ → §11.19。ティック検算・劣化・重要度すべて実施済み
  • 未案A × 学習窓24ヶ月 の専用検証(シャープだけ一貫して良い)
  • 済~~案A の劣化の確認~~ → §11.19。減衰 −22%(案Bは −41%)。案Aのほうが強い
  • 済~~最小構成での運用可否~~ → §11.20。上位10列が109列を上回る。直近ほど差が出る
  • 済~~最小構成で対照実験を通し直す~~ → §11.21。3本すべて通過(14.4σ)
  • 未features.py を10列に絞る。 サポレジの毎バー再計算が不要になり、 生成が数分から数秒になる。成績は §11.21 で確認済みなので、あとは実装の整理

これは検証の記録であって、売買の推奨ではありません。過去のデータで測った成績は将来を保証しません。

データ: Dukascopy ティック 2016-08-22 〜 2026-08-14 / 約定は次足始値・買いask・売りbid、同一足で両方に触れた場合は損切りを優先。