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 GKD_FisherTransformMTF(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