On this page
Detrended Price Oscillator (DPO) - Built-in Indicator Guide
Computes Detrended Price Oscillator as difference between price and a shifted simple moving average, with optional centering.
| Language | Indie Script v5 |
| Platform | TakeProfit |
| Category | Oscillators |
| Type | Built-in indicator |
| Author | TakeProfit |
| License | MIT |
| Documentation | Built-in indicators |
| Source file | Detrended Price Oscillator.indie5 |
Overview
The Detrended Price Oscillator (DPO) removes the trend component from price by subtracting a simple moving average that is shifted in time. It is meant for identifying cyclical swings around a trend, showing whether price is relatively above or below the middle of the averaging window.
The indicator draws a green line that oscillates around a grey zero level. A length parameter controls the averaging period, and a centered boolean switches between comparing current price to an older moving average and comparing older price to the current moving average.
How it works
- Read user parameters
lengthandcenteredfrom the settings UI. - Compute
bars_back = length // 2 + 1, the number of bars used for the time shift. - Create a simple moving average of
closeoverlengthbars usingSma.new, which returns a series. - If
centeredis false, subtract the SMA value frombars_backbars ago from the current close:close[0] - ma[bars_back]. - If
centeredis true, subtract the current SMA from the close valuebars_backbars ago:close[bars_back] - ma[0]. - Return the result as a green line with
offset=-bars_backwhen centered, otherwise withoffset=0.
Mathematical model
Let (n = \text{length}), (b = \lfloor n/2 \rfloor + 1), and (\text{SMA}t = \frac{1}{n}\sum{i=0}^{n-1} \text{close}_{t-i}).
Non-centered mode:
\[\text{DPO}_t = \text{close}_t - \text{SMA}_{t-b}\]Centered mode:
\[\text{DPO}_t = \text{close}_{t-b} - \text{SMA}_t\]In centered mode the plotted value is also shifted left by (b) bars via the offset parameter.
Logic flow
flowchart TD
A["Start"] --> B["Read length and centered"]
B --> C["bars_back = length // 2 + 1"]
C --> D["ma = Sma.new(close, length)"]
D --> E{"centered is true"}
E -- "yes" --> F["dpo = close[bars_back] - ma[0]"]
E -- "no" --> G["dpo = close[0] - ma[bars_back]"]
F --> H["offset = -bars_back"]
G --> I["offset = 0"]
H --> J["plot.Line(dpo, offset)"]
I --> J
Parameters
| Parameter | Type | Default | Range | Description |
|---|---|---|---|---|
length |
int | 21 | ≥ 1 | |
centered |
bool | false | Centered |
Code walkthrough
Indicator declaration and settings
Lines 6-10 of Detrended Price Oscillator.indie5:
@indicator('DPO', format=format.PRICE) # Detrended Price Oscillator
@param.int('length', default=21, min=1)
@param.bool('centered', default=False, title='Centered')
@level(0, line_color=color.GRAY, title='Zero')
@plot.line(color=color.GREEN, title='Detrended Price Oscillator')
The decorators define the indicator name, price formatting, the integer length parameter with a default of 21, a boolean centered option, a grey zero level, and a green line plot. These decorators generate the settings UI and the chart appearance.
Shift calculation
Lines 11-13 of Detrended Price Oscillator.indie5:
def Main(self, length, centered):
bars_back = length // 2 + 1
ma = Sma.new(self.close, length)
bars_back is derived from length using integer division; it determines how far the price or moving average is shifted. Sma.new(self.close, length) creates a simple moving average series that can be indexed with [0] for the current bar or [bars_back] for earlier bars.
DPO modes and output
Lines 14-15 of Detrended Price Oscillator.indie5:
dpo = self.close[bars_back] - ma[0] if centered else self.close[0] - ma[bars_back]
return plot.Line(dpo, offset=-bars_back if centered else 0)
The conditional expression selects the centered or non-centered formula. plot.Line(dpo, offset=-bars_back if centered else 0) draws the value as a line; the negative offset in centered mode shifts the plot left by bars_back bars so the oscillator aligns with the center of the moving average window.
Reading the chart
- The grey horizontal line marks the zero level.
- The green line is the DPO value; positive values sit above zero, negative values below.
- In default non-centered mode, a positive value means the current close is above the simple moving average from
bars_backbars ago. - In centered mode, the value at a chart bar is the close from
bars_backbars earlier minus the moving average calculated later, shifted left so it compares price with the center of the window. - Zero crossings occur when the relationship between price and the shifted moving average changes sign.
Implementation notes
bars_back = length // 2 + 1uses integer division, so the shift is not always exactly half the period.- In centered mode, the plotted value at a historical bar is based on data from later bars because it uses the current SMA and then offsets the line backward.
ma[bars_back]indexes the SMA series frombars_backbars ago, whileclose[bars_back]indexes historical close prices.- The
format=format.PRICEdecorator causes the oscillator values to be displayed using price formatting.
FAQ
How do I change the smoothing period?
Set the length parameter to any integer greater than or equal to 1. The default is 21, and bars_back is recalculated automatically from this value.
What does the centered option do?
When centered is true, the DPO uses a close value from bars_back bars ago minus the current SMA and plots the line with a negative offset. When false, it uses the current close minus the SMA from bars_back bars ago with no offset.
Does this indicator generate buy or sell signals?
No. The script only plots the oscillator line and a zero level. It contains no signal logic, alerts, or trade entries.
Full source code
Indie Script v5, the built-in indicator as shipped with TakeProfit. Add it from the indicators menu or copy the code into the platform’s script editor.
# indie:lang_version = 5
from indie import indicator, format, param, level, color, plot
from indie.algorithms import Sma
@indicator('DPO', format=format.PRICE) # Detrended Price Oscillator
@param.int('length', default=21, min=1)
@param.bool('centered', default=False, title='Centered')
@level(0, line_color=color.GRAY, title='Zero')
@plot.line(color=color.GREEN, title='Detrended Price Oscillator')
def Main(self, length, centered):
bars_back = length // 2 + 1
ma = Sma.new(self.close, length)
dpo = self.close[bars_back] - ma[0] if centered else self.close[0] - ma[bars_back]
return plot.Line(dpo, offset=-bars_back if centered else 0)