orderflow_backtest/strategies.py

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}")