Bot CEX 開発ログ

🛠️開発記録#481(2026/3/11)「multi_market_probe開発ログ ― J-Reversionを先に定義し直した話」

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

前回は Q / C / M 系統を整理し、multi_market_probe を「読める観測装置」に近づけるところまで進めました。

関連記事
🛠️開発記録#477(2026/3/9)「multi_market_probe開発ログ ― 閾値監視から分布観測へ、M系統を作り直した話」

続きを見る

今回はその続きとして、F / J 系統に手を入れようとしたのですが、実際には先に J 側で何を判定したいのかを定義し直す必要があると分かりました。そこで今回は対象を歪み回帰に絞り、J-Reversion を執行判定器ではなく「構造があるかどうかを見る判定器」として実装し直しています。この記事では、その判断理由と、YES / HOLD / NO の3値判定、方向別メトリクス、Grafana パネル追加までの流れを整理します。

1. 前回の続き:F / J に進もうとして止まった理由

前回の記事では、multi_market_probe のうち Q / C / M 系統を整理しました。
品質が正常か、観測対象の市場同士に関係があるか、premium がどのような分布を持っているか。この三つを先に固めることで、観測器としての土台を整えるところまでは進められたと思っています。

その流れで、次は F / J 系統に進む予定でした。
Forward で結果分布を見て、Judgment でそれを戦略の視点から読む。設計としては自然ですし、前回の時点でもそのつもりでした。

ただ、実際に手を動かしてみると、ここで少し止まりました。

理由はシンプルで、F 系統を見直そうとしたときに、そもそも J 系統で何を判定したいのかがまだ曖昧だったからです。

たとえば F 系統では、

一定以上の premium が発生したあと
何秒後にどれくらい縮んだか
hit rate はどうか
といった future facts を作れます。

これは一見すると便利なのですが、その値をどう読むかが決まっていないと、結局「数値が並んでいるだけ」で終わってしまいます。
歪み回帰として読みたいのか、Lead-Lag として読みたいのか、あるいは Hedge feasibility の補助として使いたいのか。その前提が曖昧なままでは、F の設計も定まりません。

つまり、前回までで

観測の土台

市場状態の分布

まではかなり整理できたのですが、

その分布をどう解釈するのか

という Judgment 側の定義が、まだ十分に言語化できていなかったのです。

この状態で F を先に作り込むと、
「何のための future 指標なのか分からないままメトリクスだけ増える」
という形になりやすいと感じました。

そこで今回は、順番を少し変えることにしました。

Forward を先に厚くするのではなく、
まず Judgment 側で

何を判定したいのか
何を YES / HOLD / NO としたいのか
その判定をどの戦略仮説に対応させるのか

を先に固めることにしたのです。

要するに今回は、F を作る回ではなく、
F と整合する J の定義を先に立て直す回
になりました。

その結果、最初から F / J 全体を同時に進めるのではなく、まずは一つの戦略仮説に絞って Judgment を作る方がよい、という結論になりました。
今回対象を歪み回帰に絞ったのは、そのためです。

前回までは「観測できる状態を作る」ことが主眼でした。
今回はそこから一歩進んで、
その観測結果をどう読む装置にするのかを定義する
段階に入っています。

次の章では、その中でも今回あえて歪み回帰だけに絞った理由を書いていきます。

2. 今回の対象は歪み回帰だけに絞った

Judgment 側を先に定義し直すと決めたあと、次に考えたのは「何から着手するか」でした。

候補としては、もともと想定していた三つがあります。

  • 歪み回帰
  • Lead-Lag
  • Hedge

multi_market_probe の目的を考えると、最終的にはこの三つをそれぞれ Judgment 系統として整理したいと思っています。
ただ、ここで三つを同時に進めるのはやめました。

理由は単純で、やるべきことと思考リソースがすぐに拡散するからです。

たとえば歪み回帰を見るなら、

