Bot CEX 開発ログ

🛠️開発記録#471(2026/3/4)「Premiumを“取れる歪み”として評価する ― multi_market_probe の設計」

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

トレードbotを開発していると、必ず一度は次の問いにぶつかります。

「その歪みは、本当に取れるのか?」

市場間の価格差、いわゆる premium は、グラフ上では頻繁に観測できます。しかし、そのすべてがトレード可能なエッジとは限りません。スプレッド、約定遅延、板の厚さ、そして市場ごとの反応速度といった現実の制約を考えると、単に「歪みがある」という事実だけでは、実際に利益につながるかどうかは判断できないからです。

そこで今回、私は premium を「観測する」のではなく、「取れる歪みとして評価する」ための観測機として、multi_market_probe という仕組みを設計しました。

この観測機の目的はシンプルです。
市場間の premium を、

  • どれくらいの幅で発生するのか(width)
  • どの程度の頻度で出現するのか(frequency)
  • どれくらい持続するのか(persistence)
  • その後どのような結果になるのか(future outcome)

といった形で整理し、最終的に 「この歪みは取れるのか/取れないのか」を白黒つけることです。

本記事では、この multi_market_probe の設計思想と実装について、できるだけ技術寄りに整理していきます。特に、premium を単なる価格差ではなく トレード可能性を評価するための状態量として扱うために、どのような観測指標と判定ロジックを用意したのかを中心に解説します。

トレードbotの研究は、ともすると「理解」を深める方向に進みがちです。しかし実務的には、重要なのは 市場を説明することではなく、取れるかどうかを判定することです。本記事が、同じように bot を作っている方にとって、「歪みをどう評価するか」を考えるヒントになれば嬉しいです。

1. 目的:白黒をつける観測機を作る

トレードbotの研究を続けていると、ある問題に必ず直面します。

「この歪みは、本当に取れるのか?」

市場間の価格差(premium)は、観測するだけならいくらでも見つかります。しかし、その多くは実際のトレードでは消えてしまいます。理由はシンプルで、スプレッド・手数料・板厚・レイテンシといった現実の制約が存在するからです。

つまり、

歪みがあること

歪みを取れること

は、全く別の問題です。

そこで今回作ったのが、**「multi_market_probe」**という観測機です。これはトレードbotではなく、観測専用の装置として設計しています。

コードの冒頭にも、その思想をはっきり書いています。

"Observation-only multi-market probe for main-market distortion detection."

この装置の役割はシンプルです。

歪みを見つけることではなく、
歪みが“取れるかどうか”を判定すること。

そのために、観測機は以下の構造を持っています。


観測機の基本構造

multi_market_probe は、複数市場の価格を観測しながら、主戦場との歪みをリアルタイムに評価します。

設計上の前提は次の通りです。

  • 主戦場:bitFlyer FX
  • 参照市場:Binance / Bybit / Coinbase / etc
  • FX換算:USD → JPY

この設計はコードでも明確に定義されています。

self.base_market = str(self.config.get("base_market", "bf_fx"))
self.fx_market = str(self.config.get("fx_market", "usd_jpy"))

multi_market_probe_v1

つまりこの観測機は、

「国内市場 vs 世界価格」

という構造を常に観測し続ける装置です。


観測するのは「歪み」ではなく「取れる歪み」

この観測機では、単なる価格差ではなく、

executable ベースの歪み

を計算します。

exec_ab_raw = base.bid - ref_ask_jpy
exec_ba_raw = ref_bid_jpy - base.ask
exec_ab_bps = (exec_ab_raw / ref_mid_jpy) * 10000.0
exec_ba_bps = (exec_ba_raw / ref_mid_jpy) * 10000.0
exec_best_bps = max(exec_ab_bps, exec_ba_bps)

ここでやっていることは、

  • base.bid − ref.ask
  • ref.bid − base.ask

という 実際に約定可能な価格差の計算です。

そして最終的に、

exec_best_bps

として、実行可能な最大歪みを評価します。

これは単なる mid 差ではありません。

