Adaptive Candlestick Pattern Recognition System
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © SolCollector 
// BS Computer Science
// Spencer G.
//
// This script aims to recognize a significant number of different candlestick 
// patterns ranging from 1 to 5 candles in length. There are numerous inputs that 
// the user may manipulate which alter how these patterns are detected, and 
// switches have been implemented to turn on/off the detection of each pattern 
// individually. Patterns which completely become a part of a larger pattern will
// be absorbed and their performance will not be measured. Each pattern will have 
// analysis done on them to determine performance, grouped together by the 
// partitions of an asset's price action they had appeared in. 
// There are two overarching modes:
//
// CLASSIC - Patterns are recognized normally and statistics are gathered for 
// these patterns based only on the partitions they had appeared in.
//
// BREAKOUT - Patterns are recognized and processed according to 'breakout' 
// rules. The user sets (default value 1) the number of candles a pattern has 
// until it has a 'breakout' in either an upward or downward direction. A 
// breakout is constituted when, within the number of candles specified, there is
// a close that exceeds either the highest high or lowest low of the pattern. 
// The performance of these patterns will then be grouped together by the 
// partition they had appeared in as well as the breakout direction. Any pattern
// that does not breakout will be ignored.
//
// IMPORTANT FUNCTIONALITY NOTE:
// There is an additional mode the user may use called 'TARGET MODE'. This mode 
// breaks some of the rules set above, and patterns which are selectively picked 
// out using 'TARGET MODE' will have performance analysis done on them regardless
// of if they would have been absorbed by a larger pattern. This may lead to 
// duplicate returns being recorded as a part of the percent returns set. 
//
// Many of the candlestick patterns implemented in this script will be 
// accompanied by ASCII art depicting what I had aimed to achieve with the 
// logical statements associated with each.
//
//@version=5
indicator("Adaptive Candlestick Pattern Recognition System", 
  overlay=true, max_labels_count = 500)

// MASTER PATTERN NAME/ABBREVIATION SETS
var string[] PAT_ABBREV = array.from("H", "ShooStar", "HM", "TL", "WLD", "BLD", 
  "GDD", "SD", "ND", "D", "LL", "RSM", "DF", "GS", "GUD", "BearEng", "LEB", 
  "BullEng", "LET", "DCC", "BearDS", "BullDS", "BullH", "BullHC", "BearH", 
  "BearHC", "HI", "HP", "BearK", "BullK", "BearSL", "BearML", "BullSL", "BullML", 
  "ML", "ON", "IN", "Pierce", "SS2L", "BS", "AS", "Thrust", "TB", "TT", "2BG", 
  "FW", "RW", "BearAB", "BullAB", "ADV", "CD", "DELIB", "UG3M", "DG3M", "3OD", 
  "3ID", "3IU", "3OU", "BearSBS", "BullSBS", "EVED", "EVE", "MORND", "MORN", 
  "SAND", "DTG", "UTG", "IBC", "TBC", "TWS", "3SITS", "BearTS", "BullTS", "TC", 
  "UG2C", "U3RB", "CBS", "Bull3LS", "Bear3LS", "BearBA",  "BullBA", "LB", "MH", 
  "F3M", "R3M")

var string[] PAT_NAME = array.from("Hammer", "Shooting Star", "Hanging Man", 
  "Takuri Line", "White Long Day", "Black Long Day", "Gapping Down Doji", 
  "Southern Doji", "Northern Doji", "Doji", "Long Legged Doji", "Rickshaw Man", 
  "Dragonfly Doji", "Gravestone Doji", "Gapping Up Doji", "Bearish Engulfing", 
  "Last Engulfing Bottom", "Bullish Engulfing", "Last Engulfing Top", 
  "Dark Cloud Cover", "Bearish Doji Star", "Bullish Doji Star", "Bullish Harami", 
  "Bullish Harami Cross", "Bearish Harami", "Bearish Harami Cross", 
  "Hammer Inverted", "Homing Pigeon", "Bearish Kicking", "Bullish Kicking", 
  "Bearish Separating Lines", "Bearish Meeting Lines", "Bullish Separating Lines", 
  "Bullish Meeting Lines", "Matching Low", "On Neck", "In Neck", "Piercing", 
  "Shooting Star (Two Lines)", "Below Stomach", "Above Stomach", "Thrusting", 
  "Tweezers Bottom", "Tweezers Top", "Two Black Gapping", "Falling Window", 
  "Rising Window", "Bearish Abandoned Baby", "Bullish Abandoned Baby", 
  "Advance Block", "Collapsing Doji", "Deliberation", "Upside Gap Three Methods", 
  "Downside Gap Three Methods", "Three Outside Down", "Three Inside Down", 
  "Three Inside Up", "Three Outside Up", "Bearish Side-By-Side", 
  "Bullish Side-By-Side", "Evening Doji Star", "Evening Star", "Morning Doji Star",
  "Morning Star", "Stick Sandwich", "Downside Tasuki Gap", "Upside Tasuki Gap",
  "Identical Black Crows", "Three Black Crows", "Three White Soldiers", 
  "Three Stars in the South", "Bearish Tri-Star", "Bullish Tri-Star", "Two Crows", 
  "Upside Gapping Two Crows", "Unique Three River Bottom", "Concealing Baby Swallow", 
  "Bullish Three Line Strike", "Bearish Three Line Strike", "Bearish Breakaway", 
  "Bullish Breakaway", "Ladder Bottom", "Mat Hold", "Falling Three Methods", 
  "Rising Three Methods")

// Return Array is a user defined type to be used in the matrix that contains 
// all returns of all types of patterns to be found by this hunter. The variables
// it utilizes are:
//  
// @field returns Contains % representations of all returns for a pattern in an array.
// @field size The size of the returns array. 
// @field stdDev The Standard Deviation of the values in the returns array. 
// @field median The Median of the values in the returns array.
// @field avg The Average of the values in the returns array. 
// @field polarities An array of size 3 that contains the respective polarities of the returns for that pattern (negative, neutral, positive). 
type returnArray 
    float[] returns = na
    int size = 0
    float avg = 0
    float median = 0
    float stdDev = 0
    int[] polarities = na

// Initializes the ReturnArray object to be placed into the matrix that keeps track 
// of the returns for all patterns.
f_InitializeReturnArray() =>
    returnArray initial = 
      returnArray.new(returns = array.new_float(), size = 0, avg = 0, median = 0, 
      stdDev = 0, polarities = array.new_int(3, 0))

    initial

// Pattern Object is a user defined type which contains all relevant information 
// corresponding to a candlestick pattern.
//
// Primary fields:
// @field ID The ID number for this pattern. This is what will be used to get the abbreviation, name, and matrix row position for that pattern in its analysis.
// @field part The partition the pattern was found in.
// @field trend The trend this pattern was found in (mainly used for label positioning).
// @field size The number of candles this pattern is.
// @field overridden A boolean that will be used to determine if a pattern has been absorbed into a larger pattern.
// @field patLabel The label that is used to show this pattern.
// @field labelTooltip A string that holds the label tooltip to be modified by other functions in the future (displays statistics for this pattern).
// Secondary fields:
// @field upTarget (BREAKOUT mode) The price target for an upwards breakout.
// @field downTarget (BREAKOUT mode) The price target for a downwards breakout.
// @field candlesLeft (BREAKOUT mode) The number of candles this pattern has left to breakout in one direction.
// @field breakoutDir (BREAKOUT mode) The direction which the pattern had broken out into.
// @field confOffset (BREAKOUT mode) The number of candles needed for this pattern to breakout.
type patternObj 
    int ID = -1
    int part = -1
    int trend = 0
    int size = 0
    bool overridden = false
    label patLabel = na
    string labelTooltip = ""

    // Secondary Values
    float upTarget = -1.0
    float downTarget = -1.0
    int candlesLeft = 0
    int breakoutDir = 0
    int confOffset = 0

// ConfObj is an object which will be used to determine the percent returns 
// of patterns in BREAKOUT mode. This object will be added to a queue where
// it will have its candlesLeft field decremented until it reaches 0. When
// this value reaches 0, it signals it is at the candle where the P/L 
// calculation will be conducted. This revision from a linked-list was 
// done when it was discovered that linked-lists do not work as originally
// thought in Pine.
//
// @field ID The pattern ID that had a breakout.
// @field part The partition the pattern had broken out in.
// @field breakoutDir The direction which the pattern had broken out into.
// @field ccandles The number of candles needed to breakout in one direction.
// @field candlesLeft The remaining candles until a P/L calculation is conducted.
type confObj 
    int ID = -1
    int part = -1
    int breakoutDir = na
    int ccandles = 0
    int candlesLeft = 0

// BarColorTuple is a simple object which will contain the offset (for BREAKOUT mode) 
// and number of candles of a pattern for coloration.
//
// @field offset Corresponds to the number of candles needed for confirming a breakout direction of a pattern. (CLASSIC mode only uses the value of 1 for its offset)
// @field numCandles The size of the pattern to color for.
type barColTup
    int offset = 0
    int numCandles = 0

// Point is a simple data object which will be used to efficiently prune through the 
// matrix of returns to reset MIN/MAX return values used in adaptive coloration.
//
// @field x The matrix row position for a pattern.
// @field y The matrix column position for a pattern.
type point
    int x = -1
    int y = -1

// GROUP NAMES/INDEPENDENT CONSTANTS
var FUNC_SET = "FUNCTIONALITY SETTINGS"
var STAT_G = "STATISTICS SETTINGS"
var MASTER_DET = "MASTER DETECTION SETTINGS"
var TRENDS = "TREND/MA SETTINGS"
var SET_1C = "1 CANDLE SETTINGS"
var SET_2C = "2 CANDLE SETTINGS"
var SET_3C = "3 CANDLE SETTINGS"
var SET_4C = "4 CANDLE SETTINGS"
var SET_5C = "5 CANDLE SETTINGS"
var TICKERSTRING = "Ticker: " + syminfo.tickerid + "\nResolution: " + timeframe.period + "\n"
var color COLOR_INVIS = #00000000
//-------------------- CENTRALLY IMPORTANT GLOBAL VARIABLES --------------------
// These two global variables keep track of unconfirmed and confirmed patterns 
// for the purpose of pattern confirmation and P/L calculations (respectively).
var UNCONF_PATTERNS = array.new<patternObj>()
var CONF_PATTERNS = array.new<confObj>()

// ╔═══════════════════════════════════════════════════════════════════════════╗
// ║                        ____               __                              ║
// ║                       /  _/__  ___  __ __/ /____                          ║
// ║                      _/ // _ \/ _ \/ // / __(_-<                          ║
// ║                     /___/_//_/ .__/\_,_/\__/___/                          ║
// ║                              /_/                                          ║
// ║                                                                           ║
// ║ Dependencies of these inputs (and methods) will have the code section     ║
// ║ abbreviated in parentheses next to the name of the variable/method        ║
// ║ associated with it.                                                       ║
// ║ These abbreviations are as follows:                                       ║
// ║                                                                           ║
// ║    HM     - Helper Methods                                                ║
// ║    GV     - Global Variables                                              ║
// ║    LOG[N] - Logic (N indicates how many candles in the specific section)  ║
// ║    LOGC   - Section of logic calls that determine which patterns appeared ║
//#region                                                                      ║
// ║                                                                           ║
// ║ Each section of this script can be quickly navigated to by using the      ║
// ║ built-in search functionality of the Pine-Editor (ctrl-f). They are:      ║
// ║                                                                           ║
// ║ XSTATS - Statistics settings                                              ║
// ║ XDETECT - Detection settings                                              ║
// ║ XTREND - Moving Average/Trend Settings                                    ║
// ║ XHELPERS - Helper Functions                                               ║
// ║ XGLOBVARS - Global Variables section                                      ║
// ║ XLOG1/2/3/4/5 - The 5 individual Logic Function sections                  ║
// ║ XLOGC - The Logic Function Calls section                                  ║
// ║ XMAIN - The 'main' call block which runs this script                      ║
//#endregion                                                                   ║
// ╚═══════════════════════════════════════════════════════════════════════════╝

//--------------------------- CORE FUNCTIONAL INPUTS ---------------------------
i_DetectionType = input.string("CLASSIC", title = "Pattern Detection Mode", 
  options = ["CLASSIC", "BREAKOUT"], tooltip = "Sets the mode of operation this "
  + "script will run.\n\nCLASSIC - All patterns are identified normally. They will "
  + "have their returns calculated from the candle immediately succeeding the "
  + "candle from their appearance.\n\nBREAKOUT - Patterns abide by breakout "
  + "direction rules. The price action following the pattern must exceed either "
  + "the highest high or lowest low of the pattern within a set amount of candles. "
  + "Percent returns will be broken up by breakout direction and 'non-breakout' "
  + "patterns will be ignored; price analysis will be decided by the user (see: "
  + "'P/L Offset').", group = FUNC_SET, confirm = true)

i_MaxToBreakout = input.int(defval = 1, title = "Breakout Candles (BREAKOUT mode)", 
  minval = 1, maxval = 5, tooltip = "Sets the maximum number of candles allowed "
  + "in 'BREAKOUT mode' that a pattern has to exceed either target value.", 
  group = FUNC_SET, confirm = true)

i_PnLOffset = input.string(defval = "FROM BREAKOUT", 
  title = "P/L Offset (BREAKOUT mode)", options = ["FROM BREAKOUT", "FROM APPEARANCE"], 
  tooltip = "Sets where the P/L calculation will begin on patterns detected with "
  + "the BREAKOUT mode enabled.\n\nFROM BREAKOUT - P/L calculation will be done "
  + "starting from the candle succeeding the one which confirmed the breakout "
  + "direction of the pattern.\n\nFROM APPEARANCE - P/L calculation will be done " 
  + "starting from the candle immediately following the appearance of the pattern.", 
  group = FUNC_SET, confirm = true)
//------------------------------------------------------------------------------
i_AlertOnFind = input.bool(defval = false, title = "Alert on: Find", 
  inline = "PAT ALERTS", group = FUNC_SET, confirm = true)
i_AlertOnBreakout = input.bool(defval = false, title = "Breakout", 
  inline = "PAT ALERTS", group = FUNC_SET, confirm = true)
i_AlertOnNonBreakout = input.bool(defval = false, title = "Non-Breakout", 
  inline = "PAT ALERTS", group = FUNC_SET, confirm = true)
i_AlertOnOverrides = input.bool(defval = false, title = "Overrides", 
  inline = "PAT ALERTS", group = FUNC_SET, confirm = true)
i_AlertOnReturn = input.bool(defval = false, title = "With Return", 
  inline = "PAT ALERTS", group = FUNC_SET, confirm = true, 
  tooltip = "When enabled and set to fire by TradingView's 'all alert() function "
  + "calls' under the alerts tab, these five alerts will fire with a message "
  + "containing the ticker/resolution they were triggered on, plus:\n\nFind - "
  + "The name of the pattern that had been identified with the switchboard "
  + "activation.\nBreakout - On 'BREAKOUT' mode, will show the name of the "
  + "pattern plus its breakout direction and relevant statistics.\nNon-Breakout "
  + "- Will inform the user when a pattern had not broken out in either "
  + "direction.\nOverrides - Will inform the user if the pattern found earlier "
  + "will be overridden and absorbed into a larger pattern.\nWith Return - Will "
  + "inform the user the % P/L that a pattern had experienced by the settings "
  + "the alert was created with.")

//These 5 methods are responsible for firing alerts for patterns during their 
// various points of execution. They will inform the user when a pattern has been:
//    Found
//    Confirmed to breakout
//    Non-Confirmed to breakout
//    Overridden
//    With their return
f_AlertOnFind(patternObj _pat) =>
    if i_AlertOnFind  
        string patString = ""
        if i_DetectionType == "CLASSIC"
            patString := _pat.labelTooltip
        else
            patString := array.get(PAT_NAME, _pat.ID)
              
        alert(TICKERSTRING + patString)
f_AlertOnBreakout(patternObj _pat) =>
    if i_AlertOnBreakout
        alert(TICKERSTRING + _pat.labelTooltip)
f_AlertOnNonBreakout(patternObj _pat) =>
    if i_AlertOnNonBreakout
        failMessage = array.get(PAT_NAME, _pat.ID) + " breakout failed"

        alert(TICKERSTRING + failMessage)
f_AlertOnOverrides(patternObj _pat) =>
    if i_AlertOnOverrides
        patOverridden = array.get(PAT_NAME, _pat.ID) + " has been overridden"

        alert(TICKERSTRING + patOverridden)
f_AlertOnReturns(patternObj _pat, float _return) =>
    if i_AlertOnReturn
        patName = array.get(PAT_NAME, _pat.ID) + " has returned " + 
          str.tostring(_return, "#.####") + "%"

        alert(TICKERSTRING + patName)

// ╔═══════════════════════════════════════════════════════════════════════════╗ 
// ║                  _____ __        __  _      __  _                         ║
// ║                 / ___// /_____ _/ /_(_)____/ /_(_)_________               ║
// ║                 \__ \/ __/ __ `/ __/ / ___/ __/ / ___/ ___/               ║
// ║                ___/ / /_/ /_/ / /_/ (__  ) /_/ / /__(__  )                ║
// ║               /____/\__/\__,_/\__/_/____/\__/_/\___/____/                 ║
// ║                                                                           ║
// ║ This section of inputs will allow the user to manipulate how the %        ║
// ║ returns are calculated for each of the patterns that are recognized by    ║
// ║ this script. This includes: # of candles to perform calculations on (from ║
// ║ the occurrence of the pattern), types stats to use for analysis (median,  ║
// ║ mean), and the source to conduct analysis on.                             ║
//#region                                                                      ║
// ║       PartRes - Partition resolution, sets which higher timeframe will be ║
// ║               used for finding the recent trading range of an asset to    ║
// ║               group the returns of patterns together by.                  ║
// ║                                                                           ║
// ║               default: 'W' (weekly)                                       ║
// ║               dependencies: (GV) BARINDREF, HIGHREF, LOWREF               ║
// ║                                                                           ║
// ║    PartRefLen - Partition Reference Length, sets how many candles back at ║
// ║               the higher timeframe resolution to determine the trading    ║
// ║               range of an asset for partitioning.                         ║
// ║                                                                           ║
// ║               default: 52 (@ weekly resolution = yearly trading range)    ║
// ║               dependencies: (GV) BARINDTERN                               ║
// ║                                                                           ║
// ║  PartUpperLim - Partition Upper Limit, sets which point in the high-low   ║
// ║               range at the resolution/length which separates the upper    ║
// ║               section and middle section.                                 ║
// ║                                                                           ║
// ║               default: 80 (Upper section: yearly high to 80% range)       ║
// ║               dependencies: (GV) UPPERSECTION                             ║
// ║                                                                           ║
// ║  PartLowerLim - Partition Lower Limit, sets which point in the high-low   ║
// ║               range at the resolution/length which separates the lower    ║
// ║               section and middle section.                                 ║
// ║                                                                           ║
// ║               default: 33 (Lower section: yearly low to 33% range)        ║
// ║               dependencies: (GV) LOWERSECTION                             ║
// ║                                                                           ║
// ║ PartBGEnabled - Partition Background Enabled, Sets if the three sections  ║
// ║               will be displayed onto the chart to show how the pattern    ║
// ║               returns will be grouped together.                           ║
// ║                                                                           ║
// ║               default: false                                              ║
// ║               dependencies: (GV) plot() calls                             ║
// ║                                                                           ║
// ║   Part Colors - (Upper/Middle/Lower) These three inputs dictate which     ║
// ║               colors will be used for the individual sections if shown.   ║
// ║                                                                           ║
// ║               defaults: green/yellow/red                                  ║
// ║               dependencies: Out-of-Place Global Variables below           ║
// ║                                                                           ║
// ║   PartOpacity - Partition Opacity sets how transparent or opaque the      ║
// ║               partition background coloring will be when enabled.         ║
// ║                                                                           ║
// ║               default: 75 (25% transparent)                               ║
// ║               dependencies: Out-of-Place Global Variables below           ║
// ║                                                                           ║
// ║      StatsPOR - Statistic Point-of-Reference. Sets which return metric    ║
// ║               will be used to determine bullish/bearishness of patterns.  ║
// ║                                                                           ║
// ║               options: ['AVG', 'MEDIAN']                                  ║
// ║               default: 'AVG'                                              ║
// ║               dependencies: (GV)f_MatrixMinMax, (LOGC)f_GrabColor         ║
// ║                                                                           ║
// ║     PnLLength - Sets the number of candles ahead of the appearance (or    ║
// ║               breakout) of a pattern to perform % return calculations at. ║
// ║                                                                           ║
// ║               default: 10 (candles ahead)                                 ║
// ║               dependencies: (MAIN) P/L Calculations in main call block    ║
// ║                                                                           ║
// ║ MinRetsNeeded - Minimum Returns Needed, sets the number of times that a   ║
// ║               pattern needs to occur and record returns to be considered  ║
// ║               for coloration in future instances based on its             ║
// ║               performance.                                                ║
// ║                                                                           ║
// ║               default: 10                                                 ║
// ║               dependencies: (HM)f_DisplayStatsOnLabel                     ║
// ║                             (GV)f_MatrixMinMax                            ║
// ║                             (LOGC)f_GrabColor                             ║
// ║                                                                           ║
// ║   AdaptiveCol - Boolean value to set if the patterns will use adaptive    ║
// ║               coloration instead. If enabled, these patterns will be      ║
// ║               colored on a gradient based on either their average or      ║
// ║               median return (see StatsPOR) compared to the whole set of   ║
// ║               returns, rather than solidly one of three colors.           ║
// ║                                                                           ║
// ║               default: false                                              ║
// ║               dependencies: (LOGC)f_GrabColor, (MAIN) one conditional     ║
// ║                                                                           ║
// ║     HardLimit - Boolean value to limit the maximum values a pattern's     ║
// ║               statistic value to the return tolerances set by the user.   ║
// ║               Any patterns exceeding these two tolerances will be colored ║
// ║               solidly one color, and a gradient for those in between.     ║
// ║                                                                           ║
// ║               default: false                                              ║
// ║               dependencies: (LOGC)f_GrabColor, (MAIN) one conditional     ║
// ║                                                                           ║
// ║        Colors - Bull: Sets the color used for bullish patterns.           ║
// ║               Neutral: Sets the color used for neutral patterns.          ║
// ║               Bear: Sets the color used for bearish patterns.             ║
// ║               NEI: Sets the color used for patterns which have not met    ║
// ║               the required number of occurrences.                         ║
// ║               Processing: (BREAKOUT mode only) Sets the color used for    ║
// ║               patterns that have yet to break out in one direction.       ║
// ║                                                                           ║
// ║               defaults: navy, yellow, maroon, gray, lime                  ║
// ║               dependencies: (LOGC)f_RunPatternConfirmation, f_GrabColor   ║
// ║                                                                           ║
// ║   Return Tols - NegRetTol/PosRetTol These two values determine what the   ║
// ║               returns of patterns must exceed in either direction to be   ║
// ║               considered negative or positive. Patterns with returns      ║
// ║               between these two values will be logged as 'neutral'.       ║
// ║                                                                           ║
// ║               defaults: -3, 3 (-3% -> +3%)                                ║
// ║               dependencies: (HM)f_AddReturnAndUpdate                      ║
// ║                             (LOGC)f_GrabColor                             ║
// ║                                                                           ║
// ║    LabelNudge - Nudges the labels by a % of the largest of 5 candle's     ║
// ║               high-low range at the price they are placed at. (+ if high, ║
// ║               - if low).                                                  ║
// ║                                                                           ║
// ║               range: 0.0001 - 0.25 (0.01% - 25%)                          ║
// ║               default: 0.05 (5%)                                          ║
// ║               step: 0.0001 (0.01%)                                        ║
// ║               dependencies: (GV)f_InitializeLabel                         ║
//#endregion                                                                   ║
// ╚XSTATS═════════════════════════════════════════════════════════════════════╝

i_PartRes = input.timeframe(defval = "W", title = "Partition Resolution", 
  tooltip = "Defines which timeframe the trading range will be grabbed from "
  + "within the 'Partition Reference Length'. This value will be used with the "
  + "specified upper and lower limits to represent three sections (upper, middle, "
  + "and lower) that the price of an asset may reside in when patterns appear. "
  + "Confirmed patterns will then have their returns grouped by which partition "
  + "they had appeared in.", group = STAT_G)
i_PartRefLen = input.int(defval = 52, title = "Partition Reference Length", 
  minval = 3, tooltip = "Sets how many candles back at the given timeframe to "
  + "grab the highest 'high' from and used for separating the chart into three "
  + "partitions.\n\nThe default values for 'Partition Resolution' and 'Partition "
  + "Reference Length' will have this script grab the 52-week 'high' and 'low' "
  + "as the reference values that break up the chart into three separate "
  + "sections.", group = STAT_G)
i_PartUpperLim = input.float(defval = 80, title = "Partition Upper Limit", 
  minval = 0, maxval = 100, step = 0.0001, inline = "PARTITION LIMITS", 
  group = STAT_G)
i_PartLowerLim = input.float(defval = 33, title = "Lower Limit", 
  minval = 0, maxval = 100, step = 0.0001, inline = "PARTITION LIMITS", 
  group = STAT_G, tooltip = "These inputs represent the relative % "
  + "of the reference 'range' that will break the chart up into their "
  + "respective sections.\n\nDefault values:\n\nUpper Section -> 100% - 80%\n"
  + "Middle Section -> 80% - 33%\nLower Section -> 33% - 0%")
i_PartBGEnabled =input.bool(defval = false, 
  title = "Enable Partition Coloring", group = STAT_G)
i_PartUpperCol = input.color(defval = color.green, 
  title = "Partition Colors: Upper", group = STAT_G, inline = "PART COLS")
i_PartMiddleCol = input.color(defval = color.yellow, 
  title = "Middle", group = STAT_G, inline = "PART COLS")
i_PartLowerCol = input.color(defval = color.red, 
  title = "Lower", group = STAT_G, inline = "PART COLS")
i_PartOpacity = input.int(defval = 75, title = "Partition Color Opacity", 
  group = STAT_G)

// OUT-OF-PLACE GLOBAL VARIABLES
// To account for user input that may not be caught by PineScrypt, there are 
// two global variables placed here that correspond to the default upper and 
// lower limits to be used when 'Partition Upper Limit' is less than 'Partition 
// Lower Limit'
// This also includes color modification for applying a transparency to the 
// colors used in the fill call that colors the separate partitions.
var color UPPER_COLOR_MOD = color.new(i_PartUpperCol, i_PartOpacity)
var color MIDDLE_COLOR_MOD = color.new(i_PartMiddleCol, i_PartOpacity)
var color LOWER_COLOR_MOD = color.new(i_PartLowerCol, i_PartOpacity)

i_StatsPOR = input.string(defval = "AVG", title = "Statistic POR", 
  options = ["AVG", "MEDIAN"], tooltip = "Statistics Point-of-Reference:\n\n"
  + "Determines which set function to use when determining candle pattern "
  + "behavior/coloration. This will grab the associated value from the set of "
  + "collected returns for each specific pattern at that partition that has been "
  + "recognized so far.", 
  group = STAT_G)
i_PnLLength = input.int(defval = 10, title = "P/L Sample Length", minval = 1, 
  maxval = 1000, tooltip = "Sets how many candles from the appearance/breakout "
  + "of a pattern to sample in determining profit/loss.", group = STAT_G)
i_MinRetsNeeded = input.int(defval = 10, minval = 1, 
  title = "Min # of Pattern Instances", 
  tooltip = "This sets the minimum number of occurrences of a pattern before "
  + "behavior for that pattern is determined/represented by coloration and not "
  + "NEI color.", group = STAT_G)

i_AdaptiveCol = input.bool(defval = false, title = "Adaptive Coloring",
  tooltip = "Changes the coloring of patterns based on statistics derived from "
  + "the prior occurrences of each pattern. The color gradient will be created "
  + "based on the 'Negative/Positive Return Tolerance' values. These values will "
  + "be changed if the 'Stats POR' value chosen of any patterns that meet the "
  + "minimum number of returns needed exceed these values in either direction. "
  + "The closer the pattern's 'Stats POR' value is to the Tolerance values (or "
  + "substituted values from the set of returns), the stronger the coloration to "
  + "their respective 'Pattern Color' will be starting from the 'Neutral Color'.", 
  group = STAT_G)

i_HardLimit = input.bool(defval = false, title = "Hard Limit", tooltip = "If "
  + "enabled, color gradient rules are changed so that any pattern which exceeds "
  + "either 'Return Tolerance' values will be colored the full Bullish or "
  + "Bearish gradient colors.\n\nNote: This also affects the perceived 'strength' "
  + "of less bullish/bearish patterns. I recommended increasing the related "
  + "Tolerances with this setting.", group = STAT_G)
  
ic_BullColor = input.color(defval = color.navy, title = "Candle Pattern Colors:"
  + " Bullish", group = STAT_G, inline = "CANDLE COLS")
ic_NeutralColor = input.color(defval = color.yellow, title = "Neutral", 
  group = STAT_G, inline = "CANDLE COLS")
ic_BearColor = input.color(defval = color.maroon, title = "Bearish", 
  group = STAT_G, inline = "CANDLE COLS", tooltip = "Sets the three colors to be "
  + "used with patterns after enough instances have occurred to generate "
  + "statistics for them.")
ic_NEIColor = input.color(defval = color.gray, title = "NEI Color",
  group = STAT_G, tooltip = "Sets the color to be used for patterns that have "
  + "not occurred enough times to generate statistics for.")
ic_ProcessingCol = input.color(defval = color.lime, title = "Processing Color", 
  group = STAT_G, tooltip = "(BREAKOUT MODE)\n\nSets the color for patterns that "
  + "have not exceeded either breakout direction price while processing.")

i_NegRetTol = input.float(defval = -3.00, title = "Negative Return Tolerance", 
  tooltip = "Sets the maximum % return a pattern must be below to be considered "
  + "'Negative'.", minval = -100.00, maxval = 0.00, step = 0.0001, group = STAT_G)
i_PosRetTol = input.float(defval = 3.00, title = "Positive Return Tolerance", 
  tooltip = "Sets the minimum return a pattern must exceed to be considered "
  + "'Positive'.\n\nValues in between these two tolerances will be considered "
  + "'Neutral'.", minval = 0.00, maxval = 100.00, step = 0.0001, group = STAT_G)
  
i_LabelNudge = input.float(title = "Label Nudge", minval = 0.0001, maxval = 0.25,
  step = 0.0001, defval = 0.05, group = STAT_G, 
  tooltip = "Nudges labels further away from the pattern by a given percentage "
  + "of the nearby candles' ranges.")

// Out-of-place global variables/input-error handling, these values will be 
// rewritten/used for the color gradient scheme and will be changed based on the 
// returns in the matrix set if i_HardLimit is not used. Error handling accounts 
// for P/L Calculations being done before patterns have a chance to be overridden
// in the case of extended breakout periods.
var float MINRETVAL = i_NegRetTol
var float MAXRETVAL = i_PosRetTol

if i_DetectionType == "BREAKOUT" and i_MaxToBreakout >= i_PnLLength 
  and i_PnLOffset == "FROM APPEARANCE"
    runtime.error("P/L Length needs to be at least +1 the Max-To-Breakout length "
      + "to properly handle overrides on patterns before the calculation may "
      + "be done. (FROM APPEARANCE)")

