こんにちは、ぼっちbotterよだかです。
前回は、J-Reversion を 30 / 60 / 90 / 120 秒で比較できるようにして、歪み回帰をどこまで短期構造として扱うべきかを観測機ベースで切り分け始めました。今回はその続きとして、時間幅比較をさらに賢くするのではなく、そもそも今見えている歪み回帰構造が、主戦場である bitFlyer FX の取引執行に本当に有利に働くのかを、価格ベースで観測できるようにすることに取り組みました。J-Reversion の後段に 30 秒の価格プローブを追加し、YES / NO ごとの executable 応答や到達遅延をダッシュボードで直接読めるようにしたことで、「構造があるように見えること」と「実際に取れること」が別問題である、という事実が少しずつ見え始めています。

-
-
🛠️開発記録#488(2026/3/18)multi_market_probe開発ログ ― 歪み回帰をどこまで短期構造として扱うかを、観測機で切り分け始めた話
続きを見る
1. 今回のゴール:J-Reversion の後段に、bf_fx価格ベースの検証をつなぐ
今回のゴールは、J-Reversion の判定ロジックをさらに複雑にすることではありませんでした。前回までで、J-Reversion は 30 / 60 / 90 / 120 秒の比較ができる最小構成の構造判定器として一応立っています。つまり、「この条件で歪み回帰構造があるのかないのか」を観測する器は、すでにある程度できていました。そこで今回は、その判定結果が 主戦場である bitFlyer FX の執行に本当に意味を持つのか を、価格ベースで確かめられるようにすることを目標にしました。
ここで大事なのは、今の J-Reversion が返しているものと、bf_fx で実際に注文を出したときの有利不利はまだ同じではない、という点です。J-Reversion が見ているのは、bf_fx と binance_perp のあいだで実行可能ベースの相対プレミアムが一定以上広がったあと、それが一定時間内で縮みやすいかどうかです。つまり得られるのは「縮みやすい構造があるかもしれない」という条件付きの傾向であって、「bf_fx で出せば取れる」という保証ではありません。ここを切り分けずに判定器だけを磨いていくと、構造としては綺麗でも執行では死んでいる、という状態になりかねません。
そのため今回追加したのが、J-Reversion の後段で bf_fx の価格応答を追う 30 秒の価格プローブです。YES / NO の状態遷移をアンカーにして、その後の directional executable mean、win rate、各bps閾値への touch rate、first touch delay、1 / 3 / 5 / 10 / 15 / 30 秒時点の executable mean を観測できるようにしました。いきなり EV 推定や実運用チューニングに入るのではなく、まずは「この構造判定のあとに主戦場で何が起きているのか」を、そのまま見られる状態にすることを優先しています。
この順番にした理由もシンプルです。relation の再設計には気になる点がありますが、そこを先にいじると、「設計が良くなった」のか「実際に優位が出た」のかがまた混ざります。最終的に見たいのは、bitFlyer FX で優位があるかどうかです。だから今回は、現行 J-Reversion をいったんそのまま使い、まずは YES と NO が bf_fx executable で差を持つのかを確認できる土台を作りました。そのうえで差が出ないなら、初めて relation や判定ロジック側を疑えばいい、という順番です。
要するに今回は、J-Reversion を賢くする回ではなく、その判定結果を 主戦場の価格変化とつないで読めるようにする回 でした。歪み回帰があるように見えることと、それが実際に取引優位へ変換できることは別です。今回やったのは、その二つを混ぜずに観測するための後段実装です。
2. なぜ relation 再設計より先に、価格ベース検証を作るのか
今回の作業に入る前に、かなり気になっていたのが relation の扱いでした。現状の J-Reversion では、relation を対数リターン相関ベースで見ていますが、これには違和感があります。同時刻の相関で歪み回帰構造を見ようとすると、そもそも見たい現象と少しズレている可能性がある。相関は連動の強さは見ても、連動の形まではあまり見ませんし、abs(corr) を使うと符号情報もかなり捨ててしまいます。しかも relation を強く置きすぎると、研究全体が「relation を満たすかどうか」の話に引っ張られやすくなります。つまり、relation の再設計にはちゃんとした理由がありました。
ただ、それでも今回は relation を先にいじりませんでした。理由は単純で、先にそこへ入ると、また 判定器の設計を最適化する話 と 主戦場で実際に優位があるかを見る話 が混ざるからです。relation を作り直せば、state の出方や reason の納得感はたしかに変わるかもしれません。でも、その変更が bitFlyer FX の executable 優位につながるかは別問題です。設計を整えるほど、つい「良くなった気がする」方向へ引っ張られやすいのですが、ここで見たいのはそこではありません。
今回まず必要だったのは、現行 J-Reversion をそのまま使ったときに、主戦場で何が起きているのかを事実として見られる状態を作ることでした。YES が出たあとに bf_fx が本当に同方向へ動くのか。NO と比べて executable mean や win rate に差があるのか。何bpsくらいまで触り、そこに何秒くらいかかるのか。こうした後段の観測がないまま relation だけをいじると、改善したのが構造判定そのものなのか、単に見た目が整っただけなのかが分からなくなります。
逆に言えば、価格ベース検証を先に作っておけば、その後の relation 再設計も同じ物差しで比較できます。現行 relation のままでは YES が弱いのか。relation を変えると YES と NO の差が広がるのか。それとも相変わらず executable では取れないのか。こういう比較ができて初めて、relation 再設計は「気になるからやる作業」ではなく、「主戦場での優位に近づくための変更」になります。今回この順番を選んだのは、そのためです。
要するに、relation 再設計は重要ではあるけれど、優先順位としてはその一段後ろです。まずは現状の J-Reversion が、主戦場で本当に意味を持つのかを価格ベースで確かめる。その事実を見たうえで、なお YES が弱いなら、そのとき初めて relation や evidence の定義を疑う。今回は、設計を先にいじるのではなく、検証の土台を先に作る方を選びました。
3. まず潰したこと:h90 / h120 が正しく観測されていなかった問題
今回、価格ベース検証に入る前に、先に潰しておくべき問題がありました。h90 / h120 が「見えているつもり」で、実際にはまともに観測されていなかったことです。前回、J-Reversion を 30 / 60 / 90 / 120 秒で比較できる形に広げた時点では、ダッシュボード上では h90 / h120 も並んでいました。ただ、あとから確認すると、判定器側は h90 / h120 を見る前提になっているのに、元になる future sample を作る側に 90 秒と 120 秒のトラッカーが入っていませんでした。これでは、比較系を追加したように見えても、実際には h90 / h120 だけサンプル不足で NO に寄りやすい状態になります。
原因はかなり単純で、pair_premium_eval 側の additional_pairs に future_horizon_sec=90 / 120 が入っていなかったことです。J-Reversion は 90 秒や 120 秒の判定を返す設定になっていても、その後ろで「その horizon に対応する future facts」を蓄積していなければ、比較の前提自体が崩れます。つまりこの問題は、h90 / h120 に構造があるかどうか以前に、「そもそもその horizon を観測対象としてちゃんと成立させていなかった」という話でした。
そこでまずやったのは、この追加 horizon を pair_premium_eval 側へ明示的に入れることでした。base_market=bf_fx、ref_market=binance_perp、threshold_bps=10 の条件で、future_horizon_sec=90 と 120 を追加し、h90 / h120 も独立にサンプルが積まれる状態へ直しました。やっていること自体は地味ですが、ここを直さないまま価格ベース検証へ進むと、「h30 は見えているが h90 / h120 は弱い」という結果が出ても、それが市場構造の差なのか、単に観測不足なのか分からなくなります。先に潰すべきなのは、まさにこの種の配線問題でした。

