Bot CEX 開発ログ

🛠️開発記録#524(2026/4/25)リードラグ研究DAY17 — EV表示の誤読をきっかけに、構造判定と取引候補を分け直した話

こんにちは、ぼっちbotterよだかです。

今日は、リードラグ観測機で溜まった5営業日分のデータをもとに、GlobalBTC・CME・ETFそれぞれの構造をどう判断するかを整理しました。

当初の目的は、Grafana上で見えていた「EV>0」らしき状態が、本当にトレードシグナル候補になるのかを確認することでした。ところが確認を進めるうちに、そもそも自分が見ていた「EV>0」の意味が、実装上の意味とズレていたことが分かりました。

今回は、そこで見つかった誤読の原因と、構造判定と取引候補を分けるために行った修正、そして次の5営業日で何を見るのかを整理しておきます。

前回の話
🛠️開発記録#523(2026/4/24)観測チェック&今週の振り返り&来週の計画

続きを見る

1. 今日のゴールは、5営業日分のデータから判断を作ることだった

今日の最初のゴールは、かなりはっきりしていました。

リードラグ観測機は朝チェック上は安定して動いており、5営業日分のデータも溜まっていました。そこで今日は、GlobalBTC・CME・ETFそれぞれについて、現時点でどこまで判断できるのかを整理することにしました。

特に確認したかったのは、Grafana上で見えていた「EV>0らしき判定」です。

具体的には、BTCグローバルとCMEを対象にした residual decay 系の判定パネルで、過去7日間に GlobalBTC は11回、CME は4回、2 にタッチしていました。自分の理解では、この 2 は「構造が出ていて、かつ執行コスト込みでEV>0になっている状態」だと思っていました。

なので、CODEXにはかなり具体的に確認を依頼しました。

BTCグローバルとCMEに対して出ているEV>0はトレードシグナル生成に使えるか?
執行に繋ぐことはできそうか?
どのタイミングでどのようなEV構造があったのか?
それは執行込みでも本当に取れそうか?
ダメならダメとはっきり判定してほしい。

この時点では、自分の中で「EV>0」という言葉の意味はかなり強く読まれていました。
つまり、ただ構造が出たというだけではなく、取引候補として見てよい可能性があるものとして扱っていました。

しかし、最初の分析結果はかなり冷静なものでした。

GlobalBTCについては、residual gap の減衰は見えているものの、PnLには変換できていませんでした。5営業日窓で residual_decision2 は147点あったものの、primary net中央値は -29.27bps、best horizonでも -25.96bps、cost pass は0%。事前に見えていた residual2 状態から次イベントを取る検証でも、cost pass は0.6%だけでした。

CMEについても、residual_decision2 は84点ありましたが、current-eventでは primary net中央値 -30.28bps、best net中央値 -21.54bps。事前に residual2 が見えていた次イベントでは25件中9件だけ後続horizonで cost pass したものの、primaryでは全敗、h900/h1800でも中央値はマイナスでした。

この時点で、まず一つ目の結論が出ました。

GlobalBTC/CME の residual_decay EV>0 を、そのまま執行シグナルへ繋ぐのは No。

GlobalBTCは構造観測用。
CMEは研究継続候補。
ETFは現行定義が不適切。

この整理自体は、そこまで違和感のあるものではありませんでした。短期リードラグ構造が見えても、Binance Japan BTC/JPYで29bps前後のコストを超えて取れるかは別問題です。

ただ、ここで本当に大きかったのは、単に「EVがなかった」という話ではありません。

分析を進めるうちに、そもそも自分が見ていた EV>0 の意味が、実装上の意味とズレていることが分かってきました。

CODEXの確認では、residual_decay の EV は PnL EV ではなく、実装上は structural_improvement でした。計算としても、|residual_primary| - |residual_tail|、つまり「残差がどれだけ縮んだか」を見ているものであり、実際に売買してコスト後にプラスになるかを直接表すものではありませんでした。

ここで、自分の理解が一段崩れました。

自分は、

2 = 構造あり + 執行コスト込みEV>0 + トレードシグナル候補

として見ていた。

でも実装上は、

2 = 構造あり + そのdefinitionに紐づく値が0より大きい

でした。

しかも、その「definitionに紐づく値」は一種類ではありませんでした。

fixed_horizon なら primary net。
follow_arrival なら arrival net。
state_persistence なら tail net。
residual_decay なら residual improvement。

これらを全部まとめて EV と見せていたため、Grafana上では「執行EVがプラスになった」ように見えてしまっていたわけです。

この時点で、今日の作業の意味が変わりました。

当初は、5営業日分のデータから「この構造はトレード候補になるか」を判断する予定でした。
しかし実際には、その前に、観測機が出している判定の意味を、自分が正しく読めているかを確認し直す必要がありました。

これは地味ですが、かなり重要です。

なぜなら、ここを誤解したまま次の5営業日に進むと、また同じように「構造が出た」「EV>0が出た」「これは取れるかもしれない」と読んでしまうからです。

今日の本当のスタート地点は、ここだったと思います。

5営業日分のデータから判断を作ろうとした。
しかし、その判断の前提になっていた EV>0 の意味が、実装とズレていた。
だから今日は、シグナルを探す前に、まず観測機の見方を修正する日になった。

結果として、これは単なる表示名の修正では済みませんでした。

構造があること。
definition別の値がプラスであること。
執行コスト込みで取引候補になること。

この3つを分けなければいけない。

今日の開発は、そこをはっきり認識するところから始まりました。

2. 旧パネルの「EV>0」は、私が思っていた意味ではなかった

今回いちばん大きかったのは、GlobalBTCやCMEの結果そのものよりも、自分がGrafana上の表示をどう読んでいたのかを見直すことでした。

最初に見ていたパネルでは、各リード市場ごとの構造判定が 0/1/2 のような状態値で表示されていました。自分の理解では、この 2 はかなり強い意味を持っていました。

要するに、

0 = 構造なし
1 = 構造はあるが、EVは足りない
2 = 構造があり、かつ執行コスト込みでEV>0

くらいの読み方をしていました。

つまり、2 にタッチしているなら、少なくとも「トレードシグナル候補として検討する価値がある状態」だと思っていたわけです。

しかし、CODEXに実装を確認させていくと、この理解は正確ではありませんでした。

実装上の 2 は、私が思っていたような「構造あり + 取引EVプラス」ではなく、

構造あり + そのdefinitionに紐づく値が0より大きい

という意味に近いものでした。

ここで問題になるのが、「そのdefinitionに紐づく値」の中身です。

fixed_horizon なら、primary horizonでの research net に近い値です。
follow_arrival なら、初回到達時の arrival net。
state_persistence なら、tail horizon の net。
residual_decay なら、residual improvement。

このうち、fixed_horizon は取引EVのproxyとしてまだ読めます。完全な実発注ベースのPnLではないにせよ、コスト込みの research net を見ているので、「取引候補に近い値」として扱う余地があります。

一方で、residual_decay はまったく違います。

CODEXの確認では、residual_decay_ev_bps の実体は abs(residual_primary) - abs(residual_tail) でした。つまり、primary時点の残差とtail時点の残差を比べて、どれだけ残差が縮んだかを見ています。これはPnLでも取引EVでもありません。あくまで「残差が改善したか」を見る構造指標です。

ここが今回の誤読の核心でした。

自分は「EV>0」と表示されているものを、当然のように「執行コスト込みで期待値がプラス」と読んでいました。
でも実際には、そこには複数の意味が混ざっていました。

たとえば residual_decay で 2 が出ていても、それは、

残差が縮んだ

