fieldtest: harvest 2026-07-26 — 4 examples, 2 bugs / 4 friction / 1 spec-gap / 4 working
Source-blind per-cycle fieldtest over the sweep's four axes: the
declared-tap discovery loop closes in three commands without provoked
refusals; fixed{distance} is campaign-reachable with honest R
compression beside the vol regime; the C14 exit partition branches
reliably across sixteen probes; reference-semantics hashes agree
between record line and trace index across bare/no-op/substantive
overrides. Findings routed: both bugs fixed in-cycle (next commit),
frictions bundled as #345, the trace-overwrite spec gap consolidated
onto #311, the guide-rot instance evidenced on #344.
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
# Fieldtest corpus — harvest 2026-07-26
|
||||
|
||||
Downstream-consumer fixtures for the harvest cycle (`77ad046..f108291`): the
|
||||
declared-tap discovery view, the `fixed{distance}` risk regime, `exec`'s
|
||||
refusal families, and the zero-trade notes / override identity.
|
||||
|
||||
`TRANSCRIPT.md` is the recorded session — what was written first, what each
|
||||
command said, and where the two diverged. The findings drawn from it live in
|
||||
`docs/specs/fieldtest-harvest-2026-07-26.md` (git-ignored working file).
|
||||
|
||||
## Layout
|
||||
|
||||
| Path | What |
|
||||
|---|---|
|
||||
| `hv_1_blend_taps.ops.json` | crossover ⊕ momentum blended through `LinComb`, two declared taps |
|
||||
| `hv_1b_unnamed_taps.ops.json` | same graph without the `name` op (default root name `"graph"`) |
|
||||
| `hv_1c_named_notaps.ops.json` | same graph without the `tap` ops |
|
||||
| `hv_2_cross.ops.json` | SMA crossover with `fast.length` left open (the campaign axis) |
|
||||
| `hv_3_exec_refusals.sh` | replayable matrix of sixteen `aura exec` refusals |
|
||||
| `hv_4_flat.ops.json` | deliberately flat bias (`Const 0.0 → Bias`) — never trades |
|
||||
| `lab/` | a plain `aura new lab` scaffold holding the built blueprints and the process / campaign documents (`lab/runs/` is gitignored) |
|
||||
|
||||
## Replaying
|
||||
|
||||
From this directory, with the workspace built (`cargo build --workspace`) and
|
||||
the resulting `aura` on `PATH`:
|
||||
|
||||
```sh
|
||||
cd lab
|
||||
aura graph build < ../hv_1_blend_taps.ops.json > blueprints/ma-blend.json
|
||||
aura graph build < ../hv_1b_unnamed_taps.ops.json > blueprints/unnamed.json
|
||||
aura graph build < ../hv_1c_named_notaps.ops.json > blueprints/notaps.json
|
||||
aura graph build < ../hv_2_cross.ops.json > blueprints/hv-cross.json
|
||||
aura graph build < ../hv_4_flat.ops.json > blueprints/hv-flat.json
|
||||
aura graph register blueprints/ma-blend.json --name ma-blend
|
||||
aura graph register blueprints/hv-cross.json --name hv-cross
|
||||
aura graph register blueprints/hv-flat.json --name hv-flat
|
||||
aura process register blueprints/hv_2_process_sweep.json
|
||||
|
||||
aura graph introspect --taps blueprints/ma-blend.json
|
||||
aura exec blueprints/ma-blend.json --tap spread=mean --tap blend=record
|
||||
aura exec blueprints/hv_2_campaign_two_regimes.json # two stop regimes, real GER40 data
|
||||
aura exec blueprints/hv_4_campaign_zero_trade.json # the benign zero-trade cell
|
||||
sh ../hv_3_exec_refusals.sh
|
||||
```
|
||||
|
||||
The blueprints under `lab/blueprints/` are already built and registered at the
|
||||
content ids the campaign documents reference; rebuilding them reproduces those
|
||||
ids byte-identically.
|
||||
@@ -0,0 +1,251 @@
|
||||
# Fieldtest transcript — harvest 2026-07-26
|
||||
|
||||
Everything below was run against `target/debug/aura` built from the worktree at
|
||||
`f108291` (`cargo build --workspace`, exit 0). The project is `lab/`, a plain
|
||||
`aura new lab` scaffold; `lab/runs/` is gitignored by the scaffold's own
|
||||
`.gitignore` and is not part of the corpus.
|
||||
|
||||
Reading order: each section is one axis of the cycle, and each records what was
|
||||
written first, what the command said, and where the two diverged.
|
||||
|
||||
---
|
||||
|
||||
## Axis 1 — the declared-tap discovery loop
|
||||
|
||||
Task: *a colleague hands you `ma-blend.json`; find out what it measures,
|
||||
subscribe one tap to a summary fold and one to the full series, and run it.*
|
||||
|
||||
Fixture: `hv_1_blend_taps.ops.json` — an SMA crossover spread and a
|
||||
price-vs-slow momentum term blended through `LinComb` (`args` arity 2, per the
|
||||
guide's new §1 example), two declared taps, a `name` and a `doc` op.
|
||||
|
||||
```
|
||||
$ aura graph build < ../hv_1_blend_taps.ops.json > blueprints/ma-blend.json
|
||||
$ aura graph introspect --taps blueprints/ma-blend.json
|
||||
spread spread_node.value F64
|
||||
blend combo.value F64
|
||||
```
|
||||
|
||||
Built and introspected on the first try — the op-script needed no correction.
|
||||
The same view by content id, after registering:
|
||||
|
||||
```
|
||||
$ aura graph register blueprints/ma-blend.json --name ma-blend
|
||||
registered blueprint ab84251518597f5d06df8fbbaf82572dd5362e06d18af9d33e265e3cc44bf7c6 (…)
|
||||
label "ma-blend" -> ab84251518597f5d06df8fbbaf82572dd5362e06d18af9d33e265e3cc44bf7c6
|
||||
$ aura graph introspect --taps ab84251518597f5d06df8fbbaf82572dd5362e06d18af9d33e265e3cc44bf7c6
|
||||
spread spread_node.value F64
|
||||
blend combo.value F64
|
||||
```
|
||||
|
||||
The two edge cases, via `hv_1b_unnamed_taps.ops.json` (the same graph with the
|
||||
`name` op removed) and `hv_1c_named_notaps.ops.json` (named, no taps):
|
||||
|
||||
```
|
||||
$ aura graph introspect --taps blueprints/unnamed.json # default root name "graph"
|
||||
spread spread_node.value F64
|
||||
blend combo.value F64
|
||||
exit=0
|
||||
$ aura graph introspect --taps blueprints/notaps.json
|
||||
aura: note: blueprints/notaps.json declares no taps
|
||||
exit=0
|
||||
$ aura graph introspect --taps e22a7524f4d41b9e957cb9a2eae15e7fc27488fc9acbd3bd32f06d7ae52b87a3
|
||||
aura: note: e22a7524…b87a3 declares no taps
|
||||
exit=0
|
||||
```
|
||||
|
||||
Subscribing and running, with the fold roster discovered rather than guessed
|
||||
(`aura graph introspect --folds` lists all eight with a doc line each):
|
||||
|
||||
```
|
||||
$ aura exec blueprints/ma-blend.json --tap spread=mean --tap blend=record
|
||||
{"manifest":{…,"topology_hash":"ab8425…f7c6",…},"metrics":{…}}
|
||||
exit=0
|
||||
$ cat runs/traces/ma-blend/spread.json
|
||||
{"tap":"spread","kinds":["F64"],"ts":[18],"columns":[[0.00001148148148140867]]}
|
||||
$ head -c 200 runs/traces/ma-blend/blend.json
|
||||
{"tap":"blend","kinds":["F64"],"ts":[10,11,…,18],"columns":[[-0.00124…,…,0.004526…]]}
|
||||
```
|
||||
|
||||
`mean` landed exactly one row stamped at the last warm instant, `record` the
|
||||
full nine-row series — as C27 describes. The recovery half still works and is
|
||||
now redundant rather than load-bearing:
|
||||
|
||||
```
|
||||
$ aura exec blueprints/ma-blend.json --tap spred=mean
|
||||
aura: the tap plan names 'spred', but the blueprint declares no such tap — declared taps: spread, blend
|
||||
exit=1
|
||||
$ aura exec blueprints/ma-blend.json --tap spread=median
|
||||
aura: unknown fold 'median' — available: count, first, last, max, mean, min, record, sum
|
||||
exit=1
|
||||
```
|
||||
|
||||
**No refusal was needed to learn a real tap name.** Axis 1 answered yes.
|
||||
|
||||
---
|
||||
|
||||
## Axis 2 — the fixed-stop campaign regime
|
||||
|
||||
Task: *compare a constant 10-point stop against a volatility stop on the same
|
||||
SMA-crossover signal over a month of GER40.*
|
||||
|
||||
Fixtures: `hv_2_cross.ops.json` (crossover, `fast.length` left open),
|
||||
`hv_2_process_sweep.json` (one `std::sweep`, `sqn`/`argmax`),
|
||||
`hv_2_campaign_two_regimes.json` (`risk: [vol{3, 2.0}, fixed{10.0}]`,
|
||||
`fast.length ∈ {5,10,20}`, GER40, 2024-09-01 → 2024-10-01).
|
||||
|
||||
All three validate tiers passed on the first authoring, with no iteration:
|
||||
|
||||
```
|
||||
$ aura campaign validate blueprints/hv_2_campaign_two_regimes.json
|
||||
campaign document valid (intrinsic): 1 strategy(ies), 1 axes (3 points), 1 instrument(s), 1 window(s), 2 regime(s) — 2 cell(s)
|
||||
campaign document valid (referential): all references resolve, axes are in the param space
|
||||
campaign document valid (executable): pipeline shape and static guards pass
|
||||
```
|
||||
|
||||
```
|
||||
$ aura exec blueprints/hv_2_campaign_two_regimes.json
|
||||
… 6 member lines, 2 selection_report lines, 1 campaign_run line …
|
||||
aura: campaign run 0 recorded: 2 cells
|
||||
aura: traces persisted: c6ffe19d-0 (1 tap(s) x 2 cell(s))
|
||||
exit=0
|
||||
```
|
||||
|
||||
Per-member readout (`family_id`, stamped stop knobs, R metrics):
|
||||
|
||||
| family_id | stamped stop | n_trades | E[R] | sqn | avg_win_r | avg_loss_r |
|
||||
|---|---|---|---|---|---|---|
|
||||
| `c6ffe19d-0-GER40-w0-r0-s0-0` (fast=5) | `stop_length=3, stop_k=2.0` | 1781 | −0.0306 | −0.508 | 2.239 | −0.974 |
|
||||
| `c6ffe19d-0-GER40-w0-r0-s0-0` (fast=10) | `stop_length=3, stop_k=2.0` | 1604 | 0.0360 | 0.432 | 2.488 | −1.104 |
|
||||
| `c6ffe19d-0-GER40-w0-r0-s0-0` (fast=20) | `stop_length=3, stop_k=2.0` | 1648 | 0.1038 | 1.238 | 2.331 | −1.115 |
|
||||
| `c6ffe19d-0-GER40-w0-r1-s0-0` (fast=5) | `stop_distance=10.0` | 1542 | −0.0439 | −0.994 | 1.646 | −0.776 |
|
||||
| `c6ffe19d-0-GER40-w0-r1-s0-0` (fast=10) | `stop_distance=10.0` | 1387 | −0.0188 | −0.371 | 1.717 | −0.866 |
|
||||
| `c6ffe19d-0-GER40-w0-r1-s0-0` (fast=20) | `stop_distance=10.0` | 1449 | 0.0354 | 0.719 | 1.602 | −0.904 |
|
||||
|
||||
The R-unit semantics read coherently: under the fixed stop both tails compress
|
||||
(`avg_win_r` 1.60–1.72 against 2.24–2.49, `avg_loss_r` −0.78 to −0.90 against
|
||||
−0.97 to −1.12), which is what a stop that never adapts should do — the R unit
|
||||
is a constant 10 points rather than a per-cycle volatility multiple, so the same
|
||||
price excursion buys fewer R in either direction. Both regimes rank the same
|
||||
winner (`fast.length=20`) with different raw metrics, and the record keys them
|
||||
separately (`regime_ordinal` absent for the first regime, `1` for the second) —
|
||||
compared, never pooled, as C10 requires.
|
||||
|
||||
Trace dirs follow the documented ordinal rule:
|
||||
|
||||
```
|
||||
runs/traces/c6ffe19d-0/33af5e38-GER40-w0/r_equity.json
|
||||
runs/traces/c6ffe19d-0/33af5e38-GER40-w0-r1/r_equity.json
|
||||
```
|
||||
|
||||
but the **family ids do not** — see finding B1.
|
||||
|
||||
Regime validation refuses cleanly:
|
||||
|
||||
```
|
||||
$ aura exec <doc with risk:[{"fixed":{"distance":-1.0}}]>
|
||||
aura: campaign document invalid:
|
||||
risk[0]: stop length must be >= 1, k must be > 0, and a fixed-stop distance must be > 0
|
||||
exit=1
|
||||
$ aura exec <doc with risk:[{"trailing":{"distance":5.0}}]>
|
||||
aura: campaign document: unknown variant `trailing`, expected one of `vol`, `vol_tf`, `fixed`
|
||||
exit=1
|
||||
```
|
||||
|
||||
`emit` selectors were probed separately (short-window copies of the same doc):
|
||||
`family_table` produces the per-member `{"family_id","report"}` lines,
|
||||
`selection_report` the `{"selection_report":…}` line; the `{"campaign_run":…}`
|
||||
line is unconditional.
|
||||
|
||||
---
|
||||
|
||||
## Axis 3 — exec's refusal families
|
||||
|
||||
Fixture: `hv_3_exec_refusals.sh`, replayable from inside `lab/`. Full output in
|
||||
that script's own run; the branch table it establishes:
|
||||
|
||||
| invocation | exit | class |
|
||||
|---|---|---|
|
||||
| op-script array as target | 2 | routing-seam content fault |
|
||||
| `kind:"process"` document | 2 | routing-seam content fault |
|
||||
| `kind:"widget"` document | 2 | routing-seam content fault |
|
||||
| file that is not JSON | 2 | routing-seam content fault |
|
||||
| missing file | 1 | missing state |
|
||||
| well-formed unregistered 64-hex id | 1 | missing state |
|
||||
| bare non-id token | 1 | missing state |
|
||||
| open (free-knob) blueprint | 2 | blueprint-leg content fault |
|
||||
| `--override` without `=` | 2 | argv fault |
|
||||
| `--override nosuch.param=3` | 1 | blueprint-dependent |
|
||||
| `--override fast.length=abc` | 2 | argv fault (**misdiagnosed** — see B2) |
|
||||
| `--override fast.length=3.5` | 1 | blueprint-dependent |
|
||||
| `--override` colliding with a declared axis | 2 | argv fault |
|
||||
| `--tap` on a campaign target | 2 | argv fault |
|
||||
| campaign document content fault (empty axis / bad emit / bad regime) | 1 | campaign-leg content fault |
|
||||
| campaign completing with a failed cell | 3 | completed-with-failures |
|
||||
|
||||
The verbatim prose that a script author would branch on, for the three new
|
||||
routing-seam families:
|
||||
|
||||
```
|
||||
aura: exec: blueprints/opscript-target.json: this document is an op-script (a JSON array of construction ops), not something exec can run directly — build it into a blueprint first with `aura graph build < blueprints/opscript-target.json`, then exec the result
|
||||
aura: exec: blueprints/hv_2_process_sweep.json: this document's kind is "process", but exec executes only a campaign document or a fully-bound blueprint
|
||||
aura: exec: blueprints/notjson.json: target file is not valid JSON: expected ident at line 1 column 2
|
||||
```
|
||||
|
||||
Every one names the target, the fault, and (where one exists) the next command.
|
||||
The exit-3 case was confirmed against a weekend window with no GER40 bars:
|
||||
|
||||
```
|
||||
aura: warning: cell (33af5e38…, GER40, [1725148800000, 1725235200000]) failed at stage 0: no data for instrument GER40 in window … — recorded, campaign continues
|
||||
aura: campaign run 1 recorded: 1 cells, 1 failed (GER40: no_data)
|
||||
exit=3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Axis 4 — zero-trade notes and the override identity
|
||||
|
||||
### The two new notes
|
||||
|
||||
`hv_4_flat.ops.json` is a deliberately flat strategy (`Const 0.0 → Bias`), so it
|
||||
never opens a position. Executed bare, against the built-in synthetic stream:
|
||||
|
||||
```
|
||||
$ aura exec blueprints/hv-flat.json
|
||||
aura: note: the built-in synthetic stream (18 cycles) is a small smoke fixture; zero trades is expected here — bind real data via a campaign document's `data` section (`aura exec <campaign.json>`) for a meaningful run
|
||||
exit=0
|
||||
```
|
||||
|
||||
The same strategy over real GER40 bars, as `hv_4_campaign_zero_trade.json` (a
|
||||
non-walk-forward `std::sweep`, two axis points, both trading zero times):
|
||||
|
||||
```
|
||||
$ aura exec blueprints/hv_4_campaign_zero_trade.json
|
||||
aura: note: the cell's 2 reports all recorded zero trades; its metrics are vacuous, not a break-even result
|
||||
aura: campaign run 0 recorded: 1 cells
|
||||
exit=0
|
||||
```
|
||||
|
||||
Both carry the benign `aura: note: ` marker and leave the exit code at 0 — the
|
||||
grep-stable class C14 specifies, and the honest reading a consumer needs before
|
||||
mistaking `expectancy_r: 0.0` for break-even.
|
||||
|
||||
### Reference semantics under `--override`
|
||||
|
||||
Four executions of the same registered blueprint (`ma-blend`, base content id
|
||||
`ab8425…f7c6`), each with `--tap blend=record` so the trace store is written:
|
||||
|
||||
| run | record-line `topology_hash` | `runs/traces/ma-blend/index.json` hash | manifest `params` |
|
||||
|---|---|---|---|
|
||||
| bare | `ab8425…f7c6` | `ab8425…f7c6` | `[]` |
|
||||
| `--override bias.scale=1.0` (no-op) | `ab8425…f7c6` | `ab8425…f7c6` | `[["bias.scale",{"F64":1.0}]]` |
|
||||
| `--override bias.scale=2.0` | `ab8425…f7c6` | `ab8425…f7c6` | `[["bias.scale",{"F64":2.0}]]` |
|
||||
| `--override fast.length=4` | `ab8425…f7c6` | `ab8425…f7c6` | `[["fast.length",{"I64":4}]]` |
|
||||
|
||||
This is exactly the revised C24 clause: the executed run stamps the **loaded
|
||||
base document's** content id on both surfaces, the variation lives in `params`
|
||||
(moved out of `defaults`), and the hash stays store-resolvable — `ab8425…f7c6`
|
||||
is the id `graph register` printed. Record line and trace index never diverged.
|
||||
|
||||
What the same four runs also show is that all four wrote into the *same*
|
||||
`runs/traces/ma-blend/` directory, each overwriting the last — see finding S1.
|
||||
@@ -0,0 +1,23 @@
|
||||
[
|
||||
{"op": "name", "name": "ma-blend"},
|
||||
{"op": "doc", "text": "weighted blend of an SMA crossover spread and a price-vs-slow momentum term"},
|
||||
{"op": "source", "role": "price", "kind": "F64"},
|
||||
{"op": "add", "type": "SMA", "name": "fast", "bind": {"length": {"I64": 3}}},
|
||||
{"op": "add", "type": "SMA", "name": "slow", "bind": {"length": {"I64": 10}}},
|
||||
{"op": "add", "type": "Sub", "name": "spread_node"},
|
||||
{"op": "add", "type": "Sub", "name": "mom"},
|
||||
{"op": "feed", "role": "price", "into": ["fast.series", "slow.series", "mom.lhs"]},
|
||||
{"op": "connect", "from": "fast.value", "to": "spread_node.lhs"},
|
||||
{"op": "connect", "from": "slow.value", "to": "spread_node.rhs"},
|
||||
{"op": "connect", "from": "slow.value", "to": "mom.rhs"},
|
||||
{"op": "add", "type": "LinComb", "name": "combo",
|
||||
"args": {"arity": "2"},
|
||||
"bind": {"weights[0]": {"F64": 0.7}, "weights[1]": {"F64": 0.3}}},
|
||||
{"op": "connect", "from": "spread_node.value", "to": "combo.term[0]"},
|
||||
{"op": "connect", "from": "mom.value", "to": "combo.term[1]"},
|
||||
{"op": "add", "type": "Bias", "name": "bias", "bind": {"scale": {"F64": 1.0}}},
|
||||
{"op": "connect", "from": "combo.value", "to": "bias.signal"},
|
||||
{"op": "tap", "from": "spread_node.value", "as": "spread"},
|
||||
{"op": "tap", "from": "combo.value", "as": "blend"},
|
||||
{"op": "expose", "from": "bias.bias", "as": "bias"}
|
||||
]
|
||||
@@ -0,0 +1,121 @@
|
||||
[
|
||||
{
|
||||
"op": "doc",
|
||||
"text": "weighted blend of an SMA crossover spread and a price-vs-slow momentum term"
|
||||
},
|
||||
{
|
||||
"op": "source",
|
||||
"role": "price",
|
||||
"kind": "F64"
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"type": "SMA",
|
||||
"name": "fast",
|
||||
"bind": {
|
||||
"length": {
|
||||
"I64": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"type": "SMA",
|
||||
"name": "slow",
|
||||
"bind": {
|
||||
"length": {
|
||||
"I64": 10
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"type": "Sub",
|
||||
"name": "spread_node"
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"type": "Sub",
|
||||
"name": "mom"
|
||||
},
|
||||
{
|
||||
"op": "feed",
|
||||
"role": "price",
|
||||
"into": [
|
||||
"fast.series",
|
||||
"slow.series",
|
||||
"mom.lhs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"op": "connect",
|
||||
"from": "fast.value",
|
||||
"to": "spread_node.lhs"
|
||||
},
|
||||
{
|
||||
"op": "connect",
|
||||
"from": "slow.value",
|
||||
"to": "spread_node.rhs"
|
||||
},
|
||||
{
|
||||
"op": "connect",
|
||||
"from": "slow.value",
|
||||
"to": "mom.rhs"
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"type": "LinComb",
|
||||
"name": "combo",
|
||||
"args": {
|
||||
"arity": "2"
|
||||
},
|
||||
"bind": {
|
||||
"weights[0]": {
|
||||
"F64": 0.7
|
||||
},
|
||||
"weights[1]": {
|
||||
"F64": 0.3
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"op": "connect",
|
||||
"from": "spread_node.value",
|
||||
"to": "combo.term[0]"
|
||||
},
|
||||
{
|
||||
"op": "connect",
|
||||
"from": "mom.value",
|
||||
"to": "combo.term[1]"
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"type": "Bias",
|
||||
"name": "bias",
|
||||
"bind": {
|
||||
"scale": {
|
||||
"F64": 1.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"op": "connect",
|
||||
"from": "combo.value",
|
||||
"to": "bias.signal"
|
||||
},
|
||||
{
|
||||
"op": "tap",
|
||||
"from": "spread_node.value",
|
||||
"as": "spread"
|
||||
},
|
||||
{
|
||||
"op": "tap",
|
||||
"from": "combo.value",
|
||||
"as": "blend"
|
||||
},
|
||||
{
|
||||
"op": "expose",
|
||||
"from": "bias.bias",
|
||||
"as": "bias"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,115 @@
|
||||
[
|
||||
{
|
||||
"op": "name",
|
||||
"name": "ma-blend-notaps"
|
||||
},
|
||||
{
|
||||
"op": "doc",
|
||||
"text": "weighted blend of an SMA crossover spread and a price-vs-slow momentum term"
|
||||
},
|
||||
{
|
||||
"op": "source",
|
||||
"role": "price",
|
||||
"kind": "F64"
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"type": "SMA",
|
||||
"name": "fast",
|
||||
"bind": {
|
||||
"length": {
|
||||
"I64": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"type": "SMA",
|
||||
"name": "slow",
|
||||
"bind": {
|
||||
"length": {
|
||||
"I64": 10
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"type": "Sub",
|
||||
"name": "spread_node"
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"type": "Sub",
|
||||
"name": "mom"
|
||||
},
|
||||
{
|
||||
"op": "feed",
|
||||
"role": "price",
|
||||
"into": [
|
||||
"fast.series",
|
||||
"slow.series",
|
||||
"mom.lhs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"op": "connect",
|
||||
"from": "fast.value",
|
||||
"to": "spread_node.lhs"
|
||||
},
|
||||
{
|
||||
"op": "connect",
|
||||
"from": "slow.value",
|
||||
"to": "spread_node.rhs"
|
||||
},
|
||||
{
|
||||
"op": "connect",
|
||||
"from": "slow.value",
|
||||
"to": "mom.rhs"
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"type": "LinComb",
|
||||
"name": "combo",
|
||||
"args": {
|
||||
"arity": "2"
|
||||
},
|
||||
"bind": {
|
||||
"weights[0]": {
|
||||
"F64": 0.7
|
||||
},
|
||||
"weights[1]": {
|
||||
"F64": 0.3
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"op": "connect",
|
||||
"from": "spread_node.value",
|
||||
"to": "combo.term[0]"
|
||||
},
|
||||
{
|
||||
"op": "connect",
|
||||
"from": "mom.value",
|
||||
"to": "combo.term[1]"
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"type": "Bias",
|
||||
"name": "bias",
|
||||
"bind": {
|
||||
"scale": {
|
||||
"F64": 1.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"op": "connect",
|
||||
"from": "combo.value",
|
||||
"to": "bias.signal"
|
||||
},
|
||||
{
|
||||
"op": "expose",
|
||||
"from": "bias.bias",
|
||||
"as": "bias"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,14 @@
|
||||
[
|
||||
{"op": "name", "name": "hv-cross"},
|
||||
{"op": "doc", "text": "SMA crossover bias: fast minus slow, clamped into the bias contract"},
|
||||
{"op": "source", "role": "price", "kind": "F64"},
|
||||
{"op": "add", "type": "SMA", "name": "fast"},
|
||||
{"op": "add", "type": "SMA", "name": "slow", "bind": {"length": {"I64": 30}}},
|
||||
{"op": "feed", "role": "price", "into": ["fast.series", "slow.series"]},
|
||||
{"op": "add", "type": "Sub", "name": "sub"},
|
||||
{"op": "connect", "from": "fast.value", "to": "sub.lhs"},
|
||||
{"op": "connect", "from": "slow.value", "to": "sub.rhs"},
|
||||
{"op": "add", "type": "Bias", "name": "bias", "bind": {"scale": {"F64": 0.5}}},
|
||||
{"op": "connect", "from": "sub.value", "to": "bias.signal"},
|
||||
{"op": "expose", "from": "bias.bias", "as": "bias"}
|
||||
]
|
||||
@@ -0,0 +1,48 @@
|
||||
#!/bin/sh
|
||||
# hv_3 — exec's refusal families as a script author meets them.
|
||||
#
|
||||
# Run from inside the sibling `lab/` project (a plain `aura new` scaffold), with
|
||||
# `aura` on PATH:
|
||||
#
|
||||
# cd lab && sh ../hv_3_exec_refusals.sh
|
||||
#
|
||||
# The question this fixture answers: can a script author branch on exit code
|
||||
# alone, without parsing stderr? Each block prints the invocation, its exit
|
||||
# code, and its verbatim stderr.
|
||||
|
||||
set -u
|
||||
|
||||
probe() {
|
||||
printf -- '--- $ aura exec %s\n' "$*"
|
||||
aura exec "$@" >/dev/null 2>/tmp/aura-hv3.err
|
||||
printf 'exit=%s\n' "$?"
|
||||
cat /tmp/aura-hv3.err
|
||||
printf '\n'
|
||||
}
|
||||
|
||||
# Fixtures the probes need, built in place.
|
||||
cp ../hv_1_blend_taps.ops.json blueprints/opscript-target.json
|
||||
printf 'this is not json at all\n' >blueprints/notjson.json
|
||||
printf '{"format_version":1,"kind":"widget","name":"x"}' >blueprints/wrongkind.json
|
||||
|
||||
# --- target classification at the routing seam (C14's pinned partition) ---
|
||||
probe blueprints/opscript-target.json # op-script array
|
||||
probe blueprints/hv_2_process_sweep.json # kind:"process"
|
||||
probe blueprints/wrongkind.json # kind:"widget"
|
||||
probe blueprints/notjson.json # not JSON at all
|
||||
probe blueprints/does-not-exist.json # no such file
|
||||
probe 0000000000000000000000000000000000000000000000000000000000000000 # unregistered id
|
||||
probe some-random-token # neither file nor id
|
||||
|
||||
# --- the blueprint leg's own gates ---
|
||||
probe blueprints/hv-cross.json # open (free-knob) blueprint
|
||||
probe blueprints/hv-cross.json --override fast.length=7 # ... and an override that does not close it
|
||||
|
||||
# --- --override argument faults ---
|
||||
probe blueprints/ma-blend.json --override fast.length # no '='
|
||||
probe blueprints/ma-blend.json --override nosuch.param=3 # no such param
|
||||
probe blueprints/ma-blend.json --override fast.length=abc # unparseable value
|
||||
probe blueprints/ma-blend.json --override fast.length=3.5 # wrong scalar kind
|
||||
|
||||
# --- flags that do not apply to the chosen leg ---
|
||||
probe blueprints/hv_2_campaign_two_regimes.json --tap spread=mean
|
||||
@@ -0,0 +1,11 @@
|
||||
[
|
||||
{"op": "name", "name": "hv-flat"},
|
||||
{"op": "doc", "text": "deliberately flat bias: emits a constant zero signal, so the executor never opens a position"},
|
||||
{"op": "source", "role": "price", "kind": "F64"},
|
||||
{"op": "add", "type": "Const", "name": "flat", "bind": {"value": {"F64": 0.0}}},
|
||||
{"op": "feed", "role": "price", "into": ["flat.clock"]},
|
||||
{"op": "add", "type": "Bias", "name": "bias", "bind": {"scale": {"F64": 1.0}}},
|
||||
{"op": "connect", "from": "flat.value", "to": "bias.signal"},
|
||||
{"op": "tap", "from": "flat.value", "as": "flat_signal"},
|
||||
{"op": "expose", "from": "bias.bias", "as": "bias"}
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
/runs
|
||||
@@ -0,0 +1,4 @@
|
||||
# Static project context only (C17); paths only.
|
||||
[paths]
|
||||
runs = "runs"
|
||||
# data = "/path/to/archive" # the recorded-data root; defaults to the built-in path
|
||||
@@ -0,0 +1,24 @@
|
||||
# lab — an aura research project (data-only)
|
||||
|
||||
This directory is an aura project: blueprints + research documents over the
|
||||
std vocabulary, anchored by `Aura.toml`. There is no crate and no build step.
|
||||
|
||||
- Run: `aura exec blueprints/signal.json` (single smoke run, synthetic stream
|
||||
— the starter is closed; all params bound; bound values are defaults —
|
||||
`--override NODE.PARAM=VALUE` may override one for this run (#246))
|
||||
- Campaign: `aura exec <campaign.json>` executes a registered campaign
|
||||
document (file or content id) — the multi-cell/axis surface
|
||||
- Axes: `aura graph introspect --params blueprints/signal.json` lists the
|
||||
open + bound-overridable axes, RAW `<node>.<param>` names (#328)
|
||||
- Native nodes: when the project needs its first project-specific node,
|
||||
`aura nodes new <name>` scaffolds a node crate beside this project and
|
||||
attaches it via `[nodes]` in `Aura.toml` (build it with `cargo build`).
|
||||
- Topology is data (`blueprints/*.json`); results land in `runs/`.
|
||||
- Execution model: a strategy emits a bias in [-1,+1] per cycle, held as the
|
||||
continuously-tracked target position; a protective stop defines the risk
|
||||
unit R, and quality metrics are R-based. Entry signals become held state
|
||||
via the signal-side latch/edge-pulse idiom (see
|
||||
`aura graph introspect --vocabulary`).
|
||||
- Data plane: process/campaign documents are authored directly with
|
||||
`aura process` / `aura campaign`, growing one from a bare `{}` via
|
||||
`aura campaign introspect --unwired`.
|
||||
@@ -0,0 +1 @@
|
||||
{"format_version":1,"blueprint":{"name":"hv-cross","doc":"SMA crossover bias: fast minus slow, clamped into the bias contract","nodes":[{"primitive":{"type":"SMA","name":"fast"}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":30}}]}},{"primitive":{"type":"Sub","name":"sub"}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":0.5}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":3,"field":0,"name":"bias"}]}}
|
||||
@@ -0,0 +1 @@
|
||||
{"format_version":1,"blueprint":{"name":"hv-flat","doc":"deliberately flat bias: emits a constant zero signal, so the executor never opens a position","nodes":[{"primitive":{"type":"Const","name":"flat","bound":[{"pos":0,"name":"value","kind":"F64","value":{"F64":0.0}}]}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":1.0}}]}}],"edges":[{"from":0,"to":1,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0}],"source":"F64"}],"output":[{"node":1,"field":0,"name":"bias"}],"taps":[{"name":"flat_signal","from":{"node":0,"field":0}}]}}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"format_version": 1,
|
||||
"kind": "campaign",
|
||||
"name": "hv-ger40-cross-vol-vs-fixed",
|
||||
"seed": 7,
|
||||
"data": {
|
||||
"instruments": ["GER40"],
|
||||
"windows": [ { "from_ms": 1725148800000, "to_ms": 1727740800000 } ]
|
||||
},
|
||||
"risk": [
|
||||
{ "vol": { "length": 3, "k": 2.0 } },
|
||||
{ "fixed": { "distance": 10.0 } }
|
||||
],
|
||||
"strategies": [
|
||||
{
|
||||
"ref": { "content_id": "33af5e380d5e67d9b5464b4b9644419db1cdd2480104bd520592f6addcaeef34" },
|
||||
"axes": {
|
||||
"fast.length": { "kind": "I64", "values": [5, 10, 20] }
|
||||
}
|
||||
}
|
||||
],
|
||||
"process": { "ref": { "content_id": "c7fb729490770305d657e1e78d8daf12b1cd4dab990aea8cf4620c0b369a3c48" } },
|
||||
"presentation": { "persist_taps": ["r_equity"], "emit": ["family_table", "selection_report"] }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"format_version": 1,
|
||||
"kind": "process",
|
||||
"name": "hv-sweep-sqn",
|
||||
"description": "Single-stage sweep ranked by SQN — the minimum methodology for comparing two stop regimes.",
|
||||
"pipeline": [
|
||||
{ "block": "std::sweep", "metric": "sqn", "select": "argmax" }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"format_version": 1,
|
||||
"kind": "campaign",
|
||||
"name": "hv-ger40-flat-zero-trade",
|
||||
"seed": 3,
|
||||
"data": {
|
||||
"instruments": [
|
||||
"GER40"
|
||||
],
|
||||
"windows": [
|
||||
{
|
||||
"from_ms": 1725235200000,
|
||||
"to_ms": 1725408000000
|
||||
}
|
||||
]
|
||||
},
|
||||
"strategies": [
|
||||
{
|
||||
"ref": {
|
||||
"content_id": "6075193021f0226fdede891814401fc4abc51e19f4b7e2605113799fd781a6c7"
|
||||
},
|
||||
"axes": {
|
||||
"bias.scale": {
|
||||
"kind": "F64",
|
||||
"values": [
|
||||
1.0,
|
||||
2.0
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"process": {
|
||||
"ref": {
|
||||
"content_id": "c7fb729490770305d657e1e78d8daf12b1cd4dab990aea8cf4620c0b369a3c48"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"persist_taps": [],
|
||||
"emit": [
|
||||
"family_table"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"format_version":2,"blueprint":{"name":"ma-blend","doc":"weighted blend of an SMA crossover spread and a price-vs-slow momentum term","nodes":[{"primitive":{"type":"SMA","name":"fast","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":3}}]}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":10}}]}},{"primitive":{"type":"Sub","name":"spread_node"}},{"primitive":{"type":"Sub","name":"mom"}},{"primitive":{"type":"LinComb","name":"combo","args":[{"name":"arity","value":"2"}],"bound":[{"pos":0,"name":"weights[0]","kind":"F64","value":{"F64":0.7}},{"pos":1,"name":"weights[1]","kind":"F64","value":{"F64":0.3}}]}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":1.0}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":1,"to":3,"slot":1,"from_field":0},{"from":2,"to":4,"slot":0,"from_field":0},{"from":3,"to":4,"slot":1,"from_field":0},{"from":4,"to":5,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0},{"node":3,"slot":0}],"source":"F64"}],"output":[{"node":5,"field":0,"name":"bias"}],"taps":[{"name":"spread","from":{"node":2,"field":0}},{"name":"blend","from":{"node":4,"field":0}}]}}
|
||||
@@ -0,0 +1 @@
|
||||
{"format_version":2,"blueprint":{"name":"ma-blend-notaps","doc":"weighted blend of an SMA crossover spread and a price-vs-slow momentum term","nodes":[{"primitive":{"type":"SMA","name":"fast","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":3}}]}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":10}}]}},{"primitive":{"type":"Sub","name":"spread_node"}},{"primitive":{"type":"Sub","name":"mom"}},{"primitive":{"type":"LinComb","name":"combo","args":[{"name":"arity","value":"2"}],"bound":[{"pos":0,"name":"weights[0]","kind":"F64","value":{"F64":0.7}},{"pos":1,"name":"weights[1]","kind":"F64","value":{"F64":0.3}}]}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":1.0}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":1,"to":3,"slot":1,"from_field":0},{"from":2,"to":4,"slot":0,"from_field":0},{"from":3,"to":4,"slot":1,"from_field":0},{"from":4,"to":5,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0},{"node":3,"slot":0}],"source":"F64"}],"output":[{"node":5,"field":0,"name":"bias"}]}}
|
||||
@@ -0,0 +1 @@
|
||||
{"format_version":1,"blueprint":{"name":"lab_signal","doc":"fast/slow SMA difference clamped into a directional bias","nodes":[{"primitive":{"type":"SMA","name":"fast","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":2}}]}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":4}}]}},{"primitive":{"type":"Sub"}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":0.5}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":3,"field":0,"name":"bias"}]}}
|
||||
@@ -0,0 +1 @@
|
||||
{"format_version":2,"blueprint":{"name":"graph","doc":"weighted blend of an SMA crossover spread and a price-vs-slow momentum term","nodes":[{"primitive":{"type":"SMA","name":"fast","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":3}}]}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":10}}]}},{"primitive":{"type":"Sub","name":"spread_node"}},{"primitive":{"type":"Sub","name":"mom"}},{"primitive":{"type":"LinComb","name":"combo","args":[{"name":"arity","value":"2"}],"bound":[{"pos":0,"name":"weights[0]","kind":"F64","value":{"F64":0.7}},{"pos":1,"name":"weights[1]","kind":"F64","value":{"F64":0.3}}]}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":1.0}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":1,"to":3,"slot":1,"from_field":0},{"from":2,"to":4,"slot":0,"from_field":0},{"from":3,"to":4,"slot":1,"from_field":0},{"from":4,"to":5,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0},{"node":3,"slot":0}],"source":"F64"}],"output":[{"node":5,"field":0,"name":"bias"}],"taps":[{"name":"spread","from":{"node":2,"field":0}},{"name":"blend","from":{"node":4,"field":0}}]}}
|
||||
Reference in New Issue
Block a user