実際に取引可能な歪みです。


さらにコストを差し引く

歪みが見えても、まだトレード可能とは限りません。

そこで次に行うのが、

コストを引いた期待値の評価です。

total_cost_bps = self._resolve_total_cost_bps(ref_market)
edge_net_bps = exec_best_bps - total_cost_bps

つまり観測機は、

Edge = executable_spread - trading_cost

という式で 純粋なエッジを計算しています。

そしてこの値がプラスのときだけ、

ev_positive_flag = 1

になります。


この観測機の本当の役割

この設計の目的は、戦略を作ることではありません。

むしろ逆です。

戦略の前に、エッジの存在を証明すること。

multi_market_probe は、

  • premiumの幅
  • premiumの頻度
  • premiumの持続
  • premiumの収束

といったデータを収集し、

最終的に次の問いに答えるための装置です。

この歪みは
本当に取れるのか?

YESなら戦略を作る。
NOならその歪みは捨てる。

つまりこの観測機は、

トレードbotではなく、
トレード戦略の裁判官です。


2. 歪みを「構造」ではなく「分布」で見る

premium(市場間の歪み)について考えるとき、私は以前まで「なぜこの歪みが出たのか」という構造の説明に意識が引っ張られがちでした。たとえば、海外が先に動いたのか、国内フローが偏ったのか、板が薄いのか、レイテンシが崩れているのか……といった具合です。

もちろん構造の理解は大事です。しかし、トレードの観点では、構造を理解する前に必ず答えなければならない問いがあります。

「その歪みは、どのくらいの頻度で起きて、どのくらいの幅で現れるのか?」

つまり、まず必要なのは「構造」ではなく 分布 です。
分布が分からない状態で原因を掘っても、トレードに繋がる判断はできません。


premiumは「イベント」ではなく「状態量」

multi_market_probeでは、premiumを「条件を満たしたらトレードするイベント」として扱う前に、まず **状態量(state variable)**として継続観測します。

premiumは、mid差とexecutable差(実際に約定可能な差)の両方を計算しますが、特に重要視しているのは executable 側です。実装上は、base/refの両方向を計算して大きい方を採用します。

  • base を売って ref を買う(base.bid − ref.ask)
  • ref を売って base を買う(ref.bid − base.ask)

この「取れる側」の最大値が exec_best_bps で、以降の観測・評価は基本的にこれを使います。multi_market_probe_v1

ここで言いたいのは、premiumを「ここで発火」みたいな点ではなく、
premium(t) という連続的な状態量として扱う、ということです。


なぜ「分布」を先に見る必要があるのか

premiumをイベントとして扱う典型的な発想はこうです。

  • premium > 10bps ならチャンス
  • premium > 10bps になったらトレード

ただ、この発想には致命的な穴があります。
premiumが10bpsを超える状態が「毎分起きる」のか「1日に数回」なのかで、トレード設計は完全に変わるからです。

つまり、トレードの前に必要なのは

  • どのくらいの幅で(width)
  • どのくらいの頻度で(frequency)

premiumが出るのか、という分布情報です。

期待値の観点でも、ざっくり言えば次の3要素で決まります。

  • width(幅)
  • frequency(頻度)
  • persistence(持続)

このうち width と frequency は、分布を見ない限り評価できません。


width:歪みの「典型値」と「スパイク」を分ける

multi_market_probeでは、premiumの幅を「瞬間値」で眺めるだけでなく、短い窓で集計して典型値とスパイクの両方を見られるようにします。

ダッシュボードでは、たとえば次のような指標を置きます。

  • 直近5分の |exec_best| の平均(平均的な歪み幅)
  • 直近5分の |exec_best| の P95(スパイク込みの実効歪み幅)

これにより、「常に8bpsくらい歪んでいる」のか、「普段は小さいが時々10bpsを超える」のかを分離できます。


frequency:閾値別に「どれくらい出るか」を見る

次に重要なのが、premiumが閾値を超える頻度です。
ここは一つの閾値だけを見ると誤解しやすいので、私は複数閾値(例:8/10/12bps)で同時に観測します。