// Derive Confirmation Object returns the simplified object of the patternObj in 
// a Linked-List form.
//
// @param:
//    patternObj _pat - The current object that has been confirmed.
//
// @return:
//    patConfirm - An object that contains the pattern ID, number of candles 
//               needed to confirm the pattern, the pattern's partition, and an
//               'na' link.
//
f_DeriveConfirmObj(patternObj _pat) =>
    confObj co = confObj.new(ID = _pat.ID, part = _pat.part, 
      ccandles = _pat.confOffset, breakoutDir = _pat.breakoutDir, 
      candlesLeft = i_PnLLength)

    co
    
// ╔═══════════════════════════════════════════════════════════════════════════╗ 
// ║                   ____       __            __  _                          ║
// ║                  / __ \___  / /____  _____/ /_(_)___  ____                ║
// ║                 / / / / _ \/ __/ _ \/ ___/ __/ / __ \/ __ \               ║
// ║                / /_/ /  __/ /_/  __/ /__/ /_/ / /_/ / / / /               ║
// ║               /_____/\___/\__/\___/\___/\__/_/\____/_/ /_/                ║
// ║                                                                           ║
// ║ This section contains the overarching detection settings that determine   ║
// ║ if patterns will be displayed. The two modes are: SWITCHBOARD and TARGET  ║
// ║ MODE. SWITCHBOARD will have all patterns with their switches enabled      ║
// ║ appear, while TARGET MODE allows the user to specify a specific pattern   ║
// ║ to appear.                                                                ║
// ║                                                                           ║
// ║ IMPORTANT FUNCTIONALITY NOTE: The switch for the pattern selected in      ║
// ║ TARGET MODE *MUST* be enabled to work properly.                           ║
// ║                                                                           ║
// ╚XDETECT════════════════════════════════════════════════════════════════════╝

i_CandleDetSet = input.string(title = "Pattern Detection Setting",
  options = ["SWITCHBOARD", "TARGET MODE"], defval = "SWITCHBOARD",
  inline = "PATTERN DETECTION", group = MASTER_DET)

i_CandleTarget = input.string(title = "Target Pattern",
  options = ["Hammer (1)", "Shooting Star (1)", "Hanging Man (1)", 
  "Takuri Line (1)", "White Long Day (1)", "Black Long Day (1)", 
  "Gapping Down Doji (1)", "Southern Doji (1)", "Northern Doji (1)", "Doji (1)", 
  "Long Legged Doji (1)", "Rickshaw Man (1)", "Dragonfly Doji (1)", 
  "Gravestone Doji (1)", "Gapping Up Doji (1)", "Bearish Engulfing (2)", 
  "Last Engulfing Bottom (2)", "Bullish Engulfing (2)", "Last Engulfing Top (2)", 
  "Dark Cloud Cover (2)", "Bearish Doji Star (2)", "Bullish Doji Star (2)", 
  "Bullish Harami (2)", "Bullish Harami Cross (2)", "Bearish Harami (2)", 
  "Bearish Harami Cross (2)", "Hammer Inverted (2)", "Homing Pigeon (2)", 
  "Bearish Kicking (2)", "Bullish Kicking (2)", "Bearish Separating Lines (2)", 
  "Bearish Meeting Lines (2)", "Bullish Separating Lines (2)", 
  "Bullish Meeting Lines (2)", "Matching Low (2)", "On Neck (2)", "In Neck (2)", 
  "Piercing (2)", "Shooting Star (Two Lines) (2)", "Below Stomach (2)", 
  "Above Stomach (2)", "Thrusting (2)", "Tweezers Bottom (2)", "Tweezers Top (2)", 
  "Two Black Gapping (2)", "Falling Window (2)", "Rising Window (2)", 
  "Bearish Abandoned Baby (3)", "Bullish Abandoned Baby (3)", "Advance Block (3)", 
  "Collapsing Doji (3)", "Deliberation (3)", "Upside Gap Three Methods (3)", 
  "Downside Gap Three Methods (3)", "Three Outside Down (3)", 
  "Three Inside Down (3)", "Three Inside Up (3)", "Three Outside Up (3)", 
  "Bearish Side-By-Side (3)", "Bullish Side-By-Side (3)", "Evening Doji Star (3)", 
  "Evening Star (3)", "Morning Doji Star (3)", "Morning Star (3)", 
  "Stick Sandwich (3)", "Downside Tasuki Gap (3)", "Upside Tasuki Gap (3)", 
  "Identical Black Crows (3)", "Three Black Crows (3)", "Three White Soldiers (3)", 
  "Three Stars in the South (3)", "Bearish Tri-Star (3)", "Bullish Tri-Star (3)", 
  "Two Crows (3)", "Upside Gapping Two Crows (3)", "Unique Three River Bottom (3)", 
  "Concealing Baby Swallow (4)", "Bullish Three Line Strike (4)", 
  "Bearish Three Line Strike (4)", "Bearish Breakaway (5)", "Bullish Breakaway (5)", 
  "Ladder Bottom (5)", "Mat Hold (5)", "Falling Three Methods (5)", 
  "Rising Three Methods (5)"], 
  defval = "Hammer (1)", inline = "PATTERN DETECTION", group = MASTER_DET)

// Get ID From Target is a simple out-of-place helper function which will 
// identify the ID of the pattern selected in 'TARGET MODE' by cutting off the 
// last 4 characters of the target string (" (#)") and requesting the index of 
// the pattern from the PAT_NAME global array.
//
// @input:
//    i_CandleTarget - The string for the selected target pattern selected by 
//                   the user.
//
// @return:
//    int - The ID corresponding to the specified target pattern.
//
f_GetIDFromTarget() =>
    strlen = str.length(i_CandleTarget)
    strlen -= 4

    sub = str.substring(i_CandleTarget, 0, strlen)

    idNum = array.indexof(PAT_NAME, sub)

    idNum

// ╔═══════════════════════════════════════════════════════════════════════════╗ 
// ║     ______                    __   _____      __  __  _                   ║
// ║    /_  __/_______  ____  ____/ /  / ___/___  / /_/ /_(_)___  ____ ______  ║
// ║     / / / ___/ _ \/ __ \/ __  /   \__ \/ _ \/ __/ __/ / __ \/ __ `/ ___/  ║   
// ║    / / / /  /  __/ / / / /_/ /   ___/ /  __/ /_/ /_/ / / / / /_/ (__  )   ║
// ║   /_/ /_/   \___/_/ /_/\__,_/   /____/\___/\__/\__/_/_/ /_/\__, /____/    ║
// ║                                                           /____/          ║
// ║                                                                           ║
// ║ These inputs below will set the specific trends that patterns will be     ║
// ║ compared against. Patterns which appear in an uptrend will only be found  ║
// ║ when the close of the candle preceding the pattern is above the moving    ║
// ║ average(s) set by these settings.                                         ║
// ║                                                                           ║
//#region                                                                      ║
// ║    TrendPrice - Price to use in determining trend direction for comparing ║
// ║               against the Moving Average(s).                              ║
// ║                                                                           ║
// ║               default: 'close'                                            ║
// ║               dependencies: (GV)CURR_TREND                                ║
// ║                                                                           ║
// ║     MASetting - Setting for which of the two Moving Averages to use with  ║
// ║               their respective settings for trend analysis. If set to     ║
// ║               'BOTH' then the Two Moving Average lengths will be compared ║
// ║               and used instead of the TrendPrice for comparison. (Shorter ║
// ║               MA above the longer MA will lead to 'uptrend' and vice      ║
// ║               versa).                                                     ║
// ║                                                                           ║
// ║               options: ['MA 1', 'MA 2', 'BOTH']                           ║
// ║               default: "MA 1"                                             ║
// ║               dependencies: (GV)CURR_TREND, IN_UPTREND1                   ║
// ║                                                                           ║
// ║       MAXType - (X ∈ [1,2]) Sets the type of Moving Average to use for    ║
// ║               each of the Moving Averages.                                ║
// ║                                                                           ║
// ║               options: ['SMA', 'EMA', 'Volume Weighted', 'Weighted',      ║
// ║               'Hull', 'Symmetrical', 'Smoothed', 'Arnaud Legoux',         ║
// ║               'Least Squares']                                            ║
// ║               default: 'SMA'                                              ║
// ║               dependencies: (GV) MAX, CURR_TREND, IN_UPTREND1             ║
// ║                                                                           ║
// ║     MAXLength - (X ∈ [1,2]) Trend tolerance to determine if this candle   ║
// ║               pattern has occurred in an uptrend or downtrend. Uses       ║
// ║               Simple Moving Average by default.                           ║
// ║                                                                           ║
// ║               range: 1 - 200                                              ║
// ║               default: 20                                                 ║
// ║               dependencies: (GV)CURR_TREND                                ║
// ║                                                                           ║
// ║      MAXWidth - (X ∈ [1,2]) Width of the moving average to be displayed   ║
// ║               on the chart.                                               ║
// ║                                                                           ║
// ║               range: 1 - 4                                                ║
// ║               default: 1                                                  ║
// ║               dependencies: (MAIN)p_MAX_PLOT (X ∈ [1,2])                  ║
// ║                                                                           ║
// ║     MAXSource - (X ∈ [1,2]) Sets the source to use for each of the Moving ║
// ║               Averages.                                                   ║
// ║                                                                           ║
// ║               default: 'close'                                            ║
// ║               dependencies: (GV)MA1, MA2                                  ║
// ║                                                                           ║
// ║      LSOffset - (Least Squares Moving Average only) Sets the offset to be ║
// ║               used for the Least Squares Moving Average.                  ║
// ║                                                                           ║
// ║               range: -500 - 500                                           ║
// ║               default: 0                                                  ║
// ║               dependencies: (HM)f_GetMA                                   ║
// ║                                                                           ║
// ║       ALFloor - (Arnaud Legoux Moving Average) Floors the ALMA offset     ║
// ║               calculation before the moving average calculation.          ║
// ║                                                                           ║
// ║               default: false                                              ║
// ║               dependencies: (HM)f_GetMA                                   ║
// ║                                                                           ║
// ║      ALOffset - (Arnaud Legoux Moving Average only) Sets the offset for   ║
// ║               the Gaussian analysis applied by this MA type. Setting to 0 ║
// ║               will make this MA behave like a Simple Moving Average.      ║
// ║               Setting to 1 will make this MA behave like an Exponential   ║
// ║               Moving Average.                                             ║
// ║                                                                           ║
// ║               range: 0 - 1                                                ║
// ║               default: 0.85                                               ║
// ║               step: 0.00001                                               ║
// ║               dependencies: (HM)f_GetMA                                   ║
// ║                                                                           ║
// ║       ALSigma - (Arnaud Legoux Moving Average only) Standard deviation    ║
// ║               that will be applied to this MA type. Affects smoothness of ║
// ║               the line generated by this MA.                              ║
// ║                                                                           ║
// ║               range: 1 - 12                                               ║
// ║               default: 6.00                                               ║
// ║               step: 0.0001                                                ║
// ║               dependencies: (HM)f_GetMA                                   ║
//#endregion                                                                   ║
// ╚XTREND═════════════════════════════════════════════════════════════════════╝

i_TrendPrice = input.source(title = "Trend Price Point",  
  defval = close, group = TRENDS, tooltip = "Defines which price point to use "
  + "for determining trend direction with the Moving Average. If Trend Price "
  + "Point is above the Moving Average rule (IFF \" Moving Average Setting\" is "
  + "not 'BOTH') then the current trend is 'uptrend', 'downtrend' otherwise.")
i_MASetting = input.string(title = "Moving Average Setting", defval = "MA 1", 
  options = ["MA 1", "MA 2", "BOTH"], group = TRENDS,
  tooltip = "Utilizes whichever selected Moving Average settings to determine "
  + "trend direction. If \"BOTH\", lengths of moving averages will be compared; "
  + "If the shorter moving average is above the longer moving average, then "
  + "recognition will be 'uptrend', 'downtrend' otherwise. If the lengths of "
  + "the moving averages are the same, this will default to 'MA 1'.")
  
i_MA1Type = input.string(title = "Moving Average 1 Type", 
  options = ["SMA", "EMA", "Volume Weighted", "Weighted", "Hull", "Symmetrical", 
  "Smoothed", "Arnaud Legoux", "Least Squares"], defval = "SMA", 
  group = TRENDS)
i_MA2Type = input.string(title = "Moving Average 2 Type", 
  options = ["SMA", "EMA", "Volume Weighted", "Weighted", "Hull", "Symmetrical", 
  "Smoothed", "Arnaud Legoux", "Least Squares"], defval = "SMA", 
  group = TRENDS)
  
i_MA1Length = input.int(defval = 20, title = "MA Length 1",
  minval = 1, maxval = 1000,  group = TRENDS)
i_MA2Length = input.int(defval = 50, title = "MA Length 2",
  minval = 1, maxval = 1000,  group = TRENDS)
i_MA1Width = input.int(defval = 1, title = "MA 1 Width",
  minval = 1, maxval = 4, group = TRENDS)
i_MA2Width = input.int(defval = 1, title = "MA 2 Width", 
  minval = 1, maxval = 4, group = TRENDS)
i_MA1Source = input.source(title = "MA 1 Source", defval = close, 
  group = TRENDS, tooltip = "Defines what source value to use for "
  + "MA 1.")
i_MA2Source = input.source(title = "MA 2 Source", defval = close, 
  group = TRENDS, tooltip = "Defines what source value to use for "
  + "MA 2.")

// Special MA Settings
i_LSOffset = input.int(defval = 0, title = "Least Squares Offset",
  minval = -500, maxval = 500, group = TRENDS)
  
i_ALFloor = input.bool(title = "Arnaud Legoux Floor", 
  defval = false, group = TRENDS)
i_ALOffset = input.float(title = "Arnaud Legoux Gaussian Offset",
  minval = 0, maxval = 1.00, defval = 0.85, step = 0.00001, 
  group = TRENDS, tooltip = "Offset for the Gaussian "
  + "function used by the Arnaud Legoux Moving Average. (0 makes this function "
  + "like an SMA, 1 makes this function like an EMA).")
i_ALSigma = input.float(title = "Arnaud Legoux Sigma", defval = 6.00, minval = 1.00,
  maxval = 12.00, step = 0.0001, group = TRENDS, 
  tooltip = "Adjusts the smoothness of the Arnaud Legoux Moving Average.")

// ╔═══════════════════════════════════════════════════════════════════════════╗
// ║              ________   _____         _ __       __                       ║
// ║             / / ____/  / ___/      __(_) /______/ /_  ___  _____          ║
// ║            / / /       \__ \ | /| / / / __/ ___/ __ \/ _ \/ ___/          ║
// ║           /_/ /___    ___/ / |/ |/ / / /_/ /__/ / / /  __(__  )           ║
// ║          (_)\____/   /____/|__/|__/_/\__/\___/_/ /_/\___/____/            ║
// ║                                                                           ║
// ║ This section encompasses all switches associated with 1 candle patterns.  ║
// ║ In the settings for this script, they will be placed directly above all   ║
// ║ settings associated with 1 Candle Patterns.                               ║
// ║                                                                           ║
// ╚═══════════════════════════════════════════════════════════════════════════╝

i_NDS = 
  input.bool(defval = true, title = "ND", inline = "1C UPTREND", 
  group = SET_1C, confirm = true)
i_HMS = 
  input.bool(defval = true, title = "HM", inline = "1C UPTREND", 
  group = SET_1C, confirm = true)
i_GUDS = 
  input.bool(defval = true, title = "GUD", inline = "1C UPTREND", 
  group = SET_1C, confirm = true)
i_ShooS = 
  input.bool(defval = true, title = "ShooStar", inline = "1C UPTREND", 
  group = SET_1C, tooltip = "1 Candle Patterns (uptrend):\n\nND - "
  + "Northern Doji\nHM - Hanging Man\nGUD - Gapping Up Doji\nShooStar - Shooting "
  + "Star", confirm = true)
  
i_GDDS = 
  input.bool(defval = true, title = "GDD", inline = "1C DOWNTREND", 
  group = SET_1C, confirm = true)
i_SDS = 
  input.bool(defval = true, title = "SD", inline = "1C DOWNTREND", 
  group = SET_1C, confirm = true)
i_RegHS = 
  input.bool(defval = true, title = "H", inline = "1C DOWNTREND", 
  group = SET_1C, confirm = true)
i_TLS = 
  input.bool(defval = true, title = "TL", inline = "1C DOWNTREND", 
  group = SET_1C, tooltip = "1 Candle Patterns (downtrend):\n\nGDD "
  + "- Gapping Down Doji\nSD - Southern Doji\nH - Hammer\nTL - Takuri Line", 
  confirm = true)

i_LLDS = 
  input.bool(defval = true, title = "LL", inline = "1C NO TREND.1", 
  group = SET_1C, confirm = true)
i_GSS = 
  input.bool(defval = true, title = "GS", inline = "1C NO TREND.1", 
  group = SET_1C, confirm = true)
i_DFS = input.bool(defval = true, title = "DF", inline = "1C NO TREND.1", 
  group = SET_1C, confirm = true)
i_RSMS = input.bool(defval = true, title = "RSM", inline = "1C NO TREND.1", 
  group = SET_1C, confirm = true)
i_DS = input.bool(defval = true, title = "Doji", inline = "1C NO TREND.1", 
  group = SET_1C, tooltip = "1 Candle Patterns (no trend pt.1):\n\n"
  + "LL - Long Legged Doji\nGS - Gravestone Doji\nDF - Dragonfly Doji\nRSM - "
  + "Rickshaw Man\nDoji - All Dojis", confirm = true)
  
i_BLDS = input.bool(defval = true, title = "BLD", inline = "1C NO TREND.2", 
  group = SET_1C, confirm = true)
i_WLDS = input.bool(defval = true, title = "WLD", inline = "1C NO TREND.2", 
  group = SET_1C, tooltip = "1 Candle Patterns (no trend pt.2):\n\n"
  + "BLD - Black Long Day\nWLD - White Long Day", confirm = true)

// ╔═══════════════════════════════════════════════════════════════════════════╗
// ║              _________   _____      __  __  _                             ║
// ║             <  / ____/  / ___/___  / /_/ /_(_)___  ____ ______            ║
// ║             / / /       \__ \/ _ \/ __/ __/ / __ \/ __ `/ ___/            ║
// ║            / / /___    ___/ /  __/ /_/ /_/ / / / / /_/ (__  )             ║
// ║           /_/\____/   /____/\___/\__/\__/_/_/ /_/\__, /____/              ║
// ║                                                 /____/                    ║
// ║                                                                           ║
// ║ This section contains all settings associated with all 1 Candle patterns. ║
// ║ For the script's settings to have some ease-of-use for the user, I found  ║
// ║ that I had to rearrange the settings into a specific order for them to    ║
// ║ look right when the user wished to adjust how the script would behave.    ║
// ║ Originally, I had all of the settings placed by their type (bool, int,    ║
// ║ float, string, etc.) however this made managing these inputs difficult if ║
// ║ I needed to change something and was awful to look at.                    ║
// ║                                                                           ║
// ║ Breaking apart the 1,2, and 3 candle settings in this way will hopefully  ║
// ║ provide users and developers alike a better ability to interact with      ║
// ║ these settings and understand them/their function.                        ║
//#region                                                                      ║
// ║      TCsample - Number of candles to consider in calculating most recent  ║
// ║               body size average in determining if a candle is 'tall'.     ║
// ║                                                                           ║
// ║               range: 1 - 21                                               ║
// ║               default: 14                                                 ║
// ║               dependencies: (HM)f_IsTall                                  ║
// ║                                                                           ║
// ║     TCSetting - Tall Candle Setting: Specifies which range will be used   ║
// ║               in both the calculation and comparison portions in finding  ║
// ║               'tall' candles.                                             ║
// ║                                                                           ║
// ║               options: ['RANGE', 'BODY']                                  ║
// ║               default: range                                              ║
// ║               dependencies: (HM)f_IsTall, (HM)f_GenerateSecondary         ║
// ║                                                                           ║
// ║               RANGE - Uses a sample of candle ranges (high - low) that    ║
// ║                     sets an average needed to be exceeded by the candle   ║
// ║                     range by [TCTolerance]% to be considered 'tall'.      ║
// ║                BODY - Uses a sample of candle bodies (absolute value      ║
// ║                     of close - open) that sets an average needed to be    ║
// ║                     exceeded by the candle body by [TCTolerance]% to be   ║
// ║                     considered 'tall'.                                    ║
// ║                                                                           ║
// ║         TCTol - (Tall Candle Specific) Tall candle tolerance is what will ║
// ║               dictate how tall a candle must be in relation to the avg of ║
// ║               the previous [TCsample] candles. A candle is considered     ║
// ║               'tall' if it is at least [TCTol] * the average body height  ║
// ║               of the previous [TCsample] candles.                         ║
// ║                                                                           ║
// ║               range: 1.25 - 2.99 (125% - 299% average body length)        ║
// ║               default: 1.50 (150%)                                        ║
// ║               dependencies: (HM)f_IsTall                                  ║
// ║                                                                           ║
// ║     HammerTol - Tolerance for Hammers. This value determines how large    ║
// ║               the discarded wick (see f_IsHammerPat) may be in relation   ║
// ║               to the body of the hammer. (The point of this is to         ║
// ║               disregard hammers with wicks that are too large on the      ║
// ║               short side.)                                                ║
// ║                                                                           ║
// ║               range: 0 - 0.5 (0% - 50%)                                   ║
// ║               default: 35%                                                ║
// ║               dependencies: (LOG)[1]f_IsHammerPat                         ║
// ║                                                                           ║
// ║       DojiTol - Tolerance for Doji's. Assets with high values may not be  ║
// ║               able to be "within a few pennies" in the open and closing   ║
// ║               prices, but the change in value overall was minimal.        ║
// ║               A doji can then be recognized the body of the candle being  ║
// ║               a small percentage of the high-low range for that candle.   ║
// ║               That way this will apply to high and low value assets.      ║
// ║                                                                           ║
// ║               range: 0.005 - 0.10 (0.5% to 10%)                           ║
// ║               default: 0.04 (4%)                                          ║
// ║               step: 0.001 (0.1%)                                          ║
// ║               dependencies: (LOG)[1]f_IsDojiPat                           ║
// ║                                                                           ║
// ║SimplifyNSDoji - (Northern and Southern Dojis) Allows for the user to      ║
// ║               simplify Northern and Southern Doji recognition. If true,   ║
// ║               trend direction is ignored -> (North/South)ern Dojis are    ║
// ║               the same.                                                   ║
// ║                                                                           ║
// ║               default: false                                              ║
// ║               dependencies: (LOG)[1]f_IsDojiPat                           ║
// ║                                                                           ║
// ║ GS_DFDojiShad - (Gravestone/Dragonfly Doji specific) Allows for the Grav- ║
// ║               estone and Dragonfly Doji variants to have a small shadow   ║
// ║               instead matching the low/high of each.                      ║
// ║                                                                           ║
// ║               default: false                                              ║
// ║               dependencies: (LOG)[1]f_IsDojiPat                           ║
// ║                                                                           ║
// ║GS_DFDojiSSize - (Gravestone/Dragonfly Doji specific) Tolerance for how    ║
// ║               large the shadows may be for a Gravestone/Dragonfly doji    ║
// ║               and still be considered valid. Taken as a %age of the       ║
// ║               high-low range. [requires [GS_DFDojiShad]]                  ║
// ║                                                                           ║
// ║               range: 0.001 - 0.05 (0.1% - 5%)                             ║
// ║               default: 0.02 (2%)                                          ║
// ║               step: 0.001 (0.1%)                                          ║
// ║               dependencies: (LOG)[1]f_IsDojiPat                           ║
// ║                                                                           ║
// ║  DojiWickBase - Changes the calculation that determines if a doji wick(s) ║
// ║               qualifies it to be a 'long legged' doji variant.            ║
// ║                                                                           ║
// ║               options: ['RANGE', 'WICKS']                                 ║
// ║               default: wicks                                              ║
// ║               dependencies: (LOG)[1]f_GetWickAverages, f_IsDojiPat        ║
// ║                                                                           ║
// ║               RANGE - Doji wicks are considered 'tall' if the proportion  ║
// ║               of the range they encompass exceeds the average proportion  ║
// ║               of the previous [DojiWickSam] candles by [DLWT]%.           ║
// ║               WICKS - Doji wicks are considered 'tall' if the size of the ║
// ║               wick exceeds the average size of the previous [DojiWickSam] ║
// ║               wicks by [DLWT]%.                                           ║
// ║                                                                           ║
// ║   DojiWickSam - (Long Legged Doji and Rickshaw Man specific) Number of    ║
// ║               candles to consider in calculating the average of wick      ║
// ║               sizes for finding the Long Legged and Rickshaw Man Dojis.   ║
// ║                                                                           ║
// ║               range: 1 - 21                                               ║
// ║               default: 14                                                 ║
// ║               dependencies: (LOG)[1]f_IsDojiPat                           ║
// ║                                                                           ║
// ║  DojiTallWick - Optional argument to determine which tall wick dojis will ║
// ║               be considered valid.                                        ║
// ║                                                                           ║
// ║               options: ['ONE', 'BOTH', 'AVG']                             ║
// ║               default: 'ONE'                                              ║
// ║               dependencies: (LOG)[1]f_IsDojiPat                           ║
// ║                                                                           ║
// ║               ONE - only one wicklength needs to exceed [LL_RSMWickTol]%  ║
// ║               the size of the prior [DojiWickSam] candles.                ║
// ║               'BOTH' - both wicklengths need to exceed [LL_RSMWickTol]%   ║
// ║               the size of the prior [DojiWickSam] candles.                ║
// ║               'AVG' - the average of the two wick lengths needs to exceed ║
// ║               [LL_RSMWickTol]% the size of the prior [DojiWickSam]        ║
// ║               candles.                                                    ║
// ║                                                                           ║
// ║          DLWT - (Doji Long Wick Tolerance) Specifies how large the wick(s)║
// ║               of Dojis must be compared to the average of the previous    ║
// ║               [DojiWickSam] candles' wicks to be considered 'Long         ║
// ║               Legged'.*                                                   ║
// ║                                                                           ║
// ║               range: 1.500 - 10.000 (150% - 1000%)                        ║
// ║               default: 3.000 (300%)                                       ║
// ║               step: 0.001 (0.1%)                                          ║
// ║               dependencies: (LOG)[1]f_IsDojiPat                           ║
// ║                                                                           ║
// ║               * See [DojiWickBase] for details on calculation differences ║
// ║                                                                           ║
// ║    RSMBodyTol - (Rickshaw Man specific) Tolerance for the body placement  ║
// ║               of a Rickshaw Man doji. This will allow a Rickshaw Man to   ║
// ║               be confirmed if the median price of the body is within      ║
// ║               ±[RSMBodyTol] of 0.5 (center of high-low range).            ║
// ║                                                                           ║
// ║               range: 0.005 - 0.10 (0.5% to 10% deviation from center)     ║
// ║               default: 0.05 (±5%)                                         ║
// ║               step: 0.001 (0.1%)                                          ║
// ║               dependencies: (LOG)[1]f_IsDojiPat                           ║
// ║                                                                           ║
// ║      MaruType -  Optional argument to allow the opening/closing variants  ║
// ║               of Marubozu candles to be considered 'valid' in candlestick ║
// ║               patterns that require Marubozus.                            ║
// ║                                                                           ║
// ║               options: ['exclusive', 'inclusive'] [-3, -1]*               ║
// ║               default: 'exclusive'                                        ║
// ║               dependencies: (LOG)[2]f_IsKicking                           ║
// ║                             (LOG)[3]f_IsThree                             ║
// ║                             (LOG)[4]f_IsConcealingBabySwallow             ║
// ║                                                                           ║
// ║               * For the logical expressions being checked by this, this   ║
// ║               set of values represents the values that correspond to dif- ║
// ║               ferent types of Black Marubozu Candles. White will be repr- ║
// ║               esented with the range [3, 1].                              ║
//#endregion                                                                   ║
// ╚═══════════════════════════════════════════════════════════════════════════╝

i_TCSample = input.int(defval = 14, title = "Tall Candle Sample Size",
  minval = 1, maxval = 21,  group = SET_1C,
  tooltip = "The number of candles sampled to determine if a candle is 'tall'.")
i_TCSetting = input.string(title = "Tall Candle Setting", options = ["RANGE", 
  "BODY"], defval = "RANGE", tooltip = "Sets what values to use in calculating "
  + "if a candle is 'tall'.\n\nRANGE - averages the high-low ranges and compares"
  + " candle ranges to the averages.\n\nBODY - averages the absolute value of the "
  + "close-open values, comparing the size of the bodies of candles instead.", 
  group = SET_1C)
i_TCTol = input.float(title = "Tall Candle Tolerance",
  minval = 1.25, maxval = 2.99, step = 0.01, defval = 1.50, 
  group = SET_1C, tooltip = "Requires candles to exceed "
  + "at least [Tall Candle Tolerance] * 100% * average size (default high-low "
  + "range, may also be body size) of the previous [Tall Candle Sample Size] "
  + "candles in order to be considered 'tall'.\n\nIMPORTANT FUNCTIONALITY NOTE: "
  + "This modifies behavior for at least 47 patterns in this script, they are as "
  + "follows.\n\nTwo Candle Patterns (20):\nDCC - BullDS - BearDS - HI - BullH\n"
  + "BullHC - BearH - BearHC - HP - BullK\nBearK - BullML - BearML - BullSL - "
  + "BearSL\nML - ON - IN - AS - BS\n\nThree Candle Patterns (18):\nDELIB - UG3M"
  + " - DG3M - 3ID - 3OD - 3IU\n3OU - MORND - MORN - EVED - EVE - IBC\nTBC - TWS"
  + " - 3SITS - UG2C - TC - U3RB\n\nFour/Five Candle Patterns (9):\nCBS - BU3LS - "
  + "BE3LS - BULLBA - BEARBA\nLB - MH - R3M - F3M")
  
i_HammerTol = input.float(title = "Discarded Hammer Wick Length Tolerance",
  minval = 0, maxval = 0.5, step = 0.01, defval = 0.35, 
  group = SET_1C, tooltip = "Specifies how large the discarded wick length may be "
  + "as a % of the candle body in order for a candle to be considered a 'valid' "
  + "hammer.\n\nIf the center of the candle body is above the midpoint of the "
  + "candle range, discarded wick length is the upper wick, lower wick otherwise.")
  
i_DojiTol = input.float(title = "Doji Body Tolerance",
  minval = 0.005, maxval = 0.10, step = 0.001, defval = 0.04, 
  group = SET_1C, tooltip = "Specifies how large the "
  + "candle body of a candle may be as a % of the range to be considered a "
  + "'valid' Doji.")
i_SimplifyNSDoji = input.bool(defval = true, 
  title = "Ignore (North/South)ern Dojis", group = SET_1C,
  tooltip = "Ignores trend direction in recognition of Doji candles with no "
  + "special attributes.")
i_GS_DFDojiShad = input.bool(defval = false, 
  title = "Allow Gravestone/Dragonfly Doji Shadows", 
  group = SET_1C, tooltip = "Allows for the Gravestone/Dragonfly "
  + "Doji candles to have small shadows on their respective ends (upperwick on "
  + "Dragonfly, lowerwick on Gravestone).")
i_GS_DFDojiSSize = input.float(
  title = "GS/DF Doji Shadow Tolerance",
  minval = 0.001, maxval = 0.05, step = 0.001, defval = 0.02, 
  group = SET_1C, tooltip = "Specifies how large as a % "
  + "of the range the (no longer) ignored shadows of Gravestone and Dragonfly "
  + "Dojis may be to still be considered 'valid'.")