この修正の意味はかなり大きかったです。少なくともこれで、h90 / h120 が NO 固定に見える理由として「future sample がそもそも無い」という可能性は外せるようになりました。言い換えると、ここでやったのは h90 / h120 に構造があると証明したことではありません。あくまで、「h90 / h120 も 비교対象として成立する土俵」に戻しただけです。ただ、この土俵修正を先に入れたことで、以後は 30 / 60 / 90 / 120 の差を、少なくとも“設定漏れではない”前提で読めるようになりました。
今回のフェーズでは、こういう前提のズレを先に潰しておくことがかなり重要でした。価格ベース検証を作ると言っても、元の観測系に穴が残っていれば、あとから何を見ても解釈が濁ります。h90 / h120 の問題を先に直したのは、比較系をきちんと比較系として成立させるためです。今回の価格プローブ追加はその後段の話であって、まず必要だったのは「見えているように見えるだけの horizon」を本当に観測できる horizon に直すことでした。
4. 実装変更①:J-Reversionの遷移アンカーから、30秒価格プローブを追加した

今回の実装で最初にやったのは、J-Reversion の判定結果をその場で終わらせず、後段へ流せる形にすることでした。もともと _JReversionStructureTracker は、direction ごとに state と reason を判定し、update_multi_market_j_reversion_state() で Prometheus へ流す構造になっています。今回ここに on_state_update コールバックを持たせ、判定のたびに ts_ms、base_market、ref_market、threshold_bps、horizon_main_sec、direction、state、reason_code、ready_flag、quality_flag をまとめて後段へ渡せるようにしました。つまり J-Reversion を「その場で verdict を返す判定器」から、「判定結果を別の観測器へ引き渡せる判定器」に一段進めたわけです。
この後段にぶら下げたのが、今回追加した _JReversionPriceProbeTracker です。設定側では j_reversion_eval.price_probe として独立に切り出していて、main_horizon_sec: 30、timeout_sec: 30、touch_thresholds_bps: [1, 3, 5, 7, 10]、snapshot_seconds: [1, 3, 5, 10, 15, 30]、require_ready_quality: true を持つ最小構成にしています。さらに max_anchor_price_lag_sec: 2.0 と max_pending_anchors: 20000 も置いてあり、今回の価格プローブは「30秒だけを見る」「quality の悪いアンカーは採らない」「アンカー時点の価格が古すぎるものは捨てる」というかなり明示的な条件で動くようにしました。
実際のアンカー生成は on_state_update() で行っています。ここではまず state を YES と NO に丸めています。今回の設計では HOLD を独立系列として扱わず、YES vs NO(=HOLD+NO) の2分類に寄せたかったので、_state_group() で YES 以外は全部 NO に倒す形にしました。そのうえで、前回状態と同じならアンカーを打たず、prev_group から state_group が変わった瞬間だけを採用しています。つまり今回の価格プローブは、YES 継続中に何本もアンカーを打つのではなく、「状態遷移そのもの」を起点にする遷移アンカー方式です。ここは、同じ局面を何度も数えないための意図的な選択でした。
アンカーに使う価格は、on_state_update() の瞬間に別途取得しているわけではありません。_JReversionPriceProbeTracker は base market の最新 tick を self._last_tick として保持していて、状態遷移が来たときに、その tick がアンカー時点から 2 秒以内なら採用する、という形にしています。ここで保存しているのは entry_bid、entry_ask、entry_mid と方向符号 sign です。つまり今回のプローブは、「状態遷移が起きたその瞬間」に対して完全同期の tick を要求するのではなく、直近の観測済み価格をアンカー価格として使う設計です。このあたりも、まずは最小構成で動かすことを優先した実装だと思っています。
アンカー後の追跡は on_market_sample() で行っています。base market の tick が来るたびに、pending アンカーをなめて elapsed 秒数を計算し、そこから directional_mid_bps と directional_exec_bps を更新します。directional_exec_bps の計算もかなり素直で、long なら entry_ask -> future_bid、short なら entry_bid -> future_ask を使っています。要するに、今回見ている executable は「その時点で成行っぽく入って、未来時点で反対側へ抜けたらどうなるか」という理論値です。手数料やスリッページはまだ引いていませんが、少なくとも mid だけを見ていた前段よりは、執行に近い基準へ一歩寄せています。
そのうえで今回のプローブでは、30秒後の一点だけでなく、途中経過も取っています。touch_thresholds_bps に対しては、directional_mid_bps >= X を満たしたかどうかを各閾値ごとに追い、touch_any、touch_count、first_touch_sec を保存します。さらに snapshot_seconds に対しては、1 / 3 / 5 / 10 / 15 / 30 秒を初めて超えたタイミングで、その時点の directional_mid_bps と directional_exec_bps を固定します。つまり今回の追加実装は、単に「30秒後どうだったか」を見るだけではなく、「途中でどれだけ触れたか」「初回到達が何秒だったか」「各時点で executable がどう推移したか」まで同時に取る構成です。価格プローブと言っても、かなり観測器寄りの設計になっています。
そして 30 秒が経過したアンカーは _finalize_anchor() で集計に落ちます。ここでは YES と NO ごとに n、sum_dir_mid、sum_dir_exec、win_dir_mid、win_dir_exec を積み上げ、touch 系と snapshot 系も同時に蓄積しています。最後に _emit_state() で平均値に直し、update_multi_market_j_reversion_probe_state() へ渡して Prometheus メトリクスへ変換します。Exporter 側でも obx_mmarb_j_reversion_probe_directional_exec_bps_mean、obx_mmarb_j_reversion_probe_directional_exec_win_rate_pct、obx_mmarb_j_reversion_probe_touch_any_rate_pct、obx_mmarb_j_reversion_probe_first_touch_sec_mean、obx_mmarb_j_reversion_probe_directional_exec_bps_at_sec_mean などが用意されていて、今回見たいものがそのまま Gauge として出るようになっています。
要するに今回の実装変更①でやったのは、J-Reversion の中身を作り替えることではありません。_JReversionStructureTracker が返した state 遷移を、_JReversionPriceProbeTracker が受け取り、bf_fx の 30 秒価格応答へつなぐ配線を作った、ということです。しかも見る指標も、directional executable mean、win rate、touch、delay、snapshot までに絞っていて、まだ解釈や最適化を入れていません。この段階では、とにかく「YES と NO のあとに主戦場で何が起きているか」を、そのまま観測できるようにすることを優先しました。
5. 実装変更②:YES / NO の executable 応答を Grafana で見えるようにした

