#!/usr/bin/env python3
"""
SignalGenerator 基类
所有策略信号生成器必须继承此基类，实现标准接口
"""
from abc import ABC, abstractmethod
from typing import Any


class BaseSignalGenerator(ABC):
    """
    信号生成器基类

    实现规范：
      1. analyze() 只读 df（K线数据），不感知账户/持仓/配置
      2. 返回标准 Signal dict（带 schema_version + trace_id）
      3. get_params_schema() 供 Hyperopt 调用
    """

    SCHEMA_VERSION = "2.1.0"
    DEFAULT_RUNTIME_CONTRACT = {
        "strategy_style": "hybrid",
        "applicable_regimes": ["UNKNOWN"],
        "applicable_sessions": ["ALL"],
        "min_regime_confidence": 0.0,
        "countertrend_allowed": False,
        "entry_style_preference": "MARKET",
        "hard_veto_rules": [],
    }

    def __init__(self, params: dict = None):
        """初始化信号生成器，合并默认参数。子类可覆盖此方法扩展初始化逻辑。"""
        defaults = getattr(self, "DEFAULT_PARAMS", {})
        self.params = {**defaults} if isinstance(defaults, dict) else {}
        if isinstance(params, dict):
            self.params.update(params)

    @abstractmethod
    def analyze(self, bars: list, params: dict) -> dict:
        """
        分析市场并生成信号

        输入：
            bars: list[dict], K线数据
                  [{"time", "open", "high", "low", "close", "tick_volume"}, ...]
            params: dict, 策略参数
                  {
                      "ema_short": 9,
                      "ema_long": 21,
                      "ema50": 50,
                      "rsi_period": 14,
                      "macd_fast": 12,
                      "macd_slow": 26,
                      "atr_period": 14,
                      ...
                  }

        输出：标准 Signal dict
            {
                "schema_version": "2.1.0",
                "trace_id": str,
                "strategy_id": str,        # 例如 "ema_macd_v2"
                "model_version": None,     # 有ML时为模型版本，无则为None
                "timestamp": str,
                "payload": {
                    "action": "OPEN" | "CLOSE" | "HOLD",
                    "confidence": float,    # 0.0 ~ 1.0
                    "reason": str,
                    "indicators": dict,
                    # 以下字段仅在 action != "HOLD" 时有效
                    "side": "BUY" | "SELL",
                    "entry": float,
                    "sl": float,
                    "tp": float,
                }
            }
        """
        pass

    @abstractmethod
    def get_params_schema(self) -> dict:
        """
        返回参数定义，供 Hyperopt 调用

        输出：
            dict: 参数名 -> 参数定义
            {
                "ema_short": {"type": "int", "default": 9, "range": [5, 20]},
                "ema_long": {"type": "int", "default": 21, "range": [15, 50]},
                ...
            }
        """
        pass

    def get_strategy_id(self) -> str:
        """返回策略标识符，子类覆盖"""
        return self.__class__.__name__

    def get_runtime_contract(self) -> dict[str, Any]:
        """返回运行契约，供 pipeline 在生成前做硬准入。"""
        contract = dict(self.DEFAULT_RUNTIME_CONTRACT)
        strategy_id = self.get_strategy_id()
        contract["strategy_id"] = strategy_id
        return contract
