# source: https://raw.githubusercontent.com/AtomNotShy/factor_mining/ef4e87f4de0025745a849c3373a35064ba8f8351/src/strategies/base/interface.py
"""
IStrategy 接口定义

Freqtrade 风格的策略协议，定义所有策略必须实现的方法。
"""

from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from datetime import datetime
from typing import Dict, List, Optional, Any, Protocol, Type, TypeVar
import pandas as pd
import numpy as np

from src.core.types import (
    Signal, OrderIntent, Fill, PortfolioState, RiskState,
    MarketData, ActionType, OrderSide, OrderType,
)
from src.core.context import RunContext
from src.utils.logger import get_logger

logger = get_logger("strategy.interface")


# ============ 策略配置 ============

@dataclass
class SellType:
    """卖出原因类型"""
    ROI = "roi"           # 达到目标收益
    STOP_LOSS = "stop_loss"  # 止损
    TRAILING_STOP = "trailing_stop"  # 追踪止损
    TIMEOUT = "timeout"   # 超时
    CUSTOM = "custom"     # 自定义


@dataclass 
class BuyType:
    """买入原因类型"""
    LONG = "long"         # 正常做多
    SHORT = "short"       # 做空
    REBuy = "rebuy"       # 补仓


@dataclass
class StrategyConfig:
    """
    策略配置（类属性，自动从策略类读取）
    
    使用方式：
        class Github_AtomNotShy_factor_mining__interface__20260114_095947(IStrategy):
            strategy_name = "My Strategy"
            timeframe = "1d"
            stoploss = -0.05
            trailing_stop = False
            trailing_stop_positive = 0.02
            trailing_stop_positive_offset = 0.03
            startup_candle_count = 20
            order_types = {
                "entry": "limit",
                "exit": "limit", 
                "stoploss": "market",
                "stoploss_on_exchange": False,
            }
            position_adjustment_enable = False
            use_exit_signal = True
            exit_profit_only = False
            ignore_roi_if_entry_signal = False
    """
    # 策略标识
    strategy_name: str = ""
    strategy_id: str = ""  # 简短的策略ID，用于文件命名
    
    # 时间框架
    timeframe: str = "1d"
    startup_candle_count: int = 0  # 策略启动所需的最小K线数量
    
    # 止损配置
    stoploss: float = -0.10  # 止损比例（如 -0.05 表示亏损5%止损）
    trailing_stop: bool = False
    trailing_stop_positive: float = 0.0
    trailing_stop_positive_offset: float = 0.0
    trailing_only_offset_is_reached: bool = False
    trailing_stop_dry_run: bool = False  # 仅测试模式，不真正下单
    
    # 订单类型
    order_types: Dict[str, str] = field(default_factory=dict)  # {"entry": "limit", "exit": "limit", "stoploss": "market"}
    
    # 仓位配置
    position_size: float = 1.0  # 仓位比例 (0.0 - 1.0)
    stake_amount: Optional[float] = None  # 固定金额，如果为None则使用资金的比例
    default_stake_amount: float = 1000.0  # 默认每笔交易金额
    ask_last_balance: float = 0.0  # 保留现金比例
    
    # 止盈配置
    minimal_roi: Dict[str, float] = field(default_factory=dict)  # {"0": 0.05, "60": 0.03, "240": 0.02}
    
    # 信号配置
    use_exit_signal: bool = True
    exit_profit_only: bool = False
    ignore_roi_if_entry_signal: bool = False
    
    # 仓位调整
    position_adjustment_enable: bool = False
    
    # 交易时间段
    can_short: bool = False  # 是否支持做空
    use_book_value: bool = False
    
    # 保护配置
    ignore_protections: bool = False
    protections: List[Dict] = field(default_factory=list)
    
    # 白名单/黑名单
    whitelist: List[str] = field(default_factory=list)
    blacklist: List[str] = field(default_factory=list)
    
    # 循环模式
    process_only_new_candles: bool = False
    keep_interested_candles: bool = False
    
    # 期货/杠杆配置
    futures_mode: bool = False
    leverage: float = 1.0
    
    class Config:
        arbitrary_types_allowed = True


# ============ 生命周期钩子 ============

