Candlestick Pattern Criteria and Analysis Indicator
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © kurtsmock
//@version=5
indicator(title='Candles', overlay=true, max_lines_count = 500)
import kurtsmock/PD/1 as pd
import kurtsmock/ArrayMatrixHUD/3 as va
rows = 15, cols = 2
varip console = matrix.new<string>(rows,cols,'')

// -- Testing Range Function {
backtest(int _start, int _end, bool _sessions, bool _incWeekends, string _sessPeriod) => 
    session         = _sessions and _incWeekends ? not na(time(timeframe.period, _sessPeriod + ":1234567")) : _sessions ? not na(time(timeframe.period, _sessPeriod + ":23456")) : true
    backtestRange   = time >= _start and time <= _end and session ? true : false
    backtestRange
// //}

// -- Inputs {
bd   = 'Body',          rg   = "Range"
bull = "Bull Bars",     bear = 'Bear Bars',          both = 'Both'
sd   = 'StDev',         avg  = 'Average Multiplier', ch   = "Select"
o    = 'Open',          h    = 'High',               l    = 'Low',      c  = 'Close',   z  = 'OHLC4'
tck  = 'Ticks',         pct  = 'Percent',            pts  = "Points"
lt   = '(<) Less Than', gt   = '(>) Greater Than',   th   = '(> <) Range Threshold'
hl   = "High/Low",      oc   = "Open/Close",         scp  = "SCP",      val = "Value",  hlc = 'HLC3'

cs_g00 = "General Parameters"   
c_type          = input.string(both,  "Candle Type",                group = cs_g00, options=[bull, bear, both])
c_src           = input.source(open,  "External Source",            group = cs_g00, tooltip = "Imported signal must be a numerical boolean ('1 or 0') and 'Use Alternate Criteria' switch must be ON.")

cs_g01 = "Candle Attributes"
s_body          = input.bool(false,   "Use Candle Bodies-----|",    group = cs_g01, inline='a')
s_avg           = input.bool(false,   "Use Average Size",           group = cs_g01, inline='a')
s_uWick         = input.bool(false,   "Use Upper Wick--------|",    group = cs_g01, inline='b')
s_lWick         = input.bool(false,   "Use Lower Wick",             group = cs_g01, inline='b')
s_rng           = input.bool(false,   'Search in Price Range-|',    group = cs_g01, inline='c')
s_time          = input.bool(false,   'Search in Time Range',       group = cs_g01, inline='c')
s_alt           = input.bool(false,   "Use Alternate Criteria |",   group = cs_g01, inline='d')
s_hlc3          = input.bool(false,   'Use HLC3',                   group = cs_g01, inline='d')
s_scp           = input.bool(false,   'Use Scalar Close Percentage',group = cs_g01, inline='e', tooltip='Scalar Close Percentage (SCP): It\'s the position of the price attribute relative to the scale of the bar range (high - low)\nFormula: (close - low) / (high - low).')
s_lines         = input.bool(false,   'Show Lines--------------|',  group = cs_g01, inline='f')
s_lCol  = input.color(color.orange, "Line Color",       group = cs_g01, inline='f')
s_bg            = input.bool(false,   "Show Sess BG Color----|",   group = cs_g01, inline='g')
s_bgCol  = input.color(color.new(color.gray, 90), " Bg Color",    group = cs_g01, inline='g')
c_barCol = input.color(color.yellow, "Candle Highlight Color",    group = cs_g01, inline='h')

cs_g02 = "Candle Body/Range Parameters"
c_bodyCalc      = input.string(   ch, "Body Quantity",                       group = cs_g02,    options = [tck, pct, pts, ch]) 
bodyRng         = input.string(   ch, "Bodies or Range",                     group = cs_g02,    options = [bd, rg, ch], tooltip="Using Range Requires Your Parameters To Be In Ticks or Points. (The range is always 100% of the candle)")
bodyOper        = input.string(   ch, 'Define Operator: <. >, or > <',       group = cs_g02,    options = [lt, gt, th, ch])
bodyLtGt        = input.int(       0, "Define Limit: '<,' '>,' & '<>'",      group = cs_g02,    minval  = 0, tooltip="Use for Less Than, Greater Than, or High of Threshold\nIf ticks or points, any number of ticks or points.\nIf Pct, any number between 0-100")
bodyLth         = input.int(       0, 'Define Lower Threshold',              group = cs_g02,    minval  = 0, tooltip = 'For the Low of the Threshhold')