価格差がどれくらい発生しているか
その価格差が一定時間内に縮む傾向があるか
どの時間幅で判定するか
YES / HOLD / NO をどう切るか

を決める必要があります。

一方で Lead-Lag をやるなら、

どちらの市場が lead なのか
lag 側がどの程度追随するのか
相関と追随をどう区別するのか

といった別の論点が出てきます。

Hedge になると、さらに

ヘッジ先市場をどう扱うか
板厚や滑りをどう見るか
実行可能性と構造判定をどう分けるか

といった話が入ってきます。

つまり、この三つは名前こそ並列ですが、実際には必要な前提も判定材料もかなり違います。
それを一度にまとめようとすると、J 系統そのものが曖昧になりやすいと感じました。

そこで今回は、まず 歪み回帰だけに絞る ことにしました。

これは「歪み回帰が本命だから最優先」という意味もありますが、それ以上に、

一つの戦略仮説に対して、Judgment を最後まで言語化してみる

こと自体が重要だと考えたからです。

歪み回帰なら、少なくとも今の multi_market_probe が持っている観測材料と比較的つなぎやすいです。

  • bf_fx と binance_perp の関係性を見る C 系統
  • premium の幅や分布を見る M 系統
  • future 側の縮小傾向を見る F 系統
  • それを判定へ落とす J 系統

という流れを作りやすいからです。

また、今回の観測対象と主戦場の関係も、歪み回帰に絞る判断を後押ししました。

現時点では、

  • 主戦場は bitFlyer FX
  • 比較対象は binance_perp
  • 収束先は当面 binance_perp

という前提で観測しています。

この前提に立つなら、最初に問うべきことは
「bf_fx と binance_perp のあいだに、歪み回帰構造があるのか」
です。

Lead-Lag や Hedge も将来的には重要ですが、そこへ行く前に、

そもそもこの市場ペアで
回帰という読み方自体が成立するのか

を確かめる方が順番として自然です。

もう一つ大きかったのは、今回の目的が
戦略の完成ではなく、構造の白黒をつけること
だった点です。

歪み回帰は、その意味でも扱いやすいテーマでした。

価格差が発生している

一定時間内に縮むのかを見る

構造として読めるか判断する

という流れを比較的素直に作れるからです。

もちろん、ここで「歪み回帰だけをやれば十分」というつもりはありません。
Lead-Lag や Hedge には、歪み回帰とは別の構造があるかもしれませんし、むしろそちらの方が重要になる可能性もあります。

ただ今回は、Judgment を本当に形にするために、
まずは対象を一つに絞る必要がありました。

その結果、今回の J 系統は

歪み回帰 J-Reversion を先に作る

という形で進めています。

この判断によって、少なくとも「何を判定したいのか分からないままメトリクスだけ増える」状態からは抜けやすくなりました。

次の章では、その J-Reversion をどう定義したのか、特に「執行判定」ではなく「構造判定」として置いた理由について整理します。

3. J-Reversionを「執行判定」ではなく「構造判定」として定義した

今回、歪み回帰の J 系統を作るにあたって、最初に決めたのは
これを執行判定器にはしない
ということでした。

言い換えると、今回作っている J-Reversion は

「今この瞬間に注文してよいか」

を返す装置ではなく、

「bf_fx × binance_perp という組み合わせに、歪み回帰構造があると見てよいか」

を返す装置として定義しています。

この違いはかなり重要です。

執行判定まで含めようとすると、どうしても

スリップ
注文サイズ
板厚
実行コスト
ローカル実行か外部サーバ実行か
といった話まで一気に入ってきます。

もちろん、それらは最終的には必要です。
実際にトレードする以上、実行コストを無視した判定には意味がありません。

ただ、今回の multi_market_probe の役割は、そこではありません。

この観測機でまずやりたいのは、

そもそもその構造が存在するのか
その構造はどの方向に出やすいのか
観測結果として「ある / ない / まだ不明」を言えるのか

を白黒つけることです。

ここでいきなり執行条件まで混ぜてしまうと、

