"""
极简策略测试 — 一条均线 + 一个止损

核心理念：多加过滤条件往往不如简洁的信号。

策略：价格上穿 EMA(N) → 买入，价格下穿 EMA(N) → 卖出，ATR 动态止损。
      无成交量、无多周期、无ADX、无双均线交叉。

对比 N=10/20/30，4个币种，4h周期，2024-2026。

用法：
    source .venv/bin/activate && python example/strategy_simple.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 ema, atr


class SingleEMAConfig(StrategyConfig):
    period: int = 20
    atr_stop: float = 2.5


class SingleEMAStrategy(BaseStrategy):
    """一条EMA均线 + ATR止损。没有更多了。"""

    strategy_type = "single_ema"

    def __init__(self, c: SingleEMAConfig):
        super().__init__(c)
        self.cfg = c
        self._closes: list[float] = []
        self._highs: list[float] = []
        self._lows: list[float] = []
        self._highest: 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.period + 5:
            return None

        ema_vals = ema(self._closes, self.cfg.period)
        atr_vals = atr(self._highs, self._lows, self._closes, 14)
        cur_ema, cur_atr = ema_vals[-1], atr_vals[-1]
        prev_ema = ema_vals[-2]
        if cur_ema == 0 or cur_atr == 0:
            return None

        # ── 出场 ──
        if self._in_position:
            self._highest = max(self._highest, k.high)
            stop = self._highest - self.cfg.atr_stop * cur_atr
            cross_down = self._closes[-2] >= prev_ema and k.close < cur_ema

            if k.close < stop:
                self._in_position = False
                return Signal(symbol=self.cfg.symbol, side="SELL", reason="ATR止损", timestamp=k.open_time)
            if cross_down:
                self._in_position = False
                return Signal(symbol=self.cfg.symbol, side="SELL",
                              reason=f"下穿EMA{self.cfg.period}", timestamp=k.open_time)

        # ── 入场 ──
        if not self._in_position:
            cross_up = self._closes[-2] <= prev_ema and k.close > cur_ema
            if cross_up:
                self._in_position = True
                self._highest = k.close
                return Signal(symbol=self.cfg.symbol, side="BUY",
                              reason=f"上穿EMA{self.cfg.period}", timestamp=k.open_time)

        return None


# ════════════════════════════════════════════════════════

SYMBOLS = ["BTCUSDT", "ETHUSDT", "BNBUSDT", "SOLUSDT"]
PERIODS = [10, 20, 30]
DATE_START = datetime(2024, 1, 1)
DATE_END = datetime(2026, 1, 1)


async def main():
    print()
    print("═" * 105)
    print("  极简策略 — 一条 EMA + ATR 止损 | 4h | 2024-2026")
    print("═" * 105)
    print(f"  {'EMA':<8} {'币种':<10} {'收益%':>7} {'夏普':>6} {'回撤%':>7} {'交易':>5} {'胜率%':>6} {'盈亏比':>6}")
    print("─" * 105)

    all_rows = []
    for period in PERIODS:
        for symbol in SYMBOLS:
            sc = SingleEMAConfig(symbol=symbol, period=period)
            bt = BacktestConfig(symbol=symbol, interval="4h",
                                start_time=DATE_START, end_time=DATE_END, initial_capital=10_000.0)
            engine = BacktestEngine(bt, db_config=config.db)
            r = await engine.run(SingleEMAStrategy, sc)
            m = r.metrics
            all_rows.append((period, symbol, m))
            print(f"  EMA({period:<2})   {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  ■ 对比：极简 vs 之前最优 (EMA v3 双均线)")
    V3 = {
        "BTCUSDT":  (39.9, 1.03, -11.5),
        "ETHUSDT":  (53.6, 1.04, -15.3),
        "BNBUSDT":  (26.0, 0.64, -23.4),
        "SOLUSDT":  (73.6, 1.18, -25.7),
    }
    print(f"  {'币种':<10} {'策略':<20} {'收益%':>7} {'夏普':>6} {'回撤%':>7}")
    for symbol in SYMBOLS:
        v3 = V3[symbol]
        print(f"  {symbol:<10} {'EMA v3(最优参数)':<20} {v3[0]:>6.1f}% {v3[1]:>6.2f} {v3[2]:>6.1f}%")
        # 找极简最佳
        best = max([(p, m) for p, s, m in all_rows if s == symbol], key=lambda x: x[1].sharpe_ratio)
        print(f"  {'':<10} {'单EMA('+str(best[0])+') 极简':<20} {best[1].total_return_pct:>6.1f}% {best[1].sharpe_ratio:>6.2f} {best[1].max_drawdown_pct:>6.1f}%")
        print()

    # ── 汇总 ──
    print("─" * 105)
    ranked = sorted(all_rows, key=lambda x: x[2].sharpe_ratio, reverse=True)
    print("  ■ 按夏普 TOP 5:")
    for i, (p, s, m) in enumerate(ranked[:5]):
        print(f"  {i+1}. {s} EMA({p:<2})  夏普={m.sharpe_ratio:.2f}  收益={m.total_return_pct:+.1f}%  回撤={m.max_drawdown_pct:.1f}%  胜率={m.win_rate*100:.0f}%  交易={m.total_trades}")

    avg_sh = sum(x[2].sharpe_ratio for x in all_rows) / len(all_rows)
    print(f"\n  12组平均夏普: {avg_sh:.2f}")

    print("\n═" * 105)


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