"""
更多策略尝试 — 不限于趋势跟踪

新策略：
  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}<MA={cur_ma:.0f}",
                              timestamp=k.open_time)

        return None


# ════════════════════════════════════════════════════════
# 策略 3：多TF动量共振 (1h MACD + 4h EMA)
# ════════════════════════════════════════════════════════


class MultiTFConfig(StrategyConfig):
    ema_4h: int = 50
    macd_fast: int = 12
    macd_slow: int = 26
    macd_signal: int = 9
    atr_stop: float = 2.5
    data_start: Optional[datetime] = None
    data_end: Optional[datetime] = None


class MultiTFStrategy(BaseStrategy):
    """1h MACD金叉 + 4h EMA多头 → 共振做多"""

    strategy_type = "multi_tf_momentum"

    def __init__(self, c: MultiTFConfig):
        super().__init__(c)
        self.cfg = c
        self._klines_4h: list[Kline] = []
        self._closes_1h: list[float] = []
        self._highs_1h: list[float] = []
        self._lows_1h: list[float] = []
        self._highest: float = 0.0
        self._in_position = False

    async def on_start(self):
        from engine.common.config import config as app_config
        ds = DataService(app_config.db)
        await ds.connect()
        try:
            self._klines_4h = await ds.fetch_klines(
                symbol=self.cfg.symbol, interval="4h",
                start_time=self.cfg.data_start, end_time=self.cfg.data_end, limit=1_000_000,
            )
        finally:
            await ds.close()
        await super().on_start()

    def _is_4h_bull(self, ts: float) -> 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())
