VolatilityIndicators
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © andre_007

// @version=5

// @description This is a library of 'Volatility Indicators'.
//      It aims to facilitate the grouping of this category of indicators, and also offer the customized supply of
//      the parameters and sources, not being restricted to just the closing price.
// @Thanks and credits:
//      1. Dynamic Zones: Leo Zamansky, Ph.D., and David Stendahl (code by allanster)
//      2. Deviation: Karl Pearson (code by TradingView)
//      3. Variance: Ronald Fisher (code by TradingView)
//      4. Z-score: Veronique Valcu (code by HPotter)
//      5. Standard deviation: Ronald Fisher (code by TradingView)
//      6. ATR (Average True Range): J. Welles Wilder (code by TradingView)
//      7. ATRP (Average True Range Percent): millerrh
//      8. Historical Volatility: HPotter
//      9. Min-Max Scale Normalization: gorx1
//      10. Mean Normalization: gorx1
//      11. Standardization: gorx1
//      12. Scaling to unit length: gorx1
//      13. LS Volatility Index: Alexandre Wolwacz (Stormer), Fabrício Lorenz, Fábio Figueiredo (Vlad) (code by me)
//      14. Bollinger Bands: John Bollinger (code by TradingView)
//      15. Bollinger Bands %: John Bollinger (code by TradingView)
//      16. Bollinger Bands Width: John Bollinger (code by TradingView)
//      17. Keltner Channels: Chester W. Keltner (code by TradingView)
//      18. Keltner Channels %: adapted from Bollinger Bands % by me (andre_007
//      19. Keltner Channels Width: adapted from Bollinger Bands Width by me (andre_007)
//      20. Moving Envelopes: Alan Hull (code by TradingView)
//      21. Donchian Channels: Richard Donchian (code by TradingView)
library("VolatilityIndicators")

import andre_007/MovingAveragesProxy/3 as MAPROXY

// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// #region Dispersion

// @function        Deviation. Measure the difference between a source in relation to another source
// @param src               (float) Source to calculate standard deviation used in Bollinger Bands
// @param length            (int) Sequential period to calculate the deviation
// @param anotherSource     (float) Source to compare
// @returns 	            (float) Bollinger Bands Width
export dev(float source, simple int length, float anotherSource) =>
    sum = 0.0
    for i = 0 to length - 1
        val = source[i]
        sum := sum + math.abs(val - anotherSource)
    dev = sum/length

// @function                Variance. A statistical measurement of the spread between numbers in a data set. More specifically,
// variance measures how far each number in the set is from the mean (average), and thus from every other number in the set.
// Variance is often depicted by this symbol: σ2. It is used by both analysts and traders to determine volatility and market security.
// @param src               (float) Source to calculate variance
// @param mean              (float) Mean (Moving average)
// @param length            (int) The sequential period to calcule the variance (number of values in data set)
// @param biased            (bool) Defines the type of standard deviation. If true, uses biased sample variance (n),
// @param degreesOfFreedom  (int)  Degrees of freedom. The number of values in the final calculation of a statistic that are free to vary.
// Default value is n-1, where n here is length. Only applies when biased parameter is defined as false.
// @returns 	    (float) Standard deviation
export variance(float src, float mean, simple int length, simple bool biased=true, simple int degreesOfFreedom=na) =>
    dev = src - mean

    variance = if biased
        variance = math.sum(dev * dev, length) / length
    else
        if na(degreesOfFreedom) or (degreesOfFreedom == 0) or (degreesOfFreedom >= length)
            runtime.error("When the biased parameter is false (unbiased), the degrees of freedom must be greater than zero and less than or equal to length-1")
        variance = math.sum(dev * dev, length) / degreesOfFreedom

// @function    Measure the Standard deviation from a source in relation to it's moving average.
// In this implementation, you pass the average as a parameter, allowing a more personalized calculation.
// @param src               (float) Source to calculate standard deviation
// @param mean              (float) Moving average.
// @param length            (int) The sequential period to calcule the standard deviation
// @param biased            (bool) Defines the type of standard deviation. If true, uses biased sample variance (n),
// else uses unbiased sample variance (n-1 or another value, as long as it is in the range between 1 and n-1), where n=length.
// @param degreesOfFreedom   (int)  Degrees of freedom. The number of values in the final calculation of a statistic that are free to vary.
// Default value is n-1, where n here is length. Only applies when biased parameter is defined as false.
// @returns 	    (float) Standard deviation
export stDev(float src, simple int length, float mean, simple bool biased=true, simple int degreesOfFreedom=na) =>
    variance = variance(src, mean, length, biased, degreesOfFreedom)
    math.sqrt(variance)

// @function  Standard Error. The standard error of the mean is a measure of the variation in the sample mean
//  relative to the population mean. The formula is given by:
//
//  SE = σ / √n
//  SE: Standard error of the mean (Standard Error)
//  σ: Population standard deviation (Standard Deviation)
//  n: Sample Size
//
// In this formula, the population standard deviation (σ) is divided by the square root of the sample size (n).
// This indicates that the standard error decreases as the sample size increases, which means that the estimate of the
// sample mean becomes more accurate.
// @param stdDev            (float) Standard Deviation
// @param sampleSize        (int) Sample size
// @returns 	            (float) Standard error
export stErr(float stdDev, simple int sampleSize) =>
    stderror = stdDev/math.sqrt(sampleSize)

// @function  Z-Score. A z-score is a statistical measurement that indicates how many standard deviations a data point is from
// the mean of a data set. It is also known as a standard score. The formula for calculating a z-score is (x - μ) / σ,
// where x is the individual data point, μ is the mean of the data set, and σ is the standard deviation of the data set.
// Z-scores are useful in identifying outliers or extreme values in a data set. A positive z-score indicates that the
// data point is above the mean, while a negative z-score indicates that the data point is below the mean. A z-score of
// 0 indicates that the data point is equal to the mean.
// Z-scores are often used in hypothesis testing and determining confidence intervals. They can also be used to compare
// data sets with different units or scales, as the z-score standardizes the data. Overall, z-scores provide a way to
// measure the relative position of a data point in a data
// @param src           (float) Source to calculate z-score
// @param mean          (float) Moving average.
// @param length        (int) The sequential period to calcule the standard deviation
// @param biased        (bool) Defines the type of standard deviation. If true, uses biased sample variance (n),
// else uses unbiased sample variance (n-1 or another value, as long as it is in the range between 1 and n-1), where n=length.
// @param degreesOfFreedom   (int)  Degrees of freedom. The number of values in the final calculation of a statistic that are free to vary.
//   Default value is n-1, where n here is length. Only applies when biased parameter is defined as false.
// @returns 	    (float) Z-score
export zscore(float src, float mean, simple int length, simple bool biased=true, simple int degreesOfFreedom=na) =>
    float zscore = (src - mean) / stDev(src, length, mean, biased, degreesOfFreedom)

// @function  T-Score. A t-score is a statistical measurement that indicates how many standard errors a data point is from
// the mean of a data set. It is also known as a standard score. The formula for calculating a t-score is (x - μ) / SE,
// where x is the individual data point, μ is the mean of the data set, and SE is the standard error of the data set.
// T-scores are useful in identifying outliers or extreme values in a data set. A positive t-score indicates that the
// data point is above the mean, while a negative t-score indicates that the data point is below the mean. A t-score of
// 0 indicates that the data point is equal to the mean.
// T-scores are often used in hypothesis testing and determining confidence intervals. They can also be used to compare
// data sets with different units or scales, as the t-score standardizes the data. Overall, t-scores provide a way to
// measure the relative position of a data point in a data.
// When to use t-score instead of z-score? When the sample size is small (n < 30).
// @param src           (float) Source to calculate t-score
// @param mean          (float) Moving average.
// @param length        (int) The sequential period to calcule the sample standard deviation
// @param degreesOfFreedom   (int)  Degrees of freedom. The number of values in the final calculation of a statistic that are free to vary.
//   Default value is n-1, where n here is length. Only applies when biased parameter is defined as false.
export tscore(float src, float mean, simple int length, simple int degreesOfFreedom) =>
    float stdDev = stDev(src=src, length=length, mean=mean, biased=false, degreesOfFreedom=degreesOfFreedom)
    float stdError = stErr(stdDev, length)
    float tscore = (src - mean) / stdError

