Heikin Ashi Volatility Percentile - TraderHalai
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/

// This Volatility Oscillator was inspired by the legendary Bollinger Band Width Percentile indicator(known as BBWP), written by Caretaker, and made famous by Eric Krown, a famous influencer. 
//
// This script borrows aspects of the BBWP indicator which enables the HAVP oscillator to visually match the look and feel of BBWP and allows similar configuration functions (such as colouring function and alerts)
//
// The fundamentals of this script are however different to BBWP. Instead of bollinger band width, this script uses a reverse function of heikin ashi close implemented in my Smoothed Heikin Ashi Trend
// indicator. This reverse Heikin Ashi close is smoothed using Ehler's SuperSmoother function, which provides very smooth oscillation and earlier signals of volatility tops and bottoms.

// From backtesting this on BTCUSD index pair, I have observed comparable performance to BBWP across multiple timeframes when combining with stochastic direction to give a bias
// on overall direction. Using parameters I have tested, it performs better on mid term timeframes such as 3h,4h and 6h. BBWP outperforms on 1h and 1d, with lower timeframes being comparable.

// The sharper oscillations tend to result in reduced holding time and more frequent trades, which may or may not be desirable, although these can be adjusted using the parameters provided.

// This makes it a viable (but not necessarily better) alternative to BBWP, and I would encourage you to try out both oscillators and see which fits your trading style.

// Releasing this as open source to allow for the betterment of the communuity and to allow further development, criticism and discussion.

// © TraderHalai

//@version=5
indicator("Heikin Ashi Volatility Percentile - TraderHalai", " HAVP", overlay=false, format=format.percent, precision=2, max_bars_back = 1000)

timeperiod = timeframe.period

var string STM = 'Spectrum'
var string SLD = 'Solid'
var string BGR = 'Blue Green Red'
var string BR  = 'Blue Red'
var string SH1 = "Red ⮀ Green"
var string SH2 = "Purple ⮀ Blue"
var string SH3 = "Yellow ⮀  DarkGreen"
var string SH4 = "White ⮀ Black"
var string SH5 = "White ⮀ Blue"
var string SH6 = "White ⮀ Red"
var string SH7 = "Rainbow"

//Inputs
i_lookback      = input.int     (252,     "Bars Lookback",                      minval=10,                                                          group='HAVP Calculation Settings')
ssfLength       = input.int     (13,   "HAVP Smoothing Length",                 minval=1,                                                           group='HAVP Calculation Settings')
i_havp_maType   = input.string  ("Three-pole Ehlers Smoother", "HAVP Smoothing type", options= ["Two-pole Ehlers Butterworth", "Two-pole Ehlers smoother", "Three-pole Ehlers Butterworth", "Three-pole Ehlers Smoother"], group='HAVP Calculation Settings')
i_haCandleType  = input.string  ("Heikin Ashi", "Heikin Ashi Candle Type",      options=["Heikin Ashi", "Heikin Ashi Better"],                      group='HAVP Calculation Settings')
i_roofingOn     = input.bool    (false,     'High Pass Roofing Filter',                                                                 inline='1', group='HAVP Calculation Settings')
i_roofingPeriod = input.int     (192,        'Length',                                                                                  inline='1', group='HAVP Calculation Settings')