具体的には、

  • |premium_exec_best_bps| >= 8
  • |premium_exec_best_bps| >= 10
  • |premium_exec_best_bps| >= 12

をそれぞれ「直近5分の超過率(%)」として出します。

これをやると、分布の形がざっくり掴めます。

  • 8bpsはよく出るが、12bpsはほぼ出ない → しきい値を上げると発火が死ぬ
  • 10bpsもそこそこ出る → “取れる歪み”候補として検証価値がある

この段階で初めて、探索対象の優先度が決まります。


「分布」を見たあとに、次の問いへ進む

ここまでで、premiumを

  • 幅(width)
  • 頻度(frequency)

という分布として扱えるようになります。

すると次に自然に出てくる問いはこれです。

「その歪みは、どれくらい続くのか?」

つまり persistence(持続)です。

分布(width/frequency)を見ずに persistence に入ると、そもそも母集団が薄かったり、希少スパイクに引っ張られて判断が歪みます。だから私は、まず分布を出してから持続と未来評価に進む、という順番を採用しています。

次の章では、この persistence を起点にして、**signal → future outcome(その後どうなったか)**を測定するところへ進みます。


3. signal → future outcome を直接測る

ここまでで、premium を 分布(width / frequency)として観測するところまで整理しました。しかし、トレードとして一番重要なのは次の問いです。

「その歪みが出たあと、実際に何が起きるのか?」

つまり、
signal → future outcome
を直接測る必要があります。

多くのトレード研究では、この部分が意外と曖昧です。たとえば、

  • premium が大きい
  • premium が続いている
  • premium が過去に有効だった

といった理由でシグナルを作り、そのままバックテストに進んでしまうことがあります。しかし、この順番だと「なぜ勝ったのか」が分からないまま戦略が複雑化しやすく、結果として過去データへの過剰適合に陥りやすくなります。

そこで multi_market_probe では、シグナル発生時点をアンカーとして未来の結果を評価するという方法を採用しています。


premiumイベントを「アンカー」として記録する

具体的には、premium が一定の条件を満たした瞬間をイベントとして記録します。たとえば、

abs(premium_exec_best_bps) >= threshold

の瞬間です。

このイベントを「アンカー」として保存し、一定時間後の premium を比較します。

premium(t + horizon) - premium(t)

この差分を future premium delta として計測します。

multi_market_probe のコードでは、このイベントを内部キューに保存して後から評価する形になっています。multi_market_probe_v1

この仕組みによって、

  • premium が発生した時点
  • その後の premium の変化

を直接結びつけて観測できます。


「未来の結果」をどう評価するか

future outcome を評価する方法はいくつかありますが、今回の観測機では主に次の2つを見ています。

1. future premium delta

これは

premium(t+Δ) - premium(t)

という単純な差分です。

値の解釈は次の通りです。

  • 負の値
    → premium が縮小した(回帰)
  • 正の値
    → premium が拡大した(持続)

つまり、この値を見るだけで

  • mean reversion
  • continuation

のどちらの挙動が強いかが分かります。

Grafana では、最新の評価値として

Future Premium Delta Latest

を表示しています。


2. reversion hit rate

さらに、premium が縮小した割合も計測します。

つまり、

abs(premium(t+Δ)) < abs(premium(t))

となった割合です。

これを

Future Reversion Hit Rate

としてダッシュボードに表示しています。

この値が高ければ、

  • premium は平均回帰する傾向がある

と判断できます。


persistenceとfuture outcomeを結びつける

もう一つ重要なのが、premium の **持続時間(persistence)**です。

multi_market_probe では、

premium_persistence_sec

として、premium が閾値以上の状態で連続している秒数を計測しています。

これを future outcome と組み合わせることで、

  • premium が 長く続いたとき
  • premium が 瞬間的に出たとき

で結果がどう変わるかを見ることができます。

たとえば、

  • persistence が長い premium は回帰しやすい
  • persistence が短い premium はノイズ

といった構造が見えてくる場合があります。


