# source: https://raw.githubusercontent.com/tellyoung/money_game/9a55b7d1b9242eeb79f7209be9375eae9c89b130/Trading/user_data/strategies/MyStrategy.py
import sys
import importlib
import json
import os
import pandas as pd
import numpy as np
from freqtrade.strategy import IStrategy
from typing import Dict, Any
import logging  # 添加logger属性，兼容因子加载等日志输出

# 将factor_mining目录加入sys.path，确保importlib能找到factors包。
factor_mining_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../factor_mining'))
sys.path.insert(0, factor_mining_path)

class Github_tellyoung_money_game__MyStrategy__20250529_143720(IStrategy):
    """
    在策略类初始化时加载factor_logic.json，动态注册所有有效因子。
    在populate_indicators中，利用这些因子逻辑生成因子数据。
    在populate_entry_trend和populate_exit_trend中，加载买卖信号生成逻辑（如zscore、quantile、ML模型），并据此生成买卖信号。
    支持ML模型时，自动加载pkl模型文件并预测信号。
    这样策略类就能无缝衔接你的因子挖掘pipeline，直接用于回测。
    """
    INTERFACE_VERSION = 3
    can_short = True
    timeframe = "5m"

    # ROI table (hyperoptable)
    minimal_roi = {
        "0": 0.03,  # Default values, will be overridden by hyperopt
        "30": 0.02,
        "60": 0.01
    }

    # Stoploss (hyperoptable)
    stoploss = -0.01  # Default value, will be overridden by hyperopt

    # Trailing stop:
    trailing_stop = True
    trailing_stop_positive = 0.05
    trailing_stop_positive_offset = 0.06
    trailing_only_offset_is_reached = False

    def __init__(self, config: Dict[str, Any]) -> None:
        super().__init__(config)

        self.logger = logging.getLogger('Github_tellyoung_money_game__MyStrategy__20250529_143720')
        # 加载因子逻辑
        self.factors = {}
        self.best_factor_logic_path = os.path.join("/Users/yutieyang/Documents/yuty/yuty_projects/money_game/Trading/factor_mining/factor_data/factor_logic.json")
        self.load_factor_logic(self.best_factor_logic_path)

        # 加载best_factors顺序
        best_factors_path = os.path.join("/Users/yutieyang/Documents/yuty/yuty_projects/money_game/Trading/factor_mining/factor_data/best_factors.json")
        with open(best_factors_path, 'r', encoding='utf-8') as f:
            self.best_factors = json.load(f)

        # 预加载联合多因子ML模型（如有）
        self.all_factors_ml_model = self.load_factors_ml_model()

    def load_factor_logic(self, filepath):
        with open(filepath, 'r', encoding='utf-8') as f:
            factor_logic = json.load(f)
        # 记录原始顺序
        self.factor_order = list(factor_logic.keys())
        # 按类型分组
        self.basic_factors = {}
        self.advanced_factors = {}
        self.parametric_dynamic_factors = {}
        self.dynamic_factors = {}
        for name, info in factor_logic.items():
            try:
                module = importlib.import_module(info["module"])
                cls = getattr(module, info["class"])
                factor = cls(**info["params"])
                # 分类
                if info["module"].endswith("basic_factors"):
                    self.basic_factors[name] = factor
                elif info["module"].endswith("advanced_factors"):
                    self.advanced_factors[name] = factor
                elif info["class"] == "ParametricDynamicFactor":
                    self.parametric_dynamic_factors[name] = factor
                elif info["class"] == "DynamicFactor":
                    self.dynamic_factors[name] = factor
                else:
                    self.dynamic_factors[name] = factor  # 兜底
                self.logger.info(f"因子加载成功: {name}")
            except Exception as e:
                self.logger.error(f"因子加载失败: {name}, 错误: {e}")
        # 合并所有因子，便于后续兼容
        self.factors = {**self.basic_factors, **self.advanced_factors, **self.parametric_dynamic_factors, **self.dynamic_factors}
        self.logger.info(f"已加载因子逻辑: {list(self.factors.keys())}")

    def load_factors_ml_model(self):
        # 支持加载联合多因子ML模型
        model_path = os.path.join("/Users/yutieyang/Documents/yuty/yuty_projects/money_game/Trading/factor_mining/factor_data/ml_model.pkl")
        if os.path.exists(model_path):
            import pickle
            with open(model_path, 'rb') as f:
                return pickle.load(f)
        return None

    def populate_indicators(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        """
        按依赖顺序分阶段生成所有因子，最后按原始顺序排列。
        """
        # 1. basic_factors
        for name, factor in self.basic_factors.items():
            try:
                dataframe[name] = factor.generate(dataframe.copy())
                self.logger.info(f"因子生成成功: {name}")
            except Exception as e:
                self.logger.error(f"因子生成失败: {name}, 错误: {e}")
                dataframe[name] = 0
        # 2. advanced_factors
        for name, factor in self.advanced_factors.items():
            try:
                dataframe[name] = factor.generate(dataframe.copy())
                self.logger.info(f"因子生成成功: {name}")
            except Exception as e:
                self.logger.error(f"因子生成失败: {name}, 错误: {e}")
                dataframe[name] = 0
        # 3. parametric_dynamic_factors
        for name, factor in self.parametric_dynamic_factors.items():
            try:
                dataframe[name] = factor.generate(dataframe.copy())
                self.logger.info(f"因子生成成功: {name}")
            except Exception as e:
                self.logger.error(f"因子生成失败: {name}, 错误: {e}")
                dataframe[name] = 0
        # 4. dynamic_factors
        for name, factor in self.dynamic_factors.items():
            try:
                dataframe[name] = factor.generate(dataframe.copy())
                self.logger.info(f"因子生成成功: {name}")
            except Exception as e:
                self.logger.error(f"因子生成失败: {name}, 错误: {e}")
                dataframe[name] = 0

        return dataframe

    def generate_signal(self, dataframe: pd.DataFrame, factor_name: str = None, method: str = 'zscore', buy_thr=1, sell_thr=-1, window=60, use_all_factors=False):
        """
        支持单因子和多因子ML推理。use_all_factors=True时，使用联合ML模型。
        """
        if use_all_factors and self.all_factors_ml_model is not None:
            # 多因子ML推理，严格按best_factors顺序取特征
            X = dataframe[self.best_factors].values
            X = np.nan_to_num(X, nan=0.0, posinf=0.0, neginf=0.0)
            # 置信度阈值
            prob_thr = 0.8
            if hasattr(self.all_factors_ml_model, 'predict_proba'):
                probas = self.all_factors_ml_model.predict_proba(X)
                preds = self.all_factors_ml_model.predict(X)
                preds = np.where(preds == 0, -1, preds)

                # 只在最大概率大于阈值时才输出预测，否则为0
                max_proba = np.max(probas, axis=1)
                preds = np.where(max_proba >= prob_thr, preds, 0)
            else:
                preds = self.all_factors_ml_model.predict(X)
            return pd.Series(preds, index=dataframe.index)
        if factor_name is None:
            return pd.Series(0, index=dataframe.index)

    def populate_entry_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        # 支持ML联合信号和单因子信号
        use_ml = self.all_factors_ml_model is not None
        if use_ml:
            signal = self.generate_signal(dataframe, use_all_factors=True)
        dataframe.loc[signal == 1, 'enter_long'] = 1
        dataframe.loc[signal == -1, 'enter_short'] = 1
        return dataframe

    def populate_exit_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        # 支持ML联合信号和单因子信号
        use_ml = self.all_factors_ml_model is not None
        if use_ml:
            signal = self.generate_signal(dataframe, use_all_factors=True)
        dataframe.loc[signal == -1, 'exit_long'] = 1
        dataframe.loc[signal == 1, 'exit_short'] = 1
        return dataframe