i_c_havpTyped   = input.string  ( STM,      'Color Type',                       options=[STM, SLD],                                     inline='1', group='HAVP Plot Settings')
i_c_spctrmType  = input.string  ( BGR,      'Spectrum',                         options=[BR, BGR, SH1, SH2, SH3, SH4, SH5, SH6, SH7],   inline='1', group='HAVP Plot Settings')
i_c_havpsolid   = input.color   ( #FFFF00,  'Solid',                                                                                    inline='1', group='HAVP Plot Settings')
i_havpWidth     = input.int     ( 2,        'Line Width',                       minval=1, maxval=4,                                     inline='2', group='HAVP Plot Settings')
i_repaint       = input.bool    (true,    "Repaint",                                                                                    inline='2', group='HAVP Plot Settings')
i_invert        = input.bool    (false,   "Invert Colors - e2 only")
i_ma1On         = input.bool    ( true, '',                                                                                             inline='1', group='Moving Average Settings')
i_ma1Type       = input.string  ( 'SMA',    'MA 1 Type',                        options=['SMA', 'EMA', 'VWMA'],                         inline='1', group='Moving Average Settings')
i_c_ma1         = input.color   ( #FFFFFF,  '',                                                                                         inline='1', group='Moving Average Settings')
i_ma1Len        = input.int     ( 3,        'Length',                           minval=1,                                               inline='1', group='Moving Average Settings')
i_ma2On         = input.bool    ( false,    '',                                                                                         inline='2', group='Moving Average Settings')
i_ma2Type       = input.string  ( 'SMA',    'MA 2 Type',                        options=['SMA', 'EMA', 'VWMA'],                         inline='2', group='Moving Average Settings')
i_c_ma2         = input.color   ( #00FFFF,  '',                                                                                         inline='2', group='Moving Average Settings')
i_ma2Len        = input.int     ( 5,        'Length',                           minval=1,                                               inline='2', group='Moving Average Settings')

i_alrtsOn       = input.bool    ( true,     'Alerts On',                                                                                            group='Visual Alerts')
i_upperLevel    = input.int     ( 98,       'Extreme High',                     minval=1, inline='1',                                               group='Visual Alerts')
i_lowerLevel    = input.int     ( 2,        'Extreme Low',                      minval=1, inline='1',                                               group='Visual Alerts')


var c_prcntSpctrm1 = array.new_color(na)
if barstate.isfirst
    array.push ( c_prcntSpctrm1, #0000FF )
    array.push ( c_prcntSpctrm1, #000AFF ), array.push ( c_prcntSpctrm1, #0014FF ), array.push ( c_prcntSpctrm1, #001FFF ), array.push ( c_prcntSpctrm1, #0029FF ), array.push ( c_prcntSpctrm1, #0033FF ),
    array.push ( c_prcntSpctrm1, #003DFF ), array.push ( c_prcntSpctrm1, #0047FF ), array.push ( c_prcntSpctrm1, #0052FF ), array.push ( c_prcntSpctrm1, #005CFF ), array.push ( c_prcntSpctrm1, #0066FF ),
    array.push ( c_prcntSpctrm1, #0070FF ), array.push ( c_prcntSpctrm1, #007AFF ), array.push ( c_prcntSpctrm1, #0085FF ), array.push ( c_prcntSpctrm1, #008FFF ), array.push ( c_prcntSpctrm1, #0099FF ),
    array.push ( c_prcntSpctrm1, #00A3FF ), array.push ( c_prcntSpctrm1, #00ADFF ), array.push ( c_prcntSpctrm1, #00B8FF ), array.push ( c_prcntSpctrm1, #00C2FF ), array.push ( c_prcntSpctrm1, #00CCFF ),
    array.push ( c_prcntSpctrm1, #00D6FF ), array.push ( c_prcntSpctrm1, #00E0FF ), array.push ( c_prcntSpctrm1, #00EBFF ), array.push ( c_prcntSpctrm1, #00F5FF ), array.push ( c_prcntSpctrm1, #00FFFF ),
    array.push ( c_prcntSpctrm1, #00FFF5 ), array.push ( c_prcntSpctrm1, #00FFEB ), array.push ( c_prcntSpctrm1, #00FFE0 ), array.push ( c_prcntSpctrm1, #00FFD6 ), array.push ( c_prcntSpctrm1, #00FFCC ),
    array.push ( c_prcntSpctrm1, #00FFC2 ), array.push ( c_prcntSpctrm1, #00FFB8 ), array.push ( c_prcntSpctrm1, #00FFAD ), array.push ( c_prcntSpctrm1, #00FFA3 ), array.push ( c_prcntSpctrm1, #00FF99 ),
    array.push ( c_prcntSpctrm1, #00FF8F ), array.push ( c_prcntSpctrm1, #00FF85 ), array.push ( c_prcntSpctrm1, #00FF7A ), array.push ( c_prcntSpctrm1, #00FF70 ), array.push ( c_prcntSpctrm1, #00FF66 ),
    array.push ( c_prcntSpctrm1, #00FF5C ), array.push ( c_prcntSpctrm1, #00FF52 ), array.push ( c_prcntSpctrm1, #00FF47 ), array.push ( c_prcntSpctrm1, #00FF3D ), array.push ( c_prcntSpctrm1, #00FF33 ),
    array.push ( c_prcntSpctrm1, #00FF29 ), array.push ( c_prcntSpctrm1, #00FF1F ), array.push ( c_prcntSpctrm1, #00FF14 ), array.push ( c_prcntSpctrm1, #00FF0A ), array.push ( c_prcntSpctrm1, #00FF00 ),
    array.push ( c_prcntSpctrm1, #0AFF00 ), array.push ( c_prcntSpctrm1, #14FF00 ), array.push ( c_prcntSpctrm1, #1FFF00 ), array.push ( c_prcntSpctrm1, #29FF00 ), array.push ( c_prcntSpctrm1, #33FF00 ),
    array.push ( c_prcntSpctrm1, #3DFF00 ), array.push ( c_prcntSpctrm1, #47FF00 ), array.push ( c_prcntSpctrm1, #52FF00 ), array.push ( c_prcntSpctrm1, #5CFF00 ), array.push ( c_prcntSpctrm1, #66FF00 ),
    array.push ( c_prcntSpctrm1, #70FF00 ), array.push ( c_prcntSpctrm1, #7AFF00 ), array.push ( c_prcntSpctrm1, #85FF00 ), array.push ( c_prcntSpctrm1, #8FFF00 ), array.push ( c_prcntSpctrm1, #99FF00 ),
    array.push ( c_prcntSpctrm1, #A3FF00 ), array.push ( c_prcntSpctrm1, #ADFF00 ), array.push ( c_prcntSpctrm1, #B8FF00 ), array.push ( c_prcntSpctrm1, #C2FF00 ), array.push ( c_prcntSpctrm1, #CCFF00 ),
    array.push ( c_prcntSpctrm1, #D6FF00 ), array.push ( c_prcntSpctrm1, #E0FF00 ), array.push ( c_prcntSpctrm1, #EBFF00 ), array.push ( c_prcntSpctrm1, #F5FF00 ), array.push ( c_prcntSpctrm1, #FFFF00 ),
    array.push ( c_prcntSpctrm1, #FFF500 ), array.push ( c_prcntSpctrm1, #FFEB00 ), array.push ( c_prcntSpctrm1, #FFE000 ), array.push ( c_prcntSpctrm1, #FFD600 ), array.push ( c_prcntSpctrm1, #FFCC00 ),
    array.push ( c_prcntSpctrm1, #FFC200 ), array.push ( c_prcntSpctrm1, #FFB800 ), array.push ( c_prcntSpctrm1, #FFAD00 ), array.push ( c_prcntSpctrm1, #FFA300 ), array.push ( c_prcntSpctrm1, #FF9900 ),
    array.push ( c_prcntSpctrm1, #FF8F00 ), array.push ( c_prcntSpctrm1, #FF8500 ), array.push ( c_prcntSpctrm1, #FF7A00 ), array.push ( c_prcntSpctrm1, #FF7000 ), array.push ( c_prcntSpctrm1, #FF6600 ),
    array.push ( c_prcntSpctrm1, #FF5C00 ), array.push ( c_prcntSpctrm1, #FF5200 ), array.push ( c_prcntSpctrm1, #FF4700 ), array.push ( c_prcntSpctrm1, #FF3D00 ), array.push ( c_prcntSpctrm1, #FF3300 ),
    array.push ( c_prcntSpctrm1, #FF2900 ), array.push ( c_prcntSpctrm1, #FF1F00 ), array.push ( c_prcntSpctrm1, #FF1400 ), array.push ( c_prcntSpctrm1, #FF0000 ), array.push ( c_prcntSpctrm1, #FF0000 )

var c_prcntSpctrm2 = array.new_color(na)
if barstate.isfirst
    array.push ( c_prcntSpctrm2, #0000FF )
    array.push ( c_prcntSpctrm2, #0200FC ), array.push ( c_prcntSpctrm2, #0500F9 ), array.push ( c_prcntSpctrm2, #0700F7 ), array.push ( c_prcntSpctrm2, #0A00F4 ), array.push ( c_prcntSpctrm2, #0C00F2 ),
    array.push ( c_prcntSpctrm2, #0F00EF ), array.push ( c_prcntSpctrm2, #1100ED ), array.push ( c_prcntSpctrm2, #1400EA ), array.push ( c_prcntSpctrm2, #1600E8 ), array.push ( c_prcntSpctrm2, #1900E5 ),
    array.push ( c_prcntSpctrm2, #1C00E2 ), array.push ( c_prcntSpctrm2, #1E00E0 ), array.push ( c_prcntSpctrm2, #2100DD ), array.push ( c_prcntSpctrm2, #2300DB ), array.push ( c_prcntSpctrm2, #2600D8 ),
    array.push ( c_prcntSpctrm2, #2800D6 ), array.push ( c_prcntSpctrm2, #2B00D3 ), array.push ( c_prcntSpctrm2, #2D00D1 ), array.push ( c_prcntSpctrm2, #3000CE ), array.push ( c_prcntSpctrm2, #3300CC ),
    array.push ( c_prcntSpctrm2, #3500C9 ), array.push ( c_prcntSpctrm2, #3800C6 ), array.push ( c_prcntSpctrm2, #3A00C4 ), array.push ( c_prcntSpctrm2, #3D00C1 ), array.push ( c_prcntSpctrm2, #3F00BF ),
    array.push ( c_prcntSpctrm2, #4200BC ), array.push ( c_prcntSpctrm2, #4400BA ), array.push ( c_prcntSpctrm2, #4700B7 ), array.push ( c_prcntSpctrm2, #4900B5 ), array.push ( c_prcntSpctrm2, #4C00B2 ),
    array.push ( c_prcntSpctrm2, #4F00AF ), array.push ( c_prcntSpctrm2, #5100AD ), array.push ( c_prcntSpctrm2, #5400AA ), array.push ( c_prcntSpctrm2, #5600A8 ), array.push ( c_prcntSpctrm2, #5900A5 ),
    array.push ( c_prcntSpctrm2, #5B00A3 ), array.push ( c_prcntSpctrm2, #5E00A0 ), array.push ( c_prcntSpctrm2, #60009E ), array.push ( c_prcntSpctrm2, #63009B ), array.push ( c_prcntSpctrm2, #660099 ),
    array.push ( c_prcntSpctrm2, #680096 ), array.push ( c_prcntSpctrm2, #6B0093 ), array.push ( c_prcntSpctrm2, #6D0091 ), array.push ( c_prcntSpctrm2, #70008E ), array.push ( c_prcntSpctrm2, #72008C ),
    array.push ( c_prcntSpctrm2, #750089 ), array.push ( c_prcntSpctrm2, #770087 ), array.push ( c_prcntSpctrm2, #7A0084 ), array.push ( c_prcntSpctrm2, #7C0082 ), array.push ( c_prcntSpctrm2, #7F007F ),
    array.push ( c_prcntSpctrm2, #82007C ), array.push ( c_prcntSpctrm2, #84007A ), array.push ( c_prcntSpctrm2, #870077 ), array.push ( c_prcntSpctrm2, #890075 ), array.push ( c_prcntSpctrm2, #8C0072 ),
    array.push ( c_prcntSpctrm2, #8E0070 ), array.push ( c_prcntSpctrm2, #91006D ), array.push ( c_prcntSpctrm2, #93006B ), array.push ( c_prcntSpctrm2, #960068 ), array.push ( c_prcntSpctrm2, #990066 ),
    array.push ( c_prcntSpctrm2, #9B0063 ), array.push ( c_prcntSpctrm2, #9E0060 ), array.push ( c_prcntSpctrm2, #A0005E ), array.push ( c_prcntSpctrm2, #A3005B ), array.push ( c_prcntSpctrm2, #A50059 ),
    array.push ( c_prcntSpctrm2, #A80056 ), array.push ( c_prcntSpctrm2, #AA0054 ), array.push ( c_prcntSpctrm2, #AD0051 ), array.push ( c_prcntSpctrm2, #AF004F ), array.push ( c_prcntSpctrm2, #B2004C ),
    array.push ( c_prcntSpctrm2, #B50049 ), array.push ( c_prcntSpctrm2, #B70047 ), array.push ( c_prcntSpctrm2, #BA0044 ), array.push ( c_prcntSpctrm2, #BC0042 ), array.push ( c_prcntSpctrm2, #BF003F ),
    array.push ( c_prcntSpctrm2, #C1003D ), array.push ( c_prcntSpctrm2, #C4003A ), array.push ( c_prcntSpctrm2, #C60038 ), array.push ( c_prcntSpctrm2, #C90035 ), array.push ( c_prcntSpctrm2, #CC0033 ),
    array.push ( c_prcntSpctrm2, #CE0030 ), array.push ( c_prcntSpctrm2, #D1002D ), array.push ( c_prcntSpctrm2, #D3002B ), array.push ( c_prcntSpctrm2, #D60028 ), array.push ( c_prcntSpctrm2, #D80026 ),
    array.push ( c_prcntSpctrm2, #DB0023 ), array.push ( c_prcntSpctrm2, #DD0021 ), array.push ( c_prcntSpctrm2, #E0001E ), array.push ( c_prcntSpctrm2, #E2001C ), array.push ( c_prcntSpctrm2, #E50019 ),
    array.push ( c_prcntSpctrm2, #E80016 ), array.push ( c_prcntSpctrm2, #EA0014 ), array.push ( c_prcntSpctrm2, #ED0011 ), array.push ( c_prcntSpctrm2, #EF000F ), array.push ( c_prcntSpctrm2, #F2000C ),
    array.push ( c_prcntSpctrm2, #F4000A ), array.push ( c_prcntSpctrm2, #F70007 ), array.push ( c_prcntSpctrm2, #F90005 ), array.push ( c_prcntSpctrm2, #FC0002 ), array.push ( c_prcntSpctrm2, #FF0000 )


// —————————— e2 Color(s) array {
var color[] c_gradients = array.new_color(na)

// ————— Fill the array with colors only on the first iteration of the script
if barstate.isfirst
    if i_c_spctrmType == SH1
        array.push(c_gradients, #ff0000), array.push(c_gradients, #ff2c00), array.push(c_gradients, #fe4200), array.push(c_gradients, #fc5300), array.push(c_gradients, #f96200), array.push(c_gradients, #f57000), array.push(c_gradients, #f07e00), array.push(c_gradients, #ea8a00), array.push(c_gradients, #e39600), array.push(c_gradients, #dca100), array.push(c_gradients, #d3ac00), array.push(c_gradients, #c9b600), array.push(c_gradients, #bfc000), array.push(c_gradients, #b3ca00), array.push(c_gradients, #a6d400), array.push(c_gradients, #97dd00), array.push(c_gradients, #86e600), array.push(c_gradients, #71ee00), array.push(c_gradients, #54f700), array.push(c_gradients, #1eff00)
    else if i_c_spctrmType == SH2
        array.push(c_gradients, #ff00d4), array.push(c_gradients, #f71fda), array.push(c_gradients, #ef2ee0), array.push(c_gradients, #e63ae5), array.push(c_gradients, #de43ea), array.push(c_gradients, #d44bee), array.push(c_gradients, #cb52f2), array.push(c_gradients, #c158f6), array.push(c_gradients, #b75df9), array.push(c_gradients, #ac63fb), array.push(c_gradients, #a267fe), array.push(c_gradients, #966bff), array.push(c_gradients, #8b6fff), array.push(c_gradients, #7e73ff), array.push(c_gradients, #7276ff), array.push(c_gradients, #6479ff), array.push(c_gradients, #557bff), array.push(c_gradients, #437eff), array.push(c_gradients, #2e80ff), array.push(c_gradients, #0082ff)
    else if i_c_spctrmType == SH3
        array.push(c_gradients, #fafa6e), array.push(c_gradients, #e0f470), array.push(c_gradients, #c7ed73), array.push(c_gradients, #aee678), array.push(c_gradients, #97dd7d), array.push(c_gradients, #81d581), array.push(c_gradients, #6bcc86), array.push(c_gradients, #56c28a), array.push(c_gradients, #42b98d), array.push(c_gradients, #2eaf8f), array.push(c_gradients, #18a48f), array.push(c_gradients, #009a8f), array.push(c_gradients, #00908d), array.push(c_gradients, #008589), array.push(c_gradients, #007b84), array.push(c_gradients, #0c707d), array.push(c_gradients, #196676), array.push(c_gradients, #215c6d), array.push(c_gradients, #275263), array.push(c_gradients, #2a4858)
    else if i_c_spctrmType == SH4
        array.push(c_gradients, #ffffff), array.push(c_gradients, #f0f0f0), array.push(c_gradients, #e1e1e1), array.push(c_gradients, #d2d2d2), array.push(c_gradients, #c3c3c3), array.push(c_gradients, #b5b5b5), array.push(c_gradients, #a7a7a7), array.push(c_gradients, #999999), array.push(c_gradients, #8b8b8b), array.push(c_gradients, #7e7e7e), array.push(c_gradients, #707070), array.push(c_gradients, #636363), array.push(c_gradients, #575757), array.push(c_gradients, #4a4a4a), array.push(c_gradients, #3e3e3e), array.push(c_gradients, #333333), array.push(c_gradients, #272727), array.push(c_gradients, #1d1d1d), array.push(c_gradients, #121212), array.push(c_gradients, #000000)
    else if i_c_spctrmType == SH5
        array.push(c_gradients, #ffffff), array.push(c_gradients, #f4f5fa), array.push(c_gradients, #e9ebf5), array.push(c_gradients, #dee1f0), array.push(c_gradients, #d3d7eb), array.push(c_gradients, #c8cde6), array.push(c_gradients, #bdc3e1), array.push(c_gradients, #b2b9dd), array.push(c_gradients, #a7b0d8), array.push(c_gradients, #9ca6d3), array.push(c_gradients, #919dce), array.push(c_gradients, #8594c9), array.push(c_gradients, #7a8bc4), array.push(c_gradients, #6e82bf), array.push(c_gradients, #6279ba), array.push(c_gradients, #5570b5), array.push(c_gradients, #4768b0), array.push(c_gradients, #385fab), array.push(c_gradients, #2557a6), array.push(c_gradients, #004fa1)
    else if i_c_spctrmType == SH6
        array.push(c_gradients, #ffffff), array.push(c_gradients, #fff4f1), array.push(c_gradients, #ffe9e3), array.push(c_gradients, #ffded6), array.push(c_gradients, #ffd3c8), array.push(c_gradients, #fec8bb), array.push(c_gradients, #fdbdae), array.push(c_gradients, #fbb2a1), array.push(c_gradients, #f8a794), array.push(c_gradients, #f69c87), array.push(c_gradients, #f3917b), array.push(c_gradients, #f0856f), array.push(c_gradients, #ec7a62), array.push(c_gradients, #e86e56), array.push(c_gradients, #e4634a), array.push(c_gradients, #df563f), array.push(c_gradients, #db4933), array.push(c_gradients, #d63a27), array.push(c_gradients, #d0291b), array.push(c_gradients, #cb0e0e)
    else if i_c_spctrmType == SH7
        array.push(c_gradients, #E50000), array.push(c_gradients, #E6023B), array.push(c_gradients, #E70579), array.push(c_gradients, #E908B7), array.push(c_gradients, #E00BEA), array.push(c_gradients, #A70DEB), array.push(c_gradients, #6E10ED), array.push(c_gradients, #3613EE), array.push(c_gradients, #162DEF), array.push(c_gradients, #1969F1), array.push(c_gradients, #1CA4F2), array.push(c_gradients, #1FDFF4), array.push(c_gradients, #22F5D2), array.push(c_gradients, #25F69C), array.push(c_gradients, #28F867), array.push(c_gradients, #2CF933), array.push(c_gradients, #5DFA2F), array.push(c_gradients, #96FC32), array.push(c_gradients, #CDFD35), array.push(c_gradients, #FFF938)
    
    // Invert colors in array
    if i_invert
        array.reverse(c_gradients)
        
// ————— Rescale function 
// Credits to LucF, https://www.pinecoders.com/faq_and_code/#how-can-i-rescale-an-indicator-from-one-scale-to-another
f_rescale(_src, _min, _max) =>
    // Rescales series with known min/max to the color array size.
    // Dependency : c_gradients array
    // _src       : series to rescale.
    // _min, _max : min/max values of series to rescale.
    var int _size = array.size(c_gradients) - 1
    int _colorStep = int(_size * (_src - _min)) / int(math.max(_max - _min, 10e-10))
    _colorStep := _colorStep > _size ? _size : _colorStep < 0 ? 0 : _colorStep
    int(_colorStep)

// ————— Result
// Dependency : c_gradients array
f_colGrad(_src, _min, _max) => array.get(c_gradients, f_rescale(_src, _min, _max))


//FUNCTIONS

//EHLER Smoothers

//Three pole Ehlers Butterworth
_3polebuttfilt(src, len)=>
    a1 = 0., b1 = 0., c1 = 0.
    coef1 = 0., coef2 = 0., coef3 = 0., coef4 = 0.
    bttr = 0., trig = 0.
    a1 := math.exp(-math.pi / len)
    b1 := 2 * a1 * math.cos(1.738 * math.pi / len)
    c1 := a1 * a1
    coef2 := b1 + c1
    coef3 := -(c1 + b1 * c1)
    coef4 := c1 * c1
    coef1 := (1 - b1 + c1) * (1 - c1) / 8
    bttr := coef1 * (src + 3 * nz(src[1]) + 3 * nz(src[2]) + nz(src[3])) + coef2 * nz(bttr[1]) + coef3 * nz(bttr[2]) + coef4 * nz(bttr[3])
    bttr := bar_index < 4 ? src : bttr
    trig := nz(bttr[1])
    bttr

_ssf3(src, length) =>
    arg = math.pi / length
    a1 = math.exp(-arg)
    b1 = 2 * a1 * math.cos(1.738 * arg)
    c1 = math.pow(a1, 2)

    coef4 = math.pow(c1, 2)
    coef3 = -(c1 + b1 * c1)
    coef2 = b1 + c1
    coef1 = 1 - coef2 - coef3 - coef4

    src1 = nz(src[1], src)
    src2 = nz(src[2], src1)
    src3 = nz(src[3], src2)

    ssf = 0.0
    ssf := coef1 * src + coef2 * nz(ssf[1], src1) + coef3 * nz(ssf[2], src2) + coef4 * nz(ssf[3], src3)

//Three pole Ehlers smoother
_3polesss(src, len)=>
    a1 = 0., b1 = 0., c1 = 0.
    coef1 = 0., coef2 = 0., coef3 = 0., coef4 = 0.
    filt = 0., trig = 0.
    a1 := math.exp(-math.pi / len)
    b1 := 2 * a1 * math.cos(1.738 * math.pi / len)
    c1 := a1 * a1
    coef2 := b1 + c1
    coef3 := -(c1 + b1 * c1)
    coef4 := c1 * c1
    coef1 := 1 - coef2 - coef3 - coef4
    filt := coef1 * src + coef2 * nz(filt[1]) + coef3 * nz(filt[2]) + coef4 * nz(filt[3])
    filt := bar_index < 4 ? src : filt
    trig := nz(filt[1])
    filt

//Two pole Ehlers Butterworth
_2polebutter(src, len)=>
    a1 = 0., b1 = 0.
    coef1 = 0., coef2 = 0., coef3 = 0.
    bttr = 0., trig = 0.
    a1 := math.exp(-1.414 * math.pi / len)
    b1 := 2 * a1 * math.cos(1.414 * math.pi / len)
    coef2 := b1
    coef3 := -a1 * a1
    coef1 := (1 - b1 + a1 * a1) / 4
    bttr := coef1 * (src + 2 * nz(src[1]) + nz(src[2])) + coef2 * nz(bttr[1]) + coef3 * nz(bttr[2])
    bttr := bar_index < 3 ? src : bttr
    trig := nz(bttr[1])
    bttr

//Two pole Ehlers smoother
_2poless(src, len)=>
    a1 = 0., b1 = 0.
    coef1 = 0., coef2 = 0., coef3 = 0.
    filt = 0., trig = 0.
    a1 := math.exp(-1.414 * math.pi / len)
    b1 := 2 * a1 * math.cos(1.414 * math.pi / len)
    coef2 := b1
    coef3 := -a1 * a1
    coef1 := 1 - coef2 - coef3
    filt := coef1 * src + coef2 * nz(filt[1]) + coef3 * nz(filt[2])
    filt := bar_index < 3 ? src : filt
    trig := nz(filt[1])
    filt

_roofingFilt(src, len)=>
    alpha1 = 0.00, HP = 0.00
    //Highpass filter cyclic components whose periods are shorter than x bars
    alpha1 := (math.cos(2*math.pi / len) + math.sin(2*math.pi / len) - 1) / math.cos(2*math.pi / len)
    HP := (1 - alpha1 / 2)*(src - nz(src[1])) + (1 - alpha1)*nz(HP[1])
    HP

//Other functions
f_security(_symbol, _res, _src, _repaint) => request.security(_symbol, _res, _src[_repaint ? 0 : barstate.isrealtime ? 1 : 0])[_repaint ? 0 : barstate.isrealtime ? 0 : 1]

f_clrSlct ( _percent, _select, _type, _solid, _array1, _array2 ) =>
    _select == 'Solid' ? _solid : _type == BGR or _type == BR ? array.get ( _type == 'Blue Green Red' ? _array1 : _array2, math.round ( _percent ) ) : f_colGrad(math.round (_percent), 0, 100)
    
f_havpMaType (_price, _len, _type) =>
    _type == 'Three-pole Ehlers Smoother' ? _3polesss(_price, _len) : _type == "Two-pole Ehlers Butterworth" ?  _2polebutter(_price, _len) : _type == "Two-pole Ehlers smoother" ?  _2poless(_price, _len) :  _type == "Three-pole Ehlers Butterworth" ?  _3polebuttfilt(_price, _len) : _ssf3(_price, _len)


f_maType ( _price, _len, _type ) =>
    _type == 'SMA' ? ta.sma ( _price, _len ) : _type == 'EMA' ? ta.ema ( _price, _len ) : ta.vwma ( _price, _len )

f_delta () =>
    candleClose = f_security(syminfo.tickerid, timeperiod, close, i_repaint)
    candleOpen = f_security(syminfo.tickerid, timeperiod, open, i_repaint)
    candleLow = f_security(syminfo.tickerid, timeperiod, low, i_repaint)
    candleHigh = f_security(syminfo.tickerid, timeperiod, high, i_repaint)

    haTicker = ticker.heikinashi(syminfo.tickerid)
    haClose = f_security(haTicker, timeperiod, close, i_repaint)
    haOpen = f_security(haTicker, timeperiod, open, i_repaint)
    haLow = f_security(haTicker, timeperiod, low, i_repaint)
    haHigh= f_security(haTicker, timeperiod, high, i_repaint)
    
    reverseClose = (2 * (haOpen[1] + haClose[1])) - candleHigh - candleLow - candleOpen

    if(reverseClose < candleLow)
        reverseClose := (candleLow + reverseClose) / 2

    if(reverseClose > candleHigh)
        reverseClose := (candleHigh + reverseClose) / 2
    
    delta = close - reverseClose
    delta
    
//Heikin Ashi Better Close Delta
f_betterDelta() =>
    candleClose = f_security(syminfo.tickerid, timeperiod, close, i_repaint)
    candleOpen = f_security(syminfo.tickerid, timeperiod, open, i_repaint)
    candleLow = f_security(syminfo.tickerid, timeperiod, low, i_repaint)
    candleHigh = f_security(syminfo.tickerid, timeperiod, high, i_repaint)


    habClose = ((candleOpen + candleClose) /2 ) + ((candleClose - candleOpen)/(candleHigh-candleLow)) * (math.abs(candleClose - candleOpen) / 2)
    habOpen = 0.
    habOpen := (nz(habOpen[1]) + habClose[1])/2


    //Calculate reverse Better HA close - as this formula uses Square roots, it is possible for non real values to be produced, therefore we need to try all 4 permutations
    
    // c = 1/2 (sqrt(-8 a h + 8 a l + h^2 - 2 h l + 8 h o + l^2 - 8 l o) + h - l + 2 o) - Formula sourced from Wolfram Alpha, solving for close price.

    reverseBetterA = (-math.sqrt(candleHigh - candleLow)    * math.sqrt(candleHigh - candleLow + 8 * candleOpen - 8 * habOpen) + candleHigh - candleLow + 2*candleOpen) /2
    reverseBetterB = (math.sqrt(candleHigh - candleLow)     * math.sqrt(candleHigh - candleLow - 8 * candleOpen + 8 * habOpen) + candleHigh - candleLow + 2*candleOpen) /2
    reverseBetterC = (-math.sqrt(candleHigh - candleLow)     * math.sqrt(candleHigh - candleLow - 8 * candleOpen + 8 * habOpen) - candleHigh + candleLow + 2*candleOpen) /2
    reverseBetterD = (math.sqrt(candleHigh - candleLow)     * math.sqrt(candleHigh - candleLow - 8 * candleOpen + 8 * habOpen) - candleHigh + candleLow + 2*candleOpen) /2

    //Try reverse reverse function to see which one fits

    rreverseBetterA = ((candleOpen + reverseBetterA) /2 ) + ((reverseBetterA - candleOpen)/(candleHigh-candleLow)) * (math.abs(reverseBetterA - candleOpen) / 2)
    rreverseBetterB = ((candleOpen + reverseBetterB) /2 ) + ((reverseBetterB - candleOpen)/(candleHigh-candleLow)) * (math.abs(reverseBetterB - candleOpen) / 2)
    rreverseBetterC = ((candleOpen + reverseBetterC) /2 ) + ((reverseBetterC - candleOpen)/(candleHigh-candleLow)) * (math.abs(reverseBetterC - candleOpen) / 2)
    rreverseBetterD = ((candleOpen + reverseBetterD) /2 ) + ((reverseBetterD - candleOpen)/(candleHigh-candleLow)) * (math.abs(reverseBetterD - candleOpen) / 2)

    float reverseClose = na

    if(rreverseBetterA == habOpen)
        reverseClose := reverseBetterA
    else if(rreverseBetterB == habOpen)
        reverseClose := reverseBetterB
    else if(rreverseBetterC == habOpen)
        reverseClose := reverseBetterC
    else if(rreverseBetterD == habOpen)
        reverseClose := reverseBetterD
        
    if(reverseClose < candleLow)
        reverseClose := (candleLow + reverseClose) / 2

    if(reverseClose > candleHigh)
        reverseClose := (candleHigh + reverseClose) / 2
    
    delta = close - reverseClose
    delta

f_haType (_type) =>
    _type == 'Heikin Ashi' ? f_delta() : _type == 'Heikin Ashi Better' ? f_betterDelta() : f_delta()

f_havp (_lookbackPeriod, _ssfLength, _type, _haType, _roofing, _roofingPeriod) =>
    _delta = math.abs(f_haType(_haType))
    if(_roofing)
        _delta := _roofingFilt(_delta, _roofingPeriod)
    _havp = f_havpMaType(_delta,_ssfLength, _type)
    _havpSum = 0.0
    _len = bar_index < _lookbackPeriod ? bar_index : _lookbackPeriod
    for _i = 1 to _len by 1
        _havpSum += ( _havp[_i] > _havp ? 0 : 1 )
        _havpSum
    _return = bar_index >= _ssfLength ? ( _havpSum / _len) * 100 : na
    _return
    

// calculations

havp        = f_havp(i_lookback, ssfLength, i_havp_maType, i_haCandleType, i_roofingOn, i_roofingPeriod)
c_havp    = f_clrSlct ( havp, i_c_havpTyped, i_c_spctrmType, i_c_havpsolid, c_prcntSpctrm1, c_prcntSpctrm2 )
havpMA      =  i_ma1On ? f_maType ( havp, i_ma1Len, i_ma1Type ) : na
havpMA2     = i_ma2On ? f_maType ( havp, i_ma2Len, i_ma2Type ) : na
hiAlrtBar   = i_alrtsOn and havp >= i_upperLevel ? havp : na
loAlrtBar   = i_alrtsOn and havp <= i_lowerLevel ? havp : na


// plots

p_scaleHi   = hline ( 100,  'Scale High', #ff0000, hline.style_solid )
p_midLine   = hline ( 50,   'Mid-Line', color.silver, hline.style_dashed )
p_scaleLo   = hline ( 0,    'Scale Low', #0000ff, hline.style_solid )

p_bbwp      = plot ( havp,      'HAVP',         c_havp, i_havpWidth, plot.style_line, editable=false )
p_hiAlrt    = plot ( hiAlrtBar, 'Extreme Hi',   c_havp, 1, plot.style_columns, histbase=0, editable=false )
p_loAlrt    = plot ( loAlrtBar, 'Extreme Lo',   c_havp, 1, plot.style_columns, histbase=100, editable=false )
p_ma1       = plot ( havpMA,   'MA 1',          i_c_ma1, 1, plot.style_line, 0 )
p_ma2       = plot ( havpMA2,   'MA 2',         i_c_ma2, 1, plot.style_line, 0 )