価格プローブを追加しただけでは、まだ十分ではありません。後段で集計した数値を、そのまま低コストで読めるようにして初めて、観測機として使える形になります。今回ダッシュボード側でやったのは、J-Reversion の verdict パネルとは別に、[J-Reversion Probe 30s] という専用セクションを切り、YES / NO の価格応答をまとめて読めるようにしたことでした。これは単にグラフを増やしたというより、J-Reversion の「構造判定」と、その後の「主戦場価格の反応」を役割ごとに分離した変更です。
まず基礎になるのは、Exporter 側で price probe 用のメトリクスを独立ラベルで持たせたことです。prometheus_exporter.py では、通常の J-Reversion 用ラベルとは別に、_MMARB_J_REVERSION_PROBE_LABELS = ("base_market", "ref_market", "threshold_bps", "horizon_main_sec", "anchor_state") を定義し、その上で obx_mmarb_j_reversion_probe_anchors_total、obx_mmarb_j_reversion_probe_directional_exec_bps_mean、obx_mmarb_j_reversion_probe_directional_exec_win_rate_pct などを用意しています。さらに touch 系と snapshot 系には touch_bps と snapshot_sec を追加ラベルとして持たせているので、「YES と NO の差」だけでなく、「何bpsまで届いたか」「何秒時点でどうだったか」までそのまま引けるようになっています。
ダッシュボード側で最初に置いたのは、[J-Reversion Probe 30s] Finalized Anchors (YES vs NO) です。ここで見ているのは obx_mmarb_j_reversion_probe_anchors_total で、price probe が実際にサンプルを確定できているかを最初に確認するためのパネルです。もしここが立っていなければ、後ろに並ぶ executable mean や win rate を読んでも意味がありません。つまりこのパネルは、結果を見るためのものというより、「観測器がちゃんと動いているか」を最初に切るための入り口です。今回、パネルの並び順をこの形にしたのも、まず母数を見てから次へ進む読解順を固定したかったからでした。
その次に置いたのが、Directional Executable Mean (YES vs NO) と Directional Executable Win Rate (YES vs NO) です。ここで使っているのは obx_mmarb_j_reversion_probe_directional_exec_bps_mean と obx_mmarb_j_reversion_probe_directional_exec_win_rate_pct で、どちらも anchor_state="YES" と anchor_state="NO" を並べて比較する形にしています。description にも入れている通り、ここで見ている executable は「理論約定ベース」で、まだ手数料やスリッページは引いていません。つまり今回のフェーズでは、実運用の EV を断定するためではなく、「YES のあとと NO のあとで、主戦場価格がどちらへどれだけ動いたか」を、そのまま比較するための指標として置いています。
さらに今回のダッシュボードでは、mean だけでなく途中経過も読めるようにしました。[J-Reversion Probe 30s] Touch Any Rate by Threshold (1/3/5/7/10bps) は obx_mmarb_j_reversion_probe_touch_any_rate_pct を引いていて、30秒内に方向付き mid 変化が各閾値へ一度でも届いた割合を並べています。一方、[J-Reversion Probe 30s] First Touch Delay by Threshold (sec) は obx_mmarb_j_reversion_probe_first_touch_sec_mean を使っていて、その閾値へ最初に到達した平均秒数を見られるようにしました。これで、「どちらが勝ったか」だけでなく、「どのくらいの大きさまで届くのか」「そこに何秒かかるのか」を切り分けて読めます。歪み解消を短期構造として見たいなら、この二つはかなり重要です。

