odb-microstructure-forensics について
ClickHouse の結果から Parquet の生の取引データまでたどり、Open Deviation Bar の異常を調査するフォレンジック手順です。データ分析者やトレーディング分析者が、アルゴリズムの欠陥と市場マイクロストラクチャの影響を見分けるのに役立ちます。
主な機能
- 01バーから取引までの三層調査
- 02閾値超過と継続時間ゼロのバー検出
- 03タイムスタンプの集中と注文フロー分析
- 04市場要因とデータ要因の分類
活用シーン
- 01異常に大きい、または短い ODB バーの調査
- 02閾値超過がデータ特性によるものか確認
- 03取引記録からデータ欠損の疑いを追跡
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ terrylica/ cc-skills --skill odb-microstructure-forensicsインストール後、Claude Code で/odb-microstructure-forensicsを入力して読み込まれたか確認します。
同梱ファイルreferences/clickhouse-schema.mdreferences/evolution-log.md
詳細
ODB マイクロストラクチャーのフォレンジック調査
ClickHouse キャッシュから生の Parquet 取引データまでたどり、Open Deviation Bar の異常を調査する体系的な手法です。アルゴリズムの正確性に関する問題と、市場のマイクロストラクチャー上の現象(清算カスケード、オーダーブックのスイープ、マッチングエンジンのバッチ処理による影響)を区別します。
自己進化するスキル:このスキルは使用を通じて改善されます。指示に誤りがある、パラメーターがずれている、または回避策が必要だった場合は、先送りせず、すぐにこのファイルを修正してください。実際に再現可能な問題がある場合にのみ更新してください。
使用するタイミング
- ODB バーが、同じ閾値の隣接バーより視覚的に大きく(高さや幅が)見える
- バーの継続時間がゼロまたはほぼゼロで、価格レンジが極端に大きい
abs_dev_dbpsが閾値を超えている(例:100 dbps のバーで 22 dbps)- 1 つのタイムスタンプにマイクロバーが集中している
- 異常がデータのバグか市場のマイクロストラクチャーによるものかを診断する
- Flowsurface チャート UI でフラグが立った特定の時間帯を調査する
データソース
| ソース | 場所 | スキーマ | アクセス |
|---|---|---|---|
| ClickHouse キャッシュ | 上の opendeviationbar_cache.open_deviation_bars | 76 列。スキーマリファレンスを参照してください | ssh <gpu-host> 'curl -s http://localhost:8123/ -d "..."' |
| Parquet ティックキャッシュ | 上の /home/<user>/.cache/opendeviationbar/ticks/{SYMBOL}/{YYYY-MM-DD}.parquet | agg_trade_id, price, quantity, first_trade_id, last_trade_id, timestamp, is_buyer_maker | Polars を使った ssh <gpu-host> 'cd /home/<user> && uv run --python 3.14 python3 -c "..."' |
アクセス方法:常に SSH 経由で に直接クエリしてください。SSH トンネル(localhost:18123)は Flowsurface アプリの実行時専用です。フォレンジック調査のクエリは直接実行してください。
調査方法
調査は 3 層のドリルダウンに従います:ClickHouse の概要 → バーごとの異常検出 → Parquet の取引レベルでの根本原因分析。
レイヤー 1:ClickHouse バーの概要
疑わしい時間帯のバーをクエリします。結果セット内の異常の兆候を確認してください。
-- Adjust symbol, threshold, and time window to match the investigation
SELECT
toDateTime64(close_time_us / 1000000, 3) AS close_ts,
toDateTime64(open_time_us / 1000000, 3) AS open_ts,
open, high, low, close,
round((high - low) / open * 10000, 1) AS range_dbps,
round(abs(close - open) / open * 10000, 1) AS abs_dev_dbps,
agg_record_count AS n_agg,
individual_trade_count AS n_trades,
round(duration_us / 1e6, 1) AS dur_s,
first_agg_trade_id AS first_id,
last_agg_trade_id AS last_id,
is_orphan, is_liquidation_cascade AS is_liq
FROM opendeviationbar_cache.open_deviation_bars
WHERE symbol = '{SYMBOL}'
AND threshold_decimal_bps = {THRESHOLD}
AND close_time_us >= toUnixTimestamp('{START_UTC}', 'UTC') * 1000000
AND close_time_us <= toUnixTimestamp('{END_UTC}', 'UTC') * 1000000
ORDER BY close_time_us
FORMAT PrettyCompact
検出すべき異常の兆候:
| 兆候 | 列のパターン | 意味 |
|---|---|---|
| 閾値の超過 | abs_dev_dbps >> threshold / 10 | 単一の取引が閾値を超えている |
| 継続時間ゼロ | dur_s = 0 | 1 回のマッチングエンジンのサイクル内にバー全体が形成された |
| 極端なレンジ | range_dbps > 2 * threshold / 10 | 価格が閾値を大きく超えてスイープされた |
| マイクロ取引数 | レンジが大きく、n_agg < 10 | 巨大な個別約定がオーダーブックを食い尽くしている |
| バーストの集中 | 同じ秒に複数のバーがある | 流動性スイープが複数のバーに分割されている |
レイヤー 2:異常の切り分け
異常なバーだけに絞り込み、Parquet での詳細調査に使う agg_trade_id の範囲を取得します:
-- Bars with threshold overshoot or zero duration
SELECT close_ts, dur_s, open, high, low, close,
range_dbps, abs_dev_dbps, n_agg,
first_agg_trade_id, last_agg_trade_id
FROM (... Layer 1 query ...)
WHERE dur_s < 1.0 OR abs_dev_dbps > {THRESHOLD / 10 * 1.5}
first_agg_trade_id と last_agg_trade_id の範囲を記録してください。これらが Parquet 検索のキーになります。
レイヤー 3:Parquet の取引レベルでの根本原因分析
上で Polars を使い、生の取引データを分析します。以下の 3 つの分析を順に行います:
3a. タイムスタンプバーストの検出
タイムスタンプで取引をグループ化し、マッチングエンジンのバッチ(同じマイクロ秒を共有する数百件の取引)を見つけます。
import polars as pl
df = pl.read_parquet("/home/<user>/.cache/opendeviationbar/ticks/{SYMBOL}/{DATE}.parquet")
burst = df.filter(
(pl.col("agg_trade_id") >= {FIRST_ID}) &
(pl.col("agg_trade_id") <= {LAST_ID})
).sort("agg_trade_id")
# Group by timestamp to find single-cycle batches
ts_groups = burst.group_by("timestamp").agg([
pl.col("price").min().alias("min_price"),
pl.col("price").max().alias("max_price"),
pl.col("quantity").sum().alias("total_qty"),
pl.len().alias("count"),
]).sort("timestamp")
重要な診断ポイント:1つのタイムスタンプに価格帯全体にわたる数百件の取引がある場合、それはマッチングエンジンのバッチです(1件の大口注文が板を掃討しています)。
3b. 注文フロー分析
スイープが買い優勢か売り優勢かを判定します。
buys = burst.filter(~pl.col("is_buyer_maker")) # taker buy
sells = burst.filter(pl.col("is_buyer_maker")) # taker sell
print(f"Taker buys: {len(buys)} trades, {buys['quantity'].sum():.4f} BTC")
print(f"Taker sells: {len(sells)} trades, {sells['quantity'].sum():.4f} BTC")
清算カスケードは、通常、95%以上が一方向です(すべてテイカー売り、またはすべてテイカー買い)。
3c. 価格ギャップ分析
大きな価格変動がある個別の取引を見つけます。これらがしきい値超過の直接的な原因です。
with_gap = burst.sort("agg_trade_id").with_columns([
(pl.col("price") - pl.col("price").shift(1)).alias("price_diff"),
(pl.col("timestamp") - pl.col("timestamp").shift(1)).alias("ts_diff_us"),
])
# Trades with gaps exceeding threshold dollar equivalent
threshold_dollars = open_price * threshold_dbps / 10000
big_jumps = with_gap.filter(pl.col("price_diff").abs() > threshold_dollars)
個々の取引間の価格ギャップがしきい値を超える場合、ODB アルゴリズムは1つの agg_trade 内を分割できません。超過は本質的なものであり、正しい動作です。
根本原因の分類
3層の分析を完了したら、調査結果を分類します。
| 分類 | 証拠のパターン | 対応 |
|---|---|---|
| 清算カスケード | 95%以上が一方向、50~100 BTC 以上、同じ µs タイムスタンプ、200 ドル以上のスイープ | オラクルはビット単位で完全一致しています。修正は不要です。イベントを記録してください。 |
| 薄い板のスイープ | 取引数は少ないものの、価格水準間に大きな価格ギャップがある | オラクルはビット単位で完全一致しています。その時点で板が薄かったことを示しています。 |
| 孤立バー | ClickHouse で is_orphan = 1 | 既知の現象です。ライター境界のアーティファクトです。分析から除外してください。 |
| アルゴリズムのバグ | 取引が通常どおりに分布し、バーストもないのに、バーがしきい値を超過する | opendeviationbar に上流の問題を報告してください。 |
| データギャップ | 隣接するバー間で agg_trade_id が不連続 | Parquet データが欠落しています。収集パイプラインを確認してください。 |
しきい値超過の仕組み
ODB アルゴリズムは agg_trades を順番に処理します。最初の取引がバーの始値からしきい値を超えて乖離すると、バーが確定します。超過の仕組みは次のとおりです。
- 価格 P₀、しきい値 T(例:100 dbps = 0.1%)でバーが始まります
- アルゴリズムは取引 P₁、P₂、... Pₙ を順に調べます
- 取引 Pₖ で
|Pₖ - P₀| / P₀ ≥ Tとなり、バーは Pₖ で確定します - Pₖ₋₁ がしきい値内だった一方で Pₖ が大きく跳ねた場合、超過分は
|Pₖ - P₀| / P₀ - Tです
しきい値が低いほど、超過は大きくなります。
- BPR10 のしきい値は、BTC が 7 万ドルの場合 70 ドルです。板のギャップが 150 ドルなら、超過は 2 倍になります。
- BPR50 のしきい値は、BTC が 7 万ドルの場合 350 ドルです。同じ 150 ドルのギャップでも、しきい値内に十分収まります。
これは離散的な取引データに固有の性質であり、バグではありません。
関連スキル
| スキル | 関連性 |
|---|---|
| opendeviation-eval-metrics | ODB シグナルの品質(出力メトリクス)を評価します。このスキルでは入力データの品質を調査します。 |
| exchange-session-detector | ClickHouse のセッションフラグです。カスケードはセッションの境界(NY の開始/終了時刻)に集中することがよくあります。 |
実行後の振り返り
このスキルの実行後、終了する前に次の点を確認します。
- クエリは成功しましたか? — ClickHouse のスキーマが変更された場合(列名の変更、新しい列の追加)は、レイヤー 1 の SQL テンプレートを更新します。
- Parquet のスキーマは変更されましたか? — ティックキャッシュの列が変更された場合は、Polars のスニペットを更新します。
- 新しい根本原因のパターンが見つかりましたか? — エビデンスのパターンと対応策を添えて、根本原因の分類表に追加します。
- 閾値のオーバーシュートの仕組みに関する説明は妥当でしたか? — 新たなオーバーシュートの仕組みが見つかった場合は、記録します。
問題が実在し、再現可能な場合に限り更新します。推測に基づいて更新してはいけません。