// #endregion
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {



// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// #region ATR

// @function 			ATR: Average True Range. Customized version with source parameter.
// @param source		(float) Source
// @param length		(int) Length (number of bars back)
// @returns 			(float) ATR
export atr(float source, simple int length) =>

    _high = ta.highest(source, 2)
    _low = ta.lowest(source, 2)

	trueRange = na(_high[1]) ? _high-_low : math.max(math.max(_high - _low, math.abs(_high - source[1])), math.abs(_low - source[1]))

    ta.rma(trueRange, length)

// @function 			ATRP (Average True Range Percent)
// @param length		(int) Length (number of bars back) for ATR
// @param sourceP		(float) Source for calculating percentage relativity
// @returns 			(float) ATRP
export atrp(simple int length, float sourceP) =>

    atr = ta.atr(length)
    atrp = (atr/sourceP)*100

// @function 			ATRP (Average True Range Percent). Customized version with source parameter.
// @param source		(float) Source for ATR
// @param length		(int) Length (number of bars back) for ATR
// @param sourceP		(float) Source for calculating percentage relativity
// @returns 			(float) ATRP
export atrp(float source, simple int length, float sourceP) =>

    _high = ta.highest(source, 2)
    _low = ta.lowest(source, 2)

	trueRange = na(_high[1]) ? _high-_low : math.max(math.max(_high - _low, math.abs(_high - source[1])), math.abs(_low - source[1]))

    atr = ta.rma(trueRange, length)
    atrp = (atr/sourceP)*100

// #endregion
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }



// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// #region Historical Volatility

// @function 			Historical Volatility (Accumulated ATR Version)
// @param lengthATR		(int) Length (number of bars back) for ATR
// @param lengthHist	(int) Length (number of bars back) for Historical Volatility
// @returns 			(float) Historical Volatility
export historicalVolatilityATR(simple int lengthATR, simple int lengthHist) =>
    historicalVolatility = 0.0

    xATR = ta.atr(lengthATR)
    historicalVolatility := ((lengthHist - 1) * nz(historicalVolatility[1], 0) + xATR) / lengthHist

// @function 			Historical Volatility (Accumulated ATR Version)
// @param source		(float) Source for ATR
// @param lengthATR		(int) Length (number of bars back) for ATR
// @param lengthHist	(int) Length (number of bars back) for Historical Volatility
// @returns 			(float) Historical Volatility
export historicalVolatilityATR(float source, simple int lengthATR, simple int lengthHist) =>
    historicalVolatility = 0.0

    xATR = atr(source, lengthATR)
    historicalVolatility := ((lengthHist - 1) * nz(historicalVolatility[1], 0) + xATR) / lengthHist

// @function             Historical Volatility (Returns version)
// @param source         (float) Source to calculate returns.
// @param sampleLength   (int) Period to calculate mean and standard deviation.
// @param returnLength   (int) Period to annualize/monthly/convert historical volatility. 252 for trading days per year,
//  52 for weeks per year, 12 for months per year. Default is 252.
// @param degreesOfFreedom   (int)  Degrees of freedom. The number of values in the final calculation of a statistic that are free to vary.
//   Default value is n-1, where n here is length. Only applies when biased parameter is defined as false.
export historicalVolatilityRet(float source, simple int sampleLength, simple int returnLength=252, simple int degreesOfFreedom=na) =>
    _return = math.log(source/source[1])
    mean = ta.sma(_return, sampleLength)
    stDev = stDev(src=_return, length=sampleLength, mean=mean, biased=false, degreesOfFreedom=degreesOfFreedom)
    historicalVolatility = stDev * math.sqrt(returnLength) * 100
// #endregion
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }



// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// #region Normalization

// @function 			Min-Max Scale Normalization. Maximum and minimum values are taken from the sequential range of
// numbars bars back, where numbars is a number defined by the user.
// @param src   		(float) Source to normalize
// @param numbars		(int) Numbers of sequential bars back to seek for lowest and hightest values.
// @returns 			(float) Normalized value
export minMaxNormalization(series float src, simple int numbars) =>
    min = ta.lowest(source=src, length=numbars)
    max = ta.highest(source=src, length=numbars)
    scaledValue = (src - min)/(max - min)

// @function 			Min-Max Scale Normalization. Maximum and minimum values are taken from the sequential range of
// numbars bars back, where numbars is a number defined by the user.
// In this implementation, the user explicitly provides the desired minimum (min) and maximum (max) values for the scale,
// rather than using the minimum and maximum values from the data.
// @param src   		(float) Source to normalize
// @param numbars		(int) Numbers of sequential bars back to seek for lowest and hightest values.
// @param minimumLimit	(float) Minimum value to scale
// @param maximumLimit	(float) Maximum value to scale
// @returns 			(float) Normalized value
export minMaxNormalization(series float src, simple int numbars, simple float minimumLimit, simple float maximumLimit) =>
    min = ta.lowest(source=src, length=numbars)
    max = ta.highest(source=src, length=numbars)
    scaledValue = minimumLimit + ((src - min)*(maximumLimit - minimumLimit))/(max - min)

// @function 		    Mean Normalization
// @param src   		(float) Source to normalize
// @param numbars		(int) Numbers of sequential bars back to seek for lowest and hightest values.
// @param mean	        (float) Mean of source
// @returns 			(float) Normalized value
export meanNormalization(series float src, simple int numbars, float mean) =>
    min = ta.lowest(source=src, length=numbars)
    max = ta.highest(source=src, length=numbars)
    scaledValue = src - mean / (max - min)

// @function 		    Standardization (Z-score Normalization). How "outside the mean" values relate to the standard deviation (ratio between first and second)
// @param src   		(float) Source to normalize
// @param mean	        (float) Mean of source
// @param stDev	        (float) Standard Deviation
// @returns 			(float) Normalized value
export standardization(series float src, float mean, float stDev) =>
    scaledValue = src - mean / stDev

// @function 		    Scaling to unit length
// @param src   		(float) Source to normalize
// @param numbars		(int) Numbers of sequential bars back to seek for lowest and hightest values.
// @returns 			(float) Normalized value
export scalingToUnitLength(series float src, simple int numbars) =>
    min = ta.lowest(source=src, length=numbars)
    max = ta.highest(source=src, length=numbars)
    scaledValue = src / (max - min)

// #endregion
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }



// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// #region LS Volatility Index

// @function 			    LS Volatility Index. Measures the volatility of price in relation to an moving average.
// @param movingAverage     (float) The moving average to be used in the calculation. It can be any moving average,
//  not just the simple moving average (SMA). It can be an exponential moving average (EMA), weighted moving average (WMA), etc.
// @param historicalVolatilityType (string) Type of historical volatility to be used in the calculation.\n
//  Default value is 'Returns'. Possible values are:\n
//  'Returns': Historical Volatility calculated from returns (logarithmic).\n
//  'Accumulated ATR': Historical Volatility calculated from accumulated ATR.
// @param source            (float) Source for calculating the historical volatility
// @param sampleLength      (int) Length for calculating the historical volatility.
// @param lengthATR         (float) Length for calculating the ATR (Average True Range). Only applies when historicalVolatilityType is 'Accumulated ATR'.
// @param returnLength      (int) Period to annualize/monthly/convert historical volatility. 252 for trading days per year,
//  52 for weeks per year, 12 for months per year. Default is 252. Only applies when historicalVolatilityType is 'Returns'.
// @param degreesOfFreedom  (int)  Degrees of freedom for calculating the historical volatility.
//  Default value is n-1, where n here is sampleLength. Only applies when historicalVolatilityType is 'Returns'.
//  The standard deviation is calculated using the unbiased sample variance with this degrees of freedom.
// @param lenNormal         (float) Length for normalization. If not defined (na value), the result will not be normalized.
// @param lowerLimit        (float) Lower limit for normalization. Only used if lenNormal is defined.
// @param upperLimit        (float) Upper limit for normalization. Only used if lenNormal is defined.
// @returns 			    (float) LS Volatility Index
export lsVolatilityIndex(
  // Moving Average parameters
  float movingAverage,
  // Historical Volatility parameters
  simple string historicalVolatilityType='Returns',
  float source, simple int sampleLength,
  simple int returnLength=252, simple int degreesOfFreedom,
  simple int lengthATR,
  // Normalization parameters
  simple int lenNormal=na, simple int lowerLimit=0, simple int upperLimit=100) =>

    // Distance between price and moving average.
    // If price is above MA, take the high value of bar to calculate the distance, otherwise the low value
    distanceMA = ( math.abs ( (high >= movingAverage ? high : low) - movingAverage) / movingAverage ) * 100

    hvol = switch historicalVolatilityType
        'Returns' => historicalVolatilityRet(source=source, sampleLength=sampleLength, returnLength=returnLength, degreesOfFreedom=degreesOfFreedom)
        'Accumulated ATR' => historicalVolatilityATR(source=source, lengthATR=lengthATR, lengthHist=sampleLength)

    lsVol = (distanceMA/hvol) * 100
    lsVol := na(lenNormal) ? lsVol : minMaxNormalization(lsVol, lenNormal, lowerLimit, upperLimit)