という意味であって、

その場で売買すればコスト後に取れた

という意味ではありません。

この差はかなり大きいです。

しかも、メトリクス名にも誤読を誘う要素が残っていました。CODEXは、まだEVに見えるが取引EVではない名前として、obx_llp_ev_trend_median_30m_by_definition{definition="residual_decay"}residual_decay_ev_bps を挙げています。特に residual_decay については、Grafana上で ev_trend とbps表示が並ぶため、取引EVに見える余地があると指摘されていました。

これはかなり危ないです。

パネルを見る人間が、自分のように「EV」という言葉を強く読んでしまうと、実際には構造改善量にすぎないものを、取引可能性として読んでしまいます。

今回の確認で、さらに厳密な整理もできました。

CODEXによると、現在の実装では Trade Candidate State=2 に直接寄与するtrade value definitionは fixed_horizon のみです。residual_decay はStructure Stateを1にする理由にはなり得ますが、Trade Candidate State=2には直接寄与しません。つまり、新しい設計では residual improvement は「構造材料」にはなるが、「取引候補」にはならないように分離されています。

この分離はかなり重要です。

以前の自分の理解では、

residual_decay で 2 が出た
つまり残差が減衰していてEV>0
なら、何かしら取引候補に近いのではないか

という読み方になっていました。

しかし、正しくはこうです。

residual_decay で良い値が出る
それは「残差が縮む構造があるかもしれない」という意味
ただし、それ単体では取引候補ではない
取引候補にするには、fixed_horizon側でコスト込みのtrade EV proxyが通る必要がある

ここを分けられたのは大きかったです。

今回の勘違いは、単に「自分がパネルを雑に読んだ」という話ではありません。
もちろん、最終的に判断する自分が確認すべきだったのは間違いありません。ただ、パネルの名前、メトリクス名、0/1/2 という状態表示、そして EV という言葉が重なったことで、「これは取引EVを見ているのだ」と読めてしまう構造になっていました。

これは研究用ダッシュボードとしては、かなり危ない設計です。

なぜなら、bot開発では「構造がある」だけでは足りないからです。
構造があっても、コストを超えられなければ取引できません。
さらに、事後的に見えた構造と、リアルタイムで事前に使えたシグナルも別です。

今回の旧パネルでは、このあたりが一つの 2 に圧縮されすぎていました。

その結果、自分は、

構造判定
definition別の値の正負
取引EV
Trade Candidate

を同じものに近い感覚で読んでしまっていました。

ここで一度立ち止まれたのは良かったと思います。

もしこのまま進んでいたら、「GlobalBTCで11回、CMEで4回、EV>0が出ている」という見え方に引っ張られて、かなり危ない判断をしていた可能性があります。実際には、5営業日窓でGlobalBTCのresidual_decision2は147点あってもprimary net中央値は -29.27bps、cost passは0%。CMEもcurrent-eventではprimary net中央値 -30.28bpsで、primaryでは全敗でした。

つまり、見た目としては「EV>0っぽいもの」が出ていても、実際の取引候補としてはまったく別の評価が必要だったわけです。

今回の教訓はかなりはっきりしています。

EVという言葉は、本当に取引EVに近いものにだけ使う。
residual improvement や arrival net や tail net を、同じEVとして主判断に混ぜない。
構造があることと、取引できることを分ける。
2 という強い状態表示には、強い意味を持たせる。

この整理ができたことで、次にやるべきこともはっきりしました。

旧来のdefinition別判定は、研究用の診断として残す。
人間が見る主判断は、Structure State と Trade Candidate State に分ける。
そして、Trade Candidate State=2 は「構造あり + 執行コスト込みで取れそう」という、最初に自分が期待していた意味に限定する。

この方向で観測機を直すことにしました。

3. 「構造あり」という判定にも、設計思想のズレがあった

今回の問題は、単に「構造があることと、取引できることは別だった」という話ではありません。

それ自体は、すでに理解していたつもりです。
市場構造としては面白い動きがあっても、執行コストを超えられなければ取れない。短期リードラグ研究でも、構造の存在と収益化の間には大きな段差があることは、これまで何度も確認してきました。

今回の違和感は、もう少し手前にありました。

問題だったのは、観測機が「構造あり」と判定しているものを、自分がどのような意味で受け取っていたのかです。

自分は、Grafana上の構造判定パネルを見たときに、かなり強い意味で読んでいました。
たとえば 2 が出ていれば、

そのリード市場からBinance Japanへのリードラグ構造が見えている
しかも、何らかのEV的な値もプラスになっている
だから、少なくとも取引候補に近い構造がある

くらいに受け取っていました。

しかし、CODEXに実装を確認させると、マシン側の「構造あり」はもっと広いものでした。

現在の実装では、Structure Statefixed_horizon / follow_arrival / state_persistence / residual_decay のいずれかで structure_candidate=True になれば 1 になります。つまり、1つでも構造候補があれば、Structure State は点灯します。

ここで重要なのは、これら4つのdefinitionが見ているものがそれぞれ違うことです。

fixed_horizon は、primary horizonでlag側がlead方向へ動いたかを見る。
follow_arrival は、horizon群のどこかで期待方向へ到達したかを見る。
state_persistence は、primaryとtailの両方で正方向が続いたかを見る。
residual_decay は、lead move と lag gross の差がprimaryからtailへ縮んだかを見る。

全部、たしかに「構造らしさ」を見るための材料ではあります。

ただし、人間側が「構造あり」と聞いて想像していたものは、たぶんもう少し狭かった。

自分の中では、「構造あり」は、

そのリード市場の変化が、Binance Japan側の遅れた価格変化として、それなりに一貫して観測されている

くらいの意味でした。

さらに、旧パネルではそこに EV>0 的な表記や 2 という状態値が乗っていたため、

構造候補の一部
診断用の到達・持続・残差改善
取引候補に近いfixed horizonの反応

が、かなり近いものとして見えてしまいました。

ここに、マシン側の設計と人間側の理解のズレがありました。

マシン側は、複数のdefinitionを使って「構造候補」を広めに拾っていました。
これは研究用の観測機としては自然です。最初から狭くしすぎると、見えるはずの構造を落としてしまうからです。

一方で、人間側の自分は、その「広めに拾った構造候補」を、主判断に近いものとして読んでいました。

ここが危なかった。

特に residual_decay は典型です。
CODEX確認では、residual_decay_ev_bps の実体は abs(residual_primary)-abs(residual_tail) であり、PnLでも取引EVでもありません。つまり、残差が縮んだかどうかを見る指標です。

これは構造診断としては意味があります。
しかし、これを主判断の EV>0 と同じ見え方で表示すると、人間はかなり簡単に誤読します。

実際、自分は residual_decay の 2 を見て、

残差が減衰している
しかもEV>0
なら、取引候補に近いのではないか

と読みかけていました。

でも、正確にはそうではありませんでした。

正しくは、

residual_decay は、残差が縮む構造を見ている
それはStructure Stateを点灯させる材料にはなる
しかしTrade Candidate State=2には直接寄与しない

です。

CODEX確認でも、TRADE_VALUE_DEFINITIONSfixed_horizon のみで、residual_decay は active_structure にはなり得るが、active_trade_definition にはならず、trade_ev_bps にも使われないと説明されています。

つまり、マシン側では本来、

residual_decay = 構造診断
fixed_horizon = Trade Candidateの入力

という分離が必要でした。

しかし旧パネルでは、その分離が人間に伝わりにくかった。

ここで起きていたのは、単なる命名ミスではありません。
研究用に広く構造候補を拾う設計と、人間が主判断として読む画面設計が噛み合っていなかったということです。

