こんにちは、よだかです。
今日は lead-lag 観測機の表示と判定ロジックを整理していました。作業自体はそこまで派手ではないのですが、やってみてわかったのは「見える情報を増やせば判断しやすくなるわけではない」という、かなり重要な罠でした。機械の内部で必要な情報と、人間が常時見たい情報は別物です。そこで今回は、主パネルを Structure Candidate と EV Judge の2つに絞り、今の時点で何をリードラグ構造候補と呼ぶのかも暫定的に定義し直しました。

-
-
🛠️開発記録#511(2026/4/12)リードラグ研究DAY7part.2mempool実装から一歩引き、資金移動・再配分の遅れを狙う3候補を整理した話
続きを見る
今日のゴール
今日のゴールは、lead-lag 観測機をただ「動くもの」として進めることではなく、自分が判断できるものに引き戻すことでした。
もともと今回やりたかったのは、3つのリード候補市場を並べて観測し、「構造があるのか」と「コスト後でも残るのか」を見られる状態を作ることです。方向性自体は間違っていませんでした。けれど、実際にパネルを見ながら考えていく中で、だんだん頭がこんがらがってきました。何が起きているのかというと、見ている情報の量が多いというより、別の役割を持つ情報が同じ場所に押し込まれていたんですよね。
たとえば、値動きそのものの事実と、サンプルがちゃんと取れているかどうかと、構造候補の判定条件と、さらにその先のEV判定までが、近い場所で並んでいました。どれも必要な情報ではあるのですが、人間側の都合からすると、常時同じ重みで見たいものではありません。機械の内部ロジックとして自然な並び方と、人間が判断しやすい並び方は、どうやら別物でした。
そこで今日の作業は、単なるパネルの見た目調整ではなく、「今この観測機で何を主役として見るのか」を整理し直すことになりました。結論から言うと、主パネルとして常時見たいのは Structure Candidate と EV Judge の2つだけです。逆に、Raw の値動きや Sample Flow、Reject の事情などは、必要なときだけ掘る診断情報として分けた方が良い。この切り分けをしないと、見える情報は増えても、判断はむしろ壊れる。今日はそこをかなり強く実感した日でした。
何が混乱の原因だったのか
今回いちばん大きかったのは、情報が足りなかったことではなく、必要な情報の役割が混ざっていたことでした。
最初は、「もっと見えるようにすれば判断しやすくなるはずだ」と思っていました。実際、開発しているとあれもこれも出したくなります。lead 側の動き、lag 側の追随、サンプル数、pending の残り、reject の理由、コスト後の値…。どれも無意味ではありません。むしろ、機械の内部で判定を作るうえでは必要なものが多いです。でも、人間が常時見る画面にそれらをまとめて載せると、話は別でした。
何が厄介かというと、どの情報も「間違い」ではないことです。だからこそ、見ていて余計に迷うんですよね。値動きそのものの事実を見ているのか、観測パイプラインが正常に流れているかを見ているのか、構造候補の判定を見ているのか、それとも執行候補まで踏み込んだEV判定を見ているのか。これらは本来、問いが違います。問いが違うものを同じパネルや近い位置に置くと、人間側は「で、私は今どの問いに答えればいいの?」という状態になります。
今回とくに強く感じたのは、機械の内部論理と、人間が理解しやすい表示は別物だということでした。機械にとっては、複数の条件や中間情報を同時に持っていても何も困りません。でも人間はそうではない。たとえば、サンプル流量を示す値と、追随率を示す値と、未確定イベント残を示す値が同じ場所にあると、それだけで「継続性を見ろ」「質を見ろ」「詰まりも見ろ」と複数の仕事を同時に押しつけられることになります。慣れで多少は読めるようになるかもしれませんが、それは設計が良いという話ではなく、単にこちらが無理をして適応しているだけです。
つまり、今回の混乱は、自分の理解力が急に落ちたというよりも、人間向けに分離されていない情報設計に、ちゃんと混乱したという方が実態に近いと思います。そしてこれはむしろ、悪いことではありませんでした。どこが読みにくいのか、何が主役で何が診断用なのか、そのズレを自分で検出できたからです。今日の作業は、観測機を進める作業であると同時に、判断を壊す表示から、判断できる表示に戻す作業でもありました。
Structure Candidate と EV Judge を主役にした