cs_g03 = "Upper Wick Parameters"
c_uwCalc        = input.string(   ch, "Upper Wick Quantity",                 group = cs_g03,    options = [tck, pct, pts, ch]) 
upperOper       = input.string(   ch, "Define Operator: '<,' '>,' or '><''", group = cs_g03,    options = [lt, gt, th, ch])
upperLtGt       = input.int(       0, "Define Limit: '<,' '>,' & '<>'",      group = cs_g03,    minval  = 0, tooltip="Use for Less Than, Greater Than, or High of Threshold\nIf ticks or points, any number of ticks or points.\nIf Pct, any number between 0-100")
upperLth        = input.int(       0, 'Define Lower Threshold',              group = cs_g03,    minval  = 0, tooltip = 'For the Low of the Threshhold')

cs_g04 = "Lower Wick Parameters"
c_lwCalc        = input.string(   ch, "Lower Wick Quantity",                 group = cs_g04,    options = [tck, pct, pts, ch]) 
lowerOper       = input.string(   ch, "Define Operator: '<,' '>,' or '><''", group = cs_g04,    options = [lt, gt, th, ch])
lowerLtGt       = input.int(       0, "Define Limit: '<,' '>,' & '<>'",      group = cs_g04,    minval  = 0)
lowerLth        = input.int(       0, 'Define Lower Threshold',              group = cs_g04,    minval  = 0)

cs_g05 = "Other Attributes"
c_hlc3Comp      = input.string(   ch, "HLC3 Comparison Attribute",  group = cs_g05, options = [o, c, scp, ch])
c_hlc3Oper      = input.string(   ch, "HLC3 Operator",              group = cs_g05, options = [gt, lt, ch])
c_scpComp       = input.string(   ch, "SCP Comparison Atrribute",   group = cs_g05, options = [val, hlc, ch])
c_scpOper       = input.string(   ch, "SCP Operator",               group = cs_g05, options = [gt, lt, ch])
c_scpVal        = input.float(   0.0, "SCP Value",                  group = cs_g05, minval  = 0.0)

cs_g06 = "Average Size"
c_avgCalc       = input.string( ch,   "Averaging Quantity",         group = cs_g06, options = [tck, pts, ch]) 
c_avgDef        = input.string( ch,   'Averaging Method',           group = cs_g06, options = [sd, avg, ch])
c_ovrUndr       = input.string( ch,   "> Avg or < Avg",             group = cs_g06, options = [gt, lt, ch])
c_HLorOC        = input.string( ch,   "High/Lo or Open/Close",      group = cs_g06, options = [hl, oc, ch],  tooltip="Benchmark for Averaging")
i_mult          = input.float( 1.0,   'Avg Size Multiplier',        group = cs_g06, minval  = 0, step = 0.1, tooltip = 'Average Multiplier\n1.0 = the average. +/- 1.0 = % increase/decrease of average\n\n Standard Deviation:\n1.0 = 1 stdev. It is the number of standard devations added to average. (0.0 would = The Average)')

cs_g07 = "Search Within Price Range"
rangeH          = input.float(100000, 'Top of Price Range',         group = cs_g07,    minval = 0)
rangeL          = input.float(     1, 'Bottom of Price Range',      group = cs_g07,    minval = 0)
rangeOn         = s_rng ? high <= rangeH and low >= rangeL : close > 0

bt_g1 = "Search Within Time Range"
c_start         = input.time(         title='Start Date',           group = bt_g1,     defval=timestamp('18 May 2022 00:00 +0000'))
c_end           = input.time(         title='End Date',             group = bt_g1,     defval=timestamp('01 Jan 2023 00:00 +0000'))
s_useSess       = input.bool(true,    'Use Session Constraint |',   group = bt_g1,     inline='i')
s_incWE         = input.bool(true,    'Include Weekends',           group = bt_g1,     inline='i')
c_sessPd        = input.session('1800-1500', 'Session Definition',  group = bt_g1)
o_sessionActive = backtest(c_start, c_end, s_useSess, s_incWE, c_sessPd)