研究用のマシンは、多少広めに拾ってもよい。
後から診断すればいいからです。

でも、人間が常時見る主画面は、広く拾ったものをそのまま見せると危ない。
特に EV2 のような強い表現があると、人間はそれを「判断してよい状態」と読んでしまいます。

だから今回、必要だったのは「構造と取引可能性を分ける」だけではありませんでした。

もっと正確には、

マシンが内部で拾う構造候補
人間が主判断として読む構造状態
取引候補として扱う状態

を分けることでした。

この整理に基づいて、CODEXには主判断パネルの再設計を依頼しました。

方針としては、既存のdefinition別 0/1/2 は研究用に下げる。
人間が見る主役は Structure StateTrade Candidate State にする。
そして Trade Candidate State=2 は、最初に自分が期待していた意味、つまり「構造あり + 執行コスト込みで取れそう」に限定する、という形です。

実装後の確認でも、Structure StateTrade Candidate State は分離されました。Trade Candidate State=2fixed_horizonprimary_net だけが対象になり、residual_decay / follow_arrival / state_persistence は構造診断・研究用に降格されています。

この修正によって、ようやく人間側の読み方とマシン側の設計が近づきました。

今後は、Structure State=1 を見たときに、

何らかの構造候補がある

と読む。

そして Trade Candidate State=2 を見たときにだけ、

取引候補としてレビューする価値がある

と読む。

この違いを画面上でも実装上でも分けたわけです。

今回の教訓は、かなり地味ですが大きいです。

botの観測機では、マシンが内部で広く拾うこと自体は悪くありません。
むしろ研究初期では必要です。

ただし、その広く拾ったものを人間の主判断画面にそのまま出すと、ほぼ確実に誤読が起きます。

特に、自分のようにGrafanaを見ながら判断を作るタイプのbotterにとっては、これはかなり危ない。

パネルの色。
0/1/2 の状態値。
EV という言葉。
Structure というラベル。

こういうものは、思った以上に判断を引っ張ります。

だからこそ、今回の修正では「構造あり」という言葉自体を見直す必要がありました。

構造ありとは何か。
それは、どのdefinitionで立っているのか。
それは診断用なのか、主判断用なのか。
それは取引候補に直結するのか。

ここを曖昧にしたまま進めると、また同じミスをします。

今回の核心は、多分ここです。

取引できるかどうか以前に、そもそも“構造あり”という表示を、自分が強く読みすぎていた。
マシンは研究用に広く構造候補を拾っていた。
しかし人間の主判断画面には、その広さをそのまま出してはいけなかった。
だからStructure StateとTrade Candidate Stateを分ける必要があった。

4. Structure State と Trade Candidate State に分け直した

ここまで確認して、次にやるべきことははっきりしました。

旧来のdefinition別パネルを、そのまま人間の主判断に使うのは危ない。
だから、主判断をもっと絞る必要がありました。

今回の修正方針は、パネルを増やすことではありません。
むしろ逆です。

人間が常時見るものを減らして、意味を強くする。

そのために、主判断を次の2つに分けました。

Structure State
Trade Candidate State

Structure State は、構造候補があるかを見るものです。
ここでは、fixed_horizon / follow_arrival / state_persistence / residual_decay のいずれかで構造候補が立っていれば、Structure State が点灯します。

つまり、これは広めの判定です。

Structure State=1 は、

何らかのリードラグ的な構造候補がある

という意味です。

ただし、これは取引候補ではありません。
residual_decayだけで点灯することもあります。follow_arrivalだけで点灯することもあります。つまり、Structure Stateはあくまで「研究上見る価値のある構造候補がある」という表示です。

一方で、Trade Candidate State はもっと狭くしました。

こちらは、

構造があり、かつ取引候補として扱える条件を満たしているか

を見るものです。

CODEXの確認では、現在の実装では Trade Candidate State=2 に直接使われるtrade value definitionは fixed_horizon のみです。residual_decay はStructure Stateを立てる理由にはなっても、Trade Candidate State=2には直接寄与しません。TRADE_VALUE_DEFINITIONSfixed_horizon のみに制限されているためです。

ここは、今回の修正でかなり重要なポイントでした。

旧パネルでは、residual_decay の残差改善も、follow_arrival の到達も、state_persistence のtail netも、かなり近い見え方をしていました。
その結果、自分はそれらをまとめて「EV>0っぽいもの」として読んでしまいました。

しかし、新しい設計では違います。

residual_decay は構造診断
follow_arrival も構造診断
state_persistence も構造診断
Trade Candidateに使うのは、現状 fixed_horizon のprimary net系だけ

という分離にしました。

これで、少なくとも主判断では、

構造がある
取引候補である

を同じ意味で読まなくて済むようになります。

Trade Candidate State の条件も、CODEXにかなり具体的に確認しました。

実装上は、ざっくり次のような流れです。

active structure がなければ no_structure
source がreadyでなければ source_not_ready
sessionが閉じていれば session_closed
allowed sessionでなければ session_not_allowed
lag側がreadyでなければ lag_not_ready
trade対象のstructureがなければ no_trade_structure
trade_ev_bps が閾値以下なら trade_ev_non_positive
cost_pass_rate が足りなければ cost_pass_rate_low
entry spread が広ければ spread_too_wide
すべて通れば ok

そして、reason == "ok" のときだけ Trade Candidate State=2 になります。

現在のconfigでは、条件はかなり保守的です。

min_trade_ev_bps は0.0。
min_cost_pass_rate は0.5。
max_entry_spread_bps は10.0bps。
allowed_session_labelsregular

つまり、単に構造が出ただけではTrade Candidateになりません。
fixed_horizon側でtrade EV proxyがプラスになり、rolling sample内でcost pass rateが50%以上あり、さらにBinance Japan側のentry spreadが10bps以下で、session条件も通っている必要があります。

この条件が最終的に正しいかは、まだ分かりません。

特に cost_pass_rate >= 0.5 を GlobalBTC / ETF / CME に共通で使うのは、まだ粗い前提です。CODEXも、GlobalBTCはprimary=60s、ETFは300s、CMEは180sとhorizonが違うため、同じcost_pass_rate閾値を同じ統計信頼度として扱うのはやや粗いと指摘していました。

ただ、それでも今はこの形で良いと思っています。

理由は、今回の目的が「最適なシグナルを作ること」ではなく、まず 構造候補と取引候補を混同しないこと だからです。

ここでいきなり市場ごとに細かく最適化すると、また別の問題が起きます。

何が効いたのか分からなくなる。
5営業日後に、ロジック変更の影響と市場構造の影響が混ざる。
そして、また「見えているものの意味」が曖昧になる。

なので、まずは主判断の意味を固定しました。

実装後、Grafanaのパネルも見直しました。

最初は Structure State | 3 LeadsTrade Candidate State | 3 Leads のように、3市場分を一つのパネルにまとめる形になっていました。
しかし、これは実際に見てみると分かりにくかったです。

一つのパネルに情報が詰まりすぎていて、GlobalBTC、ETF、CMEのどれがどういう状態なのか、直感的に見にくい。
そこで、自分から「これって一つのパネルに情報を押し込みすぎてないですか?シンプルに見にくいです」と指摘しました。

その後、主パネルは6つに分けました。

GlobalBTC | Structure
ETF | Structure
CME | Structure
GlobalBTC | Trade Candidate
ETF | Trade Candidate
CME | Trade Candidate

これで、市場ごとの現在状態がかなり見やすくなりました。

ただ、ここでもまだ問題がありました。

