Indicator · Pine Script v5 · Own pane

AHM v2 Adaptive Hurst Regime

by AssetWhisper

An oscillator in a separate pane that estimates the Hurst exponent with rescaled range analysis over three window sizes, with optional bias correction and smoothing.

View on GitHub

GitHub stars1on the repository
Pine versionv5runs as is
Lines of code147
Settings11inputs
LicenceMPL-2.0
Last update7 weeks ago10 Sep 2026

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.

Source Code

147 lines of Pine Script v5 from indicators/AHM_Adaptive_Hurst_Regime.pine, published under MPL-2.0. Keep the author's credit and licence lines when you reuse it.

AHM_Adaptive_Hurst_Regime.pine
//@version=5
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © AssetWhisper — https://assetwhisper.com/indicators/
// ============================================================================
//  AHM v2 — Adaptive Hurst Regime
//  Idea: el exponente de Hurst H distingue series persistentes (H > 0.5: tendencia),
//  antipersistentes (H < 0.5: reversión a la media) y paseo aleatorio (H ≈ 0.5).
//  v2 estima H con análisis R/S en tres escalas (N, N/2, N/4) y ajuste por mínimos
//  cuadrados de log(R/S) vs log(n), corrige el sesgo de muestra finita (Anis-Lloyd
//  con corrección de Peters) y aplica histéresis al régimen. El oscilador híbrido
//  pondera momentum vs mean-reversion con H y solo emite señales cuando el régimen
//  no es aleatorio.
// ============================================================================
indicator("AHM v2 — Adaptive Hurst Regime", shorttitle="AHM v2", overlay=false)

grpH = "Hurst"
hurstLen = input.int(128, "R/S window N (uses N, N/2, N/4)", minval=32, maxval=512, group=grpH)
hSmooth  = input.int(5, "H smoothing", minval=1, maxval=20, group=grpH)
trendTh  = input.float(0.55, "Trend threshold (H >)", minval=0.5, maxval=0.8, step=0.01, group=grpH)
mrTh     = input.float(0.45, "Mean-reversion threshold (H <)", minval=0.2, maxval=0.5, step=0.01, group=grpH)
hyst     = input.float(0.02, "Hysteresis", minval=0.0, maxval=0.1, step=0.005, group=grpH)
biasCorr = input.bool(true, "Anis-Lloyd bias correction", group=grpH)
grpO = "Oscilador"
momLen    = input.int(14, "Momentum RSI length", minval=5, maxval=50, group=grpO)
mrLen     = input.int(20, "Mean-reversion z-score length", minval=10, maxval=100, group=grpO)
oscSmooth = input.int(3, "Oscillator smoothing", minval=1, maxval=10, group=grpO)
sigLvl    = input.int(30, "MR signal level (±)", minval=10, maxval=50, group=grpO)
showH     = input.bool(true, "Show H (scaled)", group=grpO)

lr = math.log(close / close[1])

// R/S clásico sobre las últimas n barras de log-retornos
rsStat(n) =>
    m = 0.0
    for i = 0 to n - 1
        m += nz(lr[i])
    m /= n
    cum = 0.0
    mx  = 0.0
    mn  = 0.0
    ss  = 0.0
    for i = n - 1 to 0
        d = nz(lr[i]) - m
        cum += d
        mx := math.max(mx, cum)
        mn := math.min(mn, cum)
        ss += d * d
    s = math.sqrt(ss / n)
    s > 0 ? (mx - mn) / s : na

// E[R/S] de un paseo aleatorio (Anis-Lloyd, aproximación para n grande, corrección de Peters)
expectedRS(n) =>
    acc = 0.0
    for i = 1 to n - 1
        acc += math.sqrt((n - i) / i)
    (n - 0.5) / n * math.pow(n * math.pi / 2, -0.5) * acc

// Pendiente por mínimos cuadrados de 3 puntos
slope3(x1, y1, x2, y2, x3, y3) =>
    xm = (x1 + x2 + x3) / 3
    ym = (y1 + y2 + y3) / 3
    num = (x1 - xm) * (y1 - ym) + (x2 - xm) * (y2 - ym) + (x3 - xm) * (y3 - ym)
    den = (x1 - xm) * (x1 - xm) + (x2 - xm) * (x2 - xm) + (x3 - xm) * (x3 - xm)
    den > 0 ? num / den : na