cs_g08 = "Line Settings"
selector2       = input.string(    c, 'Strike Line From:',          group = cs_g08,    options = [o, h, l, c, z])
fewerLines      = input.int(      50, 'Max Number of Lines',        group = cs_g08,    minval = 1,   maxval = 500)
lineLookback    = input.int(    4999, 'Max Bar Lookback',           group = cs_g08,    minval = 100, maxval = 4999)

s_debug         = input.bool(  false, "Show Debug Panel")
// //}

// Specify altCriteria. (Hard Coded Candle Type) ///////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// {
s_bos   = input.bool(false, "Show Breakouts")
s_bosCt = input.bool(false, "Show Breakout Ct")

var breakoutLvl     = float(na)
var breakoutArray   = array.new_float(20, float(na))    // Short-Term Breakouts
breakout        = close > high[1]
breakoutFrom    = 0.0

if breakout
    breakoutLvl := high
    array.unshift(breakoutArray, breakoutLvl)
    for i = 0 to 19
        if breakoutLvl >= array.get(breakoutArray, i)
            breakoutFrom := breakoutFrom + 1
        if array.get(breakoutArray, i + 1) > array.get(breakoutArray, i)
            break
else 
    breakoutLvl := breakoutLvl[1]
if array.size(breakoutArray) > 20
    array.pop(breakoutArray)


var breakdownLvl     = float(na)
var breakdownArray   = array.new_float(20, float(na))   // Short-Term Breakdowns
breakdown       = close < low[1]
breakdownFrom   = 0.0

if breakdown
    breakdownLvl := low
    array.unshift(breakdownArray, breakdownLvl)
    for i = 0 to 19
        if breakdownLvl <= array.get(breakdownArray, i)
            breakdownFrom := breakdownFrom + 1
        if array.get(breakdownArray, i + 1) < array.get(breakdownArray, i)
            break
else 
    breakdownLvl := breakdownLvl[1]
if array.size(breakdownArray) > 20
    array.pop(breakdownArray)
plotshape(s_bos ? breakoutFrom > breakoutFrom[1]   : na, "Breakout",  style=shape.triangleup,   location=location.abovebar, color=color.new(color.green,50), size=size.tiny)
plotshape(s_bos ? breakdownFrom > breakdownFrom[1] : na, "Breakdown", style=shape.triangledown, location=location.belowbar, color=color.new(color.red,50),   size=size.tiny)
if s_bosCt and breakoutFrom > breakoutFrom[1] and c_type != bear
    label.new(bar_index, low, text=str.tostring(breakoutFrom), xloc=xloc.bar_index, yloc=yloc.belowbar, style=label.style_label_up,   color=color.rgb(255, 255, 255, 100), textcolor=color.black, size=size.small)
if s_bosCt and breakdownFrom > breakdownFrom[1] and c_type != bull
    label.new(bar_index, low, text=str.tostring(breakdownFrom), xloc=xloc.bar_index, yloc=yloc.abovebar, style=label.style_label_down, color=color.rgb(255, 255, 255, 100), textcolor=color.black, size=size.small)
// }

// -- Calculations {
o_hlc3Comp  = c_hlc3Comp == o ? open : c_hlc3Comp == c ? close : c_hlc3Comp == scp ? pd.pct('scp') : na

// --- Override Calculation Type When Attribute Isn't Selected
if (c_bodyCalc == ch or c_uwCalc == ch or c_lwCalc == ch) and (c_bodyCalc != ch or c_uwCalc != ch or c_lwCalc != ch)
    if c_bodyCalc != ch
        c_uwCalc := c_uwCalc == ch ? c_bodyCalc : c_uwCalc
        c_lwCalc := c_lwCalc == ch ? c_bodyCalc : c_uwCalc
    else if c_uwCalc != ch
        c_bodyCalc := c_bodyCalc == ch ? c_uwCalc : c_bodyCalc
        c_lwCalc := c_lwCalc == ch ? c_uwCalc : c_lwCalc
    else if c_lwCalc != ch
        c_uwCalc := c_uwCalc == ch ? c_lwCalc : c_uwCalc
        c_bodyCalc := c_bodyCalc == ch ? c_lwCalc : c_bodyCalc