構造がない
のか
構造はあるが、今の自分の執行条件では取れない
のか
が分からなくなります。

これは観測器としてはあまり良い状態ではありません。

たとえば、bf_fx と binance_perp の間に価格差があり、一定時間後にそれが縮む傾向も観測されていたとしても、
その時点で

「じゃあ実際に利益が残るのか」

まで一足飛びに言うのは危険です。

なぜなら、その間には

コスト
執行速度
約定のされ方
注文の取り方

といった別の層が挟まるからです。

今回の段階では、それらをまだ十分に固定できていません。
だからこそ、J-Reversion はまず

構造の有無を判定する装置

として切り出した方がよいと判断しました。

今回の定義では、歪み回帰とはざっくり言えば、

binance_perp を収束先とみなしたときに、
bf_fx がそこから外れた状態が観測され、
その外れが一定時間内に縮む傾向を持つか

を見るものです。

さらに今回は、高頻度の超短期勝負は避ける前提を置いています。
数ミリ秒〜数秒未満の世界は、自分の戦場ではないと最初に切っています。

そのため、今回の J-Reversion で見たいのは、

高速アルゴが先回りする世界を避けたうえで、
なお「回帰構造がある」と言えるか

です。

これは執行条件の話に似ていますが、まだ執行判定そのものではありません。
ここで見ているのは、

どの時間幅で構造が見えるのか
市場間の関係は十分にあるのか
観測された distortion が本当に縮小するのか

といった、あくまで構造の側です。

だから今回の J-Reversion では、最終出力も

YES / HOLD / NO

の3値にしました。

YES は「構造あり」
NO は「構造なし」
HOLD は「まだ言い切れない」

です。

ここで重要なのは、YES が

「今すぐ注文してよい」

を意味していないことです。

YES が意味するのは、

「少なくともこの市場ペア、この方向、この時間幅において、
歪み回帰構造があると読めるだけの観測事実がある」

ということです。

この切り分けによって、

観測器がやること
執行部がやること

を分けやすくなりました。

まず観測器で

構造の存在を判定する

そのうえで後段で

その構造が実際に取れるかを検証する

という順番にできるからです。

個人的には、この順番はかなり大事だと思っています。

最初から「取れるかどうか」だけを見始めると、
構造が見えていないのか、
見えているけど執行が悪いのか、
その切り分けが曖昧になりやすいからです。

今回の J-Reversion は、そこを分離するための設計です。

つまり今回は、
歪み回帰の J 系統を「強いシグナルを出す装置」としてではなく、
構造の白黒を慎重につける装置として置き直した、ということになります。

次の章では、その構造判定器として J-Reversion に YES / HOLD / NO の3値判定をどう入れたのかを書いていきます

4. YES / HOLD / NO の3値判定を導入した

J-Reversion を構造判定器として定義した以上、次に必要になったのは
その判定結果をどう返すか
でした。

最初は、単純に「ある / ない」の二値でもよいかと思っていました。
ただ、実際に設計を詰めていくと、それでは足りないとすぐに分かりました。

理由は、観測器が返すべきなのは

構造がある
構造がない
だけではなく、

まだ言い切れない

という状態もかなり多いからです。

たとえば、

相関計算に使う観測がまだ足りない
サンプル数が十分に溜まっていない
一時的に relation が弱いが、まだ長く崩れているとは言えない
30秒では弱いが、60秒では少し見える

といった状態があります。

これらを全部「NO」に落としてしまうと、
実際には

観測不足
一時的な揺れ
判定保留

でしかないものまで、
「構造なし」として扱ってしまうことになります。

これは今回の設計思想と合いません。

今回やりたいのは、NO を軽く出すことではなく、
「構造がない」と言うときの重みをちゃんと持たせること
でした。

そのため、最終的な出力は

  • YES
  • HOLD
  • NO

の3値にしました。

YES は、
現時点の観測に基づけば、その方向で歪み回帰構造があると見てよい状態です。

