"""
多因子组合回测 — 三重共振策略

随机挑选 3 个技术指标组合成一个策略：
  - MACD  (趋势因子) — 金叉/死叉判断方向
  - RSI   (动量因子) — 阈值过滤避免追高抄底
  - Bollinger (波动率因子) — 中轨确认趋势强度

用法：
    source .venv/bin/activate && python example/factor_demo.py
"""

import asyncio
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
from engine.indicators import macd, rsi, bollinger, atr


# ============================================================
# 三重共振策略
# ============================================================


class TripleFactorConfig(StrategyConfig):
    """三重因子组合策略配置"""

    # MACD
    macd_fast: int = 12
    macd_slow: int = 26
    macd_signal: int = 9

    # RSI
    rsi_period: int = 14
    rsi_oversold: float = 30.0    # 超卖线（入场需在此之上）
    rsi_overbought: float = 65.0  # 入场过热线（入场需在此之下）
    rsi_exit: float = 75.0        # 卖出线

    # Bollinger
    bb_period: int = 20
    bb_std: float = 2.0

    # ATR 动态止损倍数（0 表示不启用）
    atr_period: int = 14
    atr_stop_mult: float = 2.0


class TripleFactorStrategy(BaseStrategy):
    """三重共振策略

    ┌─────────────┬──────────────────────────────────────┐
    │ 因子         │ 作用                                  │
    ├─────────────┼──────────────────────────────────────┤
    │ MACD (趋势)  │ 金叉=看多入场信号，死叉=看空出场信号    │
    │ RSI  (动量)  │ 30<RSI<65 区间入场，RSI>75 过热出场    │
    │ BB   (波动)  │ 价格>中轨确认多头趋势，跌破下轨出场      │
    └─────────────┴──────────────────────────────────────┘

    入场（三重共振）：
      1. MACD 金叉（上穿信号线）
      2. RSI 在 [30, 65] 区间（合理动量）
      3. 价格 > 布林中轨（趋势向上）

    出场（任一触发）：
      1. MACD 死叉（下穿信号线）
      2. RSI > 75（过热）
      3. 价格 < 布林下轨（趋势破位）
    """

    strategy_type = "triple_factor"

    def __init__(self, config: TripleFactorConfig):
        super().__init__(config)
        self.cfg: TripleFactorConfig = config
        self._closes: list[float] = []
        self._highs: list[float] = []
        self._lows: list[float] = []

    async def on_kline(self, kline: Kline) -> Optional[Signal]:
        self._closes.append(kline.close)
        self._highs.append(kline.high)
        self._lows.append(kline.low)

        n = len(self._closes)
        max_period = max(
            self.cfg.macd_slow + self.cfg.macd_signal,
            self.cfg.rsi_period + 1,
            self.cfg.bb_period,
        )
        if n < max_period:
            return None

        # ── 全量计算因子（每个 bar 一次）──
        macd_line, signal_line, _hist = macd(
            self._closes,
            fast=self.cfg.macd_fast,
            slow=self.cfg.macd_slow,
            signal=self.cfg.macd_signal,
        )
        rsi_vals = rsi(self._closes, period=self.cfg.rsi_period)
        _upper, mid, lower = bollinger(
            self._closes,
            period=self.cfg.bb_period,
            std=self.cfg.bb_std,
        )

        # 当前值和前一根的值
        cur_macd = macd_line[-1]
        cur_signal = signal_line[-1]
        prev_macd = macd_line[-2]
        prev_signal = signal_line[-2]
        cur_rsi = rsi_vals[-1]
        prev_rsi = rsi_vals[-2]
        cur_mid = mid[-1]
        cur_lower = lower[-1]
        cur_price = kline.close

        if cur_macd == 0.0 or cur_rsi == 0.0 or cur_mid == 0.0:
            return None

        # ── 入场：2/3 共振即可 ──
        golden_cross = prev_macd <= prev_signal and cur_macd > cur_signal
        rsi_ok = self.cfg.rsi_oversold < cur_rsi < self.cfg.rsi_overbought
        above_mid = cur_price > cur_mid

        score = golden_cross + rsi_ok + above_mid
        if score >= 2:
            return Signal(
                symbol=self.cfg.symbol,
                side="BUY",
                signal_type="MARKET",
                confidence=0.6 + score * 0.1,
                reason=(
                    f"{score}/3共振"
                    f"{' MACD金叉' if golden_cross else ''}"
                    f"{' RSI=' + str(round(cur_rsi, 1)) if rsi_ok else ''}"
                    f"{' Price>BBmid' if above_mid else ''}"
                ),
                timestamp=kline.open_time,
            )

        # ── 出场：任一强信号 ──
        death_cross = prev_macd >= prev_signal and cur_macd < cur_signal
        rsi_overheat = cur_rsi > self.cfg.rsi_exit
        below_lower = cur_price < cur_lower

        if death_cross:
            return Signal(
                symbol=self.cfg.symbol,
                side="SELL",
                signal_type="MARKET",
                confidence=0.8,
                reason="MACD死叉",
                timestamp=kline.open_time,
            )
        if rsi_overheat and cur_rsi > prev_rsi:
            return Signal(
                symbol=self.cfg.symbol,
                side="SELL",
                signal_type="MARKET",
                confidence=0.7,
                reason=f"RSI过热({cur_rsi:.1f})",
                timestamp=kline.open_time,
            )
        if below_lower and cur_price < cur_mid:
            return Signal(
                symbol=self.cfg.symbol,
                side="SELL",
                signal_type="MARKET",
                confidence=0.6,
                reason=f"跌破BB下轨({cur_lower:.2f})",
                timestamp=kline.open_time,
            )

        return None