i_DojiWickBase = input.string(defval = "WICKS", title = "Doji Wick Sample Base",
  options = ["RANGE", "WICKS"], tooltip = "Determines which measure will be used "
  + "in conjunction with \"Doji Wick Sample Size\" and \"Doji Long Wick "
  + "Tolerance\" to differentiate between \"Long Doji Wicks\" in special case "
  + "Doji candles.\n\nRANGE - The range of the previously sampled candles will "
  + "be used as a benchmark for how large wicks must be to be considered for "
  + "\"Long Doji Wicks\". Recommended range for \"Doji Long Wick Tolerance\" is "
  + "0.300 (A Doji wick is at least 30% the size of the average range for the "
  + "previously sampled candles) to 1.000.\n\nWICKS - The average size of both "
  + "the upper and lower wicks is used as a benchmark to compare the current "
  + "candle's wicks against. (Recommended range for \"Doji Long Wick Tolerance\" "
  + "is between 1.500 (150% the size of previous wicks) to 10.000", 
  group = SET_1C)
i_DojiWickSam = input.int(defval = 14, title = "Doji Wick Sample Size",
  minval = 1, maxval = 21,  group = SET_1C,
  tooltip = "The number of candles to sample in determining average wick "
  + "lengths for special case Dojis. (Rickshaw Man, Long-Legged, Gravestone and "
  + "Dragonfly).")
i_DojiTallWick = input.string(defval = "ONE", title = "Doji Long Wick Behavior", 
  options = ["ONE", "BOTH", "AVG"], group = SET_1C,
  tooltip = "Defines how Long-Legged Dojis will be recognized.\n\n'ONE' - only "
  + "one wick length needs to exceed the [Doji Long Wick Tolerance] * 100% of "
  + "the average wick length of the previous [Doji Wick Sample Size] candles.* "
  + "\n\n'TWO' - Requires both wick lengths to exceed this* average.\n\n'AVG' - "
  + "The average of the two wick lengths are compared against the combined "
  + "average of the previous [Doji Wick Sample Size] candles.")
i_DLWT = input.float(title = "Doji Long Wick Tolerance",
  minval = 1.500, maxval = 10.000, step = 0.001, defval = 3.000, 
  group = SET_1C, tooltip = "Requires the wick length(s) "
  + "of a Doji Candle to exceed [Doji Long Wick Tolerance] * 100% * the average "
  + "of the previous [Doji Wick Sample Size] candles to be considered for any "
  + "long-legged variant.")
i_RSMBodyTol = input.float(title = "RSM Body Tolerance",
  minval = 0.005, maxval = 0.10, step = 0.001, defval = 0.05, 
  group = SET_1C, tooltip = "Specifies how close to the "
  + "center the body of a valid Doji candle with long wick lengths must be to be "
  + "considered a 'Rickshaw Man'.")

i_MaruType = input.string(title = "Marubozu Type Recognition",
  defval = "EXCLUSIVE", options = ["EXCLUSIVE", "INCLUSIVE"], 
  group = SET_1C, tooltip = "For patterns that require "
  + "Marubozu candles, this allows the program to restrict Marubozu patterns to "
  + "pure Marubozus ('EXCLUSIVE') or allow the opening/closing variants "
  + "('INCLUSIVE') to be accepted by these patterns.")
  
// ╔═══════════════════════════════════════════════════════════════════════════╗
// ║           ___   ______   _____         _ __       __                      ║
// ║          |__ \ / ____/  / ___/      __(_) /______/ /_  ___  _____         ║
// ║          __/ // /       \__ \ | /| / / / __/ ___/ __ \/ _ \/ ___/         ║
// ║         / __// /___    ___/ / |/ |/ / / /_/ /__/ / / /  __(__  )          ║
// ║        /____/\____/   /____/|__/|__/_/\__/\___/_/ /_/\___/____/           ║
// ║                                                                           ║
// ║ This section encompasses all switches associated with 2 Candle Patterns.  ║
// ║ In the script's settings, these switches will be placed directly above    ║
// ║ all settings associated with patterns that have 2 candles.                ║
// ║                                                                           ║
// ╚═══════════════════════════════════════════════════════════════════════════╝

i_UTGS = 
  input.bool(defval = true, title = "UTG", inline = "2C UPTREND.1", 
  group = SET_2C, confirm = true)
i_BEES = 
  input.bool(defval = true, title = "BearEng", inline = "2C UPTREND.1", 
  group = SET_2C, confirm = true)
i_BESS = 
  input.bool(defval = true, title = "BS", inline = "2C UPTREND.1", 
  group = SET_2C, confirm = true)
i_BEDSS = 
  input.bool(defval = true, title = "BearDS", inline = "2C UPTREND.1", 
  group = SET_2C, confirm = true)
i_BEHS = 
  input.bool(defval = true, title = "BearH", inline = "2C UPTREND.1", 
  group = SET_2C, tooltip = "2 Candle Patterns (uptrend pt.1)\n\n"
  + "UTG - Upside Tasuki Gap\nBearEng - Bearish Engulfing\nBS - Below Stomach\n"
  + "BearDS - Bearish Doji Star\nBearH - Bearish Harami", confirm = true)
  
i_BEHCS = 
  input.bool(defval = true, title = "BearHC", inline = "2C UPTREND.2", 
  group = SET_2C, confirm = true)
i_TTS = 
  input.bool(defval = true, title = "TT", inline = "2C UPTREND.2", 
  group = SET_2C, confirm = true)
i_LETS = 
  input.bool(defval = true, title = "LET", inline = "2C UPTREND.2", 
  group = SET_2C, confirm = true)
i_BEMLS = 
  input.bool(defval = true, title = "BearML", inline = "2C UPTREND.2", 
  group = SET_2C, confirm = true)
i_BUSLS = 
  input.bool(defval = true, title = "BullSL", inline = "2C UPTREND.2", 
  group = SET_2C, tooltip = "2 Candle Patterns (uptrend pt.2):\n\n"
  + "BearHC - Bearish Harami Cross\nTT - Tweezer Top\nLET - Last Engulfing Top\n"
  + "BearML - Bearish Meeting Lines\nBullSL - Bullish Separating Lines", 
  confirm = true)
  
i_SS2LS = 
  input.bool(defval = true, title = "SS2L", inline = "2C UPTREND.3", 
  group = SET_2C, confirm = true)
i_RWS = 
  input.bool(defval = true, title = "RW", inline = "2C UPTREND.3", 
  group = SET_2C, confirm = true)
i_DCCS = 
  input.bool(defval = true, title = "DCC", inline = "2C UPTREND.3",
  group = SET_2C, tooltip = "2 Candle Patterns (uptrend pt.3):\n\n"
  + "SS2L - Shooting Star (2 Lines)\nRW - Rising Window\nDCC - Dark Cloud Cover", 
  confirm = true)

i_BUDSS = 
  input.bool(defval = true, title = "BullDS", inline = "2C DOWNTREND.1", 
  group = SET_2C, confirm = true)
i_HIS = 
  input.bool(defval = true, title = "HI", inline = "2C DOWNTREND.1", 
  group = SET_2C, confirm = true)
i_BUHCS = 
  input.bool(defval = true, title = "BullHC", inline = "2C DOWNTREND.1", 
  group = SET_2C, confirm = true)
i_HPS = 
  input.bool(defval = true, title = "HP", inline = "2C DOWNTREND.1", 
  group = SET_2C, confirm = true)
i_ONS = 
  input.bool(defval = true, title = "ON", inline = "2C DOWNTREND.1", 
  group = SET_2C, tooltip = "2 Candle Patterns (downtrend pt.1):\n\n"
  + "BullDS - Bullish Doji Star\nHI - "
  + "Hammer Inverted\nBullHC - Bullish Harami Cross\nHP - Homing Pigeon\nON - "
  + "On Neck", confirm = true)

i_INS = 
  input.bool(defval = true, title = "IN", inline = "2C DOWNTREND.2", 
  group = SET_2C, confirm = true)
i_ABSS = 
  input.bool(defval = true, title = "AS", inline = "2C DOWNTREND.2", 
  group = SET_2C, confirm = true)
i_BUES = 
  input.bool(defval = true, title = "BullEng", inline = "2C DOWNTREND.2", 
  group = SET_2C, confirm = true)
i_BUHS = 
  input.bool(defval = true, title = "BullH", inline = "2C DOWNTREND.2", 
  group = SET_2C, confirm = true)
i_BUMLS = 
  input.bool(defval = true, title = "BullML", inline = "2C DOWNTREND.2", 
  group = SET_2C, tooltip = "2 Candle Patterns (downtrend pt.2):\n\n"
  + "IN - In Neck\nAS - Above Stomach\nBullEng - Bullish Engulfing\nBullH - "
  + "Bullish Harami\nBullML - Bullish Meeting Lines", confirm = true)

i_PS = 
  input.bool(defval = true, title = "Pierce", inline = "2C DOWNTREND.3", 
  group = SET_2C, confirm = true)
i_TS = 
  input.bool(defval = true, title = "Thrust", inline = "2C DOWNTREND.3", 
  group = SET_2C, confirm = true)
i_LEBS = 
  input.bool(defval = true, title = "LEB", inline = "2C DOWNTREND.3", 
  group = SET_2C, confirm = true)
i_MLS = 
  input.bool(defval = true, title = "ML", inline = "2C DOWNTREND.3", 
  group = SET_2C, confirm = true)
i_BESLS = input.bool(defval = true, title = "BearSL", inline = "2C DOWNTREND.3", 
  group = SET_2C, tooltip = "2 Candle Patterns (downtrend pt.3):\n\n"
  + "Pierce - Piercing\nThrust - Thrusting\nLEB - Last Engulfing Bottom\nML - "
  + "Matching Low\nBearSL - Bearish Separating Lines", confirm = true)

i_TBS = 
  input.bool(defval = true, title = "TB", inline = "2C DOWNTREND.4", 
  group = SET_2C, confirm = true)
i_2BGS = 
  input.bool(defval = true, title = "2BG", inline = "2C DOWNTREND.4", 
  group = SET_2C, confirm = true)
i_FWS = 
  input.bool(defval = true, title = "FW", inline = "2C DOWNTREND.4", 
  group = SET_2C, tooltip = "2 Candle Patterns (downtrend pt.4):\n\n"
  + "TB - Tweezer Bottom\n2BG - Two Black Gapping\nFW - Falling Window", 
  confirm = true)
  
i_BEKS = 
  input.bool(defval = true, title = "BearK", inline = "2C NO TREND", 
  group = SET_2C, confirm = true)
i_BUKS = 
  input.bool(defval = true, title = "BullK", inline = "2C NO TREND", 
  group = SET_2C, tooltip = "2 Candle Patterns (No Trend):\n\n"
  + "BearK - Bearish Kicking\nBullK - Bullish Kicking", confirm = true)

// ╔═══════════════════════════════════════════════════════════════════════════╗
// ║             ___   ______   _____      __  __  _                           ║
// ║            |__ \ / ____/  / ___/___  / /_/ /_(_)___  ____ ______          ║
// ║            __/ // /       \__ \/ _ \/ __/ __/ / __ \/ __ `/ ___/          ║
// ║           / __// /___    ___/ /  __/ /_/ /_/ / / / / /_/ (__  )           ║
// ║          /____/\____/   /____/\___/\__/\__/_/_/ /_/\__, /____/            ║
// ║                                                   /____/                  ║
// ║                                                                           ║
//#region                                                                      ║
// ║     EngIncl - Allows Engulfing candlesticks to include the Open/Close     ║
// ║               prices of the candle being engulfed. This would allow the   ║
// ║               case of the engulfing candle swapping the open/close prices ║
// ║               of the candle being engulfed                                ║
// ║                                                                           ║
// ║               default: true                                               ║
// ║               dependencies: (HM)f_Engulfs                                 ║
// ║                                                                           ║
// ║    OCincluded - Include Open and Close prices for candlestick patterns    ║
// ║               that require candles to 'open within body' of another in    ║
// ║               the pattern.                                                ║
// ║                                                                           ║
// ║               default: false                                              ║
// ║               dependencies: (HM)f_WithinVals                              ║
// ║                                                                           ║
// ║    KickingGap - (Kicking patterns specific) requires that the Kicking     ║
// ║               patterns have a gap between the two candlesticks. (Extra    ║
// ║               flexibility when dealing with the opening/closing variants  ║
// ║               allowed to be considered by the user for these patterns).   ║
// ║                                                                           ║
// ║               default: true                                               ║
// ║               dependencies: (LOG)[2]f_IsKicking                           ║
// ║                                                                           ║
// ║   TallStom1/2 - (Above/Below the Stomach specific) requires that the      ║
// ║               specified candle in the Above/Below the stomach patterns is ║
// ║               a tall candle.                                              ║
// ║                                                                           ║
// ║               default: false/false                                        ║
// ║               dependencies: (LOG)[2]f_IsStomach                           ║
// ║                                                                           ║
// ║  HammerInvTol - (Inverted Hammer specific) The two candle pattern         ║
// ║               requires that the first candle closes near the low. This    ║
// ║               tolerance will allow the user to input how close to this    ║
// ║               candle's low the close may be to be considered valid (as a  ║
// ║               percentage of the high-low range).                          ║
// ║                                                                           ║
// ║               range: 0.005 - 0.15 (0.5% - 15%)                            ║
// ║               default: 0.05 (5%)                                          ║
// ║               step: 0.001 (0.1%)                                          ║
// ║               dependencies: (LOG)[1]f_isHammerPat                         ║
// ║                                                                           ║
// ║      LinesTol - Tolerance for the Lines patterns. For these four patterns ║
// ║               this tolerance will allow the user to specify how close     ║
// ║               together the opens and closes of these patterns may be for  ║
// ║               them to be considered valid (as a % of the high-low range   ║
// ║               of the first candles in each pattern).                      ║
// ║                                                                           ║
// ║               range: 0.005 - 0.25 (0.5% - 25%)                            ║
// ║               default: 0.05 (5%)                                          ║
// ║               step: 0.0001 (0.01%)                                        ║
// ║               dependencies: (LOG)[2]f_IsLines                             ║
// ║                                                                           ║
// ║   MatchLowTol - (Matching Low specific) Since it may be improbable that   ║
// ║               high value assets will close near each other on two         ║
// ║               consecutive bearish candles, this will allow some leeway as ║
// ║               to what may be considered valid for this pattern. (Serves   ║
// ║               as a % difference from the close of C1 in this pattern.)    ║
// ║                                                                           ║
// ║               range: 0 - 0.05 (0% - 5%)                                   ║
// ║               default: 0 (0%)                                             ║
// ║               step: 0.0001 (0.01%)                                        ║
// ║               dependencies: (LOG)[2]f_IsMatchLow                          ║
// ║                                                                           ║
// ║ OnNeckWickTol - (On Neck specific) Since the validity of the pattern will ║
// ║               be based upon how close to the low the second candle closes ║
// ║               as a function of the lower wick, this will act as a restri- ║
// ║               ction as to how large the lower wick may be as a function   ║
// ║               of the high-low range.                                      ║
// ║                                                                           ║
// ║               range: 0.05 - 0.25 (5% - 25% of high-low range)             ║
// ║               default: 0.10 (10%)                                         ║
// ║               step: 0.001 (0.1%)                                          ║
// ║               dependencies: (LOG)[2]f_IsNeck                              ║
// ║                                                                           ║
// ║     OnNeckTol - (On Neck specific) As a function of the lower wick on the ║
// ║               first candle in the pattern, this tolerance will give a     ║
// ║               small range of values that the On Neck pattern will recogn- ║
// ║               ize as being valid above and below the low of the candle.   ║
// ║                                                                           ║
// ║               range: 0.01 - 1.00 (1% - 100% size of lower wick)           ║
// ║               default: 0.50 (50%)                                         ║
// ║               step: 0.001 (0.1%)                                          ║
// ║               dependencies: (LOG)[2]f_IsNeck                              ║
// ║                                                                           ║
// ║     InNeckTol - (In Neck specific) Operates slightly differently than the ║
// ║               On Neck tolerance, this tolerance will set a maximum posit- ║
// ║               ion inside of the body of the previous that the second      ║
// ║               candle may close under in order to be considered valid.     ║
// ║                                                                           ║
// ║               range: 0 - 0.50 (C1 close [exclusive] to midpoint)          ║
// ║               default: 0.15 (15% into body)                               ║
// ║               step: 0.001 (0.1%)                                          ║
// ║               dependencies: (LOG)[2]f_IsNeck                              ║
// ║                                                                           ║
// ║     ThrustTol - (Thrusting specific) Tolerance for the range of values    ║
// ║               that the second candle may close in between the midpoint    ║
// ║               and the close (first candle is bearish) in order to be      ║
// ║               considered valid.                                           ║
// ║                                                                           ║
// ║               range: 0 - 1.00 (midpoint -> close)                         ║
// ║               default: 0.50 (first 25% of body size below midpoint)       ║
// ║               step: 0.001 (0.1%)                                          ║
// ║               dependencies: (LOG)[2]f_IsThrusting                         ║
// ║                                                                           ║
// ║    TweezerTol - (Tweezers Top/Bottom specific) Tolerance for the range of ║
// ║               values that both the Tweezers Top and Tweezers Bottom may   ║
// ║               be in order to be considered valid. (As a percentage of the ║
// ║               high-low ranges of the first candle in each pattern         ║
// ║               respectively).                                              ║
// ║                                                                           ║
// ║               range: 0 - 0.10 (matching to 10% difference)                ║
// ║               default: 0.025 (2.5%)                                       ║
// ║               step: 0.00001 (0.001%)                                      ║
// ║               dependencies: (LOG)[2]f_IsTweezer                           ║
//#endregion                                                                   ║
// ╚═══════════════════════════════════════════════════════════════════════════╝

i_EngulfSet = input.string(defval = "BODY", options = ["BODY", "RANGE", "BOTH"],
  title = "Engulfing Setting", group = SET_2C, tooltip = "Sets which values "
  + "will be used to define if a candle has 'engulfed' another.\n\nBODY - A "
  + "candle engulfs another if its body completely encloses the body of the " 
  + "candle prior.\n\nRANGE - A candle engulfs another if its high and low range "
  + "completely covers the high low range of the previous candle.\n\nBOTH - All"
  + "engulfings described above will be recognized.")
i_EngulfInc = input.bool(defval = false, title = "Inclusive Engulfing", 
  group = SET_2C, tooltip = "If 'true', allows for the values of an engulfing "
  + "candle to match the values of the candle being engulfed.")

i_OCincluded = input.bool(defval = false,
  title = "Include Open/Close in 'opens within body' patterns", tooltip = "The "
  + "patterns affected by this include:\n\nBullHC - BullH - BearHC - BearH - "
  + "HP - BullML - BullSL - BearML - BearSL - ML - ON - IN - Thrust - TB - TT - "
  + "ADV - DELIB - UG3M - DG3M - BullSBS - BearSBS - SAND - UTG - DTG - TBC - "
  + "TWS - 3SITS - TC - UG2C - U3RB - CBS", group = SET_2C)
  
i_KickingGap = input.bool(defval = true, title = "Kicking Gap", 
  group = SET_2C, tooltip = "Forces Kicking patterns to have a gap "
  + "if Marubozu Candle settings allow for the opening and closing variants of "
  + "Marubozu Candles.")
  
i_TallStom1 = input.bool(defval = true, title = "Stomach Patterns - Tall C1", 
  group = SET_2C, inline = "STOMACH CANDLES")
i_TallStom2 = input.bool(defval = false, title = "Tall C2", group = SET_2C, 
  inline = "STOMACH CANDLES", tooltip = "If true, then a 'Tall' candle will be "
  + "required for this pattern to be recognized as 'valid' in that candle "
  + "position.")
  
i_HammerInvTol = input.float(title = "Inverted Hammer C1 Close Tolerance",
  minval = 0.005, maxval = 0.15, step = 0.001, defval = 0.05, 
  group = SET_2C, tooltip = "Specifies how close to the low the "
  + "'close' of the first candle for the 'Hammer Inverted' pattern must be to be "
  + "considered 'valid'. (as a % of the range)\n\nValidity Calculation: "
  + "IHC1CT * (high-low) + low >= close.")
  
i_LinesTol = input.float(title = "Lines Patterns Opens/Closes Tolerance",
  minval = 0.001, maxval = 0.25, step = 0.0001, defval = 0.05, 
  group = SET_2C, tooltip = "Specifies how close the opens/closes "
  + "the second candle in Separating/Meeting Lines patterns (respectively) must "
  + "be to the first candle in order to be considered 'valid'.\n\nCalculation: "
  + "c2 open/close within c1 open/close ± LPO/CT * 100%.")
  
i_MatchLowTol = input.float(title = "Matching Low Tolerance",
  minval = 0, maxval = 0.15, step = 0.0001, defval = 0, 
  group = SET_2C, tooltip = "Specifies some leeway (if any) given "
  + "to the 'Matching Low' pattern for the closes on both candles to be similar "
  + "(rather than exact).\n\nCalculated as a % of the first candle's range.")
  
i_OnNeckWickTol = input.float(title = "On Neck Wick Size Tolerance", 
  minval = 0.05, maxval = 0.25, step = 0.001, defval = 0.10, 
  group = SET_2C, tooltip = "Determines how close to the low that "
  + "the first candle in the 'On Neck' pattern must close in order for it to be "
  + "considered 'valid'. (Checks what % of the range the lower wick of the "
  + "candle is, if the wick exceeds this % then the pattern is invalidated.)")
i_OnNeckTol = input.float(title = "On Neck Tolerance", 
  minval = 0.01, maxval = 1.00, step = 0.001, defval = 0.50, 
  group = SET_2C, tooltip = "Determines how far into the lower wick "
  + "(from the close of the first candle) the second candle of an 'On Neck' "
  + "pattern must close to be considered 'valid'.")
i_InNeckTol = input.float(title = "In Neck Tolerance",
  minval = 0, maxval = 0.50, step = 0.001, defval = 0.15, 
  group = SET_2C, tooltip = "Defines the upper limit (as a % of the "
  + "candle body) that the close of the second candle of an 'In Neck' pattern "
  + "may be into the first candle body to be considered 'valid'.")
  
i_ThrustTol = input.float(title = "Thrusting Tolerance",
  minval = 0, maxval = 1.00, step = 0.001, defval = 0.50, 
  group = SET_2C, tooltip = "Defines the % (from midpoint to close) "
  + "portion that the close of the second candle must be within to be considered "
  + "a 'valid' Thrusting pattern.")
  
i_TweezerTol = input.float(title = "Tweezer Tolerance", 
  minval = 0, maxval = 0.10, step = 0.00001, defval = 0.025, 
  group = SET_2C, tooltip = "Defines how close to the high/low "
  + "(as a % of the high or low for the first candle) the high/low of the second "
  + "candle must be to be considered 'valid' in either 'Tweezers' pattern.")
  
// ╔═══════════════════════════════════════════════════════════════════════════╗
// ║           _____ ______   _____         _ __       __                      ║
// ║          |__  // ____/  / ___/      __(_) /______/ /_  ___  _____         ║
// ║           /_ </ /       \__ \ | /| / / / __/ ___/ __ \/ _ \/ ___/         ║
// ║         ___/ / /___    ___/ / |/ |/ / / /_/ /__/ / / /  __(__  )          ║
// ║        /____/\____/   /____/|__/|__/_/\__/\___/_/ /_/\___/____/           ║
// ║                                                                           ║
// ╚═══════════════════════════════════════════════════════════════════════════╝

i_BEABS = 
  input.bool(defval = true, title = "BearAB", inline = "3C UPTREND.1", 
  group = SET_3C, confirm = true)
i_COLS = 
  input.bool(defval = true, title = "CD", inline = "3C UPTREND.1", 
  group = SET_3C, confirm = true)
i_ESDS = 
  input.bool(defval = true, title = "EVED", inline = "3C UPTREND.1", 
  group = SET_3C, confirm = true)
i_ESS = 
  input.bool(defval = true, title = "EVE", inline = "3C UPTREND.1", 
  group = SET_3C, confirm = true)
i_IBCS = 
  input.bool(defval = true, title = "IBC", inline = "3C UPTREND.1", 
  group = SET_3C, tooltip = "3 Candle Patterns (uptrend pt.1):\n\n"
  + "BearAB - Bearish Abandoned Baby\nCD - Collapsing Doji Star\nEVED - Evening "
  + "Doji Star\nEVE - Evening Star\nIBC - Identical Black Crows", confirm = true)
  
i_TBCS = 
  input.bool(defval = true, title = "TBC", inline = "3C UPTREND.2", 
  group = SET_3C, confirm = true)
i_3ODS = 
  input.bool(defval = true, title = "3OD", inline = "3C UPTREND.2", 
  group = SET_3C, confirm = true)
i_3IDS = 
  input.bool(defval = true, title = "3ID", inline = "3C UPTREND.2", 
  group = SET_3C, confirm = true)
i_BETSS = 
  input.bool(defval = true, title = "BearTS", inline = "3C UPTREND.2", 
  group = SET_3C, confirm = true)
i_TCS = 
  input.bool(defval = true, title = "TC", inline = "3C UPTREND.2", 
  group = SET_3C, tooltip = "3 Candle Patterns (uptrend pt.2):\n\n"
  + "TBC - Three Black Crows\n3OD - Three Outside Down\n3ID - Three Inside Down\n"
  + "BearTS - Bearish Tri-Star\nTC - Two Crows", confirm = true)

i_UG3MS = 
  input.bool(defval = true, title = "UG3M", inline = "3C UPTREND.3", 
  group = SET_3C, confirm = true)
i_ADVS = 
  input.bool(defval = true, title = "ADV", inline = "3C UPTREND.3", 
  group = SET_3C, confirm = true)
i_DELS = 
  input.bool(defval = true, title = "DELIB", inline = "3C UPTREND.3", 
  group = SET_3C, confirm = true)
i_BUSBSS = 
  input.bool(defval = true, title = "BullSBS", inline = "3C UPTREND.3", 
  group = SET_3C, confirm = true)
i_UG2CS = 
  input.bool(defval = true, title = "UG2C", inline = "3C UPTREND.3", 
  group = SET_3C, tooltip = "3 Candle Patterns (uptrend pt.3):\n\n"
  + "UG3M - Upside Gap Three Methods\nADV - Advance Block\nDELIB - Deliberation\n"
  + "BullSBS - Bullish Side-by-Side\nUG2C - Upside Gap Two Crows", confirm = true)

i_BESBSS = 
  input.bool(defval = true, title = "BearSBS", inline = "3C DOWNTREND.1", 
  group = SET_3C, confirm = true)
i_SANS = 
  input.bool(defval = true, title = "SAND", inline = "3C DOWNTREND.1", 
  group = SET_3C, confirm = true)
i_U3RBS = 
  input.bool(defval = true, title = "U3RB", inline = "3C DOWNTREND.1", 
  group = SET_3C, confirm = true)
i_BUABS = 
  input.bool(defval = true, title = "BullAB", inline = "3C DOWNTREND.1", 
  group = SET_3C, confirm = true)
i_DG3MS = 
  input.bool(defval = true, title = "DG3M", inline = "3C DOWNTREND.1", 
  group = SET_3C, tooltip = "3 Candle Patterns (downtrend pt.1):\n\n"
  + "BearSBS - Bearish Side-By-Side\nSAND - Stick Sandwich\nU3RB - Unique Three "
  + "River Bottom\nBullAB - Bullish Abandoned Baby\nDG3M - Downside Gap Three "
  + "Methods", confirm = true)
  
i_DTGS = 
  input.bool(defval = true, title = "DTG", inline = "3C DOWNTREND.2", 
  group = SET_3C, confirm = true)
i_MSDS = 
  input.bool(defval = true, title = "MORND", inline = "3C DOWNTREND.2", 
  group = SET_3C, confirm = true)
i_MSS = 
  input.bool(defval = true, title = "MORN", inline = "3C DOWNTREND.2", 
  group = SET_3C, confirm = true)
i_3IUS = 
  input.bool(defval = true, title = "3IU", inline = "3C DOWNTREND.2", 
  group = SET_3C, confirm = true)
i_3OUS = 
  input.bool(defval = true, title = "3OU", inline = "3C DOWNTREND.2", 
  group = SET_3C, tooltip = "3 Candle Patterns (downtrend pt.2):\n\n"
  + "DTG - Downside Tasuki Gap\nMORND - Morning Doji\nStar\nMORN - Morning"
  + " Star\n3IU - Three Inside Up\n3OU - Three Outside Up", confirm = true)
  
i_SITSS = 
  input.bool(defval = true, title = "3SITS", inline = "3C DOWNTREND.3", 
  group = SET_3C, confirm = true)
i_TWSS = 
  input.bool(defval = true, title = "TWS", inline = "3C DOWNTREND.3", 
  group = SET_3C, confirm = true)
i_BUTSS = 
  input.bool(defval = true, title = "BullTS", inline = "3C DOWNTREND.3", 
  group = SET_3C, tooltip = "3 Candle Patterns (downtrend pt.3):\n\n"
  + "3SITS - Three Stars In The South\nTWS - Three White Soldiers\nBullTS - "
  + "Bullish Tri-Star", confirm = true)
  
