71 lines
2.8 KiB
Python
71 lines
2.8 KiB
Python
|
|
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)
|
||
|
|
|