Options Ladder Pro (◇ OLP)
By: Dskyz | DAFE Trading Systems
Most options tools show you a chain. The Ladder shows you where the weight is. It pulls open interest from the nearest expirations, stacks it by strike into a live ladder, finds the magnet (POC) and the 70% value area, and then asks the only question that matters intraday: is price being pulled toward the mass or escaping it? When there is no chain — futures, index, crypto, after hours — it says so and builds the same ladder from volume, labeled.
#TSBuild26 (Most Unique Options Indicator)
How it works — in 6 steps:
- Data Source Auto detects the instrument: equities take the live chain, futures / index / crypto take the SLF volume-profile ladder, and a labeled Advanced Synthetic tenor surface stands in when a chain is requested but unavailable. The source tag is always printed.
- Up to five nearest expirations are requested in one parallel round-trip — one schedule call plus one chain call per expiry, capped at the platform's six — and merged into one strike surface with OI, OI change and daily volume; a failed expiry is skipped and counted, never faked.
- The Resolution Cascade syncs strike spacing to your visible range: zoom in for every strike, zoom out for the significant ones.
- The surface yields the POC (max-OI strike), VAH / VAL (70% of OI) and smart rails at the strikes bracketing price; a put/call ratio and five-zone mass split read where the weight sits above, around and below value.
- Signals are structural: POC cross, value-area breakout, value-area rejection, and end-of-tape flow shifts — gated by a zero-lag MFI / CCI momentum filter and a sensitivity dial, with a wick-pressure kernel grading peaks and valleys, entropy filtering chop and fractal/pivot confluence.
- Every signal is intra-bar capable (orange LIVE on the forming bar), confirms on close, and is registered for alerts: VA/POC Long, VA/POC Short, Valley Long, Peak Short.
Every system with guides:
Input Guide — every menu item explained in plain language.
Visual Guide — every line, marker, board column and dial wedge explained.
What you see on chart:
POC line in gold that breathes and flashes when crossed; VAH / VAL that dim inside value and brighten on escape; dotted strike rails bracketing price; ▲ POC / ▼ POC cross markers; peak and valley markers with orange LIVE previews.
The Board:
Strike ladder — PUTS bar left, strike and tag center, CALLS bar right; the POC row pulses gold, fresh-flow rows (new OI today) glow, a traveling highlight scans the rows.
OI Profile — the horizontal open-interest-by-strike chart inside the board.
Mass Split dial — five solid wedges, radius = share of total OI, hue = put/call balance: ABV above VAH, UVA POC→VAH, POC the magnet, LVA VAL→POC, BLW below VAL — percentages printed beside each label.
Dashboard reads live: source and session state, expirations loaded, POC / VAH / VAL and price position, put/call ratio and flow shift, the signal state with its reason, momentum-filter reads, and the ladder itself.
Limits (know them):
Open interest is yesterday's settled positioning; OI change and daily volume are today's — the Fresh-Flow glow is how you tell them apart.
No chain on futures, index or crypto: the ladder is volume (SLF) and labeled as such.
Levels are magnets and fences, not forecasts — a POC cross is a structure event, never a guaranteed move.
Alerts fire on confirmed closes; orange LIVE markers may vanish.
Find the mass. Trade the escape. Respect the magnet.
— Dskyz | DAFE Trading Systems
describe_indicator('Options Ladder Pro [DAFE]', 'overlay', { shortName: '◇ OLP', mainColorInheritFrom: 'legend_anchor' });
// ==============================================================================================
// ------------------ Options Ladder Pro [DAFE] -------------------
// ==============================================================================================
// LIBRARIES & DAFE QUANT CORE & HELPERS
// ==============================================================================================
var jStat = library('jstat');
var bsBounds = library('binary-search-bounds');
var fftjs; try { fftjs = library('fft-js'); } catch (eFft) { fftjs = null; }
var tiny = library('tinycolor2');
var DAFE = { utils: {} };
DAFE.utils.clamp = function(v, lo, hi) { return Math.max(lo, Math.min(hi, v)); };
DAFE.utils.nz = function(v, fb) { return (v == null || v !== v || !isFinite(v)) ? (fb || 0) : v; };
DAFE.utils.make = function(len, val) { var a = [], i; for (i = 0; i < len; i++) a.push(val); return a; };
function clamp(v, lo, hi) { return DAFE.utils.clamp(v, lo, hi); }
function clamp01(v) { return clamp(v, 0, 1); }
function nz(v, fb) { return DAFE.utils.nz(v, fb); }
function make(len, val) { return DAFE.utils.make(len, val); }
function make_array(len, val) { return make(len, val); }
function hexToRgba(c, a) { var h = String(c || '').replace('#', ''); if (h.length === 3) h = h.charAt(0) + h.charAt(0) + h.charAt(1) + h.charAt(1) + h.charAt(2) + h.charAt(2); if (!/^[0-9a-fA-F]{6}$/.test(h)) return c; return 'rgba(' + parseInt(h.slice(0, 2), 16) + ', ' + parseInt(h.slice(2, 4), 16) + ', ' + parseInt(h.slice(4, 6), 16) + ', ' + Math.max(0, Math.min(1, a)) + ')'; }
function rgba(c, a) { try { if (tiny) return tiny(c).setAlpha(a).toRgbString(); } catch (eR) {} return hexToRgba(c, a); } // alpha survives a missing colour library
var EPS = 1e-10;
function rangeOpts(min, max, step, dec) {
var out = [], steps = Math.round((max - min) / step);
for (var k = 0; k <= steps; k++) {
var v = min + k * step; v = parseFloat(v.toFixed(dec !== undefined ? dec : 6)); out.push(v);}
return out;}
function tt(text) { return { tooltip: text, description: text, help: text }; }
// ============================================================================
// INPUTS — all dropdowns, consolidated
// ============================================================================
var i_sourceMode = input.select('Data Source', 'Auto', ['Auto', 'Live Options', 'Advanced Synthetic', 'SLF'], tt('Auto detects the instrument: futures/index/crypto get SLF (volume profile), equities get Live Options chain with Advanced Synthetic fallback. Override to force a source.'));
var i_resMode = input.select('Resolution', 'Auto (Range-Synced)', ['Auto (Range-Synced)', 'Fine (0.5x)', 'Normal (1x)', 'Coarse (2x)'], tt('The Resolution Cascade: Auto syncs strike spacing to your visible chart range — zoom in for tight strikes, zoom out for significant ones. Fixed modes lock the spacing multiplier.'));
var i_numExp = input.select('Expirations', '1', ['1', '2', '3', '4', '5'], tt('Number of nearest expirations to aggregate into the OI surface. More = fuller magnet structure but slower.'));
var i_sensMode = input.select('Signal Sensitivity', 'Balanced', ['Loose', 'Balanced', 'Strict'], tt('Signal spacing and flow-shift thresholds. Loose = more signals. Strict = fewer, higher-conviction.'));
var i_intrabar = input.select('Intra-bar Signals', 'On', ['Off', 'On'], tt('On: signals on the forming bar show as orange LIVE markers and confirm or vanish at close. Off: signals only on closed bars. Alerts always fire on confirmed close.'));
var i_embedMode = input.select('Embed Charts', 'Both', ['Off', 'Profile', 'Split', 'Both'], tt('Embedded Chart.js inside the board: Profile = horizontal OI-by-strike bar chart. Split = the Mass Split dial (OI mass by value-area zone with put/call hue). Both = profile and dial side by side. Terrain = 3D strike x tenor surface in a bottom overlay — spectacular.'));
var i_cameraStyle = input.select('Camera Style', 'Cinematic', ['Static Card', 'Slow Drift', 'Cinematic', 'Rapid Spin'], tt('Camera orbit for the terrain. Static Card = fixed view. Cinematic = slow orbit with tilt. Rapid Spin = fast perspective orbit.'));
var i_cometSpeed = input.select('Highlight Scan', 'Off', ['Off', 'Fast', 'Slow'], tt('The traveling highlight box that scans down the ladder rows. Fast = ~0.5s per row. Slow = ~2s per row. Off = static.'));
var i_freshGlow = input.select('Fresh-Flow Glow', 'Off', ['Off', 'On'], tt('Strikes with new positioning today (OI change ≠ 0 or daily volume) glow brighter than static OI rows. Live-data dimension.'));
var i_textSize = input.select('Dashboard Text Size', 'Large', ['Small', 'Medium', 'Large'], tt('Scales all dashboard typography (Large matches original default).'));
var i_theme = input.select('Theme', 'Neon Night', ['Neon Night', 'Cyber Punk', 'Emerald Abyss', 'Gold Sovereign'], tt('Four color skins driving every overlay, cloud, marker, raster and dashboard. Cosmetic only.'));
var i_guideMode = input.select('Guide Panel', 'Off', ['Off', 'Input Guide', 'Visual Guide'], tt('Reference panels. Cosmetic only.'));
var i_showInputGuide = i_guideMode === 'Input Guide';
var i_showVisualGuide = i_guideMode === 'Visual Guide';
var i_visRange = input.anchor('visible range');
var CAM_MAP = {
'Static Card': { auto: false, rate: 0, spin: 25, tilt: 24, proj: 'Isometric' },
'Slow Drift': { auto: true, rate: 3, spin: 20, tilt: 26, proj: 'Isometric' },
'Cinematic': { auto: true, rate: 6, spin: 30, tilt: 30, proj: 'Perspective' },
'Rapid Spin': { auto: true, rate: 14, spin: 0, tilt: 34, proj: 'Perspective' } };
var CAM = CAM_MAP[i_cameraStyle] || CAM_MAP['Cinematic'];
var SURF = { numExp: parseInt(i_numExp, 10) || 3, rows: 21, lookback: 120 };
var SYN = { window: 6.0, aggression: 1.45, glow: 0.22, memory: 0.92 };
var LIB_OK = true;
try { if (typeof jStat === 'undefined' || !jStat) LIB_OK = false; } catch (e) { LIB_OK = false; }
var n = close.length;
var i;
var lastIdx = n - 1;
var currentPrice = close[lastIdx];
var CLOCK_EARLY = (function(){
var w = 0, has = false;
try {
if (typeof Date !== 'undefined' && typeof Date.now === 'function') {
w = Date.now();
if (w > 0) has = true; }
} catch (e) {}
if (!has) w = n * 1000;
var c = { wallMs: w, hasClock: has, barMod8: (n % 8) / 8, barMod12: (n % 12) / 12 };
c.phase12 = c.barMod12 * 2 * Math.PI;
c.slow = (c.wallMs / 1000) % 100000;
c.render = c.hasClock ? c.wallMs : n * 250;
c.phase8 = c.barMod8 * 2 * Math.PI;
c.beat = c.hasClock ? (c.wallMs / 1000) : n;
return c;})();
var CLOCK = CLOCK_EARLY;
function _barTimeMs(idx) {
var t = 0;
try { t = nz(time[idx], 0); } catch (eT) { t = 0; }
if (t > 0 && t < 1e12) t *= 1000;
return t;}
function _dowUTC(ms) {
var days = Math.floor(ms / 86400000);
return ((days % 7) + 7 + 4) % 7; }
function _civilYearFromMs(ms) {
var z = Math.floor(ms / 86400000) + 719468;
var era = Math.floor((z >= 0 ? z : z - 146096) / 146097);
var doe = z - era * 146097;
var yoe = Math.floor((doe - Math.floor(doe / 1460) + Math.floor(doe / 36524) - Math.floor(doe / 146096)) / 365);
var y = yoe + era * 400;
var doy = doe - (365 * yoe + Math.floor(yoe / 4) - Math.floor(yoe / 100));
var mp = Math.floor((5 * doy + 2) / 153);
var m = mp + (mp < 10 ? 3 : -9);
return y + (m <= 2 ? 1 : 0);}
function _nthSundayUTC(year, month0, nth) {
var day1ms = Date.UTC(year, month0, 1);
var dow = _dowUTC(day1ms);
var firstSun = 1 + ((7 - dow) % 7);
return firstSun + (nth - 1) * 7;}
function _etHourOf(ms) {
var y = _civilYearFromMs(ms);
var dstStart = Date.UTC(y, 2, _nthSundayUTC(y, 2, 2), 7);
var dstEnd = Date.UTC(y, 10, _nthSundayUTC(y, 10, 1), 6);
var offset = (ms >= dstStart && ms < dstEnd) ? 4 : 5;
var frac = (ms % 86400000) / 3600000;
var et = frac - offset;
if (et < 0) et += 24;
return et;}
function _etWeekdayOf(ms) {
var dow = _dowUTC(ms);
var offset = 5;
try {
var y = _civilYearFromMs(ms);
var dstStart = Date.UTC(y, 2, _nthSundayUTC(y, 2, 2), 7);
var dstEnd = Date.UTC(y, 10, _nthSundayUTC(y, 10, 1), 6);
offset = (ms >= dstStart && ms < dstEnd) ? 4 : 5;
} catch (eDow) {}
var frac = (ms % 86400000) / 3600000;
var crossedBack = frac < offset;
return crossedBack ? ((dow + 6) % 7) : dow;}
function _isExtendedSessionInstrument() {
try {
var ex = String((typeof constants !== 'undefined' && constants && constants.exchange) || '').toUpperCase();
if (ex === 'CME' || ex === 'CBOT' || ex === 'NYMEX' || ex === 'COMEX' || ex === 'NYBOT') return true;
var ticker = String((typeof constants !== 'undefined' && constants && constants.ticker) || '').toUpperCase();
if (/USD$|USDT$|BTC|ETH|XRP|SOL/.test(ticker)) return true;
if (/.(FUT|F)$/i.test(ticker) || ticker.indexOf('_FV') >= 0) return true;
} catch (eX) {}
return false;}
function sessionState() {
var ms = _barTimeMs(typeof lastIdx === 'number' ? lastIdx : (n - 1));
if (!(ms > 0)) return { live: false, ah: false, label: 'no-time' };
var et = _etHourOf(ms), dow = _etWeekdayOf(ms);
var barMs = 60000;
if (n >= 2) {
var t1 = _barTimeMs(n - 1), t0 = _barTimeMs(n - 2);
if (t1 > t0 && t1 - t0 < 8.64e7) barMs = t1 - t0;}
var wall = CLOCK.hasClock ? CLOCK.wallMs : ms;
var barAgeMin = (wall - ms) / 60000;
var recent = barAgeMin <= Math.max(5, (barMs / 60000) * 2.5);
var inRegHours;
if (_isExtendedSessionInstrument()) {
var halted = (dow === 6) || (dow === 0 && et < 18) || (dow === 5 && et >= 17) || (et >= 17 && et < 18);
inRegHours = !halted;
} else {
inRegHours = (dow >= 1 && dow <= 5) && et >= 9.5 && et < 16;}
var live = inRegHours && recent;
var label = !inRegHours ? 'CLOSED' : !recent ? 'LAST CLOSE' : 'LIVE';
return { live: live, ah: !inRegHours, label: label, barAgeMin: barAgeMin };}
// ==============================================================================================
// INTERNAL MAPPINGS & DASHBOARD DIMENSIONS
// ==============================================================================================
var numExp = parseInt(i_numExp, 10) || 3;
var RES_MAP = { 'Auto (Range-Synced)': null, 'Fine (0.5x)': 0.5, 'Normal (1.0x)': 1.0, 'Coarse (2.0x)': 2.0, 'Macro (4.0x)': 4.0 };
var SENS_MAP = {
'Loose': { minDist: 5, pcrHi: 1.25, pcrLo: 0.80 },
'Balanced': { minDist: 10, pcrHi: 1.15, pcrLo: 0.85 },
'Strict': { minDist: 15, pcrHi: 1.05, pcrLo: 0.95 }};
var SN = SENS_MAP[i_sensMode] || SENS_MAP['Balanced'];
var SENS = SENS_MAP[i_sensMode] || SENS_MAP['Balanced'];
var baseFontSize = i_textSize === 'Small' ? 6 : (i_textSize === 'Medium' ? 7 : 8);
var headFontSize = baseFontSize + 1;
var subFontSize = Math.max(7, baseFontSize - 1);
var BOARD_WIDTH_CONFIG = {
'Large': { 'Off': 270, 'Split': 300, 'Profile': 330, 'Both': 390, 'Terrain': 340 },
'Medium': { 'Off': 240, 'Split': 270, 'Profile': 300, 'Both': 350, 'Terrain': 310 },
'Small': { 'Off': 215, 'Split': 240, 'Profile': 270, 'Both': 315, 'Terrain': 280 } };
var boardWidthPx = (BOARD_WIDTH_CONFIG[i_textSize] && BOARD_WIDTH_CONFIG[i_textSize][i_embedMode]) || 340;
var boardWidth = boardWidthPx + 'px';
var SURF = { numExp: parseInt(i_numExp, 10) || 3, rows: 21, lookback: 120 };
var SYN = { window: 6.0, aggression: 1.45, glow: 0.22, memory: 0.92 };
var RADAR = { enabled: true, decay: 0.88, gain: 1.35 };
// ============================================================================
// THEME
// ============================================================================
var THEME_MAP = {
'Neon Night': { bull: '#00C853', bear: '#D32F2F', a1: '#4DA3FF', a2: '#D34BD3', a3: '#80DEEA', warn: '#FFA500', gold: '#FFD700', txt: '#E0E0FF', dim: '#8888AA', panel: 'rgba(16,18,42,0.92)', bg: 'rgba(10,11,31,0.96)', ent: '#05589a', acc: '#4DA3FF', pur: '#D34BD3' },
'Cyber Punk': { bull: '#00FF00', bear: '#FF007F', a1: '#00FFFF', a2: '#A855F7', a3: '#39FF14', warn: '#FFFF00', gold: '#FFFF00', txt: '#FFE5FF', dim: '#9A7FB0', panel: 'rgba(26,7,46,0.92)', bg: 'rgba(15,5,29,0.96)', ent: '#7B2FBE', acc: '#00FFFF', pur: '#A855F7' },
'Emerald Abyss': { bull: '#00FF99', bear: '#FF4500', a1: '#3CB371', a2: '#ADFF2F', a3: '#98FB98', warn: '#FFD700', gold: '#FFD700', txt: '#E0FFE0', dim: '#5D8A6E', panel: 'rgba(5,26,14,0.92)', bg: 'rgba(2,10,5,0.96)', ent: '#2E8B57', acc: '#3CB371', pur: '#98FB98' },
'Gold Sovereign': { bull: '#FFD700', bear: '#E65100', a1: '#FFB300', a2: '#FFF8E1', a3: '#FFE57F', warn: '#FF6D00', gold: '#FFD700', txt: '#FFF8E1', dim: '#A08B4A', panel: 'rgba(28,21,0,0.92)', bg: 'rgba(10,7,0,0.96)', ent: '#B8860B', acc: '#FFB300', pur: '#FFE57F' }};
var T = THEME_MAP[i_theme] || THEME_MAP['Neon Night'];
if (!T) T = { bull: '#00C853', bear: '#D32F2F', a1: '#4DA3FF', a2: '#D34BD3', a3: '#80DEEA', warn: '#FFA500', gold: '#FFD700', txt: '#E0E0FF', dim: '#8888AA', panel: 'rgba(16,18,42,0.92)', bg: 'rgba(10,11,31,0.96)', ent: '#05589a', acc: '#4DA3FF', pur: '#D34BD3' };
var THEME = T;
var TT = {
bull: T.bull, bear: T.bear, neu: T.a2 || T.a1, prim: T.a1, sec: T.a3 || T.a1,
accent: T.a1, ghost: T.dim, hi: T.txt, lo: T.dim,
bg: T.bg, panel: T.panel, gold: T.gold, field: T.a1,
void_: '#05060C', fresh: T.warn, sec2: T.a3};
var PV = { bull: T.bull, bear: T.bear, a1: T.a1, a2: T.a2, a3: T.a3, warn: T.warn, txt: T.txt, dim: T.dim, panel: T.panel, bg: T.bg };
// ============================================================================
// DERIVED CONFIG
// ============================================================================
var n = close.length;
var i;
var lastIdx = n - 1;
var currentPrice = close[lastIdx];
var SESSION = sessionState();
var adaptivePeriod = 30;
var ema21 = ema(close, 21);
var ema55 = ema(close, 55);
var ema200 = ema(close, 200);
var atr14 = atr(14);
var roc10 = roc(close, 10);
var atrFast = sma(atr14, 10);
var atrSlow = sma(atr14, 50);
var volSma20 = sma(volume, 20);
var relVolArr = make(n, 1);
var logRet = make(n, 0);
var downMoves = make(n, 0), upMoves = make(n, 0);
for (var _ci = 0; _ci < n; _ci++) { relVolArr[_ci] = volSma20[_ci] > 0 ? nz(volume[_ci], 0) / volSma20[_ci] : 1;
if (_ci > 0) logRet[_ci] = Math.log(Math.max(close[_ci], EPS) / Math.max(close[_ci - 1], EPS));
if (_ci > 0) { var chg = close[_ci] - close[_ci - 1]; if (chg < 0) downMoves[_ci] = Math.abs(chg); else upMoves[_ci] = chg; } }
var downAvg = sma(downMoves, 14), upAvg = sma(upMoves, 14);
var synPCRLast = clamp(safeDiv(nz(downAvg[lastIdx], 1), nz(upAvg[lastIdx], 1), 1.0), 0.3, 3.0);
var trendEMA = ema(close, adaptivePeriod);
var hlcc4 = make(n, 0);
for (i = 0; i < n; i++) hlcc4[i] = (high[i] + low[i] + close[i] + close[i]) / 4;
var hlcc4_wma = wma(hlcc4, 3);
var velocity = make(n, 0);
var acceleration = make(n, 0);
var velHalf = Math.max(1, Math.round(adaptivePeriod / 2));
for (i = 0; i < n; i++) { velocity[i] = (i >= velHalf) ? hlcc4_wma[i] - hlcc4_wma[i - velHalf] : 0; acceleration[i] = (i > 0) ? velocity[i] - velocity[i - 1] : 0;}
var atr14 = atr(14);
var deltaProxyArr = make(n, 0);
for (i = 1; i < n; i++) deltaProxyArr[i] = clamp((close[i] - close[i - 1]) / Math.max(atr14[i] || 1, EPS), -3, 3);
var SENS_MAP = {
'Loose': { minDist: 5, pcrHi: 1.25, pcrLo: 0.80 },
'Balanced': { minDist: 10, pcrHi: 1.15, pcrLo: 0.85 },
'Strict': { minDist: 15, pcrHi: 1.05, pcrLo: 0.95 }};
var SN = SENS_MAP[i_sensMode];
var minSignalDistance = SN.minDist;
var pcrHiThresh = SN.pcrHi, pcrLoThresh = SN.pcrLo;
var numExp = parseInt(i_numExp, 10);
// ============================================================================
// MOMENTUM FILTER
// ============================================================================
var mfiZl = make(n, 50);
var cciArr = make(n, 0);
var vwapArr = make(n, 0);
var tpCum = 0, volCum = 0;
for (i = 0; i < n; i++) {
var tp = (high[i] + low[i] + close[i]) / 3; tpCum += tp * (volume[i] || 0); volCum += (volume[i] || 0); vwapArr[i] = volCum > EPS ? tpCum / volCum : close[i];}
var ema21 = ema(close, 21);
var ema55 = ema(close, 55);
var ema200 = ema(close, 200);
var volAvg = sma(volume, 20);
// ============================================================================
// INSTRUMENT PROFILE
// ============================================================================
function detectInstrumentProfile() {
var ticker = String((typeof constants !== 'undefined' && constants && constants.ticker) || '').toUpperCase();
var isFuturesByRoot = /^(ES|NQ|MNQ|YM|MYM|RTY|MRTY|CL|MCL|GC|MGC|SI|MSI|NG|MNG|ZB|MZB|ZN|MZN|ZF|MZF|ZT|MZT|6E|M6E|6J|M6J|6B|M6B|6A|M6A|HG|PL|PA|ZW|MZW|ZC|MZC|ZS|MZS|CT|KC|SB|CC|LE|HE|GF)/.test(ticker);
var isFuturesBySuffix = /.(FUT|F)$/i.test(ticker) || ticker.indexOf('_FV') >= 0;
var isFuturesByExchange = constants.exchange === 'CME' || constants.exchange === 'CBOT' || constants.exchange === 'NYMEX' || constants.exchange === 'COMEX';
var likelyFuturesByPrice = close[lastIdx] > 1000 && !/^[A-Z]{4}$/.test(ticker);
var isIndex = /^(SPX|NDX|DJX|RUT|VIX|XSP|NSE|DAX|FTSE|CAC|NIKKEI)/i.test(ticker) || ticker.indexOf('I:SPX') >= 0 || ticker.indexOf('TVC:') >= 0;
var isCrypto = /USD$|USDT$|BTC|ETH|XRP|SOL|DOGE/i.test(ticker) && !/.P$/i.test(ticker);
var isForex = /^(EUR|GBP|JPY|AUD|CAD|CHF|NZD)[/.]?(USD|EUR|GBP|JPY|AUD|CAD|CHF|NZD)?$/i.test(ticker);
var avgVol = 0, avCnt = 0;
for (var av = Math.max(0, n - 20); av < n; av++) { avgVol += (volume[av] || 0); avCnt++; }
avgVol = avCnt > 0 ? avgVol / avCnt : 0;
var atrPct = atr14[lastIdx] > 0 ? (atr14[lastIdx] / close[lastIdx]) * 100 : 0;
var hasOptionsChain = !isFuturesByRoot && !isFuturesBySuffix && !isFuturesByExchange && !isIndex && !isCrypto && !isForex;
var finalIsFutures = isFuturesByRoot || isFuturesBySuffix || isFuturesByExchange;
var reason = finalIsFutures ? (isFuturesByRoot ? 'FUTURES_ROOT_MATCH' : isFuturesBySuffix ? 'FUTURES_SUFFIX_MATCH' : isFuturesByExchange ? 'FUTURES_EXCHANGE_MATCH' : 'FUTURES_PRICE_HEURISTIC') : isIndex ? 'INDEX_NO_CHAIN' : isCrypto ? 'CRYPTO_SPOT' : isForex ? 'FOREX_SPOT' : hasOptionsChain ? 'EQUITY_OPTIONS_OK' : 'UNKNOWN_USE_SLF';
return { ticker: ticker, isFutures: finalIsFutures, isIndex: isIndex, isCrypto: isCrypto, isForex: isForex, hasOptionsChain: hasOptionsChain, avgVolume: avgVol, atrPct: atrPct, reason: reason, confidence: (finalIsFutures || hasOptionsChain) ? 'HIGH' : 'MEDIUM' };}
// ============================================================================
// HELPERS
// ============================================================================
function sortNum(a, b) { return a - b; }
function roundToStep(v, step) { return Math.round(v / step) * step; }
function gaussian(x, mu, sigma) { var s = Math.max(1e-8, sigma), z = (x - mu) / s; return Math.exp(-0.5 * z * z); }
function percentile(arr, p) {
if (!arr.length) return 0;
var sorted = arr.slice().sort(function (a, b) { return a - b; });
var idx = clamp(Math.floor(p * sorted.length), 0, sorted.length - 1);
return sorted[idx];}
function safeDiv(a, b, c) { return (b && b !== 0 && !isNaN(b)) ? (a / b) : c; }
function repeatStr(s, n) { var out = ''; for (var r = 0; r < n; r++) out += s; return out; }
function niceNum(x) {
if (!(x > 0) || !isFinite(x)) return 1;
var exp = Math.floor(Math.log(x) / Math.LN10);
var f = x / Math.pow(10, exp);
var nf = f <= 1.25 ? 1 : f <= 2.25 ? 2 : f <= 3.75 ? 2.5 : f <= 7.5 ? 5 : 10;
return nf * Math.pow(10, exp);}
function inferPriceStep(spot, atrV, rangeW) {
var raw = (rangeW > 0)
? rangeW / 20
: Math.max(nz(atrV, 0) * 0.4, Math.abs(nz(spot, 0)) * 0.0002, 0.01);
var step = niceNum(Math.max(raw, 0.01));
if (Math.abs(spot) >= 1000 && step < 1) step = 1;
if (Math.abs(spot) >= 1000 && step === 2.5) step = 5;
return step;}
function medianStrikeStep(strikes) {
if (!strikes || strikes.length < 2) return null;
var diffs = [], iD;
for (iD = 1; iD < strikes.length; iD++) {
var d = strikes[iD] - strikes[iD - 1];
if (d > EPS) diffs.push(d);}
if (!diffs.length) return null;
diffs.sort(sortNum);
return diffs[Math.floor(diffs.length * 0.5)];}
function localPriceWindow(spot, atrV) {
var hi = -Infinity, lo = Infinity, iW;
var bars = 120;
try { if (typeof CASCADE !== 'undefined' && CASCADE && CASCADE.bars) bars = CASCADE.bars; } catch (eC) {}
bars = clamp(Math.max(bars, SURF.lookback || 120), 20, 500);
var start = Math.max(0, n - bars);
for (iW = start; iW < n; iW++) {
if (high[iW] > hi) hi = high[iW];
if (low[iW] < lo) lo = low[iW];}
if (!isFinite(hi) || !isFinite(lo) || hi <= lo) {
var w0 = Math.max(atrV * 4, Math.abs(spot) * 0.008, 1);
return { lo: spot - w0, hi: spot + w0 };}
var pad = Math.max(atrV * 0.75, (hi - lo) * 0.08);
return { lo: lo - pad, hi: hi + pad };}
function fmtPrice(v) {
if (v == null || !isFinite(v)) return '—';
var step = (typeof PRICE_STEP === 'number' && PRICE_STEP > 0) ? PRICE_STEP : 0;
var x = step > 0 ? roundToStep(v, step) : v;
var a = Math.abs(x);
if (step >= 1 || a >= 1000) return Math.round(x).toString();
if (Math.abs(x - Math.round(x)) < 1e-6) return Math.round(x).toString();
if (Math.abs(x * 2 - Math.round(x * 2)) < 1e-6) return (Math.round(x * 2) / 2).toFixed(1);
if (a >= 1) {
var s2 = x.toFixed(2);
if (s2.slice(-3) === '.00') return s2.slice(0, -3);
if (s2.slice(-1) === '0') return s2.slice(0, -1);
return s2;}
if (a >= 0.1) return x.toFixed(4);
if (a >= 0.01) return x.toFixed(5);
return x.toFixed(6);}
function fmtOI(v) {
if (v >= 1e6) return (v / 1e6).toFixed(2) + 'M';
if (v >= 1e3) return (v / 1e3).toFixed(1) + 'K';
return v.toFixed(0);}
function fmtSignedPct(v) { return (v >= 0 ? '+' : '') + (v * 100).toFixed(1) + '%'; }
function chooseWindow(strikes, price, count) {
if (strikes.length <= count) return strikes.slice();
var result = [price];
var lo = [], hi = [];
for (var i = 0; i < strikes.length; i++) {
if (strikes[i] < price) lo.push(strikes[i]); else if (strikes[i] > price) hi.push(strikes[i]);}
lo.sort(function (a, b) { return b - a; });
var loN = Math.floor(count / 2), hiN = count - loN - 1;
for (var j = 0; j < loN && j < lo.length; j++) result.push(lo[j]);
for (var k = 0; k < hiN && k < hi.length; k++) result.push(hi[k]);
return result;}
var PRICE_STEP = inferPriceStep(currentPrice, nz(atr14[lastIdx], 1), 0);
// ============================================================================
// TERRAIN SUPPORT
// ============================================================================
function lerp(a, b, t) { return a + (b - a) * clamp01(t); }
function hashRand(x) { var v = Math.sin(x * 12.9898) * 43758.5453; return v - Math.floor(v); }
function hexToRgba(hex, alpha) {
var r = /^#?([a-fd]{2})([a-fd]{2})([a-fd]{2})$/i.exec(hex);
if (!r) return 'rgba(128,128,128,' + alpha + ')';
return 'rgba(' + parseInt(r[1], 16) + ',' + parseInt(r[2], 16) + ',' + parseInt(r[3], 16) + ',' + alpha + ')'; }
function hx(c) { try { return tiny ? tiny(c).toHexString() : c; } catch (e) { return c; } }
function mixTowards(hexA, hexB, pct) {
try { if (tiny) return tiny.mix(hexA, hexB, pct).toHexString(); } catch (eM) {}
return hexA; }
function lighten(hexC, amt) {
try { if (tiny) return tiny(hexC).lighten(amt).toHexString(); } catch (eL) {}
return hexC; }
function lineSeries(v) {
var a = make(n, null);
if (!isFinite(v)) return a;
for (var iLs = 0; iLs < n; iLs++) a[iLs] = v;
return a; }
function specGlyph(mags) {
var ref = EPS, iSp, s = '', take, h, blocks = ['·', '▂', '▃', '▅', '█'];
for (iSp = 0; iSp < mags.length; iSp++) if (mags[iSp] > ref) ref = mags[iSp]; take = Math.min(10, mags.length);
for (iSp = 0; iSp < take; iSp++) { h = clamp(Math.round(clamp01(mags[iSp] / ref) * 4), 0, 4); s += blocks[h]; }
return s || '··········'; }
function fbNextPow2(v) { var p = 1; v = Math.max(1, v | 0); while (p < v) p <<= 1; return p; }
function fbBitRev(idx, bits) { var rev = 0, i; for (i = 0; i < bits; i++) { rev = (rev << 1) | (idx & 1); idx >>= 1; } return rev; }
function fbFftComplex(reIn, imIn) {
var nfft = reIn.length;
if (nfft < 2 || (nfft & (nfft - 1))) return { re: reIn.slice(0), im: imIn.slice(0) };
var bits = 0, tmp = nfft; while (tmp > 1) { tmp >>= 1; bits++; }
var re = reIn.slice(0), im = imIn.slice(0);
var i, j, size, half, wr, wi, evenR, evenI, oddR, oddI, tr, ti, k, ang;
for (i = 0; i < nfft; i++) { j = fbBitRev(i, bits); if (j > i) { tmp = re[i]; re[i] = re[j]; re[j] = tmp; tmp = im[i]; im[i] = im[j]; im[j] = tmp; } }
for (size = 2; size <= nfft; size <<= 1) { half = size >> 1;
for (i = 0; i < nfft; i += size) {
for (k = 0; k < half; k++) {
ang = (-2 * Math.PI / size) * k; wr = Math.cos(ang); wi = Math.sin(ang);
evenR = re[i + k]; evenI = im[i + k]; oddR = re[i + k + half]; oddI = im[i + k + half];
tr = wr * oddR - wi * oddI; ti = wr * oddI + wi * oddR;
re[i + k] = evenR + tr; im[i + k] = evenI + ti; re[i + k + half] = evenR - tr; im[i + k + half] = evenI - ti; } } }
return { re: re, im: im };}
function fbFft(signal) {
var nfft = fbNextPow2(signal.length);
var re = make_array(nfft, 0), im = make_array(nfft, 0), i, out, ph = [];
for (i = 0; i < signal.length && i < nfft; i++) re[i] = nz(signal[i], 0); out = fbFftComplex(re, im);
for (i = 0; i < nfft; i++) ph.push([out.re[i], out.im[i]]);
return ph;}
function fbIfft(phasors) {
var nfft = phasors.length, re = [], im = [], i, out, sig = [];
for (i = 0; i < nfft; i++) { re.push(nz(phasors[i][0], 0)); im.push(-nz(phasors[i][1], 0)); }
out = fbFftComplex(re, im);
for (i = 0; i < nfft; i++) sig.push(out.re[i] / nfft);
return sig;}
function fbMag(ph) { var m = [], i, a, b; for (i = 0; i < ph.length; i++) { a = nz(ph[i][0], 0); b = nz(ph[i][1], 0); m.push(Math.sqrt(a * a + b * b)); } return m; }
function fbAng(ph) { var a = [], i; for (i = 0; i < ph.length; i++) a.push(Math.atan2(nz(ph[i][1], 0), nz(ph[i][0], 0))); return a; }
var QC = {};
QC.hann = function (len, i) { if (len <= 1) return 1; return 0.5 * (1 - Math.cos((2 * Math.PI * i) / (len - 1))); };
QC.fft = function (padded) { return LIB_OK ? fftjs.fft(padded) : fbFft(padded); };
QC.ifft = function (ph) { return LIB_OK ? fftjs.ifft(ph) : fbIfft(ph); };
QC.fftMag = function (ph) { return LIB_OK ? fftjs.util.fftMag(ph) : fbMag(ph); };
QC.fftAng = function (ph) { return LIB_OK ? fftjs.util.fftAng(ph) : fbAng(ph); };
QC.spectrum = function (values) {
var nfft = fbNextPow2(values.length), padded = values.slice(0), i;
for (i = values.length; i < nfft; i++) padded.push(0);
var ph = QC.fft(padded);
return { ph: ph, mag: QC.fftMag(ph), ang: QC.fftAng(ph), nfft: nfft }; };
QC.lowpass = function (values, keepFrac) {
var sp = QC.spectrum(values), ph = sp.ph, nfft = sp.nfft, keep, i, rec, out = [];
keep = Math.max(2, Math.floor(nfft * clamp(keepFrac || 0.12, 0.04, 0.45)));
for (i = keep; i < nfft - keep; i++) { ph[i][0] = 0; ph[i][1] = 0; }
rec = QC.ifft(ph);
for (i = 0; i < values.length && i < rec.length; i++) out.push(rec[i]);
return out; };
function fbMean(a) { var s = 0, c = 0, i; for (i = 0; i < a.length; i++) if (isFinite(a[i])) { s += a[i]; c++; } return c ? s / c : 0; }
function fbStdev(a, sample) { var mu = fbMean(a), s = 0, c = 0, i, d; for (i = 0; i < a.length; i++) if (isFinite(a[i])) { d = a[i] - mu; s += d * d; c++; } if (c < 2) return 0; return Math.sqrt(s / (sample ? c - 1 : c)); }
QC.mean = function (a) { return LIB_OK ? jStat.mean(a) : fbMean(a); };
QC.stdev = function (a, sample) { try { return LIB_OK ? jStat.stdev(a, !!sample) : fbStdev(a, sample); } catch (e) { return fbStdev(a, sample); } };
QC.sum = function (a) { var s = 0, i; for (i = 0; i < a.length; i++) s += nz(a[i], 0); return s; };
QC.skewness = function (a) { try { return LIB_OK ? jStat.skewness(a) : 0; } catch (e) { return 0; } };
QC.kurtosis = function (a) { try { return LIB_OK ? jStat.kurtosis(a) : 0; } catch (e) { return 0; } };
QC.corrcoeff = function (a, b) {
var len = Math.min(a.length, b.length), i, xa = [], xb = [];
for (i = 0; i < len; i++) if (isFinite(a[i]) && isFinite(b[i])) { xa.push(a[i]); xb.push(b[i]); }
if (xa.length < 3) return 0;
try { return LIB_OK ? jStat.corrcoeff(xa, xb) : 0; } catch (e) { return 0; } };
QC.percentile = function (arr, p) {
var xs = [], i; for (i = 0; i < arr.length; i++) if (isFinite(arr[i])) xs.push(arr[i]);
if (!xs.length) return 0;
xs.sort(sortNum);
try { if (LIB_OK) return jStat.percentile(xs, clamp(p, 0, 1)); } catch (e) {}
var t = clamp(p, 0, 1) * (xs.length - 1), lo = Math.floor(t), hi = Math.ceil(t);
return lo === hi ? xs[lo] : xs[lo] + (xs[hi] - xs[lo]) * (t - lo); };
QC.percentileOfScore = function (arr, score) {
var xs = [], i; for (i = 0; i < arr.length; i++) if (isFinite(arr[i])) xs.push(arr[i]);
if (!xs.length) return 0;
try { if (LIB_OK) return jStat.percentileOfScore(xs, score); } catch (e) {}
var c = 0; for (i = 0; i < xs.length; i++) if (xs[i] <= score) c++;
return c / xs.length; };
QC.normalCdf = function (x, mean, sd) {
try { if (LIB_OK) return jStat.normal.cdf(x, mean || 0, Math.max(sd || 1, EPS)); } catch (e) {}
return 0.5; };
QC.shannonH = function (weights) {
var tot = QC.sum(weights), i, p, h = 0, c = 0;
if (tot <= EPS) return 1;
for (i = 0; i < weights.length; i++) { p = nz(weights[i], 0) / tot; if (p > EPS) { h -= p * Math.log(p); c++; } }
return c > 1 ? h / Math.log(c) : 1; };
QC.nearestIdx = function (sorted, y) {
if (!sorted.length) return -1;
var i;
try { i = LIB_OK ? bsBounds.ge(sorted, y) : -1; } catch (e) { i = -1; }
if (i < 0) { i = 0; while (i < sorted.length && sorted[i] < y) i++; }
if (i >= sorted.length) return sorted.length - 1;
if (i <= 0) return 0;
return Math.abs(sorted[i] - y) < Math.abs(sorted[i - 1] - y) ? i : i - 1; };
QC.nearestKey = function (sortedKeys, y, maxDist) {
var i = QC.nearestIdx(sortedKeys, y);
if (i < 0) return null;
if (maxDist != null && Math.abs(sortedKeys[i] - y) > maxDist) return null;
return sortedKeys[i]; };
function makeCamera() {
var spinDeg = CAM.spin + (CAM.auto ? (CLOCK.slow * CAM.rate) : 0);
spinDeg = ((spinDeg % 360) + 360) % 360;
var tiltEff = CAM.tilt + (CAM.auto ? 1.1 * Math.sin(CLOCK.slow * 0.30) : 0);
var spinRad = spinDeg * Math.PI / 180, tiltRad = tiltEff * Math.PI / 180;
return {
spinDeg: spinDeg,
cosS: Math.cos(spinRad), sinS: Math.sin(spinRad),
cosT: Math.cos(tiltRad), sinT: Math.sin(tiltRad),
cosS2: Math.cos(spinRad * 0.38), sinS2: Math.sin(spinRad * 0.38),
camDist: 92, mode: CAM.proj }; }
function project3D(cam, x, y, z, parallax) {
var cS = parallax ? cam.cosS2 : cam.cosS, sS = parallax ? cam.sinS2 : cam.sinS;
var rx = x * cS + z * sS, rz = -x * sS + z * cS;
if (cam.mode === 'Perspective') {
var w = cam.camDist / Math.max(cam.camDist + rz, 8);
return { x: rx * w, y: ((rx + rz) * cam.sinT + y) * w, z: rz }; }
return { x: (rx - rz) * cam.cosT, y: (rx + rz) * cam.sinT + y, z: rz }; }
function ringXZ(cam, radius, steps, y0) {
var pts = [], s, t;
for (s = 0; s <= (steps || 56); s++) { t = (s / (steps || 56)) * 2 * Math.PI; pts.push(project3D(cam, radius * Math.cos(t), y0 || 0, radius * Math.sin(t))); }
return pts; }
var LIVE_EDGE_TOL = 2;
function resolveVisibleWindow() {
var bars = null, srcTag = 'FALLBACK';
try {
if (i_visRange && typeof i_visRange.candleIndexFrom === 'number' && typeof i_visRange.candleIndexTo === 'number' && i_visRange.candleIndexTo > i_visRange.candleIndexFrom) {
var start = i_visRange.candleIndexFrom, end = i_visRange.candleIndexTo;
var live = (n - 1 - end) <= LIVE_EDGE_TOL;
if (live) end = n - 1;
start = clamp(Math.floor(start), 0, n - 1);
end = clamp(Math.ceil(end), start + 1, n - 1);
bars = Math.max(1, end - start);
srcTag = 'ANCHOR_VISIBLE_RANGE'; }
} catch (eVr) { bars = null; }
if (bars == null || !isFinite(bars) || bars <= 0) { bars = SURF.lookback; srcTag = 'FALLBACK'; }
bars = clamp(bars, 15, 3000);
var minBars = 25, maxBars = 1500;
var loB = Math.log(minBars), hiB = Math.log(maxBars);
var zoomT = clamp01(safeDiv(Math.log(clamp(bars, minBars, maxBars)) - loB, hiB - loB, 0));
var label = zoomT < 0.33 ? 'TIGHT' : zoomT < 0.66 ? 'STANDARD' : 'WIDE';
return {
bars: bars, zoomT: zoomT, label: label, sourceTag: srcTag,
strikeWindowMult: clamp(lerp(0.65, 1.85, zoomT), 0.60, 1.90),
rowCountAdj: clamp(lerp(0.85, 1.25, zoomT), 0.80, 1.30),
stepMult: clamp(lerp(0.70, 2.20, zoomT), 0.65, 2.30) };}
var CASCADE = resolveVisibleWindow();
// ============================================================================
// ENTROPY
// ============================================================================
var upBars = make(n, 0);
var entropy = make(n, 0);
var hlcc4_wma = wma(hlcc4, 3);
for (i = 1; i < n; i++) upBars[i] = (hlcc4_wma[i] > hlcc4_wma[i - 1]) ? 1 : 0;
var pUpSeries = sma(upBars, adaptivePeriod);
for (i = 0; i < n; i++) {
var pE = isNaN(pUpSeries[i]) ? 0.5 : pUpSeries[i]; entropy[i] = (pE > 0 && pE < 1) ? (-pE * Math.log(pE) - (1 - pE) * Math.log(1 - pE)) / Math.log(2) : 0;}
function makeRollingPercentile(maxLen) {
var sorted = [], queue = [];
return function push(value) {
var insertIdx = bsBounds.ge(sorted, value); sorted.splice(insertIdx, 0, value); queue.push(value);
var removedIdx = -1;
if (queue.length > maxLen) { var old = queue.shift(); removedIdx = bsBounds.eq(sorted, old); if (removedIdx >= 0) sorted.splice(removedIdx, 1); }
var rankIdx = insertIdx;
if (removedIdx >= 0 && removedIdx < insertIdx) rankIdx -= 1;
var len = sorted.length;
return len > 1 ? (rankIdx / (len - 1)) * 100 : 50;};}
var entropyPercentileArr = make(n, 50);
var entropyCalibLookback = 200;
{ var tracker = makeRollingPercentile(entropyCalibLookback);
for (i = 0; i < n; i++) entropyPercentileArr[i] = isNaN(entropy[i]) ? 50 : tracker(entropy[i]);}
var entropyPercentile = 40;
var effEntropyPercentile = make(n, 40);
for (i = 0; i < n; i++) effEntropyPercentile[i] = entropyPercentile;
// ============================================================================
// TREND + VOLUME + FRACTAL + PIVOT
// ============================================================================
var trendUp = make(n, false), trendDown = make(n, false);
for (i = 0; i < n; i++) { trendUp[i] = close[i] > ema21[i]; trendDown[i] = close[i] < ema21[i]; }
var volAvg = sma(volume, 20);
var enableVol = true;
var volMultiplier = 1.5;
var volSpike = make(n, false);
for (i = 0; i < n; i++) volSpike[i] = enableVol ? (volume[i] > volMultiplier * (volAvg[i] > 0 ? volAvg[i] : 1)) : true;
var fractalHigh = make(n, false), fractalLow = make(n, false);
for (i = 2; i < n - 2; i++) {
fractalHigh[i] = high[i] > high[i - 1] && high[i] > high[i + 1] && high[i - 1] > high[i - 2] && high[i + 1] > high[i + 2];
fractalLow[i] = low[i] < low[i - 1] && low[i] < low[i + 1] && low[i - 1] < low[i - 2] && low[i + 1] < low[i + 2];}
var pivotHigh = make(n, false), pivotLow = make(n, false);
for (i = 2; i < n; i++) {
pivotHigh[i] = high[i - 1] > high[i - 2] && high[i - 1] > high[i];
pivotLow[i] = low[i - 1] < low[i - 2] && low[i - 1] < low[i];}
// ============================================================================
// WICK PRESSURE KERNEL (WPK)
// ============================================================================
var WPK = make(n, 0);
for (i = 0; i < n; i++) {
var sup = 0.0, dem = 0.0;
var t0 = Math.max(0, i - 30 + 1);
for (var jW = t0; jW <= i; jW++) {
var ageW2 = i - jW;
var wAge = Math.exp(-ageW2 / 60);
var aW2 = atr14[jW] || 1e-8;
var dUp = Math.exp(-Math.abs(close[i] - high[jW]) / (1.5 * aW2));
var dDn = Math.exp(-Math.abs(close[i] - low[jW]) / (1.5 * aW2));
var jBodyTop = Math.max(open[jW], close[jW]);
var jBodyBot = Math.min(open[jW], close[jW]);
var jUpper = Math.max(0, high[jW] - jBodyTop) / Math.max(1e-8, high[jW] - low[jW]);
var jLower = Math.max(0, jBodyBot - low[jW]) / Math.max(1e-8, high[jW] - low[jW]);
sup += wAge * dUp * jUpper; dem += wAge * dDn * jLower;}
WPK[i] = dem - sup;}
var meanW = sma(WPK, adaptivePeriod);
var devW = stdev(WPK, adaptivePeriod);
var WPKz = make(n, 0);
for (i = 0; i < n; i++) WPKz[i] = (devW[i] > 0) ? (WPK[i] - meanW[i]) / devW[i] : 0;
// ============================================================================
// CONFIDENCE LOGIC
// ============================================================================
var i_tradeMode = 'Hybrid';
var i_predictiveMode = true;
var i_enableLiveSignals = true;
var i_confirmAtr = 0.25;
var eagerEntry = true;
var adaptiveEngineOn = true;
var adaptiveMemory = 20;
var adaptiveFeedbackClamp = 10;
var adaptiveEvalHorizon = 12;
var effThresholdPeak = 55, effThresholdValley = 55;
var i_enableSpectral = true;
var spectralOrderArr = make(n, 0.5);
var lowestLowPrior = make(n, null), highestHighPrior = make(n, null);
for (i = 1; i < n; i++) {
var loW = [], hiW = [];
for (var jP = Math.max(0, i - adaptivePeriod); jP < i; jP++) { loW.push(low[jP]); hiW.push(high[jP]); }
lowestLowPrior[i] = loW.length ? Math.min.apply(null, loW) : null;
highestHighPrior[i] = hiW.length ? Math.max.apply(null, hiW) : null;}
var peakSignals = make(n, null), valleySignals = make(n, null);
var peakProbs = make(n, null), valleyProbs = make(n, null);
var peakTentative = make(n, false), valleyTentative = make(n, false);
var peakSignalSource = make(n, null), valleySignalSource = make(n, null);
var peakOutcomeQueue = [], valleyOutcomeQueue = [];
var recentPeakResults = [], recentValleyResults = [];
var peakWinRate = 0.5, valleyWinRate = 0.5;
var peakAdaptiveAdj = 0, valleyAdaptiveAdj = 0;
var shortConf = make(n, false);
var lastSignalType = 'None';
var lastPeakPrice = null, lastPeakVel = null;
var lastValleyPrice = null, lastValleyVel = null;
var lastPeakSignalBar = -999, lastValleySignalBar = -999;
function pushCapped(arr, val, cap) { arr.push(val); if (arr.length > cap) arr.shift(); }
function rollingWinRate(arr) { if (!arr.length) return 0.5; var s = 0; for (var q = 0; q < arr.length; q++) if (arr[q]) s++; return s / arr.length; }
var sigStart = Math.max(adaptivePeriod + 10, 5);
for (i = sigStart; i < n; i++) {
if (isNaN(entropy[i])) continue;
if (adaptiveEngineOn) {
while (peakOutcomeQueue.length && peakOutcomeQueue[0].resolveAt <= i) {
var pendP = peakOutcomeQueue.shift();
pushCapped(recentPeakResults, close[i] < pendP.price, adaptiveMemory);}
while (valleyOutcomeQueue.length && valleyOutcomeQueue[0].resolveAt <= i) {
var pendV = valleyOutcomeQueue.shift();
pushCapped(recentValleyResults, close[i] > pendV.price, adaptiveMemory);}
peakWinRate = rollingWinRate(recentPeakResults);
valleyWinRate = rollingWinRate(recentValleyResults);
peakAdaptiveAdj = clamp((0.5 - peakWinRate) * 30, -adaptiveFeedbackClamp, adaptiveFeedbackClamp);
valleyAdaptiveAdj = clamp((0.5 - valleyWinRate) * 30, -adaptiveFeedbackClamp, adaptiveFeedbackClamp);}
else { peakAdaptiveAdj = 0; valleyAdaptiveAdj = 0; }
var isPeakEvent = pivotHigh[i];
var isValleyEvent = pivotLow[i];
var atrHere = atr14[i] || 1e-8;
var buf = i_confirmAtr * atrHere;
var statePeak = high[i] >= highest(high, adaptivePeriod)[i] - buf;
var stateValley = low[i] <= lowest(low, adaptivePeriod)[i] + buf;
var bearDiv = isPeakEvent && lastPeakPrice != null && high[i] > lastPeakPrice && velocity[i] < lastPeakVel;
var bullDiv = isValleyEvent && lastValleyPrice != null && low[i] < lastValleyPrice && velocity[i] > lastValleyVel;
var enoughDistPeak = (i - lastPeakSignalBar) >= minSignalDistance;
var enoughDistValley = (i - lastValleySignalBar) >= minSignalDistance;
var wpkVal = WPKz[i] || 0;
var trendStrNorm = Math.abs(velocity[i] || 0) / atrHere;
var accNorm = (acceleration[i] || 0) / atrHere;
var barRangeNorm = atrHere > 0 ? (high[i] - low[i]) / atrHere : 1;
var avgVolRatio = volAvg[i] > 0 ? volume[i] / volAvg[i] : 1;
var entropyOk = entropyPercentileArr[i] <= effEntropyPercentile[i];
var entropyGatePeak = eagerEntry ? true : entropyOk;
var entropyGateValley = eagerEntry ? true : entropyOk;
var qualityBoostPeak = eagerEntry ? (1 + (entropyOk ? 0.15 : 0)) : 1;
var qualityBoostValley = eagerEntry ? (1 + (entropyOk ? 0.15 : 0)) : 1;
var trendIsUp = trendUp[i], trendIsDown = trendDown[i];
var peakCondition1 = (i_tradeMode === 'Reversal' || i_tradeMode === 'Hybrid') && isPeakEvent && close[i] < low[i - 1] - buf && trendIsUp && entropyGatePeak;
var peakCondition2 = (i_tradeMode === 'Breakout' || i_tradeMode === 'Hybrid') && lowestLowPrior[i] != null && close[i] < lowestLowPrior[i] && volSpike[i];
var peakConditionPredictive = i_predictiveMode && acceleration[i] < 0 && statePeak && entropyGatePeak;
if ((peakCondition1 || peakCondition2 || peakConditionPredictive) && enoughDistPeak) {
var orderlinessScore = clamp(24 * (1 - entropyPercentileArr[i] / 100), 0, 24);
var trendScore = clamp((trendStrNorm / 2.5) * 20, 0, 20);
var divScore = bearDiv ? clamp(16 + Math.abs(accNorm) * 8, 16, 22) : 0;
var volScore = volSpike[i] ? clamp(6 + (avgVolRatio - 1) * 4, 6, 12) : 0;
var wpkConfidence = jStat.normal.cdf(Math.abs(Math.max(0, -wpkVal)), 0, 1) - 0.5;
var wpkScoreVal = clamp(wpkConfidence * 2 * 12, 0, 15);
var adaptiveAdj = clamp(peakAdaptiveAdj || 0, -adaptiveFeedbackClamp, adaptiveFeedbackClamp);
var variation = clamp((barRangeNorm - 1) * 3, -3, 3);
var spectralBonus = i_enableSpectral ? clamp((spectralOrderArr[i] - 0.5) * 10, -5, 5) : 0;
var rawP = 34 + orderlinessScore + trendScore + divScore + volScore + wpkScoreVal + adaptiveAdj + variation + spectralBonus; rawP *= (qualityBoostPeak || 1);
var probP = clamp(Math.round(rawP), 0, 99);
if (probP >= effThresholdPeak) {
var tentativeP = i_enableLiveSignals && i === n - 1;
var sourceP;
if (peakCondition2) sourceP = 'breakout';
else if (peakConditionPredictive) sourceP = 'predictive';
else if (peakCondition1 && pivotHigh[i - 1]) sourceP = 'pivot';
else sourceP = 'fractal';
peakSignals[i] = high[i]; peakProbs[i] = probP; peakTentative[i] = tentativeP;
peakSignalSource[i] = sourceP;
lastPeakSignalBar = i; lastSignalType = 'Peak';
if (adaptiveEngineOn && !tentativeP) peakOutcomeQueue.push({ price: high[i], resolveAt: i + adaptiveEvalHorizon });}}
if (isPeakEvent) { lastPeakPrice = high[i]; lastPeakVel = velocity[i]; }
var valleyCondition1 = (i_tradeMode === 'Reversal' || i_tradeMode === 'Hybrid') && isValleyEvent && close[i] > high[i - 1] + buf && trendIsDown && entropyGateValley;
var valleyCondition2 = (i_tradeMode === 'Breakout' || i_tradeMode === 'Hybrid') && highestHighPrior[i] != null && close[i] > highestHighPrior[i] && volSpike[i];
var valleyConditionPredictive = i_predictiveMode && acceleration[i] > 0 && stateValley && entropyGateValley;
if ((valleyCondition1 || valleyCondition2 || valleyConditionPredictive) && enoughDistValley) {
var orderlinessScore2 = clamp(24 * (1 - entropyPercentileArr[i] / 100), 0, 24);
var trendScore2 = clamp((trendStrNorm / 2.5) * 20, 0, 20);
var divScore2 = bullDiv ? clamp(16 + Math.abs(accNorm) * 8, 16, 22) : 0;
var volScore2 = volSpike[i] ? clamp(6 + (avgVolRatio - 1) * 4, 6, 12) : 0;
var wpkConfidence2 = jStat.normal.cdf(Math.abs(Math.max(0, wpkVal)), 0, 1) - 0.5;
var wpkScoreVal2 = clamp(wpkConfidence2 * 2 * 12, 0, 15);
var adaptiveAdj2 = clamp(valleyAdaptiveAdj || 0, -adaptiveFeedbackClamp, adaptiveFeedbackClamp);
var spectralBonus2 = i_enableSpectral ? clamp((spectralOrderArr[i] - 0.5) * 10, -5, 5) : 0;
var rawV = 34 + orderlinessScore2 + trendScore2 + divScore2 + volScore2 + wpkScoreVal2 + adaptiveAdj2 + variation + spectralBonus2; rawV *= (qualityBoostValley || 1);
var probV = clamp(Math.round(rawV), 0, 99);
if (probV >= effThresholdValley) {
var tentativeV = i_enableLiveSignals && i === n - 1;
var sourceV;
if (valleyCondition2) sourceV = 'breakout';
else if (valleyConditionPredictive) sourceV = 'predictive';
else if (valleyCondition1 && pivotLow[i - 1]) sourceV = 'pivot';
else sourceV = 'fractal';
valleySignals[i] = low[i]; valleyProbs[i] = probV; valleyTentative[i] = tentativeV;
valleySignalSource[i] = sourceV;
lastValleySignalBar = i; lastSignalType = 'Valley';
if (adaptiveEngineOn && !tentativeV) valleyOutcomeQueue.push({ price: low[i], resolveAt: i + adaptiveEvalHorizon });}}
if (isValleyEvent) { lastValleyPrice = low[i]; lastValleyVel = velocity[i]; }}
// ============================================================================
// ADVANCED SYNTHETIC MODEL
// ============================================================================
var currentPhase = 'FLOW';
var synPCRLast = 1.0;
var relVolArr = make(n, 1);
for (i = 0; i < n; i++) relVolArr[i] = (volAvg[i] > 0) ? (volume[i] || 0) / volAvg[i] : 1;
function fillSyntheticSurface(widen, oiScale) {
var spot = close[lastIdx], atrV = Math.max(nz(atr14[lastIdx], 1), EPS);
var tBias = ema21[lastIdx] >= ema55[lastIdx] ? 1 : -1;
var phaseBias = currentPhase === 'FLOW' ? 0.18 : currentPhase === 'EXPANSION' ? -0.14 : 0.00;
var pcrBias = clamp((synPCRLast - 1) / 1.4, -0.65, 0.65), volBias = clamp01(relVolArr[lastIdx] / 2.5), memBias = clamp01(SYN.memory);
var winSR = localPriceWindow(spot, atrV);
var strikeRange = Math.max((winSR.hi - winSR.lo) / 2, atrV * 3);
var strikeStep = inferPriceStep(spot, atrV, strikeRange * 2);
var strikeLo = roundToStep(spot - strikeRange, strikeStep);
var strikeHi = roundToStep(spot + strikeRange, strikeStep);
var roundStep = niceNum(Math.max(strikeStep * 5, spot * 0.002));
var hiLook = -Infinity, loLook = Infinity, i6;
for (i6 = Math.max(0, n - SURF.lookback); i6 < n; i6++) { if (high[i6] > hiLook) hiLook = high[i6]; if (low[i6] < loLook) loLook = low[i6]; }
var anchors = [];
function addAnchor(p, w, s) {
if (isFinite(p) && p >= strikeLo - strikeStep && p <= strikeHi + strikeStep)
anchors.push({ p: p, w: w, s: Math.max(s || 1, EPS) }); }
var sigmaBase = Math.max(atrV * 1.8, strikeStep * 2.5, 1) * Math.max(widen, 0.6);
var aggr = clamp(SYN.aggression, 0.80, 2.50);
addAnchor(spot, 1.45 * aggr, sigmaBase * 0.90 * memBias);
addAnchor(vwapArr[lastIdx], 1.20 * aggr, sigmaBase * 1.10);
addAnchor(ema21[lastIdx], 1.00 * aggr, sigmaBase * 1.18);
addAnchor(ema55[lastIdx], 0.92 * aggr, sigmaBase * 1.32);
addAnchor(ema200[lastIdx], 0.72 * aggr, sigmaBase * 1.45);
addAnchor(hiLook, 0.75 * aggr, sigmaBase * 1.20);
addAnchor(loLook, 0.75 * aggr, sigmaBase * 1.20);
addAnchor(open[lastIdx], 0.78 * aggr, sigmaBase * 1.05);
addAnchor(spot + atrV * 2, 0.60 * aggr, sigmaBase * 1.05);
addAnchor(spot - atrV * 2, 0.60 * aggr, sigmaBase * 1.05);
var roundBase = roundToStep(spot, roundStep);
addAnchor(roundBase, 1.20 * aggr, sigmaBase * 0.95);
addAnchor(roundBase + roundStep, 0.68 * aggr, sigmaBase * 0.95);
addAnchor(roundBase - roundStep, 0.68 * aggr, sigmaBase * 0.95);
var surface = {}, kWalk;
for (kWalk = strikeLo; kWalk <= strikeHi + EPS; kWalk += strikeStep) {
var K = Math.round(kWalk * 100) / 100, base = 0, ai;
for (ai = 0; ai < anchors.length; ai++) base += anchors[ai].w * gaussian(K, anchors[ai].p, anchors[ai].s);
var roundMag = gaussian(K, roundToStep(K, roundStep), strikeStep * 1.20);
var centerMag = gaussian(K, spot + tBias * atrV * 0.10, sigmaBase * 0.95);
var sessionPulse = gaussian(K, hiLook, sigmaBase * 1.20) + gaussian(K, loLook, sigmaBase * 1.20);
var wave = 1 + 0.05 * Math.sin((K - spot) / Math.max(strikeStep, 1) * 1.6 + tBias * 0.75) + 0.03 * Math.cos((K - spot) / Math.max(strikeStep, 1) * 0.9);
var pressureSeed = (base * 0.66 + centerMag * 0.18 + roundMag * 0.10 + sessionPulse * 0.06); pressureSeed = Math.pow(Math.max(pressureSeed, EPS), 0.88) * wave * aggr * (1 + volBias * 0.28) * oiScale;
var above = K >= spot ? 1 : 0;
var callTilt = clamp(0.48 + 0.20 * above + 0.10 * tBias + 0.08 * phaseBias - 0.10 * pcrBias + 0.05 * volBias, 0.12, 0.92);
var putTilt = clamp(0.52 + 0.20 * (1 - above) - 0.10 * tBias - 0.08 * phaseBias + 0.10 * pcrBias + 0.04 * (1 - volBias), 0.12, 0.92);
var callOI = pressureSeed * 10000 * callTilt, putOI = pressureSeed * 10000 * putTilt; surface[K] = { callOI: callOI, putOI: putOI, totalOI: callOI + putOI }; }
return surface;}
function buildAdvancedSyntheticModel() {
var nExp = Math.max(3, Math.min(SURF.numExp, 8)), slices = [], surface = {}, e, ki, K;
for (e = 0; e < nExp; e++) {
var widen = 1 + e * 0.32, oiScale = 1 - e * 0.09;
var sliceSurf = fillSyntheticSurface(widen, Math.max(oiScale, 0.35));
var totOI = 0, keys = Object.keys(sliceSurf);
for (ki = 0; ki < keys.length; ki++) totOI += sliceSurf[keys[ki]].totalOI; slices.push({ dte: 7 * (e + 1), label: (7 * (e + 1)) + 'D', surface: sliceSurf, totalOI: totOI });
for (ki = 0; ki < keys.length; ki++) { K = Number(keys[ki]);
if (!surface[K]) surface[K] = { callOI: 0, putOI: 0, totalOI: 0 }; surface[K].callOI += sliceSurf[K].callOI; surface[K].putOI += sliceSurf[K].putOI; surface[K].totalOI += sliceSurf[K].totalOI; } }
return finalizeStrikeSurface(surface, { sourceTag: 'SHADOW', sourceText: 'ADVANCED SYNTHETIC', note: 'Synthetic tenor surface — live chain unavailable', slices: slices }, close[lastIdx], nz(atr14[lastIdx], 1));}
// ============================================================================
// LIVE OPTIONS MODEL
// ============================================================================
async function buildLiveOptionsModel() {
var surface = {}, slices = [];
var schedule = null;
try { schedule = await request.options_schedule(constants.ticker); }
catch (e) { return { ok: false, error: String(e) }; }
if (!schedule || schedule.length === 0) return { ok: false, error: 'No options schedule available' };
var sorted = schedule.slice(0).sort(function (a, b) {
return nz(a && a.expiration && a.expiration.dte, 0) - nz(b && b.expiration && b.expiration.dte, 0);});
// request budget: 1 options_schedule + ≤ REQ_BUDGET_O − 1 per-expiry chains — the platform allows 6 "other" request.* calls per script, so the cap is explicit, not implied by the input list
var REQ_BUDGET_O = 6, targets = sorted.slice(0, Math.max(0, Math.min(numExp, REQ_BUDGET_O - 1, sorted.length)));
var liveExpCount = 0, failedExp = 0;
var aggCallOI = 0, aggPutOI = 0;
var sliceSurfaces = [];
// all expirations are requested in parallel (one round-trip instead of numExp sequential ones); a failed expiry resolves to null and is skipped exactly as before
var jobs = [], ti;
for (ti = 0; ti < targets.length; ti++) { (function (expInfo) {
var pr; if (!expInfo || !expInfo.code) pr = Promise.resolve(null); else { try { pr = Promise.resolve(request.options_data_for_expiration(constants.ticker, expInfo.code, ['oi', 'oic', 'dvol'])); } catch (eSync) { pr = Promise.resolve(null); } }
jobs.push(pr.then(function (d) { return d; }, function (eExp) { return null; })); })(targets[ti] && targets[ti].expiration); }
var chains = await Promise.all(jobs);
for (ti = 0; ti < targets.length; ti++) {
var expInfo = targets[ti] && targets[ti].expiration;
if (!expInfo || !expInfo.code) continue;
var chainData = chains[ti];
if (!chainData || !chainData.resultByStrike) { failedExp++; continue; } liveExpCount++;
var keys = Object.keys(chainData.resultByStrike);
var sliceSurf = {};
var sCall = 0, sPut = 0;
for (var ki = 0; ki < keys.length; ki++) {
var K = Number(keys[ki]);
if (!isFinite(K)) continue;
var entry = chainData.resultByStrike[keys[ki]];
var callOI = nz(entry && entry.C && entry.C.oi, 0);
var putOI = nz(entry && entry.P && entry.P.oi, 0);
var callOic = nz(entry && entry.C && entry.C.oic, 0);
var putOic = nz(entry && entry.P && entry.P.oic, 0);
var callDvol = nz(entry && entry.C && entry.C.dvol, 0);
var putDvol = nz(entry && entry.P && entry.P.dvol, 0);
sliceSurf[K] = { callOI: callOI, putOI: putOI, totalOI: callOI + putOI, callOIC: callOic, putOIC: putOic, callDVol: callDvol, putDVol: putDvol };
if (!surface[K]) surface[K] = { callOI: 0, putOI: 0, totalOI: 0, callOic: 0, putOic: 0, dvol: 0 };
surface[K].callOI += callOI;
surface[K].putOI += putOI;
surface[K].totalOI += callOI + putOI;
surface[K].callOic += callOic;
surface[K].putOic += putOic;
surface[K].dvol += callDvol + putDvol;
aggCallOI += callOI;
aggPutOI += putOI; sCall += callOI; sPut += putOI;}
slices.push({ dte: nz(expInfo.dte, ti * 7), label: (nz(expInfo.dte, ti * 7) | 0) + 'D', surface: sliceSurf, totalOI: sCall + sPut }); }
if (Object.keys(surface).length === 0) return { ok: false, error: 'Live chain fetched but empty' };
var model = finalizeStrikeSurface(surface, {
sourceTag: 'LIVE',
sourceText: 'LIVE OPTIONS',
note: liveExpCount + ' expirations loaded' + (failedExp ? ' · ' + failedExp + ' failed' : ''),
slices: slices
}, close[lastIdx], nz(atr14[lastIdx], 1));
model.liveExpCount = liveExpCount; model.ok = true;
return model;}
// ============================================================================
// SLF MODEL
// ============================================================================
function histogramSlice(start, end, pLo, rowH, rowCount) {
var rowBuy = make(rowCount, 0), rowSell = make(rowCount, 0), rowTotal = make(rowCount, 0), i4, r;
for (i4 = start; i4 < end; i4++) {
var loRow = clamp(Math.floor((low[i4] - pLo) / rowH), 0, rowCount - 1);
var hiRow = clamp(Math.floor((high[i4] - pLo) / rowH), 0, rowCount - 1);
var span = Math.max(1, hiRow - loRow + 1), v = nz(volume[i4], 0) / span;
var buyFrac = clamp01((deltaProxyArr[i4] + 1) / 2), buyV = v * buyFrac, sellV = v * (1 - buyFrac);
for (r = loRow; r <= hiRow; r++) { rowBuy[r] += buyV; rowSell[r] += sellV; rowTotal[r] += v; } }
var surface = {};
for (r = 0; r < rowCount; r++) { var px = pLo + (r + 0.5) * rowH; surface[px] = { callOI: rowBuy[r], putOI: rowSell[r], totalOI: rowTotal[r] }; }
return { surface: surface, rowBuy: rowBuy, rowSell: rowSell, rowTotal: rowTotal };}
function buildProfileModel() {
var lookback = Math.min(300, n);
var start = Math.max(0, n - lookback);
var pHi = -Infinity, pLo = Infinity;
for (var i = start; i < n; i++) { if (high[i] > pHi) pHi = high[i]; if (low[i] < pLo) pLo = low[i]; }
if (!isFinite(pHi) || !isFinite(pLo)) { pHi = close[lastIdx]; pLo = close[lastIdx]; }
var step = inferPriceStep(close[lastIdx], nz(atr14[lastIdx], 1), pHi - pLo);
pLo = Math.floor(pLo / step) * step;
pHi = Math.ceil(pHi / step) * step;
if (pHi <= pLo) pHi = pLo + step * 8;
var rowH = inferPriceStep(close[lastIdx], nz(atr14[lastIdx], 1), pHi - pLo);
if (rowH < step) rowH = step;
rowH = Math.max(step, Math.round(rowH / step) * step);
var rowCount = clamp(Math.round((pHi - pLo) / rowH), 8, 36);
rowH = (pHi - pLo) / Math.max(rowCount, 1);
PRICE_STEP = step;
var nSlices = Math.max(3, Math.min(SURF.numExp, 8)), sliceBars = Math.max(8, Math.floor(Math.min(SURF.lookback, n) / nSlices));
var slices = [], s;
for (s = 0; s < nSlices; s++) {
var sEnd = n - (nSlices - 1 - s) * sliceBars, sStart = Math.max(0, sEnd - sliceBars);
var hist = histogramSlice(sStart, sEnd, pLo, rowH, rowCount);
slices.push({ dte: (nSlices - 1 - s) * sliceBars, label: s === nSlices - 1 ? 'NOW' : 'T-' + (nSlices - 1 - s), surface: hist.surface, totalOI: hist.rowTotal.reduce(function(a,b){return a+b;}, 0) }); }
var rowPrice = make(rowCount, 0), rowBuy = make(rowCount, 0), rowSell = make(rowCount, 0), rowTotal = make(rowCount, 0), rowDelta = make(rowCount, 0);
for (var r = 0; r < rowCount; r++) rowPrice[r] = roundToStep(pLo + (r + 0.5) * rowH, step);
for (i = start; i < n; i++) {
var loRow = clamp(Math.floor((low[i] - pLo) / rowH), 0, rowCount - 1);
var hiRow = clamp(Math.floor((high[i] - pLo) / rowH), 0, rowCount - 1);
var span = Math.max(1, hiRow - loRow + 1);
var v = nz(volume[i], 0) / span;
var barSpan = high[i] - low[i];
var buyFrac = barSpan > EPS ? clamp01((close[i] - low[i]) / barSpan) : 0.5;
var sellFrac = 1 - buyFrac;
var buyV = v * buyFrac, sellV = v * sellFrac;
for (var r2 = loRow; r2 <= hiRow; r2++) { rowBuy[r2] += buyV; rowSell[r2] += sellFrac > 0 ? sellV : 0; rowTotal[r2] += v; rowDelta[r2] += buyV - sellV; }}
var totalVol = 0, totalBuy = 0, totalSell = 0;
for (r = 0; r < rowCount; r++) { totalVol += rowTotal[r]; totalBuy += rowBuy[r]; totalSell += rowSell[r]; }
var pocRow = 0, pocMax = -Infinity;
for (r = 0; r < rowCount; r++) if (rowTotal[r] > pocMax) { pocMax = rowTotal[r]; pocRow = r; }
var left = pocRow, right = pocRow, acc = rowTotal[pocRow], target = totalVol * 0.70;
while (acc < target && (left > 0 || right < rowCount - 1)) {
var lv = left > 0 ? rowTotal[left - 1] : -1, rv = right < rowCount - 1 ? rowTotal[right + 1] : -1;
if (rv >= lv) { right++; acc += Math.max(rv, 0); } else { left--; acc += Math.max(lv, 0); }}
var pocPrice = roundToStep(rowPrice[pocRow], step), valPrice = roundToStep(pLo + left * rowH, step), vahPrice = roundToStep(pLo + (right + 1) * rowH, step);
var curRow = clamp(Math.floor((close[lastIdx] - pLo) / rowH), 0, rowCount - 1);
var rows = [];
for (var rr = rowCount - 1; rr >= 0; rr--) {
var isPOC = rr === pocRow;
var isVAH = Math.abs(rowPrice[rr] - vahPrice) <= rowH * 0.45;
var isVAL = Math.abs(rowPrice[rr] - valPrice) <= rowH * 0.45;
var isCUR = rr === curRow;
rows.push({ level: rowPrice[rr], label: isPOC ? 'POC' : isVAH ? 'VAH' : isVAL ? 'VAL' : isCUR ? 'CUR' : 'MID', leftVal: rowSell[rr], rightVal: rowBuy[rr], totalVal: rowTotal[rr], deltaVal: rowDelta[rr], isATM: false, isPOC: isPOC, isVAH: isVAH, isVAL: isVAL, isCUR: isCUR });}
var refsA = [], refsB = [], refsT = [], refsD = [];
for (i = 0; i < rows.length; i++) { refsA.push(rows[i].leftVal); refsB.push(rows[i].rightVal); refsT.push(rows[i].totalVal); refsD.push(Math.abs(rows[i].deltaVal)); }
return { ok: true, kind: 'SLF', sourceTag: 'SLF', sourceText: 'SLF', note: 'Price profile mode',
currentPrice: close[lastIdx], pcr: totalBuy > 0 ? totalSell / totalBuy : 99,
bias: safeDiv(totalBuy - totalSell, totalBuy + totalSell, 0),
leftName: 'SELLS', rightName: 'BUYS', surfaceCount: rowCount, rowCount: rows.length, rows: rows,
slices: slices,
pocStrike: pocPrice, vahStrike: vahPrice, valStrike: valPrice, curStrike: close[lastIdx],
rails: [{ name: 'POC', value: pocPrice, color: T.gold, label: 'POC ' + fmtPrice(pocPrice) },
{ name: 'VAH', value: vahPrice, color: T.bull, label: 'VAH ' + fmtPrice(vahPrice) },
{ name: 'VAL', value: valPrice, color: T.bear, label: 'VAL ' + fmtPrice(valPrice) }],
leftTotal: totalSell, rightTotal: totalBuy, allTotal: totalVol, refs: { leftRef: percentile(refsA, 0.80), rightRef: percentile(refsB, 0.80), totalRef: percentile(refsT, 0.80), deltaRef: percentile(refsD, 0.80) } };}
var deltaProxy = 0;
function analyzeSurface(mdl, atrV, spot) {
var lab = { cog: spot, cogATR: 0, skew: 0, kurt: 0, entropy: 1, putCallR: 0, pocZ: 0, structure: 0, specBars: '··········', domPeriodATR: 0, cycleBars: 0, cyclePhase: CLOCK.phase12, cycleConf: 0 };
var rows = mdl.rows || [];
if (!rows.length) return lab;
var mass = [], lefts = [], rights = [], levels = [], wsum = 0, wlev = 0, i7;
for (i7 = 0; i7 < rows.length; i7++) { mass.push(nz(rows[i7].totalVal, 0)); lefts.push(nz(rows[i7].leftVal, 0)); rights.push(nz(rows[i7].rightVal, 0)); levels.push(rows[i7].level); wsum += nz(rows[i7].totalVal, 0); wlev += rows[i7].level * nz(rows[i7].totalVal, 0); }
lab.cog = wsum > EPS ? wlev / wsum : spot; lab.cogATR = (lab.cog - spot) / Math.max(atrV, EPS); lab.skew = QC.skewness(mass); lab.kurt = QC.kurtosis(mass); lab.entropy = QC.shannonH(mass); lab.putCallR = QC.corrcoeff(lefts, rights);
var mu = QC.mean(mass), sd = QC.stdev(mass, true), pocMass = 0, ri;
for (ri = 0; ri < rows.length; ri++) if (rows[ri].isPOC) pocMass = nz(rows[ri].totalVal, 0);
lab.pocZ = sd > EPS ? (pocMass - mu) / sd : 0;
try {
var win = mass.slice(0), wi;
for (wi = 0; wi < win.length; wi++) win[wi] = win[wi] * QC.hann(win.length, wi);
var sp = QC.spectrum(win), mags = sp.mag, half = Math.floor(mags.length / 2);
var band = [], bi, totB = 0, peakB = 0, peakI = 1;
for (bi = 1; bi < half; bi++) { band.push(mags[bi]); totB += mags[bi]; if (mags[bi] > peakB) { peakB = mags[bi]; peakI = bi; } }
lab.structure = totB > EPS ? peakB / totB : 0;
lab.specBars = specGlyph(band.slice(0, 10));
var dK = 1;
if (levels.length > 1) { var diffs = []; for (wi = 1; wi < levels.length; wi++) diffs.push(Math.abs(levels[wi] - levels[wi - 1])); diffs.sort(sortNum); dK = Math.max(diffs[Math.floor(diffs.length * 0.25)] || 1, EPS); }
lab.domPeriodATR = (mags.length * dK / Math.max(peakI, 1)) / Math.max(atrV, EPS);
} catch (eSpec) {}
try {
var Nfft = 64, rs = [], rsStart = Math.max(1, n - Nfft), ri2;
for (ri2 = rsStart; ri2 < n; ri2++) rs.push(nz(logRet[ri2], 0) * QC.hann(Math.min(Nfft, n - rsStart), ri2 - rsStart));
if (rs.length >= 16) {
var sp2 = QC.spectrum(rs), pm = sp2.mag, phalf = Math.floor(pm.length / 2);
var pTot = 0, pPeak = 0, pI = 2, pj;
for (pj = 1; pj < phalf; pj++) { pTot += pm[pj]; if (pj >= 2 && pj <= Math.floor(phalf * 0.35) && pm[pj] > pPeak) { pPeak = pm[pj]; pI = pj; } }
lab.cycleConf = pTot > EPS ? pPeak / pTot : 0; lab.cycleBars = pm.length / Math.max(pI, 1);
var angs = sp2.ang; lab.cyclePhase = nz(angs[pI], 0) + (2 * Math.PI * (n % Math.max(lab.cycleBars, 1))) / Math.max(lab.cycleBars, 1);}
} catch (eCyc) {}
return lab;}
// ============================================================================
// SMART AUTO RESOLVER
// ============================================================================
async function resolveModel() {
if (i_sourceMode === 'SLF') return buildProfileModel();
if (i_sourceMode === 'Advanced Synthetic') return buildAdvancedSyntheticModel();
if (i_sourceMode === 'Live Options') {
var live = await buildLiveOptionsModel();
if (live && live.ok) return live;
var synth = buildAdvancedSyntheticModel(); synth.note = 'Live Options requested but unavailable → Advanced Synthetic';
return synth;}
var profile = detectInstrumentProfile();
if (profile.isFutures || profile.isIndex || profile.isCrypto || profile.isForex) {
var slfModel = buildProfileModel(); slfModel.note = 'Auto: ' + profile.reason + ' → SLF selected';
return slfModel;}
if (profile.hasOptionsChain) {
var autoLive = await buildLiveOptionsModel();
if (autoLive && autoLive.ok) { autoLive.note = 'Auto: EQUITY_OPTIONS_OK → ' + (SESSION.live ? 'Live chain active' : 'Options ' + SESSION.label + ' — last close surface'); return autoLive; }
var fallbackSynth = buildAdvancedSyntheticModel(); fallbackSynth.note = 'Auto: Live chain unavailable → Advanced Synthetic';
return fallbackSynth;}
var safeSLF = buildProfileModel(); safeSLF.note = 'Auto: Unknown instrument → SLF selected';
return safeSLF;}
// ============================================================================
// BUILD RAILS
// ============================================================================
function buildSmartRails(rows, currentPrice, atrV) {
var allStrikes = [];
for (var i = 0; i < rows.length; i++) { var s = rows[i].level; if (isFinite(s)) allStrikes.push(s); }
allStrikes.sort(sortNum);
if (!allStrikes.length) return [];
var strikeStep = 1;
if (allStrikes.length > 1) {
var diffs = [];
for (i = 1; i < allStrikes.length; i++) diffs.push(allStrikes[i] - allStrikes[i - 1]); diffs.sort(sortNum); strikeStep = diffs[Math.floor(diffs.length * 0.25)] || 1;}
var minSep = Math.max(strikeStep * 0.45, atrV * 0.12);
var atmStrike = allStrikes[0], atmDist = Infinity;
for (i = 0; i < allStrikes.length; i++) { var d = Math.abs(allStrikes[i] - currentPrice); if (d < atmDist) { atmDist = d; atmStrike = allStrikes[i]; } }
var atmTouching = atmDist < minSep;
var abvFloor = atmTouching ? atmStrike + Math.max(strikeStep * 0.90, minSep) : atmStrike + minSep;
var abvStrike = null;
for (i = 0; i < allStrikes.length; i++) if (allStrikes[i] > abvFloor) { abvStrike = allStrikes[i]; break; }
var blwCeil = atmTouching ? atmStrike - Math.max(strikeStep * 0.90, minSep) : atmStrike - minSep;
var blwStrike = null;
for (i = allStrikes.length - 1; i >= 0; i--) if (allStrikes[i] < blwCeil) { blwStrike = allStrikes[i]; break; }
function abvLabel(s) { if (s === null) return 'ABV —'; return 'ABV +' + ((s - currentPrice) / Math.max(atrV, EPS)).toFixed(1) + ' ATR'; }
function blwLabel(s) { if (s === null) return 'BLW —'; return 'BLW ' + ((s - currentPrice) / Math.max(atrV, EPS)).toFixed(1) + ' ATR'; }
return [
{ name: 'ABV', value: abvStrike, color: T.bear, label: abvLabel(abvStrike) },
{ name: 'ATM', value: atmStrike, color: T.a1, label: 'ATM ' + fmtPrice(atmStrike) },
{ name: 'BLW', value: blwStrike, color: T.bull, label: blwLabel(blwStrike) }];}
// ============================================================================
// FINALIZE STRIKE SURFACE
// ============================================================================
function finalizeStrikeSurface(surfaceMap, meta, currentPrice, atrV) {
var strikes = Object.keys(surfaceMap).map(function (k) { return Number(k); }).filter(function (v) { return isFinite(v); }).sort(sortNum);
if (!strikes.length) return { ok: false, error: 'Empty strike surface' };
function cellAt(K) { return surfaceMap[K] || { callOI: 0, putOI: 0, totalOI: 0 }; }
var stkStep = medianStrikeStep(strikes) || inferPriceStep(currentPrice, atrV, 0); PRICE_STEP = stkStep;
var win = localPriceWindow(currentPrice, atrV);
var struct = [];
var si;
for (si = 0; si < strikes.length; si++)
if (strikes[si] >= win.lo && strikes[si] <= win.hi) struct.push(strikes[si]);
if (struct.length < 9)
struct = chooseWindow(strikes, currentPrice, Math.min(15, strikes.length)).sort(sortNum);
if (!struct.length) struct = strikes.slice();
struct = struct.filter(function (v) { return surfaceMap[v] !== undefined; });
if (!struct.length) struct = strikes.slice();
var totalStructOI = 0;
for (si = 0; si < struct.length; si++) totalStructOI += nz(cellAt(struct[si]).totalOI, 0);
var pocStrike = struct[0], pocMaxOI = -Infinity;
for (si = 0; si < struct.length; si++) {
var tOI = nz(cellAt(struct[si]).totalOI, 0);
if (tOI > pocMaxOI) { pocMaxOI = tOI; pocStrike = struct[si]; }}
var pocIdx = struct.indexOf(pocStrike);
var vaLo = pocIdx, vaHi = pocIdx, vaAcc = nz(cellAt(pocStrike).totalOI, 0);
var vaTarget = totalStructOI * 0.70;
while (vaAcc < vaTarget && (vaLo > 0 || vaHi < struct.length - 1)) {
var loOI = vaLo > 0 ? nz(cellAt(struct[vaLo - 1]).totalOI, 0) : -1;
var hiOI = vaHi < struct.length - 1 ? nz(cellAt(struct[vaHi + 1]).totalOI, 0) : -1;
if (hiOI >= loOI) { vaHi++; vaAcc += Math.max(hiOI, 0); } else { vaLo--; vaAcc += Math.max(loOI, 0); }}
var vahStrike = struct[vaHi], valStrike = struct[vaLo];
var displayCount = Math.min(30, strikes.length);
var displayStrikes = chooseWindow(strikes, currentPrice, displayCount); displayStrikes.sort(sortNum).reverse();
var rows = [];
var leftTotal = 0, rightTotal = 0, allTotal = 0;
var curStrike = strikes[0], curDist = Infinity;
for (si = 0; si < strikes.length; si++) { var dS = Math.abs(strikes[si] - currentPrice); if (dS < curDist) { curDist = dS; curStrike = strikes[si]; } }
for (i = 0; i < displayStrikes.length; i++) {
var K = displayStrikes[i];
var row = surfaceMap[K] || { putOI: 0, callOI: 0, totalOI: 0 };
var putOI = nz(row.putOI, 0), callOI = nz(row.callOI, 0), totalOI = nz(row.totalOI, putOI + callOI);
var deltaOI = callOI - putOI;
var freshOI = nz(row.callOic, 0) + nz(row.putOic, 0) + nz(row.dvol, 0);
var isPOC = K === pocStrike, isVAH = K === vahStrike, isVAL = K === valStrike, isCUR = K === curStrike;
rows.push({ level: K, leftVal: putOI, rightVal: callOI, totalVal: totalOI, deltaVal: deltaOI, freshOI: freshOI, isFresh: freshOI > 0, isPOC: isPOC, isVAH: isVAH, isVAL: isVAL, isCUR: isCUR, isATM: false, label: isPOC ? 'POC' : isVAH ? 'VAH' : isVAL ? 'VAL' : isCUR ? 'CUR' : K > currentPrice ? 'ABV' : 'BLW' });
leftTotal += putOI; rightTotal += callOI; allTotal += totalOI;}
var smartRails = buildSmartRails(rows, currentPrice, atrV);
var atmStrike = null;
for (var ri = 0; ri < smartRails.length; ri++) if (smartRails[ri].name === 'ATM') { atmStrike = smartRails[ri].value; break; }
for (i = 0; i < rows.length; i++) { rows[i].isATM = rows[i].level === atmStrike; rows[i].label = rows[i].isATM ? 'ATM' : rows[i].label; }
var refsA = [], refsB = [], refsT = [], refsD = [];
for (i = 0; i < rows.length; i++) { refsA.push(rows[i].leftVal); refsB.push(rows[i].rightVal); refsT.push(rows[i].totalVal); refsD.push(Math.abs(rows[i].deltaVal)); }
var oiRails = [
{ name: 'POC', value: pocStrike, color: T.gold, label: 'POC ' + fmtPrice(pocStrike) },
{ name: 'VAH', value: vahStrike, color: T.bull, label: 'VAH ' + fmtPrice(vahStrike) },
{ name: 'VAL', value: valStrike, color: T.bear, label: 'VAL ' + fmtPrice(valStrike) },
{ name: 'CUR', value: currentPrice, color: T.a1, label: 'SPOT ' + fmtPrice(currentPrice) }];
return { ok: true, kind: 'OPTIONS', sourceTag: meta.sourceTag, sourceText: meta.sourceText, note: meta.note || '',
currentPrice: currentPrice, pcr: rightTotal > 0 ? leftTotal / rightTotal : 99,
bias: safeDiv(rightTotal - leftTotal, rightTotal + leftTotal, 0),
leftName: 'PUTS', rightName: 'CALLS',
surfaceCount: strikes.length, rowCount: rows.length, rows: rows,
rails: oiRails, pocStrike: pocStrike, vahStrike: vahStrike, valStrike: valStrike, curStrike: curStrike,
slices: meta.slices || [],
leftTotal: leftTotal, rightTotal: rightTotal, allTotal: allTotal,
refs: { leftRef: percentile(refsA, 0.80), rightRef: percentile(refsB, 0.80), totalRef: percentile(refsT, 0.80), deltaRef: percentile(refsD, 0.80) }};}
// ============================================================================
// RESOLVE
// ============================================================================
var instrProfile = detectInstrumentProfile();
var model = await resolveModel();
if (!model || !model.ok) { model = buildProfileModel(); model.note = 'Emergency SLF fallback'; }
if (model && model.rows && model.rows.length > 1) {
var _lv = [], _ri;
for (_ri = 0; _ri < model.rows.length; _ri++) _lv.push(model.rows[_ri].level);
_lv.sort(sortNum);
var _ms = medianStrikeStep(_lv);
if (_ms && _ms > 0) PRICE_STEP = _ms;}
var sourceTag = model.sourceTag || 'SHADOW';
var sourceTxt = model.sourceText || 'ADVANCED SYNTHETIC';
var noteTxt = model.note || '';
var leftTotal = nz(model.leftTotal, 0), rightTotal = nz(model.rightTotal, 0);
var pcrVal = nz(model.pcr, 1.0);
var biasVal = nz(model.bias, 0);
var pocLevel = model.pocStrike || null;
var vahLevel = model.vahStrike || null;
var valLevel = model.valStrike || null;
var curLevel = currentPrice;
var atrLast = Math.max(nz(atr14[lastIdx], 1), EPS);
var pocPx = pocLevel, vahPx = vahLevel, valPx = valLevel, painPx = null;
if (model && model.maxPain != null) painPx = model.maxPain;
var QLAB = analyzeSurface(model, atrLast, currentPrice);
var cycleHalo = QLAB.cycleConf > 0.18 ? (0.5 + 0.5 * Math.sin(QLAB.cyclePhase)) : (0.5 + 0.5 * Math.sin(CLOCK.phase12 || 0));
// ============================================================================
// SIGNAL ENGINE
// ============================================================================
var longSignal = make(n, false), shortSignal = make(n, false);
var longLive = make(n, false), shortLive = make(n, false);
var longConf = make(n, false), shortConf = make(n, false);
var signalWhy = make(n, '');
var prevAbovePOC = null, prevInsideVA = null;
var lastFlowShift = -999;
for (i = Math.max(adaptivePeriod, 5); i < n; i++) {
if (pocLevel === null || valLevel === null || vahLevel === null) { signalWhy[i] = 'no-levels'; continue; }
var px = close[i];
var distOk = (i - Math.max(lastPeakSignalBar, lastValleySignalBar)) >= minSignalDistance;
var insideVA = px >= valLevel && px <= vahLevel;
var abovePOC = px > pocLevel;
var isSig = false, sigDir = 0, why = '';
if (prevAbovePOC !== null && prevAbovePOC !== abovePOC && distOk && true) {
isSig = true; sigDir = abovePOC ? 1 : -1; why = abovePOC ? 'POC↑' : 'POC↓'; }
else if (prevInsideVA !== null && prevInsideVA !== insideVA) {
if (!insideVA && distOk) { isSig = true; sigDir = px > vahLevel ? 1 : -1; why = px > vahLevel ? 'VA+BRK' : 'VA-BRK'; }
else if (insideVA && distOk) { isSig = true; sigDir = px > pocLevel ? 1 : -1; why = 'VA-REJ'; }}
if (!isSig && i === lastIdx && i > lastFlowShift + minSignalDistance) {
var pcrNow = leftTotal > 0 ? leftTotal / rightTotal : 1;
if (pcrNow > pcrHiThresh) { isSig = true; sigDir = -1; why = 'FLOW-S'; lastFlowShift = i; }
else if (pcrNow < pcrLoThresh) { isSig = true; sigDir = 1; why = 'FLOW-L'; lastFlowShift = i; }}
if (isSig) {
if (sigDir === 1) { longSignal[i] = true; lastValleySignalBar = i; lastSignalType = 'Valley'; }
else { shortSignal[i] = true; lastPeakSignalBar = i; lastSignalType = 'Peak'; }
signalWhy[i] = why;}
prevAbovePOC = abovePOC; prevInsideVA = insideVA;}
for (i = 0; i < n; i++) {
if (!longSignal[i] && !shortSignal[i]) continue;
if (i === lastIdx && i_intrabar === 'On') { longLive[i] = longSignal[i]; shortLive[i] = shortSignal[i]; }
else { longConf[i] = longSignal[i]; shortConf[i] = shortSignal[i]; }}
register_signal(longConf, 'Options Ladder Pro: VA/POC Long');
register_signal(shortConf, 'Options Ladder Pro: VA/POC Short');
var peakConf = make(n, false), valleyConf = make(n, false);
for (i = 0; i < n; i++) {
if (peakSignals[i] != null && !peakTentative[i]) peakConf[i] = true;
if (valleySignals[i] != null && !valleyTentative[i]) valleyConf[i] = true;}
register_signal(valleyConf, 'Options Ladder Pro: Valley Long');
register_signal(peakConf, 'Options Ladder Pro: Peak Short');
var rPeakSig = paint(peakSignals, { name: 'OLP Peak Signals', color: 'transparent', affects_scale: false, show_in_legend: false });
var rValleySig = paint(valleySignals, { name: 'OLP Valley Signals', color: 'transparent', affects_scale: false, show_in_legend: false });
for (i = 0; i < n; i++) {
if (peakSignals[i] != null) paint_label_at_line(rPeakSig, i, peakTentative[i] ? '▼!' : '▼', { color: peakTentative[i] ? T.warn : T.bear, background_color: 'transparent', border_color: 'transparent', vertical_align: 'top' });
if (valleySignals[i] != null) paint_label_at_line(rValleySig, i, valleyTentative[i] ? '▲!' : '▲', { color: valleyTentative[i] ? T.warn : T.bull, background_color: 'transparent', border_color: 'transparent', vertical_align: 'bottom' });}
// ============================================================================
// MAIN CHART
// ============================================================================
var pocSeries = make(n, null), vahSeries = make(n, null), valSeries = make(n, null);
var lastPoc = null, lastVah = null, lastVal = null;
for (i = 0; i < n; i++) { pocSeries[i] = pocLevel; vahSeries[i] = vahLevel; valSeries[i] = valLevel; }
var pocBreach = make(n, null);
var prevPOCSide = null;
for (i = 1; i < n; i++) {
if (pocLevel === null) continue;
var side = close[i] > pocLevel ? 1 : -1;
if (prevPOCSide !== null && side !== prevPOCSide) pocBreach[i] = side > 0 ? 1 : -1; prevPOCSide = side;}
var pocPulse = 0.5 + 0.5 * Math.sin(Date.now() / 800);
var gap = 4;
var pocSeries2 = make(n, null), vahSeries2 = make(n, null), valSeries2 = make(n, null);
for (var _gi = 0; _gi < n - gap; _gi++) { pocSeries2[_gi] = pocLevel; vahSeries2[_gi] = vahLevel; valSeries2[_gi] = valLevel; }
for (var _gi2 = n - gap; _gi2 < n; _gi2++) { pocSeries2[_gi2] = null; vahSeries2[_gi2] = null; valSeries2[_gi2] = null; }
pocSeries = pocSeries2; vahSeries = vahSeries2; valSeries = valSeries2;
paint(pocSeries, { name: 'POC', color: T.gold, thickness: 2, affects_scale: false });
paint(vahSeries, { name: 'VAH', color: rgba(T.bull, 0.55), thickness: 1, affects_scale: false });
paint(valSeries, { name: 'VAL', color: rgba(T.bear, 0.55), thickness: 1, affects_scale: false });
var pocLblY = make(n, null); pocLblY[lastIdx] = pocLevel || currentPrice;
var vahLblY = make(n, null); vahLblY[lastIdx] = vahLevel || currentPrice;
var valLblY = make(n, null); valLblY[lastIdx] = valLevel || currentPrice;
var rPocLbl = paint(pocLblY, { name: 'POC Label', color: 'transparent', affects_scale: false, show_in_legend: false });
var rVahLbl = paint(vahLblY, { name: 'VAH Label', color: 'transparent', affects_scale: false, show_in_legend: false });
var rValLbl = paint(valLblY, { name: 'VAL Label', color: 'transparent', affects_scale: false, show_in_legend: false });
if (pocLevel !== null) paint_label_at_line(rPocLbl, lastIdx, 'POC ' + fmtPrice(pocLevel), { color: T.gold, background_color: 'transparent', border_color: 'transparent', vertical_align: 'middle' });
if (vahLevel !== null) paint_label_at_line(rVahLbl, lastIdx, 'VAH ' + fmtPrice(vahLevel), { color: rgba(T.bull, 0.8), background_color: 'transparent', border_color: 'transparent', vertical_align: 'middle' });
if (valLevel !== null) paint_label_at_line(rValLbl, lastIdx, 'VAL ' + fmtPrice(valLevel), { color: rgba(T.bear, 0.8), background_color: 'transparent', border_color: 'transparent', vertical_align: 'middle' });
for (i = 1; i < n; i++) {
if (pocBreach[i]) {paint_label_at_line(rPocLbl, i, pocBreach[i] > 0 ? '▲ POC' : '▼ POC', { color: pocBreach[i] > 0 ? T.bull : T.bear, background_color: 'transparent', border_color: 'transparent', vertical_align: 'middle' }); }}
// ============================================================================
// DASHBOARD HELPERS
// ============================================================================
var TH = { bg: 'var(--background-color)', tx: 'var(--text-color)', bdr: 'var(--border-color)', acc: THEME.acc, bul: THEME.bull, ber: THEME.bear, wrn: THEME.warn, pur: THEME.pur, dim: THEME.dim };
function hexA2(hex, alpha) {hex = hex.replace('#', '');
if (hex.length === 3) hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
var r = parseInt(hex.substring(0, 2), 16), g = parseInt(hex.substring(2, 4), 16), b = parseInt(hex.substring(4, 6), 16);
return 'rgba(' + r + ',' + g + ',' + b + ',' + alpha + ')';}
function pCell(t, c, al, b, cs, bg, w) { var o = { text: String(t), color: c || TH.tx, textAlign: al || 'left', fontWeight: b ? 'bold' : 'normal', colspan: cs || 1, background: bg || TH.bg, fontSize: 10 }; if (w) o.width = w; return o; }
function pRow3(a, b, c) { return { cells: [a, b, c] }; }
function pSect(t, col) { return { cells: [pCell(t, col, 'left', true, 3, hexA2(col || TH.acc, 0.10))] }; }
function confBar(pct, size) { size = size || 10; var filled = clamp(Math.round(clamp(pct, 0, 100) / 100 * size), 0, size); var s = '', k; for (k = 0; k < size; k++) s += k < filled ? '█' : '░'; return s; }
function scoreColor(v) { if (v == null) return TH.tx; if (v >= 90) return TH.bul; if (v >= 75) return TH.wrn; return TH.tx; }
function sliceOiMax(slices) {
var m = EPS, si;
for (si = 0; si < slices.length; si++) if (slices[si].totalOI > m) m = slices[si].totalOI;
var m2 = EPS, ki2, t, keys;
for (si = 0; si < slices.length; si++) { keys = Object.keys(slices[si].surface || {}); for (ki2 = 0; ki2 < keys.length; ki2++) { t = nz(slices[si].surface[keys[ki2]].totalOI, 0); if (t > m2) m2 = t; } }
return m2; }
function worldX(strike) { return clamp((strike - currentPrice) / atrLast, -5.2, 5.2) * 6.6; }
function worldY(oi, oiMax) {
var t = clamp01(oi / Math.max(oiMax, EPS));
return Math.pow(t, 0.46) * 40; }
function worldZ(idx, count) { return (idx - (count - 1) / 2) * (46 / Math.max(count - 1, 1)); }
// ==============================================================================================
// MASS SPLIT — solid radial wedges (mass = radius, put/call hue), split ring, PCR hub, vector labels. Stretch-proof: x-range = ±R × true pixel aspect.
// ==============================================================================================
// vector label font: smooth polylines (arcs sampled) with round caps and a dark halo — reads like a clean sans face on the canvas
function msArcPts(cx, cy, rx, ry, a0, a1, n) { var o = [], i; for (i = 0; i <= n; i++) { var a = (a0 + (a1 - a0) * i / n) * Math.PI / 180; o.push([cx + Math.cos(a) * rx, cy + Math.sin(a) * ry]); } return o; }
function msCirc(cx, cy, r) { return msArcPts(cx, cy, r, r, 0, 360, 16); }
var MS_GLYPHS = {
'0': [msArcPts(0.5, 0.5, 0.42, 0.5, 0, 360, 18)], '1': [[[0.18, 0.74], [0.5, 1], [0.5, 0]], [[0.22, 0], [0.78, 0]]],
'2': [msArcPts(0.5, 0.68, 0.36, 0.32, 165, -25, 10).concat([[0.02, 0], [0.98, 0]])],
'3': [[[0.08, 1], [0.9, 1], [0.45, 0.58]].concat(msArcPts(0.52, 0.29, 0.36, 0.29, 90, -180, 10))],
'4': [[[0.74, 0], [0.74, 1], [0.04, 0.3], [0.98, 0.3]]],
'5': [[[0.9, 1], [0.18, 1], [0.12, 0.56], [0.5, 0.62]].concat(msArcPts(0.5, 0.31, 0.38, 0.31, 90, -175, 10))],
'6': [[[0.85, 0.97], [0.6, 1], [0.35, 0.92], [0.2, 0.7], [0.15, 0.35]].concat(msArcPts(0.5, 0.31, 0.35, 0.31, 180, -180, 14))],
'7': [[[0.04, 1], [0.96, 1], [0.38, 0]]],
'8': [msArcPts(0.5, 0.73, 0.3, 0.27, -90, 270, 12), msArcPts(0.5, 0.28, 0.36, 0.28, 90, 450, 12)],
'9': [msArcPts(0.5, 0.69, 0.35, 0.31, 0, 360, 14).concat([[0.85, 0.69], [0.8, 0.3], [0.65, 0.08], [0.4, 0], [0.15, 0.03]])],
'.': [msCirc(0.5, 0.08, 0.08)], '-': [[[0.15, 0.5], [0.85, 0.5]]],
'%': [[[0.12, 0], [0.88, 1]], msCirc(0.24, 0.76, 0.17), msCirc(0.76, 0.24, 0.17)],
'A': [[[0, 0], [0.5, 1], [1, 0]], [[0.19, 0.36], [0.81, 0.36]]],
'B': [[[0, 0], [0, 1], [0.58, 1]].concat(msArcPts(0.58, 0.76, 0.26, 0.24, 90, -90, 8)).concat([[0, 0.52], [0.64, 0.52]]).concat(msArcPts(0.64, 0.26, 0.3, 0.26, 90, -90, 8)).concat([[0, 0]])],
'C': [msArcPts(0.56, 0.5, 0.5, 0.5, 45, 315, 14)], 'E': [[[0.95, 1], [0, 1], [0, 0], [0.95, 0]], [[0, 0.5], [0.72, 0.5]]],
'H': [[[0, 0], [0, 1]], [[1, 0], [1, 1]], [[0, 0.5], [1, 0.5]]], 'L': [[[0, 1], [0, 0], [0.95, 0]]],
'M': [[[0, 0], [0, 1], [0.5, 0.36], [1, 1], [1, 0]]], 'N': [[[0, 0], [0, 1], [1, 0], [1, 1]]],
'O': [msArcPts(0.5, 0.5, 0.48, 0.5, 0, 360, 18)],
'P': [[[0, 0], [0, 1], [0.6, 1]].concat(msArcPts(0.6, 0.74, 0.3, 0.26, 90, -90, 8)).concat([[0, 0.48]])],
'R': [[[0, 0], [0, 1], [0.6, 1]].concat(msArcPts(0.6, 0.74, 0.3, 0.26, 90, -90, 8)).concat([[0, 0.48]]), [[0.4, 0.48], [1, 0]]],
'S': [msArcPts(0.5, 0.75, 0.34, 0.25, 40, 270, 10).concat(msArcPts(0.5, 0.25, 0.34, 0.25, 90, -140, 10))],
'T': [[[0, 1], [1, 1]], [[0.5, 1], [0.5, 0]]], 'U': [[[0, 1], [0, 0.36]].concat(msArcPts(0.5, 0.36, 0.5, 0.36, 180, 360, 10)).concat([[1, 1]])],
'V': [[[0, 1], [0.5, 0], [1, 1]]], 'W': [[[0, 1], [0.24, 0], [0.5, 0.62], [0.76, 0], [1, 1]]], 'Y': [[[0, 1], [0.5, 0.5], [1, 1]], [[0.5, 0.5], [0.5, 0]]], ' ': [] };
function msStrWidth(str, w) { return String(str).length * w; }
function msText(sets, str, x, y, w, h, col, width, order, halo) { // x = left edge, y = baseline (data units); glyph cell 0.72·w wide, 0.28·w spacing; halo = dark outline for legibility over colour
var pts = [], cx = x, c, k, q, sst = String(str).toUpperCase();
for (c = 0; c < sst.length; c++) { var g = MS_GLYPHS[sst.charAt(c)]; if (g) for (k = 0; k < g.length; k++) { for (q = 0; q < g[k].length; q++) pts.push({ x: cx + g[k][q][0] * w * 0.72, y: y + g[k][q][1] * h }); pts.push({ x: null, y: null }); } cx += w; }
if (!pts.length) return;
if (halo !== false) sets.push({ label: 'mth' + sets.length, data: pts, showLine: true, fill: false, spanGaps: false, borderColor: rgba('#05060F', 0.78), borderWidth: (width || 1.3) + 2.4, borderCapStyle: 'round', borderJoinStyle: 'round', pointRadius: 0, tension: 0, order: (order || 3) + 1 });
sets.push({ label: 'mtx' + sets.length, data: pts, showLine: true, fill: false, spanGaps: false, borderColor: col, borderWidth: width || 1.3, borderCapStyle: 'round', borderJoinStyle: 'round', pointRadius: 0, tension: 0, order: order || 3 }); }
function msArc(cx, cy, r, a0, a1, n) { var out = [], i; for (i = 0; i <= n; i++) { var a = a0 + (a1 - a0) * i / n; out.push({ x: cx + Math.cos(a) * r, y: cy + Math.sin(a) * r }); } return out; }
function msHexMix(a, b, pct) { try { return tiny.mix(a, b, pct).toHexString(); } catch (e) { return a; } }
var MS_MIDC = '#FF9A3C'; // balanced put/call = amber-orange (distinct from the gold POC)
function buildPolarPack(mdl, aspect, wpx, hpx) {
var sets = [], w, k, CX = 0, CY = 0, RMAX = 6.0, RIN = 1.6, WEDGES = 5, Rpad = 8.4;
aspect = (aspect && aspect > 0.4) ? aspect : 1.3; hpx = hpx || 148;
var PXU = hpx / (2 * Rpad); // pixels per data unit (vertical) — every stroke width below is derived from it, so the fill stays solid at any size
var rows = (mdl && mdl.rows) ? mdl.rows : [];
var poc = mdl ? mdl.pocStrike : null, vah = mdl ? mdl.vahStrike : null, val = mdl ? mdl.valStrike : null;
var buckets = [
{ key: 'ABV', short: 'ABV', name: 'ABOVE VAH', tot: 0, left: 0, right: 0, fresh: 0 },
{ key: 'UVA', short: 'UVA', name: 'POC → VAH', tot: 0, left: 0, right: 0, fresh: 0 },
{ key: 'POC', short: 'POC', name: 'POC MAGNET', tot: 0, left: 0, right: 0, fresh: 0 },
{ key: 'LVA', short: 'LVA', name: 'VAL → POC', tot: 0, left: 0, right: 0, fresh: 0 },
{ key: 'BLW', short: 'BLW', name: 'BELOW VAL', tot: 0, left: 0, right: 0, fresh: 0 } ];
function classify(level, isPOC) {
if (isPOC) return 2;
if (val != null && isFinite(val) && level < val) return 4;
if (vah != null && isFinite(vah) && level > vah) return 0;
if (poc != null && isFinite(poc) && level < poc) return 3;
if (poc != null && isFinite(poc) && level > poc) return 1;
if (level < currentPrice) return 3;
if (level > currentPrice) return 1;
return 2; }
var allL = 0, allR = 0;
for (k = 0; k < rows.length; k++) {
var rw = rows[k], bi = classify(rw.level, rw.isPOC), t = nz(rw.totalVal, nz(rw.leftVal, 0) + nz(rw.rightVal, 0));
buckets[bi].tot += t; buckets[bi].left += nz(rw.leftVal, 0); buckets[bi].right += nz(rw.rightVal, 0); buckets[bi].fresh += nz(rw.freshOI, 0); allL += nz(rw.leftVal, 0); allR += nz(rw.rightVal, 0); }
var all = 0; for (k = 0; k < WEDGES; k++) all += buckets[k].tot;
if (all <= EPS) { for (k = 0; k < WEDGES; k++) buckets[k].tot = 1; all = WEDGES; }
var maxT = buckets[0].tot; for (k = 1; k < WEDGES; k++) if (buckets[k].tot > maxT) maxT = buckets[k].tot;
var items = [], maxPct = 0;
for (k = 0; k < WEDGES; k++) {
var pct = 100 * buckets[k].tot / all; if (pct > maxPct) maxPct = pct;
var sideMix = buckets[k].tot > EPS ? buckets[k].right / buckets[k].tot : 0.5, col;
try { col = (k === 2) ? T.gold : (sideMix < 0.5 ? tiny.mix(T.bear, MS_MIDC, Math.round(clamp01(sideMix / 0.5) * 100)) : tiny.mix(MS_MIDC, T.bull, Math.round(clamp01((sideMix - 0.5) / 0.5) * 100))).saturate(22).toHexString(); } catch (eCol) { col = k === 2 ? T.gold : (sideMix >= 0.5 ? T.bull : T.bear); } // vivid put → amber → call ramp (no muddy red/green midpoint)
items.push({ key: buckets[k].key, short: buckets[k].short, name: buckets[k].name, tot: buckets[k].tot, pct: pct, color: col, mix: sideMix, fresh: buckets[k].fresh > 0, left: buckets[k].left, right: buckets[k].right }); }
var STEP_PX = 2.1, GAP = 0.055, i, sIdx;
// guide rings 25 / 50 / 75 / 100 % of the largest bucket + faint back-plate
sets.push({ label: 'mplate', data: msArc(CX, CY, (RIN + RMAX) / 2, 0, Math.PI * 2, 72), showLine: true, fill: false, borderColor: rgba(T.a1, 0.05), borderWidth: (RMAX - RIN) * PXU, pointRadius: 0, tension: 0, order: 40 });
for (i = 1; i <= 4; i++) sets.push({ label: 'pring' + i, data: msArc(CX, CY, RIN + (RMAX - RIN) * i / 4, 0, Math.PI * 2, 72), showLine: true, fill: false, borderColor: rgba(T.dim, i === 4 ? 0.34 : 0.18), borderWidth: 1, borderDash: i === 4 ? [] : [3, 3], pointRadius: 0, tension: 0, order: 35 });
for (w = 0; w < WEDGES; w++) { // wedges: concentric arc strokes → solid sector with a radial gradient (dark core → bright rim), rim highlight, glow, fresh dots
var it = items[w], a0 = -Math.PI / 2 + w * (Math.PI * 2 / WEDGES) + GAP, a1 = a0 + (Math.PI * 2 / WEDGES) - 2 * GAP, mid = (a0 + a1) / 2;
var rOut = RIN + Math.pow(it.tot / maxT, 0.85) * (RMAX - RIN), span = rOut - RIN, nStep = Math.max(3, Math.ceil(span * PXU / STEP_PX)), stepU = span / nStep, arcN = Math.max(10, Math.round((a1 - a0) * rOut * PXU / 2.5));
for (sIdx = 0; sIdx < nStep; sIdx++) {
var rr = RIN + (sIdx + 0.5) * stepU, tq = sIdx / Math.max(nStep - 1, 1), hexq = msHexMix(msHexMix(it.color, '#000000', 42 - 42 * tq), '#FFFFFF', 18 * tq * tq);
sets.push({ label: 'w' + w + '_' + sIdx, data: msArc(CX, CY, rr, a0, a1, arcN), showLine: true, fill: false, borderColor: rgba(hexq, 0.92), borderWidth: stepU * PXU * 1.35, pointRadius: 0, tension: 0, order: 30 }); }
var glowA = w === 2 ? 0.22 + 0.22 * (typeof breathe === 'number' ? breathe : 0.5) : 0.16;
sets.push({ label: 'wglow' + w, data: msArc(CX, CY, rOut + 0.12, a0, a1, arcN), showLine: true, fill: false, borderColor: rgba(it.color, glowA), borderWidth: 7, pointRadius: 0, tension: 0, order: 29 });
sets.push({ label: 'wrim' + w, data: msArc(CX, CY, rOut, a0, a1, arcN), showLine: true, fill: false, borderColor: rgba(lighten(it.color, 18), 0.98), borderWidth: 2.2, pointRadius: 0, tension: 0, order: 20 });
if (it.fresh) sets.push({ label: 'wfresh' + w, data: msArc(CX, CY, rOut + 0.42, a0 + 0.08, a1 - 0.08, Math.max(3, Math.round(arcN / 4))), showLine: false, pointStyle: 'circle', pointRadius: 1.6, pointBackgroundColor: rgba(T.txt, 0.9), pointBorderWidth: 0, order: 18 });
// labels: bucket key just outside the rim, share inside the wedge (when it is tall enough)
var lw = 0.58, lh = 0.82, keyR = RMAX + 0.95, kx = CX + Math.cos(mid) * keyR, ky = CY + Math.sin(mid) * keyR, keyTxt = it.short + ' ' + it.pct.toFixed(0) + '%'; // zone key and its share together on the outer ring
msText(sets, keyTxt, kx - msStrWidth(keyTxt, lw) / 2 + lw * 0.14, ky - lh / 2, lw, lh, rgba(lighten(it.color, 10), 0.98), 1.25, 12); }
var lShare = (allL + allR) > EPS ? allL / (allL + allR) : 0.5;
var leftN = (mdl && mdl.leftName) ? mdl.leftName : 'PUTS', rightN = (mdl && mdl.rightName) ? mdl.rightName : 'CALLS';
return { sets: sets, items: items, maxPct: maxPct, lShare: lShare,
caption: 'radius = OI mass · hue = ' + leftN + ' (red) → ' + rightN + ' (green) · dots = fresh flow · PCR ' + (isFinite(pcrVal) ? pcrVal.toFixed(2) : '—'),
xmin: -Rpad * aspect, xmax: Rpad * aspect, ymin: -Rpad, ymax: Rpad };}
function polarChartCfg(pack, hpx) {
return {
width: '100%', height: hpx, type: 'line',
data: { datasets: pack.sets },
options: {
devicePixelRatio: 2, maintainAspectRatio: false, animation: { duration: 0 },
layout: { padding: { top: 0, bottom: 0, left: 0, right: 0 } },
scales: {
x: { type: 'linear', min: pack.xmin, max: pack.xmax, display: false, grid: { display: false } },
y: { type: 'linear', min: pack.ymin, max: pack.ymax, display: false, grid: { display: false } } },
plugins: { legend: { display: false }, tooltip: { enabled: false } },
elements: { line: { tension: 0, fill: false } } }};}
function polarLegendRow(pack) {
var cells = [], i, it, fs, extra, mid; mid = Math.floor(pack.items.length / 2);
for (i = 0; i < pack.items.length; i++) {it = pack.items[i]; fs = Math.max(7, Math.round((subFontSize - 1) + it.pct / 18)); extra = (i === mid) ? (7 - pack.items.length) : 1;
if (extra < 1) extra = 1;
cells.push({ text: it.short + ' ' + it.pct.toFixed(0) + '%', color: it.color, textAlign: 'center', fontWeight: it.pct >= pack.maxPct - 0.01 ? 'bold' : 'normal', fontSize: fs, colspan: extra, background: 'linear-gradient(180deg,' + rgba(it.color, 0.28 + 0.40 * (it.pct / 100)) + ' 0%,' + rgba(it.color, 0.08) + ' 100%)', textShadow: i === mid ? '0 0 7px ' + rgba(it.color, 0.55) : undefined });}
return { cells: cells }; }
// ==============================================================================================
// THE BOARD
// ==============================================================================================
function styleCell(cell, extra) { extra = extra || {};
for (var sk in extra) {
if (extra[sk] != null) cell[sk] = extra[sk]; }
return cell;}
function pCell(t, c, al, b, cs, bg, w, fs) {
var o = { text: String(t), color: c || TH.tx, textAlign: al || 'left', fontWeight: b ? 'bold' : 'normal', colspan: cs || 1, background: bg || TH.bg, fontSize: fs || baseFontSize || 10, padding: '1px 2px' };
if (w) o.width = w;
return o;}
function barBgLeft(pct, fillCol, baseBg) {
var p = clamp01(pct) * 100;
var inv = (100 - p).toFixed(2);
return 'linear-gradient(90deg,' +
hexA2(fillCol, 0) + ' 0%,' +
hexA2(fillCol, 0) + ' ' + inv + '%,' +
hexA2(fillCol, 0.50) + ' ' + inv + '%,' +
hexA2(fillCol, 0.88) + ' 100%),' + (baseBg || 'transparent');}
function barBgRight(pct, fillCol, baseBg) {
var p = (clamp01(pct) * 100).toFixed(2);
return 'linear-gradient(90deg,' +
hexA2(fillCol, 0.88) + ' 0%,' +
hexA2(fillCol, 0.50) + ' ' + p + '%,' +
hexA2(fillCol, 0) + ' ' + p + '%,' +
hexA2(fillCol, 0) + ' 100%),' + (baseBg || 'transparent');}
function profileYAxis(fontSize) {
return { display: true, position: 'right', type: 'category', grid: { display: false, drawTicks: false }, ticks: { color: T.dim, font: { size: fontSize || 8 }, autoSkip: true, maxTicksLimit: 10, padding: 0 } };}
function collectProfileSeries(srcRows) {
var out = { labels: [], puts: [], calls: [], putCols: [], callCols: [], maxVal: EPS };
var iP, pv, cv;
for (iP = 0; iP < srcRows.length; iP++) {
pv = nz(srcRows[iP].leftVal, 0);
cv = nz(srcRows[iP].rightVal, 0);
if (pv > out.maxVal) out.maxVal = pv;
if (cv > out.maxVal) out.maxVal = cv;
out.labels.push(fmtPrice(srcRows[iP].level));
out.puts.push(pv);
out.calls.push(cv);
out.putCols.push(rgba(srcRows[iP].isPOC ? T.gold : T.bear, srcRows[iP].isPOC ? 0.90 : 0.70));
out.callCols.push(rgba(srcRows[iP].isPOC ? T.gold : T.bull, srcRows[iP].isPOC ? 0.90 : 0.70)); }
return out;}
function makeProfileChart(pack, wpx, hpx, yFont) {
return {
width: wpx + 'px', height: hpx + 'px', type: 'bar',
data: {
labels: pack.labels,
datasets: [
{ label: model.leftName || 'PUTS', data: pack.puts, backgroundColor: pack.putCols, borderWidth: 0, barPercentage: 0.88, categoryPercentage: 0.90 },
{ label: model.rightName || 'CALLS', data: pack.calls, backgroundColor: pack.callCols, borderWidth: 0, barPercentage: 0.88, categoryPercentage: 0.90 } ] },
options: {
devicePixelRatio: 2,
maintainAspectRatio: false,
animation: { duration: 0 },
indexAxis: 'y',
layout: { padding: { top: 0, bottom: 0, left: 0, right: 0 } },
scales: {
x: {display: false, grid: { display: false }, stacked: false, min: 0, max: pack.maxVal, beginAtZero: true },
y: profileYAxis(yFont) },
plugins: { legend: { display: false } } }};}
function polarCfgSized(pack, wpx, hpx) { var cfg = polarChartCfg(pack, hpx + 'px'); cfg.width = wpx + 'px'; cfg.height = hpx + 'px'; return cfg; } // same fixed px box as the profile chart: equal columns, no ladder distortion
var CS = 12;
var LCS = 5;
var MCS = 2;
var RCS = 5;
var SIDE_W = '42%';
var MID_W = '16%';
var boardRows = [];
var scanIdx = i_cometSpeed !== 'Off'
? Math.floor((CLOCK.beat * (i_cometSpeed === 'Fast' ? 3 : 0.7)) % Math.max(1, model.rowCount))
: -1;
var breathe = CLOCK.hasClock ? (0.5 + 0.5 * Math.sin(CLOCK.wallMs / 600)) : 0.5;
var pocGlow = 0.14 + 0.14 * breathe;
var pcrCol = pcrVal > 1.15 ? T.bear : pcrVal < 0.85 ? T.bull : T.txt;
var biasCol = biasVal >= 0 ? T.bull : T.bear;
var resTxt = i_resMode.indexOf('Auto') >= 0 ? 'SYNCED' : i_resMode;
var putShare = (leftTotal + rightTotal) > EPS ? leftTotal / (leftTotal + rightTotal) : 0.5;
var splitPct = putShare * 100;
var splitLo = Math.max(0, splitPct - 10).toFixed(1);
var splitHi = Math.min(100, splitPct + 10).toFixed(1);
var fieldBarBg = 'linear-gradient(90deg,' +
hexA2(T.bear, 0.55) + ' 0%,' +
hexA2(T.bear, 0.32) + ' ' + splitLo + '%,' +
hexA2(T.gold, 0.38) + ' ' + splitPct.toFixed(1) + '%,' +
hexA2(T.bull, 0.32) + ' ' + splitHi + '%,' +
hexA2(T.bull, 0.55) + ' 100%)';
var leftRef = (model.refs && model.refs.leftRef) || EPS;
var rightRef = (model.refs && model.refs.rightRef) || EPS;
boardRows.push({ cells: [styleCell(
pCell('◇ OPTIONS LADDER PRO', T.a1, 'center', true, CS, 'linear-gradient(90deg,' + hexA2(T.bull, 0.18) + ' 0%,' + hexA2(T.a1, 0.26) + ' 50%,' + hexA2(T.bear, 0.18) + ' 100%)', '100%', headFontSize), { letterSpacing: '0.14em', textShadow: '0 0 10px ' + hexA2(T.a1, 0.55), padding: '3px 4px' } )] });
boardRows.push({ cells: [
pCell('SRC ' + sourceTxt, T.acc, 'left', true, LCS, hexA2(T.acc, 0.10), SIDE_W, baseFontSize),
pCell('PCR ' + pcrVal.toFixed(2), pcrCol, 'center', true, MCS, hexA2(pcrCol, 0.12), MID_W, baseFontSize),
pCell('RES ' + resTxt, T.acc, 'right', true, RCS, hexA2(T.acc, 0.10), SIDE_W, baseFontSize) ] });
boardRows.push({ cells: [
styleCell(pCell('POC ' + fmtPrice(pocLevel), T.gold, 'left', true, LCS, 'linear-gradient(90deg,' + hexA2(T.gold, 0.34 + pocGlow) + ' 0%, transparent 100%)', SIDE_W, headFontSize), { textShadow: '0 0 8px ' + hexA2(T.gold, 0.70) }),
pCell('VAH ' + fmtPrice(vahLevel), T.bull, 'center', true, MCS, hexA2(T.bull, 0.14), MID_W, subFontSize),
pCell('VAL ' + fmtPrice(valLevel), T.bear, 'right', true, RCS, 'linear-gradient(270deg,' + hexA2(T.bear, 0.34) + ' 0%, transparent 100%)', SIDE_W, headFontSize) ] });
boardRows.push({ cells: [
pCell('BIAS ' + fmtSignedPct(biasVal), biasCol, 'left', true, LCS, hexA2(biasCol, 0.10), SIDE_W, baseFontSize),
pCell('EXP ' + String(numExp), T.acc, 'center', true, MCS, hexA2(T.acc, 0.08), MID_W, baseFontSize),
pCell(SESSION.label || '', SESSION.live ? T.bull : T.dim, 'right', true, RCS, SESSION.live ? hexA2(T.bull, 0.12) : 'transparent', SIDE_W, baseFontSize) ] });
boardRows.push({ cells: [styleCell(
pCell('PUTS ' + splitPct.toFixed(0) + '% FIELD ' + (100 - splitPct).toFixed(0) + '% CALLS', T.txt, 'center', true, CS, fieldBarBg, '100%', subFontSize), { letterSpacing: '0.08em', textShadow: '0 0 6px ' + hexA2(T.gold, 0.35), padding: '2px 4px' } )] });
for (var ri = 0; ri < model.rows.length; ri++) {
var row = model.rows[ri];
var lv = row.level;
var lftV = nz(row.leftVal, 0), rgtV = nz(row.rightVal, 0);
var leftNorm = lftV / Math.max(leftRef, EPS);
var rightNorm = rgtV / Math.max(rightRef, EPS);
var isPOC = !!row.isPOC, isVAH = !!row.isVAH, isVAL = !!row.isVAL, isCUR = !!row.isCUR, isATM = !!row.isATM;
var fresh = i_freshGlow === 'On' && nz(row.freshOI, 0) > 0;
var isScan = ri === scanIdx;
var vertT = model.rows.length > 1 ? ri / (model.rows.length - 1) : 0.5;
var atmo = mixTowards(T.bear, T.bull, vertT * 100);
var putHot = i_theme === 'Cyber Punk' ? '#FF0055' : i_theme === 'Gold Sovereign' ? '#FF3D00' : '#FF1744';
var callHot = i_theme === 'Cyber Punk' ? '#39FF14' : i_theme === 'Gold Sovereign' ? '#FFF275' : '#00FFAA';
var lftCol = isPOC ? T.gold : leftNorm > 0.82 ? putHot : leftNorm > 0.35 ? mixTowards(T.bear, atmo, 18) : mixTowards(T.bear, T.dim, 28);
var rgtCol = isPOC ? T.gold : rightNorm > 0.82 ? callHot : rightNorm > 0.35 ? mixTowards(T.bull, atmo, 18) : mixTowards(T.bull, T.dim, 28);
if (fresh) { lftCol = lighten(lftCol, 10); rgtCol = lighten(rgtCol, 10); }
var tag = isPOC ? ' POC' : isATM ? ' ATM' : isVAH ? ' VAH' : isVAL ? ' VAL' : isCUR ? ' ◈' : (fresh ? ' ●' : '');
var midTxt = (isScan ? '›' : '') + fmtPrice(lv) + tag;
var midCol = isPOC ? T.gold : isATM ? T.a1 : isVAH ? T.bull : isVAL ? T.bear : isScan ? T.warn : fresh ? T.warn : T.txt;
var rowBg = 'transparent';
if (isPOC) rowBg = hexA2(T.gold, 0.16 + pocGlow);
else if (isVAH) rowBg = hexA2(T.bull, 0.10);
else if (isVAL) rowBg = hexA2(T.bear, 0.10);
else if (isATM || isCUR) rowBg = hexA2(T.a1, 0.10);
if (fresh && !isPOC) rowBg = hexA2(T.warn, 0.10 + 0.06 * breathe);
if (isScan) {rowBg = 'linear-gradient(90deg,' + hexA2(T.warn, 0.24) + ' 0%,' + hexA2(T.a1, 0.12) + ' 50%,' + hexA2(T.warn, 0.24) + ' 100%)'; }
var leftCell = pCell(fmtOI(lftV), lftCol, 'right', isPOC || leftNorm > 0.82, LCS, barBgLeft(leftNorm, lftCol, rowBg), SIDE_W, baseFontSize);
leftCell.fontFamily = 'ui-monospace, SFMono-Regular, Menlo, Consolas, monospace';
leftCell.padding = '1px 3px 1px 0px';
leftCell.whiteSpace = 'nowrap';
if (fresh) leftCell.textShadow = '0 0 6px ' + hexA2(T.warn, 0.45);
var midCell = pCell(midTxt, midCol, 'center', true, MCS, rowBg, MID_W, (isPOC || isATM) ? headFontSize : baseFontSize);
midCell.padding = '1px 0px';
midCell.whiteSpace = 'nowrap';
if (isPOC) midCell.textShadow = '0 0 8px ' + hexA2(T.gold, 0.70);
else if (isScan) midCell.textShadow = '0 0 7px ' + hexA2(T.warn, 0.55);
var rightCell = pCell(fmtOI(rgtV), rgtCol, 'left', isPOC || rightNorm > 0.82, RCS, barBgRight(rightNorm, rgtCol, rowBg), SIDE_W, baseFontSize);
rightCell.fontFamily = 'ui-monospace, SFMono-Regular, Menlo, Consolas, monospace';
rightCell.padding = '1px 0px 1px 3px';
rightCell.whiteSpace = 'nowrap';
if (fresh) rightCell.textShadow = '0 0 6px ' + hexA2(T.warn, 0.45);
boardRows.push({ cells: [leftCell, midCell, rightCell] });}
boardRows.push({ cells: [
pCell((model.leftName || 'PUTS') + ' ' + fmtOI(leftTotal), T.bear, 'left', true, LCS, hexA2(T.bear, 0.10), SIDE_W, subFontSize),
pCell('Σ ' + fmtOI(nz(model.allTotal, leftTotal + rightTotal)), T.gold, 'center', true, MCS, hexA2(T.gold, 0.10), MID_W, subFontSize),
pCell(fmtOI(rightTotal) + ' ' + (model.rightName || 'CALLS'), T.bull, 'right', true, RCS, hexA2(T.bull, 0.10), SIDE_W, subFontSize) ] });
var cogTxt = isFinite(QLAB.cog) ? fmtPrice(QLAB.cog) : '—';
var cogAtrTxt = isFinite(QLAB.cogATR) ? QLAB.cogATR.toFixed(2) : '—';
boardRows.push({ cells: [
pCell('COG ' + cogTxt, T.txt, 'left', false, LCS, 'transparent', SIDE_W, subFontSize),
pCell('H ' + (isFinite(QLAB.entropy) ? QLAB.entropy.toFixed(2) : '—'), T.acc, 'center', false, MCS, 'transparent', MID_W, subFontSize),
pCell('ΔATR ' + cogAtrTxt, T.dim, 'right', false, RCS, 'transparent', SIDE_W, subFontSize) ] });
boardRows.push({ cells: [
pCell('STRUCT ' + (isFinite(QLAB.structure) ? (QLAB.structure * 100).toFixed(0) + '%' : '—') + ' ' + (QLAB.specBars || ''), T.acc, 'left', false, 6, hexA2(T.acc, 0.06), '50%', subFontSize),
pCell((signalWhy[lastIdx] ? 'SIG ' + signalWhy[lastIdx] : ('SIG ' + (lastSignalType || 'None'))) + ' · ' + (SESSION.label || ''), SESSION.live ? T.bull : T.dim, 'right', true, 6, SESSION.live ? hexA2(T.bull, 0.10) : 'transparent', '50%', subFontSize) ] });
if (noteTxt) {boardRows.push({ cells: [styleCell(
pCell(noteTxt, T.dim, 'center', false, CS, 'transparent', '100%', subFontSize),
{ letterSpacing: '0.04em' } )] });}
var isBoth = i_embedMode === 'Both';
var halfW = Math.max(110, Math.floor(boardWidthPx * 0.46));
var chartHBoth = clamp(Math.round(halfW * 0.78), 110, 148);
if (i_embedMode === 'Profile') {
var profA = collectProfileSeries(model.rows);
var profHA = clamp(model.rows.length * 11, 140, 240);
var profWA = Math.max(160, boardWidthPx - 16);
boardRows.push({ cells: [pCell('◈ OI PROFILE', T.a1, 'center', true, CS, hexA2(T.a1, 0.10), '100%', subFontSize)] });
boardRows.push({ cells: [{
chart: makeProfileChart(profA, profWA, profHA, 8),
colspan: CS, background_color: T.bg, border_color: 'transparent', border_width: 0, padding: '0px', width: '100%' }] });}
if (i_embedMode === 'Split') {
var splitW = Math.max(160, boardWidthPx - 16);
var splitH = 160;
var pPackS = buildPolarPack(model, splitW / splitH, splitW, splitH);
boardRows.push({ cells: [pCell('◈ MASS SPLIT', T.a1, 'center', true, CS, hexA2(T.a1, 0.10), '100%', subFontSize)] });
boardRows.push({ cells: [{
chart: polarCfgSized(pPackS, splitW, splitH),
colspan: CS, background_color: T.bg, border_color: 'transparent', border_width: 0, padding: '0px', width: '100%' }] });
boardRows.push({ cells: [pCell(pPackS.caption, T.dim, 'center', false, CS, 'transparent', '100%', Math.max(7, subFontSize - 1))] });}
if (isBoth) {
var pRowsB = model.rows;
if (pRowsB.length > 16) {
var thinStep = Math.ceil(pRowsB.length / 16);
var thinned = [];
for (var pti = 0; pti < pRowsB.length; pti += thinStep) thinned.push(pRowsB[pti]);
pRowsB = thinned; }
var profB = collectProfileSeries(pRowsB);
var pPackB = buildPolarPack(model, halfW / chartHBoth, halfW, chartHBoth); // identical box to the OI profile beside it
boardRows.push({ cells: [
pCell('◈ OI PROFILE', T.a1, 'center', true, 6, hexA2(T.a1, 0.10), '50%', subFontSize),
pCell('◈ MASS SPLIT', T.a1, 'center', true, 6, hexA2(T.a1, 0.10), '50%', subFontSize) ] });
boardRows.push({ cells: [ {
chart: makeProfileChart(profB, halfW, chartHBoth, 7),
colspan: 6, background_color: T.bg, border_color: 'transparent', border_width: 0, padding: '0px', width: '50%' }, {
chart: polarCfgSized(pPackB, halfW, chartHBoth),
colspan: 6, background_color: T.bg, border_color: 'transparent', border_width: 0, padding: '0px', width: '50%' } ] });
boardRows.push({ cells: [
pCell((model.leftName || 'PUTS') + ' / ' + (model.rightName || 'CALLS') + ' by strike', T.dim, 'center', false, 6, 'transparent', '50%', Math.max(7, subFontSize - 1)),
pCell(pPackB.caption, T.dim, 'center', false, 6, 'transparent', '50%', Math.max(7, subFontSize - 1)) ] });}
paint_overlay('OLP Board', { position: 'top_right', offset_x: 6, offset_y: 6, order: 'above_all' }, {background: T.bg, border: '1px solid ' + rgba(T.a1, 0.32), borderRadius: 8, width: boardWidth, padding: '2px', rows: boardRows});
// ============================================================================
// GUIDES
// ============================================================================
var GUIDE_BG = 'rgba(10,11,31,0.95)';
var GUIDE_BORD = '#3d2c5e';
var GUIDE_HEAD = '#C084FF';
var GUIDE_LBL = '#00CCFF';
var GUIDE_TXT = '#E0E0FF';
function guideRow(label, body) {
return { cells: [{ text: label, color: GUIDE_LBL, textAlign: 'left', fontSize: 10, fontFamily: 'monospace', fontWeight: 'bold', width: '28%' }, { text: body, color: GUIDE_TXT, textAlign: 'left', fontSize: 9.5, fontFamily: 'monospace', width: '72%' }]};}
function buildGuideRows() {
var gR = [];
gR.push({ cells: [{ text: '◇ OPTIONS LADDER PRO — INPUT GUIDE ◇', color: GUIDE_HEAD, textAlign: 'center', fontWeight: 'bold', colspan: 2, background: 'rgba(170,0,255,0.15)', fontSize: 11 }] });
gR.push(guideRow('Data Source', 'Auto detects the instrument: futures/index/crypto get SLF (volume profile), equities get the live options chain with Advanced Synthetic fallback. Override to force a source.'));
gR.push(guideRow('Resolution', 'Auto (Range-Synced) = the Resolution Cascade: strike spacing syncs to your visible chart range. Zoom in for tight strikes, zoom out for significant ones. Fixed modes lock the spacing.'));
gR.push(guideRow('Expirations', 'Number of nearest expirations aggregated into the OI surface. More = fuller magnet structure.'));
gR.push(guideRow('Signal Sensitivity', 'Signal spacing and flow-shift thresholds. Loose = more signals. Strict = fewer, higher conviction.'));
gR.push(guideRow('Momentum Filter', 'Zero-Lag MFI and CCI gate the signals: MFI over-threshold confirms peaks, under-threshold confirms valleys. MFI+CCI requires both. Off disables.'));
gR.push(guideRow('Intra-bar Signals', 'On: signals on the forming bar show as orange LIVE markers and confirm or vanish at close. Alerts always fire on confirmed close.'));
gR.push(guideRow('Embed Charts', 'Profile = horizontal OI-by-strike bar chart inside the board. Split = the Mass Split dial. Both = profile and dial side by side. Off = table only.'));
gR.push(guideRow('Highlight Scan', 'The traveling highlight box that scans down the ladder rows. Fast = energetic. Slow = cinematic. Off = static.'));
gR.push(guideRow('Fresh-Flow Glow', 'Strikes with new positioning today glow brighter than static OI. Uses live OI-change and daily volume data.'));
gR.push(guideRow('Theme', 'Four color skins driving every overlay, cloud, line, raster and dashboard. Cosmetic only.'));
gR.push({ cells: [{ text: 'Turn this panel off once you are comfortable.', color: 'rgba(255,255,255,0.4)', textAlign: 'center', colspan: 2, fontSize: 9 }] });
return gR;}
var guideRows = i_showInputGuide ? buildGuideRows() : [];
paint_overlay('OLP Input Guide', { position: 'top_left', offset_x: 0, offset_y: 0, order: 'above_all' }, { background: i_showInputGuide ? GUIDE_BG : 'transparent', border: i_showInputGuide ? ('1px solid ' + GUIDE_BORD) : '0px solid transparent', borderRadius: 5, width: i_showInputGuide ? '480px' : '0px', rows: guideRows });
function buildVisualGuideRows() {
var vR = [];
vR.push({ cells: [{ text: '◇ OPTIONS LADDER PRO — VISUAL GUIDE ◇', color: GUIDE_HEAD, textAlign: 'center', fontWeight: 'bold', colspan: 2, background: 'rgba(170,0,255,0.15)', fontSize: 11 }] });
vR.push(guideRow('POC Line', 'Gold line at the max-open-interest strike — the options magnet. Breathes via alpha pulse. Flashes when price crosses. The single most important level on the chart.'));
vR.push(guideRow('VAH / VAL Lines', 'Value Area High and Low — 70% of open interest is between them. Dim when price is inside value (acceptance), brighten when price escapes (rejection or breakout).'));
vR.push(guideRow('Strike Rails', 'Dotted lines at the strikes bracketing price — the ladder structure felt on the main chart without clutter.'));
vR.push(guideRow('POC Cross Markers', '▲ POC or ▼ POC printed at bars where price crossed the POC level — the momentum confirmation.'));
vR.push(guideRow('The Board', 'The animated strike ladder. PUTS bar left, strike + tag center, CALLS bar right. POC row pulses gold. Fresh-flow rows (new OI today) glow. The traveling highlight scans down the rows. Embedded Chart.js shows the full OI profile and Call/Put split.'));
vR.push(guideRow('Mass Split dial', 'Five wedges = where the open interest sits: ABV (above VAH), UVA (POC→VAH), POC (the magnet, gold), LVA (VAL→POC), BLW (below VAL). Wedge radius = share of total OI mass (rings at 25 / 50 / 75 / 100% of the largest); wedge hue = put (red) → call (green) mix of that zone; white dots outside a rim = fresh flow today; the gold POC wedge breathes. Each key carries its share of total mass; the put/call ratio is in the caption and the board header.'));
vR.push(guideRow('Signals', 'POC cross, VA breakout, VA rejection, and flow-shift signals — all gated by the momentum filter, all intra-bar capable, all registered for alerts.'));
vR.push({ cells: [{ text: 'Switch to Input Guide or Off from the Guide Panel dropdown.', color: 'rgba(255,255,255,0.4)', textAlign: 'center', colspan: 2, fontSize: 9 }] });
return vR;}
var visualGuideRows = i_showVisualGuide ? buildVisualGuideRows() : [];
paint_overlay('OLP Visual Guide', { position: 'top_left', offset_x: 0, offset_y: 0, order: 'above_all' }, { background: i_showVisualGuide ? GUIDE_BG : 'transparent', border: i_showVisualGuide ? ('1px solid ' + GUIDE_BORD) : '0px solid transparent', borderRadius: 5, width: i_showVisualGuide ? '560px' : '0px', rows: visualGuideRows });