new strategies
This commit is contained in:
@ -6,13 +6,16 @@ import logging
|
||||
from datetime import datetime, timezone
|
||||
import sqlite3
|
||||
|
||||
from logging_utils import setup_logging
|
||||
|
||||
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.
|
||||
It provides common functionality like loading data, saving status, and state management.
|
||||
"""
|
||||
|
||||
def __init__(self, strategy_name: str, params: dict, log_level: str):
|
||||
def __init__(self, strategy_name: str, params: dict):
|
||||
# Note: log_level is not needed here as logging is set up by the process
|
||||
self.strategy_name = strategy_name
|
||||
self.params = params
|
||||
self.coin = params.get("coin", "N/A")
|
||||
@ -20,21 +23,17 @@ class BaseStrategy(ABC):
|
||||
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}")
|
||||
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]
|
||||
periods = [v for k, v in self.params.items() if 'period' in k or '_ma' in k or 'slow' in k or 'fast' in k]
|
||||
limit = max(periods) + 50 if periods else 500
|
||||
|
||||
try:
|
||||
@ -51,10 +50,30 @@ class BaseStrategy(ABC):
|
||||
|
||||
@abstractmethod
|
||||
def calculate_signals(self, df: pd.DataFrame) -> pd.DataFrame:
|
||||
"""
|
||||
The core logic of the strategy. Must be implemented by child classes.
|
||||
"""
|
||||
"""The core logic of the strategy. Must be implemented by child classes."""
|
||||
pass
|
||||
|
||||
def calculate_signals_and_state(self, df: pd.DataFrame):
|
||||
"""
|
||||
A wrapper that calls the strategy's signal calculation and then
|
||||
determines the last signal change from the historical data.
|
||||
"""
|
||||
df_with_signals = self.calculate_signals(df)
|
||||
df_with_signals.dropna(inplace=True)
|
||||
if df_with_signals.empty: return
|
||||
|
||||
df_with_signals['position_change'] = df_with_signals['signal'].diff()
|
||||
|
||||
last_signal = df_with_signals['signal'].iloc[-1]
|
||||
if last_signal == 1: self.current_signal = "BUY"
|
||||
elif last_signal == -1: self.current_signal = "SELL"
|
||||
else: self.current_signal = "HOLD"
|
||||
|
||||
last_change_series = df_with_signals[df_with_signals['position_change'] != 0]
|
||||
if not last_change_series.empty:
|
||||
last_change_row = last_change_series.iloc[-1]
|
||||
self.last_signal_change_utc = last_change_row.name.tz_localize('UTC').isoformat()
|
||||
self.signal_price = last_change_row['close']
|
||||
|
||||
def _save_status(self):
|
||||
"""Saves the current strategy state to its JSON file."""
|
||||
|
||||
Reference in New Issue
Block a user