Files
Aura/CLAUDE.md
T
Brummel f040b66f30 design(c10): reframe to a bias stream + R-unit signal quality
Adopt the R-reframe ratified in #117: the strategy's native unit is risk
(R), not pips/dollars, and its primary output is a directional bias.

- exposure -> bias: the primary output is an UNSIZED direction + conviction
  `f64 in [-1,+1]`; sizing and the protective stop leave the strategy.
- Signal quality is measured in R (Stage 1): an R-evaluator integrates the
  per-trade R-outcomes of a flat-1R RiskExecutor into R-expectancy. R is
  defined by the stop, account-/instrument-agnostic; pips retired as the unit.
  Flat-1R needs no equity, so Stage 1 is feed-forward (no cycle).
- Risk-based execution is a decoupled layer: a RiskExecutor composite
  (stop-rule -> Sizer -> Veto -> position-management) per symbol, nested in a
  Broker/Account composite; account mode (netting/hedging) = composition
  constraint. "Sizer" is not "risk-manager" (that names the Veto layer).
- Currency P&L is Stage 2 (fixed-fractional, compounding). The only feedback
  (equity -> Sizer) is cut by a z^-1 register on the fill edge (mark-to-market
  stays a same-cycle price read), encapsulated in the executor; flat-1R vs
  compounding is a structural axis (the explicit register is mandatory because
  C23 reorders the flat graph).
- Position-event table stays the decoupled Stage-2 audit layer = first
  difference of the book (deal = target - book - in_flight); the flawed 0064
  exposure-integral derive is abandoned.

Touches: ledger C10 + CLAUDE.md domain invariant #7 + glossary (adds bias, R,
R-evaluator, RiskExecutor, Sizer, veto, Stage 1/2; revises broker, exposure
stream, position table, sim-optimal broker, strategy, ...). Also fixes
cross-file drift surfaced by adversarial review (C20 guarantee + front-matter
provenance still claimed "exposure stream"; cross-refs corrected to
C9/C19/C20/C23). Industry-grounded against LEAN / nautilus_trader / backtrader
/ QSTrader / vectorbt / zipline.

refs #117
2026-06-23 13:14:31 +02:00

