generated from mathias/template-go-web
Compare commits
16
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e75c05f7d6 | ||
|
|
24166a71e3 | ||
|
|
fb2737fc4a | ||
|
|
09a37a5130 | ||
|
|
95f2fb72db | ||
|
|
4608c8ee29 | ||
|
|
a27200cc5e | ||
|
|
4338abf176 | ||
|
|
192651cb66 | ||
|
|
5c24864021 | ||
|
|
414ad1bdb2 | ||
|
|
2112b3f765 | ||
|
|
cf1c47048f | ||
|
|
668f800868 | ||
|
|
a2414cf4c9 | ||
|
|
f7dbe5cc1c |
+72
-5
@@ -1,13 +1,80 @@
|
|||||||
# hostexecutor
|
# jepa-fx-risk
|
||||||
|
|
||||||
## Identity
|
## Identity
|
||||||
|
|
||||||
- **Name**: hostexecutor
|
- **Name**: jepa-fx-risk
|
||||||
- **Owner**: Mathias
|
- **Owner**: Mathias
|
||||||
- **Client**: personal
|
- **Client**: personal / research
|
||||||
- **Repo**: gitea.d-ma.be/mathias/hostexecutor
|
- **Repo**: gitea.d-ma.be/mathias/jepa-fx-risk
|
||||||
- **Status**: active
|
- **Status**: active
|
||||||
|
- **Brain wing**: `jepa-fx` (`wiki/jepa-fx/`)
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Research project. Not a product. The "user" is future-self and research readers. Success is a reproducible, publishable result — not deployment.
|
||||||
|
|
||||||
## Stack
|
## Stack
|
||||||
|
|
||||||
Go + Templ + HTMX + CDN Tailwind. See `~/dev/.context/AGENT.md` for cross-project conventions.
|
- **Primary language**: Go 1.24+ (pipeline, eval harness, CLI, experiment runner)
|
||||||
|
- **ML layer**: Python 3.12 + PyTorch (TS-JEPA training loop only — isolated in `model/`)
|
||||||
|
- **Build**: Task (Taskfile.yml)
|
||||||
|
- **Target infra**: koala (Arch + Blackwell GPU, training), iguana (Mac Studio M2, dev)
|
||||||
|
- **MCP**: brain (knowledge), gitea (version control)
|
||||||
|
|
||||||
|
## Research context
|
||||||
|
|
||||||
|
**Primary hypothesis:**
|
||||||
|
> JEPA embeddings trained on FX time-series will produce latent market-state representations structurally separable by regime without explicit labels, measurable by silhouette score > 0.35 on k-means clusters vs. realised-volatility regime labels, on held-out data including at least one structural break.
|
||||||
|
|
||||||
|
**Current phase:** Phase 0 — SSL Feasibility Gate
|
||||||
|
|
||||||
|
**Training cutoff:** 2023-01-01 (hard — never look at post-2023 data during development)
|
||||||
|
|
||||||
|
**Out of scope:** options pricing, directional alpha, live/paper trading, exotic pairs (Phases 1–3)
|
||||||
|
|
||||||
|
**Brain wing for prior decisions and failure modes:** `brain_query wing=jepa-fx`
|
||||||
|
|
||||||
|
## Conventions
|
||||||
|
|
||||||
|
### Scientific discipline
|
||||||
|
- Every experiment has a spec in `specs/` before any code runs
|
||||||
|
- Hypotheses are falsifiable and have quantitative acceptance criteria
|
||||||
|
- Null results are recorded and published — not discarded
|
||||||
|
- Training cutoff is sacred — post-2023 data never informs any design decision
|
||||||
|
- Results reported with baselines; no cherry-picking
|
||||||
|
|
||||||
|
### Code style
|
||||||
|
- Go: `gofumpt`, `golangci-lint` with project config; table-driven tests; `testify`
|
||||||
|
- Errors: `fmt.Errorf("context: %w", err)` — no naked returns
|
||||||
|
- Python: `ruff` for lint; type hints throughout; `pytest` for tests
|
||||||
|
- No Jupyter notebooks for anything reproducible — notebooks are EDA scratch only
|
||||||
|
|
||||||
|
### Git
|
||||||
|
- Conventional commits: `feat:`, `fix:`, `chore:`, `docs:`, `experiment:`, `result:`
|
||||||
|
- Branch: `feat/`, `experiment/phase-N-description`, `fix/`
|
||||||
|
- Every experiment run gets a git tag: `exp/YYYYMMDD-short-description`
|
||||||
|
- PRs: one concern per PR; description explains *why* not *what*
|
||||||
|
|
||||||
|
### Experiment discipline
|
||||||
|
- One spec per phase in `specs/` — written before any implementation
|
||||||
|
- Each run recorded in `experiments/YYYYMMDD-HHMMSS-description/`
|
||||||
|
- Metric summaries committed to `results/summaries/` — large outputs gitignored
|
||||||
|
- `task check` must pass before any commit
|
||||||
|
|
||||||
|
### Security / data
|
||||||
|
- No raw FX data committed (gitignored) — see `data/README.md` for reproducible download
|
||||||
|
- No API keys or tokens in code — env vars only
|
||||||
|
- Training data and results stay local — nothing to cloud unless explicitly decided
|
||||||
|
|
||||||
|
## Agent instructions
|
||||||
|
|
||||||
|
When acting as a coding agent on this project:
|
||||||
|
|
||||||
|
1. Read this file and all `SKILL.md` files in `.skills/` before starting work
|
||||||
|
2. Run `brain_query wing=jepa-fx` to load current decisions and failure modes
|
||||||
|
3. Run `task check` before every commit (lint + vet + test)
|
||||||
|
4. Check `DECISIONS.md` before making any architecture or methodology choice
|
||||||
|
5. Every experiment needs a spec in `specs/` — no specless experiments
|
||||||
|
6. Never touch post-2023 data during development; it is sealed
|
||||||
|
7. Record null results honestly — do not iterate until metrics pass without noting it
|
||||||
|
8. When adding a Python dependency, justify it; prefer pure Go alternatives
|
||||||
|
|||||||
+13
-3
@@ -1,8 +1,18 @@
|
|||||||
{
|
{
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
"knowledge": {
|
"brain": {
|
||||||
"url": "http://localhost:3100/mcp",
|
"type": "http",
|
||||||
"description": "Project knowledge base — vector + graph retrieval"
|
"url": "https://brain-mcp.d-ma.be/mcp",
|
||||||
|
"headers": {
|
||||||
|
"Authorization": "Bearer ${BRAIN_MCP_TOKEN}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gitea": {
|
||||||
|
"type": "http",
|
||||||
|
"url": "https://git-mcp.d-ma.be/mcp",
|
||||||
|
"headers": {
|
||||||
|
"Authorization": "Bearer ${GITEA_MCP_TOKEN}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-22
@@ -1,30 +1,39 @@
|
|||||||
# ---> Go
|
# Go build artifacts
|
||||||
# If you prefer the allow list template instead of the deny list, see community template:
|
bin/
|
||||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
|
||||||
#
|
|
||||||
# Binaries for programs and plugins
|
|
||||||
*.exe
|
|
||||||
*.exe~
|
|
||||||
*.dll
|
|
||||||
*.so
|
|
||||||
*.dylib
|
|
||||||
|
|
||||||
# Test binary, built with `go test -c`
|
|
||||||
*.test
|
*.test
|
||||||
|
|
||||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
|
||||||
*.out
|
*.out
|
||||||
|
|
||||||
# Dependency directories (remove the comment below to include it)
|
# Go workspace
|
||||||
# vendor/
|
|
||||||
|
|
||||||
# Go workspace file
|
|
||||||
go.work
|
go.work
|
||||||
go.work.sum
|
go.work.sum
|
||||||
|
|
||||||
# env file
|
# Environment
|
||||||
.env
|
.env
|
||||||
|
|
||||||
# Project-specific
|
# Python
|
||||||
bin/
|
model/.venv/
|
||||||
*.templ.go
|
model/__pycache__/
|
||||||
|
model/**/__pycache__/
|
||||||
|
model/**/*.pyc
|
||||||
|
model/.pytest_cache/
|
||||||
|
model/**/.pytest_cache/
|
||||||
|
model/.ruff_cache/
|
||||||
|
|
||||||
|
# Data — never commit raw or processed FX data
|
||||||
|
data/raw/
|
||||||
|
data/processed/
|
||||||
|
data/cache/
|
||||||
|
|
||||||
|
# Experiment outputs — commit summaries only (results/summaries/)
|
||||||
|
experiments/*/embeddings/
|
||||||
|
experiments/*/checkpoints/
|
||||||
|
experiments/*/logs/
|
||||||
|
|
||||||
|
# Large results — commit metric tables and figures only
|
||||||
|
results/raw/
|
||||||
|
|
||||||
|
# Notebooks — never commit outputs
|
||||||
|
notebooks/**/.ipynb_checkpoints/
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
This directory contains skill symlinks for this project.
|
||||||
|
|
||||||
|
Agents: load all SKILL.md files in subdirectories before starting work.
|
||||||
|
|
||||||
|
Active skills:
|
||||||
|
- experiment-spec — write experiment specs before running any phase
|
||||||
|
- feature-spec — write component specs before implementing within a phase
|
||||||
|
- tdd — acceptance criteria map to tests; red-green-refactor
|
||||||
|
- grill-me — stress-test specs and hypotheses before committing
|
||||||
|
- session-retrospective — after each phase, surface learnings for the brain
|
||||||
|
- clean-code — Go and Python style conventions
|
||||||
|
- planning — break phases into trackable tasks
|
||||||
|
- debug — systematic debugging approach
|
||||||
@@ -1,13 +1,5 @@
|
|||||||
# hostexecutor
|
# jepa-fx-risk — Agent context
|
||||||
|
# Auto-generated from .context/PROJECT.md by `task context:sync`
|
||||||
|
# Do not edit directly.
|
||||||
|
|
||||||
## Identity
|
See .context/PROJECT.md for the canonical source.
|
||||||
|
|
||||||
- **Name**: hostexecutor
|
|
||||||
- **Owner**: Mathias
|
|
||||||
- **Client**: personal
|
|
||||||
- **Repo**: gitea.d-ma.be/mathias/hostexecutor
|
|
||||||
- **Status**: active
|
|
||||||
|
|
||||||
## Stack
|
|
||||||
|
|
||||||
Go + Templ + HTMX + CDN Tailwind. See `~/dev/.context/AGENT.md` for cross-project conventions.
|
|
||||||
|
|||||||
@@ -1,13 +1,5 @@
|
|||||||
# hostexecutor
|
# jepa-fx-risk — Agent context
|
||||||
|
# Auto-generated from .context/PROJECT.md by `task context:sync`
|
||||||
|
# Do not edit directly.
|
||||||
|
|
||||||
## Identity
|
See .context/PROJECT.md for the canonical source.
|
||||||
|
|
||||||
- **Name**: hostexecutor
|
|
||||||
- **Owner**: Mathias
|
|
||||||
- **Client**: personal
|
|
||||||
- **Repo**: gitea.d-ma.be/mathias/hostexecutor
|
|
||||||
- **Status**: active
|
|
||||||
|
|
||||||
## Stack
|
|
||||||
|
|
||||||
Go + Templ + HTMX + CDN Tailwind. See `~/dev/.context/AGENT.md` for cross-project conventions.
|
|
||||||
|
|||||||
+120
@@ -0,0 +1,120 @@
|
|||||||
|
# DECISIONS.md
|
||||||
|
|
||||||
|
Architecture and methodology decisions for `jepa-fx-risk`. Every non-obvious choice lives here with its rationale. Agents: read this before making any design decision.
|
||||||
|
|
||||||
|
Last updated: 2026-05-27
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Language split: Go-first, Python-minimal
|
||||||
|
|
||||||
|
**Decision:** Data pipeline, evaluation harness, CLI, and experiment runner in Go 1.24+. Python 3.12 + PyTorch only for the TS-JEPA model training loop, isolated in `model/`.
|
||||||
|
|
||||||
|
**Rationale:** Python's dependency ecosystem is a reliability risk for a multi-year project (conflicting CUDA versions, transitive breakage, environment drift). Go produces a single static binary, has excellent CSV/Parquet support, is fast enough for all non-training workloads, and keeps the reproducible parts of the project dependency-free. The training loop genuinely requires PyTorch — that is the one place Python is unavoidable.
|
||||||
|
|
||||||
|
**Boundary:** `model/` is the Python perimeter. Nothing outside it imports Python.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Primary hypothesis
|
||||||
|
|
||||||
|
**Decision:** Regime detection via embedding structural separability is the primary hypothesis. Distributional VaR forecasting is secondary and only pursued if Phase 1 and 2 succeed.
|
||||||
|
|
||||||
|
**Rationale:** Regime detection is testable in Phase 1 without regulatory-quality outputs. If embeddings don't show regime structure, distributional VaR will also fail. Choosing a primary hypothesis prevents the project from retreating from one to the other on failure.
|
||||||
|
|
||||||
|
**Success criterion (Phase 1):** Silhouette score > 0.35 on k-means clusters (k=3–5) vs. realised-volatility regime label (rolling 30-day HV percentile, high/low), computed on held-out test data including at least one structural break.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Training data: 2008–2022, all G10 pairs
|
||||||
|
|
||||||
|
**Decision:** Train on 15 years of all G10 FX pairs from DUKASCopy, 2008–2022. Do not start with a single 5-year EUR/USD window.
|
||||||
|
|
||||||
|
**Rationale:** A single 5-year window (e.g. 2019–2024) is dominated by one or two regimes and gives the encoder insufficient regime diversity to learn regime-sensitive representations. Training on 2008–2022 ensures the encoder sees: GFC (2008), EUR sovereign debt (2011–2012), SNB cap removal (2015), COVID (2020), USD rate cycle (2022). Multi-pair training also allows cross-currency transfer (Phase 4).
|
||||||
|
|
||||||
|
**Alternative considered:** Start simple with EUR/USD only, expand later. Rejected because regime diversity in training is a structural requirement, not a nice-to-have. Retrofitting it in Phase 2 would require retraining from scratch.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Hard training cutoff: 2023-01-01
|
||||||
|
|
||||||
|
**Decision:** All data from 2023-01-01 onward is sealed. No architecture, hyperparameter, or methodology decision may be informed by post-2023 data. Post-2023 test set opened only once, for final evaluation.
|
||||||
|
|
||||||
|
**Rationale:** Out-of-sample integrity is essential for publishability and honest self-assessment. The held-out window (2023–2026) includes: 2023 US regional bank stress, 2024 JPY intervention episodes. These are the test of genuine generalisation.
|
||||||
|
|
||||||
|
**Enforcement:** `data/raw/` is gitignored. The download script hard-stops at 2022-12-31 for training splits. Any deviation requires a DECISIONS.md entry explaining why.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 0: SSL feasibility gate before JEPA work
|
||||||
|
|
||||||
|
**Decision:** Before any JEPA-specific implementation, run a Phase 0 experiment: masked autoencoder (MAE) baseline on EUR/USD hourly data. If MAE silhouette < 0.20, SSL-based regime detection is likely not feasible on this data — stop and investigate before proceeding to JEPA.
|
||||||
|
|
||||||
|
**Rationale:** JEPA's complexity is only justified if the core SSL premise (that latent representations capture regime structure) holds for FX data. A failed MAE experiment tells us this in 2 weeks rather than 4 months. Added after Full Grill session (2026-05-27).
|
||||||
|
|
||||||
|
**Go/no-go threshold:** MAE silhouette < 0.20 on held-out 2023 data → pause, investigate, do not proceed to Phase 1.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture: TS-JEPA as starting implementation
|
||||||
|
|
||||||
|
**Decision:** Use TS-JEPA (Ennadir et al., 2025) as the starting JEPA implementation. MTS-JEPA (He et al., 2026) is the upgrade path if multi-resolution proves necessary.
|
||||||
|
|
||||||
|
**Rationale:** TS-JEPA is simpler. Validate the concept before adding multi-resolution complexity. If Phase 1 succeeds with TS-JEPA, MTS-JEPA is an ablation, not a prerequisite.
|
||||||
|
|
||||||
|
**Risk:** Both are preprints. Code reproducibility is unconfirmed. First task of Phase 0 is reproducing TS-JEPA on the paper's own benchmark — if this takes > 2 weeks, contact authors or fall back to implementing JEPA masking from scratch using V-JEPA codebase as reference.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Input features: minimal for Phase 0/1
|
||||||
|
|
||||||
|
**Decision:** Phase 0 and Phase 1 use three features only: log-return (hourly), rolling 20-period realised volatility (hourly), VIX (daily, interpolated to hourly).
|
||||||
|
|
||||||
|
**Rationale:** Too many input features in early phases makes it impossible to distinguish "JEPA learned regime structure" from "JEPA learned to encode a feature that correlates with regime." Minimal features reduce confounding.
|
||||||
|
|
||||||
|
**Expansion path:** Add DXY, G10 vol surface, yield spreads in Phase 2 if Phase 1 succeeds.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Collapse diagnostic
|
||||||
|
|
||||||
|
**Decision:** If PC1 of JEPA embeddings correlates > 0.85 with rolling 30-day HV, treat Phase 1 as a partial failure — the encoder learned volatility level, not regime structure. This is a useful finding but not the hypothesis.
|
||||||
|
|
||||||
|
**Rationale:** EUR/USD hourly returns are strongly heteroskedastic. A JEPA encoder trained to predict future embeddings will strongly tend to encode current volatility as its primary latent dimension. This is predictable, not regime-sensitive. The linear probe and PC1 diagnostic together distinguish "learned volatility" from "learned regime."
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Linear probe as mandatory Phase 1 exit gate
|
||||||
|
|
||||||
|
**Decision:** Train a linear model on frozen JEPA embeddings to predict realised volatility decile. R² < 0.4 → embeddings are not encoding useful risk structure → do not proceed to Phase 2.
|
||||||
|
|
||||||
|
**Rationale:** If a simple linear model cannot extract volatility regime from the embeddings, the representations are not useful for risk management purposes regardless of their silhouette score. Interpretability-by-linear-probe is the minimum bar for any downstream use.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Explicit out-of-scope (Phases 1–3)
|
||||||
|
|
||||||
|
The following are explicitly out of scope for this research programme and go to a parking lot if they arise:
|
||||||
|
|
||||||
|
- Options / derivatives pricing
|
||||||
|
- Directional alpha generation
|
||||||
|
- Live or paper trading
|
||||||
|
- Exotic pairs beyond G10
|
||||||
|
- Institutional deployment
|
||||||
|
- Real-time inference systems
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Experiment spec required before any experiment
|
||||||
|
|
||||||
|
**Decision:** Every experiment phase must have a written spec in `specs/` before any code runs. No specless experiments.
|
||||||
|
|
||||||
|
**Rationale:** Consistent with spec-driven-dev way of working. Specs force falsifiable hypothesis statement, quantitative acceptance criteria, and explicit null-result protocol before results are known — preventing post-hoc rationalisation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Null results are results
|
||||||
|
|
||||||
|
**Decision:** Null results (hypothesis rejected) are recorded in `results/summaries/` and treated as valid research outputs, not failures to be iterated away silently.
|
||||||
|
|
||||||
|
**Rationale:** A confirmed null result (e.g. "SSL cannot find regime structure in FX hourly data") is publishable and scientifically valuable. Iterating hyperparameters until metrics pass without recording the failed attempts is p-hacking. Humble attitude; scientific approach.
|
||||||
@@ -1,13 +1,66 @@
|
|||||||
# hostexecutor
|
# jepa-fx-risk
|
||||||
|
|
||||||
> Generated from `mathias/template-go-web`.
|
Research project exploring JEPA (Joint Embedding Predictive Architecture) as a framework for latent representation learning applied to FX trading risk management.
|
||||||
|
|
||||||
## Bootstrap
|
**Primary hypothesis:** JEPA embeddings trained on FX time-series will produce latent market-state representations that are structurally separable by regime without explicit regime labels, measurable by silhouette score on k-means clusters validated against held-out realised-volatility regime labels.
|
||||||
|
|
||||||
After creating from template, run:
|
**Current phase:** Phase 0 — SSL Feasibility Gate (not yet started)
|
||||||
|
|
||||||
|
**Brain wing:** `jepa-fx` — query via `brain_query wing=jepa-fx`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
This is a **Go-first, Python-minimal** research project.
|
||||||
|
|
||||||
|
| Layer | Language | Rationale |
|
||||||
|
|---|---|---|
|
||||||
|
| Data pipeline | Go | Type-safe, fast, no dependency hell |
|
||||||
|
| Experiment runner | Go | CLI tooling, reproducible invocations |
|
||||||
|
| Evaluation harness | Go | Silhouette, linear probe, collapse diagnostics |
|
||||||
|
| Model training | Python + PyTorch | TS-JEPA requires it; kept minimal and isolated |
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
specs/ experiment specs (one per phase)
|
||||||
|
src/ Go packages: pipeline, eval, cmd
|
||||||
|
model/ Python: TS-JEPA training loop only
|
||||||
|
experiments/ one directory per run (gitignored except summaries)
|
||||||
|
results/ tracked: metric tables, key figures
|
||||||
|
data/ gitignored; see data/README.md for download instructions
|
||||||
|
notebooks/ EDA only; outputs stripped before commit
|
||||||
|
docs/ ADRs and research notes
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go mod tidy # regenerate go.sum with real module path
|
task data:fetch # download DUKASCopy G10 tick data
|
||||||
task generate # generate templ files
|
task pipeline:build # build Go data pipeline
|
||||||
task build # build the binary
|
task check # lint + vet + test
|
||||||
|
task experiment:run -- --spec specs/phase-0-ssl-feasibility.md
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Phases
|
||||||
|
|
||||||
|
| Phase | Goal | Status |
|
||||||
|
|---|---|---|
|
||||||
|
| 0 | SSL feasibility gate — MAE baseline on FX data | 🔲 Not started |
|
||||||
|
| 1 | JEPA representation PoC — silhouette > 0.35 | 🔲 Not started |
|
||||||
|
| 2 | Regime detection validation — recall > 70%, lead > 5d | 🔲 Not started |
|
||||||
|
| 3 | Distributional forecasting — VaR passes Kupiec | 🔲 Not started |
|
||||||
|
| 4 | Multi-pair transfer — exotic pair improvement | 🔲 Not started |
|
||||||
|
|
||||||
|
## Key constraints
|
||||||
|
|
||||||
|
- **Training cutoff:** 2023-01-01 — all post-2023 data is sealed for final evaluation
|
||||||
|
- **Training data:** 2008–2022, all G10 pairs, DUKASCopy
|
||||||
|
- **Out of scope:** options pricing, directional alpha, live trading, exotic pairs (Phases 1–3)
|
||||||
|
- **End-state:** publishable research result — not institutional deployment
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- Brain wing: `wiki/jepa-fx/` — decisions, hypotheses, failure modes
|
||||||
|
- Synthesis document: `JEPA_FX_Risk_Synthesis.docx` (session 2026-05-27)
|
||||||
|
|||||||
+72
-30
@@ -1,37 +1,79 @@
|
|||||||
version: '3'
|
version: '3'
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
generate:
|
|
||||||
desc: Run templ generate
|
# ── Quality gate ──────────────────────────────────────────────────────────
|
||||||
cmds: [templ generate]
|
|
||||||
build:
|
|
||||||
desc: Build the binary
|
|
||||||
deps: [generate]
|
|
||||||
cmds: [go build -o bin/hostexecutor ./cmd/hostexecutor]
|
|
||||||
run:
|
|
||||||
deps: [build]
|
|
||||||
cmds: [./bin/hostexecutor]
|
|
||||||
test:
|
|
||||||
desc: Run all tests
|
|
||||||
deps: [generate]
|
|
||||||
cmds: [go test ./... -race]
|
|
||||||
lint:
|
|
||||||
cmds: [golangci-lint run ./...]
|
|
||||||
check:
|
check:
|
||||||
desc: Lint, vet, and test (used by CI)
|
desc: "Full quality gate: lint + vet + test (run before every commit)"
|
||||||
deps: [generate]
|
|
||||||
cmds:
|
cmds:
|
||||||
- golangci-lint run ./...
|
- golangci-lint run ./src/...
|
||||||
- go vet ./...
|
- go vet ./src/...
|
||||||
- go test ./... -race -count=1
|
- go test ./src/... -race -count=1
|
||||||
|
- cd model && python -m pytest tests/ -q
|
||||||
|
|
||||||
|
test:
|
||||||
|
desc: Run Go tests only
|
||||||
|
cmds: [go test ./src/... -race]
|
||||||
|
|
||||||
|
lint:
|
||||||
|
desc: Lint Go code
|
||||||
|
cmds: [golangci-lint run ./src/...]
|
||||||
|
|
||||||
|
# ── Data ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
data:fetch:
|
||||||
|
desc: "Download G10 tick data from DUKASCopy (2003–2022)"
|
||||||
|
cmds: [go run ./src/cmd/fetch --config data/config.yaml]
|
||||||
|
|
||||||
|
data:process:
|
||||||
|
desc: "Resample ticks → hourly OHLCV + features (log-return, rolling HV)"
|
||||||
|
cmds: [go run ./src/cmd/process --input data/raw --output data/processed]
|
||||||
|
|
||||||
|
data:validate:
|
||||||
|
desc: "Validate processed data: gap detection, outlier report, regime coverage"
|
||||||
|
cmds: [go run ./src/cmd/validate --input data/processed]
|
||||||
|
|
||||||
|
# ── Experiment ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
experiment:run:
|
||||||
|
desc: "Run an experiment from a spec file. Usage: task experiment:run -- --spec specs/phase-0-ssl-feasibility.md"
|
||||||
|
cmds: [go run ./src/cmd/experiment {{.CLI_ARGS}}]
|
||||||
|
|
||||||
|
experiment:list:
|
||||||
|
desc: List all recorded experiment runs
|
||||||
|
cmds: [ls -lt experiments/ | head -20]
|
||||||
|
|
||||||
|
# ── Evaluation ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
eval:silhouette:
|
||||||
|
desc: "Compute silhouette score on embedding output. Usage: task eval:silhouette -- --run experiments/RUNID"
|
||||||
|
cmds: [go run ./src/cmd/eval silhouette {{.CLI_ARGS}}]
|
||||||
|
|
||||||
|
eval:probe:
|
||||||
|
desc: "Run linear probe on frozen embeddings vs. realised-vol decile"
|
||||||
|
cmds: [go run ./src/cmd/eval probe {{.CLI_ARGS}}]
|
||||||
|
|
||||||
|
eval:collapse:
|
||||||
|
desc: "Check collapse diagnostic: PC1 correlation with rolling HV"
|
||||||
|
cmds: [go run ./src/cmd/eval collapse {{.CLI_ARGS}}]
|
||||||
|
|
||||||
|
# ── Model (Python) ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
model:train:
|
||||||
|
desc: "Train TS-JEPA model. Usage: task model:train -- --config model/configs/phase0.yaml"
|
||||||
|
dir: model
|
||||||
|
cmds: [python train.py {{.CLI_ARGS}}]
|
||||||
|
|
||||||
|
model:setup:
|
||||||
|
desc: "Create Python venv and install model dependencies (uv)"
|
||||||
|
dir: model
|
||||||
|
cmds:
|
||||||
|
- uv venv .venv
|
||||||
|
- uv pip install -r requirements.txt
|
||||||
|
|
||||||
|
# ── Context ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
context:sync:
|
context:sync:
|
||||||
desc: Regenerate all harness-specific context files
|
desc: "Regenerate CLAUDE.md and AGENTS.md from .context/PROJECT.md"
|
||||||
cmds:
|
cmds: [bash scripts/context-sync.sh]
|
||||||
- bash scripts/context-sync.sh
|
|
||||||
context:sync:claude:
|
|
||||||
cmds: [bash scripts/context-sync.sh claude]
|
|
||||||
context:sync:agents:
|
|
||||||
cmds: [bash scripts/context-sync.sh agents]
|
|
||||||
context:sync:cursor:
|
|
||||||
cmds: [bash scripts/context-sync.sh cursor]
|
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# Data
|
||||||
|
|
||||||
|
Raw and processed FX data is **gitignored** — never committed.
|
||||||
|
|
||||||
|
## Download: DUKASCopy G10 tick data
|
||||||
|
|
||||||
|
Use the provided fetch task:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
task data:fetch # downloads raw tick data → data/raw/
|
||||||
|
task data:process # resamples to hourly OHLCV + features → data/processed/
|
||||||
|
task data:validate # gap detection, outlier report, regime coverage check
|
||||||
|
```
|
||||||
|
|
||||||
|
## Expected structure (local only)
|
||||||
|
|
||||||
|
```
|
||||||
|
data/
|
||||||
|
raw/ G10 pairs, tick OHLCV, 2003–present (gitignored)
|
||||||
|
processed/ Hourly log-returns, rolling HV, VIX-merged (gitignored)
|
||||||
|
cache/ Intermediate artefacts (gitignored)
|
||||||
|
config.yaml Download configuration (committed)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pairs
|
||||||
|
|
||||||
|
EUR/USD, GBP/USD, USD/JPY, USD/CHF, AUD/USD, USD/CAD, NZD/USD, EUR/GBP, EUR/JPY, EUR/CHF
|
||||||
|
|
||||||
|
## Splits
|
||||||
|
|
||||||
|
| Split | Date range | Purpose |
|
||||||
|
|---|---|---|
|
||||||
|
| Train | 2008-01-01 – 2022-12-31 | Model training only |
|
||||||
|
| Held-out test | 2023-01-01 – 2023-12-31 | Final evaluation — do not open until evaluation |
|
||||||
|
|
||||||
|
**Training cutoff is hard: 2023-01-01. No post-2022 data informs any design decision.**
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# Experiments
|
||||||
|
|
||||||
|
One directory per experiment run. Large outputs (embeddings, checkpoints, logs) are gitignored. Only metric summaries are committed to `results/summaries/`.
|
||||||
|
|
||||||
|
## Naming convention
|
||||||
|
|
||||||
|
```
|
||||||
|
YYYYMMDD-HHMMSS-phase-N-short-description/
|
||||||
|
config.yaml parameters used for this run (committed via results/summaries/)
|
||||||
|
metrics.json final metric snapshot (committed via results/summaries/)
|
||||||
|
embeddings/ (gitignored — large)
|
||||||
|
checkpoints/ (gitignored — large)
|
||||||
|
logs/ (gitignored — large)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Git tag convention
|
||||||
|
|
||||||
|
Every run that produces reportable metrics gets a tag:
|
||||||
|
|
||||||
|
```
|
||||||
|
exp/YYYYMMDD-phase-N-description
|
||||||
|
```
|
||||||
|
|
||||||
|
Example: `exp/20260601-phase-0-mae-baseline`
|
||||||
|
|
||||||
|
## Starting a run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
task experiment:run -- --spec specs/phase-0-ssl-feasibility.md --tag my-run-description
|
||||||
|
```
|
||||||
|
|
||||||
|
The runner creates the directory, writes config, runs the experiment, and outputs metrics.json.
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# model
|
||||||
|
|
||||||
|
Python + PyTorch perimeter. This is the only directory in the project that uses Python.
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
|
||||||
|
```
|
||||||
|
model/
|
||||||
|
train.py TS-JEPA training entry point
|
||||||
|
configs/ YAML configs per phase
|
||||||
|
phase0-mae.yaml
|
||||||
|
phase1-tsjepa.yaml
|
||||||
|
tsjepa/ TS-JEPA model implementation (adapted from paper)
|
||||||
|
tests/ pytest tests for model components
|
||||||
|
requirements.txt pinned Python dependencies
|
||||||
|
.venv/ (gitignored — created by `task model:setup`)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
task model:setup # creates .venv and installs requirements via uv
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dependency policy
|
||||||
|
|
||||||
|
Every Python dependency must be justified in a comment in `requirements.txt`. Prefer Go implementations for anything outside the training loop. When adding a new dependency, add an entry to DECISIONS.md explaining why a Go alternative wasn't sufficient.
|
||||||
|
|
||||||
|
## Python version
|
||||||
|
|
||||||
|
3.12 (pinned in `.python-version`)
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# Notebooks
|
||||||
|
|
||||||
|
Exploratory Data Analysis (EDA) scratch space only.
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
- **Never use notebooks for anything reproducible.** Training, evaluation, and metric computation belong in `src/` with tests.
|
||||||
|
- **Strip all outputs before committing.** Use `nbstripout` or equivalent.
|
||||||
|
- **Label every notebook with a phase prefix:** `phase0-eda-eurusd-distribution.ipynb`
|
||||||
|
|
||||||
|
Notebooks are thinking tools, not research artifacts. If a finding from a notebook is worth keeping, it goes into a spec, a decision in `DECISIONS.md`, or a result in `results/summaries/` — not the notebook itself.
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# Results
|
||||||
|
|
||||||
|
Tracked outputs from experiment runs. Large raw outputs are gitignored — only summaries are committed.
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
results/
|
||||||
|
summaries/ metric tables, key figures, per-phase result records (committed)
|
||||||
|
raw/ full embedding outputs, backtest CSVs (gitignored)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Per-phase result record format
|
||||||
|
|
||||||
|
Each concluded phase produces a result record in `summaries/`:
|
||||||
|
|
||||||
|
```
|
||||||
|
summaries/
|
||||||
|
phase-0-[pass|null].md
|
||||||
|
phase-1-[pass|null].md
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Each record must include:
|
||||||
|
- Phase name and hypothesis
|
||||||
|
- Key metrics (silhouette score, R², etc.) with confidence intervals where applicable
|
||||||
|
- Baseline comparison
|
||||||
|
- Verdict: PASS / NULL RESULT
|
||||||
|
- If null: what was investigated, what was found, next step taken
|
||||||
|
- Link to experiment git tag
|
||||||
|
|
||||||
|
**Null results are valid research outputs and must be committed, not discarded.**
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
# Experiment Spec: Phase 0 — SSL Feasibility Gate
|
||||||
|
|
||||||
|
## Hypothesis
|
||||||
|
|
||||||
|
> We believe that a masked autoencoder (MAE) trained on FX hourly time-series will
|
||||||
|
> produce latent embeddings that show structural separability by volatility regime
|
||||||
|
> without explicit regime labels, measurable by silhouette score > 0.20 on k-means
|
||||||
|
> clusters evaluated against a held-out realised-volatility regime label on 2023 data.
|
||||||
|
|
||||||
|
This hypothesis is FALSE if silhouette score ≤ 0.20 on the held-out evaluation.
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
Before investing in JEPA-specific machinery, we need to confirm that self-supervised
|
||||||
|
representation learning can find regime structure in FX time-series at all. If the
|
||||||
|
simplest SSL method (MAE) cannot find structure, JEPA will not either — and the root
|
||||||
|
cause needs to be understood before proceeding.
|
||||||
|
|
||||||
|
Also validates that TS-JEPA code is reproducible: first task is running TS-JEPA on
|
||||||
|
the paper's own benchmark, not on FX data. If reproduction takes > 2 weeks, contact
|
||||||
|
authors or fall back to implementing JEPA masking from V-JEPA codebase.
|
||||||
|
|
||||||
|
Added post Full Grill (2026-05-27). See DECISIONS.md: "Phase 0: SSL feasibility gate."
|
||||||
|
|
||||||
|
## Design
|
||||||
|
|
||||||
|
### Data
|
||||||
|
- **Source:** DUKASCopy, EUR/USD hourly OHLCV
|
||||||
|
- **Train:** 2008-01-01 – 2022-12-31
|
||||||
|
- **Held-out test:** 2023-01-01 – 2023-12-31 (sealed until final evaluation)
|
||||||
|
- **Features:** log-return (hourly), rolling 20-period realised HV, VIX (daily → hourly interpolation)
|
||||||
|
- **Regime label (evaluation only):** rolling 30-day HV percentile; binary: top 50% = high-vol, bottom 50% = low-vol
|
||||||
|
|
||||||
|
### Model
|
||||||
|
- **Architecture:** 1D temporal Masked Autoencoder
|
||||||
|
- Encoder: 3-layer 1D CNN + positional encoding
|
||||||
|
- Decoder: 2-layer MLP reconstructing masked segment
|
||||||
|
- **Masking:** contiguous temporal block (target); context window = 120h (5 days)
|
||||||
|
- **Loss:** MSE reconstruction on masked segment
|
||||||
|
|
||||||
|
### TS-JEPA reproduction task (runs in parallel / before MAE)
|
||||||
|
- Reproduce TS-JEPA paper results on the authors' benchmark dataset
|
||||||
|
- Go/no-go: if reproduction fails within 2 weeks → contact authors or pivot to V-JEPA adaptation
|
||||||
|
|
||||||
|
### Baseline
|
||||||
|
- **PCA** on raw feature vectors (same 120h context window, flattened)
|
||||||
|
- Tests whether any dimensionality reduction finds regime structure; confirms SSL is adding something
|
||||||
|
|
||||||
|
### Ablations
|
||||||
|
- Masking horizon K ∈ {8h, 24h, 72h} — does context length affect embedding quality?
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] MAE silhouette score > 0.20 on held-out 2023 data (k-means k=3, vs. binary HV regime label)
|
||||||
|
- [ ] MAE silhouette exceeds PCA baseline silhouette
|
||||||
|
- [ ] Rerun ×3 within ±10% of reported silhouette (reproducibility)
|
||||||
|
- [ ] PC1 / rolling-HV correlation < 0.95 (encoder is not purely encoding volatility level)
|
||||||
|
- [ ] TS-JEPA reproduced on paper's benchmark within 2 weeks of start
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- JEPA implementation (Phase 1)
|
||||||
|
- Multi-pair training (Phase 1+)
|
||||||
|
- VaR, ES, or any risk metric computation
|
||||||
|
- Any data after 2022-12-31 (training); 2023 test set opened only for final evaluation
|
||||||
|
- Hyperparameter search beyond the three masking horizons defined above
|
||||||
|
|
||||||
|
## Null Result Protocol
|
||||||
|
|
||||||
|
If MAE silhouette ≤ 0.20 on held-out data:
|
||||||
|
1. Conclude: SSL-based regime detection is not straightforwardly feasible on EUR/USD hourly data with these three features
|
||||||
|
2. Investigate in order: (a) try daily resolution instead of hourly, (b) expand feature set to 6 features (add DXY, yield spread), (c) try 4-class regime label (HV quartiles) instead of binary
|
||||||
|
3. If all three investigations fail: conclude SSL regime detection is not viable on FX data; document and consider pivoting the primary hypothesis to distributional forecasting directly
|
||||||
|
4. Record result in `results/summaries/phase-0-null.md`
|
||||||
|
5. Write failure mode to brain: `brain_write wing=jepa-fx hall=failures`
|
||||||
|
|
||||||
|
## Risks
|
||||||
|
|
||||||
|
| Risk | Canary | Mitigation |
|
||||||
|
|---|---|---|
|
||||||
|
| TS-JEPA code unreproducible | Benchmark result doesn't match paper within 2 weeks | Contact authors; fall back to V-JEPA adaptation |
|
||||||
|
| MAE encoder collapses | Reconstruction loss plateau in first 10 epochs; all embeddings near-identical | Add batch normalisation; reduce LR; check masking ratio |
|
||||||
|
| Regime label too coarse | Silhouette low even with visually structured embeddings | Also evaluate with 4-class HV quartile label |
|
||||||
|
| PC1 is just volatility | PC1/HV correlation > 0.95 | Useful finding; record it; do not declare success |
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
# Experiment Spec: Phase 1 — JEPA Representation PoC
|
||||||
|
|
||||||
|
## Hypothesis
|
||||||
|
|
||||||
|
> We believe that a TS-JEPA encoder trained on G10 FX hourly data (2008–2022) will
|
||||||
|
> produce latent market-state embeddings that are structurally separable by volatility
|
||||||
|
> regime without explicit regime labels, measurable by silhouette score > 0.35 on
|
||||||
|
> k-means clusters evaluated against realised-volatility regime labels on held-out
|
||||||
|
> 2023 data including at least one structural break.
|
||||||
|
|
||||||
|
This hypothesis is FALSE if silhouette score ≤ 0.35 OR linear probe R² ≤ 0.40 on
|
||||||
|
held-out evaluation.
|
||||||
|
|
||||||
|
**Prerequisites:** Phase 0 passed (MAE silhouette > 0.20, TS-JEPA reproduced).
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
Phase 0 confirmed that SSL-based representation learning can find regime structure in
|
||||||
|
FX time-series. Phase 1 tests whether JEPA's specific inductive bias (predict target
|
||||||
|
embeddings from context embeddings, never reconstruct raw data) produces richer
|
||||||
|
representations than a simple MAE — and whether those representations are useful for
|
||||||
|
risk management tasks (measurable via linear probe).
|
||||||
|
|
||||||
|
## Design
|
||||||
|
|
||||||
|
### Data
|
||||||
|
- **Source:** DUKASCopy, all G10 pairs (EUR/USD, GBP/USD, USD/JPY, USD/CHF, AUD/USD, USD/CAD, NZD/USD, EUR/GBP, EUR/JPY, EUR/CHF), hourly
|
||||||
|
- **Train:** 2008-01-01 – 2022-12-31 (all 10 pairs, jointly)
|
||||||
|
- **Held-out test:** 2023-01-01 – 2023-12-31 (sealed until final evaluation)
|
||||||
|
- **Features:** log-return, rolling 20-period HV, VIX (daily → hourly)
|
||||||
|
- **Regime label (evaluation only):** rolling 30-day HV percentile; binary + 4-class (quartiles)
|
||||||
|
|
||||||
|
### Model
|
||||||
|
- **Architecture:** TS-JEPA (Ennadir et al., 2025)
|
||||||
|
- Context encoder: maps observed window → latent embedding
|
||||||
|
- Target encoder: EMA of context encoder (momentum β ≈ 0.996); stop-gradient
|
||||||
|
- Predictor: shallow MLP bridging context → target embedding
|
||||||
|
- **Masking horizons:** ablate K ∈ {1h, 8h, 24h}; context window = 120h
|
||||||
|
- **Training:** multi-pair joint training (one model, all G10 pairs)
|
||||||
|
|
||||||
|
### Baseline
|
||||||
|
- Phase 0 MAE (best masking horizon from Phase 0)
|
||||||
|
- PCA on raw features (Phase 0 baseline)
|
||||||
|
|
||||||
|
### Ablations
|
||||||
|
1. TS-JEPA vs MAE — is JEPA's no-reconstruction inductive bias adding value?
|
||||||
|
2. Single-pair (EUR/USD only) vs. multi-pair — does joint training improve representations?
|
||||||
|
3. Masking horizon K: {1h, 8h, 24h}
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Silhouette score > 0.35 on held-out 2023 data (k-means k=3–5, binary HV label)
|
||||||
|
- [ ] Linear probe R² > 0.40 on frozen embeddings vs. realised-vol decile
|
||||||
|
- [ ] PC1 / rolling-HV correlation < 0.85 (encoder learning more than volatility level)
|
||||||
|
- [ ] TS-JEPA silhouette exceeds Phase 0 MAE silhouette by > 5%
|
||||||
|
- [ ] Rerun ×3 within ±10% of reported silhouette
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- VaR, ES, distributional forecasting (Phase 3)
|
||||||
|
- Exotic pairs beyond G10
|
||||||
|
- Options pricing, alpha generation, live trading
|
||||||
|
- Any data after 2022-12-31 for training; test set opened only for final evaluation
|
||||||
|
- Regime detection backtesting (Phase 2 — embedding drift as early warning)
|
||||||
|
|
||||||
|
## Null Result Protocol
|
||||||
|
|
||||||
|
If primary criteria not met:
|
||||||
|
1. If silhouette > 0.20 but ≤ 0.35: JEPA shows partial structure; not sufficient for Phase 2. Investigate whether multi-pair training, longer context window, or additional features close the gap. One retry permitted with documented rationale.
|
||||||
|
2. If silhouette ≤ 0.20: regression from Phase 0; investigate JEPA training stability (collapse risk). Do not proceed.
|
||||||
|
3. If linear probe R² ≤ 0.40 despite good silhouette: embeddings are structured but not encoding risk-relevant information. Record as a finding; reconsider feature set.
|
||||||
|
4. Record all results in `results/summaries/phase-1-[pass|null].md`
|
||||||
|
5. Write findings to brain: `brain_write wing=jepa-fx hall=failures`
|
||||||
|
|
||||||
|
## Risks
|
||||||
|
|
||||||
|
| Risk | Canary | Mitigation |
|
||||||
|
|---|---|---|
|
||||||
|
| EMA encoder collapses | All embeddings converge to near-zero; loss goes to ~0 early | Verify EMA momentum schedule; check stop-gradient implementation |
|
||||||
|
| Encoder learns only EUR/USD vol | PC1 dominated by EUR/USD HV even in multi-pair model | Evaluate per-pair silhouette; if EUR/USD dominates, weight loss by pair |
|
||||||
|
| Phase 0 silhouette was data-split artefact | Phase 1 MAE baseline doesn't reproduce Phase 0 numbers | Fix random seeds; document split methodology in Phase 0 |
|
||||||
|
| Insufficient regime diversity (2008–2022) | Embedding clusters don't separate 2023 structural break | Verify 2023 test includes high-vol episode; add 4-class label as backup |
|
||||||
Reference in New Issue
Block a user