0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TradingView MCPのバックテスト機能を機能強化してみた

0
Last updated at Posted at 2026-09-08

TL;DR

  • TradingView デスクトップアプリを CDP 経由で操作する MCP サーバーを機能強化した
  • バックテスト系のツールを増やすうちに、機能追加より「都合の良い結果を採用してしまう経路を塞ぐ」ほうが重要であることに気づいた
  • リポジトリ: https://github.com/tedorigawa001/TradingView-MCP (npm: bushido-tradingview-mcp)

なぜAIを「騙さない」が主題になったか

バックテストのツールを AI に持たせると、最初はうまくいきます。
AI は喜んで条件を変え、良い数字が出た構成を報告してきます。

問題は、その報告が一つ一つは正直だということです。

手元の台帳で試したことがあります。
200件・全体の平均が -1.96bps・PF 0.857 という、はっきり負けている記録です。
ここに「銘柄を絞る」「方向を絞る」「期間を絞る」を組み合わせて 12回呼んだだけで、こういうスライスが出ます。

台帳全体          : records=200  mean=-1.96bps  PF=0.857
12スライス中の最良 : records=34   mean=+3.09bps  PF=1.293

34件は「少数標本」の閾値(30件)を超えているので警告も出ません。
この結果だけ見ると、残り(166件)の存在を一言も述べません。嘘は一つもないのに、読んだ人は勝てる手法を見つけたと思います。

機能を増やすほどこの穴は広がります。
フィルタが増えれば探索空間が増えるからです。それで方針を変えました。

1. 台帳をハッシュで束縛する

get_strategy_trade_ledger は Strategy Tester の全取引を500件ずつページングし、SHA-256 の ID に束縛します。
ページをまたいだ再計算は拒否されます。

地味ですが効きます。バックテストの数字は「どの実行の、どのページの話か」が曖昧になりがちで、そこが曖昧だと後から検証できません。

含み損益の行(最後の未決済ポジション)を分離しているのも同じ理由です。
決済済みの成績に未決済の評価益が混ざると、都合の良い方向にだけ数字が動きます。

2. 成果物を不変にする

summarize_backtest_ledger は、ローカルに登録された内容アドレスの成果物を読みます。

npm run import:backtest-ledger -- --input /abs/path/normalized.json --confirm-local-import
# => { "artifact_id": "sha256:8d22d028...", "records": 200 }

同じ内容を再インポートすると同じ ID が返り、既存の証跡は置き換わりません。公開は一時ファイルを fsync してから link() で行い、直後に読み戻してハッシュを照合します。壊れた宛先があれば置換ではなく拒否です。

ただしここには限界があって、応答にもそう書いてあります。

content_hash_is_integrity_not_source_authentication

内容ハッシュが保証するのは「後から書き換えられていない」ことだけです。その数字が正しく作られたことは保証しません

3. スライスの分母を返す

冒頭の 34/200 の問題への直接の答えです。応答に2つのフィールドを足しました。

フィールド 意味
ledger_records 絞り込みの全件数
selected_fraction 選択件数 ÷ 全件数

34件が「34件」ではなく「200件中の0.17」として出ます。数字を一つ足しただけですが、読む側の判断が変わります。

4. 探索回数を記録する

分母だけでは足りません。何回切ったかは、単発の応答には原理的に書けないからです。

そこで research_id を渡すと、追記型のローカル台帳に記録してから指標を返すようにしました。実際に動かすとこうなります。

  whole ledger          calls=1 distinct=1 cells=0 frac=1.000
  XAUUSD only           calls=2 distinct=2 cells=0 frac=0.330
  XAUUSD only (repeat)  calls=3 distinct=2 cells=0 frac=0.330   ← 回数は増え、条件数は据え置き
  XAUUSD long           calls=4 distinct=3 cells=0 frac=0.165
  group_by month        calls=5 distinct=4 cells=9 frac=1.000   ← 1回で9セル提示

実装で気を使ったのは3点です。

