Phantom_Flow_Futures_Screener_1.pine
Copy
// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © darup67
//@version=6
// =============================================================================
// PHANTOM FLOW — FUTURES SCREENER (MNQ FOCUS) + BUY CONFIRMATION ENGINE
// Personal-use companion to Phantom Flow Core (getphantomflow.com).
//
// THREE PANELS:
// 1. MNQ FOCUS — Shift + Oscillator across five timeframes, with a
// buy-confirmation score per timeframe.
// 2. FUTURES BOARD — same engine across 30 markets on one scan timeframe.
// 3. BUY CHECKLIST — which confirmations are passing on the CHART symbol.
//
// Pine caps every script at 40 request.security() calls: 5 focus timeframes
// + 30 board slots = 35. The chart-symbol engine runs locally (0 calls).
// =============================================================================
indicator('Phantom Flow Futures Screener', 'PF Futures', overlay = true, max_labels_count = 100)
// -----------------------------------------------------------------------------
// FOCUS PANEL SETTINGS
// -----------------------------------------------------------------------------
G_FOCUS = 'MNQ Focus'
focusSym = input.symbol('CME_MINI:MNQ1!', 'Focus contract', group = G_FOCUS)
showFocus = input.bool(true, 'Show focus panel', group = G_FOCUS)
fTF1 = input.timeframe('5', 'TF 1', group = G_FOCUS, inline = 'f1')
fTF2 = input.timeframe('15', 'TF 2', group = G_FOCUS, inline = 'f1')
fTF3 = input.timeframe('60', 'TF 3', group = G_FOCUS, inline = 'f2')
fTF4 = input.timeframe('240', 'TF 4', group = G_FOCUS, inline = 'f2')
fTF5 = input.timeframe('D', 'TF 5', group = G_FOCUS, inline = 'f2')
focusAlert = input.bool(true, 'Alert on focus signals', group = G_FOCUS, tooltip = 'Fires when the focus contract prints a Shift, Oscillator or Confluence signal on any of the five timeframes.')
focusPos = input.string('Top right', 'Focus panel position', options = ['Top right', 'Top left', 'Bottom right', 'Bottom left', 'Middle right'], group = G_FOCUS)
// -----------------------------------------------------------------------------
// BOARD SETTINGS
// -----------------------------------------------------------------------------
G_BOARD = 'Futures Board'
showBoard = input.bool(true, 'Show futures board', group = G_BOARD)
scanTF = input.timeframe('D', 'Board scan timeframe', group = G_BOARD)
confirmedOnly = input.bool(true, 'Confirmed bars only (no repaint)', group = G_BOARD, tooltip = 'On = read the last CLOSED bar. Off = read the live forming bar, which can flip mid-session. Applies to all panels.')
rowMode = input.string('Signals only', 'Rows to show', options = ['Signals only', 'All markets'], group = G_BOARD)
sigMode = input.string('Any', 'Signal filter', options = ['Any', 'Shift only', 'Oscillator only', 'Confluence only', 'Buy setups only'], group = G_BOARD)
boardPos = input.string('Bottom right', 'Board position', options = ['Top right', 'Top left', 'Bottom right', 'Bottom left', 'Middle right'], group = G_BOARD)
boardAlert = input.bool(true, 'Alert on board signals', group = G_BOARD)
tblSizeInput = input.string('Tiny', 'Text size', options = ['Tiny', 'Small', 'Normal'], group = G_BOARD)
// -----------------------------------------------------------------------------
// BUY CONFIRMATION ENGINE
// -----------------------------------------------------------------------------
G_BUY = 'Buy Confirmations'
minScore = input.int(3, 'Minimum score to flag a buy', minval = 0, maxval = 5, group = G_BUY, tooltip = 'A Shift or Oscillator buy only counts as a setup once this many of the five confirmations pass.')
trendLen = input.int(200, 'Regime EMA length', minval = 10, group = G_BUY)
rsiLen = input.int(14, 'RSI length', minval = 2, group = G_BUY, inline = 'r')
rsiFloor = input.float(50, 'floor', step = 1, group = G_BUY, inline = 'r')
volLen = input.int(20, 'Volume MA length', minval = 2, group = G_BUY, inline = 'v')
volMult = input.float(1.2, 'multiple', step = 0.1, group = G_BUY, inline = 'v')
adxLen = input.int(14, 'ADX length', minval = 2, group = G_BUY, inline = 'a')
adxMin = input.float(20, 'minimum', step = 1, group = G_BUY, inline = 'a')
maxAge = input.int(5, 'Freshness — max bars since flip', minval = 1, group = G_BUY, tooltip = 'Keeps you from grading a trend that is already 40 bars old as a fresh entry.')
rrMult = input.float(2.0, 'Target R multiple', step = 0.5, minval = 0.5, group = G_BUY, tooltip = 'Target = entry + this multiple of (entry − Shift stop).')
G_CHART = 'Chart Symbol Signals'
showChartSignals = input.bool(true, 'Plot buy markers on chart', group = G_CHART)
showChecklist = input.bool(true, 'Show buy checklist panel', group = G_CHART)
showStopLine = input.bool(true, 'Plot Shift stop line', group = G_CHART)
checklistPos = input.string('Middle right', 'Checklist position', options = ['Top right', 'Top left', 'Bottom right', 'Bottom left', 'Middle right'], group = G_CHART)
// -----------------------------------------------------------------------------
// SIGNAL ENGINE SETTINGS (mirror Phantom Flow Core defaults)
// -----------------------------------------------------------------------------
G_SHIFT = 'Phantom Shift'
shiftATR = input.int(10, 'ATR period', minval = 1, group = G_SHIFT)
shiftMult = input.float(3.0, 'Multiplier', step = 0.1, group = G_SHIFT)
shiftBuiltIn = input.bool(true, 'Use built-in ATR', group = G_SHIFT)
G_OSC = 'Phantom Oscillator'
oscType = input.string('SMA', 'MA type', options = ['SMA', 'EMA', 'SMMA (RMA)', 'WMA', 'VWMA'], group = G_OSC)
oscLength = input.int(40, 'MA length', minval = 1, group = G_OSC)
oscLen = input.int(15, 'Oscillator length', minval = 1, group = G_OSC)
oscThresh = input.float(0.5, 'Threshold', step = 0.1, group = G_OSC)
oscLookback = input.int(500, 'Normalisation lookback', minval = 100, maxval = 1000, group = G_OSC, tooltip = 'Core uses 1000. Lower = faster scan across 35 requests, slightly different scaling.')
// -----------------------------------------------------------------------------
// FUTURES UNIVERSE — blank a slot to disable it
// -----------------------------------------------------------------------------
G_B1 = 'Board: Equity Index & Volatility'
b01 = input.symbol('CME_MINI:ES1!', 'Slot 01', group = G_B1)
b02 = input.symbol('CME_MINI:NQ1!', 'Slot 02', group = G_B1)
b03 = input.symbol('CME_MINI:RTY1!', 'Slot 03', group = G_B1)
b04 = input.symbol('CBOT_MINI:YM1!', 'Slot 04', group = G_B1)
b05 = input.symbol('CME:NKD1!', 'Slot 05', group = G_B1)
b06 = input.symbol('CBOE_FUTURES:VX1!', 'Slot 06', group = G_B1)
G_B2 = 'Board: Metals'
b07 = input.symbol('COMEX:GC1!', 'Slot 07', group = G_B2)
b08 = input.symbol('COMEX:SI1!', 'Slot 08', group = G_B2)
b09 = input.symbol('COMEX:HG1!', 'Slot 09', group = G_B2)
b10 = input.symbol('NYMEX:PL1!', 'Slot 10', group = G_B2)
G_B3 = 'Board: Energy'
b11 = input.symbol('NYMEX:CL1!', 'Slot 11', group = G_B3)
b12 = input.symbol('NYMEX:NG1!', 'Slot 12', group = G_B3)
b13 = input.symbol('NYMEX:RB1!', 'Slot 13', group = G_B3)
b14 = input.symbol('NYMEX:HO1!', 'Slot 14', group = G_B3)
b15 = input.symbol('ICEEUR:BRN1!', 'Slot 15', group = G_B3)
G_B4 = 'Board: Rates'
b16 = input.symbol('CBOT:ZT1!', 'Slot 16', group = G_B4)
b17 = input.symbol('CBOT:ZF1!', 'Slot 17', group = G_B4)
b18 = input.symbol('CBOT:ZN1!', 'Slot 18', group = G_B4)
b19 = input.symbol('CBOT:ZB1!', 'Slot 19', group = G_B4)
b20 = input.symbol('CBOT:UB1!', 'Slot 20', group = G_B4)
G_B5 = 'Board: FX'
b21 = input.symbol('CME:6E1!', 'Slot 21', group = G_B5)
b22 = input.symbol('CME:6J1!', 'Slot 22', group = G_B5)
b23 = input.symbol('CME:6B1!', 'Slot 23', group = G_B5)
b24 = input.symbol('CME:6A1!', 'Slot 24', group = G_B5)
b25 = input.symbol('CME:6C1!', 'Slot 25', group = G_B5)
b26 = input.symbol('CME:6S1!', 'Slot 26', group = G_B5)
G_B6 = 'Board: Ags & Crypto'
b27 = input.symbol('CBOT:ZC1!', 'Slot 27', group = G_B6)
b28 = input.symbol('CBOT:ZS1!', 'Slot 28', group = G_B6)
b29 = input.symbol('CBOT:ZW1!', 'Slot 29', group = G_B6)
b30 = input.symbol('CME:BTC1!', 'Slot 30', group = G_B6)
// -----------------------------------------------------------------------------
// SIGNAL ENGINE — evaluated in the requested symbol/timeframe context.
// -----------------------------------------------------------------------------
getMA(float src, int len, string maType) =>
switch maType
'SMA' => ta.sma(src, len)
'EMA' => ta.ema(src, len)
'SMMA (RMA)' => ta.rma(src, len)
'WMA' => ta.wma(src, len)
'VWMA' => ta.vwma(src, len)
=> ta.sma(src, len)
pfSignals() =>
// --- Phantom Shift ---
float atrS = shiftBuiltIn ? ta.atr(shiftATR) : ta.sma(ta.tr, shiftATR)
float src = hl2
float up = src - shiftMult * atrS
float upP = nz(up[1], up)
up := close[1] > upP ? math.max(up, upP) : up
float dn = src + shiftMult * atrS
float dnP = nz(dn[1], dn)
dn := close[1] < dnP ? math.min(dn, dnP) : dn
var int dir = 1
dir := dir == -1 and close > dnP ? 1 : dir == 1 and close < upP ? -1 : dir
bool sBuy = dir == 1 and dir[1] == -1
bool sSell = dir == -1 and dir[1] == 1
// --- Phantom Oscillator ---
float maBase = getMA(hl2, oscLength, oscType)
float diff = hl2 - maBase
float rng = ta.percentile_linear_interpolation(diff, oscLookback, 99)
float osc = ta.hma(ta.change(diff / rng, oscLen), 10)
bool oBuy = ta.crossover(osc, osc[2]) and osc < -oscThresh
bool oSell = ta.crossunder(osc, osc[2]) and osc > oscThresh
// --- Context ---
float trailLine = dir == 1 ? up : dn
int barsHeld = nz(ta.barssince(dir != dir[1]), bar_index)
// --- Buy confirmations ---
float regimeMA = ta.ema(close, trendLen)
float rsiVal = ta.rsi(close, rsiLen)
float volMA = ta.sma(volume, volLen)
[diPlus, diMinus, adxVal] = ta.dmi(adxLen, adxLen)
bool cRegime = close > regimeMA
bool cMomo = rsiVal > rsiFloor and rsiVal > rsiVal[1]
bool cVol = na(volume) or volume == 0 ? false : volume > volMA * volMult
bool cAdx = adxVal > adxMin and diPlus > diMinus
bool cFresh = dir == 1 and barsHeld <= maxAge
int score = (cRegime ? 1 : 0) + (cMomo ? 1 : 0) + (cVol ? 1 : 0) + (cAdx ? 1 : 0) + (cFresh ? 1 : 0)
int flags = (cRegime ? 1 : 0) + (cMomo ? 2 : 0) + (cVol ? 4 : 0) + (cAdx ? 8 : 0) + (cFresh ? 16 : 0)
int b = confirmedOnly ? 1 : 0
float chg = nz((close[b] / close[b + 1] - 1) * 100)
[sBuy[b], sSell[b], oBuy[b], oSell[b], dir[b], close[b], chg, osc[b], trailLine[b], barsHeld[b], score[b], flags[b], rsiVal[b]]
// -----------------------------------------------------------------------------
// RESULT BUFFERS (rebuilt every bar)
// -----------------------------------------------------------------------------
array<string> fLbl = array.new<string>()
array<bool> fSB = array.new<bool>()
array<bool> fSS = array.new<bool>()
array<bool> fOB = array.new<bool>()
array<bool> fOS = array.new<bool>()
array<int> fDir = array.new<int>()
array<float> fPx = array.new<float>()
array<float> fOsc = array.new<float>()
array<float> fLine = array.new<float>()
array<int> fBars = array.new<int>()
array<int> fScr = array.new<int>()
array<string> rSym = array.new<string>()
array<bool> rSB = array.new<bool>()
array<bool> rSS = array.new<bool>()
array<bool> rOB = array.new<bool>()
array<bool> rOS = array.new<bool>()
array<int> rDir = array.new<int>()
array<float> rPx = array.new<float>()
array<float> rChg = array.new<float>()
array<int> rScr = array.new<int>()
array<float> rLine = array.new<float>()
// One request.security call site per request — each keeps its own state.
focusRow(string sym, string tf) =>
[sb, ss, ob, os, dir, px, chg, osc, ln, bars, scr, flg, rsi] = request.security(sym, tf, pfSignals(), gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_off)
if showFocus and not na(px)
fLbl.push(tf == 'D' ? '1D' : tf == 'W' ? '1W' : tf)
fSB.push(sb), fSS.push(ss), fOB.push(ob), fOS.push(os)
fDir.push(dir), fPx.push(px), fOsc.push(osc), fLine.push(ln), fBars.push(bars), fScr.push(scr)
boardRow(string sym) =>
bool enabled = str.length(sym) > 0
[sb, ss, ob, os, dir, px, chg, osc, ln, bars, scr, flg, rsi] = request.security(enabled ? sym : syminfo.tickerid, scanTF, pfSignals(), gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_off)
if showBoard and enabled and not na(px)
rSym.push(sym)
rSB.push(sb), rSS.push(ss), rOB.push(ob), rOS.push(os)
rDir.push(dir), rPx.push(px), rChg.push(chg), rScr.push(scr), rLine.push(ln)
focusRow(focusSym, fTF1)
focusRow(focusSym, fTF2)
focusRow(focusSym, fTF3)
focusRow(focusSym, fTF4)
focusRow(focusSym, fTF5)
boardRow(b01), boardRow(b02), boardRow(b03), boardRow(b04), boardRow(b05), boardRow(b06)
boardRow(b07), boardRow(b08), boardRow(b09), boardRow(b10)
boardRow(b11), boardRow(b12), boardRow(b13), boardRow(b14), boardRow(b15)
boardRow(b16), boardRow(b17), boardRow(b18), boardRow(b19), boardRow(b20)
boardRow(b21), boardRow(b22), boardRow(b23), boardRow(b24), boardRow(b25), boardRow(b26)
boardRow(b27), boardRow(b28), boardRow(b29), boardRow(b30)
// Chart symbol — local call, costs zero request.security slots.
[cSB, cSS, cOB, cOS, cDir, cPx, cChg, cOsc, cLine, cBars, cScore, cFlags, cRSI] = pfSignals()
// -----------------------------------------------------------------------------
// PRESENTATION HELPERS
// -----------------------------------------------------------------------------
UP_COL = #089981
DOWN_COL = #F23645
OSC_UP = #1dd1c2
OSC_DN = color.orange
HEAD_BG = color.new(color.gray, 80)
cleanName(string s) =>
int p = str.pos(s, ':')
na(p) ? s : str.substring(s, p + 1)
// Pine has no bitwise operators — decode the flag mask arithmetically.
hasFlag(int f, int bit) =>
math.floor(f / bit) % 2 == 1
gradeOf(int s) =>
s >= 5 ? 'A+' : s == 4 ? 'A' : s == 3 ? 'B' : s == 2 ? 'C' : 'D'
gradeCol(int s) =>
s >= 4 ? UP_COL : s == 3 ? OSC_UP : s == 2 ? color.gray : color.new(color.gray, 40)
isBuySetup(bool sb, bool ob, int scr) =>
(sb or ob) and scr >= minScore
passesFilter(bool sb, bool ss, bool ob, bool os, int scr) =>
switch sigMode
'Shift only' => sb or ss
'Oscillator only' => ob or os
'Confluence only' => (sb and ob) or (ss and os)
'Buy setups only' => isBuySetup(sb, ob, scr)
=> sb or ss or ob or os
signalText(bool sb, bool ss, bool ob, bool os) =>
sb and ob ? '★ BUY' : ss and os ? '★ SELL' : sb ? 'Buy' : ss ? 'Sell' : ob ? '◆ Buy' : os ? '◆ Sell' : '—'
signalColor(bool sb, bool ss, bool ob, bool os) =>
sb and ob ? UP_COL : ss and os ? DOWN_COL : sb ? UP_COL : ss ? DOWN_COL : ob ? OSC_UP : os ? OSC_DN : color.gray
posFrom(string s) =>
switch s
'Top right' => position.top_right
'Top left' => position.top_left
'Bottom right' => position.bottom_right
'Bottom left' => position.bottom_left
=> position.middle_right
txtSize() =>
switch tblSizeInput
'Tiny' => size.tiny
'Normal' => size.normal
=> size.small
// -----------------------------------------------------------------------------
// CHART SYMBOL — BUY MARKERS, STOP AND TARGET
// -----------------------------------------------------------------------------
bool chartBuy = showChartSignals and isBuySetup(cSB, cOB, cScore)
bool chartSell = showChartSignals and (cSS or cOS)
float cRisk = cDir == 1 ? cPx - cLine : na
float cTarget = cDir == 1 ? cPx + rrMult * cRisk : na
plotshape(chartBuy, 'Buy setup', shape.triangleup, location.belowbar, UP_COL, text = 'BUY', textcolor = UP_COL, size = size.small)
plotshape(chartSell, 'Sell signal', shape.triangledown, location.abovebar, DOWN_COL, text = 'SELL', textcolor = DOWN_COL, size = size.tiny)
plot(showStopLine ? cLine : na, 'Shift stop', color = cDir == 1 ? UP_COL : DOWN_COL, style = plot.style_linebr, linewidth = 1)
if chartBuy
label.new(bar_index, low, gradeOf(cScore) + ' ' + str.tostring(cScore) + '/5\nStop ' + str.tostring(cLine, format.mintick) + '\nTgt ' + str.tostring(cTarget, format.mintick), style = label.style_label_up, color = color.new(UP_COL, 82), textcolor = UP_COL, size = size.tiny)
// -----------------------------------------------------------------------------
// PANEL 3 — BUY CHECKLIST (chart symbol)
// -----------------------------------------------------------------------------
var table checkTable = table.new(posFrom(checklistPos), 2, 9, border_width = 1, frame_width = 1, frame_color = color.new(color.gray, 60))
checkCell(int r, string name, bool ok) =>
table.cell(checkTable, 0, r, name, text_color = color.new(color.white, 20), text_size = txtSize(), text_halign = text.align_left)
table.cell(checkTable, 1, r, ok ? '✓' : '·', text_color = ok ? UP_COL : color.new(color.gray, 30), text_size = txtSize())
if barstate.islast and showChecklist
table.clear(checkTable, 0, 0, 1, 8)
table.cell(checkTable, 0, 0, cleanName(syminfo.tickerid) + ' buy', bgcolor = HEAD_BG, text_color = color.new(color.white, 0), text_size = txtSize())
table.cell(checkTable, 1, 0, gradeOf(cScore) + ' ' + str.tostring(cScore) + '/5', bgcolor = color.new(gradeCol(cScore), 75), text_color = gradeCol(cScore), text_size = txtSize())
checkCell(1, 'Regime (EMA' + str.tostring(trendLen) + ')', hasFlag(cFlags, 1))
checkCell(2, 'Momentum (RSI)', hasFlag(cFlags, 2))
checkCell(3, 'Volume', hasFlag(cFlags, 4))
checkCell(4, 'Trend strength (ADX)', hasFlag(cFlags, 8))
checkCell(5, 'Fresh flip', hasFlag(cFlags, 16))
table.cell(checkTable, 0, 6, 'Entry', text_color = color.new(color.white, 20), text_size = txtSize(), text_halign = text.align_left)
table.cell(checkTable, 1, 6, str.tostring(cPx, format.mintick), text_color = color.new(color.white, 10), text_size = txtSize())
table.cell(checkTable, 0, 7, 'Stop', text_color = color.new(color.white, 20), text_size = txtSize(), text_halign = text.align_left)
table.cell(checkTable, 1, 7, str.tostring(cLine, format.mintick), text_color = DOWN_COL, text_size = txtSize())
table.cell(checkTable, 0, 8, 'Target ' + str.tostring(rrMult, '#.#') + 'R', text_color = color.new(color.white, 20), text_size = txtSize(), text_halign = text.align_left)
table.cell(checkTable, 1, 8, cDir == 1 ? str.tostring(cTarget, format.mintick) : '—', text_color = UP_COL, text_size = txtSize())
// -----------------------------------------------------------------------------
// PANEL 1 — FOCUS CONTRACT, MULTI-TIMEFRAME
// -----------------------------------------------------------------------------
var table focusTable = table.new(posFrom(focusPos), 7, 8, border_width = 1, frame_width = 1, frame_color = color.new(color.gray, 60))
if barstate.islast and showFocus
table.clear(focusTable, 0, 0, 6, 7)
table.cell(focusTable, 0, 0, cleanName(focusSym), bgcolor = HEAD_BG, text_color = color.new(color.white, 0), text_size = txtSize())
table.cell(focusTable, 1, 0, 'Last', bgcolor = HEAD_BG, text_color = color.new(color.white, 10), text_size = txtSize())
table.cell(focusTable, 2, 0, 'Trend', bgcolor = HEAD_BG, text_color = color.new(color.white, 10), text_size = txtSize())
table.cell(focusTable, 3, 0, 'Stop', bgcolor = HEAD_BG, text_color = color.new(color.white, 10), text_size = txtSize())
table.cell(focusTable, 4, 0, 'Osc', bgcolor = HEAD_BG, text_color = color.new(color.white, 10), text_size = txtSize())
table.cell(focusTable, 5, 0, 'Score', bgcolor = HEAD_BG, text_color = color.new(color.white, 10), text_size = txtSize())
table.cell(focusTable, 6, 0, 'Signal', bgcolor = HEAD_BG, text_color = color.new(color.white, 10), text_size = txtSize())
int ups = 0
int n = fLbl.size()
string fMsg = ''
if n > 0
for i = 0 to n - 1
string lbl = fLbl.get(i)
bool sb = fSB.get(i)
bool ss = fSS.get(i)
bool ob = fOB.get(i)
bool os = fOS.get(i)
int dir = fDir.get(i)
float px = fPx.get(i)
float osv = fOsc.get(i)
float ln = fLine.get(i)
int bh = fBars.get(i)
int scr = fScr.get(i)
ups := ups + (dir == 1 ? 1 : 0)
color sigCol = signalColor(sb, ss, ob, os)
bool hit = sb or ss or ob or os
bool buySet = isBuySetup(sb, ob, scr)
table.cell(focusTable, 0, i + 1, lbl, text_color = color.new(color.white, 10), text_size = txtSize())
table.cell(focusTable, 1, i + 1, str.tostring(px, format.mintick), text_color = color.new(color.white, 20), text_size = txtSize())
table.cell(focusTable, 2, i + 1, (dir == 1 ? 'Up ' : 'Dn ') + str.tostring(bh), text_color = dir == 1 ? UP_COL : DOWN_COL, text_size = txtSize(), tooltip = 'Bars held in the current trend')
table.cell(focusTable, 3, i + 1, str.tostring(ln, format.mintick), text_color = color.new(color.gray, 10), text_size = txtSize(), tooltip = 'Phantom Shift trail level — the flip point / stop')
table.cell(focusTable, 4, i + 1, str.tostring(osv, '#.##'), text_color = osv > 0 ? OSC_UP : OSC_DN, text_size = txtSize())
table.cell(focusTable, 5, i + 1, gradeOf(scr) + ' ' + str.tostring(scr), text_color = gradeCol(scr), bgcolor = buySet ? color.new(UP_COL, 80) : na, text_size = txtSize(), tooltip = 'Buy confirmations passing, out of 5')
table.cell(focusTable, 6, i + 1, signalText(sb, ss, ob, os), text_color = sigCol, bgcolor = hit ? color.new(sigCol, 80) : na, text_size = txtSize())
if hit
fMsg := fMsg + (str.length(fMsg) > 0 ? '\n' : '') + lbl + ' ' + signalText(sb, ss, ob, os) + ' [' + gradeOf(scr) + ' ' + str.tostring(scr) + '/5] @ ' + str.tostring(px, format.mintick) + ' stop ' + str.tostring(ln, format.mintick)
// Alignment summary row
string alignTxt = str.tostring(ups) + '/' + str.tostring(n) + (ups == n ? ' ALL LONG' : ups == 0 ? ' ALL SHORT' : ' mixed')
color alignCol = ups == n ? UP_COL : ups == 0 ? DOWN_COL : color.gray
table.cell(focusTable, 0, n + 1, 'Align', bgcolor = HEAD_BG, text_color = color.new(color.white, 10), text_size = txtSize())
table.cell(focusTable, 1, n + 1, alignTxt, bgcolor = color.new(alignCol, 80), text_color = alignCol, text_size = txtSize())
table.cell(focusTable, 2, n + 1, '', bgcolor = HEAD_BG)
table.cell(focusTable, 3, n + 1, '', bgcolor = HEAD_BG)
table.cell(focusTable, 4, n + 1, '', bgcolor = HEAD_BG)
table.cell(focusTable, 5, n + 1, '', bgcolor = HEAD_BG)
table.cell(focusTable, 6, n + 1, '', bgcolor = HEAD_BG)
if focusAlert and str.length(fMsg) > 0
alert(cleanName(focusSym) + ' — Phantom Flow\n' + fMsg, alert.freq_once_per_bar_close)
// -----------------------------------------------------------------------------
// PANEL 2 — FUTURES BOARD
// -----------------------------------------------------------------------------
var table boardTable = table.new(posFrom(boardPos), 6, 32, border_width = 1, frame_width = 1, frame_color = color.new(color.gray, 60))
if barstate.islast and showBoard
table.clear(boardTable, 0, 0, 5, 31)
string headTF = scanTF == '' ? timeframe.period : scanTF
table.cell(boardTable, 0, 0, 'Futures — ' + headTF, bgcolor = HEAD_BG, text_color = color.new(color.white, 0), text_size = txtSize())
table.cell(boardTable, 1, 0, 'Last', bgcolor = HEAD_BG, text_color = color.new(color.white, 10), text_size = txtSize())
table.cell(boardTable, 2, 0, 'Chg %', bgcolor = HEAD_BG, text_color = color.new(color.white, 10), text_size = txtSize())
table.cell(boardTable, 3, 0, 'Trend', bgcolor = HEAD_BG, text_color = color.new(color.white, 10), text_size = txtSize())
table.cell(boardTable, 4, 0, 'Score', bgcolor = HEAD_BG, text_color = color.new(color.white, 10), text_size = txtSize())
table.cell(boardTable, 5, 0, 'Signal', bgcolor = HEAD_BG, text_color = color.new(color.white, 10), text_size = txtSize())
int row = 0
string bMsg = ''
// Render highest-scoring buy setups first, then everything else.
for pass = 0 to 1
if rSym.size() > 0
for i = 0 to rSym.size() - 1
string sym = rSym.get(i)
bool sb = rSB.get(i)
bool ss = rSS.get(i)
bool ob = rOB.get(i)
bool os = rOS.get(i)
int dir = rDir.get(i)
float px = rPx.get(i)
float chg = rChg.get(i)
int scr = rScr.get(i)
float ln = rLine.get(i)
bool buySet = isBuySetup(sb, ob, scr)
bool hit = passesFilter(sb, ss, ob, os, scr)
bool show = rowMode == 'All markets' or hit
bool inPass = pass == 0 ? buySet : not buySet
if show and inPass and row < 31
row += 1
color sigCol = signalColor(sb, ss, ob, os)
table.cell(boardTable, 0, row, cleanName(sym), text_color = color.new(color.white, 10), text_size = txtSize(), text_halign = text.align_left)
table.cell(boardTable, 1, row, str.tostring(px, format.mintick), text_color = color.new(color.white, 20), text_size = txtSize())
table.cell(boardTable, 2, row, str.tostring(chg, '#.##') + '%', text_color = chg >= 0 ? UP_COL : DOWN_COL, text_size = txtSize())
table.cell(boardTable, 3, row, dir == 1 ? 'Up' : 'Down', text_color = dir == 1 ? UP_COL : DOWN_COL, text_size = txtSize())
table.cell(boardTable, 4, row, gradeOf(scr) + ' ' + str.tostring(scr), text_color = gradeCol(scr), bgcolor = buySet ? color.new(UP_COL, 80) : na, text_size = txtSize())
table.cell(boardTable, 5, row, signalText(sb, ss, ob, os), text_color = sigCol, bgcolor = hit ? color.new(sigCol, 85) : na, text_size = txtSize())
if hit and pass == 0 and buySet
bMsg := bMsg + (str.length(bMsg) > 0 ? '\n' : '') + cleanName(sym) + ': ' + signalText(sb, ss, ob, os) + ' [' + gradeOf(scr) + ' ' + str.tostring(scr) + '/5] @ ' + str.tostring(px, format.mintick) + ' stop ' + str.tostring(ln, format.mintick)
if row == 0
table.cell(boardTable, 0, 1, 'No signals', text_color = color.gray, text_size = txtSize())
if boardAlert and str.length(bMsg) > 0
alert('Futures board ' + headTF + ' — buy setups\n' + bMsg, alert.freq_once_per_bar_close)
Show all 470 lines