I'm working on a script to calculate and plot the Hurst Exponent and Smoothed Hurst Exponent for a stock's historical price data using Python. When I run the script, I face two major issues:
The Smoothed Hurst Exponent and the Hurst Exponent values are the same. I expect the Smoothed Hurst Exponent to be different from the Hurst Exponent, as it should be a moving average of the Hurst Exponent.
The plotting doesn't seem to be done correctly. I'm trying to plot the Hurst Exponent, the Smoothed Hurst Exponent, and the confidence intervals, but the resulting plot doesn't display the data as expected.
I'm looking for help in identifying the issues in my code that are causing these problems and suggestions on how to fix them.
Any assistance would be greatly appreciated
My code is as follows:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import yfinance as yf
from scipy.stats import linregress
# (a) - inputs
def inputs(barsize=100, slen=18, Min=8, Max=2):
return barsize, slen, Min, Max
# (b) - Declaration
Min = 8
Max = 2
fluc = np.full(10, np.nan)
scale = np.full(10, np.nan)
slope = np.nan
# (c) - SS function
def ss(series, period):
PI = 2.0 * np.arcsin(1.0)
SQRT2 = np.sqrt(2.0)
_lambda = PI * SQRT2 / period
a1 = np.exp(-_lambda)
coeff2 = 2.0 * a1 * np.cos(_lambda)
coeff3 = - np.power(a1, 2.0)
coeff1 = 1.0 - coeff2 - coeff3
filt1 = np.zeros_like(series)
for i in range(2, len(series)):
filt1[i] = coeff1 * (series[i] + (series[i - 1] if i - 1 >= 0 else 0)) * 0.5 + coeff2 * filt1[i - 1] + coeff3 * filt1[i - 2]
return filt1
# (d) - Calculations
def RMS(N1, N, csum):
seq = np.arange(1, N + 1)
y = csum[N1 : N1 + N]
sdx = np.std(seq) * np.sqrt(N / (N - 1))
sdy = np.std(y) * np.sqrt(N / (N - 1))
cov = np.cov(seq, y, bias=True)[0, 1] * (N / (N - 1))
r2 = np.power(cov / (sdx * sdy), 2)
rms = np.sqrt(1 - r2) * sdy
return rms
def Arms(bar, csum, barsize):
num = np.floor(barsize / bar).astype(int)
sumr = sum(RMS(i * bar, bar, csum) for i in range(num))
avg = np.log10(sumr / num)
return avg
def fs(x, barsize, Min, Max):
return np.round(Min * np.power(np.power(barsize / (Max * Min), 0.1111111111), x)).astype(int)
def hurst_exponent(close, barsize=100, slen=18, Min=8, Max=2):
# Calculate Log Return
r = np.log(close / np.roll(close, 1))
# Mean of Log Return
mean = np.convolve(r, np.ones(barsize) / barsize, mode="valid")
mean = np.pad(mean, (barsize - 1, 0), 'constant', constant_values=0)
# Calculate Cumulative Sum
csum = np.cumsum(r - mean)
# Set Ten Points of Root Mean Square Average along the Y log axis
fluc = np.array([Arms(fs(i, barsize, Min, Max), csum, barsize) for i in range(10)])
# Set Ten Points of data scale along the X log axis
scale = np.array([np.log10(fs(i, barsize, Min, Max)) for i in range(10)])
# Calculate Slope Measured From RMS and Scale on Log log plot using linear regression
slopes = np.array([np.cov(scale, fluc, bias=True)[0, 1] / np.var(scale, ddof=0) for i in range(len(close) - barsize + 1)])
# Calculate Moving Average Smoothed Hurst Exponent
smooth = ss(slopes, slen)
# Calculate Critical Value based on Confidence Interval (95% Confidence)
ci = 1.645 * (0.3912 / np.power(barsize, 0.3))
# Calculate Expected Value plus Critical Value
cu = 0.5 + ci
cd = 0.5 - ci
return slopes, smooth, cu, cd
# (e) - Plots
def plot_hurst_exponent(close, barsize=100, slen=18, Min=8, Max=2):
slopes, smooth, cu, cd = hurst_exponent(close, barsize, slen, Min, Max)
# Color of HE
c = "green" if slopes[-1] > cu else "blue" if slopes[-1] >= 0.5 else "red" if slopes[-1] < cd else "orange" if slopes[-1] < 0.5 else "black"
# Text of Table
text = "Significant Trend" if slopes[-1] > cu else "Trend" if slopes[-1] >= 0.5 else "Significant Mean Reversion" if slopes[-1] < cd else "Mean Reversion" if slopes[-1] < 0.5 else "N/A"
# Plotting
fig, ax = plt.subplots()
# Hurst Exponent
ax.plot(slope, label="Hurst Exponent", color=c, linewidth=2)
# Confidence Interval
ax.axhline(cu, label="Confidence Interval", color="purple", linestyle="--")
ax.axhline(cd, label="Confidence Interval", color="purple", linestyle="--")
# Moving Average
ax.plot(smooth, label="MA", color="gray", linewidth=1)
# 0.5 Mid Level
ax.axhline(0.5, color="black", linestyle="dashed")
# Display legend and text
ax.legend()
plt.title(f"Hurst Exponent: {slopes[-1]:.3f} ({text})")
print(f"Hurst Exponent: {slopes[-1]:.3f}")
print(f"Smoothed Hurst Exponent: {smooth[-1]:.3f}")
plt.show()
# Example usage
import yfinance as yf
# Fetch historical stock data for Apple Inc. (AAPL)
ticker = "AAPL"
data = yf.download(ticker, start="2020-01-01", end="2021-01-01")
# Use the 'Close' column for Hurst Exponent calculation
close_prices = data['Close'].values
plot_hurst_exponent(close_prices)