p_range     = c_bodyCalc == tck ?  pd.tick("rg") : (c_bodyCalc == pts ? pd.pt('rg') : 100)
p_body      = bodyRng   == rg  ?  (c_bodyCalc == tck ? pd.tick("rg") : c_bodyCalc == pct ? pd.pct('rg', 2) * 100 : pd.pt('rg')) : (c_bodyCalc == tck ? pd.tick("oc") : c_bodyCalc == pct ? pd.pct('bd', 2) * 100 : pd.pt('oc'))
p_upper     = c_uwCalc  == tck ?  pd.tick("uw") : (c_uwCalc == pct  ? pd.pct("uw", 2) * 100 : pd.pt('uw'))
p_lower     = c_lwCalc  == tck ?  pd.tick("lw") : (c_lwCalc == pct  ? pd.pct('lw', 2) * 100 : pd.pt('lw'))
p_avg       = s_avg ? (c_avgCalc == tck ? pd.tick('rg') : (c_avgCalc == pts ? pd.pt('rg') : na)) : pd.tick('rg')

var bullCummArray = array.new_float()
var bearCummArray = array.new_float()

if close > open and (s_time ? o_sessionActive : true) and (s_rng ? rangeOn : true)
    array.push(bullCummArray, c_bodyCalc == pct ? (s_rng ? p_range : pd.tick('rg')) : (bodyRng == bd ? (c_bodyCalc == tck ? pd.tick('bd') : pd.pt('bd')) : (c_bodyCalc == tck ? pd.tick('rg') : pd.pt('rg'))))
avgBullDisp = math.round_to_mintick(array.avg(bullCummArray))
sdBullDisp  = math.round_to_mintick(array.stdev(bullCummArray))

if close < open and (s_time ? o_sessionActive : true) and (s_rng ? rangeOn : true)
    array.push(bearCummArray, c_bodyCalc == pct ? (s_rng ? p_range : pd.tick('rg')) : (bodyRng == bd ? (c_bodyCalc == tck ? pd.tick('bd') : pd.pt('bd')) : (c_bodyCalc == tck ? pd.tick('rg') : pd.pt('rg'))))
avgBearlDisp = math.round_to_mintick(array.avg(bearCummArray))
sdBearlDisp  = math.round_to_mintick(array.stdev(bearCummArray))

altCriteriaBull = breakoutFrom  > breakoutFrom[1]   // true (if altCriteria not in use)
altCriteriaBear = breakdownFrom > breakdownFrom[1]  // true (if altCriteria not in use)
// //}

// -- Conditions/Criteria {

// logic -> Check for altCriteria (coded criteria)
//          If Alternative Criteria switch is on, then check altCriteriaBull, altCriteriaBear, or Both. if altCriteria is off, result is false
altCriteria = s_alt ? (c_type == bull ? altCriteriaBull : 
                      (c_type == bear ? altCriteriaBear : 
                      altCriteriaBull or altCriteriaBear)) : 
              false 
// logic -> If using alternatvie criteria and c_src == 1 then criteria = true, otherwise ignore. 
//              Default of c_src is open price. It is rare that open price would be 1. 
altCriteria := s_alt and c_src == 1 ? true : altCriteria
va.debug(1,0, console, str.tostring(altCriteria), 'altCriteria')

// logic -> Validate against c_type:
//          When s_alt (using altCriteria), you need to test the candle for direction with (and) altCriteria.
//          When not s_alt, you need to test for candle direction alone. Both is default. So false can never never triggers if s_alt is not switched on 
//          False only occurs when s_alt is on and altCriteria is not met
candleType = s_alt ?  (c_type == bull ? altCriteria and close > open : (c_type == bear ? altCriteria and close < open : altCriteria))  : 
                      (c_type == bull ?                 close > open : (c_type == bear ?                 close < open : c_type == both ? true : false      ))
va.debug(1,1, console, str.tostring(candleType), 'candleType')

// The output of candleType (true or false) is derived by giving priority to altCriteria over candle direction
// That is to say if only altCriteria switch is on, you will get the output of altCriteria even when a direction is not chosen. It will test the bull and bear altcritieria... 
// Effectively creating the same output as if the candleType is being disregared when altCriteria is being used. 

