Indicator · Pine Script v5 · Own pane

Detrended Rhythm Oscillator

by regalouisei· code credited to WhenToTrade

The Detrended Rhythm Oscillator combines a Detrended Price Oscillator with ZigZag pivot logic to measure market cycle lengths and dominant swing rhythms.

View on GitHub

Repaints

GitHub stars2on the repository
Pine versionv5runs as is
Lines of code194
Settings13inputs
LicenceNone statedfile: MPL-2.0
Last update8 months ago25 Feb 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

194 lines of Pine Script v5 from pinescript/editors_picks/detrended-rhythm-oscillator-dro.pine, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

detrended-rhythm-oscillator-dro.pine
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
//
// Detrended Rhythm Oscillator (DRO)
// ----------------------------------
// Uses a Detrended Price Oscillator to calculate detrended "zig zag" swings and plots the distance between the last High-High and Low-Low 
//
// Purpose:
// A tool to measure and visualize the current dominant cycle (or rhythm/beat) in the dataset
// Look for common length numbers on the Zig Zag turns to determine the current dominant cycle length, also check for harmonics (2x, 3x)
// You can use the spotted length to optimize the length parameter of technical indicators (use the length or half of the length as input for any technical indicator)
// A technical indicator with the "correct" length setting in regards to the current dominant cycle will provide much better signals
//
// References:
// Zig Zag array functions used from open-source user "LonesomeTheBlue", script Name: "Double Zig Zag with HHLL"
//
// v.1.1 
//  - fixed a bug when showing the distance lables
//  - added option to turn on/off detrending pre-processing
// v.2
//  - converted to pine v5
//  - added a final median score as label and data stream
//
// © WhenToTrade

//@version=5
indicator('Detrended Rhythm Oscillator', shorttitle='DRO', max_lines_count=30, overlay=false)

// { Inputs
// -----------------------------------------------------------------------------
indiversion = input.string('DRO', '==== Select Indicator ====', options=['DRO', 'DPO', 'BOTH'])
src = input(close, '  Source')


detrendS = input.string('On', '  Detrend', options=['On', 'Off'], group='DPO', inline='de')

fastLength = input.int(3, '  Fast  - ', group='DPO', inline='l')
slowLength = input.int(50, ' Slow:', group='DPO', inline='l')

//... ZigZag
zigperiod = input.int(30, '  Period', minval=5, group='ZigZag', inline='p')
zigstyle = input.string('Dotted', '  Line -', options=['Solid', 'Dotted'], group='ZigZag', inline='line')
zigwidth = input.int(2, ' Width  -- ', options=[1, 2, 3], group='ZigZag', inline='line')

showdistanceS = input.string('On', '  Labels', options=['On', 'Off'], group='ZigZag', inline='switch')
showdetrendedS = input.string('On', ' Detrended', options=['On', 'Off'], group='ZigZag', inline='switch')