Statパネルとして表示されると、「これは現在の状態なのか?」「いつの判定なのか?」が分かりにくかったのです。
そこでさらに、パネル名を Now | ... StructureNow | ... Trade Candidate に変更しました。

加えて、Last sample age も追加しました。

これは地味ですがかなり大事です。

Now | Trade Candidate0 でも、その判定が1秒前のサンプルに基づくのか、数分前の古いサンプルに基づくのかで意味が違います。
データが新鮮で 0 なのか、そもそも更新されていないから 0 なのかを分ける必要があります。

最終的に、主画面には次の要素が並ぶ形になりました。

Now | Structure
Now | Trade Candidate
Last sample age
Structure History
Trade Candidate History
Trade EV bps
Cost Pass Rate
Entry Spread bps

これで、少なくとも人間が見る順番はかなり整理されました。

まず、データが生きているかを見る。
次に、Structureがあるかを見る。
そのうえで、Trade Candidateになっているかを見る。
さらに、Trade EV bps、Cost Pass Rate、Entry Spread bpsで中身を確認する。

以前のように、definition別の 0/1/2 を見て、いきなり「EV>0が出た」と読む構成ではなくなりました。

もちろん、definition別のパネルを完全に消したわけではありません。
fixed_horizon / follow_arrival / state_persistence / residual_decay は、研究用・診断用として残しています。

ただし、それらは主判断ではありません。

ここが大事です。

今後の読み方は、こうです。

Structure State=1
何らかの構造候補がある。診断を見る価値はある。

Trade Candidate State=1
構造はあるが、取引候補にはなっていない。reasonを見る。

Trade Candidate State=2
取引候補としてレビュー対象にする。ただし即執行ではない。

この設計にしたことで、ようやく「人間が見る主画面」と「マシンが内部で持っている研究用definition」が分かれました。

これが今回いちばん大きな修正だったと思います。

今回の件で、自分は改めて、Grafanaの見た目が思考に与える影響の大きさを感じました。

パネル名が EV になっている。
状態値が 2 になっている。
赤や緑で強く表示されている。
それだけで、人間はかなり強く意味を読み込んでしまいます。

だから、主判断パネルに出すものは、内部実装の都合ではなく、人間がどう読むかを基準に設計しないといけません。

今回の修正は、シグナルを強くする修正ではありません。
儲かりそうなものを見つけたわけでもありません。

ただ、これまでよりも、

何が構造候補なのか
何が取引候補なのか
何が診断用の中間値なのか

を分けて見られるようになりました。

これは地味ですが、次の検証の前提としてかなり重要です。

5. 市場別の現時点の整理

ここまでで、表示と判定の整理はかなり進みました。

ただし、表示を直しただけでは意味がありません。次に必要なのは、GlobalBTC・CME・ETFそれぞれについて、何を残し、何を否定し、次に何を見ればよいのかを具体的に分けることです。

現時点では、3市場を同じ温度感で見るべきではありません。

まず、現在のTrade Candidate gateは3市場で共通です。
Trade Candidate State=2になるには、fixed_horizon が構造候補になり、trade_ev_bps > 0.0cost_pass_rate >= 0.5entry_spread_bps <= 10.0bpssession_label in ["regular"] などを満たす必要があります。違いはgateそのものではなく、市場ごとのprimary horizon・window・sample数にあります。GlobalBTCはprimary=60s / window=1800s / valid>=10、ETFはprimary=300s / window=3600s / valid>=6、CMEはprimary=180s / window=5400s / valid>=6です。

この設定は、あくまで「次の検証に入るための暫定基準」です。
最適化済みの基準ではありません。

特に cost_pass_rate >= 0.5 を3市場に共通で使う点は粗いです。CODEXも、GlobalBTCの60秒、ETFの300秒、CMEの180秒を同じ統計信頼度として扱うのは研究上やや粗い前提だと指摘していました。

なので、次の5営業日で見るべきことは「このgateで勝てるか」だけではありません。
正確には、

この暫定gateで、どの市場が候補として残るか。
どの市場はgate以前に構造が薄いのか。
どの市場は構造はあるが、session・spread・cost passで落ちているのか。

を分けることです。

GlobalBTC:基準線として残す。ただし単独シグナルはかなり厳しい

GlobalBTCは、現時点では単独の執行シグナルとしてはかなり厳しいです。

最初の5営業日分析では、GlobalBTCの residual_decision2 は多く出ていました。にもかかわらず、primary net中央値は -29.27bps、best horizonでも -25.96bps、cost passは0%。事前に見えていた residual2 状態から次イベントを取る検証でも、cost passは0.6%だけでした。

この結果から、少なくとも今のBinance Japan BTC/JPYの29bpsコスト前提では、GlobalBTCを単独のTrade Candidate発火源として扱うのは無理があります。

ここで否定するラインは明確です。

GlobalBTCでStructure Stateが出ても、Trade Candidate State=2がほとんど出ない。
あるいはState=2が出ても、日次report上の trade_state_2_realized_primary_net_median が0bps以下、かつ trade_state_2_cost_pass_rate が50%未満で終わる。

この場合、GlobalBTCは単独シグナル候補から外します。

ただし、GlobalBTCを完全に切る必要はありません。
むしろ、役割は別にあります。

GlobalBTCは、BTCUSDT × USDJPY によるfair valueの基準線です。CODEX確認では、GlobalBTCの regular はBTCの24/7時間ではなく、USD/JPYを含む合成fair valueのFX 24x5カレンダーを指していると整理されています。BTC legは24/7でも、fair value全体はFX側の可用性に制約されるため、この制約自体は理解できます。

なので、GlobalBTCの現実的な役割は、

Binance Japan BTC/JPYが、グローバルBTCとUSDJPYから見たfair valueに対してどれくらいズレているかを見る基準線

です。

この用途なら、GlobalBTCは残す価値があります。

次の検証で見るべきラインはこうです。

見るもの判断
Trade Candidate State=2 が出るそのイベントだけ個別レビュー
Structure State=1だがTrade Candidateにならないreasonを見る。特に trade_ev_non_positive / cost_pass_rate_low / spread_too_wide
residual系だけが強い単独シグナルではなく、fair value / residual監視線として残す
5営業日でState=2が0、または実現net中央値<=0単独執行候補からは外す
spreadが極端に薄い時間帯だけ改善「低spread限定filter」として再検討

自分の現時点の仮説は、GlobalBTCは エントリーシグナルではなく、状態フィルター です。

たとえば、CMEやETF側の構造を見るときに、

BJがGlobal fair valueから大きくズレているか
そのズレがすでに縮み始めているか
そもそもBJ側の価格が薄い状態なのか

を見る補助線として使う。

この仮説を修正する条件は、GlobalBTCでTrade Candidate State=2が複数回出て、かつ日次report上で trade_state_2_realized_primary_net_median > 0trade_state_2_cost_pass_rate >= 0.5 が確認できた場合です。

逆に、5営業日でState=2が出ない、または出てもrealized netがマイナスなら、GlobalBTCは取引候補ではなく基準線に固定します。

CME:研究継続候補。ただしprimary=180sのまま執行には進めない

CMEは、3市場の中では一番「次の研究候補」として残す価値があります。

理由は、GlobalBTCより制度的に遠く、ETFよりはBTC価格発見に近いからです。CME BTC先物は米国市場時間・先物市場の参加者・伝統金融側の価格発見を含むため、Binance Japan BTC/JPYへの短期〜中期の影響を見る対象としてはまだ自然です。

ただし、現時点で執行候補ではありません。

