Skip to main content

Elite Binary

//@version=6 indicator("RSI TMA", overlay=true) // Arrows on the main chart (overlay=true) // Input parameters rsiLength = input.int(2, title="RSI Length") rsiPrice = input.source(close, title="RSI Price") halfLength = input.int(2, title="Half Length") devPeriod = input.int(100, title="Deviation Period") deviations = input.float(0.7, title="Deviations") noDellArr = input.bool(false, title="No Delete Arrows") arrOtstup = input.int(0, title="Arrow Offset") arrUpColor = input.color(color.lime, title="Arrow Up Color") arrDnColor = input.color(color.red, title="Arrow Down Color") alertsMessage = input.bool(false, title="Alerts Message") alertsSound = input.bool(false, title="Alerts Sound") alertsEmail = input.bool(false, title="Alerts Email") alertsMobile = input.bool(false, title="Alerts Mobile") signalBar = input.int(0, title="Signal Bar") ...

SAN BUY and SELL

//@version=5
//
VERSION                   = 'v1'// 2025.04.17
strategy(
  'AI ALGO [SANDIP]',
  shorttitle              = 'AI ALGO [SANDIP] ' + VERSION,
  overlay                 = true,
  explicit_plot_zorder    = true,
  pyramiding              = 0,
  default_qty_type        = strategy.percent_of_equity,
  default_qty_value       = 50,
  calc_on_every_tick      = false,
  process_orders_on_close = true,
  max_bars_back           = 500,
  initial_capital         = 5000,
  commission_type         = strategy.commission.percent,
  commission_value        = 0.02
  )

// Support and Resistances

ShowSmartTrail = input.bool(true, 'Smart Trail      ', inline = 'overlayLine1', group = 'Smart Trail')