upcolor = input.color(defval=#363A45, title='  Colors:  Up', group='ZigZag', inline='color')
downcolor = input.color(defval=#363A45, title=' Down', group='ZigZag', inline='color')
txtcol = input.color(defval=#787B86, title=' Text', group='ZigZag', inline='color')

//... Switches
showdistance = showdistanceS == 'On' ? true : false
showdetrended = showdetrendedS == 'On' ? true : false
detrenddata = detrendS == 'On' ? true : false
displayDPOdata = (indiversion == 'DPO') ? display.all : (indiversion == 'BOTH') ? display.all : display.none 
// } Inputs

// { Variables
// -----------------------------------------------------------------------------
var max_array_size = 15
var ziggyzags = array.new_float(0)
var zzl = array.new_float(0)
var cycles = array.new_float(0)
var dir1 = 0
var lastPivot = 0
var lastPivotDistance = 0
float domcycle = 0
// } Variables

// { Functions
// -----------------------------------------------------------------------------

add_to_zigzag(pointer, value, bindex) =>
    array.unshift(pointer, bindex)
    array.unshift(pointer, value)
    if array.size(pointer) > max_array_size
        array.pop(pointer)
        array.pop(pointer)

update_zigzag(pointer, value, bindex, dir) =>
    if array.size(pointer) == 0
        add_to_zigzag(pointer, value, bindex)
    else
        if dir == 1 and value > array.get(pointer, 0) or dir == -1 and value < array.get(pointer, 0)
            array.set(pointer, 0, value)
            array.set(pointer, 1, bindex)
        0.

// } Functions

// { Calculations
// -----------------------------------------------------------------------------

//... DPO - Detrending
ppo = if detrenddata
    100 * (ta.ema(src, fastLength) - ta.ema(src, slowLength)) / ta.ema(src, slowLength)
else
    ppo = src
    ppo

//... ZigZag
float highs = ta.highestbars(ppo, zigperiod) == 0 ? ppo : na
float lows = ta.lowestbars(ppo, zigperiod) == 0 ? ppo : na

iff_1 = bool(lows) and na(highs) ? -1 : dir1
dir1 := bool(highs) and na(lows) ? 1 : iff_1

dir1changed = ta.change(dir1)
if bool(highs) or bool(lows)
    if bool(dir1changed)
        add_to_zigzag(ziggyzags, dir1 == 1 ? highs : lows, bar_index)
    else
        update_zigzag(ziggyzags, dir1 == 1 ? highs : lows, bar_index, dir1)

// } Calculations

// { Plots
// -----------------------------------------------------------------------------

//... Detrended Price Oscillator
plot(indiversion == 'DPO' or indiversion == 'BOTH' ? ppo : na, display = displayDPOdata )

//... Detrended Zig-Zag lines with distance labels
if array.size(ziggyzags) >= 6
    var line zzline1 = na
    var label zzlabel1 = na
    float val = array.get(ziggyzags, 0)
    int point = math.round(array.get(ziggyzags, 1))
    if bool(ta.change(val)) or bool(ta.change(point))
        float val1 = array.get(ziggyzags, 2)
        int point1 = math.round(array.get(ziggyzags, 3))
        plabel = '?'
        lastPivotDistance := point - lastPivot
        lastPivot := point1

        if ta.change(val1) == 0 and ta.change(point1) == 0
            line.delete(zzline1)
            label.delete(zzlabel1)

            if array.size(zzl) > 1
                lastDistance = array.get(zzl, 1)
                plabel := str.tostring(lastDistance + lastPivotDistance)
                plabel

            if array.size(zzl) > 0
                array.shift(zzl)
            0.
        else
            if array.size(zzl) > 0
                lastPivotDistance := point - lastPivot
                if array.size(zzl) > 1
                    int nw = math.round(array.get(zzl, 0))
                    plabel := str.tostring(lastPivotDistance + nw)
                    plabel
                0
            else
                if array.size(zzl) > 0
                    array.shift(zzl)
                0
            0.

        if indiversion == 'DRO' or indiversion == 'BOTH'
            zzline1 := line.new(x1=point, x2=point1, y1=showdetrended ? dir1 == 1 ? 100 : -100 : val, y2=showdetrended ? dir1 == 1 ? -100 : 100 : val1, color=dir1 == 1 ? upcolor : downcolor, width=zigwidth, style=zigstyle == 'Solid' ? line.style_solid : line.style_dotted)
            zzline1

        if (indiversion == 'DRO' or indiversion == 'BOTH') and showdistance
            zzlabel1 := label.new(x=point, y=showdetrended ? dir1 == 1 ? 100 : -125 : val, text=plabel, textcolor=txtcol, style=label.style_none)
            array.unshift(zzl, lastPivotDistance)

//... zzl hold the lengths of the distance between h-h, l-l / only need the last 3 points
if array.size(zzl) > max_array_size
    array.pop(zzl)

// ...calculate median cycle length (ignore last leg, as this one is not completed)
if array.size(zzl)>3
    array.clear(cycles)
    for i = 1 to array.size(zzl) - 2 
        z1 = array.get(zzl, i)
        z2 = array.get(zzl, i+1)
        cl = z1+z2
        cycles.push(cl)
    
    domcycle := array.median(cycles)

//... Alert data stream for input of another indicator, or for data export
plot(domcycle > 0 ? domcycle : na , "Mean Dominant Cycle Length", display=display.data_window) 

// } Plots and alert data stream      

//... show final label
if barstate.islast
    //log.info("--Dominant median cycle length: " + str.tostring(domcycle))
    if (domcycle>0)
        label.new(chart.point.now(0), "Mean: "+str.tostring(domcycle), color = color.teal, textcolor = color.white, style = label.style_label_left, size=size.small)

Overview

Detrended Rhythm Oscillator plots detrended oscillations in a separate pane along with dynamic distance labels between swing highs and lows. Users can switch between displaying the oscillator, standard detrended price oscillator data, or both, while adjusting fast, slow, and ZigZag periods. Because the underlying ZigZag calculations confirm pivots retroactively, swing lines and distance labels update on past bars as price moves.

Details

Published by

regalouisei on GitHub

Code credit

WhenToTrade, as written in the script's header

Type

Indicator, Pine Script v5

Draws

Separate pane below the chart

Category

Oscillators & momentum, Support & resistance

Markets

Any symbol

Timeframes

Any timeframe

Risk Notes

  • Signals can change on the live bar or after it closes (repainting)

Settings

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

SettingTypeDefaultVariable
==== Select Indicator ====stringDROindiversion
  Sourcesourceclosesrc
  DetrendstringOndetrendS
  Fast - int3fastLength
 Slow:int50slowLength
  Periodint30zigperiod
  Line -stringDottedzigstyle
 Width -- int2zigwidth
  LabelsstringOnshowdistanceS
 DetrendedstringOnshowdetrendedS
  Colors:  Upcolor#363A45upcolor
 Downcolor#363A45downcolor
Show all 13 settings
SettingTypeDefaultVariable
 Textcolor#787B86txtcol
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

114 stars

This indicator combines a scaled Forecast Oscillator with a Point of Force state detector. The Forecast Oscillator calculates the percentage deviation of price from its linear regression curve and normalizes the output between 0 and 1, plotting a line in a sep

Indicator v4 Oscillators & momentumSupport & resistance Repaints

CCI DC

by be-thomas
65 stars

This indicator plots the Commodity Channel Index in a separate pane and surrounds it with a Donchian channel whose lookback length changes with volatility. It measures volatility as a ratio of short and long ATR values, then ranks that ratio and shortens or le

Indicator v5 Oscillators & momentumVolatility & bands
Updated 4 days ago · GPL-3.0 GitHub repository of CCI DC

WaveTrend 3D

by nersent
29 stars

WaveTrend 3D is a redesigned oscillator that implements the classic WaveTrend algorithm with kernel-based regression for trend reversal confirmation and multi-timeframe support. It calculates a smoothed oscillator line and displays a symmetrical three-dimensio

Indicator v5 Oscillators & momentumDivergence
Updated 11 months ago · Custom licence GitHub repository of WaveTrend 3D

Benso Pendulum

by benso87
24 stars

Benso Pendulum is an indicator that plots a customizable Relative Strength Index (RSI) oscillator in a separate pane on the price chart. It calculates the RSI using the specified source and period, applying dynamic colors that change based on overbought and ov

Indicator v5 Oscillators & momentumSupport & resistance
Updated 4 years ago · MPL-2.0 GitHub repository of Benso Pendulum
9 stars

This indicator plots currency strength values for EUR, USD, JPY, GBP, CHF, AUD, CAD, NZD and optionally XAU or XAG on a separate pane. It computes strength by comparing current pair closes to daily closes using broker-specific data from OANDA, FX or FOREXCOM.

Indicator v4 Oscillators & momentumSupport & resistance Repaints
Updated 5 years ago · None stated GitHub repository of 4th Dimension Currency Strength

More indicators

Trade Your Charts With a Regulated Broker

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

Compare brokers