最初の5営業日分析では、CMEの residual_decision2 は84点ありましたが、current-eventのprimary net中央値は -30.28bps、best net中央値も -21.54bpsでした。事前にresidual2が見えていた次イベントでは25件中9件だけ後続horizonでcost passしましたが、primaryでは全敗でした。

ここから、CMEについてはこう整理します。

CMEには「遅れて大きく動く例」はある。
ただし、primary=180sの現行Trade Candidate定義では、まだ期待値シグナルではない。

したがって、CMEで見るべきなのは、単純なTrade Candidate State=2の有無だけではありません。
特に見るべきは、どのhorizonで改善が出ているのかです。

現時点の仮説は、

CMEはprimary=180sでは短すぎるか、ノイズに埋もれている。
h900/h1800、つまり15分〜30分側でようやく追随・調整が見える可能性がある。

です。

この仮説の根拠は、事前にresidual2が見えていた次イベントで、primaryでは全敗だった一方、後続horizonでは一部cost passが出ていたことです。まだ期待値シグナルではありませんが、完全に捨てるには早い。

CMEについての判定ラインは、次のように置きます。

条件判断
regular sessionでStructure State=1が継続的に出る研究継続
Trade Candidate State=2が出る個別イベントレビュー対象
State=2の realized_primary_net_median > 0 かつ cost_pass_rate >= 0.5fixed_horizon候補として継続
primaryは弱いがh900/h1800でnet改善CME専用horizon再設計
session_not_allowed が多いCME session定義の見直し
source_not_ready / lag_not_ready が多い価格ソース品質の問題として優先修正
5営業日でStructureもTrade Candidateもほぼ出ないCME短期lead-lag優先度を下げる

ここで注意したいのは、CMEを「すぐ取れそうな候補」として扱わないことです。

次の5営業日は、CMEについては、

fixed_horizon=180sでTrade Candidateが出るか
出ない場合、reasonは何か
出た場合、realized primary netはどうか
primaryではなく後続horizonに寄せるべきか

を確認する期間です。

もし、Trade Candidate=2が出てもrealized primary netがマイナスなら、primary=180sのTrade Candidate定義は棄却します。
ただし、h900/h1800で明確に改善が見えるなら、CMEは「短期primary」ではなく「15〜30分追随」の別定義として残します。

逆に、h900/h1800でもnet中央値がマイナス、cost passも弱いなら、CMEは研究継続候補から優先度を下げます。

ETF:現行の短期lead-lag定義は否定寄り。別枠に逃がす

ETFは、現行の短期lead-lag定義ではかなり否定寄りです。

理由は2つあります。

1つ目は、市場として遠いことです。
US spot BTC ETFは、Binance Japan BTC/JPYに対して、制度・取引時間・参加者・情報反映の経路が遠い。GlobalBTCやCMEよりも、短期価格追随のリード対象としては距離があります。

2つ目は、観測定義がETFの特性に合っていなかったことです。
CODEXの最初の分析では、ETFは max horizon=3600s かつ structure_window=3600s だったため、評価完了時点でサンプルがローリング窓から落ちる問題がありました。イベントログ再計算ではETFにもresidual2は26点ありましたが、現行ライブ窓では「事前に見えて次を取る」状態が0件でした。

さらに、ETFの regular は米株コア時間 09:30-16:00 ET で、pre/afterは別ラベルです。
つまり、ETFで構造が出ても、pre/after/closedであればTrade Candidate State=2にはなりません。CODEXも、allowed_session_labelsがregularに限定されているため、pre/after/weekendに構造が出てもState=2にならず、特にETFでは「構造ありなのに候補にならない」ように見える可能性があると指摘しています。

ここから、ETFについては次のように切ります。

現行の短期lead-lag対象としては、かなり否定寄り。
ただし、ETFを完全に捨てるのではなく、session/regime用の別枠に移す。

ETFについて次の5営業日で見るべきものは、Trade Candidate State=2の有無よりも、まずsession別の落選理由です。

見るもの判断
session_not_allowed が多いETFは短期Trade Candidateではなくsession/regime候補
no_trade_structure が多いresidual等はあってもfixed_horizonには繋がっていない
cost_pass_rate_low が多い構造はあっても収益性不足
source_not_ready が多いソース品質・時間帯設計の問題
regular中でもStructureがほぼ出ないETF短期lead-lagは棄却
regular外にStructureが偏るsession-flow / 日跨ぎ研究へ移行

自分の現時点の仮説は、ETFは 短期エントリーシグナルではなく、日跨ぎ・session-flow・regime側の材料 です。

たとえば、

前日ETF regular sessionでのBTC ETF主導の買い/売り圧力
regular終了後のBTC現物側への影響
翌日のBinance Japan側の残差状態
ETF session中の出来高や価格変化が、BJ側の流動性やspread状態に与える影響

のような方向です。

ただし、これは今回のTrade Candidate検証とは別テーマです。
なので、ETFについては次の5営業日で短期lead-lagとしての見込みが薄いことを確認したら、短期枠からは切ります。

ETFを短期枠に残す条件は厳しめで良いです。

regular session内でStructure State=1が複数回出る。
そのうちTrade Candidate State=2が少なくとも発生する。
さらにState=2のrealized primary net中央値がプラス、cost pass rateが50%以上になる。

これが出ないなら、ETFを短期lead-lag候補として残す理由は薄いです。

次の5営業日で最終判断に使うもの

ここで重要なのは、Grafanaだけで判断しないことです。

CODEX確認では、live Grafanaと日次reportは完全一致ではありません。GrafanaのTrade Candidate Stateは現在時点のrolling stateであり、日次reportは各valid eventの直前prior windowからtrade snapshotを再構築して集計します。5営業日後に候補判定ロジックの成績を見るなら、最終判断の正は日次report側にすべきです。

したがって、次の検証では以下を見ることにします。

項目目的
trade_state_2_countTrade Candidateが実際に何回出たか
trade_state_2_ratiovalid event中の候補化率
trade_state_2_realized_primary_net_median候補化後の実現primary net
trade_state_2_cost_pass_rate候補化後にコストを超えた割合
trade_reason_counts_json落選理由
structure_on_ratio構造候補の発生率
decision_2_ratiodefinition別の旧判定がどれくらい出たか
event_valid / event_rejected母数とreject状況
reject_reasons_jsonデータ品質・session・sample不足の原因

CODEX確認でも、5営業日後に見るべき項目として、これらのtrade_state系・definition別構造系・reject系が挙げられています。

自分の中での判断フローは、次のように置きます。

5営業日後の判断フロー1. まず health を見る
- source_alive
- sample age
- event_valid / event_rejected
- reject_reasons2. 次に Structure を見る
- structure_on_ratio
- definition別 structure_on
- 市場別に構造候補が出ているか3. 次に Trade Candidate を見る
- trade_state_2_count
- trade_state_2_ratio
- trade_reason_counts4. State=2 がある場合
- realized_primary_net_median > 0 か
- cost_pass_rate >= 0.5 か
- entry spread は許容範囲か
- 個別イベントを確認する5. State=2 がない場合
- no_structure なのか
- no_trade_structure なのか
- trade_ev_non_positive なのか
- cost_pass_rate_low なのか
- session_not_allowed なのか
- spread_too_wide なのか
に分解する

このフローで見れば、「出た/出ない」だけで終わらず、次に何を変えるべきかが分かります。

現時点の結論

5章時点での市場別結論は、かなりはっきりしています。