// logic -> If none of the the candle attributes switches are on, these variables are bypassed and CandleType is used as the bool. 
//              The reason is that this allows for true/false of criteria to be defined only by candleType (and thus altCriteria). 
//              That is to say the result of candleType is passed through to be the result of o_body, o_uWick and o_lWick. Also otherwise stated these variables are bypassed, ignoring gt/lt/th inputs.
//          However is one or more switches is on, THEN Else: 
//          Check CandleType for true/false. If false result of variable is false.
//          If candleType is true, then check the ___Oper inputs
//              If none of them are true, it will result in true. This means the switch is on, the candleType is found, but no operator has been definied. Its still meets the criteria because the criteria is candleType, not the operators. 
//              if any Operators are selected, the comparison will be made and true/false will reflect that result. 
bool o_body  = s_body  ? candleType ? (bodyOper  == lt ? p_body  < bodyLtGt  : (bodyOper  == gt ? p_body  > bodyLtGt  : (bodyOper  == th ? p_body  < bodyLtGt  and p_body  > bodyLth  : true))) : false : candleType
bool o_uWick = s_uWick ? candleType ? (upperOper == lt ? p_upper < upperLtGt : (upperOper == gt ? p_upper > upperLtGt : (upperOper == th ? p_upper < upperLtGt and p_upper > upperLth : true))) : false : candleType
bool o_lWick = s_lWick ? candleType ? (lowerOper == lt ? p_lower < lowerLtGt : (lowerOper == gt ? p_lower > lowerLtGt : (lowerOper == th ? p_lower < lowerLtGt and p_lower > lowerLth : true))) : false : candleType
va.debug(1,2, console, str.tostring(o_body),  'o_body')
va.debug(1,3, console, str.tostring(o_uWick), 'o_uWick')
va.debug(1,4, console, str.tostring(o_lWick), 'o_lWick')

// logic -> if the switch is off for body, uppwr wick and lower wick OR
//          if the switch for time is on and its not in session OR
//          if the switch for price range is on and its not in range... you get false. Criteria is not met on that bar
//          otherwise, output for (body, uppwer wick and lower wick) all have to be true.
//              They will be true when 
//              (a) altCriteria is selected alone and altCriteria = true, 
//              (b) candle attribute switch is on and Candle meets criteria but no ___oper's are selected (regardless of altCriteria, but subject to it if selected), 
//              (c) candle attribute switch is on and oper's are selected. Still subjected to altCriteria if on 
criteria = (not s_body and not s_uWick and not s_lWick and not s_alt) or (s_time and not o_sessionActive) or (s_rng and not rangeOn) ? false : (o_body and o_uWick and o_lWick)
va.debug(1,5, console, str.tostring(criteria), 'criteria')

if criteria
    va.debug(0,1, console, str.tostring(criteria), "Qualifying Candle Found")

if s_hlc3 and criteria and (s_time ? o_sessionActive : true) and (s_rng ? rangeOn : true)
    if c_hlc3Oper == gt
        criteria := (c_hlc3Comp == o ? hlc3 > open : (c_hlc3Comp == c ? hlc3 > close : (c_hlc3Comp == scp ? hlc3 > pd.pct('scp') : criteria)))
    if c_hlc3Oper == lt
        criteria := (c_hlc3Comp == o ? hlc3 < open : (c_hlc3Comp == c ? hlc3 < close : (c_hlc3Comp == scp ? hlc3 < pd.pct('scp') : criteria)))
va.debug(1,5, console, str.tostring(criteria), 'criteria + HLC3')

if s_scp and criteria and (s_time ? o_sessionActive : true) and (s_rng ? rangeOn : true)
    if c_scpOper == gt
        criteria := (c_scpComp == val ? pd.pct('scp') * 100 > c_scpVal : (c_scpComp == hlc ? pd.pct('scp') > (hlc3 - low) / (high - low) : criteria))
    if c_scpOper == lt
        criteria := (c_scpComp == val ? pd.pct('scp') * 100 < c_scpVal : (c_scpComp == hlc ? pd.pct('scp') < (hlc3 - low) / (high - low) : criteria))
va.debug(1,6, console, str.tostring(criteria), 'criteria + HLC3 + SCP')