173 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# aura — project rules
aura is a "game engine for traders": a Rust framework **and** a playground to
author trading nodes, backtest them deterministically and massively in parallel,
compose them fractally, validate them (sweep / Monte-Carlo / walk-forward), and
freeze a validated strategy into a standalone bot with a broker connection.
This file is the project sittenkodex. It imports the universal discipline from
`~/dev/skills/templates/CLAUDE.md.fragment` and adds aura's domain invariants.
The full architecture lives in the design ledger (`docs/design/`), not here.
Per-cycle specs (`docs/specs/`) and plans (`docs/plans/`) are ephemeral working
artifacts, kept git-tracked — committed while their cycle is live and removed
(`git rm`) at cycle close, valid only for the cycle that produces them. Durable
rationale is lifted to the ledger; a spec/plan is never treated as a live API
reference (its code snippets drift the moment the code moves). The canonical
record of past cycles is the ledger plus the git history.
## Roles
I am the **orchestrator**, not the implementer. The skills-plugin agents are my
workers: I plan, design, decide, and integrate; they implement, refactor, test,
and diagnose. Trivial mechanical edits I may do directly; anything needing broad
reading or judgement goes to an agent. Agent reports describe intent, not
outcome — I verify the diff and the test output myself before committing.
## Commit discipline and main-branch sanctity
- **Only the orchestrator commits.** No skill agent runs `git commit`; agents
leave their output as unstaged working-tree changes for me to inspect and shape
into commits.
- **main HEAD is sacrosanct — with one narrow `/boss` exception.** As a rule, no
`git reset`/`git revert` on main: it moves forward only via my commits, and
**pushed or user-ratified history is never rewound** (a bad landing there is
fixed forward with `git revert`, never `reset`). Wrong agent output is discarded
with `git checkout -- <paths>`/`git stash`. **The exception:** under `/boss`,
the orchestrator MAY `git reset --hard` to wind back its *own* autonomous,
**unpushed** commits made this run — those above the *session anchor* (main HEAD
at the run's start) — when it has run a line of work into a genuine dead end.
Never below the anchor; never a pushed commit; the discarded attempt is
hard-dropped (its trace survives in the run's reference issue). This is the
`/boss` rollback sandbox, not a licence to rewrite history.
- When a commit closes a Gitea issue, reference it in the body: `closes #N`
(or `refs #N` for non-final work).
## Design rationale ≠ implementation effort
Design choices are justified by substance — semantics, structural fit, what the
design permits vs forbids, compositional clarity, future-proofing. Implementation
effort ("approach A touches 250 sites, B touches 1") is an observation about the
current code, not a rationale. Effort is at most a named tiebreaker after
substantive reasons tie.
## Bug fixes — TDD, always
Bug fixes are RED-first and autonomous: the failing test exists in the working
tree before any fix. The `debug` skill is mandatory for any observable
misbehaviour (failing test, panic, wrong output).
## Domain invariants (load-bearing — never silently violate)
These are the contracts the whole design rests on. A change that breaks one is a
design decision, not a refactor, and belongs in the ledger.
1. **Determinism.** A backtest is a deterministic, synchronous, non-concurrent
event loop that reaches a unique state after each input tick. Same input →
same run, reproducibly. Two backtests are fully disjoint → concurrently
executable without locking. Parallelism is *across* sims, never within one.
2. **Causality / no look-ahead.** A node sees only the past. Look-ahead is made
structurally impossible (read-only input windows that end at the cursor;
resamplers emit a bar only once it is complete), not merely discouraged.
3. **One merge, at ingestion only.** Heterogeneous timestamped sources are
k-way-merged into a single chronological cycle stream at the ingestion
boundary. There is no merge / as-of join inside the graph.
4. **The four scalar base types, streamed as SoA.** Only `i64`, `f64`, `bool`,
`timestamp` are streamed, as columnar Structure-of-Arrays. Composite streams
(e.g. OHLCV) are bundles of base columns. Non-scalars (String, Records,
tables, calendars) exist as metadata beside the hot path, never in it.
5. **Acyclic dataflow.** The graph is a DAG; the only feedback path is an
explicit delay/state node (the RTL "register"). The "cycle" of the research
workflow is not a dataflow cycle.
6. **Record-then-replay determinism boundary.** Anything non-deterministic,
external, or slow (LLM news agents, web sources) is materialized into a
recorded, timestamped stream *before* it enters the engine. The sim never
makes a live external call mid-replay. (See `~/.claude/CLAUDE.md` for the
IONOS consent rule: external LLM calls happen at the recording/live-source
edge, with explicit per-session consent, never inside a backtest.)
7. **Strategy output is a directional bias stream; risk-based execution and
realistic brokers are decoupled downstream layers; signal quality is measured
in R.** The DAG expresses one state at t (C8: ≤1 record per `eval`), so a
strategy's primary, backtestable output is a signed, bounded **bias** `f64 ∈
[-1,+1]` per cycle — direction (sign) + conviction (magnitude, optional),
**unsized** (not an equity curve, not a position, not a size). Sizing leaves the
strategy: a decoupled **risk-based execution** layer `bias → stop-rule → Sizer →
Veto → position-management` (the **RiskExecutor** composite, per symbol, nested
in a Broker/Account composite) turns bias + a protective **stop** into a sized
intent. The **stop defines the risk unit R** (1R = the loss if stopped).
**Signal quality is measured in R** (R-multiples / expectancy), the account- and
instrument-agnostic yardstick — **Stage 1**: flat-1R sizing, feed-forward, no
equity feedback, the primary research loop. **Currency P&L is Stage 2** (deploy
viability): fixed-fractional sizing reads equity (compounding) → realistic
brokers apply real frictions → currency equity, entered only after `E[R] > 0`.
The single feedback (equity → Sizer) is cut by a `z⁻¹` register on the **fill
edge** (mark-to-market stays a same-cycle price read), encapsulated in the
executor composite; flat-1R vs compounding is a **structural axis** (C11). The
broker-independent **position-event table** (`event_ts, action[buy/sell/close],
position_id, instrument_id, volume`) remains the **decoupled, derived** Stage-2
audit layer — the first difference of the *book* (`deal = target book
in_flight`), a computed table (not a per-`eval` output, since one decision
instant may yield >1 event) — feeding realistic brokers. Brokers/executors are
ordinary downstream nodes, never part of the strategy; account mode
(netting/hedging) is a composition constraint.
8. **Deploy artifacts are frozen.** Hot-reload (cdylib) is an authoring-loop
tool only. The live bot is a statically-linked, versioned, frozen artifact —
never hot-swapped (audit trail: this bot = this commit).
9. **Engine / project separation.** This repo is the reusable *engine*. Research
projects are separate external repos that depend on it. Project-specific
signals live in a project's `nodes/`; cross-project-reusable nodes in shared
crates; universal blocks in `aura-std` (shipped here). Reuse is cargo-native;
the hot-reload unit is always the project-side `cdylib`. No user/project
signals in this repo (only `examples/` fixtures for the engine's own tests);
no multi-project manager or node registry inside aura. **A project is always
a Rust crate** (a cdylib library of node / strategy / experiment blueprints +
a static `Aura.toml`), hosted by `aura` during research and frozen to a
standalone binary for deploy.
10. **Authoring surface — all logic is Rust.** Nodes, strategies, *and*
experiments/harnesses are authored in native Rust via Claude Code + the
skills pipeline, using builder APIs. Declarative config (`Aura.toml`) carries
only static project context, never logic — no experiment/strategy DSL (the
RustAst trap). aura ships no embedded coding-LLM; IONOS LLMs are used only as
a runtime data source (news bias), with per-session consent, never in the
code path.
11. **Construction is a bootstrap phase.** Blueprints (param-generic graph-as-
data from a Rust builder) are bootstrapped into frozen instances (buffers
sized, topology fixed) by binding params + data + seed. Params configure and
size nodes but never change topology (a topology change is a different
blueprint). The harness — sources + strategy + broker node(s) + sinks — is
the root sim graph, itself bootstrapped, and is C1's disjoint unit; its
structural axes form the experiment matrix, its tuning params the sweep.
**The bootstrap is a compilation** to a flat, type-erased `FlatGraph`, wired
by raw index (composites inline — a composite is an authoring-level node, not
a runtime object; names survive only as non-load-bearing debug symbols, as
today). The flat graph is the target of **behaviour-preserving** optimisation,
C1 the correctness invariant — intra-graph (CSE/DCE) and across a sweep family
(sweep-invariant sub-graph computed once, shared via C11/C12). See C9/C19/C23.
12. **The World is the product; the playground is a trace explorer.** Three
ontological tiers: a *node* is an open, composable fragment (at most one
output, C8); a *harness* is the closed root graph that runs (sources +
strategy + brokers + sinks + clock — C1's disjoint unit / root scope),
**not** a node; the *World* is the program that dynamically constructs and
orchestrates *families* of harnesses (walk-forward / sweep / MC / comparison)
— aura's differentiator, not the single backtest. The World is a program:
nothing is displayable until it runs, and what is displayable is exactly what
a **sink** (C8) records into the registry (C18). The playground plays *any*
harness and is an execution viewer / trace explorer (structure before, live
streams during, recorded traces after) — never a scene editor; topology is
grown in Rust + hot-reload, runtime params are UI-tunable.
## Skills plugin: project facts
The few facts the skills plugin needs that genuinely vary per project.
Everything else is a fixed convention (see `~/dev/skills/docs/conventions.md`).
- **Code roots** — `crates`
- **Build** — `cargo build --workspace`
- **Test** — `cargo test --workspace`
- **Lint** — `cargo clippy --workspace --all-targets -- -D warnings`
- **Doc build** — `cargo doc --workspace --no-deps 2>&1`
- **Design ledger** — `docs/design/INDEX.md`
- **Glossary** — `docs/glossary.md`
- **Issue tracker** — Gitea, repo `Brummel/Aura`:
- browsable URL: `http://192.168.178.103:3000/Brummel/Aura/issues`
- list open issues: `tea issues ls --repo Brummel/Aura --state open`