最後に置いたのが、[J-Reversion Probe 30s] Directional Exec Mean @ 1/3/5/10/15/30s です。これは obx_mmarb_j_reversion_probe_directional_exec_bps_at_sec_mean を snapshot_sec=~"1|3|5|10|15|30" で並べたもので、YES と NO のあとに executable がどのように推移したかを時点別に見られるようにしています。ここを入れた理由はかなりはっきりしていて、「30秒後だけ見ても、途中で何が起きたかは分からない」からです。1秒で一番悪くて、その後少し戻るのか。逆に序盤は反応せず、後半でようやく効くのか。こうした時間構造は、単発の mean だけでは見えません。今回の J-Reversion Probe は 30 秒固定ですが、その中身を分解して読むために、この snapshot パネルを最後に置きました。
要するに今回の実装変更②でやったのは、price probe の集計結果を単に Prometheus に積んだだけで終わらせず、Grafana 上でそのまま読める順番に並べ直したことです。まず anchors で母数を見る。次に executable mean と win rate で YES / NO の差を見る。さらに touch と delay で「どれくらい届くか」「何秒かかるか」を見る。最後に snapshot で時間推移を見る。この順番にしておくことで、J-Reversion の構造判定と、その後段で起きる主戦場価格の反応を、同じ画面の中で自然に読み分けられるようになりました。
6. スクショで見る現在地:配線確認から安定稼働確認まで
今回の実装で最初に確認したかったのは、数値の良し悪しではなく、そもそも price probe がちゃんと動いているかどうかでした。いくら _JReversionPriceProbeTracker を追加しても、on_state_update() からアンカーが渡らず、on_market_sample() で finalization されず、Exporter にも series が立たないなら意味がありません。そこで最初に見たのが [J-Reversion Probe 30s] Finalized Anchors (YES vs NO) です。ここで obx_mmarb_j_reversion_probe_anchors_total が立っていることを確認できた時点で、少なくとも state 遷移、アンカー採用、30秒後の確定、Prometheus への出力、Grafana での取得までが一通り通っていることは確認できました。まずここで、「配線は死んでいない」という段階に入れたのが大きかったです。