// ╔═══════════════════════════════════════════════════════════════════════════╗
// ║             _____ ______   _____      __  __  _                           ║
// ║            |__  // ____/  / ___/___  / /_/ /_(_)___  ____ ______          ║
// ║             /_ </ /       \__ \/ _ \/ __/ __/ / __ \/ __ `/ ___/          ║
// ║           ___/ / /___    ___/ /  __/ /_/ /_/ / / / / /_/ (__  )           ║
// ║          /____/\____/   /____/\___/\__/\__/_/_/ /_/\__, /____/            ║
// ║                                                   /____/                  ║
// ║                                                                           ║
//#region                                                                      ║
// ║ ForceDelibGap - (Deliberation Specific) Forces Candle three to gap above  ║
// ║               Candle two in the Deliberation pattern.                     ║
// ║                                                                           ║
// ║               default: true                                               ║
// ║               dependencies: (LOG)[3]f_IsDeliberation                      ║
// ║                                                                           ║
// ║   DelibGapTol - Tolerance for Deliberation. Deliberation requires that    ║
// ║               the final candle in the pattern opens near the close of the ║
// ║               previous candle. This will include values at or below the   ║
// ║               candle, and this range is found as a function of this tole- ║
// ║               rance multiplied by the body of Candle two ± the closing    ║
// ║               price. Lower values may be excluded with [ForceDelibGap]    ║
// ║               being set to 'true'. To include the closing price, set      ║
// ║               [OCincluded] to 'true'.                                     ║
// ║                                                                           ║
// ║               range: 0.005 - 0.25 (0.5% - 25%)                            ║
// ║               default: 0.10 (10%)                                         ║
// ║               step: 0.001 (0.1%)                                          ║
// ║               dependencies: (LOG)[3]f_IsDeliberation                      ║
// ║                                                                           ║
// ║   DelibC3Size - Tolerance for Deliberation. The third candle in a Delibe- ║
// ║               ration pattern may need to vary in size. This tolerance     ║
// ║               will allow the third candle a little more room to be consi- ║
// ║               dered 'valid' in this pattern. In use, this tolerance will  ║
// ║               be multiplied by the average size of the first two candles  ║
// ║               of the pattern.                                             ║
// ║                                                                           ║
// ║               range: 0.10 - 0.75 (10% - 75%)                              ║
// ║               default: 0.33 (33%)                                         ║
// ║               dependencies: (LOG)[3]f_IsDeliberation                      ║
// ║                                                                           ║
// ║    TwoCShadEx - (Two Crows Specific) Excludes the shadows of the gapping  ║
// ║               candle (candle 2) in either Two Crows patterns.             ║
// ║                                                                           ║
// ║               default: true                                               ║
// ║               dependencies: (LOG)[3]f_IsTwoCrows                          ║
// ║                                                                           ║
// ║          IBCT - (Identical Black Crow specific) This input is meant to    ║
// ║               give leeway as to what will be considered valid in the      ║
// ║               Identical Black Crows pattern. The candles in the pattern   ║
// ║               will be considered valid if they are within ±this % of the  ║
// ║               average size of the candles of the pattern.                 ║
// ║                                                                           ║
// ║               range: 0 - 0.75 (0% - 75%)                                  ║
// ║               default: 0.25 (25%)                                         ║
// ║               dependencies: (LOG)[3]f_IsThree                             ║
// ║                                                                           ║
// ║      OC_3CTol - Open/Close Tolerance (3 candlestick patterns specific).   ║
// ║               For candlestick patterns that require the open price of one ║
// ║               or more candles to be 'close' to the closing price of the   ║
// ║               previous candle. Booleans will yield true if the open price ║
// ║               is found to be within [OC_3CTol]% the body.                 ║
// ║                                                                           ║
// ║               range: 0.01 - 0.35 (1% - 35%)                               ║
// ║               default: 0.03 (3%)                                          ║
// ║               dependencies: (LOG)[3]f_IsThree                             ║
// ║                                                                           ║
// ║   SITSCandle1 - (Star in the South specific) Tolerance for where the mid- ║
// ║               dle of the body for the first candle in the Star in the     ║
// ║               South pattern must be. Body position is found by adding the ║
// ║               open and close of the candle together and dividing by two,  ║
// ║               then subtracting the low and dividing that result by the    ║
// ║               high-low range for that candle. Candles with a middle body  ║
// ║               closer to the high will have a body position greater than   ║
// ║               0.5 and for the first candle in this pattern to be valid it ║
// ║               must be above the [SITSCandle1] tolerance.                  ║
// ║                                                                           ║
// ║               range: 0.50 - 0.95 (middle of body to 5% below high)        ║
// ║               default: 0.60                                               ║
// ║               step: 0.01                                                  ║
// ║               dependencies: (LOG)[3]f_IsThree                             ║
// ║                                                                           ║
// ║   SITSCandle2 - (Star in the South specific) Tolerance for how different  ║
// ║               the second candlestick in a Star in the South pattern can   ║
// ║               be to be considered 'valid'. This will serve in two         ║
// ║               calculations:                                               ║
// ║                                                                           ║
// ║                   1st: determine if body position of candle 2 is within   ║
// ║                   ±[SITSCandle2]% the body position of candle 1.          ║
// ║                                                                           ║
// ║                   2nd: determine if the body size ratio of candle 2 is    ║
// ║                   within ±[SITSCandle2]% of the body size ratio to candle ║
// ║                   1. Body size ratio for the candles is found by dividing ║
// ║                   the body by the high-low range.                         ║
// ║                                                                           ║
// ║               These two calculations will ensure that not only is the     ║
// ║               second candle body positioned similarly to the first, but   ║
// ║               that the relative size of the body in relation to its high- ║
// ║               low range of both candles is also similar.                  ║
// ║                                                                           ║
// ║               range: 0.01 - 0.50 (1% - 50%)                               ║
// ║               default: 0.20 (20%)                                         ║
// ║               step: 0.001 (0.1%)                                          ║
// ║               dependencies: (LOG)[3]f_IsThree                             ║
// ║                                                                           ║
// ║ SITSC2MaxProp - (Star in the South specific) Tolerance for how large the  ║
// ║               second candle in the Star in the South may be in relation   ║
// ║               to the high-low range of the first candle. (This prevents   ║
// ║               a SITS pattern from being valid if the second candle is     ║
// ║               proportionally similar to the first but larger).            ║
// ║                                                                           ║
// ║               range: 0.20 - 0.95 (20% - 95%)                              ║
// ║               default: 0.75 (75%)                                         ║
// ║               step: 0.001 (0.1%)                                          ║
// ║               dependencies: (LOG)[3]f_IsThree                             ║
// ║                                                                           ║
// ║  SBSTolerance - (Side By Side patterns specific) Tolerance for how diffe- ║
// ║               rent the final two candles in the Side By Side candlestick  ║
// ║               patterns may be. This changes how much leeway from candle 2 ║
// ║               to candle 3 may be in body size/opening and closing prices. ║
// ║               See f_IsSBS for details.                                    ║
// ║                                                                           ║
// ║               range: 0.05 - 0.50 (5% - 50%)                               ║
// ║               default: 0.15 (15%)                                         ║
// ║               dependencies: (LOG)[3]f_IsSBS                               ║
// ║                                                                           ║
// ║  StickSandTol - Tolerance for Stick Sandwich. This tolerance modifies how ║
// ║               close to the closing price candle 3 may be in relation to   ║
// ║               the closing price of candle 1 in the stick sandwich pattern.║
// ║               This will allow candle 3 to be valid in a Stick Sandwich    ║
// ║               pattern if the close is within [StickSandTol]% the size of  ║
// ║               candle 1's body added/subtracted from its close.            ║
// ║                                                                           ║
// ║               range: 0.05 - 0.33 (5% - 33%)                               ║
// ║               default: 0.10 (10%)                                         ║
// ║               dependencies: (LOG)[3]f_isStickSand                         ║
//#endregion                                                                   ║
// ╚═══════════════════════════════════════════════════════════════════════════╝

i_ForceDelibGap = input(defval = true,
  title = "Force Deliberation C2 - C3 Gap", 
  group = SET_3C)
i_DelibGapTol = input.float(title = "Deliberation Gap Tolerance",
  minval = 0.005, maxval = 0.25, step = 0.001, defval = 0.10, 
  group = SET_3C, tooltip = "Determines how close to the close of "
  + "Candle 2 (as a ±% of the body size) Candle 3 must open. Use 'Force Delibe"
  + "ration C2 - C3 Gap' to limit opens to gapping on the third candle.")
i_DelibC3Size = input.float(title = "Deliberation 3rd Candle Tolerance",
  minval = 0.10, maxval = 0.75, defval = 0.33, group = SET_3C,
  tooltip = "Defines how large the third candle in this pattern may be relative "
  + "to the average size of the first and second candles.")
  
i_TwoCShadEx = input.bool(defval = true,
  title = "Exclude Shadows in Two Crows", group = SET_3C, 
  tooltip = "Changes the Two Crows patterns mechanics and invalidates the "
  + "pattern if the low of C2 does not gap C1 close.")

i_IBCT = input.float(title = "Identical Black Crow Tolerance",
  minval = 0, maxval = 0.75, step = 0.001, defval = 0.25, 
  group = SET_3C, tooltip = "Defines how large the ±% deviation of "
  + "each candle body may be compared to the average size of the three candles "
  + "in the 'Identical Black Crows' pattern for the candles to be considered "
  + "'valid'.")

i_OC_3CTol = input.float(title = "Open/Close Tolerance (3 candlesticks)",
  minval = 0.01, maxval = 0.35, step = 0.001, defval = 0.03, 
  group = SET_3C, tooltip = "Multi-Purpose:\n\nThree White Soldiers: "
  + "Defines how close (as a % of the candle range) to the highs the three "
  + "candles in the 'Three White Soldiers' must close to be considered 'valid'."
  + "\n\nThree Black Crows: Defines how close (as a % of the candle range) to "
  + "the lows the three candles in the 'Three Black Crows' must close to be "
  + "considered 'valid'.")

i_SITSCandle1 = 
  input.float(title = "Three Stars In The South C1 Body Pos Tolerance",
  minval = 0.50, maxval = 0.95, step = 0.01, defval = 0.60, 
  group = SET_3C, tooltip = "Defines where the middle of the body "
  + "for the first candle in the Stars In the South must be above (as a part of "
  + "the range) to be considered 'valid'.")
i_SITSCandle2 = input.float(title = "Three Stars In The South C2 Deviation Tolerance",
  minval = 0.01, maxval = 0.50, step = 0.001, defval = 0.20, 
  group = SET_3C, tooltip = "Multi-Purpose:\n\nDefines the accepted "
  + "range for where the midpoint of the second candle body may be in relation "
  + "to the first.\n\nDefines the accepted range the % of the second candle's "
  + "range the body may be relative to the first.\n\nLayman's Terms: \"Is the "
  + "center of the second body similar to the first? Are the body sizes  "
  + "relatively the same compared to their respective ranges?\"")
i_SITSC2MaxProp = input.float(title = "Star in the South Candle 2 Max Proportion",
  minval = 0.20, maxval = 0.95, step = 0.001, defval = 0.75, 
  group = SET_3C, tooltip = "If the first two candles are similar "
  + "to each other, this defines the maximum size the second candle's range may "
  + "be compared to the first to be considered 'valid'.")
  
i_SBSTolerance = input.float(title = "Side By Side Tolerance",
  minval = 0.05, maxval = 0.50, defval = 0.15, group = SET_3C,
  tooltip = "Defines how close to the opens/closes of Candle 2 Candle 3 must "
  + "open and close to be considered 'valid'.")
  
i_StickSandTol = input.float(title = "Stick Sand Tolerance", 
  minval = 0.05, maxval = 0.33, defval = 0.10, group = SET_3C,
  tooltip = "Defines how close to the close of Candle 1 (as a % of the candle "
  + "body) that Candle 3 must close to be considered 'valid'.")
  
// ╔═══════════════════════════════════════════════════════════════════════════╗
// ║            __ __  ______   _____      __  __  _                           ║
// ║           / // / / ____/  / ___/___  / /_/ /_(_)___  ____ ______          ║
// ║          / // /_/ /       \__ \/ _ \/ __/ __/ / __ \/ __ `/ ___/          ║
// ║         /__  __/ /___    ___/ /  __/ /_/ /_/ / / / / /_/ (__  )           ║
// ║           /_/  \____/   /____/\___/\__/\__/_/_/ /_/\__, /____/            ║
// ║                                                   /____/                  ║
// ║                                                                           ║
// ║ This section is comprised of both the switches and settings for all 4     ║
// ║ candle patterns that are recognized by this script.                       ║
// ║                                                                           ║
// ║      CBSC3Tol - (Concealing Baby Swallow Specific) Tolerance for candle   ║
// ║               three in the Concealing Baby Swallow pattern. Allows the    ║
// ║               user to specify how small/large the body of the third       ║
// ║               candle may be in relation to the size of the candle's upper ║
// ║               wick.                                                       ║
// ║                                                                           ║
// ║               range: 0.10 - 1.00 ([upperwick : body] 10:1 - 1:1)          ║
// ║               default: 0.50 (2:1)                                         ║
// ║               dependencies: (LOG)[4]f_IsConcealingBabySwallow             ║
// ║                                                                           ║
// ╚═══════════════════════════════════════════════════════════════════════════╝

i_BU3LSS = 
  input.bool(defval = true, title = "Bull3LS", inline = "4C PATTERNS", 
  group = SET_4C, confirm = true)
i_LBS = 
  input.bool(defval = true, title = "LB", inline = "4C PATTERNS", 
  group = SET_4C, confirm = true)
i_BE3LSS = 
  input.bool(defval = true, title = "Bear3LS", inline = "4C PATTERNS", 
  group = SET_4C, confirm = true)
i_CBSS = 
  input.bool(defval = true, title = "CBS", inline = "4C PATTERNS", 
  group = SET_4C, tooltip = "4 Candle Patterns:\n\nUptrend:\nBull3LS"
  + " - Bullish Three Line Strike\n\nDowntrend:\nLB - Ladder Bottom\nBear3LS - "
  + "Bearish Three Line Strike\nCBS - Concealing Baby Swallow", confirm = true)

i_CBSC3Tol = input.float(title = "Concealing Baby Swallow Candle 3 Tolerance",
  minval = 0.10, maxval = 1.00, defval = 0.50, group = SET_4C,
  tooltip = "Defines the % size relative to the upperwick the body of the "
  + "third candle may be to be considered 'valid'.")
  
// ╔═══════════════════════════════════════════════════════════════════════════╗
// ║            ____________   _____      __  __  _                            ║
// ║           / ____/ ____/  / ___/___  / /_/ /_(_)___  ____ ______           ║
// ║          /___ \/ /       \__ \/ _ \/ __/ __/ / __ \/ __ `/ ___/           ║
// ║         ____/ / /___    ___/ /  __/ /_/ /_/ / / / / /_/ (__  )            ║
// ║        /_____/\____/   /____/\___/\__/\__/_/_/ /_/\__, /____/             ║
// ║                                                  /____/                   ║
// ║                                                                           ║
// ║ This section is comprised of both the switches and settings for all 5     ║
// ║ candle patterns recognized by this script.                                ║
//#region                                                                      ║
// ║   LBC4WickMin - (Ladder Bottom Specific) Ladder Bottom Body Lower %.      ║
// ║               This tolerance specifies the minimum size, as a % of the    ║
// ║               candle's high-low range, the upper wick of candle four      ║
// ║               must be.                                                    ║
// ║                                                                           ║
// ║               range: 0.01 - 0.75 (1% - 75%)                               ║
// ║               default: 0.20 (20%)                                         ║
// ║               step: 0.001 (0.1%)                                          ║
// ║               dependencies: (LOG)[5]f_IsLadderBottom                      ║
// ║                                                                           ║
// ║   LBC4WickMax - (Ladder Bottom Specific) Ladder Bottom Body Upper %.      ║
// ║               This tolerance specifies the maximum size, as a % of the    ║
// ║               candle's high-low range, the body of candle four may be.    ║
// ║                                                                           ║
// ║               range: LBC4BodyMin - 1.00 (variable - 100%)                 ║
// ║               default: 0.50 (50%)                                         ║
// ║               step = 0.001 (0.1%)                                         ║
// ║               dependencies: (LOG)[5]f_IsLadderBottom                      ║
//#endregion                                                                   ║
// ╚═══════════════════════════════════════════════════════════════════════════╝

i_BEBAS = 
  input.bool(defval = true, title = "BearBA", inline = "5C PATTERNS", 
  group = SET_5C, confirm = true)
i_MHS = 
  input.bool(defval = true, title = "MH", inline = "5C PATTERNS", 
  group = SET_5C, confirm = true)
i_R3MS = 
  input.bool(defval = true, title = "R3M", inline = "5C PATTERNS", 
  group = SET_5C, confirm = true)
i_F3MS = 
  input.bool(defval = true, title = "F3M", inline = "5C PATTERNS", 
  group = SET_5C, confirm = true)
i_BUBAS = 
  input.bool(defval = true, title = "BullBA", inline = "5C PATTERNS", 
  group = SET_5C, tooltip = "5 Candle Patterns:\n\nUptrend:\nBearBA "
  + "- Bearish Breakaway\nMH - Mat Hold\nR3M - Rising Three Methods\n\nDowntrend:"
  + "\nF3M - Falling Three Methods\nBullBA - Bullish Breakaway", confirm = true)
  
i_LBC4WickMin = input.float(title = "LBC4 Upper Wick Min %",
  minval = 0.01, maxval = 0.75, defval = 0.50, step = 0.001, inline = "LBC4W",
  group = SET_5C)
  
i_LBC4WickMax = input.float(title = "Max %", minval = 0.01, maxval = 1.00, 
  defval = 0.90, step = 0.001, inline = "LBC4W", group = SET_5C, 
  tooltip = "These two values define a range of values the Ladder Bottom's Upper "
  + "wick may be as a proportion of the range to be valid.\n\nNote: Min % must "
  + "be greater than Max %, otherwise an error will be thrown.")

// Error handling on inputs
if i_LBC4WickMin > i_LBC4WickMax
    runtime.error("Ladder Bottom's Wick Min % must be less than the "
      + "specified Max %.")

// ╔═══════════════════════════════════════════════════════════════════════════╗ 
// ║           __ __    __              __  ___    __  __         __           ║
// ║          / // ___ / ___ ___ ____  /  |/  ___ / /_/ / ___ ___/ ___         ║
// ║         / _  / -_/ / _ / -_/ __/ / /|_/ / -_/ __/ _ / _ / _  (_-<         ║
// ║        /_//_/\__/_/ .__\__/_/   /_/  /_/\__/\__/_//_\___\_,_/___/         ║
// ║                  /_/                                                      ║
// ║                                                                           ║
// ║ Many methods used in this section will be used repeatedly throughout this ║
// ║ script to perform basic logical operations like determining if a candle   ║
// ║ is bullish or returning the average body size of the most recent candles. ║
// ║                                                                           ║
// ╚XHELPERS═══════════════════════════════════════════════════════════════════╝

// This function will append the given return array with a new return. It will 
// also update all relevant statistics for that return array:
//    The array's size
//    Array's average
//    Array's median
//    Array's standard deviation
//    Which polarity of returns this pattern has impacted
//
// @inputs:
//    i_(Nega/Posi)tiveRetTol - These two inputs specify which polarities to
//                            change dependent upon the _returnVal to be added. 
//                            
//                            If the _returnVal is:
//                              <= i_NegativeRetTol -> increment negative val
//                              > i_NegativeRetTol & < i_PositiveRetTol -> 
//                              increment neutral val
//                              >= i_PositiveRetTol -> increment positive val
// 
// @params:
//       returnArray _r - The return array that corresponds to this pattern.
//     float _returnVal - The value to add to the returnArray's collection and
//                      generate statistics from.
//
// @return:
//      void
//
f_AddReturnAndUpdate(returnArray _r, float _returnVal) =>
    array.push(_r.returns, _returnVal)
    polarityToInc = _returnVal <= i_NegRetTol ? 0 : _returnVal > 
      i_NegRetTol and _returnVal < i_PosRetTol ? 1 : 2
    currPolVal = array.get(_r.polarities, polarityToInc)
    array.set(_r.polarities, polarityToInc, currPolVal + 1)
    _r.size := _r.size + 1
    _r.stdDev := array.stdev(_r.returns)
    _r.median := array.median(_r.returns)
    _r.avg := array.avg(_r.returns)

// Append Tooltip is a helper method which will take in a string and place that
// string at the end of the tooltip for the label of a pattern, updating the 
// tooltip in the process. This is necessary as PineScrypt currently does not
// have a label.get_tooltip function. 
//
// @params:
//    patternObj _pat - The pattern object to modify the tooltip of.
//        string _tip - The tip to be appended to the end of that label tooltip.
//
// @return:
//    void
//
f_AppendTooltip(patternObj _pat, string _tip) =>
    _pat.labelTooltip += _tip

    label.set_tooltip(_pat.patLabel, _pat.labelTooltip)

// Display Stats on Label will take all of the relevant statistics from a 
// returnArray matrix entry for a specific pattern, and display them onto the 
// tooltip of the pattern's label. When the user scrolls over the pattern label,
// it will display:
//
//    - The partition of the chart the pattern has been found in.
//    - ("BREAKOUT" mode only) The breakout direction of the pattern.
//    - The number of previous instances.
//    - The median return for that pattern.
//    - The average return for that pattern.
//    - The standard deviation of returns for that pattern.
//    - A 95% confidence interval of the expected return for this pattern.
//    - The current number of negative, neutral, and positive returns this pattern
//    has experienced.
//
// If the pattern has not occurred at least i_MinRetsNeeded times, it will 
// instead specify an 'return stats unavailable' message with the remaining number 
// of patterns needed.
//
// @input:
//    i_MinRetsNeeded - The number of patterns that need to occur before the 
//                       statistics of the pattern is displayed on the tooltip.
//
// @params:
//     patternObj _p - The pattern to assess data from (partition) and the 
//                   label/tooltip to be modified.
//    returnArray _r - The returnArray that contains all stats on the pattern 
//                   that has occurred.
//
// @return:
//      void
//
f_DisplayStatsOnLabel(patternObj _p, returnArray _r) =>
    updateTooltip = ""

    sizeStr = str.tostring(_r.size)
    partString = switch _p.part
        0 => "Upper"
        1 => "Middle"
        2 => "Lower"
    partString := partString + " Section"
    breakoutDirStr = ""

    if i_DetectionType == "BREAKOUT"
        breakoutDirStr := (_p.breakoutDir == 1 ? " (Upward" : " (Downward")
          + " breakout)"

    if _r.size >= i_MinRetsNeeded
        stdDevStr = str.tostring(_r.stdDev, "#.####")
        medianStr = str.tostring(_r.median, "#.####")
        averageStr = str.tostring(_r.avg, "#.####")
        // CONFIDENCE INTERVAL CALC (ASSUMES NORMAL DISTRIBUTION):
        //    CI = avg ± (Z * (σ / √n))
        //    Z -> Z-score for 95% intervals (~1.96)
        //    σ -> standard deviation of the set
        //    n -> number of elements in the set
        CILower = str.tostring(_r.avg - 
          (1.96 * (_r.stdDev / math.sqrt(_r.size))), "#.####") + "%"
        CIUpper = str.tostring(_r.avg + 
          (1.96 * (_r.stdDev / math.sqrt(_r.size))), "#.####") + "%"
        negReturns = array.get(_r.polarities, 0)
        neuReturns = array.get(_r.polarities, 1)
        posReturns = array.get(_r.polarities, 2)
        totReturns = negReturns + neuReturns + posReturns
        negRetStr = str.tostring(negReturns) + " (" + 
          str.tostring((negReturns / totReturns) * 100, "#.####") + "%)"
        neuRetStr = str.tostring(neuReturns) + " (" + 
          str.tostring((neuReturns / totReturns) * 100, "#.####") + "%)"
        posRetStr = str.tostring(posReturns) + " (" + 
          str.tostring((posReturns / totReturns) * 100, "#.####") + "%)"
        combinedString = "\nReturn Stats:\nPartition: "+ partString
          + "\nPrevious Instances: " + sizeStr 
          + "\nMedian Return: " + medianStr + "%"
          + "\nAverage Return: " + averageStr + "%"
          + "\nStandard Deviation: ±" + stdDevStr + "%" 
          + "\n95% Confidence Interval: [" + CILower + ", " + CIUpper + "]"
          + "\nNegative Returns: " + negRetStr 
          + "\nNeutral Returns: " + neuRetStr
          + "\nPositive Returns: " + posRetStr
        updateTooltip := breakoutDirStr + combinedString
    else
        updateTooltip := breakoutDirStr + "\nReturn Stats (Unavailable):\nPartition: " 
          + partString + "\nPrevious Instances: " + sizeStr + "\n\n" 
          + str.tostring(i_MinRetsNeeded - _r.size) + " more patterns needed."
    f_AppendTooltip(_p, updateTooltip)
  
// Override Previous is a unique helper function which will cause this hunter to 
// absorb smaller patterns that are completely encapsulated by a larger pattern. 
// When patterns are absorbed, the switch for overriding those patterns is set 
// to true, and checked down the line when percent returns are being calculated 
// for that pattern. Patterns with a 'true' overridden value will not have their 
// returns calculated.
//
// @param:
//    patternObj[] _overrides - An array that contains a specific number of 
//                            previous TEST_VAL (see global vars) patterns. From 
//                            left to right, the number of candles a pattern may
//                            have in this set to be absorbed increases by 1. *
//
// * This will result in the following using a three candle pattern as an example:
// The previous two TEST_VAL values will be historically referenced and passed to 
// this function.
// On the first candle of that pattern, candle patterns of size 1 will be absorbed.
// On the second candle, candle patterns of size 2 will be absorbed and so on.
// This prevents a 3 candle pattern which was found on the first candle of a new 
// 3 candle pattern from being absorbed.
//
// NOTE: Patterns selected in TARGET MODE will not be absorbed by larger patterns,
// but the returns for all values 
//
// @return:
//    void, will trigger alerts if enabled.
//
f_OverridePrevious(patternObj[] _overrides) =>
    // Edge case at beginning of charts
    if not na(_overrides)
        sizeOfArr = array.size(_overrides)
        for i = 0 to sizeOfArr - 1
            currPat = array.get(_overrides, i)
            if not na(currPat)
                // Special case: do not override patterns selected by target mode
                if not (i_CandleDetSet == "TARGET MODE" and 
                  currPat.ID == f_GetIDFromTarget())
                    // Only absorb patterns that are less than/equal to the current 
                    // number of candles than the numerical position of the candle 
                    // in the larger pattern.
                    if currPat.size <= (i + 1)
                        currPat.overridden := true
                        label.delete(currPat.patLabel)
                        currPat.labelTooltip := ""
                        f_AlertOnOverrides(currPat)

// Helper function to return the proper moving average (with its length) to the
// variable that requests it. 
//
//  NOTE: Symmetrical moving average does not require a specified length, see
//  https://www.tradingview.com/pine-script-reference/v5/#fun_ta{dot}swma for 
//  details. (Using multiple Symmetrical Moving Averages is effectively useless 
//  for this program, but is included as a feature instead. Padded protection 
//  to prevent the user from using multiple SWMAs has been added.)
//
//  Other moving average functions will be directly copied from PineScrypt's 
//  built in functions (Smoothed and Least Squares).
//
//----------------------------NOTE VERSION #(v5)--------------------------------
//
//  @inputs:
//      (Arnaud Legoux Specific)
//      i_ALOffset - Smoothness/Responsiveness tradeoff value (0 - 1)
//      i_ALSigma - General Smoothness (larger value -> more smooth)
//      i_ALFloor - (optional) Floors Offset pre ALMA calculations.
//
//      (See: https://www.tradingview.com/pine-script-reference/v5/#fun_ta{dot}alma)
//
//
//      (Linear Regression MA specific)
//      i_LSOffset - Offset to used for the Least Squares Moving Average.
//  
//  @params: 
//      _MAType - input type of Moving Average to return.
//      _MALength - Length of the MA to return.
//      _source - Source input to use for the MA to return.
//
//  @return:
//      movAvg - series[] moving average function specified by _MAType
//
f_GetMA(_MAType, _MALength, _source) =>

    switch(_MAType)
        "SMA" => ta.sma(_source, _MALength)
        "EMA" => ta.ema(_source, _MALength)
        "Volume Weighted" => ta.vwma(_source, _MALength)
        "Weighted" => ta.wma(_source, _MALength)
        "Hull" => ta.hma(_source, _MALength)
        "Symmetrical" => ta.swma(_source)
        "Smoothed" =>
            smma = 0.0
            smma := smma[1] ? ta.sma(_source, _MALength) :
              (smma[1] * (_MALength - 1) + _source) / _MALength
            smma
        "Arnaud Legoux" => ta.alma(_source, _MALength, i_ALOffset, i_ALSigma, i_ALFloor)
        => ta.linreg(_source, _MALength, i_LSOffset)
    
// Returns a tuple containing the higher and lower of the two prices given to it
// (will be mainly used in the sense where candlestick color does not matter but
// the pattern requires the open/close price of said candle). 
//
// If a candle is bearish, then the opening price will be higher than the closing 
// price.  
// 
// @params: 
//      _c open - opening price of this candle.
//        close - closing price of this candle.
//      
// @return: 
//      higher - the higher of these two candles.
//       lower - the lower of these two candles.
//
// @dependencies:
//      (HM)f_IsMarubozu
//      (HM)f_Engulfs
//      (HM)f_WithinVals
//      (GV)f_IsDojiPat
//      (LOG)[3]f_IsStar
//      (LOG)[5]f_IsBreakaway
//      (LOG)[5]f_IsMatHold
//      (LOG)[5]f_IsThreeMethods
//
f_GetHigherLowerTuple(_copen, _cclose) =>
    higher = math.max(_copen, _cclose)
    lower = math.min(_copen, _cclose)
    
    [higher, lower]
    
// Returns a tuple containing the range of values that would be considered valid
// when a non-definite answer is needed. Values will be returned in upper/lower
// order.
// 
//          definite: 'open', 'high', 'low', 'close' (and for the purposes of 
//                    this program, some values derived from these [see Global
//                    Variables - Secondary values])
//      non-definite: 'near', 'close to'
//
// The way this method will be used is for added flexibility so multiple logic
// functions may use it and specify their own tolerances. Diminishing effects 
// may be added to account for outlier candlesticks (like long days with
// extremely large bodies being a part of a candlestick pattern that uses this
// function).
//
// @params: 
//         _cvalue - A candle value for tolerizing.
//          _cbody - A proportion value to tolerize _cvalue by.
//      _tolerance - The proportion of _cbody to tolerize _cvalue by.
//
// @return:
//      upperLim - the upper limit for the adjusted _cvalue given
//      lowerLim - the lower limit for the adjusted _cvalue given.
//
// @dependencies:
//      (GV)f_IsDojiPat
//      (LOG)[2]f_IsLines
//      (LOG)[2]f_IsMatchLow
//      (LOG)[2]f_IsTweezer
//      (LOG)[3]f_IsDeliberation
//      (LOG)[3]f_IsSBS
//      (LOG)[3]f_IsStickSandwich
//
f_GetTolerancedValueTuple(_cvalue, _cbody, _tolerance) =>
    lowerLim = (_cvalue - (_cbody * _tolerance))
    
    upperLim = (_cvalue + (_cbody * _tolerance))
  
    [upperLim, lowerLim]

// Returns the average body size of the previous [i_TCSample] candles. Starting
// at the candle before the first candle to be tested for patterns.
//
// @input:
//    i_TCSetting - Tall Candle Setting: Determines which to use for the body 
//                values in defining "tall" candles: either range (high - low) 
//                or body abs(close - open).
//     i_TCSample - The number of candles to back test for average candle size.
// 
// @return: 
//      float - average requested size of the previous [i_TCSample] candles.
//
// @dependencies:
//      (HM)f_IsTall (below)
//      (LOG)[1]f_IsLongDay
//
f_TallCandlePrevAvg() =>
    bodyVal = i_TCSetting == "RANGE" ? high - low : math.abs(close - open)
    // Second candle is back tested to get average up to the first candle 
    // back (candle tested for single-candle patterns)
    float sizeAvg = math.sum(bodyVal[2], i_TCSample) / i_TCSample
        
    sizeAvg
    
// Returns true if the given candlestick body is at least [TCTol * 100]% the
// size of the average [TCsample] previous candlesticks.
//
// @inputs:
//      i_TCTol - Tolerance for how much larger this candle must be to be
//              considered 'tall'.
// 
// @params:
//      _candlesize - Size of this candle's body/range.
//
// @return:
//      bool - true if this candle's body size is at least [TCTol * 100]% the 
//           average size of the previous [TCsample] candles.
//           - false otherwise.
//
// @dependencies:
//      (HM)f_GenerateSecondary [below]
//
f_IsTall(_candleSize) =>
    avgBody = f_TallCandlePrevAvg()
    
    math.abs(_candleSize) >= avgBody * i_TCTol
    
