"""
全周期回测 — 2017-2026，覆盖完整牛熊

多空双向 EMA 趋势跟踪，展示牛市/熊市/全周期分段表现。

用法：
    source .venv/bin/activate && python example/full_cycle.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 BacktestConfig
from engine.indicators import ema, atr
from engine.example.long_short import LongShortEngine, LongShortEmaConfig, LongShortEmaStrategy


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

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

PARAMS = {
    "BTCUSDT": (10, 50),
    "ETHUSDT": (10, 75),
    "BNBUSDT": (20, 50),
    "SOLUSDT": (30, 50),
}

# 牛熊分段（以 BTC 为参考）
PERIODS = [
    ("2017-2018 牛市",  datetime(2017, 1, 1), datetime(2018, 1, 1)),
    ("2018 熊市",       datetime(2018, 1, 1), datetime(2019, 1, 1)),
    ("2019 反弹",       datetime(2019, 1, 1), datetime(2020, 1, 1)),
    ("2020 牛初+312",   datetime(2020, 1, 1), datetime(2021, 1, 1)),
    ("2021 牛市",       datetime(2021, 1, 1), datetime(2022, 1, 1)),
    ("2022 熊市",       datetime(2022, 1, 1), datetime(2023, 1, 1)),
    ("2023 复苏",       datetime(2023, 1, 1), datetime(2024, 1, 1)),
    ("2024-2025 牛市",  datetime(2024, 1, 1), datetime(2026, 1, 1)),
]

DATE_START = datetime(2017, 1, 1)
DATE_END = datetime(2026, 1, 1)


async def run_backtest(symbol, fast, slow, start, end):
    sc = LongShortEmaConfig(symbol=symbol, fast=fast, slow=slow)
    bt = BacktestConfig(symbol=symbol, interval="4h", start_time=start, end_time=end,
                        initial_capital=10_000.0)
    engine = LongShortEngine(bt, db_config=config.db)
    return await engine.run(LongShortEmaStrategy, sc)


async def main():
    print()
    print("═" * 125)
    print("  全周期多空回测 — 2017-2026 完整牛熊 | 4h EMA趋势")
    print("═" * 125)

    # ── 全周期汇总 ──
    print(f"\n  ■ 全周期 2017-2026 汇总")
    print(f"  {'币种':<10} {'总收益%':>7} {'年化%':>7} {'夏普':>6} {'回撤%':>7} {'交易':>5} {'多头P&L':>10} {'空头P&L':>10}")
    print("  " + "─" * 105)

    for symbol in SYMBOLS:
        fast, slow = PARAMS[symbol]
        r = await run_backtest(symbol, fast, slow, DATE_START, DATE_END)
        m = r.metrics
        long_t = [t for t in r.trades if t.pnl is not None and t.side == "SELL"]
        short_t = [t for t in r.trades if t.pnl is not None and t.side == "BUY"]
        long_pnl = sum(t.pnl for t in long_t) if long_t else 0
        short_pnl = sum(t.pnl for t in short_t) if short_t else 0
        print(f"  {symbol:<10} {m.total_return_pct:>6.1f}% {m.annual_return_pct:>6.1f}% {m.sharpe_ratio:>6.2f} {m.max_drawdown_pct:>6.1f}% {m.total_trades:>5} {long_pnl:>+9.0f} {short_pnl:>+9.0f}")

    # ── BTC 分段 ──
    print(f"\n  ■ BTC 各阶段表现 (参数 EMA{10},{50})")
    print(f"  {'阶段':<22} {'总收益%':>7} {'年化%':>7} {'夏普':>6} {'回撤%':>7} {'多头P&L':>10} {'空头P&L':>10}")
    print("  " + "─" * 105)

    for period_name, p_start, p_end in PERIODS:
        try:
            r = await run_backtest("BTCUSDT", 10, 50, p_start, p_end)
            m = r.metrics
            long_t = [t for t in r.trades if t.pnl is not None and t.side == "SELL"]
            short_t = [t for t in r.trades if t.pnl is not None and t.side == "BUY"]
            long_pnl = sum(t.pnl for t in long_t) if long_t else 0
            short_pnl = sum(t.pnl for t in short_t) if short_t else 0
            print(f"  {period_name:<22} {m.total_return_pct:>6.1f}% {m.annual_return_pct:>6.1f}% {m.sharpe_ratio:>6.2f} {m.max_drawdown_pct:>6.1f}% {long_pnl:>+9.0f} {short_pnl:>+9.0f}")
        except Exception as e:
            print(f"  {period_name:<22} 数据不足或错误: {e}")

    # ── 只做多 vs 多空全周期对比 ──
    print(f"\n  ■ BTC 只做多 vs 多空 (全周期)")
    # 只做多需要单跑一次（LongShortEngine 本身就支持只做多：不开空就行）
    # 简单做法：用原版 BacktestEngine 跑一次只做多
    from engine.backtest import BacktestEngine
    from engine.common.base import BaseStrategy as BS, Signal as Sig, StrategyConfig as SC

    class LongOnlyEMAConfig(SC):
        fast: int = 10; slow: int = 50; atr_stop: float = 2.5

    class LongOnlyEMAStrategy(BS):
        strategy_type = "long_only"
        def __init__(self, c): super().__init__(c); self.cfg = c
        async def on_start(self): self._c = []; self._h = []; self._l = []; self._hp = 0.0; self._in = False; await super().on_start()
        async def on_kline(self, k):
            self._c.append(k.close); self._h.append(k.high); self._l.append(k.low)
            n = len(self._c)
            if n < self.cfg.slow + 5: return None
            f = ema(self._c, self.cfg.fast); s = ema(self._c, self.cfg.slow)
            a = atr(self._h, self._l, self._c, 14)
            cf, cs, ca = f[-1], s[-1], a[-1]; pf, ps = f[-2], s[-2]
            if cf == 0 or cs == 0 or ca == 0: return None
            if self._in:
                self._hp = max(self._hp, k.high); stop = self._hp - self.cfg.atr_stop * ca
                if (pf >= ps and cf < cs) or k.close < stop:
                    self._in = False
                    return Sig(symbol=self.cfg.symbol, side="SELL", reason="死叉" if pf >= ps else "ATR止损", timestamp=k.open_time)
            else:
                if pf <= ps and cf > cs:
                    self._in = True; self._hp = k.close
                    return Sig(symbol=self.cfg.symbol, side="BUY", reason="金叉", timestamp=k.open_time)
            return None

    lo_sc = LongOnlyEMAConfig(symbol="BTCUSDT")
    lo_bt = BacktestConfig(symbol="BTCUSDT", interval="4h", start_time=DATE_START, end_time=DATE_END, initial_capital=10_000.0)
    lo_eng = BacktestEngine(lo_bt, db_config=config.db)
    lo_r = await lo_eng.run(LongOnlyEMAStrategy, lo_sc)
    lo_m = lo_r.metrics

    # 多空
    ls_r = await run_backtest("BTCUSDT", 10, 50, DATE_START, DATE_END)
    ls_m = ls_r.metrics

    print(f"  {'':<10} {'总收益%':>7} {'年化%':>7} {'夏普':>6} {'回撤%':>7} {'交易':>5}")
    print(f"  {'只做多':<10} {lo_m.total_return_pct:>6.1f}% {lo_m.annual_return_pct:>6.1f}% {lo_m.sharpe_ratio:>6.2f} {lo_m.max_drawdown_pct:>6.1f}% {lo_m.total_trades:>5}")
    print(f"  {'多空':<10} {ls_m.total_return_pct:>6.1f}% {ls_m.annual_return_pct:>6.1f}% {ls_m.sharpe_ratio:>6.2f} {ls_m.max_drawdown_pct:>6.1f}% {ls_m.total_trades:>5}")

    print("\n═" * 125)


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