// @function 			    LS Volatility Index. Measures the volatility of price in relation to an moving average.
// @param _high             (float) High source
// @param _low              (float) Low source
// @param movingAverage     (float) The moving average to be used in the calculation. It can be any moving average,
//  not just the simple moving average (SMA). It can be an exponential moving average (EMA), weighted moving average (WMA), etc.
// @param historicalVolatilityType (string) Type of historical volatility to be used in the calculation.\n
//  Default value is 'Returns'. Possible values are:\n
//  'Returns': Historical Volatility calculated from returns (logarithmic).\n
//  'Accumulated ATR': Historical Volatility calculated from accumulated ATR.
// @param source            (float) Source for calculating the historical volatility
// @param sampleLength      (int) Length for calculating the historical volatility.
// @param lengthATR         (float) Length for calculating the ATR (Average True Range). Only applies when historicalVolatilityType is 'Accumulated ATR'.
// @param returnLength      (int) Period to annualize/monthly/convert historical volatility. 252 for trading days per year,
//  52 for weeks per year, 12 for months per year. Default is 252. Only applies when historicalVolatilityType is 'Returns'.
// @param degreesOfFreedom  (int)  Degrees of freedom for calculating the historical volatility.
//  Default value is n-1, where n here is sampleLength. Only applies when historicalVolatilityType is 'Returns'.
//  The standard deviation is calculated using the unbiased sample variance with this degrees of freedom.
// @param lenNormal         (float) Length for normalization. If not defined (na value), the result will not be normalized.
// @param lowerLimit        (float) Lower limit for normalization. Only used if lenNormal is defined.
// @param upperLimit        (float) Upper limit for normalization. Only used if lenNormal is defined.
// @returns 			    (float) LS Volatility Index
export lsVolatilityIndex(
  // Price parameters
  float _high, float _low,
  // Moving Average parameters
  float movingAverage,
  // Historical Volatility parameters
  simple string historicalVolatilityType='Returns',
  float source, simple int sampleLength,
  simple int returnLength=252, simple int degreesOfFreedom,
  simple int lengthATR,
  // Normalization parameters
  simple int lenNormal=na, simple int lowerLimit=0, simple int upperLimit=100) =>

    // Distance between price and moving average.
    // If price is above MA, take the high value of bar to calculate the distance, otherwise the low value
    distanceMA = ( math.abs ( (_high >= movingAverage ? _high : _low) - movingAverage) / movingAverage ) * 100

    hvol = switch historicalVolatilityType
        'Returns' => historicalVolatilityRet(source=source, sampleLength=sampleLength, returnLength=returnLength, degreesOfFreedom=degreesOfFreedom)
        'Accumulated ATR' => historicalVolatilityATR(source=source, lengthATR=lengthATR, lengthHist=sampleLength)

    lsVol = (distanceMA/hvol) * 100
    lsVol := na(lenNormal) ? lsVol : minMaxNormalization(lsVol, lenNormal, lowerLimit, upperLimit)

// #endregion
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {



// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// #region Dynamic Zones

// @function 			Get Dynamic Zones
// @param source		(float) Source
// @param sampleLength	(int) Sample Length
// @param pcntAbove		(float) Calculates the top of the dynamic zone, considering that the maximum values are above x% of the sample
// @param pcntBelow		(float) Calculates the bottom of the dynamic zone, considering that the minimum values are below x% of the sample
// @returns 			[float, float, float] A tuple with 3 series of values: (1) Upper Line of Dynamic Zone;
//						(2) Lower Line of Dynamic Zone; (3) Center of Dynamic Zone (x = 50%)
export dynamicZone(float source, simple int sampleLength, simple float pcntAbove, simple float pcntBelow) =>

	dZoneAbove 	= ta.percentile_nearest_rank(source, sampleLength, pcntAbove)
	dZoneBelow 	= ta.percentile_nearest_rank(source, sampleLength, 100 - pcntBelow)
	dZoneCenter	= ta.percentile_nearest_rank(source, sampleLength, 100 - 50)

	[dZoneAbove, dZoneBelow, dZoneCenter]

// #endregion
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }



// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// #region Bollinger Bands

// @function        Bollinger Bands. A Bollinger Band is a technical analysis tool defined by a set of lines plotted
//  two standard deviations (positively and negatively) away from a simple moving average (SMA) of the security's price,
// but can be adjusted to user preferences. In this version you can pass a customized basis (moving average), not only SMA.
// @param src       (float) Source to calculate standard deviation used in Bollinger Bands
// @param length    (int) The time period to be used in calculating the standard deviation
// @param mult      (float) Multiplier used in standard deviation. Basically, the upper/lower bands are standard deviation multiplied by this.
// @param basis     (float) Basis of Bollinger Bands (a moving average)
// @returns 	    (float) A tuple of Bollinger Bands, where index 1=basis; 2=basis+dev; 3=basis-dev; and dev=multiplier*stdev
export bollingerBands(float src, simple int length, simple float mult=2.0, float basis) =>
    float dev = mult * ta.stdev(src, length)
    [basis, basis + dev, basis - dev]

// @function        Bollinger Bands (multiple bands version). A Bollinger Band is a technical analysis tool defined by a set of lines plotted
//  two standard deviations (positively and negatively) away from a simple moving average (SMA) of the security's price,
// but can be adjusted to user preferences. In this version you can pass a customized basis (moving average), not only SMA.
// Also, various multipliers can be passed, thus getting more bands (instead of just 2).
// @param src       (float) Source to calculate standard deviation used in Bollinger Bands
// @param length    (int) The time period to be used in calculating the standard deviation
// @param aMult     (float[]) An array of multipliers used in standard deviation. Basically, the upper/lower bands are standard deviation multiplied by this.
// This array of multipliers permit the use of various bands, not only 2.
// @param basis     (float) Basis of Bollinger Bands (a moving average)
// @returns 	    (float[]) An array of Bollinger Bands, where:
// index 1=basis; 2=basis+dev1; 3=basis-dev1; 4=basis+dev2, 5=basis-dev2, 6=basis+dev2, 7=basis-dev2, Nup=basis+devN, Nlow=basis-devN
// and dev1, dev2, devN are ```multiplier N * stdev```
export bollingerBands(float src, simple int length, simple float[] aMult, float basis) =>
    float[] aBands = array.new_float(1, basis)
    float dev = ta.stdev(src, length)

    for i = 0 to array.size(aMult) - 1
        _dev = array.get(aMult, i) * dev
        array.push(aBands, basis + _dev)
        array.push(aBands, basis - _dev)

    aBands

// @function        Bollinger Bands %B - or Percent Bandwidth (%B).
// Quantify or display where price (or another source) is in relation to the bands.
// %B can be useful in identifying trends and trading signals.
// Calculation:
// %B = (Current Price - Lower Band) / (Upper Band - Lower Band)
// @param src       (float) Source to calculate standard deviation used in Bollinger Bands
// @param length    (int) The time period to be used in calculating the standard deviation
// @param mult      (float) Multiplier used in standard deviation
// @param basis     (float) Basis of Bollinger Bands (a moving average)
// @returns 	    (float) Bollinger Bands %B
export bollingerBandsB(float src, simple int length, simple float mult=2.0, float basis) =>
    float dev = mult * ta.stdev(src, length)
    float upper = basis + dev
    float lower = basis - dev
    float bbr = (src - lower)/(upper - lower)

