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

//@version=5
indicator("The TrendLiner", overlay = true, max_bars_back = 5000, max_boxes_count = 500, max_labels_count = 500, max_lines_count = 500)

GROUP_GENERAL = "General Options"
GROUP_TF1 = "Timeframe 1"
GROUP_TF1_APPEARANCE = "Timeframe 1 Appearance"
GROUP_TF2 = "Timeframe 2"
GROUP_TF2_APPEARANCE = "Timeframe 2 Appearance"
GROUP_TF3 = "Timeframe 3"
GROUP_TF3_APPEARANCE = "Timeframe 3 Appearance"
GROUP_TF4 = "Timeframe 4"
GROUP_TF4_APPEARANCE = "Timeframe 4 Appearance"

CHECK_TYPE_BODY = "Body"
CHECK_TYPE_WICK = "Wick"

// GENERAL
i_min_distance_threshold = input.float( 0.4, "Min Distance from Previous Trendline (%)", minval = 0, step = 0.1, group = GROUP_GENERAL, tooltip = "Minimum distance of a new trendline from the last one in order for it to be shown on chart. Prevents cluttering of your chart by hiding trendlines that would otherwise appear much too close to each other." )
i_text_offset = input.int( 5, "Text Offset on Trendline", minval = 0, group = GROUP_GENERAL, tooltip = "How many candles to the right from current candle should a trendline text be offset to the right."  )
i_right_line_width = input.int( 10, "Off-Chart Lines Width", minval = 2, group = GROUP_GENERAL, tooltip = "How long should right-hand-side trend lines be.\nThese trendlines are off-chart, i.e. not present in between of candles to prevent unneccessary clutter.\n\nYou can set up how many maximum on-chart trendlines you want to see for each timeframe used. The rest will be displayed off-chart, on the right hand side, into the future."  )
i_check_type = input.string( CHECK_TYPE_WICK, "Trendline At", options = [ CHECK_TYPE_BODY, CHECK_TYPE_WICK ], group = GROUP_GENERAL )
i_show_price_on_labels = input.bool( true, "Show Price on Labels", group = GROUP_GENERAL )

// TIMEFRAME 1
i_use_tf1 = input.bool( true, "Use Timeframe 1", group = GROUP_TF1 )
i_tf_t1 = input.timeframe( "", "Timeframe", group = GROUP_TF1 )
i_text_t1 = input.string( "", "Text on Trendline", group = GROUP_TF1, inline = "t1_txt")
i_text_use_period_t1 = input.bool( false, "Use Period Name", group = GROUP_TF1, inline = "t1_txt" )
i_text_size_t1 = input.string( size.normal, "Text Size", options = [ size.auto, size.huge, size.large, size.normal, size.small, size.tiny ], group = GROUP_TF1 )
i_untested_max_t1 = input.int( 12, "Max Untested Trendlines", group = GROUP_TF1, maxval = 500, minval = 1, tooltip = "This is the number of untested trendlines for this timeframe to show, displayed either directly over candles on the chart or indirectly as lines to the right side of the chart. At least a single untested trendline must be present on chart for this indicator to work, but you can use the Style tab to hide even this one." )
//i_untested_lines_on_chart_t1 = input.int( 5, "Untested Trendlines Directly on Chart", group = GROUP_TF1, minval = -1, maxval = 500, tooltip = "This is the number of untested trendlines to show over candles on your chart.\n\nTrendlines can be either shown directly between candles on the chart or as lines to the right of the chart, so they don't get in the way.\n\n0 = show all untested trendlines directly on chart\n-1 = show all untested trendlines as lines to the right of the chart\n1-500 = show that many untested trendlines directly on chart and the rest to the right of the chart" )
i_untested_display_off_chart_t1 = input.bool( false, "Display Untested Trendlines off Chart", group = GROUP_TF1)
i_untested_show_label_t1 = input.bool( true, "Show Untested Labels", group = GROUP_TF1 )
i_touched_max_t1 = input.int( 12, "Max Touched Trendlines", group = GROUP_TF1, maxval = 500, minval = 0, tooltip = "This is the number of touched trendlines for this timeframe to show, displayed either directly over candles on the chart or indirectly as lines to the right side of the chart. Set to 0 to hide all touched trendlines." )
//i_touched_lines_on_chart_t1 = input.int( 5, "Touched Trendlines Directly on Chart", group = GROUP_TF1, minval = -1, maxval = 500, tooltip = "This is the number of touched trendlines to show over candles on your chart.\n\nTrendlines can be either shown directly between candles on the chart or as lines to the right of the chart, so they don't get in the way.\n\n0 = show all touched trendlines directly on chart\n-1 = show all touched trendlines as lines to the right of the chart\n1-500 = show that many touched trendlines directly on chart and the rest to the right of the chart" )
i_touched_display_off_chart_t1 = input.bool( false, "Display Touched Trendlines off Chart", group = GROUP_TF1)
i_touched_show_label_t1 = input.bool( true, "Show Touched Labels", group = GROUP_TF1 )
i_mitigated_max_t1 = input.int( 8, "Max Mitigated Trendlines", group = GROUP_TF1, maxval = 500, minval = 0, tooltip = "This is the number of mitigated trendlines for this timeframe to show, displayed either directly over candles on the chart or indirectly as lines to the right side of the chart. Set to 0 to hide all mitigated trendlines." )
//i_mitigated_lines_on_chart_t1 = input.int( 5, "Mitigated Trendlines Directly on Chart", group = GROUP_TF1, minval = -1, maxval = 500, tooltip = "This is the number of mitigated trendlines to show over candles on your chart.\n\nTrendlines can be either shown directly between candles on the chart or as lines to the right of the chart, so they don't get in the way.\n\n0 = show all mitigated trendlines directly on chart\n-1 = show all mitigated trendlines as lines to the right of the chart\n1-500 = show that many mitigated trendlines directly on chart and the rest to the right of the chart" )
i_mitigated_display_off_chart_t1 = input.bool( true, "Display Mitigated Trendlines off Chart", group = GROUP_TF1)
i_mitigated_show_label_t1 = input.bool( false, "Show Mitigated Labels", group = GROUP_TF1 )

i_line_color_untested_t1 = input.color( color.green, "Untested Trendline", group = GROUP_TF1_APPEARANCE, inline = "1_untested" )
i_line_size_untested_t1 = input.int( 1, "", group = GROUP_TF1_APPEARANCE, inline = "1_untested" )
i_line_style_untested_t1 = input.string( line.style_solid, "", options = [line.style_solid, line.style_dashed, line.style_dotted], group = GROUP_TF1_APPEARANCE, inline = "1_untested" )
i_line_extend_untested_t1 = input.bool( false, "Extend", group = GROUP_TF1_APPEARANCE, inline = "1_untested", tooltip = "Untested trendlines are the ones where price made a swing high / low but the price did not return there yet." )

i_line_color_touched_t1 = input.color( color.orange, "Touched Trendline", group = GROUP_TF1_APPEARANCE, inline = "1_touched" )
i_line_size_touched_t1 = input.int( 1, "", group = GROUP_TF1_APPEARANCE, inline = "1_touched" )
i_line_style_touched_t1 = input.string( line.style_solid, "", options = [line.style_solid, line.style_dashed, line.style_dotted], group = GROUP_TF1_APPEARANCE, inline = "1_touched" )
i_line_extend_touched_t1 = input.bool( false, "Extend", group = GROUP_TF1_APPEARANCE, inline = "1_touched", tooltip = "Touched trendlines are the ones where price returned but failed to cross over that swing high/low." )

i_line_color_mitigated_t1 = input.color( color.rgb(205, 206, 209), "Mitigated Trendline", group = GROUP_TF1_APPEARANCE, inline = "1_mitigated" )
i_line_size_mitigated_t1 = input.int( 1, "", group = GROUP_TF1_APPEARANCE, inline = "1_mitigated" )
i_line_style_mitigated_t1 = input.string( line.style_dashed, "", options = [line.style_solid, line.style_dashed, line.style_dotted], group = GROUP_TF1_APPEARANCE, inline = "1_mitigated" )
i_line_extend_mitigated_t1 = input.bool( false, "Extend", group = GROUP_TF1_APPEARANCE, inline = "1_mitigated", tooltip = "Mitigated trendlines are the ones where price returned and crossed over, thus rendering the trendline invalid." )

