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

//@version=5
//
// Updated Dec 13, 2022 to allow immediate reversal to pending trade in opposite direction
//
indicator(title="ScalpiusTrend", shorttitle="SPTRND", format=format.price, precision=4, timeframe="", timeframe_gaps=true)
OPTHIST="Histogram"
OPTLINE="Line"
showPending = input.bool(false,"Pending Bars")
useReverse = input.bool(true,"Let trend reverse to pending")
//
// COMPUTE BOLLINGER BANDS INTERNALLY    
//
source = close
length = 20
mult = 2.0
basis = ta.sma(source, length)
dev = mult * ta.stdev(source, length)
upperBBValue = basis + dev
lowerBBValue = basis - dev

NO_UPTREND = 0,
UPTREND_PENDING = 1,
UPTREND_CONFIRMED = 2

NO_DOWNTREND = 0,
DOWNTREND_PENDING = 1,
DOWNTREND_CONFIRMED = 2
        
DOWN = -1,
NONE=0,
UP=1

ID_TRENDMODEON = 0
ID_TRENDDIRECTION = 0
ID_UPTRENDMODESTATE = 1
ID_DOWNTRENDMODESTATE = 2
ID_PENDINGCANDLECOUNT = 3

ID_TRENDHIGH = 0
ID_TRENDLOW = 1
ID_TRENDCLOSE = 2

var trendMode = false
var trendDirection = NONE
var upTrendModeState = NO_UPTREND
var downTrendModeState = NO_DOWNTREND
var pendingCandleCount = 0
var trendHigh = 0.0
var trendLow = 1.0e+20
var trendClose = 0.0

global_bool = array.new_bool(1)
array.set(global_bool,ID_TRENDMODEON,trendMode)

global_int = array.new_int(4)
array.set(global_int,ID_TRENDDIRECTION,trendDirection)
array.set(global_int,ID_UPTRENDMODESTATE,upTrendModeState)
array.set(global_int,ID_DOWNTRENDMODESTATE,downTrendModeState)
array.set(global_int,ID_PENDINGCANDLECOUNT,pendingCandleCount)

global_float = array.new_float(3)
array.set(global_float,ID_TRENDHIGH,trendHigh)
array.set(global_float,ID_TRENDLOW,trendLow)
array.set(global_float,ID_TRENDCLOSE,trendClose)

isUptrendReversed() =>
    if (useReverse == true and  low <= lowerBBValue)
        true
    else
        false

isDowntrendReversed() =>
    if (useReverse == true and high >= upperBBValue) 
        true
    else
        false

isUptrendEnded() =>
    if (low <= lowerBBValue)
        true
    else if (array.get(global_int,ID_PENDINGCANDLECOUNT) >= 20)
        true
    else
        if (high > array.get(global_float,ID_TRENDHIGH))
            array.set(global_int,ID_PENDINGCANDLECOUNT,0)
        false

isDowntrendEnded() =>
    if (high >= upperBBValue)
        true
    else if (array.get(global_int,ID_PENDINGCANDLECOUNT) >= 20)
        true
    else
        if (low < array.get(global_float,ID_TRENDLOW))
            array.set(global_int,ID_PENDINGCANDLECOUNT,0)
        false

