私は普段、アルゴリズムトレードや暗号資産のシグナル開発工作中、WebSocket接続の不安定さに何度も頭を悩ませてきました。「ConnectionError: timeout」「401 Unauthorized」「pong timeout」「JSON decode error」——これらのエラーは原因がわかれば一瞬で解決できるものばかりですが、初めて遭遇すると数時間つぶしてしまうことも珍しくありません。
本記事では、OKX(欧易)のパブリックWebSocket APIを使ってBTC/USDの板情報(深度データ)をリアルタイムで取得・解析するPythonコードを、ゼロから丁寧に解説します。後半では筆者が実際に踏んだエラーとその解決策を3つ以上まとめ、最後にHolySheep AIを組み合わせた高度な活用術までお届けします。
前提条件と環境準備
動作確認环境は以下の通りです。
- Python 3.9〜3.12
- websockets ライブラリ(11.x系)
- pandas(データ整形に使用)
- HolySheep API(深度データ分析のAI処理に使用)
# 必要なライブラリのインストール
pip install websockets pandas requests
本記事の動作確認済みバージョン
python --version
Python 3.11.5
pip show websockets pandas requests
websockets==11.0.3
pandas==2.0.3
requests==2.31.0
OKX WebSocketの基本構造
OKXのパブリックWebSocketエンドポイントは wss://ws.okx.com:8443/ws/v5/public です。深度データを.subscribeするには、購読したいチャンネルと銘柄をJSON形式で送信します。
{
"op": "subscribe",
"args": [
{
"channel": "books5", # 板情報(トップ5)
"instId": "BTC-USDT" # インスタンスID
}
]
}
リアルタイム深度データ解析の完全コード
基本実装:WebSocket接続とパース
import json
import asyncio
import websockets
from datetime import datetime
from dataclasses import dataclass
from typing import Optional
@dataclass
class DepthLevel:
"""板情報の1レベルを表現"""
price: float
size: float
side: str # "ask" or "bid"
@dataclass
class DepthSnapshot:
"""深度データのスナップショット"""
inst_id: str
timestamp: datetime
asks: list[DepthLevel] # 売板(価格昇順)
bids: list[DepthLevel] # 買板(価格降順)
mid_price: float
spread: float
spread_bps: float # basis points
@classmethod
def from_okx_data(cls, inst_id: str, data: dict) -> "DepthSnapshot":
asks = [
DepthLevel(price=float(p), size=float(s), side="ask")
for p, s in data.get("asks", [])
]
bids = [
DepthLevel(price=float(p), size=float(s), side="bid")
for p, s in data.get("bids", [])
]
best_ask = asks[0].price if asks else 0.0
best_bid = bids[0].price if bids else 0.0
mid = (best_ask + best_bid) / 2
spread = best_ask - best_bid
spread_bps = (spread / mid * 10000) if mid > 0 else 0.0
return cls(
inst_id=inst_id,
timestamp=datetime.utcnow(),
asks=asks,
bids=bids,
mid_price=mid,
spread=spread,
spread_bps=spread_bps
)
def best_five(self) -> dict:
"""トップ5の板情報を整形"""
return {
"inst_id": self.inst_id,
"timestamp": self.timestamp.isoformat(),
"mid_price": round(self.mid_price, 2),
"spread": round(self.spread, 2),
"spread_bps": round(self.spread_bps, 2),
"top_asks": [
{"price": round(a.price, 2), "size": round(a.size, 4)}
for a in self.asks[:5]
],
"top_bids": [
{"price": round(b.price, 2), "size": round(b.size, 4)}
for b in self.bids[:5]
],
}
class OKXDepthClient:
"""OKX WebSocket深度データクライアント"""
WS_URL = "wss://ws.okx.com:8443/ws/v5/public"
PING_INTERVAL = 20 # OKX推奨のping間隔(秒)
def __init__(self, inst_id: str = "BTC-USDT", depth_levels: int = 5):
self.inst_id = inst_id
self.depth_levels = depth_levels
self.channel = f"books{depth_levels}"
self.latest_snapshot: Optional[DepthSnapshot] = None
self._running = False
def _build_subscribe_message(self) -> dict:
return {
"op": "subscribe",
"args": [{
"channel": self.channel,
"instId": self.inst_id
}]
}
async def _heartbeat(self, ws: websockets.WebSocketClientProtocol):
"""20秒間隔でpingを送信(OKXのタイムアウト対策)"""
while self._running:
try:
await asyncio.sleep(self.PING_INTERVAL)
if self._running:
await ws.send("ping")
except Exception:
break
async def connect_and_listen(self):
"""WebSocket接続+メッセージlisten"""
self._running = True
reconnect_delay = 1
max_delay = 60
while self._running:
try:
async with websockets.connect(
self.WS_URL,
ping_interval=None # 手動pingのため無効化
) as ws:
reconnect_delay = 1 # 正常接続時にリセット
await ws.send(json.dumps(self._build_subscribe_message()))
print(f"[OKX] 購読開始: {self.channel} / {self.inst_id}")
# heartbeat coroutine 並行起動
hb_task = asyncio.create_task(self._heartbeat(ws))
async for raw in ws:
if raw == "pong":
continue
try:
msg = json.loads(raw)
await self._handle_message(msg)
except json.JSONDecodeError as e:
print(f"[ERROR] JSON decode failed: {e} | raw: {raw[:100]}")
except Exception as e:
print(f"[ERROR] Unexpected: {type(e).__name__}: {e}")
hb_task.cancel()
except websockets.ConnectionClosed as e:
print(f"[WARN] 切断 → {e.code} {e.reason} | {reconnect_delay}s後に再接続")
except OSError as e:
print(f"[ERROR] ConnectionError: {e} | {reconnect_delay}s後に再接続")
except Exception as e:
print(f"[ERROR] {type(e).__name__}: {e} | {reconnect_delay}s後に再接続")
await asyncio.sleep(reconnect_delay)
reconnect_delay = min(reconnect_delay * 2, max_delay)
async def _handle_message(self, msg: dict):
"""メッセージの種類に応じて処理分岐"""
if msg.get("event") == "subscribe":
print(f"[OKX] 購読成功: {msg.get('arg', {})}")
return
if msg.get("event") == "error":
print(f"[ERROR] Subscribe error: {msg.get('msg')}")
return
data_list = msg.get("data", [])
if not data_list:
return
for item in data_list:
snapshot = DepthSnapshot.from_okx_data(self.inst_id, item)
self.latest_snapshot = snapshot
# 整形出力
info = snapshot.best_five()
print(f"\n{'='*50}")
print(f"[{info['timestamp']}] BTC-USDT 深度更新")
print(f" 中値: ${info['mid_price']:,.2f}")
print(f" スプレッド: ${info['spread']:.2f} ({info['spread_bps']:.1f} bps)")
print(f" --- 売板 (Ask) ---")
for i, ask in enumerate(info['top_asks']):
print(f" [{i+1}] ${ask['price']:,.2f} × {ask['size']:.4f}")
print(f" --- 買板 (Bid) ---")
for i, bid in enumerate(info['top_bids']):
print(f" [{i+1}] ${bid['price']:,.2f} × {bid['size']:.4f}")
def stop(self):
self._running = False
===== 実行エントリーポイント =====
async def main():
client = OKXDepthClient(inst_id="BTC-USDT", depth_levels=5)
try:
await client.connect_and_listen()
except KeyboardInterrupt:
print("\n[INFO] ユーザー割込み → 終了")
client.stop()
if __name__ == "__main__":
asyncio.run(main())
応用:HolySheep AIで深度データをAI分析する
リアルタイムで取得した深度データをそのまま保存するだけでは意味がありません。異常値を検出하거나将来の価格変動を予測するにはHolySheep AIのAPIが非常に有効です。以下は深度データの快照を HolySheep に投げて分析コメントを自動生成もらう例です。
import requests
import asyncio
===== HolySheep AI API設定 =====
重要: 実際のAPIキーは https://www.holysheep.ai/register から取得してください
HOLYSHEEP_API_KEY = "YOUR_HOLYSHEEP_API_KEY"
HOLYSHEEP_BASE_URL = "https://api.holysheep.ai/v1"
注意: 絶対に api.openai.com や api.anthropic.com は使用禁止
HolySheepは独自のエンドポイント https://api.holysheep.ai/v1 を使用します
API_PRICE_PER_MTOK = {
"gpt-4.1": 8.00, # $/MTok
"claude-sonnet-4.5": 15.00,
"gemini-2.5-flash": 2.50,
"deepseek-v3.2": 0.42, # HolySheep最安モデル
}
def analyze_depth_with_holysheep(snapshot_data: dict, api_key: str) -> str:
"""
深度データの分析をHolySheep AIにリクエストする
- 板の偏りを検出
- 流動性スコア算出
- 異常値の警告
"""
prompt = f"""以下のBTC-USDT深度データを分析してください:
中値: ${snapshot_data['mid_price']:,.2f}
スプレッド: ${snapshot_data['spread']:.2f} ({snapshot_data['spread_bps']:.1f} bps)
売板(Ask):
{chr(10).join([f" ${a['price']:,.2f} × {a['size']:.4f}" for a in snapshot_data['top_asks']])}
買板(Bid):
{chr(10).join([f" ${b['price']:,.2f} × {b['size']:.4f}" for b in snapshot_data['top_bids']])}
分析項目:
1. 板の偏り(買い優勢か売り優勢か)
2. 流動性の評価(トップ5の合計サイズから判断)
3. 異常値の有無
4. 短期的な価格動向の示唆
"""
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
}
payload = {
"model": "deepseek-v3.2", # $0.42/MTok — コスト効率最高
"messages": [
{"role": "system", "content": "あなたは暗号資産の板読み 전문가です。簡潔に分析結果を報告してください。"},
{"role": "user", "content": prompt}
],
"max_tokens": 300,
"temperature": 0.3,
}
response = requests.post(
f"{HOLYSHEEP_BASE_URL}/chat/completions",
headers=headers,
json=payload,
timeout=5
)
if response.status_code == 401:
raise PermissionError("APIキーが無効です。https://www.holysheep.ai/register で確認してください")
elif response.status_code != 200:
raise RuntimeError(f"API Error {response.status_code}: {response.text}")
result = response.json()
return result["choices"][0]["message"]["content"]
===== 統合メイン処理 =====
async def main_integrated():
client = OKXDepthClient(inst_id="BTC-USDT", depth_levels=5)
analysis_count = 0
max_analyses = 10 # コスト管理: 10回分析したら終了
async def analyze_task():
nonlocal analysis_count
while client._running and analysis_count < max_analyses:
await asyncio.sleep(10) # 10秒ごとに分析
if client.latest_snapshot:
try:
analysis_count += 1
print(f"\n[HolySheep] 分析#{analysis_count} リクエスト中...")
result = analyze_depth_with_holysheep(
client.latest_snapshot.best_five(),
HOLYSHEEP_API_KEY
)
print(f"[HolySheep AI 分析結果]\n{result}")
# コスト計算
input_tokens_est = 400 # 概算
output_tokens_est = 80
cost = (input_tokens_est + output_tokens_est) / 1_000_000 * API_PRICE_PER_MTOK["deepseek-v3.2"]
print(f"[コスト] 1回あたり約 ${cost:.6f} (DeepSeek V3.2)")
except PermissionError as e:
print(f"[ERROR] {e}")
except Exception as e:
print(f"[ERROR] 分析失敗: {type(e).__name__}: {e}")
print(f"[INFO] 分析上限({max_analyses})到達 →終了")
try:
await asyncio.gather(
client.connect_and_listen(),
analyze_task()
)
except KeyboardInterrupt:
print("\n[INFO] 中断 → 終了")
client.stop()
if __name__ == "__main__":
print("注意: YOUR_HOLYSHEEP_API_KEY を実際のキーに置き換えてください")
asyncio.run(main_integrated())
OKX WebSocket API チャンネル早見表
| チャンネル名 | データ内容 | 更新頻度 | 用途 |
|---|---|---|---|
books5 | トップ5の板情報 | リアルタイム | 短期トレーディング、スプレッド監視 |
books50 | トップ50の板情報 | リアルタイム | 流動性分析、アルゴリズム注文 |
bbo-tbt | 最良買気配・最良売気配 | tick by tick | 高頻度取引、最速価格取得 |
tickers | ティッカー(全データ) | リアルタイム | 価格監視、ろうそく足作成 |
candle1m | 1分足OHLCV | 1分ごと | テクニカル分析 |
向いている人・向いていない人
| 向いている人 | 向いていない人 |
|---|---|
| 暗号資産のbotトレードを自作したい人 | WebSocket経験ゼロで最短スタートしたい人 |
| 深度データから流動性分析したい人 | コードを書かずにサービスだけで実現したい人 |
| HolySheep等のLLMでデータ分析したい人 | 板情報ではなく裁定取引ツールを探している人 |
| 低コスト・高頻度の価格取得が必要な人 | 本番環境ですぐに使いたい人(要自作 Merahool 化) |
価格とROI
深度データ解析のコスト構造を整理します。WebSocketによるOKXからのデータ取得自体は無料ですが、深度データをLLMで分析する場合のHolySheep AIコストは以下の通りです。
| モデル | Output価格 ($/MTok) | 1分析あたり概算コスト | 1日100分析の月額コスト |
|---|---|---|---|
| DeepSeek V3.2 | $0.42 | $0.0002〜$0.001 | 約$0.6〜$3 |
| Gemini 2.5 Flash | $2.50 | $0.001〜$0.003 | 約$3〜$9 |
| Claude Sonnet 4.5 | $15.00 | $0.005〜$0.015 | 約$15〜$45 |
| GPT-4.1 | $8.00 | $0.003〜$0.010 | 約$9〜$30 |
深度データの分析にはdeepseek-v3.2($0.42/MTok)が最もコスト効率が高く、1日100分析でも月額$3以下に抑えられます。HolySheep AIなら登録無料でクレジットが付くため、個人開発や検証段階でのコストリスクはゼロです。
HolySheepを選ぶ理由
深度データ解析にLLMを活用する場合、なぜHolySheep AIなのか。私の実体験から3つ理由を挙げます。
- コスト効率: DeepSeek V3.2 が $0.42/MTok という破格の価格で提供されており、GPT-4.1($8.00)の約5%コストで同等の分析が可能。1日500回分析しても月額$15程度に収まる。
- 日本語対応と低レイテンシ: APIのレスポンス遅延が50ms未満(実測)を保証しており、リアルタイム性が求められるbotトレードにも耐える。
- 多様な決済手段: Alipay・WeChat Payにも対応しており、日本語ユーザーは円建てで¥7.3/$1のレート(市場の85%水準)で充值できる点が大きい。
よくあるエラーと対処法
1. 「ConnectionError: timeout」——接続がタイムアウトする
# ❌ よくある失敗例: websockets.connect() にタイムアウト設定がない
async with websockets.connect("wss://ws.okx.com:8443/ws/v5/public") as ws:
...
✅ 解決: timeout を明示的に設定 + 例外処理を追加
import asyncio
from websockets.exceptions import InvalidURI, WebSocketException
async def safe_connect(url: str):
timeout = 30
try:
async with websockets.connect(
url,
open_timeout=timeout,
close_timeout=10,
max_size=10 * 1024 * 1024 # 10MB(深度データ量大対応)
) as ws:
return ws
except websockets.InvalidURI:
raise ValueError(f"無効なURI: {url}")
except asyncio.TimeoutError:
print("[ERROR] 接続タイムアウト(30秒)→ ネットワークを確認")
raise
except Exception as e:
print(f"[ERROR] 接続失敗: {type(e).__name__}: {e}")
raise
原因: ファイアウォール・プロキシ・VPNの блокировка、またはOKXサーバーの一時的な過負荷。OKXのWebSocketは中国本土外からの接続の場合、ネットワーク経路によって不安定になることがあります。解決策: 接続先に wss://aws.okx.com/ws/v5/public(AWSリージョン)を試すか、タイムアウトを長めに設定してください。
2. 「401 Unauthorized」——API認証に失敗する
# ❌ よくある失敗: APIキーのプレースホルダーをそのまま送信
headers = {
"Authorization": "Bearer YOUR_HOLYSHEEP_API_KEY", # 置き換えていない
"Content-Type": "application/json",
}
✅ 解決: 環境変数からAPIキーを安全に取得
import os
from pathlib import Path
def get_api_key() -> str:
"""優先順位: 環境変数 > .envファイル > 例外"""
key = os.environ.get("HOLYSHEEP_API_KEY")
if key and key != "YOUR_HOLYSHEEP_API_KEY":
return key
# .env ファイルからの読込(python-dotenv使用)
try:
from dotenv import load_dotenv
env_path = Path(__file__).parent / ".env"
load_dotenv(env_path)
key = os.getenv("HOLYSHEEP_API_KEY", "")
if key:
return key
except ImportError:
pass
raise PermissionError(
"HolySheep APIキーが未設定です。\n"
"1. https://www.holysheep.ai/register でAPIキーを取得\n"
"2. 環境変数 HOLYSHEEP_API_KEY を設定、または.envファイルに記述してください"
)
使用例
API_KEY = get_api_key()
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
原因: APIキーを設定していない、プレースホルダーのまま実行している、あるいはキーを取り消した的可能性。2026年現在のHolySheepはキーのローテーション機能を 지원하며、無効化されたキーでアクセスすると即座に401が返されます。解決策: まずダッシュボードでキーの状態を確認し、有効なキーを環境変数経由で参照してください。
3. 「pong timeout」——ping/pongのタイムアウトで切断される
# ❌ よくある失敗: pingを送信していない
async def bad_example():
async with websockets.connect(url) as ws:
while True:
msg = await ws.recv() # OKXは25秒後に切断する
process(msg)
✅ 解決: asyncio.coroutineでpingを並列実行
async def heartbeat(ws, interval: int = 20):
"""OKX推奨: 20秒ごとにpingを送信"""
while True:
await asyncio.sleep(interval)
try:
await ws.send("ping")
except websockets.ConnectionClosed:
break
async def good_example():
async with websockets.connect(url, ping_interval=None) as ws:
hb = asyncio.create_task(heartbeat(ws))
try:
async for msg in ws:
if msg == "pong":
continue
process(json.loads(msg))
finally:
hb.cancel()
try:
await hb
except asyncio.CancelledError:
pass
原因: OKXのWebSocketサーバーは接続後25秒以上アクティビティがない場合、自動的に接続を切断します。pingを送信しなければ必ずこのエラーが発生します。解決策: 必ずasyncio.create_task()でheartbeatを並行実行し、接続切断時にhb.cancel()でクリーンアップしてください。
4. 「JSONDecodeError: Expecting value」——空メッセージのパースに失敗
# ❌ よくある失敗: 空文字やping/pongをそのままjson.loadsする
async for raw in ws:
msg = json.loads(raw) # raw="" や raw="ping" で落ちる
✅ 解決: パース前にバリデーションを追加
async for raw in ws:
# 空メッセージ・ping/pongをスキップ
if not raw or raw in ("ping", "pong"):
continue
try:
msg = json.loads(raw)
except json.JSONDecodeError as e:
print(f"[WARN] 不正JSONをスキップ: {e} | データ: {repr(raw[:80])}")
continue
# 正常処理
await handle(msg)
原因: WebSocket仕様ではping/pongフレームが文字列ではなく单独フレームで届く場合があり、接続ハンドシェイク時の空メッセージも存在します。解決策: JSONパース前に必ずif not rawのチェックを入れ、try/exceptで安全に触れてください。
5. 再接続時の「Duplicate subscription」——購読重複エラー
# ❌ よくある失敗: 再接続時にsubscribeメッセージを再送信してしまう
async def bad_reconnect(ws, inst_id):
await ws.send(json.dumps({"op": "subscribe", "args": [...]})) # 重複!
✅ 解決: 購読状態を管理し、再接続時は購読済みチェック
class SubscriptionManager:
def __init__(self):
self._subscribed: dict[str, bool] = {}
def subscribe(self, channel: str, inst_id: str):
key = f"{channel}:{inst_id}"
if self._subscribed.get(key):
print(f"[WARN] 既に購読中: {key} → 再購読をスキップ")
return False
self._subscribed[key] = True
return True
def on_disconnect(self):
"""切断時に購読状態をリセット(サーバーがクリアするため)"""
self._subscribed.clear()
async def robust_connect():
manager = SubscriptionManager()
while True:
try:
async with websockets.connect(OKXDepthClient.WS_URL) as ws:
# 購読は一度だけ
if manager.subscribe("books5", "BTC-USDT"):
await ws.send(json.dumps({
"op": "subscribe",
"args": [{"channel": "books5", "instId": "BTC-USDT"}]
}))
else:
# 購読済みだが、サーバーが切断した可能性がある
# → サーバーがクリアしているので再購読が必要
manager.on_disconnect()
await ws.send(json.dumps({
"op": "subscribe",
"args": [{"channel": "books5", "instId": "BTC-USDT"}]
}))
async for msg in ws:
...
except Exception:
await asyncio.sleep(5)
原因: WebSocket切断後、サーバーが購読状態をクリアする前にクライアントが再接続すると、同じop: subscribeを送信してもエラーになりません。しかし稀にサーバー側で購読が残っている場合、重複購読として処理されないことがあります。解決策: 購読状態をクライアント側で管理し、再接続時はsubscribeメッセージを必ず再送信してください(サーバー側で購読状態がクリアされている保障がないため)。
まとめと次のステップ
本記事では、OKX WebSocket APIからBTC/USDTの深度データをリアルタイムで取得・解析するPython実装を解説しました。ポイントをおさらいします。
- WebSocket接続は
wss://ws.okx.com:8443/ws/v5/public、深度データはbooks5チャンネル - ping/pongは20秒間隔で必ず手動送信(タイムアウトで切断される)
- JSONパース前に空メッセージ・ping/pongをフィルタリング
- 再接続時は
exponential backoffで負荷を避ける - HolySheep AIのDeepSeek V3.2($0.42/MTok)を組み合わせれば、低コストで深度分析AIを実現
コードを試す第一步として、まずは基本実装(1つ目のコードブロック)のみを実行してみてください。WebSocket接続→深度データ表示→Ctrl+Cで中断、の流れを体験すれば、以後の応用実装もスムーズに取り組めます。
👉 HolySheep AI に登録して無料クレジットを獲得