NO は、
現時点の観測に基づけば、その方向では歪み回帰構造を採用しないと判断する状態です。

そして HOLD は、その中間です。
観測不足、品質不足、一時的な矛盾、relation の弱化などがあって、
まだ YES にも NO にも寄せきれない状態をここへ入れます。

この HOLD を明示的に設けたことが、今回の J-Reversion の設計ではかなり大きかったと思っています。

実際、実装を進める中で見えてきたのは、
J 系統で本当に難しいのは YES 条件よりも、
HOLD と NO の境界をどう切るか
でした。

たとえば relation が弱いときでも、

単発で弱いだけなら HOLD
長時間ほぼずっと弱いなら NO候補
という分け方が必要になります。

同じように sample が少ない場合も、

まだ観測不足なら HOLD
十分に観測したうえで構造が弱いなら NO
と分けないと、判定器としての意味が曖昧になります。

この三値判定を入れたことで、
J-Reversion は単なるスコア装置ではなく、
何が理由で前に進めないのかまで含めて返す装置
に近づきました。

実際の実装でも、YES / HOLD / NO だけでなく、
その背景理由を reason_code として出すようにしています。

たとえば、

品質不足なのか
ready 不足なのか
sample 不足なのか
relation が保留状態なのか
relation を含めても NO と言えるのか

を、数値コードとして機械可読な形で出すようにしました。

この reason_code は、ダッシュボードで人間が読むためにも重要ですが、
それ以上に「何がボトルネックで YES にならないのか」を切り分けるために役立ちます。

つまり今回は、単に3値判定を導入したというよりも、
YES / HOLD / NO を中心に J-Reversion の責務をはっきりさせた
という方が近いです。

YES は「構造あり」
NO は「構造なし」
HOLD は「まだ判定保留」

この三つを明示したことで、
観測不足と構造否定を分けながら、
J 系統を少しずつ「白黒をつける装置」に近づけられるようになりました。

次の章では、この3値判定をさらに実務寄りにするために、
なぜ方向別で判定する形にしたのかを書いていきます。

5. 方向別判定を入れた理由

YES / HOLD / NO の3値判定を入れると決めたあと、次に考えたのは
それを片方向で返すのか、方向別で返すのか
でした。

結論から言うと、今回は方向別で返す形にしました。

つまり J-Reversion は、

  • long 方向
  • short 方向

を分けて判定するようにしています。

これは単なる見た目の都合ではなく、実際にはかなり重要な変更でした。

理由はまず、主戦場である bitFlyer FX の性質にあります。

bitFlyer FX は、当然ですが買いでも売りでも入れます。
そのため、観測器が最終的に執行部へ何かを渡すことを考えると、

「歪み回帰構造がある」

という一言だけでは足りません。

必要なのは、

どちら向きに歪み回帰構造が出ているのか

です。

もしここが曖昧なままだと、後段の執行部で

その歪みは bf_fx を買う話なのか
それとも bf_fx を売る話なのか
を、もう一度解釈し直さなければならなくなります。

これは、設計としてあまりきれいではありません。

今回の J-Reversion は構造判定器ではありますが、
それでも将来的には執行部につながる前提で作っています。
そのときに、観測器の側で

どの方向に構造が出ているか

まで返せるようにしておいた方が、後段の負荷をかなり下げられます。

これが一つ目の理由です。

もう一つの理由は、
市場の状態をより解像度高く読めるようにするため
です。

歪み回帰という言葉だけでまとめてしまうと、
つい「この市場ペアに回帰があるかないか」という一つの問いに見えます。

しかし実際には、

bf_fx が上に外れたときの戻り方
bf_fx が下に外れたときの戻り方
は、必ずしも同じとは限りません。

片側ではよく戻るのに、もう片側ではほとんど戻らない。
あるいは、片側だけ premium の event が多く、逆側はほとんど出ない。
そういう非対称性は十分ありえます。

もし方向をまとめたまま判定してしまうと、
こうした違いがかなり見えにくくなります。