市場現時点の扱い5営業日後の判断
GlobalBTCfair value / residual基準線。単独執行は厳しいState=2の実現netがプラスなら再評価。ダメなら基準線に固定
CME研究継続候補。ただしprimary=180sのまま執行は不可primaryでダメならh900/h1800別定義へ。後続horizonもダメなら優先度下げ
ETF短期lead-lagは否定寄り。session/regime候補regular短期で出ないなら短期枠から外す。session-flow/日跨ぎへ移す

今回の段階で、無理に「どれが当たりか」を決める必要はありません。

むしろ大事なのは、切るラインを先に決めることです。

GlobalBTCは、Trade Candidateとして取れなければ基準線に固定する。
CMEは、primaryで取れなければhorizonを見直す。それでもダメなら優先度を下げる。
ETFは、短期枠で取れなければ別研究に逃がす。

これで、次の5営業日観測の意味がかなり明確になります。

次に見るのは、都合の良いパネルではありません。
市場ごとに、どの理由で落ちたのか。
どの条件なら残せるのか。
どの条件なら切るのか。

ここまで決めてから観測に入ることで、少なくとも次の判断は前よりかなりブレにくくなるはずです。

6. 次の5営業日で見ること

今回の修正を入れたことで、次の5営業日は「なんとなく観測する期間」ではなくなりました。

見るものはかなり絞れています。

まず、Grafanaは現在状態の監視に使います。
一方で、5営業日後の判断には日次reportを使います。

ここは明確に分けます。

CODEX確認でも、live Grafana の obx_llp_trade_candidate_state_0_1_2 は現在時点の rolling state であり、日次reportは各valid eventの直前prior windowからtrade snapshotを再構築して集計するものだと整理されていました。したがって、5営業日後に候補判定ロジックの成績を見るなら、最終判断の正は日次report側に置くべきです。

つまり、Grafanaは運用監視。
日次reportは検証判断。

この分け方で進めます。

まずはhealthを確認する

最初に見るのは、シグナルではありません。

まず観測機がまともに動いているかを見ます。

具体的には、毎朝 make morning-check を通します。
今回の修正で、morning_checkには [2c] Structure / trade candidate main decision metrics が追加され、obx_llp_structure_state_0_1obx_llp_trade_candidate_state_0_1_2obx_llp_trade_ev_bps をPrometheusから読むようになっています。欠損していればNG、Trade Candidate Stateが0/1/2以外ならNG、正常なら現在値を表示する、という生存確認です。

ここで見るべきことは、次の通りです。

確認項目見る理由NG時の扱い
make morning-check がPASSするか観測機の基本稼働確認FAILならその日の判定は保留
structure_state_0_1 が出ているか主判断メトリクス欠損の確認欠損なら実装/Prometheus側を確認
trade_candidate_state_0_1_2 が出ているかTrade Candidate判定が出力されているか欠損なら検証不能
trade_ev_bps が出ているかTrade Candidate側の値が取得できているか欠損なら判定不能
Last sample age判定が新鮮なデータに基づくか古ければsource/lagの停止を疑う
event_valid / event_rejected母数が育っているかrejected偏重なら理由を見る

これを通さずにStructureやTrade Candidateを見ても意味がありません。

特に、Grafana上で No Structure と出ていても、それが「本当に構造なし」なのか、「サンプルが古いだけ」なのかは分ける必要があります。今回のダッシュボードでは Last sample age も追加したので、少なくとも現在値が何秒前のサンプルに基づいているかは確認できます。パネル上でも、GlobalBTC/ETF/CMEのsample ageが1〜2秒程度で出ており、現在状態の判定鮮度を見る導線は作れています。

日次reportで見る項目を固定する

5営業日後の判断に使うのは、日次reportです。

ここでは、次の項目を中心に見ます。

項目意味判断に使うポイント
trade_state_2_countTrade Candidate State=2の件数0なら取引候補は出ていない
trade_state_2_ratiovalid event中のState=2比率候補化率を見る
trade_state_2_realized_primary_net_medianState=2後の実現primary net中央値0bps超が最低ライン
trade_state_2_cost_pass_rateState=2後にコスト超えした割合0.5以上が暫定合格ライン
trade_reason_counts_json落選理由次の改善対象を決める
structure_on_ratio構造候補の発生率構造自体があるかを見る
decision_2_ratiodefinition別の旧2比率診断用。主判断ではない
event_valid / event_rejected母数とreject状況データ不足か構造不足かを分ける
reject_reasons_jsonreject理由source/session/sample問題の切り分け

CODEXも、5営業日後に見るべき項目として、trade_state_2_ratiotrade_state_2_counttrade_reason_counts_jsontrade_state_2_realized_primary_net_mediantrade_state_2_cost_pass_rate、definition別の structure_on_ratiodecision_2_ratioevent_valid/event_rejectedreject_reasons_json などを挙げています。

ここで重要なのは、decision_2_ratio を主判断にしないことです。

definition別の 2 は、まだ診断用です。
主判断は trade_state_2_counttrade_state_2_realized_primary_net_median です。

State=2が出た場合の判定

Trade Candidate State=2が出た場合でも、即執行ではありません。

State=2は「レビュー対象」です。

そのイベントについて、最低限見るのは次の項目です。

State=2 event review- event_ts
- scenario
- session_label
- prior_trade_ev_bps
- prior_trade_cost_pass_rate
- prior_trade_entry_spread_bps
- bj_entry_spread_bps
- source_quality_flag
- lag_quality_flag
- research_net_move_bps
- cost_pass_flag

このあたりは、今回CODEXに追加実装を依頼している明細CSVの目的そのものです。もともとイベントログには prior_trade_candidate_state_0_1_2prior_trade_candidate_reasonprior_trade_ev_bpsprior_trade_cost_pass_rateprior_trade_entry_spread_bpssession_labelsource_quality_flaglag_quality_flagbj_entry_spread_bpsresearch_net_move_bpscost_pass_flag などが記録されており、日次reportでもState=2の件数、実現primary net中央値、実現cost pass rateまでは出せることが確認されています。

State=2が出た場合の判断ラインは、まず次のように置きます。

条件判断
realized_primary_net_median > 0 かつ cost_pass_rate >= 0.5候補継続
realized_primary_net_median <= 0fixed_horizon定義としては不合格
cost_pass_rate < 0.5方向は合っても再現性不足
entry_spread_bps > 10bps が多いBJ側の執行条件が悪い
source/lag quality badが混ざるデータ品質由来の偽候補を疑う
sessionが偏るsession限定の再設計対象

ここでの最低合格ラインは、realized_primary_net_median > 0cost_pass_rate >= 0.5 です。

ただし、これはあくまで一次判定です。
State=2が数件しか出ない場合は、中央値やcost pass rateを過信しません。少数サンプルなら「候補継続」ではなく「観測継続」です。

自分の中では、5営業日での判断をこう分けます。

State=2件数realized net / cost pass判断
0件-候補なし。落選理由を見る
1〜2件プラスでも参考値判断保留。個別イベントを見る
3〜5件プラスなら追加観測弱い候補
6件以上net>0 & cost pass>=0.5候補として次フェーズ検討
6件以上net<=0 or cost pass<0.5棄却または定義変更

この件数ラインは、統計的に十分という意味ではありません。
ただ、5営業日という短い窓で「何を判断してよいか」を分けるための実務上の仮置きです。

少数サンプルで勝ったように見えても、それはまだ勝ち筋ではありません。
逆に、0件なら少なくとも現行gateでは候補が出ていない、と判断できます。

State=2が出なかった場合の見る順番

State=2が出なかった場合も、すぐに「この市場はダメ」とはしません。

まず、なぜ出なかったのかを見ます。