シグナルの有効性を直接判定する

このように、

  • premium 発生
  • future premium delta
  • reversion hit rate
  • persistence

を組み合わせることで、シグナルの有効性を直接評価できます。

つまり、

signal → future outcome

という形で、シグナルと結果を一対一で結びつけるわけです。

この設計のメリットは非常にシンプルです。

トレード戦略を作る前に、シグナルの期待値を確認できる。

多くのトレード研究では、

signal → strategy → backtest

という順番になりますが、multi_market_probe では

signal → future outcome → EV判断

という順番で評価します。

この順番にすることで、
「そもそもこの歪みは取れるのか?」
という問いに、より直接的に答えられるようになります。

そして、この結果をまとめて最終的に判断するのが、次の章で説明する EV判定ロジックです。


4. トレード研究が迷子になる瞬間

トレード研究を続けていると、ある瞬間から急に進まなくなることがあります。
新しい指標を試し、データを増やし、分析も深まっているはずなのに、なぜか結論に辿り着かない。むしろ、やればやるほど研究の範囲が広がっていくような感覚になります。

私自身も、この状態に何度も入りました。

原因はシンプルです。
トレード研究が迷子になる瞬間は、たいてい次の流れで起こります。

歪みを観測する

なぜ起きたのかを説明する

説明変数を増やす

過去データへの説明力が上がる

未来の再現性が下がる

つまり、

「理解」を深めようとしすぎると、トレードから遠ざかる

という現象です。


構造を説明し始めると、研究は終わらなくなる

たとえば premium を観測していると、自然と次のような疑問が出てきます。

  • 海外市場が先に動いたのか
  • 国内フローが偏っているのか
  • 板が薄いのか
  • レイテンシが崩れているのか
  • マクロニュースの影響なのか

これらはすべて、市場構造の説明としては正しい問いです。しかしトレードの観点では、これらを掘り始めると研究が終わらなくなります。

なぜなら、説明できる要因はいくらでも増やせるからです。

  • order book imbalance
  • trade flow
  • spread regime
  • volatility regime
  • cross-market lead lag
  • latency structure

など、説明変数は無限に増やせます。

そして変数を増やせば増やすほど、過去データに対する説明力は上がります。
しかしそれは同時に、

過去への過剰適合(overfitting)

に近づくことでもあります。


トレードに必要なのは「理解」ではなく「判定」

ここで一度立ち止まる必要があります。

トレード研究の目的は、

  • 市場を理解すること
    ではなく
  • トレードできるかどうかを判定すること

です。

つまり、研究のゴールは

この歪みは取れる

または

この歪みは取れない

という 白黒の判断です。

それ以上の説明は、必ずしも必要ではありません。


説明よりも分布を見る

そのため、この観測機では

  • premium の原因
  • premium の構造

を掘ることよりも、まず

  • premium の幅(width)
  • premium の頻度(frequency)
  • premium の持続(persistence)
  • premium の結果(future outcome)

という 分布ベースの観測を優先しています。

この4つが分かれば、

EV ≈ width × frequency × persistence

という形で、歪みの期待値をおおまかに評価できます。

つまり、

構造を説明できなくても、期待値は判断できる

ということです。


探索はどこかで止めなければならない

トレード研究は、放っておくと無限に広がります。
市場の構造を完全に理解することは、おそらく不可能です。

だからこそ、研究には「止めるポイント」が必要です。

multi_market_probe の役割は、まさにそこにあります。

この観測機は、

premiumの分布

premiumの持続

signal → future outcome

EV判定

という順番でデータを整理し、最終的に

GO / NO-GO

という判断を出すことを目的にしています。

つまりこの装置は、

市場を理解するためのものではなく、
探索を止めるためのもの

です。

トレード研究が迷子になる瞬間というのは、たいてい「理解」を追いかけ始めたときです。
その代わりに、観測 → 分布 → 結果 → 判定、という順番に戻ることで、研究を再び前に進めることができます。


5. 探索を止めるための装置

