Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //@version=6
- // =====================================================================
- // Strong Start — RVOL table
- // A glanceable, live-updating table of Relative Volume for up to 30
- // symbols, sorted highest RVOL first. Built for spotting "strong starts":
- // unusually heavy volume early in the day + an up move.
- //
- // RVOL = (today's volume so far) / (average daily volume over N days)
- // shown as % by default — 100% = a full average day's volume already
- // traded; ~8% in the first few minutes is a strong start. Same number
- // your "Simple Volume" indicator and the screener use.
- //
- // Pulls DAILY data internally, so it works on ANY chart timeframe
- // (daily or intraday) and ANY market (US, NSE, …).
- //
- // NOTE: Pine cannot read your watchlist — type your symbols in Settings
- // as EXCHANGE:TICKER (e.g. NASDAQ:NVDA, NSE:RELIANCE). Blank = skip.
- // Max 40 symbols (Pine's cross-symbol limit); 40 slots provided.
- // =====================================================================
- indicator("Strong Start — RVOL table", overlay = true, max_bars_back = 500)
- // ---- settings ----
- int lookback = input.int(20, "Avg volume lookback (days)", minval = 1, maxval = 100, tooltip = "20 is a steadier baseline than 10 (less skewed by a recent volume spike). Set 10 to match the screener exactly.", group = "Settings")
- string showAs = input.string("Percent", "Show RVOL as", options = ["Percent", "Ratio"], group = "Settings")
- float rvolFlag = input.float(8.0, "Flag when RVOL% ≥", step = 0.5, group = "Settings")
- float chgFlag = input.float(1.5, "Move threshold (Chg% for green)", step = 0.1, tooltip = "A high-volume row colors green when up at least this, red when down by it, amber in between (volume but no move).", group = "Settings")
- int gatePct = input.int(50, "Light up top % by RVOL", minval = 10, maxval = 100, step = 5, tooltip = "Only the heaviest-volume X% of the list gets a colored row (a rank, so it self-calibrates through the day and catches mid-day RVOL risers). Lower = stricter / fewer, hotter names.", group = "Settings")
- string posStr = input.string("Top Right", "Table position", options = ["Top Right", "Top Left", "Center Right", "Center Left", "Bottom Right", "Bottom Left"], group = "Display")
- int szStr = input.int(1, "Text size (1 tiny … 4 large)", minval = 1, maxval = 4, tooltip = "Tiny fits the most rows on screen.", group = "Display")
- string theme = input.string("Dark", "Theme", options = ["Dark", "Light"], tooltip = "Switch to Light if your chart background is light — it darkens the table text so it stays readable. Dark is the default.", group = "Display")
- // ---- symbols (EXCHANGE:TICKER; blank = skip) ----
- string g = "Symbols"
- string s01 = input.string("NSE:JASH", "01", group = g)
- string s02 = input.string("NSE:MOTILALOFS", "02", group = g)
- string s03 = input.string("NSE:DIACABS", "03", group = g)
- string s04 = input.string("NSE:FIVESTAR", "04", group = g)
- string s05 = input.string("NSE:TARSONS", "05", group = g)
- string s06 = input.string("NSE:GOLDIAM", "06", group = g)
- string s07 = input.string("NSE:AVALON", "07", group = g)
- string s08 = input.string("NSE:OLECTRA", "08", group = g)
- string s09 = input.string("NSE:TEJASNET", "09", group = g)
- string s10 = input.string("NSE:NELCO", "10", group = g)
- string s11 = input.string("NSE:ADANIPOWER", "11", group = g)
- string s12 = input.string("NSE:EMMVEE", "12", group = g)
- string s13 = input.string("NSE:SKYGOLD", "13", group = g)
- string s14 = input.string("NSE:BDL", "14", group = g)
- string s15 = input.string("NSE:TARIL", "15", group = g)
- string s16 = input.string("NSE:RAYMOND", "16", group = g)
- string s17 = input.string("NSE:DATAPATTNS", "17", group = g)
- string s18 = input.string("NSE:SUPRIYA", "18", group = g)
- string s19 = input.string("NSE:BHARATCOAL", "19", group = g)
- string s20 = input.string("NSE:IFCI", "20", group = g)
- string s21 = input.string("NSE:CMPDI", "21", group = g)
- string s22 = input.string("NSE:STALLION", "22", group = g)
- string s23 = input.string("NSE:RAIN", "23", group = g)
- string s24 = input.string("NSE:BALAMINES", "24", group = g)
- string s25 = input.string("NSE:EXICOM", "25", group = g)
- string s26 = input.string("NSE:EPACKPEB", "26", group = g)
- string s27 = input.string("", "27", group = g)
- string s28 = input.string("", "28", group = g)
- string s29 = input.string("", "29", group = g)
- string s30 = input.string("", "30", group = g)
- string s31 = input.string("", "31", group = g)
- string s32 = input.string("", "32", group = g)
- string s33 = input.string("", "33", group = g)
- string s34 = input.string("", "34", group = g)
- string s35 = input.string("", "35", group = g)
- string s36 = input.string("", "36", group = g)
- string s37 = input.string("", "37", group = g)
- string s38 = input.string("", "38", group = g)
- string s39 = input.string("", "39", group = g)
- string s40 = input.string("", "40", group = g)
- // ---- per-symbol daily RVOL + change% (vs previous close) ----
- // Computed on DAILY resolution in each symbol's own context. On the live
- // (incomplete) day, `volume` is the running cumulative volume so far today;
- // `volume[1]` back are completed days. So this updates in real time.
- calcDaily(simple int N) =>
- float avgv = ta.sma(volume[1], N) // avg of last N completed days
- float rv = avgv > 0 ? volume / avgv : na // today-so-far / avg day (ratio)
- float cg = close[1] != 0 ? (close - close[1]) / close[1] * 100 : na // % vs prev close
- [rv, cg]
- req(string sym) =>
- string s = sym == "" ? syminfo.tickerid : sym // placeholder for blanks
- request.security(s, "1D", calcDaily(lookback))
- [rv01, cg01] = req(s01)
- [rv02, cg02] = req(s02)
- [rv03, cg03] = req(s03)
- [rv04, cg04] = req(s04)
- [rv05, cg05] = req(s05)
- [rv06, cg06] = req(s06)
- [rv07, cg07] = req(s07)
- [rv08, cg08] = req(s08)
- [rv09, cg09] = req(s09)
- [rv10, cg10] = req(s10)
- [rv11, cg11] = req(s11)
- [rv12, cg12] = req(s12)
- [rv13, cg13] = req(s13)
- [rv14, cg14] = req(s14)
- [rv15, cg15] = req(s15)
- [rv16, cg16] = req(s16)
- [rv17, cg17] = req(s17)
- [rv18, cg18] = req(s18)
- [rv19, cg19] = req(s19)
- [rv20, cg20] = req(s20)
- [rv21, cg21] = req(s21)
- [rv22, cg22] = req(s22)
- [rv23, cg23] = req(s23)
- [rv24, cg24] = req(s24)
- [rv25, cg25] = req(s25)
- [rv26, cg26] = req(s26)
- [rv27, cg27] = req(s27)
- [rv28, cg28] = req(s28)
- [rv29, cg29] = req(s29)
- [rv30, cg30] = req(s30)
- [rv31, cg31] = req(s31)
- [rv32, cg32] = req(s32)
- [rv33, cg33] = req(s33)
- [rv34, cg34] = req(s34)
- [rv35, cg35] = req(s35)
- [rv36, cg36] = req(s36)
- [rv37, cg37] = req(s37)
- [rv38, cg38] = req(s38)
- [rv39, cg39] = req(s39)
- [rv40, cg40] = req(s40)
- // ---- display ----
- var table t = table.new(
- posStr == "Top Right" ? position.top_right : posStr == "Top Left" ? position.top_left : posStr == "Center Right" ? position.middle_right : posStr == "Center Left" ? position.middle_left : posStr == "Bottom Right" ? position.bottom_right : position.bottom_left,
- 3, 41, border_width = 1, frame_width = 1, frame_color = color.new(color.gray, 40))
- sz = szStr == 1 ? size.tiny : szStr == 2 ? size.small : szStr == 3 ? size.normal : size.large
- // Theme palette. Dark (default) keeps the original look exactly; Light swaps the
- // text/header to dark shades so nothing washes out on a light chart background.
- bool light = theme == "Light"
- color cText = light ? color.black : color.white // symbol text
- color cUp = light ? #0b8043 : color.lime // up / high RVOL
- color cDown = light ? #cc2222 : color.red // down
- color cFlat = light ? #c77800 : color.orange // mid RVOL / flat
- color cMuted = light ? #5b616b : color.silver // low RVOL
- color cNa = light ? #9aa0aa : color.gray // no value
- color cHdrBg = light ? color.new(#d1d4dc, 0) : color.new(color.gray, 15)
- color cHdrTx = light ? color.black : color.white
- fmtRvol(float rv) =>
- na(rv) ? "–" : showAs == "Percent" ? str.tostring(rv * 100, "#") + "%" : str.tostring(rv, "#.00")
- if barstate.islast
- names = array.from(s01, s02, s03, s04, s05, s06, s07, s08, s09, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20, s21, s22, s23, s24, s25, s26, s27, s28, s29, s30, s31, s32, s33, s34, s35, s36, s37, s38, s39, s40)
- rvs = array.from(rv01, rv02, rv03, rv04, rv05, rv06, rv07, rv08, rv09, rv10, rv11, rv12, rv13, rv14, rv15, rv16, rv17, rv18, rv19, rv20, rv21, rv22, rv23, rv24, rv25, rv26, rv27, rv28, rv29, rv30, rv31, rv32, rv33, rv34, rv35, rv36, rv37, rv38, rv39, rv40)
- cgs = array.from(cg01, cg02, cg03, cg04, cg05, cg06, cg07, cg08, cg09, cg10, cg11, cg12, cg13, cg14, cg15, cg16, cg17, cg18, cg19, cg20, cg21, cg22, cg23, cg24, cg25, cg26, cg27, cg28, cg29, cg30, cg31, cg32, cg33, cg34, cg35, cg36, cg37, cg38, cg39, cg40)
- // header
- table.cell(t, 0, 0, "Symbol", text_color = cHdrTx, bgcolor = cHdrBg, text_size = sz)
- table.cell(t, 1, 0, "RVOL", text_color = cHdrTx, bgcolor = cHdrBg, text_size = sz)
- table.cell(t, 2, 0, "Chg%", text_color = cHdrTx, bgcolor = cHdrBg, text_size = sz)
- // valid (non-blank, has a value) indices
- idx = array.new<int>()
- for i = 0 to array.size(names) - 1
- if array.get(names, i) != "" and not na(array.get(rvs, i))
- array.push(idx, i)
- cnt = array.size(idx)
- // sort indices by RVOL descending (simple selection sort)
- for a = 0 to cnt - 2
- for b = a + 1 to cnt - 1
- ia = array.get(idx, a)
- ib = array.get(idx, b)
- if array.get(rvs, ib) > array.get(rvs, ia)
- array.set(idx, a, ib)
- array.set(idx, b, ia)
- // clear all data rows first — use table.clear (not empty cells) so unused
- // rows collapse to zero height instead of padding the table off-screen.
- table.clear(t, 0, 1, 2, 40)
- // VOLUME LEADS: only the heaviest-volume slice of the list (top `gatePct`%)
- // is eligible for a colored row. A rank, not an absolute %, so it
- // self-calibrates through the day and catches names whose RVOL climbs mid-
- // session. Movers on low relative volume are deliberately left plain.
- int hiCount = math.max(1, int(math.round(cnt * gatePct / 100.0)))
- // fill sorted rows
- for r = 0 to cnt - 1
- i = array.get(idx, r)
- nm = array.get(names, i)
- parts = str.split(nm, ":")
- disp = array.size(parts) > 1 ? array.get(parts, 1) : nm
- rv = array.get(rvs, i)
- cg = array.get(cgs, i)
- float rvPct = na(rv) ? na : rv * 100
- bool isHigh = r < hiCount // among the heaviest-volume names right now
- // Brightness scales with RVOL rank — heaviest volume = most opaque, so the
- // explosive names pop hardest and ZEEL-type moderate-RVOL movers show as a
- // softer shade. transp 0 = solid, 100 = invisible.
- int tp = 45 + int(35.0 * r / math.max(1, hiCount - 1))
- color rvCol = na(rvPct) ? cNa : rvPct >= rvolFlag ? cUp : rvPct >= rvolFlag * 0.6 ? cFlat : cMuted
- // Among the high-volume names, color by the move:
- // green = up by chgFlag+ → big guys buying and price following (target)
- // amber = flat → volume but no move yet (early / churn)
- // red = down by chgFlag+ → volume but being sold (skip)
- color rowBg = na
- if isHigh and not na(cg)
- rowBg := cg >= chgFlag ? color.new(color.green, tp) : cg <= -chgFlag ? color.new(color.red, tp) : color.new(color.orange, tp)
- color cgCol = na(cg) ? cNa : cg >= 0 ? cUp : cDown
- table.cell(t, 0, r + 1, disp, text_color = cText, bgcolor = rowBg, text_size = sz)
- table.cell(t, 1, r + 1, fmtRvol(rv), text_color = rvCol, bgcolor = rowBg, text_size = sz)
- table.cell(t, 2, r + 1, na(cg) ? "–" : str.tostring(cg, "#.0") + "%", text_color = cgCol, bgcolor = rowBg, text_size = sz)
Add Comment
Please, Sign In to add comment