特に見るのは、trade_reason_counts_json と、今回追加を依頼している session別・structure_on限定の落選理由です。

State=2が出ない理由は、最低でも次のように分かれます。

理由意味次の判断
no_structureそもそも構造候補がない市場/definitionの優先度を下げる
no_trade_structure構造はあるがfixed_horizonに繋がらないresidual等は診断用。Trade定義見直し
trade_ev_non_positivefixed_horizonのnetがプラスでない収益性不足。基本は棄却寄り
cost_pass_rate_lownetプラスの再現率が低い再現性不足。閾値/条件を見直す
spread_too_wideBJ側のentry spreadが広い低spread時間帯限定の可能性
session_not_allowedregular外で構造が出ているsession定義の見直し
source_not_readylead sourceが不安定データソース側の問題
lag_not_readyBJ側データが不安定lag側取得・品質を確認

ここで重要なのは、落選理由によって次の行動が変わることです。

trade_ev_non_positive が多いなら、構造があっても取れない可能性が高い。
session_not_allowed が多いなら、市場特性とsession設計が合っていない可能性があります。
source_not_ready が多いなら、仮説以前にデータが悪い。
no_trade_structure が多いなら、residualやarrivalの構造はあるが、fixed_horizonの取引候補には繋がっていない。

この切り分けのために、今CODEXに追加で依頼しているのが、trade_reason_counts_by_session_jsonstructure_on_trade_reason_counts_jsonstructure_on_trade_reason_counts_by_session_jsontrade_state_by_session_json などの集計です。CODEX確認でも、現状のreportではscenario別の落選理由は見える一方、session別やStructure=1に限定した理由集計は不足していると整理されていました。

これはロジックを強くするための実装ではありません。
次の検証で、落ちた理由を間違えないための記録強化です。

市場別に見るもの

5営業日後は、3市場を同じ基準で雑に並べません。
市場ごとに見るポイントを変えます。

GlobalBTC

GlobalBTCは、まず単独Trade Candidateとしては厳しめに見ます。

見るものは、

GlobalBTC- trade_state_2_count
- trade_state_2_realized_primary_net_median
- trade_state_2_cost_pass_rate
- spread_too_wide の比率
- trade_ev_non_positive の比率
- residual系Structureだけが出ていないか

判断ラインはこうします。

結果判断
State=2が0件単独シグナル候補から外す
State=2は出るがrealized net<=0単独シグナル候補から外す
State=2は出るがspread_too_wideが多い低spread限定を検討
residual系だけが出るfair value / residual基準線に固定
State=2が複数出てnet>0/cost pass>=0.5低優先で追加観測

現時点の基本仮説は、GlobalBTCはエントリー信号ではなく基準線です。

CME

CMEは、まだ研究継続候補として見ます。

見るものは、

CME- regular session中のStructure発生
- trade_state_2_count
- trade_state_2_realized_primary_net_median
- trade_state_2_cost_pass_rate
- cost_pass_rate_low の比率
- no_trade_structure の比率
- h900/h1800側の改善有無

判断ラインはこうします。

結果判断
primary=180sでState=2が出てnet>0fixed_horizon候補として継続
primaryは弱いがh900/h1800で改善CME専用horizonへ再設計
cost_pass_rate_lowが多い構造はあるが収益性不足
source_not_readyが多いCME source品質を優先修正
5営業日でStructureも薄いCME短期lead-lagの優先度を下げる

CMEは「primary=180sで取れるか」だけで終わらせません。
ただし、h900/h1800でもnetがマイナスなら、研究優先度は下げます。

ETF

ETFは、短期Trade Candidateとしては否定寄りに見ます。

見るものは、

ETF- regular session内のStructure発生
- session_not_allowed の比率
- no_trade_structure の比率
- cost_pass_rate_low の比率
- pre/after/closedにStructureが偏っていないか

判断ラインはこうします。

結果判断
regular内でもStructureがほぼ出ない短期lead-lagは棄却
Structureは出るがsession_not_allowedが多いsession-flow/日跨ぎへ移行
no_trade_structureが多いresidual等はあるがfixed_horizonに繋がらない
State=2が出てもnet<=0短期Trade Candidateから外す
State=2が複数出てnet>0/cost pass>=0.5例外的に追加観測

ETFは、短期で無理なら完全に捨てるというより、別枠に移します。
具体的には、日跨ぎ、session-flow、regime判定です。

途中で変えないこと

次の5営業日で一番大事なのは、途中で定義を動かさないことです。

変えないものは、次の通りです。

凍結するもの- Trade Candidate State の基本定義
- fixed_horizonのみをTrade Candidate入力にする方針
- min_trade_ev_bps = 0.0
- min_cost_pass_rate = 0.5
- max_entry_spread_bps = 10.0
- allowed_session_labels = ["regular"]
- 市場ごとの primary horizon / window / min_valid_samples

もちろん、プロセス停止、データ欠損、パネル描画バグ、レポート出力バグのようなhealth系は直します。
ただし、閾値や判定ロジックを途中で変えた場合、その時点から観測期間はリセット扱いにします。

ここを曖昧にすると、5営業日後に何を評価しているのか分からなくなります。

次の5営業日の結論パターン

5営業日後の結論は、いきなり「執行Go」ではありません。

結論パターンは、次のどれかです。

結論条件次の行動
継続観測State=2少数、netは悪くないが母数不足さらに5営業日見る
定義修正StructureはあるがTrade Candidateにならない理由が明確horizon/session/gateを1点だけ修正
優先度下げStructureもTrade Candidateも薄い別テーマへリソース移動
別枠化ETFなど、短期ではなくsession/regime向き別scenarioとして再設計
棄却State=2が出てもrealized net<=0が続く現行定義を切る

ここで絶対に避けたいのは、State=2が1〜2件出ただけで「当たりかも」と読むことです。

逆に、State=2が出ないだけで即棄却するのも危ない。
落選理由を見て、no_structure なのか、session_not_allowed なのか、cost_pass_rate_low なのかを分けます。

次の検証の目的

今回の5営業日は、勝ち筋を確定するための期間ではありません。

目的はもっと限定します。

新しいStructure State / Trade Candidate Stateの定義で、誤読なく市場別の継続・修正・優先度下げを判断できるか。

これです。

GlobalBTCは基準線として固定するのか。
CMEはhorizonを伸ばして再検証する価値があるのか。
ETFは短期lead-lagから外してsession/regimeへ移すべきか。
そして、Trade Candidate State=2が本当にレビュー対象として機能するのか。

この4つを見ます。

今日までの修正で、ようやく「何が出たか」ではなく、「なぜ出たか」「なぜ落ちたか」を見られる形に近づきました。

次の5営業日は、そのための検証期間です。

7. 今日のまとめ

今日は、リードラグ観測機に溜まった5営業日分のデータをもとに、GlobalBTC・CME・ETFの現状を判断するつもりで作業を始めました。

最初に見たかったのは、Grafana上で見えていた EV>0 らしき状態です。
特に、GlobalBTCやCMEの residual_decay 系パネルで 2 にタッチしていたものが、本当にトレードシグナル候補になるのか、執行に繋げられるのかを確認しようとしていました。

しかし、作業を進めるうちに、そもそも自分が見ていた EV>0 の意味が、実装上の意味とズレていたことが分かりました。

旧パネル上の 2 は、自分の中では、

構造あり
かつ
執行コスト込みでEV>0
つまり、トレードシグナル候補に近い状態

という意味で読んでいました。

しかし実際には、

構造あり
かつ
definition別の値が0より大きい

という意味でした。