// Generates all the secondary derivative values with the input candlestick
// (code reduction).
//
// @inputs: 
//      i_TCSetting - Sets which range will be used in determining if a candle 
//                  is tall.
//
// @params:
//      _CX_OPEN  - open of the given candle.
//      _CX_HIGH  - high of the given candle.
//      _CX_LOW   - low of the given candle.
//      _CX_CLOSE - close of the given candle.
//
// @returns:
//      CX_BODY      - (float) Body of the candle.
//      CX_ISBULLISH   - (boolean) true/false if candle is bullish (open < close).
//      CX_ISBEARISH   - (boolean) true/false if candle is bearish (~CX_ISBULLISH).
//      CX_UPPERWICK - (float) upper wick of the candle.
//      CX_LOWERWICK - (float) lower wick of the candle.
//      CX_RANGE     - (float) high - low range of the candle.
//      CX_MIDPRICE  - (float) middle price of body.
//      CX_BODYPOS   - (float) position of CX_MIDPRICE in the high-low range.
//      CX_ISTALL    - (boolean) true/false if the candle body exceeds i_TCTol%
//                   of the average.
//
f_GenerateSecondary(_CX_OPEN, _CX_HIGH, _CX_LOW, _CX_CLOSE) =>
    CX_BODY = _CX_CLOSE - _CX_OPEN
    CX_ISBULLISH = CX_BODY >= 0
    CX_ISBEARISH = not CX_ISBULLISH
    CX_UPPERWICK = CX_ISBULLISH ? _CX_HIGH - _CX_CLOSE : _CX_HIGH - _CX_OPEN
    CX_LOWERWICK = CX_ISBULLISH ? _CX_OPEN - _CX_LOW : _CX_CLOSE - _CX_LOW
    CX_RANGE = _CX_HIGH - _CX_LOW
    CX_MIDPRICE = (_CX_OPEN + _CX_CLOSE) / 2
    CX_BODYPOS = (CX_MIDPRICE - _CX_LOW) / CX_RANGE
    CX_ISTALL = f_IsTall(i_TCSetting == "RANGE" ? CX_RANGE : CX_BODY)
    
    [CX_BODY, CX_ISBULLISH, CX_ISBEARISH, CX_UPPERWICK, CX_LOWERWICK, CX_RANGE,
      CX_MIDPRICE, CX_BODYPOS, CX_ISTALL]

// Returns true if this candle is a Marubozu: It has no upper or lower shadow
// or both.
// 
// @params:
//      _c high - given high of this candle.
//         open - given open of this candle.
//        close - given close of this candle.
//          low - given low of this candle.
// 
// @return:
//      int -  3 if the candle is a White Marubozu
//          -  2 if the candle is an Opening White Marubozu
//          -  1 if the candle is a Closing White Marubozu
//          - -3 if the candle is a Black Marubozu
//          - -2 if the candle is a Closing Black Marubozu
//          - -1 if the candle is an Opening Black Marubozu
//          -  0 if NOTA.
//
// @dependencies:
//      (LOG)[2]f_IsKicking
//      (LOG)[3]f_IsThree
//      (LOG)[4]f_IsConcealingBabySwallow
//
f_IsMarubozu(_copen, _chigh, _clow, _cclose) =>
    [higher, lower] = f_GetHigherLowerTuple(_copen, _cclose)
    cBody = _cclose - _copen
    cIsBullish = cBody >= 0
    
    if cIsBullish
        opening = _clow == lower
        closing = _chigh == higher
      
        opening and closing ? 3 : opening ? 2 : closing ? 1 : 0
      
    else
        closing = _clow == lower
        opening = _chigh == higher
      
        opening and closing ? -3 : closing ? -2 : opening ? -1 : 0

// Returns true if candle 2 engulfs candle 1. Candle color is ignored here as to
// allow functions that call this helper method to enforce candle color.
//
//              Ex:              
//                                    1   2
//                                    ▄
//                                    ▀   ▓
//
// @input:
//      i_InclEngOC - Allows engulfing candles that have the same opening or 
//                  closing price (or both) 
//
// @params:
//     _c2 FirstVal - given value of candle 2.
//        SecondVal - given value of candle 2.
//     _c1 FirstVal - given value of candle 1.
//        SecondVal - given value of candle 1.
//
// @return:
//      bool - true if candle 2 engulfs candle 1.
//           - false otherwise.
//
// @dependencies:
//      (LOG)[2]f_Is2CEngulfing
//      (LOG)[3]f_Is3InAndOut
//      (LOG)[3]f_IsTwoCrows
//      (LOG)[4]f_IsConcealingBabySwallow
//
f_Engulfs(_c2FirstVal, _c2SecondVal, _c1FirstVal, _c1SecondVal) =>
    [tallerHigher, tallerLower]   = f_GetHigherLowerTuple(_c2FirstVal, _c2SecondVal)
    [shorterHigher, shorterLower] = f_GetHigherLowerTuple(_c1FirstVal, _c1SecondVal)
    
    if i_EngulfInc
        tallerHigher >= shorterHigher and tallerLower <= shorterLower
    else
        tallerHigher > shorterHigher and tallerLower < shorterLower

// Returns whether or not the supplied candle price is within the other two 
// values given to it. (Many chart patterns depend on this working correctly).
//
// @inputs: 
//      i_OCincluded - Boolean to include the given prices in the range.
//
// @params: 
//      _c 1price - candle 1 price.
//        2price1 - candle 2 first price.
//        2close2 - candle 2 second price.
//
// @return: 
//      bool - true if the given price is within the supplied price range.
//           - false otherwise.
//
// @dependencies:
//      (LOG)[1]f_IsDojiPat
//      (LOG)[2]f_IsHarami
//      (LOG)[2]f_IsHomingPige
//      (LOG)[2]f_IsLines
//      (LOG)[2]f_IsMatchLow
//      (LOG)[2]f_IsNeck
//      (LOG)[2]f_IsThrusting
//      (LOG)[2]f_IsTweezer
//      (LOG)[3]f_IsAdvanceBlock
//      (LOG)[3]f_IsDeliberation
//      (LOG)[3]f_IsGap3Methods
//      (LOG)[3]f_IsSBS
//      (LOG)[3]f_IsStickSandwich
//      (LOG)[3]f_IsTasukiGap
//      (LOG)[3]f_IsThree
//      (LOG)[3]f_IsTwoCrows
//      (LOG)[3]f_IsUnique3RB
//      (LOG)[4]f_IsConcealingBabySwallow
//
f_WithinVals(_c1price, _c2price1, _c2price2) =>
    [higher, lower] = f_GetHigherLowerTuple(_c2price1, _c2price2)
    
    if i_OCincluded
        _c1price >= lower and _c1price <= higher
    else
        _c1price > lower and _c1price < higher

// Helper function which determines a % change between two prices given a 
// number of candles to go back (modified by some offset if provided).
//
// @params: 
//      _length - The number of candles to find a difference in price between.
//      _offset - A number of candles to shift the calculation to the left by.
//
// Calculation:
//    (close[_offset] - open[_length + _offset]) / open[_length + _offset] * 100.
//
// @return:
//      float - value representing the % price difference from the open to the
//            close [_length] candles ahead.
//            
f_CalculatePercentReturn(int _length, int _offset) =>
    openAt = open[_length + _offset]
    closeTo = close[_offset]

    percentReturn = ((closeTo - openAt) / openAt) * 100

    percentReturn
    
// ╔═══════════════════════════════════════════════════════════════════════════╗ 
// ║        _______     __       __  _   __         _      __   __             ║ 
// ║       / ___/ ___  / / ___ _/ / | | / ___ _____(____ _/ /  / ___ ___       ║
// ║      / (_ / / _ \/ _ / _ `/ /  | |/ / _ `/ __/ / _ `/ _ \/ / -_(_-<       ║
// ║      \___/_/\___/_.__\_,_/_/   |___/\_,_/_/ /_/\_,_/_.__/_/\__/___/       ║
// ║                                                                           ║
// ║ Global variables for this program that store data to be used by all the   ║
// ║ logic functions will be placed here. Doing this will cut down on          ║
// ║ unnecessary calls for information that has already been accessed and      ║
// ║ allows for their reuse/modification. PineScrypt's property of applying    ║
// ║ this entire script to every individual candlestick is the main driving    ║
// ║ factor behind this design.                                                ║
// ║                                                                           ║
// ║ Each candlestick will be using the CX_ notation with a descriptor after   ║
// ║ the underscore that corresponds to the element of that candle, and the    ║
// ║ candle number (X) will be from the set of integers [1,2,3,4,5].           ║
// ║                                                                           ║
// ║ #################### IMPORTANT FUNCTIONALITY NOTE ####################### ║
// ║                                                                           ║
// ║ Since candlestick patterns will be analyzed from left to right, C1_ will  ║
// ║ correspond to the fifth candle back, C2_ the fourth, and so on. In this   ║
// ║ code, notations may appear in reverse to utilize the built in historical  ║
// ║ reference ([]) for efficiency (less function calls).                      ║
// ║                                                                           ║
// ║ ######################################################################### ║
// ║                                                                           ║
// ║ Each cX_ candle will have the following variables for each candle:        ║
// ║                                                                           ║
// ║ Primary: (direct access from built-ins: open, high, low, close)           ║
// ║     open      - open of this candle                                       ║
// ║     high      - high of this candle                                       ║
// ║     low       - low of this candle                                        ║
// ║     close     - close of this candle                                      ║
// ║                                                                           ║
// ║ Secondary: (derived values from the stored primary values of each candle) ║
// ║     body      - (close - open) of this candle                             ║
// ║     isbullish - true/false if the candle body is positive                 ║
// ║     isbearish - true/false if the candle body is negative                 ║
// ║     upperwick - the upperwick for this candle (see ?:)                    ║
// ║     lowerwick - the lowerwick for this candle (see ?:)                    ║
// ║     range     - the (high - low) range for this candle                    ║
// ║     midprice  - the middle price of the body, ([open + close] / 2)        ║
// ║     bodypos   - the placement of the median price of this candle based on ║
// ║               the high-low range. (Valued between 0 [low of candle] and 1 ║
// ║               [high of candle].)                                          ║
// ║     istall    - Determines if the candle is tall based upon the specified ║
// ║               number of candlesticks to sample for average size as well   ║
// ║               as specified tolerance values (see i_TCTol).                ║
// ║                                                                           ║
// ║ The final trend variables will be used in determining if the candle prior ║
// ║ to each candle pattern (accounting for the number of candles per pattern) ║
// ║ is in an uptrend or downtrend. (Ex: For a candlestick pattern that has 3  ║
// ║ candles, the fourth candle will determine the current trend based on the  ║
// ║ specified moving average [default is close])                              ║
// ║                                                                           ║
// ║ The section for determining if a candle is a Doji has also been moved to  ║
// ║ below these global variables as specific parts of candles prior to the    ║
// ║ one that most recently closed is required.                                ║
// ║                                                                           ║
// ╚XGLOBVARS══════════════════════════════════════════════════════════════════╝

//------------------------ MAIN CALL BLOCK GLOBAL VARS -------------------------

// IMPORTANT GLOBAL VAR -> This value will be what is compared to determine if a 
// pattern has appeared. The value of this variable changing will prevent further
// execution of functions which determine the appearance of candlestick patterns.
patternObj TEST_VAL = na
// Setup matrix sizing
var int MATRIX_ROWS = array.size(PAT_NAME)
var int MATRIX_COLS = i_DetectionType == "CLASSIC" ? 3 : 6
var matrix<returnArray> PERCENT_RETURNS = 
  matrix.new<returnArray>(MATRIX_ROWS, MATRIX_COLS, na)

// Used to avoid referencing 'na' blocks of the matrix for highest/lowest return
// calculation (see f_MatrixMinMax).
var point[] RETURN_POSITIONS = array.new<point>()

// These values correspond to non-special Doji pattern IDs, as well as 
// 4 two-candle Doji patterns
var int[] DOJI_VALID_COMPS = array.from(23, 25, 20, 21, 6, 7, 8, 9, 14)

// Coloration values to be used with barcolor()
barColTup[] BAR_COLOR_VALS = array.new<barColTup>()
color[] CANDLE_COLOR_ARR = array.new<color>(5, na)
CURR_CLOSE = close
CURR_BARSTATE = barstate.isconfirmed

//------------------------------------------------------------------------------

// 1st candle back
C5_OPEN  =  open[1]
C5_HIGH  =  high[1]
C5_LOW   =   low[1]
C5_CLOSE = close[1]

[C5_BODY, C5_ISBULLISH, C5_ISBEARISH, C5_UPPERWICK, C5_LOWERWICK, C5_RANGE,
  C5_MIDPRICE, C5_BODYPOS, C5_ISTALL] =
  f_GenerateSecondary(C5_OPEN, C5_HIGH, C5_LOW, C5_CLOSE)

// 2nd candle back
C4_OPEN      =  open[2]
C4_HIGH      =  high[2]
C4_LOW       =   low[2]
C4_CLOSE     = close[2]
C4_BODY      = C5_BODY[1]
C4_ISBULLISH   = C5_ISBULLISH[1]
C4_ISBEARISH   = not C4_ISBULLISH
C4_UPPERWICK = C5_UPPERWICK[1]
C4_LOWERWICK = C5_LOWERWICK[1]
C4_RANGE     = C5_RANGE[1]
C4_MIDPRICE  = C5_MIDPRICE[1]
C4_BODYPOS   = C5_BODYPOS[1]
C4_ISTALL    = C5_ISTALL[1]
C4_HIGHEST   = math.max(C4_HIGH, C5_HIGH)
C4_LOWEST    = math.min(C4_LOW, C5_LOW)

// 3rd candle back
C3_OPEN      =  open[3]
C3_HIGH      =  high[3]
C3_LOW       =   low[3]
C3_CLOSE     = close[3]
C3_BODY      = C5_BODY[2]
C3_ISBULLISH   = C5_ISBULLISH[2]
C3_ISBEARISH   = not C3_ISBULLISH
C3_UPPERWICK = C5_UPPERWICK[2]
C3_LOWERWICK = C5_LOWERWICK[2]
C3_RANGE     = C5_RANGE[2]
C3_MIDPRICE  = C5_MIDPRICE[2]
C3_BODYPOS   = C5_BODYPOS[2]
C3_ISTALL    = C5_ISTALL[2]
C3_HIGHEST   = math.max(C3_HIGH, C4_HIGHEST)
C3_LOWEST    = math.min(C3_LOW, C4_LOWEST)

// 4th candle back
C2_OPEN      =  open[4]
C2_HIGH      =  high[4]
C2_LOW       =   low[4]
C2_CLOSE     = close[4]
C2_BODY      = C5_BODY[3]
C2_ISBULLISH   = C5_ISBULLISH[3]
C2_ISBEARISH   = not C2_ISBULLISH
C2_UPPERWICK = C5_UPPERWICK[3]
C2_LOWERWICK = C5_LOWERWICK[3]
C2_RANGE     = C5_RANGE[3]
C2_MIDPRICE  = C5_MIDPRICE[3]
C2_BODYPOS   = C5_BODYPOS[3]
C2_ISTALL    = C5_ISTALL[3]
C2_HIGHEST   = math.max(C2_HIGH, C3_HIGHEST)
C2_LOWEST    = math.min(C2_LOW, C3_LOWEST)

// 5th candle back
C1_OPEN      =  open[5]
C1_HIGH      =  high[5]
C1_LOW       =   low[5]
C1_CLOSE     = close[5]
C1_BODY      = C5_BODY[4]
C1_ISBULLISH   = C5_ISBULLISH[4]
C1_ISBEARISH   = not C1_ISBULLISH
C1_UPPERWICK = C5_UPPERWICK[4]
C1_LOWERWICK = C5_LOWERWICK[4]
C1_RANGE     = C5_RANGE[4]
C1_MIDPRICE  = C5_MIDPRICE[4]
C1_BODYPOS   = C5_BODYPOS[4]
C1_ISTALL    = C5_ISTALL[4]
C1_HIGHEST   = math.max(C1_HIGH, C2_HIGHEST)
C1_LOWEST    = math.min(C1_LOW, C2_LOWEST)

// Trend values setup
MA1 = f_GetMA(i_MA1Type, i_MA1Length, i_MA1Source)
MA2 = f_GetMA(i_MA2Type, i_MA2Length, i_MA2Source)

SHORTER_MA = i_MA1Length < i_MA2Length ? MA1 : MA2
LONGER_MA = i_MA1Length > i_MA2Length ? MA1 : MA2

CURR_TREND = i_MASetting == "MA 1" or 
  (i_MASetting == "BOTH" and i_MA1Length == i_MA2Length) or 
  (i_MA1Type == "Symmetrical" and i_MA2Type == "Symmetrical") ? MA1 : 
  i_MASetting == "MA 2" ? MA2 : na

// Grab trend for each of the candle patterns. These global variables will 
// indicate the trend at the candle prior to the first candle in each of the 
// candlestick patterns identified based upon their length. 
//
// IE: If the candlestick pattern is 3 candles, the fourth candle's i_TrendPrice
// will be compared against the current moving average. If above, it's in an 
// uptrend; if below, it's in a downtrend. 
IN_UPTREND1 = i_MASetting != "BOTH" or i_MA1Length == i_MA2Length or
  (i_MA1Type == "Symmetrical" and i_MA2Type == "Symmetrical" and 
  i_MASetting == "BOTH") ? i_TrendPrice[2] > CURR_TREND[2] : 
  SHORTER_MA[2] > LONGER_MA[2]
IN_DOWNTREND1 = not IN_UPTREND1

IN_UPTREND2 = IN_UPTREND1[1]
IN_DOWNTREND2 = not IN_UPTREND2

IN_UPTREND3 = IN_UPTREND1[2]
IN_DOWNTREND3 = not IN_UPTREND3

IN_UPTREND4 = IN_UPTREND1[3]
IN_DOWNTREND4 = not IN_UPTREND4

IN_UPTREND5 = IN_UPTREND1[4]
IN_DOWNTREND5 = not IN_UPTREND5

// Request.security calls for determining partitioning
BARINDREF = request.security(syminfo.tickerid, i_PartRes, bar_index)
BARINDTERN = BARINDREF > i_PartRefLen ? i_PartRefLen : BARINDREF + 1

HIGHREF = request.security(syminfo.tickerid, i_PartRes, 
  ta.highest(high, BARINDTERN))
LOWREF = request.security(syminfo.tickerid, i_PartRes, 
  ta.lowest(low, BARINDTERN))
UPPERSECTION = ((HIGHREF - LOWREF) * (i_PartUpperLim / 100)) + LOWREF
LOWERSECTION = ((HIGHREF - LOWREF) * (i_PartLowerLim / 100)) + LOWREF

// These plot/fill calls are kept here to avoid confusion with the relevant
// global vars associated with each.
REFLINE = plot(series = i_PartBGEnabled ? HIGHREF : na, 
  title = "High Ref Line", color = COLOR_INVIS, editable = false)
UPPERLINE = plot(series = i_PartBGEnabled ? UPPERSECTION : na, 
  title = "Upper Limit Line", color = COLOR_INVIS, editable = false)
LOWERLINE = plot(series = i_PartBGEnabled ? LOWERSECTION : na, 
  title = "Lower Limit Line", color = COLOR_INVIS, editable = false)
PSEUDOZEROLINE = plot(series = i_PartBGEnabled ? LOWREF : na, 
  title = "Zero Line", color = COLOR_INVIS, editable = false)

fill(plot1 = REFLINE, plot2 = UPPERLINE, color = UPPER_COLOR_MOD, 
  editable = false)
fill(plot1 = UPPERLINE, plot2 = LOWERLINE, color = MIDDLE_COLOR_MOD, 
  editable = false)
fill(plot1 = LOWERLINE, plot2 = PSEUDOZEROLINE, color = LOWER_COLOR_MOD, 
  editable = false)

// Use historical reference when finding the partition the pattern was in from
// the size of the pattern itself.
PRICE_PARTITION1 = C4_CLOSE >= UPPERSECTION[1] ? 0 : 
  C4_CLOSE < UPPERSECTION[1] and C4_CLOSE > LOWERSECTION[1] ? 1 : 2
PRICE_PARTITION2 = PRICE_PARTITION1[1]
PRICE_PARTITION3 = PRICE_PARTITION1[2]
PRICE_PARTITION4 = PRICE_PARTITION1[3]
PRICE_PARTITION5 = PRICE_PARTITION1[4]

//--------------------- CONSTANT DEPENDENT HELPER FUNCTIONS --------------------

// (BREAKOUT mode) BarColTup Contains (BCC) is an out-of-place helper function 
// which determines if the two values supplied to it are contained in the 
// BAR_COLOR_VALS array of barColTup objects. If the array contains a barColTup 
// object which has the same offset provided to this function, all number of 
// candles values below the barColTup's numCandles field with that offset will 
// yield true. This is for patterns which break out, as the offset corresponds 
// to the number of candles required for the pattern to break out, and the 
// numCandles field corresponds to the number of candles in the pattern that had 
// broken out. This will back paint patterns from the Processing color to the 
// generated color that pattern should be colored based on prior performance.
//
// @params:
//      _offset - The number of candles back being checked for back painting.
//  _numCandles - The number of candles the pattern must be [below/equal to]
//              to paint.
//
// @return:
//    bool - true if the BAR_COLOR_VALS global array contains a matching offset 
//         and greater numCandles value than what was provided to this function 
//         (back paint). false otherwise (no back paint)
//
f_BCContains(int _offset, int _numCandles) =>
    size = array.size(BAR_COLOR_VALS)
    retVal = false

    if size != 0
        for i = 0 to size - 1
            barColV = array.get(BAR_COLOR_VALS, i)
            offsetToComp = barColV.offset == _offset
            numCandlesToComp = barColV.numCandles >= _numCandles

            if offsetToComp and numCandlesToComp
                retVal := true
                break

        retVal
    else
        retVal

// (BREAKOUT mode) Confirm Pat is a simple helper function which determines if a 
// pattern has broken out in either direction based on the current close.
//
// @param:
//    patternObj _pat - Pattern object which provides the information needed to 
//                    determine thresholds for breakout directions.
f_ConfirmPat(patternObj _pat) =>
    int confirm = 0
    
    if CURR_CLOSE >= _pat.upTarget
        confirm := 1
    
    if CURR_CLOSE <= _pat.downTarget
        confirm := -1

    confirm

// Get RetArr From Pat is a helper function which will grab the associated return 
// array from the matrix that corresponds to the pattern provided to this 
// function.
//
// @param:
//    patternObj _pat - The pattern to grab the return array for.
//
// @return:
//    returnArray - The UDT which contains all information related to the returns 
//                of the pattern provided to this function.
//
f_GetRetArrFromPat(patternObj _pat) => 
    matRowPos = _pat.ID
    matColPos = _pat.part + 
      (i_DetectionType == "CLASSIC" or _pat.breakoutDir == 1 ? 0 : 3)

    retArr = matrix.get(PERCENT_RETURNS, matRowPos, matColPos)

    retArr

// GrabColor is a helper function which will be used to grab or generate all
// regular or gradient colors to be used by the patterns recognized in this
// script. It handles the cases of hard-limiting, adaptive coloration, and even
// the Not-Enough-Information color returns for patterns. 
//
// @inputs:
//    i_MinRetsNeeded - Compares if the returnArray provided has had the 
//                    sufficient number of occurrences to generate a color
//                    for that pattern.
//         i_StatsPOR - Determines which statistics metric will be used to 
//                    compare this returnArray value against in the color 
//                    generation process.
//      i_AdaptiveCol - Sets whether or not gradients will be used for 
//                    creating colors for all patterns.
//        i_HardLimit - Restricts the gradient to only be applied to 
//                    patterns between the 'Return Tolerance' inputs
//                    (i_PosRetTol/i_NegRetTol).
//
// @param:
//    returnArray - The returnArray for which a color is requested to 
//                be generated for based on its contents. 
//
// @return:
//    color - A newly generated color which is tailored to the inputs 
//          set by the user creating the color scheme for patterns to 
//          abide by.
//                    
f_GrabColor(returnArray _retArr) =>
    if _retArr.size < i_MinRetsNeeded
        ic_NEIColor

    else
        gradientVal = i_StatsPOR == "AVG" ? _retArr.avg : _retArr.median
        if i_AdaptiveCol
            if gradientVal > 0
                if i_HardLimit and gradientVal > MAXRETVAL
                    ic_BullColor
                else
                    color.from_gradient(gradientVal, 0, MAXRETVAL, ic_NeutralColor, 
                      ic_BullColor)
            else
                if i_HardLimit and gradientVal < MINRETVAL
                    ic_BearColor
                else
                    color.from_gradient(gradientVal, MINRETVAL, 0, ic_BearColor, 
                      ic_NeutralColor)
        else
            gradientVal >= i_PosRetTol ? ic_BullColor : 
              gradientVal <= i_NegRetTol ? ic_BearColor : ic_NeutralColor

// Initialize Label will generate a new label for a pattern and properly position 
// it based on certain attributes of the pattern provided to this function.
//
// It will grab the abbreviated version of the name for the pattern to be placed 
// onto the label. Place the label (pointing down) at the high if the pattern is 
// one which occurs in an uptrend (low, pointing up otherwise). Place the label 
// directly at the center of the pattern regardless of the number of candles. 
// 
// (BREAKOUT mode) Append a message for the number of candles remaining for a 
// pattern to confirm a breakout direction.
//
// @params:
//       int _ID - The ID number for that pattern a label is being created for.
//    int _trend - The trend direction needed for that pattern to occur.
//     int _size - The number of candles in the pattern.
//
// @return:
//    label - A newly generated label specially created for properly labelling
//          the occurrence of the pattern that was called with function.
//
f_InitializeLabel(int _ID, int _trend, int _size) =>
    labelAbbrev = array.get(PAT_ABBREV, _ID)

    if i_DetectionType == "BREAKOUT"
        plurality = i_MaxToBreakout != 1
        labelAbbrev += "\n\n(" + str.tostring(i_MaxToBreakout) + " candle" + 
          (plurality ? "s" : "") + " remain" + (plurality ? "" : "s") + ")"

    int xOffset = _size < 3 ? 1 : _size >= 3 and _size < 5 ? 2 : 3

    largestRange = math.max(C1_RANGE, C2_RANGE, C3_RANGE, C4_RANGE, C5_RANGE)
    highestHigh = math.max(C5_HIGH, C4_HIGH, C3_HIGH, C2_HIGH, C1_HIGH)
    lowestLow = math.min(C5_LOW, C4_LOW, C3_LOW, C2_LOW, C1_LOW)
    yPos = _trend == 1 ? highestHigh + (i_LabelNudge * largestRange) :
      lowestLow - (i_LabelNudge * largestRange)

    labelStyle = _trend == 1 ? label.style_label_down : label.style_label_up

    label patLabel = label.new(x = bar_index - xOffset, y = yPos, 
      text = labelAbbrev, color = color.white, style = labelStyle, 
      textcolor = color.black)

    patLabel

// Matrix Min/Max is a helper function which will reference all initialized 
// returnArray objects in the matrix and return the minimum and maximum 
// values of the user-specified value (average or median return) of the matrix.
// These values will then be used to determine a color gradient to be used for 
// patterns. 
//
// @inputs:
//    i_MinReturnsNeeded - Defines the minimum number of occurred patterns 
//                       necessary to include the user-specified value of that 
//                       pattern's return array as a part of the determination
//                       process.
//         i_GradientRef - The specified statistics value to be used in comparing
//                       all returns in the matrix.
//
// @returns:
//        minVal - The minimum value for the matrix.
//        maxVal - The maximum value for the matrix.
//
f_MatrixMinMax() =>
    pointListSize = array.size(RETURN_POSITIONS)
    float minVal = na
    float maxVal = na
    if pointListSize != 0
        for i = 0 to pointListSize - 1
            pointV = array.get(RETURN_POSITIONS, i)
            retRow = pointV.x
            retCol = pointV.y
            returnArr = matrix.get(PERCENT_RETURNS, retRow, retCol)
            if returnArr.size >= i_MinRetsNeeded
                valToAssign = i_StatsPOR == "AVG" ? returnArr.avg : returnArr.median
                minVal := na(minVal) or valToAssign < minVal ? valToAssign : minVal
                maxVal := na(maxVal) or valToAssign > maxVal ? valToAssign : maxVal
    [minVal, maxVal]

// (BREAKOUT mode) Update Remaining Text is a helper function which updates a 
// label's text with the proper number of candles remaining/informs the user 
// which type of breakout had occurred in a pattern when called.
//
// @params:
//    int _updateType - A value corresponding to the kind of update to be 
//                    performed on the _pat object. If 0, update the label with 
//                    the number of candles that remain. Otherwise, update the 
//                    label with the pattern's breakout direction (with a 
//                    'confirming status' if the current candle confirming the 
//                    pattern has not closed yet).
//    patternObj _pat - The identified pattern to update this label for.
// 
// @return:
//    void
f_UpdateRemainingText(int _updateType, patternObj _pat) =>
    if _updateType == 0
        candlesLeft = _pat.candlesLeft
        plurality = candlesLeft != 1

        newText = array.get(PAT_ABBREV, _pat.ID) 
          + "\n\n(" + str.tostring(candlesLeft) + " candle" 
          + (plurality ? "s" : "") + " remain" + (plurality ? "" : "s") + ")"

        label.set_text(_pat.patLabel, newText)
    else
        BODirString = _pat.breakoutDir == 1 ? "Upward" : "Downward"
        barstateText = CURR_BARSTATE ? "" : " [confirming]"

        newText = array.get(PAT_ABBREV, _pat.ID) + "\n\n(" + BODirString 
          + " Breakout" + barstateText + ")"

        label.set_text(_pat.patLabel, newText)

// Setup pattern is a major helper function which sets all the necessary 
// attributes of a patternObj UDT for identifying patterns. It sets the ID, 
// trend, size, partition (part), label (+ tooltip with actual name of the 
// pattern) and breakout targets for the pattern if BREAKOUT mode is enabled. It
// is also responsible for setting up the returnArrays in the matrix of returns 
// at the specified partition(s) (columns) so that they may be added onto when 
// percent return calculations are performed.
//
// @input:
//    i_DetectionType - Determines if secondary values are added onto the pattern 
//                    objects themselves as well as sets up both returnArray UDTs 
//                    in the matrix of returns. ('BREAKOUT' mode)
//
// @params:
//       int _ID - The ID of the pattern to set up.
//    int _trend - The trend direction of the pattern.
//     int _size - The number of candles the pattern is.
//
// @return:
//    patternObj - A newly created/specialized patternObj UDT which contains all
//               the necessary information to perform analysis on.
//
f_SetupPattern(int _ID, int _trend, int _size) =>
    patternObj pat = patternObj.new(ID = _ID, trend = _trend, size = _size)

    pat.part := switch _size
        1 => PRICE_PARTITION1
        2 => PRICE_PARTITION2
        3 => PRICE_PARTITION3
        4 => PRICE_PARTITION4
        5 => PRICE_PARTITION5

    // Setup both returnArrays in the matrix and secondary values
    if i_DetectionType == "BREAKOUT"
        retArr2 = matrix.get(PERCENT_RETURNS, pat.ID, (pat.part + 3))
        if na(retArr2)
            matrix.set(PERCENT_RETURNS, pat.ID, (pat.part + 3), 
              f_InitializeReturnArray())
            array.push(RETURN_POSITIONS, point.new(pat.ID, (pat.part + 3)))
        pat.candlesLeft := i_MaxToBreakout
        [upTgt, downTgt] = switch _size
            1 => [C5_HIGH, C5_LOW]
            2 => [C4_HIGHEST, C4_LOWEST]
            3 => [C3_HIGHEST, C3_LOWEST]
            4 => [C2_HIGHEST, C2_LOWEST]
            5 => [C1_HIGHEST, C1_LOWEST]
        pat.upTarget := upTgt
        pat.downTarget := downTgt

    // Setup returnArray if needed
    retArr = matrix.get(PERCENT_RETURNS, pat.ID, pat.part)
    if na(retArr)
        matrix.set(PERCENT_RETURNS, pat.ID, pat.part, f_InitializeReturnArray())
        array.push(RETURN_POSITIONS, point.new(pat.ID, pat.part))
        retArr := matrix.get(PERCENT_RETURNS, pat.ID, pat.part)

    // Create/modify label
    pat.patLabel := f_InitializeLabel(_ID, _trend, _size)
    f_AppendTooltip(pat, array.get(PAT_NAME, _ID))
    if i_DetectionType == "CLASSIC"
        f_DisplayStatsOnLabel(pat, retArr)
        
    pat

