""" 牛熊自适应策略 — 日内级别全币种扫描 (15m / 30m / 1h) 用法: source .venv/bin/activate && python example/regime_intraday.py """ import asyncio import sys import time from datetime import datetime, timezone from pathlib import Path _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.config import config from engine.backtest import BacktestConfig from engine.data import DataService from engine.example.regime_all import RegimeEmaStrategy, RegimeEmaConfig from engine.example.long_short import LongShortEngine SYMBOLS = ["BTCUSDT", "ETHUSDT", "BNBUSDT", "SOLUSDT"] INTERVALS = ["15m", "30m", "1h"] # 沿用 4h 级别优化参数(日内级别可能需单独调参) PARAMS = { "BTCUSDT": (10, 50), "ETHUSDT": (10, 75), "BNBUSDT": (20, 50), "SOLUSDT": (30, 50), } async def get_actual_range(ds: DataService, symbol: str, interval: str): """获取币种指定周期的实际数据范围""" start, end = await ds.fetch_symbol_date_range(symbol, interval) return start, end async def main(): ds = DataService(config.db) await ds.connect() print() print("═" * 130) print(" 牛熊自适应策略 — 日内级别全币种扫描 | 牛市只多/熊市只空/震荡空仓") print("═" * 130) total_start = time.time() results: list[dict] = [] for interval in INTERVALS: print(f"\n ■ {interval} 级别") print(f" {'币种':<10} {'数据范围':<22} {'总收益%':>8} {'年化%':>8} {'夏普':>7} {'回撤%':>8} {'交易':>6} {'多头P&L':>11} {'空头P&L':>11} {'耗时s':>7}") print(" " + "─" * 125) for symbol in SYMBOLS: fast, slow = PARAMS[symbol] sc = RegimeEmaConfig(symbol=symbol, fast=fast, slow=slow) try: act_start, act_end = await get_actual_range(ds, symbol, interval) range_str = f"{act_start.date()}~{act_end.date()}" except Exception: # 数据不存在,跳过 print(f" {symbol:<10} {'无数据':<22}") continue bt = BacktestConfig( symbol=symbol, interval=interval, start_time=act_start, end_time=act_end, initial_capital=10_000.0, ) engine = LongShortEngine(bt, db_config=config.db) t0 = time.time() try: r = await engine.run(RegimeEmaStrategy, sc) elapsed = time.time() - t0 except Exception as ex: print(f" {symbol:<10} {range_str:<22} {'错误: ' + str(ex)[:30]}") continue m = r.metrics long_t = [t for t in r.trades if t.pnl is not None and t.side == "SELL"] short_t = [t for t in r.trades if t.pnl is not None and t.side == "BUY"] long_pnl = sum(t.pnl for t in long_t) if long_t else 0 short_pnl = sum(t.pnl for t in short_t) if short_t else 0 print(f" {symbol:<10} {range_str:<22} {m.total_return_pct:>7.1f}% {m.annual_return_pct:>7.1f}% {m.sharpe_ratio:>7.2f} {m.max_drawdown_pct:>7.1f}% {m.total_trades:>6} {long_pnl:>+10.0f} {short_pnl:>+10.0f} {elapsed:>6.1f}s") results.append({ "interval": interval, "symbol": symbol, "return": m.total_return_pct, "annual": m.annual_return_pct, "sharpe": m.sharpe_ratio, "dd": m.max_drawdown_pct, "trades": m.total_trades, "win_rate": m.win_rate, "profit_factor": m.profit_factor, "long_pnl": long_pnl, "short_pnl": short_pnl, "elapsed": elapsed, }) await ds.close() # ── 汇总排名 ── total_elapsed = time.time() - total_start print(f"\n ■ 最佳组合 (按夏普排名)") print(f" {'排名':<5} {'级别':<6} {'币种':<10} {'总收益%':>8} {'夏普':>7} {'回撤%':>8} {'交易':>6} {'胜率%':>7} {'盈亏比':>7}") print(" " + "─" * 75) sorted_results = sorted(results, key=lambda x: x["sharpe"], reverse=True) for i, r in enumerate(sorted_results): medal = ["🥇", "🥈", "🥉"][i] if i < 3 else f"{i+1:>2}." print(f" {medal:<5} {r['interval']:<6} {r['symbol']:<10} {r['return']:>7.1f}% {r['sharpe']:>7.2f} {r['dd']:>7.1f}% {r['trades']:>6} {r['win_rate']*100:>6.1f}% {r['profit_factor']:>7.2f}") print(f"\n 总耗时: {total_elapsed:.1f}s") print("═" * 130) if __name__ == "__main__": asyncio.run(main())