//@version=5
indicator("Kurutoga Screener Timing Fix", overlay=true, max_lines_count=500, max_labels_count=500, max_boxes_count=500)
// --- Inputs ---
// Screener Display
col_width = input.float(5, title="Column Width (%)", group="Screener Display")
scr_numb = input.int(1, title="Screen #", tooltip="1 - rightmost screener", minval=1, group="Screener Display")
// Signal Parameters
baseLength = input.int(14, title="Base Length", minval=1, group="Signal Parameters")
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
// SYMBOLS - Enable Flags
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
u01 = input.bool(true, title="", group="Symbols", inline="s01")
u02 = input.bool(true, title="", group="Symbols", inline="s02")
u03 = input.bool(true, title="", group="Symbols", inline="s03")
u04 = input.bool(true, title="", group="Symbols", inline="s04")
u05 = input.bool(true, title="", group="Symbols", inline="s05")
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
// SYMBOLS - Tickers
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
s01 = input.symbol("NVDA", group="Symbols", inline="s01")
s02 = input.symbol("AAPL", group="Symbols", inline="s02")
s03 = input.symbol("TSLA", group="Symbols", inline="s03")
s04 = input.symbol("AMZN", group="Symbols", inline="s04")
s05 = input.symbol("GOOGL", group="Symbols", inline="s05")
// --- Script Name ---
string SCRIPT_NAME = "KURU"
// --- Colors Definition ---
colorNeutralBG = color.new(color.gray, 0)
colorSignalTXT = color.new(color.white, 0)
colorBullishBG = color.new(color.green, 75)
colorBearishBG = color.new(color.red, 75)
// --- Helper function for symbol name ---
_only_symbol_name(s) =>
string _sym = ""
if not na(s)
if str.contains(s, ":")
_sym := array.get(str.split(s, ':'), 1)
else
_sym := s
_sym := str.replace_all(_sym, "_", "-")
_sym
// --- Helper function to calculate midpoint ---
_calc_midpoint(h, l) =>
(h + l) / 2
// --- Improved Kurutoga Signal Calculation with Timing Fix ---
calculateKurutogaSignalWithTimingFix() =>
// Define the three lengths
len1 = baseLength
len2 = baseLength * 2
len3 = baseLength * 4
// Calculations for Length 1 (baseLength)
high1 = ta.highest(high, len1)
low1 = ta.lowest(low, len1)
midpoint1 = _calc_midpoint(high1, low1)
divergence1 = close - midpoint1
// Calculations for Length 2 (2 * baseLength)
high2 = ta.highest(high, len2)
low2 = ta.lowest(low, len2)
midpoint2 = _calc_midpoint(high2, low2)
divergence2 = close - midpoint2
// Calculations for Length 3 (4 * baseLength)
high3 = ta.highest(high, len3)
low3 = ta.lowest(low, len3)
midpoint3 = _calc_midpoint(high3, low3)
divergence3 = close - midpoint3
// Signal Logic - Using explicit bar indexing to ensure proper timing
// Bullish condition: All three are positive, and weren't on the previous bar
allPositive = divergence1 > 0 and divergence2 > 0 and divergence3 > 0
bullishSignal = allPositive and not allPositive[1]
// Bearish condition: All three are negative, and weren't on the previous bar
allNegative = divergence1 < 0 and divergence2 < 0 and divergence3 < 0
bearishSignal = allNegative and not allNegative[1]
// Signal Determination
string signalCode = "---"
if bullishSignal
signalCode := "BUY"
else if bearishSignal
signalCode := "SELL"
// Append percentage change if a signal exists
if signalCode != "---"
// Use the current bar's close and previous bar's close for percentage change
float pct_change = ((close - close[1]) / close[1]) * 100.0
string pct_change_str = str.format("{0,number,#.##}%", pct_change)
signalCode := signalCode + " " + pct_change_str
signalCode
// --- Security Calls with explicit bar indexing ---
s_expr = calculateKurutogaSignalWithTimingFix()
// Use lookahead = barmerge.lookahead_off to ensure we don't get future data
signal01 = request.security(s01, timeframe.period, s_expr, lookahead = barmerge.lookahead_off)
signal02 = request.security(s02, timeframe.period, s_expr, lookahead = barmerge.lookahead_off)
signal03 = request.security(s03, timeframe.period, s_expr, lookahead = barmerge.lookahead_off)
signal04 = request.security(s04, timeframe.period, s_expr, lookahead = barmerge.lookahead_off)
signal05 = request.security(s05, timeframe.period, s_expr, lookahead = barmerge.lookahead_off)
// --- ARRAYS ---
var s_arr = array.new_string(5)
var u_arr = array.new_bool(5)
var signal_arr = array.new_string(5)
if barstate.isfirst
array.set(s_arr, 0, _only_symbol_name(s01))
array.set(u_arr, 0, u01)
array.set(s_arr, 1, _only_symbol_name(s02))
array.set(u_arr, 1, u02)
array.set(s_arr, 2, _only_symbol_name(s03))
array.set(u_arr, 2, u03)
array.set(s_arr, 3, _only_symbol_name(s04))
array.set(u_arr, 3, u04)
array.set(s_arr, 4, _only_symbol_name(s05))
array.set(u_arr, 4, u05)
// Update signal_arr on every bar
array.set(signal_arr, 0, signal01)
array.set(signal_arr, 1, signal02)
array.set(signal_arr, 2, signal03)
array.set(signal_arr, 3, signal04)
array.set(signal_arr, 4, signal05)
// --- Table Drawing ---
var tbl = table.new(position.top_right, scr_numb > 1 ? 3 : 2, 10, frame_color=#151715, frame_width=1, border_width=2, border_color=color.new(color.white, 100))
if barstate.islast
table.clear(tbl, 0, 0, scr_numb > 1 ? 2 : 1, 9)
var int countBuy = 0
var int countSell = 0
countBuy := 0
countSell := 0
int rounded_col_width = int(math.round(col_width))
string header_text = SCRIPT_NAME + " - " + str.tostring(scr_numb)
table.cell(tbl, 0, 0, 'Symbol', width=rounded_col_width, text_halign=text.align_center, bgcolor=color.gray, text_color=color.white, text_size=size.small)
table.cell(tbl, 1, 0, header_text, width=rounded_col_width, text_halign=text.align_center, bgcolor=color.gray, text_color=color.white, text_size=size.small)
if scr_numb > 1
table.cell(tbl, 2, 0, '', width=rounded_col_width * 2 * (scr_numb - 1), text_halign=text.align_center, bgcolor=color.new(color.gray, 100), text_size=size.small)
int visible_row_index = 1
for i = 0 to 4 by 1
if array.get(u_arr, i)
string signal_val_from_array = array.get(signal_arr, i)
string current_signal = na(signal_val_from_array) ? "---" : signal_val_from_array
string ticker_name = array.get(s_arr, i)
color signal_bg_col = colorNeutralBG
if str.contains(current_signal, "BUY")
countBuy += 1
signal_bg_col := colorBullishBG
else if str.contains(current_signal, "SELL")
countSell += 1
signal_bg_col := colorBearishBG
table.cell(tbl, 0, visible_row_index, ticker_name, text_halign=text.align_center, bgcolor=color.gray, text_color=color.white, text_size=size.small, width=rounded_col_width)
table.cell(tbl, 1, visible_row_index, current_signal, text_halign=text.align_center, bgcolor=signal_bg_col, text_color=colorSignalTXT, text_size=size.small, width=rounded_col_width)
visible_row_index += 1
Overview
By assessing divergence consistency across base, double, and quadruple lengths, it identifies synchronized momentum conditions. The results are formatted into an on-chart screener table overlay for up to five user-defined symbols. Configurable settings include the base lookback period, table width, screener position, and individual symbol toggles.
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Column Width (%)
float
5
col_width
Screen #
int
1
scr_numb
Base Length
int
14
baseLength
u01
bool
true
u01
u02
bool
true
u02
u03
bool
true
u03
u04
bool
true
u04
u05
bool
true
u05
s01
symbol
NVDA
s01
s02
symbol
AAPL
s02
s03
symbol
TSLA
s03
s04
symbol
AMZN
s04
Show all 13 settings
Setting
Type
Default
Variable
s05
symbol
GOOGL
s05
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 indicator screens multiple symbols for regular RSI divergence and extreme RSI readings. It compares pivot lows and highs in price with a 14-period RSI, then alerts when price makes a lower low against a higher RSI low, or a higher high against a lower RSI
This indicator implements a full Chanlun pipeline on the price chart. It merges contained bars, marks top and bottom fractals, and connects alternating fractals into strokes. Overlapping strokes form pivot boxes, and MACD area ratios flag trend or consolidatio
This indicator plots calendar-spread context for commodity futures on the price chart. It maps the chart symbol to front and next month contracts automatically, or uses manual symbols, then calculates the spread as a difference or ratio. Fast and slow EMAs, a
This indicator projects a dual-asset momentum matrix visualizer directly onto the price chart overlay. It calculates normalized momentum for the chart symbol using either RSI or Stochastic inputs adjusted by ATR volatility, and compares it against a benchmark
This indicator scans multiple symbols for regular MACD bullish and bearish divergences filtered by long-term trend direction using 150-period and 600-period EMAs. When a divergence aligns with the trend filter, it dispatches a JSON-formatted alert on bar close
This indicator scans a pasted list of symbols for RSI directional momentum and shows matching long and short signals in a table in a separate pane. The user chooses continuous break, continuous retracement, flip break, flip retracement, divergence, or hidden d