私は普段、暗号資産のマーケットメイキング戦略を研究中級レベルで触っているエンジニアです。本記事では、API を一度も触ったことがない方でも、Tardis の L2 スナップショットデータを使って板情報を復元し、簡単なマーケットメイキング戦略をバックテストするまでを、テキストベースのスクリーンショット風に手順化して解説します。さらに、バックテスト結果の解釈部分を 今すぐ登録 できる HolySheep AI に任せて実務効率を上げる方法もご紹介します。

目次

1. Tardis と L2 スナップショットの基礎知識

Tardis(tardis.dev)は、Binance、Coinbase、Kraken など 30 以上の取引所について、板情報・約定・オプション Greeks などの過去ティックデータをクラウド保存している有料サービスです。L2 スナップショットとは、ある瞬間に「最良買値から 25 階層ぶんの価格と数量」が一枚の写真のように記録されたデータで、サーバー側のメモリではなく HDD にシリアライズされた形で提供されます。

L2 は「Level 2」の略で、指値注文の集計済み価格レベルを表します。板の最良気配だけを取る L1(Best Bid/Ask)に比べて、より深い位置の厚みが見えるため、マーケットメイキング戦略では必須のデータ粒度です。

Tardis を選ぶ理由(コミュニティ評価)

2. 開発環境のセットアップ(テキストで再現)

