Estrategia de seguimiento dinámico de tendencias con bandas de Bollinger y ATR: sistema de trading cuantitativo multitemporal

ATR BB 多时间框架确认 趋势跟踪 突破交易 动态支撑阻力 自适应交易 波动率过滤
Fecha de creación: 2025-04-08 09:51:56 Última modificación: 2025-04-08 09:51:56
Copiar: 0 Número de Visitas: 458
2
Seguir
319
Seguidores

Estrategia de seguimiento dinámico de tendencias con bandas de Bollinger y ATR: sistema de trading cuantitativo multitemporal Estrategia de seguimiento dinámico de tendencias con bandas de Bollinger y ATR: sistema de trading cuantitativo multitemporal

Descripción general de la estrategia

La estrategia de seguimiento de tendencias dinámicas de Brinbelt y ATR es un sistema de negociación cuantitativa avanzado que combina la función de ajuste dinámico de la señal de ruptura de Brinbelt y el rango de fluctuación real promedio (ATR) para identificar y seguir las tendencias del mercado a través del mecanismo de “línea de seguimiento” (Follow Line). La estrategia introdujo especialmente un mecanismo de confirmación de HTF en múltiples marcos de tiempo, que puede filtrar las señales de negociación en función de la dirección de la tendencia en marcos de tiempo más altos, lo que mejora significativamente la estabilidad y la rentabilidad de la estrategia.

Principio de estrategia

