"""
策略优化对比 — 原始 vs 优化版本

优化点：
  EMA v2: 增加 ATR 动态止损 + 趋势过滤（EMA200）
  RSI v2: 趋势确认（只在 EMA50 上方做多）+ 放宽入场到 RSI<40
  MACD v2: 零轴过滤（MACD>0 时才做多）+ 信号连续性确认
  COMBO:  多因子组合（EMA趋势 + RSI回调 + ATR风控）

用法：
    source .venv/bin/activate && python example/strategy_optimize.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, BacktestResult
from engine.indicators import macd, ema, rsi, bollinger, atr


# ════════════════════════════════════════════════════════
# EMA v2: 双均线 + ATR动态止损 + EMA200趋势过滤
# ════════════════════════════════════════════════════════


class EmaV2Config(StrategyConfig):
    fast: int = 20
    slow: int = 50
    trend: int = 100  # 长期趋势均线
    atr_period: int = 14
    atr_stop_mult: float = 3.0  # 止损倍率


class EmaV2Strategy(BaseStrategy):
    """EMA双均线优化版：EMA200过滤只做多 + ATR动态止损"""

    strategy_type = "ema_v2"

    def __init__(self, c: EmaV2Config):
        super().__init__(c)
        self.cfg = c
        self._closes: list[float] = []
        self._highs: list[float] = []
        self._lows: list[float] = []
        self._entry_price: float = 0.0
        self._highest_since_entry: float = 0.0
        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.slow + 5:
            return None

        fast = ema(self._closes, self.cfg.fast)
        slow = ema(self._closes, self.cfg.slow)
        trd = ema(self._closes, self.cfg.trend)
        atr_vals = atr(self._highs, self._lows, self._closes, self.cfg.atr_period)

        cur_f, cur_s, cur_trd, cur_atr = fast[-1], slow[-1], trd[-1], atr_vals[-1]
        if cur_f == 0 or cur_s == 0 or cur_atr == 0:
            return None

        is_bull_market = cur_trd > 0 and k.close > cur_trd

        # ── 出场：ATR 动态止损 或 EMA死叉 ──
        if self._in_position:
            self._highest_since_entry = max(self._highest_since_entry, k.high)
            stop_price = self._highest_since_entry - self.cfg.atr_stop_mult * cur_atr
            death_cross = fast[-2] >= slow[-2] and cur_f < cur_s

            if k.close < stop_price or death_cross:
                self._in_position = False
                reason = f"ATR止损" if k.close < stop_price else "EMA死叉"
                return Signal(symbol=self.cfg.symbol, side="SELL", reason=reason, timestamp=k.open_time)

        # ── 入场：EMA金叉 + 多头趋势 ──
        if not self._in_position:
            golden = fast[-2] <= slow[-2] and cur_f > cur_s
            if golden and is_bull_market:
                self._in_position = True
                self._entry_price = k.close
                self._highest_since_entry = k.close
                return Signal(symbol=self.cfg.symbol, side="BUY",
                              reason=f"EMA金叉+多头 P={k.close:.0f}>EMA{self.cfg.trend}={cur_trd:.0f}",
                              timestamp=k.open_time)

        return None


# ════════════════════════════════════════════════════════
# RSI v2: 趋势过滤 + 放宽入场
# ════════════════════════════════════════════════════════


class RsiV2Config(StrategyConfig):
    period: int = 14
    entry_rsi: float = 40.0   # 放宽入场（原 30）
    exit_rsi: float = 75.0    # 放宽出场（原 70）
    trend_ema: int = 50        # 趋势过滤


class RsiV2Strategy(BaseStrategy):
    """RSI优化版：EMA50只做多 + RSI<40入场 + RSI>75出场"""

    strategy_type = "rsi_v2"

    def __init__(self, c: RsiV2Config):
        super().__init__(c)
        self.cfg = c
        self._closes: list[float] = []
        self._in_position = False

    async def on_kline(self, k: Kline) -> Optional[Signal]:
        self._closes.append(k.close)
        n = len(self._closes)
        if n < self.cfg.trend_ema + 5:
            return None

        vals = rsi(self._closes, self.cfg.period)
        trd = ema(self._closes, self.cfg.trend_ema)
        v, cur_trd = vals[-1], trd[-1]
        if v == 0 or cur_trd == 0:
            return None

        is_bull = k.close > cur_trd

        if self._in_position:
            if v > self.cfg.exit_rsi or not is_bull:
                self._in_position = False
                reason = f"RSI过热({v:.0f})" if v > self.cfg.exit_rsi else f"跌破EMA{self.cfg.trend_ema}"
                return Signal(symbol=self.cfg.symbol, side="SELL", reason=reason, timestamp=k.open_time)

        if not self._in_position:
            if v < self.cfg.entry_rsi and is_bull:
                self._in_position = True
                return Signal(symbol=self.cfg.symbol, side="BUY",
                              reason=f"RSI回调({v:.0f}) 多头确认 P>{cur_trd:.0f}",
                              timestamp=k.open_time)

        return None


# ════════════════════════════════════════════════════════
# MACD v2: 零轴过滤 + 信号线确认
# ════════════════════════════════════════════════════════


class MacdV2Config(StrategyConfig):
    fast: int = 12
    slow: int = 26
    signal: int = 9


class MacdV2Strategy(BaseStrategy):
    """MACD优化版：只做MACD>0时的金叉，过滤零轴下方假信号"""

    strategy_type = "macd_v2"

    def __init__(self, c: MacdV2Config):
        super().__init__(c)
        self.cfg = c
        self._closes: list[float] = []

    async def on_kline(self, k: Kline) -> Optional[Signal]:
        self._closes.append(k.close)
        mline, sline, _ = macd(self._closes, self.cfg.fast, self.cfg.slow, self.cfg.signal)
        if len(mline) < 4:
            return None
        cur_m, cur_s = mline[-1], sline[-1]
        prev_m, prev_s = mline[-2], sline[-2]
        if cur_m == 0:
            return None

        # 金叉 + MACD线在零轴上方（多头确认）→ 买入
        golden = prev_m <= prev_s and cur_m > cur_s
        if golden and cur_m > 0:
            return Signal(symbol=self.cfg.symbol, side="BUY",
                          reason=f"零轴上金叉 MACD={cur_m:.1f}", timestamp=k.open_time)

        # 死叉 → 卖出
        death = prev_m >= prev_s and cur_m < cur_s
        if death:
            return Signal(symbol=self.cfg.symbol, side="SELL",
                          reason=f"MACD死叉", timestamp=k.open_time)

        return None


# ════════════════════════════════════════════════════════
# COMBO: 多因子组合
# ════════════════════════════════════════════════════════


class ComboConfig(StrategyConfig):
    ema_trend: int = 50     # 趋势过滤
    rsi_period: int = 14
    rsi_entry: float = 45.0
    rsi_exit: float = 72.0


class ComboStrategy(BaseStrategy):
    """多因子组合：EMA50趋势 + RSI入场 + 趋势反转出场"""

    strategy_type = "combo"

    def __init__(self, c: ComboConfig):
        super().__init__(c)
        self.cfg = c
        self._closes: list[float] = []
        self._in_position = False

    async def on_kline(self, k: Kline) -> Optional[Signal]:
        self._closes.append(k.close)
        n = len(self._closes)
        if n < self.cfg.ema_trend + 5:
            return None

        vals = rsi(self._closes, self.cfg.rsi_period)
        trd = ema(self._closes, self.cfg.ema_trend)
        v, cur_trd, prev_trd = vals[-1], trd[-1], trd[-2]
        if v == 0 or cur_trd == 0:
            return None

        trend_up = cur_trd > prev_trd  # EMA上行
        price_above_trend = k.close > cur_trd

        if self._in_position:
            if v > self.cfg.rsi_exit or not price_above_trend:
                self._in_position = False
                reason = f"RSI过热{v:.0f}" if v > self.cfg.rsi_exit else "趋势转弱"
                return Signal(symbol=self.cfg.symbol, side="SELL", reason=reason, timestamp=k.open_time)

        if not self._in_position:
            if v < self.cfg.rsi_entry and trend_up and price_above_trend:
                self._in_position = True
                return Signal(symbol=self.cfg.symbol, side="BUY",
                              reason=f"多头共振 RSI={v:.0f} EMA↑ P>{cur_trd:.0f}",
                              timestamp=k.open_time)

        return None


# ════════════════════════════════════════════════════════
# 运行
# ════════════════════════════════════════════════════════

OPT_STRATEGIES = [
    ("EMA v2 趋势+止损", EmaV2Strategy, EmaV2Config()),
    ("RSI v2 趋势过滤", RsiV2Strategy, RsiV2Config()),
    ("MACD v2 零轴过滤", MacdV2Strategy, MacdV2Config()),
    ("COMBO 多因子", ComboStrategy, ComboConfig()),
]

SYMBOLS = ["BTCUSDT", "ETHUSDT", "BNBUSDT", "SOLUSDT"]

# 原始策略结果（从上一次运行提取，用于对比）
ORIGINAL = {
    ("BTCUSDT", "EMA双均线"):       (45.5, 0.74, -31.6, 42, 26.2),
    ("BTCUSDT", "RSI超卖反弹"):     (45.4, 0.74, -26.0, 20, 70.0),
    ("BTCUSDT", "MACD金叉死叉"):    (-21.3, -0.16, -41.7, 169, 32.5),
    ("ETHUSDT", "EMA双均线"):       (24.4, 0.47, -54.8, 41, 24.4),
    ("ETHUSDT", "RSI超卖反弹"):     (-42.8, -0.28, -66.1, 18, 61.1),
    ("ETHUSDT", "MACD金叉死叉"):    (47.6, 0.64, -41.5, 162, 34.0),
    ("BNBUSDT", "EMA双均线"):       (52.0, 0.71, -39.8, 41, 39.0),
    ("BNBUSDT", "RSI超卖反弹"):     (67.4, 0.93, -34.2, 18, 77.8),
    ("BNBUSDT", "MACD金叉死叉"):    (4.4, 0.24, -38.1, 177, 35.0),
    ("SOLUSDT", "EMA双均线"):       (27.8, 0.49, -39.5, 45, 40.0),
    ("SOLUSDT", "RSI超卖反弹"):     (-5.3, 0.24, -42.8, 16, 56.2),
    ("SOLUSDT", "MACD金叉死叉"):    (-15.9, 0.17, -58.6, 169, 34.9),
}


async def run_one(symbol, s_name, s_cls, s_cfg):
    bt = BacktestConfig(
        symbol=symbol, interval="4h",
        start_time=datetime(2024, 1, 1), end_time=datetime(2026, 1, 1),
        initial_capital=10_000.0,
    )
    s_cfg.symbol = symbol
    s_cfg.name = f"{s_name}_{symbol}"
    engine = BacktestEngine(bt, db_config=config.db)
    return await engine.run(s_cls, s_cfg)


async def main():
    print()
    print("═" * 115)
    print("  策略优化对比 — 原始 vs 优化版 | 4h 周期 | 2024-2026")
    print("═" * 115)

    opt_results: dict[tuple[str, str], BacktestResult] = {}
    for symbol in SYMBOLS:
        for s_name, s_cls, s_cfg in OPT_STRATEGIES:
            cfg = s_cfg.model_copy()
            r = await run_one(symbol, s_name, s_cls, cfg)
            opt_results[(symbol, s_name)] = r

    # ── 打印对比表 ──
    print()
    print(f"  {'币种':<10} {'策略':<20} {'类型':<10} {'收益%':>7} {'夏普':>6} {'回撤%':>7} {'交易':>5} {'胜率%':>6}  Δ收益")
    print("─" * 115)

    mapping = {
        "EMA v2 趋势+止损": "EMA双均线",
        "RSI v2 趋势过滤": "RSI超卖反弹",
        "MACD v2 零轴过滤": "MACD金叉死叉",
    }

    for symbol in SYMBOLS:
        for opt_name, orig_name in mapping.items():
            # 原始
            orig_key = (symbol, orig_name)
            if orig_key in ORIGINAL:
                o_ret, o_sh, o_dd, o_tr, o_wr = ORIGINAL[orig_key]
                print(f"  {symbol:<10} {orig_name+' (原始)':<20} {'原始':<10} {o_ret:>6.1f}% {o_sh:>6.2f} {o_dd:>6.1f}% {o_tr:>5} {o_wr:>5.1f}%")

            # 优化
            opt_key = (symbol, opt_name)
            if opt_key in opt_results:
                m = opt_results[opt_key].metrics
                delta = m.total_return_pct - o_ret if orig_key in ORIGINAL else 0
                print(f"  {symbol:<10} {opt_name+' (优化)':<20} {'优化':<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}%  {delta:+.1f}%")
            print()

        # COMBO
        combo_key = (symbol, "COMBO 多因子")
        if combo_key in opt_results:
            m = opt_results[combo_key].metrics
            print(f"  {symbol:<10} {'COMBO 多因子':<20} {'新增':<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}%")
            print()

    # ── 优化效果汇总 ──
    print("─" * 115)
    print("\n  ■ 优化效果汇总 (平均 Δ收益):")
    improvements = []
    for (symbol, opt_name), r in opt_results.items():
        orig_name = mapping.get(opt_name)
        if orig_name and (symbol, orig_name) in ORIGINAL:
            delta = r.metrics.total_return_pct - ORIGINAL[(symbol, orig_name)][0]
            improvements.append((f"{symbol} {opt_name}", delta, r.metrics.sharpe_ratio))
    improvements.sort(key=lambda x: x[1], reverse=True)
    for name, delta, sh in improvements:
        print(f"    {name:<30} Δ收益={delta:+.1f}%  夏普={sh:.2f}")

    print("\n  ■ 最佳组合 TOP 5:")
    all_results = [(f"{s} {n}", r) for (s, n), r in opt_results.items()]
    all_results.sort(key=lambda x: x[1].metrics.sharpe_ratio, reverse=True)
    for i, (name, r) in enumerate(all_results[:5]):
        m = r.metrics
        print(f"    {i+1}. {name:<30} 夏普={m.sharpe_ratio:.2f}  收益={m.total_return_pct:+.1f}%  回撤={m.max_drawdown_pct:.1f}%  胜率={m.win_rate*100:.0f}%")

    print("\n═" * 115)


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