ここまで説明してきたように、multi_market_probe の役割は「歪みを観測すること」ではありません。
本当の目的はもっと単純です。

探索を止めること。

トレード研究を続けていると、どうしても次のような状態に陥りがちです。

  • premium の原因を探す
  • 新しい説明変数を追加する
  • 分析を細かくする

すると、研究はどんどん精密になります。しかしその一方で、結論が出なくなるという問題が生まれます。

なぜなら、市場を説明する要因はほぼ無限に存在するからです。

そのため、どこかの段階で次の問いに答える必要があります。

この市場構造はトレード対象として成立するのか?

multi_market_probe は、この問いに答えるための装置として設計されています。


EV判定を中心に設計する

この観測機では、最終的な判断を EV(期待値)ベースで行います。

まず、実際に取引可能な歪みを計算します。

edge_net_bps = executable_spread - trading_cost

この値はコード上では edge_net_bps として計算され、Prometheus メトリクスとしても公開されています。prometheus_exporter

この値が正であれば、理論上はトレード可能な歪みが存在することになります。

つまり、

edge_net_bps > 0

EV positive の状態です。

ダッシュボードでは、この状態がどれくらいの割合で発生しているかを

EV Positive Ratio

として表示しています。


複数の条件をまとめて判定する

ただし、EVが正であることだけではトレードには不十分です。

そこで multi_market_probe では、次のような条件を組み合わせて最終判定を行います。

  • EV positive ratio
  • premium persistence
  • future reversion hit rate
  • lead-lag signal

Grafana ダッシュボードでは、これらを合算して

Pair Verdict Score

というスコアを計算しています。

さらにすべての条件を満たした場合のみ、

Pair Verdict

GO になります。

つまりこの観測機は、最終的に

GO
or
NO-GO

という判断を出す装置になっています。


研究のゴールは「理解」ではなく「判定」

ここで重要なのは、multi_market_probe が

市場を説明する装置ではない

という点です。

この観測機は、

  • premium がなぜ発生するのか
  • 市場の構造がどうなっているのか

といった問いに答えるためのものではありません。

むしろ目的はその逆です。

説明を始める前に、トレード可能かどうかを判定する。

この順番にすることで、研究の方向が大きく変わります。

通常の研究では、

構造理解

仮説

バックテスト

という流れになります。

しかし multi_market_probe では、

観測

分布

future outcome

EV判定

という順番を採用しています。


観測機は「研究の終点」を作る

トレード研究が長く続く理由の一つは、終わりがないことです。

新しいデータ、新しい指標、新しい説明……
研究はどこまでも広げることができます。

だからこそ、意図的に「終点」を作る必要があります。

multi_market_probe の役割は、その終点を作ることです。

  • premium を観測する
  • premium の分布を見る
  • premium の未来結果を測る
  • EV を計算する

そして最後に、

この歪みは取れるのか?

という問いに答えます。

YESなら戦略を作る。
NOならその歪みは捨てる。

それ以上の説明は、トレードにとって必須ではありません。


multi_market_probe は、
市場理解のための装置ではなく、探索を止めるための装置です。

そしてトレード研究においては、
この「止めるための装置」があることが、意外と重要だったりします。


6. 今日の状態

ここまでで、multi_market_probe の基本的な観測構造は一通り実装できました。
現時点ではまだ「戦略」と呼べる段階ではありませんが、少なくとも 歪みを評価するための観測装置としては、最低限の形が整った状態です。

現在のダッシュボードは、大きく3つのレイヤーで構成されています。


① 市場の歪み分布(Market Observation)

まず最初に観測しているのは、premium の分布そのものです。

具体的には次のような指標を出しています。

  • Premium Width Avg(平均歪み幅)
  • Premium Width P95(スパイク込みの歪み幅)
  • Premium Frequency(歪み発生頻度)

さらに、複数の閾値を並べて比較できるようにしています。

|premium| >= 8bps
|premium| >= 10bps
|premium| >= 12bps

これにより、

  • 歪みがどの程度の幅で現れるのか
  • どの程度の頻度で発生するのか

を、イベントではなく分布として観測できるようになっています。


