133 lines
5.2 KiB
Python
133 lines
5.2 KiB
Python
import argparse
|
|
import logging
|
|
import sys
|
|
import time
|
|
import pandas as pd
|
|
import sqlite3
|
|
import json
|
|
import os
|
|
|
|
from logging_utils import setup_logging
|
|
|
|
class TradingStrategy:
|
|
"""
|
|
A template for a trading strategy that reads data from the SQLite database
|
|
and executes its logic in a loop.
|
|
"""
|
|
|
|
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")
|
|
|
|
# Load strategy-specific parameters
|
|
self.rsi_period = params.get("rsi_period")
|
|
self.short_ma = params.get("short_ma")
|
|
self.long_ma = params.get("long_ma")
|
|
self.sma_period = params.get("sma_period")
|
|
|
|
setup_logging(log_level, f"Strategy-{self.strategy_name}")
|
|
logging.info(f"Initializing strategy with parameters: {self.params}")
|
|
|
|
def load_data(self) -> pd.DataFrame:
|
|
"""Loads historical data for the configured coin and timeframe from the database."""
|
|
table_name = f"{self.coin}_{self.timeframe}"
|
|
# Ensure we load enough data for the longest indicator period
|
|
limit = 500
|
|
if self.sma_period and self.sma_period > limit:
|
|
limit = self.sma_period + 50 # Add a buffer
|
|
elif self.long_ma and self.long_ma > limit:
|
|
limit = self.long_ma + 50
|
|
|
|
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)
|
|
df['datetime_utc'] = pd.to_datetime(df['datetime_utc'])
|
|
df.set_index('datetime_utc', inplace=True)
|
|
df.sort_index(inplace=True) # Ensure data is chronological
|
|
return df
|
|
except Exception as e:
|
|
logging.error(f"Failed to load data from table '{table_name}': {e}")
|
|
return pd.DataFrame()
|
|
|
|
def run_logic(self):
|
|
"""
|
|
The main loop where the strategy's logic is executed.
|
|
This should be implemented with your specific trading rules.
|
|
"""
|
|
logging.info(f"Starting main logic loop for {self.coin} on {self.timeframe} timeframe.")
|
|
while True:
|
|
data = self.load_data()
|
|
|
|
if data.empty:
|
|
logging.warning("No data loaded. Waiting before retrying...")
|
|
time.sleep(60)
|
|
continue
|
|
|
|
last_close = data['close'].iloc[-1]
|
|
logging.info(f"Latest data loaded. Last close price for {self.coin}: {last_close}")
|
|
|
|
# --- SMA Strategy Logic ---
|
|
if self.sma_period:
|
|
if len(data) < self.sma_period:
|
|
logging.warning(f"Not enough data to calculate {self.sma_period}-period SMA. "
|
|
f"Need {self.sma_period}, have {len(data)}.")
|
|
else:
|
|
# Calculate the Simple Moving Average
|
|
sma = data['close'].rolling(window=self.sma_period).mean().iloc[-1]
|
|
logging.info(f"Current Price: {last_close}, {self.sma_period}-period SMA: {sma:.4f}")
|
|
|
|
if last_close > sma:
|
|
logging.warning("--- BUY SIGNAL --- (Price is above SMA)")
|
|
elif last_close < sma:
|
|
logging.warning("--- SELL SIGNAL --- (Price is below SMA)")
|
|
else:
|
|
logging.info("--- HOLD SIGNAL --- (Price is at SMA)")
|
|
|
|
# --- RSI Strategy Logic (Placeholder) ---
|
|
if self.rsi_period:
|
|
logging.info(f"RSI Period is set to: {self.rsi_period}. (RSI calculation not implemented).")
|
|
|
|
# --- MA Cross Strategy Logic (Placeholder) ---
|
|
if self.short_ma and self.long_ma:
|
|
logging.info(f"Short MA: {self.short_ma}, Long MA: {self.long_ma}. (MA Cross logic not implemented).")
|
|
|
|
logging.info("Logic execution finished. Waiting for next cycle.")
|
|
time.sleep(60)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description="Run a trading strategy.")
|
|
parser.add_argument("--name", required=True, help="The name of the strategy instance from the config.")
|
|
parser.add_argument("--params", required=True, help="A JSON string of the strategy's parameters.")
|
|
parser.add_argument(
|
|
"--log-level",
|
|
default="normal",
|
|
choices=['off', 'normal', 'debug'],
|
|
help="Set the logging level for the script."
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
try:
|
|
strategy_params = json.loads(args.params)
|
|
strategy = TradingStrategy(
|
|
strategy_name=args.name,
|
|
params=strategy_params,
|
|
log_level=args.log_level
|
|
)
|
|
strategy.run_logic()
|
|
except json.JSONDecodeError:
|
|
logging.error("Failed to decode JSON from --params argument.")
|
|
sys.exit(1)
|
|
except KeyboardInterrupt:
|
|
logging.info("Strategy process stopped.")
|
|
sys.exit(0)
|
|
except Exception as e:
|
|
logging.error(f"A critical error occurred: {e}")
|
|
sys.exit(1)
|
|
|