El núcleo de la estrategia es el mecanismo de “línea de seguimiento”, que identifica y se adapta dinámicamente a las tendencias del mercado a través de los siguientes pasos:

  1. Generación de la señal de la banda de BrynEl sistema primero calcula las bandas estándar de Bollinger, que generan una señal de aumento cuando el precio rompe la vía ascendente, y una señal de descenso cuando rompe la vía descendente. La señal es neutral cuando está dentro de la banda.

  2. Cálculo de la línea de seguimiento: De acuerdo con la señal de la banda de Brin y la posición actual del precio, el sistema calcula el valor provisional de la línea de seguimiento. En caso de una señal positiva, la línea de seguimiento se establece como el punto bajo de la línea K actual menos el valor de ATR (cuando se activa el filtro ATR) o se utiliza directamente el punto bajo; en caso de una señal negativa, la línea de seguimiento se establece como el punto alto de la línea K actual más el valor de ATR o se utiliza directamente el punto alto.

  3. Mecanismo de bloqueo de la línea de seguimiento: La estrategia utiliza la lógica de la “espina” para mantener la línea de seguimiento en la tendencia ascendente, el nuevo valor de la línea de seguimiento es el valor temporal con respecto al valor anterior mayor; en la tendencia descendente, es el valor temporal con respecto al valor anterior menor. Esto asegura que la línea de seguimiento sólo puede moverse en la dirección de la tendencia, formando un nivel de soporte / resistencia dinámico.

  4. La tendencia está establecida: Al comparar la línea de seguimiento actual con el valor de la línea de seguimiento anterior, el sistema determina la dirección de la tendencia. La columna ascendente representa una tendencia múltiple ((1)), la descendente representa una tendencia horizontal ((-1), mientras que la horizontal mantiene la tendencia anterior.

  5. Análisis de marcos de tiempo múltiplesLa estrategia utiliza una lógica similar para calcular la línea de seguimiento y el estado de la tendencia en un marco de tiempo más alto, y puede seleccionar el marco de tiempo más alto adecuado de forma automática o manual (por ejemplo, 1 minuto corresponde automáticamente a 15 minutos de HTF).

  6. Condiciones de ingreso: Cuando la tendencia del marco de tiempo de negociación cambia de neutral o descendente a ascendente y el HTF confirma la tendencia ascendente, genera una señal de hacer más; a la inversa, produce una señal de hacer menos.

  7. Condiciones de salida: Cuando la tendencia del marco de tiempo de negociación cambia en la dirección opuesta, o la tendencia del HTF cambia en la dirección opuesta (nueva versión v2.5) la estrategia de liquidación de posición existente.

  8. El filtro del tiempo: Se puede elegir realizar operaciones solo en un período de negociación específico (como el horario regular de negociación de acciones de EE. UU. 0930-1600).

Ventajas estratégicas

  1. La adaptabilidad: El mecanismo de línea de seguimiento puede ajustarse automáticamente a la volatilidad del mercado, especialmente cuando se activa el filtro ATR, lo que proporciona una adaptabilidad dinámica a diferentes entornos de volatilidad.

  2. Mecanismo de reconocimiento de tendencias: La función de confirmación de marcos de tiempo múltiple filtra eficazmente las transacciones “ruidosas” y solo opera cuando la dirección de la tendencia HTF es consistente, lo que mejora considerablemente la calidad de la señal.

  3. Opciones de configuración flexiblesLas estrategias ofrecen una gran variedad de configuraciones de parámetros, incluidos los ciclos y desviaciones de las bandas de Bryn, los ciclos ATR, los filtros de tiempo y los métodos de selección de HTF, que se pueden optimizar para diferentes mercados y variedades de transacciones.

  4. Alta capacidad de respuestaLa versión v2.5 del nuevo mecanismo de respuesta a cambios de tendencia de HTF permite a la estrategia reaccionar más rápidamente a los cambios de tendencia más grandes, deteniendo los daños a tiempo y evitando retrocesos graves.

  5. Ayuda visualLa estrategia consiste en trazar el marco de tiempo de la operación y la línea de seguimiento de la HTF en el gráfico, y mostrar selectivamente las etiquetas de las señales de compra y venta para que la lógica de la operación sea intuitiva.

  6. Gestión de las posiciones: La configuración pyramiding=0 evita la acumulación de riesgos innecesarios al evitar múltiples entradas en la misma dirección.

Riesgo estratégico

  1. Riesgo de una falsa brechaA pesar del uso de la confirmación de la banda de Brin y la HTF, los mercados pueden producir falsas rupturas, especialmente en entornos de alta volatilidad. Solución: Se puede aumentar el valor de la desviación de la banda de Brin o extender el período de confirmación, o incluso agregar un mecanismo adicional de confirmación de ruptura.

  2. Sensibilidad de los parámetrosEl rendimiento de la estrategia es sensible a parámetros como el ciclo ATR, la configuración de la banda de Brin. Solución: Se debe encontrar la combinación de parámetros que mejor se adapte a la variedad de transacción específica mediante retroalimentación, para evitar el problema de ajuste de la curva causado por la optimización excesiva.

  3. El retraso en el cambio de tendencia: El mecanismo de la línea de seguimiento puede reaccionar lentamente en las etapas iniciales de la tendencia, lo que lleva a una entrada un poco más tardía. Solución: Se puede considerar el uso de un menor número de ATR o el ciclo de Bryn para mejorar la velocidad de respuesta, pero hay que equilibrar la calidad de la señal y la capacidad de respuesta.

  4. Dependencia del marco de tiempoMétodo de solución: Se recomienda usar la función de selección automática de HTF, que selecciona automáticamente el marco de tiempo más alto adecuado según el marco de tiempo de la gráfica actual.

  5. Falta de gestión de fondosLa estrategia en sí misma no incluye un mecanismo de gestión de fondos completo. Solución: En la aplicación práctica, se debe combinar una estrategia de pérdida adecuada con reglas de gestión de posiciones, como el riesgo porcentual fijo o el stop loss multiplicado por el ATR.

Dirección de optimización de la estrategia

  1. Filtración de señales de refuerzoSe puede considerar la introducción de otros indicadores técnicos, como el indicador relativamente fuerte ((RSI) o el indicador aleatorio ((Stochastic) para confirmar la señal de entrada y ejecutar operaciones solo cuando el indicador muestre un estado de sobreventa / sobreventa. Esto reducirá aún más las señales de falsa ruptura y aumentará la tasa de ganancias.

  2. Ajuste de parámetros dinámicosSe puede desarrollar un mecanismo de ajuste de parámetros de adaptación basado en el estado del mercado, por ejemplo, aumentar automáticamente el valor del desvío de la banda de Bryn en un entorno de alta volatilidad y reducir el valor del desvío en un entorno de baja volatilidad, para que la estrategia se adapte mejor a las diferentes condiciones del mercado.

  3. Optimización de las tendencias de HTF: Se pueden mejorar los algoritmos de confirmación de tendencias de HTF, como la introducción de cruces de medias móviles de índices u otros indicadores de tendencias, en lugar de depender solo de la dirección de la línea de seguimiento para obtener un juicio de tendencias más estable.

  4. Mejorar la gestión de los fondosIntegración de un sistema integral de gestión de fondos que ajusta el tamaño de las posiciones en función de la volatilidad del mercado y la dinámica del tamaño de la cuenta, establece objetivos de stop loss y ganancias basados en ATR y maximiza el retorno ajustado al riesgo.

  5. Añadir análisis del estado del mercadoIntroducción de la clasificación de entornos de mercado para distinguir entre mercados de tendencia y mercados de oscilación, y ajustar automáticamente los parámetros de la estrategia o las reglas de negociación en función de la situación del mercado, e incluso suspender la negociación en entornos de mercado que no se ajusten a la estrategia.

  6. Integración de varias estrategias: tomar esta estrategia como un componente y combinarla con otras estrategias complementarias (como la estrategia de inversión o la estrategia de confirmación de ruptura) para formar una combinación completa de estrategias que equilibre el rendimiento en diferentes entornos de mercado.

Resumir

La estrategia de seguimiento de tendencias dinámicas de Brinbelt y ATR es un sistema de negociación cuantitativa de diseño ingenioso que identifica y sigue de manera efectiva las tendencias del mercado mediante la combinación de Brinbelt, ATR y análisis de marcos de tiempo múltiples. La ventaja central de la estrategia reside en su adaptabilidad y flexibilidad, que permite adaptarse a la dinámica de las condiciones del mercado, al tiempo que mejora la calidad de la señal y la ganancia a través del mecanismo de confirmación HTF.

A pesar de la existencia de algunos riesgos inherentes, como la sensibilidad de los parámetros y los problemas de falsos avances, estos pueden ser mitigados con la optimización de los parámetros adecuados y mecanismos de filtración adicionales. Las direcciones de optimización estratégica, como el aumento de la filtración de señales, el ajuste de los parámetros dinámicos y la mejora de la gestión de fondos, proporcionan un camino claro para mejorar aún más el rendimiento de la estrategia.

En general, esta estrategia es especialmente adecuada para los operadores de tendencias a medio y largo plazo, ya que ofrece un marco sólido para identificar cambios de tendencia y ejecutar operaciones en condiciones favorables del mercado. Con una configuración razonable de parámetros y una gestión adecuada del riesgo, la estrategia tiene el potencial de generar un rendimiento estable en una variedad de entornos de mercado.

Código Fuente de la Estrategia
/*backtest
start: 2024-07-20 00:00:00
end: 2025-04-07 00:00:00
period: 2d
basePeriod: 2d
exchanges: [{"eid":"Futures_Binance","currency":"ETH_USDT"}]
*/

//@version=6
//@fenyesk
//Optional Working Hours and ATR based TP/SL removed
// Added Optional Higher Timeframe Confirmation with Auto/Manual Selection
// Revised for improved profitability: Trend-following Entries/Exits
// v2.5: React to HTF trend changes as well

strategy('Follow Line Strategy Version 2.5 (React HTF)', overlay = true, process_orders_on_close = true, default_qty_type = strategy.percent_of_equity, default_qty_value = 1, pyramiding = 0) // Version bump overlay=true, process_orders_on_close=true, default_qty_type=strategy.percent_of_equity, default_qty_value=1, pyramiding=0) // Prevent multiple entries in the same direction )

// --- Settings ---
// Indicator Parameters
atrPeriodInput = input.int(defval = 5, title = 'ATR Period', minval = 1, group = 'Indicator Settings')
bbPeriodInput = input.int(defval = 21, title = 'Bollinger Bands Period', minval = 1, group = 'Indicator Settings')
bbDeviationInput = input.float(defval = 1.00, title = 'Bollinger Bands Deviation', minval = 0.1, step = 0.1, group = 'Indicator Settings')
useAtrFilterInput = input.bool(defval = true, title = 'Use ATR for Follow Line Offset?', group = 'Indicator Settings')
showSignalsInput = input.bool(title = 'Show Trade Signals on Chart?', defval = true, group = 'Indicator Settings')



// --- Higher Timeframe Confirmation ---
htf_group = 'Higher Timeframe Confirmation'
useHTFConfirmationInput = input.bool(false, title = 'Enable HTF Confirmation?', group = htf_group)
htfSelectionMethodInput = input.string('Auto', title = 'HTF Selection Method', options = ['Auto', 'Manual'], group = htf_group)
manualHTFInput = input.timeframe('240', title = 'Manual Higher Timeframe', group = htf_group) // Default to 4h if Manual
showHTFLineInput = input.bool(false, title = 'Show HTF Follow Line?', group = htf_group)

// --- Determine Higher Timeframe ---
// Revised function with explicit return variable
f_getAutoHTF() =>
    string htfResult = 'D' // Initialize with a default value (e.g., Daily)
    if timeframe.isintraday
        if timeframe.multiplier <= 1 and timeframe.isminutes
            htfResult := '15' // 1min -> 15min
            htfResult
        else if timeframe.multiplier <= 5 and timeframe.isminutes
            htfResult := '240' // 5min -> 4h (240min)
            htfResult
        else if timeframe.multiplier <= 30 and timeframe.isminutes
            htfResult := '240' // 15-30min -> 4h (240min)
            htfResult
        else if timeframe.multiplier == 60 and timeframe.isminutes // 1 hour
            htfResult := 'D' // 1h -> 1 Day
            htfResult
        else if timeframe.multiplier <= 240 and timeframe.isminutes // Up to 4 hours
            htfResult := 'W' // 4h -> 1 Week
            htfResult
    // else // The default "D" is already set if none of the above match
    // htfResult := "D" // Default for other intraday -> 1 Day (already default)
    else if timeframe.isdaily // Daily
        htfResult := 'M' // 1 Day -> 1 Month
        htfResult
    else if timeframe.isweekly // Weekly
        htfResult := 'M' // 1 Week -> 1 Month
        htfResult
    else // Monthly or higher (or unknown)
        htfResult := '3M' // Default to 3 Months
        htfResult
    htfResult // Explicitly return the variable value

autoHTF = f_getAutoHTF()
selectedHTF = htfSelectionMethodInput == 'Auto' ? autoHTF : manualHTFInput

// --- Trade Timeframe Calculations ---
// Bollinger Bands calculation
bbMiddle_trade = ta.sma(close, bbPeriodInput)
bbStdDev_trade = ta.stdev(close, bbPeriodInput)
BBUpper_trade = bbMiddle_trade + bbStdDev_trade * bbDeviationInput
BBLower_trade = bbMiddle_trade - bbStdDev_trade * bbDeviationInput

// ATR calculation
atrValue_trade = ta.atr(atrPeriodInput)

// Signal initialization for Trade TF
var float followLine_trade = na
var int bbSignal_trade = 0
var int trend_trade = 0 // Renamed from iTrend

// Determine BB signal based on current close (Trade TF)
if close > BBUpper_trade
    bbSignal_trade := 1
    bbSignal_trade
else if close < BBLower_trade
    bbSignal_trade := -1
    bbSignal_trade
else
    bbSignal_trade := 0 // Reset signal if price is within bands
    bbSignal_trade

// Calculate potential new FollowLine value for the current bar (Trade TF)
float tempFollowLine_trade = na // Explicit type
if bbSignal_trade == 1
    tempFollowLine_trade := useAtrFilterInput ? low - atrValue_trade : low
    tempFollowLine_trade
else if bbSignal_trade == -1
    tempFollowLine_trade := useAtrFilterInput ? high + atrValue_trade : high
    tempFollowLine_trade

// Determine the final FollowLine for the current bar, applying the "ratchet" logic (Trade TF)
if bbSignal_trade == 1 // Price closed above upper BB
    followLine_trade := na(followLine_trade[1]) ? tempFollowLine_trade : math.max(tempFollowLine_trade, nz(followLine_trade[1], tempFollowLine_trade))
    followLine_trade
else if bbSignal_trade == -1 // Price closed below lower BB
    followLine_trade := na(followLine_trade[1]) ? tempFollowLine_trade : math.min(tempFollowLine_trade, nz(followLine_trade[1], tempFollowLine_trade))
    followLine_trade
else // Price closed within bands, FollowLine continues from previous bar
    if not na(followLine_trade[1])
        followLine_trade := followLine_trade[1]
        followLine_trade
        // else followLine_trade remains na if followLine_trade[1] was na

// Trend direction determination (Based on current FollowLine vs previous FollowLine - Trade TF)
if not na(followLine_trade) and not na(followLine_trade[1])
    if followLine_trade > followLine_trade[1]
        trend_trade := 1
        trend_trade
    else if followLine_trade < followLine_trade[1]
        trend_trade := -1
        trend_trade
    else
        trend_trade := nz(trend_trade[1], 0) // Maintain previous trend if line is flat but valid
        trend_trade
else if not na(followLine_trade) and na(followLine_trade[1])
    trend_trade := bbSignal_trade == 1 ? 1 : bbSignal_trade == -1 ? -1 : 0 // Use ternary for initial trend
    trend_trade
else if na(followLine_trade)
    trend_trade := 0 // Reset trend if FollowLine becomes invalid
    trend_trade


// --- Higher Timeframe Calculations ---
// Function revised to return only one value (as float) based on parameter
f_calculateHTFData(htf_close, htf_high, htf_low, return_type) =>
    // Explicitly type potentially 'na' indicator results
    float htf_atrValue = ta.atr(atrPeriodInput)
    float htf_bbMiddle = ta.sma(htf_close, bbPeriodInput)
    float htf_bbStdDev = ta.stdev(htf_close, bbPeriodInput)
    float htf_BBUpper = na
    float htf_BBLower = na

    // Calculate BBands only if middle/stdev are valid
    if not na(htf_bbMiddle) and not na(htf_bbStdDev)
        htf_BBUpper := htf_bbMiddle + htf_bbStdDev * bbDeviationInput
        htf_BBLower := htf_bbMiddle - htf_bbStdDev * bbDeviationInput
        htf_BBLower

    // Determine BB signal (HTF) - Default to 0
    int htf_bbSignal = 0
    // Check if bands are valid before comparing
    if not na(htf_BBUpper) and not na(htf_BBLower)
        if htf_close > htf_BBUpper
            htf_bbSignal := 1
            htf_bbSignal
        else if htf_close < htf_BBLower
            htf_bbSignal := -1
            htf_bbSignal

    // Calculate potential new FollowLine (HTF)
    float htf_tempFollowLine = na // Explicitly typed float
    if htf_bbSignal == 1
        htf_tempFollowLine := useAtrFilterInput and not na(htf_atrValue) ? htf_low - htf_atrValue : htf_low
        htf_tempFollowLine
    else if htf_bbSignal == -1
        htf_tempFollowLine := useAtrFilterInput and not na(htf_atrValue) ? htf_high + htf_atrValue : htf_high
        htf_tempFollowLine

    // Maintain FollowLine state using 'var'
    var float htf_followLine = na
    var int htf_trend = 0

    // Determine the final FollowLine (HTF)
    if htf_bbSignal == 1
        htf_followLine := na(htf_followLine[1]) ? htf_tempFollowLine : math.max(htf_tempFollowLine, nz(htf_followLine[1], htf_tempFollowLine))
        htf_followLine
    else if htf_bbSignal == -1
        htf_followLine := na(htf_followLine[1]) ? htf_tempFollowLine : math.min(htf_tempFollowLine, nz(htf_followLine[1], htf_tempFollowLine))
        htf_followLine
    else
        if not na(htf_followLine[1])
            htf_followLine := htf_followLine[1]
            htf_followLine
            // else htf_followLine remains na if htf_followLine[1] was na (unless reset below)

    // Reset FollowLine if it's based on invalid temp line
    if na(htf_tempFollowLine) and htf_bbSignal != 0 // If the signal existed but calc failed (e.g., na ATR)
        htf_followLine := na // Reset line
        htf_followLine

    // Determine Trend (HTF)
    if not na(htf_followLine) and not na(htf_followLine[1])
        if htf_followLine > htf_followLine[1]
            htf_trend := 1
            htf_trend
        else if htf_followLine < htf_followLine[1]
            htf_trend := -1
            htf_trend
        else
            htf_trend := nz(htf_trend[1], 0)
            htf_trend
    else if not na(htf_followLine) and na(htf_followLine[1])
        htf_trend := htf_bbSignal == 1 ? 1 : htf_bbSignal == -1 ? -1 : 0
        htf_trend
    else if na(htf_followLine) // Trend is 0 if line becomes (or is) na
        htf_trend := 0
        htf_trend

    // Return the requested value as float type (or na)
    float return_value = na
    if return_type == 'line'
        return_value := htf_followLine
        return_value
    else if return_type == 'trend'
        return_value := float(htf_trend) // Convert int trend to float for consistent return type
        return_value

    return_value // Return the single calculated value


// Explicitly declare variables that will receive the security call result
float followLine_htf = na
int trend_htf = 0 // Initialize with a default value (0 for neutral)

// Request HTF data UNCONDITIONALLY
followLine_htf_result = request.security(syminfo.tickerid, selectedHTF, f_calculateHTFData(close, high, low, 'line'), lookahead = barmerge.lookahead_off)
trend_htf_result_float = request.security(syminfo.tickerid, selectedHTF, f_calculateHTFData(close, high, low, 'trend'), lookahead = barmerge.lookahead_off)

// Conditionally assign the results based on whether the HTF feature is enabled
if useHTFConfirmationInput or showHTFLineInput
    // Assign results, handling potential 'na' values safely
    followLine_htf := followLine_htf_result // Assign float/na directly
    trend_htf := na(trend_htf_result_float) ? 0 : int(nz(trend_htf_result_float)) // Convert float result back to int, default to 0 if na
    trend_htf
else // If HTF features are disabled, set variables to 'na'
    followLine_htf := na
    trend_htf := 0 // or na if preferred
    trend_htf




// HTF Filter
// Use the potentially 'na' followLine_htf and the guaranteed non-'na' trend_htf
htfConfirmsLong = not useHTFConfirmationInput or useHTFConfirmationInput and trend_htf == 1
htfConfirmsShort = not useHTFConfirmationInput or useHTFConfirmationInput and trend_htf == -1

// --- Entry/Exit Conditions ---
// Buy & Sell Conditions (Based on Trade TF trend crossover)
longCondition_trade = nz(trend_trade[1]) <= 0 and trend_trade == 1
shortCondition_trade = nz(trend_trade[1]) >= 0 and trend_trade == -1

// Combined Entry Conditions with Filters
goLong = htfConfirmsLong and longCondition_trade  and strategy.position_size <= 0 // Only enter long if flat or short & HTF confirms
goShort = htfConfirmsShort and shortCondition_trade  and strategy.position_size >= 0 // Only enter short if flat or long & HTF confirms

// Exit conditions based on *either* TTF or HTF changing trend against the position
exitLong = trend_trade == -1 or trend_htf == -1 // TTF to short OR HTF to short
exitShort = trend_trade == 1 or trend_htf == 1 // TTF to long OR HTF to long


// --- Strategy Execution ---

if goLong
    strategy.close('Short', comment = 'Close Short for Long')
    strategy.entry('Long', strategy.long, comment = 'Enter Long')

if goShort
    strategy.close('Long', comment = 'Close Long for Short')
    strategy.entry('Short', strategy.short, comment = 'Enter Short')

if exitLong
    strategy.close('Long', comment = 'Exit Long')

if exitShort
    strategy.close('Short', comment = 'Exit Short')

// --- Alerts ---
// Alerts trigger on the same bar as the entry condition, respecting all filters
// NOTE: Removed dynamic HTF from message as alertcondition requires const string
alertcondition(goLong, title = 'FL Buy Signal', message = 'Follow Line Buy Signal - {{ticker}} {{interval}}')
alertcondition(goShort, title = 'FL Sell Signal', message = 'Follow Line Sell Signal - {{ticker}} {{interval}}')
alertcondition(goLong or goShort, title = 'FL Signal', message = 'Follow Line Signal - {{ticker}} {{interval}}')

// --- Plotting ---
// Plot the Trade Timeframe Follow Line
lineColor_trade = trend_trade > 0 ? color.new(color.blue, 0) : trend_trade < 0 ? color.new(color.red, 0) : color.new(color.gray, 0)
plot(followLine_trade, color = lineColor_trade, linewidth = 2, title = 'Follow Line (Trade TF)')

// Plot the Higher Timeframe Follow Line (optional)
// Use the potentially 'na' followLine_htf and the guaranteed non-'na' trend_htf for coloring
lineColor_htf = trend_htf > 0 ? color.new(color.aqua, 0) : trend_htf < 0 ? color.new(color.orange, 0) : color.new(color.gray, 70)
plot(showHTFLineInput and useHTFConfirmationInput ? followLine_htf : na, color = lineColor_htf, linewidth = 2, style = plot.style_circles, title = 'Follow Line (HTF)', offset = 0)

// Plot shapes on the bar the trade signal occurs (based on trade TF condition), placing them AT the calculated Trade TF price level.
// Use the original trade long/short conditions for plotting shapes for clarity, before plots
plotshape(longCondition_trade and showSignalsInput and not na(followLine_trade) and not na(atrValue_trade) ? followLine_trade - atrValue_trade : na, text = 'BUY', style = shape.labelup, location = location.absolute, color = color.new(color.blue, 0), textcolor = color.new(color.white, 0), offset = 0, size = size.auto)
plotshape(shortCondition_trade and showSignalsInput and not na(followLine_trade) and not na(atrValue_trade) ? followLine_trade + atrValue_trade : na, text = 'SELL', style = shape.labeldown, location = location.absolute, color = color.new(color.red, 0), textcolor = color.new(color.white, 0), offset = 0, size = size.auto)

// Plot BBands for reference if desired
// plot(BBUpper_trade, "Upper BB", color=color.gray)
// plot(BBLower_trade, "Lower BB", color=color.gray)