Dynamic Trendlines Multi-Timeframe
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © jjustingreyy


//@version=5
indicator(title='Dynamic Trendlines Multi-Timeframe', overlay=true, shorttitle="MTF Dyno Lines")

// Input settings
atrLength = input(14, title="ATR Length")
thresholdMultiplier = input(1.5, title="Threshold Multiplier")
HTF = input.timeframe('', 'TimeFrame')

// Get data from the higher timeframe
[h_high, h_low, h_close] = request.security(syminfo.tickerid, HTF, [high, low, close])

// Calculate True Range and Average True Range
trueRange = math.max(math.max(h_high - h_low, math.abs(h_high - h_close[1])), math.abs(h_low - h_close[1]))
atr = ta.sma(trueRange, atrLength)
threshold = atr * thresholdMultiplier

// Determine if it's a high volatility candle
isHighVolatility = h_close > h_close[1] + threshold

// Line settings
last = input(5, 'Display last X lines')
lineLevel = isHighVolatility ? (h_close[1] > h_close ? h_high : h_low) : na
// Line management variables
var line[] mylines = array.new_line(0)
var bool[] mylinestyles = array.new_bool(0) // Store initial styles
var int[] crossCount = array.new_int(0) // Store cross counts
var float prevLineLevel = na
var int prevBarIndex = na
var bool prevState = na

// Draw trendlines
if isHighVolatility
    if not na(prevLineLevel)
        // Determine the initial color and style based on price action
        initialColor = close[1] > lineLevel ? color.rgb(108, 240, 126) : color.red
        initialStyle = close[1] > lineLevel ? line.style_solid : line.style_solid

        // Create the new line and add it to the array
        lineId = line.new(x1=prevBarIndex, y1=prevLineLevel, x2=bar_index[0], y2=lineLevel, xloc=xloc.bar_index, style=initialStyle, extend=array.size(mylines) == last - 1 ? extend.right : extend.none, color=initialColor, width=2)
        array.push(mylines, lineId)
        array.push(mylinestyles, initialStyle == line.style_solid) // Store the initial style as a boolean (true for solid)
        array.push(crossCount, 0) // Initialize cross count for the new line

        // Remove lines and associated data beyond the recent X amount of lines
        if array.size(mylines) > last
            line.delete(array.shift(mylines))
            array.shift(mylinestyles)
            array.shift(crossCount)

    // Update previous line level and bar index
    prevLineLevel := lineLevel
    prevBarIndex := bar_index[0]

// Check for price crossing the trendline
var line closestLine = na
if array.size(mylines) > 0
    closestLine := array.get(mylines, array.size(mylines) - 1)

crossOver = ta.crossover(close, line.get_price(closestLine, bar_index))
crossUnder = ta.crossunder(close, line.get_price(closestLine, bar_index))

// Declare isAboveLine variable
bool isAboveLine = na

// Update the color of the closest trendline based on price action
if array.size(mylines) > 0
    isAboveLine := close >= line.get_price(closestLine, bar_index)
    if na(prevState)
        prevState := isAboveLine

    if crossOver or crossUnder
        // Increment the cross count
        array.set(crossCount, array.size(crossCount) - 1, array.get(crossCount, array.size(crossCount) - 1) + 1)

        // Check if the price has crossed the trendline twice
        if array.get(crossCount, array.size(crossCount) - 1) >= 2
            line.set_style(closestLine, line.style_dashed) // Set line style to dashed when the price crosses a red line
        else
            line.set_color(closestLine, isAboveLine ? color.rgb(108, 240, 126) : color.rgb(255, 82, 82))
        prevState := isAboveLine

    // Extend only the current line to the right
    for i = 0 to array.size(mylines) - 1
        line.set_extend(array.get(mylines, i), i == array.size(mylines) - 1 ? extend.right : extend.none)

// Plot triangles for high volatility candles with color based on the trendline it starts
shapeColor = color.rgb(255, 255, 255)
plotshape(isHighVolatility and array.size(mylines) <= last, style=shape.triangleup, location=location.belowbar, color=shapeColor, size=size.tiny)