今回の整理で、常時見る主パネルは Structure Candidate と EV Judge の2つに絞りました。ここが今日のいちばん大きな変更点です。
まず、Structure Candidate は「この市場ペアに、そもそもリードラグ構造候補があるのか」を見るためのパネルです。ここではまだ「取れるかどうか」は言いません。あくまで、lead の変化に対して lag が遅れてついてくる傾向が、最低限の母数・方向性・量で確認できるかだけを見ます。つまり、構造候補の白黒です。ここにコストや執行可能性まで混ぜると、また話が濁るので、一旦そこは切り離しました。
一方の EV Judge は、その構造候補の上に乗る判定です。こちらは、構造があるとして、そのうえでコスト後でも残るのかを見るためのパネルです。言い換えると、「構造候補あり」と「執行候補あり」は別判定にした、ということですね。これまでは頭の中でなんとなく一体化しかけていたものを、画面上でもちゃんと分けた形です。
逆に、もともと前面に出ていた Raw の値動きや Sample Flow、Reject の事情などは、主役から降ろしました。これらは不要な情報ではありません。実際、診断や深掘りには必要です。ただ、毎回の判断で最初に見るものではない。だから、主パネルからは外して、必要なときだけ掘る診断用情報として分離することにしました。
この整理をしてみて改めて思ったのは、観測機というのは「何でも見える機械」を作れば良いわけではない、ということです。必要なのは、今の自分がどの問いに答えるためにこの画面を見るのかが、開いた瞬間に分かることでした。今回でようやく、その入口が少し整ってきた感じがあります。
現時点でのリードラグ構造の暫定定義

今回、いちばん大事だったのは「何となくそれっぽい」ではなく、今この観測機が何をリードラグ構造候補と呼ぶのかを、実装可能な形で固定したことです。
現時点で global_fx に対して置いている暫定定義は、かなり具体的です。
まず lead 側は、Global BTC/USD × USD/JPY で作った fair value です。lag 側は Binance Japan BTC/JPY。そして、lead 側でイベントとみなす条件は、過去300秒に対する変化の絶対値が 4bps 以上になったときです。さらにイベント同士は最低 15 秒あける設定にしています。つまり、「fair value が5分の中でそれなりに動いた場面」を lead イベントとして切り出しているわけです。
そのうえで、lag 側が追随したかどうかを見る基準時間、つまり primary horizon は global_fx では 60秒です。要するに、lead 側にイベントが起きたあと、60秒後の Binance Japan BTC/JPY を見て、同じ向きへ動いたかどうかを判定する、という形です。ここでいう「同じ向きへ動いた」は、lead が上なら lag も上、lead が下なら lag も下、という意味です。実装上は lead の向きに合わせて符号をそろえた gross move を使っていて、60秒後の gross move がプラスなら追随したと数えます。
ここから先が、今回の Structure Candidate v0 の本体です。
直近30分の rolling 集計に対して、次の3条件を全部満たしたときだけ、structure_candidate_flag = 1 にします。
1つ目は、評価可能サンプルが10件以上あること。
これは valid_sample_n_30m >= 10 です。
ここでいう「評価可能サンプル」は、lead イベントが起きたあと、lag 側で anchor が取れ、必要な horizon までちゃんと追跡できて、reject されずに最後まで評価が完了した事例のことです。たまたま1、2件うまくいっただけでは構造候補とは呼ばない、という意味ですね。
2つ目は、60秒後の方向一致率が55%以上であること。
これは follow_rate_primary_h_30m >= 0.55 です。
つまり、直近30分で評価できた事例を集めたとき、そのうち 55%以上で lag が60秒後に lead と同じ向きへ動いていることを求めています。五分五分ではなく、「半分を明確に超えている」と最低限言えるラインとして、いったん 0.55 を置きました。
3つ目は、60秒後の追随量の中央値がプラスであること。
これは gross_move_primary_h_median_30m > 0 です。
ここで見ている gross move は、手数料やスリッページを引く前の、純粋な追随量です。つまり、コストを考える前の世界で見ても、lag 側が一応プラス方向へついてきているかを確認しています。平均値ではなく中央値を使うのは、外れ値に振られすぎないようにするためです。
この3条件を全部満たしたとき、今の観測機は global_fx に対して、**「一次的なリードラグ構造候補あり」**とみなします。逆に言えば、現時点での定義はかなり限定的です。
「5分で4bps以上動いた fair value のあとに、Binance Japan BTC/JPY が60秒後に、直近30分で10件以上・55%以上・中央値プラスで同方向追随しているなら、構造候補あり」と呼ぶ。今の白黒基準は、要するにこれです。
大事なのは、ここでまだ 執行して勝てる とは言っていないことです。
あくまでここで定義したのは、**「lead の変化に対して lag がついてくる地形があるか」**という構造候補の条件です。コストを引いたあとでも残るか、実際に執行候補として扱ってよいかは、その次の EV Judge で別に見る。構造と執行を分けるという意味でも、今回ここを具体的な閾値込みで固定できたのは大きかったと思います。
実装で直したこと

