# source: https://raw.githubusercontent.com/jaredrsommer/freqtradestrategies/9d66f71ce21f2f65feaad6bb967215be22a3cfd6/GKD_FisherTransformMTF.py
from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter, 
                               IStrategy, IntParameter, RealParameter, merge_informative_pair, informative)
from pandas_ta import ema
import pandas as pd
import numpy as np
import datetime
import talib.abstract as ta
from typing import Dict, List
from pandas import DataFrame
from datetime import datetime
from freqtrade.persistence import Trade

class Github_jaredrsommer_freqtradestrategies__GKD_FisherTransformMTF__20260518_195114(IStrategy):
    # Strategy parameters
    timeframe = "1h"
    minimal_roi = {}
    stoploss = -0.10
    trailing_stop = False
    trailing_stop_positive = 0.02
    trailing_stop_positive_offset = 0.03

    # Custom parameters
    fisher_period = IntParameter(10, 15, default=12, space="buy")      # Lookback period for Fisher Transform
    fisher_smooth = IntParameter(5, 10, default=8, space="buy")         # EMA smoothing period for Fisher Transform
    fisher_sell_threshold = DecimalParameter(2.0, 3.9, default=2.5, decimals=2, space="sell")
    fisher_buy_threshold = DecimalParameter(-1.0, 2.5, default=-0.5, decimals=2, space="buy")
    fisher_period_inf = IntParameter(10, 15, default=12, space="buy")      # Lookback period for Fisher Transform
    fisher_smooth_inf = IntParameter(5, 10, default=8, space="buy")         # EMA smoothing period for Fisher Transform
    fisher_sell_threshold_inf = DecimalParameter(2.0, 3.9, default=2.5, decimals=2, space="sell")
    fisher_buy_threshold_inf = DecimalParameter(-1.0, 2.5, default=-0.5, decimals=2, space="buy")
    baseline_period = IntParameter(5, 21, default=21, space="buy")        # Period for baseline EMA
    atr_period = IntParameter(10, 21, default=14, space="buy")             # Period for ATR (volatility filter)
    goldie_locks = DecimalParameter(1.5, 3.0, default=2.0, decimals=2, space="buy")    # Max multiplier for Goldie Locks Zone

    @informative('4h')
    def populate_indicators_4h(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Calculate Fisher Transform
        dataframe["fisher"] = self.calculate_fisher(dataframe, self.fisher_period.value)
        
        # Smooth Fisher with EMA
        dataframe["fisher_smooth"] = ema(dataframe["fisher"], length=self.fisher_smooth_inf.value)
        dataframe["fisher_trend"] = ema(dataframe["fisher_smooth"], length=21)

        # Baseline (EMA)
        dataframe["baseline"] = ema(dataframe["close"], length=self.baseline_period.value)
        dataframe["baseline_diff"] = dataframe["baseline"].diff()
        dataframe["baseline_up"] = dataframe["baseline_diff"] > 0
        dataframe["baseline_down"] = dataframe["baseline_diff"] < 0

        # Volatility (ATR for Goldie Locks Zone)
        dataframe["atr"] = ta.ATR(
            dataframe["high"], dataframe["low"], dataframe["close"], timeperiod=self.atr_period.value
        )
        dataframe["goldie_min"] = dataframe["baseline"] - (dataframe["atr"] * self.goldie_locks.value)
        dataframe["goldie_max"] = dataframe["baseline"] + (dataframe["atr"] * self.goldie_locks.value)
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=4)
        return dataframe

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Calculate Fisher Transform
        dataframe["fisher"] = self.calculate_fisher(dataframe, self.fisher_period.value)
        
        # Smooth Fisher with EMA
        dataframe["fisher_smooth"] = ema(dataframe["fisher"], length=self.fisher_smooth.value)
        dataframe["fisher_trend"] = ema(dataframe["fisher_smooth"], length=21)

        # Baseline (EMA)
        dataframe["baseline"] = ema(dataframe["close"], length=self.baseline_period.value)
        dataframe["baseline_diff"] = dataframe["baseline"].diff()
        dataframe["baseline_up"] = dataframe["baseline_diff"] > 0
        dataframe["baseline_down"] = dataframe["baseline_diff"] < 0

        # Volatility (ATR for Goldie Locks Zone)
        dataframe["atr"] = ta.ATR(
            dataframe["high"], dataframe["low"], dataframe["close"], timeperiod=self.atr_period.value
        )
        dataframe["goldie_min"] = dataframe["baseline"] - (dataframe["atr"] * self.goldie_locks.value)
        dataframe["goldie_max"] = dataframe["baseline"] + (dataframe["atr"] * self.goldie_locks.value)

        return dataframe

    def calculate_fisher(self, dataframe: DataFrame, period: int) -> pd.Series:
        # Fisher Transform calculation
        median_price = (dataframe["high"] + dataframe["low"]) / 2
        fisher = pd.Series(0.0, index=dataframe.index)

        for i in range(period, len(dataframe)):
            # Normalize price
            price_window = median_price.iloc[i-period:i]
            price_min = price_window.min()
            price_max = price_window.max()
            if price_max != price_min:
                norm = (median_price.iloc[i] - price_min) / (price_max - price_min)
                norm = 2 * norm - 1  # Scale to [-0.999, 0.999]
                norm = max(min(norm, 0.999), -0.999)  # Prevent division by zero
                # Apply Fisher Transform
                fisher.iloc[i] = 0.5 * np.log((1 + norm) / (1 - norm))
            else:
                fisher.iloc[i] = 0.0

        return fisher

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Standard Entry Logic (GKD-C Confirmation)
        dataframe.loc[
            # (dataframe["fisher_smooth"] > self.fisher_buy_threshold.value) & 
            # (dataframe["fisher_smooth"] < self.fisher_sell_threshold.value) &
            (dataframe["fisher_smooth"] > dataframe["fisher_smooth"].shift()) & 
            # (dataframe["fisher_smooth"].shift() < -0) &  
            (dataframe["fisher_4h"] < self.fisher_sell_threshold_inf.value) & 
            (dataframe["fisher_smooth_4h"] < dataframe['fisher_4h']), # &

             # Fisher indicates bullish reversal
            # (dataframe["baseline_up"]) &                              # Baseline confirms uptrend
            # (dataframe["close"] >= dataframe["goldie_min"]) &         # Within Goldie Locks Zone
            # (dataframe["close"] <= dataframe["goldie_max"]),
            "enter_long"
        ] = 1


        if self.can_short == True:
            dataframe.loc[
                (dataframe["fisher_smooth"] < self.fisher_sell_threshold_inf.value) &  # Fisher indicates bearish reversal
                (dataframe["baseline_down"]) &                              # Baseline confirms downtrend
                (dataframe["close"] >= dataframe["goldie_min"]) &           # Within Goldie Locks Zone
                (dataframe["close"] <= dataframe["goldie_max"]),
                "enter_short"
            ] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Exit Logic: Fisher reverses or crosses neutral zone
        dataframe.loc[
            (
            (dataframe["fisher_smooth_4h"].shift() > 0) & 
            (dataframe["fisher_smooth_4h"] < 0) & 
            (dataframe["fisher_smooth_4h"] > dataframe['fisher_4h'])
            ),  # Fisher indicates weakening or bearish reversal
            "exit_long"
        ] = 1

        # dataframe.loc[
        #     (
        #     (dataframe["fisher_smooth"] < self.fisher_buy_threshold.value)
        #     ),  # Fisher indicates weakening or bearish reversal
        #     "exit_long"
        # ] = 1

        if self.can_short == True:
            dataframe.loc[
                (dataframe["fisher_smooth"] > 0),  # Fisher indicates weakening or bullish reversal
                "exit_short"
            ] = 1

        return dataframe


   # 193/1000:    358 trades. 190/25/143 Wins/Draws/Losses. Avg profit   0.87%. Median profit   0.17%. Total profit 31413.09084858 USDT ( 157.07%). Avg duration 23:52:00 min. Objective: -31413.09085


    # # Buy hyperspace params:
    # buy_params = {
    #     "atr_period": 21,
    #     "baseline_period": 16,
    #     "fisher_buy_threshold": -3.5,
    #     "fisher_period": 12,
    #     "fisher_smooth": 8,
    #     "goldie_locks": 1.61,
    # }

    # # Sell hyperspace params:
    # sell_params = {
    #     "fisher_sell_threshold": 3.74,
    # }

    # # ROI table:
    # minimal_roi = {
    #     "0": 1.035,
    #     "1166": 0.25,
    #     "2510": 0.176,
    #     "3562": 0
    # }

    # # Stoploss:
    # stoploss = -0.21

    # # Trailing stop:
    # trailing_stop = True
    # trailing_stop_positive = 0.011
    # trailing_stop_positive_offset = 0.045
    # trailing_only_offset_is_reached = False
    

   #  # Max Open Trades:
   #  max_open_trades = 3
   # 248/1000:    298 trades. 176/0/122 Wins/Draws/Losses. Avg profit   0.81%. Median profit   2.48%. Total profit 21364.22232610 USDT ( 106.82%). Avg duration 1 day, 2:44:00 min. Objective: -21364.22233


   #  # Buy hyperspace params:
   #  buy_params = {
   #      "atr_period": 5,
   #      "baseline_period": 9,
   #      "fisher_buy_threshold": -2.86,
   #      "fisher_period": 9,
   #      "fisher_smooth": 9,
   #      "goldie_locks": 2.38,
   #  }

   #  # Sell hyperspace params:
   #  sell_params = {
   #      "fisher_sell_threshold": 0.23,
   #  }

   #  # ROI table:
   #  minimal_roi = {
   #      "0": 1.234,
   #      "2209": 0.438,
   #      "3223": 0.166,
   #      "12718": 0
   #  }

   #  # Stoploss:
   #  stoploss = -0.22

   #  # Trailing stop:
   #  trailing_stop = True
   #  trailing_stop_positive = 0.01
   #  trailing_stop_positive_offset = 0.034
   #  trailing_only_offset_is_reached = False
    

   #  # Max Open Trades:
   #  max_open_trades = 3
