# source: https://raw.githubusercontent.com/remiotore/freqtrade/44beaeb6a420cd8e9f2e4ea93e11d6cfa192ee03/strategies/newstrategy_modify_support_fin.py
import json
from pathlib import Path
from typing import Dict, Any
import freqtrade.vendor.qtpylib.indicators as qtpylib

import numpy as np
import pandas as pd
from pandas import DataFrame
import talib.abstract as ta

from freqtrade.strategy import IStrategy, merge_informative_pair
from freqtrade.strategy import IntParameter, DecimalParameter, RealParameter

class Github_remiotore_freqtrade__newstrategy_modify_support_fin__20260111_210550(IStrategy):

    can_short = True
    timeframe = '5m'
    inf_timeframe = '1h'

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # === 自动加载 JSON ===
        self.best_config: Dict[str, Any] = {}
        path = Path("user_data/strategies/Github_remiotore_freqtrade__newstrategy_modify_support_fin__20260111_210550.json")
        if path.exists():
            try:
                with open(path) as f:
                    self.best_config = json.load(f).get("results", {}).get("best_config", {})
                print(f"[Hyperopt] Loaded config: {self.best_config}")
            except Exception as e:
                print(f"[Hyperopt] Load error: {e}")

        # === fallback 参数 ===
        self.fallback_buy_params = {
            "bbdelta_close": 0.00082, "bbdelta_tail": 0.85788, "close_bblower": 0.00128,
            "closedelta_close": 0.00987, "low_offset": 0.991, "rocr1_1h": 0.9346,
            "rocr_1h": 0.65666, "base_nb_candles_buy": 12, "buy_bb_width": 0.095,
            "buy_cci": -116, "buy_cci_length": 25, "buy_closedelta": 15.0,
            "buy_clucha_bbdelta_close": 0.049, "buy_clucha_bbdelta_tail": 1.146,
            "buy_clucha_close_bblower": 0.018, "buy_clucha_closedelta_close": 0.017,
            "buy_clucha_rocr_1h": 0.526, "buy_ema_diff": 0.025, "buy_rmi": 49,
            "buy_rmi_length": 17, "buy_roc_1h": 10, "buy_srsi_fk": 32,
            "ai_buy_threshold": 0.6
        }

        self.fallback_sell_params = {
            "high_offset": 1.012, "high_offset_2": 1.016, "sell_deadfish_bb_factor": 1.089,
            "sell_deadfish_bb_width": 0.11, "sell_deadfish_profit": -0.107,
            "sell_deadfish_volume_factor": 1.761, "base_nb_candles_sell": 22,
            "pHSL": -0.397, "pPF_1": 0.012, "pPF_2": 0.07, "pSL_1": 0.015,
            "pSL_2": 0.068, "sell_bbmiddle_close": 1.09092, "sell_fisher": 0.46406,
            "sell_trail_down_1": 0.03, "sell_trail_down_2": 0.015,
            "sell_trail_profit_max_1": 0.4, "sell_trail_profit_max_2": 0.11,
            "sell_trail_profit_min_1": 0.1, "sell_trail_profit_min_2": 0.04
        }

        def best(key: str, fallback: Any = None):
            return self.best_config.get(
                key,
                self.fallback_buy_params.get(key, self.fallback_sell_params.get(key, fallback))
            )
        # === BUY 参数 ===
        self.ai_buy_threshold = RealParameter(0.4, 0.9, default=best("ai_buy_threshold", 0.45), space='buy')
        self.rocr_1h = RealParameter(0.5, 1.0, default=best("rocr_1h", 0.65666), space='buy')
        self.rocr1_1h = RealParameter(0.5, 1.0, default=best("rocr1_1h", 0.9346), space='buy')
        self.bbdelta_close = RealParameter(0.0005, 0.02, default=best("bbdelta_close", 0.00082), space='buy')
        self.closedelta_close = RealParameter(0.0005, 0.02, default=best("closedelta_close", 0.00987), space='buy')
        self.bbdelta_tail = RealParameter(0.7, 1.2, default=best("bbdelta_tail", 0.85788), space='buy')
        self.close_bblower = RealParameter(0.0005, 0.02, default=best("close_bblower", 0.00128), space='buy')
        self.low_offset = DecimalParameter(0.985, 0.995, default=best("low_offset", 0.991), space='buy')
        self.base_nb_candles_buy = IntParameter(8, 20, default=best("base_nb_candles_buy", 12), space='buy')

        # === SELL 参数（示例）===
        self.sell_fisher = RealParameter(0.1, 0.5, default=best("sell_fisher", 0.46406), space='sell')
        self.sell_bbmiddle_close = RealParameter(0.97, 1.1, default=best("sell_bbmiddle_close", 1.09092), space='sell')
        self.high_offset = DecimalParameter(1.005, 1.015, default=best("high_offset", 1.012), space='sell')
        self.high_offset_2 = DecimalParameter(1.010, 1.020, default=best("high_offset_2", 1.016), space='sell')
        self.base_nb_candles_sell = IntParameter(8, 30, default=best("base_nb_candles_sell", 22), space='sell')

        # === 止损设置 ===
        self.stoploss = best("stoploss", -0.99)
        self.position_adjustment_enable = best("position_adjustment_enable", True)

        # === ROI 设置 ===
        self.minimal_roi = {
            "0": best("roi_0", 0.276),
            "32": best("roi_32", 0.105),
            "88": best("roi_88", 0.037),
            "208": best("roi_208", 0.0)
        }

        # === Trailing 止盈 ===
        self.trailing_stop = best("trailing_stop", False)
        self.trailing_stop_positive = best("trailing_stop_positive", 0.02)
        self.trailing_stop_positive_offset = best("trailing_stop_positive_offset", 0.10)
        self.trailing_only_offset_is_reached = best("trailing_only_offset_is_reached", True)
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        if dataframe.empty:
            return dataframe

        # === Heikin Ashi candles ===
        heikinashi = qtpylib.heikinashi(dataframe)
        dataframe['ha_open'] = heikinashi['open']
        dataframe['ha_close'] = heikinashi['close']

        # === EWO: Elliott Wave Oscillator ===
        dataframe['ema_fast'] = ta.EMA(dataframe['close'], timeperiod=50)
        dataframe['ema_slow'] = ta.EMA(dataframe['close'], timeperiod=200)
        dataframe['ewo'] = (dataframe['ema_fast'] - dataframe['ema_slow']) / dataframe['close'] * 100

        # === CTI: Correlation Trend Indicator ===
        def cti(series, length=20):
            diff = series.diff()
            up = diff.where(diff > 0, 0)
            down = -diff.where(diff < 0, 0)
            rs = up.rolling(length).mean() / down.rolling(length).mean()
            rsi = 100 - 100 / (1 + rs)
            return (rsi - 50) / 50

        dataframe['cti'] = cti(dataframe['close'], 20)

        # === Bollinger Bands ===
        upper, middle, lower = ta.BBANDS(dataframe['close'], timeperiod=20)
        
        dataframe['bb_upperband'] = upper
        dataframe['bb_middleband'] = middle
        dataframe['bb_lowerband'] = lower
        dataframe['bbdelta_close'] = abs(dataframe['bb_middleband'] - dataframe['close'])
        dataframe['closedelta_close'] = abs(dataframe['close'] - dataframe['close'].shift())
        dataframe['bbdelta_tail'] = abs(dataframe['close'] - dataframe['low'])
        dataframe['close_bblower'] = abs(dataframe['close'] - dataframe['bb_lowerband']) / dataframe['bb_lowerband']
        dataframe['bb_width'] = (upper - lower) / dataframe['close']

        # === 1h Informative Merge (for rocr_1h etc.) ===
        informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.inf_timeframe)
        informative['rocr_1h'] = ta.ROCR(informative['close'], timeperiod=28)
        informative['roc_1h'] = ta.ROC(informative['close'], timeperiod=9)
        upper, middle, lower = ta.BBANDS(informative['close'], timeperiod=20)
        informative['bb_upperband_1h'] = upper
        informative['bb_lowerband_1h'] = lower
        informative['bb_width_1h'] = (upper - lower) / informative['close']

        informative['rsi_1h'] = ta.RSI(informative['close'], timeperiod=14)

        dataframe = merge_informative_pair(dataframe, informative, self.timeframe, self.inf_timeframe, ffill=True)

        return dataframe
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe['enter_long'] = 0

        # === 标签：FreqAI AI signal ===
        if 'lightgbm_prediction' in dataframe.columns:
            dataframe.loc[
                (dataframe['lightgbm_prediction'] > self.ai_buy_threshold.value),
                ['enter_long', 'enter_tag']
            ] = (1, 'freqai_signal')

        # === 标签：vwap 策略 ===
        dataframe.loc[
            (dataframe['close'] < dataframe['bb_lowerband']) &
            (dataframe['ewo'] < -5) &
            (dataframe['cti'] < -0.8),
            ['enter_long', 'enter_tag']
        ] = (1, 'vwap')

        # === 标签：cluc_HA 策略 ===
        dataframe.loc[
            (dataframe['ha_close'] < dataframe['bb_lowerband']) &
            (dataframe['bbdelta_close'] > self.bbdelta_close.value) &
            (dataframe['closedelta_close'] > self.closedelta_close.value) &
            (dataframe['bbdelta_tail'] < self.bbdelta_tail.value),
            ['enter_long', 'enter_tag']
        ] = (1, 'cluc_HA')

        # === 标签：insta_signal 策略 ===
        rsi = dataframe.get('rsi')
        if rsi is not None:
            dataframe.loc[
                (rsi < 56) &
                (dataframe['ewo'] > 5) &
                (dataframe['cti'] < -0.7),
            ['enter_long', 'enter_tag']
        ] = (1, 'insta_signal')

        # === 标签：NFINext7（示例）===
        rsi_1h = dataframe.get('rsi_1h')
        if rsi_1h is not None:
            dataframe.loc[
                (rsi_1h < -75) &
                (dataframe['ewo'] > 9.8) &
                (dataframe['cti'] < -0.8),
                ['enter_long', 'enter_tag']
            ] = (1, 'NFINext7')
        return dataframe
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe['exit_long'] = 0

        # === AI 辅助退出（FreqAI 反信号）===
        if 'lightgbm_prediction' in dataframe.columns:
            dataframe.loc[
                (dataframe['lightgbm_prediction'] < self.ai_buy_threshold.value * 0.9),
                ['exit_long', 'exit_tag']
            ] = (1, 'freqai_exit')

        # === 固定止盈逻辑（示例）===
        rsi = dataframe.get('rsi')
        if rsi is not None:
            dataframe.loc[
            (rsi > 70),
            ['exit_long', 'exit_tag']
        ] = (1, 'rsi>70')

        return dataframe

    # === 可选: 自定义 exit（用于 trailing 或其他条件）===
    # def custom_exit(self, pair: str, trade: Trade, current_time: datetime, current_rate: float,
    #                 current_profit: float, **kwargs) -> Optional[Union[str, bool]]:
    #     # 你可以在这里接入 FreqAI 或技术指标作为 exit 逻辑
    #     return None

# === 策略类结束 ===