if s_avg and criteria and (s_time ? o_sessionActive : true) and (s_rng ? rangeOn : true)
    if c_avgDef == sd
        if c_ovrUndr == gt
            if c_type == bull
                criteria := p_avg  > math.round_to_mintick(avgBullDisp + (sdBullDisp * i_mult))                                            // sd, Greater than, bull candles
                va.debug(0,2, console, str.tostring(criteria), "Found Using Stdev, >, Bull Bar")
            else if c_type == bear
                criteria := p_avg  > math.round_to_mintick(avgBearlDisp + (sdBearlDisp * i_mult))                                          // sd, Greater than, bear candles
                va.debug(0,3, console, str.tostring(criteria), "Found Using Stdev, >, Bear Bar")
            else
                criteria := p_avg  > math.round_to_mintick(((avgBullDisp + avgBearlDisp)/2) + (((sdBullDisp + sdBearlDisp)/2) * i_mult))   // sd, Greater Than, bull and bear candles
                va.debug(0,4, console, str.tostring(criteria), "Found Using StDev, >, Any Direction")
        else if c_ovrUndr == lt
            if c_type == bull
                criteria := p_avg  < math.round_to_mintick(avgBullDisp + (sdBullDisp * i_mult))                                            // sd, Less Than, bull candles
                va.debug(0,5, console, str.tostring(criteria), "Found Using Stdev, <, Bull Bar")
            else if c_type == bear
                criteria := p_avg  < math.round_to_mintick(avgBearlDisp + (sdBearlDisp * i_mult))                                          // sd, Less Than, bear candles
                va.debug(0,6, console, str.tostring(criteria), "Found Using StDev, <, Bear Bar")
            else
                criteria := p_avg  < math.round_to_mintick(((avgBullDisp + avgBearlDisp)/2) + (((sdBullDisp + sdBearlDisp)/2) * i_mult))   // sd, less than, bull and bear candles
                va.debug(0,7, console, str.tostring(criteria), "Found Using Stdev. <, Any Direction")
    if c_avgDef == avg
        if c_ovrUndr == gt
            if c_type == bull
                criteria := p_avg > math.round_to_mintick(avgBullDisp * i_mult)                                                            // avg, Greater Than, Bull candles
                va.debug(0,8, console, str.tostring(criteria), "Found Using Avg, >, Bull Bars")
            else if c_type == bear
                criteria := p_avg > math.round_to_mintick(avgBearlDisp * i_mult)                                                           // avg, Greater Than, Bear candles
                va.debug(0,9, console, str.tostring(criteria), "Found Using Avg, >, Bear Bars")
            else 
                criteria := p_avg > math.round_to_mintick(((avgBullDisp + avgBearlDisp)/2) * i_mult)                                       // avg, Greater Than, Bull and Bear candles
                va.debug(0,10, console, str.tostring(criteria), "Found Using Avg, >, Any Direction")
        else if c_ovrUndr == lt
            if c_type == bull
                criteria := p_avg < math.round_to_mintick(avgBullDisp * i_mult)                                                            // avg, Less Than, Bull candles
                va.debug(0,11, console, str.tostring(criteria), "Found Using Avg, <, Bull Bars")
            else if c_type == bear
                criteria := p_avg < math.round_to_mintick(avgBearlDisp * i_mult)                                                           // avg, Less Than, Bear candles
                va.debug(0,12, console, str.tostring(criteria), "Found Using Avg, <, Bear Bars")
            else 
                criteria := p_avg < math.round_to_mintick(((avgBullDisp + avgBearlDisp)/2) * i_mult)                                       // avg, Less Than, Bull and Bear candles
                va.debug(0,13, console, str.tostring(criteria), "Found Using Avg, <, Any Direction")
va.debug(1,6, console, str.tostring(criteria), 'Criteria + HLC3 + SCP + Avg')

var criteriaCt = 0
if criteria
    criteriaCt += 1

if barstate.islast and criteriaCt == 0
    if s_debug
        va.debug(0,0, console, "", "NO CANDLE TYPE SELECTED\n OR NO CANDLES FIT CRITERIA\n--- Edit Selections in Settings Menu! ---")
    else
        runtime.error("No Candles Found Using the Current Criteria Or No Criteria Chosen. Change Settings to find candles matching your criteria.")
else
    va.debug(0,0, console, "Debug Window", "")
// //}

