暗号資産取引において、L2気配値データ(指値注文簿)は市場マイクロストラクチャを理解する上で最も重要な情報源の一つです。私は2024年半ばから大手デリバティブ取引所のストリーミングデータをリアルタイム処理するパイプラインを構築しましたが、その過程で直面した技術的課題と解決策を本稿で共有します。 HolySheep AI のAPIを活用することで、データ処理パイプラインにおける異常検知やパターン認識を低コストで実装できました。

注文簿データ清洗の重要性と技術的課題

L2注文簿データは每秒数十件の更新が発生し、ネットワーク遅延やシステム輻輳によって以下の異常が生じます:

これらの異常を放置すると、量化因子の計算精度が著しく低下し、トレーディング戦略の足を引っ張ることになります。私の環境では、清洗前の生データで計算した流動性因子は約23%もの誤差を含んでいましたが、適切な清洗工程を追加することで誤差は2%以下に抑えられました。

システムアーキテクチャ設計

私が構築した注文簿処理システムは、3層アーキテクチャを採用しています:

┌─────────────────────────────────────────────────────────────┐
│                    データ収集層 (Collector)                    │
│  WebSocket接続 → メッセージキュー(Kafka)→ パーティション分割  │
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│                    データ清洗層 (Cleaner)                     │
│  状態管理(Redis)→ タイムスタンプ正規化 → 重複除去 → 整合性検証│
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│                    因子抽出層 (Factor Engine)                 │
│  窓関数計算 → 特徴量生成 → HolySheep AI APIで異常パターン検出 │
└─────────────────────────────────────────────────────────────┘

この設計のポイントは、状態管理を外部メモリ(Redis)に分離することで、スケールアウトを容易にしている点です。各ワーカーは独立して清洗処理を行え、障害発生時の再処理も容易になります。

コア実装:Pythonによる注文簿清洗エンジン

import asyncio
import redis.asyncio as redis
import json
from dataclasses import dataclass, field
from typing import Dict, List, Optional, Tuple
from collections import defaultdict
import time
from datetime import datetime

@dataclass
class OrderBookLevel:
    price: float
    quantity: float
    orders: int  # 指値注文数
    timestamp: int  # ミリ秒タイムスタンプ

@dataclass
class OrderBookState:
    bids: Dict[float, OrderBookLevel] = field(default_factory=dict)
    asks: Dict[float, OrderBookLevel] = field(default_factory=dict)
    last_update_id: int = 0
    last_processed_ts: int = 0

class OrderBookCleaner:
    """
    L2注文簿データの清洗エンジン
    欠落更新・重複・順序崩れ・不整合を検出して修正
    """
    
    def __init__(self, redis_client: redis.Redis, symbol: str, 
                 max_staleness_ms: int = 5000):
        self.redis = redis_client
        self.symbol = symbol
        self.max_staleness_ms = max_staleness_ms
        self.state_key = f"ob:state:{symbol}"
        self.dedup_cache: Dict[str, float] = {}
        self.dedup_window_sec = 1.0
        
    async def load_state(self) -> OrderBookState:
        """Redisから状態復元"""
        data = await self.redis.get(self.state_key)
        if data:
            parsed = json.loads(data)
            state = OrderBookState(
                last_update_id=parsed.get('last_update_id', 0),
                last_processed_ts=parsed.get('last_processed_ts', 0)
            )
            for level in parsed.get('bids', []):
                state.bids[level['price']] = OrderBookLevel(**level)
            for level in parsed.get('asks', []):
                state.asks[level['price']] = OrderBookLevel(**level)
            return state
        return OrderBookState()
    
    async def save_state(self, state: OrderBookState):
        """Redisに状態保存"""
        data = {
            'last_update_id': state.last_update_id,
            'last_processed_ts': state.last_processed_ts,
            'bids': [vars(v) for v in state.bids.values()],
            'asks': [vars(v) for v in state.asks.values()]
        }
        await self.redis.set(self.state_key, json.dumps(data), ex=3600)
    
    def deduplicate(self, update_id: int, ts: int) -> bool:
        """重複更新を検出・除外"""
        dedup_key = f"{update_id}:{ts // 1000}"
        current_time = time.time()
        
        # ウィンドウ内の古いエントリを削除
        self.dedup_cache = {
            k: v for k, v in self.dedup_cache.items()
            if current_time - v < self.dedup_window_sec
        }
        
        if dedup_key in self.dedup_cache:
            return False  # 重複検出
        
        self.dedup_cache[dedup_key] = current_time
        return True
    
    def check_order_integrity(self, bids: List, asks: List) -> bool:
        """bid < ask の整合性チェック"""
        if not bids or not asks:
            return True
        best_bid = max(bids, key=lambda x: x['price'])['price']
        best_ask = min(asks, key=lambda x: x['price'])['price']
        return best_bid < best_ask
    
    async def process_update(self, raw_message: dict) -> Optional[OrderBookState]:
        """単一更新メッセージを処理"""
        try:
            # 基本フィールド検証
            if not all(k in raw_message for k in ['update_id', 'bids', 'asks', 'timestamp']):
                return None
            
            update_id = raw_message['update_id']
            ts = raw_message['timestamp']
            
            # staleness check
            current_ts = int(time.time() * 1000)
            if current_ts - ts > self.max_staleness_ms:
                return None  # 古すぎる更新は破棄
            
            # 重複チェック
            if not self.deduplicate(update_id, ts):
                return None
            
            state = await self.load_state()
            
            # 順序保証:update_idが前方のみ受理
            if update_id <= state.last_update_id:
                return None
            
            # 整合性検証
            if not self.check_order_integrity(raw_message['bids'], raw_message['asks']):
                # 異常検知時HolySheep APIでログ記録
                await self._report_anomaly(raw_message, "integrity_violation")
                return None
            
            # 差分適用
            for bid in raw_message['bids']:
                if