Your Backtest Is Lying: How Cohort Data Exposes the Gaps
By @CoinMarketMan - 14-Jul-2026
バックテストは嘘をついている:コホートデータが露わにするギャップ
バックテストを実行した。建玉が急増し、ファンディングがマイナスに転じ、シグナルが発動し、シミュレーションのP&Lはプラスで返ってきた。シャープレシオは綺麗に見える。勝率は200トレードにわたって安定している。そこでライブに移行すると、2週間以内に戦略は損失を垂れ流し始める。
問題はシグナル自体にあることはほとんどない。シグナルが伝えられないことにある。ファンディングの反転は、それを引き起こしているウォレットが歴史的に利益を出しているトレーダーであれ、連続して清算される被害者であれ、同じファンディングの反転だ。OIの急増は、Smart Moneyコホートのウォレットがポジションを積み上げているのが原因であれ、Giga-Rektのウォレットが負けているテーゼに二倍賭けしているのが原因であれ、見た目は同じだ。バックテストは両方のシナリオを同じように扱った。区別する手段がなかったからだ。
その区別こそ、コホートデータが埋めるギャップだ。HyperTrackerはHyperliquid上のすべてのウォレットを16のコホートに分類する。口座サイズ別で8つ(Shrimp からLeviathan)、累計PnL別で8つ(Money Printer からGiga-Rekt)だ。その分類をバックテストに重ねることで、「シグナルが発動したか?」という問いから「このトレードの両サイドに誰がいるか?」という問いへと変わる。
標準的なバックテストの盲点
Hyperliquid上のほとんどのバックテストは、価格・出来高・建玉・ファンディングレートを使う。これらは取引所のパブリックAPIから取得できるインプットであり、単体でテストしたときに良好な結果を出す方向性シグナルを構築するには十分だ。問題は、何を見逃しているかを確認したときに浮上する。
シンプルなロングシグナルを考えてみよう。OIが急上昇しながら、ファンディングがフラットかわずかにマイナスに留まっている。標準的な解釈は、新たな資金がロングポジションに入っているが過剰なキャリーコストを払っていない、つまりレバレッジ投機ではなく真の確信を示唆するというものだ。バックテストでは、このパターンは数百トレードにわたってそこそこポジティブなエッジを生み出す。
しかし集計されたOIは、誰の確信を見ているのかを教えてくれない。OIの増加が主にMoney Printerコホート(累計利益$1M以上、セグメントID 8)とSmart Moneyコホート($100K〜$1M、セグメントID 9)から来ているなら、おそらく情報を持つポジショニングを見ていることになる。同じOIの急増がGiga-Rektのウォレット(生涯損失$1M以下、セグメントID 15)とFull Rektのウォレット(-$1M〜-$100K、セグメントID 14)によって主に引き起こされているなら、その「確信」は清算後の投げや意地張りのトレードである可能性が高い。
どちらのシナリオも同じOIチャートを生み出す。どちらも同じシグナルを発動する。しかし期待される結果は意味のある差がある。動きの背後にある資本の質が重要だからだ。
ヒストリカルデータのカバー範囲(そして限界)
バックテストにコホートのコンテキストを加える前に、実際にどのようなデータが利用可能で、どこまで遡れるかを理解する必要がある。HyperTrackerのヒストリカルエンドポイントはデータタイプによって異なる遡及期間を持つ。
最も長い履歴はポジションにある。個々のポジションのスナップショットは2025年4月から約10ヶ月遡ることができ、任意の時点での市場の状態を再構築するための素材が得られる。フィル(個々のトレード記録)は2025年7月から約6.5ヶ月分をカバーし、価格系列の構築やトレードレベルの執行分析に役立つ。
コホート固有のデータはウィンドウが短い。コイン別コホートメトリクス(特定の資産でどのセグメントがどのようにポジションを取っていたかを示すエンドポイント)は、APIコールあたり約4週間の遡及期間を持つ。これがコホートベースのバックテストの重要な制約だ。自分でデータを収集していない限り、6ヶ月前のBTCでMoney Printerコホートが何をしていたかを遡ってクエリすることはできない。
コホートバイアス(特定のコホートの全資産にわたる方向性の傾きを測定するもの)はさらに短く、12時間のローリングウィンドウだ。オーダースナップショット(逆指値、利確、指値クラスター)は約3週間分遡れる。ヒートマップとリーダーボードは現在のみで、履歴アーカイブはない。
ビルダーへのヒント: 実践的な示唆はシンプルだ。意味のある深さでコホートを考慮したバックテストを実行したいなら、今すぐデータの収集を始めること。コホートメトリクスエンドポイントを5分ごとに呼び出してデータベースにレスポンスを書き込むcronジョブを設定すれば、1ヶ月で1ヶ月分、3ヶ月で3ヶ月分のデータが蓄積される。APIの4週間のルックバックウィンドウはローリングウィンドウなので、自分で保存しない限り古いデータは消えていく。
既存のバックテストへのコホートフィルターの重ね合わせ
バックテストエンジンを一から作り直す必要はない。最もクリーンなアプローチは、コホートフィルターをポストシグナルのゲートとして追加することだ。既存のシグナルが候補を生成し、コホートフィルターがどの候補を実行してどれをスキップするかを決める。
フィルターは3段階で機能する。
ステージ1:生のシグナル発動。 既存のロジックがトレードの機会を特定する。OIの急増、ファンディングの乖離、価格のブレイクアウト、移動平均のクロス。シグナルは変わらない。
ステージ2:コホートのルックアップ。 シグナルが発動した瞬間、対象資産のコホートメトリクスをクエリする。確認するのは2つ。自分のシグナルの方向にポジションを持つコホートはどれか、そして反対方向にポジションを持つコホートはどれか。
ステージ3:確信のゲート。 歴史的に利益を出しているコホート(Money Printer、Smart Money、Consistent Grinder)がシグナルの方向と一致している場合、トレードを実行する。歴史的に損失を出しているコホート(Giga-Rekt、Full Rekt、Semi-Rekt)が主なドライバーで、利益コホートが反対側にポジションを持っている場合、スキップする。状況が混在している場合、サイズを縮小するか見送る。
実際のルックアップとゲートのロジックはこうなる。
import requests
BASE_URL = "https://ht-api.coinmarketman.com/api/external"
HEADERS = {"Authorization": "Bearer YOUR_API_KEY"}
# PnLベースのコホートセグメントID
PROFITABLE = [8, 9, 10] # Money Printer, Smart Money, Consistent Grinder
UNPROFITABLE = [13, 14, 15] # Semi-Rekt, Full Rekt, Giga-Rekt
def fetch_cohort_metrics(coin):
resp = requests.get(
f"{BASE_URL}/cohort/metrics",
headers=HEADERS,
params={"coin": coin}
)
resp.raise_for_status()
return resp.json()
def cohort_conviction(metrics, signal_direction):
"""
利益コホートがシグナルと一致しているかに基づいて
'pass'、'skip'、または 'reduce' を返す。
signal_direction: 'long' または 'short'
"""
profitable_net = 0
unprofitable_net = 0
for cohort in metrics:
seg = cohort.get("segmentId")
long_n = cohort.get("longNotional", 0)
short_n = cohort.get("shortNotional", 0)
net = long_n - short_n # 正 = ネットロング
if seg in PROFITABLE:
profitable_net += net
elif seg in UNPROFITABLE:
unprofitable_net += net
# 一致度を判定
if signal_direction == "long":
aligned = profitable_net > 0 and unprofitable_net < profitable_net
opposed = profitable_net < 0
else:
aligned = profitable_net < 0 and unprofitable_net > profitable_net
opposed = profitable_net > 0
if aligned:
return "pass"
elif opposed:
return "skip"
else:
return "reduce"
ライブのバックテストループでは、シグナルが発動するたびにcohort_conviction()を呼び出し、その結果を使って実行をゲートする。「pass」になったトレードはフルサイズで実行する。「reduce」になったトレードは一部のサイズで実行する(割合は自分で決める)。「skip」になったトレードは実行しない。
コレクションパイプラインの構築
コホートメトリクスには4週間のルックバックウィンドウがあるため、本格的なバックテストには継続的なデータ収集が必要だ。パイプラインは単純だ。定期実行されるスクリプトがAPIを呼び出し、レスポンスをパースして永続ストレージに書き込む。
import requests
import json
import sqlite3
from datetime import datetime
BASE_URL = "https://ht-api.coinmarketman.com/api/external"
HEADERS = {"Authorization": "Bearer YOUR_API_KEY"}
COINS = ["BTC", "ETH", "SOL", "HYPE"]
def collect_cohort_snapshot(db_path="cohort_history.db"):
conn = sqlite3.connect(db_path)
conn.execute("""
CREATE TABLE IF NOT EXISTS cohort_snapshots (
timestamp TEXT, coin TEXT, segment_id INTEGER,
long_notional REAL, short_notional REAL,
total_notional REAL, raw_json TEXT
)
""")
ts = datetime.utcnow().isoformat() + "Z"
for coin in COINS:
resp = requests.get(
f"{BASE_URL}/cohort/metrics",
headers=HEADERS,
params={"coin": coin}
)
if resp.status_code != 200:
continue
for cohort in resp.json():
conn.execute(
"INSERT INTO cohort_snapshots VALUES (?,?,?,?,?,?,?)",
(ts, coin, cohort.get("segmentId"),
cohort.get("longNotional", 0),
cohort.get("shortNotional", 0),
cohort.get("totalNotional", 0),
json.dumps(cohort))
)
conn.commit()
conn.close()
これをcronまたはsystemdタイマーで5分ごとに実行する。4コインへの呼び出しで1サイクルにつきAPIリクエストが4回、つまり1日あたり1,152コールになる。フリー層の100コール/日では継続収集には足りないが、$179/月のPulseプランで月50,000コールが使え、複数の資産に対して24時間365日収集しても余裕がある。
数週間の収集後には、すべての動きの両サイドに誰がいたかを実際に考慮したバックテストを走らせるのに十分なコホートデータの歴史が得られる。
バックテストが答えるべき3つの問い
コホートデータをバックテストに重ねることで、価格だけのバックテストでは答えられない問いが立てられるようになる。
1. Smart Moneyが一致したとき、勝率はどう変わるか?
ベース戦略を2回走らせる。コホートフィルターなしで一度、フィルターありで一度。勝率を比較する。フィルターありのバージョンが意味のある高い勝率を持ちながらも総トレード数が少なければ、コホートフィルターはその役割を果たしている。平均パフォーマンスを引き下げる確信の低いセットアップを除外しているのだ。
トレードオフは常に同じだ。フィルターはトレード数を減らす。トレードが少ないということは複利の成長は遅くなるが、質の低いセットアップからのドローダウンも減る。フィルタリング後のシャープレシオが、総P&Lが横ばいであっても改善するなら、その戦略は資本効率が高くサイズを増やしやすい。
2. どのコホートの一致が最も重要か?
すべてのコホートが等しい予測力を持つわけではない。各コホートをフィルターとして独立してテストする。Money Printerコホートとの一致だけで結果は改善するか?Leviathanコホート(パーペチュアル証拠金$5M以上、セグメントID 7)はどうか?サイズベースとPnLベースのコホートは異なるものを測定しており、どちらが最も重要かはトレードする資産と時間軸によって異なる。
BTCやETHのような大型資産では、市場が十分に深いためサイズだけでは方向性を予測しないことが多く、PnLベースのコホートがより情報量が高い傾向がある。中型や小型資産では、WhaleやLeviathanの1つのウォレットがポジションに入ることが総建玉のより大きなシェアを占めるため、サイズベースのコホートの方が重要になりえる。
3. コホートの乖離はドローダウンを予測するか?
バックテストにおけるコホートデータの最も価値の高い使い方の一つはドローダウン分析だ。バックテストの最悪の負け続きを振り返り、エントリー時のコホート構成がどのようなものだったかを確認する。最悪のドローダウンが一貫して、シグナルが不利益コホートと一致し利益コホートに反対されていた瞬間と重なっているなら、標準的なバックテストでは決して明かされなかった戦略の構造的な弱点を発見したことになる。
これがコホートを考慮したバックテストの真の報酬だ。平均パフォーマンスを改善するだけでなく、戦略が最も脆弱な具体的な条件を特定できる。それは平均リターンの数ベーシスポイントの上乗せよりも価値がある。
コホートデータでバックテストする際のよくある落とし穴
コホートデータは強力なレンズを加えるが、固有の失敗パターンも生み出す。
特定のコホートへの過学習。 Money Printerコホートだけを追う戦略に最適化してバックテストが美しく見えたとき、自問してほしい。本物のシグナルをトレードしているのか、それともテスト期間中たまたま正しかった少数のウォレットにフィットしているだけなのか?Money Printerコホートは市場のごく一部を占めるに過ぎない。一つのセグメントのポジショニングに完全に依存する戦略は脆い。
コホートのレジーム変化の無視。 ウォレットは時間とともにコホート間を移動する。1月にSmart Moneyだったウォレットが、一連の損失の後に6月にはSemi-Rektになっているかもしれない。HyperTrackerはウォレットを累計PnLに基づいて再分類するため、コホートのラベルは動的だ。静的なコホートメンバーシップを前提とするバックテストはシグナルの質を過大評価する。追っている「スマートマネー」のポジショニングの一部は、当時はスマートだったが今はそうでないウォレットによって生成されたものだからだ。
レイテンシーの前提。 データは5分ごとにリフレッシュされ、完全な状態更新には15〜20分かかる。バックテストでは、すべてのタイムスタンプで完璧なデータが手に入る。ライブトレードでは、常に数分前のデータを見ている。現在のバーのコホートデータではなく、一つ前のスナップショットのコホートデータを使うことでバックテストにそのラグを組み込む。現在バーのコホートデータでしかシグナルが機能しないなら、本番環境では機能しない。
相関と因果関係の混同。 スマートマネーのポジショニングが価格を特定の方向に動かすわけではない。それは同時的な指標だ。利益を出しているウォレットは、損失を出しているウォレットよりも正しい方向にポジションを持っていることが多い傾向がある。この関係は確率的であって決定論的ではない。コホートの一致は、多くのトレードにわたって期待値を改善するフィルターとして扱うべきであり、個々のトレードの保証ではない。
バックテスト用のコホートデータの収集を始める
フリー層は1日100回のAPIコールを提供する。スケールアップする前に、収集パイプラインのプロトタイプを作りシグナルの質を検証するには十分だ。
すべてのバックテストは現実の簡略化されたモデルだ。問題は、どの簡略化が損失を生むかだ。すべての資本を同等に扱い、フローの背後にいる誰かを無視し、OIはその出所にかかわらずOIだと仮定すること。これらは戦略をライブに移行してシミュレーションのカーブを下回るパフォーマンスを目の当たりにするまでは無害に見える簡略化だ。コホートデータはバックテストを完璧にしない。嘘を小さくするだけだ。しかしそれは結果を変えるのに十分だ。