// -- Plots - Displayed In Data Window {
plot(p_body,       "Current Body Size",                  display = display.data_window, color=color.red)
plot(p_range,      "Current Range",                      display = display.data_window, color=color.red)
plot(p_upper,      "Current Upper Wick Size",            display = display.data_window, color=color.red)
plot(p_lower,      "Current Lower Wick Size",            display = display.data_window, color=color.red)

plot(avgBullDisp,  "Avg Size of Bull Bar in Test Range", display = display.data_window, color=color.orange)
plot(sdBullDisp,   "Stdev of Bull Bar in Test Range",    display = display.data_window, color=color.orange)
plot(avgBearlDisp, "Overall Avg Bear Bar",               display = display.data_window, color=color.orange)
plot(sdBearlDisp,  "Overall SD Bear Bar",                display = display.data_window, color=color.orange)

plot(((avgBullDisp + avgBearlDisp)/2) * i_mult, 
              "Avg * Mult Candle Size (Avg Output)",     display = display.data_window, color=color.orange)
plot(((avgBullDisp + avgBearlDisp)/2) + (((sdBullDisp + sdBearlDisp)/2) * i_mult), 
              "Avg + SD Candle Size (Stdev Output)",     display = display.data_window, color=color.orange)
barcolor(criteria ? c_barCol : na)
bgcolor(s_time and s_bg ? (o_sessionActive ? s_bgCol : na) : na)
barcolor(criteria[1] and barstate.isconfirmed and close > open and high >  high[1] and low >= low[1] ? color.green : na)
barcolor(criteria[1] and barstate.isconfirmed and close < open and high <= high[1] and low <  low[1] ? color.red   : na)
barcolor(criteria[1] and barstate.isconfirmed and not (close > open and high > high[1] and low > low[1]) and not (close < open and high < high[1] and low < low[1]) ? color.gray : na)

// -- Statistics -- // 
var criteriaArray   = array.new_float()
var bullArray       = array.new_float()
var bearArray       = array.new_float()
var otherBullArray  = array.new_float()
var otherBearArray  = array.new_float()
var totalCandles    = 0
displacement = s_body and bodyRng == bd         ? (c_bodyCalc == tck ? pd.tick("oc") : (c_bodyCalc == pct ? pd.pct('oc') : pd.pt('oc'))) : 
               (s_body and bodyRng == rg) or 
               (s_body and s_uWick and s_lWick) ? (c_bodyCalc == tck ? pd.tick('rg') : (c_bodyCalc == pct ? pd.pct('rg') : pd.pt('rg'))) :
               s_uWick                          ? (c_uwCalc == tck   ? pd.tick('uw') : (c_uwCalc   == pct ? pd.pct('uw') : pd.pt('uw'))) :
               s_lWick                          ? (c_lwCalc == tck   ? pd.tick('lw') : (c_lwCalc   == pct ? pd.pct('lw') : pd.pt('lw'))) : 
               s_uWick and s_lWick              ? (c_uwCalc == tck   ? pd.tick('uw') + pd.tick('lw') : (c_uwCalc == pct ? pd.pct('uw') + pd.pct('lw') : pd.pt('uw') + pd.pt('lw'))) : 0

if criteria
    array.unshift(criteriaArray, displacement)
avgCriteria = array.avg(criteriaArray)
stdCriteria = array.stdev(criteriaArray)
maxCriteria = array.max(criteriaArray)
minCriteria = array.min(criteriaArray)

// -- You would edit these condtions to change the readouts in the Data Window
if criteria[1] and barstate.isconfirmed
    if close > open 
        if high > high[1] and low >= low[1]
            array.unshift(bullArray, displacement)
        else
            array.unshift(otherBullArray, displacement)
    if close < open 
        if high <= high[1] and low < low[1]
            array.unshift(bearArray, displacement)
        else
            array.unshift(otherBearArray, displacement)

total = array.size(bullArray) + array.size(bearArray) + array.size(otherBullArray) + array.size(otherBearArray)
totalCandles  := (s_rng ? (rangeOn ? (s_time ? (o_sessionActive ? totalCandles + 1 : totalCandles) : totalCandles + 1) : totalCandles + 1) : 
                 (s_time ? (o_sessionActive ? totalCandles + 1 : totalCandles) : 
                  totalCandles + 1))