たとえば実際の観測でも、

short 側だけサンプルが積み上がる
long 側は sample 不足のまま
といった形が見えていました。

このとき、片方向にまとめた判定しかないと、

全体として弱い
全体としてまだ不明
という曖昧な読みになりやすくなります。

でも方向別にしておけば、

short 側には何かありそう
long 側はまだ判断材料がない
というように、かなり具体的に読めます。

これは構造判定器としても大きなメリットです。

今回の J-Reversion では、
単に YES / HOLD / NO を返すだけでなく、
どの方向についてその判定なのか
まで明示できるようにしました。

その結果、ダッシュボード上でも

long 側の state / reason
short 側の state / reason

を分けて確認できるようになっています。

これは運用上かなり見やすくなりました。

たとえば、

片側は HOLD_LOW_SAMPLE
片側は HOLD_NO_RELATION_PENDING
のように分かれていれば、

まだ両方向とも不明
というより、
それぞれ別の理由で止まっている

と読めます。

この差は意外と大きいです。

観測器を作っていると、つい情報をまとめてしまいたくなります。
その方がダッシュボードもすっきり見えるからです。
ただ、今回に関しては、まとめることで消えてしまう情報の方が大きいと判断しました。

歪み回帰の有無だけを見るのではなく、
どの方向に偏っているのかまで含めて見たい。
そして、それを将来の執行部にも渡しやすい形にしておきたい。

その結果として、J-Reversion は方向別判定を前提にした構成になりました。

今回の実装でやったことを一言でまとめると、
「歪み回帰があるか」ではなく、「どちら向きの歪み回帰が、どの程度読めるか」を返す形にした
ということです。

次の章では、その方向別判定を支えるために実際に追加した
J-Reversion のメトリクスと Grafana パネルについて整理していきます。

6. 今回実装したもの:J-Reversion のメトリクスとGrafanaパネル

ここまで書いてきた設計思想を、今回は実際にメトリクスとパネルへ落とし込みました。

今回の実装でやったことを大きく分けると、次の二つです。

  • J-Reversion の判定ロジックを追加したこと
  • その判定結果を Grafana 上で方向別に読めるようにしたこと

まず J-Reversion 側では、
bf_fx × binance_perp を対象に、

  • long 側の state / reason
  • short 側の state / reason

をそれぞれ独立して出せるようにしました。

state は前章までで書いた通り、

  • YES
  • HOLD
  • NO

の3値です。

ただし実装上は Grafana で扱いやすいように数値化していて、
たとえば

  • 0 = NO
  • 1 = HOLD
  • 2 = YES

のような形で出しています。

これに加えて、なぜその state になったのかを reason_code で出しています。

たとえば今回の実装では、

  • HOLD_NOT_READY
  • HOLD_LOW_SAMPLE
  • HOLD_QUALITY_ISSUE
  • HOLD_NO_RELATION_PENDING
  • NO_NO_RELATION
  • NO_NO_DISTORTION
  • NO_NO_REVERSION_30S
  • YES_REVERSION_CONFIRMED

といった理由を、機械可読な形で返すようにしました。

この reason_code を入れたのはかなり重要でした。
state だけだと「止まっている」ことしか分かりませんが、reason まで出しておくと、

quality が問題なのか
sample が足りないのか
relation が弱いのか
それとも reversion そのものが弱いのか

を切り分けられるからです。

J 系統を実際に調整していて分かったのですが、
このレイヤーでは「何を返すか」以上に
なぜその判定になったのかを見えるようにすること
の方が大事な場面が多いです。

今回はそこも含めて、J-Reversion を単なるスコアではなく、
判定と理由を返す形に寄せました。

もう一つやったのは、F 系統の最小改修です。

今回は F 系統を全面的に作り直すのではなく、
J-Reversion を成立させるために必要な最小限だけ追加しました。

具体的には、方向別に

  • future samples
  • reversion hit rate
  • delta

を持てるようにしています。

