Trail Blaze - (Multi Function Trailing Stop Loss) - [mutantdog]
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © mutantdog

//@version=5
indicator ('Trail Blaze', 'Trail Blaze', true, explicit_plot_zorder=true)

// v1.1
//     

string  S01     =   'open'
string  S02     =   'high'
string  S03     =   'low'
string  S04     =   'close'
string  S11     =   'hl2'
string  S12     =   'hlc3'
string  S13     =   'ohlc4'
string  S14     =   'hlcc4'

string  S51     =   'oc2'
string  S52     =   'hlc2-o2'
string  S53     =   'hl-oc2'
string  S54     =   'cc-ohlc4'

string  S91     =   '*AUX 1'
string  S92     =   '*AUX 2'
string  S93     =   '*TRIGGER'


string  M00     =   'none'

string  M01     =   'SMA'
string  M02     =   'EMA'
string  M03     =   'WMA'
string  M04     =   'RMA'
string  M11     =   'VWMA'
string  M12     =   'VWEMA'
string  M13     =   'HMA'
string  M41     =   'Median'
string  M42     =   'Mid Range'



// INPUTS

auxSource_1     =   input.source    (close,     'Aux 1 ',                                                                                               inline='01')
auxSource_2     =   input.source    (close,     'Aux 2 ',                                                                                               inline='01')

triggerSource   =   input.string    ('close',   'Source',           [S01, S02, S03, S04, S11, S12, S13, S14, S51, S52, S53, S54, S91, S92],             inline='11',    group='Trigger')
triggerFilter   =   input.string    ('RMA',     'Filter ',          [M00, M01, M02, M03, M04, M11, M12, M13, M41, M42],                                 inline='12',    group='Trigger')
triggerLength   =   input.int       (2,         'Length',                   minval=1,                                                                   inline='12',    group='Trigger')

centreSource    =   input.string    ('hl2',     'Source',           [S01, S02, S03, S04, S11, S12, S13, S14, S51, S52, S53, S54, S91, S92, S93],        inline='21',    group='Centre')
centreFilter    =   input.string    ('EMA',     'Filter ',          [M00, M01, M02, M03, M04, M11, M12, M13, M41, M42],                                 inline='22',    group='Centre')
centreLength    =   input.int       (8,         'Length',                   minval=1,                                                                   inline='22',    group='Centre')


tslActive       =   input.bool      (true,      'Trailing  ',                                                                                           inline='41',    group='Dynamics')
tslPercent      =   input.float     (0,         '%',                                                            step=0.1,                               inline='41',    group='Dynamics')

atrActive       =   input.bool      (true,      'ATR',                                                                                                  inline='42',    group='Dynamics')
atrLength       =   input.int       (8,         'Length',                   minval=1,                                                                   inline='42',    group='Dynamics')
atrMult         =   input.float     (1,         'Mult',                                                         step=0.1,                               inline='42',    group='Dynamics')

iqrActive       =   input.bool      (true,      'IQR',                                                                                                  inline='43',    group='Dynamics')
iqrLength       =   input.int       (12,        'Length',                   minval=1,                                                                   inline='43',    group='Dynamics')
iqrMult         =   input.float     (0,         'Mult',                                                         step=0.1,                               inline='43',    group='Dynamics')

filterSum       =   input.bool      (false,     'Filter Sum',                                                                                           inline='47',    group='Dynamics')

loMult          =   input.float     (1,         'Side Mult:   Lo',          minval=0,                           step=0.05,                              inline='49',    group='Dynamics')
hiMult          =   input.float     (1,         'Hi  ',                     minval=0,                           step=0.05,                              inline='49',    group='Dynamics')


showLostop      =   input.bool      (true,      'LoStop',                                                                                               inline='71',    group='Visual')
showHistop      =   input.bool      (true,      'HiStop',                                                                                               inline='71',    group='Visual')
showCentre      =   input.bool      (false,     'Centre',                                                                                               inline='71',    group='Visual')
showTrigger     =   input.bool      (false,     'Trigger',                                                                                              inline='71',    group='Visual')
showBands       =   input.bool      (false,     'Bands',                                                                                                inline='71',    group='Visual')

