Indicator · Pine Script v6 · On the chart

ICT + FVG + RSI

by Allysson-Rodrigues

This multi-timeframe indicator overlays ICT Fair Value Gaps (FVGs) and higher-timeframe candle projections directly onto the price chart.

View on GitHub

Repaints

GitHub stars2on the repository
Pine versionv6current
Lines of code1,412
Settings40inputs
LicenceMIT
Last update7 weeks ago5 Sep 2026

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.

Source Code

1,412 lines of Pine Script v6 from src/ict_fvg_rsi_v1.2.12.pine, published under MIT. Keep the author's credit and licence lines when you reuse it.

ict_fvg_rsi_v1.2.12.pine
//@version=6
indicator('ICT + FVG + RSI V1.2.12', shorttitle="ICT + FVG Pro 1.2.12", overlay=true, max_boxes_count=500, max_lines_count=500, max_labels_count=500, max_bars_back=5000)

// ═══════════════════════════════════════════════════════════════════════════
// 📌 METADADOS
// ═══════════════════════════════════════════════════════════════════════════
// Nome:        ICT + FVG + RSI
// Versão:      1.2.12
// Atualizado:  2026-08-30
//
// ═══════════════════════════════════════════════════════════════════════════
// 📜 CHANGELOG
// ═══════════════════════════════════════════════════════════════════════════
// V1.2.12 (2026-08-30)
//    - Refactor: consolidação do núcleo HTF usando CandleSet em arrays iteráveis (P1).
//    - Manutenção: remoção de duplicação massiva na renderização, cleanup e requests de HTFs.
//
// V1.2.11 (2026-08-30)
//    - Refactor: consolidação do pipeline RSI MTF em estrutura orientada a dados (RSISlot), reduzindo repeticao (P1).
//
// V1.2.10 (2026-03-10)
//    - Refactor: sincronização de trace lines e price labels extraída para helpers dedicados
//    - Limpeza: funções auxiliares mortas removidas do bloco RSI/HTF
//    - Manutenção: redução de duplicação sem alterar lógica visual ou sinais
//
// V1.2.9 (2026-03-10)
//    - UX: trace lines, trace labels e labels HTF agora reaplicam cor, tamanho e espessura em runtime
//    - UX: DOW labels sincronizam texto e estilo ao alterar inputs visuais
//    - Manutenção: update visual concentrado sem alterar lógica de sinais
//
// V1.2.8 (2026-03-10)
//    - UX: labels e timers HTF agora sincronizam corretamente ao trocar posição, timer e visibilidade
//    - UX: tabela RSI é recriada ao mudar a posição para evitar estado visual antigo
//    - Refactor: ciclo de vida dos labels HTF extraído para helpers dedicados
//    - Hotfix: fluxo HTF e leituras MTF ajustados para compatibilidade com o compilador Pine v6
//
// V1.2.7 (2026-03-10)
//    - Refactor: orquestração dos grupos HTF centralizada em helpers compartilhados
//    - Refactor: critério de elegibilidade dos HTFs reutilizado em cálculo e renderização
//    - Manutenção: avanço de offset HTF extraído para helper único
//
// V1.2.6 (2026-03-10)
//    - Refactor: pipeline RSI MTF consolidado com dynamic requests e helper único
//    - Performance: request.security é evitado quando o TF consultado é o do gráfico
//    - Performance: tabela RSI passa a reutilizar células com table.cell_set_*()
//    - Refactor: leitura FVG MTF centralizada em helper dedicado
//    - Limpeza: UpdateTime deixa de carregar parâmetro não utilizado
//
// V1.2.5 (2026-03-10)
//    - Bug Fix: timer HTF passa a usar time_close() com fallback visual seguro
//    - Bug Fix: alertas de novo FVG agora dependem do evento real de inserção
//    - Bug Fix: trace anchor "Last Timeframe" aponta para o último HTF efetivamente renderizado
//    - UX: Daily Auto e Weekly Auto respeitam o toggle manual dos HTFs 5 e 6
//    - UX: trace lines e labels agora são limpos ao desligar a feature ou zerar HTFs renderizados
//
// V1.2.4 (2026-03-09)
//    - Anti-Repaint: request.security agora oferece modo HTF Confirmado ou Tempo Real
//    - Robustez: FVG custom bloqueia timeframe inferior ao gráfico atual
//    - Bug Fix: expiração do FVG passa a usar barras do gráfico atual, consistente com TF custom
//    - Performance: request.security recebe calc_bars_count para reduzir custo de histórico
//    - UX: inputs dependentes agora são ativados dinamicamente nos grupos EMA, RSI, Tabela e FVG
//
// V1.2.3 (2026-03-09)
//    - Bug Fix: fluxo HTF reordenado para evitar contaminar o candle anterior na virada do período
//    - Bug Fix: labels das trace lines agora atualizam texto junto com o preço em tempo real
//    - Bug Fix: manutenção/remoção de FVGs percorre toda a lista ativa, respeitando o limite configurado
//    - UX: extensão do FVG renomeada como projeção temporal para refletir o comportamento real no eixo X
//    - Docs: README alinhado aos recursos realmente implementados no indicador
//
// V1.2.2 (2026-02-19)
//    - Bug Fix: FVG expiração migrada de tempo-relógio para bar_index
//      (corrige inflação de idade em mercados com gap noturno, ex: ações)
//    - Bug Fix: Garbage collection de candles HTF ao desativar o grupo
//    - Performance: Reorder/Update restritos a barstate.isnew/islast
//    - Performance: FindImbalance com dirty flag — evita recriação total
//    - Hotfix: Monitor refatorado para evitar warnings de consistência do Pine v6
//    - Refactor: Variáveis globais de dirty flag removidas (rastreamento via objeto)

// V1.2.1 (2026-02-14)
//    - Anti-Repaint: Monitor usa barstate.ishistory em vez de barstate.islast
//    - Bug Fix: RSI fallback nz() removido — evita mistura de dados entre TFs
//    - Limpeza: helper.name (código morto) removido de todos os métodos
//    - Manutenção: Constantes SEC_1H, SEC_4H, SEC_1D extraídas
//
// V1.2.0 (2026-02-12)
//    - Bug Fix: request.security desenrolado do loop (5 chamadas explícitas)
//    - Bug Fix: DayofWeek corrigido (usa constantes dayofweek.monday etc.)
//    - Bug Fix: str.tonumber substituído por timeframe.in_seconds no Monitor
//    - Anti-Repaint: Monitor agora exige barstate.isconfirmed para criar candle
//    - Anti-Repaint: Mitigação/expiração FVG apenas em barstate.isconfirmed
//    - Performance: var adicionado a Settings, Helper e color_transparent
//    - Robustez: ValidTimeframe usa modulo (n2 % n1 == 0)
//    - Robustez: Debug table protegido contra NaN (nz/na checks)
//
// V1.1.8 (2025-12-20)
//    - Robustness: Zero-division protection in isExpired method.
//    - Robustness: Bounds checking and fallback for invalid RSI data.
//    - Robustness: RSI table automatically recreates when changing columns.
//    - Extensibility: ATR Period is now configurable via user input.
//    - Extensibility: Configurable Timezone (5 options).
//    - Extensibility: RSI table timeframes are now dynamic.
//
// V1.1.6 (2025-12-19)
//    - Optimization: Simplified conditional calculation of RSI levels.
//    - Refactoring: RSI Cache only calculates levels when visual lines are active.
//    - UX: Updated Debug table version to reflect current release.
//
// V1.1.5 (2025-12-18)
//    - New: Checkboxes to toggle RSI columns in the table.
//    - Improved UX: Independent control for RSI display and levels.
//
// V1.1.4 (2025-12-18)
//    - Optimization: RSI_ALPHA pre-calculated as a 'var' constant.
//    - Fix: f_fvg_detect() now normalizes gap size by ATR when ATR Auto is active.
//    - Prevents false positives on volatile or low-priced assets.
//    - Logical Consistency: Threshold and gap size now use the same unit of measure.
//
// V1.1.3 (2025-12-17)
//    - Tooltips added to all 60+ indicator inputs.
//    - Improved inline documentation for better TradingView UX.
//    - Clear descriptions for every parameter.
//
// V1.1.2 (2025-12-16)
//    - Critical Fix: box.set_top/bottom now uses math.max/min.
//    - Bugfix: Corrected rendering for bullish HTF candles.
//    - Improvement: Candle body now always uses correct coordinates.
//
// V1.1.1 (2025-12-16)
//    - Historical timer changed from '---' to '--:--' (time format).
//    - Optimized FindImbalance: Now processes only on new HTF candles.
//    - Performance: Removed unnecessary box.copy() + box.delete() calls.
//    - Tracking: Implemented idx-based tracking to prevent imbalance duplication.
//    - Visual Sync: FVG boxes now follow HTF candle movement.
//    - Fix: Condition FVG >= 3 (detects with exactly 3 candles).
//
// V1.1 (2025-12-15)
//    - Removed scale=scale.none for better candle visualization.
//    - Timer changed from 'n/a' to '---' on historical data.
//    - Added comprehensive documentation.
//
// V1.0 (2025-12-14)
//    - Initial Release: ICT HTF Candles + FVG + RSI + MTF merge.
//    - RSI Cache system implemented.
//    - Added Debug mode for FVG.
//    - Configured alerts.
// ═══════════════════════════════════════════════════════════════════════════
// 🔢 CONSTANTES
// ═══════════════════════════════════════════════════════════════════════════
const int MAX_TABLE_ROWS = 7
const int MS_PER_SECOND = 1000
const int SEC_1H  = 3600
const int SEC_4H  = 14400
const int SEC_1D  = 86400
const int RSI_MTF_SLOT_COUNT = 5
const int RSI_REQUEST_MIN_BARS = 300
const int FVG_REQUEST_MIN_BARS = 50

// ═══════════════════════════════════════════════════════════════════════════
// 📦 TYPES
// ═══════════════════════════════════════════════════════════════════════════
type Candle
    float o
    float c
    float h
    float l
    int o_time
    int o_idx
    int c_idx
    int h_idx
    int l_idx
    string dow
    box body
    line wick_up
    line wick_down
    label dow_label

type Trace
    line o
    line c
    line h
    line l
    label o_l
    label c_l
    label h_l
    label l_l

type Imbalance
    box b
    int idx
    int arr_i1
    int arr_i2

type CandleSettings
    bool show
    string htf
    int max_display

type Settings
    int max_sets
    color bull_body
    color bull_border
    color bull_wick
    color bear_body
    color bear_border
    color bear_wick
    int offset
    int buffer
    int htf_buffer
    int width
    bool daily_name
    bool trace_show
    color trace_o_color
    string trace_o_style
    int trace_o_size
    color trace_c_color
    string trace_c_style
    int trace_c_size
    color trace_h_color
    string trace_h_style
    int trace_h_size
    color trace_l_color
    string trace_l_style
    int trace_l_size
    string trace_anchor
    bool label_show
    color label_color
    string label_size
    string label_position
    string label_alignment
    bool fvg_show
    color fvg_color
    bool vi_show
    color vi_color
    bool htf_label_show
    color htf_label_color
    string htf_label_size
    bool htf_timer_show
    color htf_timer_color
    string htf_timer_size
    color dow_color
    string dow_size

type CandleSet
    array<Candle> candles
    array<Imbalance> imbalances
    CandleSettings settings
    label tfNameTop
    label tfNameBottom
    label tfTimerTop
    label tfTimerBottom


type FVG
    float top
    float bottom
    bool isBull
    int birthTime
    int birthBarIndex
    box boxObj
    line mitigatedLine
    bool isActive

