generated from mathias/template-go-web
feat: project setup — research structure, specs, decisions, conventions #1
+72
-5
@@ -1,13 +1,80 @@
|
||||
# hostexecutor
|
||||
# jepa-fx-risk
|
||||
|
||||
## Identity
|
||||
|
||||
- **Name**: hostexecutor
|
||||
- **Name**: jepa-fx-risk
|
||||
- **Owner**: Mathias
|
||||
- **Client**: personal
|
||||
- **Repo**: gitea.d-ma.be/mathias/hostexecutor
|
||||
- **Client**: personal / research
|
||||
- **Repo**: gitea.d-ma.be/mathias/jepa-fx-risk
|
||||
- **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
|
||||
|
||||
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": {
|
||||
"knowledge": {
|
||||
"url": "http://localhost:3100/mcp",
|
||||
"description": "Project knowledge base — vector + graph retrieval"
|
||||
"brain": {
|
||||
"type": "http",
|
||||
"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
|
||||
# If you prefer the allow list template instead of the deny list, see community template:
|
||||
# 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`
|
||||
# Go build artifacts
|
||||
bin/
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
# Go workspace file
|
||||
# Go workspace
|
||||
go.work
|
||||
go.work.sum
|
||||
|
||||
# env file
|
||||
# Environment
|
||||
.env
|
||||
|
||||
# Project-specific
|
||||
bin/
|
||||
*.templ.go
|
||||
# Python
|
||||
model/.venv/
|
||||
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
|
||||
|
||||
- **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.
|
||||
See .context/PROJECT.md for the canonical source.
|
||||
|
||||
@@ -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
|
||||
|
||||
- **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.
|
||||
See .context/PROJECT.md for the canonical source.
|
||||
|
||||
+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
|
||||
go mod tidy # regenerate go.sum with real module path
|
||||
task generate # generate templ files
|
||||
task build # build the binary
|
||||
task data:fetch # download DUKASCopy G10 tick data
|
||||
task pipeline:build # build Go data pipeline
|
||||
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'
|
||||
|
||||
tasks:
|
||||
generate:
|
||||
desc: Run templ generate
|
||||
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 ./...]
|
||||
|
||||
# ── Quality gate ──────────────────────────────────────────────────────────
|
||||
|
||||
check:
|
||||
desc: Lint, vet, and test (used by CI)
|
||||
deps: [generate]
|
||||
desc: "Full quality gate: lint + vet + test (run before every commit)"
|
||||
cmds:
|
||||
- golangci-lint run ./...
|
||||
- go vet ./...
|
||||
- go test ./... -race -count=1
|
||||
- golangci-lint run ./src/...
|
||||
- go vet ./src/...
|
||||
- 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:
|
||||
desc: Regenerate all harness-specific context files
|
||||
cmds:
|
||||
- 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]
|
||||
desc: "Regenerate CLAUDE.md and AGENTS.md from .context/PROJECT.md"
|
||||
cmds: [bash scripts/context-sync.sh]
|
||||
|
||||
@@ -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