pctSignal     = total / totalCandles
bullFollowed  = array.size(bullArray)
notBullFollow = array.size(otherBullArray)

avgBullFollow = array.avg(bullArray)
bearFollowed  = array.size(bearArray)
notBearFollow = array.size(otherBearArray)
avgBearFollow = array.avg(bearArray)

bullBreakout  = math.round((bullFollowed / total), 2)
pctBullBar    = math.round(((bullFollowed + notBullFollow) / total),2)
bearBreakout  = math.round((bearFollowed / total), 2)
pctBearBar    = math.round(((bearFollowed + notBearFollow) / total), 2)

plot(0,                             "--- Signal Freq. Analysis --",                         display = display.data_window)
plot(criteriaCt,                    "Instances Criteria = True",                            display = display.data_window)
plot(criteriaCt - total,            "Number of Follow Up Doji's",                           display = display.data_window)
plot(total,                         "Total Useful Signals",                                 display = display.data_window)
plot(pctSignal,                     "Pct of Time Useful Signal Occurence",                  display = display.data_window)

plot(0,                             "-- Signal Size Analysis --",                           display = display.data_window, color=color.green)
plot(avgCriteria,                   "Avg Size of Matching Candles",                         display = display.data_window, color=color.green)
plot(stdCriteria,                   "Stdev of Matching Candles",                            display = display.data_window, color=color.green)
plot(maxCriteria,                   "Size of Largest of Matching Candle",                   display = display.data_window, color=color.green)
plot(minCriteria,                   "Size of Smallest of Matching Candle",                  display = display.data_window, color=color.green)

plot(0,                             '-- Bar After Criteria is Met Bull or Bear  --',        display = display.data_window, color=color.black)
plot(bullFollowed + notBullFollow,  "Total Follow Up Bars (FUB) where close>open",          display = display.data_window, color=color.purple)
plot(bearFollowed + notBearFollow,  "Total Follow Up Bars (FUB) where close<open",          display = display.data_window, color=color.fuchsia)

plot(0,                             "-- Occurence Rate for Follow Up Bull Bars --",         display = display.data_window, color=color.black)
plot(bullFollowed,                  "FUB Bull Breakout (High>High[1] and Low>=Low[1])(ct)", display = display.data_window, color=color.purple)
plot(avgBullFollow,                 "Avg Size of FUB Bull Breakout",                        display = display.data_window, color=color.purple)
plot(notBullFollow,                 "All Other Follow Up Bars Where close > open (ct)",     display = display.data_window, color=color.purple)

plot(0,                             "-- Occurence Rate for Follow Up Bear Bars --",         display = display.data_window, color=color.black)
plot(bearFollowed,                  "FUB Bear Breakout (High<=High[1] and Low<Low[1])(ct)", display = display.data_window, color=color.fuchsia)
plot(avgBearFollow,                 "Avg Size of FUB Bear Breakout",                        display = display.data_window, color=color.fuchsia)
plot(notBearFollow,                 "All Other Follow Up Bars Where close < open (ct)",     display = display.data_window, color=color.fuchsia)

plot(0,                             "-- Pct Occurence Compared to Total Signals --",        display = display.data_window, color=color.black)
plot(bullBreakout,                  "Pct of Time Bull Breakout Followed",                   display = display.data_window, color=color.lime)
plot(bearBreakout,                  "Pct of Time Bear Breakdown Followed",                  display = display.data_window, color=color.lime)

plot(pctBullBar,                    "Pct of Time Bull Bar Followed",                        display = display.data_window, color=color.lime)
plot(pctBearBar,                    "Pct of Time Bear Bar Followed",                        display = display.data_window, color=color.lime)
// //}

// -- Lines {
yloc = switch selector2
    o => open 
    h => high
    l => low 
    c => close 
    z => ohlc4

var lineArray = array.new_line()
if s_lines and criteria
    array.unshift(lineArray, line.new(bar_index[1], yloc, bar_index, yloc, extend=extend.right, color=s_lCol, width=1))
if array.size(lineArray) > fewerLines
    line.delete(array.get(lineArray, fewerLines))
    array.remove(lineArray, fewerLines)

if s_debug
    va.viewMatrix(console, position.bottom_right, size.normal, bar_index > 1)

// Edit to be able to update thumbnail
// //}