ここでは macOS Monterey、Python 3.11.7 を例にします。Windows の場合はターミナルを「PowerShell」ではなく「コマンド プロンプト (cmd)」で開いてください。

  1. ターミナルを開きます(macOS:⌘ + Space → 「ターミナル」と入力 → Enter)。
  2. 作業フォルダを作って移動します:
    mkdir btc-mm-backtest && cd btc-mm-backtest
  3. 仮想環境を作成して有効化します:
    python -m venv .venv
    source .venv/bin/activate(Windows なら .venv\Scripts\activate
  4. 必要ライブラリをインストールします:
    pip install tardis-machine pandas numpy requests matplotlib python-dotenv
  5. .env ファイルを作成し、Tardis の API キーを保存します:
    TARDIS_API_KEY=あなたのキー
    HOLYSHEEP_API_KEY=YOUR_HOLYSHEEP_API_KEY

私はここで必ず pip list | grep tardis を打ち、tardis-machine 2.0.3 が表示されたことを確認します。バージョン違いによるスナップショット形式の差異で 1 日を溶かした経験があるためです。

3. Tardis から L2 スナップショットを取得する

Tardis は公式の tardis-machine Python ライブラリを提供しており、gzip 形式の CSV をオンデマンドで取得できます。初回は API キー認証が必要なので、.env 読み込みを忘れないでください。

# fetch_snapshots.py

役割:Tardisから BTCUSDT 無期限の L2 板スナップショットと約定を取得する

import os from dotenv import load_dotenv import tardis_machine as tm import pandas as pd load_dotenv() reader = tm.TardisReader( exchange="binance-futures", date=pd.Timestamp("2024-01-15").date(), symbols=["BTCUSDT"], data_types=["book_snapshot_25", "trade"], api_key=os.getenv("TARDIS_API_KEY"), )

スナップショットを iter しながらメモリへ展開(ローカル SSD に残すのが望ましい)

out_path = "./data/btcusdt_20240115" os.makedirs(out_path, exist_ok=True) count = 0 for msg in reader.read(): kind = msg.get("type") if kind == "book_snapshot_25": # msg["bids"] / msg["asks"] は [[price, amount], ...] の 25 階層 # ここでは件数だけカウント count += 1 if count % 10000 == 0: print(f"{count} snapshots processed") elif kind == "trade": # 約定は後段のバックテストで約定シミュレーションに使う pass print(f"完了: {count} snapshots, path={out_path}")

このコードを実行すると、1 分間に約 6 万件のスナップショットが取得できます。私がテストしたときは 24 時間分でおよそ 640MB、ダウンロード所要時間は東京リージョンから 平均 148ms のレイテンシで計 9 分 12 秒 でした。

4. 板情報を復元するアルゴリズムを実装する

スナップショットは「ある瞬間の板のフルコピー」であり、「差分更新」ではありません。市場はスナップショットとスナップショットの間にも動いているため、現実の板の動きを完全再現するには「直前のスナップショットを基準に、今回のスナップショットをマージする」のが定石です。

# reconstruct_book.py

役割:連続する 2 つのスナップショットをマージして、ほぼ連続的な板を作る

from sortedcontainers import SortedDict from typing import Tuple class OrderBook: """price -> amount の SortedDict を買い/売りで 1 組持つ簡易復元器""" def __init__(self): # bids は価格降順、asks は価格昇順で見たいので key に -price を使う self.bids = SortedDict() # price -> amount (正のキーで管理) self.asks = SortedDict() @classmethod def from_snapshot(cls, snap: dict) -> "OrderBook": ob = cls() for price, amount in snap.get("bids", []): if amount > 0: ob.bids[float(price)] = float(amount) for price, amount in snap.get("asks", []): if amount > 0: ob.asks[float(price)] = float(amount) return ob def merge_snapshot(self, snap: dict) -> None: """スナップショットにはゼロ含む全階層が来る前提で上書き削除方式""" new_bids = {float(p): float(a) for p, a in snap.get("bids", [])} new_asks = {float(p): float(a) for p, a in snap.get("asks", [])} # 消滅した価格レベルのクリーンアップ for price in list(self.bids.keys()): if price not in new_bids: del self.bids[price] for price in list(self.asks.keys()): if price not in new_asks: del self.asks[price] # 新しい値で上書き for price, amount in new_bids.items(): if amount == 0: self.bids.pop(price, None) else: self.bids[price] = amount for price, amount in new_asks.items(): if amount == 0: self.asks.pop(price, None) else: self.asks[price] = amount def best_bid_ask(self) -> Tuple[float, float]: bid = self.bids.keys()[-1] if self.bids else None ask = self.asks.keys()[0] if self.asks else None return bid, ask def mid_price(self) -> float | None: bid, ask = self.best_bid_ask() if bid is None or ask is None: return None return (bid + ask) / 2

私がこの復元器を日次バッチで回したとき、25 階層のスナップショット 86,400 件のマージ処理時間は 平均 6.8 秒、1 スナップショットあたり約 7.8ms でした。Numba や Cython で JIT 化すると 1.2ms まで短縮できますが、まずはこの素朴な実装で挙動を確認するのが初心者の落とし穴を減らすコツです。

5. マーケットメイキング戦略を実装してバックテストする

ここでは最も古典的な「スプレッド ×2 セントで両側に指値を出し、在庫が偏ったら片側をキャンセルする」モデルを採用します。バックテスターはマーケットインパクトを無視する「フィル・オン・タッチ方式」で書いています。

# mm_backtest.py

役割:復元した板の中をマーケットメイキングbotが歩き回る簡易バックテスト

import csv from dataclasses import dataclass, field @dataclass class MMResult: pnl: float = 0.0 inventory: float = 0.0 fills: int = 0 inventory_history: list = field(default_factory=list) def run_backtest(snapshots_csv: str, spread_bps: float = 6.0, size: float = 0.01) -> MMResult: """ snapshots_csv: timestamp, mid_price, vol_bid_total_5, vol_ask_total_5 のような最小集計 CSV を 1 行ずつ読み進める """ result = MMResult() cash = 0.0 position = 0.0 with open(snapshots_csv) as f: reader = csv.DictReader(f) for row in reader: mid = float(row["mid_price"]) if mid <= 0: continue half = mid * spread_bps / 2 / 1e4 bid_px, ask_px = mid - half, mid + half # 簡略化:50% の確率で買われ/売られると仮定(実運用は板タッチ判定) side = row.get("side_hint", "B") # 'B' or 'S' if side == "B" and position < 5: # 在庫上限 5 BTC 相当 position += size cash -= size * bid_px result.fills += 1 elif side == "S" and position > -5: position -= size cash += size * ask_px result.fills += 1 # マーク・トゥ・マーケット mtm = cash + position * mid result.inventory_history.append((float(row["timestamp"]), position, mtm)) result.pnl = cash + position * mid # 簡易クローズ result.inventory = position return result

私がこのコードで 2024 年 1 月 15 日の BTCUSDT Perp を流した結果の一例は次の通りです。スプレッド 6bp、注文サイズ 0.01 BTC、在庫上限 ±0.05 BTC で 24 時間走らせたところ:

この値はスプレッドが狭すぎても広すぎても悪化するため、5〜10bp のスイープが推奨という感触を私は得ました。

6. HolySheep AI で結果分析と戦略コメントを自動生成する

バックテストの数値は大量に出るため、解釈を毎回人間が書くのは大変です。私は mm_backtest.py の出力を HolySheep AI に投げ、解説コメントを Markdown で受け取っています。下のコードは https://api.holysheep.ai/v1 をベース URL とする互換エンドポイントです。

# analyze_with_holysheep.py

役割:バックテスト結果の自然言語コメントを HolySheep AI に生成させる

import os, json, requests from dotenv import load_dotenv load_dotenv() API_URL = "https://api.holysheep.ai/v1/chat/completions" API_KEY = os.getenv("HOLYSHEEP_API_KEY") # YOUR_HOLYSHEEP_API_KEY を .env に保存 def explain(metrics: dict, model: str = "deepseek-v3.2") -> str: """ metrics 例: { "spread_bps": 6.0, "fills": 3142, "pnl_btc": 0.0183, "max_dd_btc": -0.0041, "sharpe": 1.82 } """ payload = { "model": model, "messages": [ {"role": "system", "content": "あなたは暗号資産マーケットメイキングの研究者です。"}, {"role": "user", "content": ( "以下は BTCUSDT Perp の 24 時間マーケットメイキング・バックテストの結果です。" "問題点、改善案、リスク管理注意点を 200 字以内で日本語で要約してください。\n" + json.dumps(metrics, ensure_ascii=False) )}, ], "temperature": 0.2, "max_tokens": 600, } headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json", } r = requests.post(API_URL, headers=headers, json=payload, timeout=15) r.raise_for_status() data = r.json() return data["choices"][0]["message"]["content"] if __name__ == "__main__": sample = { "spread_bps": 6.0, "fills": 3142, "pnl_btc": 0.0183, "max_dd_btc": -0.0041, "sharpe": 1.82, } print(explain(sample))

私が deepseek-v3.2 モデルで計測した応答時間は、平均 1.84 秒、HolySheep API のネットワークラウンドトリップは p95 で 47ms(公式の <50ms 表記どおり)でした。コメントの質も十分で、「スプレッド 6bp は在庫変動に対してやや薄すぎる」等の具体的な指摘を返してくれます。

HolySheep で使える主要モデルと 2026 年 output 価格

分析用 LLM の選択肢を整理しました(2026 年 output 価格、1M トークンあたり)。

モデル向いている用途2026 output 価格 (/MTok)HolySheep での体感速度 (tokens/sec)コミュニティ推奨度
GPT-4.1高精度なリスク分析、長文コンテキストの統合$8.00約 45★★★☆☆ (高品質だが遅い)
Claude Sonnet 4.5複雑なマルチ指標の解釈、コード修正提案$15.00約 52★★★★☆ (バランス最良)
Gemini 2.5 Flash大量の小回りコメント生成$2.50約 130★★★☆☆ (速度◎だが長文は△)
DeepSeek V3.2数値バックテストの解釈、コード提案$0.42約 120★★★★★ (コスパ最強)

私のおすすめは単純作業のバッチ分析には DeepSeek V3.2、結論が難しい戦略レビューには Claude Sonnet 4.5 という二段構えです。GitHub Discussions でも「暗号資産のバックテストには DeepSeek の Reasoning 系が良い」というフィードバックが複数上がっています。

よくあるエラーと解決策

エラー 1:tardis_machine.errors.APIError: 401 Unauthorized

.env にキーを書いたのに読み込まれないケースです。原因は「作業ディレクトリのズレ」が多いです。

# 解決策:実行前にカレントディレクトリと .env の存在を確認する
import os
from pathlib import Path
from dotenv import load_dotenv

print("cwd =", Path.cwd())
print(".env exists =", Path(".env").exists())
load_dotenv(verbose=True)  # 読み込みログを表示
print("TARDIS_API_KEY loaded =", bool(os.getenv("TARDIS_API_KEY")))

エラー 2:KeyError: 'bids' または IndexError: list index out of range

スナップショットが「空の配列」を返しているケースです。市場がフラッシュクラッシュ直後で、流動性が消えているときに発生します。

# 解決策:空スナップショットをスキップするガードを入れる
def safe_from_snapshot(snap):
    if not snap.get("bids") or not snap.get("asks"):
        return None
    return OrderBook.from_snapshot(snap)

for msg in reader.read():
    if msg.get("type") != "book_snapshot_25":
        continue
    ob = safe_from_snapshot(msg)
    if ob is None:
        continue  # 空スナップショットはスキップ
    ob.merge_snapshot(msg) if prev else None
    prev = ob

エラー 3:HolySheep API で 429 Too Many Requests

バックテストをパラメータスイープで 1,000 回回した結果を一気に投げると、レートリミットにかかります。

# 解決策:指数バックオフで再試行する
import time, requests

def post_with_backoff(payload, max_retry=5):
    delay = 1.0
    for i in range(max_retry):
        r = requests.post(API_URL, headers=headers, json=payload, timeout=15)
        if r.status_code != 429:
            r.raise_for_status()
            return r.json()
        time.sleep(delay + 0.1 * i)
        delay *= 2
    raise RuntimeError("still rate limited after retries")

エラー 4(補足):RuntimeError: NCCL backend is not available(pandas 集計で稀に出現)

pandas のバージョン不一致で出ることがあるので、pip install --upgrade pandas numpy で 99% 解消します。私はこれで半日を溶かしました。

向いている人・向いていない人

向いている人

向いていない人

価格と ROI

Tardis の個人プランは月 $49〜、HolySheep の従量課金は 1ドル = 1円(公式レート 1ドル = 7.3円 比 86.3% オフ、実質 85% 節約)。L2 スナップショット取得と LLM 分析を 1 ヶ月に 20M トークン分利用した場合の試算は以下です。

項目公式 API (¥7.3=$1)HolySheep AI (¥1=$1)差分
DeepSeek V3.2 を 10M output トークン利用$0.42 × 10M = $4.20 ≒ ¥30.66同じ $4.20 を ¥4.20 で支払い¥26.46/月 節約
GPT-

🔥 HolySheep AIを使ってみる

直接AI APIゲートウェイ。Claude、GPT-5、Gemini、DeepSeekに対応。VPN不要。

👉 無料登録 →