# source: https://raw.githubusercontent.com/tellyoung/money_game/e99e68c0be99fdb85969585d0198b74ec1119e81/Trading/user_data/strategies/MyStrategy.py
import json
import os
import pandas as pd
import numpy as np
from freqtrade.strategy import IStrategy
from typing import Dict, Any

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

    def __init__(self, config: Dict[str, Any]) -> None:
        super().__init__(config)
        # 加载因子逻辑
        self.factor_logic_path = os.path.join("/Users/yutieyang/Documents/yuty/yuty_projects/money_game/Trading/factor_mining/factor_data/factor_logic.json")
        self.factors = self.load_factor_logic(self.factor_logic_path)
        # 预加载ML模型（如有）
        self.ml_models = self.load_ml_models()

    def load_factor_logic(self, filepath):
        with open(filepath, 'r', encoding='utf-8') as f:
            factor_logic = json.load(f)
        return factor_logic

    def load_ml_models(self):
        models = {}
        for factor_name in self.factors:
            model_path = os.path.join(os.path.dirname(__file__), '../..', f'Trading/factor_mining/{factor_name}_ml_model.pkl')
            if os.path.exists(model_path):
                import pickle
                with open(model_path, 'rb') as f:
                    models[factor_name] = pickle.load(f)
        return models

    def populate_indicators(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        # 根据factor_logic.json动态生成所有有效因子
        for name, info in self.factors.items():
            params = info['params']
            # 这里只做简单示例：如有更复杂逻辑可扩展
            if 'window' in params and 'features' in params:
                feat = params['features'][0] if isinstance(params['features'], list) else params['features']
                if params.get('operation') == 'rolling_mean' and feat in dataframe:
                    dataframe[name] = dataframe[feat].rolling(window=params['window']).mean()
                elif params.get('operation') == 'rolling_std' and feat in dataframe:
                    dataframe[name] = dataframe[feat].rolling(window=params['window']).std()
                elif params.get('operation') == 'pct_change' and feat in dataframe:
                    dataframe[name] = dataframe[feat].pct_change(params['window'])
                elif params.get('operation') == 'lag' and feat in dataframe:
                    dataframe[name] = dataframe[feat].shift(params['lag'])
                elif params.get('operation') == 'zscore' and feat in dataframe:
                    mean = dataframe[feat].rolling(window=params['window']).mean()
                    std = dataframe[feat].rolling(window=params['window']).std()
                    dataframe[name] = (dataframe[feat] - mean) / (std + 1e-10)
                # 其他操作可按需扩展
        return dataframe

    def generate_signal(self, dataframe: pd.DataFrame, factor_name: str, method: str = 'zscore', buy_thr=1, sell_thr=-1, window=60):
        series = dataframe[factor_name]
        if method == 'zscore':
            mean = series.rolling(window).mean()
            std = series.rolling(window).std()
            z = (series - mean) / (std + 1e-10)
            signal = pd.Series(0, index=series.index)
            signal[z > buy_thr] = 1
            signal[z < sell_thr] = -1
            return signal
        elif method == 'quantile':
            q_high = series.rolling(window).quantile(0.8)
            q_low = series.rolling(window).quantile(0.2)
            signal = pd.Series(0, index=series.index)
            signal[series > q_high] = 1
            signal[series < q_low] = -1
            return signal
        elif method == 'ml' and factor_name in self.ml_models:
            model = self.ml_models[factor_name]
            X = series.values.reshape(-1, 1)
            preds = model.predict(X)
            return pd.Series(preds, index=series.index)
        else:
            return pd.Series(0, index=series.index)

    def populate_entry_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        # 以第一个有效因子为例，实际可批量处理
        best_factor = list(self.factors.keys())[0]
        method = 'zscore'  # 可改为'quantile'或'ml'
        signal = self.generate_signal(dataframe, best_factor, method=method, buy_thr=1, sell_thr=-1, window=60)
        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:
        # 简单示例：信号反向时平仓
        best_factor = list(self.factors.keys())[0]
        method = 'zscore'
        signal = self.generate_signal(dataframe, best_factor, method=method, buy_thr=1, sell_thr=-1, window=60)
        dataframe.loc[signal == -1, 'exit_long'] = 1
        dataframe.loc[signal == 1, 'exit_short'] = 1
        return dataframe



"""
import pickle
import pandas as pd

# 1. 加载新数据并生成所有best_factors特征（用你的engine流程）
# engine.data = pd.read_feather("新数据.feather")
# engine.load_factor_logic()
# engine.generate_factors(parallel=False)
X_new = engine.data[best_factors].values

# 2. 加载模型
with open("all_factors_ml_model.pkl", "rb") as f:
    model = pickle.load(f)

# 3. 预测买卖信号
pred_signal = model.predict(X_new)  # 0/1 或 -1/1
engine.data["ml_signal"] = pred_signal
"""