// TIMEFRAME 2
i_use_tf2 = input.bool( false, "Use Timeframe 2", group = GROUP_TF2 )
i_tf_t2 = input.timeframe( "60", "Timeframe", group = GROUP_TF2 )
i_text_t2 = input.string( "", "Text on Trendline", group = GROUP_TF2, inline = "t2_txt")
i_text_use_period_t2 = input.bool( false, "Use Period Name", group = GROUP_TF2, inline = "t2_txt" )
i_text_size_t2 = input.string( size.normal, "Text Size", options = [ size.auto, size.huge, size.large, size.normal, size.small, size.tiny ], group = GROUP_TF2 )
i_untested_max_t2 = input.int( 12, "Max Untested Trendlines", group = GROUP_TF2, maxval = 500, minval = 1, tooltip = "This is the number of untested trendlines for this timeframe to show, displayed either directly over candles on the chart or indirectly as lines to the right side of the chart. At least a single untested trendline must be present on chart for this indicator to work, but you can use the Style tab to hide even this one." )
//i_untested_lines_on_chart_t2 = input.int( 5, "Untested Trendlines Directly on Chart", group = GROUP_TF2, minval = -1, maxval = 500, tooltip = "This is the number of untested trendlines to show over candles on your chart.\n\nTrendlines can be either shown directly between candles on the chart or as lines to the right of the chart, so they don't get in the way.\n\n0 = show all untested trendlines directly on chart\n-1 = show all untested trendlines as lines to the right of the chart\n1-500 = show that many untested trendlines directly on chart and the rest to the right of the chart" )
i_untested_display_off_chart_t2 = input.bool( false, "Display Untested Trendlines off Chart", group = GROUP_TF2)
i_untested_show_label_t2 = input.bool( true, "Show Untested Labels", group = GROUP_TF2 )
i_touched_max_t2 = input.int( 12, "Max Touched Trendlines", group = GROUP_TF2, maxval = 500, minval = 0, tooltip = "This is the number of touched trendlines for this timeframe to show, displayed either directly over candles on the chart or indirectly as lines to the right side of the chart. Set to 0 to hide all touched trendlines." )
//i_touched_lines_on_chart_t2 = input.int( 5, "Touched Trendlines Directly on Chart", group = GROUP_TF2, minval = -1, maxval = 500, tooltip = "This is the number of touched trendlines to show over candles on your chart.\n\nTrendlines can be either shown directly between candles on the chart or as lines to the right of the chart, so they don't get in the way.\n\n0 = show all touched trendlines directly on chart\n-1 = show all touched trendlines as lines to the right of the chart\n1-500 = show that many touched trendlines directly on chart and the rest to the right of the chart" )
i_touched_display_off_chart_t2 = input.bool( false, "Display Touched Trendlines off Chart", group = GROUP_TF2)
i_touched_show_label_t2 = input.bool( true, "Show Touched Labels", group = GROUP_TF2 )
i_mitigated_max_t2 = input.int( 8, "Max Mitigated Trendlines", group = GROUP_TF2, maxval = 500, minval = 0, tooltip = "This is the number of mitigated trendlines for this timeframe to show, displayed either directly over candles on the chart or indirectly as lines to the right side of the chart. Set to 0 to hide all mitigated trendlines." )
//i_mitigated_lines_on_chart_t2 = input.int( 5, "Mitigated Trendlines Directly on Chart", group = GROUP_TF2, minval = -1, maxval = 500, tooltip = "This is the number of mitigated trendlines to show over candles on your chart.\n\nTrendlines can be either shown directly between candles on the chart or as lines to the right of the chart, so they don't get in the way.\n\n0 = show all mitigated trendlines directly on chart\n-1 = show all mitigated trendlines as lines to the right of the chart\n1-500 = show that many mitigated trendlines directly on chart and the rest to the right of the chart" )
i_mitigated_display_off_chart_t2 = input.bool( true, "Display Mitigated Trendlines off Chart", group = GROUP_TF2)
i_mitigated_show_label_t2 = input.bool( false, "Show Mitigated Labels", group = GROUP_TF2 )

i_line_color_untested_t2 = input.color( color.aqua, "Untested Trendline", group = GROUP_TF2_APPEARANCE, inline = "2_untested" )
i_line_size_untested_t2 = input.int( 1, "", group = GROUP_TF2_APPEARANCE, inline = "2_untested" )
i_line_style_untested_t2 = input.string( line.style_solid, "", options = [line.style_solid, line.style_dashed, line.style_dotted], group = GROUP_TF2_APPEARANCE, inline = "2_untested" )
i_line_extend_untested_t2 = input.bool( false, "Extend", group = GROUP_TF2_APPEARANCE, inline = "2_untested", tooltip = "Untested trendlines are the ones where price made a swing high / low but the price did not return there yet." )

i_line_color_touched_t2 = input.color( color.yellow, "Touched Trendline", group = GROUP_TF2_APPEARANCE, inline = "2_touched" )
i_line_size_touched_t2 = input.int( 1, "", group = GROUP_TF2_APPEARANCE, inline = "2_touched" )
i_line_style_touched_t2 = input.string( line.style_solid, "", options = [line.style_solid, line.style_dashed, line.style_dotted], group = GROUP_TF2_APPEARANCE, inline = "2_touched" )
i_line_extend_touched_t2 = input.bool( false, "Extend", group = GROUP_TF2_APPEARANCE, inline = "2_touched", tooltip = "Touched trendlines are the ones where price returned but failed to cross over that swing high/low." )

