105 lines
3.9 KiB
Python
105 lines
3.9 KiB
Python
import logging
|
|
from typing import Optional, Any, cast, List
|
|
from pathlib import Path
|
|
from storage import Book, BookSnapshot
|
|
from models import MetricCalculator, Metric
|
|
from repositories.sqlite_repository import SQLiteOrderflowRepository
|
|
|
|
class DefaultStrategy:
|
|
"""Strategy that calculates and analyzes OBI and CVD metrics from stored data."""
|
|
|
|
def __init__(self, instrument: str):
|
|
self.instrument = instrument
|
|
self._db_path: Optional[Path] = None
|
|
|
|
def set_db_path(self, db_path: Path) -> None:
|
|
"""Set the database path for loading stored metrics."""
|
|
self._db_path = db_path
|
|
|
|
def compute_OBI(self, book: Book) -> List[float]:
|
|
"""Compute Order Book Imbalance using MetricCalculator.
|
|
|
|
Returns:
|
|
list: A list of OBI values, one for each snapshot in the book.
|
|
"""
|
|
if not book.snapshots:
|
|
return []
|
|
|
|
obi_values = []
|
|
|
|
for snapshot in book.snapshots:
|
|
obi = MetricCalculator.calculate_obi(snapshot)
|
|
obi_values.append(obi)
|
|
|
|
return obi_values
|
|
|
|
def load_stored_metrics(self, start_timestamp: int, end_timestamp: int) -> List[Metric]:
|
|
"""Load stored OBI and CVD metrics from database.
|
|
|
|
Args:
|
|
start_timestamp: Start of time range to load.
|
|
end_timestamp: End of time range to load.
|
|
|
|
Returns:
|
|
List of Metric objects with OBI and CVD data.
|
|
"""
|
|
if not self._db_path:
|
|
logging.warning("Database path not set, cannot load stored metrics")
|
|
return []
|
|
|
|
try:
|
|
repo = SQLiteOrderflowRepository(self._db_path)
|
|
with repo.connect() as conn:
|
|
return repo.load_metrics_by_timerange(conn, start_timestamp, end_timestamp)
|
|
except Exception as e:
|
|
logging.error(f"Error loading stored metrics: {e}")
|
|
return []
|
|
|
|
def get_metrics_summary(self, metrics: List[Metric]) -> dict:
|
|
"""Get summary statistics for loaded metrics.
|
|
|
|
Args:
|
|
metrics: List of metric objects.
|
|
|
|
Returns:
|
|
Dictionary with summary statistics.
|
|
"""
|
|
if not metrics:
|
|
return {}
|
|
|
|
obi_values = [m.obi for m in metrics]
|
|
cvd_values = [m.cvd for m in metrics]
|
|
|
|
return {
|
|
"obi_min": min(obi_values),
|
|
"obi_max": max(obi_values),
|
|
"obi_avg": sum(obi_values) / len(obi_values),
|
|
"cvd_start": cvd_values[0],
|
|
"cvd_end": cvd_values[-1],
|
|
"cvd_change": cvd_values[-1] - cvd_values[0],
|
|
"total_snapshots": len(metrics)
|
|
}
|
|
|
|
def on_booktick(self, book: Book):
|
|
"""Hook called on each book tick; can load and analyze stored metrics."""
|
|
# Load stored metrics if database path is available
|
|
if self._db_path and book.first_timestamp and book.last_timestamp:
|
|
metrics = self.load_stored_metrics(book.first_timestamp, book.last_timestamp)
|
|
|
|
if metrics:
|
|
# Analyze stored metrics
|
|
summary = self.get_metrics_summary(metrics)
|
|
logging.info(f"Metrics summary: {summary}")
|
|
|
|
# Check for significant imbalances using stored OBI
|
|
latest_metric = metrics[-1]
|
|
if abs(latest_metric.obi) > 0.2: # 20% imbalance threshold
|
|
logging.info(f"Significant imbalance detected: OBI={latest_metric.obi:.3f}, CVD={latest_metric.cvd:.1f}")
|
|
else:
|
|
# Fallback to real-time calculation for compatibility
|
|
obi_values = self.compute_OBI(book)
|
|
if obi_values:
|
|
latest_obi = obi_values[-1]
|
|
if abs(latest_obi) > 0.2:
|
|
logging.info(f"Significant imbalance detected: {latest_obi:.3f}")
|