#!/usr/bin/env python3 """Bake site/index.html — the static results page for aura-quadriga Arc 1. Reads the runs/ registry (campaign_runs.jsonl, families.jsonl, persisted r_equity traces) and emits a fully self-contained HTML page: no external requests, all charts are build-time inline SVG. Style contract (aura engine rule, issue #209): the page's first ") add("") add('
') # ---- 1 · hero ---------------------------------------------------------- # NB: a
, not
— aura.css styles the bare header element for # the runtime viewer shell (flex row, overflow hidden), which would # swallow the stats grid and callout. ger_a, ger_b = mc_by[("GER40", "A")], mc_by[("GER40", "B")] add('
') add("

aura-quadriga — Arc 1

") add('

Does an EMA trend regime reshape an H1 breakout’s ' "R-distribution?

") add('
') add(f'engine {engine_commit}') add(f'project {esc(project_commit)}') add('2018-01 → 2026-06 · m1') add('GER40 · US500 · EURUSD · XAUUSD') add('gross R — no cost model') add(f'seed {main["seed"]}') add("
") add('
') add(f'
{len(main["cells"])}
' '
cells — 2 strategies × 4 instruments
') add(f'
{n_windows}
' '
walk-forward rolls per cell (90 d IS / 30 d OOS)
') add(f'
{fc(pooled_trades["A"])} / {fc(pooled_trades["B"])}
' '
pooled OOS trades — bo_h1 / bo_h1_trend
') add(f'
{fp(best_p)}
' f'
best-cell P(E[R]≤0) — {best_p_label}
') add(f'
{sign_agree["A"]} / {n_instr}
' '
cross-instrument sign agreement, either variant
') worst_key = min(worst_case, key=worst_case.get) add(f'
{fr(worst_case[worst_key], 2)}
' f'
worst generalization floor (E[R], ' f'{var_by_key[worst_key]["name"]})
') add("
") add('
The gate reshapes the R-distribution — per instrument, ' "not uniformly. On the pooled walk-forward OOS bootstrap, GER40 flips sign: mean " f'E[R] {fr(ger_a["e_r"]["mean"], 3)} (P(E[R]≤0) = {fp(ger_a["prob_le_zero"])}) ' f'without the gate, {fr(ger_b["e_r"]["mean"], 3)} ({fp(ger_b["prob_le_zero"])}) with it. ' "US500 improves, EURUSD deteriorates sharply, XAUUSD stays roughly flat. That " "per-instrument conditionality — B reshapes A, instrument-dependently — is the finding " "of this arc. No variant survives as a deployable edge: the best cell still shows " f"P(E[R]≤0) = {fp(best_p)} across eight unadjusted comparisons, the " "cross-instrument worst case is deeply negative for both variants " f'({fr(worst_case["A"], 2)} plain, {fr(worst_case["B"], 2)} gated), sign agreement is ' f'{sign_agree["A"]}/{n_instr} — and every figure on this page is gross of costs.
') add("
") # ---- 2 · the experiment -------------------------------------------------- add(section_h(2, "The experiment", "Two blueprint variants over an identical data / window / risk / seed matrix; " "the comparison is per cell. Risk regime Vol{length: 3, k: 2.0} — " "the protective stop defines 1 R. No cost block: net == gross " "everywhere.")) add('
') add('

bo_h1 variant A — base signal

') add(f'
{VARIANTS[0]["cid"]}
') add("

H1-resampled donchian-style breakout: RollingMax/Min(24) over Delay(1) high/low, " "close vs channel (Gt), an SR-latch pair holds the breakout state; " "bias = latch_long − latch_short ∈ {−1, 0, +1}.

") add("
h1.period_minutes = 60      prev_high/low.lag = 1\n"
        "channel_hi.length = 24      channel_lo.length = 24\n"
        "risk = Vol { length: 3, k: 2.0 }   (shared)
") add('

bo_h1_trend variant B — gated

') add(f'
{VARIANTS[1]["cid"]}
') add("