// @function        Bollinger Bands %B - or Percent Bandwidth (%B). Multiple bands version.
// Quantify or display where price (or another source) is in relation to the bands.
// %B can be useful in identifying trends and trading signals.
// Calculation
// %B = (Current Price - Lower Band) / (Upper Band - Lower Band)
// @param src       (float) Source to calculate standard deviation used in Bollinger Bands
// @param length    (int) The time period to be used in calculating the standard deviation
// @param aMult     (float[]) Array of multiplier used in standard deviation. Basically, the upper/lower bands are standard deviation multiplied by this.
// This array of multipliers permit the use of various bands, not only 2.
// @param basis     (float) Basis of Bollinger Bands (a moving average)
// @returns 	    (float[]) An array of Bollinger Bands %B. The number of results in this array is equal the numbers of multipliers passed via parameter.
export bollingerBandsB(float src, simple int length, simple float[] aMult, float basis) =>
    float[] aBBR = array.new_float(0)
    float dev = ta.stdev(src, length)

    for i = 0 to array.size(aMult) - 1
        _dev = array.get(aMult, i) * dev
        float upper = basis + _dev
        float lower = basis - _dev
        float bbr = (src - lower)/(upper - lower)
        array.push(aBBR, bbr)

    aBBR

// @function        Bollinger Bands Width. Serve as a way to quantitatively measure the width between the Upper and Lower Bands
// Calculation:
// Bollinger Bands Width = (Upper Band - Lower Band) / Middle Band
// @param src       (float) Source to calculate standard deviation used in Bollinger Bands
// @param length    (int) Sequential period to calculate the standard deviation
// @param mult      (float) Multiplier used in standard deviation
// @param basis     (float) Basis of Bollinger Bands (a moving average)
// @returns 	    (float) Bollinger Bands Width
export bollingerBandsW(float src, simple int length, simple float mult=2.0, float basis) =>
    float dev = mult * ta.stdev(src, length)
    float upper = basis + dev
    float lower = basis - dev
    float bbw = (upper-lower)/basis

// @function        Bollinger Bands Width. Serve as a way to quantitatively measure the width between the Upper and Lower Bands
// Calculation
// Bollinger Bands Width = (Upper Band - Lower Band) / Middle Band
// @param src       (float) Source to calculate standard deviation used in Bollinger Bands. Multiple bands version.
// @param length    (int) Sequential period to calculate the standard deviation
// @param aMult     (float[]) Array of multiplier used in standard deviation. Basically, the upper/lower bands are standard deviation multiplied by this.
// This array of multipliers permit the use of various bands, not only 2.
// @param basis     (float) Basis of Bollinger Bands (a moving average)
// @returns 	    (float[]) An array of Bollinger Bands Width. The number of results in this array is equal the numbers of multipliers passed via parameter.
export bollingerBandsW(float src, simple int length, simple float[] aMult, float basis) =>

    float[] aBBW = array.new_float(0)
    float dev = ta.stdev(src, length)

    for i = 0 to array.size(aMult) - 1
        _dev = array.get(aMult, i) * dev
        float upper = basis + _dev
        float lower = basis - _dev
        float bbw = (upper-lower)/basis
        array.push(aBBW, bbw)

    aBBW

// #endregion
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }



// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// #region Keltner Channels

// @function        Keltner Channels. The Keltner Channels (KC) indicator is a banded indicator similar to Bollinger Bands
//  and Moving Average Envelopes. They consist of an Upper Envelope above a Middle Line as well as a Lower Envelope below
//  the Middle Line. The Middle Line is a moving average of price over a user-defined time period. Either a simple moving average or an
//  exponential moving average are typically used. The Upper and Lower Envelopes are set a (user-defined multiple) of a
//  range away from the Middle Line. This can be a multiple of the daily high/low range, or more commonly a multiple of the Average True Range.
// @param length        (int) The time period to be used in calculating the "Average True Range" and "Range"
// @param basis         (float) Basis of Keltner Channels (a moving average)
// @param bandsStyle    (string) Bands Style. Valid options: "True Range" (default), "Average True Range", "Range".
// @param multiplier    (float) Multiplier used to calc the bands. Default is 2.
// @returns 	        (float) A tuple of Keltner Channels, where index 1=basis; 2=basis+dev; 3=basis-dev; and dev=multiplier*range
export keltnerChannels(simple int length, float basis, simple string bandsStyle="True Range", float multiplier=2) =>
    _range = switch bandsStyle
        "True Range" => ta.tr(true)
        "Average True Range" => ta.atr(length)
        "Range" => ta.rma(high - low, length)

    upperBand = basis + _range * multiplier
    lowerBand = basis - _range * multiplier
    [basis, upperBand, lowerBand]

// @function        Keltner Channels (version with custom source).
//  The Keltner Channels (KC) indicator is a banded indicator similar to Bollinger Bands
//  and Moving Average Envelopes. They consist of an Upper Envelope above a Middle Line as well as a Lower Envelope below
//  the Middle Line. The Middle Line is a moving average of price over a user-defined time period. Either a simple moving average or an
//  exponential moving average are typically used. The Upper and Lower Envelopes are set a (user-defined multiple) of a
//  range away from the Middle Line. This can be a multiple of the daily high/low range, or more commonly a multiple of the Average True Range.
// @param source        (float) Source to calculate "Average True Range" and "Range"
// @param length        (int) The time period to be used in calculating the "Average True Range" and "Range"
// @param basis         (float) Basis of Keltner Channels (a moving average)
// @param bandsStyle    (string) Bands Style. Valid options: "True Range" (default), "Average True Range", "Range".
// @param multiplier    (float) Multiplier used to calc the bands. Default is 2.
// @returns 	        (float) A tuple of Keltner Channels, where index 1=basis; 2=basis+dev; 3=basis-dev; and dev=multiplier*range
export keltnerChannels(float source, simple int length, float basis, simple string bandsStyle="True Range", float multiplier=2) =>

    _high = ta.highest(source, 2)
    _low = ta.lowest(source, 2)

    _range = switch bandsStyle
        "True Range" => ta.tr(true)
        "Average True Range" => atr(source, length)
        "Range" => MAPROXY.rma(_high - _low, length)

    upperBand = basis + _range * multiplier
    lowerBand = basis - _range * multiplier
    [basis, upperBand, lowerBand]


// @function        Keltner Channels (version with custom source and multiple bands).
//  The Keltner Channels (KC) indicator is a banded indicator similar to Bollinger Bands
//  and Moving Average Envelopes. They consist of an Upper Envelope above a Middle Line as well as a Lower Envelope below
//  the Middle Line. The Middle Line is a moving average of price over a user-defined time period. Either a simple moving average or an
//  exponential moving average are typically used. The Upper and Lower Envelopes are set a (user-defined multiple) of a
//  range away from the Middle Line. This can be a multiple of the daily high/low range, or more commonly a multiple of the Average True Range.
// @param aMult         (float[]) An array of multipliers used in calculation. Basically, the upper/lower bands are ranges multiplied by this.
// @param source        (float) Source to calculate "Average True Range" and "Range"
// @param length        (int) The time period to be used in calculating the "Average True Range" and "Range"
// @param basis         (float) Basis of Keltner Channels (a moving average)
// @param bandsStyle    (string) Bands Style. Valid options: "True Range" (default), "Average True Range", "Range".
// @returns 	        (float[]) An array of Keltner Channels, where:
// index 1=basis; 2=basis+dev1; 3=basis-dev1; 4=basis+dev2, 5=basis-dev2, 6=basis+dev2, 7=basis-dev2, Nup=basis+devN, Nlow=basis-devN
// and dev1, dev2, devN are ```multiplier N * stdev```
export keltnerChannels(simple float[] aMult, float source, simple int length, float basis, simple string bandsStyle="True Range") =>
    float[] aBands = array.new_float(1, basis)

    _high = ta.highest(source, 2)
    _low = ta.lowest(source, 2)

    _range = switch bandsStyle
        "True Range" => ta.tr(true)
        "Average True Range" => atr(source, length)
        "Range" => MAPROXY.rma(_high - _low, length)

    for i = 0 to array.size(aMult) - 1
        _dev = array.get(aMult, i) * _range
        array.push(aBands, basis + _dev)
        array.push(aBands, basis - _dev)

    aBands