// ═══════════════════════════════════════════════════════════════════════════
// ⚙️ INPUTS
// ═══════════════════════════════════════════════════════════════════════════
// [FIX v1.2.0] var adicionado — Settings e Helper alocados uma única vez
var Settings settings = Settings.new()
var CandleSettings SettingsHTF1 = CandleSettings.new()
var CandleSettings SettingsHTF2 = CandleSettings.new()
var CandleSettings SettingsHTF3 = CandleSettings.new()
var CandleSettings SettingsHTF4 = CandleSettings.new()
var CandleSettings SettingsHTF5 = CandleSettings.new()
var CandleSettings SettingsHTF6 = CandleSettings.new()

string group_htf = "📊 HTF Candles  ━━━━━━━━━━━━━━━━━━━━"
string group_style = "🎨 Styling  ━━━━━━━━━━━━━━━━━━━━━━"
string group_label = "🏷️ Labels  ━━━━━━━━━━━━━━━━━━━━━━━"
string group_imbalance = "⚖️ MTF Imbalances  ━━━━━━━━━━━━━━━"
string group_trace = "📍 Trace Lines  ━━━━━━━━━━━━━━━━━━"
string group_ema = "📈 EMA  ━━━━━━━━━━━━━━━━━━━━━━━━━"
string group_rsi = "📉 RSI  ━━━━━━━━━━━━━━━━━━━━━━━━━"
string group_table = "📋 Tabela RSI  ━━━━━━━━━━━━━━━━━━━"
string group_fvg = "🎯 FVG Gráfico  ━━━━━━━━━━━━━━━━━━"

SettingsHTF1.show := input.bool(true, 'HTF 1', inline='htf1', group=group_htf, tooltip='Ativa/desativa exibição do timeframe 1')
htf_1 = input.timeframe('5', '', inline='htf1', group=group_htf)
SettingsHTF1.htf := htf_1
SettingsHTF1.max_display := input.int(10, '', inline='htf1', group=group_htf, tooltip='Quantidade máxima de candles a exibir')

SettingsHTF2.show := input.bool(true, 'HTF 2', inline='htf2', group=group_htf, tooltip='Ativa/desativa exibição do timeframe 2')
htf_2 = input.timeframe('15', '', inline='htf2', group=group_htf)
SettingsHTF2.htf := htf_2
SettingsHTF2.max_display := input.int(8, '', inline='htf2', group=group_htf, tooltip='Quantidade máxima de candles a exibir')

SettingsHTF3.show := input.bool(true, 'HTF 3', inline='htf3', group=group_htf, tooltip='Ativa/desativa exibição do timeframe 3')
htf_3 = input.timeframe('60', '', inline='htf3', group=group_htf)
SettingsHTF3.htf := htf_3
SettingsHTF3.max_display := input.int(6, '', inline='htf3', group=group_htf, tooltip='Quantidade máxima de candles a exibir')

SettingsHTF4.show := input.bool(true, 'HTF 4', inline='htf4', group=group_htf, tooltip='Ativa/desativa exibição do timeframe 4')
htf_4 = input.timeframe('240', '', inline='htf4', group=group_htf)
SettingsHTF4.htf := htf_4
SettingsHTF4.max_display := input.int(5, '', inline='htf4', group=group_htf, tooltip='Quantidade máxima de candles a exibir')

SettingsHTF5.show := input.bool(true, 'HTF 5', inline='htf5', group=group_htf, tooltip='Ativa/desativa exibição do timeframe 5 (Daily)')
htf_5 = input.timeframe('1D', '', inline='htf5', group=group_htf)
SettingsHTF5.htf := htf_5
SettingsHTF5.max_display := input.int(5, '', inline='htf5', group=group_htf, tooltip='Quantidade máxima de candles a exibir')

SettingsHTF6.show := input.bool(true, 'HTF 6', inline='htf6', group=group_htf, tooltip='Ativa/desativa exibição do timeframe 6 (Weekly)')
htf_6 = input.timeframe('1W', '', inline='htf6', group=group_htf)
SettingsHTF6.htf := htf_6
SettingsHTF6.max_display := input.int(3, '', inline='htf6', group=group_htf, tooltip='Quantidade máxima de candles a exibir')

settings.max_sets := input.int(3, 'Limite HTFs', minval=1, maxval=6, group=group_htf, tooltip='Número máximo de grupos HTF a exibir simultaneamente (1-6)')
i_timezone = input.string('America/Sao_Paulo', 'Fuso Horário', options=['America/Sao_Paulo', 'America/New_York', 'Europe/London', 'Asia/Tokyo', 'UTC'], group=group_htf, tooltip='Fuso horário para cálculo de datas e horários dos candles HTF')
i_auto_daily = input.bool(true, 'Daily Auto (>=1H)', group=group_htf, tooltip='Mostra MTF Daily apenas quando gráfico está em 1H ou acima')
i_auto_weekly = input.bool(true, 'Weekly Auto (>=4H)', group=group_htf, tooltip='Mostra MTF Weekly apenas quando gráfico está em 4H ou acima')
i_htf_security_mode = input.string('Confirmado', 'Modo Dados HTF', options=['Confirmado', 'Tempo Real'], group=group_htf, tooltip='Define como request.security trata timeframes acima do gráfico. Confirmado usa apenas candles HTF fechados para evitar repaint; Tempo Real usa o candle HTF em formação.')

bool showDailyAuto = i_auto_daily and timeframe.in_seconds() >= SEC_1H
bool showWeeklyAuto = i_auto_weekly and timeframe.in_seconds() >= SEC_4H
bool htf5_show = SettingsHTF5.show and (not i_auto_daily or showDailyAuto)
bool htf6_show = SettingsHTF6.show and (not i_auto_weekly or showWeeklyAuto)