checkForTrendMode() => 
    if (high >= upperBBValue and low <= lowerBBValue)
        array.set(global_int,ID_TRENDDIRECTION,NONE)
        array.set(global_int,ID_UPTRENDMODESTATE,NO_UPTREND)
        array.set(global_int,ID_DOWNTRENDMODESTATE,NO_DOWNTREND)
        array.set(global_int,ID_PENDINGCANDLECOUNT,0)
        array.set(global_bool,ID_TRENDMODEON,false)
    else if (not array.get(global_bool,ID_TRENDMODEON))
        if array.get(global_int,ID_UPTRENDMODESTATE) == NO_UPTREND
            if (high >= upperBBValue)
                array.set(global_int,ID_UPTRENDMODESTATE,UPTREND_PENDING)
                array.set(global_float,ID_TRENDHIGH,high)
                array.set(global_float,ID_TRENDCLOSE,close)
                array.set(global_int,ID_PENDINGCANDLECOUNT,0)
        else if (array.get(global_int,ID_UPTRENDMODESTATE) == UPTREND_PENDING) 
            array.set(global_int,ID_PENDINGCANDLECOUNT,array.get(global_int,ID_PENDINGCANDLECOUNT) + 1)
            if isUptrendEnded()
                array.set(global_int,ID_UPTRENDMODESTATE,NO_UPTREND)
                array.set(global_int,ID_PENDINGCANDLECOUNT,0)
                array.set(global_bool,ID_TRENDMODEON,false)
                array.set(global_int,ID_TRENDDIRECTION,NONE)
            if (high > array.get(global_float,ID_TRENDHIGH) and close > array.get(global_float,ID_TRENDCLOSE))
                array.set(global_int,ID_UPTRENDMODESTATE,UPTREND_CONFIRMED)
                array.set(global_bool,ID_TRENDMODEON,true)
                array.set(global_int,ID_PENDINGCANDLECOUNT,0)
                array.set(global_int,ID_TRENDDIRECTION,UP)
                    
        if array.get(global_int,ID_DOWNTRENDMODESTATE) == NO_DOWNTREND
            if (low <= lowerBBValue)
                array.set(global_int,ID_DOWNTRENDMODESTATE,DOWNTREND_PENDING)
                array.set(global_float,ID_TRENDLOW,low)
                array.set(global_float,ID_TRENDCLOSE,close)
                array.set(global_int,ID_PENDINGCANDLECOUNT,0)
        else if (array.get(global_int,ID_DOWNTRENDMODESTATE) == DOWNTREND_PENDING) 
            array.set(global_int,ID_PENDINGCANDLECOUNT,array.get(global_int,ID_PENDINGCANDLECOUNT) + 1)
            if isDowntrendEnded()
                array.set(global_int,ID_DOWNTRENDMODESTATE,NO_DOWNTREND)
                array.set(global_int,ID_PENDINGCANDLECOUNT,0)
                array.set(global_bool,ID_TRENDMODEON,false)
                array.set(global_int,ID_TRENDDIRECTION,NONE)
            if (low < array.get(global_float,ID_TRENDLOW) and close < array.get(global_float,ID_TRENDCLOSE))
                array.set(global_int,ID_DOWNTRENDMODESTATE,DOWNTREND_CONFIRMED)
                array.set(global_bool,ID_TRENDMODEON,true)
                array.set(global_int,ID_PENDINGCANDLECOUNT,0)
                array.set(global_int,ID_TRENDDIRECTION,DOWN)
    else
        if (array.get(global_int,ID_UPTRENDMODESTATE) == UPTREND_CONFIRMED)
            if isUptrendReversed()
                array.set(global_int,ID_UPTRENDMODESTATE,NO_UPTREND)
                array.set(global_int,ID_PENDINGCANDLECOUNT,0)
                array.set(global_bool,ID_TRENDMODEON,false)
                array.set(global_int,ID_TRENDDIRECTION,NONE)
                array.set(global_int,ID_DOWNTRENDMODESTATE,DOWNTREND_PENDING)
                array.set(global_float,ID_TRENDLOW,low)
                array.set(global_float,ID_TRENDCLOSE,close)
                array.set(global_int,ID_PENDINGCANDLECOUNT,0)
            else if isUptrendEnded()
                array.set(global_int,ID_UPTRENDMODESTATE,NO_UPTREND)
                array.set(global_int,ID_PENDINGCANDLECOUNT,0)
                array.set(global_bool,ID_TRENDMODEON,false)
                array.set(global_int,ID_TRENDDIRECTION,NONE)
            else
                if (high > array.get(global_float,ID_TRENDHIGH))
                    array.set(global_int,ID_PENDINGCANDLECOUNT,0)
                    array.set(global_float,ID_TRENDHIGH,high)
                else
                    array.set(global_int,ID_PENDINGCANDLECOUNT,array.get(global_int,ID_PENDINGCANDLECOUNT) + 1)
        if (array.get(global_int,ID_DOWNTRENDMODESTATE) == DOWNTREND_CONFIRMED) 
            if isDowntrendReversed()
                array.set(global_int,ID_DOWNTRENDMODESTATE,NO_DOWNTREND)
                array.set(global_int,ID_PENDINGCANDLECOUNT,0)
                array.set(global_bool,ID_TRENDMODEON,false)
                array.set(global_int,ID_TRENDDIRECTION,NONE)
                array.set(global_int,ID_UPTRENDMODESTATE,UPTREND_PENDING)
                array.set(global_float,ID_TRENDHIGH,high)
                array.set(global_float,ID_TRENDCLOSE,close)
                array.set(global_int,ID_PENDINGCANDLECOUNT,0)
            else if isDowntrendEnded()
                array.set(global_int,ID_DOWNTRENDMODESTATE,NO_DOWNTREND)
                array.set(global_int,ID_PENDINGCANDLECOUNT,0)
                array.set(global_bool,ID_TRENDMODEON,false)
                array.set(global_int,ID_TRENDDIRECTION,NONE)
            else
                if (low < array.get(global_float,ID_TRENDLOW))
                    array.set(global_int,ID_PENDINGCANDLECOUNT,0)
                    array.set(global_float,ID_TRENDLOW,low)
                else
                    array.set(global_int,ID_PENDINGCANDLECOUNT,array.get(global_int,ID_PENDINGCANDLECOUNT) + 1)
                

checkForTrendMode()
//
// Save the vars for next bar
//
trendMode := array.get(global_bool,ID_TRENDMODEON)
trendDirection := array.get(global_int,ID_TRENDDIRECTION)
upTrendModeState := array.get(global_int,ID_UPTRENDMODESTATE)
downTrendModeState := array.get(global_int,ID_DOWNTRENDMODESTATE)
pendingCandleCount := array.get(global_int,ID_PENDINGCANDLECOUNT)
trendHigh := array.get(global_float,ID_TRENDHIGH)
trendLow := array.get(global_float,ID_TRENDLOW)
trendClose := array.get(global_float,ID_TRENDCLOSE)

float trendDetail = 0
c=#000000
if upTrendModeState == UPTREND_PENDING
    trendDetail := 0.35
    c := #004000
else if upTrendModeState == UPTREND_CONFIRMED
    trendDetail := 1.0
    c := #00C000
else if downTrendModeState == DOWNTREND_PENDING
    trendDetail := -0.35
    c := #400000
else if downTrendModeState == DOWNTREND_CONFIRMED
    trendDetail := -1.0
    c := #C00000
    
zeroLine = 0.0
plot(zeroLine, color=#800000, title="TREND")
plot(showPending ? na : trendDirection, style=plot.style_histogram, color=c, title="TREND", linewidth=3)
plot(showPending ? trendDetail : na, color=c, title="TREND", style=plot.style_histogram,linewidth=3)

