orderflow_backtest/strategies.py

105 lines
3.9 KiB
Python
Raw Normal View History

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