class IStrategyLifecycle(Protocol):
    """
    策略生命周期钩子协议
    
    策略可以实现这些方法来响应不同的生命周期事件。
    """
    
    def bot_start(self, **kwargs) -> None:
        """机器人启动时调用"""
        pass
    
    def bot_loop_start(self, **kwargs) -> None:
        """每个循环开始时调用"""
        pass
    
    def custom_stoploss(self, pair: str, current_profit: float, 
                       current_rate: float, **kwargs) -> float:
        """
        自定义止损逻辑
        
        Args:
            pair: 交易对
            current_profit: 当前盈亏比例
            current_rate: 当前价格
            
        Returns:
            止损价格或比例
        """
        return self.config.stoploss
    
    def custom_sell(self, pair: str, current_profit: float, 
                   current_rate: float, current_time: datetime, **kwargs) -> Optional[str]:
        """
        自定义卖出逻辑
        
        Args:
            pair: 交易对
            current_profit: 当前盈亏比例
            current_rate: 当前价格
            current_time: 当前时间
            
        Returns:
            卖出原因或None（使用默认逻辑）
        """
        return None
    
    def custom_buy(self, pair: str, current_rate: float, 
                  current_time: datetime, **kwargs) -> Optional[str]:
        """
        自定义买入逻辑
        
        Args:
            pair: 交易对
            current_rate: 当前价格
            current_time: 当前时间
            
        Returns:
            买入原因或None（使用默认逻辑）
        """
        return None
    
    def confirm_trade_entry(self, pair: str, order_type: str, 
                           amount: float, rate: float, time_in_force: str,
                           current_time: datetime, **kwargs) -> bool:
        """
        确认订单进入（可用于二次确认）
        
        Returns:
            True: 确认下单
            False: 取消订单
        """
        return True
    
    def confirm_trade_exit(self, pair: str, order_type: str, 
                          amount: float, rate: float, time_in_force: str,
                          current_time: datetime, **kwargs) -> bool:
        """
        确认订单退出（可用于二次确认）
        
        Returns:
            True: 确认下单
            False: 取消订单
        """
        return True
    
    def adjust_trade_position(self, trade: Dict, current_time: datetime,
                             current_rate: float, current_profit: float,
                             min_stake: float, max_stake: float,
                             current_stake_amount: float, **kwargs) -> Optional[float]:
        """
        调整仓位（加仓/减仓）
        
        Args:
            trade: 交易信息字典
            current_time: 当前时间
            current_rate: 当前价格
            current_profit: 当前盈亏
            min_stake: 最小仓位
            max_stake: 最大仓位
            current_stake_amount: 当前仓位金额
            
        Returns:
            调整金额（正数加仓，负数减仓）或None（不调整）
        """
        return None
    
    def botShutdown(self, **kwargs) -> None:
        """机器人关闭时调用"""
        pass
    
    def analyze_strategy(self, *args, **kwargs) -> Dict[str, Any]:
        """
        策略分析（用于 hyperopt）
        
        Returns:
            优化结果字典
        """
        return {}


# ============ 核心策略接口 ============

