// ============================================================================
// Project : anomaly-atlas
// File : web/lib/charts.js
// Purpose : Server-rendered SVG figures built from experiment results.json
// Author : Simon-Pierre Boucher
// Contact : contact@spboucher.ai
// Data src : hfmarketdata.io (sole data source)
// Created : 2026-08-12
// Modified : 2026-08-12
// Platform : macOS / Apple Silicon (arm64) — Node.js (deployed on MacLustr)
// License : All rights reserved (research code)
// ============================================================================
"use strict";
/* Figures follow the dataviz method: form by job, validated palette
* (slots: blue #2a78d6, orange #eb6834 — all-pairs PASS on #fffdf9; gray
* #898781 is de-emphasis ink, not a series), thin marks with 2px surface
* rings, hairline grid, text in ink tokens (never series color), native
*
tooltips, selective direct labels. Every figure is generated from
* the latest committed results.json — never hand-typed numbers. */
const path = require("path");
const BLUE = "#2a78d6";
const ORANGE = "#eb6834";
const GRAY = "#898781";
const SURFACE = "#fffdf9";
const GRID = "#e5e1d6";
const INK = "#1a1c20";
const MUTED = "#5d6167";
function esc(s) {
return String(s).replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c]));
}
function latestResults(C, experiment) {
const run = C.listResultRuns().find((r) => r.experiment === experiment);
if (!run) return null;
const data = C.readJson(path.posix.join(run.rel, "results.json"));
return data ? { data, run: run.timestamp } : null;
}
function fig(svg, caption) {
return `${svg}${caption}`;
}
const AXIS_TXT = `font-size="11" fill="${MUTED}"`;
// ------------------------------------------------------------------- expB
function expBScatterPanel(rows, key, title, W, H, xLabelBottom) {
const ML = 46, MR = 14, MT = 26, MB = 34;
const iw = W - ML - MR, ih = H - MT - MB;
const ys = rows.map((r) => r[key]);
const ymin = Math.min(...ys, 0), ymax = Math.max(...ys, 0);
const pad = (ymax - ymin) * 0.12 || 0.01;
const y0 = ymin - pad, y1 = ymax + pad;
const xp = (v) => ML + v * iw;
const yp = (v) => MT + ih - ((v - y0) / (y1 - y0)) * ih;
let g = `${esc(title)}`;
for (const t of [y0 + pad, 0, y1 - pad]) {
const v = Math.round(t * 1000) / 1000;
g += ``;
// skip an extreme tick label that would collide with the zero label
if (v === 0 || Math.abs(yp(v) - yp(0)) > 14) {
g += `${v}`;
}
}
g += ``;
for (const t of [0, 0.5, 1]) {
g += `${t}`;
}
if (xLabelBottom) g += `staleness (share of RTH minutes without a fresh print)`;
const extreme = rows.reduce((a, b) => (Math.abs(b[key]) > Math.abs(a[key]) ? b : a), rows[0]);
for (const r of rows) {
g += `${esc(r.ticker)} — staleness ${r.staleness}, ${esc(title)} ${r[key]}`;
}
for (const r of [extreme]) {
g += `${esc(r.ticker)}`;
}
return g;
}
function expBFigure(C) {
const res = latestResults(C, "expB_artifact_baselines");
if (!res) return "";
const rows = Object.entries(res.data.per_ticker || {})
.map(([ticker, m]) => ({ ticker, ...m }))
.filter((r) => Number.isFinite(r.staleness));
if (rows.length < 5) return "";
const W = 760, H = 250;
const half = 372;
const svg = ``;
return fig(svg, `expB — artifact null levels, one dot per ticker (Q1 2024, RTH 1min). ` +
`Bounce pushes AC1 negative and LOCF joins make SPY spuriously lead, both in proportion to staleness. ` +
`Run ${esc(res.run)}, regenerated from results.json.`);
}
// ------------------------------------------------------------------- expC
function expCFigure(C) {
const res = latestResults(C, "expC_reversion_scan");
if (!res) return "";
const cells = (res.data.cells || []).filter((c) => Number.isFinite(c.vr30) && Number.isFinite(c.ac1));
if (!cells.length) return "";
for (const c of cells) c.vr30_excess = c.vr30 - (1 + 2 * c.ac1 * (1 - 1 / 30));
const rowsDef = [];
for (const tf of ["1day", "30min", "5min", "1min"]) {
for (const per of ["2000-2007", "2008-2015", "2014-2015"]) {
const cs = cells.filter((c) => c.timeframe === tf && c.period === per);
if (cs.length) rowsDef.push({ label: `${tf} · ${per}`, cells: cs });
}
}
const W = 760, ML = 150, MR = 16, MT = 30, RH = 30, MB = 46;
const H = MT + rowsDef.length * RH + MB;
const xmin = -0.32, xmax = 0.16;
const iw = W - ML - MR;
const xp = (v) => ML + ((Math.max(xmin, Math.min(xmax, v)) - xmin) / (xmax - xmin)) * iw;
let g = "";
for (const t of [-0.3, -0.2, -0.1, 0, 0.1]) {
g += `
${t}`;
}
g += `VR(30) excess over the MA(1)-consistent null · negative = multi-lag reversion beyond any lag-1 effect`;
const labeled = new Set(
cells.filter((c) => c.fdr_vr30 && c.vr30_excess < -0.05)
.sort((a, b) => a.vr30_excess - b.vr30_excess).slice(0, 3).map((c) => c.ticker + c.period + c.timeframe));
rowsDef.forEach((row, i) => {
const y = MT + i * RH + RH / 2;
g += `${esc(row.label)}`;
for (const c of row.cells) {
const surv = c.fdr_vr30 && c.vr30_excess < -0.05;
const tip = `${c.ticker} ${c.timeframe} ${c.period}: VR30 ${c.vr30}, excess ${c.vr30_excess.toFixed(3)}${surv ? " (FDR survivor)" : ""}`;
g += surv
? `${esc(tip)}`
: `${esc(tip)}`;
if (labeled.has(c.ticker + c.period + c.timeframe)) {
g += `${esc(c.ticker)}`;
}
}
});
const legend = `
FDR survivor (excess < −0.05)
other scan cells (Level 0)`;
const svg = ``;
return fig(svg, `expC — multi-lag reversion triage on TRAIN. One dot per ticker-cell; ` +
`the MA(1)-consistent null absorbs all lag-1 effects (bounce included); values beyond the axis range ` +
`pile at its edge. Run ${esc(res.run)}, regenerated from results.json.`);
}
// ------------------------------------------------------------------- expD
function expDFigure(C) {
const res = latestResults(C, "expD_leadlag_scan");
if (!res) return "";
const out = [];
for (const window of ["2014-2015", "2006-2007"]) {
const rows = (res.data.pairs || [])
.filter((p) => p.window === window && p.raw_xcorr && p.fresh_xcorr &&
Number.isFinite(p.raw_xcorr["1"]) && Number.isFinite(p.fresh_xcorr["1"]))
.sort((a, b) => (a.bucket + "").localeCompare(b.bucket + "") || b.raw_xcorr["1"] - a.raw_xcorr["1"]);
if (!rows.length) continue;
const W = 760, ML = 170, MR = 16, MT = 34, RH = 19, MB = 44;
const H = MT + rows.length * RH + MB;
const vals = rows.flatMap((p) => [p.raw_xcorr["1"], p.fresh_xcorr["1"]]);
const xmin = Math.min(...vals, 0) - 0.01, xmax = Math.max(...vals, 0) + 0.01;
const iw = W - ML - MR;
const xp = (v) => ML + ((v - xmin) / (xmax - xmin)) * iw;
let g = "";
const ticks = [0, 0.05, 0.1].filter((t) => t >= xmin && t <= xmax);
for (const t of ticks) {
g += `
${t}`;
}
g += `cross-correlation at +1 min (leader → follower)`;
let lastBucket = "";
rows.forEach((p, i) => {
const y = MT + i * RH + RH / 2;
if (p.bucket !== lastBucket) {
lastBucket = p.bucket;
g += `${esc(p.bucket.toUpperCase())}`;
}
const raw = p.raw_xcorr["1"], fresh = p.fresh_xcorr["1"];
const surv = p.fdr_fresh_p1 || p["fdr_fresh_+1"];
g += `${esc(p.pair)}
${esc(p.pair)} raw LOCF join: ${raw}
${esc(p.pair)} both-fresh: ${fresh}${surv ? " (FDR survivor)" : ""}`;
});
const legend = `
raw LOCF join (artifact included)
both-fresh (synchronized)`;
out.push(fig(
``,
`expD — lead at +1 min per pair, ${window}: the gap between the raw join and the both-fresh ` +
`subsample is the non-synchronicity artifact (T3), measured. Run ${esc(res.run)}, regenerated from results.json.`
));
}
return out.join("");
}
// ------------------------------------------------------------------- expE
function expEFigure(C) {
const res = latestResults(C, "expE_calendar_scan");
if (!res) return "";
const t = res.data.calendar_tests || {};
const obs = t.observed_bp || {}, band = t.perm_band95_bp || {};
const names = Object.keys(obs);
if (!names.length) return "";
let out = "";
// Panel 1 — observed effect vs permuted-calendar 95% band (interval + dot)
{
const W = 760, ML = 130, MR = 20, MT = 30, RH = 30, MB = 44;
const H = MT + names.length * RH + MB;
const vals = names.flatMap((n) => [obs[n], ...(band[n] || [])]);
const xmin = Math.min(...vals) - 2, xmax = Math.max(...vals) + 2;
const iw = W - ML - MR;
const xp = (v) => ML + ((v - xmin) / (xmax - xmin)) * iw;
let g = "";
for (const tick of [-20, -10, 0, 10, 20].filter((v) => v > xmin && v < xmax)) {
g += `
${tick}`;
}
g += `mean daily SPY return in class minus overall mean (bp) · gray bar = permuted-calendar 95% band`;
names.forEach((n, i) => {
const y = MT + i * RH + RH / 2;
const [lo, hi] = band[n] || [0, 0];
g += `${esc(n.replace(/_/g, " "))}
${esc(n)}: observed ${obs[n]} bp — marginal p ${t.p_marginal?.[n]}, family-wise p ${t.p_familywise?.[n]}`;
});
out += fig(
``,
`expE — all 8 pre-declared calendar tests on SPY (train 2000–2016): every observed effect (dot) sits ` +
`inside its permuted-calendar 95% band (bar). Nothing survives; the last-survivor turn-of-month included. ` +
`Run ${esc(res.run)}, regenerated from results.json.`
);
}
// Panel 2 — H20 intraday profile: |return| and EDGE spread by half-hour
const prof = (res.data.h20_intraday_profile || {}).profile || [];
if (prof.length) {
const W = 760, ML = 52, MR = 16, MT = 34, MB = 46, H = 260;
const iw = W - ML - MR, ih = H - MT - MB;
const ys = prof.flatMap((p) => [p.median_abs_1min_ret_bp, p.median_edge_spread_bp]).filter(Number.isFinite);
const ymax = Math.max(...ys) * 1.12;
const xp = (i) => ML + (i / (prof.length - 1)) * iw;
const yp = (v) => MT + ih - (v / ymax) * ih;
let g = "";
for (const tick of [0, 2, 4, 6].filter((v) => v <= ymax)) {
g += `
${tick}`;
}
prof.forEach((p, i) => {
if (i % 2 === 0) g += `${esc(p.bucket)}`;
});
const series = [
["median_abs_1min_ret_bp", BLUE, "median |1min return|"],
["median_edge_spread_bp", ORANGE, "median EDGE spread"],
];
for (const [key, color, label] of series) {
const pts = prof.map((p, i) => [xp(i), yp(p[key])]).filter((q) => Number.isFinite(q[1]));
g += ``;
prof.forEach((p, i) => {
if (Number.isFinite(p[key])) g += `${esc(p.bucket)} — ${esc(label)}: ${p[key]} bp`;
});
}
g += `
median |1min return| (bp)
median EDGE spread (bp)
half-hour bucket (RTH) · liquid 12, 1min, 2014–2015`;
out += fig(
``,
`expE / H20 — the intraday artifact profile (taxonomy input): volatility is U-shaped (6.6 bp at the open, ` +
`2.4 midday, 2.9 at the close); the spread declines monotonically (2.8 → 1.2 bp). Any "first-30-minutes" return ` +
`claim faces 2–3× the midday artifact level. Run ${esc(res.run)}.`
);
}
return out;
}
// ------------------------------------------------------------------- expF
function expFFigure(C) {
const res = latestResults(C, "expF_multiple_testing");
if (!res) return "";
const f = res.data.funnel || {};
const stages = [
["searched universe", f.universe_rules, "all scanned cells/pairs/classes (×2 signs)"],
["naive |t| > 1.96", f.naive_t196, "uncorrected in-sample t-test"],
["BH-FDR 5%", f.fdr_survivors, "false-discovery-rate correction"],
["Hansen SPA step-1", (f.spa_step1_survivors || []).length, "data-snooping correction"],
].filter((s) => Number.isFinite(s[1]));
if (stages.length < 3) return "";
const W = 760, ML = 235, MR = 90, MT = 26, RH = 46, MB = 40;
const H = MT + stages.length * RH + MB;
const iw = W - ML - MR;
const max = stages[0][1];
let g = "";
stages.forEach(([label, n, sub], i) => {
const y = MT + i * RH;
const w = Math.max(2, (n / max) * iw);
g += `${esc(label)}
${esc(sub)}
${esc(label)}: ${n} rules (${Math.round((n / max) * 100)}%)
${n} (${Math.round((n / max) * 100)}%)`;
});
g += `Survivors are gross and artifact-laden — costs (expG) are the next layer.`;
const svg = ``;
return fig(svg, `expF — the survival curve: what fraction of the searched rule universe survives each ` +
`statistical-correction layer on TRAIN. Statistical correction fixes the search, not the mechanism. ` +
`Run ${esc(res.run)}, regenerated from results.json.`);
}
// ------------------------------------------------------------------- expG
function expGFigure(C) {
const res = latestResults(C, "expG_cost_frontier");
if (!res) return "";
const rows = (res.data.items || [])
.filter((i) => Number.isFinite(i.kappa_star) && i.kappa_star > 0)
.sort((a, b) => b.kappa_star - a.kappa_star);
if (rows.length < 5) return "";
const W = 760, ML = 235, MR = 20, MT = 40, RH = 19, MB = 46;
const H = MT + rows.length * RH + MB;
const iw = W - ML - MR;
const xmin = Math.log10(0.001), xmax = Math.log10(2);
const xp = (v) => ML + ((Math.log10(Math.max(v, 0.001)) - xmin) / (xmax - xmin)) * iw;
let g = "";
for (const [t, label] of [[0.001, "0.001"], [0.01, "0.01"], [0.1, "0.1"], [1, "1"]]) {
g += `
${label}`;
}
for (const [t, label] of [[0.25, "patient execution"], [1.0, "full half-spread"]]) {
g += `
${label}`;
}
g += `breakeven cost multiplier κ* (× half-spread paid per trade, log scale) — right of a line = survives that cost level`;
rows.forEach((r, i) => {
const y = MT + i * RH + RH / 2;
g += `${esc(r.rule)}
${esc(r.rule)} — κ* = ${r.kappa_star} · gross ${r.gross_mean_daily_bp} bp/day · turnover ${r.turnover_per_day}/day · half-spread ${r.half_spread_bp} bp`;
});
const svg = ``;
return fig(svg, `expG — the cost frontier: every rule that beat the artifact nulls AND the search correction ` +
`dies when it must pay a fraction of its own half-spread (median κ* = ${res.data.summary?.kappa_star_median}). ` +
`Run ${esc(res.run)}, regenerated from results.json.`);
}
// ------------------------------------------------------------------- expH
function expHFigure(C) {
const res = latestResults(C, "expH_oos_stability");
if (!res) return "";
const s = res.data.summary || {};
const fam = s.daily_family_median_vr30_excess || {};
const rows = [
["daily reversal family (median VR30 excess)", fam.train_2008_2015, fam.validation, "alpha"],
["ES→SPY cross-serial corr (fresh, −1 min)", -0.032, s["es_spy_val_fresh_-1"], "alpha"],
["SPX→SPY staleness lead (+1 min)", 0.132, s["spx_spy_val_fresh_-1"], "artifact"],
].filter((r) => Number.isFinite(r[1]) && Number.isFinite(r[2]));
if (rows.length < 2) return "";
const W = 760, ML = 300, MR = 30, MT = 40, RH = 52, MB = 20;
const H = MT + rows.length * RH + MB;
const iw = W - ML - MR;
let g = `
train (2008–2015)
validation (2016–2021)`;
rows.forEach(([label, tr, va, kind], i) => {
const y = MT + i * RH + RH / 2;
const lim = Math.max(Math.abs(tr), Math.abs(va)) * 1.25;
const xp = (v) => ML + ((v + lim) / (2 * lim)) * iw;
g += `${esc(label)}
${kind === "artifact" ? "known artifact — persists OOS" : "candidate 'alpha' — collapses OOS"}
${esc(label)} — train: ${tr}
${esc(label)} — validation: ${va}
${tr}
${va}`;
});
const svg = ``;
return fig(svg, `expH — the validation split, opened once: every candidate "alpha" collapses toward zero ` +
`out-of-sample while the known staleness artifact persists. Vertical tick = zero (each row has its own ` +
`scale). Run ${esc(res.run)}, regenerated from results.json.`);
}
const BUILDERS = {
expB_artifact_baselines: expBFigure,
expC_reversion_scan: expCFigure,
expD_leadlag_scan: expDFigure,
expE_calendar_scan: expEFigure,
expF_multiple_testing: expFFigure,
expG_cost_frontier: expGFigure,
expH_oos_stability: expHFigure,
};
/** Figures for an experiment page ("" when none apply). */
function figuresFor(experiment, C) {
const builder = BUILDERS[experiment];
try {
return builder ? builder(C) : "";
} catch {
return "";
}
}
/** The most recent experiment figure, for the home page. */
function homeFigure(C) {
for (const exp of ["expH_oos_stability", "expG_cost_frontier", "expF_multiple_testing",
"expE_calendar_scan", "expD_leadlag_scan", "expC_reversion_scan",
"expB_artifact_baselines"]) {
const html = figuresFor(exp, C);
if (html) return { experiment: exp, html: html.split("")[0] + "" };
}
return null;
}
module.exports = { figuresFor, homeFigure };