Timeframe
1h
Direction
Long Only
Stoploss
-10.0%
Trailing Stop
Yes
ROI
0m: 5.0%, 60m: 3.0%, 120m: 1.0%
Interface Version
N/A
Startup Candles
N/A
Indicators
2
from freqtrade.strategy import IStrategy
from pandas_ta import ema
import pandas as pd
import numpy as np
import talib
import logging
from typing import Dict, List
from pandas import DataFrame
# Set up logging for debugging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
class GKD_HurstExponent(IStrategy):
# Strategy parameters
timeframe = "1h"
minimal_roi = {"0": 0.05, "60": 0.03, "120": 0.01}
stoploss = -0.10
trailing_stop = True
trailing_stop_positive = 0.02
trailing_stop_positive_offset = 0.03
# Custom parameters
hurst_period = 64 # Lookback period for Hurst Exponent
hurst_smooth_period = 5 # EMA smoothing period for Hurst Exponent
hurst_threshold = 0.55 # Threshold for trending market
hurst_exit_threshold = 0.25 # Threshold for exit (random/mean-reverting)
baseline_period = 20 # Period for baseline EMA
atr_period = 14 # Period for ATR (Goldie Locks Zone)
goldie_locks_min = 0.2 # Min multiplier for Goldie Locks Zone
goldie_locks_max = 1.0 # Max multiplier for Goldie Locks Zone
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# Calculate log-returns
dataframe["log_return"] = np.log(dataframe["close"] / dataframe["close"].shift(1))
# Calculate Hurst Exponent
dataframe["hurst"] = self.calculate_hurst(dataframe["log_return"], self.hurst_period)
dataframe['zero'] = 0.5
# Smooth Hurst Exponent
dataframe["hurst_smooth"] = ema(dataframe["hurst"], length=self.hurst_smooth_period)
# Baseline (EMA)
dataframe["baseline"] = ema(dataframe["close"], length=self.baseline_period)
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"] = talib.ATR(
dataframe["high"], dataframe["low"], dataframe["close"], timeperiod=self.atr_period
)
dataframe["goldie_min"] = dataframe["baseline"] - (dataframe["atr"] * self.goldie_locks_min)
dataframe["goldie_max"] = dataframe["baseline"] + (dataframe["atr"] * self.goldie_locks_max)
# Debug logging
logger.debug(f"Recent hurst values: {dataframe['hurst'].tail(10).to_list()}")
logger.debug(f"Recent hurst_smooth values: {dataframe['hurst_smooth'].tail(10).to_list()}")
return dataframe
def calculate_hurst(self, series: pd.Series, period: int) -> pd.Series:
# Hurst Exponent using single-window R/S analysis on log-returns
hurst = pd.Series(np.nan, index=series.index)
for i in range(period, len(series)):
window = series.iloc[i-period:i].dropna()
if len(window) < period:
logger.debug(f"Insufficient data at i={i}, len={len(window)}")
hurst.iloc[i] = 0.5
continue
# Mean-adjusted series
mean = window.mean()
mean_adj = window - mean
# Cumulative deviation
cum_dev = mean_adj.cumsum()
# Range (R)
r = cum_dev.max() - cum_dev.min()
# Standard deviation (S)
s = window.std()
# R/S calculation
if s == 0 or r == 0 or np.isnan(s) or np.isnan(r):
logger.debug(f"Invalid R/S at i={i}: r={r}, s={s}")
# Fallback: variance-based estimate
variance = window.var()
if variance > 0:
hurst.iloc[i] = 0.5 + np.log(variance) / (2 * np.log(period))
hurst.iloc[i] = np.clip(hurst.iloc[i], 0, 1)
else:
hurst.iloc[i] = 0.5
continue
rs = r / s
if rs <= 0:
logger.debug(f"Invalid rs at i={i}: rs={rs}")
hurst.iloc[i] = 0.5
continue
# Approximate H without Anis-Lloyd correction
h = np.log(rs) / np.log(period)
hurst.iloc[i] = np.clip(h, 0, 1)
logger.debug(f"H at i={i}: rs={rs}, h={h}, clipped={hurst.iloc[i]}")
return hurst
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# Volatility/Volume Entry Logic (GKD-V)
dataframe.loc[
(dataframe["hurst_smooth"] > self.hurst_threshold) & # Hurst indicates trending market
(dataframe["baseline_up"]) & # Baseline confirms uptrend
(dataframe["close"] >= dataframe["goldie_min"]) & # Within Goldie Locks Zone
(dataframe["close"] <= dataframe["goldie_max"]),
"enter_long"
] = 1
dataframe.loc[
(dataframe["hurst_smooth"] > self.hurst_threshold) & # Hurst indicates trending market
(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: Hurst indicates random/mean-reverting market
dataframe.loc[
(dataframe["hurst_smooth"] < self.hurst_exit_threshold), # Hurst below threshold
"exit_long"
] = 1
dataframe.loc[
(dataframe["hurst_smooth"] < self.hurst_exit_threshold), # Hurst below threshold
"exit_short"
] = 1
return dataframe