// @function        Keltner Channels %B - or Percent Bandwidth (%B).
//  This is the same as Bollinger Bands %B, but instead of Bollinger Bands, the logic of Keltner Channels is used to obtain the Percent Bandwidth (%B).
// Calculation:
//      float kcb = (source - lower band)/(upper band - lower band)
//  The Keltner Channels (KC) indicator is a banded indicator similar to Bollinger Bands
//  and Moving Average Envelopes. They consist of an Upper Envelope above a Middle Line as well as a Lower Envelope below
//  the Middle Line. The Middle Line is a moving average of price over a user-defined time period. Either a simple moving average or an
//  exponential moving average are typically used. The Upper and Lower Envelopes are set a (user-defined multiple) of a
//  range away from the Middle Line. This can be a multiple of the daily high/low range, or more commonly a multiple of the Average True Range.
// @param source        (float) Source to calculate "Average True Range" and "Range"
// @param length        (int) The time period to be used in calculating the "Average True Range" and "Range"
// @param basis         (float) Basis of Keltner Channels (a moving average)
// @param bandsStyle    (string) Bands Style. Valid options: "True Range" (default), "Average True Range", "Range".
// @param multiplier    (float) Multiplier used to calc the bands. Default is 2.
// @returns 	        (float) Percent Bandwidth (%B)
export keltnerChannelsB(float source, simple int length, float basis, simple string bandsStyle="True Range", float multiplier=2) =>

    _high = ta.highest(source, 2)
    _low = ta.lowest(source, 2)

    _range = switch bandsStyle
        "True Range" => ta.tr(true)
        "Average True Range" => atr(source, length)
        "Range" => MAPROXY.rma(_high - _low, length)

    upperBand = basis + _range * multiplier
    lowerBand = basis - _range * multiplier
    kcB = (source - lowerBand)/(upperBand - lowerBand)

// @function        Keltner Channels %B - or Percent Bandwidth (%B). Multiple bands version.
//  This is the same as Bollinger Bands %B, but instead of Bollinger Bands, the logic of Keltner Channels is used to obtain the Percent Bandwidth (%B).
// Calculation:
//      float kcb = (source - lower band)/(upper band - lower band)
//  The Keltner Channels (KC) indicator is a banded indicator similar to Bollinger Bands
//  and Moving Average Envelopes. They consist of an Upper Envelope above a Middle Line as well as a Lower Envelope below
//  the Middle Line. The Middle Line is a moving average of price over a user-defined time period. Either a simple moving average or an
//  exponential moving average are typically used. The Upper and Lower Envelopes are set a (user-defined multiple) of a
//  range away from the Middle Line. This can be a multiple of the daily high/low range, or more commonly a multiple of the Average True Range.
// @param aMult         (float[]) An array of multipliers used in calculation. Basically, the upper/lower bands are ranges multiplied by this.
// @param source        (float) Source to calculate "Average True Range" and "Range"
// @param length        (int) The time period to be used in calculating the "Average True Range" and "Range"
// @param basis         (float) Basis of Keltner Channels (a moving average)
// @param bandsStyle    (string) Bands Style. Valid options: "True Range" (default), "Average True Range", "Range".
// @param multiplier    (float) Multiplier used to calc the bands. Default is 2.
// @returns 	        (float) An array of Percent Bandwidth (%B)
export keltnerChannelsB(simple float[] aMult, float source, simple int length, float basis, simple string bandsStyle="True Range") =>
    float[] aKCB = array.new_float(0)

    _high = ta.highest(source, 2)
    _low = ta.lowest(source, 2)

    _range = switch bandsStyle
        "True Range" => ta.tr(true)
        "Average True Range" => atr(source, length)
        "Range" => MAPROXY.rma(_high - _low, length)

    for i = 0 to array.size(aMult) - 1
        _dev = array.get(aMult, i) * _range
        upperBand = basis + _dev
        lowerBand = basis - _dev
        kcB = (source - lowerBand)/(upperBand - lowerBand)
        array.push(aKCB, kcB)

    aKCB

// @function         Keltner Channels Width. Serve as a way to quantitatively measure the width between the Upper and Lower Bands.
// Same as Bollinger Bands Width, but instead of Bollinger Bands, the logic of Keltner Channels is used to obtain the width.
// Calculation:
//  Keltner Channels Width = (Upper Band - Lower Band) / Middle Band
// @param source        (float) Source to calculate "Average True Range" and "Range"
// @param length        (int) The time period to be used in calculating the "Average True Range" and "Range"
// @param basis         (float) Basis of Keltner Channels (a moving average)
// @param bandsStyle    (string) Bands Style. Valid options: "True Range" (default), "Average True Range", "Range".
// @param multiplier    (float) Multiplier used to calc the bands. Default is 2.
// @returns 	        (float) Percent Bandwidth (%B)
export keltnerChannelsW(float source, simple int length, float basis, simple string bandsStyle="True Range", float multiplier=2) =>

    _high = ta.highest(source, 2)
    _low = ta.lowest(source, 2)

    _range = switch bandsStyle
        "True Range" => ta.tr(true)
        "Average True Range" => atr(source, length)
        "Range" => MAPROXY.rma(_high - _low, length)

    upperBand = basis + _range * multiplier
    lowerBand = basis - _range * multiplier
    kcW = (upperBand - lowerBand)/basis

// @function         Keltner Channels Width. Serve as a way to quantitatively measure the width between the Upper and Lower Bands. Multiple bands version.
// Same as Bollinger Bands Width, but instead of Bollinger Bands, the logic of Keltner Channels is used to obtain the width.
// Calculation:
//  Keltner Channels Width = (Upper Band - Lower Band) / Middle Band
// @param aMult         (float[]) An array of multipliers used in calculation. Basically, the upper/lower bands are ranges multiplied by this.
// @param source        (float) Source to calculate "Average True Range" and "Range"
// @param length        (int) The time period to be used in calculating the "Average True Range" and "Range"
// @param basis         (float) Basis of Keltner Channels (a moving average)
// @param bandsStyle    (string) Bands Style. Valid options: "True Range" (default), "Average True Range", "Range".
// @param multiplier    (float) Multiplier used to calc the bands. Default is 2.
// @returns 	        (float[]) An array of Percent Bandwidth (%B)
export keltnerChannelsW(simple float[] aMult, float source, simple int length, float basis, simple string bandsStyle="True Range") =>
    float[] aKCW = array.new_float(0)

    _high = ta.highest(source, 2)
    _low = ta.lowest(source, 2)

    _range = switch bandsStyle
        "True Range" => ta.tr(true)
        "Average True Range" => atr(source, length)
        "Range" => MAPROXY.rma(_high - _low, length)

    for i = 0 to array.size(aMult) - 1
        _dev = array.get(aMult, i) * _range
        upperBand = basis + _dev
        lowerBand = basis - _dev
        kcW = (upperBand - lowerBand)/basis
        array.push(aKCW, kcW)

    aKCW

// #endregion
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }



// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// #region Moving Average Envelopes

// @function        Moving Average Envelopes. Moving Average Envelopes are percentage-based envelopes set above and
//   below a moving average. The moving average, which forms the base for
//   this indicator, can be a simple or exponential moving average. Each
//   envelope is then set the same percentage above or below the moving average.
//   This creates parallel bands that follow price action. With a moving average
//   as the base, Moving Average Envelopes can be used as a trend following indicator.
//   However, this indicator is not limited to just trend following. The envelopes
//   can also be used to identify overbought and oversold levels when the trend is
//   relatively flat.
//   Calculation:
//      Upper Band = moving average + (moving average x percent shift)
//      Lower Band = moving average - (moving average x percent shift)
// @param ma               (float) A moving average
// @param percentShift     (float) Multiplier used to calculate the envelope (percent shift).
// This array of multipliers permit the use of various bands, not only 2.
// @returns 	    (float) A tuple with envelope bands: [basis, upper band, lower band]
export movingAverageEnvelopes(float ma, simple float percentShift) =>

    dev = ma * percentShift / 100
    upperBand = ma + dev
    lowerBand = ma - dev

    [ma, upperBand, lowerBand]