しかも、そのdefinition別の値には、fixed_horizon の primary net だけでなく、follow_arrival の arrival net、state_persistence の tail net、residual_decay の residual improvement も含まれていました。

特に residual_decay は重要です。
residual_decay_ev_bps は、実体としては abs(residual_primary)-abs(residual_tail)、つまり残差がどれだけ縮んだかを見る値であり、PnLでも取引EVでもありません。

ここを自分は、かなり危うく読み違えていました。

残差が縮むこと。
構造候補があること。
definition別の値がプラスであること。
そして、実際にBinance Japan BTC/JPYでコスト後に取れること。

この4つは別です。

今回の作業では、この別物をいったん分け直しました。

そのために、主判断を Structure StateTrade Candidate State に分けました。

Structure State は、構造候補があるかを見るものです。
fixed_horizon / follow_arrival / state_persistence / residual_decay のいずれかで構造候補が立てば、Structure State は点灯します。

一方で、Trade Candidate State は、取引候補として扱えるかを見るものです。
現在の実装では、Trade Candidate State=2 に直接使うのは fixed_horizon のみです。residual_decay はStructure Stateを立てる材料にはなりますが、Trade Candidate State=2には直接寄与しません。

この分離によって、少なくとも主判断画面では、

構造候補がある
取引候補である
診断用の中間値である

を分けて見られるようになりました。

今日の作業は、エッジを発見した日ではありません。
むしろ、エッジがあるように見える表示を、誤読しにくい形へ直した日です。

これは地味ですが、かなり重要です。

もしこの修正をしないまま次の5営業日に進んでいたら、またGrafana上の 2EV という表記に引っ張られていた可能性があります。
「構造が出た」「EV>0が出た」「これは取れるかもしれない」と読み、実際には residual improvement や診断用valueを取引EVとして読んでしまう。これはかなり危ないです。

だから今回は、シグナルを強くする前に、判断の前提を締め直しました。

市場別の整理も、いったん明確になりました。

GlobalBTCは、現時点では単独の執行シグナル候補としては厳しいです。
ただし、BTCUSDT × USDJPYによるfair value、つまりBinance Japan BTC/JPYがグローバル基準からどれくらいズレているかを見る基準線としては残す価値があります。

CMEは、研究継続候補です。
ただし、primary=180sのまま執行へ進むのは無理があります。今後は、regular session、visible-prior、h900/h1800側の改善、cost pass rate、落選理由を見ながら、CME専用のhorizon再設計に進む価値があるかを判断します。

ETFは、現行の短期lead-lag対象としては否定寄りです。
US spot BTC ETFは、Binance Japan BTC/JPYに対して市場構造として距離があります。今後も見るなら、短期エントリーシグナルではなく、session-flow、日跨ぎ、regime判定のような別枠に移す方が自然です。

次の5営業日で見るものも決めました。

Grafanaは、運用監視と現在状態の確認に使います。
最終判断には日次reportを使います。CODEX確認でも、live Grafanaは現在時点のrolling state、日次reportは各valid event直前のprior windowから再構築した集計であり、5営業日後の検証判断では日次reportを正にすべきだと整理されています。

具体的には、

trade_state_2_count
trade_state_2_ratio
trade_state_2_realized_primary_net_median
trade_state_2_cost_pass_rate
trade_reason_counts_json
structure_on_ratio
event_valid / event_rejected
reject_reasons_json

を見ます。

そして、State=2が出た場合は、即執行ではなくレビュー対象にします。
State=2が出なかった場合も、即棄却ではなく、なぜ出なかったのかを分けます。

no_structure なのか。
no_trade_structure なのか。
trade_ev_non_positive なのか。
cost_pass_rate_low なのか。
spread_too_wide なのか。
session_not_allowed なのか。
source_not_ready なのか。

ここを分けることで、次に何を直すべきか、あるいはどの市場の優先度を下げるべきかが判断しやすくなります。

今回CODEXに追加で依頼しているのも、まさにこのためです。
ロジックを強くするためではなく、次の検証で「なぜ出たか」「なぜ落ちたか」を後から切り分けられるようにするための記録強化です。

一方で、ここから先はやりすぎにも注意します。

パネルを増やし続ける。
診断項目を増やし続ける。
気になった条件をその場で変える。
CODEXに調査を投げ続けて、自分の判断を後回しにする。

これは避けたいです。

次の5営業日は、定義を固定して観測します。
health系の修正はしますが、Trade Candidateの基本定義や閾値、horizon、session条件を途中でいじるなら、そこから観測期間はリセット扱いにします。

ここまでが、今日の開発・観測機側のまとめです。

ただ、今回の件で改めて感じたのは、実装やパネルだけを整えても足りないということです。

結局、リードラグ構造を見ているときに大事なのは、

なぜその市場が先に動くのか
なぜ別の市場が遅れて動くのか
その遅れは誰の制約から生じているのか
そのズレはなぜ裁定されずに残るのか
そして、それはなぜ自分のような個人botterが取れる余地になるのか

を説明できることです。

ここが弱いままだと、Grafana上に何か面白い形が出ても、それを「金になる構造」として扱ってよいのか判断できません。

今の自分に必要なのは、市場で生じた現象を、参加者や取引構造全体の中で説明する力です。

そのために、引き続き書籍『市場と取引』を読み進めます。
単に知識として読むのではなく、いま自分が観測している現象に引きつけて読みたい。

たとえば、CMEが動いたあとにBinance Japanが遅れて反応するなら、その間に何が起きているのか。
参加者は誰なのか。
裁定できる人とできない人の違いは何か。
取引時間、在庫、ヘッジ、流動性、スプレッド、注文の出し方、情報の見え方がどう関わっているのか。

こういう問いに答えられるようにならないと、「これって金になるのは何故?」を自分の言葉で説明できません。

逆に言えば、そこを説明できるようになることが、今の自分にとって戦略立案力を伸ばす最短経路だと思っています。

これはたぶん、かなり妥当な方向です。

AIに実装を手伝わせることはできます。
CODEXに調査をさせることもできます。
Grafanaに観測結果を表示させることもできます。

でも、最終的に、

これは市場構造として意味があるのか
これは取引可能な歪みなのか
これはただの見かけの相関なのか
これは切るべきなのか、残すべきなのか

を決めるのは自分です。

その判断を強くするには、実戦データを見るだけでなく、市場構造そのものへの理解を深める必要があります。

今日の作業は、その意味でも良い警告でした。

観測機が何かを出したとしても、それをどう読むかは人間側の問題です。
そして、人間側の読みが浅ければ、きれいなパネルも誤読の入口になります。

次の5営業日は、観測機の新しい定義を固定して見る。
同時に、自分自身も『市場と取引』を読みながら、市場で起きている現象を参加者と取引構造の中で説明する訓練を続ける。

今日の結論は、こうです。

旧パネルのEV表示は、自分の想定よりも広い意味を持っていた。
そのままでは構造候補と取引候補を混同する危険があった。
そこで、Structure StateとTrade Candidate Stateを分けた。
市場別には、GlobalBTCは基準線、CMEは研究継続候補、ETFは短期lead-lagからは否定寄りと整理した。
次の5営業日は、日次reportを正として、候補が出た理由・落ちた理由を切り分ける。
そして並行して、市場構造の理解を深め、「これがなぜ金になるのか」を自分の言葉で説明できる力を鍛える。

今日は、勝てるシグナルを見つけた日ではありません。

でも、勝てそうに見えるものを誤読しないための仕組みを、一段まともにした日ではあります。
この違いはかなり大きいと思っています。

-Bot, CEX, 開発ログ