"""
同周期三EMA策略 — 4h EMA50>200 定方向 + EMA20金叉EMA50入场

所有信号在同一周期(4h)上，不跨级。
"""

import asyncio
import sys
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.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 ThreeEMAConfig(StrategyConfig):
    ema_entry: int = 20     # 入场均线（金叉慢线时入场）
    ema_trend: int = 50     # 趋势均线（在200上方=多头）
    ema_filter: int = 200   # 长期过滤（50必须在其上）
    atr_stop: float = 2.5


class ThreeEMAStrategy(BaseStrategy):
    """三EMA同周期策略

    EMA200 长期方向 → EMA50>200 才做多
    EMA20 金叉 EMA50 → 入场
    EMA20 死叉 EMA50 或 EMA50<200 → 出场
    ATR 动态止损
    """

    strategy_type = "three_ema"

    def __init__(self, c: ThreeEMAConfig):
        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.ema_filter + 10:
            return None

        # 三条EMA
        e20 = ema(self._closes, self.cfg.ema_entry)
        e50 = ema(self._closes, self.cfg.ema_trend)
        e200 = ema(self._closes, self.cfg.ema_filter)
        atr_vals = atr(self._highs, self._lows, self._closes, 14)

        # 当前值和前值
        c20, p20 = e20[-1], e20[-2]
        c50, p50 = e50[-1], e50[-2]
        c200 = e200[-1]
        cur_atr = atr_vals[-1]

        if c20 == 0 or c50 == 0 or c200 == 0 or cur_atr == 0:
            return None

        is_bull = c50 > c200  # EMA50在200上方=多头市场
        golden = p20 <= p50 and c20 > p50  # 金叉
        death = p20 >= p50 and c20 < p50   # 死叉

        # ── 出场 ──
        if self._in_position:
            self._highest = max(self._highest, k.high)
            stop = self._highest - self.cfg.atr_stop * cur_atr

            if not is_bull:
                self._in_position = False
                return Signal(symbol=self.cfg.symbol, side="SELL", reason="EMA50<200转空", timestamp=k.open_time)
            if k.close < stop:
                self._in_position = False
                return Signal(symbol=self.cfg.symbol, side="SELL", reason="ATR止损", timestamp=k.open_time)
            if death:
                self._in_position = False
                return Signal(symbol=self.cfg.symbol, side="SELL", reason="EMA20死叉50", timestamp=k.open_time)

        # ── 入场 ──
        if not self._in_position and is_bull and golden:
            self._in_position = True
            self._highest = k.close
            return Signal(symbol=self.cfg.symbol, side="BUY",
                          reason=f"EMA20金叉50 多头确认", timestamp=k.open_time)

        return None


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

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

# 之前最优对比
BEST = {
    "BTCUSDT": ("EMA v3(10,50)",     39.9, 1.03, -11.5, 20),
    "ETHUSDT": ("EMA v3(10,75)",     53.6, 1.04, -15.3, 18),
    "BNBUSDT": ("EMA v1(20,50)",     52.0, 0.71, -39.8, 41),
    "SOLUSDT": ("EMA v3(30,50)",     73.6, 1.18, -25.7, 13),
}


async def main():
    print()
    print("═" * 105)
    print("  三EMA同周期 — 4h EMA200定势 / EMA20×50交易 | 2024-2026")
    print("═" * 105)
    print(f"  {'币种':<10} {'收益%':>7} {'夏普':>6} {'回撤%':>7} {'交易':>5} {'胜率%':>6} {'盈亏比':>6}  {'vs最优':>8}")
    print("─" * 105)

    results = {}
    for symbol in SYMBOLS:
        sc = ThreeEMAConfig(symbol=symbol)
        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(ThreeEMAStrategy, sc)
        m = r.metrics
        results[symbol] = m

        _, best_ret, best_sh, _, _ = BEST[symbol]
        delta = m.total_return_pct - best_ret
        tag = " ← 新最佳!" if m.sharpe_ratio > best_sh else ""

        print(f"  {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}  {delta:>+7.1f}%{tag}")

        sells = [t for t in r.trades if t.side == "SELL" and t.pnl is not None]
        for t in sells[-2:]:
            dt = datetime.fromtimestamp(t.timestamp / 1000, tz=timezone.utc).strftime("%m-%d %H:%M")
            print(f"  {'':<10}  └ {dt} {t.pnl:>+8.2f}  {t.reason}")

    print("─" * 105)
    print(f"\n  {'币种':<10} {'之前最优':<20} {'收益%':>7} {'夏普':>6}  →  {'三EMA收益%':>9} {'三EMA夏普':>8}")
    for symbol in SYMBOLS:
        name, ret, sh, _, _ = BEST[symbol]
        m = results[symbol]
        print(f"  {symbol:<10} {name:<20} {ret:>6.1f}% {sh:>6.2f}  →  {m.total_return_pct:>8.1f}% {m.sharpe_ratio:>7.2f}")

    print("\n═" * 105)


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