そのあと数時間回してみると、Finalized Anchors は YES / NO ともに継続的に積み上がるようになりました。つまり今回の price probe は、一瞬だけ数値が出たのではなく、少なくとも 7〜8 時間程度の運転では安定して母数を増やし続けていることになります。これは地味ですがかなり重要です。今回の観測器は _pending にアンカーをためて、tick を受けながら30秒後に _finalize_anchor() する作りなので、どこかで tick が詰まったり、アンカー採用条件が厳しすぎたりすると、すぐに series が伸びなくなります。そこが素直に積み上がっているということは、現時点では少なくとも「稼働が不安定で何も読めない」状態ではなくなった、ということです。
そのうえで次に見たのが、Directional Executable Mean (YES vs NO) と Directional Executable Win Rate (YES vs NO) です。ここで重要だったのは、YES が立っているかどうかではなく、YES と NO のあとで executable の応答に差があるかどうかでした。実際にスクショを見ていくと、少なくとも稼働初期の段階から「YES だから明確に強い」という絵にはなっていませんでした。むしろ、mean も win rate も YES が NO より特に良くない、あるいは少し悪い場面が見えてきています。つまり price probe を付けたことで、J-Reversion の後段に主戦場価格の差が本当にあるのかを、そのまま読める状態には入ったものの、現時点でその差は強くありません。ここで初めて、「配線が通った」ことと「優位がある」ことが別問題だとはっきりしました。

touch 系と delay 系も、同じように今の現在地をよく表しています。Touch Any Rate by Threshold を見ると、YES 側でも 1 / 3 / 5 / 7 / 10bps への到達はそれなりに観測されています。つまり、何も起きていないわけではありません。一方で、First Touch Delay by Threshold を見ると、その到達には数秒から十数秒かかっていて、しかも閾値が大きくなるほど遅くなります。これはかなり示唆的です。少なくとも今見えている歪み回帰は、「瞬間的に鋭く解消する構造」というより、「時間をかけてじわっと届くこともある構造」に近いかもしれません。ここはまだ解釈を決め切る段階ではありませんが、少なくとも想像していたより速くない、という感触は出始めています。