n1 = hurstLen
n2 = math.max(16, math.floor(hurstLen / 2))
n3 = math.max(8, math.floor(hurstLen / 4))

rs1 = rsStat(n1)
rs2 = rsStat(n2)
rs3 = rsStat(n3)
var float ers1 = expectedRS(n1)
var float ers2 = expectedRS(n2)
var float ers3 = expectedRS(n3)

lx1 = math.log(n1)
lx2 = math.log(n2)
lx3 = math.log(n3)
hRaw = slope3(lx1, math.log(rs1), lx2, math.log(rs2), lx3, math.log(rs3))
hRw  = slope3(lx1, math.log(ers1), lx2, math.log(ers2), lx3, math.log(ers3))
hAdj = biasCorr ? 0.5 + (hRaw - hRw) : hRaw
H    = ta.ema(math.max(0.0, math.min(1.0, nz(hAdj, 0.5))), hSmooth)

// === RÉGIMEN CON HISTÉRESIS: 1 tendencia, -1 reversión, 0 aleatorio ===
var int regime = 0
if regime != 1 and H > trendTh
    regime := 1
else if regime == 1 and H < trendTh - hyst
    regime := 0
if regime != -1 and H < mrTh
    regime := -1
else if regime == -1 and H > mrTh + hyst
    regime := 0

regimeLabel = regime == 1 ? "TREND" : regime == -1 ? "MEAN-REV" : "RANDOM"
regimeColor = regime == 1 ? color.green : regime == -1 ? color.red : color.gray

// === OSCILADOR HÍBRIDO ===
momOsc = ta.rsi(close, momLen) - 50                       // ±50
basis  = ta.sma(close, mrLen)
sd     = ta.stdev(close, mrLen)
z      = sd > 0 ? (close - basis) / sd : 0.0
mrOsc  = math.max(-50.0, math.min(50.0, -z * 25))         // +50 = 2σ por debajo (compra por reversión)
w      = math.max(-1.0, math.min(1.0, (H - 0.5) / 0.2))
trendW = (1 + w) / 2                                      // 0..1
hybrid = ta.ema(trendW * momOsc + (1 - trendW) * mrOsc, oscSmooth)

xUp0 = ta.crossover(hybrid, 0)
xDn0 = ta.crossunder(hybrid, 0)
xUpL = ta.crossover(hybrid, -sigLvl)
xDnU = ta.crossunder(hybrid, sigLvl)
buySignal  = regime == 1 ? xUp0 : regime == -1 ? xUpL : false
sellSignal = regime == 1 ? xDn0 : regime == -1 ? xDnU : false

// === VISUAL ===
hline(0, "Zero", color=color.gray)
hline(sigLvl, "MR upper", color=color.new(color.red, 60), linestyle=hline.style_dashed)
hline(-sigLvl, "MR lower", color=color.new(color.green, 60), linestyle=hline.style_dashed)
plot(hybrid, "Hybrid oscillator", color=regimeColor, linewidth=2)
plot(showH ? (H - 0.5) * 200 : na, "H (scaled: 0 = 0.5)", color=color.new(color.orange, 40), linewidth=1)
bgcolor(color.new(regimeColor, 93), title="Regime")
plotshape(buySignal, "Buy", style=shape.triangleup, location=location.bottom, color=color.green, size=size.small)
plotshape(sellSignal, "Sell", style=shape.triangledown, location=location.top, color=color.red, size=size.small)
plot(regime, "AHM regime (export: 1 trend / -1 MR / 0 random)", display=display.none)
plot(H, "Hurst H (export)", display=display.none)

