1Copy the scriptPress Copy script above, or the copy button on the code below.
2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
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.
Multi-timeframe Fair Value Gap detection
Higher timeframe candle projection on price chart
Multi-timeframe RSI table and target level projections
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
HTF 1
bool
true
show
htf_1
timeframe
5
htf_1
max_display
int
10
max_display
HTF 2
bool
true
show
htf_2
timeframe
15
htf_2
max_display
int
8
max_display
HTF 3
bool
true
show
htf_3
timeframe
60
htf_3
max_display
int
6
max_display
HTF 4
bool
true
show
htf_4
timeframe
240
htf_4
max_display
int
5
max_display
Show all 40 settings
Setting
Type
Default
Variable
HTF 5
bool
true
show
htf_5
timeframe
1D
htf_5
max_display
int
5
max_display
HTF 6
bool
true
show
htf_6
timeframe
1W
htf_6
max_display
int
3
max_display
Limite HTFs
int
3
max_sets
Fuso Horário
string
America/Sao_Paulo
i_timezone
Daily Auto (>=1H)
bool
true
i_auto_daily
Weekly Auto (>=4H)
bool
true
i_auto_weekly
Modo Dados HTF
string
Confirmado
i_htf_security_mode
Body Bull/Bear
color
color.new(#089981, 70)
bull_body
bear_body
color
color.new(#f23645, 70)
bear_body
Border
color
color.new(#000000,70)
bull_border
bear_border
color
color.new(#000000,70)
bear_border
Wick
color
color.new(color.black, 70)
bull_wick
bear_wick
color
color.new(color.black, 70)
bear_wick
Offset
int
30
offset
Buffer
int
1
buffer
HTF Buffer
int
5
htf_buffer
Width
int
1
width
HTF Label
bool
true
htf_label_show
htf_label_color
color
color.new(#000000, 0)
htf_label_color
htf_label_size
string
size.normal
htf_label_size
Position
string
Top
label_position
Alignment
string
Follow Candles
label_alignment
Timer
bool
true
htf_timer_show
htf_timer_color
color
color.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.
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
This indicator identifies Smart Money Concepts market structure across higher and lower timeframes directly on the main price chart. It detects Break of Structure, Market Structure Shifts, Order Blocks, and regular or inverse Fair Value Gaps. Entry signals are
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
This indicator identifies market impulses, consolidation zones, and potential stop levels directly on the price chart. It evaluates price movement using dual ATR ratios, tight-range consolidation filters, stochastic momentum, and candle body-to-wick proportion
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
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