"""
多周期策略回测 — 4h 定趋势，30m 找买点

策略逻辑：
  1. 4h EMA20 判断大趋势：价格 > EMA20 = 上升趋势
  2. 30m RSI 寻找入场时机：上升趋势中 RSI < 35 = 回调买入
  3. 出场：RSI > 70（超买）或 4h 趋势反转向下

用法：
    source .venv/bin/activate && python example/multi_tf_demo.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, DBConfig
from engine.backtest import BacktestEngine, BacktestConfig
from engine.data import DataService
from engine.indicators import ema, rsi


# ============================================================
# 多周期趋势回调策略
# ============================================================


class MultiTFConfig(StrategyConfig):
    """多周期策略配置"""

    # 4h 趋势参数
    trend_ema_period: int = 20

    # 30m 入场参数
    entry_rsi_period: int = 14
    entry_rsi_threshold: float = 35.0  # RSI 低于此值视为回调

    # 出场参数
    exit_rsi_threshold: float = 70.0   # RSI 高于此值出场

    # 数据范围（用于预加载 4h 数据）
    data_start: Optional[datetime] = None
    data_end: Optional[datetime] = None


class MultiTimeframeStrategy(BaseStrategy):
    """多周期趋势回调策略

            ┌─────────────────────────────┐
            │  4h K 线 → EMA20 判断趋势    │
            │  Price > EMA20 = 上升趋势    │
            └─────────────┬───────────────┘
                          │ 上升趋势
                          ▼
            ┌─────────────────────────────┐
            │  30m K 线 → 寻找入场时机      │
            │  RSI < 35 = 回调买入          │
            └─────────────┬───────────────┘
                          │ 持仓中
                          ▼
            ┌─────────────────────────────┐
            │  出场条件                     │
            │  RSI > 70 或 4h 趋势反转      │
            └─────────────────────────────┘
    """

    strategy_type = "multi_tf"

    def __init__(self, config: MultiTFConfig):
        super().__init__(config)
        self.cfg: MultiTFConfig = config

        # 4h 数据（在 on_start 中加载）
        self._klines_4h: list[Kline] = []
        self._ema_4h: list[float] = []

        # 30m 数据积累
        self._closes_30m: list[float] = []

        # 持仓状态
        self._has_position: bool = False

    async def on_start(self) -> None:
        """预加载 4h K 线数据并计算 EMA"""
        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,
            )
            closes_4h = [k.close for k in self._klines_4h]
            self._ema_4h = ema(closes_4h, self.cfg.trend_ema_period)
        finally:
            await ds.close()

        await super().on_start()

    def _get_4h_trend(self, ts: float) -> tuple[bool, float, float]:
        """获取指定时间戳对应的 4h 趋势

        只使用已完成的 4h K 线（close_time <= ts），避免前视偏差。

        Returns:
            (is_uptrend, price, ema_value)
        """
        if not self._klines_4h:
            return False, 0.0, 0.0

        # 从后往前找最近已完成的 4h bar
        for i in range(len(self._klines_4h) - 1, -1, -1):
            if self._klines_4h[i].close_time <= ts:
                price = self._klines_4h[i].close
                ema_val = self._ema_4h[i]
                if ema_val == 0.0:
                    return False, price, ema_val
                return price > ema_val, price, ema_val

        return False, 0.0, 0.0

    async def on_kline(self, kline: Kline) -> Optional[Signal]:
        self._closes_30m.append(kline.close)

        # ── 获取 4h 趋势 ──
        is_uptrend, price_4h, ema_4h = self._get_4h_trend(kline.open_time)

        # ── 计算 30m RSI ──
        rsi_vals = rsi(self._closes_30m, self.cfg.entry_rsi_period)
        cur_rsi = rsi_vals[-1]

        if cur_rsi == 0.0:
            return None

        # ── 出场逻辑 ──
        if self._has_position:
            # 4h 趋势反转（价格跌破 EMA）→ 止损出场
            if not is_uptrend and price_4h > 0:
                self._has_position = False
                return Signal(
                    symbol=self.cfg.symbol,
                    side="SELL",
                    signal_type="MARKET",
                    confidence=0.9,
                    reason=f"4h趋势反转 Price={price_4h:.2f}<EMA={ema_4h:.2f}",
                    timestamp=kline.open_time,
                )

            # 30m RSI 过热 → 止盈出场
            if cur_rsi > self.cfg.exit_rsi_threshold:
                self._has_position = False
                return Signal(
                    symbol=self.cfg.symbol,
                    side="SELL",
                    signal_type="MARKET",
                    confidence=0.8,
                    reason=f"30m RSI过热 {cur_rsi:.1f}>{self.cfg.exit_rsi_threshold}",
                    timestamp=kline.open_time,
                )

        # ── 入场逻辑 ──
        if not self._has_position:
            # 条件1：4h 上升趋势
            if not is_uptrend:
                return None

            # 条件2：30m RSI 回调到超卖区
            if cur_rsi < self.cfg.entry_rsi_threshold:
                self._has_position = True
                return Signal(
                    symbol=self.cfg.symbol,
                    side="BUY",
                    signal_type="MARKET",
                    confidence=0.7,
                    reason=(
                        f"4h升势回调买入 | "
                        f"4hPrice={price_4h:.0f}>EMA={ema_4h:.0f} | "
                        f"30mRSI={cur_rsi:.1f}"
                    ),
                    timestamp=kline.open_time,
                )

        return None


# ============================================================
# 主函数
# ============================================================


async def main():
    bt_config = BacktestConfig(
        symbol="ETHUSDT",
        interval="30m",
        start_time=datetime(2024, 1, 1),
        end_time=datetime(2026, 1, 1),
        initial_capital=10_000.0,
        commission_pct=0.001,
        slippage_pct=0.0005,
        warmup_bars=100,
    )

    strategy_config = MultiTFConfig(
        name="multi_tf_eth",
        symbol="ETHUSDT",
        trend_ema_period=20,
        entry_rsi_period=14,
        entry_rsi_threshold=35.0,
        exit_rsi_threshold=70.0,
        data_start=bt_config.start_time,
        data_end=bt_config.end_time,
    )

    print()
    print("╔" + "═" * 60 + "╗")
    print("║" + "  多周期策略 — 4h 定趋势 / 30m 找买点".center(54) + "║")
    print("╠" + "═" * 60 + "╣")
    print(f"║  {'交易对:':<8} {bt_config.symbol:<14} {'周期:':<6} {bt_config.interval:<12}  ║")
    print(f"║  {'时间:':<8} {bt_config.start_time.date()} ~ {bt_config.end_time.date()}         ║")
    print("╠" + "═" * 60 + "╣")
    print("║  策略逻辑:                                          ║")
    print(f"║    4h EMA{strategy_config.trend_ema_period} → 判断趋势方向                          ║")
    print(f"║    30m RSI{strategy_config.entry_rsi_period} < {strategy_config.entry_rsi_threshold} → 回调买入                        ║")
    print(f"║    30m RSI{strategy_config.entry_rsi_period} > {strategy_config.exit_rsi_threshold} → 止盈 / 4h趋势反转 → 止损      ║")
    print("╚" + "═" * 60 + "╝")
    print()

    engine = BacktestEngine(bt_config, db_config=config.db)
    result = await engine.run(MultiTimeframeStrategy, strategy_config)

    print(result.summary())

    # 打印最近交易
    sells = [t for t in result.trades if t.pnl is not None]
    if sells:
        print(f"\n最近 10 笔平仓交易:")
        print(f"{'时间':<22} {'方向':<6} {'价格':>10} {'盈亏':>10}  原因")
        print("-" * 85)
        for t in sells[-10:]:
            dt = datetime.fromtimestamp(t.timestamp / 1000, tz=timezone.utc).strftime("%Y-%m-%d %H:%M")
            print(f"{dt:<22} {t.side:<6} {t.price:>10.2f} {t.pnl:>+10.2f}  {t.reason}")

    print("\n回测完成。")


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