これを入れた理由はシンプルで、
方向別判定をする以上、future 側の事実も方向別に見えないと不自然だからです。

もし F が全方向まとめてしか持っていないと、

long / short のどちらで縮みやすいのか
どちらで event が立ちやすいのか

が見えにくくなります。

そのため今回は、J を先に固める方針を維持しつつ、
J-Reversion に必要な範囲だけ F を方向別対応させました。

Grafana 側では、これらを読むためのパネルも追加しました。

今回追加した J-Reversion パネルは、役割としてはかなりシンプルです。

一つは、
方向別の state をそのまま確認するパネル
です。

ここでは long / short それぞれについて、

いま YES なのか
HOLD なのか
NO なのか

を直感的に見られるようにしています。

もう一つは、
方向別の reason と sample を確認するパネル
です。

こちらは、たとえば

なぜ HOLD に留まっているのか
なぜ NO に落ちたのか
30秒サンプルはどれくらい溜まっているのか

を確認するためのものです。

実際にこのパネルを眺めてみると、
今回の実装で見えるようになったことがいくつかあります。

たとえば、

short 側だけ sample が積み上がる
long 側は sample 不足のまま
state は HOLD と NO を行き来する
reason は relation 系と sample 系が中心になる

といった形です。

これはまさに、方向別表示にした意義が出ている部分だと思います。

もしこれが全方向まとめた一つの判定しかなかったら、

「全体としてまだ弱い」
くらいの曖昧な読みになっていたはずです。

しかし方向別に出したことで、

どちら側に事象が偏っているのか
何がボトルネックになっているのか
がかなり見えやすくなりました。

今回の実装でまだやっていないこともあります。

たとえば、

実行コスト込みの厳密判定
執行部へそのまま渡せる完成ロジック
Lead-Lag や Hedge 用の J 系統

まではまだ入れていません。

ただ、今回の時点で少なくとも

J-Reversion という判定器の骨格
方向別 state / reason の返却
その結果を Grafana で読めるパネル

までは形になりました。

個人的には、この段階に来たこと自体がかなり大きいと思っています。

前回までは「観測できる」状態を作っていました。
今回はそこから一歩進んで、
観測結果をどう読むかを、J-Reversion という具体的な装置として置けるようになった
からです。

次の章では、その実装を踏まえて、いま実際にどこを調整しているのか、
特に HOLD と NO の境界をどう詰めているのかを書いていきます。

7. いま調整していること:HOLD と NO の境界

J-Reversion を実装してみて、いちばん難しかったのは YES 条件よりも、むしろ HOLD と NO の境界 でした。

これは実際にコードを書いてダッシュボードを眺めてみると、かなりはっきり見えてきました。
最初に問題になったのは relation 判定です。もともとの実装では、relation が一定条件を満たして弱いと判断された時点で、そのまま NO_NO_RELATION に落ちやすい構造になっていました。実際、初期版の _judge_direction では、

if relation_weak_persistent:
return "NO", "NO_NO_RELATION"

というかなり強い形になっていて、方向別の reversion 事実や sample がある程度積み上がっていても、relation が最上流で全体を潰しやすい状態でした。 multi_market_probe_service

ただ、これだと今回の設計思想に合いません。
今回やりたいのは、単に NO を増やすことではなく、「構造がない」と言えるときだけ NO を出すこと だからです。

そのため、relation の扱いを少しずつ変えていきました。
現在のロジックでは、relation が弱いだけでは即 NO にせず、方向側の evidence も弱いときだけ NO に落とす形へ寄せています。実際のコードでも、

directional_evidence_weak = (hit_30 < self.no_hit_rate_pct) and (not edge_ok)

と方向側の弱さを作ったうえで、relation が持続的に弱い場合でも

if relation_weak_persistent:
if directional_evidence_weak:
return "NO", "NO_NO_RELATION"
return "HOLD", "HOLD_NO_RELATION_PENDING"

という分岐にしています。