条件の正規化。 [EURUSD, USDJPY][USDJPY, EURUSD] は同じ条件です。整列と重複除去をしてからハッシュを取らないと、distinct_conditions が水増しされて探索を実際より広く見せます。過小に見せるより厄介です。

記録が先、指標が後。 記録に失敗したら指標を返さずエラーにします。逆順だと「数字だけ見て記録は失われた」が起こります。

グループセルを別に数える。 group_by: "month" は1回の呼び出しで12セル提示することがあります。呼び出し回数だけでは探索の広さを表せません。

そして research_id を渡さない場合は status: "untracked" が明示されます。追跡していないことが黙って隠れないのが要点です。

5. train でだけ選ぶ

run_strategy_walk_forward は 2〜8 個の候補を 2〜12 の train/embargo/test 窓に分割し、train でのみ選抜して、選ばれた候補の OOS だけを返します。

全候補の OOS を並べて返すと、人間(と AI)は必ず一番良いものを見ます。それは選抜を OOS でやったのと同じことです。返さないのが唯一の対策でした。

6. 事前に protocol を検査する

validate_research_protocol は Pine のバージョンと凍結済みプロトコルを突き合わせ、以下を blocked / warning に分類します。

  • IS/OOS の重複
  • 未来を含む窓
  • 未確定足の混入
  • 試行回数
  • 最小取引数
  • コスト未設定
  • OOS 確定後の変更

「後から気づく」のではなく、走らせる前に止まります。

実装の評価結果

M5/M15/M30 × 6銘柄 × 手法/閾値の 243構成を回しました。

コスト 平均の中央値 陽性
0bps(グロス) -0.150 93/235
2bps -2.150 11/235

コスト適用は全構成で厳密な定数シフトでした。つまりコストが edge を消したのではなく、グロスの時点で edge が無い。235構成のうち、記述的な95%区間がゼロを正の側に外したものは 0本です(負の側に外したものは185本)。

唯一「良さそう」に見えたセルも、同じ手法・同じ銘柄の 3×3 グリッドで見ると9セル中1つだけの孤立点で、合計の48%が7取引(全体の3.3%)から来ていました。

採用したものはありません。 この結論が正しく出たことが重要な実績です。

作り方の話: ミューテーションを通す

テストの受け入れ基準を「通ること」から「欠陥を戻したら落ちること」に変えました。

このセッションだけで、こういうものが見つかっています。

  • limitations の配列を丸ごと消してもテストが全部緑だった(読む人が最も寄りかかる部分です)
  • 監視機構が自分の失敗時に通知しないコードで、テストは通っていた
  • 排他作成のシンボリックリンク検査を消しても、902件が緑のままだった

一つ実例を出すと、価格レベルの正規化(浮動小数の丸め誤差だけを回収し、本物の半ティック価格は拒否する)では、厳しすぎる変異と緩すぎる変異の両方を確かめました。片側だけだと、緩める変異を見逃します。

### M1: strict integers only  → Expected 4 BUY 3 3 6 1 1, got
### M2: accept anything that rounds → Expected , got 4 BUY 3 3 6 1 1

npm testpretest で再ビルドするので、build/ を書き換えて npm test すると変異が消えて全部通ります。これに一度騙されました。変異検証は node --test <file> を直接使う必要があります。

使い方

npm install -g bushido-tradingview-mcp
open -a TradingView --args --remote-debugging-port=9222

MCP クライアント側の設定はリポジトリの README にあります。
npm 利用者に Java は不要です(Bookmap アドオンのビルド時のみ JDK が要ります)。

正直に書いておくこと

  • MCPでは勝てる手法は提供しません。 このサーバーが提供するのは、負けを負けと認識しやすくする道具です
  • バックテストの結果は約定を証明しません。スプレッド・スリッページ・資金調達・通貨換算は測定していません
  • candidateEligible は常に false です。採用判断は人間が別途行うもので、このツールは出しません
  • 探索回数の記録は、有効化前の探索も、このサーバー外の探索も数えられません
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?