// (BREAKOUT mode) Run Pattern Confirmation is the function that processes the 
// confirmation status of patterns in BREKAOUT mode. Each pattern object that is 
// fed to this function will have the number of candles remaining to confirm 
// decremented by one as its breakout direction is tested over its lifespan. 
// Patterns which confirm will have their breakout direction set to the value 
// given from the f_ConfirmPat function, their labels updated, and colors set. 
// Patterns which do not confirm and are no longer processing will have their 
// labels deleted and signaled to be removed from the queue which contains them.
// 
// @inputs:
//    i_CandleDetSet - Candle Detection setting, if Target Mode is enabled, 
//                   delete pattern labels on patterns which do not have a 
//                   matching ID, but process them normally (this will continue 
//                   to populate the matrix with returns of patterns enabled 
//                   with the switchboard).
//   i_MaxToBreakout - More for calculation purposes, this input used here will 
//                   help determine the number of candles a pattern had required 
//                   to confirm.
//
// @param:
//    patternObj _pat - The pattern to process for confirmation.
//
// @return:
//    bool - true If the pattern supplied has confirmed in a breakout direction 
//         OR the number of candles which remains for this pattern is 0 (kick 
//         from queue); false if this pattern is still processing (candles 
//         remain, no breakout yet).
//
f_RunPatternConfirmation(patternObj _pat) =>
    _pat.candlesLeft := CURR_BARSTATE ? _pat.candlesLeft - 1 : _pat.candlesLeft
    barstateOffset = CURR_BARSTATE ? 0 : 1
    bool noPaint = false

    if i_CandleDetSet == "TARGET MODE" and _pat.ID != f_GetIDFromTarget()
        noPaint := true
        label.delete(_pat.patLabel)
        _pat.labelTooltip := ""

    int breakoutDir = f_ConfirmPat(_pat)
    bool patConfirmed = breakoutDir != 0

    if patConfirmed
        neededToConf = i_MaxToBreakout - _pat.candlesLeft + barstateOffset
        _pat.confOffset := neededToConf
        _pat.breakoutDir := breakoutDir

        retArr = f_GetRetArrFromPat(_pat)

        if not (noPaint or _pat.overridden)
            f_UpdateRemainingText(1, _pat)
            array.push(BAR_COLOR_VALS, barColTup.new(_pat.confOffset, _pat.size))
            array.set(CANDLE_COLOR_ARR, _pat.confOffset - 1, f_GrabColor(retArr))

        true
    else
        if _pat.candlesLeft == 0 and CURR_BARSTATE
            label.delete(_pat.patLabel)
            _pat.labelTooltip := ""
            true
        else
            if not CURR_BARSTATE
                currColPos = i_MaxToBreakout - _pat.candlesLeft + barstateOffset
                if not (noPaint or _pat.overridden)
                    f_UpdateRemainingText(0, _pat)
                    array.push(BAR_COLOR_VALS, barColTup.new(currColPos, _pat.size))
                    array.set(CANDLE_COLOR_ARR, currColPos - 1, ic_ProcessingCol)

            false

// ╔═══════════════════════════════════════════════════════════════════════════╗
// ║                  __                _          __   ___   _                ║
// ║                 / /   ____  ____ _(_)____   _/_/  <  /  | |               ║
// ║                / /   / __ \/ __ `/ / ___/  / /    / /   / /               ║
// ║               / /___/ /_/ / /_/ / / /__   / /    / /   / /                ║
// ║              /_____/\____/\__, /_/\___/  / /    /_/  _/_/                 ║
// ║                          /____/          |_|        /_/  + Specials       ║
// ║                                                                           ║
// ║ This section will be dedicated to identifying all one-candle patterns as  ║
// ║ well as some special patterns which required either a Doji or Hammer as a ║
// ║ part of that pattern. Unlike the other logic sections which follow, this  ║
// ║ section will not be in alphabetical order.                                ║
// ║                                                                           ║
// ║ It recognizes 23 patterns across 3 methods:                               ║
// ║    HammerPat (6)                                                          ║
// ║    LongDay (White and Black Long Days)                                    ║
// ║    DojiPat (15)                                                           ║
//#region                                                                      ║
// ║ Each Function details the patterns they will recognize so I will not      ║
// ║ provide them here.                                                        ║
// ║                                                                           ║
// ║ To other PineCoders: up until this point, all functions declare their     ║
// ║ returns using the @return tag. Most functions in these sections           ║
// ║ (LOG1/2/3/4/5) do not do this, as it became extremely repetitive. Every   ║
// ║ function which performs the needed true/false statements in determining   ║
// ║ if a candle pattern has appeared will have the following return:          ║
// ║                                                                           ║
// ║ @return:                                                                  ║
// ║    patternObj - An object which represents a pattern containing the       ║
// ║               the necessary information for processing returns (and in    ║
// ║               BREAKOUT mode, the pattern itself). 'na' if the pattern's   ║
// ║               conditions have not been met.                               ║
//#endregion                                                                   ║
// ╚XLOG1══════════════════════════════════════════════════════════════════════╝

// This function will check if the current candle is a hammer in both a single
// and two-candle context. It identifies 6 patterns:
//
//      - Shooting Star (2 Lines)[2]    - Hanging Man [1]
//      - Hammer Inverted [2]           - Takuri Line [1]
//      - Shooting Star [1]             - Regular Hammers [1]
//
//                  Ex:
//
//                                   | (usedWick)
//                                   |
//                                   ▄ (cBody)
//                                   ' (discardedWick)
//
// @inputs: 
//      i_HammerTol - tolerance for how large the [discardedWick] may be in
//                  relation to the size of [cBody].
//
f_IsHammerPat() =>
    // Set up calculations
    highOrLow = C5_BODYPOS > 0.5
    [discardedWick, usedWick] = if highOrLow 
        [C5_UPPERWICK, C5_LOWERWICK]
    else
        [C5_LOWERWICK, C5_UPPERWICK]
    
    multiplier = usedWick / math.abs(C5_BODY)
    ishammer = discardedWick / math.abs(C5_BODY) <= i_HammerTol

    if ishammer
        takuri = i_TLS and multiplier >= 3.0 and highOrLow and IN_DOWNTREND1 
        regular = i_RegHS and multiplier >= 2.0 and not takuri and IN_DOWNTREND1 

        hangingMan = i_HMS and IN_UPTREND1 and highOrLow and multiplier >= 2.0 

        // inverted hammer types
        shootingStar = i_ShooS and IN_UPTREND1 and not highOrLow and 
          multiplier >= 2.0
        shooStar2L = i_SS2LS and IN_UPTREND2 and not highOrLow and 
          multiplier >= 3.0 and C4_ISBULLISH
        hammerInverted = i_HIS and IN_DOWNTREND2 and C4_ISBEARISH and C4_ISTALL 
          and not highOrLow and multiplier >= 2.0

        // Setup pattern switch block
        switch
            shooStar2L => f_SetupPattern(38, 1, 2)
            hammerInverted => f_SetupPattern(26, -1, 2)
            shootingStar => f_SetupPattern(1, 1, 1)
            hangingMan => f_SetupPattern(2, 1, 1)
            takuri => f_SetupPattern(3, -1, 1)
            regular => f_SetupPattern(0, -1, 1)
            => na
    else
        na

// Returns whether or not this candle is a 'Long Day'; it is at least 3x the 
// average range/body height of the previous [i_TCSample] candles.
// 
// @input:
//     i_TCSetting - Sets which value of the candle to test against the sampled
//                 value in picking out White/Black Long Days.
//
f_IsLongDay() =>
    avgBodyCandle = f_TallCandlePrevAvg()
    
    cValToComp = if i_TCSetting == "RANGE"
        C5_RANGE 
    else
        C5_BODY

    validSize = math.abs(cValToComp) >= avgBodyCandle * 3

    if validSize
        noTrendVal = IN_UPTREND1 ? 1 : -1

        C5_ISBULLISH and i_WLDS ? f_SetupPattern(4, noTrendVal, 1) : 
          C5_ISBEARISH and i_BLDS ? f_SetupPattern(5, noTrendVal, 1) : na
    else
        na

// Returns the average wick size of the previous [DojiWickSam] candles (separates 
// upper and lower wicks).
//
// @inputs:
//    i_DojiWickBase - RANGE/WICKS, If "RANGE" then the average proportion of the 
//                   high-low range the upper and lower wicks will be used for 
//                   comparisons. If "WICKS" then the average size of the wicks
//                   themselves will be used instead.
//     i_DojiWickSam - The number of candles back to sample for the average 
//                   size of the wicks.
//
// @return:
//      [upperWAvg, lowerWAvg] - A tuple containing a calculated average of 
//                             previous wicks to be used for comparison.
//
// @dependencies:
//      (LOG)[1]f_IsDojiPat (below)
//
f_GetWickAverages() =>
    upperWVal = C4_UPPERWICK / (i_DojiWickBase == "RANGE" ? C5_RANGE : 1)
    lowerWVal = C4_LOWERWICK / (i_DojiWickBase == "RANGE" ? C5_RANGE : 1)

    upperWAvg = math.sum(upperWVal, i_DojiWickSam) / i_DojiWickSam
    lowerWAvg = math.sum(lowerWVal, i_DojiWickSam) / i_DojiWickSam
    
    [upperWAvg, lowerWAvg]

// Returns if the previous candle meets any of the requirements for numerous 
// Doji patterns ranging from 1 to 3 candles. This function recognizes 15 
// patterns:
//    
//    - Bearish Tri-Star [3]        - Gravestone Dojis [1]
//    - Bullish Tri-Star [3]        - Dragonfly Dojis [1]
//    - Bullish Harami Cross [2]    - Rickshaw Man Dojis [1]
//    - Bearish Harami Cross [2]    - Long-Legged Dojis [1]
//    - Bearish Doji Star [2]       - Regular Dojis [1] (Ignores Trend)
//    - Bullish Doji Star [2]       - Southern Dojis [1]
//    - Gapping Down Doji [1]       - Northern Dojis [1]
//    - Gapping Up Doji [1]
//
// @inputs:
//              i_DojiTol - tolerance for the relative size C5_BODY / C5_RANGE
//                        may be.
//         i_DojiWickBase - Changes calculation of doji wick proportions based
//                        on either the doji wicks' percentage of the range or 
//                        by measuring the size of the wicks themselves.
//                 i_DLWT - Tolerance for how large the wicks are required to 
//                        be compared to the average of the previous 
//                        [i_DojiWickSam] candles in order to be considered 
//                        "long" for Gravestone, Dragonfly, Long-Legged and 
//                        Rickshaw Man dojis.
//           i_RSMBodyTol - Tolerance for the difference the center of the body
//                        of a Rickshaw man may be to the center of the range.
//         i_DojiTallWick - Optional behavior for how the wicks would be 
//                        factored in for the validity of long-legged doji 
//                        variants.
//        i_GS_DFDojiShad - Boolean value for determining if small shadows will
//                        be allowed in a Gravestone/Dragonfly doji.
//       i_GS_DFDojiSSize - Tolerance for how large the shadows may be for a 
//                        Gravestone/Dragonfly doji (i_GS_DFDojiShad req'd).
//       i_SimplifyNSDoji - Boolean value for simplifying the northern/southern
//                        dojis into standard doji recognition (ignores trend 
//                        direction).
//      
f_IsDojiPat() =>
    // Find % of range body takes up
    cBodyPercent = math.abs(C5_BODY) / C5_RANGE
    dojiBodyReq = cBodyPercent <= i_DojiTol

    if dojiBodyReq
      // 1 Candle Patterns
        cUpperWickVal = C5_UPPERWICK / (i_DojiWickBase == "RANGE" ? C5_RANGE : 1)
        cLowerWickVal = C5_LOWERWICK / (i_DojiWickBase == "RANGE" ? C5_RANGE : 1)

        // Check if within user-defined tolerance
        [upperWAvg, lowerWAvg] = f_GetWickAverages()

        upperWAvg *= i_DLWT
        lowerWAvg *= i_DLWT

        [upperVal, lowerVal] = f_GetHigherLowerTuple(C5_OPEN, C5_CLOSE)

        [RSMPosUpper, RSMPosLower] = 
          f_GetTolerancedValueTuple(0.5, 1, i_RSMBodyTol)
    
        gappingDown = i_GDDS and IN_DOWNTREND1 and C5_HIGH < C4_LOW
        gappingUp = i_GUDS and IN_UPTREND1 and C5_LOW > C4_HIGH

        longLegged = i_LLDS and i_DojiTallWick == "ONE" ? 
          cUpperWickVal >= upperWAvg or cLowerWickVal >= lowerWAvg : 
          i_DojiTallWick == "BOTH" ? 
          cUpperWickVal >= upperWAvg and cLowerWickVal >= lowerWAvg :
          ((cLowerWickVal + cUpperWickVal) / 2) >= ((upperWAvg + lowerWAvg) / 2)

        gravestone = i_GSS and cUpperWickVal >= upperWAvg and
          i_GS_DFDojiShad ? cLowerWickVal / C5_RANGE <= i_GS_DFDojiSSize : 
          C5_LOW == lowerVal
          
        dragonfly = i_DFS and cLowerWickVal >= lowerWAvg 
          and i_GS_DFDojiShad ? C5_UPPERWICK / C5_RANGE <= i_GS_DFDojiSSize : 
          C5_HIGH == upperVal
          
        rickshawMan = i_RSMS and longLegged and 
          (C5_BODYPOS >= RSMPosLower and C5_BODYPOS <= RSMPosUpper)
          
        regDoji = i_SimplifyNSDoji and i_DS

        northDoji = i_NDS and IN_UPTREND1
        
        southDoji = i_SDS and IN_DOWNTREND1

        noTrendVal = IN_UPTREND1 ? 1 : -1

        // 2 Candle Patterns req' Dojis
        // These and the 3 candles patterns are done here to restrict special
        // dojis from being used as a part of those patterns
        bearishSetup = IN_UPTREND2 and C4_ISTALL and C4_ISBULLISH 
          and not longLegged
        bullishSetup = IN_DOWNTREND2 and C4_ISTALL and C4_ISBEARISH 
          and not longLegged

        secondWithinBody = f_WithinVals(C5_HIGH, C4_LOW, C4_HIGH) 
          and f_WithinVals(C5_LOW, C4_LOW, C4_HIGH)

        bearishDojiStar = i_BEDSS and bearishSetup and C5_LOW > C4_CLOSE
        bullishDojiStar = i_BUDSS and bullishSetup and C5_HIGH < C4_CLOSE

        bearishHCross = i_BEHCS and bearishSetup and secondWithinBody
        bullishHCross = i_BUHCS and bullishSetup and secondWithinBody

        any2CPats = 
          bearishDojiStar or bullishDojiStar or bearishHCross or bullishHCross

        // 3-Candle Tri-Star Doji pats
        prevPat1 = TEST_VAL[2]
        prevPat2 = TEST_VAL[1]
        c1ValidDoji = false
        c2ValidDoji = false

        if not na(prevPat1) and not na(prevPat2)
            c1ValidDoji := array.includes(DOJI_VALID_COMPS, prevPat1.ID)
            c2ValidDoji := array.includes(DOJI_VALID_COMPS, prevPat2.ID)

        bearishTriStar = false
        bullishTriStar = false

        if c1ValidDoji and c2ValidDoji and not (longLegged or dragonfly or gravestone)
            [c1Higher, c1Lower] = f_GetHigherLowerTuple(C3_OPEN, C3_CLOSE)
            [c2Higher, c2Lower] = f_GetHigherLowerTuple(C4_OPEN, C4_CLOSE)
            [c3Higher, c3Lower] = f_GetHigherLowerTuple(C5_OPEN, C5_CLOSE)
    
            bearishTriStar := i_BETSS and IN_UPTREND3 and c2Lower > c1Higher and 
              c2Lower > c3Higher
      
            bullishTriStar := i_BUTSS and IN_DOWNTREND3 and c2Higher < c1Lower and
              c2Higher < c3Lower

        // While most patterns are in alphabetical order, Harami Crosses take
        // precedent over Doji Stars since they are more specialized (entire candle
        // needs to be within the body of the previous).
        switch
            bearishTriStar => f_SetupPattern(71, 1, 3)
            bullishTriStar => f_SetupPattern(72, -1, 3)
            bullishHCross => f_SetupPattern(23, -1, 2)
            bearishHCross => f_SetupPattern(25, 1, 2)
            bearishDojiStar => f_SetupPattern(20, 1, 2)
            bullishDojiStar => f_SetupPattern(21, -1, 2)
            gappingDown => f_SetupPattern(6, -1, 1)
            gappingUp => f_SetupPattern(14, 1, 1)
            gravestone => f_SetupPattern(13, noTrendVal, 1)
            dragonfly => f_SetupPattern(12, noTrendVal, 1)
            rickshawMan => f_SetupPattern(11, noTrendVal, 1)
            longLegged => f_SetupPattern(10, noTrendVal, 1)
            regDoji => f_SetupPattern(9, noTrendVal, 1)
            southDoji => f_SetupPattern(7, -1, 1)
            northDoji => f_SetupPattern(8, 1, 1)
            // Executes under certain switch conditions
            => na
    else
        na

// ╔═══════════════════════════════════════════════════════════════════════════╗
// ║                   __             _        __  ___    _                    ║
// ║                  / /  ___  ___ _(_)___  _/_/ |_  |  | |                   ║
// ║                 / /__/ _ \/ _ `/ / __/ / /  / __/   / /                   ║
// ║                /____/\___/\_, /_/\__/ / /  /____/ _/_/                    ║
// ║                          /___/        |_|        /_/                      ║
// ║                                                                           ║
// ║ This section will include 26 2-candle patterns recognized by 14 methods:  ║
// ║    2C Engulfing:                                                          ║
// ║        - Bullish Engulfing         - Bearish Engulfing                    ║
// ║        - Last Engulfing Bottom     - Last Engulfing Top                   ║
// ║    D(ark) C(loud) C(over)                                                 ║
// ║    Harami (Bullish, Bearish)                                              ║
// ║    Homing Pige(on)                                                        ║
// ║    Kicking (Bullish and Bearish)                                          ║
// ║    Lines (Meeting and Separating [with Bullish/Bearish variants of each]) ║
// ║    Match(ing) Low                                                         ║
// ║    Neck (In and On)                                                       ║
// ║    Piercing                                                               ║
// ║    Stomach (Above and Below)                                              ║
// ║    Thrusting                                                              ║
// ║    Tweezer (Bottom and Top)                                               ║
// ║    Two Black Gap(ping)                                                    ║
// ║    Window (Falling and Rising)                                            ║
// ║                                                                           ║
// ╚XLOG2══════════════════════════════════════════════════════════════════════╝

// Returns if the previous two candles match the requirements for one of four
// Engulfing patterns:
//      - Bearish Engulfing         - Bullish Engulfing
//      - Last Engulfing Bottom     - Last Engulfing Top
//
//              Ex: (Last Engulfing Bottom)
//
//
//                             ░
//                               ░    ▲   ▼
//                                 ░  ▄   █
//                                    █   █   
//                                        ▀    
//                                              
//
f_Is2CEngulfing() =>
    c2Engulfing = if i_EngulfSet == "BODY" 
        f_Engulfs(C5_OPEN, C5_CLOSE, C4_OPEN, C4_CLOSE)
    else if i_EngulfSet == "RANGE"
        f_Engulfs(C5_HIGH, C5_LOW, C4_HIGH, C4_LOW)
    else
        f_Engulfs(C5_OPEN, C5_CLOSE, C4_OPEN, C4_CLOSE) or 
          f_Engulfs(C5_HIGH, C5_LOW, C4_HIGH, C4_LOW)
    
    c1Bearish = C4_ISBULLISH
    c2Bearish = C5_ISBEARISH and c2Engulfing
    
    bearishEng = i_BEES and c1Bearish and c2Bearish and IN_UPTREND2
    
    lastEngBot = i_LEBS and c1Bearish and c2Bearish and IN_DOWNTREND2
    
    c1Bullish = C4_ISBEARISH
    c2Bullish = C5_ISBULLISH and c2Engulfing
    
    bullishEng = i_BUES and c1Bullish and c2Bullish and IN_DOWNTREND2
    
    lastEngTop = i_LETS and c1Bullish and c2Bullish and IN_UPTREND2
    
    switch
        bearishEng => f_SetupPattern(15, 1, 2)
        lastEngBot => f_SetupPattern(16, -1, 2)
        bullishEng => f_SetupPattern(17, -1, 2)
        lastEngTop => f_SetupPattern(18, 1, 2)
        => na

// Returns if the previous two candles match the requirements for a Dark Cloud
// Cover pattern. 
//
//                  Ex:
//                                       ▼
//                                    ▲..▄.... c2 opens above c1 high
//                                    |  █
//                                    █__█____ c2 closes below c1 midpoint(body)
//                                 ░  █  ▀   
//                               ░    '  |    
//                             ░               
//
f_IsDCC() =>
    if i_DCCS and IN_UPTREND2
        c1valid = C4_ISBULLISH and C4_ISTALL
        c2valid = C5_ISBEARISH and C5_OPEN > C4_HIGH and C5_CLOSE < C4_MIDPRICE 
          and C5_CLOSE > C4_OPEN
            
        c1valid and c2valid ? f_SetupPattern(19, 1, 2) : na
    else
        na

// Returns if the previous two candles matched one of the two non-cross variants
// of the Harami pattern.
// 
//                  Ex: (standard bullish)
//                                      
//                                     
//                             ░               
//                               ░    ▼  ▲   
//                                 ░  █  ▄---------- 
//                                    █  ▀---------┴-- c2 within c1 body
//
f_IsHarami() =>
    if C4_ISTALL
        c2WithinBody = f_WithinVals(C5_OPEN, C4_HIGH, C4_LOW) 
          and f_WithinVals(C5_CLOSE, C4_HIGH, C4_LOW)

        xorBody = not ((C5_OPEN == C4_CLOSE and C5_CLOSE == C4_OPEN) 
          or (C5_OPEN == C4_OPEN and C5_CLOSE == C4_CLOSE))

        bearish = i_BEHS and C4_ISBULLISH and C5_ISBEARISH and IN_UPTREND2 
          and c2WithinBody and xorBody
        bullish = i_BUHS and C4_ISBEARISH and C5_ISBULLISH and IN_DOWNTREND2 
          and c2WithinBody and xorBody

        bullish ? f_SetupPattern(22, -1, 2) : 
          bearish ? f_SetupPattern(24, 1, 2) : na
    else  
        na

// Returns if the previous two candles match the requirements for a Homing
// Pigeon pattern.
//
//                  Ex:  
//                                     
//                             ░              
//                               ░    ▼  ▼   
//                                 ░  █  ▄--------- 
//                                    █  ▀---------┴-- c2 within c1 body,
//                                                     both candles bearish
//                                            
f_IsHomingPige() => 
    if i_HPS and IN_DOWNTREND2
        
        c1AndC2Bearish = C4_ISBEARISH and C5_ISBEARISH

        c2WithinC1 = f_WithinVals(C5_OPEN, C4_OPEN, C4_CLOSE) 
          and f_WithinVals(C5_CLOSE, C4_OPEN, C4_CLOSE)

        C4_ISTALL and c1AndC2Bearish and c2WithinC1 and IN_DOWNTREND2 ? 
          f_SetupPattern(27, -1, 2) : na
    else
        na

// Returns if the previous two candles match the requirements for either the 
// Bullish or Bearish Kicking patterns. 
// 
//                  Ex: (Bullish)
//                                               
//                                        ▲    
//                 no prior trend         ▄    
//                    required        ▼___█____ 
//                                    █        bearish, then bullish a candle 
//                                    ▀        meeting at the opens
// 
// @inputs:
//      i_MaruType - Specifies which types of Marubozu candles will be 
//                 considered valid. If "exclusive" then only pure Marubozu 
//                 candles will be considered for these patterns. Otherwise the
//                 opening and closing variants will also be considered valid.
//    i_KickingGap - Forces there to be a gap between the two candles in each 
//                 pattern.
//
f_IsKicking() =>
    bothTall = C4_ISTALL and C5_ISTALL

    if bothTall
        validWhiteMaru = i_MaruType == "exclusive" ? 3 : 1
        validBlackMaru = i_MaruType == "exclusive" ? -3 : -1

        c1isMaru = f_IsMarubozu(C4_OPEN, C4_HIGH, C4_LOW, C4_CLOSE)
        c2isMaru = f_IsMarubozu(C5_OPEN, C5_HIGH, C5_LOW, C5_CLOSE)

        bearish = i_BEKS and C4_ISBULLISH and c1isMaru >= validWhiteMaru and C5_ISBEARISH
          and c2isMaru <= validBlackMaru and (not i_KickingGap or C5_HIGH < C4_LOW)

        bullish = i_BUKS and C4_ISBEARISH and c1isMaru <= validBlackMaru and C5_ISBULLISH
          and c2isMaru >= validWhiteMaru and (not i_KickingGap or C5_LOW > C4_HIGH)

        trendVal = IN_UPTREND2 ? 1 : -1

        bearish ? f_SetupPattern(28, trendVal, 2) : 
          bullish ? f_SetupPattern(29, trendVal, 2) : na
    else
        na

// Returns if the previous two candles match any of the four meeting/separating
// lines patterns.
// 
//                  Ex: (Bullish Meeting Lines)
//
//
//                             ░              
//                               ░    ▼   ▲      
//                                 ░  ▄        
//                                    █       
//                                    ▀---▄------- Both tall candles meet near
//                                        █        the closes.
//                                        ▀
//
// @input:
//      i_LinesTol - Tolerance for how close each of the opens and closes of the
//                 respective patterns may be to be considered valid.
//
f_IsLines() =>
    bothTall = C4_ISTALL and C5_ISTALL

    if bothTall

        [openHigh, openLow] = 
          f_GetTolerancedValueTuple(C4_OPEN, C4_RANGE, i_LinesTol)
        [closeHigh, closeLow] = 
          f_GetTolerancedValueTuple(C4_CLOSE, C4_RANGE, i_LinesTol)
    
        bearishColorScheme = C4_ISBULLISH and C5_ISBEARISH
        bullishColorScheme = C4_ISBEARISH and C5_ISBULLISH

        c2OpenValid = f_WithinVals(C5_OPEN, openHigh, openLow)
        c2CloseValid = f_WithinVals(C5_CLOSE, closeHigh, closeLow)

        bearSepLines = i_BESLS and bearishColorScheme and c2OpenValid 
          and IN_DOWNTREND2
        bearMeetLines = i_BEMLS and bearishColorScheme and c2CloseValid 
          and IN_UPTREND2
        bullMeetLines = i_BUMLS and bullishColorScheme and c2CloseValid 
          and IN_DOWNTREND2
        bullSepLines = i_BUSLS and bullishColorScheme and c2OpenValid 
          and IN_UPTREND2

        switch
            bearSepLines => f_SetupPattern(30, -1, 2)
            bearMeetLines => f_SetupPattern(31, 1, 2)
            bullSepLines => f_SetupPattern(32, 1, 2)
            bullMeetLines => f_SetupPattern(33, -1, 2)
            => na
    else
        na

// Returns if the previous two candles meet the requirements for a Matching Low
// pattern. While a little confusing, the Matching Low refers to the closes of 
// the previous two candles, instead of the lows of each.
//
//                  Ex:
//
//
//                             ░              
//                               ░    ▼   ▼   
//                                 ░  █   ▄
//                                    █___█__ ___ closes match (or are similar)
//                                    |   '    
//                                              
// @input: 
//      i_MatchLowTol - Tolerance for how close the previous two candles may 
//                    close in order for this pattern to be considered valid.
//                    (Accounts for high value assets. Default is set to 0 [must
//                    match exactly]).
//
f_IsMatchLow() =>
    if i_MLS and IN_DOWNTREND2
        c1c2Lengths = C4_ISTALL and not C5_ISTALL

        bothBearish = C4_ISBEARISH and C5_ISBEARISH

        // Mind the line wrapping here
        matching = 
          if i_MatchLowTol == 0 
            C4_CLOSE == C5_CLOSE
        else
            [higher, lower] = 
              f_GetTolerancedValueTuple(C4_CLOSE, C4_RANGE, i_MatchLowTol)
            f_WithinVals(C5_CLOSE, higher, lower)

        c1c2Lengths and bothBearish and matching ? f_SetupPattern(34, -1, 2) : na
    else
        na

// Returns if the previous two candles match either the On Neck or In Neck 
// patterns. 
//
//                  Ex: (In Neck)                                              
//
//
//                             ░              
//                               ░    ▼   ▲
//                                 ░  █   
//                                    █   ▄--------┬ c2 opens below c1 low, 
//                                    '---█--------  closes into c1 body
//                                             
//                                              
// @inputs:
//  i_OnNeckWickTol - A percentage of the high-low range of the first candle 
//                  that the lower wick may be to be considered valid.
//      i_OnNeckTol - A percentage of the size of the lower wick that the close 
//                  of the second candle may be within the close of the first
//                  and be considered valid.
//      i_InNeckTol - How far into the body that the close of C2 may be in order
//                  for it to be considered valid.
//                  
f_IsNeck() =>
    if IN_DOWNTREND2
        validSetup = C4_ISBEARISH and C5_ISBULLISH
        validCandleSizes = C4_ISTALL and not C5_ISTALL

        if validSetup and validCandleSizes
            validWick = C4_LOWERWICK / C4_RANGE <= i_OnNeckWickTol
            lowerC2CloseLim = C4_CLOSE - (C4_LOWERWICK * i_OnNeckTol) 

            onNeck = i_ONS and f_WithinVals(C5_CLOSE, C4_CLOSE, lowerC2CloseLim) 
              and validWick

            inNeckUpperClose = C4_CLOSE + (math.abs(C4_BODY) * i_InNeckTol)

            inNeck = i_INS and f_WithinVals(C5_CLOSE, C4_CLOSE, inNeckUpperClose) 
              and C5_OPEN < C4_LOW

            onNeck ? f_SetupPattern(35, -1, 2) : 
              inNeck ? f_SetupPattern(36, -1, 2) : na
        else
            na
    else
        na

// Returns if the previous two candles match a Piercing pattern.
//
//                  Ex:                                                         
//
//
//                             ░                
//                               ░    ▼   ▲   
//                                 ░  █---▄------- 
//                                    █   █       ├---- c2 opens below c1 low,
//                                    |___█______/      closes between c1 body
//                                        ▀             midpoint and open
//                   
f_IsPiercing() => 
    if i_PS and IN_DOWNTREND2
        pierce = C4_ISBEARISH and C5_ISBULLISH and C5_OPEN < C4_LOW
          and C5_CLOSE > C4_MIDPRICE and C5_CLOSE < C4_OPEN 
        
        pierce ? f_SetupPattern(37, -1, 2) : na
        
    else
        na