var table t = table.new(position.top_right, 2, 7, bgcolor=color.new(color.black, 60))
table.cell(t, 0, 0, "REGIME", text_color=color.white, text_size=size.small)
table.cell(t, 1, 0, regimeLabel, bgcolor=color.new(regimeColor, 30), text_color=color.white, text_size=size.small)
table.cell(t, 0, 1, "H (adj.)", text_color=color.white, text_size=size.small)
table.cell(t, 1, 1, str.tostring(H, "#.###"), text_color=color.orange, text_size=size.small)
table.cell(t, 0, 2, "H raw / RW", text_color=color.white, text_size=size.small)
table.cell(t, 1, 2, str.tostring(hRaw, "#.##") + " / " + str.tostring(hRw, "#.##"), text_color=color.gray, text_size=size.small)
table.cell(t, 0, 3, "Trend weight", text_color=color.white, text_size=size.small)
table.cell(t, 1, 3, str.tostring(trendW * 100, "#") + "%", text_color=color.white, text_size=size.small)
table.cell(t, 0, 4, "R/S " + str.tostring(n1) + "/" + str.tostring(n2) + "/" + str.tostring(n3), text_color=color.white, text_size=size.small)
table.cell(t, 1, 4, str.tostring(rs1, "#.#") + " / " + str.tostring(rs2, "#.#") + " / " + str.tostring(rs3, "#.#"), text_color=color.gray, text_size=size.small)
table.cell(t, 0, 5, "z (MR)", text_color=color.white, text_size=size.small)
table.cell(t, 1, 5, str.tostring(z, "#.##"), text_color=z > 2 ? color.red : z < -2 ? color.lime : color.white, text_size=size.small)
table.cell(t, 0, 6, "Signals", text_color=color.white, text_size=size.small)
table.cell(t, 1, 6, regime == 1 ? "0-cross (trend)" : regime == -1 ? "±" + str.tostring(sigLvl) + " (MR)" : "off (random)", text_color=regimeColor, text_size=size.small)

// === ALERTAS ===
alertcondition(regime != regime[1], "AHM Regime change", "AHM v2: Hurst regime changed")
alertcondition(buySignal, "AHM Buy", "AHM v2: buy signal for current regime")
alertcondition(sellSignal, "AHM Sell", "AHM v2: sell signal for current regime")

Overview

Hysteresis around trend and mean-reversion thresholds classifies the market as trending, mean-reverting or random. A hybrid oscillator weights RSI momentum against a mean-reversion z-score by that exponent and shows signals only when the regime is not random. Settings cover window, thresholds and oscillator lengths.

Details

Published by

AssetWhisper on GitHub

Draws

Separate pane below the chart

Markets

Any symbol

Timeframes

Any timeframe

Risk Notes

No repainting, lookahead or stacked entries were found in its code or README. That says nothing about how well it works: test it first.

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
R/S window N (uses N, N/2, N/4)int128hurstLen
H smoothingint5hSmooth
Trend threshold (H >)float0.55trendTh
Mean-reversion threshold (H <)float0.45mrTh
Hysteresisfloat0.02hyst
Anis-Lloyd bias correctionbooltruebiasCorr
Momentum RSI lengthint14momLen
Mean-reversion z-score lengthint20mrLen
Oscillator smoothingint3oscSmooth
MR signal level (±)int30sigLvl
Show H (scaled)booltrueshowH
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

261 stars

This indicator calculates standardized z-scores of price data across four different lookback periods (6, 14, 26, and 100). Standardizing data subtracts the moving average from the price and divides by the standard deviation. It applies a Double Exponential Mov

Indicator v5 Mean reversionVolatility & bands
114 stars

This indicator calculates normalized percentage bounds around price highs and lows to anticipate local trend reversals. It oscillates within a fixed -1 to 1 channel and plots two boundary tracking curves alongside a background Karobein oscillator histogram for

Indicator v4 Mean reversionOscillators & momentum Repaints
Updated 1 year ago · None stated GitHub repository of Mean Reversion Indicator
99 stars

This indicator calculates the statistical autocorrelation coefficient of an input series over a user-defined lookback window at a specified lag. It tracks values using an internal circular buffer, computes sample mean, variance, and autocovariance, and normali

Indicator v6 TrendMean reversion
Updated 4 weeks ago · Apache-2.0 GitHub repository of Autocorrelation Function (ACF)

Pretty Good Oscillator

by mihakralj
47 stars

The Pretty Good Oscillator calculates the distance between the selected price source and its simple moving average, normalized by the Average True Range over a specified period. It renders as an oscillator line in a separate pane alongside a zero line and thre

Indicator v6 Oscillators & momentumMean reversion
Updated 8 months ago · MIT GitHub repository of Pretty Good Oscillator

Calculates a composite carry and decay oscillator fusing impulse-autocorrelation half-life, a Hurst exponent approximation, path efficiency, and range expansion ratios. It evaluates whether an impulse is likely to persist or mean-revert, plotting the oscillato

Indicator v6 TrendOscillators & momentum
Updated 2 days ago · MIT GitHub repository of Market Memory & Decay Oscillator [WavesUnchained]

More Oscillators & momentum scripts

Related on ForexR

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers