Expansion Contraction Library
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © viewer405

//@version=5

// @description Library for Expansion Contraction Indicator, a zero-lag dual perspective indicator created by Brian Latta based on Jake Bernstein’s principles of Moving Average Channel system.
library("ExpansionContraction")

// @function Calculates Expansion Contraction values.
// @param shortLookback Integer for the short lookback calculation, defaults to 8
// @param longLookback Integer for the long lookback calculation, defaults to 32
// @return Returns array of Expansion Contraction values
export calc(int shortLookback = 8, int longLookback = 32) =>
    avgHighShort = ta.sma(high, shortLookback)
    avgHighLong = ta.sma(high, longLookback)
    xcPositiveShort = (high - avgHighShort) + (low - avgHighShort)
    xcPositiveLong = (high - avgHighLong) + (low - avgHighLong)
    
    avgLowShort = ta.sma(low, shortLookback)
    avgLowLong = ta.sma(low, longLookback)
    xcNegativeShort = (avgLowShort - low) + (avgLowShort - high)
    xcNegativeLong = (avgLowLong - low) + (avgLowLong - high)
    
    mediumShort = (xcPositiveShort + xcNegativeShort) / 2
    mediumLong = (xcPositiveLong + xcNegativeLong) / 2
    
    positiveShort = xcPositiveShort - mediumShort
    positiveLong = xcPositiveLong - mediumLong
    
    negativeShort = xcNegativeShort - mediumShort
    negativeLong = xcNegativeLong - mediumLong
    
    [positiveShort, negativeShort, positiveLong, negativeLong]

// @function Calculates standard deviation lines based on Expansion Contraction Long and Short values.
// @param positiveShort Float for the positive short XC value from calculation
// @param negativeShort Float for the negative short XC value from calculation
// @param positiveLong Float for the positive long XC value from calculation
// @param negativeLong Float for the negative long XC value from calculation
// @param stdevLookback Integer for the standard deviation lookback, defaults to 500
// @return Returns array of standard deviation values
export stdevCalc(float positiveShort, float negativeShort, float positiveLong, float negativeLong, int stdevLookback = 500) =>
    avgShort = ta.sma(math.max(positiveShort, negativeShort), stdevLookback)
    stdevShort = ta.stdev(avgShort, stdevLookback)
    
    avgLong = ta.sma(math.max(positiveLong, negativeLong), stdevLookback)
    stdevLong = ta.stdev(avgLong, stdevLookback)
    
    upperShort = avgShort + stdevShort
    lowerShort = -upperShort
    
    upperLong = avgLong + stdevLong
    lowerLong = -upperLong
    
    upperLong2 = upperLong * 2
    lowerLong2 = -upperLong2
    
    [upperShort, lowerShort, upperLong, lowerLong, upperLong2, lowerLong2]

// @function Determines if trend is strong or weak based on Expansion Contraction values.
// @param positiveShort Float for the positive short XC value from calculation
// @param negativeShort Float for the negative short XC value from calculation
// @param positiveLong Float for the positive long XC value from calculation
// @param negativeLong Float for the negative long XC value from calculation
// @return Returns array of boolean values indicating strength or weakness of trend
export trend(float positiveShort, float negativeShort, float positiveLong, float negativeLong) =>
    bool weakBull = positiveLong > negativeLong and positiveShort < negativeShort
    bool weakBear = negativeLong > positiveLong and negativeShort < positiveShort
    bool strongBull = positiveLong > negativeLong and positiveShort > negativeShort
    bool strongBear = negativeLong > positiveLong and negativeShort > positiveShort
    
    [weakBull, strongBull, weakBear, strongBear]

[positiveShort, negativeShort, positiveLong, negativeLong] = calc()
[upperShort, lowerShort, upperLong, lowerLong, upperLong2, lowerLong2] = stdevCalc(positiveShort, negativeShort, positiveLong, negativeLong)
[weakBull, strongBull, weakBear, strongBear] = trend(positiveShort, negativeShort, positiveLong, negativeLong)

shortLookback=input(defval=8, title="Short-Term Length")
longLookback=input(defval=32, title="Long-Term Length")
stdevLookback=input(defval=500, title="Standard Deviation Length")

bullColor = #26a69a
bearColor = #ef5350

enableBarColor=input(defval=true, title="Enable Trend Bar Colors", group="Candlestick Colors")
weakBullColor=input(color.new(bullColor, 50), "Weak Bull Bar", group="Candlestick Colors")
weakBearColor=input(color.new(bearColor, 50), "Weak Bear Bar", group="Candlestick Colors")
strongBullColor=input(color.new(bullColor, 0), "Strong Bull Bar", group="Candlestick Colors")
strongBearColor=input(color.new(bearColor, 0), "Strong Bear Bar", group="Candlestick Colors")

positiveShortColor=input(color.new(color.green, 0), "+ Short Exp/Con Line", group="Expansion/Contraction Color Settings")
negativeShortColor=input(color.new(color.red, 0), "- Short Exp/Con Line", group="Expansion/Contraction Color Settings")
mediumColor=input(color.new(color.gray, 0), "Medium Line", group="Expansion/Contraction Color Settings")
positiveLongColor=input(color.new(color.green, 80), "+ Long Exp/Con Line", group="Expansion/Contraction Color Settings")
negativeLongColor=input(color.new(color.red, 80), "- Long Exp/Con Line", group="Expansion/Contraction Color Settings")

cloudColor=(positiveLong > negativeLong) ? positiveLongColor : negativeLongColor

plot(positiveShort, color=positiveShortColor, linewidth=2, title="+ Short Exp/Con Line")
plot(negativeShort, color=negativeShortColor, linewidth=2, title="- Short Exp/Con Line")

hline(0, color=mediumColor, title="Medium")

u = plot(positiveLong, color=cloudColor, title="+ Long Exp/Con Line")
l = plot(negativeLong, color=cloudColor, title="- Long Exp/Con Line")
fill(u, l, color=cloudColor, title="Long Exp/Con Cloud")

plot(upperShort, color=color.new(color.gray, 50), linewidth=1, title="+ 1 Standard Deviation - Short")
plot(lowerShort, color=color.new(color.gray, 50), linewidth=1, title="- 1 Standard Deviation - Short")
plot(upperLong, color=color.new(color.gray, 25), linewidth=1, title="+ 1 Standard Deviation - Long")
plot(lowerLong, color=color.new(color.gray, 25), linewidth=1, title="- 1 Standard Deviation - Long")
plot(upperLong2, color=color.new(color.gray, 20), linewidth=1, title="+ 2 Standard Deviation - Long")
plot(lowerLong2, color=color.new(color.gray, 20), linewidth=1, title="- 2 Standard Deviation - Long")

candleColor = open > close ? bullColor : bearColor
if weakBull == true
    candleColor:=weakBullColor
else if strongBull == true
    candleColor:=strongBullColor
else if weakBear == true
    candleColor:=weakBearColor
else if strongBear == true
    candleColor:=strongBearColor

candleColor:=enableBarColor == true ? candleColor : na

barcolor(candleColor)