Triple Ehlers Market State
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/

//@version=5

//╱//╭╮╱╱/╭╮╱╱╱/╭━╮╭━╮
//╱//┃┃╱//┃┃╱╱//╰╮╰╯╭╯
//╭━━┫┃╭━━┫╰━┳━━╮╰╮╭╯╭┳━━┳╮╭╮
//┃╭╮┃┃┃╭╮┃╭╮┃╭╮┃╭╯╰╮┣┫╭╮┃╰╯┃
//┃╭╮┃╰┫╰╯┃┃┃┃╭╮┣╯╭╮╰┫┃╰╯┃┃┃┃
//╰╯╰┻━┫╭━┻╯╰┻╯╰┻━╯╰━┻┻━━┻┻┻╯
//╱╱╱╱/┃┃
//╱╱╱//╰╯ 

indicator('Triple Market State', 'Triple MS', false, format.price, 2)

source          = input(close, 'Source')

period1         = input.int(28, '  Slow Period', minval=2, inline = 'rp', group = 'Triple Market State')
thresholdMS1    = input.int(9, 'Threshold °', minval=1, inline = 'rp', group = 'Triple Market State')

period2         = input.int(18, 'Medium Period', minval=2, inline = 'rp', group = 'Triple Market State')
thresholdMS2    = input.int(15, 'Threshold °', minval=1, inline = 'rp', group = 'Triple Market State')

period3         = input.int(9, '  Fast Period', minval=2, inline = 'rp', group = 'Triple Market State')
thresholdMS3    = input.int(10, 'Threshold °', minval=1, inline = 'rp', group = 'Triple Market State')

weighting       = input.bool(defval = true, title = 'Recent trend weighting', inline = 'e', group = 'Triple Market State', tooltip = "Shows on bars ONLY")

var M1 = 'Classic'
var M2 = 'Night'
var M3 = 'Mono'
var M4 = 'Urban'
 
var string GRP_UI  = '══════ UI ══════'

the_m           = input.string(M1, "Theme", options = [M1, M2, M3, M4], inline= 'ez', group=GRP_UI)
bars            = input.bool(defval = true, title = 'Bar coloring', inline = 'ez', group=GRP_UI)

i_bullColor_c   = input.color(#41c607, "Up", inline='COLORc', group=GRP_UI)
i_bearColor_c   = input.color(#ca066c, "Down", inline='COLORc', group=GRP_UI)
i_consColor_c   = input.color(#d88305, "Retracement", inline='COLORc', group=GRP_UI)

i_bullColor_a   = input.color(#cdb14e, "Up", inline='COLORa', group=GRP_UI)
i_bearColor_a   = input.color(#c068e3, "Down", inline='COLORa', group=GRP_UI) 
i_consColor_a   = input.color(#2e6d80, "Retracement", inline='COLORa', group=GRP_UI) 

i_bullColor_o   = input.color(#c2bdbd, "Up", inline='COLORo', group=GRP_UI)
i_bearColor_o   = input.color(#3179f5, "Down", inline='COLORo', group=GRP_UI)
i_consColor_o   = input.color(#d88305, "Retracement", inline='COLORo', group=GRP_UI)

i_bullColor_u   = input.color(#cdb14e, "Up", inline='COLORu', group=GRP_UI)
i_bearColor_u   = input.color(#3179f5, "Down", inline='COLORu', group=GRP_UI)
i_consColor_u   = input.color(#ffffff, "Retracement", inline='COLORu', group=GRP_UI)

up_ = the_m == M1 ? i_bullColor_c : the_m == M2 ? i_bullColor_a : the_m == M3 ? i_bullColor_o : the_m == M4 ? i_bullColor_u : na
dn_ = the_m == M1 ? i_bearColor_c : the_m == M2 ? i_bearColor_a : the_m == M3 ? i_bearColor_o : the_m == M4 ? i_bearColor_u : na
md_ = the_m == M1 ? i_consColor_c : the_m == M2 ? i_consColor_a : the_m == M3 ? i_consColor_o : the_m == M4 ? i_consColor_u : na

areaTransp      = input.int(70, 'Slow Period area transparency', minval = 0, maxval = 100, inline= 'r', group = GRP_UI)

// Functions ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟

rad2deg(Rad) =>  // Radians To Degrees
    var DEGREES_IN_1_RADIAN = 90.0 / math.asin(1.0)  // 57.29577951308 Constant
    Rad * DEGREES_IN_1_RADIAN

cc(Series, Period) =>  // Correlation Cycle Function
    var PIx2 = 4.0 * math.asin(1.0)  // 6.28318530718 Constant
    period = math.max(2, Period)
    Rx = 0.0
    Rxx = 0.0
    Rxy = 0.0
    Ryy = 0.0
    Ry = 0.0
    Ix = 0.0
    Ixx = 0.0
    Ixy = 0.0
    Iyy = 0.0
    Iy = 0.0
    for i = 1 to period by 1
        iMinusOne = i - 1
        X = nz(Series[iMinusOne])
        temp = PIx2 * iMinusOne / period
        Yc = math.cos(temp)
        Ys = -math.sin(temp)
        Rx += X
        Ix += X
        Rxx += X * X
        Ixx += X * X
        Rxy += X * Yc
        Ixy += X * Ys
        Ryy += Yc * Yc
        Iyy += Ys * Ys
        Ry += Yc
        Iy += Ys
        Iy
    realPart = 0.0
    temp_1 = period * Rxx - Rx * Rx
    temp_2 = period * Ryy - Ry * Ry
    if temp_1 > 0.0 and temp_2 > 0.0
        realPart := (period * Rxy - Rx * Ry) / math.sqrt(temp_1 * temp_2)
        realPart
    imagPart = 0.0
    temp_1 := period * Ixx - Ix * Ix
    temp_2 := period * Iyy - Iy * Iy
    if temp_1 > 0.0 and temp_2 > 0.0
        imagPart := (period * Ixy - Ix * Iy) / math.sqrt(temp_1 * temp_2)
        imagPart
    [realPart, imagPart]

cap(RealPart, ImaginaryPart) =>  // Correlation Angle Phasor Function
    var HALF_OF_PI = math.asin(1.0)  // 1.57079632679 Constant
    angle = ImaginaryPart == 0.0 ? 0.0 : rad2deg(math.atan(RealPart / ImaginaryPart) + HALF_OF_PI)
    if ImaginaryPart > 0.0
        angle -= 180.0
        angle
    priorAngle = nz(angle[1])
    if priorAngle > angle and priorAngle - angle < 270.0
        angle := priorAngle
        angle
    angle

mstate(Angle, Degrees) =>  // Market State Function
    var thresholdInDegrees = Degrees
    state = 0
    temp = math.abs(Angle - Angle[1]) < thresholdInDegrees
    if Angle >= 0.0 and temp
        state := 1
        state
    if Angle < 0.0 and temp
        state := -1
        state
    state

// Correlation Cycle ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟

[real1, imaginary1] = cc(source, period1)
[real2, imaginary2] = cc(source, period2)
[real3, imaginary3] = cc(source, period3)

// Correlation Angle/Phasor ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟

angle1 = cap(real1, imaginary1)
angle2 = cap(real2, imaginary2)
angle3 = cap(real3, imaginary3)

// Market State ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟

state1 = mstate(angle1, thresholdMS1)
state2 = mstate(angle2, thresholdMS2)
state3 = mstate(angle3, thresholdMS3)

colorMS1 = state1 > 0.5 ? color.new(up_, areaTransp) :  state1 < -0.5 ? color.new(dn_, areaTransp) : na
colorMS2 = state2 > 0.5 ? color.new(up_, 0)  :  state2 < -0.5 ? color.new(dn_, 0)  : na
colorMS3 = state3 > 0.5 ? color.new(up_, 0)  :  state3 < -0.5 ? color.new(dn_, 0)  : na

plot(state1, color = colorMS1, style = plot.style_area, title = 'State Slow', linewidth = 2)
plot(-state1, color = colorMS1, style = plot.style_area, title = 'State Slow', linewidth = 2)

plot(-state2, color = colorMS2, style = plot.style_histogram, title = 'State Medium')
plot(-state3, color = colorMS3, style = plot.style_circles, title = 'State Fast', linewidth = 2)

// Bar Gates ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟

var trend_slo = 0
if state1 > 0.5 
    trend_slo := 1
    trend_slo
if state1 < -0.5 
    trend_slo := -1
    trend_slo
if state1 < 0.5 and state1 > -0.5
    trend_slo := 2
    trend_slo

var trend_med = 0
if state2 > 0.5 
    trend_med := 1
    trend_med
if state2 < -0.5 
    trend_med := -1
    trend_med
if state2 < 0.5 and state2 > -0.5
    trend_med := 2
    trend_med

var trend_fas = 0
if state3 > 0.5 
    trend_fas := 1
    trend_fas
if state3 < -0.5 
    trend_fas := -1
    trend_fas
if state3 < 0.5 and state3 > -0.5
    trend_fas := 2
    trend_fas

var trend_double = 0
if trend_slo == 1 and trend_med == 1 or trend_slo == 1 and trend_fas == 1 or trend_med == 1 and trend_fas == 1
    trend_double := 1
    trend_double
if trend_slo == -1 and trend_med == -1 or trend_slo == -1 and trend_fas == -1 or trend_med == -1 and trend_fas == -1
    trend_double := -1
    trend_double

// Parliament arrays ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟

sig_up      =  array.from(
                   trend_slo == 1 ?  1 : 0 ,
                   trend_med == 1 ?  1 : 0 ,
                   trend_fas == 1 ?  1 : 0 ,            
                   trend_double == 1 and weighting ?  1 : 0)

sigSum_up   = array.sum (sig_up)

sig_dn      =  array.from(
                   trend_slo == -1 ?  1 : 0 ,
                   trend_med == -1 ?  1 : 0 ,
                   trend_fas == -1 ?  1 : 0 ,                     
                   trend_double == -1 and weighting ?  1 : 0)

sigSum_dn   = array.sum (sig_dn)

mid_signal = sigSum_up == sigSum_dn

plot(mid_signal ? 0 : na, color = color.new(md_, 0), style = plot.style_circles, title = 'Retracement', linewidth = 2)

// ------------------------------------------------------------------------------
// Bars

var bar_trigs = 0
if sigSum_up > sigSum_dn 
    bar_trigs := 1
    bar_trigs
if sigSum_up < sigSum_dn
    bar_trigs := -1
    bar_trigs
if sigSum_up == sigSum_dn
    bar_trigs := 2
    bar_trigs

agreg_trig_color = bar_trigs == 1 ? color.new(up_, 0) : bar_trigs == -1 ? color.new(dn_, 0) : bar_trigs == 2 ? color.new(md_, 0) : color.white
//plotshape(close, title='Trend', style=shape.circle, location=location.bottom, color=alert_trig_color, size = size.tiny) // test alerts

barcolor(bars ? agreg_trig_color : na)

// ------------------------------------------------------------------------------
// Alerts

bull_mega   = bar_trigs == 1 and bar_trigs[1] == -1 or bar_trigs == 1 and bar_trigs[1] == 2
bull_close  = bar_trigs == -1 and bar_trigs[1] == 1 or bar_trigs == 2 and bar_trigs[1] == 1
bear_mega   = bar_trigs == -1 and bar_trigs[1] == 1 or bar_trigs == -1 and bar_trigs[1] == 2
bear_close  = bar_trigs == 1 and bar_trigs[1] == -1 or bar_trigs == 2 and bar_trigs[1] == -1
all_trigs   = bull_mega or bull_close or bear_mega or bear_close

alertcondition(all_trigs, '..... TMS Any alert 🟢🟡🔴', 'TMS Any alert 🟢🟡🔴')
alertcondition(bull_mega, '....TMS BUY 🟢', 'Up 🟢')
alertcondition(bull_close, '...TMS Close long: retrace/reverse 🟡❎', 'Close long 🟡❎')
alertcondition(bear_mega, '..TMS SELL 🔴', 'Down 🔴')
alertcondition(bear_close, '.TMS Close short: retrace/reverse 🟡❌', 'Close short 🟡❌')

plotshape(all_trigs, title='any alert test', style=shape.circle, location=location.top, color = #ffffff, size = size.tiny, display = display.none) // test alerts
plotshape(bull_mega, title='long test', style=shape.circle, location=location.top, color = up_, size = size.tiny, display = display.none) // test alerts
plotshape(bull_close, title='close long test', style=shape.circle, location=location.top, color = #c7db90, size = size.tiny, display = display.none) // test alerts
plotshape(bear_mega, title='short test', style=shape.circle, location=location.top, color = dn_, size = size.tiny, display = display.none) // test alerts
plotshape(bear_close, title='close short test', style=shape.circle, location=location.top, color = #cf9a9a, size = size.tiny, display = display.none) // test alerts


// (  __)(  ( \(    \
//  ) _) /    / ) D (
// (____)\_)__)(____/

// ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟ ⮟
