ML Documentation

オーダーブックデータ保存戦略

📊 現在の保存方式分析

テーブル構造と保存内容

1. orderbook_snapshots テーブル

2. orderbook_levels テーブル

3. orderbook_metrics テーブル

データ量推定

1秒1更新4シンボルの場合
- 日次: ~590MB
- 月次: ~17.7GB  
- 年次: ~212GB

🤔 全レベル保存 vs 集約データ保存

トレードオフ分析

アプローチ メリット デメリット 推奨用途
全レベル保存 • 完全な市場深度
• 詳細バックテスト可能
• マイクロストラクチャー分析
• 高ストレージコスト
• クエリ性能低下
• 管理複雑性
HFT戦略開発、研究
集約データ保存 • 低ストレージコスト
• 高速クエリ
• 管理簡単
• 詳細情報喪失
• 限定的バックテスト
• 深度分析制限
リアルタイム監視、基本分析

🎯 推奨:階層型ストレージアーキテクチャ

時間軸による階層化

ホットティア(0-24時間):
  ストレージ: インメモリ(Redis/QuestDB キャッシュ)
  データ: 全レベル(最大50レベル)
  アクセス: < 1ms
  用途: リアルタイム取引、HFT

ウォームティア(1-7日):
  ストレージ: QuestDB(NVMe SSD)
  データ: 上位20レベル + 集約メトリクス
  アクセス: < 10ms
  用途: 短期分析、戦略調整

コールドティア(7-30日):
  ストレージ: QuestDB(圧縮)+ S3
  データ: 上位5レベル + 集約メトリクス
  アクセス: < 100ms
  用途: パフォーマンス分析

アーカイブティア(30日以上):
  ストレージ: S3 Glacier
  データ: 集約メトリクスのみ
  アクセス: 分単位
  用途: 長期トレンド分析

データライフサイクル自動化

-- 自動データ移行ルール
CREATE TABLE data_lifecycle_rules (
    tier_name TEXT,
    retention_days INT,
    compression TEXT,
    levels_to_keep INT,
    action TEXT
);

INSERT INTO data_lifecycle_rules VALUES
('hot', 1, 'none', 50, 'keep_all_levels'),
('warm', 7, 'snappy', 20, 'reduce_levels'),
('cold', 30, 'zstd', 5, 'reduce_levels_compress'),
('archive', 365, 'zstd_max', 0, 'aggregate_only');

🚀 段階的実装アプローチ

Phase 1: 監視と分析(2週間)

// storage_monitor.rs
impl StorageMonitor {
    pub async fn analyze_usage_patterns(&self) -> UsageReport {
        // アクセスパターン分析
        let access_frequency = self.analyze_access_frequency().await?;

        // レベル別使用率
        let level_usage = self.analyze_level_usage().await?;

        // 時間帯別クエリパターン
        let query_patterns = self.analyze_query_patterns().await?;

        UsageReport {
            frequently_accessed_levels: level_usage.top_10(),
            peak_hours: query_patterns.peak_times(),
            recommended_hot_levels: calculate_optimal_levels(access_frequency),
        }
    }
}

Phase 2: インデックス最適化(1週間)

-- パフォーマンス最適化インデックス
CREATE INDEX idx_orderbook_symbol_time 
    ON orderbook_snapshots(symbol, timestamp DESC) 
    WHERE timestamp > NOW() - INTERVAL '7 days';

CREATE INDEX idx_levels_symbol_time_side 
    ON orderbook_levels(symbol, timestamp DESC, side, level) 
    WHERE level <= 10;

-- パーティショニング
ALTER TABLE orderbook_levels 
    PARTITION BY RANGE (timestamp);

CREATE TABLE orderbook_levels_2024_01 
    PARTITION OF orderbook_levels 
    FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');

Phase 3: スマートサンプリング実装(3週間)

