Peak and Valley Kernel (⛰ PVK)
By: Dskyz | DAFE Trading Systems
Most swing tools draw a zigzag after the fact and call it structure. PVK builds the swing live, from the same forces that actually make a peak or a valley: a center-of-gravity core losing momentum, wicks absorbing pressure, volume abandoning one side for the other, and price sitting inside the value area the tape actually agreed on. It doesn't wait for the zigzag to confirm — it reads the kernel that produces it.
#TSBuild26 (Most Beautiful On-Chart Indicator)
How it works — in 6 steps:
1. Price is compressed into a linear-weighted center-of-gravity spine. Its momentum, not its position, is what tells you a swing is turning.
2. A Dominance Transfer Score tracks bull vs. bear volume-pressure z-scores bar by bar — the tape openly changing hands before price confirms it.
3. An exhaustion trio (impulse decay, curvature, RSI/volume boost) grades bull and bear pressure independently, so a peak needs real selling exhaustion, not just a lower high.
4. A volume profile computes POC/VAH/VAL on a rolling window — swings near the value-area edges score higher, and clean breaks of it can fire their own trend signal.
Chop entropy gates the whole engine — only bars inside your chosen calm-percentile are allowed to fire. Choppy tape is filtered, not traded.
5. Everything fuses into one 0–1 kernel score per side. A single Conviction dial (1–9 or Auto) sets the bar; nothing fires below it, and raising it can only cut signal count, never add noise back in.
6. An optional RCM layer grades every fire as continuation or fade against rolling tape evidence, and a Q-learning bandit sizes the internal R-book between 50–100% — it no longer vetoes trades, because measured backtests showed the trades it used to skip were still profitable. Exit strategy is chosen per trade, causally, from the entropy read at entry: calm tape trails, normal tape runs a hybrid bracket, chaotic tape takes a fixed ATR target/stop.
Every system with guides:
Input Guide — every menu item explained in plain language.
Visual Guide — every ribbon, rail, badge, and dashboard section explained.
What you see on chart:
Adaptive Reservoir — nine breathing ribbons wrapped around one opaque gravity core, expanding with kernel coherence and leaning with peak/valley imbalance, with a slow tunneling cascade running through the whole fan.
Candle tint — green for a forming valley, rust for a forming peak, opacity set by kernel strength.
▲ / ▼ confirmed entries, △ / ▽ watch bars, a T badge when the fire came from a value-area break instead of a swing, ✕ marks a booked exit, yellow + ! is the live forming-bar preview.
Lower panel, two views:
Signal Raster — A dense event-to-fire web across pivot, fractal, COG, and value-area rows.
Harmonic Rosette — A polar confluence instrument with a coherence guilloché, causal spiral arms, phase spokes, an impulse-spectrum ring, a spectral scalogram, and a live neural correlation lattice.
Dashboard reads live: structure and dominant kernel, signal state and why it isn't firing if it isn't, quality vs. your conviction threshold, chop clearance, directional bias, the swing kernel breakdown, the live value area, the sizing agent's mode and epsilon, and the full ledger — trade count, win rate, profit factor, expectancy, net R, drawdown, in-sample vs. out-of-sample, and the last exit booked.
Limits (know them):
Needs warm-up for the value-area profile, kernel memory, and normalization windows — weakest on brand-new charts.
The sizing agent adjusts size, not entries — it cannot create or block a signal.
It's a signal engine, not a broker: the R-ledger is internal R-math on confirmed closes, not real fills.
A high kernel score means strong confluence at that instant — it is never a guaranteed turn.
Structure over noise. Value before entry. Trade the inflection.
— Dskyz | DAFE Trading Systems
describe_indicator('Peak and Valley Kernel', { shortName: '⛰ PVK', mainColorInheritFrom: 'legend_anchor' });
// ==============================================================================================
// --------------------- Peak and Valley Kernel ----------------------------------------------
// ==============================================================================================
// LIBRARIES & QUANT-CORE
// ==============================================================================================
var jStat = library('jstat');
var fftjs = library('fft-js');
var tinycolor2 = library('tinycolor2');
var bsBounds = library('binary-search-bounds');
var tiny = tinycolor2;
var EPS = 1e-10;
var DAFE = {
utils: {
clamp: function (x, lo, hi) { x = (x === null || x === undefined || isNaN(x)) ? lo : x; return x < lo ? lo : (x > hi ? hi : x); },
nz: function (v, fb) { return (v === null || v === undefined || isNaN(v)) ? (fb === undefined ? 0 : fb) : v; },
make: function (len, v) { var a = [], k; for (k = 0; k < len; k++) a.push(v); return a; },
sum: function (a) { var s = 0, k; for (k = 0; k < a.length; k++) s += DAFE.utils.nz(a[k], 0); return s; } },
stats: {
mean: function (a) { return a.length ? DAFE.utils.sum(a) / a.length : 0; },
stdev: function (a) { if (a.length < 2) return 0; var m = DAFE.stats.mean(a), s = 0, k; for (k = 0; k < a.length; k++) { var d = DAFE.utils.nz(a[k], m) - m; s += d * d; } return Math.sqrt(s / (a.length - 1)); },
zscoreAt: function (arr, idx, per) {
if (idx < per) return 0;
var w = arr.slice(idx - per + 1, idx + 1), m = DAFE.stats.mean(w), sd = DAFE.stats.stdev(w);
return sd > 0 ? (DAFE.utils.nz(arr[idx], m) - m) / sd : 0; },
normalize: function (v, lo, hi) { return hi - lo !== 0 ? DAFE.utils.clamp((v - lo) / (hi - lo), 0, 1) : 0.5; },
skew: function (a) { var m = DAFE.stats.mean(a), sd = DAFE.stats.stdev(a); if (sd <= 0) return 0; var s = 0, k; for (k = 0; k < a.length; k++) { var d = (DAFE.utils.nz(a[k], m) - m) / sd; s += d * d * d; } return s / a.length; },
kurtosis: function (a) { var m = DAFE.stats.mean(a), sd = DAFE.stats.stdev(a); if (sd <= 0) return 0; var s = 0, k; for (k = 0; k < a.length; k++) { var d = (DAFE.utils.nz(a[k], m) - m) / sd; s += d * d * d * d; } return s / a.length - 3; } },
entropy: {
shannonAt: function (arr, idx, per, bins) {
if (idx < per) return 0.5;
bins = bins || 8;
var w = arr.slice(idx - per + 1, idx + 1), lo = Infinity, hi = -Infinity, k;
for (k = 0; k < w.length; k++) { var v = DAFE.utils.nz(w[k], 0); if (v < lo) lo = v; if (v > hi) hi = v; }
if (hi - lo <= 0) return 0;
var cnt = DAFE.utils.make(bins, 0);
for (k = 0; k < w.length; k++) { var b = Math.min(bins - 1, Math.floor((DAFE.utils.nz(w[k], lo) - lo) / (hi - lo) * bins)); cnt[b]++; }
var H = 0;
for (k = 0; k < bins; k++) { var p = cnt[k] / w.length; if (p > 0) H -= p * Math.log(p); }
return DAFE.utils.clamp(H / Math.log(bins), 0, 1); } },
regime: {
probability: function (t, v, e) {
var bull = DAFE.utils.clamp(0.5 + 0.5 * t - 0.25 * e, 0, 1);
var bear = DAFE.utils.clamp(0.5 - 0.5 * t - 0.25 * e, 0, 1);
var comp = DAFE.utils.clamp((1 - v) * (1 - e), 0, 1);
var stress = DAFE.utils.clamp(v * e, 0, 1);
var s = bull + bear + comp + stress;
if (s <= 0) return { bull: 0.25, bear: 0.25, compression: 0.25, stress: 0.25 };
return { bull: bull / s, bear: bear / s, compression: comp / s, stress: stress / s }; } },
expectancy: {
summary: function (rList) {
var t = rList.length, w = 0, l = 0, gp = 0, gl = 0, net = 0, peak = 0, dd = 0, run = 0, k;
for (k = 0; k < t; k++) {
var r = DAFE.utils.nz(rList[k], 0);
net += r; run += r;
if (run > peak) peak = run;
if (peak - run > dd) dd = peak - run;
if (r > 0) { w++; gp += r; } else { l++; gl += Math.abs(r); } }
return { trades: t, wins: w, losses: l, wr: t ? w / t : 0, pf: gl > 0 ? gp / gl : (gp > 0 ? 99 : 0), expR: t ? net / t : 0, netR: net, ddR: dd }; } } };
function clamp(x, lo, hi) { return DAFE.utils.clamp(x, lo, hi); }
function nz(v, fb) { return DAFE.utils.nz(v, fb); }
function clamp01(x) { return clamp(x, 0, 1); }
function clamp11(x) { return clamp(x, -1, 1); }
function make(len, v) { return DAFE.utils.make(len, v); }
function safeDiv(a, b, c) { return (b && b !== 0 && !isNaN(b)) ? (a / b) : c; }
function cauchyCDF(x, x0, g) {
if (!(g > 0)) g = 1e-6;
try { var v = jStat.cauchy.cdf(x, x0, g); if (v !== null && v !== undefined && !isNaN(v)) return clamp01(v); } catch (e) {}
return clamp01(0.5 + Math.atan((x - x0) / g) / Math.PI);}
function normalCDF(x, mu, sd) {
if (!(sd > 0)) sd = 1e-6;
try { var v = jStat.normal.cdf(x, mu, sd); if (v !== null && v !== undefined && !isNaN(v)) return clamp01(v); } catch (e) {}
var z = (x - mu) / (sd * Math.sqrt(2)), t = 1 / (1 + 0.3275911 * Math.abs(z));
var y = 1 - (((((1.061405429 * t - 1.453152027) * t) + 1.421413741) * t - 0.284496736) * t + 0.254829592) * t * Math.exp(-z * z);
return clamp01(0.5 * (1 + (z >= 0 ? y : -y)));}
// ==============================================================================================
// INPUTS
// ==============================================================================================
function rangeOpts(min, max, step, dec) {
var out = [], steps = Math.round((max - min) / step), k, v;
for (k = 0; k <= steps; k++) { 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 }; }
var i_preset = input.select('◈ Engine Preset ◈', 'Balanced', ['Scalper', 'Balanced', 'Swing', 'Position'], tt('Owns every window and threshold: the COG length, the swing momentum sensitivity, the value-area lookback, the chandelier and normalisation windows, signal spacing and trade horizon.'));
var i_tempo = input.select('◈ Preset Multiplier ◈', 1.00, rangeOpts(0.50, 2.00, 0.05, 2), tt('The sensitivity dial for the whole preset. BELOW 1.00 shortens every window — swings register faster, more signals. ABOVE 1.00 lengthens them — steadier read, fewer signals. 1.00 is the preset exactly as calibrated.'));
var i_chopStrict = input.select('◈ Chop Strictness ◈', 'Balanced', ['Loose', 'Balanced', 'Strict'], tt('How much of the entropy distribution is allowed through. Loose admits the calmest 80 percent of bars, Strict only the calmest 20. MEASURED on holdout: rank 0.80 gives +0.7955 ATR at 63.0 percent over 227 trades, rank 0.20 gives +0.7624 at 71.8 percent over 170.'));
var i_minAmp = input.select('◈ Min Swing Amplitude ◈', 0.40, rangeOpts(0.00, 3.00, 0.10, 2), tt('Minimum move in ATR since the last opposite swing before a new swing may register. This gate tracks the REAL zigzag: the swing state advances on every chop-passing trigger, so this dial genuinely shapes the swing sequence — higher = only larger swings are traded.'));
var i_conv = input.select('◈ Conviction ◈', 'Auto', ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'Auto'], tt('The single selectivity dial, family-standard with AUR and SGE. Auto (default) reads the preset calibrated conviction. 1 = loosest — many signals, thin edge. 9 = strictest — rare signals, deep edge. Monotonic: raising it can only reduce signal count. MEASURED profitable across the whole band on 30 holdout seeds.'));
var i_agentMode = input.select('◈ Agent Mode ◈', 'Balanced', ['Off', 'Fast Learner', 'Balanced', 'Deep Thinker'], tt('A Q-learning bandit that modulates the LEDGER conviction weight between 50 and 100 percent — it sizes the internal R book, it does not place orders and it can NO LONGER veto an entry. MEASURED reason: scored at full weight, the trades it skipped were still profitable (Fast Learner skipped 403 worth +0.3241 ATR while keeping 1187 worth +0.4482), so vetoing discarded edge rather than avoiding losses.'));
var i_rcmMode = input.select('◈ RCM Layer ◈', 'Soft', ['Off', 'Soft', 'Gate'], tt('Rolling evidence on the two fire families: a swing taken with the trend is continuation, one against it is a fade attempt. Soft grades the fire by tape integrity. Gate can also block the family the tape does not own. Neither creates a signal.'));
var i_exitStrategy = input.select('◈ Exit Strategy ◈', 'Auto', ['Auto', 'Trailing Stop', 'ATR Target/Stop', 'Time Stop', 'Entropy Barrier Breach', 'Hybrid Trail+ATR'], tt('Auto (default) picks the exit PER TRADE from the tape at entry — decided causally from the entry bar: calm entropy rides a Trailing Stop, normal tape takes the Hybrid bracket, chaotic entropy takes a fixed ATR Target/Stop. The named modes force one exit for every trade. All respect Max Hold Bars. Signal Reversal was removed in v7.2 — exiting on the opposite signal cut winning turns short.'));
var i_slAtr = input.select('◈ Stop Loss (ATR) ◈', 1.50, rangeOpts(0.30, 6.00, 0.10, 1), tt('Stop distance from entry in ATR multiples. The Execution Profile stretches this.'));
var i_tpAtr = input.select('◈ Take Profit (ATR) ◈', 2.40, rangeOpts(0.50, 10.00, 0.10, 1), tt('Target distance in ATR multiples. Keep above the stop for positive reward-to-risk. Used by ATR Target/Stop and Hybrid.'));
var i_trailAtr = input.select('◈ Trailing Stop (ATR) ◈', 1.60, rangeOpts(0.30, 6.00, 0.10, 1), tt('Trail distance behind the best price reached. Used by Trailing Stop, Hybrid and Entropy Barrier Breach.'));
var i_maxHold = input.select('◈ Max Hold Bars ◈', 40, rangeOpts(4, 300, 1, 0), tt('Hard duration cap applied regardless of exit strategy. Time Stop and Hybrid also exit on the clock at this cap.'));
var i_intrabar = input.select('◈ Intra-bar ◈', 'On', ['Off', 'On'], tt('On paints a yellow LIVE badge on the forming bar the moment a swing qualifies. Alerts still fire on bar close only, so nothing you are notified about can vanish.'));
var i_theme = input.select('◈ Theme ◈', 'Timber', ['Timber', 'Glacier', 'Ember', 'Mono'], tt('Color family for the reservoir ribbons, value-area rails, badges, dashboard and lower panel. Cosmetic only.'));
var i_dispSuite = input.select('◈ Display Suite ◈', 'Full', ['Off', 'Compact', 'Full', 'Chart Only'], tt('Off hides the dashboard and the chart labels. Compact is a short dashboard plus labels. Full is the whole dashboard plus labels. Chart Only keeps labels and hides the dashboard.'));
var i_guideMode = input.select('◈ Guide Panel ◈', 'Off', ['Off', 'Input Guide', 'Visual Guide'], tt('Off hides both. Only one guide renders at a time. Cosmetic only.'));
var i_density = input.select('◈ Visual Density ◈', 'Standard', ['Minimal', 'Standard', 'Rich'], tt('How much chart art draws. Minimal is the gravity core and badges. Standard adds the adaptive reservoir fan. Rich adds the value-area rails and swing-leg connectors. Zero effect on signals.'));
var i_brightness = input.select('◈ Visual Brightness ◈', '5', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'], tt('Main-chart visuals only (reservoir ribbons, gravity core, value-area rails, candle tint). 1 = barely visible, 5 = classic look, 10 = brightest. Badges, dashboard, guides and the lower panel are never dimmed.'));
var i_manifold = input.select('◈ Lower Manifold ◈', 'Signal Raster', ['Off', 'Signal Raster', 'Harmonic Rosette'], tt('Two lower-panel visual systems only. Signal Raster is the updated Flip-Mode event web. Harmonic Rosette is a polar confluence instrument: a coherence guilloché rosette, causal spiral arms, phase-sector spokes, a radix-2 impulse spectrum ring, R-ledger ticks, radar sweep and beat waves — pure plane geometry, zero 3D projection.'));
var i_manifoldH = input.select('◈ Manifold Height ◈', 'Compact', ['Petite', 'Compact', 'Standard', 'Tall', 'Grand'], tt('Scales the lower manifold panel height: Petite 110px, Compact 150px, Standard 200px, Tall 260px, Grand 330px. Cosmetic only — zero effect on signals.'));
var MANIFOLD_H = { 'Petite': 110, 'Compact': 150, 'Standard': 200, 'Tall': 260, 'Grand': 330 }[i_manifoldH] || 150;
var i_visRange = input.anchor('visible range');
// ==============================================================================================
// AUTO-CONFIG
// ==============================================================================================
var USE_CHOP = true;
var CHOP_PCT = true;
var BOTH_READ = true;
var TREND_ON = true;
var PRESET_MAP = {
'Scalper': { cog: 6, va: 30, rows: 20, ch: 14, norm: 240, space: 3, horizon: 24 },
'Balanced': { cog: 10, va: 50, rows: 24, ch: 20, norm: 400, space: 6, horizon: 40 },
'Swing': { cog: 15, va: 80, rows: 28, ch: 30, norm: 520, space: 12, horizon: 64 },
'Position': { cog: 20, va: 150, rows: 32, ch: 40, norm: 640, space: 22, horizon: 96 } };
// ---- PRESET CALIBRATION CARD ---------------------------------------------
var CHOP_MAP = { 'Loose': 0.80, 'Balanced': 0.50, 'Strict': 0.20 };
var AGENT_MAP = {
'Off': { on: false, eps: 0.00, decay: 1.0000, lr: 0.00 },
'Fast Learner': { on: true, eps: 0.35, decay: 0.9990, lr: 0.15 },
'Balanced': { on: true, eps: 0.20, decay: 0.9995, lr: 0.10 },
'Deep Thinker': { on: true, eps: 0.10, decay: 0.9998, lr: 0.05 } };
var VIS_MAP = {
'Minimal': { arcs: false, rails: false, legs: false, glow: false, step: 4, visWin: 1000 },
'Standard': { arcs: true, rails: false, legs: false, glow: true, step: 3, visWin: 1500 },
'Rich': { arcs: true, rails: true, legs: true, glow: true, step: 2, visWin: 2000 } };
var THEME_MAP = {
'Timber': { a1: '#4ADE80', a2: '#C2410C', a3: '#A3E635', bull: '#4ADE80', bear: '#C2410C', warn: '#FDE047', info: '#BBF7D0', txt: '#F0FDF4', dim: '#5F7A68', bg: 'rgba(8,16,11,0.92)', panel: 'rgba(16,32,22,0.92)' },
'Glacier': { a1: '#7DD3FC', a2: '#A78BFA', a3: '#5EEAD4', bull: '#38BDF8', bear: '#F472B6', warn: '#FDE047', info: '#7DD3FC', txt: '#E6F4FF', dim: '#5A7186', bg: 'rgba(5,14,26,0.92)', panel: 'rgba(10,26,44,0.92)' },
'Ember': { a1: '#FDBA74', a2: '#F43F5E', a3: '#FBBF24', bull: '#FBBF24', bear: '#F43F5E', warn: '#FDBA74', info: '#FCA5A5', txt: '#FFF1E6', dim: '#8A6552', bg: 'rgba(24,10,4,0.92)', panel: 'rgba(44,20,10,0.92)' },
'Mono': { a1: '#CBD5E1', a2: '#94A3B8', a3: '#E2E8F0', bull: '#E2E8F0', bear: '#64748B', warn: '#CBD5E1', info: '#94A3B8', txt: '#F1F5F9', dim: '#64748B', bg: 'rgba(10,12,16,0.92)', panel: 'rgba(20,24,32,0.92)' } };
var CFG = PRESET_MAP[i_preset] || PRESET_MAP['Balanced'];
var AGT = AGENT_MAP[i_agentMode] || AGENT_MAP['Balanced'];
var VIS = VIS_MAP[i_density] || VIS_MAP['Standard'];
var TH = THEME_MAP[i_theme] || THEME_MAP['Timber'];
function scaleLen(base, lo, hi) { return clamp(Math.round(base * i_tempo), lo, hi); }
var L_cog = scaleLen(CFG.cog, 3, 60);
var L_va = scaleLen(CFG.va, 15, 300);
var L_rows = clamp(Math.round(CFG.rows), 10, 40);
var L_ch = scaleLen(CFG.ch, 8, 120);
var L_norm = scaleLen(CFG.norm, 120, 900);
var L_atr = clamp(Math.round(14 * i_tempo), 5, 120);
var L_eq = clamp(Math.round(L_ch * 2), 8, 400);
var CHOP_Q = clamp(nz(CHOP_MAP[i_chopStrict], 0.50), 0.05, 1.00);
var CONV_AUTO = { 'Scalper': 0.38, 'Balanced': 0.52, 'Swing': 0.64, 'Position': 0.68 };
var CONV = i_conv === 'Auto' ? clamp(nz(CONV_AUTO[i_preset], 0.52), 0.30, 0.80)
: clamp(0.34 + 0.48 * (clamp(parseInt(i_conv, 10) || 1, 1, 9) - 1) / 8, 0.34, 0.82);
var SPACING = clamp(Math.round(CFG.space * i_tempo), 1, 60);
var HORIZON = clamp(Math.round(CFG.horizon * i_tempo), 8, 300);
var MIN_AMP = clamp(i_minAmp, 0, 3);
var SENS = 1.00;
var i_rcmOn = i_rcmMode !== 'Off';
var i_rcmGate = i_rcmMode === 'Gate';
var i_live = i_intrabar === 'On';
var i_dash = i_dispSuite === 'Off' || i_dispSuite === 'Chart Only' ? 'Off' : i_dispSuite;
var i_marks = i_dispSuite !== 'Off';
var cBull = TH.bull, cBear = TH.bear, cWarn = TH.warn, cInfo = TH.info;
var cTxt = TH.txt, cDim = TH.dim, cBg = TH.bg, cLive = '#FFE066';
var BR = parseInt(i_brightness, 10);
var BR_F = Math.pow(BR / 5, 1.7);
function brA(a) { return clamp01(a * BR_F); }
function brC(col) { try { return tiny.mix('#0A140E', col, clamp(100 * Math.min(BR_F, 1), 4, 100)).toHexString(); } catch (eBr) { return col; } }
function ribA(a) {
var depthA = clamp01(a), dimF = Math.min(BR_F, 1);
var brightLift = clamp01(Math.max(BR_F - 1, 0) * 0.18);
return clamp01(depthA * dimF + (1 - depthA) * brightLift);}
// ==============================================================================================
// UTILITIES
// ==============================================================================================
var n = close.length;
var lastBar = n - 1;
function rgba(hex, a) { try { return tiny(hex).setAlpha(a).toRgbString(); } catch (e) { return hex; } }
function makeNull(len) { return make(len, null); }
function prefixSums(arr) {
var m = arr.length, ps = make(m + 1, 0), ps2 = make(m + 1, 0), ii, v;
for (ii = 0; ii < m; ii++) { v = nz(arr[ii], 0); ps[ii + 1] = ps[ii] + v; ps2[ii + 1] = ps2[ii] + v * v; }
return { ps: ps, ps2: ps2 };}
function fastZscore(arr, pref, idx, per) {
if (idx < per) return 0;
var lo = idx - per + 1, sum = pref.ps[idx + 1] - pref.ps[lo], sumSq = pref.ps2[idx + 1] - pref.ps2[lo];
var m = sum / per, varr = (sumSq - per * m * m) / Math.max(1, per - 1);
var sd = varr > 0 ? Math.sqrt(varr) : 0;
return sd > 0 ? (nz(arr[idx], m) - m) / sd : 0;}
function newDeque() { return { a: [], h: 0 }; }
function dqPushMax(dq, idx, valArr) { var v = valArr[idx]; while (dq.a.length > dq.h && valArr[dq.a[dq.a.length - 1]] <= v) dq.a.pop(); dq.a.push(idx); if (dq.h > 256 && dq.h * 2 > dq.a.length) { dq.a = dq.a.slice(dq.h); dq.h = 0; } }
function dqPushMin(dq, idx, valArr) { var v = valArr[idx]; while (dq.a.length > dq.h && valArr[dq.a[dq.a.length - 1]] >= v) dq.a.pop(); dq.a.push(idx); if (dq.h > 256 && dq.h * 2 > dq.a.length) { dq.a = dq.a.slice(dq.h); dq.h = 0; } }
function dqAdvance(dq, minIdx) { while (dq.h < dq.a.length && dq.a[dq.h] < minIdx) dq.h++; }
function norm01(series, win) {
var m = series.length, clean = make(m, 0), i2;
for (i2 = 0; i2 < m; i2++) clean[i2] = nz(series[i2], 0);
var out = make(m, 0.5), maxDq = newDeque(), minDq = newDeque();
for (i2 = 0; i2 < m; i2++) {
dqPushMax(maxDq, i2, clean); dqPushMin(minDq, i2, clean);
dqAdvance(maxDq, i2 - win + 1); dqAdvance(minDq, i2 - win + 1);
var hi = clean[maxDq.a[maxDq.h]], lo = clean[minDq.a[minDq.h]];
out[i2] = hi - lo !== 0 ? clamp01((clean[i2] - lo) / (hi - lo)) : 0.5;}
return out;}
function lag1(series) { var out = make(series.length, null), i; for (i = 1; i < series.length; i++) out[i] = series[i - 1]; return out; }
var ret = make(n, 0), aret = make(n, 0), i;
for (i = 1; i < n; i++) { ret[i] = Math.log(safeDiv(close[i], close[i - 1], 1)); aret[i] = Math.abs(ret[i]); }
var atr14 = atr(L_atr);
function sgn(x) { return x > 0 ? 1 : (x < 0 ? -1 : 0); }
function sigmoid(x) { return 1 / (1 + Math.exp(-clamp(x, -40, 40))); }
function gaussK(x, mu, sg) { var s = Math.max(1e-8, sg), z = (x - mu) / s; return Math.exp(-0.5 * z * z); }
var entArr = make(n, 0.5), retZ = make(n, 0), regStress = make(n, 0.25);
var retPref = prefixSums(ret);
var ENT_PER96 = Math.min(L_norm, 96);
for (i = 0; i < n; i++) {
entArr[i] = DAFE.entropy.shannonAt(aret, i, ENT_PER96, 8);
retZ[i] = fastZscore(ret, retPref, i, ENT_PER96);}
var volN0 = norm01(aret, L_norm);
for (i = 0; i < n; i++) regStress[i] = clamp01(nz(volN0[i], 0.25) * nz(entArr[i], 0.5));
var ENGINE_START = Math.min(n - 1, Math.max(L_va + 8, L_ch * 2, 60) + 4);
var emaTrend = ema(close, L_eq);
var L_norm4 = clamp(Math.round(L_norm * 0.25), 8, 300);
var atrMean = sma(atr14, L_norm4), atrZ = make(n, 1);
for (i = 0; i < n; i++) { var am = nz(atrMean[i], 0); atrZ[i] = am > 0 ? nz(atr14[i], 0) / am : 1; }
var spine = ema(close, L_eq);
var fastE = ema(close, Math.max(2, Math.round(L_eq * 0.4))), slowE = ema(close, L_eq);
var DirBias = make(n, 0), DirConv = make(n, 0), DIR = make(n, 0);
var hiClean = make(n, 0), loClean = make(n, 0);
for (i = 0; i < n; i++) { hiClean[i] = nz(high[i], 0); loClean[i] = nz(low[i], 0); }
var hiDq = newDeque(), loDq = newDeque();
for (i = 0; i < n; i++) {
var aRef = Math.max(nz(atr14[i], 0), Math.abs(nz(close[i], 1)) * 0.0001);
var slopeN = clamp11(i > 0 ? (nz(spine[i], 0) - nz(spine[i - 1], 0)) / aRef * 10 : 0);
var sepN = clamp11((nz(fastE[i], 0) - nz(slowE[i], 0)) / aRef * 2.2);
dqPushMax(hiDq, i, hiClean); dqPushMin(loDq, i, loClean);
dqAdvance(hiDq, i - L_eq); dqAdvance(loDq, i - L_eq);
var hh = hiClean[hiDq.a[hiDq.h]], ll = loClean[loDq.a[loDq.h]];
var posN = hh - ll > 0 ? clamp11(((nz(close[i], 0) - ll) / (hh - ll)) * 2 - 1) : 0;
DirBias[i] = clamp11(0.45 * slopeN + 0.35 * sepN + 0.20 * posN);
DirConv[i] = clamp01(Math.abs(DirBias[i]));
DIR[i] = clamp11(nz(close[i], 0) > nz(emaTrend[i], 0) ? 1 : -1);}
// ==============================================================================================
// CORE ENGINE
// ==============================================================================================
var hl2A = make(n, 0);
for (i = 0; i < n; i++) hl2A[i] = (nz(high[i], 0) + nz(low[i], 0)) / 2;
function lwmaOf(series, len) {
var out = make(series.length, 0), a, k;
for (a = 0; a < series.length; a++) {
var m = Math.max(0, a - len + 1), num = 0, den = 0;
for (k = m; k <= a; k++) { var w = k - m + 1; num += nz(series[k], 0) * w; den += w; }
out[a] = den > 0 ? num / den : 0;}
return out;}
var cogArr = lwmaOf(hl2A, L_cog);
var cogMom = make(n, 0);
for (i = 1; i < n; i++) cogMom[i] = nz(cogArr[i], 0) - nz(cogArr[i - 1], 0);
var strictHiA = make(n, null), strictLoA = make(n, null);
for (i = 2; i < n - 2; i++) {
var okH = true, okL = true, k2;
for (k2 = i - 2; k2 <= i + 2; k2++) {
if (k2 === i) continue;
if (nz(high[k2], 0) >= nz(high[i], 0)) okH = false;
if (nz(low[k2], 0) <= nz(low[i], 0)) okL = false;}
if (okH) strictHiA[i] = nz(high[i], 0);
if (okL) strictLoA[i] = nz(low[i], 0);}
var pivHiA = make(n, false), pivLoA = make(n, false);
for (i = 2; i < n; i++) {
pivHiA[i] = nz(high[i - 1], 0) > nz(high[i - 2], 0) && nz(high[i - 1], 0) > nz(high[i], 0);
pivLoA[i] = nz(low[i - 1], 0) < nz(low[i - 2], 0) && nz(low[i - 1], 0) < nz(low[i], 0);}
var entPer = clamp(Math.round(30 * i_tempo), 10, 120);
var chopEnt = make(n, 1), chopRank = make(n, 0.5);
var upRun = 0, totRun = 0, m3Prev = 1;
for (i = 0; i < n; i++) {
var m3 = Math.max(1, i - entPer + 1);
if (i >= 1) { upRun += (nz(close[i], 0) > nz(close[i - 1], 0)) ? 1 : 0; totRun++; }
while (m3Prev < m3) { upRun -= (nz(close[m3Prev], 0) > nz(close[m3Prev - 1], 0)) ? 1 : 0; totRun--; m3Prev++; }
var p3 = totRun > 0 ? upRun / totRun : 0.5;
chopEnt[i] = (p3 <= 0 || p3 >= 1) ? 0 : -(p3 * Math.log(p3) + (1 - p3) * Math.log(1 - p3)) / Math.log(2);}
var CR_BINS = 256, crHist = make(CR_BINS, 0);
function crBinOf(v) { var vv = clamp01(nz(v, 1)); var b = Math.floor(vv * CR_BINS); return b >= CR_BINS ? CR_BINS - 1 : (b < 0 ? 0 : b); }
for (i = 0; i < n; i++) {
crHist[crBinOf(chopEnt[i])]++;
var winLen = i + 1;
if (winLen > L_norm) { crHist[crBinOf(chopEnt[i - L_norm])]--; winLen = L_norm; }
var myBin = crBinOf(chopEnt[i]), c4 = 0, bb;
for (bb = 0; bb <= myBin; bb++) c4 += crHist[bb];
chopRank[i] = winLen > 1 ? clamp01((c4 - 1) / (winLen - 1)) : 0.5;}
var chopOk = make(n, true);
for (i = 0; i < n; i++) chopOk[i] = chopRank[i] <= CHOP_Q;
var bullP = make(n, 0), bearP = make(n, 0);
for (i = 0; i < n; i++) {
var rg4 = Math.max(1e-9, nz(high[i], 0) - nz(low[i], 0)), vv = Math.max(0, nz(volume ? volume[i] : 1, 1));
bullP[i] = (nz(close[i], 0) - nz(low[i], 0)) / rg4 * vv;
bearP[i] = (nz(high[i], 0) - nz(close[i], 0)) / rg4 * vv;}
var dtsArr = make(n, 0);
var DTS_PER = Math.min(L_norm, 120);
var bullPref = prefixSums(bullP), bearPref = prefixSums(bearP);
for (i = 0; i < n; i++) {
var bz = fastZscore(bullP, bullPref, i, DTS_PER);
var sz = fastZscore(bearP, bearPref, i, DTS_PER);
dtsArr[i] = clamp(bz - sz, -6, 6);}
var pocA = make(n, null), vahA = make(n, null), valA = make(n, null);
function profileAt(endIdx, lookback, rows) {
var st = Math.max(0, endIdx - lookback + 1), pL = Infinity, pH = -Infinity, k5;
for (k5 = st; k5 <= endIdx; k5++) {
if (nz(low[k5], 0) < pL) pL = nz(low[k5], 0);
if (nz(high[k5], 0) > pH) pH = nz(high[k5], 0);}
if (!(pH > pL)) return { poc: nz(close[endIdx], 0), vah: nz(close[endIdx], 0), val: nz(close[endIdx], 0) };
var rh = (pH - pL) / rows, bins = make(rows, 0);
for (k5 = st; k5 <= endIdx; k5++) {
var idx5 = clamp(Math.floor((nz(close[k5], 0) - pL) / rh), 0, rows - 1);
bins[idx5] += Math.max(0, nz(volume ? volume[k5] : 1, 1));}
var tv = 0, pi = 0, pv = -1, k6;
for (k6 = 0; k6 < rows; k6++) { tv += bins[k6]; if (bins[k6] > pv) { pv = bins[k6]; pi = k6; } }
var target = tv * 0.70, acc = bins[pi], loR = pi, hiR = pi;
while (acc < target && (loR > 0 || hiR < rows - 1)) {
var dLo = loR > 0 ? bins[loR - 1] : -1, dHi = hiR < rows - 1 ? bins[hiR + 1] : -1;
if (dHi >= dLo) { hiR++; acc += Math.max(0, dHi); } else { loR--; acc += Math.max(0, dLo); }}
return { poc: pL + (pi + 0.5) * rh, vah: pL + (hiR + 1) * rh, val: pL + loR * rh };}
var profStride = clamp(Math.round(L_va / 10), 1, 12);
var lastProf = null;
for (i = 0; i < n; i++) {
if (i < ENGINE_START) continue;
if (lastProf === null || (i - ENGINE_START) % profStride === 0)
lastProf = profileAt(i - 1, L_va, L_rows);
pocA[i] = lastProf.poc;
vahA[i] = lastProf.vah;
valA[i] = lastProf.val;}
var pricePeakA = make(n, false), priceValleyA = make(n, false);
var fracHiA = make(n, false), fracLoA = make(n, false);
for (i = 3; i < n; i++) {
var mPrev = nz(hl2A[i - 1], 0) - nz(hl2A[i - 2], 0);
var mCurr = nz(hl2A[i], 0) - nz(hl2A[i - 1], 0);
var mTh = Math.max(nz(atr14[i], 0), Math.abs(nz(close[i], 1)) * 1e-4) * 0.05 * SENS;
pricePeakA[i] = (mPrev > mTh && mCurr < -mTh) || pivHiA[i];
priceValleyA[i] = (mPrev < -mTh && mCurr > mTh) || pivLoA[i];
fracHiA[i] = strictHiA[i - 2] !== null || strictHiA[i - 3] !== null;
fracLoA[i] = strictLoA[i - 2] !== null || strictLoA[i - 3] !== null;}
var rsiN = rsi(close, 14);
var volSmaX = sma(volume, 20);
var exBull = make(n, 0), exBear = make(n, 0), exBullN = make(n, 0), exBearN = make(n, 0);
var exBullPER = make(n, 0), exBearPER = make(n, 0), exBullGAL = make(n, 0), exBearGAL = make(n, 0);
for (i = 1; i < n; i++) {
var aRx = Math.max(nz(atr14[i], 0), Math.abs(nz(close[i], 1)) * 1e-4);
var loW = Math.max(0, i - L_ch), hiW2 = -Infinity, loW2 = Infinity, k9;
for (k9 = loW; k9 <= i; k9++) { if (nz(low[k9], 0) < loW2) loW2 = nz(low[k9], 0); if (nz(high[k9], 0) > hiW2) hiW2 = nz(high[k9], 0); }
var bImp = 0, sImp = 0;
if (Math.abs(nz(low[i], 0) - loW2) <= aRx * 0.5) bImp += 1.0;
if (Math.abs(nz(high[i], 0) - hiW2) <= aRx * 0.5) sImp += 1.0;
var curveX = i >= 4 ? nz(close[i], 0) - 2 * nz(close[i - 2], 0) + nz(close[i - 4], 0) : 0;
if (curveX > 0) bImp += 0.7;
if (curveX < 0) sImp += 0.7;
var rX = nz(rsiN[i], 50);
if (rX < 45) bImp += clamp((45 - rX) / 100, 0, 0.5);
if (rX > 55) sImp += clamp((rX - 55) / 100, 0, 0.5);
var vRx = nz(volume ? volume[i] : 1, 1) / Math.max(nz(volSmaX[i], 1), EPS);
var vBoost = clamp((vRx - 1.0) * 0.4, 0, 0.5);
if (curveX > 0) bImp += vBoost;
if (curveX < 0) sImp += vBoost;
exBull[i] = nz(exBull[i - 1]) * 0.88 + bImp;
exBear[i] = nz(exBear[i - 1]) * 0.88 + sImp;}
var exBullDq = newDeque(), exBearDq = newDeque();
for (i = 0; i < n; i++) {
dqPushMax(exBullDq, i, exBull); dqPushMax(exBearDq, i, exBear);
dqAdvance(exBullDq, i - 119); dqAdvance(exBearDq, i - 119);
var mB = Math.max(0, exBull[exBullDq.a[exBullDq.h]]), mS2 = Math.max(0, exBear[exBearDq.a[exBearDq.h]]);
exBullN[i] = mB > EPS ? exBull[i] / mB : 0;
exBearN[i] = mS2 > EPS ? exBear[i] / mS2 : 0;}
for (i = 10; i < n; i++) {
var upD = Math.max(nz(close[i], 0) - nz(close[i - 8], 0), 0), dnD = Math.max(nz(close[i - 8], 0) - nz(close[i], 0), 0);
exBullPER[i] = clamp(upD / Math.max(exBullN[i] * nz(atr14[i], EPS), EPS), 0, 4);
exBearPER[i] = clamp(dnD / Math.max(exBearN[i] * nz(atr14[i], EPS), EPS), 0, 4);
exBullGAL[i] = i >= 2 ? (exBullN[i] - exBullN[i - 1]) - (exBullN[i - 1] - exBullN[i - 2]) : 0;
exBearGAL[i] = i >= 2 ? (exBearN[i] - exBearN[i - 1]) - (exBearN[i - 1] - exBearN[i - 2]) : 0;}
var kPeak = make(n, 0), kValley = make(n, 0);
for (i = 0; i < n; i++) {
if (i < ENGINE_START) continue;
var aRefK = Math.max(nz(atr14[i], 0), Math.abs(nz(close[i], 1)) * 1e-4);
var vahN = nz(vahA[i], nz(close[i], 0)), valN = nz(valA[i], nz(close[i], 0));
var zonePk = Math.abs(nz(close[i], 0) - vahN) <= aRefK * 0.35 ? 0.22 : 0;
var zoneVl = Math.abs(nz(close[i], 0) - valN) <= aRefK * 0.35 ? 0.22 : 0;
var dtsPk = clamp01(-nz(dtsArr[i], 0) / 3) * 0.20;
var dtsVl = clamp01(nz(dtsArr[i], 0) / 3) * 0.20;
var cogPk = nz(cogMom[i], 0) < 0 ? 0.20 : 0;
var cogVl = nz(cogMom[i], 0) > 0 ? 0.20 : 0;
var pressPk = (exBearN[i] > 0.55 && exBearGAL[i] > 0) ? 0.12 : 0;
var pressVl = (exBullN[i] > 0.55 && exBullGAL[i] > 0) ? 0.12 : 0;
var perPk = exBearPER[i] > 1.0 ? 0.08 : 0;
var perVl = exBullPER[i] > 1.0 ? 0.08 : 0;
kPeak[i] = clamp01((pricePeakA[i] ? 0.30 : 0) + (fracHiA[i] ? 0.24 : 0) + cogPk + zonePk + dtsPk + pressPk + perPk);
kValley[i] = clamp01((priceValleyA[i] ? 0.30 : 0) + (fracLoA[i] ? 0.24 : 0) + cogVl + zoneVl + dtsVl + pressVl + perVl);}
// ==============================================================================================
// RCM
// ==============================================================================================
var scContArr = make(n, 50), scRevArr = make(n, 50), integArr = make(n, 1);
var aContArr = make(n, true), aRevArr = make(n, true);
var RCM_WIN = clamp(Math.round(L_norm * 0.8), 40, 300);
var contHit = make(n, 0), revHit = make(n, 0);
for (i = 1; i < n; i++) {
var barOk = nz(close[i], 0) - nz(close[i - 1], 0);
var biasPrev = nz(DirBias[i - 1], 0);
contHit[i] = (biasPrev > 0 && barOk > 0) || (biasPrev < 0 && barOk < 0) ? 1 : 0;
revHit[i] = contHit[i] === 1 ? 0 : 1;}
var cSumRun = 0, rSumRun = 0;
for (i = 0; i < n; i++) {
cSumRun += contHit[i]; rSumRun += revHit[i];
if (i - RCM_WIN >= 0) { cSumRun -= contHit[i - RCM_WIN]; rSumRun -= revHit[i - RCM_WIN]; }
var cnt2 = Math.min(i + 1, RCM_WIN);
var cRate = cnt2 > 0 ? cSumRun / cnt2 : 0.5, rRate = cnt2 > 0 ? rSumRun / cnt2 : 0.5;
scContArr[i] = clamp(cRate * 100, 0, 100);
scRevArr[i] = clamp(rRate * 100, 0, 100);
integArr[i] = clamp01(1 - nz(regStress[i], 0.25) * 0.8);
aContArr[i] = !i_rcmGate || cRate >= 0.505;
aRevArr[i] = !i_rcmGate || rRate >= 0.505;}
// ==============================================================================================
// SIGNAL ENGINE
// ==============================================================================================
var whyArr = make(n, 'warmup');
var convArr = make(n, 0), qualArr = make(n, 0), sideArr = make(n, 0);
var famArr = make(n, 'cont'), srcArr = make(n, 'pvk');
var longRaw = make(n, false), shortRaw = make(n, false);
var chHiWin = make(n, -Infinity), chLoWin = make(n, Infinity);
var chHiDq = newDeque(), chLoDq = newDeque();
for (i = 0; i < n; i++) {
dqPushMax(chHiDq, i, hiClean);
dqPushMin(chLoDq, i, loClean);
dqAdvance(chHiDq, i - L_ch);
dqAdvance(chLoDq, i - L_ch);
chHiWin[i] = hiClean[chHiDq.a[chHiDq.h]];
chLoWin[i] = loClean[chLoDq.a[chLoDq.h]];}
var lastSwingSide = 0;
var lastSwingPrice = null;
var lastCandidateBar = -100000;
var breakoutSide = 0;
for (i = 0; i < n; i++) {
if (i < ENGINE_START) continue;
var pxS = nz(close[i], 0);
var prevS = nz(close[i - 1], pxS);
var aRefS = Math.max(nz(atr14[i], 0), Math.abs(pxS) * 1e-4);
var vahS = nz(vahA[i], pxS);
var valS = nz(valA[i], pxS);
var pocS = nz(pocA[i], pxS);
if (breakoutSide === 1 && pxS < vahS) breakoutSide = 0;
if (breakoutSide === -1 && pxS > valS) breakoutSide = 0;
var clearTape = !USE_CHOP || chopOk[i];
if (!clearTape) {
whyArr[i] = 'chop';
continue; }
var pkPrice = pivHiA[i] ? nz(high[i - 1], pxS) : nz(hl2A[i - 1], pxS);
var vlPrice = pivLoA[i] ? nz(low[i - 1], pxS) : nz(hl2A[i - 1], pxS);
var pkEvent = !!pricePeakA[i];
var vlEvent = !!priceValleyA[i];
var pkAmpOK = lastSwingPrice === null ||
pkPrice - lastSwingPrice >= MIN_AMP * aRefS;
var vlAmpOK = lastSwingPrice === null ||
lastSwingPrice - vlPrice >= MIN_AMP * aRefS;
var pkCandidate = pkEvent && lastSwingSide !== -1 && pkAmpOK;
var vlCandidate = vlEvent && lastSwingSide !== 1 && vlAmpOK;
if (pkCandidate && vlCandidate) {
if (Math.abs(kPeak[i] - kValley[i]) < 0.08) {
pkCandidate = false;
vlCandidate = false;
} else if (kPeak[i] > kValley[i]) {
vlCandidate = false;
} else {
pkCandidate = false;}}
if (pkCandidate) {
lastSwingSide = -1;
lastSwingPrice = pkPrice;
} else if (vlCandidate) {
lastSwingSide = 1;
lastSwingPrice = vlPrice;}
var upBreak = breakoutSide !== 1 &&
prevS <= vahS + 0.10 * aRefS &&
pxS > vahS + 0.10 * aRefS;
var dnBreak = breakoutSide !== -1 &&
prevS >= valS - 0.10 * aRefS &&
pxS < valS - 0.10 * aRefS;
if (upBreak) breakoutSide = 1;
else if (dnBreak) breakoutSide = -1;
var swingSide = vlCandidate ? 1 : (pkCandidate ? -1 : 0);
var trendSide = TREND_ON ? (upBreak ? 1 : (dnBreak ? -1 : 0)) : 0;
var sideNow = swingSide || trendSide;
var fromTrend = swingSide === 0 && trendSide !== 0;
if (swingSide && trendSide && swingSide !== trendSide) {
sideNow = 0;
whyArr[i] = 'conflict'; }
if (!sideNow) {
if (whyArr[i] !== 'conflict') whyArr[i] = 'no-event';
continue;}
srcArr[i] = fromTrend ? 'trend' : 'pvk';
sideArr[i] = sideNow;
var established = sgn(nz(DirBias[i - 1], 0));
famArr[i] = established === 0 || established === sideNow ? 'cont' : 'rev';
var dirBias = nz(DirBias[i], 0) * sideNow;
var priorBias = nz(DirBias[i - 1], 0) * sideNow;
var candleRange = Math.max(nz(high[i], pxS) - nz(low[i], pxS), EPS);
var closeLocation = sideNow > 0 ? (pxS - nz(low[i], pxS)) / candleRange : (nz(high[i], pxS) - pxS) / candleRange;
var directionalKernel = fromTrend ? 0.55 : (sideNow > 0 ? nz(kValley[i], 0) : nz(kPeak[i], 0));
var directionalPressure = clamp01( 0.5 + sideNow * nz(dtsArr[i], 0) / 6 );
var biasEvidence = clamp01(0.5 + 0.5 * dirBias);
var persistence = clamp01(0.5 + 0.5 * priorBias);
var locationEvidence = clamp01(closeLocation);
var pressureEvidence = clamp01(directionalPressure);
var structureEvidence = fromTrend
? clamp01((sideNow > 0 ? pxS - vahS : valS - pxS) /
(1.25 * aRefS))
: clamp01(1 - Math.abs(
pxS - (sideNow > 0 ? valS : vahS)
) / (1.5 * aRefS));
var stressPenalty = 0.09 * clamp01(nz(regStress[i], 0));
var extensionPenalty = 0.08 * clamp01(
(Math.abs(pxS - prevS) / aRefS - 1.4) / 1.6 );
convArr[i] = clamp01(
0.65 * biasEvidence +
0.20 * persistence +
0.15 * locationEvidence );
var quality = clamp01(
0.24 * directionalKernel +
0.22 * biasEvidence +
0.13 * persistence +
0.14 * locationEvidence +
0.12 * pressureEvidence +
0.15 * structureEvidence -
stressPenalty -
extensionPenalty );
if (i_rcmOn) {
var familyScore = famArr[i] === 'cont'
? nz(scContArr[i], 50)
: nz(scRevArr[i], 50);
quality = clamp01(
quality + 0.06 * clamp((familyScore - 50) / 25, -1, 1) );}
qualArr[i] = quality;
if (i_rcmGate &&
!(famArr[i] === 'cont' ? aContArr[i] : aRevArr[i])) {
whyArr[i] = 'rcm-gate';
continue;}
var requiredSpace = fromTrend
? Math.max(SPACING, Math.round(SPACING * 2.5))
: SPACING;
if (i - lastCandidateBar < requiredSpace) {
whyArr[i] = 'spacing';
continue;}
lastCandidateBar = i;
if (quality < CONV) {
whyArr[i] = 'below-quality';
continue;}
if (sideNow > 0) longRaw[i] = true;
else shortRaw[i] = true;
whyArr[i] = fromTrend ? 'trend-break' : (sideNow > 0 ? 'valley-swing' : 'peak-swing');}
var Q_RCM = 3, Q_VIX = 3, Q_DIV = 3, Q_SIDE = 2, Q_ACT = 2;
var Q_STATES = Q_RCM * Q_VIX * Q_DIV * Q_SIDE;
var qTable = make(Q_STATES * Q_ACT, 0);
for (i = 0; i < Q_STATES; i++) { qTable[i * Q_ACT] = 0.25; qTable[i * Q_ACT + 1] = 0.30; }
function qHash(rcmSt, vixId, divId, sideId) {
var a = clamp(rcmSt + 1, 0, Q_RCM - 1), bq = clamp(vixId, 0, Q_VIX - 1);
var c = clamp(divId, 0, Q_DIV - 1), d = clamp(sideId, 0, Q_SIDE - 1);
return a + bq * Q_RCM + c * Q_RCM * Q_VIX + d * Q_RCM * Q_VIX * Q_DIV;}
function seededUnit(x) { var v = Math.sin(x * 12.9898 + 78.233) * 43758.5453; return v - Math.floor(v); }
var qEps = AGT.eps;
function pickSize(hash, barIdx) {
if (!AGT.on) return 1.0;
var act;
if (seededUnit(barIdx) < qEps) act = seededUnit(barIdx * 7.13 + 1) < 0.5 ? 0 : 1;
else act = qTable[hash * Q_ACT] >= qTable[hash * Q_ACT + 1] ? 0 : 1;
qEps = Math.max(0.02, qEps * AGT.decay);
return act === 0 ? 0.5 : 1.0;}
function qLearn(hash, sizeF, r) {
if (!AGT.on) return;
var act = sizeF < 0.75 ? 0 : 1, idx = hash * Q_ACT + act;
qTable[idx] = clamp(qTable[idx] + AGT.lr * (r - qTable[idx]), -5, 5);}
var longLive = make(n, false), shortLive = make(n, false), longConf = make(n, false), shortConf = make(n, false);
for (i = ENGINE_START; i < n; i++) {
if (!longRaw[i] && !shortRaw[i]) continue;
if (i === lastBar) { longLive[i] = longRaw[i]; shortLive[i] = shortRaw[i]; }
else { longConf[i] = longRaw[i]; shortConf[i] = shortRaw[i]; }}
var watchLong = make(n, false), watchShort = make(n, false);
for (i = ENGINE_START; i < n - 1; i++) {
if (longConf[i] || shortConf[i]) continue;
if (sideArr[i] === 0) continue;
if (sideArr[i] > 0 && qualArr[i] >= CONV * 0.80) watchLong[i] = true;
if (sideArr[i] < 0 && qualArr[i] >= CONV * 0.80) watchShort[i] = true;}
// ==============================================================================================
// EXECUTION LEDGER
// ==============================================================================================
var rList = [], exitMix = { SL: 0, BE: 0, TRAIL: 0, TP: 0, SHIELD: 0, TIME: 0, HOLD: 0 };
var sizeMix = { half: 0, full: 0 };
var openSide = 0, openEntry = 0, openStop = 0, openTarg = 0, openBar = 0, openMFE = 0, lastReason = '—', lastR = 0;
var openSizeF = 1.0, openHash = 0, lastSizeF = 1.0, openFam = 'cont', openExit = 'Trailing Stop';
var openPeak = null, beArmed = false;
var slMult = i_slAtr, tpMult = i_tpAtr, armR = 1.0;
var maxBars = Math.max(4, i_maxHold);
var EXIT_TAG = ({ 'Trailing Stop': 'TRAIL', 'ATR Target/Stop': 'ATR TGT',
'Time Stop': 'TIME', 'Entropy Barrier Breach': 'E-BAR', 'Hybrid Trail+ATR': 'HYBRID' })[i_exitStrategy] || i_exitStrategy;
var BE_TRIG = 1.0;
var famWeight = { cont: 1.0, rev: 1.0 };
var exitSigArr = make(n, false);
var fireRMap = make(n, null);
var isR = [], oosR = [];
var OOS_START = Math.floor(n * 0.70);
function closeTrade(px, reason, barIdx) {
var risk = Math.abs(openEntry - openStop);
var r = risk > 0 ? ((px - openEntry) / risk) * openSide : 0;
r = clamp(r, -3, 6);
qLearn(openHash, openSizeF, r);
var rBooked = r * openSizeF;
rList.push(rBooked); exitMix[reason]++; lastReason = reason; lastR = rBooked; lastSizeF = openSizeF;
exitSigArr[barIdx] = true;
fireRMap[barIdx] = rBooked;
if (barIdx < OOS_START) isR.push(rBooked); else oosR.push(rBooked);
if (reason !== 'TIME' && reason !== 'HOLD') famWeight[openFam] = clamp(famWeight[openFam] + (r > 0 ? 0.03 : -0.03), 0.50, 1.50);
openSide = 0;}
for (i = ENGINE_START; i < n - 1; i++) {
var atE = Math.max(nz(atr14[i], 0), Math.abs(nz(close[i], 1)) * 0.0001);
if (openSide !== 0) {
var barsHeld = i - openBar;
var mfe = (nz(close[i], 0) - openEntry) * openSide;
if (mfe > openMFE) openMFE = mfe;
var useTrail = openExit === 'Trailing Stop' || openExit === 'Hybrid Trail+ATR' || openExit === 'Entropy Barrier Breach';
var hitSL = false, hitTP = false, tPx;
if (openSide === 1) {
openPeak = Math.max(openPeak === null ? nz(close[i], 0) : openPeak, nz(high[i], 0));
if (!beArmed && (openPeak - openEntry) >= BE_TRIG * atE * slMult) { beArmed = true; if (openEntry > openStop) openStop = openEntry; }
if (useTrail && (openPeak - openEntry) >= armR * atE * slMult) {
tPx = openPeak - atE * i_trailAtr;
if (tPx > openStop) openStop = tPx;}
hitSL = nz(low[i], 0) <= openStop;
hitTP = openTarg !== null && nz(high[i], 0) >= openTarg;
} else {
openPeak = Math.min(openPeak === null ? nz(close[i], 0) : openPeak, nz(low[i], 0));
if (!beArmed && (openEntry - openPeak) >= BE_TRIG * atE * slMult) { beArmed = true; if (openEntry < openStop) openStop = openEntry; }
if (useTrail && (openEntry - openPeak) >= armR * atE * slMult) {
tPx = openPeak + atE * i_trailAtr;
if (tPx < openStop) openStop = tPx;}
hitSL = nz(high[i], 0) >= openStop;
hitTP = openTarg !== null && nz(low[i], 0) <= openTarg;}
var hitTime = (openExit === 'Time Stop' || openExit === 'Hybrid Trail+ATR') && barsHeld >= maxBars;
var hitHold = barsHeld >= i_maxHold;
var hitShield = false;
if (openExit === 'Entropy Barrier Breach' && nz(chopEnt[i], 1) > 0.97 && nz(chopRank[i], 0.5) > 0.85) {
if (openSide === 1 && nz(DirBias[i], 0) < -0.15) hitShield = true;
if (openSide === -1 && nz(DirBias[i], 0) > 0.15) hitShield = true;}
var trailMoved = openSide === 1 ? openStop > openEntry + EPS : openStop < openEntry - EPS;
if (hitTP) closeTrade(openTarg, 'TP', i);
else if (hitSL) closeTrade(openStop, trailMoved ? 'TRAIL' : (beArmed ? 'BE' : 'SL'), i);
else if (hitShield) closeTrade(nz(close[i], 0), 'SHIELD', i);
else if (hitTime) closeTrade(nz(close[i], 0), 'TIME', i);
else if (hitHold) closeTrade(nz(close[i], 0), 'HOLD', i);}
if (openSide === 0 && (longConf[i] || shortConf[i])) {
openSide = longConf[i] ? 1 : -1;
openEntry = nz(close[i], 0);
openPeak = openEntry; beArmed = false;
openExit = i_exitStrategy === 'Auto'
? (nz(chopRank[i], 0.5) <= 0.40 ? 'Trailing Stop' : (nz(chopRank[i], 0.5) <= 0.70 ? 'Hybrid Trail+ATR' : 'ATR Target/Stop'))
: i_exitStrategy;
openStop = openSide > 0 ? openEntry - atE * slMult : openEntry + atE * slMult;
openTarg = (openExit === 'ATR Target/Stop' || openExit === 'Hybrid Trail+ATR')
? openEntry + openSide * atE * tpMult : null;
var vixId = nz(atrZ[i], 1) < 0.8 ? 0 : (nz(atrZ[i], 1) < 1.3 ? 1 : 2);
var dAbs = Math.abs(nz(dtsArr[i], 0));
var divId = dAbs < 1.0 ? 0 : (dAbs < 2.5 ? 1 : 2);
openHash = qHash(sgn(nz(DirBias[i], 0)), vixId, divId, openSide > 0 ? 0 : 1);
openFam = famArr[i] === 'rev' ? 'rev' : 'cont';
openSizeF = clamp(pickSize(openHash, i) * famWeight[openFam], 0.35, 1.65);
if (openSizeF < 0.75) sizeMix.half++; else sizeMix.full++;
openBar = i; openMFE = 0;}}
var EXP = DAFE.expectancy.summary(rList);
var EXP_IS = DAFE.expectancy.summary(isR);
var EXP_OOS = DAFE.expectancy.summary(oosR);
var l20 = rList.slice(-20), l20w = 0, l20i;
for (l20i = 0; l20i < l20.length; l20i++) if (l20[l20i] >= 0) l20w++;
var last20WR = l20.length ? l20w / l20.length : 0;
var curStreak = 0;
for (l20i = rList.length - 1; l20i >= 0; l20i--) {
var w0 = rList[l20i] >= 0;
if (l20i === rList.length - 1) curStreak = w0 ? 1 : -1;
else if (w0 === (curStreak > 0)) curStreak += (w0 ? 1 : -1);
else break;}
function sparkEq(list, w) {
var chars = '▁▂▃▄▅▆▇█', eqS = [], sS = 0, iS, mn = Infinity, mx = -Infinity, slS, vS, ix, out = '';
for (iS = 0; iS < list.length; iS++) { sS += list[iS]; eqS.push(sS); }
slS = eqS.slice(-w);
if (!slS.length) return '—';
for (iS = 0; iS < slS.length; iS++) { vS = slS[iS]; if (vS < mn) mn = vS; if (vS > mx) mx = vS; }
if (mx <= mn) return '▄▄▄';
for (iS = 0; iS < slS.length; iS++) { ix = Math.round((slS[iS] - mn) / (mx - mn) * 7); out += chars.charAt(clamp(ix, 0, 7)); }
return out;}
// ==============================================================================================
// ULMA CORE
// ==============================================================================================
var ROT_HAS_CLOCK = true, WALL = 0;
try { WALL = Date.now(); if (!(WALL > 0)) { ROT_HAS_CLOCK = false; WALL = n * 1000; } } catch (e) { ROT_HAS_CLOCK = false; WALL = n * 1000; }
var CLOCK = { wallMs: WALL, hasClock: ROT_HAS_CLOCK, barMod12: (n % 12) / 12, barMod8: (n % 8) / 8, barMod16: (n % 16) / 16 };
CLOCK.phase12 = CLOCK.barMod12 * 2 * Math.PI;
CLOCK.phase8 = CLOCK.barMod8 * 2 * Math.PI;
CLOCK.slow = (CLOCK.wallMs / 1000) % 100000;
CLOCK.fast = (CLOCK.wallMs / 150) % 100000;
CLOCK.beat = ROT_HAS_CLOCK ? CLOCK.wallMs / 1000 : n;
CLOCK.render = ROT_HAS_CLOCK ? CLOCK.wallMs : n * 250;
function breathe(base, amp, rate, off) { return base + amp * Math.sin(CLOCK.slow * rate + (off || 0)); }
function pulse01(rate, off) { return 0.5 + 0.5 * Math.sin(CLOCK.slow * rate + (off || 0)); }
function travel(len, speed, off) { var x = (CLOCK.beat * (speed || 1) + (off || 0)) % len; return x < 0 ? x + len : x; }
function march(div) { return (CLOCK.render / (div || 60)) % 8; }
// ==============================================================================================
// CHART PLOTTING
// ==============================================================================================
function anchorOpts(nm) { return { name: nm, color: 'transparent', affects_scale: false, show_in_legend: false }; }
var RIB_WEIGHTS = [-1.90, -1.45, -0.95, -0.50, 0.50, 0.95, 1.45, 1.90];
var RIB_WIDTHS = [ 1.00, 1.50, 2.00, 2.50, 2.50, 2.00, 1.50, 1.00];
var RIB_ALPHA = [ 0.35, 0.52, 0.70, 0.90, 0.90, 0.70, 0.52, 0.35];
var RIB_EDGE = 1.90;
var T_SLOW = CLOCK.slow;
var BREATH_UP = breathe(0.82, 0.18, 0.22, 0);
var BREATH_DN = breathe(0.82, 0.18, 0.22, Math.PI);
var RIPPLE_T = CLOCK.slow * 1.15;
var CASCADE_RATE = 0.85, CASCADE_LAG = 1.35, VOIDPIT = '#070D09';
function cascadeK(rank) { var xC = T_SLOW * CASCADE_RATE - rank * CASCADE_LAG; var kC2 = 0.5 + 0.5 * Math.sin(xC); return kC2 * kC2; }
function railRank(wAbs) { return wAbs < 0.70 ? 1 : (wAbs < 1.20 ? 2 : (wAbs < 1.70 ? 3 : 4)); }
function warpHex(col, k) { try { return tiny.mix(VOIDPIT, col, clamp(4 + 96 * k, 4, 100)).toHexString(); } catch (eW) { return col; } }
var ribbon = [], rb;
for (rb = 0; rb < RIB_WEIGHTS.length; rb++) ribbon.push(make(n, null));
var spineL = make(n, null);
var railHi = make(n, null), railLo = make(n, null), railMid = make(n, null), legL = make(n, null);
for (i = 0; i < n; i++) {
if (i < ENGINE_START) continue;
var aR3 = Math.max(nz(atr14[i], 0), Math.abs(nz(close[i], 1)) * 1e-4);
var coreR = nz(cogArr[i], nz(close[i], 0));
var imbalanceR = clamp11(nz(kValley[i], 0) - nz(kPeak[i], 0));
var coherenceR = clamp01(0.35 + 0.65 * Math.max(nz(kPeak[i], 0), nz(kValley[i], 0)));
var breadthR = aR3 * (0.18 + 0.58 * coherenceR) * (0.70 + 0.30 * Math.abs(imbalanceR));
spineL[i] = coreR;
for (rb = 0; rb < RIB_WEIGHTS.length; rb++) {
var centerPullR = 1 - Math.abs(RIB_WEIGHTS[rb]) / RIB_EDGE;
var breathR = RIB_WEIGHTS[rb] < 0 ? BREATH_DN : BREATH_UP;
var warpR = 0.86 + 0.14 * cascadeK(railRank(Math.abs(RIB_WEIGHTS[rb])));
var rippleR = Math.sin(i * 0.043 - RIPPLE_T) * 0.06 * (0.35 + 0.65 * centerPullR);
ribbon[rb][i] = coreR + breadthR * breathR * warpR * (RIB_WEIGHTS[rb] + imbalanceR * 0.24 * centerPullR) + breadthR * rippleR;}
if (VIS.rails) { railHi[i] = nz(vahA[i], null); railLo[i] = nz(valA[i], null); railMid[i] = nz(pocA[i], null); }}
var kNow = nz(kPeak[lastBar], 0) >= nz(kValley[lastBar], 0) ? 'peak' : 'valley';
var spineCol = kNow === 'valley' ? '#28B7FF' : '#FF8A1D';
var ribbonCols = ['#1D78D8', '#2597FF', '#30A9FF', '#4DB9F6', '#F08A20', '#D97116', '#B95B12', '#8F3F0C'];
var railPulse = 0.34 + 0.14 * pulse01(0.5, 0.9);
var pocPulse = 0.26 + 0.12 * pulse01(0.7, 2.1);
paint(railHi, { name: 'PVK Value Area High', color: rgba(brC(TH.warn), brA(railPulse)), thickness: 1, affects_scale: false, show_in_legend: false });
paint(railLo, { name: 'PVK Value Area Low', color: rgba(brC(TH.warn), brA(railPulse)), thickness: 1, affects_scale: false, show_in_legend: false });
paint(railMid, { name: 'PVK Point of Control', color: rgba(brC(TH.info), brA(pocPulse)), thickness: 1, affects_scale: false, show_in_legend: false });
for (rb = 0; rb < RIB_WEIGHTS.length; rb++) {
var kC = cascadeK(railRank(Math.abs(RIB_WEIGHTS[rb])));
paint(ribbon[rb], { name: 'PVK Reservoir Rail ' + (rb + 1), color: rgba(warpHex(brC(ribbonCols[rb]), kC), ribA(RIB_ALPHA[rb]) * (0.04 + 0.96 * kC)), thickness: RIB_WIDTHS[rb] * (0.50 + 0.50 * kC), affects_scale: false, show_in_legend: false });}
var spinePulse = 0.80 + 0.20 * Math.sin(T_SLOW * 0.85);
var spineK = cascadeK(0);
paint(spineL, { name: 'PVK Reservoir Core', color: rgba(warpHex(brC(spineCol), spineK), ribA(spinePulse) * (0.05 + 0.95 * spineK)), thickness: 0.9 + 1.7 * spineK, affects_scale: false, show_in_legend: false });
var rComet = paint(spineL, anchorOpts('PVK Spine Comet'));
var candCol = make(n, null);
for (i = 0; i < n; i++) {
if (i < ENGINE_START) { candCol[i] = rgba(TH.dim, brA(0.40)); continue; }
var kp2 = nz(kPeak[i], 0), kv2 = nz(kValley[i], 0), kM = Math.max(kp2, kv2);
candCol[i] = rgba(kv2 >= kp2 ? '#28B7FF' : '#F08A20', brA(0.18 + 0.45 * clamp01(kM)));}
color_candles(VIS.glow ? candCol : makeNull(n));
var MANIFOLD_WIN = clamp(Math.round(L_norm * 1.2), 90, 340);
var LIVE_EDGE_TOL = 2;
var MARGIN_BARS = 32;
var usingVisRange = true;
function getVisibleBounds() {
var start, end, live;
if (i_visRange && typeof i_visRange.candleIndexFrom === 'number' && typeof i_visRange.candleIndexTo === 'number' && i_visRange.candleIndexTo > i_visRange.candleIndexFrom) {
start = i_visRange.candleIndexFrom;
end = i_visRange.candleIndexTo;
live = (n - 1 - end) <= LIVE_EDGE_TOL;
if (live) end = n - 1;
usingVisRange = true;
} else {
start = n - MANIFOLD_WIN;
end = n - 1;
live = true;}
start = clamp(Math.floor(start), 0, n - 1);
end = clamp(Math.ceil(end), start + 1, n - 1);
start = Math.max(start, ENGINE_START);
if (end <= start) { start = Math.max(0, Math.min(start, n - 2)); end = Math.min(n - 1, start + 1); }
return { start: start, end: end, live: live };}
var bounds = getVisibleBounds();
var vStart = bounds.start, vEnd = bounds.end, showNow = bounds.live;
var bAHi = make(n, null), bALo = make(n, null), bTHi = make(n, null), bTLo = make(n, null);
var pulseLo = make(n, null), pulseHi = make(n, null);
for (i = 0; i < n; i++) {
var aB = Math.max(nz(atr14[i], 0), Math.abs(nz(close[i], 1)) * 1e-4);
bALo[i] = nz(low[i], 0) - aB * 0.55;
bTLo[i] = nz(low[i], 0) - aB * 1.05;
bAHi[i] = nz(high[i], 0) + aB * 0.55;
bTHi[i] = nz(high[i], 0) + aB * 1.05;
pulseLo[i] = nz(low[i], 0) - aB * 2.1;
pulseHi[i] = nz(high[i], 0) + aB * 2.1;}
var rBAHi = paint(bAHi, anchorOpts('PVK Badge Anchor Hi'));
var rBALo = paint(bALo, anchorOpts('PVK Badge Anchor Lo'));
var rBTHi = paint(bTHi, anchorOpts('PVK Badge Text Hi'));
var rBTLo = paint(bTLo, anchorOpts('PVK Badge Text Lo'));
var rPulseLo = paint(pulseLo, anchorOpts('PVK Valley Pulse'));
var rPulseHi = paint(pulseHi, anchorOpts('PVK Peak Pulse'));
var xRail = make(n, null);
for (i = 0; i < n; i++) xRail[i] = nz(close[i], 0);
var rExit = paint(xRail, anchorOpts('PVK Exit Rail'));
if (i_marks) {
for (i = Math.max(ENGINE_START, vStart); i < n; i++) {
if (exitSigArr[i]) {
paint_label_at_line(rExit, i, '✕', { color: rgba(TH.dim, 0.90), background_color: 'transparent', border_width: 0, vertical_align: 'middle' }); }
if (VIS.glow && i % Math.max(1, VIS.step * 3) === 0) {
var kk = clamp01(Math.max(nz(kPeak[i], 0), nz(kValley[i], 0)));
if (kk >= 0.30) {
if (nz(kValley[i], 0) >= nz(kPeak[i], 0)) {
paint_label_at_line(rPulseLo, i, '.', { color: rgba(TH.a1, 0.22 + 0.60 * kk), background_color: 'transparent', border_width: 0, vertical_align: 'middle' }); }
else {
paint_label_at_line(rPulseHi, i, '.', { color: rgba(TH.a2, 0.22 + 0.60 * kk), background_color: 'transparent', border_width: 0, vertical_align: 'middle' }); }}}
if (longConf[i]) {
paint_label_at_line(rBALo, i, '▲', { color: cBull, background_color: 'transparent', border_width: 0, vertical_align: 'middle' });
paint_label_at_line(rBTLo, i, srcArr[i] === 'trend' ? ' T ' : ' L ', { color: cBull, background_color: 'transparent', border_width: 0, vertical_align: 'middle' });
} else if (watchLong[i]) {
paint_label_at_line(rBALo, i, '△', { color: rgba(cBull, 0.55), background_color: 'transparent', border_width: 0, vertical_align: 'middle' });}
if (shortConf[i]) {
paint_label_at_line(rBAHi, i, '▼', { color: cBear, background_color: 'transparent', border_width: 0, vertical_align: 'middle' });
paint_label_at_line(rBTHi, i, srcArr[i] === 'trend' ? ' T ' : ' S ', { color: cBear, background_color: 'transparent', border_width: 0, vertical_align: 'middle' });
} else if (watchShort[i]) {
paint_label_at_line(rBAHi, i, '▽', { color: rgba(cBear, 0.55), background_color: 'transparent', border_width: 0, vertical_align: 'middle' });}}
var spanC = Math.max(12, vEnd - vStart), headC = Math.floor(travel(spanC, 1.7)), qC, cometSide = nz(kValley[lastBar], 0) >= nz(kPeak[lastBar], 0);
for (qC = 0; qC < 7; qC++) {
var ciC = Math.round(vStart + headC) - qC * 4;
if (ciC < vStart || ciC > vEnd || spineL[ciC] === null || spineL[ciC] === undefined) continue;
paint_label_at_line(rComet, ciC, qC === 0 ? '●' : (qC < 3 ? '•' : '·'), { color: qC === 0 ? rgba('#FFFFFF', 0.95) : rgba(cometSide ? TH.a1 : TH.a2, 0.70 - qC * 0.09), background_color: 'transparent', border_width: 0, vertical_align: 'middle' });}
if (i_live) {
if (longLive[lastBar]) {
paint_label_at_line(rBALo, lastBar, '▲', { color: cLive, background_color: 'transparent', border_width: 0, vertical_align: 'middle' });
paint_label_at_line(rBTLo, lastBar, ' ! ', { color: cLive, background_color: 'transparent', border_width: 0, vertical_align: 'middle' });}
if (shortLive[lastBar]) {
paint_label_at_line(rBAHi, lastBar, '▼', { color: cLive, background_color: 'transparent', border_width: 0, vertical_align: 'middle' });
paint_label_at_line(rBTHi, lastBar, ' ! ', { color: cLive, background_color: 'transparent', border_width: 0, vertical_align: 'middle' });}}}
// ==============================================================================================
// METRICS DASHBOARD & HELPERS
// ==============================================================================================
function mkCell(text, color, align, bold, colspan, background) {
return { text: String(text), color: color, textAlign: align || 'left', fontWeight: bold ? 'bold' : 'normal', colspan: colspan || 1, background: background || cBg, fontSize: 10 };}
function row3(a, b, c) { var cs = []; if (a) cs.push(a); if (b) cs.push(b); if (c) cs.push(c); return { cells: cs }; }
function guideHead(text, color) { return { cells: [mkCell(text, color, 'left', true, 2, rgba(color, 0.10))] }; }
function guideRow(label, desc, accent) {
return { cells: [mkCell(label, accent, 'left', true, 1, cBg), mkCell(desc, cTxt, 'left', false, 1, cBg)] };}
function bar(p, w) {
p = clamp01(p); w = w || 8;
var nFill = Math.round(p * w), s = '', k;
for (k = 0; k < w; k++) s += (k < nFill ? '█' : '░');
return s;}
function gauge(val, scale, width) {
var p = clamp(nz(val, 0) / (scale || 1), -1, 1), w = width || 9, mid = Math.floor(w / 2);
var pos = Math.round((p + 1) / 2 * (w - 1)), s = '', k;
for (k = 0; k < w; k++) {
if (k === mid) s += '│';
else if (p >= 0 && k > mid && k <= pos) s += '▶';
else if (p < 0 && k < mid && k >= pos) s += '◀';
else s += '·';}
return s;}
function zoneOf(p01) { return p01 >= 0.70 ? cBull : p01 >= 0.50 ? cWarn : cDim; }
function pct(x) { return (x === null || x === undefined || isNaN(x)) ? '—' : (x * 100).toFixed(0) + '%'; }
function dTxt(x) { return (x === null || x === undefined || isNaN(x)) ? '—' : x.toFixed(3); }
function regName(r) { return r === 'T' ? 'TREND' : (r === 'M' ? 'MEAN REVERT' : 'CONSOLIDATE'); }
function zoneName(z) { return z === 2 ? 'EXTREME BUY' : (z === 1 ? 'BUY ZONE' : (z === -2 ? 'EXTREME SELL' : (z === -1 ? 'SELL ZONE' : 'BALANCED'))); }
function zoneCol(z) { return z >= 1 ? TH.a3 : (z <= -1 ? TH.a2 : cDim); }
var li = lastBar;
var whyMap = { 'no-event': 'no swing or break', 'no-bias': 'no bias', 'spacing': 'spacing', 'rcm-gate': 'family blocked', 'below-quality': 'low signal quality', 'peak-swing': 'peak swing', 'valley-swing': 'valley swing', 'trend-break': 'value-area break', 'warmup': 'warmup' };
var whyNow = whyMap[whyArr[li]] || String(whyArr[li]);
var liveNow = i_live && (longLive[lastBar] || shortLive[lastBar]);
var qualNow = nz(qualArr[li], 0);
var sigTxt = liveNow ? '● LIVE' : (qualNow >= CONV ? 'ARMED' : 'WAIT');
var sigCl = liveNow ? cLive : (qualNow >= CONV ? cBull : cDim);
var kPk = nz(kPeak[li], 0), kVl = nz(kValley[li], 0);
var rows = [];
rows.push({ cells: [mkCell('⛰ PVK', TH.a1, 'center', true, 3, rgba(TH.a1, 0.16))] });
rows.push(row3(mkCell('Structure', cDim, 'left', false, 1, cBg), mkCell(kVl >= kPk ? 'VALLEY' : 'PEAK', kVl >= kPk ? TH.a1 : TH.a2, 'center', true, 1, cBg), mkCell(Math.max(kPk, kVl).toFixed(2), kVl >= kPk ? TH.a1 : TH.a2, 'right', true, 1, cBg)));
rows.push(row3(mkCell('Signal', cDim, 'left', false, 1, cBg), mkCell(sigTxt, sigCl, 'center', true, 1, cBg), mkCell(whyNow, cDim, 'right', false, 1, cBg)));
rows.push(row3(mkCell('Quality', cDim, 'left', false, 1, cBg), mkCell(bar(qualNow, 8), zoneOf(qualNow), 'center', false, 1, cBg), mkCell(qualNow.toFixed(2) + ' / ' + CONV.toFixed(2), qualNow >= CONV ? cBull : cDim, 'right', true, 1, cBg)));
rows.push(row3(mkCell('Chop', cDim, 'left', false, 1, cBg), mkCell(bar(1 - clamp01(nz(chopRank[li], 0.5)), 8), chopOk[li] ? cBull : cBear, 'center', false, 1, cBg), mkCell(chopOk[li] ? 'clear' : 'blocked', chopOk[li] ? cBull : cBear, 'right', true, 1, cBg)));
rows.push(row3(mkCell('Bias', cDim, 'left', false, 1, cBg), mkCell(gauge(nz(DirBias[li], 0), 1, 9), nz(DirBias[li], 0) >= 0 ? cBull : cBear, 'center', false, 1, cBg), mkCell((nz(DirBias[li], 0) >= 0 ? '+' : '') + nz(DirBias[li], 0).toFixed(2), nz(DirBias[li], 0) >= 0 ? cBull : cBear, 'right', true, 1, cBg)));
if (i_dash === 'Full') {
rows.push({ cells: [mkCell('SWING KERNEL', TH.a3, 'center', true, 3, rgba(TH.a3, 0.10))] });
rows.push(row3(mkCell('Peak k', cDim, 'left', false, 1, cBg), mkCell(bar(kPk, 8), TH.a2, 'center', false, 1, cBg), mkCell(kPk.toFixed(2), TH.a2, 'right', true, 1, cBg)));
rows.push(row3(mkCell('Valley k', cDim, 'left', false, 1, cBg), mkCell(bar(kVl, 8), TH.a1, 'center', false, 1, cBg), mkCell(kVl.toFixed(2), TH.a1, 'right', true, 1, cBg)));
rows.push(row3(mkCell('COG mom', cDim, 'left', false, 1, cBg), mkCell(gauge(nz(cogMom[li], 0) / Math.max(1e-9, nz(atr14[li], 1)) * 6, 1, 9), nz(cogMom[li], 0) >= 0 ? cBull : cBear, 'center', false, 1, cBg), mkCell(nz(cogMom[li], 0).toFixed(3), cTxt, 'right', true, 1, cBg)));
rows.push(row3(mkCell('DTS', cDim, 'left', false, 1, cBg), mkCell(gauge(nz(dtsArr[li], 0), 3, 9), nz(dtsArr[li], 0) >= 0 ? cBull : cBear, 'center', false, 1, cBg), mkCell(nz(dtsArr[li], 0).toFixed(2), cTxt, 'right', true, 1, cBg)));
rows.push(row3(mkCell('Entropy', cDim, 'left', false, 1, cBg), mkCell(nz(chopEnt[li], 1).toFixed(3), cTxt, 'center', true, 1, cBg), mkCell('rank ' + pct(nz(chopRank[li], 0.5)), cTxt, 'right', true, 1, cBg)));
rows.push({ cells: [mkCell('VALUE AREA', TH.a3, 'center', true, 3, rgba(TH.a3, 0.10))] });
rows.push(row3(mkCell('VAH', cDim, 'left', false, 1, cBg), mkCell(nz(vahA[li], 0).toFixed(2), TH.warn, 'center', true, 1, cBg), mkCell(nz(close[li], 0) > nz(vahA[li], 0) ? 'above' : 'inside', nz(close[li], 0) > nz(vahA[li], 0) ? cBull : cDim, 'right', true, 1, cBg)));
rows.push(row3(mkCell('POC', cDim, 'left', false, 1, cBg), mkCell(nz(pocA[li], 0).toFixed(2), cInfo, 'center', true, 2, cBg), null));
rows.push(row3(mkCell('VAL', cDim, 'left', false, 1, cBg), mkCell(nz(valA[li], 0).toFixed(2), TH.warn, 'center', true, 1, cBg), mkCell(nz(close[li], 0) < nz(valA[li], 0) ? 'below' : 'inside', nz(close[li], 0) < nz(valA[li], 0) ? cBear : cDim, 'right', true, 1, cBg)));
rows.push({ cells: [mkCell('SIZING AGENT', TH.a3, 'center', true, 3, rgba(TH.a3, 0.10))] });
rows.push(row3(mkCell('Mode', cDim, 'left', false, 1, cBg), mkCell(String(i_agentMode), AGT.on ? cTxt : cDim, 'center', true, 1, cBg), mkCell(AGT.on ? 'eps ' + qEps.toFixed(3) : 'off', AGT.on ? cTxt : cDim, 'right', true, 1, cBg)));
rows.push(row3(mkCell('Size mix', cDim, 'left', false, 1, cBg), mkCell(sizeMix.half + ' half', cTxt, 'center', true, 1, cBg), mkCell(sizeMix.full + ' full', cTxt, 'right', true, 1, cBg)));
rows.push(row3(mkCell('States', cDim, 'left', false, 1, cBg), mkCell(String(Q_STATES) + ' live', cBull, 'center', true, 1, cBg), mkCell('was 162/54', cDim, 'right', true, 1, cBg)));
rows.push({ cells: [mkCell('LEDGER (R)', TH.a3, 'center', true, 3, rgba(TH.a3, 0.10))] });
rows.push(row3(mkCell('Trades', cDim, 'left', false, 1, cBg), mkCell(String(EXP.trades), cTxt, 'center', true, 1, cBg), mkCell(pct(EXP.wr) + ' WR', EXP.wr >= 0.5 ? cBull : cBear, 'right', true, 1, cBg)));
rows.push(row3(mkCell('Expect', cDim, 'left', false, 1, cBg), mkCell(EXP.expR.toFixed(2) + 'R', EXP.expR >= 0 ? cBull : cBear, 'center', true, 1, cBg), mkCell('PF ' + EXP.pf.toFixed(2), EXP.pf >= 1 ? cBull : cBear, 'right', true, 1, cBg)));
rows.push(row3(mkCell('Net / DD', cDim, 'left', false, 1, cBg), mkCell(EXP.netR.toFixed(1) + 'R', EXP.netR >= 0 ? cBull : cBear, 'center', true, 1, cBg), mkCell('-' + EXP.ddR.toFixed(1) + 'R', cWarn, 'right', true, 1, cBg)));
rows.push(row3(mkCell('L20 / Streak', cDim, 'left', false, 1, cBg), mkCell(pct(last20WR), last20WR >= 0.5 ? cBull : cBear, 'center', true, 1, cBg), mkCell((curStreak > 0 ? '+' : '') + curStreak, curStreak >= 0 ? cBull : cBear, 'right', true, 1, cBg)));
rows.push(row3(mkCell('Equity', cDim, 'left', false, 1, cBg), mkCell(sparkEq(rList, 10), TH.a3, 'center', false, 1, cBg), mkCell(openSide !== 0 ? (openSide > 0 ? 'OPEN L' : 'OPEN S') : 'flat', openSide !== 0 ? cWarn : cDim, 'right', true, 1, cBg)));
rows.push(row3(mkCell('Exit book', cDim, 'left', false, 1, cBg), mkCell(i_exitStrategy === 'Auto' ? 'AUTO · ' + openExit : EXIT_TAG, TH.a3, 'center', true, 1, cBg), mkCell('SL ' + i_slAtr.toFixed(1) + ' / TP ' + i_tpAtr.toFixed(1) + ' / TR ' + i_trailAtr.toFixed(1), cTxt, 'right', false, 1, cBg)));
rows.push(row3(mkCell('Exits', cDim, 'left', false, 1, cBg), mkCell('SL' + exitMix.SL + ' BE' + exitMix.BE + ' TR' + exitMix.TRAIL, cTxt, 'center', false, 1, cBg), mkCell('TP' + exitMix.TP + ' SD' + exitMix.SHIELD, cDim, 'right', false, 1, cBg)));
rows.push(row3(mkCell('IS / OOS', cDim, 'left', false, 1, cBg), mkCell(EXP_IS.trades + 'tr / ' + EXP_OOS.trades + 'tr', cTxt, 'center', false, 1, cBg), mkCell(pct(EXP_OOS.wr) + ' oos', zoneOf(EXP_OOS.wr), 'right', true, 1, cBg)));
rows.push(row3(mkCell('Fam trust', cDim, 'left', false, 1, cBg), mkCell('c ' + famWeight.cont.toFixed(2), famWeight.cont >= 1 ? cBull : cBear, 'center', false, 1, cBg), mkCell('r ' + famWeight.rev.toFixed(2), famWeight.rev >= 1 ? cBull : cBear, 'right', true, 1, cBg)));
rows.push(row3(mkCell('Last exit', cDim, 'left', false, 1, cBg), mkCell(String(lastReason), cTxt, 'center', true, 1, cBg), mkCell(lastR.toFixed(2) + 'R', lastR >= 0 ? cBull : cBear, 'right', true, 1, cBg)));}
paint_overlay('PVK Metrics', { position: 'top_right', order: 'above_all' }, {background: i_dash !== 'Off' ? cBg : 'rgba(0,0,0,0)', border: i_dash !== 'Off' ? '1px solid ' + cInfo : '0px solid transparent', borderRadius: 6, width: i_dash !== 'Off' ? 200 : 1, rows: i_dash !== 'Off' ? rows : [] });
// ==============================================================================================
// GUIDE DASHBOARDS (INPUTS/VISUALS)
// ==============================================================================================
var gRows = [];
gRows.push({ cells: [mkCell('⛰ PVK — INPUT GUIDE', cInfo, 'center', true, 2, rgba(cInfo, 0.16))] });
gRows.push({ cells: [mkCell('Preferences only; every window and gate is derived. Three things were broken in the shipped build. The entropy chop filter passed only 1.42% of 35,280 bars at its default, leaving 30 trades across 30 holdout seeds. The peak/valley mapping was a pure fade and measured negative in-sample AND on holdout. The Q-table allocated 162 states but only 54 were reachable.', cDim, 'left', false, 2, cBg)] });
gRows.push(guideHead('SYSTEM', cBull));
gRows.push(guideRow('Engine Preset', 'Scalper / Balanced / Swing / Position. Sets the COG length, value-area lookback and rows, the chandelier and normalisation windows, spacing and horizon.', cBull));
gRows.push(guideRow('◈ Preset Multiplier', 'The sensitivity dial for the whole preset. Below 1.00 shortens every window — swings register faster, more signals. Above 1.00 lengthens them — steadier, fewer. 1.00 is the preset exactly as calibrated.', cBull));
gRows.push(guideRow('Chop Strictness', 'The entropy rank gate — hardcoded to the percentile form after the Absolute variant measured a 1.42 percent pass rate (a silent engine-killer, now deleted). Loose admits the calmest 80 percent of bars, Strict only the calmest 20. MEASURED on holdout: rank 0.80 gives +0.7955 ATR at 63.0 percent over 227 trades, rank 0.20 gives +0.7624 at 71.8 percent over 170.', cBull));
gRows.push(guideRow('Swing Sensitivity', 'Scales the momentum threshold for a peak or valley turn. Measured a WEAK dial: the trigger rate only moves from 21.11 to 17.74 percent across its whole range, so use spacing and conviction for frequency.', cBull));
gRows.push(guideRow('Min Swing Amplitude', 'Minimum ATR move since the last opposite swing. Measured monotonically HARMFUL: 0.0 gives +0.6237 ATR over 746 trades, the old 1.5 default gives +0.4675 over 205, 3.0 gives +0.3972 over 161. It destroys sample without improving quality, so the default is now low.', cBull));
gRows.push(guideRow('◈ Conviction', 'The single selectivity dial, 1–9 plus Auto. Auto (default) reads the preset calibrated conviction. 1 = loosest — many signals, thin edge; 9 = strictest — rare, deep edge. Monotonic: raising it can only reduce count. Every point in the band measured profitable on holdout.', cBull));
gRows.push(guideHead('LAYERS', cInfo));
gRows.push(guideRow('◈ Agent Mode', 'A Q-learning bandit that modulates the LEDGER conviction weight between 50 and 100 percent — it sizes the internal R book, it does not place orders and it can no longer veto a trade: scored at full weight the trades it skipped were still profitable (403 skips worth +0.3241 ATR against 1187 keeps worth +0.4482), so vetoing threw away edge.', cInfo));
gRows.push(guideRow('◈ Exit Strategy', 'Auto (default) picks the exit per trade from the tape at entry — calm entropy rides a Trailing Stop, normal tape takes the Hybrid bracket, chaotic entropy takes a fixed ATR Target/Stop. The named modes force one exit for every trade. All respect Max Hold.', cWarn));
gRows.push(guideRow('◈ SL / TP / Trail (ATR)', 'Independent exit distances in ATR multiples — exactly what the ledger uses, no hidden scaling. Keep Take Profit above Stop Loss for positive reward-to-risk.', cWarn));
gRows.push(guideRow('◈ Max Hold Bars', 'The hard duration cap on every strategy. Time Stop and Hybrid also exit on the clock here.', cWarn));
gRows.push(guideRow('RCM Layer', 'Rolling evidence on the continuation and fade families. Soft grades a fire; Gate can also block the family the tape does not own. It never creates a signal.', cInfo));
gRows.push(guideHead('EXECUTION', cWarn));
gRows.push(guideRow('Risk ATR (Stop)', 'Stop distance in ATR for the internal R book. Target is 1.60x that stop. Sizes the honesty ledger only.', cWarn));
gRows.push(guideRow('Execution Profile', 'Tight / Adaptive / Wide stretches the stop, the trail arm and the time stop together.', cWarn));
gRows.push(guideRow('Intra-bar', 'On paints a yellow LIVE badge on the forming bar the moment a swing qualifies. Alerts always fire on the confirmed close — nothing you are notified about can vanish, and a confirmed badge never moves.', cWarn));
gRows.push(guideRow('Lower Manifold', 'Two views only. Signal Raster is a dense event-to-fire web rebuilt from Flip-Mode. Harmonic Rosette is a polar confluence instrument — coherence guilloché, causal spiral arms, phase-sector spokes, radix-2 impulse spectrum, R-ledger ticks, radar sweep and beat waves. Pure plane geometry, zero 3D projection. The Manifold Height dial scales the panel: Petite, Compact, Standard, Tall or Grand.', cInfo));
gRows.push(guideRow('Visual Brightness', 'Dims or boosts main-chart visuals (reservoir rails, core, value rails, legs and candle tint), 1 to 10. The reservoir depth-alpha hierarchy is preserved at every setting. Badges, dashboard, guides and the manifold are never dimmed.', cWarn));
gRows.push(guideRow('Ledger honesty', 'The R book is internal, booked on confirmed bars only, split in-sample vs. out-of-sample at 70 percent of the chart. Family trust adapts size from CLOSED trades only. Exits register their own alert and ✕ mark.', cWarn));
var vRows = [];
vRows.push({ cells: [mkCell('⛰ PVK — VISUAL GUIDE', cInfo, 'center', true, 2, rgba(cInfo, 0.16))] });
vRows.push({ cells: [mkCell('Every visual here is original to PVK and appears in no other DAFE indicator.', cDim, 'left', false, 2, cBg)] });
vRows.push(guideHead('MAIN CHART', cBull));
vRows.push(guideRow('Adaptive Reservoir Ribbons', 'Nine adaptive lines: four rails per side orbit one opaque gravity core. From the outside inward, width ramps 0.50 → 1.00 → 1.50 → 2.00 while alpha rises 0.35 → 0.52 → 0.70 → 0.90 → 1.00 at the core. The fan expands with kernel coherence and leans with peak/valley imbalance while a slow clock breath widens the upper and lower banks counter-phase and a travelling ripple walks the fan, and the blackout cascade warps it: the core dims to black first, then each rail pair outward in sequence, forever — a living, depth-faded bundle.', cBull));
vRows.push(guideRow('Gravity Spine', 'The fully opaque 2-pixel linear-weighted Center of Gravity core. It anchors all eight surrounding rails and takes the colour of the currently dominant kernel.', cBull));
vRows.push(guideRow('Value Area Rails', 'VAH and VAL drawn as stepped rails with the Point of Control between them, recomputed on a stride from the volume profile.', cBull));
vRows.push(guideRow('Swing Legs', 'Straight connectors linking each confirmed swing to the one before it, so the zig-zag structure the engine is trading is explicit.', cBull));
vRows.push(guideRow('Candle Tint', 'Each candle tinted by the dominant kernel, green where a valley is forming and rust at a peak, with opacity set by kernel strength.', cBull));
vRows.push(guideRow('Entry Badges', 'Filled triangles mark confirmed entries, hollow triangles watch bars. A T badge means the trade came from the Trend breakout engine rather than the swing engine. ✕ marks a booked exit. Yellow + ! is the LIVE forming-bar preview.', cBull));
vRows.push(guideHead('LOWER MANIFOLD', cInfo));
vRows.push(guideRow('Signal Raster', 'The Flip-Mode-derived event web. Pivot, delayed-fractal, COG, value-area and FIRE rows glow at the live edge; newest-first links show same-side evidence becoming a confirmed fire. A comet lights the active path.', cInfo));
vRows.push(guideRow('Harmonic Rosette', 'A polar confluence engine — every layer is plane geometry driven by a live PVK series. Core arcs read quality and chop. Four counter-rotating guilloché layers (harmonics 2/3/5/8) breathe with kernel coherence. Three spiral arms trace causal history on a 2.15-loop rail: valley energy outward, peak energy inward, bias between. 24 spokes histogram kernel energy by phase sector. A 128-point Hann-windowed radix-2 spectrum of the signed kernel impulse rings the bezel with its centroid marked. Left annex: PHASE TIDE — sixteen overlapping Hann windows each get a radix-2 FFT, stacked into a time-by-band scalogram that now uses the full annex height, with the dominant-frequency ridge, a spectral-centroid snake and an instant band ladder. Right annex: SYNAPSE — 21 neurons whose activations are live kernel series in four layers, wired by the strongest measured correlations; every 3 seconds the weakest connection is pruned and a promising one grows in, and confirmed fires flash their source neuron and propagate white pulses two hops along the lattice. A 24-trade R ticker keeps the ledger readable. Radar sweep, twin beat waves, motes and a scan beam keep it alive; stars are confirmed fires sized by booked R, hollow nodes are exits, the white glint rides the dominant arm.', cInfo));
paint_overlay('PVK Guide', { position: 'bottom_right', order: 'above_all' }, {background: i_guideMode !== 'Off' ? cBg : 'rgba(0,0,0,0)', border: i_guideMode !== 'Off' ? '1px solid ' + cInfo : '0px solid transparent', borderRadius: 6, width: i_guideMode !== 'Off' ? 460 : 1, rows: i_guideMode === 'Input Guide' ? gRows : (i_guideMode === 'Visual Guide' ? vRows : []) });
// ==============================================================================================
// MANIFOLD DASHBOARD
// ==============================================================================================
function panelHeader(text) {
return { cells: [{ text: text, color: TH.a1, background: 'linear-gradient(90deg,' + rgba(TH.a1, 0.16) + ',' + rgba(TH.a2, 0.16) + ')', textAlign: 'center', fontSize: 10, fontWeight: 'bold', width: '100%' }] };}
function legendRow(text) {
return { cells: [{ text: text, color: TH.txt, background: rgba(TH.panel, 0.85), textAlign: 'center', fontSize: 9, fontWeight: 'bold', width: '100%' }] };}
function axisRow(text) {
return { cells: [{ text: text, color: TH.dim, background: rgba(TH.panel, 0.9), textAlign: 'center', fontSize: 8, width: '100%' }] };}
function chartRow(datasets, xmin, xmax, ymin, ymax, hpx) {
return { cells: [{
chart: {
width: '100%', height: (hpx || 120) + 'px', type: 'line',
options: {
devicePixelRatio: 2, maintainAspectRatio: false,
layout: { padding: { top: 2, bottom: 2, left: 2, right: 2 } },
scales: {
x: { type: 'linear', min: xmin, max: xmax, display: false, grid: { display: false } },
y: { type: 'linear', min: ymin, max: ymax, display: false, grid: { display: false } } },
plugins: { legend: { display: false } },
elements: { line: { tension: 0.4 } } },
data: { datasets: datasets } },
background_color: 'radial-gradient(ellipse at center, ' + rgba(TH.panel, 0.60) + ' 0%, ' + rgba(TH.bg, 0.98) + ' 80%)',
border_color: 'transparent', border_width: 0, padding: '0px', width: '100%' }] };}
function ds(label, data, color, width, radius, fill, dash, dashOff) {
return { label: label, data: data, borderColor: color, backgroundColor: fill || 'transparent', borderWidth: width, pointRadius: radius, pointBackgroundColor: color, pointBorderWidth: 0, fill: fill ? 'origin' : false, borderDash: dash || [], borderDashOffset: dashOff || 0, tension: 0.4, showLine: width > 0 };}
function pt(x, y) { return { x: x, y: y }; }
function hashRand(x) { var v = Math.sin(x * 12.9898) * 43758.5453; return v - Math.floor(v); }
// (ULMA motion core lives above the chart-plotting section now — the nine-engine registry, its dispatch map and findDS were dead scaffolding and are gone.)
var mRows = [];
// TWO-VIEW CONTRACT -------------------------------------------------------------
function viewSignalRaster() {
var sets = [], rStart = Math.max(ENGINE_START, n - 160);
var ROW = { PIVOT: 5, FRACTAL: 4, COG: 3, VALUE: 2, FIRE: 0 };
var cells = [], cols = [], sizes = [], events = [], k;
for (k = rStart; k < n; k++) {
var x = k - n + 1, age = lastBar - k, glow = age <= 3 ? (4 - age) * 0.5 : 0;
if (pricePeakA[k]) { cells.push(pt(x, ROW.PIVOT)); cols.push(rgba(TH.a2, age <= 3 ? 0.95 : 0.55)); sizes.push(3.2 + glow * 2); events.push({ b:k,r:ROW.PIVOT,s:-1 }); }
if (priceValleyA[k]) { cells.push(pt(x, ROW.PIVOT)); cols.push(rgba(TH.a1, age <= 3 ? 0.95 : 0.55)); sizes.push(3.2 + glow * 2); events.push({ b:k,r:ROW.PIVOT,s:1 }); }
if (fracHiA[k]) { cells.push(pt(x, ROW.FRACTAL)); cols.push(rgba(TH.a2,0.60)); sizes.push(2.6+glow); events.push({b:k,r:ROW.FRACTAL,s:-1}); }
if (fracLoA[k]) { cells.push(pt(x, ROW.FRACTAL)); cols.push(rgba(TH.a1,0.60)); sizes.push(2.6+glow); events.push({b:k,r:ROW.FRACTAL,s:1}); }
var cm = nz(cogMom[k],0), aR = Math.max(nz(atr14[k],0),Math.abs(nz(close[k],1))*1e-4);
if (Math.abs(cm) > aR*0.02) { cells.push(pt(x,ROW.COG)); cols.push(cm < 0 ? rgba(TH.a2,0.55) : rgba(TH.a1,0.55)); sizes.push(2.3+glow); events.push({b:k,r:ROW.COG,s:cm<0?-1:1}); }
if (Math.abs(nz(close[k],0)-nz(vahA[k],0)) <= aR*0.35) { cells.push(pt(x,ROW.VALUE)); cols.push(rgba(TH.a2,0.60)); sizes.push(2.4+glow); events.push({b:k,r:ROW.VALUE,s:-1}); }
if (Math.abs(nz(close[k],0)-nz(valA[k],0)) <= aR*0.35) { cells.push(pt(x,ROW.VALUE)); cols.push(rgba(TH.a1,0.60)); sizes.push(2.4+glow); events.push({b:k,r:ROW.VALUE,s:1}); }
if (longConf[k]) { cells.push(pt(x,ROW.FIRE)); cols.push(rgba(TH.a1,0.96)); sizes.push(4.5+glow*2); events.push({b:k,r:ROW.FIRE,s:1}); }
if (shortConf[k]) { cells.push(pt(x,ROW.FIRE)); cols.push(rgba(TH.a2,0.96)); sizes.push(4.5+glow*2); events.push({b:k,r:ROW.FIRE,s:-1}); } }
var cap = 4500, made = 0, cometX = -162 + ((CLOCK.beat * 1) % 162), e1, e2;
for (e1=events.length-1; e1>=0 && made<cap; e1--) for (e2=e1-1; e2>=0 && made<cap; e2--) {
var A=events[e1], B=events[e2], gap=A.b-B.b; if(gap>16) break; if(A.s!==B.s || A.r===B.r) continue;
var flare=clamp01(1-Math.abs((A.b-n+1)-cometX)/3), fresh=clamp01(1-(lastBar-A.b)/90);
sets.push({label:'web',data:[pt(B.b-n+1,B.r),pt(A.b-n+1,A.r)],showLine:true,borderColor:rgba(A.s>0?TH.a1:TH.a2,0.05+0.30*(1-gap/16)*fresh+0.48*flare),borderWidth:1+Math.round(flare),pointRadius:0,fill:false,tension:0.3,order:4}); made++; }
sets.push({label:'cells',data:cells,showLine:false,pointStyle:'rectRot',pointRadius:sizes,pointBackgroundColor:cols,pointBorderWidth:0,order:10});
var comet=[], ccol=[], crad=[], q; for(q=0;q<6;q++){ var cx=cometX-q*2.2; if(cx<-162)break; comet.push(pt(cx,ROW.PIVOT)); ccol.push(q===0?rgba(TH.warn,0.98):rgba('#FFFFFF',clamp01(.75-.14*q))); crad.push(q===0?6:Math.max(1.5,5-.8*q)); }
sets.push({label:'comet',data:comet,showLine:false,pointStyle:'circle',pointRadius:crad,pointBackgroundColor:ccol,pointBorderWidth:0,order:45});
sets.push({label:'now',data:[pt(0,-.8),pt(0,5.8)],showLine:true,borderColor:rgba(TH.a1,.50),borderWidth:1.5,borderDash:[3,3],pointRadius:0,fill:false,order:20});
sets.push({label:'scan',data:[pt(-162+((CLOCK.beat*11)%162),-.8),pt(-162+((CLOCK.beat*11)%162),5.8)],showLine:true,borderColor:rgba('#FFFFFF',.10),borderWidth:7,pointRadius:0,fill:false,order:3});
return {sets:sets,xmin:-162,xmax:2,ymin:-.8,ymax:5.8};}
// HARMONIC ROSETTE -------------------------------------------------------------------------------
function viewHarmonicRosette() {
var sets = [];
var WIN = clamp(Math.round(L_norm * 1.15), 120, 200);
var rStart = Math.max(ENGINE_START, n - WIN), cnt = Math.max(2, n - rStart);
var XW = 0.36, CX = 0, CY = 0;
var TURNS = 2.15, PHI0 = -Math.PI / 2;
function P(rr, ang) { return pt(CX + Math.cos(ang) * rr * XW, CY + Math.sin(ang) * rr); }
function tOf(k) { return (k - rStart) / (cnt - 1); }
function angOf(k) { return PHI0 + tOf(k) * TURNS * 2 * Math.PI; }
function railOf(k) { return 1.9 + tOf(k) * 2.6; }
function arcPts(rArc, a0, a1, steps) { var pA = [], qA; for (qA = 0; qA <= steps; qA++) pA.push(P(rArc, a0 + (a1 - a0) * qA / steps)); return pA; }
var domV = nz(kValley[lastBar], 0) >= nz(kPeak[lastBar], 0);
var liveCol = domV ? TH.a1 : TH.a2;
var pulse = pulse01(0.9, 0);
var pal = [], pk, hexP;
for (pk = 0; pk < 12; pk++) {
var tP = pk / 11;
hexP = TH.a1;
try { hexP = tP < 0.5 ? tiny.mix(TH.a1, TH.a3, tP * 2).toHexString() : tiny.mix(TH.a3, TH.a2, (tP - 0.5) * 2).toHexString(); } catch (eP) { hexP = TH.a1; }
pal.push(hexP); }
var FN = 128, re = [], im = make(FN, 0), fi, sMean = 0;
for (fi = 0; fi < FN; fi++) {
var ki = n - FN + fi, osc = 0;
if (ki >= 0) osc = clamp11((nz(kValley[ki], 0) - nz(kPeak[ki], 0)) * 1.7) + 0.30 * clamp11(nz(dtsArr[ki], 0) / 3);
re.push(osc); sMean += osc; }
sMean /= FN;
for (fi = 0; fi < FN; fi++) re[fi] = (re[fi] - sMean) * (0.5 - 0.5 * Math.cos(2 * Math.PI * fi / (FN - 1)));
var jF, bitF, lenF;
for (jF = 1, bitF = 0; jF < FN; jF++) {
var bF = FN >> 1;
for (; bitF & bF; bF >>= 1) bitF ^= bF;
bitF ^= bF;
if (jF < bitF) { var trF = re[jF]; re[jF] = re[bitF]; re[bitF] = trF; var tiF = im[jF]; im[jF] = im[bitF]; im[bitF] = tiF; } }
for (lenF = 2; lenF <= FN; lenF <<= 1) {
var angF = -2 * Math.PI / lenF, wrF = Math.cos(angF), wiF = Math.sin(angF);
for (jF = 0; jF < FN; jF += lenF) {
var crF = 1, ciF = 0, hF;
for (hF = 0; hF < lenF / 2; hF++) {
var i0F = jF + hF, i1F = jF + hF + lenF / 2;
var vrF = re[i1F] * crF - im[i1F] * ciF, viF = re[i1F] * ciF + im[i1F] * crF;
re[i1F] = re[i0F] - vrF; im[i1F] = im[i0F] - viF; re[i0F] += vrF; im[i0F] += viF;
var ncrF = crF * wrF - ciF * wiF; ciF = crF * wiF + ciF * wrF; crF = ncrF; } } }
var SPEC = 32, specV = [], sMx = 1e-9, cNum = 0, cDen = 0, bS, mF;
for (bS = 1; bS <= SPEC; bS++) { mF = Math.sqrt(re[bS] * re[bS] + im[bS] * im[bS]); specV.push(mF); sMx = Math.max(sMx, mF); }
for (bS = 0; bS < SPEC; bS++) { specV[bS] = clamp01(specV[bS] / sMx); cNum += (bS + 1) * specV[bS]; cDen += specV[bS]; }
var centroidBin = cDen > 0 ? cNum / cDen : SPEC / 2;
var centroidAng = PHI0 + centroidBin / SPEC * 2 * Math.PI;
var ringCfg = [[2.3, [3, 7], 0.13, TH.info, 55], [3.45, [2, 5], 0.15, TH.info, 80], [4.6, [4, 9], 0.17, TH.dim, 40], [7.45, [1, 11], 0.10, TH.warn, 65]];
var rC, qC2;
for (rC = 0; rC < ringCfg.length; rC++) {
var ptsC = [];
for (qC2 = 0; qC2 <= 96; qC2++) ptsC.push(P(ringCfg[rC][0], qC2 / 96 * 2 * Math.PI));
ptsC.push(ptsC[0]);
sets.push({ label: 'datumRing' + rC, data: ptsC, showLine: true, fill: false, borderColor: rgba(ringCfg[rC][3], ringCfg[rC][2]), borderWidth: 1, borderDash: ringCfg[rC][1], borderDashOffset: march(ringCfg[rC][4]), pointRadius: 0, tension: 0, order: 3 }); }
var bezel = [], bezR = [], bezC = [];
for (qC2 = 0; qC2 < 72; qC2++) {
var angB = qC2 / 72 * 2 * Math.PI;
bezel.push(P(7.45, angB));
bezR.push(qC2 % 6 === 0 ? 2.1 : 1.2);
bezC.push(rgba(qC2 % 6 === 0 ? TH.warn : TH.dim, qC2 % 6 === 0 ? 0.40 : 0.20)); }
sets.push({ label: 'bezel', data: bezel, showLine: false, pointStyle: 'rectRot', pointRadius: bezR, pointBackgroundColor: bezC, pointBorderWidth: 0, order: 4 });
var qualNow = nz(qualArr[lastBar], 0);
sets.push({ label: 'qualArc', data: arcPts(1.66, -Math.PI / 2, -Math.PI / 2 + 2 * Math.PI * clamp01(qualNow / 0.9), 48), showLine: true, fill: false, borderColor: rgba(TH.a3, 0.85), borderWidth: 2.4, pointRadius: 0, tension: 0, order: 40 });
sets.push({ label: 'chopArc', data: arcPts(1.44, -Math.PI / 2, -Math.PI / 2 + 2 * Math.PI * clamp01(1 - nz(chopRank[lastBar], 0.5)), 48), showLine: true, fill: false, borderColor: rgba(TH.info, 0.60), borderWidth: 1.8, pointRadius: 0, tension: 0, order: 39 });
var hN;
for (hN = 0; hN < 4; hN++) sets.push({ label: 'coreHalo' + hN, data: [pt(CX, CY)], showLine: false, pointStyle: 'circle', pointRadius: [(5.4 - hN * 1.15) + pulse * 1.2], pointBackgroundColor: [rgba(liveCol, 0.04 + hN * 0.05)], pointBorderWidth: 0, order: 42 + hN });
sets.push({ label: 'coreStar', data: [pt(CX, CY)], showLine: false, pointStyle: 'star', pointRadius: [4.2], pointBackgroundColor: [liveCol], pointBorderColor: ['#FFFFFF'], pointBorderWidth: 1.2, order: 48 });
sets.push({ label: 'coreCrossV', data: [pt(CX, CY - 1.05), pt(CX, CY + 1.05)], showLine: true, fill: false, borderColor: rgba(TH.txt, 0.30), borderWidth: 1, pointRadius: 0, tension: 0, order: 41 });
sets.push({ label: 'coreCrossH', data: [pt(CX - 1.05 * XW, CY), pt(CX + 1.05 * XW, CY)], showLine: true, fill: false, borderColor: rgba(TH.txt, 0.30), borderWidth: 1, pointRadius: 0, tension: 0, order: 41 });
var cohSum = 0, cW = 0, kR;
for (kR = rStart; kR < n; kR++) { cohSum += Math.max(nz(kPeak[kR], 0), nz(kValley[kR], 0)); cW++; }
var cohN = clamp01((cW ? cohSum / cW : 0) / 0.55);
var petalK = [2, 3, 5, 8], petalR = [6.35, 5.90, 5.45, 5.00], petalA = [0.030, 0.042, 0.058, 0.075];
var petalCol = [TH.a1, TH.a3, TH.a2, TH.txt];
var rosL;
for (rosL = 0; rosL < 4; rosL++) {
var dirL = rosL % 2 === 0 ? 1 : -1;
var phL = dirL * CLOCK.slow * (0.045 + 0.014 * rosL);
var breathL = breathe(1.0, 0.035, 0.16 + 0.03 * rosL, rosL * 1.3);
var ampL = petalA[rosL] * (0.55 + 0.90 * cohN) * breathL;
var rosPts = [], qR;
for (qR = 0; qR <= 220; qR++) {
var thR = qR / 220 * 2 * Math.PI;
var rR2 = petalR[rosL] * breathL * (1 + ampL * Math.cos(petalK[rosL] * thR + phL) + 0.30 * ampL * Math.cos((petalK[rosL] + 2) * thR - phL * 1.7));
rosPts.push(P(rR2, thR)); }
rosPts.push(rosPts[0]);
sets.push({ label: 'rosette' + rosL, data: rosPts, showLine: true, fill: false, borderColor: rgba(petalCol[rosL], rosL === 3 ? 0.13 : 0.19 + 0.02 * rosL), borderWidth: rosL === 1 ? 1.8 : 1.25, borderDash: rosL === 2 ? [2, 6] : [], borderDashOffset: rosL === 2 ? march(70) : 0, pointRadius: 0, tension: 0, order: 12 + rosL }); }
var smV = make(n, 0), smP = make(n, 0), kSM, eV = nz(kValley[Math.max(0, rStart - 1)], 0), eP = nz(kPeak[Math.max(0, rStart - 1)], 0);
for (kSM = Math.max(0, rStart - 1); kSM < n; kSM++) {
eV = eV * 0.66 + nz(kValley[kSM], 0) * 0.34;
eP = eP * 0.66 + nz(kPeak[kSM], 0) * 0.34;
smV[kSM] = eV; smP[kSM] = eP; }
var railPts = [], armV = [], armP = [], armB = [], railTk = [], kA, kvA = 0, kpA = 0;
for (kA = rStart; kA < n; kA++) {
var tA = angOf(kA), rRl = railOf(kA);
kvA = nz(smV[kA], 0); kpA = nz(smP[kA], 0);
railPts.push(P(rRl, tA));
armV.push(P(rRl + 0.26 + kvA * 1.35, tA));
armP.push(P(rRl - 0.26 - kpA * 1.35, tA));
armB.push(P(rRl + clamp11(nz(DirBias[kA], 0)) * 0.7, tA));
if ((kA - rStart) % 10 === 0) railTk.push(P(rRl, tA)); }
sets.push({ label: 'spiralRail', data: railPts, showLine: true, fill: false, borderColor: rgba(TH.dim, 0.28), borderWidth: 1.1, borderDash: [2, 6], borderDashOffset: march(90), pointRadius: 0, tension: 0.25, order: 8 });
sets.push({ label: 'railTicks', data: railTk, showLine: false, pointStyle: 'rectRot', pointRadius: 1.6, pointBackgroundColor: rgba(TH.dim, 0.35), pointBorderWidth: 0, order: 9 });
sets.push({ label: 'armValley', data: armV, showLine: true, fill: false, borderColor: rgba(TH.a1, 0.78), borderWidth: 2.0, pointRadius: 0, tension: 0.30, order: 26 });
sets.push({ label: 'armPeak', data: armP, showLine: true, fill: false, borderColor: rgba(TH.a2, 0.72), borderWidth: 2.0, pointRadius: 0, tension: 0.30, order: 27 });
sets.push({ label: 'armBias', data: armB, showLine: true, fill: false, borderColor: rgba(TH.warn, 0.50), borderWidth: 1.2, borderDash: [5, 4], borderDashOffset: march(60), pointRadius: 0, tension: 0.30, order: 28 });
var firePts = [], fireC = [], fireR = [], exPts = [], kF;
for (kF = rStart; kF < n; kF++) {
var tF = angOf(kF), rF = railOf(kF);
if (longConf[kF] || shortConf[kF]) {
var longF = !!longConf[kF];
firePts.push(P(rF + (longF ? 0.30 : -0.30), tF));
fireC.push(rgba(longF ? TH.bull : TH.bear, 0.95));
fireR.push(2.2 + 1.9 * clamp01(Math.abs(nz(fireRMap[kF], 0.6)) / 2)); }
if (exitSigArr[kF]) exPts.push(P(rF + clamp11(nz(DirBias[kF], 0)) * 0.8, tF)); }
if (firePts.length) sets.push({ label: 'fireNodes', data: firePts, showLine: false, pointStyle: 'star', pointRadius: fireR, pointBackgroundColor: fireC, pointBorderColor: fireC, pointBorderWidth: 1, order: 44 });
if (exPts.length) sets.push({ label: 'exitNodes', data: exPts, showLine: false, pointStyle: 'circle', pointRadius: 2.3, pointBackgroundColor: rgba(TH.dim, 0.05), pointBorderColor: rgba(TH.txt, 0.55), pointBorderWidth: 1, order: 43 });
var armDom = domV ? armV : armP;
var gI = Math.floor(travel(armDom.length, 1.4)), gPts = [], gC2 = [], gR2 = [], qG;
for (qG = 0; qG < 5; qG++) {
var gi2 = (gI - qG * 4 + armDom.length * 2) % armDom.length;
gPts.push(armDom[gi2]);
gC2.push(rgba('#FFFFFF', 0.90 - qG * 0.17));
gR2.push(3.9 - qG * 0.65); }
sets.push({ label: 'armGlint', data: gPts, showLine: false, pointStyle: 'circle', pointRadius: gR2, pointBackgroundColor: gC2, pointBorderWidth: 0, order: 58 });
var thEnd = angOf(n - 1), rEnd = railOf(n - 1) + 0.30;
sets.push({ label: 'nowBeam', data: [P(4.72, thEnd), P(7.45, thEnd)], showLine: true, fill: false, borderColor: rgba(liveCol, 0.35), borderWidth: 5, pointRadius: 0, tension: 0, order: 46 });
sets.push({ label: 'nowBeamCore', data: [P(4.72, thEnd), P(7.45, thEnd)], showLine: true, fill: false, borderColor: rgba('#FFFFFF', 0.70), borderWidth: 1.2, pointRadius: 0, tension: 0, order: 47 });
for (hN = 0; hN < 3; hN++) sets.push({ label: 'nowHalo' + hN, data: [P(rEnd, thEnd)], showLine: false, pointStyle: 'circle', pointRadius: [(7.6 - hN * 2.3) + pulse * 1.8], pointBackgroundColor: [rgba(liveCol, 0.05 + hN * 0.05)], pointBorderWidth: 0, order: 50 + hN });
sets.push({ label: 'nowStar', data: [P(rEnd, thEnd)], showLine: false, pointStyle: 'star', pointRadius: [5.8], pointBackgroundColor: [liveCol], pointBorderColor: ['#FFFFFF'], pointBorderWidth: 1.4, order: 55 });
var SEC = 24, sE = make(SEC, 0), sS = make(SEC, 0), sN = make(SEC, 0), kS;
for (kS = rStart; kS < n; kS++) {
var a01 = (((angOf(kS) - PHI0) / (2 * Math.PI)) % 1 + 1) % 1;
var bI = clamp(Math.floor(a01 * SEC), 0, SEC - 1);
sE[bI] += Math.max(nz(kPeak[kS], 0), nz(kValley[kS], 0));
sS[bI] += nz(kValley[kS], 0) >= nz(kPeak[kS], 0) ? 1 : -1;
sN[bI]++; }
var sMx2 = 1e-9, bX;
for (bX = 0; bX < SEC; bX++) { sE[bX] = sN[bX] ? sE[bX] / sN[bX] : 0; sMx2 = Math.max(sMx2, sE[bX]); }
var spkSeg = [], spkTip = [], spkC = [], spkR = [];
for (bX = 0; bX < SEC; bX++) {
var nnS = clamp01(sE[bX] / sMx2);
var aM = PHI0 + (bX + 0.5) / SEC * 2 * Math.PI;
var colS = sS[bX] >= 0 ? TH.a1 : TH.a2;
spkSeg.push(P(4.74, aM)); spkSeg.push(P(4.74 + 0.45 + 1.30 * nnS, aM)); spkSeg.push({ x: null, y: null });
spkTip.push(P(4.74 + 0.45 + 1.30 * nnS, aM));
spkC.push(rgba(colS, 0.30 + 0.40 * nnS));
spkR.push(1.4 + 1.8 * nnS); }
sets.push({ label: 'phaseSpokes', data: spkSeg, showLine: true, fill: false, spanGaps: false, borderColor: rgba(TH.info, 0.18), borderWidth: 1.3, pointRadius: 0, tension: 0, order: 10 });
sets.push({ label: 'spokeTips', data: spkTip, showLine: false, pointStyle: 'rectRot', pointRadius: spkR, pointBackgroundColor: spkC, pointBorderWidth: 0, order: 11 });
var specSeg = [], specTip = [], specC = [], specR = [];
for (bX = 0; bX < SPEC; bX++) {
var aZ = PHI0 + (bX + 0.5) / SPEC * 2 * Math.PI, hZ = 0.12 + 0.58 * specV[bX];
specSeg.push(P(6.62, aZ)); specSeg.push(P(6.62 + hZ, aZ)); specSeg.push({ x: null, y: null });
specTip.push(P(6.62 + hZ, aZ));
specC.push(rgba(pal[Math.round(specV[bX] * 11)], 0.35 + 0.55 * specV[bX]));
specR.push(1.0 + 2.0 * specV[bX]); }
sets.push({ label: 'spectrumRing', data: specSeg, showLine: true, fill: false, spanGaps: false, borderColor: rgba(TH.info, 0.25), borderWidth: 1.4, pointRadius: 0, tension: 0, order: 14 });
sets.push({ label: 'spectrumTips', data: specTip, showLine: false, pointStyle: 'circle', pointRadius: specR, pointBackgroundColor: specC, pointBorderWidth: 0, order: 15 });
sets.push({ label: 'centroidArc', data: arcPts(7.30, centroidAng - 0.42, centroidAng + 0.42, 24), showLine: true, fill: false, borderColor: rgba(TH.warn, 0.85), borderWidth: 2.2, pointRadius: 0, tension: 0, order: 45 });
sets.push({ label: 'centroidMark', data: [P(7.30, centroidAng)], showLine: false, pointStyle: 'rectRot', pointRadius: 2.6, pointBackgroundColor: rgba(TH.warn, 0.95), pointBorderWidth: 0, order: 45 });
var SLICES = 16, BANDS = 8, WINW = 64;
var tideX0 = -12.9, tideX1 = -5.6, tideYTop = 6.8, tideYBot = -4.2;
var tideBaseY = -5.0;
var tideBW = (tideX1 - tideX0) / BANDS, tideRH = (tideYTop - tideYBot) / SLICES;
var stride2 = Math.max(2, Math.floor(cnt / SLICES));
var tideE = [], centroidTrail = [], sl2, b2, qT2;
for (sl2 = 0; sl2 < SLICES; sl2++) {
var endSl = n - 1 - (SLICES - 1 - sl2) * stride2;
var startSl = Math.max(0, endSl - WINW + 1), lenSl = Math.max(1, endSl - startSl + 1);
var reT = [], imT = make(WINW, 0), meanT = 0;
for (qT2 = 0; qT2 < WINW; qT2++) {
var ki2 = startSl + Math.floor(qT2 * lenSl / WINW), o2 = 0;
if (ki2 >= 0 && ki2 < n) o2 = clamp11((nz(kValley[ki2], 0) - nz(kPeak[ki2], 0)) * 1.7) + 0.30 * clamp11(nz(dtsArr[ki2], 0) / 3);
reT.push(o2); meanT += o2; }
meanT /= WINW;
for (qT2 = 0; qT2 < WINW; qT2++) reT[qT2] = (reT[qT2] - meanT) * (0.5 - 0.5 * Math.cos(2 * Math.PI * qT2 / (WINW - 1)));
var bF3;
for (jF = 1, bitF = 0; jF < WINW; jF++) {
bF3 = WINW >> 1;
for (; bitF & bF3; bF3 >>= 1) bitF ^= bF3;
bitF ^= bF3;
if (jF < bitF) { var tr3 = reT[jF]; reT[jF] = reT[bitF]; reT[bitF] = tr3; var ti3 = imT[jF]; imT[jF] = imT[bitF]; imT[bitF] = ti3; } }
for (lenF = 2; lenF <= WINW; lenF <<= 1) {
angF = -2 * Math.PI / lenF; wrF = Math.cos(angF); wiF = Math.sin(angF);
for (jF = 0; jF < WINW; jF += lenF) {
crF = 1; ciF = 0;
for (hF = 0; hF < lenF / 2; hF++) {
i0F = jF + hF; i1F = jF + hF + lenF / 2;
vrF = reT[i1F] * crF - imT[i1F] * ciF; viF = reT[i1F] * ciF + imT[i1F] * crF;
reT[i1F] = reT[i0F] - vrF; imT[i1F] = imT[i0F] - viF; reT[i0F] += vrF; imT[i0F] += viF;
ncrF = crF * wrF - ciF * wiF; ciF = crF * wiF + ciF * wrF; crF = ncrF; } } }
var rowE = [], rowMx = 1e-9, cN2 = 0, cD2 = 0;
for (b2 = 1; b2 <= BANDS; b2++) { var eB2 = Math.sqrt(reT[b2] * reT[b2] + imT[b2] * imT[b2]); rowE.push(eB2); if (eB2 > rowMx) rowMx = eB2; }
for (b2 = 0; b2 < BANDS; b2++) { rowE[b2] = clamp01(rowE[b2] / rowMx); cN2 += (b2 + 0.5) * rowE[b2]; cD2 += rowE[b2]; }
tideE.push(rowE);
centroidTrail.push(cD2 > 0 ? cN2 / cD2 : BANDS / 2); }
var cellPts = [], cellCols = [], cellRads = [], weaveSeg = [], ridgePts = [], snakePts = [];
var bX2;
for (sl2 = 0; sl2 < SLICES; sl2++) {
var rowY = tideYTop - (sl2 + 0.5) * tideRH;
var argMx = 0;
for (bX2 = 1; bX2 < BANDS; bX2++) if (tideE[sl2][bX2] > tideE[sl2][argMx]) argMx = bX2;
weaveSeg.push(pt(tideX0, rowY)); weaveSeg.push(pt(tideX1, rowY)); weaveSeg.push({ x: null, y: null });
ridgePts.push(pt(tideX0 + (argMx + 0.5) * tideBW, rowY));
snakePts.push(pt(tideX0 + centroidTrail[sl2] / BANDS * (tideX1 - tideX0), rowY));
for (bX2 = 0; bX2 < BANDS; bX2++) {
var eC2 = tideE[sl2][bX2];
cellPts.push(pt(tideX0 + (bX2 + 0.5) * tideBW, rowY));
cellCols.push(rgba(pal[Math.round(eC2 * 11)], 0.30 + 0.62 * eC2));
cellRads.push(1.0 + 2.0 * eC2); } }
sets.push({ label: 'tideCells', data: cellPts, showLine: false, pointStyle: 'rect', pointRadius: cellRads, pointBackgroundColor: cellCols, pointBorderWidth: 0, order: 8 });
sets.push({ label: 'tideWeave', data: weaveSeg, showLine: true, fill: false, spanGaps: false, borderColor: rgba(TH.dim, 0.08), borderWidth: 1, pointRadius: 0, tension: 0, order: 7 });
sets.push({ label: 'tideRidge', data: ridgePts, showLine: true, fill: false, borderColor: rgba(TH.warn, 0.55), borderWidth: 1.5, pointRadius: 0, tension: 0.45, order: 18 });
sets.push({ label: 'centroidSnake', data: snakePts, showLine: true, fill: false, borderColor: rgba('#FFFFFF', 0.35), borderWidth: 1.2, pointRadius: 0, tension: 0.45, order: 19 });
var snakeHead = [], shC = [], shR = [], qS2;
for (qS2 = 1; qS2 <= Math.min(6, snakePts.length); qS2++) {
snakeHead.push(snakePts[snakePts.length - qS2]);
shC.push(rgba(TH.a3, 0.85 - qS2 * 0.12));
shR.push(Math.max(0.8, 2.6 - qS2 * 0.35)); }
sets.push({ label: 'snakeHead', data: snakeHead, showLine: false, pointStyle: 'circle', pointRadius: shR, pointBackgroundColor: shC, pointBorderWidth: 0, order: 20 });
var ladSeg = [], ladTip = [], ladC = [], ladR = [], lastRow = tideE[SLICES - 1], bL2;
for (bL2 = 0; bL2 < BANDS; bL2++) {
var lh2 = 0.15 + 1.35 * lastRow[bL2], lx2 = tideX0 + (bL2 + 0.5) * tideBW;
ladSeg.push(pt(lx2, tideBaseY)); ladSeg.push(pt(lx2, tideBaseY + lh2)); ladSeg.push({ x: null, y: null });
ladTip.push(pt(lx2, tideBaseY + lh2));
ladC.push(rgba(pal[Math.round(lastRow[bL2] * 11)], 0.35 + 0.55 * lastRow[bL2]));
ladR.push(1.2 + 1.5 * lastRow[bL2]); }
sets.push({ label: 'tideLadder', data: ladSeg, showLine: true, fill: false, spanGaps: false, borderColor: rgba(TH.info, 0.22), borderWidth: 1.4, pointRadius: 0, tension: 0, order: 9 });
sets.push({ label: 'tideLadderTips', data: ladTip, showLine: false, pointStyle: 'circle', pointRadius: ladR, pointBackgroundColor: ladC, pointBorderWidth: 0, order: 10 });
sets.push({ label: 'tideBase', data: [pt(tideX0, tideBaseY), pt(tideX1, tideBaseY)], showLine: true, fill: false, borderColor: rgba(TH.dim, 0.32), borderWidth: 1, borderDash: [2, 4], borderDashOffset: march(85), pointRadius: 0, tension: 0, order: 4 });
var NLAY = 4, layN = [5, 7, 6, 3], layX = [8.55, 10.25, 11.95, 13.35];
var poolA = [kPeak, kValley, dtsArr, DirBias, chopRank, entArr, qualArr, regStress];
function sqN(p2, v2) { if (p2 === 2) return clamp01((v2 + 3) / 6); if (p2 === 3) return clamp01((v2 + 1) / 2); if (p2 === 4 || p2 === 5) return clamp01(1 - v2); return clamp01(v2); }
var neurons = [], nId, nY, nX, nP, nW, nTotal = 0, L4;
for (L4 = 0; L4 < NLAY; L4++) {
for (nId = 0; nId < layN[L4]; nId++) {
nP = (nTotal * 5 + L4 * 3) % poolA.length;
nW = 10 + 8 * Math.floor(hashRand(nTotal * 7 + 1) * 6);
nY = -5.3 + (nId + 0.5) / layN[L4] * 10.6 + (hashRand(nTotal * 17 + 3) - 0.5) * 0.9;
nX = layX[L4] + (hashRand(nTotal * 29 + 11) - 0.5) * 0.7;
neurons.push({ x: nX, y: nY, layer: L4, pool: nP, win: nW, id: nTotal }); nTotal++; } }
var layStart = [0, layN[0], layN[0] + layN[1], layN[0] + layN[1] + layN[2]];
var TRN = 56, trails = [], ti2, acc, bb;
for (ti2 = 0; ti2 < nTotal; ti2++) {
var trA = [], startB = Math.max(0, n - 1 - TRN - 6);
acc = sqN(neurons[ti2].pool, nz(poolA[neurons[ti2].pool][startB], 0));
for (bb = startB; bb < n; bb++) {
acc = acc * 0.86 + sqN(neurons[ti2].pool, nz(poolA[neurons[ti2].pool][bb], 0)) * 0.14;
trA.push(acc); }
trails.push(trA); }
function corrT(aT, bT) {
var len2 = Math.min(aT.length, bT.length), q2;
if (len2 < 12) return 0;
var m2 = 0, m3 = 0;
for (q2 = 0; q2 < len2; q2++) { m2 += aT[q2]; m3 += bT[q2]; }
m2 /= len2; m3 /= len2;
var num2 = 0, dA = 0, dB = 0;
for (q2 = 0; q2 < len2; q2++) { var da2 = aT[q2] - m2, db2 = bT[q2] - m3; num2 += da2 * db2; dA += da2 * da2; dB += db2 * db2; }
return (dA > 1e-12 && dB > 1e-12) ? num2 / Math.sqrt(dA * dB) : 0; }
var eCands = [], eL, eA, eB;
for (eL = 0; eL < NLAY - 1; eL++) {
for (eA = 0; eA < layN[eL]; eA++) for (eB = 0; eB < layN[eL + 1]; eB++) {
var idA2 = layStart[eL] + eA, idB2 = layStart[eL + 1] + eB;
eCands.push({ a: idA2, b: idB2, r: corrT(trails[idA2], trails[idB2]) }); } }
eCands.sort(function (x1, x2) { return Math.abs(x2.r) - Math.abs(x1.r); });
var kept = [], degOut = make(nTotal, 0), eC2;
for (eC2 = 0; eC2 < eCands.length; eC2++) {
var cE = eCands[eC2];
if (Math.abs(cE.r) < 0.30 || degOut[cE.a] >= 3 || kept.length >= 44) continue;
kept.push(cE); degOut[cE.a]++; }
var growEpoch = Math.floor(CLOCK.beat / 3), gProg = clamp01(CLOCK.beat / 3 - growEpoch);
var rejected = [];
for (eC2 = 0; eC2 < eCands.length && rejected.length < 26; eC2++) {
var rj = eCands[eC2];
if (Math.abs(rj.r) >= 0.18 && Math.abs(rj.r) < 0.30) rejected.push(rj); }
var growE = rejected.length ? rejected[growEpoch % rejected.length] : null;
var pruneE = kept.length ? kept[(growEpoch * 7 + 3) % kept.length] : null;
if (pruneE && Math.abs(pruneE.r) > 0.45) pruneE = null;
var posStrong = [], posWeak = [], negStrong = [], negWeak = [];
for (eC2 = 0; eC2 < kept.length; eC2++) {
var kE = kept[eC2];
if (pruneE && kE.a === pruneE.a && kE.b === pruneE.b) continue;
var pA2 = pt(neurons[kE.a].x, neurons[kE.a].y), pB2 = pt(neurons[kE.b].x, neurons[kE.b].y);
var bucket = kE.r > 0 ? (Math.abs(kE.r) > 0.55 ? posStrong : posWeak) : (Math.abs(kE.r) > 0.55 ? negStrong : negWeak);
bucket.push(pA2); bucket.push(pB2); bucket.push({ x: null, y: null }); }
if (posStrong.length) sets.push({ label: 'edgesPosStrong', data: posStrong, showLine: true, fill: false, spanGaps: false, borderColor: rgba(TH.a1, 0.36), borderWidth: 1.6, pointRadius: 0, tension: 0, order: 5 });
if (posWeak.length) sets.push({ label: 'edgesPosWeak', data: posWeak, showLine: true, fill: false, spanGaps: false, borderColor: rgba(TH.a1, 0.15), borderWidth: 1.0, pointRadius: 0, tension: 0, order: 5 });
if (negStrong.length) sets.push({ label: 'edgesNegStrong', data: negStrong, showLine: true, fill: false, spanGaps: false, borderColor: rgba(TH.a2, 0.30), borderWidth: 1.6, pointRadius: 0, tension: 0, order: 5 });
if (negWeak.length) sets.push({ label: 'edgesNegWeak', data: negWeak, showLine: true, fill: false, spanGaps: false, borderColor: rgba(TH.a2, 0.13), borderWidth: 1.0, pointRadius: 0, tension: 0, order: 5 });
if (growE) {
var gDots = [], gN3 = 9, q3b;
for (q3b = 0; q3b < gN3; q3b++) {
var f3b = gProg * (q3b + 1) / gN3;
if (f3b > gProg) f3b = gProg;
gDots.push(pt(neurons[growE.a].x + (neurons[growE.b].x - neurons[growE.a].x) * f3b, neurons[growE.a].y + (neurons[growE.b].y - neurons[growE.a].y) * f3b)); }
sets.push({ label: 'edgeGrowing', data: gDots, showLine: false, pointStyle: 'circle', pointRadius: 1.7, pointBackgroundColor: rgba(TH.a3, 0.30 + 0.45 * gProg), pointBorderWidth: 0, order: 6 });
sets.push({ label: 'edgeGhost', data: [pt(neurons[growE.a].x, neurons[growE.a].y), pt(neurons[growE.b].x, neurons[growE.b].y)], showLine: true, fill: false, borderColor: rgba(TH.a3, 0.10 + 0.12 * gProg), borderWidth: 1, borderDash: [2, 4], pointRadius: 0, tension: 0, order: 5 }); }
if (pruneE) sets.push({ label: 'edgePruning', data: [pt(neurons[pruneE.a].x, neurons[pruneE.a].y), pt(neurons[pruneE.b].x, neurons[pruneE.b].y)], showLine: true, fill: false, borderColor: rgba(TH.dim, 0.30 * (1 - gProg)), borderWidth: 1, pointRadius: 0, tension: 0, order: 6 });
var dendSeg = [], dW2;
for (dW2 = 0; dW2 < 3; dW2++) {
var srcD = neurons[dW2 * 2], dX = srcD.x, dY = srcD.y, st2;
for (st2 = 0; st2 < 14; st2++) {
var nx2 = dX + 0.40, ny2 = dY + (hashRand(dW2 * 91 + st2 * 7) - 0.5) * 1.15;
dendSeg.push(pt(dX, dY)); dendSeg.push(pt(nx2, ny2)); dendSeg.push({ x: null, y: null });
if (st2 === 7) {
var fY = ny2 + (dW2 % 2 === 0 ? 0.55 : -0.55);
dendSeg.push(pt(nx2, ny2)); dendSeg.push(pt(nx2 + 0.30, fY)); dendSeg.push({ x: null, y: null });
var gx2, gy2 = fY;
for (gx2 = 0; gx2 < 5; gx2++) {
dendSeg.push(pt(nx2 + 0.30 + gx2 * 0.40, gy2)); dendSeg.push(pt(nx2 + 0.30 + (gx2 + 1) * 0.40, gy2 + (hashRand(dW2 * 31 + gx2 * 13) - 0.5) * 0.9)); dendSeg.push({ x: null, y: null });
gy2 = gy2 + (hashRand(dW2 * 31 + gx2 * 13) - 0.5) * 0.9; } }
dX = nx2; dY = ny2; } }
sets.push({ label: 'dendrites', data: dendSeg, showLine: true, fill: false, spanGaps: false, borderColor: rgba(TH.info, 0.09), borderWidth: 1, pointRadius: 0, tension: 0, order: 3 });
var lt24 = rList.slice(-24), wWins = 0, qW2, sumR24 = 0;
for (qW2 = 0; qW2 < lt24.length; qW2++) { if (lt24[qW2] >= 0) wWins++; sumR24 += lt24[qW2]; }
var wrNow = lt24.length ? wWins / lt24.length : 0;
var outActs = [wrNow, clamp01(0.5 + (sumR24 / Math.max(1, lt24.length)) / 3), clamp01(lt24.length / 24)];
var layPts = [[], [], [], []], layCols = [[], [], [], []], layRads = [[], [], [], []];
for (ti2 = 0; ti2 < nTotal; ti2++) {
var nE = neurons[ti2], tr2 = trails[ti2];
var act2 = tr2.length ? tr2[tr2.length - 1] : 0;
if (nE.layer === NLAY - 1) act2 = outActs[nE.id - layStart[NLAY - 1]];
var slope2 = tr2.length > 6 ? act2 - tr2[tr2.length - 6] : 0;
var flick2 = 0.12 * Math.sin(CLOCK.slow * 2.1 + ti2 * 1.7);
var rad2 = 1.1 + 2.6 * clamp01(act2 + flick2);
var col2 = rgba(slope2 >= 0 ? TH.a1 : TH.a2, 0.35 + 0.55 * act2);
if (nE.layer === NLAY - 1) col2 = outActs[nE.id - layStart[NLAY - 1]] >= 0.5 ? rgba(TH.bull, 0.85) : rgba(TH.bear, 0.85);
layPts[nE.layer].push(pt(nE.x, nE.y)); layCols[nE.layer].push(col2); layRads[nE.layer].push(rad2); }
for (L4 = 0; L4 < NLAY; L4++) sets.push({ label: 'neuronsL' + L4, data: layPts[L4], showLine: false, pointStyle: 'circle', pointRadius: layRads[L4], pointBackgroundColor: layCols[L4], pointBorderWidth: 0, order: 20 });
var fireSrcs = [], eBar;
for (eBar = Math.max(0, n - 16); eBar < n; eBar++) {
if (longConf[eBar]) fireSrcs.push({ age: n - 1 - eBar, dir: 1 });
if (shortConf[eBar]) fireSrcs.push({ age: n - 1 - eBar, dir: -1 }); }
var spontN = 0;
for (ti2 = 0; ti2 < nTotal && spontN < 2; ti2++) {
var tE = trails[ti2];
if (tE.length > 8 && tE[tE.length - 1] > 0.74 && tE[tE.length - 5] <= 0.72) {
fireSrcs.push({ age: 0, dir: tE[tE.length - 1] > tE[tE.length - 5] ? 1 : -1, nid: ti2 }); spontN++; } }
var flashPts = [], flashR = [], flashC = [], pulsePts = [], pulseC = [], pulseR = [], glowSeg = [];
var fI, fE, srcN, qN2, outs, d3, q3;
for (fI = 0; fI < fireSrcs.length; fI++) {
fE = fireSrcs[fI];
srcN = null;
if (fE.nid !== undefined) srcN = neurons[fE.nid];
else {
var wantPool = fE.dir > 0 ? 1 : 0;
for (qN2 = 0; qN2 < layStart[1]; qN2++) if (neurons[qN2].pool === wantPool) { srcN = neurons[qN2]; break; }
if (!srcN) srcN = neurons[0]; }
var ph2 = ((CLOCK.beat * 0.55) + fE.age * 0.31) % 1;
flashPts.push(pt(srcN.x, srcN.y)); flashR.push(2.2 + 9.5 * ph2); flashC.push(rgba('#FFFFFF', (1 - ph2) * 0.42));
outs = [];
for (eC2 = 0; eC2 < kept.length && outs.length < 2; eC2++) if (kept[eC2].a === srcN.id) outs.push(kept[eC2]);
for (d3 = 0; d3 < outs.length; d3++) {
var oE = outs[d3], A3 = neurons[oE.a], B3 = neurons[oE.b];
glowSeg.push(pt(A3.x, A3.y)); glowSeg.push(pt(B3.x, B3.y)); glowSeg.push({ x: null, y: null });
for (q3 = 0; q3 < 6; q3++) {
var f4 = (ph2 + q3 * 0.14) % 1;
pulsePts.push(pt(A3.x + (B3.x - A3.x) * f4, A3.y + (B3.y - A3.y) * f4));
pulseC.push(rgba('#FFFFFF', 0.85 * (1 - q3 / 6)));
pulseR.push(2.3 - q3 * 0.25); }
var outs2 = [];
for (eC2 = 0; eC2 < kept.length && outs2.length < 1; eC2++) if (kept[eC2].a === oE.b) outs2.push(kept[eC2]);
if (outs2.length && ph2 > 0.45) {
var oE2 = outs2[0], C3 = neurons[oE2.b], f5 = (ph2 - 0.45) * 2.2;
if (f5 > 1) f5 = 1;
glowSeg.push(pt(B3.x, B3.y)); glowSeg.push(pt(C3.x, C3.y)); glowSeg.push({ x: null, y: null });
for (q3 = 0; q3 < 5; q3++) {
var f6 = f5 * (q3 + 1) / 5;
pulsePts.push(pt(B3.x + (C3.x - B3.x) * f6, B3.y + (C3.y - B3.y) * f6));
pulseC.push(rgba(TH.a3, 0.75 * (1 - q3 / 5)));
pulseR.push(1.9 - q3 * 0.22); } } } }
if (flashPts.length) sets.push({ label: 'neuronFlash', data: flashPts, showLine: false, pointStyle: 'circle', pointRadius: flashR, pointBackgroundColor: flashC, pointBorderWidth: 0, order: 30 });
if (glowSeg.length) sets.push({ label: 'edgeGlow', data: glowSeg, showLine: true, fill: false, spanGaps: false, borderColor: rgba(TH.a3, 0.30), borderWidth: 1.4, pointRadius: 0, tension: 0, order: 25 });
if (pulsePts.length) sets.push({ label: 'synapsePulse', data: pulsePts, showLine: false, pointStyle: 'circle', pointRadius: pulseR, pointBackgroundColor: pulseC, pointBorderWidth: 0, order: 32 });
var tick24 = [], q6;
for (q6 = 0; q6 < lt24.length; q6++) {
var r6 = lt24[q6], x6 = 8.7 + q6 * (4.6 / 23), up6 = r6 >= 0, ln6 = 0.24 + 0.60 * clamp01(Math.abs(r6) / 2);
tick24.push(pt(x6, -6.55)); tick24.push(pt(x6, -6.55 + (up6 ? ln6 : -ln6))); tick24.push({ x: null, y: null }); }
sets.push({ label: 'tickerBase', data: [pt(8.6, -6.55), pt(13.4, -6.55)], showLine: true, fill: false, borderColor: rgba(TH.dim, 0.30), borderWidth: 1, borderDash: [2, 4], borderDashOffset: march(85), pointRadius: 0, tension: 0, order: 4 });
if (tick24.length) sets.push({ label: 'rTicker', data: tick24, showLine: true, fill: false, spanGaps: false, borderColor: rgba(TH.txt, 0.40), borderWidth: 1.4, pointRadius: 0, tension: 0, order: 6 });
var wTop = [], wBot = [], xW3;
for (xW3 = 0; xW3 <= 224; xW3++) {
var xv = -13.4 + xW3 * (26.8 / 224);
wTop.push(pt(xv, 6.95 + 0.30 * Math.sin(0.55 * xv + CLOCK.slow * 0.55) * (0.55 + 0.45 * Math.sin(0.19 * xv - CLOCK.slow * 0.30))));
wBot.push(pt(xv, -6.95 + 0.30 * Math.sin(0.47 * xv - CLOCK.slow * 0.48) * (0.55 + 0.45 * Math.sin(0.17 * xv + CLOCK.slow * 0.34)))); }
sets.push({ label: 'beatTop', data: wTop, showLine: true, fill: false, borderColor: rgba(TH.warn, 0.22), borderWidth: 1.1, pointRadius: 0, tension: 0, order: 2 });
sets.push({ label: 'beatBottom', data: wBot, showLine: true, fill: false, borderColor: rgba(TH.a1, 0.22), borderWidth: 1.1, pointRadius: 0, tension: 0, order: 2 });
var sx2 = -13.4 + travel(26.8, 2.4);
sets.push({ label: 'scanBeam', data: [pt(sx2, -7.3), pt(sx2, 7.3)], showLine: true, fill: false, borderColor: rgba('#FFFFFF', 0.09), borderWidth: 6, pointRadius: 0, tension: 0, order: 1 });
sets.push({ label: 'scanHead', data: [pt(sx2, 0)], showLine: false, pointStyle: 'circle', pointRadius: [2.2], pointBackgroundColor: [rgba('#FFFFFF', 0.35)], pointBorderWidth: 0, order: 1 });
var tickM = Math.floor(CLOCK.render / 140), motes = [], mC = [], mR = [], qM;
for (qM = 0; qM < 12; qM++) {
motes.push(pt(-13.2 + hashRand(qM * 97 + tickM) * 26.4, -6.6 + hashRand(qM * 31 + tickM * 3) * 13.2));
mC.push(rgba(TH.a3, 0.08 + 0.10 * hashRand(qM + tickM)));
mR.push(1.1 + 1.5 * hashRand(qM * 7 + tickM)); }
sets.push({ label: 'motes', data: motes, showLine: false, pointStyle: 'circle', pointRadius: mR, pointBackgroundColor: mC, pointBorderWidth: 0, order: 2 });
var psi = CLOCK.slow * 0.45, tail = [], qT;
sets.push({ label: 'radarSweep', data: [P(1.0, psi), P(7.45, psi)], showLine: true, fill: false, borderColor: rgba('#FFFFFF', 0.12), borderWidth: 3.5, pointRadius: 0, tension: 0, order: 1 });
for (qT = 0; qT <= 14; qT++) tail.push(P(7.45 - qT * 0.16, psi - qT * 0.045));
sets.push({ label: 'radarTail', data: tail, showLine: true, fill: false, borderColor: rgba('#FFFFFF', 0.05), borderWidth: 5, pointRadius: 0, tension: 0, order: 1 });
return { sets: sets, xmin: -13.8, xmax: 13.8, ymin: -7.8, ymax: 7.8, spin: (CLOCK.slow * 57.3) % 360, centroid: centroidBin, coh: cohN }; }
if (i_manifold !== 'Off') {
var VW = i_manifold === 'Signal Raster' ? viewSignalRaster() : viewHarmonicRosette();
var isRos = i_manifold !== 'Signal Raster';
var vTitle = isRos ? '⚰ PVK — HARMONIC ROSETTE · POLAR CONFLUENCE ENGINE' : '⚰ PVK — SIGNAL RASTER · EVENT WEB → CONFIRMED FIRES';
var vLegend = isRos
? 'core arcs = quality & chop · guilloché = coherence rosette · spiral arms = causal history (valley out · peak in · bias between) · spokes = phase-sector energy · bezel = impulse FFT · left = phase-tide full-height scalogram · right = synapse neural lattice'
: 'rows = pivot · delayed fractal · COG · value-area · FIRE · newest-first webbing · comet lights active pathways';
var vAxis = isRos
? 'polar confluence instrument · 2.15-loop causal spiral · 24 phase sectors · radix-2 FFT N=128 · centroid bin ' + (isFinite(VW.centroid) ? VW.centroid.toFixed(1) : '—') + ' · coherence ' + (isFinite(VW.coh) ? VW.coh.toFixed(2) : '—') + ' · rotor ' + VW.spin.toFixed(0) + '° · phase tide 16×8 scalogram · synapse lattice 21 neurons · plane geometry only — zero 3D projection'
: 'x = bars back from present · 160-bar field · event web cap 1500';
mRows.push(panelHeader(vTitle)); mRows.push(chartRow(VW.sets,VW.xmin,VW.xmax,VW.ymin,VW.ymax,MANIFOLD_H)); mRows.push(legendRow(vLegend)); mRows.push(axisRow(vAxis));}
paint_overlay('PVK Manifold', { position: 'bottom_left', order: 'above_all' }, { background: i_manifold !== 'Off' ? rgba(TH.bg, 0.96) : 'rgba(0,0,0,0)', border: i_manifold !== 'Off' ? '1px solid ' + rgba(TH.a1, 0.35) : '0px solid transparent', borderRadius: 8, width: i_manifold !== 'Off' ? '100%' : 1, rows: i_manifold !== 'Off' ? mRows : [] });
// ==============================================================================================
// ALERTS
// ==============================================================================================
var exitConf = make(n, false);
for (i = 0; i < n - 1; i++) exitConf[i] = exitSigArr[i];
register_signal(longConf, 'PVK: Long — swing or value-area break, bias up');
register_signal(shortConf, 'PVK: Short — swing or value-area break, bias down');
register_signal(exitConf, 'PVK: Exit — swing or value-area trade closed');