// Returns if the previous two candles match either the Above or Below the 
// Stomach patterns.
//
//                  Ex: (Above The Stomach)
//                                     
//
//                             ░      ▼   ▲     
//                               ░        █   
//                                 ░  █---▀--------- c2 opens above midpoint of
//                                    ▀            c1's body.
//
// @input: 
//      i_TallStom1 - Requires the first candle in each of these patterns to be 
//                  tall.
//      i_TallStom2 - Requires the second candle in each of these patterns to be
//                  tall.
//
f_IsStomach() => 
    c1ValidSize = i_TallStom1 ? C4_ISTALL : true
    c2ValidSize = i_TallStom2 ? C5_ISTALL : true
    
    belowSetupAndTrend = C4_ISBULLISH and C5_ISBEARISH and IN_UPTREND2
    aboveSetupAndTrend = C4_ISBEARISH and C5_ISBULLISH and IN_DOWNTREND2
    
    belowStom = i_BESS and c1ValidSize and c2ValidSize and belowSetupAndTrend 
      and C5_OPEN <= C4_MIDPRICE and C5_CLOSE <= C4_MIDPRICE
    
    aboveStom = i_ABSS and c1ValidSize and c2ValidSize and aboveSetupAndTrend
      and C5_OPEN >= C4_MIDPRICE and C5_CLOSE >= C4_MIDPRICE
      
    belowStom ? f_SetupPattern(39, 1, 2) : 
      aboveStom ? f_SetupPattern(40, -1, 2) : na

// Returns if the previous two candles match a Thrusting pattern.
// 
//                  Ex:
//
//
//                             ░      ▼   ▲      
//                               ░    ▄        
//                                 ░  █---▄-------┬--- c2 opens below low of c1,
//                                    ▀---█--------   closes near/below midpoint
//                                                    of c1 body.
//
// @input: 
//      i_ThrustTol - Specifies how close (below) the midpoint of C1's body that 
//                  the second candle may close in order for it to be considered
//                  valid.
//
f_IsThrusting() => 
    if i_TS and IN_DOWNTREND2
        validColorScheme = C4_ISBEARISH and C5_ISBULLISH

        lowerVal = (C4_MIDPRICE - (i_ThrustTol * (C4_BODY / 2)))

        opensBeLOW = C5_OPEN < C4_LOW

        opensBeLOW and validColorScheme 
          and f_WithinVals(C5_CLOSE, C4_MIDPRICE, lowerVal) ?
          f_SetupPattern(41, -1, 2) : na
    else
        na

// Returns if the previous two candles match either a Tweezers Top or Tweezers 
// Bottom patterns. Because this will recognize patterns of both alternating 
// color schemes, there will be 4 possible patterns that this function may find.
//
//                  Ex: (Tweezers Bottom)
//
//
//                             ░      ▼   ▲
//                               ░    ▲   ▼
//                                 ░  .   .
//                                    █   ▄    
//                                    ▀   █     
//  Lows match, colors must __________|___|      
//  alternate.                        
//
// @input: 
//      i_TweezerTol - Tolerance for how close the tops/bottoms may be (as a % 
//                   of the high/low values [respectively]).
//
f_IsTweezer() =>
    // 2 top/bottom configurations, 2 color schemes -> 4 patterns total.
    alternatingColors = (C4_ISBEARISH and C5_ISBULLISH) 
      or (C4_ISBULLISH and C5_ISBEARISH)

    if alternatingColors
        [bottomHigh, bottomLow] = 
          f_GetTolerancedValueTuple(C4_LOW, C4_RANGE, i_TweezerTol)
        [topHigh, topLow] = 
          f_GetTolerancedValueTuple(C4_HIGH, C4_RANGE, i_TweezerTol)

        bottom = i_TBS and f_WithinVals(C5_LOW, bottomHigh, bottomLow)
          and IN_DOWNTREND2

        top = i_TTS and f_WithinVals(C5_HIGH, topHigh, topLow)
          and IN_UPTREND2
        
        bottom ? f_SetupPattern(42, -1, 2) : top ? f_SetupPattern(43, 1, 2) : na

    else
        na

// Returns if the previous two candles match the Two Black Gapping pattern.
//
//                  Ex:                                                         
//
//
//                             ░              
//                               ░     
//                                 ░ 
//                                 '-------┬--- Pattern begins with a gap in a  
//                      ______________▼---▼     Downtrend
//                      -lower high---█---.    
//                                    ▀   █      
//                                               
f_IsTwoBlackGap() => 
    if i_2BGS and IN_DOWNTREND2
        isGapped = C3_LOW > C4_HIGH
        bothBearish = C4_ISBEARISH and C5_ISBEARISH
    
        isGapped and bothBearish and C5_HIGH < C4_HIGH ? 
          f_SetupPattern(44, -1, 2) : na
    else
        na
    
// Returns if the previous two candles match the Falling or Rising windows 
// patterns.
//
//                  Ex: (Falling)
//
//
//                             ░              
//                               ░     
//                                 ░  ▄
//                                    '--------┬-- gap in Downtrend 
//                                       .------
//                                       ▀    
//
f_IsWindow() =>
    falling = i_FWS and C4_LOW > C5_HIGH and IN_DOWNTREND2
    rising = i_RWS and C4_HIGH < C5_LOW and IN_UPTREND2

    falling ? f_SetupPattern(45, -1, 2) : rising ? f_SetupPattern(46, 1, 2) : na

// ╔═══════════════════════════════════════════════════════════════════════════╗
// ║                   __             _        __  ____   _                    ║
// ║                  / /  ___  ___ _(_)___  _/_/ |_  /  | |                   ║
// ║                 / /__/ _ \/ _ `/ / __/ / /  _/_ <   / /                   ║
// ║                /____/\___/\_, /_/\__/ / /  /____/ _/_/                    ║
// ║                          /___/        |_|        /_/                      ║
// ║                                                                           ║
// ║This section will include 27 3-candle patterns recognized by 13 methods:   ║
// ║    Abandoned Baby (Bullish and Bearish)                                   ║
// ║    Advance Block                                                          ║
// ║    Collapsing Doji                                                        ║
// ║    Deliberation                                                           ║
// ║    Gap 3 Methods (Downside and Upside)                                    ║
// ║    3 In And Out (Three Inside Down/Up and Three Outside Down/Up)          ║
// ║    S(ide) B(y) S(ide) (Bullish and Bearish)                               ║
// ║    Star (Evening and Morning with Doji variants)                          ║
// ║    Stick Sandwich                                                         ║
// ║    Tasuki Gap (Downside and Upside)                                       ║
// ║    Three                                                                  ║
// ║        - Identical Black Crows     - White Soldiers                       ║
// ║        - Black Crows               - Stars In The South                   ║
// ║    Two Crows (including Upside Gap)                                       ║
// ║    Unique 3 R(iver) B(ottom)                                              ║
// ║                                                                           ║
// ╚XLOG3══════════════════════════════════════════════════════════════════════╝

// Returns if the last three candles formed an Abandoned Baby pattern (bullish
// or bearish).
// 
//              Ex: (bearish)                        
//
//                                   ▲ ┴ ▼
//                                   │   
//                                   ▓   ▒   
//                                ░             
//                              ░              
//                            ░                 
//
f_IsAbandonedBaby() =>
    prevPat = TEST_VAL[1]

    prevCandleDoji = not na(prevPat) ? 
      array.includes(DOJI_VALID_COMPS, prevPat.ID) : false
    
    if prevCandleDoji
    
        bullishAB = i_BUABS and C3_ISBEARISH and C5_ISBULLISH 
          and C4_HIGH < C3_LOW and C4_HIGH < C5_LOW and IN_DOWNTREND3

        bearishAB = i_BEABS and C3_ISBULLISH and C5_ISBEARISH 
          and C4_LOW > C3_HIGH and C4_LOW > C5_HIGH and IN_UPTREND3

        bearishAB ? f_SetupPattern(47, 1, 3) : 
          bullishAB ? f_SetupPattern(48, -1, 3) : na
    else
        na

// Returns if the last three candles formed an Advance Block. An advance block 
// consists of three candles, where candles two and three each have taller wicks
// than the previous, and both open within the body of the previous.
//
//              Ex: 
//                                        ▲     
//                                     ▲  |   
//                                  ▲  |  |  
//                                  │  █  ▀  
//                                  ▓
//                               ░       
//                             ░           
//                           ░
//                                     
f_IsAdvanceBlock() =>
    if IN_UPTREND3 and i_ADVS
        allThreeBullish = C3_ISBULLISH and C4_ISBULLISH and C5_ISBULLISH
        if allThreeBullish
            c2valid = f_WithinVals(C4_OPEN, C3_OPEN, C3_CLOSE)
              and C4_UPPERWICK > C3_UPPERWICK
      
            c3valid = f_WithinVals(C5_OPEN, C4_OPEN, C4_CLOSE)
              and C5_UPPERWICK > C4_UPPERWICK

            c2valid and c3valid ? f_SetupPattern(49, 1, 3) : na
        else
            na
    else
        na

// Returns if the last three candles formed in a Collapsing Doji Star pattern.
// This pattern entails a bullish candle, followed by a Doji where the high of
// the doji is lower than the low of the first candle, and the low of the doji
// is higher than the high of the third candle. (Gap between the Doji and the
// other two candles).
//
//              Ex: 
//                                  ▲
//
//                                  ▓
//                                     
//                                     ┼  ▼
//                               ░            
//                             ░          ▒    
//                           ░            |     
//
//
f_IsCollapsingDoji() =>
    if i_COLS and IN_UPTREND3
        prevPat = TEST_VAL[1]
        c2ValidDoji = false
        if not na(prevPat)
            c2ValidDoji := array.includes(DOJI_VALID_COMPS, prevPat.ID)

        if c2ValidDoji
            c1valid = C3_ISBULLISH
            c2valid = C4_HIGH < C3_LOW
            c3valid = C5_ISBEARISH and C4_LOW > C5_HIGH

            c1valid and c2valid and c3valid ? f_SetupPattern(50, 1, 3) : na
        else
            na
    else
        na

// Returns if the previous three candles formed a Deliberation pattern. This
// pattern consists of three bullish candles in an upward price trend. The
// first two candles in the pattern are tall, where candle two opens within the
// body of candle one. Candle three is a small candle that gaps a short bit up
// on candle two.
//
//              Ex:         
//                                        ▲
//                                     ▲  ▄     
//                                  ▲         
//                                  |  ▓     
//                                  ▓  '
//                               ░  |      
//                             ░           
//                           ░
//
// @inputs:
//        i_DelibGapTol - This tolerance specifies how close to candle 2's close
//                      candle 3 must open. 
//        i_DelibC3Size - This tolerance specifies how large candle 3 may be in 
//                      relation to the sizes of candles 1 and 2.
//      i_ForceDelibGap - Forces a Deliberation to only be valid if the third 
//                      candle gaps the second.
// 
f_IsDeliberation() =>
    if IN_UPTREND3 and i_DELS
        [upperOpen, lowerOpen] = 
          f_GetTolerancedValueTuple(C4_CLOSE, C4_BODY, i_DelibGapTol)

        c5sizevalid = C5_BODY <= ((C3_BODY + C4_BODY) / 2) * i_DelibC3Size

        c4LowerBound = i_ForceDelibGap ? C4_CLOSE : lowerOpen 

        c1valid = C3_ISBULLISH and C3_ISTALL
        c2valid = C4_ISBULLISH and C4_ISTALL and C4_CLOSE > C3_CLOSE
        c3valid = C5_ISBULLISH and f_WithinVals(C5_OPEN, upperOpen, c4LowerBound) 
          and c5sizevalid

        c1valid and c2valid and c3valid ? f_SetupPattern(51, 1, 3) : na
    else
        na

// Returns if the previous three candles formed in a "Gap Three Methods" type of
// pattern. (Gap Three Methods downside and upside). A Gap Three Methods 
// consists of three candles where the gap between the first two is closed by the 
// third after it opens in the body of the previous and closes in the body of 
// the first.
//
//              Ex: (Downside Gap 3 Methods)
//
//                          
//                            ░                  
//                              ░    ▼         
//                                ░  ▄     ▲  
//                                   ▀  ▼  | 
//                                         ▓
//                                      ▓                                       
//
//
f_IsGap3Methods() =>
    firstTwoTall = C3_ISTALL and C4_ISTALL

    if firstTwoTall
        if IN_UPTREND3 and i_UG3MS
            c1uvalid = C3_ISBULLISH
            c2uvalid = C4_ISBULLISH and C4_LOW > C3_HIGH
            c3uvalid = C5_ISBEARISH and f_WithinVals(C5_OPEN, C4_OPEN, C4_CLOSE) 
              and f_WithinVals(C5_CLOSE, C3_OPEN, C3_CLOSE)

            c1uvalid and c2uvalid and c3uvalid ? f_SetupPattern(52, 1, 3) : na

        else if IN_DOWNTREND3 and i_DG3MS
            c1dvalid = C3_ISBEARISH
            c2dvalid = C4_ISBEARISH and C4_HIGH < C3_LOW
            c3dvalid = C5_ISBULLISH and f_WithinVals(C5_OPEN, C4_OPEN, C4_CLOSE) 
              and f_WithinVals(C5_CLOSE, C3_OPEN, C3_CLOSE)

            c1dvalid and c2dvalid and c3dvalid ? f_SetupPattern(53, -1, 3) : na

        else
            na
    else 
        na

// Returns one of five possible values that correspond to one (or none) of the 
// following four candlestick patterns: Three Outside Down, Three Inside Down,
// Three Outside Up, Three Inside Up. Since many of these patterns were pretty
// similar, I found it best to combine them into one singular function.
//
//              Ex: (Three Outside Up)
//
//                                              
//                            ░                  
//                              ░          ▲   
//                                ░  ▼  ▲  ▄  
//                                   ▄     
//                                      ▓     
//                                      
// 
f_Is3InAndOut() =>
    // Check for prior engulfing
    engulfsPrior = if i_EngulfSet == "BODY" 
        f_Engulfs(C4_OPEN, C4_CLOSE, C3_OPEN, C3_CLOSE)
    else if i_EngulfSet == "RANGE"
        f_Engulfs(C4_HIGH, C4_LOW, C3_HIGH, C3_LOW)
    else
        f_Engulfs(C4_OPEN, C4_CLOSE, C3_OPEN, C3_CLOSE) or 
          f_Engulfs(C4_HIGH, C4_LOW, C3_HIGH, C3_LOW)
        
    ThreeIEq = C4_CLOSE == C3_OPEN or C4_OPEN == C3_CLOSE
    
    ThreeIXOR = not ((C4_CLOSE == C3_OPEN and C4_OPEN == C3_CLOSE))

    if IN_UPTREND3
        validSetup = C3_ISBULLISH and C4_ISBEARISH and C5_ISBEARISH

        if validSetup
            threeOutDown = i_3ODS and engulfsPrior and C5_CLOSE < C4_CLOSE

            c2ThreeIDown = C4_CLOSE >= C3_OPEN and C4_OPEN <= C3_CLOSE 
              and ThreeIXOR
            c3ThreeIDown = C5_CLOSE < C4_CLOSE
            
            threeInDown = i_3IDS and C3_ISTALL and c2ThreeIDown and c3ThreeIDown

            threeOutDown ? f_SetupPattern(54, 1, 3) : 
              threeInDown ? f_SetupPattern(55, 1, 3) : na
        else
            na
    else
        validSetup = C3_ISBEARISH and C4_ISBULLISH and C5_ISBULLISH

        if validSetup
            c2ThreeIUp = C4_CLOSE <= C3_OPEN and C4_OPEN >= C3_CLOSE
              and ThreeIXOR
            c3ThreeIUp = C5_CLOSE > C4_CLOSE

            threeInUp = i_3IUS and C3_ISTALL and c2ThreeIUp and c3ThreeIUp

            threeOutUp = i_3OUS and engulfsPrior and C5_CLOSE > C4_CLOSE

            threeInUp ? f_SetupPattern(56, -1, 3) : 
              threeOutUp ? f_SetupPattern(57, -1, 3) : na

// Returns if the previous three candlesticks formed a Side-by-Side pattern.
// This method handles both Bullish and Bearish versions of the Side-By-Side 
// pattern. 
//
//                  Ex: (Bullish)
//
//                                     ▲  ▲     
//                                            
//                                  ▲  ▓  ▓  
//                                     
//                               ░  ▓
//                             ░           
//                           ░
// @input:
//      i_SBSTolerance - Tolerance for how different the final two candles may
//                     be from each other.
//
f_IsSBS() =>
    [openLow, openHigh] = 
      f_GetTolerancedValueTuple(C4_OPEN, C4_BODY, i_SBSTolerance)
    [closeLow, closeHigh] = 
      f_GetTolerancedValueTuple(C4_CLOSE, C4_BODY, i_SBSTolerance)
    
    SBSLastTwoSimilar = f_WithinVals(C5_OPEN, openLow, openHigh) and
      f_WithinVals(C5_CLOSE, closeLow, closeHigh)
      
    if SBSLastTwoSimilar

        if IN_DOWNTREND3 and i_BESBSS
            c2BearSBS = C4_ISBULLISH and C4_CLOSE < C3_CLOSE
            c3BearSBS = C5_ISBULLISH and C5_CLOSE < C3_CLOSE

            C3_ISBEARISH and c2BearSBS and c3BearSBS ? 
              f_SetupPattern(58, -1, 3) : na

        else if IN_UPTREND3 and i_BUSBSS
            c2BullSBS = C4_ISBULLISH and C4_OPEN > C3_CLOSE
            c3BullSBS = C5_ISBULLISH and C5_OPEN > C3_CLOSE

            C3_ISBULLISH and c2BullSBS and c3BullSBS ? 
              f_SetupPattern(59, 1, 3) : na
        else
            na
    else
        na
    
// Returns if the previous three candles formed a Star pattern which includes 
// both the Morning and Evening Stars, as well as their Doji counterparts.
//
//              Ex: (Evening Star)
//                                            
//                                     ▄
//                                  |  ▀  ,
//                                  |     |
//                               ░  ▓     ▒    
//                             ░    ▲     ▼     
//                           ░                   
// 
f_IsStar() =>
    c1andc3Tall = C3_ISTALL and C5_ISTALL

    if c1andc3Tall
        [c4higher, c4lower] = f_GetHigherLowerTuple(C4_OPEN, C4_CLOSE)
        mostRecent = TEST_VAL[1]

        // And here
        bool c2IsValidDoji = 
          if not na(mostRecent)
            array.includes(DOJI_VALID_COMPS, mostRecent.ID)
        else
            false

        if IN_UPTREND3
            evening = C3_ISBULLISH and C5_ISBEARISH and c4lower > C3_CLOSE
              and c4lower > C5_OPEN and C5_CLOSE < C3_MIDPRICE

            eveningDoji = i_ESDS and evening and c2IsValidDoji
            evening := evening and i_ESS

            eveningDoji ? f_SetupPattern(60, 1, 3) : 
              evening ? f_SetupPattern(61, 1, 3) : na

        else 
            morning = C3_ISBEARISH and C5_ISBULLISH and c4higher < C3_CLOSE
              and c4higher < C5_OPEN and C5_CLOSE > C3_MIDPRICE

            morningDoji = i_MSDS and morning and c2IsValidDoji 
            morning := morning and i_MSS

            morningDoji ? f_SetupPattern(62, -1, 3) : 
              morning ? f_SetupPattern(63, -1, 3) : na
    else
        na
    
// Returns if the previous three candles formed a Stick Sandwich pattern. This  
// pattern is a bit different in terms of what is considered 'valid' where the 
// third candle must close 'at or near' the close of the first.
//
//              Ex:
//
//                            ░                 
//                              ░       ▲       
//                                ░  ▼     ▼
//                                      ▓       
//                                   ▒  ▀  ▒     
//                                                
//                                               
// @input:
//      i_StickSandTol - Tolerance for the range of values found to be the valid
//                     for candle 3's close through this expression:
//            
//  valid values = [ [C5_CLOSE] - ( [i_StickSandTol] * math.abs( [C3_BODY] ) ),
//                   [C5_CLOSE] + ( [i_StickSandTol] * math.abs( [C3_BODY] ) ) ] 
//
f_IsStickSandwich() =>
    if IN_DOWNTREND3 and i_SANS
        [closeHigh, closeLow] = 
          f_GetTolerancedValueTuple(C3_CLOSE, math.abs(C3_BODY), i_StickSandTol)

        c1Valid = C3_ISBEARISH
        c2Valid = C4_ISBULLISH and C4_CLOSE > C3_OPEN
        c3Valid = C5_ISBEARISH and f_WithinVals(C5_CLOSE, closeHigh, closeLow)

        c1Valid and c2Valid and c3Valid ? f_SetupPattern(64, -1, 3) : na
    else
        na

// Returns if the previous three candles formed a Tasuki Gap type pattern. This 
// method includes both downside and upside Tasuki Gaps.
//
//              Ex: (upside Tasuki Gap)
//
//                                     ▲  ▼
//                                     █         
//                                  ▲  ▀--▒---------
//                                  ▄--------------- c3 closes between c1-c2 gap
//                               ░  ▀ 
//                             ░    
//                           ░      
// 
f_IsTasukiGap() => 
    if IN_DOWNTREND3 and i_DTGS
        c1dvalid = C3_ISBEARISH
        c2dvalid = C4_ISBEARISH and C4_HIGH < C3_LOW
        c3dvalid = C5_ISBULLISH and f_WithinVals(C5_OPEN, C4_OPEN, C4_CLOSE)
          and f_WithinVals(C5_CLOSE, C3_LOW, C4_HIGH)

        c1dvalid and c2dvalid and c3dvalid ? f_SetupPattern(65, -1, 3) : na

    else if IN_UPTREND3 and i_UTGS
        c1uvalid = C3_ISBULLISH
        c2uvalid = C4_ISBULLISH and C4_LOW > C3_HIGH
        c3uvalid = C5_ISBEARISH and f_WithinVals(C5_OPEN, C4_OPEN, C4_CLOSE)
          and f_WithinVals(C5_CLOSE, C4_LOW, C3_HIGH)

        c1uvalid and c2uvalid and c3uvalid ? f_SetupPattern(66, 1, 3) : na
    else
        na

// Returns whether or not the given closing price closes near the candle's high
// or it's low given a certain tolerance based on the candle's closing position.
//
// @params:
//      _c close - close of this candle.
//          high - high of this candle.
//           low - low of this candle.
//    _tolerance - % tolerance between high and low prices [_cclose] may be.
//
// @return:
//      int - -1 if [_cclose] is near [_clow].
//          -  1 if [_cclose] is near [_chigh].
//          -  0 if [_cclose] is neither near the [_high] or [_low].
//
f_ClosesNearHL(_cclose, _chigh, _clow, _tolerance) =>
    pos = (_cclose - _clow) / (_chigh - _clow)
    
    lowclose = pos <= _tolerance
    highclose = pos >= 1 - _tolerance
    
    lowclose ? -1 : highclose ? 1 : 0

// Returns if the given candle body is within ±[_tolerance * 100]% of the size
// of the given average body size.
//
// @params:
//       _cbody - body of the candle requested.
//     _bodyavg - given body average.
//   _tolerance - % tolerance for how much smaller/larger this body may be to be
//              considered 'valid'.
// 
// @return:
//      bool - true if the candle body given fits within ±[_tolerance * 100]%
//           the size of the [_bodyavg] given.
//           - false otherwise.
// 
f_SimilarToAverage(_cbody, _bodyavg, _tolerance) =>
    _cbody >= (_bodyavg * (1 - _tolerance)) 
      and _cbody <= (_bodyavg * (1 + _tolerance))

// Returns if the previous three candlesticks formed one of the four following 
// three-candlestick patterns:
//      - Identical Black Crows       - Three White Soldiers
//      - Three Black Crows           - Three Stars In The South
//
//              Ex: (Three White Soldiers)
//
//                            ░                 
//                              ░                
//                                ░            
//                                         ▓  
//                                   |  ▓  ▲
//                                   ▓  ▲
//                                   ▲
// @inputs:
//                i_IBCT - (Identical Black Crows Tolerance) The allowed ±% 
//                       deviation each candle in the pattern may be of the 
//                       average of the three bodies.
//            i_OC_3CTol - (Three Black Crows and Three White Soldiers) allows 
//                       for the close of the candles in the Three Black Crows
//                       and Three White Soldiers to be within a specific %
//                       tolerance of the lows/highs of the candles 
//                       (respectively).
//         i_SITSCandle1 - (Stars in the South candle 1) Specifies where the 
//                       median price of the candle body must be above in the 
//                       high-low range of the first candle.
//         i_SITSCandle2 - (Stars in the South candle 2) Specifies how different
//                       (body size and position) that the second candle in the 
//                       Stars in the South pattern may be in order to be 
//                       considered valid.
//       i_SITSC2MaxProp - (Stars in the South Candle 2) Specifies the max 
//                       proportion that the second candle may be in relation 
//                       to the first in the Stars in the South pattern based
//                       on the high-low range of each candle. (Prevents a 
//                       second candle from being larger than the first that is
//                       proportionally similar from being considered valid).
//            i_MaruType - Specifies which types of Marubozu candles may be 
//                       considered valid in the patterns that utilize them. If
//                       i_MaruType is "exclusive" then only pure Marubozus will
//                       be considered valid. Otherwise, the opening/closing 
//                       variants will be considered valid as well. 
//
f_IsThree() =>
    allBullish = C5_ISBULLISH and C4_ISBULLISH and C3_ISBULLISH
    allBearish = C5_ISBEARISH and C4_ISBEARISH and C3_ISBEARISH
    alltall = C5_ISTALL and C4_ISTALL and C3_ISTALL
    
    c3NearHL = f_ClosesNearHL(C3_CLOSE, C3_HIGH, C3_LOW, i_OC_3CTol)
    c4NearHL = f_ClosesNearHL(C4_CLOSE, C4_HIGH, C4_LOW, i_OC_3CTol)
    c5NearHL = f_ClosesNearHL(C5_CLOSE, C5_HIGH, C5_LOW, i_OC_3CTol)
    
    c4WIc3 = f_WithinVals(C4_OPEN, C3_OPEN, C3_CLOSE)
    c5WIc4 = f_WithinVals(C5_OPEN, C4_OPEN, C4_CLOSE)
    
    avgbody = (math.abs(C5_BODY) + math.abs(C4_BODY) + math.abs(C3_BODY)) / 3
    
    // identical black crows
    c1IBCvalid = f_SimilarToAverage(C3_BODY, avgbody, i_IBCT)
      
    c2IBCvalid = (C4_OPEN + (C3_BODY * i_IBCT)) >= C3_CLOSE 
      and f_SimilarToAverage(C4_BODY, avgbody, i_IBCT)
      
    c3IBCvalid = (C5_OPEN + (C4_BODY * i_IBCT)) >= C4_CLOSE 
      and f_SimilarToAverage(C5_BODY, avgbody, i_IBCT)
    
    identicalBlackCrows = i_IBCS and allBearish and alltall and c1IBCvalid 
      and c2IBCvalid and c3IBCvalid and IN_UPTREND3

    // three black crows
    c1BCvalid = c3NearHL == -1
    
    c2BCvalid = c4NearHL == -1 and c4WIc3 and C4_LOW < C3_LOW
        
    c3BCvalid = c5NearHL == -1 and c5WIc4 and C5_LOW < C4_LOW
    
    threeBlackCrows = i_TBCS and allBearish and alltall and c1BCvalid 
      and c2BCvalid and c3BCvalid and IN_UPTREND3
    
    // three white soldiers
    c1TWSvalid = c3NearHL == 1
    
    c2TWSvalid = c4NearHL == 1 and c4WIc3 and C4_CLOSE > C3_CLOSE
      
    c3TWSvalid = c5NearHL== 1 and c5WIc4 and C5_CLOSE > C4_CLOSE
    
    TWS = i_TWSS and allBullish and alltall and c1TWSvalid and c2TWSvalid 
      and c3TWSvalid and IN_DOWNTREND3
    
    // three stars in the south
    c1SITSvalid = C3_BODYPOS >= i_SITSCandle1
    
    c2SITSvalidPosition = C4_BODYPOS >= (C3_BODYPOS * (1 - i_SITSCandle2)) 
      and C4_BODYPOS <= (C3_BODYPOS * (1 + i_SITSCandle2))
    
    c1bodyPercent = C3_BODY / C3_RANGE
    c2bodyPercent = C4_BODY / C4_RANGE
    c2WithinMaxProp = C4_RANGE / C3_RANGE <= i_SITSC2MaxProp
    
    c2SITSvalidBody = c2bodyPercent >= (c1bodyPercent * (1 - i_SITSCandle2)) 
      and c2bodyPercent <= (c1bodyPercent * (1 + i_SITSCandle2))
    
    c2SITSvalid = c2SITSvalidPosition and c2SITSvalidBody and C4_LOW > C3_LOW
      and c2WithinMaxProp
    
    c3IsMaru = f_IsMarubozu(C5_OPEN, C5_HIGH, C5_LOW, C5_CLOSE)
    validMaru = i_MaruType == "exclusive" ? -3 : -1
    
    c3SITSvalid = c3IsMaru <= validMaru
      and f_WithinVals(C5_LOW, C4_HIGH, C4_LOW) 
      and f_WithinVals(C5_HIGH, C4_HIGH, C4_LOW)
    
    starsInTheSouth = i_SITSS and allBearish and c1SITSvalid and c2SITSvalid 
      and c3SITSvalid and IN_DOWNTREND3

    switch
        identicalBlackCrows => f_SetupPattern(67, 1, 3)
        threeBlackCrows => f_SetupPattern(68, 1, 3)
        TWS => f_SetupPattern(69, 1, 3)
        starsInTheSouth => f_SetupPattern(70, 1, 3)
        => na
    
// Returns if the previous three candles formed in either a Two Crows pattern or 
// a Upside Gap Two Crows pattern. (Two Crows is bearish, Upside Gap variation 
// is bullish)
//
//              Ex: (Upside Gap Two Crows)
// 
//
//                                        ▼      
//                                     ▼  ▄    
//                                     ▀  █   
//                                        
//                                  ▓  
//                               ░  ▲ 
//                             ░    
//                           ░     
//                  
// @input:
//      i_TwoCShadEx - Exclude/Include the shadow from candle 2 as a part of the
//                   'gap'. (Applies to both patterns.)
//
f_IsTwoCrows() =>
    if IN_UPTREND3
        c3Engulfing = if i_EngulfSet == "BODY" 
            f_Engulfs(C5_OPEN, C5_CLOSE, C4_OPEN, C4_CLOSE)
        else if i_EngulfSet == "RANGE"
            f_Engulfs(C5_HIGH, C5_LOW, C4_HIGH, C4_LOW)
        else
            f_Engulfs(C5_OPEN, C5_CLOSE, C4_OPEN, C4_CLOSE) or 
              f_Engulfs(C5_HIGH, C5_LOW, C4_HIGH, C4_LOW)

        c1valid = C3_ISBULLISH and C3_ISTALL
        c2valid = C4_ISBEARISH and (i_TwoCShadEx ? C4_CLOSE : C4_LOW) > C3_CLOSE
        c3valid = C5_ISBEARISH and f_WithinVals(C5_OPEN, C4_OPEN, C4_CLOSE)
          and f_WithinVals(C5_CLOSE, C3_OPEN, C3_CLOSE)

        TwoCrows = i_TCS and c1valid and c2valid and c3valid

        c3validGap = C5_ISBEARISH and c3Engulfing and C5_CLOSE > C3_CLOSE

        TwoCrowsGap = i_UG2CS and c1valid and c2valid and c3validGap

        TwoCrows ? f_SetupPattern(73, 1, 3) : 
          TwoCrowsGap ? f_SetupPattern(74, 1, 3) : na
    else
        na