② signal → future outcome(Future Evaluation)

次に、歪みが出たあとに実際に何が起きるかを測定します。

具体的には次の指標を観測しています。

  • Premium Persistence(歪みの持続時間)
  • Future Premium Delta(一定時間後の歪み変化)
  • Future Reversion Hit Rate(回帰率)

これらはすべて、

signal → future outcome

という関係を直接測るためのものです。

つまり、

  • 歪みはどれくらい続くのか
  • その歪みはその後どうなるのか

を、シグナル起点で評価しています。

この部分が、この観測機の中核になります。


③ EV判定(Judgement Layer)

最後に、観測結果をまとめて トレード可能かどうかの判定を行います。

ダッシュボード上では、次のような指標をまとめています。

  • EV Positive Ratio
  • LeadLag PASS Count
  • Pair Verdict Score
  • Pair Verdict(GO / NO-GO)

特に重要なのは、最後の Pair Verdict です。

ここでは、

  • EVの成立
  • 歪みの持続
  • 回帰の確認
  • リードラグシグナル

といった条件を組み合わせて、

GO
or
NO-GO

という判断を出します。

つまりこの観測機は、単なる分析ツールではなく、

トレード対象として成立するかどうかを判定する装置

として機能する設計になっています。


現時点での位置づけ

今日の時点での状態をまとめると、

  • 観測機の骨格はほぼ完成
  • premium の分布は観測可能
  • signal → future outcome も測定可能
  • EV判定ロジックも実装済み

という段階です。

もちろん、これでトレード戦略が完成したわけではありません。
ただし、少なくとも次の問いには答えられるようになりました。

この歪みは、トレード対象として成立するのか?

この問いに答えられる状態になったこと自体が、今回の開発の一番大きな成果だと思っています。

次のステップはシンプルです。

データを蓄積し、判定結果を観察すること。

もし Verdict が継続的に GO を示すなら、その歪みは戦略化の候補になります。
逆に NO-GO が続くなら、その歪みは「取れない構造」として切り捨てることになります。


7. 今日の結論

今日の作業の結論を一言でまとめると、

premium を「取れる歪み」として評価するための観測機は、ひとまず形になった

というところです。

今回作った multi_market_probe は、トレードbotそのものではありません。
むしろ役割はその逆で、トレード戦略を作る前に「この歪みは本当に取れるのか」を判定する装置です。

そのために、この観測機では次の4つの要素を順番に観測します。

  • 歪みの幅(width)
  • 歪みの頻度(frequency)
  • 歪みの持続(persistence)
  • 歪みの結果(future outcome)

そして最終的に、

GO / NO-GO

という形で、トレード対象として成立するかどうかを判断します。

つまりこの装置は、市場を説明するためのものではなく、探索を止めるためのものです。

トレード研究を続けていると、どうしても市場の構造を理解しようとしてしまいます。しかし市場の説明はどこまででも深掘りできますし、その過程で研究は簡単に迷子になります。

その代わりに、今回の観測機では次の順番でデータを見るようにしました。

premium観測

分布確認

signal → future outcome

EV判定

この順番にしておくことで、

この歪みは取れるのか?

という問いに、比較的シンプルに答えられるようになります。

もちろん、現時点ではまだデータが十分に溜まっていません。
今日の段階では、あくまで観測装置が動き始めたところです。

ただ、少なくともこれで

  • premium を分布として観測する
  • premium の未来結果を測る
  • EV を計算する
  • GO / NO-GO を判定する

という一連の流れは整いました。

次にやることはシンプルです。

データを溜めて、この装置に判断させること。

もし Verdict が安定して GO を示すなら、その歪みは戦略化の候補になります。
逆に NO-GO が続くなら、その歪みはトレード対象として成立しない構造だと判断できます。

トレード研究では、つい「何か新しいことを見つけよう」としてしまいがちです。
しかし実際には、その前にやるべきことがあります。

取れない歪みを捨てること。

今回作った観測機は、そのための装置です。


-Bot, CEX, 開発ログ