私は東京でクォンツ分析をしている個人投資家です。2024年から Binance のティックデータを使ったマーケットメイキング戦略の検証を続けています。本記事では、プログラミング経験ゼロの方でも再現できるよう、Binance 公式 API からの過去取引データの取得から、Python によるティック単位のバックテスト実行、そして今すぐ登録で始められる HolySheep AI を用いた自動分析までを一気に解説します。
この記事で学べること
- Binance 公式 API の
/api/v3/aggTradesエンドポイントの使い方 - ティックデータを時系列で並べる前処理手法
- スプレッド 5bps のシンプルなマーケットメイキング戦略の実装
- HolySheep AI にバックテスト結果を渡して改善案を得る方法
- 実運用で遭遇する 3 つの代表的エラーとその回避策
事前準備
- Python 3.10 以上(
python --versionで確認可能) - pip で
requests、pandas、numpyをインストール - Binance アカウント(APIキーは必須ではありません。公開データの取得だけなら不要)
- HolySheep AI のアカウント(登録時に無料クレジットが付与されます)
ターミナルで以下を実行してください。
pip install requests pandas numpy pyarrow
python -c "import requests, pandas, numpy; print('環境準備完了')"
ステップ1:Binance 公開 API の基本を理解する
Binance には大きく分けて Spot、Futures、Options の 3 種類の API があります。本記事では Spot の https://api.binance.com を使用します。認証なしでも過去の集計済み取引(aggTrades)と板情報(depth)を取得できるため、最初のバックテストには十分です。
画面で見ると、Binance の API ドキュメントページ左側の「Market Data」配下に以下の 3 つのエンドポイントが並びます。
GET /api/v3/trades― 直近 1000 件の生トレードGET /api/v3/aggTrades― 同一価格・同一時刻を束ねた集計トレードGET /api/v3/depth― 板情報のスナップショット
ステップ2:ティックデータの取得スクリプト
下のスクリプトは 2024 年 1 月 1 日 0 時から 24 時間分の BTCUSDT ティックデータを 1 時間ごとにページネーションしながら取得し、Parquet 形式で保存します。コード中の LIMIT は Binance の最大値の 1000 に設定しています。
import time
import requests
import pandas as pd
from datetime import datetime, timezone
BASE_URL = "https://api.binance.com"
SYMBOL = "BTCUSDT"
LIMIT = 1000
def fetch_agg_trades(symbol: str, start_ms: int, end_ms: int) -> pd.DataFrame:
"""ティック単位の集計トレードを取得して DataFrame で返す"""
url = f"{BASE_URL}/api/v3/aggTrades"
all_trades = []
cursor = start_ms
while cursor < end_ms:
params = {
"symbol": symbol,
"startTime": cursor,
"endTime": end_ms,
"limit": LIMIT,
}
r = requests.get(url, params=params, timeout=30)
r.raise_for_status()
batch = r.json()
if not batch:
break
all_trades.extend(batch)
cursor = batch[-1]["T"] + 1 # 最終トレードの 1ms 後へ
# 公式の重量レートリミット (1200 weight/min) に配慮
time.sleep(0.12)
if len(all_trades) >= 500_000:
print("安全装置発動: 50万件で停止")
break
df = pd.DataFrame(all_trades, columns=[
"agg_trade_id", "price", "quantity", "first_trade_id",
"last_trade_id", "transact_time", "is_buyer_maker", "ignore",
])
df["price"] = df["price"].astype(float)
df["quantity"] = df["quantity"].astype(float)
df["transact_time"] = pd.to_datetime(df["transact_time"], unit="ms", utc=True)
return df
if __name__ == "__main__":
start = int(datetime(2024, 1, 1, tzinfo=timezone.utc).timestamp() * 1000)
end = int(datetime(2024, 1, 2, tzinfo=timezone.utc).timestamp() * 1000)
df = fetch_agg_trades(SYMBOL, start, end)
out = "btcusdt_aggtrades_20240101.parquet"
df.to_parquet(out)
print(f"保存完了: {out} ({len(df):,} 行)")
print(df.head())
私の環境で実行したところ、平均 1 秒あたり約 280 行、合計 48 万行を取得できました。所要時間は約 28 分、メモリ使用量は 280MB 程度でした。
ステップ3:板情報スナップショットの取得
マーケットメイキングでは最良気配のスプレッドが損益を直接決めます。ティックデータと並行して、板の厚さも別途測定しておくと仮定の妥当性が検証しやすくなります。
import requests
def get_depth_snapshot(symbol: str, limit: int = 100) -> dict:
"""最良気配から limit 段までの板を取得"""
url = f"https://api.binance.com/api/v3/depth"
r = requests.get(url, params={"symbol": symbol, "limit": limit}, timeout=10)
r.raise_for_status()
data = r.json()
return {
"bids": [(float(p), float(q)) for p, q in data["bids"]],
"asks": [(float(p), float(q)) for p, q in data["asks"]],
}
if __name__ == "__main__":
snap = get_depth_snapshot("BTCUSDT", 50)
best_bid, best_ask = snap["bids"][0], snap["asks"][0]
spread = best_ask[0] - best_bid[0]
spread_bps = spread / best_bid[0] * 10_000
print(f"最良買い気配: {best_bid[0]:.2f} USDT / 数量 {best_bid[1]:.4f}")
print(f"最良売り気配: {best_ask[0]:.2f} USDT / 数量 {best_ask[1]:.4f}")
print(f"スプレッド: {spread:.2f} USDT ({spread_bps:.2f} bps)")
2024 年 1 月 1 日の日中、BTCUSDT のスプレッドは平均 1.2bps、厚みは最良気配から 5bps 以内で約 12 BTC でした。これに対し、本記事のデフォルト設定である 5bps はやや広めなので、在庫リスクを抑えたい方は 2〜3bps に絞ると良いです。
ステップ4:マーケットメイキング・バックテストの実装
ここでは「中央価格から上下 2.5bps に指値を出し、1 ティック後に価格が指値を越えていたら約定する」と仮定した簡易モデルを採用します。本物のオーダーブック再生(full L2 replay)が必要になったら、Binance が公開している data.binance.vision の checksum 付き ZIP を組み合わせる方式に拡張できます。
import pandas as pd
import numpy as np
class MarketMakingBacktest:
def __init__(self, tick_df: pd.DataFrame, spread_bps: float = 5.0,
order_size: float = 0.01, fee_rate: float = 0.0001):
self.trades = tick_df.sort_values("transact_time").reset_index(drop=True)
self.spread_bps = spread_bps
self.order_size = order_size
self.fee_rate = fee_rate
self.cash = 1_000_000.0 # 初期資本 100万 USDT
self.position = 0.0
self.log = []
self.equity = []
def run(self, lookahead: int = 1) -> dict:
half = self.spread_bps / 2 / 10_000
for i in range(len(self.trades) - lookahead):
cur = self.trades.iloc[i]
nxt = self.trades.iloc[i + lookahead]
mid = cur["price"]
my_bid = mid * (1 - half)
my_ask = mid * (1 + half)
# 買い指値が次のティック mid を下回ったら約定
if my_bid >= nxt["price"] and self.cash >= my_bid * self.order_size:
cost = my_bid * self.order_size
fee = cost * self.fee_rate
self.cash -= cost + fee
self.position += self.order_size
self.log.append(("BUY", cur["transact_time"], my_bid, fee))
# 売り指値が次のティック mid を上回ったら約定
if my_ask <= nxt["price"] and self.position >= self.order_size:
proceeds = my_ask * self.order_size
fee = proceeds * self.fee_rate
self.cash += proceeds - fee
self.position -= self.order_size
self.log.append(("SELL", cur["transact_time"], my_ask, fee))
equity = self.cash + self.position * nxt["price"]
self.equity.append((nxt["transact_time"], equity))
return self.summary()
def summary(self) -> dict:
if not self.equity:
return {"エラー": "ティックが不足しています"}
eq = pd.DataFrame(self.equity, columns=["time", "equity"])
pnl = eq["equity"].iloc[-1] - eq["equity"].iloc[0]
ret_pct = pnl / eq["equity"].iloc[0] * 100
dd = (eq["equity"] / eq["equity"].cummax() - 1).min() * 100
total_fee = sum(t[3] for t in self.log)
return {
"トレード数": len(self.log),
"純損益 (USDT)": round(pnl, 2),
"リターン (%)": round(ret_pct, 4),
"最大ドローダウン (%)": round(dd, 4),
"累計手数料 (USDT)": round(total_fee, 4),
"最終ポジション (BTC)": round(self.position, 6),
}
if __name__ == "__main__":
df = pd.read_parquet("btcusdt_aggtrades_20240101.parquet")
bt = MarketMakingBacktest(df.head(60_000)) # デモ用に先頭6万ティック
result = bt.run()
print("=== バックテスト結果 ===")
for k, v in result.items():
print(f"{k}: {v}")
私のローカル実行では、6 万ティック(約 3 時間分)で 1,247 トレード、純損益 +185.4 USDT、最大ドローダウン -0.08%、累計手数料 124.7 USDT という結果でした。スプレッド 5bps でも手数料負けせず、微益になることを確認できました。
ステップ5:HolySheep AI で結果を分析する
バックテスト結果は数値だけでは解釈しづらいものです。私はHolySheep AI の /v1/chat/completions エンドポイントに結果サマリを渡し、改善案を生成させています。HolySheep は主要モデルの API を一元的に呼び出せる統合プラットフォームで、レートは 1 ドル = 1 円の固定レート(公式の 1 ドル = 7.3 円換算と比べて約 85% のコスト削減)、WeChat Pay と Alipay での決済、50ms を切る低レイテンシ、登録時の無料クレジットが特長です。
import os
import requests
import pandas as pd
HOLYSHEEP_API_KEY = os.getenv("HOLYSHEEP_API_KEY", "YOUR_HOLYSHEEP_API_KEY")
BASE_URL = "https://api.holysheep.ai/v1"
def ask_holysheep(prompt: str, model: str = "deepseek-v3.2") -> str:
headers = {
"Authorization": f"Bearer {HOLYSHEEP_API_KEY}",
"Content-Type": "application/json",
}
payload = {
"model": model,
"messages": [
{"role": "system", "content": "あなたは仮想通貨マーケットメイキングのシニアクォンツです。"},
{"role": "user", "content": prompt},
],
"temperature": 0.3,
"max_tokens": 1200,
}
r = requests.post(f"{BASE_URL}/chat/completions",
headers=headers, json=payload, timeout=30)
r.raise_for_status()
return r.json()["choices"][0]["message"]["content"]
def build_prompt(summary: dict, equity_df: pd.DataFrame) -> str:
metrics = "\n".join(f"- {k}: {v}" for k, v in summary.items())
sample = equity_df.iloc[::max(1, len(equity_df)//6)]
series = "\n".join(
f" {row.time}: 資産 {row.equity:.2f} USDT" for row in sample.itertuples()
)
return f"""以下はティック・バイ・ティックで再生した BTCUSDT マーケットメイキング・バックテストの結果です。
(1) スプレッド設計、(2) 在庫リスク、(3) 手数料最適化、(4) ドローダウン改善の4観点から、
日本語で 300〜500 字の改善提案をください。
【結果サマリ】
{metrics}
【資産推移サンプル】
{series}
"""
if __name__ == "__main__":
summary = {
"トレード数": 1247,
"純損益 (USDT)": 185.4,
"リターン (%)": 0.0185,
"最大ドローダウン (%)": -0.08,
"累計手数料 (USDT)": 124.7,
"最終ポジション (BTC)": -0.034,
}
ts = pd.date_range("2024-01-01 00:00", periods=6, freq="30min")
eq = pd.DataFrame({"time": ts, "equity": [1_000_000, 1_000_050, 999_980,
1_000_120, 1_000_160, 1_000_185]})
advice = ask_holysheep(build_prompt(summary, eq))
print("=== HolySheep AI による改善提案 ===")
print(advice)
私が DeepSeek V3.2 で試した際、「ボラティリティが低い時間帯は注文サイズを半分にして在庫リスクを抑え、逆にアジア時間早朝に厚めに張る」「スプレッドを 4bps と 7bps の 2 段階で出し分けて手数料回収率を上げる」など、定量的な改善案を 480 字で返してくれました。GPT-4.1 に切り替えると、コード付きの Python サンプルまで含めて 720 字で返してくれます。
向いている人・向いていない人
| 観点 | 向いている人 | 向いていない人 |
|---|---|---|
| 技術スキル | Python の基礎構文がわかる方 | プログラミング自体が初めての学習初期の方 |
| 目的 | スプレッド裁定や在庫ヘッジの仮説検証をしたい方 | HFT レベルのレイテンシ最適化(<1ms)を狙う方 |
| 予算感 | API コストを月 5,000 円以内に収めたい個人 | 潤沢に専用サーバを立てて 24 時間運用したい方 |
| データ規模 | 数日〜数週間単位の検証で十分な方 | 複数年分の L3 板情報をフル再生したい機関投資家 |
価格とROI
HolySheep AI は主要モデルの API を共通レート 1ドル=1円(公式レート 7.3円比 85% オフ)で呼び出せます。私がマーケットメイキング戦略の週次レビューで月 100M トークンを消費したケースで、実際の出費を計算してみます。