// Returns if the previous three candles formed a Unique Three River Bottom 
// candlestick pattern.
//
//              Ex:
//
//                            ░               
//                              ░    ▼  ▼      
//                                ░     ,    
//                                   ▓  █      
//                                      |  ▄     
//                                      |  ▀     
//                                         ▲
//
f_IsUnique3RB() =>
    if IN_DOWNTREND3 and i_U3RBS
        c1valid = C3_ISBEARISH and C3_ISTALL
        c2valid = C4_ISBEARISH and f_WithinVals(C4_OPEN, C3_OPEN, C3_CLOSE) 
          and C4_LOW < C3_LOW
        c3valid = C5_ISBULLISH and not C5_ISTALL and C5_CLOSE < C4_CLOSE

        c1valid and c2valid and c3valid ? f_SetupPattern(75, -1, 3) : na
    else
        na
    
// ╔═══════════════════════════════════════════════════════════════════════════╗
// ║                   __             _        __  ____   _                    ║
// ║                  / /  ___  ___ _(_)___  _/_/ / / /  | |                   ║
// ║                 / /__/ _ \/ _ `/ / __/ / /  /_  _/  / /                   ║
// ║                /____/\___/\_, /_/\__/ / /    /_/  _/_/                    ║
// ║                          /___/        |_|        /_/                      ║
// ║                                                                           ║
// ║This section will include 3 4-candle patterns recognized by 2 methods:     ║
// ║    Concealing Baby Swallow                                                ║
// ║    Three Line Strike (Bullish and Bearish)                                ║
// ║                                                                           ║
// ╚XLOG4══════════════════════════════════════════════════════════════════════╝

// Returns if the previous four candles match a Concealing Baby Swallow pattern.
// (I'm not too sure as to how many there will be found with this but the 
// pattern itself is unusually rare.)
//
//                  Ex:
//  
//                          ░
//                            ░    ▼
//                              ░  █  ▼     ▼
//                                 █  █  ▼  ▄
//                                    ▀  |  █
//                                       ▀  █    
//                                                
//                                                  
//
// @inputs:
//      i_MaruType - Option for the type of Marubozu candles allowed to be
//                 valid in candlestick patterns that require them. (Exclusive:
//                 no wicks allowed, inclusive: wicks allowed)
//      i_CBSC3Tol - Concealing Baby Swallow Candle 3 Tolerance: Allows the user
//                 to restrict how small/large the candle body of Candle 3 may 
//                 be in relation to the size of the candle's upperwick.
//
f_IsConcealingBabySwallow() =>
    if IN_DOWNTREND4 and i_CBSS
        validMarubozu = i_MaruType == "exclusive" ? -3 : -1

        c3ValidSize = math.abs(C4_BODY) / C4_UPPERWICK

        c1Valid = f_IsMarubozu(C2_OPEN, C2_HIGH, C2_LOW, C2_CLOSE) <= validMarubozu
          and C2_ISTALL
        c2Valid = f_IsMarubozu(C3_OPEN, C3_HIGH, C3_LOW, C3_CLOSE) <= validMarubozu
          and C3_ISTALL
        c3Valid = C4_ISBEARISH and C4_OPEN < C3_CLOSE and c3ValidSize <= i_CBSC3Tol
          and f_WithinVals(C4_HIGH, C3_OPEN, C3_CLOSE)
        c4Valid = f_Engulfs(C5_HIGH, C5_LOW, C4_HIGH, C4_LOW) and 
          f_IsMarubozu(C5_OPEN, C5_HIGH, C5_LOW, C5_CLOSE) <= validMarubozu

        // Note: it is unnecessary to check for C2,3,5_ISBEARISH as the value
        // returned from f_IsMarubozu will reveal this information.
        c1Valid and c2Valid and c3Valid and c4Valid ? 
          f_SetupPattern(76, -1, 4) : na
    else
        na

// Returns if the previous four candlesticks match either of the Three Line 
// Strike patterns (Bullish and Bearish).
//
//              Ex: (Bearish)
//
//                        
//                          ░
//                            ░             |     
//  c4 closes above first day___░__▼________▄   
//                                 █  ▼  ▼  █  
//                                 ▀  █  ▄  █
//                                       █__█____c4 opens below prior day
//                                          ▀
//                                          ▲
//
f_IsThreeLineStrike() =>
    if IN_UPTREND4 and i_BU3LSS
        c1buvalid = C2_ISBULLISH
        c2buvalid = C3_ISBULLISH and C3_HIGH > C2_HIGH
        c3buvalid = C4_ISBULLISH and C4_HIGH > C3_HIGH
        c4buvalid = C5_ISBEARISH and C5_OPEN > C4_CLOSE and C5_CLOSE < C2_OPEN
          and C5_ISTALL

        c1buvalid and c2buvalid and c3buvalid and c4buvalid ? 
          f_SetupPattern(77, 1, 4) : na

    else if IN_DOWNTREND4 and i_BE3LSS
        c1bevalid = C2_ISBEARISH
        c2bevalid = C3_ISBEARISH and C3_LOW < C2_LOW
        c3bevalid = C4_ISBEARISH and C4_LOW < C3_LOW
        c4bevalid = C5_ISBULLISH and C5_OPEN < C4_CLOSE and C5_CLOSE > C2_OPEN
          and C5_ISTALL

        c1bevalid and c2bevalid and c3bevalid and c4bevalid ? 
          f_SetupPattern(78, -1, 4) : na
    else
        na
    
// ╔═══════════════════════════════════════════════════════════════════════════╗
// ║                   __             _        __  ____   _                    ║
// ║                  / /  ___  ___ _(_)___  _/_/ / __/  | |                   ║
// ║                 / /__/ _ \/ _ `/ / __/ / /  /__ \   / /                   ║
// ║                /____/\___/\_, /_/\__/ / /  /____/ _/_/                    ║
// ║                          /___/        |_|        /_/                      ║
// ║                                                                           ║
// ║This section will include 6 5-candle patterns recognized by 4 methods:     ║
// ║    Breakaway (Bullish and Bearish)                                        ║
// ║    Ladder Bottom                                                          ║
// ║    Mat Hold                                                               ║
// ║    Three Methods (Rising and Falling)                                     ║
// ║                                                                           ║
// ╚XLOG5══════════════════════════════════════════════════════════════════════╝

// Determines if the previous five candles fit the requirements for either a
// Bullish Breakaway pattern or a Bearish Breakaway pattern. 
//
//              Ex: (Bullish)
//
//
//                          ░               
//                            ░    ▼     
//                              ░                   
//                                 ▒             
//                                               
//                                   ▄         ------ closes within c1/c2 gap
//                                   ▀  █  ▄  ▓ 
//                                   ▼     ▀  ▲
//                                         ▼
//
f_IsBreakaway() =>
    c1andc5Tall = C1_ISTALL and C5_ISTALL
    
    if c1andc5Tall
        [c3Higher, c3Lower] = f_GetHigherLowerTuple(C3_OPEN, C3_CLOSE)

        if IN_UPTREND5 and i_BEBAS
            c1bevalid = C1_ISBULLISH
            c2bevalid = C2_ISBULLISH and C2_OPEN > C1_CLOSE
            c3bevalid = c3Higher > C2_CLOSE and c3Lower > C1_CLOSE
            c4bevalid = C4_ISBULLISH and C4_CLOSE > c3Higher and C4_OPEN > C1_OPEN
            c5bevalid = C5_ISBEARISH and C5_CLOSE > C1_CLOSE and C5_CLOSE < C2_OPEN

            c1bevalid and c2bevalid and c3bevalid and c4bevalid and c5bevalid ? 
              f_SetupPattern(79, 1, 5) : na

        else if IN_DOWNTREND5 and i_BUBAS
            c1buvalid = C1_ISBEARISH
            c2buvalid = C2_ISBEARISH and C2_OPEN < C1_CLOSE 
            c3buvalid = c3Lower < C2_CLOSE and c3Higher < C1_CLOSE
            c4buvalid = C4_ISBEARISH and C4_CLOSE < c3Lower and C4_OPEN < C1_CLOSE
            c5buvalid = C5_ISBULLISH and C5_CLOSE > C2_OPEN and C5_CLOSE < C1_CLOSE

            c1buvalid and c2buvalid and c3buvalid and c4buvalid and c5buvalid ? 
              f_SetupPattern(80, -1, 5) : na
        else
            na
    else
        na
    
// Returns if the previous five candlesticks fit the requirements for a Ladder 
// Bottom pattern. The inputs will allow the user to specify the range of values
// the [C4_UPPERWICK] / [C4_RANGE] may be to be considered valid.
//
//              Ex: 
//
//                          ░               
//                            ░    ▼                   
//                              ░     ▼               
//                                 ▓     ▼     |    
//                                    ▓     ▼  █   
//                                       ▓  ...▀....... c4/c5 gap
//                                          █  ▲
//                                          '
// @inputs: 
//    i_LBC4WickMin - minimum size of the upper wick (as a % of high-low range) 
//                  for candle 4.
//    i_LBC4WickMax - maximum size of the upper wick (as a % of high-low range)
//                  for candle 4.
// 
f_IsLadderBottom() =>
    if IN_DOWNTREND5 and i_LBS
        c1c2c3Tall = C1_ISTALL and C2_ISTALL and C3_ISTALL

        c4WSize = C4_UPPERWICK / C4_RANGE

        c1valid = C1_ISBEARISH
        c2valid = C2_ISBEARISH and C2_OPEN < C1_OPEN and C2_CLOSE < C1_CLOSE
        c3valid = C3_ISBEARISH and C3_OPEN < C2_OPEN and C3_CLOSE < C2_CLOSE
        c4valid = C4_ISBEARISH and c4WSize >= i_LBC4WickMin and c4WSize <= i_LBC4WickMax
        c5valid = C5_ISBULLISH and C5_OPEN > C4_OPEN

        c1valid and c2valid and c3valid and c4valid and c5valid and c1c2c3Tall ? 
          f_SetupPattern(81, -1, 5) : na
    else
        na
    
// Returns if the previous five candles fit the requirements for a Mat Hold
// pattern. 
//
//              Ex:
//
//                                            |
//                                   ▼   _____▄____ c5 closes above highest high
//                                   ▄  |     █
//             c2 gaps above c1 -----▀  █  ▼  █
//                                 |    ▀  ▄  █       
//                                 ▓       █  ▀       
//                              ░  ▓          ▲
//                            ░    ▲
//                          ░
//
f_IsMatHold() =>
    if IN_UPTREND5 and i_MHS
        [C3_HIGHer, C3_LOWer] = f_GetHigherLowerTuple(C3_OPEN, C3_CLOSE)
        highestHigh = math.max(C4_HIGH, C3_HIGH, C2_HIGH, C1_HIGH)
        middleThreeAboveLow = C2_CLOSE > C1_LOW and C3_LOWer > C1_LOW and 
          C4_CLOSE > C1_LOW

        c1valid = C1_ISBULLISH and C1_ISTALL
        c2valid = C2_ISBEARISH and C2_CLOSE > C1_CLOSE
        c3valid = C3_HIGHer < C2_OPEN
        c4valid = C4_ISBEARISH and C4_OPEN < C3_HIGHer
        c5valid = C5_ISBULLISH and C5_CLOSE > highestHigh
          and C5_ISTALL

        c1valid and c2valid and c3valid and c4valid and c5valid 
          and middleThreeAboveLow ? f_SetupPattern(82, 1, 5) : na
    else
        na

// Returns if the previous five candles fit the requirements for a Rising or
// Falling Three Methods pattern. 
//
//              Ex: (Rising)
//                                                   
//                                ▲                
//                     ...........|..▼        ▲   
//                    /           ▄--█-----▼--█----- c5 closes above c1 close
//   c2-c4 trade  ───┤            █  ▀  █  ▄  █
//   within c1 range  \ ________░_█_____|__█  █
//                           ░    |           |
//                         ░ 
//
//
f_IsThreeMethods() =>
    c1andc5Tall = C1_ISTALL and C5_ISTALL
    
    if c1andc5Tall
        [C3_HIGHer, C3_LOWer] = f_GetHigherLowerTuple(C3_OPEN, C3_CLOSE)

        if IN_DOWNTREND5 and i_F3MS
            c1favalid = C1_ISBEARISH
            c2favalid = C2_ISBULLISH and C2_OPEN > C1_LOW and C2_CLOSE < C1_HIGH
            c3favalid = C3_HIGHer > C2_CLOSE and C3_LOWer > C2_OPEN 
              and C3_HIGHer < C1_HIGH
            c4favalid = C4_ISBULLISH and C4_OPEN > C3_LOWer and C4_CLOSE > C3_HIGHer 
              and C4_CLOSE < C1_HIGH
            c5favalid = C5_ISBEARISH and C5_CLOSE < C1_CLOSE
            
            c1favalid and c2favalid and c3favalid and c4favalid and c5favalid ? 
              f_SetupPattern(83, 1, 5) : na

        else if IN_UPTREND5 and i_R3MS
            c1rivalid = C1_ISBULLISH
            c2rivalid = C2_ISBEARISH and C2_CLOSE > C1_LOW and C2_OPEN < C1_HIGH
            c3rivalid = C3_HIGHer < C2_OPEN and C3_LOWer > C1_LOW
            c4rivalid = C4_ISBEARISH and C4_OPEN < C3_HIGHer and C4_CLOSE > C1_LOW
            c5rivalid = C5_ISBULLISH and C5_CLOSE > C1_CLOSE

            c1rivalid and c2rivalid and c3rivalid and c4rivalid and c5rivalid ? 
              f_SetupPattern(84, 1, 5) : na
        else
            na
    else
        na

// ╔═══════════════════════════════════════════════════════════════════════════╗
// ║                  __             _       _____     ____                    ║
// ║                 / /  ___  ___ _(_)___  / ___/__ _/ / /__                  ║
// ║                / /__/ _ \/ _ `/ / __/ / /__/ _ `/ / (_-<                  ║
// ║               /____/\___/\_, /_/\__/  \___/\_,_/_/_/___/                  ║
// ║                         /___/                                             ║
// ║                                                                           ║
// ║ This section is solely for the purpose of calling each of the functions   ║
// ║ which determine if a candlestick pattern has appeared. Every function     ║
// ║ in this section pertains to the number of candles in the patterns they    ║
// ║ seek to identify (excluding f_RunRemaining). They are broken up as such   ║
// ║ for the purpose of readability, on top of operating within PineScrypt's   ║
// ║ limits:                                                                   ║
// ║                                                                           ║
// ║ Pine's compiler cannot process a function which calls more than 254       ║
// ║ 'external elements' which include inputs, function parameters, and other  ║
// ║ things. If each function call for all patterns were put into a single     ║
// ║ function, it would quickly exceed that limit.                             ║
// ║                                                                           ║
// ║ Since Pine does not have a do-while loop structure, this is what I think  ║
// ║ is the next-best solution. Each loop will call every single function to   ║
// ║ attempt to recognize a pattern. If one is found, the loop is broken and   ║
// ║ that pattern will be returned; preventing the execution of other pattern  ║
// ║ functions. If not, it will process through until the end of the loop,     ║
// ║ where the condition for which the loop started running will be changed to ║
// ║ only have that loop run once. The reason behind this design is so that    ║
// ║ there is not a massive if-else-if block necessary for calling all of the  ║
// ║ pattern functions and is much easier to look at in my opinion.            ║
// ║                                                                           ║
// ╚XLOGC══════════════════════════════════════════════════════════════════════╝

// start while-loop corkscrew
f_Run5CPats() =>
    patternObj newPat = na
    bool loopBreak = false
    while not loopBreak
        newPat := f_IsThreeMethods()
        if not na(newPat)
            break
        newPat := f_IsMatHold()
        if not na(newPat)
            break
        newPat := f_IsLadderBottom()
        if not na(newPat)
            break
        newPat := f_IsBreakaway()
        if not na(newPat)
            break
        loopBreak := true
    newPat
f_Run4CPats() =>
    patternObj newPat = na
    bool loopBreak = false
    while not loopBreak
        newPat := f_IsThreeLineStrike()
        if not na(newPat)
            break
        newPat := f_IsConcealingBabySwallow()
        if not na(newPat)
            break
        loopBreak := true
    newPat
f_Run3CPats() =>
    patternObj newPat = na
    bool loopBreak = false
    while not loopBreak
        newPat := f_IsUnique3RB()
        if not na(newPat)
            break
        newPat := f_IsTwoCrows()
        if not na(newPat)
            break
        newPat := f_IsThree()
        if not na(newPat)
            break
        newPat := f_IsTasukiGap()
        if not na(newPat)
            break
        newPat := f_IsStickSandwich()
        if not na(newPat)
            break
        newPat := f_IsStar()
        if not na(newPat)
            break
        newPat := f_IsSBS()
        if not na(newPat)
            break
        newPat := f_Is3InAndOut()
        if not na(newPat)
            break
        newPat := f_IsGap3Methods()
        if not na(newPat)
            break
        newPat := f_IsDeliberation()
        if not na(newPat)
            break
        newPat := f_IsCollapsingDoji()
        if not na(newPat)
            break
        newPat := f_IsAdvanceBlock()
        if not na(newPat)
            break
        newPat := f_IsAbandonedBaby()
        if not na(newPat)
            break
        loopBreak := true
    newPat
f_Run2CPats() =>
    patternObj newPat = na
    bool loopBreak = false
    while not loopBreak
        newPat := f_IsWindow()
        if not na(newPat)
            break
        newPat := f_IsTwoBlackGap()
        if not na(newPat)
            break
        newPat := f_IsTweezer()
        if not na(newPat)
            break
        newPat := f_IsThrusting()
        if not na(newPat)
            break
        newPat := f_IsStomach()
        if not na(newPat)
            break
        newPat := f_IsPiercing()
        if not na(newPat)
            break
        newPat := f_IsNeck()
        if not na(newPat)
            break
        newPat := f_IsMatchLow()
        if not na(newPat)
            break
        newPat := f_IsLines()
        if not na(newPat)
            break
        newPat := f_IsKicking()
        if not na(newPat)
            break
        newPat := f_IsHomingPige()
        if not na(newPat)
            break
        newPat := f_IsHarami()
        if not na(newPat)
            break
        newPat := f_IsDCC()
        if not na(newPat)
            break
        newPat := f_Is2CEngulfing()
        if not na(newPat)
            break
        loopBreak := true
    newPat
f_RunRemaining() =>
    patternObj newPat = na
    bool loopBreak = false
    while not loopBreak
        newPat := f_IsDojiPat()
        if not na(newPat)
            break
        newPat := f_IsHammerPat()
        if not na(newPat)
            break
        newPat := f_IsLongDay()
        if not na(newPat)
            break
        loopBreak := true
    newPat
// End while-loop corkscrew

// ╔═══════════════════════════════════════════════════════════════════════════╗
// ║      __  ___      _          ______      ____   ____  __           __     ║
// ║     /  |/  /___ _(_)___     / ____/___ _/ / /  / __ )/ /___  _____/ /__   ║
// ║    / /|_/ / __ `/ / __ \   / /   / __ `/ / /  / __  / / __ \/ ___/ //_/   ║
// ║   / /  / / /_/ / / / / /  / /___/ /_/ / / /  / /_/ / / /_/ / /__/ ,<      ║
// ║  /_/  /_/\__,_/_/_/ /_/   \____/\__,_/_/_/  /_____/_/\____/\___/_/|_|     ║
// ║                                                                           ║
// ║ This section is the heart of the program which calls all the pattern      ║
// ║ recognition functions, overriding patterns, calculating percent returns   ║
// ║ + managing the return matrix, and barcoloration for displaying these      ║
// ║ patterns when they appear.                                                ║
// ║                                                                           ║
// ║ Honestly amazing how 5,000 lines of code can be ran in less than 220      ║
// ║ lines at the end. Much like reading through the entirety of this script,  ║
// ║ building it was one hell of a journey.                                    ║
// ║                                                                           ║ 
// ╚XMAIN══════════════════════════════════════════════════════════════════════╝4

if bar_index > 5
    // Grab previous patterns for overrides
    previousFour = array.from(TEST_VAL[4], TEST_VAL[3], TEST_VAL[2], TEST_VAL[1])
    previousThree = array.slice(previousFour, 1, 4)
    previousTwo = array.slice(previousFour, 2, 4)
    previousPat = array.slice(previousFour, 3, 4)

    // Prioritize larger candle patterns, move down, test all.
    TEST_VAL := f_Run5CPats()
    if na(TEST_VAL)
        TEST_VAL := f_Run4CPats()
    if na(TEST_VAL)
        TEST_VAL := f_Run3CPats()
    if na(TEST_VAL)
        TEST_VAL := f_Run2CPats()
    if na(TEST_VAL)
        TEST_VAL := f_RunRemaining()
    
    // Global Variables cannot be modified from a function, this 
    // works around that restriction.
    if i_AdaptiveCol and not i_HardLimit
        [minRet, maxRet] = f_MatrixMinMax()

        MINRETVAL := minRet < MINRETVAL ? minRet : MINRETVAL
        MAXRETVAL := maxRet > MAXRETVAL ? maxRet : MAXRETVAL

    // DETECTION TYPE -> CLASSIC
    if i_DetectionType == "CLASSIC"
        // Test for any new patterns 
        if not na(TEST_VAL)
            // Fire alert and begin overriding based on pattern size
            f_AlertOnFind(TEST_VAL)
            
            switch TEST_VAL.size
                5 => f_OverridePrevious(previousFour)
                4 => f_OverridePrevious(previousThree)
                3 => f_OverridePrevious(previousTwo)
                2 => f_OverridePrevious(previousPat)
            
            // Grab return array and display statistics for pattern.
            retArr = f_GetRetArrFromPat(TEST_VAL)

            // Set up boolean value for ignoring patterns in TARGET MODE
            bool noPaint = na

            if i_CandleDetSet == "TARGET MODE"
                targetID = f_GetIDFromTarget()
                if TEST_VAL.ID != targetID
                    label.delete(TEST_VAL.patLabel)
                    TEST_VAL.labelTooltip := ""
                    noPaint := true

            if not noPaint
                array.push(BAR_COLOR_VALS, barColTup.new(1, TEST_VAL.size))
                array.set(CANDLE_COLOR_ARR, 0, f_GrabColor(retArr))

        // CLASSIC MODE P/L CALCULATION
        patBacktest = TEST_VAL[i_PnLLength]
        
        // Check if not na/overridden, these are nested to avoid referencing
        // fields of 'na' objects.
        if not na(patBacktest)
            // Ignore absorbed patterns
            if not patBacktest.overridden
                // Grab the pattern's return array and append the new return to
                // it, fire any alerts, append return to pattern tooltip.
                retArrBack = f_GetRetArrFromPat(patBacktest)

                percentRet = f_CalculatePercentReturn(i_PnLLength, 0)
                if CURR_BARSTATE
                    f_AddReturnAndUpdate(retArrBack, percentRet)
                    f_AppendTooltip(patBacktest, "\nThis pattern returned " 
                      + str.tostring(percentRet, "#.####") + "%")
                    f_AlertOnReturns(patBacktest, percentRet)

    // DETECTION TYPE -> BREAKOUT
    else    
        // Test for new patterns, add to queue
        if not na(TEST_VAL)
            array.push(UNCONF_PATTERNS, TEST_VAL)
            f_AlertOnFind(TEST_VAL)

        // Check if an update is necessary
        unconfSize = array.size(UNCONF_PATTERNS)

        updateNecessary = unconfSize != 0

        if updateNecessary
            // setup indices to remove array
            int[] indicesToRemove = array.new<int>()

            loopSize = unconfSize - 1

            // run through loop checking each unconfirmed pattern
            for i = 0 to loopSize
                patToTest = array.get(UNCONF_PATTERNS, i)
                testConfVal = f_RunPatternConfirmation(patToTest)

                // Pattern either confirmed or candlesLeft is 0
                if testConfVal
                    // breakoutDir is set to 0 if candlesLeft == 0
                    if patToTest.breakoutDir != 0
                        // Override based on size, display stats on label
                        // and fire breakout alert.
                        switch patToTest.size
                            5 => f_OverridePrevious(previousFour[patToTest.confOffset - 1])
                            4 => f_OverridePrevious(previousThree[patToTest.confOffset - 1])
                            3 => f_OverridePrevious(previousTwo[patToTest.confOffset - 1])
                            2 => f_OverridePrevious(previousPat[patToTest.confOffset - 1])
                            => na

                        // add new confirmed object to confirmed array
                        newConf = f_DeriveConfirmObj(patToTest)
                        array.push(CONF_PATTERNS, newConf)

                        // get stats for confirmed pattern, display with breakout direction
                        retArr = f_GetRetArrFromPat(patToTest)
                        if CURR_BARSTATE
                            f_DisplayStatsOnLabel(patToTest, retArr)
                            f_AlertOnBreakout(patToTest)

                    // Non-breakout case
                    else
                        f_AlertOnNonBreakout(patToTest)
                    // In either case, pattern needs to be kicked from queue
                    array.push(indicesToRemove, i)

            // Since array was processed in reverse order, patterns need to be 
            // removed in regular order to avoid corrupting the queue.
            array.sort(indicesToRemove, order.ascending)
            remArrSize = array.size(indicesToRemove)

            // Loop will attempt to run even if size of indices to remove array 
            // is 0 (unsure why, the same problem was present in the Hikkake 
            // Hunter 2.0).
            if not remArrSize == 0
                for j = 0 to remArrSize - 1
                    array.remove(UNCONF_PATTERNS, array.pop(indicesToRemove))

        // BREAKOUT MODE P/L CALCULATION FOR CONFIRMED INDICIES
        confIndToRem = array.new<int>()
        arrSize = array.size(CONF_PATTERNS)
        // Check arr size before loop
        if not arrSize == 0
            for i = 0 to arrSize - 1
                // Get confirmed pattern, check if there's any remaining candles for P/L calculation
                currPatProcess = array.get(CONF_PATTERNS, i)
                // only triggers on a confirmed barstate
                if currPatProcess.candlesLeft == 0
                    // P/L offset for the pattern experiencing is always the # candles for confirm - 1
                    // check if overridden before P/L calculation
                    prevPatRef = TEST_VAL[i_PnLLength + currPatProcess.ccandles - 1]
                    if not prevPatRef.overridden
                        // Get matrix position and add new return, handle 'FROM APPEARANCE' case
                        pnLOffset = i_PnLOffset == "FROM APPEARANCE" ? currPatProcess.ccandles - 1 : 0
                        percRet = f_CalculatePercentReturn(i_PnLLength, pnLOffset)
                        matRowPos = currPatProcess.ID
                        matColPos = currPatProcess.part + (currPatProcess.breakoutDir == 1 ? 0 : 3)

                        retArrToApp = matrix.get(PERCENT_RETURNS, matRowPos, matColPos)

                        f_AddReturnAndUpdate(retArrToApp, percRet)

                        // Add onto tooltip and fire alert
                        f_AppendTooltip(prevPatRef, "\nThis pattern returned: " + str.tostring(percRet, "#.####") + "%")
                        f_AlertOnReturns(prevPatRef, percRet)
                    
                    // remove from confirmed queue
                    array.push(confIndToRem, i)
                
                // Decrement remaining candles
                else
                    currPatProcess.candlesLeft := currPatProcess.candlesLeft - (CURR_BARSTATE ? 1 : 0)
            // Always remove items in reverse order (pop) to avoid array corruption
            array.sort(confIndToRem, order.ascending)

            remArrSize = array.size(confIndToRem)
            // remove all confirmed patterns with returns from array
            if not remArrSize == 0
                for i = 0 to remArrSize - 1
                    array.remove(CONF_PATTERNS, array.pop(confIndToRem))

// Moving Average plots
p_MA1_PLOT = 
  plot(i_MASetting == "MA 1" or i_MASetting == "BOTH" ? MA1 : na, title = "MA 1",
  color = color.red, linewidth = i_MA1Width)

p_MA2_PLOT = 
  plot(i_MASetting == "MA 2" or i_MASetting == "BOTH" 
  and i_MA1Length != i_MA2Length ? MA2 : na, color = color.white, 
  title = "MA 2", linewidth = i_MA2Width)

//----------------------------- COLOR THE PATTERNS -----------------------------
barcolor(f_BCContains(5, 5) ? array.get(CANDLE_COLOR_ARR, 4) : na, -9)
barcolor(f_BCContains(5, 4) ? array.get(CANDLE_COLOR_ARR, 4) : na, -8)
barcolor(f_BCContains(5, 3) ? array.get(CANDLE_COLOR_ARR, 4) : na, -7)
barcolor(f_BCContains(5, 2) ? array.get(CANDLE_COLOR_ARR, 4) : na, -6)
barcolor(f_BCContains(5, 1) ? array.get(CANDLE_COLOR_ARR, 4) : na, -5)

barcolor(f_BCContains(4, 5) ? array.get(CANDLE_COLOR_ARR, 3) : na, -8)
barcolor(f_BCContains(4, 4) ? array.get(CANDLE_COLOR_ARR, 3) : na, -7)
barcolor(f_BCContains(4, 3) ? array.get(CANDLE_COLOR_ARR, 3) : na, -6)
barcolor(f_BCContains(4, 2) ? array.get(CANDLE_COLOR_ARR, 3) : na, -5)
barcolor(f_BCContains(4, 1) ? array.get(CANDLE_COLOR_ARR, 3) : na, -4)

barcolor(f_BCContains(3, 5) ? array.get(CANDLE_COLOR_ARR, 2) : na, -7)
barcolor(f_BCContains(3, 4) ? array.get(CANDLE_COLOR_ARR, 2) : na, -6)
barcolor(f_BCContains(3, 3) ? array.get(CANDLE_COLOR_ARR, 2) : na, -5)
barcolor(f_BCContains(3, 2) ? array.get(CANDLE_COLOR_ARR, 2) : na, -4)
barcolor(f_BCContains(3, 1) ? array.get(CANDLE_COLOR_ARR, 2) : na, -3)

barcolor(f_BCContains(2, 5) ? array.get(CANDLE_COLOR_ARR, 1) : na, -6)
barcolor(f_BCContains(2, 4) ? array.get(CANDLE_COLOR_ARR, 1) : na, -5)
barcolor(f_BCContains(2, 3) ? array.get(CANDLE_COLOR_ARR, 1) : na, -4)
barcolor(f_BCContains(2, 2) ? array.get(CANDLE_COLOR_ARR, 1) : na, -3)
barcolor(f_BCContains(2, 1) ? array.get(CANDLE_COLOR_ARR, 1) : na, -2)

barcolor(f_BCContains(1, 5) ? array.get(CANDLE_COLOR_ARR, 0) : na, -5)
barcolor(f_BCContains(1, 4) ? array.get(CANDLE_COLOR_ARR, 0) : na, -4)
barcolor(f_BCContains(1, 3) ? array.get(CANDLE_COLOR_ARR, 0) : na, -3)
barcolor(f_BCContains(1, 2) ? array.get(CANDLE_COLOR_ARR, 0) : na, -2)
barcolor(f_BCContains(1, 1) ? array.get(CANDLE_COLOR_ARR, 0) : na, -1)
//------------------------------------------------------------------------------