さらに Directional Exec Mean @ 1/3/5/10/15/30s を見ると、時間経過でどの程度改善するかも追えるようになりました。ここで見えているのは、少なくとも現在の J-Reversion では、30 秒まで待てば明確にプラスへ抜ける、という単純な絵ではないことです。初動でかなり悪く、その後に少し戻るのか、ずっと弱いのか、そのあたりの時間構造は今後もう少し丁寧に見る必要がありますが、少なくとも「30秒後だけ見れば良い」という感じではありません。今回 snapshot 系を入れた価値はまさにここで、単発の mean よりも、「どの時点で一番マシなのか」を読めるようになったことにあります。
要するに、今回スクショから確認できた現在地はかなり明確です。第一に、price probe の配線は通っていて、短時間のテストではなく、数時間スケールでも安定して母数を積めている。第二に、J-Reversion の後段で executable 応答をそのまま見られる状態には入った。第三に、そのうえで見えてきた事実として、現時点では YES が NO より明確に強いとはまだ言えない。つまり今回は、「実装した機能が動いているか」を確認する段階から、「その結果に主戦場優位が本当に乗っているか」を読み始める段階へ移れた、というのがいちばん大きな進捗でした。
7. 見え始めた事実:回帰っぽい反応はあっても、bf_fx executable ではまだ負けている
今回いちばん重要だったのは、price probe を付けたことで、「構造があるように見えること」と「主戦場で取れること」を切り分けて見られるようになったことでした。実際に数時間回してみると、J-Reversion の後段では何も起きていないわけではありません。obx_mmarb_j_reversion_probe_touch_any_rate_pct を見ると、YES 側でも 1 / 3 / 5 / 7 / 10bps の touch は観測されていますし、obx_mmarb_j_reversion_probe_first_touch_sec_mean も値を持っています。つまり、少なくとも方向付き mid ベースでは、「回帰方向にある程度触れる」現象は起きています。ここだけを見るなら、歪み回帰らしき反応が全く無いとは言えません。
ただし、今回本当に見たかったのはそこではありません。主戦場で意味があるかを確かめるために追加したのは、obx_mmarb_j_reversion_probe_directional_exec_bps_mean と obx_mmarb_j_reversion_probe_directional_exec_win_rate_pct でした。ここで見えてきたのはかなり厳しくて、少なくとも今のところ、YES のあとが NO のあとより明確に良いとは言えません。むしろスクショ上では、YES の executable mean が NO より少し悪い時間帯すらありました。つまり、J-Reversion が返している「縮みやすい構造」は、主戦場の executable 優位へそのまま変換されていません。今回ここが見えたことはかなり大きいです。前段では構造っぽく見えても、後段の執行ではまだ取れていない、ということが少なくとも可視化されました。
このズレは、touch 系と executable 系の違いを見るとかなり分かりやすいです。touch 判定は on_market_sample() の中で directional_mid_bps >= X を満たしたかどうかで数えています。つまりこれは「mid ベースではその方向へどれくらい届いたか」を見ている指標です。一方で executable mean は、long なら entry_ask -> future_bid、short なら entry_bid -> future_ask で計算しています。こちらは「その時点で実際に入って抜けたらどうなるか」に近い理論値です。だから、mid ではそこそこ触っているのに executable では負ける、というズレは普通に起こりえます。今回見え始めたのは、まさにこのズレでした。
しかも、Directional Exec Mean @ 1/3/5/10/15/30s を見ると、この問題は 30 秒時点だけの話でもありません。obx_mmarb_j_reversion_probe_directional_exec_bps_at_sec_mean で追える時点別の推移を見る限り、少なくとも現状は「最初は悪いが、少し待てば綺麗にプラスへ抜ける」という絵にはなっていません。1 秒、3 秒、5 秒、10 秒、15 秒、30 秒のどこかで多少マシになる時間帯はあっても、全体として executable 優位が立っているとはまだ言いにくい。ここから言えるのは、「時間を少し伸ばせば取れるはず」と簡単に期待できる段階ではない、ということです。
ここで大事なのは、すぐに「歪み回帰構造なんて無い」と結論しないことだと思っています。今回の結果が示しているのは、少なくとも今の J-Reversion では、回帰っぽい反応を多少捉えていても、それが bf_fx executable の優位にはまだ変わっていない、ということです。つまり問題は、「構造がゼロ」なのかもしれないし、「構造は少しあるが主戦場で取れない」のかもしれないし、「YES の定義が悪くて、取れない局面をむしろ拾っている」のかもしれない。この三つはまだ分かれていません。ただ、少なくとも一つはかなりはっきりしました。構造判定が立っていることと、実際に取引優位があることは別だ、ということです。
要するに今回見え始めた事実は、かなり痛いけれど重要です。J-Reversion の後段では、回帰方向の反応らしきもの自体は観測されている。けれど、その反応はまだ主戦場の executable 優位にはつながっていない。しかも YES が NO より明確に強いとも言えない。今回の価格プローブを入れた意味はまさにここで、前段の構造判定だけでは見えなかった「取れていない」という事実を、そのまま主戦場基準で読めるようになったことにあります。
8. 次に試すこと:YES が悪い理由を、定義・構造・執行のどこに置くか
今回の価格プローブ追加で、少なくとも次の一歩はかなり明確になりました。いま必要なのは、J-Reversion をその場の違和感で作り替えることではなく、YES が主戦場で強くならない理由を、どこに置くべきかを切り分けることです。今回見えた事実は、「YES のあとに bf_fx executable が明確に改善していない」という点でした。ここから先は、単に relation をいじるとか threshold を変えるとかではなく、その弱さが 定義の問題なのか、構造の問題なのか、執行の問題なのか を順番に切っていく必要があります。
最初に疑うべきなのは、やはり定義です。今の J-Reversion は、歪み発生を abs(premium_exec_best_bps) >= threshold_bps で置き、回帰を「一定時間内に一度でも絶対値が縮んだか」で見ています。さらに relation も relation_min_abs_corr を軸にした対数リターン相関ベースです。今回 price probe を付けたことで、こうした条件のもとで立った YES が、必ずしも executable 優位へ変換されていないことは見えてきました。つまり今後まず見るべきなのは、「YES が弱い」のではなく、「今の YES 定義が、主戦場で取れない構造まで拾っていないか」という点です。ここで初めて、relation や evidence の再設計が次の候補として意味を持ってきます。
次に切るべきなのは、構造そのものの問題です。今回の touch 系を見る限り、回帰方向への反応自体はゼロではありません。ところが executable では弱い。だとすると、「縮む現象はあるが、それは主戦場で取れるほど鋭くはない」という可能性が出てきます。つまり J-Reversion が見つけているのは、あくまで観測上の回帰っぽさであって、トレード可能なエッジではないかもしれない。この場合、問題はロジックの精度ではなく、そもそも現市場での歪み回帰が、bf_fx で取りに行くには薄すぎるということになります。もしそうなら、YES 定義を少し磨いたところで本質はあまり変わりません。
最後に残るのが、執行の問題です。今回の executable は entry_ask -> future_bid / entry_bid -> future_ask の理論値で、まだ fee や slippage は引いていません。つまり、それでもマイナスなら、実運用ではさらに厳しくなる可能性が高い。一方で、mid では多少触っているので、「現象はあるが entry / exit の持ち方が悪い」という読みも一応残ります。たとえば 30 秒固定ではなく、もっと短い抜け方や別の執行ルールなら少しマシになる可能性はあります。ただ、この議論に入る前提として、まず「YES と NO に差があるか」を見切る必要があります。今はまだ、執行の工夫で救える段階なのか、そこまで行く前に構造自体が薄いのかを切る途中です。
だから次にやることも、いきなり全部を動かす話ではありません。まずはこの price probe をもう少し回し、YES と NO の差が本当に安定して弱いのかを確認する。そのうえで、もし YES が一貫して弱いなら、次に触る順番は「定義 → 構造 → 執行」の順になると思っています。relation 再設計をするなら、そのときは price probe の同じ指標で比較する。threshold を動かすなら、時間幅は固定して動かす。もしそれでも executable 優位が出ないなら、J-Reversion は「観測上は面白いが、主戦場では取れない構造」として切る。この順番で見ていくのが、今回の結果にいちばん素直だと思います。
要するに、次のフェーズでやるべきことは、J-Reversion を無理に良く見せることではありません。YES がなぜ弱いのかを、定義・構造・執行の三つに分けて考え、どこでダメになっているのかを事実ベースで切っていくことです。今回 price probe を付けたことで、そのための物差しはようやくできました。ここから先は、その物差しを使って探索を切る段階に入っていくのだと思っています。