The same breakout, gated post-latch by an EMA(12) vs EMA(48) trend-regime latch " "(Mul per side): a held breakout is suppressed the moment the regime flips against " "it. Same channel params, same risk, same seed.

") add("
ema_fast.length = 12        ema_slow.length = 48\n"
        "channel_hi.length = 24      channel_lo.length = 24\n"
        "risk = Vol { length: 3, k: 2.0 }   (shared)
") add("
") add('
') add(f'
std::sweep {grid_cells} trials' "

3×3 channel grid (hi/lo ∈ {24, 48, 96}), argmax on sqn_normalized, " "deflated (1 000 resamples, block 5).

") add('') add(f'
std::walk_forward {n_windows} rolls' "

Rolling 90-day IS refit over the same axes, 30-day OOS, 30-day step; " "argmax on sqn_normalized per roll.

") add('') add(f'
std::monte_carlo {fc(mc_rows[0]["n_resamples"])} × ' f'block {mc_rows[0]["block_len"]}' "

Block bootstrap of the R-series pooled over all walk-forward OOS trades: " "E[R] quantiles and P(E[R]≤0) per cell.

") add('') add('
std::generalize expectancy_r' "

Cross-instrument floor: apply each instrument’s winner to the other three; " "worst case and sign agreement.

") add("
") add(f'

Screen campaign {main["campaign"][:8]}… runs the full ' f'pipeline; curves campaign {curves["campaign"][:8]}… re-runs both variants ' "at default params (channel 24/24, EMA 12/48), selection-free, persisting equity / exposure / " "r_equity taps for sections 4 and 6.

") # ---- 3 · R-distribution --------------------------------------------------- add(section_h(3, "R-distribution — pooled-OOS bootstrap", f'{fc(mc_rows[0]["n_resamples"])}-resample block-{mc_rows[0]["block_len"]} ' "bootstrap of per-trade R, pooled over each cell’s " f"{n_windows} walk-forward OOS windows (screen campaign). Whisker p5–p95, " "box p25–p75, tick median, dot mean. The dashed line is E[R] = 0 — " "mass to its right is what an edge would look like. Gross R.")) add(legend_html()) add(f'
{interval_plot(mc_rows)}
') add('

GER40 is the reshaping case: the gate moves essentially the whole ' "interval across zero. EURUSD moves the other way — the gate makes it decisively worse. " "A conditioner, not an improvement.

") # ---- 4 · per-instrument tables --------------------------------------------- add(section_h(4, "Per-instrument metrics — default params, full window", "Single members from the curves campaign (channel 24/24, EMA 12/48, no " "selection), full 2018–2026 window, gross R. This isolates the " "gate’s effect from the screen’s parameter selection. The shape is " "classic vol-stop breakout: ~10 % hit rate, average win an order of " "magnitude larger than average loss.")) add('
') for inst in INSTRUMENTS: a, b = cur[(inst, "A")], cur[(inst, "B")] add(f'

{inst} default params · gross

') add('') def row(label, va, vb, fmt, signed=False): def td(v, txt): cls = "" if signed: cls = ' class="pos"' if v > 0 else (' class="neg"' if v < 0 else "") return f"{txt}" add(f"{td(va, fmt(va))}{td(vb, fmt(vb))}") row("expectancy_r", a["expectancy_r"], b["expectancy_r"], lambda v: fr(v), signed=True) row("win_rate", a["win_rate"], b["win_rate"], lambda v: f"{fnum(v * 100)} %") row("profit_factor", a["profit_factor"], b["profit_factor"], lambda v: fnum(v, 3)) row("sqn_normalized", a["sqn_normalized"], b["sqn_normalized"], lambda v: fr(v), signed=True) row("max_r_drawdown", a["max_r_drawdown"], b["max_r_drawdown"], lambda v: f"{fnum(v)} R") row("n_trades", a["n_trades"], b["n_trades"], lambda v: fc(v)) row("bias_sign_flips", a["bias_sign_flips"], b["bias_sign_flips"], lambda v: fc(v)) add("
bo_h1bo_h1_trend
{label}
") add(tercile_svg(inst, {"A": a["conviction_terciles_r"], "B": b["conviction_terciles_r"]}, terc_max)) add('

conviction terciles — mean R per trade by tercile; within each ' "group: left bar bo_h1, right bar bo_h1_trend; green/red by sign

") add("
") add("
") # ---- 5 · walk-forward ------------------------------------------------------- max_distinct = max(wf_distinct.values()) add(section_h(5, "Walk-forward — stitched OOS R", f"Cumulative out-of-sample R across the {n_windows} rolled windows " "(per-window expectancy × trades, refit each roll). The IS " "winner is unstable: in every one of the eight cells the per-roll argmax " f"visits all {max_distinct} cells of the 3×3 channel grid at least once " "over the 100 rolls — the full-window screen winner is not a stable optimum, " "which is exactly what the deflated sweep scores already hinted at.")) add(legend_html()) add('
') for inst in INSTRUMENTS: add(f'

{inst} OOS, refit per roll

') add(wf_svg(inst, wf_series[inst])) add('' "") for var in VARIANTS: s = wf_stats[(inst, var["key"])] cls = "pos" if s["total"] > 0 else "neg" add(f'' f'' f'') add("
windowspositivestitched R
{var["name"]}{s["windows"]}{fnum(s["pos_pct"], 0)} %{fr(s["total"], 1)}
") add("
") # ---- 6 · full-period R-curves ----------------------------------------------- add(section_h(6, "Full-period R-curves — default params", "Cumulative realized + unrealized R from the persisted r_equity " "taps (curves campaign, default params — gate-effect isolation, no selection), " f"~2.6 M m1 points per member decimated to ≤{fc(ENVELOPE_BUCKETS)} " "min–max envelope buckets: the band is the per-bucket min–max range " "(drawdown spikes survive the decimation), the line its midpoint. Gross R.")) add(legend_html()) add('
') for inst in INSTRUMENTS: add(f'

{inst} r_equity · full window

') add(envelope_svg(inst, envelopes[inst], finals[inst])) add("
") add("
") # ---- 7 · artifacts & reproduce ------------------------------------------------ add(section_h(7, "Artifacts & reproduce", "Everything on this page is derived from the runs/ registry; every artifact " "is content-addressed. Same documents, same seed → same numbers.")) add('
' "aura-quadriga — reproduce
")
    add(f'$ aura campaign run research/campaign-arc1.json'
        f'        # screen → {main["campaign"][:8]}…\n')
    add(f'$ aura campaign run research/campaign-arc1-curves.json'
        f' # curves → {curves["campaign"][:8]}…\n')
    add('$ aura runs families'
        '                                    # registry index\n')
    add(f'$ aura reproduce '
        f'{stage(cell_of(main, VARIANTS[0]["cid"], "GER40"), "std::walk_forward")["family_id"]}'
        '   # any family, bit-identical\n')
    add(f'$ aura chart {curves["trace_name"]} '
        '--tap r_equity              # interactive trace viewer')
    add("
") add('
') add('

Blueprints

' f'
bo_h1
{VARIANTS[0]["cid"]}
' f'
bo_h1_trend
{VARIANTS[1]["cid"]}
') add('

Processes

' f'
screen-wf-mc-generalize
{main["process"]}
' f'
curves-sweep
{curves["process"]}
') add('

Campaigns

' f'
arc1-breakout-trend
{main["campaign"]}
' f'
arc1-curves
{curves["campaign"]}
') add("
") add(f'

generated by site/build.py from the runs/ registry · ' f'engine {engine_commit} · project {esc(project_commit)} · {PAGE_DATE}

') add("
") # .wrap add("") add("") OUT_PATH.write_text("".join(h)) print(f"wrote {OUT_PATH} ({OUT_PATH.stat().st_size:,} bytes)") def section_h(n, title, sub): return (f'
0{n}

{title}

' f'

{sub}

') # -------------------------------------------------------------------------- # page-specific CSS — tokens only (var(--x)) + the pinned series palette # -------------------------------------------------------------------------- PAGE_CSS = """ /* page-specific rules — consume aura.css tokens only; series colors are the * established palette slots #89b4fa (A) and #cba6f7 (B). */ .wrap { padding-top: 44px; padding-bottom: 28px; } .hero h1 { margin: 0 0 4px; font-size: 30px; letter-spacing: -0.5px; } .subtitle { margin: 0 0 18px; color: var(--dim); font-size: 16.5px; } .badges { margin: 14px 0 4px; line-height: 2.2; } .badge.warn { color: var(--yellow); border-color: var(--yellow); background: rgba(249,226,175,.08); } .sec { margin-top: 54px; } .sec .kick { font-family: var(--mono); font-size: 11.5px; color: var(--dim2); letter-spacing: 2px; } .sec h2 { margin: 2px 0 8px; font-size: 21px; } .sec .sub { margin: 0; color: var(--dim); font-size: 14px; max-width: 74ch; } .fnote { color: var(--dim); font-size: 13px; max-width: 74ch; } .fnote.foot { margin-top: 40px; border-top: 1px solid var(--border); padding-top: 14px; font-family: var(--mono); font-size: 12px; max-width: none; } .mini { color: var(--dim2); font-size: 11.5px; font-family: var(--mono); margin: 4px 0 0; } .chartbox { margin: 14px 0; } .legend { display: flex; gap: 22px; margin: 16px 0 4px; font-family: var(--mono); font-size: 12.5px; color: var(--fg); } .legend .lk { color: var(--dim); } .legend .sw { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-right: 7px; vertical-align: -1px; } .legend .swA { background: #89b4fa; } .legend .swB { background: #cba6f7; } /* chart internals */ .chart { display: block; } .chart text { font-family: var(--mono); } .chart .tick { fill: var(--dim); font-size: 10.5px; } .chart .grid { stroke: var(--border); stroke-width: 1; } .chart .zero { stroke: var(--dim2); stroke-width: 1; } .chart .zero-d { stroke: var(--dim2); stroke-width: 1; stroke-dasharray: 4 4; } .chart .base { stroke: var(--border2); stroke-width: 1; } .chart .inst { fill: var(--accent); font-size: 13px; } .chart .vlab { fill: var(--dim); font-size: 11px; } .chart .ro { font-size: 11.5px; } .chart .ro-p { fill: var(--fg); } .chart .ro-n { fill: var(--dim); } .chart .endlab { fill: var(--fg); font-size: 11px; } .chart .med { stroke: var(--accent); stroke-width: 2; } .chart .sA { stroke: #89b4fa; } .chart .sB { stroke: #cba6f7; } .chart .fA { fill: #89b4fa; } .chart .fB { fill: #cba6f7; } .chart .wh { stroke-width: 1.5; fill: none; } .chart .box { fill: var(--bg3); stroke-width: 1; } .chart .mean { stroke: var(--bg); stroke-width: 2; } .chart .ln { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; } .chart .mid { fill: none; stroke-width: 1.6; stroke-linejoin: round; } .chart .band { stroke: none; fill-opacity: 0.16; } .chart .pb { fill: var(--green); } .chart .nb { fill: var(--red); } .chart [data-tip] { cursor: default; } .card .chart { margin-top: 6px; } @media (max-width: 640px) { .legend { flex-wrap: wrap; gap: 10px; } } """ # hover tooltips — reuses aura.css's #tip shell styles; enhancement only, # every value is also present as text or in a table. TIP_JS = ("(function(){var t=document.getElementById('tip');" "document.addEventListener('pointerover',function(e){" "var m=e.target.closest&&e.target.closest('[data-tip]');" "if(!m){t.style.display='none';return;}" "t.textContent=m.getAttribute('data-tip');t.style.display='block';});" "document.addEventListener('pointermove',function(e){" "if(t.style.display!=='block')return;" "var x=Math.min(e.clientX+14,innerWidth-t.offsetWidth-8)," "y=Math.min(e.clientY+18,innerHeight-t.offsetHeight-8);" "t.style.left=x+'px';t.style.top=y+'px';});})();") if __name__ == "__main__": build()