// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © TradingView
//@version=6
library("ta")
// "ta" Library
// v12, 2025.12.06
// This code's style is based on the recommendations from the Pine Script User Manual's Style guide:
// https://www.tradingview.com/pine-script-docs/writing/style-guide/
import TradingView/RelativeValue/3 as TVrv
// This library exports the following functions:
// • ao(): Awesome Oscillator
// • aroon(): Aroon Indicator
// • atr2(): Average True Range (Alternative version)
// • cagr(): Compound Annual Growth Rate
// • chandelier(): Chandelier Exit
// • chandelier2(): Chandelier Exit (Alternative version)
// • changePercent(): Calculate percentage change
// • coppock(): Coppock Curve
// • dema(): Double Exponential Moving Average
// • dema2(): Double Exponential Moving Average (Alternative version)
// • dm(): Demarker Indicator
// • donchian(): Donchian Channel
// • ema2(): Exponential Moving Average (Alternative version)
// • eom(): Ease of Movement
// • er(): Kaufman's Efficiency Ratio
// • frama(): Fractal Adaptive Moving Average
// • ft(): Fisher Transform
// • highestSince(): Highest value since condition
// • ht(): Hilbert Transform function
// • ichimoku(): Ichimoku Cloud
// • ift(): Inverse Fisher Transform
// • kama(): Kaufman's Adaptive Moving Average
// • kvo(): Klinger Volume Oscillator
// • lowestSince(): Lowest value since condition
// • macd2(): Moving Average Convergence Divergence (Alternative version)
// • pmo(): Price Momentum Oscillator
// • ppo(): Percentage Price Oscillator
// • ppo2(): Percentage Price Oscillator (Alternative version)
// • pzo(): Price Zone Oscillator
// • relativeVolume(): Compare volume to its historical average
// • requestUpAndDownVolume(): Requests up/down volume data for a specified lower timeframe.
// • requestVolumeDelta(): Requests volume delta or cumulative volume delta from a specified lower timeframe.
// • rma2(): Rolling Moving Average (Alternative version)
// • rms(): Root Mean Square
// • rwi(): Random Walk Index
// • specialK(): Pring's Special K
// • stc(): Schaff Trend Cycle
// • stochFull(): Full Stochastic Oscillator
// • stochRsi(): Stochastic RSI
// • supertrend(): SuperTrend Indicator
// • supertrend2(): SuperTrend Indicator (Alternative version)
// • szo(): Sentiment Zone Oscillator
// • t3(): Tilson Moving Average (T3)
// • t3Alt(): Tilson Moving Average (T3 Alternative version)
// • tema(): Triple Exponential Moving Average
// • tema2(): Triple Exponential Moving Average (Alternative version)
// • trima(): Triangular Moving Average
// • trix(): TRIX indicator
// • ulcerIndex(): Ulcer Index
// • uo(): Ultimate Oscillator
// • vhf(): Vertical Horizontal Filter
// • vi(): Vortex Indicator
// • vStop(): Volatility Stop
// • vStop2(): Volatility Stop (Alternative version)
// • vzo(): Volume Zone Oscillator
// • williamsFractal(): Williams' Fractal
// • wpo(): Wave Period Oscillator
// @function Calculates the dynamic Exponentially Weighted Moving Average used in
// multiple functions exported by this library.
// @param source (series int/float) Series of values to process.
// @param alpha (series int/float) The smoothing factor for the filter.
// @returns The dynamic exponentially weighted moving average value.
ewma(series float source, series float alpha) =>
if alpha < 0
runtime.error("Invalid smoothing factor. The value must not be negative.")
float result = na
result := alpha * source + (1.0 - alpha) * nz(result[1], source)
// @function Calculates the value of the Awesome Oscillator.
// @param source (series int/float) Series of values to process.
// @param shortLength (simple int) Number of bars for the fast moving average.
// @param longLength (simple int) Number of bars for the slow moving average.
// @returns (float) The oscillator value.
export ao(series float source = hl2, simple int shortLength = 5, simple int longLength = 34) =>
float result = ta.sma(source, shortLength) - ta.sma(source, longLength)
// @function Calculates the values of the Aroon indicator.
// @param length (simple int) Number of bars (lookback length).
// @returns ([float, float]) A tuple of the Aroon-Up and Aroon-Down values.
export aroon(simple int length) =>
float aroonDown = 100 * (ta.lowestbars(low, length + 1) + length) / length
float aroonUp = 100 * (ta.highestbars(high, length + 1) + length) / length
[aroonUp, aroonDown]
// @function An alternative function to `ta.atr()` that supports a "series float" length value.
// @param length (series int/float) Length for the smoothing factor calculation.
// @returns (float) The ATR value.
export atr2(series float length) =>
if length < 1
runtime.error(str.format("Invalid length ({0}). The value must be greater than or equal to 1.", length))
float alpha = 1.0 / length
float result = ewma(ta.tr(true), alpha)
// @function Calculates the "Compound Annual Growth Rate" between two points in time.
// @param entryTime (series int) The starting timestamp.
// @param entryPrice (series int/float) The starting point's price.
// @param exitTime (series int) The ending timestamp.
// @param exitPrice (series int/float) The ending point's price.
// @returns (float) CAGR in % (50 is 50%). Returns `na` if there is not >=1D between `entryTime` and
// `exitTime`, or until the two time points have not been reached by the script.
export cagr(series int entryTime, series float entryPrice, series int exitTime, series float exitPrice) =>
int MS_IN_ONE_DAY = 24 * 60 * 60 * 1000
float daysBetween = (exitTime - entryTime) / MS_IN_ONE_DAY
float result = if daysBetween >= 1 and not (na(entryPrice) or na(exitPrice))
float years = daysBetween / 365.
float rate = exitPrice / entryPrice
100 * (math.pow(rate, 1 / years) - 1)
else
na
// @function Calculates the long and short Chandelier Exit values. The long value subtracts
// Average True Range (ATR), multiplied by a specified amount, from the highest high over
// `length` bars. The short value adds the ATR to the lowest low over those bars.
// @param length (series int) The number of bars for the highest high and lowest low.
// @param atrLength (simple int) The ATR length.
// @param mult (series int/float) The ATR multiplier.
// @returns ([float, float]) A tuple containing the following values:
// - The long Chandelier Exit value.
// - The short Chandelier Exit value.
export chandelier(series int length, simple int atrLength, series float mult) =>
float atr = ta.atr(atrLength) * mult
[ta.highest(length) - atr, ta.lowest(length) + atr]
// @function An alternative function to `chandelier()` that supports a "series float" ATR length.
// @param length (series int) The number of bars for the highest high and lowest low.
// @param atrLength (series int/float) The ATR length.
// @param mult (series int/float) The ATR multiplier.
// @returns ([float, float]) A tuple containing the following values:
// - The long Chandelier Exit value.
// - The short Chandelier Exit value.
export chandelier2(series int length, series float atrLength, series float mult) =>
float atr = atr2(atrLength) * mult
[ta.highest(length) - atr, ta.lowest(length) + atr]
// @function Calculates the percentage difference between two values.
// @param newValue (series int/float) The current value.
// @param oldValue (series int/float) The previous value.
// @returns (float) The difference between `oldValue` and `newValue`, expressed as a percentage of
// `oldValue`.
export changePercent(series float newValue, series float oldValue) =>
float result = 100 * (newValue - oldValue) / oldValue
// @function Calculates the value of the Coppock Curve indicator.
// @param source (series int/float) Series of values to process.
// @param longLength (simple int) Number of bars back for the longer rate of change (ROC).
// @param shortLength (simple int) Number of bars back for the shorter ROC.
// @param smoothLength (simple int) Number of bars for the weigted moving average smoothing.
// @returns (float) The oscillator value.
export coppock(series float source, simple int longLength, simple int shortLength, simple int smoothLength) =>
float result = ta.wma(ta.roc(source, longLength) + ta.roc(source, shortLength), smoothLength)
// @function Calculates the value of the Double Exponential Moving Average (DEMA).
// @param source (series int/float) Series of values to process.
// @param length (simple int) Length for the smoothing factor calculation.
// @returns (float) The double exponentially weighted moving average of the `source`.
export dema(series float source, simple int length) =>
float ema1 = ta.ema(source, length)
float ema2 = ta.ema(ema1, length)
float result = 2 * ema1 - ema2
// @function An alternative function to `ta.ema()` that supports a "series float" length value.
// @param source (series int/float) Series of values to process.
// @param length (series int/float) Length for the smoothing factor calculation.
// @returns (float) The exponentially weighted moving average of the `source`.
export ema2(series float source, series float length) =>
if length < 1
runtime.error(str.format("Invalid length ({0}). The value must be greater than or equal to 1.", length))
float alpha = 2.0 / (math.max(1.0, length) + 1.0)
float result = ewma(source, alpha)
// @function An alternative function to `dema()` that supports a "series float" length value.
// @param source (series int/float) Series of values to process.
// @param length (series int/float) Length for the smoothing factor calculation.
// @returns (float) The double exponentially weighted moving average of the `source`.
export dema2(series float source, series float length) =>
float ema1 = ema2(source, length)
float ema2 = ema2(ema1, length)
float result = 2 * ema1 - ema2
// @function Calculates the value of the "Demarker" indicator.
// "DeMark®" is a registered trademark of DeMark Analytics, LLC. This code is neither
// endorsed, nor sponsored, nor affiliated with them.
// @param length (simple int) Number of bars (lookback length).
// @returns (float) The oscillator value.
export dm(simple int length) =>
float demax = math.max(ta.change(high), 0)
float demin = -math.min(ta.change(low), 0)
float result = ta.sma(demax, length) / (ta.sma(demax, length) + ta.sma(demin, length))
// @function Calculates the values of a Donchian Channel using `high` and `low` over a given `length`.
// @param length (series int) Number of bars (lookback length).
// @returns ([float, float, float]) A tuple containing the channel high, low, and median, respectively.
export donchian(series int length) =>
float highest = ta.highest(length)
float lowest = ta.lowest(length)
[highest, lowest, math.avg(highest, lowest)]
// @function Calculates the value of the Ease of Movement indicator.
// @param length (simple int) Number of bars (lookback length).
// @param div (simple int) Optional. The factor for normalzing values. The default is 10000.
// @returns (float) The Ease of Movement value.
export eom(simple int length, simple int div = 10000) =>
float result = ta.sma(div * ta.change(hl2) * (high - low) / volume, length)
// @function Calculates Kaufman's Efficiency Ratio (ER), which represents the absolute change in a
// `source` series over `length` bars divided by the sum of absolute single-bar changes over
// the same length.
// @param source (series int/float) Series of values to process.
// @param length (series int) Number of bars (lookback length).
// @returns (float) The Efficiency Ratio of `source`.
export er(series float source, series int length) =>
float absChange = math.abs(ta.change(source, length))
float sumChanges = math.sum(math.abs(ta.change(source)), length)
float result = sumChanges == 0.0 ? 0.0 : absChange / sumChanges
// @function Calculates the Fractal Adaptive Moving Average (FRAMA) by John Ehlers. FRAMA is an adaptive
// EMA-based filter that dynamically adjusts its smoothing factor based on fractal geometry.
// @param source (series int/float) Series of values to process.
// @param length (series int) Number of bars for the fractal dimension estimate.
// @returns (float) The Fractal Adaptive Moving Average of `source`.
export frama(series float source, series int length) =>
int len = math.round(length / 2)
float hh = ta.highest(len)
float ll = ta.lowest(len)
float n1 = (hh - ll) / len
float n2 = (hh[len] - ll[len]) / len
float n3 = (ta.highest(length) - ta.lowest(length)) / length
float D = math.log((n1 + n2) / n3) / math.log(2)
float alpha = math.exp(-4.6 * (D - 1))
float result = ewma(source, alpha)
// @function Calculates the value of the Fisher Transform indicator.
// @param source (series int/float) Series of values to process.
// @param length (simple int) Number of bars (lookback length).
// @returns (float) The oscillator value.
export ft(series float source, simple int length) =>
float value1 = 0.0
float fish = 0.0
float hh = ta.highest(source, length)
float ll = ta.lowest(source, length)
value1 := 0.66 * ((source - ll) / (hh - ll) - 0.5) + 0.67 * nz(value1[1])
float value2 = value1 > 0.99 ? 0.999 : value1 < -0.99 ? -0.999 : value1
fish := 0.5 * math.log((1 + value2) / (1 - value2)) + 0.5 * nz(fish[1])
float result = fish
// @function Tracks the highest value of a series since the last occurrence of a condition.
// @param cond (series bool) A condition which, when `true`, resets the tracking of the highest `source`.
// @param source (series int/float) Optional. Series of values to process. The default is `high`.
// @returns (float) The highest `source` value since the last time the `cond` value was `true`.
export highestSince(series bool cond, series float source = high) =>
var float result = na
if cond
result := source
result := math.max(nz(source, result), nz(result, source))
// @function Calculates the value of the Hilbert Transform indicator.
// @param source (series int/float) Series of values to process.
// @returns (float) The oscillator value.
export ht(series float source) =>
float result = 0.0962 * source + 0.5769 * nz(source[2]) - 0.5769 * nz(source[4]) - 0.0962 * nz(source[6])
// @function Calculates the midpoint between the highest `high` and lowest `low` over `length` bars.
// @param length (series int) Number of bars (lookback length).
// @returns The midpoint of the data.
midpoint(series int length) =>
float result = math.avg(ta.highest(length), ta.lowest(length))
// @function Calculates values of the Ichimoku Cloud indicator, including tenkan, kijun, senkouSpan1,
// senkouSpan2, and chikou.
// NOTE: offsets forward or backward can be done using the `offset` argument in `plot()`.
// @param conLength (series int) Length for the Conversion Line (Tenkan). The default is 9 periods, which
// returns the midpoint of the 9 period Donchian Channel.
// @param baseLength (series int) Length for the Base Line (Kijun-sen). The default is 26 periods, which returns
// the midpoint of the 26 period Donchian Channel.
// @param senkouLength (series int) Length for the Senkou Span 2 (Leading Span B). The default is 52 periods,
// which returns the midpoint of the 52 period Donchian Channel.
// @returns ([float, float, float, float, float]) A tuple of the Tenkan, Kijun, Senkou Span 1,
// Senkou Span 2, and Chikou Span values. NOTE: by default, the senkouSpan1 and senkouSpan2
// should be plotted 26 periods in the future, and the Chikou Span plotted 26 days in the past.
export ichimoku(series int conLength = 9, series int baseLength = 26, series int senkouLength = 52) =>
float tenkan = midpoint(conLength)
float kijun = midpoint(baseLength)
float senkouSpan1 = math.avg(tenkan, kijun)
float senkouSpan2 = midpoint(senkouLength)
float chikou = close
[tenkan, kijun, senkouSpan1, senkouSpan2, chikou]
// @function Calculates the value of the Inverse Fisher Transform indicator.
// @param source (series int/float) Series of values to process.
// @returns (float) The oscillator value.
export ift(series float source) =>
float exp = math.exp(2.0 * source)
float result = (exp - 1.0) / (1.0 + exp)
// @function Calculates Kaufman's Adaptive Moving Average (KAMA). KAMA is an EMA-based filter that
// dynamically adjusts its smoothing factor based on the value of Kaufman's Efficiency Ratio
// (ER). It moves toward price at a slower rate when ER is low, and at a faster rate when
// ER is high.
// @param source (series int/float) Series of values to process.
// @param erLen (series int) Length for the ER calculation.
// @param fastLen (series int/float) Length for the fastest smoothing response.
// @param slowLen (series int/float) Length for the slowest smoothing response.
// @returns (float) The KAMA of `source`.
export kama(series float source, series int erLen, series float fastLen, series float slowLen) =>
float alphaFast = 2.0 / (fastLen + 1.0)
float alphaSlow = 2.0 / (slowLen + 1.0)
float alpha = math.pow(er(source, erLen) * (alphaFast - alphaSlow) + alphaSlow, 2)
float result = ewma(source, alpha)
// @function Calculates the values of the Klinger Volume Oscillator.
// @param fastLen (simple int) Length for the fast EMA smoothing factor.
// @param slowLen (simple int) Length for the slow EMA smoothing factor.
// @param trigLen (simple int) Length for the trigger EMA smoothing factor.
// @returns ([float, float]) A tuple containing the KVO value and the trigger value.
export kvo(simple int fastLen, simple int slowLen, simple int trigLen) =>
float trend = math.sign(ta.change(hlc3)) * volume * 100
float fast = ta.ema(trend, fastLen)
float slow = ta.ema(trend, slowLen)
float kvo = fast - slow
float trigger = ta.ema(kvo, trigLen)
[kvo, trigger]
// @function Tracks the lowest value of a series since the last occurrence of a condition.
// @param cond (series bool) A condition which, when `true`, resets the tracking of the lowest `source`.
// @param source (series int/float) Optional. Series of values to process. The default is `low`.
// @returns (float) The lowest `source` value since the last time the `cond` value was `true`.
export lowestSince(series bool cond, series float source = low) =>
var float result = na
if cond
result := source
result := math.min(nz(source, result), nz(result, source))
// @function An alternative function to `ta.macd()` that supports "series float" EMA length values.
// @param source (series int/float) Series of values to process.
// @param fastLen (series int/float) The length for the fast EMA smoothing factor.
// @param slowLen (series int/float) The length for the slow EMA smoothing factor.
// @param sigLen (series int/float) The length for the signal EMA smoothing factor.
// @returns ([float, float, float]) A tuple containing the following values:
// - The Moving Average Convergence Divergence (MACD) value.
// - The EMA of the MACD (signal line).
// - The histogram value, representing the difference between the MACD and signal values.
export macd2(series float source, series float fastLen, series float slowLen, series float sigLen) =>
float maFast = ema2(source, fastLen)
float maSlow = ema2(source, slowLen)
float macd = maFast - maSlow
float signal = ema2(macd, sigLen)
float hist = macd - signal
[macd, signal, hist]
// @function Calculates the values of the Price Momentum Oscillator (PMO). The PMO is a twice-smoothed
// percentage rate of change in a `source` series, typically accompanied by a smooth signal
// line.
// @param source (series int/float) Series of values to process.
// @param length1 (series int/float) The first smoothing length.
// @param length2 (series int/float) The second smoothing length.
// @param sigLen (series int/float) Signal EMA smoothing length.
// @returns ([float, float]) A tuple containing the following values:
// - The Price Momentum Oscillator.
// - The EMA of the PMO (signal line).
export pmo(series float source, series float length1, series float length2, series float sigLen) =>
float roc = ta.roc(source, 1)
float pmo = ewma(10.0 * ewma(roc, 2.0 / length1), 2.0 / length2)
float signal = ema2(pmo, sigLen)
[pmo, signal]
// @function Calculates the values of the Percentage Price Oscillator (PPO). The PPO is a momentum
// oscillator that measures the difference between two EMAs as a percentage of the slower
// moving average.
// @param source (series int/float) Series of values to process.
// @param fastLen (simple int) The length for the fast EMA smoothing factor.
// @param slowLen (simple int) The length for the slow EMA smoothing factor.
// @param sigLen (simple int) The length for the signal EMA smoothing factor.
// @returns ([float, float, float]) A tuple containing the following values:
// - The Percentage Price Oscillator.
// - The EMA of the PPO (signal line).
// - The histogram value, representing the difference between the PPO and signal values.
export ppo(series float source, simple int fastLen, simple int slowLen, simple int sigLen) =>
float maFast = ta.ema(source, fastLen)
float maSlow = ta.ema(source, slowLen)
float ppo = changePercent(maFast, maSlow)
float signal = ta.ema(ppo, sigLen)
float hist = ppo - signal
[ppo, signal, hist]
// @function An alternative function to `ppo()` that supports "series float" EMA length values.
// @param source (series int/float) Series of values to process.
// @param fastLen (series int/float) The length for the fast EMA smoothing factor.
// @param slowLen (series int/float) The length for the slow EMA smoothing factor.
// @param sigLen (series int/float) The length for the signal EMA smoothing factor.
// @returns ([float, float, float]) A tuple containing the following values:
// - The Percentage Price Oscillator.
// - The EMA of the PPO (signal line).
// - The histogram value, representing the difference between the PPO and signal values.
export ppo2(series float source, series float fastLen, series float slowLen, series float sigLen) =>
float maFast = ema2(source, fastLen)
float maSlow = ema2(source, slowLen)
float ppo = changePercent(maFast, maSlow)
float signal = ema2(ppo, sigLen)
float hist = ppo - signal
[ppo, signal, hist]
// @function The "zone" calculation used in `pzo()` and `vzo()` functions.
// @param source (series int/float) Series of values to process.
// @param length (simple int) Length for the smoothing factor calculation.
// @returns (float) The oscillator value.
zone(series float source, simple int length) =>
float result = 100 * nz(ta.ema(math.sign(ta.change(close)) * source, length) / ta.ema(source, length))
// @function Calculates the value of the Price Zone Oscillator.
// @param length (simple int) Length for the smoothing factor calculation.
// @returns (float) The oscillator value.
export pzo(simple int length) =>
float result = zone(close, length)
// @function Calculates the volume since the last change in the time value from the
// `anchorTimeframe`, the historical average volume using bars from past periods
// that have the same relative time offset as the current bar from the start of its
// period, and the ratio of these volumes. The volume values are cumulative by default,
// but can be adjusted to non-accumulated with the `isCumulative` parameter.
// @param length (simple int) The number of periods to use for the historical average calculation.
// @param anchorTimeframe (simple string) Optional. The anchor timeframe used in the calculation. The default is "1D".
// @param isCumulative (simple bool) Optional. If `true`, the function accumulates volume values across each
// `anchorTimeframe` period. If `false`, the function uses non-cumulative volume. The
// default is `true`.
// @param adjustRealtime (simple bool) Optional. If `true`, estimates the cumulative value on open bars based on the
// confirmed data since the last `anchor` condition. The default is `false`.
// @returns ([float, float, float]) A tuple containing the following data:
// - The current volume.
// - The average of volume values at equivalent time offsets from past anchors over the number
// of periods.
// - The ratio of the current volume to the historical average volume.
export relativeVolume(
simple int length, simple string anchorTimeframe = "1D",
simple bool isCumulative = true, simple bool adjustRealtime = true
) =>
bool anchor = timeframe.change(anchorTimeframe)
float currVol = isCumulative ? TVrv.calcCumulativeSeries(volume, anchor, adjustRealtime) : volume
float pastVol = TVrv.averageAtTime(volume, length, anchorTimeframe, isCumulative)
float volRatio = currVol / pastVol
[currVol, pastVol, volRatio]
// @function A helper function for use in `upAndDownVolumeCalc()`. It classifies the bar's data as
// positive or negative based on the values of `open` and `close`. The `upAndDownVolumeCalc()`
// function uses this helper to categorize volume data when it cannot directly determine
// the category using `bid` and `ask` values.
// @param currentValue (series bool) Represents the latest category assigned to the data: `true` for positive, and
// `false` for negative. The function returns this value if the price-based conditions do not
// produce a different result.
// @returns (bool) `true` if the current bar's data is considered positive, and `false` otherwise.
priceCalc(series bool currentValue)=>
bool result = switch
close > open => true
close < open => false
close > close[1] => true
close < close[1] => false
=> currentValue
// @function A helper function for use in `requestUpAndDownVolume()` and `requestVolumeDelta()`. It
// calculates sums of polarized (positive and negative) `volume` values within a bar on
// script's main timeframe, and uses the sums to compute the bar's volume delta. The function
// also tracks the highest and lowest volume delta values observed within the bar.
//
// If the timeframe is "1T", and the values of `bid` and `ask` are *not* equal, the function
// uses the following logic to categorize each `volume` value as positive or negative:
// - If the `close` value is greater than or equal to the `ask` value, the volume is
// positive.
// - If the `close` value is less than or equal to the `bid` value, the volume is negative.
// - If the `close` value is *between* the `bid` and `ask` values, the volume is positive
// if the `close` value is greater than the median of those values, or negative if the
// value is less than the median.
//
// If the timeframe is higher than "1T", the `bid` and `ask` values are equal, or the `close`
// value equals the median of the `bid` and `ask` values, the function calls `priceCalc()` to
// use the following logic instead:
// - If the `close` value is greater than the `open` value, the volume is positive.
// - If the `close` value is less than the `open` value, the volume is negative.
// - If the `close` and `open` values are equal, the volume is positive if the current
// `close` value is greater than the previous value, or negative if the current value is
// less than the previous value.
// - If none of the above conditions apply, the current volume has the same positive or
// negative status as that of the previous bar's volume.
//
// NOTE: This function is intended for use in *lower-timeframe (LTF)* requests, as demonstrated
// by this library's `requestUpAndDownVolume()` and `requestVolumeDelta()` functions.
// When used in an LTF request, the function's results represent data calculated from
// the available intrabars within the *current bar* on the script's *main timeframe*. The
// calculations reset at the open of each bar on that timeframe.
// @returns ([float, float, float, float, float]) A tuple containing the following values:
// - The total positive (up) volume within the bar on the script's main timeframe.
// - The total negative (down) volume within the bar, expressed as a negative quantity.
// - The bar's volume delta (i.e., the net difference between up and down volume).
// - The highest volume delta observed within the bar.
// - The lowest volume delta observed within the bar.
upAndDownVolumeCalc() =>
var float posVol = 0.0
var float negVol = 0.0
var float hiVol = 0.0
var float loVol = 0.0
var bool isBuyVolume = close >= open
bool newBar = switch
timeframe.isticks => ta.change(request.security("", timeframe.main_period, time)) > 0
=> timeframe.change(timeframe.main_period)
if newBar
posVol := 0.0
negVol := 0.0
hiVol := 0.0
loVol := 0.0
if timeframe.isticks
float avg = math.avg(ask, bid)
isBuyVolume := switch
close == ask and close == bid => priceCalc(isBuyVolume)
close >= ask => true
close <= bid => false
close > avg => true
close < avg => false
=> priceCalc(isBuyVolume)
else
isBuyVolume := priceCalc(isBuyVolume)
if isBuyVolume
posVol += volume
else
negVol -= volume
float delta = posVol + negVol
hiVol := math.max(delta, hiVol)
loVol := math.min(delta, loVol)
[posVol, negVol, delta, hiVol, loVol]
// @function Checks whether a specified timeframe string represents a smaller timeframe than the
// script's main timeframe. If the specified string represents a higher timeframe, the
// function raises a runtime error.
// @param lowerTimeframe (series string) The timeframe string to inspect.
// @returns (void) The function does not return a usable value.
checkLTF(series string lowerTimeframe) =>
if timeframe.in_seconds(lowerTimeframe) > timeframe.in_seconds(timeframe.main_period)
runtime.error(
str.format(
"Invalid lower timeframe: {2}{0}{2}. The timeframe must be lower than or equal to {2}{1}{2}",
lowerTimeframe, timeframe.main_period, "'"
)
)
// @function Requests data from a specified lower timeframe and categorizes the volume of each intrabar
// within the chart bar as positive (up) or negative (down). The function uses this polarized
// volume data to calculate the total positive volume, total negative volume, and volume delta
// for a bar on the script's main timeframe.
//
// Calls to this function count toward a script's `request.*()` call limit.
// @param lowerTimeframe (series string) The timeframe of the requested intrabar data. Accepts a valid timeframe
// string (e.g., "5", "15", "1D"), an empty string, `timeframe.period`, or
// `timeframe.main_period`. Smaller timeframes provide higher precision but cover fewer bars
// on the script's main timeframe. Larger timeframes cover more bars on the script's main
// timeframe but offer less granularity. If the specified timeframe is higher than the script's
// main timeframe, it causes a runtime error.
// @returns ([float, float, float]) A tuple containing the following values:
// - The total positive (up) intrabar volume within the bar on the script's main timeframe.
// - The total negative (down) intrabar volume within the bar on the script's main
// timeframe, expressed as a negative quantity.
// - The bar's volume delta (i.e., the net difference between up and down intrabar volume).
export requestUpAndDownVolume(series string lowerTimeframe) =>
checkLTF(lowerTimeframe)
[posVol, negVol, delta, _, _] = request.security(syminfo.tickerid, lowerTimeframe, upAndDownVolumeCalc())
[posVol, negVol, delta]
// @function Requests data from a specified lower timeframe and categorizes the volume of each
// intrabar within a bar on the script's main timeframe as positive (up) or negative (down).
// The function uses this information to calculate the bar's volume delta, i.e., the difference
// between the total up and down intrabar volume.
//
// If the call specifies a `cumulativePeriod` argument representing a period larger than the
// main timeframe, it calculates the cumulative volume delta (CVD), which is a running sum
// of volume delta across all bars within the period. In addition, the function tracks the
// highest and lowest CVD values calculated within each bar over the period. The sum and
// highest/lowest calculations reset when a new period starts.
//
// Calls to this function count toward a script's `request.*()` call limit.
// @param lowerTimeframe (series string) The timeframe of the requested intrabar data. Accepts a valid timeframe
// string (e.g., "5", "15", "1D"), an empty string, `timeframe.period`, or
// `timeframe.main_period`. Smaller timeframes provide higher precision but cover fewer bars
// on the script's main timeframe. Larger timeframes cover more bars on the script's main
// timeframe but offer less granularity. If the specified timeframe is higher than the script's
// main timeframe, it causes a runtime error.
// @param cumulativePeriod (series string) Determines the span of the CVD period. Accepts a valid timeframe string
// (e.g., "5", "15", "1D"), an empty string, `timeframe.period`, or `timeframe.main_period`.
// The calculations reset after a new period starts. For example, a value of "1D" means that
// the function accumulates and tracks the values from each bar within a "1D" period and resets
// at the open of a new "1D" bar.
// @returns ([float, float, float, float]) A tuple containing the following values:
// - The opening volume delta/CVD at the start the bar on the script's main timeframe.
// This value is always 0 when a new cumulative period starts.
// - The maximum volume delta/CVD within the current cumulative period.
// - The minimum volume delta/CVD within the current cumulative period.
// - The current volume delta/CVD value.
export requestVolumeDelta(series string lowerTimeframe, series string cumulativePeriod = "") =>
checkLTF(lowerTimeframe)
[_, _, delta, maxVolume, minVolume] = request.security(syminfo.tickerid, lowerTimeframe, upAndDownVolumeCalc())
var float lastVolume = 0.0
bool anchorChange = str.length(str.trim(cumulativePeriod)) == 0 or
timeframe.change(cumulativePeriod) or (not na(lastVolume) and na(lastVolume[1]))
float openVolume = anchorChange ? 0.0 : lastVolume[1]
float hiVolume = openVolume + maxVolume
float loVolume = openVolume + minVolume
lastVolume := openVolume + delta
[openVolume, hiVolume, loVolume, lastVolume]
// @function An alternative function to `ta.rma()` that supports a "series float" length value.
// @param source (series int/float) Series of values to process.
// @param length (series int/float) Length for the smoothing factor calculation.
// @returns (float) The rolling moving average of the `source`.
export rma2(series float source, series float length) =>
if length < 1
runtime.error(str.format("Invalid length ({0}). The value must be greater than or equal to 1.", length))
float alpha = 1.0 / length
float result = ewma(source, alpha)
// @function Calculates the Root Mean Square of the `source` over a specified lookback length.
// @param source (series int/float) Series of values to process.
// @param length (series int) Number of bars (lookback length).
// @returns (float) The RMS value.
export rms(series float source, series int length) =>
float result = math.sqrt(math.sum(math.pow(source, 2), length) / length)
// @function Calculates the values of the Random Walk Index.
// @param length (simple int) Lookback and ATR length.
// @returns ([float, float]) A tuple of the `rwiHigh` and `rwiLow` values.
export rwi(simple int length) =>
float divisor = ta.atr(length) * math.sqrt(length)
float rwiHigh = (high - nz(low[length])) / divisor
float rwiLow = (nz(high[length]) - low) / divisor
[rwiHigh, rwiLow]
// @function Calculates the values of Pring's Special K indicator. The Spcial K is a weighted sum of
// smoothed rates of change over multiple lengths, accompanied by a twice-smoothed signal
// line. The lengths and weights are derived from Martin Pring's market observations.
// @param source (series int/float) Series of values to process.
// @param sigLen1 (series int) The first smoothing length for the signal line.
// @param sigLen2 (series int) The second smoothing length for the signal line.
// @returns ([float, float]) A tuple containing the following values:
// - The Special K value.
// - The twice-smoothed signal line of the Special K.
export specialK(series float source, series int sigLen1, series int sigLen2) =>
float sk =
ta.sma(ta.roc(source, 10), 10) + ta.sma(ta.roc(source, 40), 50) + ta.sma(ta.roc(source, 195), 130)
+ 2.0 * (ta.sma(ta.roc(source, 15), 10) + ta.sma(ta.roc(source, 65), 65) + ta.sma(ta.roc(source, 265), 130))
+ 3.0 * (ta.sma(ta.roc(source, 20), 10) + ta.sma(ta.roc(source, 75), 75) + ta.sma(ta.roc(source, 390), 130))
+ 4.0 * (ta.sma(ta.roc(source, 30), 15) + ta.sma(ta.roc(source, 100), 100) + ta.sma(ta.roc(source, 530), 195))
float signal = ta.sma(ta.sma(sk, sigLen1), sigLen2)
[sk, signal]
// @function Calculates the Schaff Trend Cycle indicator.
// @param source (series int/float) Series of values to process.
// @param fast (simple int) Length for the MACD fast smoothing factor.
// @param slow (simple int) Length for the MACD slow smoothing factor.
// @param cycle (simple int) Number of bars for the Stochastic calculation.
// @param d1 (simple int) Length for the initial %D smoothing factor.
// @param d2 (simple int) Length for the final %D smoothing factor.
// @returns (float) The STC for the `source` series.
export stc(series float source, simple int fast, simple int slow, simple int cycle, simple int d1, simple int d2) =>
float macd = ta.ema(source, fast) - ta.ema(source, slow)
float k = nz(fixnan(ta.stoch(macd, macd, macd, cycle)))
float d = ta.ema(k, d1)
float kd = nz(fixnan(ta.stoch(d, d, d, cycle)))
float stc = ta.ema(kd, d2)
float result = math.max(math.min(stc, 100), 0)
// @function Calculates the %K and %D values of the Full Stochastic indicator.
// @param periodK (simple int) Number of bars for the Stochastic calculation.
// @param smoothK (simple int) Number of bars for smoothing the %K series.
// @param periodD (simple int) Number of bars for smoothing the %D series.
// @returns ([float, float]) A tuple containing the slow %K and the %D moving average.
export stochFull(simple int periodK, simple int smoothK, simple int periodD) =>
float k = ta.sma(ta.stoch(close, high, low, periodK), smoothK)
float d = ta.sma(k, periodD)
[k, d]
// @function Calculates the %K and %D values of the Stochastic RSI indicator.
// @param lengthRsi (simple int) Length for the RSI smoothing factor.
// @param periodK (simple int) Number of bars for the Stochastic calculation.
// @param smoothK (simple int) Number of bars for smoothing the %K series.
// @param periodD (simple int) Number of bars for smoothing the %D series.
// @param source (series int/float) Optional. Series of values to process. The default is `close`.
// @returns ([float, float]) A tuple containing the slow %K and the %D moving average.
export stochRsi(
simple int lengthRsi, simple int periodK, simple int smoothK, simple int periodD, series float source = close
) =>
float rsi = ta.rsi(source, lengthRsi)
float k = ta.sma(ta.stoch(rsi, rsi, rsi, periodK), smoothK)
float d = ta.sma(k, periodD)
[k, d]
// @function Calculates the values of the SuperTrend indicator with the ability to take candle wicks
// into account, rather than only the closing price.
// @param factor (series int/float) Multiplier for the ATR value.
// @param atrLength (simple int) Length for the ATR smoothing factor calculation.
// @param wicks (simple bool) Optional. Specifies whether the function uses `high` and `low` values to
// identify trend reversals (if `true`), or uses only `close` values (if `false`). The default
// is `false`.
// @returns ([float, int]) A tuple containing the Supertrend and trend direction values.
export supertrend(series float factor, simple int atrLength, simple bool wicks = false) =>
float source = hl2
int direction = na
float superTrend = na
float atr = ta.atr(atrLength) * factor
float upperBand = source + atr
float lowerBand = source - atr
float highPrice = wicks ? high : close
float lowPrice = wicks ? low : close
float prevLowerBand = nz(lowerBand[1])
float prevUpperBand = nz(upperBand[1])
lowerBand := lowerBand > prevLowerBand or lowPrice[1] < prevLowerBand ? lowerBand : prevLowerBand
upperBand := upperBand < prevUpperBand or highPrice[1] > prevUpperBand ? upperBand : prevUpperBand
float prevSuperTrend = superTrend[1]
if na(atr[1])
direction := 1
else if prevSuperTrend == prevUpperBand
direction := highPrice > upperBand ? -1 : 1
else
direction := lowPrice < lowerBand ? 1 : -1
superTrend := direction == -1 ? lowerBand : upperBand
[superTrend, direction]
// @function An alternative function to `supertrend()` that supports a "series float" ATR length.
// @param factor (series int/float) Multiplier for the ATR value.
// @param atrLength (series int/float) Length for the ATR smoothing factor calculation.
// @param wicks (simple bool) Optional. Specifies whether the function uses `high` and `low` values to
// identify trend reversals (if `true`), or uses only `close` values (if `false`). The default
// is `false`.
// @returns ([float, int]) A tuple containing the Supertrend and trend direction values.
export supertrend2(series float factor, series float atrLength, simple bool wicks = false) =>
float source = hl2
int direction = na
float superTrend = na
float atr = atr2(atrLength) * factor
float upperBand = source + atr
float lowerBand = source - atr
float highPrice = wicks ? high : close
float lowPrice = wicks ? low : close
float prevLowerBand = nz(lowerBand[1])
float prevUpperBand = nz(upperBand[1])
lowerBand := lowerBand > prevLowerBand or lowPrice[1] < prevLowerBand ? lowerBand : prevLowerBand
upperBand := upperBand < prevUpperBand or highPrice[1] > prevUpperBand ? upperBand : prevUpperBand
float prevSuperTrend = superTrend[1]
if na(atr[1])
direction := 1
else if prevSuperTrend == prevUpperBand
direction := highPrice > upperBand ? -1 : 1
else
direction := lowPrice < lowerBand ? 1 : -1
superTrend := direction == -1 ? lowerBand : upperBand
[superTrend, direction]
// @function Calculates the Generalized DEMA (GD) used in the `t3()` function.
// @param source (series int/float) Series of values to process.
// @param length (simple int) Length for the smoothing factor calculation.
// @param vf (simple float) Volume factor. Affects the responsiveness.
// @returns (float) The GD value of the `source`.
gd(series float source, simple int length, simple float vf) =>
float result = ta.ema(source, length) * (1 + vf) - ta.ema(ta.ema(source, length), length) * vf
// @function Calculates the value of the Tilson Moving Average (T3).
// @param source (series int/float) Series of values to process.
// @param length (simple int) Length for the smoothing factor calculation.
// @param vf (simple float) Volume factor. Affects the responsiveness.
// @returns (float) The Tilson moving average of the `source`.
export t3(series float source, simple int length, simple float vf = 0.7) =>
float result = gd(gd(gd(source, length, vf), length, vf), length, vf)
// @function An alternative function to `gd()` that supports a "series float" length value.
// @param source (series int/float) Series of values to process.
// @param length (series int/float) Length for the smoothing factor calculation.
// @param vf (simple float) Volume factor. Affects the responsiveness.
// @returns (float) The GD value of the `source`.
gd2(series float source, series float length, simple float vf) =>
float result = ema2(source, length) * (1 + vf) - ema2(ema2(source, length), length) * vf
// @function An alternative function to `t3()` that supports a "series float" length value.
// @param source (series int/float) Series of values to process.
// @param length (series int/float) Length for the smoothing factor calculation.
// @param vf (simple float) Volume factor. Affects the responsiveness.
// @returns (float) The Tilson moving average of the `source`.
export t3Alt(series float source, series float length, simple float vf = 0.7) =>
float result = gd2(gd2(gd2(source, length, vf), length, vf), length, vf)
// @function Calculates the value of the Triple Exponential Moving Average (TEMA).
// @param source (series int/float) Series of values to process.
// @param length (simple int) Length for the smoothing factor calculation.
// @returns (float) The triple exponentially weighted moving average of the `source`.
export tema(series float source, simple int length) =>
float ema1 = ta.ema(source, length)
float ema2 = ta.ema(ema1, length)
float ema3 = ta.ema(ema2, length)
float result = 3 * (ema1 - ema2) + ema3
// @function An alternative function to `tema()` that supports a "series float" length value.
// @param source (series int/float) Series of values to process.
// @param length (series int/float) Length for the smoothing factor calculation.
// @returns (float) The triple exponentially weighted moving average of the `source`.
export tema2(series float source, series float length) =>
float ema1 = ema2(source, length)
float ema2 = ema2(ema1, length)
float ema3 = ema2(ema2, length)
float result = 3 * (ema1 - ema2) + ema3
// @function Calculates the value of the Triangular Moving Average (TRIMA).
// @param source (series int/float) Series of values to process.
// @param length (series int) Number of bars (lookback length).
// @returns (float) The triangular moving average of the `source`.
export trima(series float source, series int length) =>
float result = ta.sma(ta.sma(source, math.ceil(length / 2)), math.floor(length / 2) + 1)
// @function Calculates the values of the TRIX indicator.
// @param source (series int/float) Series of values to process.
// @param length (simple int) Length for the smoothing factor calculation.
// @param signalLength (simple int) Length for smoothing the signal line.
// @param exponential (simple bool) Optional. Specifies whether the function calculates the signal line using
// an exponential moving average (if `true`) or a simple moving average (if `false`). The
// default is `true`.
// @returns ([float, float, float]) A tuple containing the TRIX value, signal, and histogram values.
export trix(series float source, simple int length, simple int signalLength, simple bool exponential = true) =>
float triple = ta.ema(ta.ema(ta.ema(source, length), length), length)
float trix = ta.roc(triple, 1)
float signal = exponential ? ta.ema(trix, signalLength) : ta.sma(trix, signalLength)
float hist = trix - signal
[trix, signal, hist]
// @function Calculates the Ulcer Index based on a `source` value. The index represents the root mean
// square of drawdown percentages over a lookback length.
// @param source (series int/float) Series of values to process.
// @param length (series int) Number of bars (lookback length).
// @returns (float) The Ulcer Index.
export ulcerIndex(series float source, series int length) =>
float highest = ta.highest(source, length)
float drawdown = changePercent(source, highest)
float result = rms(drawdown, length)
// @function Calculates the value of the Sentiment Zone Oscillator.
// @param source (series int/float) Series of values to process.
// @param length (simple int) Length for the smoothing factor calculation.
// @returns (float) The oscillator value.
export szo(series float source, simple int length) =>
float trend = math.sign(ta.change(source))
float sentPos = tema(trend, length)
float result = 100 * sentPos / length
// @function Calculates the weighted average used in the `uo()` function.
// @param bp (series int/float) A source series representing the "Buying Pressure" value.
// @param trange (series int/float) A source series representing the True Range value.
// @param length (simple int) Number of bars (lookback length).
// @returns (float) The weighted average value.
uoAverage(series float bp, series float trange, simple int length) =>
float result = math.sum(bp, length) / math.sum(trange, length)
// @function Calculates the value of the Ultimate Oscillator.
// @param fastLen (series int) Number of bars for the fast smoothing average.
// @param midLen (series int) Number of bars for the middle smoothing average.
// @param slowLen (series int) Number of bars for the slow smoothing average.
// @returns (float) The Ultimate Oscillator.
export uo(simple int fastLen, simple int midLen, simple int slowLen) =>
float tMax = math.max(high, close[1])
float tMin = math.min(low, close[1])
float tr = tMax - tMin
float bp = close - tMin
float avg1 = uoAverage(bp, tr, fastLen)
float avg2 = uoAverage(bp, tr, midLen)
float avg3 = uoAverage(bp, tr, slowLen)
float result = 100 * (4 * avg1 + 2 * avg2 + avg3) / 7
// @function Calculates the value of the Vertical Horizontal Filter.
// @param source (series int/float) Series of values to process.
// @param length (simple int) Number of bars (lookback length).
// @returns (float) The oscillator value.
export vhf(series float source, simple int length) =>
float sumChanges = math.sum(math.abs(ta.change(source)), length)
float result = math.abs(ta.highest(source, length) - ta.lowest(source, length)) / sumChanges
// @function Calculates the values of the Vortex Indicator.
// @param length (simple int) Number of bars (lookback length).
// @returns ([float, float]) A tuple containing the VI+ and VI- values.
export vi(simple int length) =>
divisor = math.sum(ta.atr(1), length)
viPlus = math.sum(math.abs(high - nz(low[1])), length) / divisor
viMinus = math.sum(math.abs(low - nz(high[1])), length) / divisor
[viPlus, viMinus]
// @function Calculates an ATR-based stop value that trails behind the `source`. Can serve as a
// possible stop-loss guide and trend identifier.
// @param source (series int/float) Series of values that the stop trails behind.
// @param atrLength (simple int) Length for the ATR smoothing factor calculation.
// @param atrFactor (series int/float) Optional. The multiplier of the ATR value. Affects the maximum distance
// between the stop and the `source` value. A value of 1 means the maximum distance is 100%
// of the ATR value. The default is 1.
// @returns ([float, bool]) A tuple containing the volatility stop and trend direction values.
export vStop(series float source, simple int atrLength, series float atrFactor = 1) =>
float src = nz(source, close)
float atrM = nz(ta.atr(atrLength) * atrFactor, ta.tr(true))
var bool trendUp = true
var float max = src
var float min = src
var float stop = 0.0
max := math.max(max, src)
min := math.min(min, src)
stop := nz(trendUp ? math.max(stop, max - atrM) : math.min(stop, min + atrM), src)
trendUp := src - stop >= 0.0
if trendUp != trendUp[1]
max := src
min := src
stop := trendUp ? max - atrM : min + atrM
[stop, trendUp]
// @function An alternative function to `vStop()` that supports a "series float" length value.
// @param source (series int/float) Series of values that the stop trails behind.
// @param atrLength (series int/float) Length for the ATR smoothing factor calculation.
// @param atrFactor (series int/float) Optional. The multiplier of the ATR value. Affects the maximum distance
// between the stop and the `source` value. A value of 1 means the maximum distance is 100%
// of the ATR value. The default is 1.
// @returns ([float, bool]) A tuple of the volatility stop value and the trend direction as a "bool".
export vStop2(series float source, series float atrLength, series float atrFactor = 1) =>
float src = nz(source, close)
float atrM = nz(atr2(atrLength) * atrFactor, ta.tr(true))
var bool trendUp = true
var float max = src
var float min = src
var float stop = 0.0
max := math.max(max, src)
min := math.min(min, src)
stop := nz(trendUp ? math.max(stop, max - atrM) : math.min(stop, min + atrM), src)
trendUp := src - stop >= 0.0
if trendUp != trendUp[1]
max := src
min := src
stop := trendUp ? max - atrM : min + atrM
[stop, trendUp]
// @function Calculates the value of the Volume Zone Oscillator.
// @param length (simple int) Length for the smoothing factor calculation.
// @returns (float) The oscillator value.
export vzo(simple int length) =>
float result = zone(volume, length)
// @function Detects Williams fractals (used by `williamsFractal()`).
// @param source (series int/float) Series of values to process.
// @param n (series int) Lookback in bars.
// @param direction (simple int) Direction of the fractal to detect (+1 for up, -1 for down).
// @returns (series bool) `true` when a fractal was detected, `false` otherwise.
flag(series float source, series int n, simple int direction) =>
float src = source * direction
bool flag = true
bool flag0 = true
bool flag1 = true
bool flag2 = true
bool flag3 = true
bool flag4 = true
for i = 1 to n
flag := flag and (src[n - i] < src[n])
flag0 := flag0 and (src[n + i] < src[n])
flag1 := flag1 and (src[n + 1] <= src[n] and src[n + i + 1] < src[n])
flag2 := flag2 and (src[n + 1] <= src[n] and src[n + 2] <= src[n] and src[n + i + 2] < src[n])
flag3 := flag3 and (src[n + 1] <= src[n] and src[n + 2] <= src[n] and src[n + 3] <= src[n] and src[n + i + 3] < src[n])
flag4 := flag4 and (src[n + 1] <= src[n] and src[n + 2] <= src[n] and src[n + 3] <= src[n] and src[n + 4] <= src[n] and src[n + i + 4] < src[n])
bool flags = flag0 or flag1 or flag2 or flag3 or flag4
bool result = (flag and flags)
// @function Detects Williams Fractals.
// @param period (series int) Number of bars (lookback length).
// @returns ([bool, bool]) A tuple of an up fractal and down fractal. Variables are true when detected.
export williamsFractal(series int period) =>
bool upFractal = flag(high, period, 1)
bool downFractal = flag(low, period, -1)
[upFractal, downFractal]
// @function Calculates the value of the Wave Period Oscillator.
// @param length (simple int) Length for the smoothing factor calculation.
// @returns (float) The oscillator value.
export wpo(simple int length) =>
float tt = 2 * math.pi / math.asin(close[1] / high)
float ti = math.sign(ta.change(close)) * tt
float result = ta.ema(ti, length)
plot(cagr(time[1], close[1], time, close))
Show all 2,141 lines