import logging from typing import List, Any, Dict, Tuple from viz_io import add_ohlc_bar, upsert_ohlc_bar, _atomic_write_json, DEPTH_FILE from db_interpreter import OrderbookUpdate from level_parser import normalize_levels, parse_levels_including_zeros from orderbook_manager import OrderbookManager from metrics_calculator import MetricsCalculator class OHLCProcessor: """ Processes trade data and orderbook updates into OHLC bars and depth snapshots. This class aggregates individual trades into time-windowed OHLC (Open, High, Low, Close) bars and maintains an in-memory orderbook state for depth visualization. It also calculates Order Book Imbalance (OBI) and Cumulative Volume Delta (CVD) metrics. The processor uses throttled updates to balance visualization responsiveness with I/O efficiency, emitting intermediate updates during active windows. Attributes: window_seconds: Time window duration for OHLC aggregation depth_levels_per_side: Number of top price levels to maintain per side trades_processed: Total number of trades processed bars_created: Total number of OHLC bars created cvd_cumulative: Running cumulative volume delta (via metrics calculator) """ def __init__(self) -> None: self.current_bar = None self.trades_processed = 0 self._orderbook = OrderbookManager() self._metrics = MetricsCalculator() @property def cvd_cumulative(self) -> float: """Access cumulative CVD from metrics calculator.""" return self._metrics.cvd_cumulative def process_trades(self, trades: List[Tuple[Any, ...]]) -> None: for trade in trades: trade_id, trade_id_str, price, size, side, timestamp_ms = trade[:6] timestamp_ms = int(timestamp_ms) self._metrics.update_cvd_from_trade(side, size) if not self.current_bar: self.current_bar = { 'open': float(price), 'high': float(price), 'low': float(price), 'close': float(price) } self.current_bar['high'] = max(self.current_bar['high'], float(price)) self.current_bar['low'] = min(self.current_bar['low'], float(price)) self.current_bar['close'] = float(price) self.current_bar['volume'] += float(size) def update_orderbook(self, ob_update: OrderbookUpdate) -> None: bids_updates = parse_levels_including_zeros(ob_update.bids) asks_updates = parse_levels_including_zeros(ob_update.asks) self._orderbook.apply_updates(bids_updates, asks_updates) total_bids, total_asks = self._orderbook.get_total_volume() self._metrics.update_obi_from_book(total_bids, total_asks)