The Adaptive Cycle Detector estimates the dominant market cycle from WaveTrend oscillator zero-crossing frequency and applies smoothing to recommend adaptive indicator lengths.
//@version=6
// ============================================================================
// Adaptive Cycle Detector [WavesUnchained]
// Version: 1.0.2
// Author: WavesUnchained
// Build: 2026-10-02 00:00:00
// ============================================================================
// Estimates the dominant market cycle from WT/StochRSI zero-crossing frequency
// and recommends adaptive indicator lengths. Fast markets (frequent oscillator
// crossings) get shorter lengths; slow swing markets get longer lengths.
//
// Also measures ATR regime (volatility trend) and trend clarity (ADX) to further
// adapt length recommendations. Pure Hilbert-Transform is approximated with a
// zero-crossing period estimator, which is accurate enough for length adaptation.
//
// Features:
// ✓ Dominant cycle estimation via WT zero-crossing period (bars between crossings)
// ✓ ATR regime: rising / falling / flat volatility (supports cycle length)
// ✓ Trend clarity via ADX (trend vs chop mode)
// ✓ Adaptive length recommendations for WT, StochRSI, MFI
// ✓ Smoothed cycle period plot with min/max bounds
// ✓ Dashboard with current period estimate and recommendations
//
// ============================================================================
indicator("Adaptive Cycle Detector [WavesUnchained]", "Adapt Cycle [WU]", format=format.price, precision=2, overlay=false)
// ─── Inputs ───────────────────────────────────────────────────────────────────
grCyc = "Cycle Detection"
cycLookback = input.int(100, "Lookback for Crossing Count", minval=20, maxval=500, group=grCyc,
tooltip="How many bars to look back when counting WT zero crossings. Longer = smoother estimate, less responsive.")
cycSmooth = input.int(5, "Period Estimate Smoothing", minval=1, maxval=20, group=grCyc)
minPeriod = input.int(8, "Minimum Cycle Period", minval=4, maxval=30, group=grCyc)
maxPeriod = input.int(60, "Maximum Cycle Period", minval=20, maxval=200, group=grCyc)
grWT = "Base WaveTrend"
wtChLen = input.int(10, "Channel Length", minval=1, group=grWT)
wtAvLen = input.int(21, "Average Length", minval=1, group=grWT)
grAdx = "ADX"
adxLen = input.int(14, "ADX Length", minval=5, group=grAdx)
trendThr = input.int(25, "Trend Threshold", minval=10, maxval=50, group=grAdx,
tooltip="ADX above this value indicates a trending market — favor longer adaptive lengths to catch the full swing.")
grD = "Display"
showDash = input.bool(true, "Show Dashboard", group=grD)
showBand = input.bool(true, "Show Min/Max Band", group=grD)
// ─── Base oscillators ─────────────────────────────────────────────────────────
_wtEsa = ta.ema(hlc3, wtChLen)
_wtD = ta.ema(math.abs(hlc3 - _wtEsa), wtChLen)
_wtCi = (hlc3 - _wtEsa) / (0.015 * math.max(_wtD, syminfo.mintick))
wtOsc = ta.ema(_wtCi, wtAvLen)
atr14 = ta.atr(14)
atrMa = ta.sma(atr14, 20)
atrUp = atr14 > atrMa * 1.05
atrDn = atr14 < atrMa * 0.95
// ADX (third element of ta.dmi is the ADX line)
[_, _, _adx] = ta.dmi(adxLen, adxLen)
adx = _adx
trending = adx > trendThr
// ─── Cycle estimation via zero-crossing period ────────────────────────────────
// Count WT zero crossings in cycLookback bars
crossingUp = ta.crossover(wtOsc, 0)
crossingDown = ta.crossunder(wtOsc, 0)
anyCrossing = crossingUp or crossingDown
// Count crossings in lookback to estimate frequency (rolling sum — O(1) per bar)
crossCount = int(nz(math.sum(anyCrossing ? 1.0 : 0.0, cycLookback)))
// Half-period from crossing count: period = 2 * lookback / crossings (each half-cycle = one crossing)
rawPeriod = crossCount > 0 ? int(2.0 * cycLookback / crossCount) : maxPeriod
clampedPeriod = math.max(minPeriod, math.min(maxPeriod, rawPeriod))
// Smooth the period estimate
cycPeriod = ta.ema(float(clampedPeriod), cycSmooth)
// ─── Adaptive length recommendations ─────────────────────────────────────────
// Base length = period / 4 (quarter-cycle is the standard WT channel length)
// Trend mode: longer (period / 3), Chop mode: shorter (period / 5)
baseLen = int(cycPeriod / 4.0)
trendFactor = trending ? 1.25 : atrDn ? 0.80 : 1.0
adaptLen = math.max(5, math.min(50, int(baseLen * trendFactor)))
recWT = adaptLen
recSt = math.max(8, math.min(30, int(cycPeriod / 3.0)))
recMfi = math.max(7, math.min(30, int(cycPeriod / 4.0)))
// ─── Plots ────────────────────────────────────────────────────────────────────
// Reference hlines for typical cycle lengths
hline(8, "Fast Market", color=color.new(color.green, 60), linestyle=hline.style_dotted)
hline(21, "Normal", color=color.new(color.gray, 50), linestyle=hline.style_dashed)
hline(40, "Slow Swing", color=color.new(color.orange, 60), linestyle=hline.style_dotted)
color cycColor = color.new(#ef5350, 0)
if cycPeriod < 12
cycColor := color.new(#00c853, 0)
else if cycPeriod < 28
cycColor := color.new(#ffd600, 0)
plot(cycPeriod, "Cycle Period", color=cycColor, linewidth=2)
plot(showBand ? float(minPeriod) : na, "Min Period", color=color.new(color.gray, 70), linewidth=1)
plot(showBand ? float(maxPeriod) : na, "Max Period", color=color.new(color.gray, 70), linewidth=1)
// Adaptive length recommendation line
plot(float(adaptLen), "Recommended WT Length", color=color.new(#2196F3, 30), linewidth=1, style=plot.style_line)
// Mark zero crossings
plotshape(crossingUp, "Cross Up", shape.triangleup, location.bottom, color.new(color.green, 20), size=size.tiny)
plotshape(crossingDown, "Cross Down", shape.triangledown, location.top, color.new(color.red, 20), size=size.tiny)
// ─── Dashboard ────────────────────────────────────────────────────────────────
color tc = color.new(color.gray, 20)
color hd = color.new(color.gray, 90)
var table t = table.new(position.top_right, 2, 7,
bgcolor=color.new(color.white, 5),
border_color=color.new(color.gray, 60), border_width=1,
frame_color=color.new(color.gray, 40), frame_width=1)
if showDash and barstate.islast
modeText = trending ? "Trend" : atrUp ? "Expansion" : atrDn ? "Compression" : "Normal"
modeColor = trending ? color.new(#00c853, 0) : atrDn ? color.new(#2196F3, 0) : tc
table.cell(t, 0, 0, "Adaptive Cycle", text_color=tc, bgcolor=hd, text_size=size.small)
table.cell(t, 1, 0, modeText, text_color=modeColor, bgcolor=hd, text_size=size.small)
table.cell(t, 0, 1, "Cycle Period", text_color=tc, text_size=size.small)
table.cell(t, 1, 1, str.tostring(math.round(cycPeriod)) + " bars",
text_color=cycColor, text_size=size.small)
table.cell(t, 0, 2, "ADX", text_color=tc, text_size=size.small)
table.cell(t, 1, 2, str.tostring(adx, "#.#"),
text_color=trending ? color.new(#00c853, 0) : tc, text_size=size.small)
// Separator
table.cell(t, 0, 3, "", bgcolor=color.new(color.gray, 80))
table.cell(t, 1, 3, "", bgcolor=color.new(color.gray, 80))
table.cell(t, 0, 4, "WT Length", text_color=tc, text_size=size.small)
table.cell(t, 1, 4, str.tostring(recWT), text_color=color.new(#2196F3, 0), text_size=size.small)
table.cell(t, 0, 5, "StochRSI Length", text_color=tc, text_size=size.small)
table.cell(t, 1, 5, str.tostring(recSt), text_color=color.new(#2196F3, 0), text_size=size.small)
table.cell(t, 0, 6, "MFI Length", text_color=tc, text_size=size.small)
table.cell(t, 1, 6, str.tostring(recMfi), text_color=color.new(#2196F3, 0), text_size=size.small)
Overview
Fast markets with frequent crossings receive shorter lengths while slow swing markets get longer ones. The script also measures ATR regime for volatility trend and ADX for trend clarity to refine length suggestions for WaveTrend, Stochastic RSI and MFI. It plots the smoothed cycle period estimate together with minimum and maximum bounds in a separate pane plus a dashboard with current estimate and recommendations. Inputs control lookback for crossing count, smoothing factor, minimum and maximum cycle periods, WaveTrend channel and average lengths and ADX settings. The script draws exclusively in a separate pane with no trading signals or position rules.
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Lookback for Crossing Count
int
100
cycLookback
Period Estimate Smoothing
int
5
cycSmooth
Minimum Cycle Period
int
8
minPeriod
Maximum Cycle Period
int
60
maxPeriod
Channel Length
int
10
wtChLen
Average Length
int
21
wtAvLen
ADX Length
int
14
adxLen
Trend Threshold
int
25
trendThr
Show Dashboard
bool
true
showDash
Show Min/Max Band
bool
true
showBand
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.
Manias, Panics and Crashes is a separate-pane market regime indicator that quantifies market sentiment and systemic risk. It processes technical metrics including RSI, moving averages, and volume alongside optional intermarket data such as VIX volatility and c
Elton's Screener is an indicator that runs in a separate pane and scores one of twenty selectable strategies, or auto-selects a route from the symbol type. It filters signals by a minimum confidence level and a higher-timeframe EMA trend alignment. ATR multipl
This indicator evaluates signal performance and multi-timeframe market dynamics through a shadow bankroll tracking framework. It measures market conditions across trend, momentum ROC Z-scores, ATR volatility expansion, and volume flow, alongside multi-timefram
Papan Gerak is a multi-dimension technical analysis dashboard aimed at Indonesian stocks. This base module declares the indicator in a separate pane and stores the display and calculation settings used by the dashboard. It covers table position, font size, com
The Market Regime Dashboard is a multi-timeframe dashboard indicator that evaluates market conditions across three user-defined timeframes. It measures four core dimensions: trend strength using ADX and DI direction, momentum using RSI z-scores, volatility via
This indicator evaluates market conditions across four user-defined timeframes by combining RSI, MACD crossovers, triple EMA trend filters, Bollinger Band boundaries, and volume spikes. It computes aggregate bullish and bearish scores, plotting buy and sell si