# ============================================================
# 主函数
# ============================================================


async def main():
    bt_config = BacktestConfig(
        symbol="BTCUSDT",
        interval="4h",
        start_time=datetime(2024, 1, 1),
        end_time=datetime(2026, 1, 1),
        initial_capital=10_000.0,
        commission_pct=0.001,
        slippage_pct=0.0005,
        warmup_bars=100,
    )

    strategy_config = TripleFactorConfig(
        name="triple_factor_btc",
        symbol="BTCUSDT",
        macd_fast=12,
        macd_slow=26,
        macd_signal=9,
        rsi_period=14,
        rsi_oversold=30.0,
        rsi_overbought=65.0,
        rsi_exit=75.0,
        bb_period=20,
        bb_std=2.0,
    )

    print()
    print("╔" + "═" * 58 + "╗")
    print("║" + "  多因子组合回测 — 三重共振策略".center(52) + "║")
    print("╠" + "═" * 58 + "╣")
    print(f"║  {'交易对:':<8} {bt_config.symbol:<12} {'周期:':<6} {bt_config.interval:<10}     ║")
    print(f"║  {'时间:':<8} {bt_config.start_time.date()} ~ {bt_config.end_time.date()}      ║")
    print(f"║  {'初始资金:':<8} {bt_config.initial_capital:>12.2f} USDT             ║")
    print("╠" + "═" * 58 + "╣")
    print("║  因子组合:                                           ║")
    print(f"║    1. MACD({strategy_config.macd_fast},{strategy_config.macd_slow},{strategy_config.macd_signal}) — 趋势方向                                 ║")
    print(f"║    2. RSI({strategy_config.rsi_period}) — 动量过滤                                 ║")
    print(f"║    3. Bollinger({strategy_config.bb_period},{strategy_config.bb_std}) — 波动率确认                           ║")
    print("╚" + "═" * 58 + "╝")
    print()

    engine = BacktestEngine(bt_config, db_config=config.db)
    result = await engine.run(TripleFactorStrategy, strategy_config)

    print(result.summary())

    # 打印全部交易
    if result.trades:
        print(f"\n全部交易 ({len(result.trades)} 笔):")
        print(f"{'时间':<22} {'方向':<6} {'价格':>10} {'数量':>10} {'盈亏':>10}  原因")
        print("-" * 100)
        for t in result.trades:
            dt = datetime.fromtimestamp(t.timestamp / 1000, tz=timezone.utc).strftime("%Y-%m-%d %H:%M")
            pnl_str = f"{t.pnl:+.4f}" if t.pnl is not None else "—"
            print(f"{dt:<22} {t.side:<6} {t.price:>10.2f} {t.quantity:>10.6f} {pnl_str:>10}  {t.reason}")

    print("\n回测完成。")


if __name__ == "__main__":
    asyncio.run(main())