// @function        Moving Average Envelopes. Multiple bands version.
//   Moving Average Envelopes are percentage-based envelopes set above and
//   below a moving average. The moving average, which forms the base for
//   this indicator, can be a simple or exponential moving average. Each
//   envelope is then set the same percentage above or below the moving average.
//   This creates parallel bands that follow price action. With a moving average
//   as the base, Moving Average Envelopes can be used as a trend following indicator.
//   However, this indicator is not limited to just trend following. The envelopes
//   can also be used to identify overbought and oversold levels when the trend is
//   relatively flat.
//   Calculation:
//      Upper Band = moving average + (moving average x percent shift)
//      Lower Band = moving average - (moving average x percent shift)
// @param ma                (float) A moving average
// @param aPercentShift     (float[]) An array of multipliers used to calculate the envelope (percent shift).
// This array of multipliers permit the use of various bands, not only 2.
// @returns 	    (float[]) An array of envelopes bands, where:
// index 1=basis; 2=basis+dev1; 3=basis-dev1; 4=basis+dev2, 5=basis-dev2, 6=basis+dev2, 7=basis-dev2, Nup=basis+devN, Nlow=basis-devN
// and dev1, dev2, devN are ```multiplier N * stdev```
export movingAverageEnvelopes(float ma, simple float[] aPercentShift) =>
    float[] aEnvelopes = array.new_float(1, ma)

    for i = 0 to array.size(aPercentShift) - 1
        _dev = ma * array.get(aPercentShift, i) / 100
        array.push(aEnvelopes, ma + _dev)
        array.push(aEnvelopes, ma - _dev)

    aEnvelopes

// #endregion
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }



// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// #region Donchian Channels

// @function        Donchian Channels.
//  The Donchian Channel is a useful indicator for seeing the volatility of a market price.
//  If a price is stable the Donchian channel will be relatively narrow. If the price fluctuates a lot the Donchian Channel will be wider.
//  It is formed by taking the highest high and the lowest low of the last n periods.
//  The area between the high and the low is the channel for the period chosen.
//  The upper channel line is the highest high of the last n periods.
//  The lower channel line is the lowest low of the last n periods.
// @param sourceHigh   (float) Source to calculate the highest high
// @param sourceLow    (float) Source to calculate the lowest low
// @param length       (int) The time period to be used in calculating the highest high and lowest low
// @returns 	       (float) A tuple of Donchian Channels, where index 1=upper band; 2=lower band; 3=basis
export donchianChannels(float sourceHigh, float sourceLow, simple int length) =>
    float upper = ta.highest(sourceHigh, length)
    float lower = ta.lowest(sourceLow, length)
    float basis = math.avg(upper, lower)

    [upper, lower, basis]

// @function        Donchian Channels with multiple bands.
//  The Donchian Channel is a useful indicator for seeing the volatility of a market price.
//  If a price is stable the Donchian channel will be relatively narrow. If the price fluctuates a lot the Donchian Channel will be wider.
//  It is formed by taking the highest high and the lowest low of the last n periods.
//  The area between the high and the low is the channel for the period chosen.
//  The upper channel line is the highest high of the last n periods.
//  The lower channel line is the lowest low of the last n periods.
// @param sourceHigh   (float) Source to calculate the highest high
// @param sourceLow    (float) Source to calculate the lowest low
// @param length       (int) Time periods to be used in calculating the highest high and lowest low
// @param aMultiplier  (float[]) An array of multipliers used in calculation.
// @param bandsStyle   (string) Bands Style. Valid options: "True Range" (default), "Average True Range", "Range", "Standard Deviation".
// @param sourceRange  (float) Source to calculate the range used in "Range"
// @returns 	       (float[]) An array of Donchian Channels, where:
//                      index 1=upper band; 2=lower band; 3=basis;
//                      4=upper band 2, 5=lower band 2, 6=basis 2 upper, 7=basis 2 lower,
//                      Nup=upper band N, Nlow=lower band N
export donchianChannels(float sourceHigh, float sourceLow, simple int length, simple float[] aMultiplier, simple string bandsStyle="True Range", float sourceRange) =>
    float[] aDonchian = array.new_float(0)
    float upper = ta.highest(sourceHigh, length)
    float lower = ta.lowest(sourceLow, length)
    float basis = math.avg(upper, lower)

    array.push(aDonchian, upper)
    array.push(aDonchian, lower)
    array.push(aDonchian, basis)

    _range = switch bandsStyle
        "True Range" => ta.tr(true)
        "Average True Range" => atr(sourceRange, length)
        "Range" => MAPROXY.rma(upper - lower, length)
        "Standard Deviation" => ta.stdev(sourceRange, length)

    for i = 0 to array.size(aMultiplier) - 1
        array.push(aDonchian, upper + (_range * array.get(aMultiplier, i)) )
        array.push(aDonchian, lower - (_range * array.get(aMultiplier, i)) )
        array.push(aDonchian, basis + (_range * array.get(aMultiplier, i)) )
        array.push(aDonchian, basis - (_range * array.get(aMultiplier, i)) )

    aDonchian

// #endregion
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }



// EXAMPLES

//@version=5
// indicator("Volatility Indicators (Examples)")
// import andre_007/VolatilityIndicators/2 as VOL

// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// Keltner Channels with multiple bands

// ma = ta.ema(hl2, 21)
// aMultipliers = array.from(3, 5, 8, 13, 21)

// aBands = keltnerChannels(aMult=aMultipliers, source=hl2, length=233, basis=ma, bandsStyle="Average True Range")

// plot(array.get(aBands, 0), 'Basis', color.white)

// plot(array.get(aBands, 1), 'Upper 1', color.blue)
// plot(array.get(aBands, 2), 'Lower 1', color.blue)

// plot(array.get(aBands, 3), 'Upper 2', color.yellow)
// plot(array.get(aBands, 4), 'Lower 2', color.yellow)

// plot(array.get(aBands, 5), 'Upper 3', color.red)
// plot(array.get(aBands, 6), 'Lower 3', color.red)

// plot(array.get(aBands, 7), 'Upper 4', color.purple)
// plot(array.get(aBands, 8), 'Lower 4', color.purple)

// plot(array.get(aBands, 9), 'Upper 5', color.fuchsia)
// plot(array.get(aBands, 10), 'Lower 5', color.fuchsia)

// Keltner Channels with 2 bands (traditional)

// ma = ta.ema(hl2, 200)
// [basis, upper, lower] = keltnerChannels(length=200, basis=ma, bandsStyle="Range", multiplier=2)

// plot(basis, 'Basis', color.white)

// plot(upper, 'Upper 1', color.blue)
// plot(lower, 'Lower 1', color.blue)

// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }

// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// Moving Average Envelopes

// ma = ta.ema(hl2, 21)
// [basis, upperBand, lowerBand] = movingAverageEnvelopes(ma, 2)

// plot(basis, 'Basis', color.white)
// plot(upperBand, 'Upper 1', color.blue)
// plot(lowerBand, 'Lower 1', color.blue)
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }



// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// Donchian Channels
// float sourceHigh = input.source(defval=high, title="Source High")
// float sourceLow = input.source(defval=low, title="Source Low")
// int length = input.int(defval=21, title="Length")

// [upper, lower, basis] = donchianChannels(sourceHigh, sourceLow, length)
// plot(series=upper, title="Upper", color=color.blue)
// plot(series=lower, title="Lower", color=color.blue)
// plot(series=basis, title="Basis", color=color.white)
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }

// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// Donchian Channels with multiple bands
// float sourceHigh = input.source(defval=high, title="Source High")
// float sourceLow = input.source(defval=low, title="Source Low")
// int length = input.int(defval=55, title="Length")
// float multiplier1 = input.float(defval = 0.618, title = 'Multipler')
// float multiplier2 = input.float(defval = 0.786, title = 'Multipler')
// float multiplier3 = input.float(defval = 1, title = 'Multipler')
// string rangeStyle = input.string(defval = "Average True Range", title = "Range Style", options = ["True Range", "Average True Range", "Range", "Standard Deviation"])
// float sourceRange = input.source(defval=hl2, title="Source Range")

// aMultipliers = array.from(multiplier1, multiplier2, multiplier3)

// aDonchian = donchianChannels(sourceHigh, sourceLow, length, aMultipliers, rangeStyle, sourceRange)

// plot(series=array.get(aDonchian, 0), title="Upper", color=color.yellow)
// plot(series=array.get(aDonchian, 1), title="Lower", color=color.yellow)
// plot(series=array.get(aDonchian, 2), title="Basis", color=color.white)