今回の修正は、単にパネルの名前を変えたとか、見た目を少し整えたとか、そういうレベルではありませんでした。やったことを一言で言うなら、「機械の中間情報をそのまま見せる観測機」から、「人間が判断できる観測機」へ引き戻した、という感じです。
まずロジック側では、Structure Candidate v0 をちゃんと機械で判定できるように、直近30分の rolling 集計を追加しました。今回の主役になったのは、
valid_sample_n_30mfollow_rate_primary_h_30mgross_move_primary_h_median_30m
の3つです。これらをもとに、sample_ready_30m_ge10、follow_ready_primary_ge0_55、gross_ready_primary_median_gt0 を作り、そのANDで structure_candidate_flag を立てる形にしました。つまり、「構造候補あり」の判定を、ようやく累積値ではなく直近30分の状態として見られるようにしたわけです。ここは今回かなり大きい変更でした。
次に、主パネルの役割を整理しました。
これまでは Fact Magnitude や Fact Persistence など、内部的には意味のある中間情報が前面に出ていたのですが、今回はそこをかなり割り切って、主パネルは Structure Candidate と EV Judge の2種類だけにしました。逆に、Raw Magnitude や Sample Flow、Reject Diagnostics は診断用に降格させています。ここは自分の中でもかなり納得感がありました。情報を減らしたというより、主役と脇役をちゃんと分けた感じです。
表示内容もかなり絞りました。Structure Candidate パネルでは、常時見る系列を最大3つに制限し、今回は structure_candidate_flag、follow_ready_primary_ge0.55、gross_ready_primary_median_gt0 を主表示にしています。本当は sample_ready_30m_ge10 も大事なのですが、主表示に全部を載せるとまた人間が詰まるので、そこは内部判定に使いつつ主パネルからは外しました。今回のテーマが「1パネル1問い」に戻すことだったので、この割り切りは必要だったと思います。
EV Judge 側も同じです。
こちらでは、structure_candidate_flag を前提にしつつ、コスト後に残るかを見るために research_net_move_primary_h_median_30m と cost_pass_rate_primary_h_30m を並べました。まだ最終的な execution_candidate_flag までは入れていませんが、少なくとも「構造候補が立っているか」と「そのうえでコスト後でも残るか」が、かなり素直に見える形にはなりました。今回の修正で、構造と執行の境界はだいぶ見やすくなったと思います。
細かいところでは、空実装のままだった箇所や命名ズレも触っています。
たとえば、time bucket の cost pass rate まわりは、見えているのに実は何もしていない状態だとかなり危険なので、そのまま放置しないように整理しました。また、CME 側のプロファイルで BTC=F を見ているのに型名が ETF 系のままだった問題も、少なくとも将来の混乱を減らす方向で手を入れています。こういう部分は地味ですが、後で自分が見返したときのわかりやすさに効いてくるはずです。
今回の実装で良かったのは、正しさを増やすために複雑にしたのではなく、判断を作るために役割を整理したことだと思います。観測機はまだ完成ではありませんし、定義も v0 の仮置きです。でも、少なくとも今の時点で「何を構造候補と呼ぶのか」「何をEV判定で見るのか」が、実装と画面の両方で揃ってきました。今日はそこまで持っていけたのが大きかったです。
今日の気づき
今日いちばん大きかった気づきは、「見える情報を増やすこと」と「判断しやすくすること」は全然違うということでした。
開発していると、どうしても「あれも見たい」「これも出しておきたい」となります。実際、機械の内部で判定を作るためには、中間情報も診断情報も必要です。だから、全部見えるようにしておけば親切だろう、と最初は思っていました。でも実際に自分でその画面を見ながら判断しようとすると、むしろ逆でした。必要な情報が揃っているはずなのに、頭の中では問いが増えすぎて、どこから何を見ればいいのかがぼやけてくる。今回しんどかったのは、情報量そのものより、役割の違う情報が同じ重みで並んでいたことだったんだと思います。
ここで改めてわかったのは、機械の内部論理と、人間が常時見る画面の設計は別問題だということです。機械にとっては、valid sample 数も、pending も、reject の理由も、follow rate も、どれも自然な中間情報です。でも、人間が毎回それを同じ画面で読む必要はない。むしろ、そこを分けてやらないと判断が壊れる。今回の観測機では、まさにその罠にハマりかけていました。
もう一つ大きかったのは、自分が混乱したこと自体は悪いサインではなかったということです。最初は「理解力が足りないのでは」とも少し思ったのですが、振り返ってみると、実際には「何が混ざっていて、何が主役で、何が診断用なのか」を途中できちんと掴めていました。つまり、読めない形になっていることにちゃんと引っかかった、ということです。これは単なる詰まりではなく、むしろ必要な違和感だったのだと思います。
そして最後に、今日は定義づけの難しさもかなり実感しました。
リードラグ構造という言葉自体は簡単に使えてしまいますが、いざ「現時点では何をもってそれと呼ぶのか」を白黒つけようとすると、lead イベントとは何か、追随とは何か、どの時間で判定するのか、何件あれば十分なのか、と一気に具体化を迫られます。正直、途中から急に難しくなった感覚はありました。でも、だからこそ今回みたいに、最終正解ではなくても一旦 v0 の測り決めを置くことが大事なんだとも思いました。曖昧なままだと永遠に判断できないし、実装にも落ちません。今日はそこをかなり身をもって学んだ日でした。
次回やること
次回やることは、今日決めた定義をいきなり賢くすることではなく、この v0 の定義で実際に観測して白黒をつけることです。
今回、global_fx については「何をリードラグ構造候補と呼ぶか」を、かなり具体的に固定しました。lead 側の fair value が過去300秒比で 4bps 以上動いた場面を切り出し、その後 Binance Japan BTC/JPY が 60 秒後に、直近30分で 10 件以上・55%以上・中央値プラスで同方向追随していれば、ひとまず構造候補ありとみなす。ここまでは定義として置けたので、次にやるべきは、その定義をまた増やすことではなく、このまま回して、実際に structure_candidate_flag が立つのか、立っても EV Judge 側で残るのかを見ることです。
見るポイントも、今は増やしすぎない方が良いと思っています。
少なくとも次回確認したいのは、
structure_candidate_flagが本当に立つのか- 立つ時間帯や頻度はどの程度か
- 立っても
research_netやcost_pass_rateが弱くて EV 側で落ちるのか - それとも、そもそも sample が足りず構造候補まで届かないのか
このくらいで十分です。ここでさらに条件を足し始めると、また何が悪いのかわからなくなるので、まずは細い定義のまま観測して壊しにいく。今日決めた「初期定義は減らす、観測は増やす、修正は1回に1個だけ」という方針は、そのまま次回にも持ち越したいです。
あとは、今回の実装変更が人間向けUIとして本当に機能しているかも、少し見ておきたいところです。主パネルを Structure Candidate と EV Judge に絞ったことで、かなり読みやすくはなったはずですが、実際に数時間〜半日くらい回してみたときに、「この2枚だけで判断できるのか」「必要なときに Diagnostics 側でちゃんと掘れるのか」は、一度は確認しておきたい。ここがダメなら、定義ではなく表示の問題がまだ残っていることになります。
要するに次回は、定義をさらにいじる回ではなく、今決めた定義で観測し、その結果をもとに次の一手を決める回です。今日ようやく土台を置けたので、次はそこに現実をぶつけていきます。