bullCol         =   input.color     (#5a6e30,   '',                                                                                                     inline='88',    group='Visual')
bearCol         =   input.color     (#a11030,   '',                                                                                                     inline='88',    group='Visual')
centreCol       =   input.color     (#58aeb1,   '',                                                                                                     inline='88',    group='Visual')
triggerCol      =   input.color     (#ccc26d,   '',                                                                                                     inline='88',    group='Visual')
bandCol         =   input.color     (#6d6969,   '',                                                                                                     inline='88',    group='Visual')

stopsOpac       =   input.int       (100,       'Opacity: Stops',           minval=0,       maxval=100,         step=5,                                inline='89',    group='Visual')
otherOpac       =   input.int       (40,        'Other',                    minval=0,       maxval=100,         step=5,                                inline='89',    group='Visual')




// FUNCTIONS

source_switch (src, aux1, aux2) => 
    source          =   switch src
        'open'      =>  open
        'high'      =>  high
        'low'       =>  low
        'close'     =>  close
        'hl2'       =>  hl2
        'hlc3'      =>  hlc3
        'ohlc4'     =>  ohlc4
        'hlcc4'     =>  hlcc4
        'oc2'       =>  (open + close) / 2
        'hlc2-o2'   =>  (high + low + close - open) / 2
        'hl-oc2'    =>  high + low - (open + close) / 2
        'cc-ohlc4'  =>  2 * close - ohlc4
        
        '*AUX 1'    =>  aux1
        '*AUX 2'    =>  aux2
//

filter_switch (src, len, mode) =>
    filter          =   switch mode
        'none'      =>  src
        'SMA'       =>  ta.sma      (src, len)
        'EMA'       =>  ta.ema      (src, len)
        'WMA'       =>  ta.wma      (src, len)
        'RMA'       =>  ta.rma      (src, len)
        'VWMA'      =>  ta.vwma     (src, len)

        'Median'    =>  ta.median   (src, len)
        'Mid Range' =>  ta.lowest   (src, len)      +   ta.range (src, len) / 2

        'VWEMA'     =>  ta.ema      (src * volume, len)     /   ta.ema (volume, len)
        'HMA'       =>  len >= 2    ?   ta.hma    (src, len)    :   src
//

iqr_array (len) =>
    float[]   hiArray   =   array.new_float (0)
    float[]   loArray   =   array.new_float (0)
    float[]   cmArray   =   array.new_float (0)

    for i = 0 to len - 1
        array.push  (hiArray, high[i])
        array.push  (loArray, low[i])
        array.push  (cmArray, hlcc4[i])

    hlArray     =   array.concat (hiArray, loArray)
    hlcmArray   =   array.concat (hlArray, cmArray)
    
    q1          =   array.percentile_linear_interpolation (hlcmArray, 25)
    q3          =   array.percentile_linear_interpolation (hlcmArray, 75)

    iqr         =   (q3 - q1) / 2
//

trail_blaze (srct, srcc, lofac, hifac) =>                                      
    lostop      =   0.0
    histop      =   0.0
    trend       =   0
    
    trend       :=  srct > histop[1]    ?   1       :   srct < lostop[1]        ?   -1      :   nz (trend[1], 1)
    
    lostop00    =   trend ==  1 and trend[1] == -1
    histop00    =   trend == -1 and trend[1] ==  1

    lostop      :=  lostop00    ?   srcc - lofac    :   srct[1] > lostop[1]     ?   math.max (srcc - lofac, lostop[1])      :   srcc - lofac
    histop      :=  histop00    ?   srcc + hifac    :   srct[1] < histop[1]     ?   math.min (srcc + hifac, histop[1])      :   srcc + hifac

    [lostop, histop, trend]
//



// PROCESS

triggerSwitch           =   source_switch (triggerSource, auxSource_1, auxSource_2)
trigger                 =   filter_switch (triggerSwitch, triggerLength, triggerFilter)

centreSwitch            =   centreSource == '*TRIGGER'      ?   trigger     :   source_switch (centreSource, auxSource_1, auxSource_2)
centre                  =   filter_switch (centreSwitch, centreLength, centreFilter)

tslFactor               =   tslActive       ?   centre  * tslPercent / 100              :   0
atrFactor               =   atrActive       ?   atrMult * ta.atr (atrLength)            :   0
iqrFactor               =   iqrActive       ?   iqrMult * iqr_array (iqrLength)         :   0

sumFactor               =   filterSum       ?   ta.swma (nz (tslFactor) + nz (atrFactor) + nz (iqrFactor))      :   nz (atrFactor) + nz (iqrFactor) + nz (tslFactor)  

loFactor                =   math.max (sumFactor, 0) * loMult
hiFactor                =   math.max (sumFactor, 0) * hiMult


[lostop, histop, trend] =   trail_blaze (trigger, centre, loFactor, hiFactor)





// VISUALS AND ALERTS

lostopColour    =   color.new (bullCol,     100 - stopsOpac)
histopColour    =   color.new (bearCol,     100 - stopsOpac)

centreColour    =   color.new (centreCol,   100 - otherOpac)
triggerColour   =   color.new (triggerCol,  100 - otherOpac)
bandColour      =   color.new (bandCol,     100 - otherOpac)


plotCentre      =   plot (showCentre    ?   centre                                      :   na,     'Centre',       centreColour,       2)
plotTrigger     =   plot (showTrigger   ?   trigger                                     :   na,     'Trigger',      triggerColour,      2)

plotLoband      =   plot (showBands     ?   centre - loFactor                           :   na,     'Lo Band',      bandColour,         2)
plotHiband      =   plot (showBands     ?   centre + hiFactor                           :   na,     'Hi Band',      bandColour,         2)

plotLostop      =   plot (showLostop    ?   trend == 1      ?   lostop  :   na          :   na,     'Lo Stop',      lostopColour,       2,      plot.style_linebr)
plotHistop      =   plot (showHistop    ?   trend == 1      ?   na      :   histop      :   na,     'Hi Stop',      histopColour,       2,      plot.style_linebr)



isNewTrend      =   trend != trend[1]
isNewUp         =   trend ==  1     and trend[1] == -1
isNewDown       =   trend == -1     and trend[1] ==  1

plotshape       (isNewUp    ?   lostop  :   na,     'Long Start',       shape.circle,       location.absolute,      lostopColour,       size=size.tiny)
plotshape       (isNewDown  ?   histop  :   na,     'Short Start',      shape.circle,       location.absolute,      histopColour,       size=size.tiny)

alertcondition  (isNewTrend,    '00: Trail Blaze - Direction Change',   'Trail Blaze has changed direction!')
alertcondition  (isNewDown,     '01: Trail Blaze - Sell',               'SELL SIGNAL:   Trail Blaze Down!')
alertcondition  (isNewUp,       '02: Trail Blaze - Buy',                'BUY SIGNAL:    Trail Blaze Up!')