// plot(series=array.get(aDonchian, 3), title="Upper +1x", color=color.green)
// plot(series=array.get(aDonchian, 4), title="Lower -1x", color=color.green)
// plot(series=array.get(aDonchian, 5), title="Basis +1x", color=color.white)
// plot(series=array.get(aDonchian, 6), title="Basis -1x", color=color.white)

// plot(series=array.get(aDonchian, 7), title="Upper +2x", color=color.blue)
// plot(series=array.get(aDonchian, 8), title="Lower -2x", color=color.blue)
// plot(series=array.get(aDonchian, 9), title="Basis +2x", color=color.white)
// plot(series=array.get(aDonchian, 10), title="Basis -2x", color=color.white)

// plot(series=array.get(aDonchian, 11), title="Upper +3x", color=color.purple)
// plot(series=array.get(aDonchian, 12), title="Lower -3x", color=color.purple)
// plot(series=array.get(aDonchian, 13), title="Basis +3x", color=color.white)
// plot(series=array.get(aDonchian, 14), title="Basis -3x", color=color.white)
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }

// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// Moving Average Envelopes

// ma = ta.ema(hl2, 21)
// aMultipliers = array.from(3, 5, 8)

// aBands = movingAverageEnvelopes(ma, aMultipliers)

// plot(array.get(aBands, 0), 'Basis', color.white)

// plot(array.get(aBands, 1), 'Upper 1', color.blue)
// plot(array.get(aBands, 2), 'Lower 1', color.blue)

// plot(array.get(aBands, 3), 'Upper 2', color.yellow)
// plot(array.get(aBands, 4), 'Lower 2', color.yellow)

// plot(array.get(aBands, 5), 'Upper 3', color.red)
// plot(array.get(aBands, 6), 'Lower 3', color.red)
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }

// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// Keltner Channel B%

// ma = ta.ema(hl2, 55)

// bPerc = keltnerChannelsB(source=hl2, length=55, basis=ma, bandsStyle="Average True Range", multiplier=2)
// plot(bPerc, 'KC B%', color.white)
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }


// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// Keltner Channel B% with multiple bands

// ma = ta.ema(hl2, 55)

// aMultipliers = array.from(2, 3, 4)

// aPerc = keltnerChannelsB(aMult = aMultipliers, source=hl2, length=55, basis=ma, bandsStyle="Average True Range")
// plot(array.get(aPerc, 0), 'KC B% 1', color.white)
// plot(array.get(aPerc, 1), 'KC B% 2', color.yellow)
// plot(array.get(aPerc, 2), 'KC B% 3', color.red)

// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }


// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// Keltner Channels Width

// ma = ta.ema(hl2, 55)

// kcW = keltnerChannelsW(source=hl2, length=55, basis=ma, bandsStyle="Average True Range", multiplier=2)

// plot(kcW, 'KC Width', color.white)
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }


// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// Keltner Channels Width with multiple bands

// ma = ta.ema(hl2, 55)
// aMultipliers = array.from(2, 3, 4)

// akcW = keltnerChannelsW(aMult=aMultipliers, source=hl2, length=55, basis=ma, bandsStyle="Average True Range")

// plot(array.get(akcW, 0), 'KC Width 1', color.white)
// plot(array.get(akcW, 1), 'KC Width 2', color.yellow)
// plot(array.get(akcW, 2), 'KC Width 3', color.red)
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }


// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// Dinamic Zone

// [upper, lower, center] = VOL.dynamicZone(close, 55, 90, 90)

// plot(upper, 'Upper Line', color.green)
// plot(lower, 'Upper Line', color.red)
// plot(center, 'Upper Line', color.white)
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }


// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// ATR

// default_atr = ta.atr(13)
// new_atr = VOL.atr(hl2, 13)

// plot(default_atr, 'Default ATR', color.red)
// plot(new_atr, 'New ATR', color.green)
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }


// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// ATRP

// default_atrp = VOL.atrp(13, close)
// atrp = VOL.atrp(close, 13, close)

// plot(default_atrp, 'Default ATRP', color.red)
// plot(atrp, 'New ATRP', color.green)
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }


// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// historicalVolatility

// histo1 = VOL.historicalVolatility(13, 55)
// histo2 = VOL.historicalVolatility(hl2, 13, 55)

// plot(histo1, 'Histo without source', color.red)
// plot(histo2, 'Histo with custom source', color.green)
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }


// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// Normalization

// x1 = VOL.minMaxNormalization(hl2, 13)
// x2 = VOL.minMaxNormalization(hl2, 13, 0, 5)

// plot(x1, 'No limits', color.red)
// plot(x2, 'With limits', color.green)

// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }

// lsVolatilityIndex(float movingAverage, float sourceHvol, simple int lengthATR, simple int lengthHist) =>
// lsVolatilityIndex(float sourcePrice, float movingAverage, float sourceHvol, simple int lengthATR, simple int lengthHist) =>

// sma = ta.ema(hl2, 55)

// // With lower and upper limits
// ls1 = VOL.lsVolatilityIndex(movingAverage = sma, sourceHvol=hl2, lengthATR=55, lengthHist=55, lenNormal=89, lowerLimit=0, upperLimit=100)
// ls2 = VOL.lsVolatilityIndex(sourcePrice=hl2, movingAverage=sma, sourceHvol=hl2, lengthATR=55, lengthHist=55, lenNormal=89, lowerLimit=0, upperLimit=100)

// // Without lower and upper limits
// ls3 = VOL.lsVolatilityIndex(movingAverage = sma, sourceHvol=hl2, lengthATR=55, lengthHist=55)
// ls4 = VOL.lsVolatilityIndex(sourcePrice=hl2, movingAverage=sma, sourceHvol=hl2, lengthATR=55, lengthHist=55)

// plot(ls1, 'LS1', color.red)
// plot(ls2, 'LS2', color.blue)
// plot(ls3, 'LS1', color.yellow)
// plot(ls4, 'LS2', color.fuchsia)

// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }


// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// Bollinger Bands with multiple bands (3 bands)

// ma = ta.ema(hl2, 233)

// aMultipliers = array.from(1, 2, 3)

// aBands = VOL.bollingerBands(hl2, 233, aMultipliers, ma)

// plotbar(open, high, low, close, title='Price', color = open < close ? color.green : color.red)

// // Arrays size = (Number of multipliers x 2) = 6

// plot(array.get(aBands, 0), 'Basis', color.white)

// plot(array.get(aBands, 1), 'Upper 1', color.blue)
// plot(array.get(aBands, 2), 'Lower 1', color.blue)

// plot(array.get(aBands, 3), 'Upper 2', color.yellow)
// plot(array.get(aBands, 4), 'Lower 2', color.yellow)

// plot(array.get(aBands, 5), 'Upper 3', color.red)
// plot(array.get(aBands, 6), 'Lower 3', color.red)
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }


// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// Bollinger Bands with multiple bands (6 bands)

// ma = ta.ema(hl2, 233)

// aMultipliers = array.from(1, 2, 3, 4, 5, 6)

// aBands = VOL.bollingerBands(hl2, 233, aMultipliers, ma)

// plotbar(open, high, low, close, title='Price', color = open < close ? color.green : color.red)

// // Arrays size = (Number of multipliers x 2) = 12

// plot(array.get(aBands, 0), 'Basis', color.white)

// plot(array.get(aBands, 1), 'Upper 1', color.blue)
// plot(array.get(aBands, 2), 'Lower 1', color.blue)

// plot(array.get(aBands, 3), 'Upper 2', color.green)
// plot(array.get(aBands, 4), 'Lower 2', color.green)

// plot(array.get(aBands, 5), 'Upper 3', color.yellow)
// plot(array.get(aBands, 6), 'Lower 3', color.yellow)

// plot(array.get(aBands, 7), 'Upper 4', color.red)
// plot(array.get(aBands, 8), 'Lower 4', color.red)

// plot(array.get(aBands, 9), 'Upper 5', color.fuchsia)
// plot(array.get(aBands, 10), 'Lower 5', color.fuchsia)

// plot(array.get(aBands, 11), 'Upper 6', color.purple)
// plot(array.get(aBands, 12), 'Lower 6', color.purple)


// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }
// Bollinger Bands with default number of bands (2 bands)

// ma = ta.ema(hl2, 233)

// [basis, upper, lower] = VOL.bollingerBands(hl2, 233, 2, ma)

// plotbar(open, high, low, close, title='Price', color = open < close ? color.green : color.red)

