from abc import ABC, abstractmethod import pandas as pd import json import os import logging from datetime import datetime, timezone import sqlite3 class BaseStrategy(ABC): """ An abstract base class that defines the blueprint for all trading strategies. It provides common functionality like loading data and saving status. """ def __init__(self, strategy_name: str, params: dict, log_level: str): self.strategy_name = strategy_name self.params = params self.coin = params.get("coin", "N/A") self.timeframe = params.get("timeframe", "N/A") self.db_path = os.path.join("_data", "market_data.db") self.status_file_path = os.path.join("_data", f"strategy_status_{self.strategy_name}.json") # --- ADDED: State variables required for status reporting --- self.current_signal = "INIT" self.last_signal_change_utc = None self.signal_price = None # This will be set up by the child class after it's initialized # setup_logging(log_level, f"Strategy-{self.strategy_name}") # logging.info(f"Initializing with parameters: {self.params}") def load_data(self) -> pd.DataFrame: """Loads historical data for the configured coin and timeframe.""" table_name = f"{self.coin}_{self.timeframe}" # Dynamically determine the number of candles needed based on all possible period parameters periods = [v for k, v in self.params.items() if 'period' in k or '_ma' in k or 'slow' in k] limit = max(periods) + 50 if periods else 500 try: with sqlite3.connect(f"file:{self.db_path}?mode=ro", uri=True) as conn: query = f'SELECT * FROM "{table_name}" ORDER BY datetime_utc DESC LIMIT {limit}' df = pd.read_sql(query, conn, parse_dates=['datetime_utc']) if df.empty: return pd.DataFrame() df.set_index('datetime_utc', inplace=True) df.sort_index(inplace=True) return df except Exception as e: logging.error(f"Failed to load data from table '{table_name}': {e}") return pd.DataFrame() @abstractmethod def calculate_signals(self, df: pd.DataFrame) -> pd.DataFrame: """ The core logic of the strategy. Must be implemented by child classes. """ pass def _save_status(self): """Saves the current strategy state to its JSON file.""" status = { "strategy_name": self.strategy_name, "current_signal": self.current_signal, "last_signal_change_utc": self.last_signal_change_utc, "signal_price": self.signal_price, "last_checked_utc": datetime.now(timezone.utc).isoformat() } try: with open(self.status_file_path, 'w', encoding='utf-8') as f: json.dump(status, f, indent=4) except IOError as e: logging.error(f"Failed to write status file for {self.strategy_name}: {e}")