// smart_sampler.rs
pub struct MarketAwareSampler {
    volatility_threshold: f64,
    normal_interval_ms: u64,    // 1000ms
    high_vol_interval_ms: u64,  // 100ms
    max_levels_normal: usize,   // 10
    max_levels_volatile: usize, // 50
}

impl MarketAwareSampler {
    pub fn determine_sampling(&self, market_state: &MarketState) -> SamplingConfig {
        if market_state.volatility > self.volatility_threshold 
           || market_state.is_news_time() {
            // 高ボラティリティ/ニュース時
            SamplingConfig {
                interval: self.high_vol_interval_ms,
                levels: self.max_levels_volatile,
                compression: CompressionLevel::None,
            }
        } else {
            // 通常時
            SamplingConfig {
                interval: self.normal_interval_ms,
                levels: self.max_levels_normal,
                compression: CompressionLevel::Snappy,
            }
        }
    }
}

💡 高頻度取引向け最適化

リアルタイムアクセス最適化

// cache_manager.rs
pub struct OrderbookCache {
    redis_client: RedisClient,
    cache_ttl: Duration,
}

impl OrderbookCache {
    pub async fn get_latest_orderbook(&self, symbol: &str) -> Option<OrderbookSnapshot> {
        // L1: Redisキャッシュ(<1ms)
        if let Some(cached) = self.redis_client.get(symbol).await? {
            return Some(cached);
        }

        // L2: QuestDB最新データ(<10ms)
        let latest = self.questdb_client
            .query_latest_orderbook(symbol)
            .await?;

        // キャッシュ更新
        self.redis_client.setex(symbol, latest, self.cache_ttl).await?;

        Some(latest)
    }
}

差分圧縮による効率化

// delta_compression.rs
pub struct DeltaCompressor {
    last_snapshots: HashMap<String, OrderbookSnapshot>,
}

impl DeltaCompressor {
    pub fn compress(&mut self, snapshot: OrderbookSnapshot) -> CompressedData {
        let symbol = &snapshot.symbol;

        if let Some(last) = self.last_snapshots.get(symbol) {
            // 差分のみを保存
            let delta = OrderbookDelta {
                timestamp: snapshot.timestamp,
                symbol: symbol.clone(),
                changed_levels: self.calculate_changed_levels(last, &snapshot),
            };

            self.last_snapshots.insert(symbol.clone(), snapshot);
            CompressedData::Delta(delta)
        } else {
            // 初回は完全スナップショット
            self.last_snapshots.insert(symbol.clone(), snapshot.clone());
            CompressedData::Full(snapshot)
        }
    }
}

📊 コスト削減効果

ストレージコスト比較

保存方式 日次容量 月次容量 年次容量 相対コスト
全レベル保存(50レベル) 2.9GB 87GB 1.04TB 100%
現在の方式(10レベル) 590MB 17.7GB 212GB 20%
推奨階層型 400MB 12GB 144GB 14%
スマートサンプリング 300MB 9GB 108GB 10%

実装による改善効果

ストレージ削減: 60-70%
クエリ速度向上: 200-300%
リアルタイムアクセス: <1ms(キャッシュヒット時)
バックテスト速度: 150%向上

🔧 実装優先順位

即座に実装可能(1週間)

  1. バッチサイズ最適化(100→500)
  2. インデックス追加
  3. 基本的な監視ダッシュボード

短期実装(2-4週間)

  1. スマートサンプリング
  2. Redisキャッシュ層
  3. 基本的な階層化

中期実装(1-2ヶ月)

  1. 完全な階層型ストレージ
  2. 差分圧縮
  3. 自動ライフサイクル管理

まとめ

オーダーブックデータの保存において重要なのは:

  1. 階層型アーキテクチャで時間軸に応じた最適化
  2. スマートサンプリングで市場状況に適応
  3. 差分圧縮でストレージ効率化
  4. キャッシュ活用でリアルタイム性確保
  5. 自動化でメンテナンスコスト削減

この戦略により、HFT要件を満たしながらコストを60-70%削減できます。