// plot(basis, 'Basis', color.white)
// plot(upper, 'Upper 1', color.blue)
// plot(lower, 'Lower 1', color.blue)
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }


// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }
// Bollinger Bands B% (default version)

// ma = ta.ema(hl2, 233)
// bolllingerB = VOL.bollingerBandsB(src=hl2, length=233, mult=2.0, basis=ma)

// plot(bolllingerB, 'Bollinger B%', color.white)
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }


// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }
// Bollinger Bands B% (multiples bands)

// aMultipliers = array.from(1, 2, 3)
// ma = ta.ema(hl2, 21)
// aBolllingerB = VOL.bollingerBandsB(src=hl2, length=21, aMult=aMultipliers, basis=ma)

// // Arrays size = Number of multipliers
// plot(array.get(aBolllingerB, 0), 'B% 1', color.blue)
// plot(array.get(aBolllingerB, 1), 'B% 2', color.yellow)
// plot(array.get(aBolllingerB, 2), 'B% 3', color.red)
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }


// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// Bollinger Bands Width (default version with 2 bands)

// ma = ta.ema(hl2, 233)

// bbw = VOL.bollingerBandsW(src=hl2, length=233, mult=2.0, basis=ma)

// plot(bbw, 'BBW', color.blue)

// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }

// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// Bollinger Bands Width (version with multiple bands)

// aMultipliers = array.from(1, 2, 3)
// ma = ta.ema(hl2, 233)

// aBBW = VOL.bollingerBandsW(src=hl2, length=233, aMult=aMultipliers, basis=ma)

// // Arrays size = Number of multipliers
// plot(array.get(aBBW, 0), 'BBW 1', color.blue)
// plot(array.get(aBBW, 1), 'BBW 2', color.yellow)
// plot(array.get(aBBW, 2), 'BBW 3', color.red)
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }


// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// Difference between a source in relation to another source
// ma = ta.ema(hl2, 233)
// dev = VOL.dev(source=hl2, length=233, anotherSource=ma)
// plot(dev, 'DEV', color.blue)
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }


// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// Standard Deviation

// ma = ta.ema(hl2, 233)

// stdev1 = VOL.stDev(src=hl2, length = 14, mean=ma, biased=true)
// stdev2 = VOL.stDev(src=hl2, length = 14, mean=ma, biased=false, degreesOfFreedom=13)
// stdev3 = VOL.stDev(src=hl2, length = 14, mean=ma, biased=false, degreesOfFreedom=12)
// stdev4 = VOL.stDev(src=hl2, length = 14, mean=ma, biased=false, degreesOfFreedom=11)
// stdev5 = VOL.stDev(src=hl2, length = 14, mean=ma, biased=false, degreesOfFreedom=10)
// stdev6 = VOL.stDev(src=hl2, length = 14, mean=ma, biased=false, degreesOfFreedom=9)
// stdev7 = VOL.stDev(src=hl2, length = 14, mean=ma, biased=false, degreesOfFreedom=8)

// plot(stdev1, 'Std Dev 1 - Biased', color.blue)
// plot(stdev2, 'Std Dev 2 - Unbiased', color.yellow)
// plot(stdev3, 'Std Dev 3 - Unbiased (custom degree of freedom)', color.red)
// plot(stdev4, 'Std Dev 4 - Unbiased (custom degree of freedom)', color.fuchsia)
// plot(stdev5, 'Std Dev 5 - Unbiased (custom degree of freedom)', color.purple)
// plot(stdev6, 'Std Dev 6 - Unbiased (custom degree of freedom)', color.green)
// plot(stdev7, 'Std Dev 7 - Unbiased (custom degree of freedom)', color.aqua)
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }


// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// Z-score
// ma = ta.sma(hl2, 14)

// zscore1  = VOL.zscore(src=hl2, mean=ma, length=14, biased=true)
// zscore2  = VOL.zscore(src=hl2, mean=ma, length=14, biased=false, degreesOfFreedom=13)
// zscore3  = VOL.zscore(src=hl2, mean=ma, length=14, biased=false, degreesOfFreedom=12)
// zscore4  = VOL.zscore(src=hl2, mean=ma, length=14, biased=false, degreesOfFreedom=11)
// zscore5  = VOL.zscore(src=hl2, mean=ma, length=14, biased=false, degreesOfFreedom=10)
// zscore6  = VOL.zscore(src=hl2, mean=ma, length=14, biased=false, degreesOfFreedom=9)
// zscore7  = VOL.zscore(src=hl2, mean=ma, length=14, biased=false, degreesOfFreedom=8)
// zscore8  = VOL.zscore(src=hl2, mean=ma, length=14, biased=false, degreesOfFreedom=7)
// zscore9  = VOL.zscore(src=hl2, mean=ma, length=14, biased=false, degreesOfFreedom=6)
// zscore10 = VOL.zscore(src=hl2, mean=ma, length=14, biased=false, degreesOfFreedom=7)
// zscore11 = VOL.zscore(src=hl2, mean=ma, length=14, biased=false, degreesOfFreedom=4)
// zscore12 = VOL.zscore(src=hl2, mean=ma, length=14, biased=false, degreesOfFreedom=3)
// zscore13 = VOL.zscore(src=hl2, mean=ma, length=14, biased=false, degreesOfFreedom=2)


// plot(zscore1,  'Z-score 1 - Biased', color.blue)
// plot(zscore2,  'Z-score 2 - Unbiased', color.blue)
// plot(zscore3,  'Z-score 3 - Unbiased', color.green)
// plot(zscore4,  'Z-score 4 - Unbiased', color.green)
// plot(zscore5,  'Z-score 5 - Unbiased', color.yellow)
// plot(zscore6,  'Z-score 6 - Unbiased', color.yellow)
// plot(zscore7,  'Z-score 7 - Unbiased', color.red)
// plot(zscore8,  'Z-score 8 - Unbiased', color.red)
// plot(zscore9,  'Z-score 9 - Unbiased', color.fuchsia)
// plot(zscore10, 'Z-score 10 - Unbiased', color.fuchsia)
// plot(zscore11, 'Z-score 11 - Unbiased', color.purple)
// plot(zscore12, 'Z-score 12 - Unbiased', color.purple)
// plot(zscore13, 'Z-score 13 - Unbiased', color.purple)

// [upper, lower, center] = VOL.dynamicZone(zscore1, 55, 90, 90)

// plot(series=upper, title='Upper Line', color=color.green, linewidth=2)
// plot(series=lower, title='Upper Line', color=color.red, linewidth=2)
// plot(series=center,title= 'Upper Line', color=color.white, linewidth=2)

// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }


// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— {
// T-Score

// indicator(title='T-Score (T-Student)', shorttitle="T-Score",
//   format=format.price, precision=2, overlay = false)
// import andre_007/VolatilityIndicators/6 as VOLATILITY

// // Standard Deviation
// srcStd = input(close, title="Source for Standard Deviation", group="Standard Deviation")
// lengthStd = input.int(21, minval=1, title="Length of Standard Deviation", group="Standard Deviation")
// biasedStd = input.bool(true, title="Biased Standard Deviation", group="Standard Deviation")
// degreesOfFreedomStd = input.int(1, minval=1, title="Degrees of Freedom for Standard Deviation", group="Standard Deviation")
// lenghMAstd = input.int(21, minval=1, title="Length of MA for Standard Deviation", group="Standard Deviation")

// // Standard Error
// lenghError = input.int(21, minval=1, title='Length for Standard Error', group="Standard Error")

// maStd = ta.ema(srcStd, lenghMAstd)
// stdDev = VOLATILITY.stDev(src=srcStd, length=lengthStd, mean=maStd, biased=biasedStd, degreesOfFreedom=degreesOfFreedomStd)

// stdError = VOLATILITY.stErr(stdDev=stdDev, sampleSize=lenghError)

// float tscore = (srcStd - maStd) / stdError
// float zscore = VOLATILITY.zscore(src=srcStd, mean=maStd, length=lengthStd, biased=true, degreesOfFreedom=degreesOfFreedomStd)

// plot(tscore, title='T-Score', color=color.red, linewidth=2, style=plot.style_line)
// plot(zscore, title='Z-Score', color=color.blue, linewidth=2, style=plot.style_line)
// ———————————————————————————————————————————————————————————————————————————————————————————————————————————— }
 