# Module: Metrics Calculation System ## Purpose The metrics calculation system provides high-performance computation of Order Book Imbalance (OBI) and Cumulative Volume Delta (CVD) indicators for cryptocurrency trading analysis. It processes orderbook snapshots and trade data to generate financial metrics with per-snapshot granularity. ## Public Interface ### Classes #### `Metric` (dataclass) Represents calculated metrics for a single orderbook snapshot. ```python @dataclass(slots=True) class Metric: snapshot_id: int # Reference to source snapshot timestamp: int # Unix timestamp obi: float # Order Book Imbalance [-1, 1] cvd: float # Cumulative Volume Delta best_bid: float | None # Best bid price best_ask: float | None # Best ask price ``` #### `MetricCalculator` (static class) Provides calculation methods for financial metrics. ```python class MetricCalculator: @staticmethod def calculate_obi(snapshot: BookSnapshot) -> float @staticmethod def calculate_volume_delta(trades: List[Trade]) -> float @staticmethod def calculate_cvd(previous_cvd: float, volume_delta: float) -> float @staticmethod def get_best_bid_ask(snapshot: BookSnapshot) -> tuple[float | None, float | None] ``` ### Functions #### Order Book Imbalance (OBI) Calculation ```python def calculate_obi(snapshot: BookSnapshot) -> float: """ Calculate Order Book Imbalance using the standard formula. Formula: OBI = (Vb - Va) / (Vb + Va) Where: Vb = Total volume on bid side Va = Total volume on ask side Args: snapshot: BookSnapshot containing bids and asks data Returns: float: OBI value between -1 and 1, or 0.0 if no volume Example: >>> snapshot = BookSnapshot(bids={50000.0: OrderbookLevel(...)}, ...) >>> obi = MetricCalculator.calculate_obi(snapshot) >>> print(f"OBI: {obi:.3f}") OBI: 0.333 """ ``` #### Volume Delta Calculation ```python def calculate_volume_delta(trades: List[Trade]) -> float: """ Calculate Volume Delta for a list of trades. Volume Delta = Buy Volume - Sell Volume - Buy trades (side = "buy"): positive contribution - Sell trades (side = "sell"): negative contribution Args: trades: List of Trade objects for specific timestamp Returns: float: Net volume delta (positive = buy pressure, negative = sell pressure) Example: >>> trades = [ ... Trade(side="buy", size=10.0, ...), ... Trade(side="sell", size=3.0, ...) ... ] >>> vd = MetricCalculator.calculate_volume_delta(trades) >>> print(f"Volume Delta: {vd}") Volume Delta: 7.0 """ ``` #### Cumulative Volume Delta (CVD) Calculation ```python def calculate_cvd(previous_cvd: float, volume_delta: float) -> float: """ Calculate Cumulative Volume Delta with incremental support. Formula: CVD_t = CVD_{t-1} + Volume_Delta_t Args: previous_cvd: Previous CVD value (use 0.0 for reset) volume_delta: Current volume delta to add Returns: float: New cumulative volume delta value Example: >>> cvd = 0.0 # Starting value >>> cvd = MetricCalculator.calculate_cvd(cvd, 10.0) # First trade >>> cvd = MetricCalculator.calculate_cvd(cvd, -5.0) # Second trade >>> print(f"CVD: {cvd}") CVD: 5.0 """ ``` ## Usage Examples ### Basic OBI Calculation ```python from models import MetricCalculator, BookSnapshot, OrderbookLevel # Create sample orderbook snapshot snapshot = BookSnapshot( id=1, timestamp=1640995200, bids={ 50000.0: OrderbookLevel(price=50000.0, size=10.0, liquidation_count=0, order_count=1), 49999.0: OrderbookLevel(price=49999.0, size=5.0, liquidation_count=0, order_count=1), }, asks={ 50001.0: OrderbookLevel(price=50001.0, size=3.0, liquidation_count=0, order_count=1), 50002.0: OrderbookLevel(price=50002.0, size=2.0, liquidation_count=0, order_count=1), } ) # Calculate OBI obi = MetricCalculator.calculate_obi(snapshot) print(f"OBI: {obi:.3f}") # Output: OBI: 0.500 # Explanation: (15 - 5) / (15 + 5) = 10/20 = 0.5 ``` ### CVD Calculation with Reset ```python from models import MetricCalculator, Trade # Simulate trading session cvd = 0.0 # Reset CVD at session start # Process trades for first timestamp trades_t1 = [ Trade(id=1, trade_id=1.0, price=50000.0, size=8.0, side="buy", timestamp=1000), Trade(id=2, trade_id=2.0, price=50001.0, size=3.0, side="sell", timestamp=1000), ] vd_t1 = MetricCalculator.calculate_volume_delta(trades_t1) # 8.0 - 3.0 = 5.0 cvd = MetricCalculator.calculate_cvd(cvd, vd_t1) # 0.0 + 5.0 = 5.0 # Process trades for second timestamp trades_t2 = [ Trade(id=3, trade_id=3.0, price=49999.0, size=2.0, side="buy", timestamp=1001), Trade(id=4, trade_id=4.0, price=50000.0, size=7.0, side="sell", timestamp=1001), ] vd_t2 = MetricCalculator.calculate_volume_delta(trades_t2) # 2.0 - 7.0 = -5.0 cvd = MetricCalculator.calculate_cvd(cvd, vd_t2) # 5.0 + (-5.0) = 0.0 print(f"Final CVD: {cvd}") # Output: Final CVD: 0.0 ``` ### Complete Metrics Processing ```python from models import MetricCalculator, Metric def process_snapshot_metrics(snapshot, trades, previous_cvd=0.0): """Process complete metrics for a single snapshot.""" # Calculate OBI obi = MetricCalculator.calculate_obi(snapshot) # Calculate volume delta and CVD volume_delta = MetricCalculator.calculate_volume_delta(trades) cvd = MetricCalculator.calculate_cvd(previous_cvd, volume_delta) # Extract best bid/ask best_bid, best_ask = MetricCalculator.get_best_bid_ask(snapshot) # Create metric record metric = Metric( snapshot_id=snapshot.id, timestamp=snapshot.timestamp, obi=obi, cvd=cvd, best_bid=best_bid, best_ask=best_ask ) return metric, cvd # Usage in processing loop current_cvd = 0.0 for snapshot, trades in snapshot_trade_pairs: metric, current_cvd = process_snapshot_metrics(snapshot, trades, current_cvd) # Store metric to database... ``` ## Dependencies ### Internal - `models.BookSnapshot`: Orderbook state data - `models.Trade`: Individual trade execution data - `models.OrderbookLevel`: Price level information ### External - **Python Standard Library**: `typing` for type hints - **No external packages required** ## Performance Characteristics ### Computational Complexity - **OBI Calculation**: O(n) where n = number of price levels - **Volume Delta**: O(m) where m = number of trades - **CVD Calculation**: O(1) - simple addition - **Best Bid/Ask**: O(n) for min/max operations ### Memory Usage - **Static Methods**: No instance state, minimal memory overhead - **Calculations**: Process data in-place without copying - **Results**: Lightweight `Metric` objects with slots optimization ### Typical Performance ```python # Benchmark results (approximate) Snapshot with 50 price levels: ~0.1ms per OBI calculation Timestamp with 20 trades: ~0.05ms per volume delta CVD update: ~0.001ms per calculation Complete metric processing: ~0.2ms per snapshot ``` ## Error Handling ### Edge Cases Handled ```python # Empty orderbook empty_snapshot = BookSnapshot(bids={}, asks={}) obi = MetricCalculator.calculate_obi(empty_snapshot) # Returns 0.0 # No trades empty_trades = [] vd = MetricCalculator.calculate_volume_delta(empty_trades) # Returns 0.0 # Zero volume scenario zero_vol_snapshot = BookSnapshot( bids={50000.0: OrderbookLevel(price=50000.0, size=0.0, ...)}, asks={50001.0: OrderbookLevel(price=50001.0, size=0.0, ...)} ) obi = MetricCalculator.calculate_obi(zero_vol_snapshot) # Returns 0.0 ``` ### Validation - **OBI Range**: Results automatically bounded to [-1, 1] - **Division by Zero**: Handled gracefully with 0.0 return - **Invalid Data**: Empty collections handled without errors ## Testing ### Test Coverage - **Unit Tests**: `tests/test_metric_calculator.py` - **Integration Tests**: Included in storage and strategy tests - **Edge Cases**: Empty data, zero volume, boundary conditions ### Running Tests ```bash # Run metric calculator tests specifically uv run pytest tests/test_metric_calculator.py -v # Run all tests with metrics uv run pytest -k "metric" -v # Performance tests uv run pytest tests/test_metric_calculator.py::test_calculate_obi_performance ``` ## Known Issues ### Current Limitations - **Precision**: Floating-point arithmetic limitations for very small numbers - **Scale**: No optimization for extremely large orderbooks (>10k levels) - **Currency**: No multi-currency support (assumes single denomination) ### Planned Enhancements - **Decimal Precision**: Consider `decimal.Decimal` for high-precision calculations - **Vectorization**: NumPy integration for batch calculations - **Additional Metrics**: Volume Profile, Liquidity metrics, Delta Flow --- The metrics calculation system provides a robust foundation for financial analysis with clean interfaces, comprehensive error handling, and optimal performance for high-frequency trading data.