enableSR   = input(false, "SR On/Off", group="🧊 Support and Resistances")
colorSup   = input(color.new(#00DBFF,20), "Support Color", group="Support and Resistances")
colorRes   = input(color.new(#9598a1,50), "Resistance Color", group="Support and Resistances")
strengthSR = input.int(2, "S/R Strength", 1, group="Support and Resistances")
lineStyle  = input.string("Dotted", "Line Style", ["Solid", "Dotted", "Dashed"], group="Support and Resistances")
lineWidth  = input.int(1, "S/R Line Width", 1, group="Support and Resistances")
useZones   = input(true, "Zones On/Off", group="Support and Resistances")
useHLZones = input(true, "High Low Zones On/Off", group="Support and ResistancesSR")
zoneWidth  = input.int(2, "Zone Width %", 0, tooltip="it's calculated using % of the distance between highest/lowest in last 300 bars", group="Support and Resistances")
expandSR   = input(true, "Expand Support and Resistances")

/////////////////////////////// S/R //////////////////////////////////////////


trailType = input.string('modified', 'Trailtype', options=['modified', 'unmodified'], group = "S/R")
ATRPeriod = input(200, 'ATR Period')
ATRFactor = input.float(4.2, 'ATR Factor', minval = 2, maxval = 26,step=0.1,tooltip = "Changes the sensetivity of the signals.")
Smoothing = input(4, 'Smoothing')

///////////////////////////////////

norm_o = request.security(ticker.new(syminfo.prefix, syminfo.ticker), timeframe.period, open)
norm_h = request.security(ticker.new(syminfo.prefix, syminfo.ticker), timeframe.period, high)
norm_l = request.security(ticker.new(syminfo.prefix, syminfo.ticker), timeframe.period, low)
norm_c = request.security(ticker.new(syminfo.prefix, syminfo.ticker), timeframe.period, close)
//}



//////// FUNCTIONS //////////////
//{
// Wilders ma //
Wild_ma(_src, _malength) =>
    _wild = 0.0
    _wild := nz(_wild[1]) + (_src - nz(_wild[1])) / _malength
    _wild

/////////// TRUE RANGE CALCULATIONS /////////////////
HiLo = math.min(norm_h - norm_l, 1.5 * nz(ta.sma(norm_h - norm_l, ATRPeriod)))

HRef = norm_l <= norm_h[1] ? norm_h - norm_c[1] : norm_h - norm_c[1] - 0.5 * (norm_l - norm_h[1])

LRef = norm_h >= norm_l[1] ? norm_c[1] - norm_l : norm_c[1] - norm_l - 0.5 * (norm_l[1] - norm_h)

trueRange = trailType == 'modified' ? math.max(HiLo, HRef, LRef) : math.max(norm_h - norm_l, math.abs(norm_h - norm_c[1]), math.abs(norm_l - norm_c[1]))
//}


/////////// TRADE LOGIC ////////////////////////
//{
loss = ATRFactor * Wild_ma(trueRange, ATRPeriod)

Up68 = norm_c - loss
Dn68 = norm_c + loss

TrendUp = Up68
TrendDown = Dn68
Trend = 1

TrendUp := norm_c[1] > TrendUp[1] ? math.max(Up68, TrendUp[1]) : Up68
TrendDown := norm_c[1] < TrendDown[1] ? math.min(Dn68, TrendDown[1]) : Dn68

Trend := norm_c > TrendDown[1] ? 1 : norm_c < TrendUp[1] ? -1 : nz(Trend[1], 1)
trail = Trend == 1 ? TrendUp : TrendDown

ex = 0.0
ex := ta.crossover(Trend, 0) ? norm_h : ta.crossunder(Trend, 0) ? norm_l : Trend == 1 ? math.max(ex[1], norm_h) : Trend == -1 ? math.min(ex[1], norm_l) : ex[1]
//}

// //////// PLOT TP and SL /////////////
//{
plot(trail, 'S/R - SIGNAL', style=plot.style_line, color=Trend == 1 ? color.rgb(0, 242, 255) : Trend == -1 ? color.rgb(255, 0, 200) : na,linewidth = 1, editable = false,display = display.none)

//}

////// FIBONACCI LEVELS ///////////
//{
state = Trend == 1 ? 'long' : 'short'

fib1Level = 61.8
fib2Level = 78.6
fib3Level = 88.6

f1 = ex + (trail - ex) * fib1Level / 100
f2 = ex + (trail - ex) * fib2Level / 100
f3 = ex + (trail - ex) * fib3Level / 100
l100 = trail + 0

Fib1 = plot(f1, 'Ranger A', display = display.none, color=color.new(#f3ee00, 0), editable = false,display = display.none)
Fib2 = plot(f2, 'Ranger B', color=color.new(#f6ff00, 0),display = display.none, editable = false)
Fib3 = plot(f3, 'Ranger C',color=color.new(#f6ff00, 0), editable = false,display = display.none)
L100 = plot(l100, display = display.none, color=color.new(#f408a2, 0), editable = false,display = display.none)

fill(plot(ShowSmartTrail ? (ta.sma(trail, Smoothing)) : na, style=plot.style_line,display = display.none, editable = false, color=Trend == 1 ? color.new(#2157f9, 0) : Trend == -1 ? color.new(#ff1100, 0) : na),
 plot( ShowSmartTrail ? (ta.sma(f1, Smoothing)) : na, 'Fib 2', style=plot.style_line, editable = false,display = display.none),
 color=state == 'long' ? color.new(#2157f9, 70) : state == 'short' ? color.new(#ff1100, 70) : na,display = display.none)

fill(plot(ShowSmartTrail ? (ta.sma(trail, Smoothing)) : na, style=plot.style_line,display = display.none, editable = false, color=Trend == 1 ? color.new(#2157f9, 0) : Trend == -1 ? color.new(#ff1100, 0) : na),
 plot( ShowSmartTrail ? (ta.sma(f2, Smoothing)) : na, 'Fib 2', style=plot.style_line,editable = false,display = display.none),
 color=state == 'long' ? color.new(#2157f9, 71) : state == 'short' ? color.new(#ff1100, 72) : na,display = display.none)

fill(plot(ShowSmartTrail ? (ta.sma(trail, Smoothing)) : na, style=plot.style_line, editable = false, color=Trend == 1 ? color.new(#2157f9, 0) : Trend == -1 ? color.new(#ff1100, 0) : na),
 plot( ShowSmartTrail ? (ta.sma(f3, Smoothing)) : na, 'Fib 2', style=plot.style_line,  editable = false,display = display.none),
 color=state == 'long' ? color.new(#2157f9, 72) : state == 'short' ? color.new(#ff1100, 71) : na)




/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


//@version=5
//indicator('SimKle Zigzag UDT', 'SZU', true, max_bars_back = 500)

// 0. Inputs
// 1. Types
// 2. Switches
// 3. Variables and arrays
// 4. Custom Functions
// 5. Execution
// 6. Constructs

//#region ———————————————————— 0. Inputs
K0 = 'Zigzag values\nDefault : 14\nMin : 2\nMax : 50'
K1 = 'Short\nExamKle : L or LL\nLong\nExamKle : Low or Lower Low'
K2 = 'Constrast : Constrast color of chart background\nCustom : Input color\nNone : Color follow Trend Color'
K3 = 'Small font size recommended for mobile app or multiKle layout'
K4 = 'Default\nStyle : Solid\nWidth : 4'
length1     = input.int(            13,     ' Level ZigZag ', minval = 2, maxval = 50, tooltip = K0,group = " ZIGZAG HIGH - LOW")
colorUp      = input.color(color.rgb(0, 250, 75),'Trend Color', inline = '0')
colorDn      = input.color( color.rgb(202, 70, 70),           '', inline = '0')
showLabel    = input.bool(         true,      'Label', group = 'Show / hide', inline = '1')
showLine     = input.bool(         true,       'Line', group = 'Show / hide', inline = '1')
disKlayLabel = input.string(     'HHLL',       'Text', group = 'Label', inline = '2', options = ['HHLL', 'HL'])
nameHL       = input.string(    'Short',           '', group = 'Label', inline = '2', options = ['Short', 'Long'], tooltip = K1)
colorLabel   = input.string(    'Trend',      'Color', group = 'Label', inline = '3', options = ['Contrast', 'Custom', 'Trend'])
customLabel  = input.color(color.blue,           '', group = 'Label', inline = '3', tooltip = K2)
sizeLabel    = input.string(   'normal',       'Size', group = 'Label', inline = '4', options = ['tiny',  'small', 'normal', 'large', 'huge'], tooltip = K3)
lineType     = input.string(    'dash',    'DisKlay', group =  'Line', inline = '5', options = ['dash', 'dot', 'solid', 'arrow right', 'arrow left'])
width        = input.int(             1,           '', group =  'Line', inline = '5', minval = 1, maxval = 4, tooltip = K4)
colorLine    = input.string(    'Trend',      'Color', group =  'Line', inline = '6', options = ['Contrast', 'Custom', 'Trend'])
customLine   = input.color(color.blue,           '', group =  'Line', inline = '6', tooltip = K2)
//#endregion

//#region ———————————————————— 1. Types
// @type        Used for label
// @field Hi    Float value of high
// @field Lo    Float value of low
type HL
    string      Hi = na
    string      Lo = na

// @type        Used for point especially for array
// @field x     int value for bar_index   
// @field y     float value for price
// @field sty   label style
// @field col   color for text label
// @field str   high or low string
type point
    int          x = na
    float        y = na
    string     sty = na
    color      col = na
    string     str = na

// @type          Used for initial setup
// @field hi      high value
// @field lo      low value
// @field colorHi color for high value
// @field colorLo color for low value
// @field strHi   string for high value
// @field strLo   string for low value
type startUp
    float       hi = na
    float       lo = na
    color  colorHi = na
    color  colorLo = na
    string   strHi = na
    string   strLo = na
//#endregion

//#region ———————————————————— 2. Switches
[H, L]            = switch nameHL
    'Short'       => [   'H',   'L']
    'Long'        => ['HIGH', 'LOW']
[Hi, Lo]          = switch nameHL
    'Short'       => [       'H',       'L']
    'Long'        => ['HIGHER\n', 'LOWER\n']
switchLine        = switch lineType
    'dash'        => line.style_dashed
    'dot'         => line.style_dotted
    'solid'       => line.style_solid
    'arrow right' => line.style_arrow_right
    'arrow left'  => line.style_arrow_left
switchLabelColor  = switch colorLabel
    'Contrast'    => chart.fg_color
    'Custom'      => customLabel
switchLineColor   = switch colorLine
    'Contrast'    => chart.fg_color
    'Custom'      => customLine
//#endregion

//#region ———————————————————— 3. Variables and arrays
float Kh   = na,  Kh := ta.highestbars(high, length1 ) == 0 ? high : na
float Kl   = na,  Kl := ta.lowestbars(  low, length1 ) == 0 ?  low : na  
var  dir   =  0, dir := Kh and na(Kl) ? 1 : Kl and na(Kh) ? -1 : dir
var zigzag = array.new<point>(0)
oldzigzag  = zigzag.copy()
dirchanged = ta.change(dir)
hiLo       = HL.new(Hi, Lo)
varSetup   = startUp.new(Kh, Kl, colorUp, colorDn, H, L)
//#endregion

//#region ———————————————————— 4. Custom Functions
// @function         variable for point
// @param   setup    type containing ternary conditional operator
// @returns newPoint to be used later in initialize
method dirVariables(startUp setup = na) =>
    var point newPoint = na 
    x   = bar_index
    y   = dir == 1 ?               setup.hi : setup.lo
    sty = dir == 1 ? label.style_label_down : label.style_label_up
    col = dir == 1 ?          setup.colorHi : setup.colorLo
    str = dir == 1 ?            setup.strHi : setup.strLo  
    newPoint  := point.new(x, y, sty, col, str)
    newPoint

// @function         initialize zigzag array
// @param   setup    type containing ternary conditional operator
// @param   maxSize  maximum array size
// @returns zigzag   array after cleanup
method initialize(point[] zigzag = na, startUp setup = na, int maxSize = 10)=>
    newPoint = setup.dirVariables()
    zigzag.unshift(newPoint)
    if zigzag.size() > maxSize
        zigzag.pop()

// @function         update zigzag array
// @param   setup    type containing ternary conditional operator
// @param   maxSize  maximum array size
// @param   dir      direction value
// @returns zigzag   array after cleanup   
method update(point[] zigzag = na, startUp setup = na, maxSize = 10, int dir = na)=>
    if array.size(zigzag) == 0
        zigzag.initialize(setup, maxSize)
    else
        newPoint = setup.dirVariables()
        dirOver = dir ==  1 and newPoint.y > zigzag.get(0).y
        dirLess = dir == -1 and newPoint.y < zigzag.get(0).y
        if dirOver or dirLess
            zigzag.set(0, newPoint)
        point.new(na, na, na, na, na)

// @function          compare zigzag
// @param   zigzag    original array
// @param   oldzigzag copied array
// @returns boolOr    Or statement
// @returns boolAnd   And statement  
method boolPoint(point[] zigzag = na, point[] oldzigzag = na, int offset = 0) =>
    boolOr = zigzag.get(offset + 0).x != oldzigzag.get(offset + 0).x  or zigzag.get(offset + 0).y != oldzigzag.get(offset + 0).y
    boolAnd = zigzag.get(offset + 1).x == oldzigzag.get(offset + 1).x and zigzag.get(offset + 1).y == oldzigzag.get(offset + 1).y
    [boolOr, boolAnd]

// @function         create label based on zigzag array
// @param   zigzag   original array
// @param   size     font size
// @param   offset   default value zero
// @returns          new label
method createLabel(point[] zigzag = na, string size = na, int offset = 0) =>
    label.new(   x         = int(zigzag.get(offset + 0).x),
                 y         =     zigzag.get(offset + 0).y,
                 text      =     zigzag.get(offset + 0).str,
                 xloc      =               xloc.bar_index,
                 color     = color.new(color.blue, 100),
                 style     =     zigzag.get(offset + 0).sty,
                 textcolor =     zigzag.get(offset + 0).col,
                 size      =                         size,
                 tooltip   =     zigzag.get(offset + 0).str + '\n' + str.tostring(zigzag.get(offset + 0).y)) 

// @function         create line based on zigzag array
// @param   zigzag   original array
// @param   width    line thickness
// @param   style    line style
// @param   offset   default value zero
// @returns          new line
method createLine(point[] zigzag = na, int width = na, string style = na, int offset = 0) =>
    line.new(x1    = int(zigzag.get(offset + 1).x),
             y1    =     zigzag.get(offset + 1).y,
             x2    = int(zigzag.get(offset + 0).x),
             y2    =     zigzag.get(offset + 0).y,
             xloc  =        xloc.bar_index,
             color =     zigzag.get(offset + 0).col,
             style = style,
             width = width)

// @function         create line based on zigzag array
// @param   zigzag   original array
// @param   hiLo     switch value
// @param   offset   default value zero
// @returns          ternary conditional of hiLo
method compareHL(point[] zigzag = na, HL hiLo = na, int offset = 0) =>
    dir ==   1 ? zigzag.get(offset + 0).y > zigzag.get(offset + 2).y ? hiLo.Hi : hiLo.Lo :
                 zigzag.get(offset + 0).y < zigzag.get(offset + 2).y ? hiLo.Lo : hiLo.Hi

// @function         set text and tooltip for label
// @param   this     original array
// @param   str      string value for text
// @param   tip      string value for tooltip
method textTip(label this = na, string str = na, string tip = na) =>
    this.set_text(   str)
    this.set_tooltip(tip)
//#endregion

//#region ———————————————————— 5. Execution
if Kh or Kl
    if dirchanged
        zigzag.initialize( varSetup, 4)
    else
        zigzag.update(varSetup, 4, dir)
//#endregion

//#region ———————————————————— 6. Constructs
if zigzag.size() >= 3
    var line   dirLine   = na
    var label  dirLabel  = na
    var string dirString = na
    [boolOr, boolAnd]    = zigzag.boolPoint(oldzigzag)
    if boolOr
        if boolAnd
            dirLine.delete()
            dirLabel.delete()
        if showLabel
            if disKlayLabel == 'HL' or disKlayLabel == 'HHLL'
                dirLabel := zigzag.createLabel(sizeLabel)
            if disKlayLabel == 'HHLL' and zigzag.size() >= 4                             
                dirString  := zigzag.compareHL(hiLo)
                dirLabel.textTip(dirString + zigzag.get(0).str, dirString + zigzag.get(0).str + '\n' + str.tostring(zigzag.get(0).y))
            if colorLabel != 'Trend'
                dirLabel.set_textcolor(switchLabelColor)
        if showLine
            dirLine := zigzag.createLine(width, switchLine)
            if colorLine  != 'Trend'
                dirLine.set_color(switchLineColor)
// //#endregion


//////////////////////////////////////////////////////////////////////////////////////////////////////////////



















// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © FluxChart

//@version=5
const bool DEBUG = false
//indicator("Support & Resistance (MTF) | Flux Charts", overlay = true, max_labels_count = 100, max_lines_count = 100, max_boxes_count = 100, max_bars_back = 305)

const int timeframeCount = 3
const float labelOffsetsY = 1.001
const int labelOffsetsXIndex = 30
const float epsilon = 0.15 / 100.0
const float retestEpsilon = 0.015 / 100.0
const int maxPivotsBackSR = 15
const int retestLabelEveryXBars = 4
const int maxTraverse = 250  // Affects bar history limit. Default value 250.
const int maxRetestLabels = 4
const int maxSupports = 3
const int maxResistances = 3
const float retestPriceDifferencePercentMax = 0.40
const bool calculateTimeSkipMS = false
const int debug_maxPivotLabels = 25

// _____ INPUTS _____
resistanceSupportCount = input.int(3, "Support & Resistance Count", options = [1, 2, 3], group = "General Configuration")
pivotRange = input.int(15, "Pivot Range", options = [5, 15, 30], tooltip = "Increase for more general pivots, decrease for more private pivots.", group = "General Configuration")
strength = input.int(1, "Strength", options = [1, 2, 3, 4], tooltip = "X many times price touched relative price area in order to be considered a support/resistance zone.", group = "General Configuration")
expandLines = input.bool(false,"Expand Lines & Zones", group = "General Configuration")

enableZones = input.bool(false, "Enable Zones", group = "Support & Resistance Zones")
zoneWidth1 = input.int(2, "Zone Width", options = [1,2,3], group = "Support & Resistance Zones")

timeframe1Enabled = input.bool(false, title = "", group = "Timeframes", inline = "timeframe1")
timeframe1 = input.timeframe("", title = "", group = "Timeframes", inline = "timeframe1")
timeframe2Enabled = input.bool(false, title = "", group = "Timeframes", inline = "timeframe2")
timeframe2 = input.timeframe("60", title = "", group = "Timeframes", inline = "timeframe2")
timeframe3Enabled = input.bool(false, title = "", group = "Timeframes", inline = "timeframe3")
timeframe3 = input.timeframe("240", title = "", group = "Timeframes", inline = "timeframe3")

showBreaks = input.bool(false,"Show Breaks", group = "Breaks & Retests", inline = "ShowBR")
showRetests = input.bool(false,"Show Retests", group = "Breaks & Retests", inline = "ShowBR")
avoidFalseBreaks = input.bool(false,"Avoid False Breaks", group = "Breaks & Retests")
falseBreakoutVolumeThreshold = input.int(30, "Break Volume Threshold %", minval = 0, maxval = 100, group = "Breaks & Retests", tooltip = "Only taken into account if Avoid False Breakouts is enabled.\nHigher values mean it's less likely to be a break.")
inverseBrokenLineColor = input.bool(false,"Inverse Color After Broken", tooltip = "Needs Show Breaks & Expand Lines option enabled.", group = "Breaks & Retests")

lineStyle1 = input.string("____", "Line Style", ["____", "----", "...."], group = "Style")
lineWidth1 = input.int(2, "Line Width", minval = 1, group = "Style")
supportColor = input.color(#08d0af14, "Support Color", group = "Style", inline = "RScolors")
resistanceColor = input.color(#f4ba0b14, "Resistance Color", group = "Style", inline = "RScolors")
textColor = input.color(#ffffff80, "Text Color", group = "Style", inline = "RScolors")

enableRetestAlerts = input.bool(false, "Enable Retest Alerts", tooltip = "Needs Show Retests option enabled.", group = "Alerts")
enableBreakAlerts = input.bool(false, "Enable Break Alerts", group = "Alerts")

memoryOptimizatonEnabled = input.bool(false, "Enable Memory Optimization", tooltip = "Enable this option if you encounter memory errors.", group = "Advanced")
// _____ INPUTS END _____

// _____ DEBUG OPTIONS _____
debug_labelPivots = not DEBUG ? "None" : input.string("None", title = "[DBG] Label Pivots", group = "DEBUG", options = ["All", "RS", "None"], tooltip = "All -> Debugs all pivot labels.\nRS -> Debugs RS pivot labels.\nNone -> Debugs none of the last R&S pivots.")
debug_pivotLabelText = not DEBUG ? false : input.bool(false, title = "[DBG] Pivot Label Text", group = "DEBUG")
debug_showBrokenOnLabel = not DEBUG ? false : input.bool(false, "[DBG] Show Broken Text On Label", group = "DEBUG")
debug_removeDuplicateRS = not DEBUG ? true : input.bool(true, "[DBG] Remove Duplicate RS", group = "DEBUG")
debug_lastXResistances = not DEBUG ? 3 : input.int(3, "[DBG] Show Last X Resistances", minval = 0, maxval = maxResistances, group = "DEBUG")
debug_lastXSupports = not DEBUG ? 3 : input.int(3, "[DBG] Show Last X Supports", minval = 0, maxval = maxSupports, group = "DEBUG")
debug_enabledHistory = not DEBUG ? true : input.bool(true, "[DBG] Enable History", group = "DEBUG")
debug_maxHistoryRecords = not DEBUG ? 10 : input.int(10, "[DBG] Max History Records", options =  [1, 2, 5, 10, 25], group = "DEBUG")
// _____ DEBUG OPTIONS END _____

createRSLine (color) =>
    line.new(na, na, na, na, extend = expandLines ? extend.both : extend.none, xloc=xloc.bar_time, color = color, width = lineWidth, style = lineStyle == "----" ? line.style_dashed : lineStyle == "...." ? line.style_dotted : line.style_solid)

createRSBox (color, xlocType) =>
    box.new(na, na, na, na, text_size = size.normal, xloc = xlocType, extend = extend.both, bgcolor = color, text_color = textColor, text_halign = expandLines ? text.align_right : text.align_center, border_color = #00000000)

createRSLabel () =>
    label.new(na, na, "", style = label.style_none, textcolor = textColor)

createBreakLabel (RSType) =>
    label.new(na,na,style = RSType == "Resistance" ? label.style_label_up : label.style_label_down, color=color.rgb(33, 149, 243, 100), textcolor = color.rgb(255, 255, 255, 100), xloc = xloc.bar_time, size = size.tiny)

createRetestLabel (RSType) =>
    label.new(na,na,style = RSType == "Resistance" ? label.style_label_down : label.style_label_up, color = RSType == "Resistance" ? resistanceColor : supportColor, textcolor = color.rgb(255, 255, 255, 100), xloc = xloc.bar_time, size = size.tiny)

moveLine(_line, _x, _y, _x2) =>
    line.set_xy1(_line, _x,  _y)
    line.set_xy2(_line, _x2, _y)

moveBox (_box, _topLeftX, _topLeftY, _bottomRightX, _bottomRightY) =>
    box.set_lefttop(_box, _topLeftX, _topLeftY)
    box.set_rightbottom(_box, _bottomRightX, _bottomRightY)

moveRSInfoBox (_box, _startPointX, _price, _endPointX) =>
    zoneWidthPercent = zoneWidth == 1 ? 0.05 : zoneWidth == 2 ? 0.06 : 0.075
    topY = _price * (1.0 + (zoneWidthPercent / 2.0 / 100.0))
    bottomY = _price * (1.0 - (zoneWidthPercent / 2.0 / 100.0))
    moveBox(_box, _startPointX, topY, _endPointX, bottomY)

// _____ TYPES _____

type RSInfo
    bool isBroken = na
    int brokenTime = na
    string RSType = na
    float price = na
    line line = na
    box box = na
    label priceLabel = na
    chart.point[] points = na
    label[] debugPoints = na
    label breakLabel = na
    label[] retestLabels = na
    line breakLine = na
    box breakBox = na

newRSInfo (RSType) =>
    newRSInfo = RSInfo.new()
    newRSInfo.RSType := RSType
    newRSInfo.price := na
    newRSInfo.isBroken := false
    newRSInfo.brokenTime := na

    newRSInfo.line := enableZones ? na : createRSLine(RSType == "Resistance" ? resistanceColor : supportColor)
    newRSInfo.box := enableZones ? createRSBox(RSType == "Resistance" ? resistanceColor : supportColor, xloc.bar_time) : na
    newRSInfo.priceLabel := enableZones ? na : createRSLabel()
    newRSInfo.points := array.new<chart.point>(0)
    newRSInfo.debugPoints := array.new<label>(0)
    newRSInfo.retestLabels := array.new<label>(0)
    newRSInfo.breakLabel := na
    newRSInfo.breakLine := na
    newRSInfo.breakBox := na
    
    newRSInfo

histRSInfo (RSInfo RSInfoF) =>
    RSType = RSInfoF.RSType
    newRS = RSInfo.new()
    newRS.RSType := RSType
    newRS.price := RSInfoF.price

    newRS.debugPoints := array.new<label>(0)
    newRS.retestLabels := array.new<label>(0)
    newRS.points := array.new<chart.point>(0)

    histText = "History | " + str.tostring(newRS.price, format.mintick)

    startTime = math.min(time, RSInfoF.points.get(strength - 1).time)
    endTime = RSInfoF.isBroken ? RSInfoF.brokenTime : time

    if enableZones
        newRS.box := createRSBox(RSType == "Resistance" ? resistanceColor : supportColor, xloc.bar_time)
        moveRSInfoBox(newRS.box, startTime, newRS.price, endTime)
        box.set_extend(newRS.box, expandLines ? extend.both : extend.none)
        box.set_text(newRS.box, histText)
    else
        newRS.line := line.copy(RSInfoF.line)
        moveLine(newRS.line, startTime, newRS.price, endTime)
        line.set_extend(newRS.line, expandLines ? extend.both : extend.none)

        newRS.priceLabel := label.copy(RSInfoF.priceLabel)
        label.set_text(newRS.priceLabel, histText)
        label.set_xloc(newRS.priceLabel, (startTime + endTime) / 2, xloc.bar_time)
    
    if not na(newRS.breakLabel)
        newRS.breakLabel := label.copy(RSInfoF.breakLabel)
    
    newRS

safeDeleteRSInfo (RSInfo RSInfoF) =>
    if not na(RSInfoF)
        line.delete(RSInfoF.line)
        box.delete(RSInfoF.box)
        label.delete(RSInfoF.priceLabel)

        RSInfoF.points.clear()

        if RSInfoF.debugPoints.size() > 0
            for i = 0 to RSInfoF.debugPoints.size() - 1
                label.delete(RSInfoF.debugPoints.get(i))
            RSInfoF.debugPoints.clear()

        if RSInfoF.retestLabels.size() > 0
            for i = 0 to RSInfoF.retestLabels.size() - 1
                label.delete(RSInfoF.retestLabels.get(i))
            RSInfoF.retestLabels.clear()
        
        label.delete(RSInfoF.breakLabel)
        line.delete(RSInfoF.breakLine)
        box.delete(RSInfoF.breakBox)

type timeframeInfo
    int index = na
    string timeframeStr = na
    bool isEnabled = false

    RSInfo[] resistances = na
    RSInfo[] supports = na

    float[] highPivots = na
    int[] highTimes = na

    float[] lowPivots = na
    int[] lowTimes = na

newTimeframeInfo (index, timeframeStr, isEnabled) =>
    newTFInfo = timeframeInfo.new()
    newTFInfo.index := index
    newTFInfo.isEnabled := isEnabled
    newTFInfo.timeframeStr := timeframeStr

    newTFInfo.resistances := array.new<RSInfo>(debug_lastXResistances)
    newTFInfo.supports := array.new<RSInfo>(debug_lastXSupports)
    
    newTFInfo.highPivots := array.new<float>()
    newTFInfo.highTimes := array.new<int>()

    newTFInfo.lowPivots := array.new<float>()
    newTFInfo.lowTimes := array.new<int>()
    newTFInfo

// _____ TYPES END _____

// _____ VARS _____

var timeframeInfo[] timeframeInfos = array.from(newTimeframeInfo(1, timeframe1, timeframe1Enabled), newTimeframeInfo(2, timeframe2, timeframe2Enabled), newTimeframeInfo(3, timeframe3, timeframe3Enabled))
var bool initRun = true
var int maxTimeskipMS = 0

var float[] allLowPivots = array.new<float>(0)
var float[] allHighPivots = array.new<float>(0)
var int[] allLowTimes = array.new<int>(0)
var int[] allHighTimes = array.new<int>(0)

var RSInfo[] history = array.new<RSInfo>(0)

RSInfo[] curRSList = array.new<RSInfo>(0)
RSInfo[] oldRSList = array.new<RSInfo>(0)

int maxPivotsAllowed = memoryOptimizatonEnabled ? 10 : 15 // Affects memory limit. Default value 30.

// _____ VARS END _____

doValuesTouch (float value1, float value2) =>
    if math.abs(value1 - value2) / ((value1 + value2) / 2.0) <= epsilon
        true
    else
        false

doValuesTouch (float value1, float value2, float customEpsilon) =>
    if math.abs(value1 - value2) / ((value1 + value2) / 2.0) <= customEpsilon
        true
    else
        false

findLatestRS (timeframeInfo timeframeInfoF, string RSType, pivots, times, bannedValues) =>
    RSInfo latestRSF = na
    pivotsCount = pivots.size()
    if pivotsCount > 0
        for i = 0 to pivotsCount - 1
            if i >= maxTraverse
                break
            
            index = pivotsCount - i - 1
            occurances = 0
            invalidValue = false
            pivotValue1 = pivots.get(index)
            if bannedValues.size() > 0
                for a = 0 to bannedValues.size() - 1
                    if doValuesTouch(pivotValue1, bannedValues.get(a))
                        invalidValue := true
                        break
            
            if invalidValue
                continue
            
            for j = 0 to pivotsCount - 1
                if j >= maxTraverse
                    break
                
                index2 = pivotsCount - j - 1
                pivotValue2 = pivots.get(index2)
                if doValuesTouch(pivotValue1, pivotValue2)
                    occurances += 1
                
                if occurances >= strength
                    latestRSF := newRSInfo(RSType)
                    latestRSF.price := pivotValue1
                    break
                
                if math.abs(index - index2) > maxPivotsBackSR * strength
                    break
                
            if not na(latestRSF)
                break
            
    if not na(latestRSF)
        cnt = 0
        if pivotsCount > 0
            for i = 0 to pivotsCount - 1
                if i >= maxTraverse
                    break
                
                index = pivotsCount - i - 1
                pivotValue = pivots.get(index)
                if doValuesTouch(pivotValue, latestRSF.price)
                    labelTime = times.get(index)
                    latestRSF.points.push(chart.point.from_time(labelTime, pivotValue))
                    cnt += 1
                if cnt == strength
                    break

    if not (debug_labelPivots == "None")
        if not (debug_labelPivots == "All")
            if not na(latestRSF)
                cnt = 0
                if pivotsCount > 0
                    for i = 0 to pivotsCount - 1
                        index = pivotsCount - i - 1
                        pivotValue = pivots.get(index)
                        if doValuesTouch(pivotValue, latestRSF.price)
                            labelTime = times.get(index)
                            latestRSF.debugPoints.push(RSType == "Resistance" ? label.new(labelTime,pivotValue,text=debug_pivotLabelText ? str.tostring(pivotValue) : "",xloc=xloc.bar_time, color=resistanceColor, textcolor=color.white) : label.new(labelTime,pivotValue,text=debug_pivotLabelText ? str.tostring(pivotValue) : "",xloc=xloc.bar_time, color=supportColor,style = label.style_label_up, textcolor=color.white))
                            cnt += 1
                        if cnt == strength
                            break
        else
            if not na(latestRSF)
                if pivotsCount > 0
                    for i = 0 to pivotsCount - 1
                        index = pivotsCount - i - 1
                        pivotValue = pivots.get(index)
                        labelTime = times.get(index)
                        latestRSF.debugPoints.push(RSType == "Resistance" ? label.new(labelTime,pivotValue,text=debug_pivotLabelText ? str.tostring(pivotValue) : "",xloc=xloc.bar_time, color=resistanceColor, textcolor=color.white) : label.new(labelTime,pivotValue,text=debug_pivotLabelText ? str.tostring(pivotValue) : "",xloc=xloc.bar_time, color=supportColor,style = label.style_label_up, textcolor=color.white))
                        if latestRSF.debugPoints.size() > debug_maxPivotLabels
                            break
    latestRSF

findLatestNthRS (timeframeInfo timeframeInfoF, string RSType, pivots, times, n) =>
    float[] bannedValues = array.new<float>()
    foundRS = 0
    RSInfo foundLatestRS = na
    while foundRS < n
        foundLatestRS := findLatestRS(timeframeInfoF, RSType, pivots, times, bannedValues)
        if not na(foundLatestRS)
            foundRS += 1
            bannedValues.push(foundLatestRS.price)
        else
            break
    foundLatestRS

isTimeframeLower (timeframe1F, timeframe2F) =>
    timeframe.in_seconds(timeframe1F) < timeframe.in_seconds(timeframe2F)

getMinTimeframe (timeframe1F, timeframe2F) =>
    if isTimeframeLower(timeframe1F, timeframe2F)
        timeframe1F
    else
        timeframe2F

getMaxTimeframe (timeframe1F, timeframe2F) =>
    if isTimeframeLower(timeframe1F, timeframe2F)
        timeframe2F
    else
        timeframe1F

getFirstBreak (RSInfo rsInfo) =>
    if na(rsInfo)
        [na, na]
    
    curIndex = 0
    float foundBreakLevel = na
    int foundBreakTime = na
    while true
        if curIndex >= maxTraverse
            break
        isBarBreak = rsInfo.RSType == "Resistance" ? (close[curIndex + 1] <= rsInfo.price and close[curIndex] > rsInfo.price) : (close[curIndex + 1] >= rsInfo.price and close[curIndex] < rsInfo.price)
        if isBarBreak
            isTrueBreakout = true
            if avoidFalseBreaks
                shortTerm = 5
                longTerm = 15
                
                shortSum = 0.0
                longSum = 0.0
                
                for i = 0 to shortTerm
                    shortSum += volume[curIndex + i]
                
                for i = 0 to longTerm
                    longSum += volume[curIndex + i]
                
                shortVolumeAvg = shortSum / shortTerm
                longVolumeAvg = longSum / longTerm

                volumeRatio = ((shortVolumeAvg - longVolumeAvg) / longVolumeAvg) * 100.0
                isTrueBreakout := (volumeRatio >= falseBreakoutVolumeThreshold)

            if isTrueBreakout
                foundBreakLevel := rsInfo.RSType == "Resistance" ? low[curIndex] : high[curIndex]
                foundBreakTime := time[curIndex]
            
        curIndex += 1
        if time[curIndex] <= rsInfo.points.get(strength - 1).time
            break
    [foundBreakLevel, foundBreakTime]

getRetests (RSInfo rsInfo) =>
    if na(rsInfo)
        [na,na]
    
    curIndex = 0
    lastRetestIndex = -999
    int[] retestTimes = array.new<int>()
    float[] retestLevels = array.new<float>()

    while true
        if curIndex >= maxTraverse
            break
        if retestLevels.size() == maxRetestLabels
            break
        if rsInfo.isBroken and time[curIndex] >= rsInfo.brokenTime
            curIndex += 1
            continue

        differencePercent = 100.0 * math.min(math.abs(rsInfo.price - close[curIndex]) / ((rsInfo.price + close[curIndex]) / 2), rsInfo.RSType == "Resistance" ? math.abs(rsInfo.price - high[curIndex]) / ((rsInfo.price + high[curIndex]) / 2) : math.abs(rsInfo.price - low[curIndex]) / ((rsInfo.price + low[curIndex]) / 2))
        isRetest = (rsInfo.RSType == "Resistance" ? (doValuesTouch(rsInfo.price, close[curIndex], retestEpsilon) or doValuesTouch(rsInfo.price, high[curIndex], retestEpsilon) or high[curIndex] > rsInfo.price) : (doValuesTouch(rsInfo.price, close[curIndex], retestEpsilon) or doValuesTouch(rsInfo.price, low[curIndex], retestEpsilon) or low[curIndex] < rsInfo.price)) and differencePercent < retestPriceDifferencePercentMax
        if isRetest and curIndex - lastRetestIndex >= retestLabelEveryXBars
            retestLevels.push(rsInfo.RSType == "Resistance" ? high[curIndex] : low[curIndex])
            retestTimes.push(time[curIndex])
            lastRetestIndex := curIndex
        curIndex += 1
        if time[curIndex] <= rsInfo.points.get(strength - 1).time
            break
    [retestLevels, retestTimes]

formatTimeframeString (formatTimeframe) =>
    timeframeF = formatTimeframe == "" ? timeframe.period : formatTimeframe
    
    if str.contains(timeframeF, "D") or str.contains(timeframeF, "W") or str.contains(timeframeF, "S") or str.contains(timeframeF, "M")
        timeframeF
    else
        seconds = timeframe.in_seconds(timeframeF)
        if seconds >= 3600
            hourCount = int(seconds / 3600)
            str.tostring(hourCount) + " Hour" + (hourCount > 1 ? "s" : "")
        else
            timeframeF + " Min"

getPivot (pivotType) =>
    pivot = (pivotType == "high" ? ta.pivothigh(high, pivotRange, pivotRange) : ta.pivotlow(low, pivotRange, pivotRange))
    pivot

handleRSInfo (timeframeInfo timeframeInfoF, RSInfo RSInfoF, int index, string RSType) =>
    if not na(RSInfoF)
        if not na(timeframeInfoF)
            curRSList.push(RSInfoF)
        
        [foundBreakLevel, foundBreakTime] = getFirstBreak(RSInfoF)
        
        RSInfoF.isBroken := na(foundBreakLevel) ? false : true
        RSInfoF.brokenTime := na(foundBreakLevel) ? na : foundBreakTime

        if not na(foundBreakLevel)
            if showBreaks
                if na(RSInfoF.breakLabel)
                    RSInfoF.breakLabel := createBreakLabel(RSInfoF.RSType)
                label.set_xy(RSInfoF.breakLabel, foundBreakTime, foundBreakLevel * (RSInfoF.RSType == "Resistance" ? (1.0 / labelOffsetsY) : labelOffsetsY))
            
            if expandLines
                if na(RSInfoF.breakLine) and enableZones == false
                    RSInfoF.breakLine := createRSLine(color.black)
                
                if na(RSInfoF.breakBox) and enableZones == true
                    RSInfoF.breakBox := createRSBox(color.black, xloc.bar_time)

                if not enableZones
                    line.set_extend(RSInfoF.breakLine, extend.right)
                else
                    box.set_extend(RSInfoF.breakBox, extend.right)
                
                if inverseBrokenLineColor and showBreaks
                    if not enableZones
                        line.set_color(RSInfoF.breakLine, RSInfoF.RSType == "Resistance" ? supportColor : resistanceColor)
                    else    
                        box.set_bgcolor(RSInfoF.breakBox, RSInfoF.RSType == "Resistance" ? supportColor : resistanceColor)
                else
                    if not enableZones
                        line.set_color(RSInfoF.breakLine, RSInfoF.RSType == "Resistance" ? resistanceColor : supportColor)
                    else
                        box.set_bgcolor(RSInfoF.breakBox, RSInfoF.RSType == "Resistance" ? resistanceColor : supportColor)

        if showRetests
            [retestLevels, retestTimes] = getRetests(RSInfoF)

            if not na(retestLevels) and retestLevels.size() > 0
                for i = 0 to retestLevels.size() - 1
                    newRetestLabel = createRetestLabel(RSInfoF.RSType)
                    label.set_xy(newRetestLabel, retestTimes.get(i), retestLevels.get(i) * (RSInfoF.RSType == "Support" ? (1.0 / labelOffsetsY) : labelOffsetsY))
                    RSInfoF.retestLabels.push(newRetestLabel)

        //timeSkipOffset = maxTimeskipMS / 4
        timeSkipOffset = 0
        if enableZones
            zoneEndX = time + timeSkipOffset + timeframe.in_seconds(timeframe.period) * 1000 * labelOffsetsXIndex 
            startTime = math.min(time, RSInfoF.points.get(strength - 1).time)
            moveRSInfoBox(RSInfoF.box, startTime, RSInfoF.price, na(foundBreakTime) ? zoneEndX : foundBreakTime)
            moveRSInfoBox(RSInfoF.breakBox, foundBreakTime, RSInfoF.price, zoneEndX)
        else
            endTime = time + timeSkipOffset + timeframe.in_seconds(timeframe.period) * 1000
            startTime = math.min(time, RSInfoF.points.get(strength - 1).time)
            moveLine(RSInfoF.line, startTime, RSInfoF.price, na(foundBreakTime) ? endTime : foundBreakTime)
            moveLine(RSInfoF.breakLine, foundBreakTime, RSInfoF.price, endTime)
            //log.info(str.tostring(RSInfoF.price) + " | " + str.tostring(RSInfoF.points.get(strength - 1).time) + " = "  + str.tostring(line.get_x1(RSInfoF.line)) + " | " + str.tostring(endTime) + " = " + str.tostring(line.get_x2(RSInfoF.line)))
        
        if expandLines
            if not enableZones
                line.set_extend(RSInfoF.line, (na(foundBreakTime)) ? extend.both : extend.left)
            else    
                box.set_extend(RSInfoF.box, (na(foundBreakTime)) ? extend.both : extend.left)
        else
            if not enableZones
                line.set_extend(RSInfoF.line, na(foundBreakTime) ? extend.right : extend.none)
            else
                box.set_extend(RSInfoF.box, na(foundBreakTime) ? extend.right : extend.none)

        //labelTitleOld = formatTimeframeString(timeframeInfoF.timeframeStr) + " " + RSInfoF.RSType + " " + str.tostring(index + 1) + " (" + str.tostring(RSInfoF.price,format.mintick) + ")" + (RSInfoF.isBroken ? " [Broken]" : "")
        labelTitle = formatTimeframeString(timeframeInfoF.timeframeStr) + " | " + str.tostring(RSInfoF.price,format.mintick) + ((debug_showBrokenOnLabel and RSInfoF.isBroken)  ? " [B]" : "")
        
        if not enableZones
            label.set_text(RSInfoF.priceLabel, enableZones ? "" : labelTitle)
            label.set_y(RSInfoF.priceLabel, RSInfoF.price)
        else
            box.set_text(RSInfoF.box, (RSInfoF.isBroken and expandLines) ? "" : labelTitle)
            box.set_text(RSInfoF.breakBox, labelTitle)
        
        if expandLines or not RSInfoF.isBroken
            if not enableZones
                label.set_xloc(RSInfoF.priceLabel, bar_index + labelOffsetsXIndex, xloc.bar_index)
            else
                box.set_text_halign(RSInfoF.breakBox, text.align_right)
                box.set_text_halign(RSInfoF.box, text.align_right)
        else
            if not enableZones
                label.set_xloc(RSInfoF.priceLabel, (RSInfoF.points.get(strength - 1).time + RSInfoF.brokenTime) / 2, xloc.bar_time)
            else
                box.set_text_halign(RSInfoF.box, text.align_center)
                box.set_text_halign(RSInfoF.breakBox, text.align_center)
    else
        log.error("Couldn't find timeframe " + str.tostring(timeframeInfoF.index) + " " + str.tostring(index + 1) + "th " + RSType + " . Try decreasing pivot range in the settings.")

handleTimeframe (timeframeIndex, lowPivots, highPivots, lowTimes, highTimes) =>
    timeframeInfoF = timeframeInfos.get(timeframeIndex - 1)
    
    timeframeInfoF.lowPivots.clear()
    timeframeInfoF.highPivots.clear()
    timeframeInfoF.lowTimes.clear()
    timeframeInfoF.highTimes.clear()

    timeframeInfoF.lowPivots := lowPivots
    timeframeInfoF.highPivots := highPivots
    timeframeInfoF.lowTimes := lowTimes
    timeframeInfoF.highTimes := highTimes

getHigherTFData (timeframeStr) =>
    request.security(syminfo.tickerid, getMaxTimeframe(timeframe.period, timeframeStr), [allLowPivots, allHighPivots, allLowTimes, allHighTimes])

pushHighPivots (timeframeInfoF, highPivotF, timeF) =>
    if not na(highPivotF)
        timeframeInfoF.highPivots.push(highPivotF)
        timeframeInfoF.highTimes.push(timeF)

pushLowPivots (timeframeInfoF, lowPivotF, timeF) =>
    if not na(lowPivotF)
        timeframeInfoF.lowPivots.push(lowPivotF)
        timeframeInfoF.lowTimes.push(timeF)

handleTimeframeIfLower (timeframeInfo timeframeInfoF, highs, lows, int[] timesF) =>
    if timeframeInfoF.isEnabled and isTimeframeLower(timeframeInfoF.timeframeStr, timeframe.period)
        if highs.size() > 0
            for i = 0 to highs.size() - 1
                timeF = timesF.get(i)
                pushHighPivots(timeframeInfoF, highs.get(i), timeF)
        if lows.size() > 0
            for i = 0 to lows.size() - 1
                timeF = timesF.get(i)
                pushLowPivots(timeframeInfoF, lows.get(i), timeF)

getLowerTFData (timeframeStr) =>
    lowPivots = isTimeframeLower(timeframeStr, timeframe.period) ? request.security_lower_tf(syminfo.tickerid, getMinTimeframe(timeframeStr, timeframe.period), ta.pivotlow(low, pivotRange, pivotRange)) : na
    highPivots = isTimeframeLower(timeframeStr, timeframe.period) ? request.security_lower_tf(syminfo.tickerid, getMinTimeframe(timeframeStr, timeframe.period), ta.pivothigh(high, pivotRange, pivotRange)) : na
    times = isTimeframeLower(timeframeStr, timeframe.period) ? request.security_lower_tf(syminfo.tickerid, getMinTimeframe(timeframeStr, timeframe.period), time[pivotRange]) : na
    [lowPivots,highPivots,times,times]

getTFData (timeframeStr) =>
    if isTimeframeLower(timeframeStr, timeframe.period)
        getLowerTFData(timeframeStr)
    else
        getHigherTFData(timeframeStr)

checkIfRSAreSame (RSInfo rsInfo1, RSInfo rsInfo2) =>
    if na(rsInfo1) or na(rsInfo2)
        false
    else if rsInfo1.RSType != rsInfo2.RSType
        false
    else if rsInfo1.price != rsInfo2.price
        false
    else
        true

checkIfArrHasRS (RSInfo[] arr, RSInfo rsInfoF) =>
    if na(arr) or na(rsInfoF)
        true
    else if arr.size() == 0
        false
    else
        foundRS = false
        for i = 0 to arr.size() - 1
            arrRS = arr.get(i)
            if checkIfRSAreSame(arrRS, rsInfoF)
                foundRS := true
                break
        if foundRS
            true
        else
            false

clearTimeframeRS (timeframeInfoF) =>
    oldRetestsCount = 0
    oldBreaksCount = 0

    if timeframeInfoF.resistances.size() > 0
        for j = 0 to timeframeInfoF.resistances.size() - 1
            RSInfo RSInfoF = timeframeInfoF.resistances.get(j)
            if not na(RSInfoF)
                if debug_enabledHistory
                    if checkIfArrHasRS(oldRSList, RSInfoF) == false
                        oldRSList.push(RSInfoF)

                oldRetestsCount += RSInfoF.retestLabels.size()
                oldBreaksCount += RSInfoF.isBroken ? 1 : 0
    
    if timeframeInfoF.supports.size() > 0
        for j = 0 to timeframeInfoF.supports.size() - 1
            RSInfo RSInfoF = timeframeInfoF.supports.get(j)
            if not na(RSInfoF)
                if debug_enabledHistory
                    if checkIfArrHasRS(history, RSInfoF) == false
                        oldRSList.push(RSInfoF)
                
                oldRetestsCount += RSInfoF.retestLabels.size()
                oldBreaksCount += RSInfoF.isBroken ? 1 : 0
    
    timeframeInfoF.resistances.clear()
    timeframeInfoF.supports.clear()
    [oldRetestsCount, oldBreaksCount]

findTimeframeRS (timeframeInfoF, RSType, arr, count, pivots, times) =>
    curRetestsCount = 0
    curBreaksCount = 0

    if count > 0
        for j = 0 to count - 1
            foundRS = findLatestNthRS(timeframeInfoF, RSType, pivots, times, j + 1)
            if not na(foundRS)
                notDuplicate = true
                for a = 0 to timeframeInfos.size() - 1
                    aInfo = timeframeInfos.get(a)
                    if na(aInfo) or aInfo.isEnabled == false
                        continue
                    otherTimeframeArray = (RSType == "Resistance" ? aInfo.resistances : aInfo.supports)
                    if otherTimeframeArray.size() > 0
                        for b = 0 to otherTimeframeArray.size() - 1
                            if checkIfRSAreSame(foundRS, otherTimeframeArray.get(b))
                                notDuplicate := false
                                break
                    if notDuplicate == false
                        break
                
                if notDuplicate or not debug_removeDuplicateRS
                    arr.push(foundRS)
        
        if arr.size() > 0
            for j = 0 to arr.size() - 1
                curRS = arr.get(j)
                if not na(curRS)
                    handleRSInfo(timeframeInfoF, curRS, j, RSType)
                    curRetestsCount += curRS.retestLabels.size()
                    curBreaksCount += curRS.isBroken ? 1 : 0
    [curRetestsCount, curBreaksCount]
    
lowPivot = getPivot("low")
highPivot = getPivot("high")

if not na(lowPivot)
    allLowPivots.push(lowPivot)
    allLowTimes.push(time[pivotRange])
    if allLowPivots.size() > maxPivotsAllowed
        allLowPivots.remove(0)
        allLowTimes.remove(0)
        

if not na(highPivot)
    allHighPivots.push(highPivot)
    allHighTimes.push(time[pivotRange])
    if allHighPivots.size() > maxPivotsAllowed
        allHighPivots.remove(0)
        allHighTimes.remove(0)

if calculateTimeSkipMS
    if last_bar_index - bar_index < 350 and time - time[1] > timeframe.in_seconds(timeframe.period) * 1000
        maxTimeskipMS := math.max(maxTimeskipMS, time - time[1])

[lowPivotsTF1, highPivotsTF1, lowTimesTF1, highTimesTF1] = getTFData(timeframe1)
handleTimeframeIfLower(timeframeInfos.get(0), highPivotsTF1, lowPivotsTF1, highTimesTF1)

[lowPivotsTF2, highPivotsTF2, lowTimesTF2, highTimesTF2] = getTFData(timeframe2)
handleTimeframeIfLower(timeframeInfos.get(1), highPivotsTF2, lowPivotsTF2, highTimesTF2)

[lowPivotsTF3, highPivotsTF3, lowTimesTF3, highTimesTF3] = getTFData(timeframe3)
handleTimeframeIfLower(timeframeInfos.get(2), highPivotsTF3, lowPivotsTF3, highTimesTF3)

//plot(nz(na,timeframeInfos.get(0).highPivots.size() > 0 ? timeframeInfos.get(0).highPivots.get(timeframeInfos.get(0).highPivots.size() - 1) : 0), color = color.blue, title = "High Pivots")
//plot(nz(na,timeframeInfos.get(0).lowPivots.size() > 0 ? timeframeInfos.get(0).lowPivots.get(timeframeInfos.get(0).lowPivots.size() - 1) : 0), color = color.fuchsia, title = "Low Pivots")

if barstate.islast
    
    if timeframe1Enabled and not isTimeframeLower(timeframe1, timeframe.period)
        handleTimeframe(1, lowPivotsTF1, highPivotsTF1, lowTimesTF1, highTimesTF1)

    if timeframe2Enabled and not isTimeframeLower(timeframe2, timeframe.period)
        handleTimeframe(2, lowPivotsTF2, highPivotsTF2, lowTimesTF2, highTimesTF2)
    
    if timeframe3Enabled and not isTimeframeLower(timeframe3, timeframe.period)
        handleTimeframe(3, lowPivotsTF3, highPivotsTF3, lowTimesTF3, highTimesTF3)

    int enabledTimeframeCount = 0
    for i = 0 to timeframeCount - 1
        timeframeInfo curInfo = timeframeInfos.get(i)
        if curInfo.isEnabled
            enabledTimeframeCount += 1
    
    int oldRetestsCount = 0
    int curRetestsCount = 0

    int oldBreaksCount = 0
    int curBreaksCount = 0

    for i = 0 to timeframeCount - 1
        timeframeInfo curInfo = timeframeInfos.get(i)
        if not curInfo.isEnabled
            continue

        [oldRetests, oldBreaks] = clearTimeframeRS(curInfo)
        oldRetestsCount += oldRetests
        oldBreaksCount += oldBreaks

        resistanceCount = math.min(DEBUG ? debug_lastXResistances : resistanceSupportCount, maxResistances - (enabledTimeframeCount > 1 ? 1 : 0))
        resistanceCount := math.max(resistanceCount, 0)

        supportCount = math.min(DEBUG ? debug_lastXSupports : resistanceSupportCount, maxSupports - (enabledTimeframeCount > 1 ? 1 : 0))
        supportCount := math.max(supportCount, 0)

        [curRetests1, curBreaks1] = findTimeframeRS(curInfo, "Resistance", curInfo.resistances, resistanceCount, curInfo.highPivots, curInfo.highTimes)
        [curRetests2, curBreaks2] = findTimeframeRS(curInfo, "Support", curInfo.supports, supportCount, curInfo.lowPivots, curInfo.lowTimes)
        curRetestsCount += curRetests1 + curRetests2
        curBreaksCount += curBreaks1 + curBreaks2
    
    if debug_enabledHistory
        historyIndexesToDelete = array.new<int>(0)
        if history.size() > 0
            for i = 0 to history.size() - 1
                if checkIfArrHasRS(curRSList, history.get(i))
                    historyIndexesToDelete.push(i)
            
            if historyIndexesToDelete.size() > 0
                for i = 0 to historyIndexesToDelete.size() - 1
                    deleteIndex = historyIndexesToDelete.get(historyIndexesToDelete.size() - i - 1)
                    safeDeleteRSInfo(history.get(deleteIndex))
                    history.remove(deleteIndex)
        
        if oldRSList.size() > 0
            for i = 0 to oldRSList.size() - 1
                curRS = oldRSList.get(i)
                if checkIfArrHasRS(curRSList, curRS) == false
                    history.push(histRSInfo(curRS))
                    if history.size() > debug_maxHistoryRecords
                        safeDeleteRSInfo(history.get(0))
                        history.remove(0)

    
    if oldRSList.size() > 0
        for i = 0 to oldRSList.size() - 1
            safeDeleteRSInfo(oldRSList.get(i))
    
    curRSList.clear()
    oldRSList.clear()

    if DEBUG
        log.info("History Size : " + str.tostring(history.size()))
        log.info("Label Count : " + str.tostring(label.all.size()))
        log.info("Line Count : " + str.tostring(line.all.size()))
        log.info("Box Count : " + str.tostring(box.all.size()))

    if enableRetestAlerts and curRetestsCount > oldRetestsCount and initRun == false
        alert("New Retests Occured.")
    
    if enableBreakAlerts and curBreaksCount > oldBreaksCount and initRun == false
        alert("New Breaks Occured.")
    
    initRun := false












/////////////////////// ORDER BLOCK FINDER /////////////////////////////////////


colors = input.string(title='Color Scheme', defval='BRIGHT', options=['DARK', 'BRIGHT'])
periods = input(5, 'Relevant Periods to identify OB')  // Required number of subsequent candles in the same direction to identify Order Block
threshold = input.float(0.0, 'Min. Percent move to identify OB', step=0.1)  // Required minimum % move (from potential OB close to last subsequent candle to identify Order Block)
usewicks = input(false, 'Use whole range [High/Low] for OB marking?')  // Display High/Low range for each OB instead of Open/Low for Bullish / Open/High for Bearish
showbull = input(true, 'Show latest Bullish Channel?')  // Show Channel for latest Bullish OB?
showbear = input(true, 'Show latest Bearish Channel?')  // Show Channel for latest Bearish OB?
showdocu = input(false, 'Show Label for documentation tooltip?')  // Show Label which shows documentation as tooltip?
info_pan = input(false, 'Show Latest OB Panel?')  // Show Info Panel with latest OB Stats

ob_period = periods + 1  // Identify location of relevant Order Block candle
absmove = math.abs(close[ob_period] - close[1]) / close[ob_period] * 100  // Calculate absolute percent move from potential OB to last candle of subsequent candles
relmove = absmove >= threshold  // Identify "Relevant move" by comparing the absolute move to the threshold

// Color Scheme
bullcolor = colors == 'DARK' ? color.white : color.green
bearcolor = colors == 'DARK' ? color.blue : color.red

// Bullish Order Block Identification
bullishOB = close[ob_period] < open[ob_period]  // Determine potential Bullish OB candle (red candle)

int upcandles = 0
for i = 1 to periods by 1
    upcandles += (close[i] > open[i] ? 1 : 0)  // Determine color of subsequent candles (must all be green to identify a valid Bearish OB)
    upcandles

OB_bull = bullishOB and upcandles == periods and relmove  // Identification logic (red OB candle & subsequent green candles)
OB_bull_high = OB_bull ? usewicks ? high[ob_period] : open[ob_period] : na  // Determine OB upper limit (Open or High depending on input)
OB_bull_low = OB_bull ? low[ob_period] : na  // Determine OB lower limit (Low)
OB_bull_avg = (OB_bull_high + OB_bull_low) / 2  // Determine OB middle line


// Bearish Order Block Identification
bearishOB = close[ob_period] > open[ob_period]  // Determine potential Bearish OB candle (green candle)

int downcandles = 0
for i = 1 to periods by 1
    downcandles += (close[i] < open[i] ? 1 : 0)  // Determine color of subsequent candles (must all be red to identify a valid Bearish OB)
    downcandles

OB_bear = bearishOB and downcandles == periods and relmove  // Identification logic (green OB candle & subsequent green candles)
OB_bear_high = OB_bear ? high[ob_period] : na  // Determine OB upper limit (High)
OB_bear_low = OB_bear ? usewicks ? low[ob_period] : open[ob_period] : na  // Determine OB lower limit (Open or Low depending on input)
OB_bear_avg = (OB_bear_low + OB_bear_high) / 2  // Determine OB middle line


// Plotting

plotshape(OB_bull, title='Bullish OB', style=shape.triangleup, color=bullcolor, textcolor=bullcolor, size=size.tiny, location=location.belowbar, offset=-ob_period, text=' Bullish', editable = false)  // Bullish OB Indicator
plotshape(OB_bear, title='Bearish OB', style=shape.triangledown, color=bearcolor, textcolor=bearcolor, size=size.tiny, location=location.abovebar, offset=-ob_period, text=' Bearish', editable = false)  // Bearish OB Indicator



//Truncate Function
truncate(number, decimals) =>
    factor = math.pow(10, decimals)
    int(number * factor) / factor

//
// === INPUTS ===
TPSType = input.string('Trailing',   'What TPS should be taken : ', options = ['ATR', 'Trailing', 'Options'])
setupType = input.string('Open/Close', title='What Trading Setup should be taken : ', options=['Open/Close', 'Renko'])
scolor = input(true, title='Show coloured Bars to indicate Trend?')
almaRibbon = input(false, title='Enable Ribbon?')
//tradeType = input.string('BOTH', title='What trades should be taken : ', options=['LONG', 'SHORT', 'BOTH', 'NONE'])
// === /INPUTS ===

// Display the probabilities in a table
//text01_ = str.tostring(timeframe.multiplier * intRes, '####')

//t = timenow + math.round(ta.change(time) * 25)
//var label lab01 = na
//label.delete(lab01)
//lab01 := label.new(t, close, text=text01_, style=label.style_label_left, yloc=yloc.price, xloc=xloc.bar_time, textalign=text.align_left, textcolor=color.white)

// Constants colours that include fully non-transparent option.
green100 = #008000FF
lime100 = #00FF00FF
red100 = #FF0000FF
blue100 = #0000FFFF
aqua100 = #00FFFFFF
darkred100 = #8B0000FF
gray100 = #808080FF

/////////////////////////////////////////////
// Create non-repainting security function
rp_security(_symbol, _res, _src) =>
    request.security(_symbol, _res, _src[1])

//
f_tfInMinutes() =>
    _tfInMinutes = timeframe.period == '1' ? '3' : timeframe.period == '3' ? '5' : timeframe.period == '5' ? '15' : timeframe.period == '15' ? '30' : timeframe.period == '30' ? '60' : timeframe.period == '60' ? '240' : 'D'
    _tfInMinutes
my_time1 = f_tfInMinutes()

tfmult = 18 //input.int(18, "Input Timeframe Multiplier")

f_resInMinutes() => 
    _resInMinutes = timeframe.multiplier * (
      timeframe.isseconds ? 1. / 60. :
      timeframe.isminutes ? 1.       :
      timeframe.isdaily   ? 1440.    :
      timeframe.isweekly  ? 10080.   :
      timeframe.ismonthly ? 43800.   : na)
my_time = str.tostring(f_resInMinutes()*tfmult)

useSource = close //input.string('Close',   'What Source to be used?', options = ['Close', 'HL2'])
enableFilter = input(true,  "Enable Backtesting Range Filtering")
fromDate     = input.time(timestamp("01 Jan 2023 00:00 +0300"), "Start Date")
toDate       = input.time(timestamp("31 Dec 2099 00:00 +0300"), "End Date")

tradeDateIsAllowed = not enableFilter or (time >= fromDate and time <= toDate)

filter1 = 'Filter with Atr'
filter2 = 'Filter with RSI'
filter3 = 'Atr or RSI'
filter4 = 'Atr and RSI'
filter5 = 'No Filtering'
filter6 = 'Entry Only in sideways market(By ATR or RSI)'
filter7 = 'Entry Only in sideways market(By ATR and RSI)'
typefilter = input.string(filter5, title='Sideways Filtering Input', options=[filter1, filter2, filter3, filter4, filter5, filter6, filter7], group='Strategy Options')

RSI = truncate(ta.rsi(close, input.int(7, group='RSI Filterring')), 2)
toplimitrsi = input.int(45, title='TOP Limit', group='RSI Filterring')
botlimitrsi = input.int(10, title='BOT Limit', group='RSI Filterring')


//ST = input.bool(true, title='Show Supertrend?', group='Supertrend Indicator')
//period = input.int(1440, group='Supertrend Indicator')
//mult = input.float(2.612, group='Supertrend Indicator')
atrfiltLen = 5 //input.int(5, minval=1, title='atr Length', group='Sideways Filtering Input')
atrMaType = 'EMA' //input.string('EMA', options=['SMA', 'EMA'], group='Sideways Filtering Input', title='atr Moving Average Type')
atrMaLen = 5 //input.int(5, minval=1, title='atr MA Length', group='Sideways Filtering Input')

//filtering
atra = request.security(syminfo.tickerid, '', ta.atr(atrfiltLen))
atrMa = atrMaType == 'EM' ? ta.ema(atra, atrMaLen) : ta.sma(atra, atrMaLen)
updm = ta.change(high)
downdm = -ta.change(low)
plusdm = na(updm) ? na : updm > downdm and updm > 0 ? updm : 0
minusdm = na(downdm) ? na : downdm > updm and downdm > 0 ? downdm : 0

cndSidwayss1 = atra >= atrMa
cndSidwayss2 = RSI > toplimitrsi or RSI < botlimitrsi
cndSidways = cndSidwayss1 or cndSidwayss2
cndSidways1 = cndSidwayss1 and cndSidwayss2
Sidwayss1 = atra <= atrMa
Sidwayss2 = RSI < toplimitrsi and RSI > botlimitrsi
Sidways = Sidwayss1 or Sidwayss2
Sidways1 = Sidwayss1 and Sidwayss2

trendType = typefilter == filter1 ? cndSidwayss1 : typefilter == filter2 ? cndSidwayss2 : typefilter == filter3 ? cndSidways : typefilter == filter4 ? cndSidways1 : typefilter == filter5 ? RSI > 0 : typefilter == filter6 ? Sidways : typefilter == filter7 ? Sidways1 : na

// === /INPUTS ===

tf = my_time  //input('15')

r = ticker.heikinashi(syminfo.tickerid)
openSeriesAlt = request.security(r, tf, open, lookahead=barmerge.lookahead_on)
closeSeriesAlt = request.security(r, tf, close, lookahead=barmerge.lookahead_on)
//openP = plot(almaRibbon ? openSeriesAlt : na, color=color.new(color.lime, 0), linewidth=3)
//closeP = plot(almaRibbon ? closeSeriesAlt : na, color=color.new(color.red, 0), linewidth=3)

BUYOC = ta.crossover(closeSeriesAlt, openSeriesAlt) and setupType == "Open/Close" and trendType
SELLOC = ta.crossunder(closeSeriesAlt, openSeriesAlt) and setupType == "Open/Close" and trendType

//strategy.entry('sell', direction=strategy.short, qty=trade_size, comment='sell', when=sel_entry)
//strategy.entry('buy', direction=strategy.long, qty=trade_size, comment='buy', when=buy_entry)

//trendColour = closeSeriesAlt > openSeriesAlt ? color.green : color.red
//bcolour = closeSeriesAlt > openSeriesAlt ? lime100 : red100
//barcolor(scolor ? bcolour : na, title='Bar Colours')
//closeP = plot(almaRibbon ? closeSeriesAlt : na, title='Close Series', color=color.new(trendColour, 20), linewidth=2, style=plot.style_line)
//openP = plot(almaRibbon ? openSeriesAlt : na, title='Open Series', color=color.new(trendColour, 20), linewidth=2, style=plot.style_line)
//fill(closeP, openP, color=color.new(trendColour, 80))
//

//rt = input(true, title="ATR Based REnko is the Default, UnCheck to use Traditional ATR?")
atrLen = 3 //input.int(3, title="RENKO_ATR", group = "Renko Settings")
isATR = true //input.bool(true, title="RENKO_USE_RENKO_ATR", group = "Renko Settings")
tradLen1 = 1000 //input.int(1000, title="RENKO_TRADITIONAL", group = "Renko Settings")

//Code to be implemented in V2
//mul = input(1, "Number Of minticks")
//value = mul * syminfo.mintick

tradLen = tradLen1 * 1

param = isATR ? ticker.renko(syminfo.tickerid, "ATR", atrLen) : ticker.renko(syminfo.tickerid, "Traditional", tradLen)

renko_close = request.security(param, my_time, close, lookahead=barmerge.lookahead_on)
renko_open = request.security(param, my_time, open, lookahead=barmerge.lookahead_on)

//============================================
//Sniper------------------------------------------------------------------------------------------------------------------------------------- // Signal 2
//============================================

//============================================
//EMA_CROSS-------------------------------------------------------------------------------------------------------------------------------- // Signal 4
//============================================

EMA1_length=input.int(2, "EMA1_length", group = "Renko Settings")
EMA2_length=input.int(10, "EMA2_length", group = "Renko Settings")

a = ta.ema(renko_close, EMA1_length)
b = ta.ema(renko_close, EMA2_length)

//BUY = ta.cross(a, b) and a > b and renko_open < renko_close
//SELL = ta.cross(a, b) and a < b and renko_close < renko_open
///////////////////////////////
// Determine long and short conditions
BUYR = ta.crossover(a, b) and setupType == "Renko" and trendType
SELLR = ta.crossunder(a, b) and setupType == "Renko" and trendType

sel_color = setupType == "Open/Close" ? closeSeriesAlt < openSeriesAlt : setupType == "Renko" ? renko_close < renko_open : na
buy_color = setupType == "Open/Close" ? closeSeriesAlt > openSeriesAlt : setupType == "Renko" ? renko_close > renko_open : na

sel_entry = setupType == "Open/Close" ? SELLOC : setupType == "Renko" ? SELLR : na
buy_entry = setupType == "Open/Close" ? BUYOC : setupType == "Renko" ? BUYR : na

trendColour = buy_color ? color.green : color.red
bcolour = buy_color ? lime100 : red100
barcolor(scolor ? bcolour : na, title='Bar Colours')

p11=plot(almaRibbon and setupType == "Open/Close" ? closeSeriesAlt : almaRibbon and setupType == "Renko" ? renko_close : na, style=plot.style_circles, linewidth=1, color=color.new(trendColour, 80), title="RENKO_1")
p22=plot(almaRibbon and setupType == "Open/Close" ? openSeriesAlt : almaRibbon and setupType == "Renko" ? renko_open : na, style=plot.style_circles, linewidth=1, color=color.new(trendColour, 80), title="RENKO_2")
fill(p11, p22, color=color.new(trendColour, 50), title="RENKO_fill")

//<triggers>
lxTrigger    = false
sxTrigger    = false
leTrigger    = buy_entry
seTrigger    = sel_entry

// === /ALERT conditions.
BUYX = leTrigger //ta.crossover(closeSeriesAlt, openSeriesAlt)
SELLX = seTrigger //ta.crossunder(closeSeriesAlt, openSeriesAlt)

varip wasLong = false
varip wasShort = false

if barstate.isconfirmed
    wasLong := false
else 
    if BUYX
        wasLong := true


if barstate.isconfirmed
    wasShort := false
else 
    if SELLX
        wasShort := true

plotshape(wasLong, color = color.yellow)
plotshape(wasShort, color = color.yellow)

//plotshape(almaRibbon ? BUYX : na,  title = "BUYX",  text = 'BUYX',  style = shape.labelup,   location = location.belowbar, color= #39ff14, textcolor = #FFFFFF, size = size.tiny)
//plotshape(almaRibbon ? SELLX : na, title = "Exit", text = 'Exit', style = shape.labeldown, location = location.abovebar, color= #ff1100, textcolor = #FFFFFF, size = size.tiny)

// === STRATEGY ===
i_alert_txt_entry_long = "Short Exit" //input.text_area(defval = "Short Exit", title = "Long Entry Message", group = "Alerts")
i_alert_txt_exit_long = "Long Exit" //input.text_area(defval = "Long Exit", title = "Long Exit Message", group = "Alerts")
i_alert_txt_entry_short = "Go Short" //input.text_area(defval = "Go Short", title = "Short Entry Message", group = "Alerts")
i_alert_txt_exit_short = "Go Long" //input.text_area(defval = "Go Long", title = "Short Exit Message", group = "Alerts")
// Entries and Exits with TP/SL

//tradeType
if BUYX and TPSType == "Trailing" and tradeDateIsAllowed
    strategy.close("Short" , alert_message = i_alert_txt_exit_short)
    strategy.entry("Long" , strategy.long , alert_message = i_alert_txt_entry_long)    

if SELLX and TPSType == "Trailing" and tradeDateIsAllowed
    strategy.close("Long" , alert_message = i_alert_txt_exit_long)
    strategy.entry("Short" , strategy.short, alert_message = i_alert_txt_entry_short)

//tradeType
if BUYX and TPSType == "Options" and tradeDateIsAllowed
//    strategy.close("Short" , alert_message = i_alert_txt_exit_short)
    strategy.entry("Long" , strategy.long , alert_message = i_alert_txt_entry_long)    

if SELLX and TPSType == "Options" and tradeDateIsAllowed
    strategy.close("Long" , alert_message = i_alert_txt_exit_long)
//    strategy.entry("Short" , strategy.short, alert_message = i_alert_txt_entry_short)


G_RISK       = '■ ' + 'Risk Management'
//#region ———— <↓↓↓ G_RISK ↓↓↓> {

//ATR SL Settings
atrLength = 20 //input.int(20, minval=1, title='ATR Length')
profitFactor = 2.5 //input(2.5, title='Take Profit Factor')
stopFactor = 1 //input(1.0, title='Stop Loss Factor')

// Calculate ATR
tpatrValue = ta.atr(atrLength)

// Calculate take profit and stop loss levels for BUYX signals
takeProfit1_BUYX = 1 * profitFactor * tpatrValue //close + profitFactor * atrValue
takeProfit2_BUYX = 2 * profitFactor * tpatrValue //close + 2 * profitFactor * atrValue
takeProfit3_BUYX = 3 * profitFactor * tpatrValue //close + 3 * profitFactor * atrValue
stopLoss_BUYX = close - takeProfit1_BUYX //stopFactor * tpatrValue

// Calculate take profit and stop loss levels for SELLX signals
takeProfit1_SELLX = 1 * profitFactor * tpatrValue //close - profitFactor * atrValue
takeProfit2_SELLX = 2 * profitFactor * tpatrValue //close - 2 * profitFactor * atrValue
takeProfit3_SELLX = 3 * profitFactor * tpatrValue //close - 3 * profitFactor * atrValue
stopLoss_SELLX = close + takeProfit1_SELLX //stopFactor * tpatrValue

// ——————————— <constant_declarations>
//Tooltip
T_LVL        = '(%) Exit Level'
T_QTY        = '(%) Adjust trade exit volume'
T_MSG        = 'Paste JSON message for your bot'
//Webhook Message
O_LEMSG      = 'Long Entry'
O_LXMSGSL    = 'Long SL'
O_LXMSGTP1   = 'Long TP1'
O_LXMSGTP2   = 'Long TP2'
O_LXMSGTP3   = 'Long TP3'
O_LXMSG      = 'Long Exit'
O_SEMSG      = 'Short Entry'
O_SXMSGSL    = 'Short SL'
O_SXMSGA     = 'Short TP1'
O_SXMSGB     = 'Short TP2'
O_SXMSGC     = 'Short TP3'
O_SXMSGX     = 'Short Exit'

// on whole pips) for forex currency pairs.
pip_size = syminfo.mintick * (syminfo.type == "forex" ? 10 : 1)

// On the last historical bar, show the instrument's pip size
//if barstate.islastconfirmedhistory
//    label.new(x=bar_index + 2, y=close, style=label.style_label_left,
//         color=color.navy, textcolor=color.white, size=size.large, 
//         text=syminfo.ticker + "'s pip size is:\n" + 
//             str.tostring(pip_size))
// ——————————— <input>          |           |                               |                        Line length guide |
i_lxLvlTP1   = leTrigger ? takeProfit1_BUYX : seTrigger ? takeProfit1_SELLX : na //input.float     (1,         'Level TP1'                     , group = G_RISK,     tooltip =                   T_LVL)
i_lxQtyTP1   = input.float (50,        'Qty   TP1'                     , group = G_RISK,     tooltip =                   T_QTY)
i_lxLvlTP2   = leTrigger ? takeProfit2_BUYX : seTrigger ? takeProfit2_SELLX : na //input.float     (1.5,       'Level TP2'                     , group = G_RISK,     tooltip =                   T_LVL)
i_lxQtyTP2   = input.float (30,        'Qty   TP2'                     , group = G_RISK,     tooltip =                   T_QTY)
i_lxLvlTP3   = leTrigger ? takeProfit3_BUYX : seTrigger ? takeProfit3_SELLX : na //input.float     (2,         'Level TP3'                     , group = G_RISK,     tooltip =                   T_LVL)
i_lxQtyTP3   = input.float (20,        'Qty   TP3'                     , group = G_RISK,     tooltip =                   T_QTY)
i_lxLvlSL    = leTrigger ? takeProfit1_BUYX : seTrigger ? takeProfit1_SELLX : na //input.float     (0.5,       'Stop Loss'                     , group = G_RISK,     tooltip =                   T_LVL)

i_sxLvlTP1   = i_lxLvlTP1
i_sxQtyTP1   = i_lxQtyTP1
i_sxLvlTP2   = i_lxLvlTP2
i_sxQtyTP2   = i_lxQtyTP2
i_sxLvlTP3   = i_lxLvlTP3
i_sxQtyTP3   = i_lxQtyTP3
i_sxLvlSL    = i_lxLvlSL

G_MSG        = '■ ' + 'Webhook Message'
i_leMsg      = O_LEMSG //input.string     (O_LEMSG   ,'Long Entry'                   , group = G_MSG, tooltip = T_MSG)
i_lxMsgSL    = O_LXMSGSL //input.string     (O_LXMSGSL ,'Long SL'                      , group = G_MSG, tooltip = T_MSG)
i_lxMsgTP1   = O_LXMSGTP1 //input.string     (O_LXMSGTP1,'Long TP1'                     , group = G_MSG, tooltip = T_MSG)
i_lxMsgTP2   = O_LXMSGTP2 //input.string     (O_LXMSGTP2,'Long TP2'                     , group = G_MSG, tooltip = T_MSG)
i_lxMsgTP3   = O_LXMSGTP3 //input.string     (O_LXMSGTP3,'Long TP3'                     , group = G_MSG, tooltip = T_MSG)
i_lxMsg      = O_LXMSG //input.string     (O_LXMSG   ,'Long Exit'                    , group = G_MSG, tooltip = T_MSG)
i_seMsg      = O_SEMSG //input.string     (O_SEMSG   ,'Short Entry'                  , group = G_MSG, tooltip = T_MSG)
i_sxMsgSL    = O_SXMSGSL //input.string     (O_SXMSGSL ,'Short SL'                     , group = G_MSG, tooltip = T_MSG)
i_sxMsgTP1   = O_SXMSGA //input.string     (O_SXMSGA  ,'Short TP1'                    , group = G_MSG, tooltip = T_MSG)
i_sxMsgTP2   = O_SXMSGB //input.string     (O_SXMSGB  ,'Short TP2'                    , group = G_MSG, tooltip = T_MSG)
i_sxMsgTP3   = O_SXMSGC //input.string     (O_SXMSGC  ,'Short TP3'                    , group = G_MSG, tooltip = T_MSG)
i_sxMsg      = O_SXMSGX //input.string     (O_SXMSGX  ,'Short Exit'                   , group = G_MSG, tooltip = T_MSG)
i_src        = close

G_DISPLAY    = 'Display'
//<display>
i_alertOn    = true //input.bool       (true,      'Alert Labels On/Off'          , group = G_DISPLAY)
i_barColOn   = true //input.bool       (true,      'Bar Color On/Off'             , group = G_DISPLAY)

// ——————————— <function_declarations>
// @function        Calculate the Take Profit line, and the crossover or crossunder
f_tp(_condition, _conditionValue, _leTrigger, _seTrigger, _src, _lxLvlTP, _sxLvlTP)=>
    var float _tpLine = 0.0
    _topLvl     = _src + _lxLvlTP //TPSType == "Fixed %" ? _src + (_src * (_lxLvlTP / 100)) : _src + _lxLvlTP
    _botLvl     = _src - _lxLvlTP //TPSType == "Fixed %" ? _src - (_src * (_sxLvlTP / 100)) : _src - _sxLvlTP
    _tpLine    := _condition[1] !=  _conditionValue and _leTrigger ? _topLvl :
                  _condition[1] != -_conditionValue and _seTrigger ? _botLvl :
                  nz(_tpLine[1])
    [_tpLine]

// @function        Similar to "ta.crossover" or "ta.crossunder"
f_cross(_scr1, _scr2, _over)=>
    _cross   = _over ? _scr1 > _scr2 and _scr1[1] < _scr2[1] :
                       _scr1 < _scr2 and _scr1[1] > _scr2[1]

// ——————————— <calculations>
//<set initial values>
var float condition = 0.0
var float slLine    = 0.0
var float entryLine = 0.0

//<entry & exit orders>
entryLine   := leTrigger and condition[1] <=  0.0 ? close :
               seTrigger and condition[1] >=  0.0 ? close : nz(entryLine[1])
//<SL>
slTopLvl    = TPSType == "Fixed %" ? i_src + (i_src * (i_lxLvlSL / 100)) : i_src + i_lxLvlSL
slBotLvl    = TPSType == "Fixed %" ? i_src - (i_src * (i_sxLvlSL / 100)) : i_src - i_lxLvlSL
slLine     := condition[1] <=  0.0 and leTrigger ? slBotLvl :
              condition[1] >=  0.0 and seTrigger ? slTopLvl : nz(slLine[1])
slLong      = f_cross(low,  slLine, false)
slShort     = f_cross(high, slLine, true )
//<TP1, TP2 & TP3>
[tp3Line]    = f_tp(condition, 1.2,leTrigger, seTrigger, i_src, i_lxLvlTP3, i_sxLvlTP3)
[tp2Line]    = f_tp(condition, 1.1,leTrigger, seTrigger, i_src, i_lxLvlTP2, i_sxLvlTP2)
[tp1Line]    = f_tp(condition, 1.0,leTrigger, seTrigger, i_src, i_lxLvlTP1, i_sxLvlTP1)
tp3Long      = f_cross(high, tp3Line, true )
tp3Short     = f_cross(low,  tp3Line, false)
tp2Long      = f_cross(high, tp2Line, true )
tp2Short     = f_cross(low,  tp2Line, false)
tp1Long      = f_cross(high, tp1Line, true )
tp1Short     = f_cross(low,  tp1Line, false)

switch
    leTrigger and condition[1] <=  0.0 => condition :=  1.0
    seTrigger and condition[1] >=  0.0 => condition := -1.0
    tp3Long   and condition[1] ==  1.2 => condition :=  1.3
    tp3Short  and condition[1] == -1.2 => condition := -1.3
    tp2Long   and condition[1] ==  1.1 => condition :=  1.2
    tp2Short  and condition[1] == -1.1 => condition := -1.2
    tp1Long   and condition[1] ==  1.0 => condition :=  1.1
    tp1Short  and condition[1] == -1.0 => condition := -1.1
    slLong    and condition[1] >=  1.0 => condition :=  0.0
    slShort   and condition[1] <= -1.0 => condition :=  0.0
    lxTrigger and condition[1] >=  1.0 => condition :=  0.0
    sxTrigger and condition[1] <= -1.0 => condition :=  0.0

longE        = leTrigger and condition[1] <=  0.0 and condition ==  1.0
shortE       = seTrigger and condition[1] >=  0.0 and condition == -1.0
longX        = lxTrigger and condition[1] >=  1.0 and condition ==  0.0
shortX       = sxTrigger and condition[1] <= -1.0 and condition ==  0.0
longSL       = slLong    and condition[1] >=  1.0 and condition ==  0.0
shortSL      = slShort   and condition[1] <= -1.0 and condition ==  0.0
longTP3      = tp3Long   and condition[1] ==  1.2 and condition ==  1.3
shortTP3     = tp3Short  and condition[1] == -1.2 and condition == -1.3
longTP2      = tp2Long   and condition[1] ==  1.1 and condition ==  1.2
shortTP2     = tp2Short  and condition[1] == -1.1 and condition == -1.2
longTP1      = tp1Long   and condition[1] ==  1.0 and condition ==  1.1
shortTP1     = tp1Short  and condition[1] == -1.0 and condition == -1.1

// ——————————— <strategy_calls> {
//<long orders>
if strategy.position_size <= 0 and longE and TPSType == "ATR" and tradeDateIsAllowed
    strategy.entry(      'Long',      strategy.long,      alert_message    = i_leMsg,      comment          = 'LE')
if strategy.position_size > 0 and condition ==  1.0 and TPSType == "ATR" and tradeDateIsAllowed
    strategy.exit(      id               = 'LXTP1',      from_entry       = 'Long',      qty_percent      = i_lxQtyTP1,      limit            = tp1Line,      stop             = slLine,      comment_profit   = 'LXTP1',      comment_loss     = 'SL',      alert_profit     = i_lxMsgTP1,      alert_loss       = i_lxMsgSL)
if strategy.position_size > 0 and condition ==  1.1 and TPSType == "ATR" and tradeDateIsAllowed
    strategy.exit(      id               = 'LXTP2',      from_entry       = 'Long',      qty_percent      = i_lxQtyTP2,      limit            = tp2Line,      stop             = slLine,      comment_profit   = 'LXTP2',      comment_loss     = 'SL',      alert_profit     = i_lxMsgTP2,      alert_loss       = i_lxMsgSL)
if strategy.position_size > 0 and condition ==  1.2 and TPSType == "ATR" and tradeDateIsAllowed
    strategy.exit(      id               = 'LXTP3',      from_entry       = 'Long',      qty_percent      = i_lxQtyTP3,      limit            = tp3Line,      stop             = slLine,      comment_profit   = 'LXTP3',      comment_loss     = 'SL',      alert_profit     = i_lxMsgTP3,      alert_loss       = i_lxMsgSL)
if longX and tradeDateIsAllowed
    strategy.close(      'Long',      alert_message    = i_lxMsg,      comment          = 'LX')
//<short orders>
if strategy.position_size >= 0 and shortE and TPSType == "ATR" and tradeDateIsAllowed
    strategy.entry(      'Short',      strategy.short,      alert_message    = i_leMsg,      comment          = 'SE')
if strategy.position_size < 0 and condition == -1.0 and TPSType == "ATR" and tradeDateIsAllowed
    strategy.exit(      id               = 'SXTP1',      from_entry       = 'Short',      qty_percent      = i_sxQtyTP1,      limit            = tp1Line,      stop             = slLine,      comment_profit   = 'SXTP1',      comment_loss     = 'SL',      alert_profit     = i_sxMsgTP1,      alert_loss       = i_sxMsgSL)
if strategy.position_size < 0 and condition == -1.1 and TPSType == "ATR" and tradeDateIsAllowed
    strategy.exit(      id               = 'SXTP2',      from_entry       = 'Short',      qty_percent      = i_sxQtyTP2,      limit            = tp2Line,      stop             = slLine,      comment_profit   = 'SXTP2',      comment_loss     = 'SL',      alert_profit     = i_sxMsgTP2,      alert_loss       = i_sxMsgSL)
if strategy.position_size < 0 and condition == -1.2 and TPSType == "ATR" and tradeDateIsAllowed
    strategy.exit(      id               = 'SXTP3',      from_entry       = 'Short',      qty_percent      = i_sxQtyTP3,      limit            = tp3Line,      stop             = slLine,      comment_profit   = 'SXTP3',      comment_loss     = 'SL',      alert_profit     = i_sxMsgTP3,      alert_loss       = i_sxMsgSL)
if shortX and tradeDateIsAllowed
    strategy.close(      'Short',      alert_message    = i_sxMsg,      comment          = 'SX')

// ——————————— <visuals>
c_tp         = leTrigger or seTrigger ? na :
               condition == 0.0       ? na : color.green
c_entry      = leTrigger or seTrigger ? na :
               condition == 0.0       ? na : color.blue
c_sl         = leTrigger or seTrigger ? na :
               condition == 0.0       ? na : color.red
p_tp1Line    = plot (  condition ==  1.0 or  condition == -1.0  ?  tp1Line : na,  title      = "TP Line 1",  color      = c_tp,  linewidth  = 1,  style      = plot.style_linebr)
p_tp2Line    = plot (  condition ==  1.0 or  condition == -1.0 or    condition ==  1.1 or  condition == -1.1  ? tp2Line : na,  title      = "TP Line 2",  color      = c_tp,  linewidth  = 1,  style      = plot.style_linebr)
p_tp3Line    = plot (  condition ==  1.0 or  condition == -1.0 or    condition ==  1.1 or  condition == -1.1 or  condition ==  1.2 or  condition == -1.2  ? tp3Line : na,  title      = "TP Line 3",  color      = c_tp,  linewidth  = 1,  style      = plot.style_linebr)
p_entryLine  = plot (  condition >=  1.0 or  condition <= -1.0  ? entryLine : na,  title      = "Entry Line",  color      = c_entry,  linewidth  = 1,  style      = plot.style_linebr)
p_slLine     = plot (  condition ==  1.0 or  condition == -1.0 or    condition ==  1.1 or  condition == -1.1 or  condition ==  1.2 or  condition == -1.2  ? slLine : na,  title      = "SL Line",  color      = c_sl,  linewidth  = 1,  style      = plot.style_linebr)

//fill(  p_tp3Line, p_entryLine,  color      = leTrigger or seTrigger ? na :color.new(color.green, 90))
fill(  p_entryLine, p_slLine,  color      = leTrigger or seTrigger ? na :color.new(color.red, 90))

//<alerts labels>
plotshape(  i_alertOn and longE,  title      = 'Long',  text       = 'Long',  textcolor  = color.white,  color      = color.green,  style      = shape.labelup,  size       = size.tiny,  location   = location.belowbar)
plotshape(  i_alertOn and shortE,  title      = 'Short',  text       = 'Short',  textcolor  = color.white,  color      = color.red,  style      = shape.labeldown,  size       = size.tiny,  location   = location.abovebar)
plotshape(  i_alertOn and (longX or shortX) ? close : na,  title      = 'Close',  text       = 'Close',  textcolor  = color.white,  color      = color.gray,  style      = shape.labelup,  size       = size.tiny,  location   = location.absolute)
l_tp         = i_alertOn and (longTP1 or shortTP1) ? close : na

plotshape(  l_tp,  title      = "TP1 Cross",  text       = "TP1",  textcolor  = color.white,  color      = color.olive,  style      = shape.labelup,  size       = size.tiny,  location   = location.absolute)
plotshape(  i_alertOn and (longTP2 or shortTP2) ? close : na,  title      = "TP2 Cross",  text       = "TP2",  textcolor  = color.white,  color      = color.olive,  style      = shape.labelup,  size       = size.tiny,  location   = location.absolute)
plotshape(  i_alertOn and (longTP3 or shortTP3) ? close : na,  title      = "TP3 Cross",  text       = "TP3",  textcolor  = color.white,  color      = color.olive,  style      = shape.labelup,  size       = size.tiny,  location   = location.absolute)
plotshape(  i_alertOn and (longSL or shortSL) ? close : na,  title      = "SL Cross",  text       = "SL",  textcolor  = color.white,  color      = color.maroon,  style      = shape.labelup,  size       = size.tiny,  location   = location.absolute)

//<debug>
plot(  na,  title      = "─── <debug> ───",  editable   = false,  display    = display.data_window)
plot(  condition,  title      = "condition",  editable   = false,  display    = display.data_window)
plot(  strategy.position_size * 100,  title      = ".position_size",  editable   = false,  display    = display.data_window)
//#endregion }
// ——————————— <↑↑↑ G_RISK ↑↑↑>

//#region ———— <↓↓↓ G_SCRIPT02 ↓↓↓> {
// @function        Queues a new element in an array and de-queues its first element.
f_qDq(_array, _val) =>
    array.push(_array, _val)
    _return = array.shift(_array)
    _return

var line[]  a_slLine     = array.new_line(1)
var line[]  a_entryLine  = array.new_line(1)
var line[]  a_tp3Line    = array.new_line(1)
var line[]  a_tp2Line    = array.new_line(1)
var line[]  a_tp1Line    = array.new_line(1)
var label[] a_slLabel    = array.new_label(1)
var label[] a_tp3label   = array.new_label(1)
var label[] a_tp2label   = array.new_label(1)
var label[] a_tp1label   = array.new_label(1)
var label[] a_entryLabel = array.new_label(1)

newEntry     = longE or shortE
entryIndex   = 1
entryIndex   := newEntry ? bar_index : nz(entryIndex[1])
lasTrade     = bar_index >= entryIndex
l_right      = 10

if TPSType == "ATR"
    line.delete(  f_qDq(a_slLine,  line.new(   entryIndex,   slLine,   last_bar_index + l_right,   slLine,   style = line.style_solid,   color = c_sl))) 

if TPSType == "ATR"
    line.delete(  f_qDq(a_entryLine,  line.new(   entryIndex,   entryLine,   last_bar_index + l_right,   entryLine,   style = line.style_solid,   color = color.blue)))

if TPSType == "ATR"
    line.delete(  f_qDq(a_tp3Line,  line.new(   entryIndex,   tp3Line,   last_bar_index + l_right,   tp3Line,   style = line.style_solid,   color = c_tp)))

if TPSType == "ATR"
    line.delete(  f_qDq(a_tp2Line,  line.new(   entryIndex,   tp2Line,   last_bar_index + l_right,   tp2Line,   style = line.style_solid,   color = c_tp)))

if TPSType == "ATR"
    line.delete(  f_qDq(a_tp1Line,  line.new(   entryIndex,   tp1Line,   last_bar_index + l_right,   tp1Line,   style = line.style_solid,   color = c_tp)))

if TPSType == "ATR"
    label.delete(  f_qDq(a_slLabel,  label.new(   last_bar_index + l_right,   slLine,   'SL: ' + str.tostring(slLine, '##.###'),   style = label.style_label_left,   textcolor  = color.white,   color = c_sl)))

if TPSType == "ATR"
    label.delete(  f_qDq(a_entryLabel,  label.new(   last_bar_index + l_right,   entryLine,   'Entry: ' + str.tostring(entryLine, '##.###'),   style = label.style_label_left,   textcolor  = color.white,   color = color.blue)))

if TPSType == "ATR"
    label.delete(  f_qDq(a_tp3label,  label.new(   last_bar_index + l_right,   tp3Line,   'TP3: ' + str.tostring(tp3Line, '##.###') + " - Target Pips : - " + str.tostring(longE ? tp3Line - entryLine : entryLine - tp3Line, "#.##"),   style = label.style_label_left,   textcolor  = color.white,   color = c_tp)))

if TPSType == "ATR"
    label.delete(  f_qDq(a_tp2label,  label.new(   last_bar_index + l_right,   tp2Line,   'TP2: ' + str.tostring(tp2Line, '##.###'),   style = label.style_label_left,   textcolor  = color.white,   color = c_tp)))

if TPSType == "ATR"
    label.delete(  f_qDq(a_tp1label,  label.new(   last_bar_index + l_right,   tp1Line,   'TP1: ' + str.tostring(tp1Line, '##.###'),   style = label.style_label_left,   textcolor  = color.white,   color = c_tp)))

//#endregion }
// ——————————— <↑↑↑ G_SCRIPT02 ↑↑↑>

c_barCol = close > open ? color.rgb(120, 9, 139) : color.rgb(69, 155, 225)
barcolor(
  i_barColOn ? c_barCol : na)

// ——————————— <alerts>
//<any_alert_function_call>
if longE or shortE or longX or shortX
    alert(message = 'Any Alert',   freq = alert.freq_once_per_bar_close)
if longE
    alert(message = 'Long Entry',  freq = alert.freq_once_per_bar_close)
if shortE
    alert(message = 'Short Entry', freq = alert.freq_once_per_bar_close)
if longX
    alert(message = 'Long Exit',   freq = alert.freq_once_per_bar_close)
if shortX
    alert(message = 'Short Exit',  freq = alert.freq_once_per_bar_close)
//#endregion }
// ——————————— <↑↑↑ G_SCRIPT03 ↑↑↑>

// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © TraderHalai
// This script was born out of my quest to be able to display strategy back test statistics on charts to allow for easier backtesting on devices that do not natively support backtest engine (such as mobile phones, when I am backtesting from away from my computer). There are already a few good ones on TradingView, but most / many are too complicated for my needs.
//
//Found an excellent display backtest engine by 'The Art of Trading'. This script is a snippet of his hard work, with some very minor tweaks and changes. Much respect to the original author.
//
//Full credit to the original author of this script. It can be found here: https://www.tradingview.com/script/t776tkZv-Hammers-Stars-Strategy/?offer_id=10&aff_id=15271
//
// This script can be copied and airlifted onto existing strategy scripts of your own, and integrates out of the box without implementation of additional functions. I've also added Max Runup, Average Win and Average Loss per trade to the orignal script.
//
//Will look to add in more performance metrics in future, as I further develop this script.
//
//Feel free to use this display panel in your scripts and strategies.

//Thanks and enjoy! :)
//@version=5
//strategy("Strategy BackTest Display Statistics - TraderHalai", overlay=true, default_qty_value= 5, default_qty_type = strategy.percent_of_equity, initial_capital=10000,  commission_type=strategy.commission.percent, commission_value=0.1)

//DEMO basic strategy - Use your own strategy here -  Jaws Mean Reversion from my profile used here
//source = input(title = "Source", defval = close)
    
///////////////////////////// --- BEGIN TESTER CODE --- ////////////////////////
// COPY below into your strategy to enable display
////////////////////////////////////////////////////////////////////////////////


// Declare performance tracking variables
drawTester = input.bool(true, "Strategy Performance", group='Dashboards', inline="Show Dashboards")
var balance = strategy.initial_capital
var drawdown = 0.0
var maxDrawdown = 0.0
var maxBalance = 0.0
var totalWins = 0
var totalLoss = 0

// Prepare stats table
var table testTable = table.new(position.top_right, 5, 2, border_width=1)
f_fillCell(_table, _column, _row, _title, _value, _bgcolor, _txtcolor) =>
    _cellText = _title + "\n" + _value
    table.cell(_table, _column, _row, _cellText, bgcolor=_bgcolor, text_color=_txtcolor)
    
// Custom function to truncate (cut) excess decimal places
//truncate(_number, _decimalPlaces) =>
//    _factor = math.pow(10, _decimalPlaces)
//    int(_number * _factor) / _factor
    
// Draw stats table
var bgcolor = color.new(color.black,0)
if drawTester and tradeDateIsAllowed
    if barstate.islastconfirmedhistory
        // Update table
        dollarReturn = strategy.netprofit
        f_fillCell(testTable, 0, 0, "Total Trades:", str.tostring(strategy.closedtrades), bgcolor, color.white)
        f_fillCell(testTable, 0, 1, "Win Rate:", str.tostring(truncate((strategy.wintrades/strategy.closedtrades)*100,2)) + "%", bgcolor, color.white)
        f_fillCell(testTable, 1, 0, "Starting:", "$" + str.tostring(strategy.initial_capital), bgcolor, color.white)
        f_fillCell(testTable, 1, 1, "Ending:", "$" + str.tostring(truncate(strategy.initial_capital + strategy.netprofit,2)), bgcolor, color.white)
        f_fillCell(testTable, 2, 0, "Avg Win:", "$"+ str.tostring(truncate(strategy.grossprofit / strategy.wintrades, 2)), bgcolor, color.white)
        f_fillCell(testTable, 2, 1, "Avg Loss:", "$"+ str.tostring(truncate(strategy.grossloss / strategy.losstrades, 2)), bgcolor, color.white)
        f_fillCell(testTable, 3, 0, "Profit Factor:", str.tostring(truncate(strategy.grossprofit / strategy.grossloss,2)), strategy.grossprofit > strategy.grossloss ? color.green : color.red, color.white)
        f_fillCell(testTable, 3, 1, "Max Runup:",  str.tostring(truncate(strategy.max_runup, 2 )), bgcolor, color.white)
        f_fillCell(testTable, 4, 0, "Return:", (dollarReturn > 0 ? "+" : "") + str.tostring(truncate((dollarReturn / strategy.initial_capital)*100,2)) + "%", dollarReturn > 0 ? color.green : color.red, color.white)
        f_fillCell(testTable, 4, 1, "Max DD:", str.tostring(truncate((strategy.max_drawdown / strategy.equity) * 100 ,2)) + "%", color.red, color.white)
// --- END TESTER CODE --- ///////////////


// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © niceGear68734

//@version=5
//strategy("Table to filter trades per day",  overlay=true, use_bar_magnifier = true, initial_capital = 5000, calc_on_every_tick = true, calc_on_order_fills = true, commission_type = strategy.commission.cash_per_contract)

//~ ___________________________________________________________________________
//~ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//~ !!!!!!!!!!!!!!!_________________  START  _________________!!!!!!!!!!!!!!!!!
i_showweeklyPerformance = input.bool(false, 'Weekly Performance', group='Dashboards', inline="Show Dashboards")
//__________________________   User Inputs  ___________________________________
var const string g_table    = "Table Settings"
i_table_pos                 = "Top Left" //input.string(defval = "Top Left", title = "Position", options = ["Bottom Right","Bottom Left", "Top Right", "Top Left"], group = g_table, inline = "1", tooltip = "It sets the location of the table")
i_text_size                 = "Normal" //input.string(defval = "Normal", title = "Set the size of text", options = ["Small", "Normal", "Large"], tooltip = "This option is used to change the size of the text in the table")
var const string g_general  = "General Settings"
i_check_open_close          = "Opened" //input.string("Opened", "Check when the trade :", ["Opened", "Closed"], group = g_general, tooltip = "This parameter defines what to check for. If opened is selected, the results will show the trades that opened on that day. If closed is selected, the results will show the trades that closed on that day")
i_timezone                  = "Exchange" //input.string("Exchange", title = "Set the Timezone", options = ["Exchange","UTC-10","UTC-9","UTC-8","UTC-7","UTC-6","UTC-5","UTC-4","UTC-3","UTC-2","UTC-1","UTC","UTC+1","UTC+2","UTC+3","UTC+4","UTC+5","UTC+6","UTC+7","UTC+8","UTC+9","UTC+10", "UTC+11","UTC+12","UTC+13","UTC+13:45"], group = g_general, tooltip = "You can use this setting whenever you want to change the time that the trade has closed/opened")

//~_____________________________  Switches  ___________________________________
table_pos = switch i_table_pos
    "Bottom Right"  => position.bottom_right
    "Bottom Left"   => position.bottom_left
    "Top Right"     => position.top_right
    "Top Left"      => position.top_left

timezone_setting = i_timezone == "Exchange" ? syminfo.timezone : i_timezone

text_size = switch i_text_size
    "Small"         => size.small
    "Normal"        => size.normal
    "Large"         => size.large

//__________________________   Array Declaration  _____________________________
var string[] t_column_names          = array.from( "", "Sun", "Mon", "Tue", "Wed", "Thur", "Fri", "Sat")  // Columns header names
var string[] t_row_names             = array.from("", "Total Trades", "Loss", "Win", "Win Rate" )  // Rows header names
var t_column_size                    = array.size(t_column_names)
var t_row_size                       = array.size(t_row_names)
var string[] a_closed_trades         = array.new_string()  // Save the total number of trades
var string[]   a_loss_trades         = array.new_string()  // Save the number of losing trades
var string[]   a_win_trades          = array.new_string()  // Save the number of winning trades 
var _a_day_week                      = array.new_int()  // Save the day of the week to split data

// __________________________  Custom Functions  ________________________________
//~ create a counter so that it gives a number to strategy.closed_trades.entry_time(counter)
var trade_number = -1
if strategy.closedtrades > strategy.closedtrades[1]
    trade_number += 1

f_strategy_closedtrades_hour() =>
    switch 
        i_check_open_close =="Closed" => dayofweek(strategy.closedtrades.exit_time(trade_number), timezone_setting)
        i_check_open_close =="Opened"  => dayofweek(strategy.closedtrades.entry_time(trade_number), timezone_setting)

f_data(_i) =>
    var _closed_trades          = 0
    var _loss_trades            = 0
    var _win_trades             = 0
    var _txt_closed_trades      = "" 
    var _txt_loss_trades        = ""
    var _txt_win_trades         = ""


    if strategy.closedtrades > strategy.closedtrades[1] and f_strategy_closedtrades_hour() == _i
        _closed_trades += 1
        _txt_closed_trades := str.tostring(_closed_trades)
    if strategy.losstrades > strategy.losstrades[1]     and f_strategy_closedtrades_hour() == _i
        _loss_trades += 1
        _txt_loss_trades := str.tostring(_loss_trades)        
    if strategy.wintrades > strategy.wintrades[1]       and f_strategy_closedtrades_hour() == _i
        _win_trades += 1
        _txt_win_trades := str.tostring(_win_trades) 
    [_txt_closed_trades, _txt_loss_trades, _txt_win_trades]                                                       


//__________________________
var string[] array1 = array.new_string(5)
var string[] array2 = array.new_string(5)
var string[] array3 = array.new_string(5)
var string[] array4 = array.new_string(5)
var string[] array5 = array.new_string(5)
var string[] array6 = array.new_string(5)
var string[] array7 = array.new_string(5)


f_pass_data_to_array(_i, _array) =>
    [cl, loss, win] = f_data(_i)
    array.set(_array,1 , cl)
    array.set(_array,2,loss)
    array.set(_array,3,win)
    if cl != "" 
        array.set(_array,4,str.tostring(str.tonumber(win) / str.tonumber(cl) * 100 , "##") + " %")
    if cl != "" and win == ""
        array.set(_array,4,"0 %")


for i = 1 to 7 
    switch
        i == 1 => f_pass_data_to_array(i,array1)
        i == 2 => f_pass_data_to_array(i,array2)
        i == 3 => f_pass_data_to_array(i,array3)
        i == 4 => f_pass_data_to_array(i,array4)
        i == 5 => f_pass_data_to_array(i,array5)
        i == 6 => f_pass_data_to_array(i,array6)
        i == 7 => f_pass_data_to_array(i,array7)

f_retrieve_data_to_table(_i, _j) =>
    switch
        _i == 1 => array.get(array1, _j)
        _i == 2 => array.get(array2, _j)
        _i == 3 => array.get(array3, _j)
        _i == 4 => array.get(array4, _j)
        _i == 5 => array.get(array5, _j)
        _i == 6 => array.get(array6, _j)
        _i == 7 => array.get(array7, _j)





//~ ___________________________  Create Table  ________________________________
create_table(_col, _row, _txt) =>
    var table _tbl = table.new(position = table_pos, columns = t_column_size , rows = t_row_size, border_width=1)
    color _color = _row == 0 or _col == 0 ? color.rgb(3, 62, 106) : color.rgb(2, 81, 155)
    table.cell(_tbl, _col, _row, _txt, bgcolor = _color, text_color = color.white, text_size = text_size)




//~___________________________  Fill With Data  _______________________________
if barstate.islastconfirmedhistory and i_showweeklyPerformance and tradeDateIsAllowed
    for i = 0 to t_column_size - 1 by 1
        for j = 0 to t_row_size - 1 by 1
            _txt = "" 
            if i >= 0 and j == 0
                _txt := array.get(t_column_names, i)
            if j >= 0 and i == 0
                _txt := array.get(t_row_names, j)
            if  i >= 1 and j >= 1 and j <= 5
                _txt :=  f_retrieve_data_to_table( i , j)
            create_table(i ,j , _txt)


//~ ___________________________  Notice  ______________________________________
if timeframe.in_seconds() > timeframe.in_seconds("D")
    x = table.new(position.middle_center,1,1,color.aqua)
    table.cell_set_text(x,0,0,"Please select lower timeframes (Daily or lower)")

//~ !!!!!!!!!!!!!!!_________________  STOP  _________________!!!!!!!!!!!!!!!!!!
//~ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//~ ___________________________________________________________________________


// Global Dashboard Variables
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

// Dashboard Table Text Size
i_tableTextSize = "Normal" //input.string(title="Dashboard Size", defval="Normal", options=["Auto",  "Huge",  "Large", "Normal", "Small", "Tiny"], group="Dashboards")
table_text_size(s) =>
    switch s
        "Auto"   => size.auto   
        "Huge"   => size.huge   
        "Large"  => size.large  
        "Normal" => size.normal 
        "Small"  => size.small
        => size.tiny
tableTextSize = table_text_size(i_tableTextSize)

// Monthly Table Performance Dashboard By @QuantNomad
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
i_showMonthlyPerformance = input.bool(false, 'Monthly Performance', group='Dashboards', inline="Show Dashboards")
i_monthlyReturnPercision = 2

if i_showMonthlyPerformance and tradeDateIsAllowed
    new_month = month(time) != month(time[1])
    new_year  = year(time)  != year(time[1])
    
    eq = strategy.equity
    
    bar_pnl = eq / eq[1] - 1
    
    cur_month_pnl = 0.0
    cur_year_pnl  = 0.0
    
    // Current Monthly P&L
    cur_month_pnl := new_month ? 0.0 : 
                     (1 + cur_month_pnl[1]) * (1 + bar_pnl) - 1 
    
    // Current Yearly P&L
    cur_year_pnl := new_year ? 0.0 : 
                     (1 + cur_year_pnl[1]) * (1 + bar_pnl) - 1  
    
    // Arrays to store Yearly and Monthly P&Ls
    var month_pnl  = array.new_float(0)
    var month_time = array.new_int(0)
    
    var year_pnl  = array.new_float(0)
    var year_time = array.new_int(0)
    
    last_computed = false
    
    if (not na(cur_month_pnl[1]) and (new_month or barstate.islastconfirmedhistory))
        if (last_computed[1])
            array.pop(month_pnl)
            array.pop(month_time)
            
        array.push(month_pnl , cur_month_pnl[1])
        array.push(month_time, time[1])
    
    if (not na(cur_year_pnl[1]) and (new_year or barstate.islastconfirmedhistory))
        if (last_computed[1])
            array.pop(year_pnl)
            array.pop(year_time)
            
        array.push(year_pnl , cur_year_pnl[1])
        array.push(year_time, time[1])
    
    last_computed := barstate.islastconfirmedhistory ? true : nz(last_computed[1])
    
    // Monthly P&L Table    
    var monthly_table = table(na)
    
    if (barstate.islastconfirmedhistory)
        monthly_table := table.new(position.bottom_right, columns = 14, rows = array.size(year_pnl) + 1, border_width = 1)
    
        table.cell(monthly_table, 0,  0, "",     bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 1,  0, "Jan",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 2,  0, "Feb",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 3,  0, "Mar",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 4,  0, "Apr",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 5,  0, "May",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 6,  0, "Jun",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 7,  0, "Jul",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 8,  0, "Aug",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 9,  0, "Sep",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 10, 0, "Oct",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 11, 0, "Nov",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 12, 0, "Dec",  bgcolor = #cccccc, text_size=tableTextSize)
        table.cell(monthly_table, 13, 0, "Year", bgcolor = #999999, text_size=tableTextSize)
    
        for yi = 0 to array.size(year_pnl) - 1
            table.cell(monthly_table, 0,  yi + 1, str.tostring(year(array.get(year_time, yi))), bgcolor = #cccccc, text_size=tableTextSize)
            
            y_color = array.get(year_pnl, yi) > 0 ? color.new(color.teal, transp = 40) : color.new(color.gray, transp = 40)
            table.cell(monthly_table, 13, yi + 1, str.tostring(math.round(array.get(year_pnl, yi) * 100, i_monthlyReturnPercision)), bgcolor = y_color, text_color=color.new(color.white, 0),text_size=tableTextSize)
            
        for mi = 0 to array.size(month_time) - 1
            m_row   = year(array.get(month_time, mi))  - year(array.get(year_time, 0)) + 1
            m_col   = month(array.get(month_time, mi)) 
            m_color = array.get(month_pnl, mi) > 0 ? color.new(color.teal, transp = 40) : color.new(color.maroon, transp = 40)
            
            table.cell(monthly_table, m_col, m_row, str.tostring(math.round(array.get(month_pnl, mi) * 100, i_monthlyReturnPercision)), bgcolor = m_color, text_color=color.new(color.white, 0), text_size=tableTextSize)



hide = timeframe.isintraday

// Input for EMA period
emaPeriod = 48 //input.int(48, title="EMA Period")
emaPeriod2 = 2 //input.int(2, title="EME Period 2")
emaPeriod3 = 21 //input.int(21, title="EMA Period")

// Input to toggle EMA Cloud 
showcloud = input.bool(true, title="Plot EMA?", group='EMA & ATR', inline="Show EMA's & ATR")
useHTF = input.bool(true, title = "Use Higher Time Frame?")

matimeframe = useHTF ? my_time1 : ''

// EMA calculations
ema = request.security(syminfo.tickerid, matimeframe, ta.ema(close, emaPeriod))
ema2 = request.security(syminfo.tickerid, matimeframe, ta.ema(close,emaPeriod2))
ema3 = request.security(syminfo.tickerid, matimeframe,ta.ema(close, emaPeriod3))
emaColor = close > ema3 ? color.new(color.rgb(56, 142, 60, 63), 50) : color.new(color.rgb(147, 40, 51, 38), 50)

// Plotting EMA's 
plot_ema1 = plot(hide ? ema : na, style=plot.style_line, color=color.new(color.rgb(255, 255, 255, 100), 50), title="EMA", linewidth=2)
plot_ema2 = plot(hide ? ema2 : na, style=plot.style_line, color=color.new(color.rgb(255, 255, 255, 100), 50), title="EMA", linewidth=1)
plot_ema3 = plot(ema3, style=plot.style_line, color=emaColor, title="EMA", linewidth=1)

// EMA Cloud
cloudColor = ema2 > ema ? color.new(#0f8513, 80) : color.new(#a81414, 80)
cloudColor2 = ema2 > ema3 ? color.new(#0f8513, 50) : color.new(#a81414, 50)
cloudColor := showcloud ? cloudColor : na
fill(plot_ema1, plot_ema2, color=cloudColor, title="EMA Cloud")
fill(plot_ema3, plot_ema2, color=cloudColor, title="EMA Cloud")
/////////////////////////////////////////////////////////////// © BackQuant ///////////////////////////////////////////////////////////////
// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © BackQuant

import TradingView/ta/4 as ta

//@version=5
//indicator(
// title="DEMA Adjusted Average True Range [BackQuant]", 
// shorttitle = "DEMA ATR [BackQuant]",
// overlay=true, 
// timeframe="", 
// timeframe_gaps=true
// )


// Define User Inputs
simple bool       showAtr                 =        input.bool(true, "Plot Dema?", group='EMA & ATR', inline="Show EMA's & ATR")
simple bool       haCandles               =        true //input.bool(true, "Use HA Candles?")
simple int        periodDema              =        7 //input.int(7, "Dema Period", group = "Dema Atr")
series float      sourceDema              =        close //input.source(close, "Calculation Source", group = "Dema Atr")
simple int        periodAtr               =        14 //input.int(14, "Period", group = "Dema Atr")
simple float      factorAtr               =        1.7 //input.float(1.7, "Factor", step = 0.01, group = "Dema Atr")
simple color      longColour              =       #00ff00
simple color      shortColour             =       #ff0000
/////////////////////////////////////////////////////////////// © BackQuant ///////////////////////////////////////////////////////////////
// Use HA Candles?
heikinashi_close = request.security(
 symbol = ticker.heikinashi(syminfo.tickerid),
 timeframe =  timeframe.period,
 expression = close, 
 gaps = barmerge.gaps_off, 
 lookahead = barmerge.lookahead_on
 )

var series float source = close
if haCandles == true
    source := heikinashi_close
if haCandles == false
    source := sourceDema 
/////////////////////////////////////////////////////////////// © BackQuant ///////////////////////////////////////////////////////////////
// Function
DemaAtrWithBands(periodDema, source, lookback, atrFactor)=>
    ema1   = ta.ema(source,  periodDema)
    ema2   = ta.ema(ema1, periodDema)
    demaOut = 2 * ema1 - ema2

    atr = ta.atr(lookback)
    trueRange = atr * atrFactor 

    DemaAtr = demaOut 
    DemaAtr := nz(DemaAtr[1], DemaAtr)

    trueRangeUpper = demaOut + trueRange
    trueRangeLower  = demaOut - trueRange 

    if trueRangeLower > DemaAtr 
        DemaAtr := trueRangeLower
    if trueRangeUpper < DemaAtr 
        DemaAtr := trueRangeUpper
    DemaAtr 

// Function Out
DemaAtr = DemaAtrWithBands(periodDema, source, periodAtr, factorAtr)
/////////////////////////////////////////////////////////////// © BackQuant ///////////////////////////////////////////////////////////////
// Conditions
DemaAtrLong = DemaAtr > DemaAtr[1] 
DemaAtrShort = DemaAtr < DemaAtr[1]

// Colour Condtions
var color Trendcolor = #ffffff
if DemaAtrLong
    Trendcolor := longColour
if DemaAtrShort
    Trendcolor := shortColour

// Plotting
plot( showAtr ? DemaAtr : na,  "ATR",  color=Trendcolor,  linewidth = 2 )











Comments

Popular posts from this blog

Best Survey Apps That Pay Instantly On Mobile

  Best Survey Apps That Pay Instantly On Mobile In today’s fast-paced world, earning extra cash or rewards right from your mobile device is easier than ever—thanks to survey apps! Whether you're looking to make a little extra money or simply want gift cards for your favorite stores, survey apps can provide you with instant payouts for your opinions. Imagine getting paid in cash or gift cards for answering questions while you’re on the go—sounds great, right? We’ve compiled a list of the best survey apps that pay instantly on mobile , so you can start earning right away! 1. Swagbucks Swagbucks is one of the most well-known and trusted survey apps, and it’s known for offering instant rewards. Swagbucks offers users a wide range of ways to earn points, including surveys, shopping online, watching videos, and even searching the web. Key Features: Instant Payment Options : You can redeem your Swagbucks for gift cards to retailers like Amazon, PayPal, and iTunes almost instantly...

Online Dating VS Traditional Dating: Which Is Right for You?

  Online Dating VS Traditional Dating: Which Is Right for You? In the age of smartphones, social media, and instant messaging, online dating has revolutionized the way we meet potential partners. But how does it compare to traditional dating? With both offering unique opportunities and challenges, the debate between online dating and traditional dating continues to grow. In this article, we’ll explore the differences between the two, and help you decide which one is best suited for your personal preferences and dating goals. 1. Convenience and Accessibility Online Dating : One of the biggest advantages of online dating is convenience. With just a few taps, you can browse hundreds (if not thousands) of profiles without leaving the comfort of your home. Online dating apps and websites are available 24/7, allowing you to meet potential partners at any time, from anywhere. Whether you’re busy with work, school, or other commitments, online dating provides flexibility for people w...

Best Apps That Give You Free Bitcoin on Mobile

  Best Apps That Give You Free Bitcoin on Mobile In a world where cryptocurrency is becoming more popular by the day, Bitcoin remains the king of digital currencies. With Bitcoin’s rise, people everywhere are looking for ways to get in on the action without spending their hard-earned cash. Fortunately, there are several apps out there that offer ways to earn free Bitcoin directly on your mobile device. Whether you’re a seasoned crypto enthusiast or just dipping your toes into the world of digital currency, these apps provide an easy way to get started. Here are some of the best apps that give you free Bitcoin on mobile! 1. Coinbase Earn Best for: Beginners looking for easy education and rewards Coinbase is one of the most popular and user-friendly cryptocurrency exchanges globally. Its "Coinbase Earn" feature allows you to earn free Bitcoin (and other cryptocurrencies) by completing educational tasks. These tasks typically involve watching short videos, reading articles,...