class IStrategy(ABC):
    """
    策略核心接口（类似 freqtrade 的 IStrategy）
    
    所有策略必须继承此类并实现必要的方法。
    
    使用方式：
        class Github_AtomNotShy_factor_mining__interface__20260114_095947(IStrategy):
            strategy_name = "My Strategy"
            timeframe = "1d"
            
            def populate_indicators(self, dataframe: pd.DataFrame, metadata: Dict) -> pd.DataFrame:
                # 计算指标
                dataframe['rsi'] = ta.rsi(dataframe['close'])
                return dataframe
            
            def populate_entry_trend(self, dataframe: pd.DataFrame, metadata: Dict) -> pd.DataFrame:
                # 生成买入信号
                dataframe['enter_long'] = dataframe['rsi'] < 30
                return dataframe
            
            def populate_exit_trend(self, dataframe: pd.DataFrame, metadata: Dict) -> pd.DataFrame:
                # 生成卖出信号
                dataframe['exit_long'] = dataframe['rsi'] > 70
                return dataframe
    """
    
    # 策略配置（子类应覆盖这些类属性）
    config: StrategyConfig = StrategyConfig()
    
    # 保留旧版接口兼容性
    @property
    def strategy_id(self) -> str:
        return self.config.strategy_id or self.__class__.__name__.lower()
    
    @property
    def strategy_name(self) -> str:
        return self.config.strategy_name or self.__class__.__name__
    
    @property
    def timeframe(self) -> str:
        return self.config.timeframe
    
    def __init__(self, config: Optional[StrategyConfig] = None):
        """初始化策略"""
        if config is not None:
            self.config = config
        
        self.logger = get_logger(f"strategy.{self.strategy_id}")
        self._prepared = False
        
    # ============ 核心方法（必须实现） ============
    
    @abstractmethod
    def populate_indicators(self, dataframe: pd.DataFrame, metadata: Dict) -> pd.DataFrame:
        """
        计算技术指标
        
        Args:
            dataframe: K线数据
            metadata: 元数据（包含 symbol, timeframe 等）
            
        Returns:
            添加了指标的 DataFrame
        """
        pass
    
    @abstractmethod
    def populate_entry_trend(self, dataframe: pd.DataFrame, metadata: Dict) -> pd.DataFrame:
        """
        生成买入信号
        
        在 dataframe 中添加 'enter_long', 'enter_short' 等列
        enter_long=1 表示买入信号
        
        Args:
            dataframe: K线数据（包含指标）
            metadata: 元数据
            
        Returns:
            添加了买入信号的 DataFrame
        """
        pass
    
    @abstractmethod
    def populate_exit_trend(self, dataframe: pd.DataFrame, metadata: Dict) -> pd.DataFrame:
        """
        生成卖出信号
        
        在 dataframe 中添加 'exit_long', 'exit_short' 等列
        exit_long=1 表示卖出信号
        
        Args:
            dataframe: K线数据（包含指标）
            metadata: 元数据
            
        Returns:
            添加了卖出信号的 DataFrame
        """
        pass
    
    # ============ 可选方法（提供默认实现） ============
    
    def get_strategy_info(self) -> Dict[str, Any]:
        """获取策略信息"""
        return {
            "strategy_id": self.strategy_id,
            "strategy_name": self.strategy_name,
            "timeframe": self.config.timeframe,
            "stoploss": self.config.stoploss,
            "trailing_stop": self.config.trailing_stop,
            "minimal_roi": self.config.minimal_roi,
            "order_types": self.config.order_types,
        }
    
    def version(self) -> int:
        """策略版本号"""
        return 1
    
    def check_buy_timeout(self, pair: str, current_time: datetime, 
                         current_rate: float, **kwargs) -> bool:
        """检查买入是否超时（返回 True 表示取消订单）"""
        return False
    
    def check_sell_timeout(self, pair: str, current_time: datetime,
                          current_rate: float, **kwargs) -> bool:
        """检查卖出是否超时（返回 True 表示取消订单）"""
        return False
    
    def adjust_entry_price(self, pair: str, current_time: datetime,
                          current_rate: float, proposed_rate: float,
                          current_order: Dict, **kwargs) -> float:
        """
        调整入场价格
        
        Args:
            pair: 交易对
            current_time: 当前时间
            current_rate: 当前价格
            proposed_rate: 提议的价格
            current_order: 当前订单信息
            
        Returns:
            调整后的价格
        """
        return proposed_rate
    
    def fee(self, pair: str, order_type: str, is_maker: bool, 
           amount: float, price: float) -> float:
        """
        计算手续费
        
        Args:
            pair: 交易对
            order_type: 订单类型
            is_maker: 是否是 maker
            amount: 数量
            price: 价格
            
        Returns:
            手续费金额
        """
        return 0.0
    
    def should_roi_on_trade(self, trade: Dict, current_time: datetime,
                           current_profit: float, min_roi: Dict,
                           max_roi: Dict, last_candle: pd.Series,
                           previous_candle: pd.Series, **kwargs) -> bool:
        """
        检查是否应该根据 ROI 退出交易
        """
        return True
    
    def should_exit(self, pair: str, current_time: datetime,
                   current_rate: float, current_profit: float,
                   dataframe: pd.DataFrame, **kwargs) -> bool:
        """
        决定是否退出交易
        
        Args:
            pair: 交易对
            current_time: 当前时间
            current_rate: 当前价格
            current_profit: 当前盈亏
            dataframe: K线数据
            
        Returns:
            True: 退出交易
            False: 继续持有
        """
        return False
    
    # ============ 保护机制 ============
    
    def _protections(self) -> List[Dict]:
        """返回保护配置"""
        return self.config.protections
    
    def ignore_buying_timeout(self, pair: str, current_time: datetime, 
                             current_value: float, **kwargs) -> bool:
        """
        检查是否应该忽略买入超时保护
        
        Returns:
            True: 忽略超时
            False: 应用超时
        """
        return self.config.ignore_protections
    
    def ignore_selling_timeout(self, pair: str, current_time: datetime,
                              current_value: float, **kwargs) -> bool:
        """
        检查是否应该忽略卖出超时保护
        
        Returns:
            True: 忽略超时
            False: 应用超时
        """
        return self.config.ignore_protections
    
    # ============ 仓位和资金管理 ============
    
    def stake_amount(self, pair: str, current_time: datetime,
                    current_rate: float, proposed_stake: float,
                    min_stake: float, max_stake: float, 
                    current_entry: float, current_close: float,
                    **kwargs) -> float:
        """
        计算仓位大小
        
        Args:
            pair: 交易对
            current_time: 当前时间
            current_rate: 当前价格
            proposed_stake: 建议的仓位
            min_stake: 最小仓位
            max_stake: 最大仓位
            current_entry: 入场价格
            current_close: 当前收盘价
            
        Returns:
            最终仓位大小
        """
        return proposed_stake
    
    def min_stake_amount(self, pair: str, current_time: datetime,
                        current_rate: float, **kwargs) -> float:
        """最小仓位"""
        return self.config.default_stake_amount
    
    def max_stake_amount(self, pair: str, current_time: datetime,
                        current_rate: float, **kwargs) -> float:
        """最大仓位"""
        return float('inf')
    
    def custom_stake_amount(self, pair: str, current_time: datetime,
                           current_rate: float, current_profit: float,
                           min_stake: float, max_stake: float,
                           current_stake_amount: float, **kwargs) -> float:
        """
        自定义仓位计算
        
        Returns:
            仓位大小
        """
        return current_stake_amount
    
    # ============ 工具方法 ============
    
    def load_pair_history(self, pair: str, timeframe: Optional[str] = None,
                         since: Optional[datetime] = None) -> pd.DataFrame:
        """
        加载交易对历史数据
        
        Args:
            pair: 交易对
            timeframe: 时间框架
            since: 开始时间
            
        Returns:
            K线数据 DataFrame
        """
        # 子类应实现此方法
        return pd.DataFrame()
    
    def get_informative_pairs(self) -> List[tuple]:
        """
        返回 informative pairs（用于多时间框架）
        
        Returns:
            [(pair, timeframe), ...] 列表
        """
        return []
    
    def timeframe_to_seconds(self, timeframe: str) -> int:
        """
        将时间框架转换为秒数
        
        Args:
            timeframe: 时间框架字符串（如 "1m", "5m", "1h", "1d"）
            
        Returns:
            秒数
        """
        timeframe_map = {
            "1m": 60,
            "3m": 180,
            "5m": 300,
            "15m": 900,
            "30m": 1800,
            "1h": 3600,
            "2h": 7200,
            "4h": 14400,
            "6h": 21600,
            "12h": 43200,
            "1d": 86400,
            "1w": 604800,
        }
        return timeframe_map.get(timeframe, 86400)
    
    def seconds_to_timeframe(self, seconds: int) -> str:
        """
        将秒数转换为时间框架
        
        Args:
            seconds: 秒数
            
        Returns:
            时间框架字符串
        """
        if seconds >= 86400 * 7:
            return "1w"
        elif seconds >= 86400:
            return "1d"
        elif seconds >= 43200:
            return "12h"
        elif seconds >= 21600:
            return "6h"
        elif seconds >= 14400:
            return "4h"
        elif seconds >= 7200:
            return "2h"
        elif seconds >= 3600:
            return "1h"
        elif seconds >= 1800:
            return "30m"
        elif seconds >= 900:
            return "15m"
        elif seconds >= 300:
            return "5m"
        elif seconds >= 180:
            return "3m"
        else:
            return "1m"


# ============ 类型别名 ============

StrategyType = TypeVar('StrategyType', bound=IStrategy)


# ============ 便利函数 ============

def create_strategy(config: Dict[str, Any]) -> StrategyConfig:
    """从字典创建策略配置"""
    return StrategyConfig(**config)


def merge_strategy_config(base: StrategyConfig, override: Dict[str, Any]) -> StrategyConfig:
    """合并策略配置"""
    config_dict = base.__dict__.copy()
    config_dict.update(override)
    return StrategyConfig(**config_dict)
