# source: https://raw.githubusercontent.com/Yohalloo/freqtrade/9ac43149e8f5c35fd0deb19d8ef7270443639caa/user_data/strategies/V5_3_Adapter.py
"""
V5.3 策略适配器（SafeLoader）

所有策略必须继承此适配器，统一接口规范。
"""
from typing import Dict, Optional, Tuple
from datetime import datetime
from pandas import DataFrame
from freqtrade.strategy import IStrategy
from freqtrade.persistence import Trade

import sys
import os
from pathlib import Path

# 添加 guardian 模块路径
# 获取 user_data 目录（策略文件所在目录的父目录）
user_data_dir = Path(__file__).parent.parent
guardian_path = user_data_dir / "guardian"

# 确保 guardian 目录在 Python 路径中
if str(user_data_dir) not in sys.path:
    sys.path.insert(0, str(user_data_dir))

# 尝试导入 guardian 模块
try:
    from guardian.allocator import Bucket
    from guardian.regime_fsm import Regime
except ImportError:
    # 如果导入失败，尝试添加绝对路径
    abs_user_data = user_data_dir.resolve()
    if str(abs_user_data) not in sys.path:
        sys.path.insert(0, str(abs_user_data))
    from guardian.allocator import Bucket
    from guardian.regime_fsm import Regime


class Github_Yohalloo_freqtrade__V5_3_Adapter__20251122_100212(IStrategy):
    """
    V5.3 策略适配器基类
    
    职责：
    1. 统一策略接口（信号 + SL）
    2. Regime Whitelist 检查
    3. 与 Guardian 系统集成
    4. Backtest 模式支持
    """
    
    INTERFACE_VERSION = 3
    
    # === 必须由子类声明的属性 ===
    # 策略桶分类
    bucket: Bucket = Bucket.TREND
    
    # 允许运行的 Regime 列表
    allowed_regimes: list = [Regime.SIDEWAYS]  # 默认仅允许震荡市
    
    # 单次请求风险（R）
    r_request: float = 0.25  # 默认 0.25R
    
    # 策略名称（用于日志）
    strategy_name: str = "Unknown"
    
    # === 可选配置 ===
    # 最小置信度（0.0-1.0）
    min_confidence: float = 0.5
    
    def __init__(self, config: dict) -> None:
        super().__init__(config)
        
        # Guardian 系统引用（由外部注入）
        self.guardian = None
        
        # Backtest 模式标志
        self.is_backtest = config.get("runmode") in ["backtest", "hyperopt"]
        
        # 启动检查
        if not self.is_backtest:
            self._startup_check()
    
    def _startup_check(self):
        """启动时检查配置"""
        assert self.bucket in Bucket, f"策略 {self.strategy_name} 必须声明有效的 bucket"
        assert isinstance(self.allowed_regimes, list) and len(self.allowed_regimes) > 0, \
            f"策略 {self.strategy_name} 必须声明 allowed_regimes"
        assert 0 < self.r_request <= 0.5, \
            f"策略 {self.strategy_name} 的 r_request 必须在 (0, 0.5] 范围内"
    
    def set_guardian(self, guardian):
        """设置 Guardian 系统引用（由外部调用）"""
        self.guardian = guardian
    
    def _check_regime_allowed(self, current_regime: Optional[Regime]) -> bool:
        """检查当前 Regime 是否允许运行"""
        if current_regime is None:
            return False
        return current_regime in self.allowed_regimes
    
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        子类必须实现此方法，计算指标
        """
        raise NotImplementedError("子类必须实现 populate_indicators")
    
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        子类必须实现此方法，输出信号
        
        输出格式：
        - enter_long / enter_short: 1 或 0
        - enter_tag: "StrategyName|RegimeAtEntry"
        - proposed_sl_price: 建议的止损价格（可选，用于计算 SL%）
        - confidence: 0.0-1.0（可选）
        """
        raise NotImplementedError("子类必须实现 populate_entry_trend")
    
    def custom_stake_amount(
        self,
        pair: str,
        current_time: datetime,
        current_rate: float,
        proposed_stake: float,
        min_stake: Optional[float],
        max_stake: float,
        leverage: float,
        entry_tag: Optional[str],
        side: str,
        **kwargs,
    ) -> float:
        """
        自定义仓位大小（与 Guardian 集成）
        
        注意：在 Backtest 模式下，使用简化逻辑
        """
        if self.is_backtest:
            # Backtest 模式：使用默认逻辑或简化版本
            return proposed_stake
        
        if self.guardian is None:
            # Guardian 未初始化，使用默认
            return proposed_stake
        
        # 获取策略信号中的 SL 信息
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        try:
            df = dataframe.loc[dataframe["date"] <= current_time]
            last = df.iloc[-1] if not df.empty else dataframe.iloc[-1]
        except Exception:
            last = dataframe.iloc[-1]
        
        # 提取 proposed_sl_price（如果存在）
        proposed_sl_price = last.get("proposed_sl_price")
        if proposed_sl_price is None or proposed_sl_price == 0:
            # 使用默认 SL（策略的 stoploss）
            strategy_sl_pct = abs(self.stoploss) if self.stoploss < 0 else 0.02
        else:
            # 计算 SL%
            if side == "long":
                strategy_sl_pct = abs((proposed_sl_price - current_rate) / current_rate)
            else:  # short
                strategy_sl_pct = abs((current_rate - proposed_sl_price) / current_rate)
        
        # 提取 confidence
        confidence = last.get("confidence", 1.0)
        if confidence is None:
            confidence = 1.0
        
        # 调用 Guardian 检查
        direction = "LONG" if side == "long" else "SHORT"
        result = self.guardian.check_trade_request(
            pair=pair,
            direction=direction,
            strategy_name=self.strategy_name,
            bucket=self.bucket,
            r_request=self.r_request,
            strategy_sl_pct=strategy_sl_pct,
            confidence=confidence,
        )
        
        if not result["allowed"]:
            # 不允许开仓，返回 0
            # 记录 reason_code（可通过日志系统）
            return 0.0
        
        # 使用 Guardian 计算的仓位大小
        position_value = result["position_value"]
        
        # 转换为 freqtrade 的 stake_amount（考虑杠杆）
        stake_amount = position_value / leverage if leverage > 0 else position_value
        
        return min(stake_amount, max_stake)
    
    def adjust_trade_position(
        self,
        trade: Trade,
        current_time: datetime,
        current_rate: float,
        current_profit: float,
        min_stake: Optional[float],
        max_stake: float,
        current_entry_rate: float,
        current_exit_rate: float,
        current_entry_profit: float,
        current_exit_profit: float,
        **kwargs,
    ) -> Optional[float]:
        """
        禁止策略自行加仓（由 Guardian 统一管理）
        """
        return None
    
    def confirm_trade_entry(
        self,
        pair: str,
        order_type: str,
        amount: float,
        rate: float,
        time_in_force: str,
        current_time: datetime,
        entry_tag: Optional[str],
        side: str,
        **kwargs,
    ) -> bool:
        """
        确认开仓（记录到 Guardian）
        """
        if not self.is_backtest and self.guardian:
            # 获取实际分配的仓位信息（需要从 custom_stake_amount 的结果中获取）
            # 简化：这里只做确认，实际记录在 custom_stake_amount 中完成
            pass
        
        return True
    
    def confirm_trade_exit(
        self,
        pair: str,
        trade: Trade,
        order_type: str,
        amount: float,
        rate: float,
        time_in_force: str,
        exit_reason: str,
        current_time: datetime,
        **kwargs,
    ) -> bool:
        """
        确认平仓（记录到 Guardian）
        """
        if not self.is_backtest and self.guardian:
            # 计算盈亏百分比
            profit_pct = trade.calc_profit_ratio(rate)
            
            # 记录到 Guardian
            self.guardian.record_trade_closed(
                pair=pair,
                bucket=self.bucket,
                profit_pct=profit_pct,
            )
        
        return True

