import time from datetime import datetime import logging from typing import Optional import pandas as pd from core.base import QuantTrader logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s: %(message)s') class QuantStrategy: def __init__(self, api_key: str, secret_key: str, passphrase: str, sandbox: bool = True, symbol: str = "BTC-USDT", position_size: float = 0.001): """ 初始化量化策略 """ self.quant_trader = QuantTrader( api_key, secret_key, passphrase, sandbox, symbol, position_size) def calculate_sma(self, df: pd.DataFrame, period: int = 20) -> pd.Series: """ 计算简单移动平均线 """ if 'close' not in df: logging.error("DataFrame缺少'close'列,无法计算SMA") return pd.Series([float('nan')] * len(df)) if len(df) < period: logging.warning(f"数据长度不足{period},SMA结果将包含NaN") return df['close'].rolling(window=period).mean() def calculate_rsi(self, df: pd.DataFrame, period: int = 14) -> pd.Series: """ 计算RSI指标 """ if 'close' not in df: logging.error("DataFrame缺少'close'列,无法计算RSI") return pd.Series([float('nan')] * len(df)) if len(df) < period: logging.warning(f"数据长度不足{period},RSI结果将包含NaN") delta = df['close'].diff() gain = (delta.where(delta > 0, 0)).rolling(window=period).mean() loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean() loss = loss.replace(0, 1e-10) # 防止除零 rs = gain / loss rsi = 100 - (100 / (1 + rs)) return rsi def simple_moving_average_strategy(self, sma_short_period: int = 5, sma_long_period: int = 20) -> None: """ 简单移动平均线策略 """ logging.info("=== 执行移动平均线策略 ===") try: df = self.quant_trader.get_kline_data(bar='5m', limit=max(50, sma_long_period+2)) except Exception as e: logging.error(f"获取K线数据失败: {e}") return if df is None or len(df) < max(sma_short_period, sma_long_period, 2): logging.warning("数据不足,无法执行策略") return df['sma_short'] = self.calculate_sma(df, sma_short_period) df['sma_long'] = self.calculate_sma(df, sma_long_period) if len(df) < 2: logging.warning("数据不足2条,无法判断金叉死叉") return latest = df.iloc[-1] prev = df.iloc[-2] if pd.isna(latest['sma_short']) or pd.isna(latest['sma_long']) or pd.isna(prev['sma_short']) or pd.isna(prev['sma_long']): logging.warning("均线数据存在NaN,跳过本次信号判断") return logging.info(f"短期均线: {latest['sma_short']:.2f}") logging.info(f"长期均线: {latest['sma_long']:.2f}") logging.info(f"当前价格: {latest['close']:.2f}") if (latest['sma_short'] > latest['sma_long'] and prev['sma_short'] <= prev['sma_long']): logging.info("信号: 买入") self.quant_trader.place_market_order('buy', self.quant_trader.position_size) elif (latest['sma_short'] < latest['sma_long'] and prev['sma_short'] >= prev['sma_long']): logging.info("信号: 卖出") self.quant_trader.place_market_order('sell', self.quant_trader.position_size) else: logging.info("信号: 持仓观望") def rsi_strategy(self, period: int = 14, oversold: int = 30, overbought: int = 70) -> None: """ RSI策略 """ logging.info("=== 执行RSI策略 ===") try: df = self.quant_trader.get_kline_data(bar='5m', limit=max(50, period+2)) except Exception as e: logging.error(f"获取K线数据失败: {e}") return if df is None or len(df) < period: logging.warning("数据不足,无法执行策略") return df['rsi'] = self.calculate_rsi(df, period) latest_rsi = df['rsi'].iloc[-1] if pd.isna(latest_rsi): logging.warning("最新RSI为NaN,跳过本次信号判断") return logging.info(f"当前RSI: {latest_rsi:.2f}") if latest_rsi < oversold: logging.info("信号: RSI超卖,买入") self.quant_trader.place_market_order('buy', self.quant_trader.position_size) elif latest_rsi > overbought: logging.info("信号: RSI超买,卖出") self.quant_trader.place_market_order('sell', self.quant_trader.position_size) else: logging.info("信号: RSI正常区间,持仓观望") def grid_trading_strategy(self, grid_levels: int = 5, grid_range: float = 0.02) -> None: """ 网格交易策略 """ if grid_levels <= 0: logging.error("网格数必须大于0") return if grid_range <= 0: logging.error("网格范围必须大于0") return logging.info(f"=== 执行网格交易策略 (网格数: {grid_levels}, 范围: {grid_range*100}%) ===") try: current_price = self.quant_trader.get_current_price() except Exception as e: logging.error(f"获取当前价格失败: {e}") return if current_price is None: logging.warning("当前价格获取失败") return grid_prices = [] for i in range(grid_levels): price = current_price * (1 + grid_range * (i - grid_levels//2) / grid_levels) grid_prices.append(price) logging.info(f"网格价格: {[f'${p:.2f}' for p in grid_prices]}") try: df = self.quant_trader.get_kline_data(bar='1m', limit=10) except Exception as e: logging.error(f"获取K线数据失败: {e}") return if df is None or len(df) == 0 or 'close' not in df: logging.warning("K线数据无效,无法执行网格策略") return latest_price = df['close'].iloc[-1] if pd.isna(latest_price): logging.warning("最新价格为NaN,跳过本次信号判断") return closest_grid = min(grid_prices, key=lambda x: abs(x - latest_price)) logging.info(f"当前价格: ${latest_price:.2f}, 最近网格: ${closest_grid:.2f}") if latest_price < closest_grid * 0.995: logging.info("信号: 价格下跌,网格买入") self.quant_trader.place_market_order('buy', self.quant_trader.position_size) elif latest_price > closest_grid * 1.005: logging.info("信号: 价格上涨,网格卖出") self.quant_trader.place_market_order('sell', self.quant_trader.position_size) else: logging.info("信号: 价格在网格内,持仓观望") def run_strategy_loop(self, strategy: str = 'sma', interval: int = 60, trading_config: dict = {}) -> None: """ 运行策略循环 """ if interval <= 0: logging.error("循环间隔必须大于0秒") return logging.info(f"开始运行{strategy}策略,间隔{interval}秒") while True: try: logging.info(datetime.now().strftime('%Y-%m-%d %H:%M:%S')) try: self.quant_trader.get_account_balance() except Exception as e: logging.error(f"获取账户余额失败: {e}") if strategy == 'sma': sma_short_period = trading_config.get("sma_short_period", 5) sma_long_period = trading_config.get("sma_long_period", 20) self.simple_moving_average_strategy(sma_short_period, sma_long_period) elif strategy == 'rsi': period = trading_config.get("rsi_period", 14) oversold = trading_config.get("rsi_oversold", 30) overbought = trading_config.get("rsi_overbought", 70) self.rsi_strategy(period, oversold, overbought) elif strategy == 'grid': grid_levels = trading_config.get("grid_levels", 5) grid_range = trading_config.get("grid_range", 0.02) self.grid_trading_strategy(grid_levels, grid_range) else: logging.error("未知策略") break logging.info(f"等待{interval}秒后继续...") time.sleep(interval) except KeyboardInterrupt: logging.info("策略运行被用户中断") break except Exception as e: logging.error(f"策略运行异常: {e}") time.sleep(interval)