settings.bull_body := input.color(color.new(#089981, 70),'Body Bull/Bear', inline='body', group=group_style, tooltip='Cor do corpo dos candles de alta (esquerda) e baixa (direita)')
settings.bear_body := input.color(color.new(#f23645, 70), '', inline='body', group=group_style)
settings.bull_border := input.color(color.new(#000000,70), 'Border', inline='borders', group=group_style, tooltip='Cor da borda dos candles de alta e baixa')
settings.bear_border := input.color(color.new(#000000,70), '', inline='borders', group=group_style)
settings.bull_wick := input.color(color.new(color.black, 70), 'Wick', inline='wick', group=group_style, tooltip='Cor dos pavios dos candles de alta e baixa')
settings.bear_wick := input.color(color.new(color.black, 70), '', inline='wick', group=group_style)
settings.offset := input.int(30, 'Offset', minval=1, group=group_style, tooltip='Distância horizontal entre o último candle do gráfico e os HTF candles')
settings.buffer := input.int(1, 'Buffer', minval=1, maxval=4, group=group_style, tooltip='Espaçamento entre candles individuais dentro de um grupo HTF')
settings.htf_buffer := input.int(5, 'HTF Buffer', minval=1, maxval=10, group=group_style, tooltip='Espaçamento entre grupos de diferentes timeframes')
settings.width := input.int(1, 'Width', minval=1, maxval=4, group=group_style, tooltip='Largura visual dos candles HTF (multiplicado por 2)') * 2

settings.htf_label_show := input.bool(true, 'HTF Label', inline='HTFlabel', group=group_label, tooltip='Exibe o nome do timeframe acima/abaixo dos candles')
settings.htf_label_color := input.color(color.new(#000000, 0), '', inline='HTFlabel', group=group_label)
settings.htf_label_size := input.string(size.normal, '', [size.tiny, size.small, size.normal, size.large, size.huge], inline='HTFlabel', group=group_label)
settings.label_position := input.string("Top", 'Position', options=['Both', 'Top', 'Bottom'], group=group_label, tooltip='Posição do label: Topo, Base ou Ambos')
settings.label_alignment := input.string("Follow Candles", "Alignment", options=['Align', 'Follow Candles'], group=group_label, tooltip='Align: labels alinhados | Follow: labels seguem posição dos candles')
settings.htf_timer_show := input.bool(true, 'Timer', inline='timer', group=group_label, tooltip='Exibe contagem regressiva até o fechamento do candle HTF')
settings.htf_timer_color := input.color(color.new(color.black, 10), '', inline='timer', group=group_label)
settings.htf_timer_size := input.string(size.small, '', [size.tiny, size.small, size.normal, size.large, size.huge], inline='timer', group=group_label)
settings.daily_name := input.bool(true, 'DOW Label', inline='dow', group=group_label, tooltip='Exibe nome do dia da semana (Seg, Ter...) para candles diários')
settings.dow_color := input.color(color.black, '', inline='dow', group=group_label)
settings.dow_size := input.string(size.small, '', [size.tiny, size.small, size.normal, size.large, size.huge], inline='dow', group=group_label)

settings.fvg_show := input.bool(true, 'FVG MTF', inline='fvg', group=group_imbalance, tooltip='Detecta e exibe Fair Value Gaps nos candles HTF')
settings.fvg_color := input.color(color.rgb(120, 123, 134, 85),'', inline='fvg', group=group_imbalance)
settings.vi_show := input.bool(false, 'VI MTF', inline='vi', group=group_imbalance, tooltip='Detecta e exibe Volume Imbalances nos candles HTF')
settings.vi_color := input.color(color.new(color.red, 50), '', inline='vi', group=group_imbalance)

settings.trace_show := input.bool(false, 'Trace Lines', group=group_trace, tooltip='Desenha linhas conectando OHLC do candle atual até os HTF candles')
settings.trace_o_color := input.color(color.new(color.gray, 50), 'Open', inline='1', group=group_trace, tooltip='Linha do preço de abertura: cor, estilo e espessura')
settings.trace_o_style := input.string('····', '', options=['⎯⎯⎯', '----', '····'], inline='1', group=group_trace)
settings.trace_o_size := input.int(1, '', options=[1, 2, 3, 4], inline='1', group=group_trace)
settings.trace_c_color := input.color(color.new(color.gray, 50), 'Close', inline='2', group=group_trace, tooltip='Linha do preço de fechamento: cor, estilo e espessura')
settings.trace_c_style := input.string('····', '', options=['⎯⎯⎯', '----', '····'], inline='2', group=group_trace)
settings.trace_c_size := input.int(1, '', options=[1, 2, 3, 4], inline='2', group=group_trace)
settings.trace_h_color := input.color(color.new(color.gray, 50), 'High', inline='3', group=group_trace, tooltip='Linha da máxima: cor, estilo e espessura')
settings.trace_h_style := input.string('⎯⎯⎯', '', options=['⎯⎯⎯', '----', '····'], inline='3', group=group_trace)
settings.trace_h_size := input.int(1, '', options=[1, 2, 3, 4], inline='3', group=group_trace)
settings.trace_l_color := input.color(color.new(color.gray, 50), 'Low', inline='4', group=group_trace, tooltip='Linha da mínima: cor, estilo e espessura')
settings.trace_l_style := input.string('⎯⎯⎯', '', options=['⎯⎯⎯', '----', '····'], inline='4', group=group_trace)
settings.trace_l_size := input.int(1, '', options=[1, 2, 3, 4], inline='4', group=group_trace)
settings.trace_anchor := input.string('First Timeframe', 'Anchor', options=['First Timeframe', 'Last Timeframe'], group=group_trace, tooltip='Define qual grupo HTF recebe as trace lines')
settings.label_show := input.bool(false, 'Price Labels', inline='label', group=group_trace, tooltip='Exibe labels com os preços OHLC nas extremidades das linhas')
settings.label_color := input.color(color.new(color.black, 10), '', inline='label', group=group_trace)
settings.label_size := input.string(size.small, '', [size.tiny, size.small, size.normal, size.large, size.huge], inline='label', group=group_trace)

i_showEMA = input.bool(false, '⚡ Ativar EMA', group=group_ema, tooltip='Ativa a Média Móvel Exponencial no gráfico')
i_emaLength = input.int(26, 'Período', minval=1, maxval=500, group=group_ema, tooltip='Número de barras para cálculo da EMA (padrão: 26)', active=i_showEMA)
i_emaColor = input.color(#4a4a4acc, 'Cor', group=group_ema, tooltip='Cor da linha EMA', active=i_showEMA)
i_emaWidth = input.int(1, 'Espessura', minval=1, maxval=5, group=group_ema, tooltip='Espessura da linha EMA', active=i_showEMA)
i_emaSource = input.source(close, 'Fonte', group=group_ema, tooltip='Fonte de dados para cálculo (close, open, high, low, hl2, etc.)', active=i_showEMA)

i_rsi_length = input.int(14, 'Período RSI', minval=2, maxval=200, group=group_rsi, tooltip='Período para cálculo do RSI (padrão: 14)')
i_show_rsi_25 = input.bool(false, 'Exibir Nível 25', group=group_rsi, tooltip='Plota linha horizontal no preço correspondente a RSI = 25')
i_show_rsi_30 = input.bool(false, 'Exibir Nível 30', group=group_rsi, tooltip='Plota linha horizontal no preço correspondente a RSI = 30 (sobrevendido)')
i_show_rsi_50 = input.bool(false, 'Exibir Nível 50', group=group_rsi, tooltip='Plota linha horizontal no preço correspondente a RSI = 50 (neutro)')
i_show_rsi_70 = input.bool(false, 'Exibir Nível 70', group=group_rsi, tooltip='Plota linha horizontal no preço correspondente a RSI = 70 (sobrecomprado)')
i_show_rsi_75 = input.bool(false, 'Exibir Nível 75', group=group_rsi, tooltip='Plota linha horizontal no preço correspondente a RSI = 75')
i_show_rsi_custom = input.bool(false, '⭐ Exibir RSI Customizado', group=group_rsi, tooltip='Plota linha horizontal para um nível RSI personalizado')
i_rsi_custom_level = input.float(40, 'Nível RSI Customizado', minval=1, maxval=99, step=1, group=group_rsi, tooltip='Define o nível RSI desejado para a linha customizada (1-99)', active=i_show_rsi_custom)
i_rsi_custom_color = input.color(color.new(#ff9800, 0), 'Cor RSI Customizado', group=group_rsi, tooltip='Cor da linha RSI customizada', active=i_show_rsi_custom)
i_rsi_max_deviation = input.float(0.30, 'Desvio Máximo (%)', minval=0.10, maxval=1.00, step=0.05, group=group_rsi, tooltip='Limite máximo de variação de preço para cálculos RSI (evita valores extremos)')

i_table_show = input.bool(true, 'Exibir Tabela', group=group_table, tooltip='Ativa a tabela multi-timeframe com valores RSI e preços-alvo')
i_rsi_tf1 = input.timeframe('5', 'TF 1', inline='rsi_tf1', group=group_table, tooltip='Timeframe 1 da tabela RSI', active=i_table_show)
i_rsi_tf1_name = input.string('5m', '', inline='rsi_tf1', group=group_table, tooltip='Nome exibido na tabela', active=i_table_show)
i_rsi_tf2 = input.timeframe('15', 'TF 2', inline='rsi_tf2', group=group_table, tooltip='Timeframe 2 da tabela RSI', active=i_table_show)
i_rsi_tf2_name = input.string('15m', '', inline='rsi_tf2', group=group_table, tooltip='Nome exibido na tabela', active=i_table_show)
i_rsi_tf3 = input.timeframe('60', 'TF 3', inline='rsi_tf3', group=group_table, tooltip='Timeframe 3 da tabela RSI', active=i_table_show)
i_rsi_tf3_name = input.string('1h', '', inline='rsi_tf3', group=group_table, tooltip='Nome exibido na tabela', active=i_table_show)
i_rsi_tf4 = input.timeframe('240', 'TF 4', inline='rsi_tf4', group=group_table, tooltip='Timeframe 4 da tabela RSI', active=i_table_show)
i_rsi_tf4_name = input.string('4h', '', inline='rsi_tf4', group=group_table, tooltip='Nome exibido na tabela', active=i_table_show)
i_rsi_tf5 = input.timeframe('D', 'TF 5', inline='rsi_tf5', group=group_table, tooltip='Timeframe 5 da tabela RSI', active=i_table_show)
i_rsi_tf5_name = input.string('1D', '', inline='rsi_tf5', group=group_table, tooltip='Nome exibido na tabela', active=i_table_show)
i_table_show_col1 = input.bool(false, 'Exibir Coluna 1', inline='col1', group=group_table, tooltip='Ativa/desativa a coluna de preço para o nível RSI 1')
i_table_col1_level = input.float(30, '', minval=1, maxval=99, step=1, inline='col1', group=group_table, tooltip='Nível RSI para calcular preço', active=i_table_show_col1)
i_table_show_col2 = input.bool(false, 'Exibir Coluna 2', inline='col2', group=group_table, tooltip='Ativa/desativa a coluna de preço para o nível RSI 2')
i_table_col2_level = input.float(70, '', minval=1, maxval=99, step=1, inline='col2', group=group_table, tooltip='Nível RSI para calcular preço', active=i_table_show_col2)
i_table_position = input.string('top_right', 'Posição', options=['top_right', 'top_left', 'bottom_right', 'bottom_left'], group=group_table, tooltip='Posição da tabela RSI no gráfico', active=i_table_show)

i_fvg_enabled = input.bool(true, '⚡ Ativar FVG', group=group_fvg, tooltip='Ativa detecção e exibição de Fair Value Gaps no gráfico principal')
i_fvg_auto_threshold = input.bool(false, 'Threshold ATR Auto', group=group_fvg, tooltip='Quando ativo, usa ATR para calcular automaticamente o limiar mínimo de FVG', active=i_fvg_enabled)
i_fvg_threshold = input.float(0.3, 'Limiar (%)', minval=0.01, maxval=10, step=0.05, group=group_fvg, tooltip='Tamanho mínimo do gap em % para ser considerado FVG', active=i_fvg_enabled)
i_fvg_adaptive_tf = input.bool(true, 'Adaptativo TF', group=group_fvg, tooltip='Multiplica o limiar baseado no timeframe', active=i_fvg_enabled)
i_fvg_max_multiplier = input.float(5.0, 'Mult. Máximo', minval=1.0, maxval=20.0, step=0.5, group=group_fvg, tooltip='Limite máximo do multiplicador adaptativo por timeframe', active=i_fvg_enabled)
i_fvg_timeframe = input.timeframe('', 'Timeframe', group=group_fvg, tooltip='Timeframe para detecção de FVG (vazio = TF atual). Apenas TF igual ou maior que o gráfico atual é suportado neste modo.', active=i_fvg_enabled)
i_fvg_extend_bars = input.int(15, 'Extensão Temporal', minval=5, maxval=200, group=group_fvg, tooltip='Quantidade de períodos do timeframe do FVG usada para projetar a caixa no eixo do tempo. Em mercados com gaps, a projeção é temporal e não representa contagem exata de candles negociados.', active=i_fvg_enabled)
i_fvg_max_count = input.int(8, 'Máximo FVGs', minval=1, maxval=50, group=group_fvg, tooltip='Número máximo de FVGs ativos simultaneamente', active=i_fvg_enabled)
i_fvg_max_age = input.int(300, 'Idade Máx', minval=50, maxval=1000, group=group_fvg, tooltip='Remove FVGs não mitigados após esta quantidade de barras do gráfico atual.', active=i_fvg_enabled)
i_fvg_bull_color = input.color(color.new(#089981, 85), 'Cor Alta', group=group_fvg, tooltip='Cor das caixas FVG bullish', active=i_fvg_enabled)
i_fvg_bear_color = input.color(color.new(#f23645, 85), 'Cor Baixa', group=group_fvg, tooltip='Cor das caixas FVG bearish', active=i_fvg_enabled)
i_fvg_mitigation_type = input.string('50% (Meio)', 'Mitigação', options=['50% (Meio)', '100% (Completo)', '25% (Conservador)'], group=group_fvg, tooltip='Define quando um FVG é considerado mitigado', active=i_fvg_enabled)
i_fvg_debug = input.bool(false, 'Debug Mode', group=group_fvg, tooltip='Exibe tabela de debug com informações técnicas sobre os FVGs', active=i_fvg_enabled)
i_atr_period = input.int(14, 'ATR Período', minval=5, maxval=50, group=group_fvg, tooltip='Período do ATR usado no threshold automático de FVG', active=i_fvg_enabled)

// ═══════════════════════════════════════════════════════════════════════════
// 🌐 VARIÁVEIS GLOBAIS
// ═══════════════════════════════════════════════════════════════════════════
var CandleSet htf1 = CandleSet.new(array.new<Candle>(), array.new<Imbalance>(), SettingsHTF1, na, na, na, na)
var CandleSet htf2 = CandleSet.new(array.new<Candle>(), array.new<Imbalance>(), SettingsHTF2, na, na, na, na)
var CandleSet htf3 = CandleSet.new(array.new<Candle>(), array.new<Imbalance>(), SettingsHTF3, na, na, na, na)
var CandleSet htf4 = CandleSet.new(array.new<Candle>(), array.new<Imbalance>(), SettingsHTF4, na, na, na, na)
var CandleSet htf5 = CandleSet.new(array.new<Candle>(), array.new<Imbalance>(), SettingsHTF5, na, na, na, na)
var CandleSet htf6 = CandleSet.new(array.new<Candle>(), array.new<Imbalance>(), SettingsHTF6, na, na, na, na)

var array<CandleSet> g_htfs = array.from(htf1, htf2, htf3, htf4, htf5, htf6)
var array<bool> g_prev_shows = array.from(true, true, true, true, true, true)

// [FIX v1.2.1] Helper removido — métodos convertidos em funções puras
var Trace trace = Trace.new()
// [FIX v1.2.0] var adicionado
var color color_transparent = #ffffff00

type RSISlot
    float rsi
    float close
    float auc
    float adc

// RSI
float RSI_ALPHA = 1.0 / i_rsi_length
var array<RSISlot> g_rsi_slots = array.new<RSISlot>(RSI_MTF_SLOT_COUNT)
if barstate.isfirst
    for i = 0 to RSI_MTF_SLOT_COUNT - 1
        g_rsi_slots.set(i, RSISlot.new(na, na, na, na))
var float g_cached_rsi25 = na
var float g_cached_rsi30 = na
var float g_cached_rsi50 = na
var float g_cached_rsi70 = na
var float g_cached_rsi75 = na
var float g_cached_rsi_custom = na
var int g_last_calc_bar = -1
var table g_rsi_table = na
var int g_prev_col_config = 0
var string g_prev_table_position = ''

// FVG Gráfico
var array<FVG> g_active_fvgs = array.new<FVG>()
var int g_last_fvg_time = 0
var array<int> g_fvg_to_remove = array.new<int>()
var int g_tf_seconds = 0
var table g_debug_table = na

// ═══════════════════════════════════════════════════════════════════════════
// 🔧 FUNÇÕES AUXILIARES - ICT
// ═══════════════════════════════════════════════════════════════════════════
f_line_style(string style) =>
    switch style
        '----' => line.style_dashed
        '····' => line.style_dotted
        => line.style_solid

// [FIX v1.2.0] DayofWeek corrigido — usa constantes Pine + default robusto
f_day_of_week(int index) =>
    if na(index)
        ''
    else
        switch index
            dayofweek.monday    => 'Seg'
            dayofweek.tuesday   => 'Ter'
            dayofweek.wednesday => 'Qua'
            dayofweek.thursday  => 'Qui'
            dayofweek.friday    => 'Sex'
            dayofweek.saturday  => 'Sáb'
            dayofweek.sunday    => 'Dom'
            => ''

// [FIX v1.2.0] ValidTimeframe usa modulo para precisão
f_valid_timeframe(string HTF) =>
    if timeframe.in_seconds(HTF) >= timeframe.in_seconds('D') and timeframe.in_seconds(HTF) > timeframe.in_seconds()
        true
    else
        n1 = timeframe.in_seconds()
        n2 = timeframe.in_seconds(HTF)
        n1 < n2 and n2 % n1 == 0

f_remaining_time(string HTF) =>
    if barstate.islast
        int closeTime = time_close(HTF)
        if na(closeTime)
            '--:--'
        else
            timeRemaining = int(math.max(0, (closeTime - timenow) / 1000))
            days = math.floor(timeRemaining / SEC_1D)
            hours = math.floor((timeRemaining - days * SEC_1D) / SEC_1H)
            minutes = math.floor((timeRemaining - days * SEC_1D - hours * SEC_1H) / 60)
            seconds = math.floor(timeRemaining - days * SEC_1D - hours * SEC_1H - minutes * 60)
            r = str.tostring(seconds, '00')
            if minutes > 0 or hours > 0 or days > 0
                r := str.tostring(minutes, '00') + ':' + r
            if hours > 0 or days > 0
                r := str.tostring(hours, '00') + ':' + r
            if days > 0
                r := str.tostring(days) + 'D ' + r
            r
    else
        '--:--'

f_htf_name(string HTF) =>
    seconds = timeframe.in_seconds(HTF)
    if seconds < 60
        str.tostring(seconds) + 's'
    else if seconds / 60 < 60
        str.tostring(seconds / 60) + 'm'
    else if seconds / 60 / 60 < 24
        str.tostring(seconds / 60 / 60) + 'H'
    else
        HTF

f_htf_enabled() =>
    array<bool> current_shows = array.from(SettingsHTF1.show, SettingsHTF2.show, SettingsHTF3.show, SettingsHTF4.show, htf5_show, htf6_show)
    int enabled = 0
    for i = 0 to 5
        if current_shows.get(i)
            enabled += 1
    math.min(enabled, settings.max_sets)

f_can_render_htf(bool isEnabled, string HTF, int renderedCount, int renderedTotal) =>
    isEnabled and f_valid_timeframe(HTF) and renderedCount < renderedTotal

f_should_show_trace(int renderedCount, int renderedTotal) =>
    settings.trace_anchor == 'First Timeframe' ? renderedCount == 0 : renderedTotal > 0 and renderedCount == renderedTotal - 1

f_htf_group_span(CandleSet candleSet) =>
    candleSet.candles.size() * settings.width + (candleSet.candles.size() > 0 ? (candleSet.candles.size() - 1) * settings.buffer : 0) + settings.htf_buffer

f_clear_trace_labels() =>
    if not na(trace.o_l)
        label.delete(trace.o_l)
        trace.o_l := na
    if not na(trace.c_l)
        label.delete(trace.c_l)
        trace.c_l := na
    if not na(trace.h_l)
        label.delete(trace.h_l)
        trace.h_l := na
    if not na(trace.l_l)
        label.delete(trace.l_l)
        trace.l_l := na

f_clear_trace() =>
    if not na(trace.o)
        line.delete(trace.o)
        trace.o := na
    if not na(trace.c)
        line.delete(trace.c)
        trace.c := na
    if not na(trace.h)
        line.delete(trace.h)
        trace.h := na
    if not na(trace.l)
        line.delete(trace.l)
        trace.l := na
    f_clear_trace_labels()

f_sync_trace_line(line currentLine, int x1, int x2, float price, color lineColor, string lineStyle, int lineWidth) =>
    line result = currentLine
    if not na(result)
        line.set_xy1(result, x1, price)
        line.set_xy2(result, x2, price)
        line.set_color(result, lineColor)
        line.set_style(result, f_line_style(lineStyle))
        line.set_width(result, lineWidth)
    else
        result := line.new(x1, price, x2, price, color=lineColor, style=f_line_style(lineStyle), width=lineWidth)
    result

f_sync_trace_label(label currentLabel, int x, float price, color textColor, string textSize) =>
    string priceText = str.tostring(price, format.mintick)
    label result = currentLabel
    if not na(result)
        label.set_xy(result, x, price)
        label.set_text(result, priceText)
        label.set_textcolor(result, textColor)
        label.set_size(result, textSize)
    else
        result := label.new(x, price, priceText, color=color_transparent, textcolor=textColor, style=label.style_label_left, size=textSize)
    result

f_candle_set_high(array<Candle> candles, float h) =>
    float _h = h
    if candles.size() > 0
        for i = 0 to candles.size() - 1
            if candles.get(i).h > _h
                _h := candles.get(i).h
    _h

f_candle_set_low(array<Candle> candles, float l) =>
    float _l = l
    if candles.size() > 0
        for i = 0 to candles.size() - 1
            if candles.get(i).l < _l
                _l := candles.get(i).l
    _l

f_candles_high() =>
    array<bool> current_shows = array.from(SettingsHTF1.show, SettingsHTF2.show, SettingsHTF3.show, SettingsHTF4.show, htf5_show, htf6_show)
    h = 0.0
    int cnt = 0
    int last = f_htf_enabled()
    for i = 0 to 5
        CandleSet cs = g_htfs.get(i)
        if f_can_render_htf(current_shows.get(i), cs.settings.htf, cnt, last)
            h := f_candle_set_high(cs.candles, h)
            cnt += 1
    h

f_candles_low(float h) =>
    array<bool> current_shows = array.from(SettingsHTF1.show, SettingsHTF2.show, SettingsHTF3.show, SettingsHTF4.show, htf5_show, htf6_show)
    l = h
    int cnt = 0
    int last = f_htf_enabled()
    for i = 0 to 5
        CandleSet cs = g_htfs.get(i)
        if f_can_render_htf(current_shows.get(i), cs.settings.htf, cnt, last)
            l := f_candle_set_low(cs.candles, l)
            cnt += 1
    l

// ═══════════════════════════════════════════════════════════════════════════
// 🔧 FUNÇÕES RSI
// ═══════════════════════════════════════════════════════════════════════════
f_rsi_ema_length() => math.max(1, 2 * i_rsi_length - 1)

f_rsi_color(float r) =>
    switch
        na(r)   => [color.new(#44475a, 0), color.new(#6272a4, 0)]  // Sem dado → Dracula comment
        r < 20  => [color.new(#14755f, 0), color.new(#f8f8f2, 0)]  // Muito sobrevendido → ciano escuro
        r < 30  => [color.new(#3de1c7, 0), color.new(#282a36, 0)]  // Sobrevendido → ciano médio
        r < 44  => [color.new(#b5f9ef, 0), color.new(#282a36, 0)]  // Leve alta → ciano pastel
        r <= 55 => [color.new(#282a36, 0), color.new(#f8f8f2, 0)]  // Neutro → fundo Dracula
        r < 70  => [color.new(#ffb8d0, 0), color.new(#282a36, 0)]  // Leve baixa → rosa pastel
        r < 80  => [color.new(#ff4d8d, 0), color.new(#282a36, 0)]  // Sobrecomprado → rosa médio
        =>         [color.new(#8b1048, 0), color.new(#f8f8f2, 0)]  // Muito sobrecomprado → rosa escuro


f_rsi_is_valid(float c, float au, float ad) =>
    not na(c) and not na(au) and not na(ad) and c > 0 and au >= 0 and ad >= 0

f_rsi_calc_price(float target_rsi, float close_val, float auc_val, float adc_val) =>
    if not f_rsi_is_valid(close_val, auc_val, adc_val) or target_rsi <= 0 or target_rsi >= 100
        na
    else
        alpha = RSI_ALPHA
        denominator = 100.0 - target_rsi
        if denominator == 0
            na
        else
            target_rs = target_rsi / denominator
            float calc_price = na
            if target_rsi < 50
                new_avg_up = (1 - alpha) * auc_val
                new_avg_down = target_rs == 0 ? na : new_avg_up / target_rs
                d_loss = na(new_avg_down) ? na : (new_avg_down - (1 - alpha) * adc_val) / alpha
                calc_price := na(d_loss) ? na : close_val - d_loss
            else
                new_avg_down = (1 - alpha) * adc_val
                new_avg_up = target_rs * new_avg_down
                d_gain = (new_avg_up - (1 - alpha) * auc_val) / alpha
                calc_price := close_val + d_gain
            if na(calc_price)
                na
            else
                min_p = close_val * (1 - i_rsi_max_deviation)
                max_p = close_val * (1 + i_rsi_max_deviation)
                math.max(min_p, math.min(max_p, calc_price))

f_rsi_pack() =>
    emaLen = f_rsi_ema_length()
    [close, ta.rsi(close, i_rsi_length), ta.ema(math.max(close - close[1], 0), emaLen), ta.ema(math.max(close[1] - close, 0), emaLen)]

f_rsi_pack_confirmed() =>
    emaLen = f_rsi_ema_length()
    [close[1], ta.rsi(close, i_rsi_length)[1], ta.ema(math.max(close - close[1], 0), emaLen)[1], ta.ema(math.max(close[1] - close, 0), emaLen)[1]]

f_rsi_tf_display(int index) =>
    switch index
        0 => i_rsi_tf1_name
        1 => i_rsi_tf2_name
        2 => i_rsi_tf3_name
        3 => i_rsi_tf4_name
        => i_rsi_tf5_name

f_request_rsi_pack(string targetTf, bool useConfirmedHtfSecurity, int chartTfSeconds, int calcBarsCount) =>
    int targetTfSeconds = timeframe.in_seconds(targetTf)
    bool useLocal = targetTfSeconds == chartTfSeconds
    bool useConfirmed = useConfirmedHtfSecurity and targetTfSeconds > chartTfSeconds
    [localClose, localRsi, localAuc, localAdc] = f_rsi_pack()
    [confirmedClose, confirmedRsi, confirmedAuc, confirmedAdc] = request.security(syminfo.tickerid, targetTf, f_rsi_pack_confirmed(), lookahead=barmerge.lookahead_on, gaps=barmerge.gaps_off, calc_bars_count=calcBarsCount)
    [realtimeClose, realtimeRsi, realtimeAuc, realtimeAdc] = request.security(syminfo.tickerid, targetTf, f_rsi_pack(), lookahead=barmerge.lookahead_off, gaps=barmerge.gaps_off, calc_bars_count=calcBarsCount)
    if useLocal
        [localClose, localRsi, localAuc, localAdc]
    else if useConfirmed
        [confirmedClose, confirmedRsi, confirmedAuc, confirmedAdc]
    else
        [realtimeClose, realtimeRsi, realtimeAuc, realtimeAdc]

f_table_seed_cells(table tbl, int cols, int rows) =>
    for row = 0 to rows - 1
        for col = 0 to cols - 1
            table.cell(tbl, col, row, '', text_color=color.new(#f8f8f2, 0), bgcolor=color.new(#282a36, 0), text_size=size.small)

f_table_set_cell(table tbl, int col, int row, string txt, color bg, color txtClr, string txtSize) =>
    table.cell_set_text(tbl, col, row, txt)
    table.cell_set_bgcolor(tbl, col, row, bg)
    table.cell_set_text_color(tbl, col, row, txtClr)
    table.cell_set_text_size(tbl, col, row, txtSize)

// ═══════════════════════════════════════════════════════════════════════════
// 🔧 FUNÇÕES FVG GRÁFICO
// ═══════════════════════════════════════════════════════════════════════════
f_fvg_get_tf_multiplier(int tf_sec) =>
    float result = switch
        tf_sec <= 60 => 0.5
        tf_sec <= 300 => 0.5 + (tf_sec - 60) * (0.75 - 0.5) / (300 - 60)
        tf_sec <= 900 => 0.75 + (tf_sec - 300) * (1.0 - 0.75) / (900 - 300)
        tf_sec <= SEC_1H => 1.0 + (tf_sec - 900) * (1.5 - 1.0) / (SEC_1H - 900)
        tf_sec <= SEC_4H => 1.5 + (tf_sec - SEC_1H) * (2.2 - 1.5) / (SEC_4H - SEC_1H)
        tf_sec <= SEC_1D => 2.2 + (tf_sec - SEC_4H) * (4.0 - 2.2) / (SEC_1D - SEC_4H)
        => 8.0
    math.min(i_fvg_max_multiplier, result)

f_fvg_get_mitigation_level(float top, float bottom, string mitType, bool isBull) =>
    if mitType == '100% (Completo)'
        isBull ? bottom : top
    else if mitType == '25% (Conservador)'
        isBull ? top - (top - bottom) * 0.25 : bottom + (top - bottom) * 0.25
    else
        (top + bottom) / 2.0

method draw(FVG this) =>
    if not na(this) and this.isActive
        x1 = this.birthTime
        // A projeção usa tempo-relógio do timeframe do FVG para manter compatibilidade com xloc.bar_time.
        x2 = this.birthTime + (g_tf_seconds * MS_PER_SECOND * i_fvg_extend_bars)
        boxColor = this.isBull ? i_fvg_bull_color : i_fvg_bear_color
        this.boxObj := box.new(left=x1, top=this.top, right=x2, bottom=this.bottom, xloc=xloc.bar_time, border_color=na, bgcolor=boxColor)

method remove(FVG this, bool wasMitigated, float mitigationPrice) =>
    if not na(this)
        if not na(this.boxObj)
            this.boxObj.delete()
            this.boxObj := na
        this.isActive := false

method checkMitigation(FVG this) =>
    bool isMitigated = false
    float mitigationLevel = f_fvg_get_mitigation_level(this.top, this.bottom, i_fvg_mitigation_type, this.isBull)
    if time > this.birthTime
        isMitigated := this.isBull ? (low <= mitigationLevel) : (high >= mitigationLevel)
    [isMitigated, mitigationLevel]

method isExpired(FVG this) =>
    if this.birthBarIndex < 0
        false
    else
        barsAge = bar_index - this.birthBarIndex
        barsAge > i_fvg_max_age

f_fvg_detect() =>
    float atr_value = i_fvg_auto_threshold or i_fvg_debug ? ta.atr(i_atr_period) : na
    float threshold = na
    float bullGapSize = na
    float bearGapSize = na
    if i_fvg_auto_threshold
        float atr_safe = math.max(atr_value, syminfo.mintick)
        threshold := 1.0
        if i_fvg_adaptive_tf
            threshold := threshold * f_fvg_get_tf_multiplier(g_tf_seconds)
        bullGapSize := (low - high[2]) / atr_safe
        bearGapSize := (low[2] - high) / atr_safe
    else
        threshold := i_fvg_threshold / 100.0
        if i_fvg_adaptive_tf
            threshold := threshold * f_fvg_get_tf_multiplier(g_tf_seconds)
        bullGapSize := (low - high[2]) / math.max(high[2], syminfo.mintick)
        bearGapSize := (low[2] - high) / math.max(low[2], syminfo.mintick)
    isBullFvg = low > high[2] and bullGapSize > threshold
    isBearFvg = high < low[2] and bearGapSize > threshold
    bool hasEnoughBars = bar_index >= 2 and not na(time[2])
    FVG newFvg = isBullFvg and hasEnoughBars ? FVG.new(low, high[2], true, time[2], bar_index - 2, na, na, true) : isBearFvg and hasEnoughBars ? FVG.new(low[2], high, false, time[2], bar_index - 2, na, na, true) : FVG.new(na, na, false, 0, -1, na, na, false)
    [isBullFvg, isBearFvg, newFvg, threshold, atr_value]

f_fvg_detect_confirmed() =>
    float atr_value = i_fvg_auto_threshold or i_fvg_debug ? ta.atr(i_atr_period)[1] : na
    float threshold = na
    float bullGapSize = na
    float bearGapSize = na
    if i_fvg_auto_threshold
        float atr_safe = math.max(atr_value, syminfo.mintick)
        threshold := 1.0
        if i_fvg_adaptive_tf
            threshold := threshold * f_fvg_get_tf_multiplier(g_tf_seconds)
        bullGapSize := (low[1] - high[3]) / atr_safe
        bearGapSize := (low[3] - high[1]) / atr_safe
    else
        threshold := i_fvg_threshold / 100.0
        if i_fvg_adaptive_tf
            threshold := threshold * f_fvg_get_tf_multiplier(g_tf_seconds)
        bullGapSize := (low[1] - high[3]) / math.max(high[3], syminfo.mintick)
        bearGapSize := (low[3] - high[1]) / math.max(low[3], syminfo.mintick)
    isBullFvg = low[1] > high[3] and bullGapSize > threshold
    isBearFvg = high[1] < low[3] and bearGapSize > threshold
    bool hasEnoughBars = bar_index >= 3 and not na(time[3])
    FVG newFvg = isBullFvg and hasEnoughBars ? FVG.new(low[1], high[3], true, time[3], -1, na, na, true) : isBearFvg and hasEnoughBars ? FVG.new(low[3], high[1], false, time[3], -1, na, na, true) : FVG.new(na, na, false, 0, -1, na, na, false)
    [isBullFvg, isBearFvg, newFvg, threshold, atr_value]

f_request_fvg_detection(string targetTf, bool useConfirmedHtfSecurity, int chartTfSeconds, int calcBarsCount) =>
    int targetTfSeconds = timeframe.in_seconds(targetTf)
    bool useLocal = targetTfSeconds == chartTfSeconds
    bool useConfirmed = useConfirmedHtfSecurity and targetTfSeconds > chartTfSeconds
    [localBull, localBear, localFvg, localThreshold, localAtr] = f_fvg_detect()
    [confirmedBull, confirmedBear, confirmedFvg, confirmedThreshold, confirmedAtr] = request.security(syminfo.tickerid, targetTf, f_fvg_detect_confirmed(), lookahead=barmerge.lookahead_on, gaps=barmerge.gaps_off, calc_bars_count=calcBarsCount)
    [realtimeBull, realtimeBear, realtimeFvg, realtimeThreshold, realtimeAtr] = request.security(syminfo.tickerid, targetTf, f_fvg_detect(), lookahead=barmerge.lookahead_off, gaps=barmerge.gaps_off, calc_bars_count=calcBarsCount)
    if useLocal
        [localBull, localBear, localFvg, localThreshold, localAtr]
    else if useConfirmed
        [confirmedBull, confirmedBear, confirmedFvg, confirmedThreshold, confirmedAtr]
    else
        [realtimeBull, realtimeBear, realtimeFvg, realtimeThreshold, realtimeAtr]

// ═══════════════════════════════════════════════════════════════════════════
// 🧱 MÉTODOS CANDLESET
// ═══════════════════════════════════════════════════════════════════════════
method UpdateTime(CandleSet candleSet) =>
    if candleSet.candles.size() > 0
        Candle candle = candleSet.candles.get(0)
        candle.c := close
        candle.c_idx := bar_index
        if high > candle.h
            candle.h := high
            candle.h_idx := bar_index
        if low < candle.l
            candle.l := low
            candle.l_idx := bar_index
    candleSet

method ClearTopHtfLabels(CandleSet candleSet) =>
    if not na(candleSet.tfNameTop)
        candleSet.tfNameTop.delete()
        candleSet.tfNameTop := na
    if not na(candleSet.tfTimerTop)
        candleSet.tfTimerTop.delete()
        candleSet.tfTimerTop := na
    candleSet

method ClearBottomHtfLabels(CandleSet candleSet) =>
    if not na(candleSet.tfNameBottom)
        candleSet.tfNameBottom.delete()
        candleSet.tfNameBottom := na
    if not na(candleSet.tfTimerBottom)
        candleSet.tfTimerBottom.delete()
        candleSet.tfTimerBottom := na
    candleSet

method ClearHtfLabels(CandleSet candleSet) =>
    candleSet.ClearTopHtfLabels()
    candleSet.ClearBottomHtfLabels()
    candleSet

method SyncHtfLabels(CandleSet candleSet, int left, float top, float bottom) =>
    bool hasCandles = candleSet.candles.size() > 0
    if not settings.htf_label_show or not hasCandles
        candleSet.ClearHtfLabels()
    else
        string labelText = f_htf_name(candleSet.settings.htf)
        string labelTextTop = labelText
        string labelTextBottom = labelText
        if settings.htf_timer_show
            labelTextTop += '\n'
            labelTextBottom := '\n' + labelTextBottom
        if settings.daily_name
            labelTextTop += '\n'
        string timerText = '(' + f_remaining_time(candleSet.settings.htf) + ')' + (settings.daily_name ? '\n' : '')
        bool showTop = settings.label_position == 'Both' or settings.label_position == 'Top'
        bool showBottom = settings.label_position == 'Both' or settings.label_position == 'Bottom'
        if showTop
            if not na(candleSet.tfNameTop)
                candleSet.tfNameTop.set_xy(left, top)
                candleSet.tfNameTop.set_text(labelTextTop)
                candleSet.tfNameTop.set_textcolor(settings.htf_label_color)
                candleSet.tfNameTop.set_size(settings.htf_label_size)
            else
                candleSet.tfNameTop := label.new(left, top, labelTextTop, color=color_transparent, textcolor=settings.htf_label_color, style=label.style_label_down, size=settings.htf_label_size)
            if settings.htf_timer_show
                if not na(candleSet.tfTimerTop)
                    candleSet.tfTimerTop.set_xy(left, top)
                    candleSet.tfTimerTop.set_text(timerText)
                    candleSet.tfTimerTop.set_textcolor(settings.htf_timer_color)
                    candleSet.tfTimerTop.set_size(settings.htf_timer_size)
                else
                    candleSet.tfTimerTop := label.new(left, top, timerText, color=color_transparent, textcolor=settings.htf_timer_color, style=label.style_label_down, size=settings.htf_timer_size)
            else if not na(candleSet.tfTimerTop)
                candleSet.tfTimerTop.delete()
                candleSet.tfTimerTop := na
        else
            candleSet.ClearTopHtfLabels()
        if showBottom
            if not na(candleSet.tfNameBottom)
                candleSet.tfNameBottom.set_xy(left, bottom)
                candleSet.tfNameBottom.set_text(labelTextBottom)
                candleSet.tfNameBottom.set_textcolor(settings.htf_label_color)
                candleSet.tfNameBottom.set_size(settings.htf_label_size)
            else
                candleSet.tfNameBottom := label.new(left, bottom, labelTextBottom, color=color_transparent, textcolor=settings.htf_label_color, style=label.style_label_up, size=settings.htf_label_size)
            if settings.htf_timer_show
                if not na(candleSet.tfTimerBottom)
                    candleSet.tfTimerBottom.set_xy(left, bottom)
                    candleSet.tfTimerBottom.set_text(timerText)
                    candleSet.tfTimerBottom.set_textcolor(settings.htf_timer_color)
                    candleSet.tfTimerBottom.set_size(settings.htf_timer_size)
                else
                    candleSet.tfTimerBottom := label.new(left, bottom, timerText, color=color_transparent, textcolor=settings.htf_timer_color, style=label.style_label_up, size=settings.htf_timer_size)
            else if not na(candleSet.tfTimerBottom)
                candleSet.tfTimerBottom.delete()
                candleSet.tfTimerBottom := na
        else
            candleSet.ClearBottomHtfLabels()
    candleSet

method Reorder(CandleSet candleSet, int offset) =>
    size = candleSet.candles.size()
    if size > 0 and (barstate.isnew or barstate.islast or barstate.isrealtime)
        for i = size - 1 to 0
            Candle candle = candleSet.candles.get(i)
            t_buffer = offset + (settings.width + settings.buffer) * (size - i - 1)
            box.set_left(candle.body, bar_index + t_buffer)
            box.set_right(candle.body, bar_index + settings.width + t_buffer)
            line.set_x1(candle.wick_up, bar_index + settings.width / 2 + t_buffer)
            line.set_x2(candle.wick_up, bar_index + settings.width / 2 + t_buffer)
            line.set_x1(candle.wick_down, bar_index + settings.width / 2 + t_buffer)
            line.set_x2(candle.wick_down, bar_index + settings.width / 2 + t_buffer)
            if settings.daily_name
                if not na(candle.dow_label)
                    candle.dow_label.set_y(candle.h)
                    candle.dow_label.set_x(bar_index + settings.width / 2 + t_buffer)
                    candle.dow_label.set_text(candle.dow)
                    candle.dow_label.set_textcolor(settings.dow_color)
                    candle.dow_label.set_size(settings.dow_size)
                else
                    candle.dow_label := label.new(bar_index + settings.width / 2 + t_buffer, candle.h, candle.dow, color=color_transparent, textcolor=settings.dow_color, style=label.style_label_down, size=settings.dow_size)
            else if not na(candle.dow_label)
                candle.dow_label.delete()
                candle.dow_label := na
    top = 0.0
    bottom = 0.0
    if settings.label_alignment == 'Align'
        top := f_candles_high()
        bottom := f_candles_low(top)
    if settings.label_alignment == 'Follow Candles'
        top := f_candle_set_high(candleSet.candles, 0)
        bottom := f_candle_set_low(candleSet.candles, top)
    int left = bar_index + offset
    if size > 0
        left := bar_index + offset + (settings.width + settings.buffer) * (size - 1) / 2
    candleSet.SyncHtfLabels(left, top, bottom)
    if candleSet.imbalances.size() > 0
        for i = 0 to candleSet.imbalances.size() - 1
            Imbalance imb = candleSet.imbalances.get(i)
            t_buf2 = offset + (settings.width + settings.buffer) * (size - imb.arr_i2 - 1)
            t_buf1 = offset + (settings.width + settings.buffer) * (size - imb.arr_i1 - 1)
            box.set_left(imb.b, bar_index + t_buf2)
            box.set_right(imb.b, bar_index + t_buf1 + settings.width)
    candleSet

method Update(CandleSet candleSet, int offset, bool showTrace) =>
    candleSet.Reorder(offset)
    if candleSet.candles.size() > 0
        Candle candle = candleSet.candles.get(0)
        bull = candle.c > candle.o
        box.set_top(candle.body, math.max(candle.o, candle.c))
        box.set_bottom(candle.body, math.min(candle.o, candle.c))
        box.set_bgcolor(candle.body, bull ? settings.bull_body : settings.bear_body)
        box.set_border_color(candle.body, bull ? settings.bull_border : settings.bear_border)
        line.set_y1(candle.wick_up, candle.h)
        line.set_y2(candle.wick_up, math.max(candle.o, candle.c))
        line.set_color(candle.wick_up, bull ? settings.bull_wick : settings.bear_wick)
        line.set_y1(candle.wick_down, math.min(candle.o, candle.c))
        line.set_y2(candle.wick_down, candle.l)
        line.set_color(candle.wick_down, bull ? settings.bull_wick : settings.bear_wick)
        if showTrace and settings.trace_show
            t_buffer = offset + (settings.width + settings.buffer) * (candleSet.candles.size() - 1)
            int x1 = bar_index
            int x2 = bar_index + t_buffer
            trace.o := f_sync_trace_line(trace.o, x1, x2, candle.o, settings.trace_o_color, settings.trace_o_style, settings.trace_o_size)
            trace.c := f_sync_trace_line(trace.c, x1, x2, candle.c, settings.trace_c_color, settings.trace_c_style, settings.trace_c_size)
            trace.h := f_sync_trace_line(trace.h, x1, x2, candle.h, settings.trace_h_color, settings.trace_h_style, settings.trace_h_size)
            trace.l := f_sync_trace_line(trace.l, x1, x2, candle.l, settings.trace_l_color, settings.trace_l_style, settings.trace_l_size)
            if settings.label_show
                int labelX = bar_index + t_buffer + settings.width
                trace.o_l := f_sync_trace_label(trace.o_l, labelX, candle.o, settings.label_color, settings.label_size)
                trace.c_l := f_sync_trace_label(trace.c_l, labelX, candle.c, settings.label_color, settings.label_size)
                trace.h_l := f_sync_trace_label(trace.h_l, labelX, candle.h, settings.label_color, settings.label_size)
                trace.l_l := f_sync_trace_label(trace.l_l, labelX, candle.l, settings.label_color, settings.label_size)
    candleSet

method FindImbalance(CandleSet candleSet) =>
    int candleCount = candleSet.candles.size()
    if candleCount >= 3 and (barstate.isrealtime or barstate.islast)
        Candle lastCandle = candleSet.candles.get(candleCount - 1)
        int currentCandleIdx = lastCandle.o_idx
        int lastProcessedIdx = candleSet.imbalances.size() > 0 ? candleSet.imbalances.get(candleSet.imbalances.size() - 1).idx : -1
        // [FIX v1.2.2] Dirty flag: só reprocessa se há candle novo
        bool isNewCandle = currentCandleIdx != lastProcessedIdx
        if isNewCandle and (barstate.isconfirmed or barstate.ishistory)
            if candleSet.imbalances.size() > 0
                for i = candleSet.imbalances.size() - 1 to 0
                    Imbalance del = candleSet.imbalances.get(i)
                    box.delete(del.b)
                    candleSet.imbalances.pop()
            if candleCount >= 3 and settings.fvg_show
                for i = 0 to candleCount - 3 by 1
                    candle1 = candleSet.candles.get(i)
                    candle2 = candleSet.candles.get(i + 2)
                    if candle1.l > candle2.h and math.min(candle1.o, candle1.c) > math.max(candle2.o, candle2.c)
                        Imbalance imb = Imbalance.new()
                        imb.b := box.new(box.get_left(candle2.body), candle2.h, box.get_right(candle1.body), candle1.l, bgcolor=settings.fvg_color, border_color=color_transparent)
                        imb.idx := currentCandleIdx
                        imb.arr_i1 := i
                        imb.arr_i2 := i + 2
                        candleSet.imbalances.push(imb)
                    if candle1.h < candle2.l and math.max(candle1.o, candle1.c) < math.min(candle2.o, candle2.c)
                        Imbalance imb = Imbalance.new()
                        imb.b := box.new(box.get_right(candle1.body), candle1.h, box.get_left(candle2.body), candle2.l, bgcolor=settings.fvg_color, border_color=color_transparent)
                        imb.idx := currentCandleIdx
                        imb.arr_i1 := i
                        imb.arr_i2 := i + 2
                        candleSet.imbalances.push(imb)
            if candleCount > 2 and settings.vi_show
                for i = 0 to candleCount - 2 by 1
                    candle1 = candleSet.candles.get(i)
                    candle2 = candleSet.candles.get(i + 1)
                    if candle1.l < candle2.h and math.min(candle1.o, candle1.c) > math.max(candle2.o, candle2.c)
                        Imbalance imb = Imbalance.new()
                        imb.b := box.new(box.get_left(candle2.body), math.min(candle1.o, candle1.c), box.get_right(candle1.body), math.max(candle2.o, candle2.c), bgcolor=settings.vi_color, border_color=color_transparent)
                        imb.idx := currentCandleIdx
                        imb.arr_i1 := i
                        imb.arr_i2 := i + 1
                        candleSet.imbalances.push(imb)
                    if candle1.h > candle2.l and math.max(candle1.o, candle1.c) < math.min(candle2.o, candle2.c)
                        Imbalance imb = Imbalance.new()
                        imb.b := box.new(box.get_right(candle1.body), math.min(candle2.o, candle2.c), box.get_left(candle2.body), math.max(candle1.o, candle1.c), bgcolor=settings.vi_color, border_color=color_transparent)
                        imb.idx := currentCandleIdx
                        imb.arr_i1 := i
                        imb.arr_i2 := i + 1
                        candleSet.imbalances.push(imb)
    candleSet

// [FIX v1.2.3] Monitor extraído para consistency (evita warnings ta.change)
method Monitor(CandleSet candleSet, bool isNewHTFCandle) =>
    // Anti-repaint: cria em barra confirmada, com fallback para carga histórica
    if isNewHTFCandle and (barstate.isconfirmed or barstate.ishistory)
        Candle candle = Candle.new()
        candle.o := open, candle.c := close, candle.h := high, candle.l := low
        candle.o_time := time, candle.o_idx := bar_index, candle.c_idx := bar_index
        candle.h_idx := bar_index, candle.l_idx := bar_index
        // [FIX v1.2.0] Usa timeframe.in_seconds() em vez de str.tonumber()
        int htfSec = timeframe.in_seconds(candleSet.settings.htf)
        candle.dow := switch
            candleSet.settings.htf == '1W' => 'Sem ' + str.tostring(weekofyear(time, i_timezone))
            candleSet.settings.htf == '1D' => f_day_of_week(dayofweek(time_tradingday))
            htfSec < SEC_1H => str.format_time(candle.o_time, 'm', i_timezone)
            htfSec >= SEC_1H and htfSec < SEC_1D => str.format_time(candle.o_time, 'H', i_timezone)
            => ''
        bull = candle.c > candle.o
        candle.body := box.new(bar_index, math.max(candle.o, candle.c), bar_index + 2, math.min(candle.o, candle.c), bull ? settings.bull_border : settings.bear_border, 1, bgcolor=bull ? settings.bull_body : settings.bear_body)
        candle.wick_up := line.new(bar_index + 1, candle.h, bar_index, math.max(candle.o, candle.c), color=bull ? settings.bull_wick : settings.bear_wick)
        candle.wick_down := line.new(bar_index + 1, math.min(candle.o, candle.c), bar_index, candle.l, color=bull ? settings.bull_wick : settings.bear_wick)
        candleSet.candles.unshift(candle)
        if candleSet.candles.size() > candleSet.settings.max_display
            Candle delCandle = array.pop(candleSet.candles)
            box.delete(delCandle.body)
            line.delete(delCandle.wick_up)
            line.delete(delCandle.wick_down)
            if not na(delCandle.dow_label)
                delCandle.dow_label.delete()
    candleSet

f_process_htf(CandleSet candleSet, bool isNewHTFCandle, int renderedCount, int renderedTotal, int offset) =>
    bool showTrace = f_should_show_trace(renderedCount, renderedTotal)
    CandleSet workingSet = candleSet
    workingSet := workingSet.Monitor(isNewHTFCandle)
    workingSet := workingSet.UpdateTime()
    workingSet := workingSet.Update(offset, showTrace)
    workingSet := workingSet.FindImbalance()
    [renderedCount + 1, offset + f_htf_group_span(workingSet)]

// ═══════════════════════════════════════════════════════════════════════════
// 📊 CÁLCULOS & REQUEST.SECURITY
// ═══════════════════════════════════════════════════════════════════════════

ema_value = ta.ema(i_emaSource, i_emaLength)
rsiEmaLength = f_rsi_ema_length()
rsi_current = ta.rsi(close, i_rsi_length)
auc_current = ta.ema(math.max(close - close[1], 0), rsiEmaLength)
adc_current = ta.ema(math.max(close[1] - close, 0), rsiEmaLength)
bool useConfirmedHtfSecurity = i_htf_security_mode == 'Confirmado'
int rsiCalcBarsCount = math.max(RSI_REQUEST_MIN_BARS, i_rsi_length * 6)
bool hasCustomFvgTf = i_fvg_timeframe != ''
int chartTfSeconds = timeframe.in_seconds()
int fvgTfSeconds = hasCustomFvgTf ? timeframe.in_seconds(i_fvg_timeframe) : chartTfSeconds
bool fvgTfIsLower = hasCustomFvgTf and fvgTfSeconds < chartTfSeconds
int fvgCalcBarsCount = math.max(FVG_REQUEST_MIN_BARS, i_atr_period + 10)

if i_fvg_enabled and fvgTfIsLower and barstate.isfirst
    runtime.error('O timeframe do FVG deve ser igual ou maior que o timeframe do gráfico atual. Para LTF, use request.security_lower_tf em uma implementação dedicada.')

// Pine exige chamadas consistentes nesse bloco. Aqui deixamos as 5 leituras explícitas.
[closeVal1, rsiVal1, aucVal1, adcVal1] = f_request_rsi_pack(i_rsi_tf1, useConfirmedHtfSecurity, chartTfSeconds, rsiCalcBarsCount)
RSISlot s0 = g_rsi_slots.get(0)
s0.close := closeVal1
s0.rsi := rsiVal1
s0.auc := aucVal1
s0.adc := adcVal1

[closeVal2, rsiVal2, aucVal2, adcVal2] = f_request_rsi_pack(i_rsi_tf2, useConfirmedHtfSecurity, chartTfSeconds, rsiCalcBarsCount)
RSISlot s1 = g_rsi_slots.get(1)
s1.close := closeVal2
s1.rsi := rsiVal2
s1.auc := aucVal2
s1.adc := adcVal2

[closeVal3, rsiVal3, aucVal3, adcVal3] = f_request_rsi_pack(i_rsi_tf3, useConfirmedHtfSecurity, chartTfSeconds, rsiCalcBarsCount)
RSISlot s2 = g_rsi_slots.get(2)
s2.close := closeVal3
s2.rsi := rsiVal3
s2.auc := aucVal3
s2.adc := adcVal3

[closeVal4, rsiVal4, aucVal4, adcVal4] = f_request_rsi_pack(i_rsi_tf4, useConfirmedHtfSecurity, chartTfSeconds, rsiCalcBarsCount)
RSISlot s3 = g_rsi_slots.get(3)
s3.close := closeVal4
s3.rsi := rsiVal4
s3.auc := aucVal4
s3.adc := adcVal4

[closeVal5, rsiVal5, aucVal5, adcVal5] = f_request_rsi_pack(i_rsi_tf5, useConfirmedHtfSecurity, chartTfSeconds, rsiCalcBarsCount)
RSISlot s4 = g_rsi_slots.get(4)
s4.close := closeVal5
s4.rsi := rsiVal5
s4.auc := aucVal5
s4.adc := adcVal5

tfFinal = hasCustomFvgTf ? i_fvg_timeframe : timeframe.period
g_tf_seconds := fvgTfSeconds
[isBull, isBear, detectedFvg, thresholdUsed, atrValue] = f_request_fvg_detection(tfFinal, useConfirmedHtfSecurity, chartTfSeconds, fvgCalcBarsCount)

// ═══════════════════════════════════════════════════════════════════════════
// ⚡ LÓGICA PRINCIPAL - FVG GRÁFICO
// ═══════════════════════════════════════════════════════════════════════════
bool didAddBullFvg = false
bool didAddBearFvg = false

if i_fvg_enabled and barstate.isconfirmed and (isBull or isBear) and detectedFvg.birthTime > g_last_fvg_time
    if g_active_fvgs.size() < i_fvg_max_count and detectedFvg.isActive
        g_last_fvg_time := detectedFvg.birthTime
        detectedFvg.birthBarIndex := bar_index
        detectedFvg.draw()
        g_active_fvgs.unshift(detectedFvg)
        didAddBullFvg := isBull
        didAddBearFvg := isBear

// [FIX v1.2.0] Anti-repaint: mitigação/expiração APENAS em barstate.isconfirmed
if i_fvg_enabled and barstate.isconfirmed and g_active_fvgs.size() > 0
    if g_fvg_to_remove.size() > 0
        g_fvg_to_remove.clear()
    int arraySize = g_active_fvgs.size()
    for i = 0 to arraySize - 1
        fvg = g_active_fvgs.get(i)
        if fvg.isActive
            [wasMitigated, mitigationPrice] = fvg.checkMitigation()
            wasExpired = fvg.isExpired()
            if wasMitigated or wasExpired
                fvg.remove(wasMitigated, mitigationPrice)
                g_fvg_to_remove.push(i)
    if g_fvg_to_remove.size() > 0
        g_fvg_to_remove.sort(order.descending)
        for i = 0 to g_fvg_to_remove.size() - 1
            g_active_fvgs.remove(g_fvg_to_remove.get(i))

// [FIX v1.2.0] Garbage collection: limpa FVGs quando feature desligada
if not i_fvg_enabled and g_active_fvgs.size() > 0
    for i = 0 to g_active_fvgs.size() - 1
        fvg = g_active_fvgs.get(i)
        if fvg.isActive
            fvg.remove(false, na)
    g_active_fvgs.clear()
    g_last_fvg_time := 0

// === BLOCO DE CACHE RSI ===
bool use_rsi_25 = i_show_rsi_25
bool use_rsi_30 = i_show_rsi_30
bool use_rsi_50 = i_show_rsi_50
bool use_rsi_70 = i_show_rsi_70
bool use_rsi_75 = i_show_rsi_75
bool use_rsi_custom = i_show_rsi_custom

bool should_calculate = barstate.isnew or (barstate.islast and g_last_calc_bar != bar_index)
if should_calculate
    g_cached_rsi25 := use_rsi_25 ? f_rsi_calc_price(25.0, close, auc_current, adc_current) : na
    g_cached_rsi30 := use_rsi_30 ? f_rsi_calc_price(30.0, close, auc_current, adc_current) : na
    g_cached_rsi50 := use_rsi_50 ? f_rsi_calc_price(50.0, close, auc_current, adc_current) : na
    g_cached_rsi70 := use_rsi_70 ? f_rsi_calc_price(70.0, close, auc_current, adc_current) : na
    g_cached_rsi75 := use_rsi_75 ? f_rsi_calc_price(75.0, close, auc_current, adc_current) : na
    g_cached_rsi_custom := i_show_rsi_custom ? f_rsi_calc_price(i_rsi_custom_level, close, auc_current, adc_current) : na
    g_last_calc_bar := bar_index

// ═══════════════════════════════════════════════════════════════════════════
// 🎨 RENDERIZAÇÃO VISUAL
// ═══════════════════════════════════════════════════════════════════════════

// EMA
plot(i_showEMA ? ema_value : na, title='EMA', color=i_emaColor, linewidth=i_emaWidth)

// RSI Lines
plot(i_show_rsi_25 ? g_cached_rsi25 : na, 'RSI 25', color.new(#00bcd4, 0), 1, plot.style_line)
plot(i_show_rsi_30 ? g_cached_rsi30 : na, 'RSI 30', color.new(color.red, 0), 1, plot.style_line)
plot(i_show_rsi_50 ? g_cached_rsi50 : na, 'RSI 50', color.new(color.gray, 0), 1, plot.style_line)
plot(i_show_rsi_70 ? g_cached_rsi70 : na, 'RSI 70', color.new(color.green, 0), 1, plot.style_line)
plot(i_show_rsi_75 ? g_cached_rsi75 : na, 'RSI 75', color.new(#ff5722, 0), 1, plot.style_line)
plot(i_show_rsi_custom ? g_cached_rsi_custom : na, 'RSI Custom', i_rsi_custom_color, 2, plot.style_line)

// === TABELA RSI MTF ===
bool show_col1 = i_table_show_col1
bool show_col2 = i_table_show_col2
int total_cols = 2
int col_p1 = na
if show_col1
    col_p1 := total_cols
    total_cols += 1
int col_p2 = na
if show_col2
    col_p2 := total_cols
    total_cols += 1

pos_rsi = switch i_table_position
    'top_right' => position.top_right
    'top_left' => position.top_left
    'bottom_right' => position.bottom_right
    => position.bottom_left


if i_table_show and barstate.islast
    bool positionChanged = i_table_position != g_prev_table_position
    int current_col_config = (show_col1 ? 1 : 0) + (show_col2 ? 2 : 0)
    if (current_col_config != g_prev_col_config or positionChanged) and not na(g_rsi_table)
        table.delete(g_rsi_table)
        g_rsi_table := na
    g_prev_col_config := current_col_config
    g_prev_table_position := i_table_position
    if na(g_rsi_table)
        g_rsi_table := table.new(pos_rsi, total_cols, MAX_TABLE_ROWS, bgcolor=color.new(#282a36, 0), border_width=0, frame_width=2, frame_color=color.new(#6272a4, 0))
        f_table_seed_cells(g_rsi_table, total_cols, MAX_TABLE_ROWS)
    color tableHeaderBg = color.new(#44475a, 0)
    color tableHeaderText = color.new(#bd93f9, 0)
    f_table_set_cell(g_rsi_table, 0, 0, '  TF  ', tableHeaderBg, tableHeaderText, size.small)
    f_table_set_cell(g_rsi_table, 1, 0, '    RSI     ', tableHeaderBg, tableHeaderText, size.small)
    if show_col1
        f_table_set_cell(g_rsi_table, col_p1, 0, '        ' + str.tostring(i_table_col1_level, '#') + '        ', tableHeaderBg, tableHeaderText, size.small)
    if show_col2
        f_table_set_cell(g_rsi_table, col_p2, 0, '        ' + str.tostring(i_table_col2_level, '#') + '        ', tableHeaderBg, tableHeaderText, size.small)
    for i = 0 to RSI_MTF_SLOT_COUNT - 1
        RSISlot slot = g_rsi_slots.get(i)
        rsi_val = slot.rsi
        [bg, txt_clr] = f_rsi_color(rsi_val)
        f_table_set_cell(g_rsi_table, 0, i + 1, f_rsi_tf_display(i), bg, txt_clr, size.small)
        f_table_set_cell(g_rsi_table, 1, i + 1, na(rsi_val) ? '—' : str.tostring(rsi_val, '#.##'), bg, txt_clr, size.normal)
        if show_col1
            p1 = f_rsi_calc_price(i_table_col1_level, slot.close, slot.auc, slot.adc)
            bg1 = (not na(rsi_val) and rsi_val <= i_table_col1_level) ? color.new(#3de1c7, 20) : color.new(#282a36, 0)
            txt1 = (not na(rsi_val) and rsi_val <= i_table_col1_level) ? color.new(#282a36, 0) : color.new(#f8f8f2, 0)
            f_table_set_cell(g_rsi_table, col_p1, i + 1, na(p1) ? '—' : str.tostring(p1, format.mintick), bg1, txt1, size.normal)
        if show_col2
            p2 = f_rsi_calc_price(i_table_col2_level, slot.close, slot.auc, slot.adc)
            bg2 = (not na(rsi_val) and rsi_val >= i_table_col2_level) ? color.new(#ff4d8d, 20) : color.new(#282a36, 0)
            txt2 = (not na(rsi_val) and rsi_val >= i_table_col2_level) ? color.new(#282a36, 0) : color.new(#f8f8f2, 0)
            f_table_set_cell(g_rsi_table, col_p2, i + 1, na(p2) ? '—' : str.tostring(p2, format.mintick), bg2, txt2, size.normal)





// ═══════════════════════════════════════════════════════════════════════════
// 📊 LÓGICA ICT - HTF CANDLES
// ═══════════════════════════════════════════════════════════════════════════
int cnt = 0
int last = f_htf_enabled()
int offset = settings.offset

// [FIX v1.2.3] Pré-calcular triggers de tempo para evitar warnings do compilador
bool new1 = ta.change(time(htf1.settings.htf)) > 0
bool new2 = ta.change(time(htf2.settings.htf)) > 0
bool new3 = ta.change(time(htf3.settings.htf)) > 0
bool new4 = ta.change(time(htf4.settings.htf)) > 0
bool new5 = ta.change(time(htf5.settings.htf)) > 0
bool new6 = ta.change(time(htf6.settings.htf)) > 0

array<bool> is_new_arr = array.from(new1, new2, new3, new4, new5, new6)
array<bool> current_shows = array.from(SettingsHTF1.show, SettingsHTF2.show, SettingsHTF3.show, SettingsHTF4.show, htf5_show, htf6_show)

for i = 0 to 5
    CandleSet cs = g_htfs.get(i)
    if f_can_render_htf(current_shows.get(i), cs.settings.htf, cnt, last)
        [nextCnt, nextOffset] = f_process_htf(cs, is_new_arr.get(i), cnt, last, offset)
        cnt := nextCnt
        offset := nextOffset

if not settings.trace_show or cnt == 0
    f_clear_trace()
else if not settings.label_show
    f_clear_trace_labels()

f_cleanup_candle_set(CandleSet candleSet) =>
    if candleSet.candles.size() > 0
        for i = candleSet.candles.size() - 1 to 0
            c = candleSet.candles.get(i)
            box.delete(c.body)
            line.delete(c.wick_up)
            line.delete(c.wick_down)
            if not na(c.dow_label)
                c.dow_label.delete()
        candleSet.candles.clear()
    if candleSet.imbalances.size() > 0
        for i = candleSet.imbalances.size() - 1 to 0
            box.delete(candleSet.imbalances.get(i).b)
        candleSet.imbalances.clear()
    candleSet.ClearHtfLabels()
// [FIX v1.2.2] Garbage collection ao desativar HTF
for i = 0 to 5
    CandleSet cs = g_htfs.get(i)
    bool is_showing = current_shows.get(i)
    if not is_showing and g_prev_shows.get(i)
        f_cleanup_candle_set(cs)
    g_prev_shows.set(i, is_showing)

// [FIX v1.2.0] Garbage collection: limpa tabela RSI quando feature desligada
if not i_table_show and not na(g_rsi_table)
    table.delete(g_rsi_table)
    g_rsi_table := na
    g_prev_col_config := 0
    g_prev_table_position := ''
// ═══════════════════════════════════════════════════════════════════════════
// 🔍 DEBUG MODE
// ═══════════════════════════════════════════════════════════════════════════
// [FIX v1.2.0] Protegido contra NaN — usa nz() e na() checks

if i_fvg_debug
    if na(g_debug_table)
        g_debug_table := table.new(position.bottom_left, 2, 8, bgcolor=color.new(#282a36, 0), border_width=2, border_color=color.new(#bd93f9, 0))
        table.cell(g_debug_table, 0, 0, '🔍 FVG Debug V1.2.12', text_color=color.new(#f8f8f2, 0), bgcolor=color.new(#6272a4, 0))
        table.merge_cells(g_debug_table, 0, 0, 1, 0)
    if barstate.islast
        activeCount = g_active_fvgs.size()
        table.cell(g_debug_table, 0, 1, 'FVGs Ativos:', text_color=color.new(#6272a4, 0), text_size=size.small)
        table.cell(g_debug_table, 1, 1, str.tostring(activeCount)+'/'+str.tostring(i_fvg_max_count), text_color=activeCount>12?color.new(#ffb86c, 0):color.new(#50fa7b, 0), text_size=size.small)
        table.cell(g_debug_table, 0, 2, 'Modo:', text_color=color.new(#6272a4, 0), text_size=size.small)
        table.cell(g_debug_table, 1, 2, i_fvg_auto_threshold?'📊 ATR':(i_fvg_adaptive_tf?'⚙️ Adaptativo':'📍 Fixo'), text_color=color.new(#f8f8f2, 0), bgcolor=color.new(#44475a, 0), text_size=size.small)
        table.cell(g_debug_table, 0, 3, 'Threshold:', text_color=color.new(#6272a4, 0), text_size=size.small)
        table.cell(g_debug_table, 1, 3, na(thresholdUsed) ? '—' : str.tostring(nz(thresholdUsed, 0)*100,'#.###')+'%', text_color=color.new(#f8f8f2, 0), text_size=size.small)
        table.cell(g_debug_table, 0, 4, 'ATR('+str.tostring(i_atr_period)+'):', text_color=color.new(#6272a4, 0), text_size=size.small)
        table.cell(g_debug_table, 1, 4, na(atrValue) ? '—' : str.tostring(atrValue, format.mintick), text_color=color.new(#8be9fd, 0), text_size=size.small)
        table.cell(g_debug_table, 0, 5, 'Mitigação:', text_color=color.new(#6272a4, 0), text_size=size.small)
        table.cell(g_debug_table, 1, 5, i_fvg_mitigation_type, text_color=color.new(#50fa7b, 0), text_size=size.tiny)
        table.cell(g_debug_table, 0, 6, 'TF Seconds:', text_color=color.new(#6272a4, 0), text_size=size.small)
        table.cell(g_debug_table, 1, 6, str.tostring(g_tf_seconds), text_color=color.new(#f8f8f2, 0), text_size=size.small)
        table.cell(g_debug_table, 0, 7, 'Último FVG:', text_color=color.new(#6272a4, 0), text_size=size.small)
        if activeCount > 0
            lastFvg = g_active_fvgs.get(0)
            fvgSize = math.abs(lastFvg.top - lastFvg.bottom)
            fvgPct = (fvgSize / math.max(lastFvg.bottom, syminfo.mintick)) * 100
            table.cell(g_debug_table, 1, 7, (lastFvg.isBull?'🟢 Bull':'🔴 Bear')+' ('+str.tostring(fvgPct,'#.##')+'%)', text_color=lastFvg.isBull?color.new(#50fa7b, 0):color.new(#ff5555, 0), text_size=size.tiny)
        else
            table.cell(g_debug_table, 1, 7, 'Nenhum', text_color=color.new(#6272a4, 0), text_size=size.small)
else if not i_fvg_debug and not na(g_debug_table)
    g_debug_table.delete()
    g_debug_table := na

// ═══════════════════════════════════════════════════════════════════════════
// 🔔 ALERTAS
// ═══════════════════════════════════════════════════════════════════════════
bool new_bull_fvg = didAddBullFvg
bool new_bear_fvg = didAddBearFvg

alertcondition(new_bull_fvg, title="Novo FVG de Alta", message="🟢 Novo FVG de Alta Detectado!")
alertcondition(new_bear_fvg, title="Novo FVG de Baixa", message="🔴 Novo FVG de Baixa Detectado!")
alertcondition(ta.crossover(rsi_current, 70.0), title="RSI Sobrecomprado (>70)", message="⚠️ RSI Cruzou 70 para cima")
alertcondition(ta.crossunder(rsi_current, 30.0), title="RSI Sobrevendido (<30)", message="⚠️ RSI Cruzou 30 para baixo")

Overview

ICT + FVG + RSI aggregates price imbalance context across up to six configurable timeframes while maintaining a dynamic multi-timeframe RSI analysis pipeline. The script projects RSI target level thresholds and displays an optional dashboard table for quick market structure and momentum context. Users can configure ATR thresholds, timezone offsets, HTF security data modes, and candle visual styles. Depending on HTF request settings, data reads may introduce lookahead bias.

Details

Published by

Allysson-Rodrigues on GitHub

Type

Indicator, Pine Script v6

Draws

On the price chart

Category

SMC / ICT / price action, Oscillators & momentum, Support & resistance

Markets

Any symbol

Timeframes

Any timeframe

Risk Notes

  • Reads higher-timeframe data with lookahead on, so the history can show signals that were not there in real time

Topics

pinescriptpinescript-indicatorsalgorithmic-tradingindicatorstechnical-analysistradingviewtradingview-indicators

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
HTF 1booltrueshow
htf_1timeframe5htf_1
max_displayint10max_display
HTF 2booltrueshow
htf_2timeframe15htf_2
max_displayint8max_display
HTF 3booltrueshow
htf_3timeframe60htf_3
max_displayint6max_display
HTF 4booltrueshow
htf_4timeframe240htf_4
max_displayint5max_display
Show all 40 settings
SettingTypeDefaultVariable
HTF 5booltrueshow
htf_5timeframe1Dhtf_5
max_displayint5max_display
HTF 6booltrueshow
htf_6timeframe1Whtf_6
max_displayint3max_display
Limite HTFsint3max_sets
Fuso HoráriostringAmerica/Sao_Pauloi_timezone
Daily Auto (>=1H)booltruei_auto_daily
Weekly Auto (>=4H)booltruei_auto_weekly
Modo Dados HTFstringConfirmadoi_htf_security_mode
Body Bull/Bearcolorcolor.new(#089981, 70)bull_body
bear_bodycolorcolor.new(#f23645, 70)bear_body
Bordercolorcolor.new(#000000,70)bull_border
bear_bordercolorcolor.new(#000000,70)bear_border
Wickcolorcolor.new(color.black, 70)bull_wick
bear_wickcolorcolor.new(color.black, 70)bear_wick
Offsetint30offset
Bufferint1buffer
HTF Bufferint5htf_buffer
Widthint1width
HTF Labelbooltruehtf_label_show
htf_label_colorcolorcolor.new(#000000, 0)htf_label_color
htf_label_sizestringsize.normalhtf_label_size
PositionstringToplabel_position
AlignmentstringFollow Candleslabel_alignment
Timerbooltruehtf_timer_show
htf_timer_colorcolorcolor.new(color.black, 10)htf_timer_color
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

This overlay indicator scores liquidity-sweep reversals on the price chart. It looks for a swing high or low violation followed by a recovery close, then grades optional conditions such as RSI and structure into a 0–2 quality score with separate long and short

Indicator v6 SMC / ICT / price actionOscillators & momentum
Updated yesterday · MIT GitHub repository of Reversal Engine Score v1 [WavesUnchained]

Cerebr Bot

by jerryinyang
5 stars

Cerebr Bot is an overlay indicator that maps order blocks and fair value gaps on the price chart. It validates order blocks using a candle-count range, a no-body buffer, and an optional reversal-magnitude filter based on candle body or the selected method. RSI

Indicator v4 SMC / ICT / price actionOscillators & momentum
Updated 2 years ago · None stated GitHub repository of Cerebr Bot
3 stars

Pieki Algo is an all-in-one technical analysis indicator that overlays trading signals, trend visualizers, and Smart Money Concepts onto the main price chart. It plots buy and sell signals based on configurable sensitivity settings, colors candles by trend or

Indicator v5 SMC / ICT / price actionTrend Repaints
Updated 12 months ago · None stated GitHub repository of Pieki Algo Signals & Overlays
2 stars

This indicator plots demand and supply zones computed from volume profile with 30-56 bins, fair value gaps on 4H, and equal highs/lows. It draws a magic line using ATR(14) x 1.618 golden ratio anchored to the 00:00 UTC close. Signals identify liquidity vacuum

Indicator v6 SMC / ICT / price actionVolume
Updated 6 months ago · None stated GitHub repository of Ghost Zone v9 — GTI Signals v18a

More indicators

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers