- fixtures/phase-a-toy.json — scaffold source for `autoresearch start`
Phase A live run: val_vol_r2 metric, daily EUR/USD, HEPA encoder toy
- .gitignore: add runs/ (ephemeral run dirs), metrics.json, embeddings.json,
HEARTBEAT, STATUS.md, pyc/__pycache__/.pytest_cache, eval binary
- untrack metrics.json (was a stale committed sample, now gitignored)
Launch on koala:
python scripts/autoresearch_start.py fixtures/phase-a-toy.json phase-a-toy
op run -- env LITELLM_KEY="$LITELLM_KEY" \
python loop.py --run-dir runs/phase-a-toy --iters 3
Closes the last gate on jepa-fx-risk#11 Phase A (code shipped v1.4.0;
live run pending).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Closes jepa-fx-risk#11 Phase A.
- scripts/autoresearch_start.py: scaffold runs/<rq-id>/ from Council backlog leaf;
fail-closed on non-autoresearch-ready; strips candidate_metric; writes program.md
+ run.json (provenance) + train.py copy. 19 TDD tests.
- loop.py: --run-dir flag redirects STATUS.md / metrics.json / HEARTBEAT / train.py
into the run dir; METRICS_OUT env var passed to train subprocess so it writes
metrics.json to the run dir; heartbeat file written each iter phase; ntfy-on-crash
via NTFY_URL env var (best-effort).
- train.py: METRICS_OUT env var overrides metrics.json path (default unchanged).
Launch: LITELLM_KEY=xxx python loop.py --run-dir runs/rq-04
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two-phase phase-1:
1a. Frozen warmup: head trains on pre-computed embeddings for PHASE1_EPOCHS=200
1b. Joint fine-tune: encoder + head for PHASE1_JOINT_EPOCHS=30 at PHASE1_ENCODER_LR=3e-6
Key design decisions:
- Warm start prevents catastrophic forgetting (PHASE1_JOINT=1 cold-start → -32 R²)
- Normalize live encoder output with FROZEN stats (mu_e/sd_e) so head sees same
embedding distribution it was warmed up on
- head LR reduced 10× in joint phase to prevent head from racing ahead
HPO sweep: 30ep@3e-6=0.3962, 30ep@1e-5=0.3930, 50ep@3e-6=0.3923
Baseline (frozen): 0.3908. New best: phase1_r2=0.3962 (+0.0054 OOS).
New knobs: JEPA_PHASE1_JOINT (default 1), JEPA_PHASE1_JOINT_EPOCHS (default 30),
JEPA_PHASE1_ENCODER_LR (default 3e-6). 4 new tests (tests 15-18). 28/28 pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- prepare_hourly.py: keep O/H/L columns from M1 zips; compute per-hour
hl_range=log(H/L) and ret_intrabar=log(close/open); backward-compat
(falls back to 4-col output only when O/H/L present in input)
- train.py build(): auto-detect extra features from parquet columns
(FEAT_COLS = [ret, realized_vol] + [hl_range, ret_intrabar] if present)
- 5 new tests (9 total in test_prepare_hourly); 24/24 pass
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- train.py knobs all readable from JEPA_* env vars (JEPA_WINDOW, JEPA_D_MODEL,
JEPA_DEPTH, etc.) so hpo_sweep.py can override without touching source
- scripts/hpo_sweep.py: 3×2×3 grid over D_MODEL × DEPTH × WINDOW,
logs to results/hpo/hpo_results.jsonl with leaderboard at end
- 3 new tests: env override correctness, configs() schema validation
- 19/19 tests pass
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Go harness reported 0.42 vs Python 0.36 because export used realized_vol[t]
(current) while Python probe used realized_vol[t+1] (next-period). Fix adds
t+1 < len(df2) guard and uses iloc[t+1] as target. Go now matches Python: 0.3585.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
BATCH_SIZE=512 per step; batched embed() at eval + export time.
78k hourly windows can't fit in GPU in one shot (was fine at 877 daily).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- train.py build(): year-based split (train≤2021, OOS≥2022) replaces
misleading 70/30 mixed-period split; true OOS val_vol_r2 now ~-0.36
vs previously reported +0.18 (artefact of cross-period data leakage)
- train.py: EXPORT_EMBEDDINGS block now exports both train+OOS embeddings
with dates and HV labels for Go eval harness
- cmd/eval: LinearProbeTrainTest uses train stats for standardisation of
both sets (no leakage); standardiseCompute/applyStandardise helpers
- internal/eval: add LinearProbeTrainTest (fit-on-train, eval-on-OOS)
alongside LinearProbe (same-set); 8/8 tests still green
Phase-0 gate result: val_vol_r2=-0.36, silhouette=0.043, erank=58.9/64.
Backbone produces high-rank embeddings (SIGReg working) but does NOT
generalize across 2021→2022 regime boundary. Gate: INCONCLUSIVE/FAIL.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase-0 SSL feasibility gate run on daily 2019-2023 EUR/USD (path B deviation:
not hourly 2008-2022 + Go harness as specced in #5). Results:
TS-JEPA silhouette mean=0.018 (need >0.20) — FAIL
PCA baseline silhouette=0.136 — also below threshold
sensitivity: 2000 ep + D=64 worsened to 0.004 (not a training-time issue)
Root cause: 2 daily features (ret, realized_vol) carry minimal regime structure
at this resolution. The JEPA objective with SIGReg pushes embeddings toward
isotropic Gaussian — good for downstream probes (val_vol_r2>0) but may actively
resist the clustering structure the silhouette gate measures.
Null protocol: real gate requires #4 (Go harness) + #2 (hourly data, more
features) before rerunning. HEPA (#14) noted as alternative backbone.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All 5 kept: val_vol_r2 -0.1543 → +0.0599 (+0.214 total). Backbone learning.
Agent tuning: LR, depth, SIGREG_LAM, EPOCHS. Still well below toy ceiling
(0.37) — real backbone room to grow via #3/#4/#5.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PatchTST-style transformer encoder with JEPA predictive loss + SIGReg
regularization (Balestriero & LeCun arXiv:2511.08544; time-series placement
from ChronoJEPA). Token-level SIGReg (dual placement) to avoid time-axis
collapse (confirmed real by ChronoJEPA). Baseline val_vol_r2=-0.1543 on first
run — expected for fresh weights with new architecture. Agent will iterate.
SIGReg source: Epps-Pulley statistic, identical math to LeJEPA MINIMAL.md.
Refs: #3 (TS-JEPA reproduce), ChronoJEPA github.com/MrRobotop/ChronoJEPA
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Toy encoder near ceiling. 1 kept (val_vol_r2 0.3032→0.3442), 5 reverts.
Consistent plateau = time to swap in TS-JEPA backbone (#3/#5).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fetch_data.py politely pulls EUR/USD M1 from histdata.com (maintained package
handles the anti-hotlink token; per-year, spaced). prepare_data.py (LOCKED per
Phase-1 contract) parses M1 -> daily series with realized_vol = the val_vol_r2
target (sqrt sum of squared intraday returns). Verified on 2019-2021: 937 days,
March-2020 COVID RV spike 5.1x over 2019 median — real signal, target works.
Data gitignored (DVC/MinIO = #10).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
scripts/check_gpu.py verifies PyTorch cu130 sees the koala Blackwell GPU
(sm_120) and computes — the Phase-0 prerequisite before any autoresearch
experiment. Verified green: torch 2.12.1+cu130, RTX 5070, GPU matmul OK.
Note: koala GPU is shared with the llama-swap LLM stack — run the autoresearch
agent on iguana/berget so the card stays free for train.py.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>