new strategies

This commit is contained in:
2025-10-25 21:51:25 +02:00
parent e4d7f85ea7
commit 576f1d6744
6 changed files with 291 additions and 52 deletions

View File

@ -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."""