この変更はかなり大きかったです。
relation が弱いというだけではなく、30秒主判定の事実も弱いときだけ NO にする ことで、relation を「単独で全部を否定する条件」から外せるようになったからです。

その relation 自体も、今は単純なサンプル数比ではなく、時間を意識した形に寄せています。
現在の設定は relation_weak_min_ready_sec: 240relation_weak_min_weak_sec: 180relation_weak_no_ratio: 0.9 で、少なくとも4分は観測できていて、そのうち3分以上 weak が続き、しかも観測時間の9割以上が weak のときだけ persistent とみなす形です。設定も config.yaml にそのまま入っています。

実装側でも _relation_weak_persistent() は、単に件数を数えるのではなく ready_msweak_ms を積み上げて比率を見る形になっていて、

if ready_ms < (self.relation_weak_min_ready_sec * 1000.0):
return False
if weak_ms < (self.relation_weak_min_weak_sec * 1000.0):
return False

と、最低観測時間と最低 weak 時間の両方を通ったうえで最終的に weak_ratio >= relation_weak_no_ratio を見ています。

つまり今やっている調整は、単に閾値をいじっているというより、
relation weak を「一時的な揺れ」ではなく「構造否定に値する持続状態」として扱えるようにするための調整 です。

もう一つ大きかったのは、sample 不足と distortion 不在を分けたことです。
以前は、30秒の matured sample が少ないことと、そもそも distortion event が立っていないことが少し混ざりやすい状態でした。ですが今は、distortion_events_total を方向別 future facts に持たせておき、J 側ではそれを使って

if total_distortion_events < float(self.min_distortion_events_total):
return "NO", "NO_NO_DISTORTION"

と判定しています。

これによって、

まだサンプルが足りない
のか
そもそも歪みイベント自体がほとんど立っていない
のか

を分けやすくなりました。
この違いは、HOLD と NO の切り分けではかなり重要です。

さらに、directional edge の見方も少し変えています。
最初は delta_latest に引っ張られやすかったのですが、今は _directional_edge_ok() の中で delta_mean_30s を主に使い、平均が弱いときだけ最新値を補助的に使う形にしています。

delta_ref = float(delta_mean_30s)
if abs(delta_ref) < self.directional_delta_min_abs_bps:
...

そしてそのうえで、long なら delta_ref > 0.0、short なら delta_ref < 0.0 を要求しています。

この変更によって、単発の最新観測だけで NO_NO_DIRECTIONAL_EDGE に寄りすぎることを少し抑えられるようになりました。構造判定器である以上、単発ノイズよりも累積的な事実を優先したいので、ここも意図に沿った修正だったと思っています。

いまの段階でダッシュボードを見ながらやっていることを一言で言うと、
NO を減らしているのではなく、NO を信用できる判定にしようとしている
ということです。

たとえば HOLD_LOW_SAMPLEHOLD_NOT_READYHOLD_NO_RELATION_PENDING が増えるのは、一見すると判定が鈍くなったようにも見えます。
でも今回の目的は、鋭いシグナルを出すことではなく、構造の白黒を慎重につけることです。そう考えると、観測不足や保留状態をきちんと HOLD へ押し込めるようになったのは、むしろ正常化に近いと思っています。reason code も exporter 側で HOLD_LOW_SAMPLE=202HOLD_NO_RELATION_PENDING=205NO_NO_RELATION=301 のように分けて出せるようにしているので、ダッシュボードでも「なぜ止まっているのか」をかなり読みやすくなりました。

現時点では、J-Reversion の大枠はかなり固まってきました。
ただし relation と directional evidence の境界、つまり どこまでを HOLD に留め、どこからを NO と言うのか はまだ調整中です。

言い換えると、いまやっているのは戦略の白黒をつける作業ではなく、
白黒判定器そのものの精度調整 です。

