Financial markets exhibit shifting personalities, transitioning
between periods of directional trending and phases of mean-reverting
chop. Traditional moving averages, with their fixed lookback periods,
often struggle to keep pace, either lagging in strong trends or
generating excessive signals in ranging markets. This article
investigates a strategy employing a Fractal Adaptive Moving
Average (FRAMA)—specifically, a simplified version driven by
the Hurst exponent (FRAMA_H). The core idea is to
dynamically adjust the moving average’s responsiveness based on an
estimate of the market’s “long-term memory.” We’ll explore its
construction using Python, the trading logic based on crossovers, and
interpret the findings from a backtest on BTC-USD
from 2021
to 2024.
The Hurst exponent (H) is a statistical measure used to classify time series. It quantifies the degree of long-range dependence or “memory” in a series:
By calculating H over a rolling window, we can attempt to gauge the market’s current character. The strategy uses these thresholds:
Snippet 1: Calculating the Rolling Hurst Exponent
The script calculates the Hurst exponent on log-returns using the
hurst
library. A rolling window of 128 periods is used for
BTC-USD
.
from hurst import compute_Hc # Ensure 'pip install hurst'
# --- Parameters from the script ---
# hurst_lookback_window = 128
# --- Column Name ---
# hurst_col = f"Hurst_{hurst_lookback_window}d"
def get_hurst(series):
"""
series: 1D array of log-returns, length >=100 (as per script, though compute_Hc min is lower)
returns: H
"""
# compute_Hc needs a series with some variance and sufficient length
if len(series) < 100 or np.std(series) == 0: # Script uses 100, compute_Hc default min is ~20
return np.nan
# Using 'change' for log-returns, 'price' for price series
= compute_Hc(series, kind='change', simplified=True)
H_val, c, data return H_val
print("Calculating rolling Hurst exponent (this may take a moment)...")
# Rolling apply on log-returns
= np.log(df['Close']).diff().dropna() # Calculate log returns
log_rets = (
df.loc[log_rets.index, hurst_col]
log_rets=hurst_lookback_window)
.rolling(windowapply(get_hurst, raw=False) # raw=False to pass Series object
.
)
# Fill start-up NaNs: forward fill, then assume neutral H=0.5
='ffill', inplace=True)
df[hurst_col].fillna(method0.5, inplace=True) df[hurst_col].fillna(
This function is applied to a rolling window of log-returns to
generate the hurst_col
. Initial NaNs are forward-filled and
then any remaining (at the very start) are set to a neutral 0.5.
Instead of a complex recursive FRAMA formula, this strategy creates an adaptive EMA (FRAMA_H) by selecting one of three pre-defined Exponential Moving Averages (EMAs) based on the previous day’s Hurst exponent:
Snippet 2: Selecting the Adaptive EMA (FRAMA_H)
# --- Parameters from the script ---
# ema_short_window = 7
# ema_medium_window = 30
# ema_long_window = 90
# h_trending_threshold = 0.6
# h_reverting_threshold = 0.4
# --- Column Names ---
# ema_short_col = f"EMA_{ema_short_window}"
# ema_medium_col = f"EMA_{ema_medium_window}"
# ema_long_col = f"EMA_{ema_long_window}"
# frama_h_col = "FRAMA_H"
# hurst_col = ... (previously defined)
# Calculate the three underlying EMAs
= df['Close'].ewm(span=ema_short_window, adjust=False).mean()
df[ema_short_col] = df['Close'].ewm(span=ema_medium_window, adjust=False).mean()
df[ema_medium_col] = df['Close'].ewm(span=ema_long_window, adjust=False).mean()
df[ema_long_col]
# Create FRAMA_H by selecting EMA based on lagged Hurst Exponent
= df[hurst_col].shift(1) # Use yesterday's H for today's EMA choice
h_shift = np.select(
df[frama_h_col] > h_trending_threshold, # Condition for trending
[h_shift < h_reverting_threshold], # Condition for mean-reverting
h_shift # Choices for these conditions
[df[ema_short_col], df[ema_long_col]], =df[ema_medium_col] # Default choice (medium EMA for neutral H)
default )
This FRAMA_H
line then becomes the dynamic reference for
our trading signals.
The strategy employs a simple price crossover system with the FRAMA_H:
pc
) was above the previous
day’s FRAMA_H (ph
), a bullish state is indicated.pc
was below ph
, a bearish state is
indicated.active_pos
(or if active_pos
is flat), a trade
is initiated at the current day’s open (o
).pc > ph
.pc < ph
.entry_price - (atr_multiplier_sl * prev_atr_sl)
.entry_price + (atr_multiplier_sl * prev_atr_sl)
.ts
) is trailed daily: for longs,
ts = max(ts, current_close - atr_multiplier_sl * current_atr_sl)
,
and symmetrically for shorts. The script uses
atr_multiplier_sl = 1.0
, which is a relatively tight
stop.The backtest loop iterates through each day, checks for stop-loss hits, then evaluates entry/flip conditions, calculates P&L, and updates the trailing stop.
The provided script output for BTC-USD
from January 2021
to December 2024, with a 128-day Hurst window and EMA periods of
7/30/90, yielded the following key metrics:
Observations and Research Insights:
kind='change'
might provide more dynamic H values than previous iterations.atr_multiplier_sl = 1.0
): This is a very tight
stop. While it can cut losses quickly, it can also lead to premature
exits from potentially profitable trades, especially in volatile assets
like BTC-USD. The high number of trades often resulting from tight stops
would also incur significant transaction costs in live trading. The
impact of this tight stop on the observed performance is a key area for
investigation; it may have fortuitously navigated BTC-USD’s volatility
during this period.The FRAMA_H concept, by attempting to tailor the moving average’s lookback period to the market’s diagnosed “memory” state, is an intelligent step beyond static indicators.
Potential Strengths:
Critical Considerations and Future Research Directions:
hurst_lookback_window
(128 days), the
kind
parameter in compute_Hc
(now
'change'
for log-returns), and the minimum series length
for get_hurst
(100 bars) all influence this. Further
research could explore different Hurst estimation techniques or lookback
periods.atr_multiplier_sl
(currently The Hurst-adaptive EMA (FRAMA_H) crossover strategy, as backtested on
BTC-USD
for 2021-2024, demonstrates a potentially effective
approach to navigating this volatile asset, outperforming a buy-and-hold
strategy on both absolute and risk-adjusted terms under the specified
parameters. The dynamic adjustment of the moving average based on market
memory, as estimated by the Hurst exponent, is the core innovation.
However, the promising results, particularly with a very tight
stop-loss, warrant careful scrutiny regarding parameter sensitivity,
robustness, and the impact of real-world trading frictions. This
framework provides a solid foundation for further quantitative research
into adaptive trading systems.