""" 更多策略尝试 — 不限于趋势跟踪 新策略: 1. Donchian 海龟 — 20周期突破买入,10周期跌破卖出,ATR止损 2. 价格乖离率 — 偏离 MA50 超 2σ 时反向交易(均值回归) 3. 1h+4h 多TF动量 — 1h MACD 金叉 + 4h EMA 多头共振 币种:BTCUSDT / ETHUSDT / BNBUSDT / SOLUSDT | 周期:4h | 2024-2026 用法: source .venv/bin/activate && python example/strategy_more.py """ import asyncio import math import sys from datetime import datetime, timezone from pathlib import Path from typing import Optional _project_root = Path(__file__).resolve().parent.parent.parent if str(_project_root) not in sys.path: sys.path.insert(0, str(_project_root)) from engine.common.base import BaseStrategy, Signal, StrategyConfig from engine.common.models import Kline from engine.common.config import config from engine.backtest import BacktestEngine, BacktestConfig, BacktestResult from engine.data import DataService from engine.indicators import ema, atr, macd, sma # ════════════════════════════════════════════════════════ # 策略 1:Donchian 海龟突破 # ════════════════════════════════════════════════════════ class DonchianConfig(StrategyConfig): entry_period: int = 20 # 突破周期 exit_period: int = 10 # 退场周期 atr_period: int = 14 atr_stop: float = 2.0 # ATR止损倍数 class DonchianStrategy(BaseStrategy): """海龟交易:突破N日最高买入,跌破M日最低卖出""" strategy_type = "donchian" def __init__(self, c: DonchianConfig): super().__init__(c) self.cfg = c self._highs: list[float] = [] self._lows: list[float] = [] self._closes: list[float] = [] self._highest: float = 0.0 self._in_position = False async def on_kline(self, k: Kline) -> Optional[Signal]: self._highs.append(k.high) self._lows.append(k.low) self._closes.append(k.close) n = len(self._closes) if n < self.cfg.entry_period + 5: return None atr_vals = atr(self._highs, self._lows, self._closes, self.cfg.atr_period) cur_atr = atr_vals[-1] if cur_atr == 0: return None # 通道计算 entry_high = max(self._highs[-self.cfg.entry_period:-1]) exit_low = min(self._lows[-self.cfg.exit_period:-1]) if self._in_position: self._highest = max(self._highest, k.high) stop = self._highest - self.cfg.atr_stop * cur_atr if k.close < exit_low or k.close < stop: self._in_position = False reason = "跌破退出通道" if k.close < exit_low else "ATR止损" return Signal(symbol=self.cfg.symbol, side="SELL", reason=reason, timestamp=k.open_time) if not self._in_position: if k.close > entry_high: self._in_position = True self._highest = k.close return Signal(symbol=self.cfg.symbol, side="BUY", reason=f"突破{self.cfg.entry_period}日高 {k.close:.0f}>{entry_high:.0f}", timestamp=k.open_time) return None # ════════════════════════════════════════════════════════ # 策略 2:价格乖离率(均值回归) # ════════════════════════════════════════════════════════ class DeviationConfig(StrategyConfig): ma_period: int = 50 entry_dev: float = -2.0 # 偏离 sigma 入场(负数=超跌) exit_dev: float = 0.5 # 回归到此附近出场 atr_period: int = 14 atr_stop: float = 1.5 class DeviationStrategy(BaseStrategy): """均值回归:价格暴跌偏离均线 → 买入博反弹""" strategy_type = "deviation" def __init__(self, c: DeviationConfig): super().__init__(c) self.cfg = c self._closes: list[float] = [] self._highs: list[float] = [] self._lows: list[float] = [] self._in_position = False async def on_kline(self, k: Kline) -> Optional[Signal]: self._closes.append(k.close) self._highs.append(k.high) self._lows.append(k.low) n = len(self._closes) if n < self.cfg.ma_period + 5: return None ma = sma(self._closes, self.cfg.ma_period) cur_ma = ma[-1] if cur_ma == 0: return None # 计算标准差 window = self._closes[-self.cfg.ma_period:] mean = sum(window) / len(window) variance = sum((x - mean) ** 2 for x in window) / len(window) stdev = math.sqrt(variance) if variance > 0 else mean * 0.01 # 乖离率(sigma单位) deviation = (k.close - cur_ma) / stdev if stdev > 0 else 0 atr_vals = atr(self._highs, self._lows, self._closes, self.cfg.atr_period) if self._in_position: # 回归到exit_dev sigma内 或 ATR止损 if deviation > self.cfg.exit_dev: self._in_position = False return Signal(symbol=self.cfg.symbol, side="SELL", reason=f"回归均线 dev={deviation:.1f}σ", timestamp=k.open_time) if not self._in_position: if deviation < self.cfg.entry_dev: self._in_position = True return Signal(symbol=self.cfg.symbol, side="BUY", confidence=0.6, # 逆势交易降低仓位 reason=f"超跌反弹 dev={deviation:.1f}σ P={k.close:.0f} bool: if not self._klines_4h: return False closes = [k.close for k in self._klines_4h] ema_vals = ema(closes, self.cfg.ema_4h) for i in range(len(self._klines_4h) - 1, -1, -1): if self._klines_4h[i].close_time <= ts: return ema_vals[i] > 0 and self._klines_4h[i].close > ema_vals[i] return False async def on_kline(self, k: Kline) -> Optional[Signal]: self._closes_1h.append(k.close) self._highs_1h.append(k.high) self._lows_1h.append(k.low) n = len(self._closes_1h) if n < 40: return None mline, sline, _ = macd(self._closes_1h, self.cfg.macd_fast, self.cfg.macd_slow, self.cfg.macd_signal) atr_vals = atr(self._highs_1h, self._lows_1h, self._closes_1h, 14) cur_m, cur_s, cur_atr = mline[-1], sline[-1], atr_vals[-1] prev_m, prev_s = mline[-2], sline[-2] if cur_m == 0 or cur_atr == 0: return None is_4h_bull = self._is_4h_bull(k.open_time) golden = prev_m <= prev_s and cur_m > cur_s if self._in_position: self._highest = max(self._highest, k.high) death = prev_m >= prev_s and cur_m < cur_s stop = self._highest - self.cfg.atr_stop * cur_atr if death or k.close < stop or not is_4h_bull: self._in_position = False reason = "MACD死叉" if death else ("ATR止损" if k.close < stop else "4h转空") return Signal(symbol=self.cfg.symbol, side="SELL", reason=reason, timestamp=k.open_time) if not self._in_position: if golden and is_4h_bull and cur_m > 0: self._in_position = True self._highest = k.close return Signal(symbol=self.cfg.symbol, side="BUY", reason="1hMACD金叉+4h多头", timestamp=k.open_time) return None # ════════════════════════════════════════════════════════ # 运行 # ════════════════════════════════════════════════════════ SYMBOLS = ["BTCUSDT", "ETHUSDT", "BNBUSDT", "SOLUSDT"] DATE_START = datetime(2024, 1, 1) DATE_END = datetime(2026, 1, 1) async def run(symbol, s_name, s_cls, s_cfg, interval="4h"): bt = BacktestConfig(symbol=symbol, interval=interval, start_time=DATE_START, end_time=DATE_END, initial_capital=10_000.0) s_cfg.symbol = symbol if hasattr(s_cfg, 'data_start'): s_cfg.data_start = DATE_START s_cfg.data_end = DATE_END engine = BacktestEngine(bt, db_config=config.db) return await engine.run(s_cls, s_cfg) async def main(): strategies = [ ("Donchian海龟", DonchianStrategy, DonchianConfig(), "4h"), ("乖离率回归", DeviationStrategy, DeviationConfig(), "4h"), ("1h+4h动量", MultiTFStrategy, MultiTFConfig(), "1h"), ] print() print("═" * 105) print(" 更多策略尝试 | 2024-2026") print("═" * 105) print(f" {'策略':<16} {'币种':<10} {'收益%':>7} {'夏普':>6} {'回撤%':>7} {'交易':>5} {'胜率%':>6} {'盈亏比':>6}") print("─" * 105) all_rows = [] for s_name, s_cls, s_cfg, interval in strategies: for symbol in SYMBOLS: r = await run(symbol, s_name, s_cls, s_cfg.model_copy(), interval) m = r.metrics all_rows.append((s_name, symbol, m)) print(f" {s_name:<16} {symbol:<10} {m.total_return_pct:>6.1f}% {m.sharpe_ratio:>6.2f} {m.max_drawdown_pct:>6.1f}% {m.total_trades:>5} {m.win_rate*100:>5.1f}% {m.profit_factor:>6.2f}") # ── 排名 ── print("─" * 105) print("\n ■ 按夏普 TOP 5:") ranked = sorted(all_rows, key=lambda x: x[2].sharpe_ratio, reverse=True) for i, (s_name, symbol, m) in enumerate(ranked[:5]): print(f" {i+1}. {symbol} {s_name:<16} 夏普={m.sharpe_ratio:.2f} 收益={m.total_return_pct:+.1f}% 回撤={m.max_drawdown_pct:.1f}% 胜率={m.win_rate*100:.0f}%") print("\n ■ 各币种最佳:") for symbol in SYMBOLS: sym_rows = [(s, m) for s, sym, m in all_rows if sym == symbol] best = max(sym_rows, key=lambda x: x[1].sharpe_ratio) print(f" {symbol}: {best[0]:<16} 夏普={best[1].sharpe_ratio:.2f} 收益={best[1].total_return_pct:+.1f}%") avg_sh = sum(x[2].sharpe_ratio for x in all_rows) / len(all_rows) print(f"\n 平均夏普: {avg_sh:.2f}") print("\n═" * 105) if __name__ == "__main__": asyncio.run(main())