この段階を雑に飛ばしてしまうと、「まだ分からない」を NO に落とす装置になってしまいます。
だから今は少し地味でも、HOLD と NO の境界を丁寧に詰めています。
次の章では、その調整を踏まえて、今回どこまで進んだのか、そして Lead-Lag / Hedge をなぜ次回へ回すのかをまとめます。

8. 今回の到達点と、Lead-Lag / Hedge を次回に回した理由

今回の開発で、multi_market_probe の J 系統はようやく「何を返す装置なのか」が見えるところまで来ました。

前回の記事では、Q / C / M 系統を整理して、観測器としての土台を整えるところまで進めました。
今回はその続きとして F / J に進もうとしたのですが、実際には F を厚くする前に、J 側で何を判定したいのかを先に定義し直す必要があると分かりました。

その結果、今回は対象を歪み回帰に絞り、J-Reversion を

執行判定器
ではなく
構造判定器

として作るところに集中しました。

ここで得られた進展は、かなりはっきりしています。

まず、J-Reversion の役割を言葉で固定できました。

それは

「bf_fx × binance_perp という市場ペアに、歪み回帰構造があると見てよいか」

を方向別に返す装置です。

そのうえで、YES / HOLD / NO の3値を導入し、

YES = 構造あり
HOLD = まだ言い切れない
NO = 構造なし

という整理を置けました。

さらに、方向別に state / reason を返す形にしたことで、

どちら向きに事象が偏っているのか
どちら側が sample 不足なのか
relation が弱いのか
構造否定まで行ってよいのか

を、かなり具体的に読めるようになりました。

これは見た目以上に大きな前進だと思っています。

もともと今回の判断は、
F を見直していたら J の定義不足が露呈した、というところから始まりました。

その意味で、今回いちばん大きかったのは、
メトリクスやパネルを増やしたことそのものよりも、

J 系統で何を見たいのかを、自分の中でかなりはっきり言語化できたこと

です。

とくに、

構造判定と執行判定を分ける
観測不足を HOLD に逃がす
NO を重く扱う
方向別に判定する

という方針が固まったのは、今後の J 系統全体にもそのまま効いてくるはずです。

一方で、まだ終わっていないこともあります。

今回かなり時間を使ったのは、実際には YES 条件よりも
HOLD と NO の境界調整 でした。

relation が弱いときに即 NO にしない
sample 不足と distortion 不在を分ける
directional evidence の見方を少しずつ安定化させる

といった調整を続けていて、今は

J-Reversion の大枠は固まった
ただし、NO をどこまで重くするかはまだ調整中

という状態です。

つまり、完成したというよりは、

ようやく「何を調整しているのか」が見える段階に入った

という方が正確です。

Lead-Lag と Hedge を今回やらなかったのも、そのためです。

もしここで勢いのまま三つ全部に広げていたら、
また同じように

何を YES / HOLD / NO としたいのか
その判定に何が必要なのか
観測不足と構造否定をどう分けるのか

が曖昧なまま広がっていたと思います。

Lead-Lag には Lead-Lag の前提がありますし、
Hedge には execution feasibility をどう扱うかという別の問題があります。

これらは歪み回帰と名前は並列でも、実際には必要な判定材料も、構造の読み方もかなり違います。

だから今回は、まず一つの J を最後まで言語化してみることを優先しました。

その結果として、

J-Reversion の骨格
3値判定
方向別表示
reason_code を含めた可視化
HOLD と NO の境界調整

まではかなり形になってきました。

今回の記事を一言でまとめるなら、
multi_market_probe の J 系統は、ようやく

「何となくスコアを出す層」

から

「構造の白黒を慎重につける層」

へ移り始めた、ということになると思います。

次回以降は、引き続き J-Reversion の境界調整を進めつつ、
Lead-Lag と Hedge についても、
今回と同じように

何を判定したいのか
何を HOLD にし、何を NO にするのか

を先に言語化してから着手する予定です。

戦略を増やす前に、判定器をちゃんと作る。
今回やっていたのは、そういう地味だけれど重要な作業でした。

-Bot, CEX, 開発ログ