i_line_color_mitigated_t2 = input.color( #b2b5be, "Mitigated Trendline", group = GROUP_TF2_APPEARANCE, inline = "2_mitigated" )
i_line_size_mitigated_t2 = input.int( 1, "", group = GROUP_TF2_APPEARANCE, inline = "2_mitigated" )
i_line_style_mitigated_t2 = input.string( line.style_dashed, "", options = [line.style_solid, line.style_dashed, line.style_dotted], group = GROUP_TF2_APPEARANCE, inline = "2_mitigated" )
i_line_extend_mitigated_t2 = input.bool( false, "Extend", group = GROUP_TF2_APPEARANCE, inline = "2_mitigated", tooltip = "Mitigated trendlines are the ones where price returned and crossed over, thus rendering the trendline invalid." )

// TIMEFRAME 3
i_use_tf3 = input.bool( false, "Use Timeframe 3", group = GROUP_TF3 )
i_tf_t3 = input.timeframe( "60", "Timeframe", group = GROUP_TF3 )
i_text_t3 = input.string( "", "Text on Trendline", group = GROUP_TF3, inline = "t3_txt")
i_text_use_period_t3 = input.bool( false, "Use Period Name", group = GROUP_TF3, inline = "t3_txt" )
i_text_size_t3 = input.string( size.normal, "Text Size", options = [ size.auto, size.huge, size.large, size.normal, size.small, size.tiny ], group = GROUP_TF3 )
i_untested_max_t3 = input.int( 12, "Max Untested Trendlines", group = GROUP_TF3, maxval = 500, minval = 1, tooltip = "This is the number of untested trendlines for this timeframe to show, displayed either directly over candles on the chart or indirectly as lines to the right side of the chart. At least a single untested trendline must be present on chart for this indicator to work, but you can use the Style tab to hide even this one." )
//i_untested_lines_on_chart_t3 = input.int( 5, "Untested Trendlines Directly on Chart", group = GROUP_TF3, minval = -1, maxval = 500, tooltip = "This is the number of untested trendlines to show over candles on your chart.\n\nTrendlines can be either shown directly between candles on the chart or as lines to the right of the chart, so they don't get in the way.\n\n0 = show all untested trendlines directly on chart\n-1 = show all untested trendlines as lines to the right of the chart\n1-500 = show that many untested trendlines directly on chart and the rest to the right of the chart" )
i_untested_display_off_chart_t3 = input.bool( false, "Display Untested Trendlines off Chart", group = GROUP_TF3)
i_untested_show_label_t3 = input.bool( true, "Show Untested Labels", group = GROUP_TF3 )
i_touched_max_t3 = input.int( 12, "Max Touched Trendlines", group = GROUP_TF3, maxval = 500, minval = 0, tooltip = "This is the number of touched trendlines for this timeframe to show, displayed either directly over candles on the chart or indirectly as lines to the right side of the chart. Set to 0 to hide all touched trendlines." )
//i_touched_lines_on_chart_t3 = input.int( 5, "Touched Trendlines Directly on Chart", group = GROUP_TF3, minval = -1, maxval = 500, tooltip = "This is the number of touched trendlines to show over candles on your chart.\n\nTrendlines can be either shown directly between candles on the chart or as lines to the right of the chart, so they don't get in the way.\n\n0 = show all touched trendlines directly on chart\n-1 = show all touched trendlines as lines to the right of the chart\n1-500 = show that many touched trendlines directly on chart and the rest to the right of the chart" )
i_touched_display_off_chart_t3 = input.bool( false, "Display Touched Trendlines off Chart", group = GROUP_TF3)
i_touched_show_label_t3 = input.bool( true, "Show Touched Labels", group = GROUP_TF3 )
i_mitigated_max_t3 = input.int( 8, "Max Mitigated Trendlines", group = GROUP_TF3, maxval = 500, minval = 0, tooltip = "This is the number of mitigated trendlines for this timeframe to show, displayed either directly over candles on the chart or indirectly as lines to the right side of the chart. Set to 0 to hide all mitigated trendlines." )
//i_mitigated_lines_on_chart_t3 = input.int( 5, "Mitigated Trendlines Directly on Chart", group = GROUP_TF3, minval = -1, maxval = 500, tooltip = "This is the number of mitigated trendlines to show over candles on your chart.\n\nTrendlines can be either shown directly between candles on the chart or as lines to the right of the chart, so they don't get in the way.\n\n0 = show all mitigated trendlines directly on chart\n-1 = show all mitigated trendlines as lines to the right of the chart\n1-500 = show that many mitigated trendlines directly on chart and the rest to the right of the chart" )
i_mitigated_display_off_chart_t3 = input.bool( true, "Display Mitigated Trendlines off Chart", group = GROUP_TF3)
i_mitigated_show_label_t3 = input.bool( false, "Show Mitigated Labels", group = GROUP_TF3 )

i_line_color_untested_t3 = input.color( color.blue, "Untested Trendline", group = GROUP_TF3_APPEARANCE, inline = "2_untested" )
i_line_size_untested_t3 = input.int( 1, "", group = GROUP_TF3_APPEARANCE, inline = "2_untested" )
i_line_style_untested_t3 = input.string( line.style_solid, "", options = [line.style_solid, line.style_dashed, line.style_dotted], group = GROUP_TF3_APPEARANCE, inline = "2_untested" )
i_line_extend_untested_t3 = input.bool( false, "Extend", group = GROUP_TF3_APPEARANCE, inline = "2_untested", tooltip = "Untested trendlines are the ones where price made a swing high / low but the price did not return there yet." )

i_line_color_touched_t3 = input.color( #801922, "Touched Trendline", group = GROUP_TF3_APPEARANCE, inline = "2_touched" )
i_line_size_touched_t3 = input.int( 1, "", group = GROUP_TF3_APPEARANCE, inline = "2_touched" )
i_line_style_touched_t3 = input.string( line.style_solid, "", options = [line.style_solid, line.style_dashed, line.style_dotted], group = GROUP_TF3_APPEARANCE, inline = "2_touched" )
i_line_extend_touched_t3 = input.bool( false, "Extend", group = GROUP_TF3_APPEARANCE, inline = "2_touched", tooltip = "Touched trendlines are the ones where price returned but failed to cross over that swing high/low." )

i_line_color_mitigated_t3 = input.color( #9598a1, "Mitigated Trendline", group = GROUP_TF3_APPEARANCE, inline = "2_mitigated" )
i_line_size_mitigated_t3 = input.int( 1, "", group = GROUP_TF3_APPEARANCE, inline = "2_mitigated" )
i_line_style_mitigated_t3 = input.string( line.style_dashed, "", options = [line.style_solid, line.style_dashed, line.style_dotted], group = GROUP_TF3_APPEARANCE, inline = "2_mitigated" )
i_line_extend_mitigated_t3 = input.bool( false, "Extend", group = GROUP_TF3_APPEARANCE, inline = "2_mitigated", tooltip = "Mitigated trendlines are the ones where price returned and crossed over, thus rendering the trendline invalid." )

// TIMEFRAME 4
i_use_tf4 = input.bool( false, "Use Timeframe 4", group = GROUP_TF4 )
i_tf_t4 = input.timeframe( "60", "Timeframe", group = GROUP_TF4 )
i_text_t4 = input.string( "", "Text on Trendline", group = GROUP_TF4, inline = "t4_txt")
i_text_use_period_t4 = input.bool( false, "Use Period Name", group = GROUP_TF4, inline = "t4_txt" )
i_text_size_t4 = input.string( size.normal, "Text Size", options = [ size.auto, size.huge, size.large, size.normal, size.small, size.tiny ], group = GROUP_TF4 )
i_untested_max_t4 = input.int( 12, "Max Untested Trendlines", group = GROUP_TF4, maxval = 500, minval = 1, tooltip = "This is the number of untested trendlines for this timeframe to show, displayed either directly over candles on the chart or indirectly as lines to the right side of the chart. At least a single untested trendline must be present on chart for this indicator to work, but you can use the Style tab to hide even this one." )
//i_untested_lines_on_chart_t4 = input.int( 5, "Untested Trendlines Directly on Chart", group = GROUP_TF4, minval = -1, maxval = 500, tooltip = "This is the number of untested trendlines to show over candles on your chart.\n\nTrendlines can be either shown directly between candles on the chart or as lines to the right of the chart, so they don't get in the way.\n\n0 = show all untested trendlines directly on chart\n-1 = show all untested trendlines as lines to the right of the chart\n1-500 = show that many untested trendlines directly on chart and the rest to the right of the chart" )
i_untested_display_off_chart_t4 = input.bool( false, "Display Untested Trendlines off Chart", group = GROUP_TF4)
i_untested_show_label_t4 = input.bool( true, "Show Untested Labels", group = GROUP_TF4 )
i_touched_max_t4 = input.int( 12, "Max Touched Trendlines", group = GROUP_TF4, maxval = 500, minval = 0, tooltip = "This is the number of touched trendlines for this timeframe to show, displayed either directly over candles on the chart or indirectly as lines to the right side of the chart. Set to 0 to hide all touched trendlines." )
//i_touched_lines_on_chart_t4 = input.int( 5, "Touched Trendlines Directly on Chart", group = GROUP_TF4, minval = -1, maxval = 500, tooltip = "This is the number of touched trendlines to show over candles on your chart.\n\nTrendlines can be either shown directly between candles on the chart or as lines to the right of the chart, so they don't get in the way.\n\n0 = show all touched trendlines directly on chart\n-1 = show all touched trendlines as lines to the right of the chart\n1-500 = show that many touched trendlines directly on chart and the rest to the right of the chart" )
i_touched_display_off_chart_t4 = input.bool( false, "Display Touched Trendlines off Chart", group = GROUP_TF4)
i_touched_show_label_t4 = input.bool( true, "Show Touched Labels", group = GROUP_TF4 )
i_mitigated_max_t4 = input.int( 8, "Max Mitigated Trendlines", group = GROUP_TF4, maxval = 500, minval = 0, tooltip = "This is the number of mitigated trendlines for this timeframe to show, displayed either directly over candles on the chart or indirectly as lines to the right side of the chart. Set to 0 to hide all mitigated trendlines." )
//i_mitigated_lines_on_chart_t4 = input.int( 5, "Mitigated Trendlines Directly on Chart", group = GROUP_TF4, minval = -1, maxval = 500, tooltip = "This is the number of mitigated trendlines to show over candles on your chart.\n\nTrendlines can be either shown directly between candles on the chart or as lines to the right of the chart, so they don't get in the way.\n\n0 = show all mitigated trendlines directly on chart\n-1 = show all mitigated trendlines as lines to the right of the chart\n1-500 = show that many mitigated trendlines directly on chart and the rest to the right of the chart" )
i_mitigated_display_off_chart_t4 = input.bool( true, "Display Mitigated Trendlines off Chart", group = GROUP_TF4)
i_mitigated_show_label_t4 = input.bool( false, "Show Mitigated Labels", group = GROUP_TF4 )

i_line_color_untested_t4 = input.color( color.fuchsia, "Untested Trendline", group = GROUP_TF4_APPEARANCE, inline = "2_untested" )
i_line_size_untested_t4 = input.int( 1, "", group = GROUP_TF4_APPEARANCE, inline = "2_untested" )
i_line_style_untested_t4 = input.string( line.style_solid, "", options = [line.style_solid, line.style_dashed, line.style_dotted], group = GROUP_TF4_APPEARANCE, inline = "2_untested" )
i_line_extend_untested_t4 = input.bool( false, "Extend", group = GROUP_TF4_APPEARANCE, inline = "2_untested", tooltip = "Untested trendlines are the ones where price made a swing high / low but the price did not return there yet." )

i_line_color_touched_t4 = input.color( color.black, "Touched Trendline", group = GROUP_TF4_APPEARANCE, inline = "2_touched" )
i_line_size_touched_t4 = input.int( 1, "", group = GROUP_TF4_APPEARANCE, inline = "2_touched" )
i_line_style_touched_t4 = input.string( line.style_solid, "", options = [line.style_solid, line.style_dashed, line.style_dotted], group = GROUP_TF4_APPEARANCE, inline = "2_touched" )
i_line_extend_touched_t4 = input.bool( false, "Extend", group = GROUP_TF4_APPEARANCE, inline = "2_touched", tooltip = "Touched trendlines are the ones where price returned but failed to cross over that swing high/low." )

i_line_color_mitigated_t4 = input.color( #787b86, "Mitigated Trendline", group = GROUP_TF4_APPEARANCE, inline = "2_mitigated" )
i_line_size_mitigated_t4 = input.int( 1, "", group = GROUP_TF4_APPEARANCE, inline = "2_mitigated" )
i_line_style_mitigated_t4 = input.string( line.style_dashed, "", options = [line.style_solid, line.style_dashed, line.style_dotted], group = GROUP_TF4_APPEARANCE, inline = "2_mitigated" )
i_line_extend_mitigated_t4 = input.bool( false, "Extend", group = GROUP_TF4_APPEARANCE, inline = "2_mitigated", tooltip = "Mitigated trendlines are the ones where price returned and crossed over, thus rendering the trendline invalid." )



// VARIABLES
transparentcolor = color.new(color.white,100)
var untested_t1 = array.new_line()
var labels_untested_t1 = array.new_label()
var touched_t1 = array.new_line()
var labels_touched_t1 = array.new_label()
var mitigated_t1 = array.new_line()
var labels_mitigated_t1 = array.new_label()
var labels_orig_x_t1 = matrix.new<float>( 0, 2 ) // 0 = X line parameter (in bars or a timestamp), 1 = Y line parameter

var untested_t2 = array.new_line()
var labels_untested_t2 = array.new_label()
var touched_t2 = array.new_line()
var labels_touched_t2 = array.new_label()
var mitigated_t2 = array.new_line()
var labels_mitigated_t2 = array.new_label()
var labels_orig_x_t2 = matrix.new<float>( 0, 2 ) // 0 = X line parameter (in bars or a timestamp), 1 = Y line parameter

var untested_t3 = array.new_line()
var labels_untested_t3 = array.new_label()
var touched_t3 = array.new_line()
var labels_touched_t3 = array.new_label()
var mitigated_t3 = array.new_line()
var labels_mitigated_t3 = array.new_label()
var labels_orig_x_t3 = matrix.new<float>( 0, 2 ) // 0 = X line parameter (in bars or a timestamp), 1 = Y line parameter

var untested_t4 = array.new_line()
var labels_untested_t4 = array.new_label()
var touched_t4 = array.new_line()
var labels_touched_t4 = array.new_label()
var mitigated_t4 = array.new_line()
var labels_mitigated_t4 = array.new_label()
var labels_orig_x_t4 = matrix.new<float>( 0, 2 ) // 0 = X line parameter (in bars or a timestamp), 1 = Y line parameter


// FUNCTIONS
s_( float txt ) =>
    str.tostring( txt )

f_debug_label( txt ) =>
    label.new(bar_index, high, str.tostring( txt ), color = color.lime, textcolor = color.black)

f_debug_label_down( txt ) =>
    label.new(bar_index, low, str.tostring( txt ), color = color.lime, textcolor = color.black, style = label.style_label_up)

f_find_orig_x_for_y( y, matrixVar ) =>
    ret = -1.0
    for i = 0 to matrix.rows( matrixVar )
        if matrix.get( matrixVar, i, 1 ) == y
            ret := matrix.get( matrixVar, i, 0 )
            break
    ret

f_remove_orig_x_for_y( y, matrixVar ) =>
    for i = 0 to matrix.rows( matrixVar )
        if matrix.get( matrixVar, i, 1 ) == y
            matrix.remove_row( matrixVar, i )
            break

f_get_tf_diff( tf ) =>
    // calculate the bars difference between current TF and the one we're processing,
    // so we can position the beginning of trendlines correctly (otherwise lines from 1H chart on 30m display would be half the bars offset to the left)
    current_tf_minutes = timeframe.in_seconds( timeframe.period )
    scanned_tf_minutes = timeframe.in_seconds( tf )
    tf_bars_difference = math.floor( scanned_tf_minutes / current_tf_minutes )
    tf_bars_difference

f_move_line_off_chart( ln ) =>
    line.set_xloc( ln, bar_index + i_text_offset - 1, bar_index + i_text_offset - 1 + i_right_line_width, xloc.bar_index )

f_return_line_to_chart( ln, y, matrixVar ) =>
    line.set_xloc( ln, math.round( f_find_orig_x_for_y( y, matrixVar ) ), time, xloc.bar_time )

f_check_line_within_threshold( theLineArray, is_swing_high, is_swing_low, hHigh1, hLow1 ) =>
    is_within_threshold = true

    for theLine in theLineArray
        if is_swing_high
            if math.abs( ( ( hHigh1 - line.get_y1( theLine ) ) / line.get_y1( theLine ) ) * 100 ) < i_min_distance_threshold
                is_within_threshold := false
                break

        if is_swing_low
            if math.abs( ( ( hLow1 - line.get_y1( theLine ) ) / line.get_y1( theLine ) ) * 100 ) < i_min_distance_threshold
                is_within_threshold := false
                break

    is_within_threshold

f_get_real_label_text( tf, label_use_period, label_text ) =>
    ret = label_text
    tf_real = tf
    if tf == ""
        tf_real := timeframe.period

    if label_use_period
        if tf_real == "1"
            ret := "1m"
        else if tf_real == "2"
            ret := "2m"
        else if tf_real == "3"
            ret := "3m"
        else if tf_real == "5"
            ret := "5m"
        else if tf_real == "15"
            ret := "15m"
        else if tf_real == "45"
            ret := "45m"
        else if tf_real == "60"
            ret := "1H"
            ret := "1H"
        else if tf_real == "120"
            ret := "2H"
        else if tf_real == "180"
            ret := "3H"
        else if tf_real == "240"
            ret := "4H"
        else
            ret := tf_real

    ret

f_check_trendlines( which, tf ) =>
    [ hOpen, hOpen1, hOpen2, hClose, hClose1, hClose2, hHigh, hHigh1, hHigh2, hLow, hLow1, hLow2, hBar, hBar1, hBar2 ] = request.security(syminfo.tickerid, tf, [ open, open[ 1 ], open[ 2 ], close, close[ 1 ], close[ 2 ], high, high[ 1 ], high[ 2 ], low, low[ 1 ], low[ 2 ], bar_index, bar_index[ 1 ], bar_index[ 2 ] ], barmerge.gaps_off, barmerge.lookahead_on)

    array<line> untested = na
    array<label> labels_untested = na
    int untested_lines_on_chart = na
    int untested_max = na
    bool untested_show_label = na
    array<line> touched = na
    array<label> labels_touched = na
    int touched_lines_on_chart = na
    int touched_max = na
    bool touched_show_label = na
    array<line> mitigated = na
    array<label> labels_mitigated = na
    int mitigated_lines_on_chart = na
    matrix<float> labels_orig_x = na
    int mitigated_max = na
    bool mitigated_show_label = na

    string label_text = na
    bool label_use_period = na
    string label_size = na

    color i_line_color_untested = na
    int i_line_size_untested = na
    string i_line_style_untested = na
    bool i_line_extend_untested = na

    color i_line_color_touched = na
    int i_line_size_touched = na
    string i_line_style_touched = na
    bool i_line_extend_touched = na

    color i_line_color_mitigated = na
    int i_line_size_mitigated = na
    string i_line_style_mitigated = na
    bool i_line_extend_mitigated = na

    if which == 1
        untested := untested_t1
        labels_untested := labels_untested_t1
        untested_lines_on_chart := i_untested_display_off_chart_t1 ? -1 : 0
        untested_max := i_untested_max_t1
        untested_show_label := i_untested_show_label_t1
        touched := touched_t1
        labels_touched := labels_touched_t1
        touched_lines_on_chart := i_touched_display_off_chart_t1 ? -1 : 0
        touched_max := i_touched_max_t1
        touched_show_label := i_touched_show_label_t1
        mitigated := mitigated_t1
        labels_mitigated := labels_mitigated_t1
        mitigated_lines_on_chart := i_mitigated_display_off_chart_t1 ? -1 : 0
        mitigated_max := i_mitigated_max_t1
        mitigated_show_label := i_mitigated_show_label_t1
        labels_orig_x := labels_orig_x_t1
        label_text := i_text_t1
        label_size := i_text_size_t1
        label_use_period := i_text_use_period_t1
        i_line_color_untested := i_line_color_untested_t1
        i_line_size_untested := i_line_size_untested_t1
        i_line_style_untested := i_line_style_untested_t1
        i_line_extend_untested := i_line_extend_untested_t1
        i_line_color_touched := i_line_color_touched_t1
        i_line_size_touched := i_line_size_touched_t1
        i_line_style_touched := i_line_style_touched_t1
        i_line_extend_touched := i_line_extend_touched_t1
        i_line_color_mitigated := i_line_color_mitigated_t1
        i_line_size_mitigated := i_line_size_mitigated_t1
        i_line_style_mitigated := i_line_style_mitigated_t1
        i_line_extend_mitigated := i_line_extend_mitigated_t1

    if which == 2
        untested := untested_t2
        labels_untested := labels_untested_t2
        untested_lines_on_chart := i_untested_display_off_chart_t2 ? -1 : 0
        untested_max := i_untested_max_t2
        untested_show_label := i_untested_show_label_t2
        touched := touched_t2
        labels_touched := labels_touched_t2
        touched_lines_on_chart := i_touched_display_off_chart_t3 ? -1 : 0
        touched_max := i_touched_max_t2
        touched_show_label := i_touched_show_label_t2
        mitigated := mitigated_t2
        labels_mitigated := labels_mitigated_t2
        mitigated_lines_on_chart := i_mitigated_display_off_chart_t3 ? -1 : 0
        mitigated_max := i_mitigated_max_t2
        mitigated_show_label := i_mitigated_show_label_t2
        labels_orig_x := labels_orig_x_t2
        label_text := i_text_t2
        label_use_period := i_text_use_period_t2
        label_size := i_text_size_t2
        i_line_color_untested := i_line_color_untested_t2
        i_line_size_untested := i_line_size_untested_t2
        i_line_style_untested := i_line_style_untested_t2
        i_line_extend_untested := i_line_extend_untested_t2
        i_line_color_touched := i_line_color_touched_t2
        i_line_size_touched := i_line_size_touched_t2
        i_line_style_touched := i_line_style_touched_t2
        i_line_extend_touched := i_line_extend_touched_t2
        i_line_color_mitigated := i_line_color_mitigated_t2
        i_line_size_mitigated := i_line_size_mitigated_t2
        i_line_style_mitigated := i_line_style_mitigated_t2
        i_line_extend_mitigated := i_line_extend_mitigated_t2

    if which == 3
        untested := untested_t3
        labels_untested := labels_untested_t3
        untested_lines_on_chart := i_untested_display_off_chart_t3 ? -1 : 0
        untested_max := i_untested_max_t3
        untested_show_label := i_untested_show_label_t3
        touched := touched_t3
        labels_touched := labels_touched_t3
        touched_lines_on_chart := i_touched_display_off_chart_t3 ? -1 : 0
        touched_max := i_touched_max_t3
        touched_show_label := i_touched_show_label_t3
        mitigated := mitigated_t3
        labels_mitigated := labels_mitigated_t3
        mitigated_lines_on_chart := i_mitigated_display_off_chart_t3 ? -1 : 0
        mitigated_max := i_mitigated_max_t3
        mitigated_show_label := i_mitigated_show_label_t3
        labels_orig_x := labels_orig_x_t3
        label_text := i_text_t3
        label_size := i_text_size_t3
        label_use_period := i_text_use_period_t3
        i_line_color_untested := i_line_color_untested_t3
        i_line_size_untested := i_line_size_untested_t3
        i_line_style_untested := i_line_style_untested_t3
        i_line_extend_untested := i_line_extend_untested_t3
        i_line_color_touched := i_line_color_touched_t3
        i_line_size_touched := i_line_size_touched_t3
        i_line_style_touched := i_line_style_touched_t3
        i_line_extend_touched := i_line_extend_touched_t3
        i_line_color_mitigated := i_line_color_mitigated_t3
        i_line_size_mitigated := i_line_size_mitigated_t3
        i_line_style_mitigated := i_line_style_mitigated_t3
        i_line_extend_mitigated := i_line_extend_mitigated_t3

    if which == 4
        untested := untested_t4
        labels_untested := labels_untested_t4
        untested_lines_on_chart := i_untested_display_off_chart_t4 ? -1 : 0
        untested_max := i_untested_max_t4
        untested_show_label := i_untested_show_label_t4
        touched := touched_t4
        labels_touched := labels_touched_t4
        touched_lines_on_chart := i_touched_display_off_chart_t4 ? -1 : 0
        touched_max := i_touched_max_t4
        touched_show_label := i_touched_show_label_t4
        mitigated := mitigated_t4
        labels_mitigated := labels_mitigated_t4
        mitigated_lines_on_chart := i_mitigated_display_off_chart_t4 ? -1 : 0
        mitigated_max := i_mitigated_max_t4
        mitigated_show_label := i_mitigated_show_label_t4
        labels_orig_x := labels_orig_x_t4
        label_text := i_text_t4
        label_size := i_text_size_t4
        label_use_period := i_text_use_period_t4
        i_line_color_untested := i_line_color_untested_t4
        i_line_size_untested := i_line_size_untested_t4
        i_line_style_untested := i_line_style_untested_t4
        i_line_extend_untested := i_line_extend_untested_t4
        i_line_color_touched := i_line_color_touched_t4
        i_line_size_touched := i_line_size_touched_t4
        i_line_style_touched := i_line_style_touched_t4
        i_line_extend_touched := i_line_extend_touched_t4
        i_line_color_mitigated := i_line_color_mitigated_t4
        i_line_size_mitigated := i_line_size_mitigated_t4
        i_line_style_mitigated := i_line_style_mitigated_t4
        i_line_extend_mitigated := i_line_extend_mitigated_t4

    // update label if we're using period
    label_text := f_get_real_label_text( tf, label_use_period, label_text )

    // calculate the bars difference between current TF and the one we're processing,
    // so we can position the beginning of trendlines correctly (otherwise lines from 1H chart on 30m display would be half the bars offset to the left)
    tf_bars_difference = f_get_tf_diff( tf )

    // when a new swing low/high is found, add it into the untested array
    if nz( hHigh1 )
        is_swing_high = false
        is_swing_low  = false

        if i_check_type == CHECK_TYPE_BODY
            if hClose > hOpen or hClose < hOpen
                is_swing_high := hClose1 > hClose and hClose1 > hClose2
                is_swing_low := hOpen1 < hOpen and hOpen1 < hOpen2
        else
            is_swing_high := hHigh1 > hHigh and hHigh1 > hHigh2
            is_swing_low := hLow1 < hLow and hLow1 < hLow2

        //f_debug_label_down( s_( hHigh2 ) + " / " + s_( hHigh1 ) + " / " + s_( hHigh ) + "\n" + s_( hLow2 ) + " / " + s_( hLow1 ) + " / " + s_( hLow ) )

        if is_swing_high or is_swing_low
            // prepare line coordinates
            x1_high = 0
            x2_high = 0
            xloc_high = ""
            x1_low = 0
            x2_low = 0
            xloc_low = ""

            highest = 0.0
            lowest = high + 10000

            starting_bar = ( hBar2 * tf_bars_difference ) - 1
            ending_bar = ( hBar * tf_bars_difference ) - 1
            // if we're on the same TF, our trendline point is the middle bar - hBar1
            trendline_bar_high = ( hBar1 * tf_bars_difference ) - 1
            trendline_bar_low = ( hBar1 * tf_bars_difference ) - 1

            // if we're showing all trendlines on the right, we must set xloc to use bars parameter,
            // as TV has a bug (or functionality) which prevents time-based xloc to create lines into the future
            if untested_lines_on_chart == -1
                x1_high := bar_index + i_text_offset
                x1_low := bar_index + i_text_offset
                x2_high := bar_index + i_text_offset + i_right_line_width
                x2_low := bar_index + i_text_offset + i_right_line_width
                xloc_high := xloc.bar_index
                xloc_low := xloc.bar_index
            else
                // check where our high/low is between the starting and ending bar on our TF
                if starting_bar != hBar2 or ending_bar != hBar
                    // go through all bars up to the last hBar2 on the HTF
                    loop_end = ( 3 * tf_bars_difference )
                    loop_end := loop_end > 4999 ? 4999 : loop_end

                    if i_check_type == CHECK_TYPE_BODY
                        for b = 0 to loop_end
                            if is_swing_high and ( hBar[ b ] == hBar or hBar[ b ] == hBar1 or hBar[ b ] == hBar2 ) and ( highest < open[ b ] or highest < close[ b ] )
                                highest := highest < open[ b ] ? open [ b ] : close[ b ]
                                trendline_bar_high := bar_index - b

                            if is_swing_low and ( hBar[ b ] == hBar or hBar[ b ] == hBar1 or hBar[ b ] == hBar2 ) and ( lowest > open[ b ] or lowest > close[ b ] )
                                lowest := lowest > open[ b ] ? open[ b ] : close[ b ]
                                trendline_bar_low := bar_index - b
                    else
                        for b = 0 to loop_end
                            if is_swing_high and ( hBar[ b ] == hBar or hBar[ b ] == hBar1 or hBar[ b ] == hBar2 ) and highest < high[ b ]
                                highest := high[ b ]
                                trendline_bar_high := bar_index - b

                            if is_swing_low and ( hBar[ b ] == hBar or hBar[ b ] == hBar1 or hBar[ b ] == hBar2 ) and lowest > low[ b ]
                                lowest := low[ b ]
                                trendline_bar_low := bar_index - b

                // convert bar values into UNIX timestamps, so we don't get "bar too far in the past" errors
                x1_high := time - ( ( bar_index - trendline_bar_high ) * ( timeframe.in_seconds( timeframe.period ) * 1000 ) )
                x2_high := time
                xloc_high := xloc.bar_time
                x1_low := time - ( ( bar_index - trendline_bar_low ) * ( timeframe.in_seconds( timeframe.period ) * 1000 ) )
                x2_low := time
                xloc_low := xloc.bar_time

            // we may have both - swing high and swing low - on the same candle,
            // so we need to store both of their Y positions for later checks
            y_high = -1.0
            y_low = -1.0

            if i_check_type == CHECK_TYPE_BODY
                y_high := is_swing_high ? hClose1 : -1.0
                y_low := is_swing_low ? hOpen1 : -1.0
            else
                y_high := is_swing_high ? hHigh1 : -1.0
                y_low := is_swing_low ? hLow1 : -1.0

            // check whether we should remove LFT trendline which would be on the same Y level as our new one (HTF trendlines are stronger)
            if which != 1 and timeframe.in_seconds( tf ) > timeframe.in_seconds( i_tf_t1 )
                if y_high > -1 and array.size( untested_t1 ) > 0
                    for t = 0 to array.size( untested_t1 ) - 1
                        if line.get_y1( array.get( untested_t1, t ) ) == y_high
                            line.delete( array.remove( untested_t1, t ) )
                            if i_text_t1 != "" or i_text_use_period_t1
                                label.delete( array.remove( labels_untested_t1, t ) )
                            break

                if y_low > -1 and array.size( untested_t1 ) > 0
                    for t = 0 to array.size( untested_t1 ) - 1
                        if line.get_y1( array.get( untested_t1, t ) ) == y_low
                            line.delete( array.remove( untested_t1, t ) )
                            if i_text_t1 != "" or i_text_use_period_t1
                                label.delete( array.remove( labels_untested_t1, t ) )
                            break

            if which != 2 and timeframe.in_seconds( tf ) > timeframe.in_seconds( i_tf_t2 )
                if y_high > -1 and array.size( untested_t2 ) > 0
                    for t = 0 to array.size( untested_t2 ) - 1
                        if line.get_y1( array.get( untested_t2, t ) ) == y_high
                            line.delete( array.remove( untested_t2, t ) )
                            if i_text_t2 != "" or i_text_use_period_t2
                                label.delete( array.remove( labels_untested_t2, t ) )
                            break

                if y_low > -1 and array.size( untested_t2 ) > 0
                    for t = 0 to array.size( untested_t2 ) - 1
                        if line.get_y1( array.get( untested_t2, t ) ) == y_low
                            line.delete( array.remove( untested_t2, t ) )
                            if i_text_t2 != "" or i_text_use_period_t2
                                label.delete( array.remove( labels_untested_t2, t ) )
                            break

            if which != 3 and timeframe.in_seconds( tf ) > timeframe.in_seconds( i_tf_t3 )
                if y_high > -1 and array.size( untested_t3 ) > 0
                    for t = 0 to array.size( untested_t3 ) - 1
                        if line.get_y1( array.get( untested_t3, t ) ) == y_high
                            line.delete( array.remove( untested_t3, t ) )
                            if i_text_t3 != "" or i_text_use_period_t3
                                label.delete( array.remove( labels_untested_t3, t ) )
                            break

                if y_low > -1 and array.size( untested_t3 ) > 0
                    for t = 0 to array.size( untested_t3 ) - 1
                        if line.get_y1( array.get( untested_t3, t ) ) == y_low
                            line.delete( array.remove( untested_t3, t ) )
                            if i_text_t3 != "" or i_text_use_period_t3
                                label.delete( array.remove( labels_untested_t3, t ) )
                            break

            if which != 4 and timeframe.in_seconds( tf ) > timeframe.in_seconds( i_tf_t4 )
                if y_high > -1 and array.size( untested_t4 ) > 0
                    for t = 0 to array.size( untested_t4 ) - 1
                        if line.get_y1( array.get( untested_t4, t ) ) == y_high
                            line.delete( array.remove( untested_t4, t ) )
                            if i_text_t4 != "" or i_text_use_period_t4
                                label.delete( array.remove( labels_untested_t4, t ) )
                            break

                if y_low > -1 and array.size( untested_t4 ) > 0
                    for t = 0 to array.size( untested_t4 ) - 1
                        if line.get_y1( array.get( untested_t4, t ) ) == y_low
                            line.delete( array.remove( untested_t4, t ) )
                            if i_text_t4 != "" or i_text_use_period_t4
                                label.delete( array.remove( labels_untested_t4, t ) )
                            break

            // check this trendline's distance from the nearest ones
            threshold_check_t1_1 = f_check_line_within_threshold( untested_t1, is_swing_high, is_swing_low, i_check_type == CHECK_TYPE_BODY ? ( hOpen1 > hClose1 ? hOpen1 : hClose1 ) : hHigh1, i_check_type == CHECK_TYPE_BODY ? ( hClose1 < hOpen1 ? hClose1 : hOpen1 ) : hLow1 )
            threshold_check_t1_2 = f_check_line_within_threshold( touched_t1, is_swing_high, is_swing_low, i_check_type == CHECK_TYPE_BODY ? ( hOpen1 > hClose1 ? hOpen1 : hClose1 ) : hHigh1, i_check_type == CHECK_TYPE_BODY ? ( hClose1 < hOpen1 ? hClose1 : hOpen1 ) : hLow1 )
            threshold_check_t1_3 = f_check_line_within_threshold( mitigated_t1, is_swing_high, is_swing_low, i_check_type == CHECK_TYPE_BODY ? ( hOpen1 > hClose1 ? hOpen1 : hClose1 ) : hHigh1, i_check_type == CHECK_TYPE_BODY ? ( hClose1 < hOpen1 ? hClose1 : hOpen1 ) : hLow1 )
            threshold_check_t2_1 = f_check_line_within_threshold( untested_t2, is_swing_high, is_swing_low, i_check_type == CHECK_TYPE_BODY ? ( hOpen1 > hClose1 ? hOpen1 : hClose1 ) : hHigh1, i_check_type == CHECK_TYPE_BODY ? ( hClose1 < hOpen1 ? hClose1 : hOpen1 ) : hLow1 )
            threshold_check_t2_2 = f_check_line_within_threshold( touched_t2, is_swing_high, is_swing_low, i_check_type == CHECK_TYPE_BODY ? ( hOpen1 > hClose1 ? hOpen1 : hClose1 ) : hHigh1, i_check_type == CHECK_TYPE_BODY ? ( hClose1 < hOpen1 ? hClose1 : hOpen1 ) : hLow1 )
            threshold_check_t2_3 = f_check_line_within_threshold( mitigated_t2, is_swing_high, is_swing_low, i_check_type == CHECK_TYPE_BODY ? ( hOpen1 > hClose1 ? hOpen1 : hClose1 ) : hHigh1, i_check_type == CHECK_TYPE_BODY ? ( hClose1 < hOpen1 ? hClose1 : hOpen1 ) : hLow1 )
            threshold_check_t3_1 = f_check_line_within_threshold( untested_t3, is_swing_high, is_swing_low, i_check_type == CHECK_TYPE_BODY ? ( hOpen1 > hClose1 ? hOpen1 : hClose1 ) : hHigh1, i_check_type == CHECK_TYPE_BODY ? ( hClose1 < hOpen1 ? hClose1 : hOpen1 ) : hLow1 )
            threshold_check_t3_2 = f_check_line_within_threshold( touched_t3, is_swing_high, is_swing_low, i_check_type == CHECK_TYPE_BODY ? ( hOpen1 > hClose1 ? hOpen1 : hClose1 ) : hHigh1, i_check_type == CHECK_TYPE_BODY ? ( hClose1 < hOpen1 ? hClose1 : hOpen1 ) : hLow1 )
            threshold_check_t3_3 = f_check_line_within_threshold( mitigated_t3, is_swing_high, is_swing_low, i_check_type == CHECK_TYPE_BODY ? ( hOpen1 > hClose1 ? hOpen1 : hClose1 ) : hHigh1, i_check_type == CHECK_TYPE_BODY ? ( hClose1 < hOpen1 ? hClose1 : hOpen1 ) : hLow1 )
            threshold_check_t4_1 = f_check_line_within_threshold( untested_t4, is_swing_high, is_swing_low, i_check_type == CHECK_TYPE_BODY ? ( hOpen1 > hClose1 ? hOpen1 : hClose1 ) : hHigh1, i_check_type == CHECK_TYPE_BODY ? ( hClose1 < hOpen1 ? hClose1 : hOpen1 ) : hLow1 )
            threshold_check_t4_2 = f_check_line_within_threshold( touched_t4, is_swing_high, is_swing_low, i_check_type == CHECK_TYPE_BODY ? ( hOpen1 > hClose1 ? hOpen1 : hClose1 ) : hHigh1, i_check_type == CHECK_TYPE_BODY ? ( hClose1 < hOpen1 ? hClose1 : hOpen1 ) : hLow1 )
            threshold_check_t4_3 = f_check_line_within_threshold( mitigated_t4, is_swing_high, is_swing_low, i_check_type == CHECK_TYPE_BODY ? ( hOpen1 > hClose1 ? hOpen1 : hClose1 ) : hHigh1, i_check_type == CHECK_TYPE_BODY ? ( hClose1 < hOpen1 ? hClose1 : hOpen1 ) : hLow1 )

            is_within_threshold = threshold_check_t1_1 and threshold_check_t1_2 and threshold_check_t1_3 and threshold_check_t2_1 and threshold_check_t2_2 and threshold_check_t2_3 and threshold_check_t3_1 and threshold_check_t3_2 and threshold_check_t3_3 and threshold_check_t4_1 and threshold_check_t4_2 and threshold_check_t4_3

            if is_within_threshold
                //f_debug_label( "tf: " + tf + "\nbar: " + s_( bar_index ) + "\nstarting: " + s_( starting_bar ) + " (" + s_( hBar2 ) + ")\nending: " + s_( ending_bar ) + " (" + s_( hBar ) + ")\ntrendline high: " + s_( trendline_bar_high ) + "\ntrendline low: " + s_( trendline_bar_low ) + "\nhighest: " + s_( highest ) + "\nlowest: " + s_( lowest ) + "\ntime: " + s_( time ) + "\nx1: " + s_( x1_high ) + ", x2: " + s_( x2_high ) + "\nhigh: " + str.tostring( is_swing_high ) + ", low: " + str.tostring( is_swing_low ) )

                // remove the oldest trendline along with its label and add a new one
                if array.size( untested ) == untested_max
                    line.delete( array.pop( untested ) )
                    if label_text != ""
                        label.delete( array.pop( labels_untested ) )

                if y_high > -1
                    //f_debug_label_down( s_( x1_high ) + " / " + s_( x2_high ) )
                    newLine = line.new( x1_high, y_high, x2_high, y_high, xloc = xloc_high, color = i_line_color_untested, width = i_line_size_untested, style = i_line_style_untested, extend = i_line_extend_untested ? extend.right : extend.none )
                    array.unshift( untested, newLine )

                    // check if we should make the previous trendline off-chart
                    if untested_lines_on_chart > 0 and array.size( untested ) > untested_lines_on_chart
                        line2change = array.get( untested, untested_lines_on_chart )
                        f_move_line_off_chart( line2change )

                    // store this label's original X in case we need to return it there
                    matrix.add_row( labels_orig_x )
                    matrix.set( labels_orig_x, matrix.rows( labels_orig_x ) - 1, 0, x1_high )
                    matrix.set( labels_orig_x, matrix.rows( labels_orig_x ) - 1, 1, y_high )

                    // add label, if any
                    if label_text != ""
                        array.unshift( labels_untested, label.new( bar_index + i_text_offset, y_high, label_text + ( i_show_price_on_labels ? " (" + s_( line.get_y1( newLine ) ) + ")" : ""), size = label_size, textcolor = ( untested_show_label ? i_line_color_untested : transparentcolor ), textalign = text.align_right, style = label.style_none ) )
                        //array.unshift( labels_untested, label.new( bar_index + i_text_offset, y_high, label_text + ( i_show_price_on_labels ? " (" + s_( line.get_y1( newLine ) ) + ")" : "") + " / " + s_( x1_high ), size = label_size, textcolor = ( untested_show_label ? i_line_color_untested : transparentcolor ), textalign = text.align_right, style = label.style_none ) )

                if y_low > -1
                    newLine = line.new( x1_low, y_low, x2_low, y_low, xloc = xloc_low, color = i_line_color_untested, width = i_line_size_untested, style = i_line_style_untested, extend = i_line_extend_untested ? extend.right : extend.none )
                    array.unshift( untested, newLine )

                    // check if we should make the previous trendline off-chart
                    if untested_lines_on_chart > 0 and array.size( untested ) > untested_lines_on_chart
                        line2change = array.get( untested, untested_lines_on_chart )
                        f_move_line_off_chart( line2change )

                    // store this label's original X in case we need to return it there
                    matrix.add_row( labels_orig_x )
                    matrix.set( labels_orig_x, matrix.rows( labels_orig_x ) - 1, 0, x1_low )
                    matrix.set( labels_orig_x, matrix.rows( labels_orig_x ) - 1, 1, y_low )

                    // add label, if any
                    if label_text != ""
                        //array.unshift( labels_untested, label.new( bar_index + i_text_offset, y_low, label_text + ( i_show_price_on_labels ? " (" + s_( line.get_y1( newLine ) ) + ")" : "") + " / " + s_( x1_low ), size = label_size, textcolor = ( untested_show_label ? i_line_color_untested : transparentcolor ), textalign = text.align_right, style = label.style_none ) )
                        array.unshift( labels_untested, label.new( bar_index + i_text_offset, y_low, label_text + ( i_show_price_on_labels ? " (" + s_( line.get_y1( newLine ) ) + ")" : ""), size = label_size, textcolor = ( untested_show_label ? i_line_color_untested : transparentcolor ), textalign = text.align_right, style = label.style_none ) )


    // check all previously untested trendlines to see if we should promote them to touched or mitigated, if enabled
    if array.size( untested ) > 0
        loop_index = 0
        untested_items_left = array.new_line()
        untested_labels_left = array.new_label()
        for untested_line in untested
            // don't check the newly added untested trendline, if one was just added
            if ( hOpen < line.get_y1( untested_line ) and hHigh >= line.get_y1( untested_line ) ) or ( hOpen > line.get_y1( untested_line ) and hLow <= line.get_y1( untested_line ) )
                if touched_max > 0 and ( hOpen < line.get_y1( untested_line ) and hHigh == line.get_y1( untested_line ) ) or ( hOpen > line.get_y1( untested_line ) and hLow == line.get_y1( untested_line ) )
                    line.set_color( untested_line, i_line_color_touched )
                    line.set_width( untested_line, i_line_size_touched )
                    line.set_style( untested_line, i_line_style_touched )
                    line.set_extend( untested_line, i_line_extend_touched ? extend.right : extend.none )

                    // check if we should make the previous trendline off-chart
                    if touched_lines_on_chart > 0 and array.size( touched ) > touched_lines_on_chart
                        line2change = array.get( touched, touched_lines_on_chart )
                        f_move_line_off_chart( line2change )

                    // remove the oldest trendline with its label if we're full
                    if array.size( touched ) == touched_max
                        removedLine = array.pop( touched )
                        f_remove_orig_x_for_y( line.get_y1( removedLine ), labels_orig_x )
                        line.delete( removedLine )
                        if label_text != ""
                            label.delete( array.pop( labels_touched ) )

                    array.unshift( touched, untested_line )

                    // change label color and move it to mitigated
                    if label_text != ""
                        label_to_move = array.get( labels_untested, loop_index )
                        array.unshift( labels_touched, label_to_move )
                        label.set_textcolor(label_to_move, touched_show_label ? i_line_color_touched : transparentcolor )

                    loop_index := loop_index + 1
                else if mitigated_max > 0
                    // if we don't have touched enabled but we do have mitigated enabled OR when we crossed the price directly, move this to mitigated
                    line.set_color( untested_line, i_line_color_mitigated )
                    line.set_width( untested_line, i_line_size_mitigated )
                    line.set_style( untested_line, i_line_style_mitigated )
                    line.set_extend( untested_line, i_line_extend_mitigated ? extend.right : extend.none )

                    // check if we should make the previous trendline off-chart
                    if mitigated_lines_on_chart > 0 and array.size( mitigated ) > mitigated_lines_on_chart
                        line2change = array.get( mitigated, mitigated_lines_on_chart )
                        f_move_line_off_chart( line2change )

                    // remove the oldest trendline with its label if we're full
                    if array.size( mitigated ) == mitigated_max
                        removedLine = array.pop( mitigated )
                        f_remove_orig_x_for_y( line.get_y1( removedLine ), labels_orig_x )
                        line.delete( removedLine )
                        if label_text != ""
                            label.delete( array.pop( labels_mitigated ) )

                    array.unshift( mitigated, untested_line )

                    // change label color and move it to mitigated
                    if label_text != ""
                        label_to_move = array.get( labels_untested, loop_index )
                        array.unshift( labels_mitigated, label_to_move )
                        label.set_textcolor(label_to_move , mitigated_show_label ? i_line_color_mitigated : transparentcolor )

                    loop_index := loop_index + 1
                else
                    f_remove_orig_x_for_y( line.get_y1( untested_line ), labels_orig_x )
                    line.delete( untested_line )
                    if label_text != ""
                        label.delete( array.get( labels_untested, loop_index ) )
            else
                array.push( untested_items_left, untested_line )
                if label_text != ""
                    array.push( untested_labels_left, array.get( labels_untested, loop_index ) )
                loop_index := loop_index + 1

        // re-populate the untested array if we've touched or mitigated some of its lines
        if array.size( untested ) != array.size( untested_items_left )
            array.clear( untested )
            if label_text != ""
                array.clear( labels_untested )
            loop_index2 = 0
            for new_untested_line in untested_items_left
                array.push( untested, new_untested_line )
                if label_text != ""
                    array.push( labels_untested, array.get( untested_labels_left, loop_index2 ) )
                loop_index2 := loop_index2 + 1
    

    // check all previously touched trendlines to see if we should promote them to mitigated, if enabled
    if array.size( touched ) > 0
        loop_index = 0
        touched_items_left = array.new_line()
        touched_labels_left = array.new_label()
        for touched_line in touched
            if ( hOpen < line.get_y1( touched_line ) and hHigh > line.get_y1( touched_line ) ) or ( hOpen > line.get_y1( touched_line ) and hLow < line.get_y1( touched_line ) )
                if mitigated_max > 0
                    line.set_color( touched_line, i_line_color_mitigated )
                    line.set_width( touched_line, i_line_size_mitigated )
                    line.set_style( touched_line, i_line_style_mitigated )
                    line.set_extend( touched_line, i_line_extend_mitigated ? extend.right : extend.none )

                    // check if we should make the previous trendline off-chart
                    if touched_lines_on_chart > 0 and array.size( touched ) > touched_lines_on_chart
                        line2change = array.get( touched, touched_lines_on_chart )
                        f_move_line_off_chart( line2change )

                    // remove the oldest trendline with its label if we're full
                    if array.size( mitigated ) == mitigated_max
                        removedLine = array.pop( mitigated )
                        f_remove_orig_x_for_y( line.get_y1( removedLine ), labels_orig_x )
                        line.delete( removedLine )
                        if label_text != ""
                            label.delete( array.pop( labels_mitigated ) )

                    array.unshift( mitigated, touched_line )

                    // change label color and move it to mitigated
                    if label_text != ""
                        label_to_move = array.get( labels_touched, loop_index )
                        array.unshift( labels_mitigated, label_to_move )
                        label.set_textcolor(label_to_move , mitigated_show_label ? i_line_color_mitigated : transparentcolor )

                    loop_index := loop_index + 1
                else
                    f_remove_orig_x_for_y( line.get_y1( touched_line ), labels_orig_x )
                    line.delete( touched_line )
                    if label_text != ""
                        label.delete( array.get( labels_touched, loop_index ) )
            else
                array.push( touched_items_left, touched_line )
                if label_text != ""
                    array.push( touched_labels_left, array.get( labels_touched, loop_index ) )

                loop_index := loop_index + 1

        // re-populate the touched array if we've touched or mitigated some of its lines
        if array.size( touched ) != array.size( touched_items_left )
            array.clear( touched )
            if label_text != ""
                array.clear( labels_touched )
            loop_index2 = 0
            for new_touched_line in touched_items_left
                array.push( touched, new_touched_line )
                if label_text != ""
                    array.push( labels_touched, array.get( touched_labels_left, loop_index2 ) )
                loop_index2 := loop_index2 + 1

    [ untested, touched, mitigated, labels_untested, labels_touched, labels_mitigated, labels_orig_x ]

f_extend_lines_to_current_bar( which ) =>
    ret = true

    string tf = na
    array<line> untested = na
    int untested_lines_on_chart = na
    array<line> touched = na
    array<label> labels_touched = na
    int touched_lines_on_chart = na
    array<line> mitigated = na
    array<label> labels_mitigated = na
    int mitigated_lines_on_chart = na
    matrix<float> labels_orig_x = na

    if which == 1
        untested := untested_t1
        touched := touched_t1
        mitigated := mitigated_t1
        untested_lines_on_chart := i_untested_display_off_chart_t1 ? -1 : 0
        touched_lines_on_chart := i_touched_display_off_chart_t1 ? -1 : 0
        mitigated_lines_on_chart := i_mitigated_display_off_chart_t1 ? -1 : 0
        labels_orig_x := labels_orig_x_t1

    if which == 2
        untested := untested_t2
        touched := touched_t2
        mitigated := mitigated_t2
        untested_lines_on_chart := i_untested_display_off_chart_t2 ? -1 : 0
        touched_lines_on_chart := i_touched_display_off_chart_t2 ? -1 : 0
        mitigated_lines_on_chart := i_mitigated_display_off_chart_t2 ? -1 : 0
        labels_orig_x := labels_orig_x_t2

    if which == 3
        untested := untested_t3
        touched := touched_t3
        mitigated := mitigated_t3
        untested_lines_on_chart := i_untested_display_off_chart_t3 ? -1 : 0
        touched_lines_on_chart := i_touched_display_off_chart_t3 ? -1 : 0
        mitigated_lines_on_chart := i_mitigated_display_off_chart_t3 ? -1 : 0
        labels_orig_x := labels_orig_x_t3

    if which == 4
        untested := untested_t4
        touched := touched_t4
        mitigated := mitigated_t4
        untested_lines_on_chart := i_untested_display_off_chart_t4 ? -1 : 0
        touched_lines_on_chart := i_touched_display_off_chart_t4 ? -1 : 0
        mitigated_lines_on_chart := i_mitigated_display_off_chart_t4 ? -1 : 0
        labels_orig_x := labels_orig_x_t4

    counter = 1
    for untested_line in untested
        if ( untested_lines_on_chart > 0 and counter > untested_lines_on_chart ) or untested_lines_on_chart == -1
            f_move_line_off_chart( untested_line )
        else
            // if this line was previously off-chart, move it back
            if line.get_x1( untested_line ) == bar_index + i_text_offset - 2
                f_return_line_to_chart( untested_line, line.get_y1( untested_line ), labels_orig_x )

            line.set_x2( untested_line, time )
        counter := counter + 1

    counter := 1
    for touched_line in touched
        if ( touched_lines_on_chart > 0 and counter > touched_lines_on_chart ) or touched_lines_on_chart == -1
            f_move_line_off_chart( touched_line )
        else
            // if this line was previously off-chart, move it back
            if line.get_x1( touched_line ) == bar_index + i_text_offset - 2
                f_return_line_to_chart( touched_line, line.get_y1( touched_line ), labels_orig_x )

            line.set_x2( touched_line, time )
        counter := counter + 1

    counter := 1
    for mitigated_line in mitigated
        if ( mitigated_lines_on_chart > 0 and counter > mitigated_lines_on_chart ) or mitigated_lines_on_chart == -1
            f_move_line_off_chart( mitigated_line )
        else
            // if this line was previously off-chart, move it back
            if line.get_x1( mitigated_line ) == bar_index + i_text_offset - 2
                f_return_line_to_chart( mitigated_line, line.get_y1( mitigated_line ), labels_orig_x )

            line.set_x2( mitigated_line, time )
        counter := counter + 1

    ret

f_extend_labels_to_current_bar( labels_untested_array, labels_touched_array, labels_mitigated_array ) =>
    ret = true

    for lab in labels_untested_array
        label.set_x( lab, bar_index + i_text_offset )

    for lab in labels_touched_array
        label.set_x( lab, bar_index + i_text_offset )

    for lab in labels_mitigated_array
        label.set_x( lab, bar_index + i_text_offset )

    ret



// LOGIC
errors = array.new_string()
if i_use_tf1
    if timeframe.in_seconds( timeframe.period ) <= timeframe.in_seconds( i_tf_t1 )
        [ untested, touched, mitigated, labels_untested, labels_touched, labels_mitigated, labels_orig_x ] = f_check_trendlines( 1, i_tf_t1 )
        untested_t1 := untested
        touched_t1 := touched
        mitigated_t1 := mitigated
        labels_untested_t1 := labels_untested
        labels_touched_t1 := labels_touched
        labels_mitigated_t1 := labels_mitigated
        labels_orig_x_t1 := labels_orig_x
        f_extend_lines_to_current_bar( 1 )
        f_extend_labels_to_current_bar( labels_untested_t1, labels_touched_t1, labels_mitigated_t1 )
    else
        array.push( errors, "Timeframe 1 on lower timeframe than current - not showing trendlines")

if i_use_tf2
    if timeframe.in_seconds( timeframe.period ) <= timeframe.in_seconds( i_tf_t2 )
        [ untested, touched, mitigated, labels_untested, labels_touched, labels_mitigated, labels_orig_x ] = f_check_trendlines( 2, i_tf_t2 )
        untested_t2 := untested
        touched_t2 := touched
        mitigated_t2 := mitigated
        labels_untested_t2 := labels_untested
        labels_touched_t2 := labels_touched
        labels_mitigated_t2 := labels_mitigated
        labels_orig_x_t2 := labels_orig_x
        f_extend_lines_to_current_bar( 2 )
        f_extend_labels_to_current_bar( labels_untested_t2, labels_touched_t2, labels_mitigated_t2 )
    else
        array.push( errors, "Timeframe 2 on lower timeframe than current - not showing trendlines")

if i_use_tf3
    if timeframe.in_seconds( timeframe.period ) <= timeframe.in_seconds( i_tf_t3 )
        [ untested, touched, mitigated, labels_untested, labels_touched, labels_mitigated, labels_orig_x ] = f_check_trendlines( 3, i_tf_t3 )
        untested_t3 := untested
        touched_t3 := touched
        mitigated_t3 := mitigated
        labels_untested_t3 := labels_untested
        labels_touched_t3 := labels_touched
        labels_mitigated_t3 := labels_mitigated
        labels_orig_x_t3 := labels_orig_x
        f_extend_lines_to_current_bar( 3 )
        f_extend_labels_to_current_bar( labels_untested_t3, labels_touched_t3, labels_mitigated_t3 )
    else
        array.push( errors, "Timeframe 3 on lower timeframe than current - not showing trendlines")

if i_use_tf4
    if timeframe.in_seconds( timeframe.period ) <= timeframe.in_seconds( i_tf_t4 )
        [ untested, touched, mitigated, labels_untested, labels_touched, labels_mitigated, labels_orig_x ] = f_check_trendlines( 4, i_tf_t4 )
        untested_t4 := untested
        touched_t4 := touched
        mitigated_t4 := mitigated
        labels_untested_t4 := labels_untested
        labels_touched_t4 := labels_touched
        labels_mitigated_t4 := labels_mitigated
        labels_orig_x_t4 := labels_orig_x
        f_extend_lines_to_current_bar( 4 )
        f_extend_labels_to_current_bar( labels_untested_t4, labels_touched_t4, labels_mitigated_t4 )
    else
        array.push( errors, "Timeframe 4 on lower timeframe than current - not showing trendlines")

if array.size( errors ) > 0
    var err_table = table.new(position = position.bottom_right, columns = 1, rows = array.size( errors ), border_width = 1)
    for i = 0 to array.size( errors ) - 1
        table.cell(table_id = err_table, column = 0, row = i , text = array.get( errors, i ), bgcolor=color.black, text_color=color.white, text_halign = text.align_left)