fieldtest: cycle 0106 research-artifact surface — GREEN core, authoring on-ramp findings
Downstream-consumer run (roles 5/6b, public interface only) over the document-authoring loop: canonicalization byte-exact and stable across reformatting (F8), intrinsic validation thorough and path-addressed (F9), referential tier correct across the project boundary with progressive resolution (F10). Findings: one bug (F2: introspect --block advertises an identity_id process ref the validator refuses), four frictions (F1 envelope not derivable from introspection incl. the block-id/field-name mismatch and the un-blocked seed; F3 content:-prefixed id vs bare-hash refs breaking referential resolution silently; F4 --unwired only reports omitted refs, which validate then fails; F5 strategy refs unresolvable from the public surface — no blueprint register/file-content-id/param-space verbs), one doc-gap (F6 no worked examples, metric vocabulary unlisted), and the spec-gap F7: a valid, registered campaign has no run path — the intent-persistence re-test the executor decision waited on. refs #189, #188
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
# Fieldtest — cycle 0106 — research-artifact document surface — 2026-07-03
|
||||
|
||||
**Role:** downstream consumer (methodology designer / campaign designer), public
|
||||
interface only (ledger C18/C20/C22/C25, glossary, CLI help + introspection, the
|
||||
fieldtests corpus shape). No `crates/` source read. Binary built from HEAD;
|
||||
referential tier exercised after `cargo build` in the scaffolded `demo_project/`.
|
||||
|
||||
## Fixtures (this directory)
|
||||
|
||||
- `c0106_1_attempt1..3.json`, `c0106_1_partial.json`, `c0106_1_reformatted.json`
|
||||
— process doc: 3-round envelope discovery + canonicalization + empty-gate probe
|
||||
- `c0106_2_strategy.json` — SMA-cross op-script (content id `573262…`)
|
||||
- `c0106_2_campaign_attempt1..2.json`, `c0106_2_campaign_valid.json`,
|
||||
`c0106_2_campaign_partial.json`, `c0106_2_probe_{barestring,refnull,noref,prefixed}.json`
|
||||
- `c0106_4_{emptyaxis,floatinint,noinstr,backwardwin,badmetric,badcmp,process_identity}.json`
|
||||
- `demo_project/` — `aura new` scaffold + campaign/process docs + in-project store
|
||||
- `runs/{processes,campaigns}/…` — content-addressed store written by `register`
|
||||
|
||||
## Findings
|
||||
|
||||
### F2 [bug] `introspect --block std::process_ref` advertises `identity_id`, which the validator refuses
|
||||
The slot line prints `ref required, one of { content_id } | { identity_id }`
|
||||
while validation refuses an identity_id process ref (`process.ref: a process is
|
||||
referenced by content id in this version`, exit 1); the block header itself says
|
||||
"(content id)". The 0106 audit fixed the parallel `--unwired` hint but not this
|
||||
`--block` describe. Repro: `aura campaign introspect --block std::process_ref`.
|
||||
→ debug (RED-pin content-id-only).
|
||||
|
||||
### F1 [friction] Document envelope not derivable from the introspection contract
|
||||
Process doc took 3 blind rounds (`stages` → wrong-kind refusal that does not
|
||||
name the `kind` key → `unknown field stages, expected one of … pipeline`).
|
||||
Campaign worse: block ids `std::data/std::strategy/std::process_ref/std::presentation`
|
||||
map to fields `data`/`strategies` (plural)/`process`/`presentation`, and the
|
||||
required `seed` appears in NO block (`missing field seed`). Introspection
|
||||
describes block slots but never the envelope composing them.
|
||||
→ plan (envelope/skeleton introspection; reconcile block ids with field names).
|
||||
|
||||
### F3 [friction] content-id printed `content:`-prefixed, refs need the bare hash — silent referential break
|
||||
`process|campaign introspect --content-id` print `content:<hash>`; `graph
|
||||
introspect --content-id` prints bare. Pasting the prefixed form into a ref
|
||||
PASSES intrinsic validation (different id!) then fails referential resolution
|
||||
without the error naming the prefix as the cause.
|
||||
→ plan (one display form across verbs; accept/strip the prefix in refs).
|
||||
|
||||
### F4 [friction] `--unwired` open-slot hints fire only when a ref is omitted, which `validate` fails
|
||||
The ref open-slot hints appear only with the `ref` key removed (then validate
|
||||
says `missing field ref`); placeholders `{}` / `null` give cryptic serde errors
|
||||
(`expected map with a single key`; `invalid type: null, expected string or map`)
|
||||
and `--unwired` reports `no open slots`. One draft file cannot both
|
||||
validate-so-far and report its open slots.
|
||||
→ plan (treat `ref: {}`/`null` as the reported open slot).
|
||||
|
||||
### F5 [friction] A campaign's strategy ref cannot be driven to resolve from the public surface
|
||||
No verb computes a blueprint FILE's content id (`graph introspect --content-id`
|
||||
is op-list-only), no verb registers a standalone blueprint into the store (only
|
||||
sweep/mc/walkforward populate it), no verb lists a strategy's `param_space`
|
||||
(axis names/kinds undiscoverable). The ref stays unresolved unless a sweep
|
||||
already ran — but the sweep is what the campaign describes (see F7).
|
||||
→ planner (blueprint register + file content-id + param-space listing; scope
|
||||
with the executor).
|
||||
|
||||
### F6 [doc-gap] No worked document example anywhere; `metric_vocabulary` unlisted by any verb
|
||||
Zero process/campaign JSON examples in docs/ or the corpus at test time. Block
|
||||
hints say "metric name (see metric_vocabulary)" but no verb lists it, and
|
||||
`unknown metric` does not enumerate valid names.
|
||||
→ tighten docs (worked example per type; metric enumeration via introspect or
|
||||
in the refusal).
|
||||
|
||||
### F7 [spec_gap] No executor — a valid campaign has no run path (the pending executor decision)
|
||||
After registering a campaign whose process declares sweep → gate → walk_forward
|
||||
over a strategy + 9-point axis, the expected `aura campaign run <id>` does not
|
||||
exist; `aura run`/`sweep` take no campaign. The campaign+process pair is a
|
||||
complete declarative spec of machinery the engine already runs
|
||||
(sweep_family / optimize_deflated / walkforward_family); what exists is
|
||||
persisted, content-addressed, legible intent that nothing consumes. The trail
|
||||
ends at `runs/campaigns/<hash>.json`.
|
||||
→ this run IS the intent-persistence re-test the executor decision waited on;
|
||||
scope the executor.
|
||||
|
||||
### F8 [working] Content-id canonicalization is exact and stable
|
||||
Reordered keys + reflowed whitespace + `0` vs `0.0` yield the identical content
|
||||
id; stored form is compact, declared-key-order, no trailing newline, and
|
||||
re-derives its id. → carry-on.
|
||||
|
||||
### F9 [working] Intrinsic validation is thorough, path-addressed, house-style
|
||||
Empty gate, unknown metric/cmp, empty axis, float-in-I64, empty instruments,
|
||||
backwards window, invalid-doc content-id refusal — all precise prose, exit
|
||||
codes usage=2 / invalid=1 / ok=0. Minor: the kind-mismatch error omits the axis
|
||||
path its siblings carry. → carry-on.
|
||||
|
||||
### F10 [working] Referential tier correct across the project boundary
|
||||
Outside: skip line names the search root. Inside without dylib: actionable
|
||||
build hint. Inside built: per-store diagnostics, progressive resolution as
|
||||
documents get registered. → carry-on.
|
||||
|
||||
## Verdict
|
||||
|
||||
The shipped surface delivers standalone value: methodology and experiment
|
||||
intent are nameable, diffable (byte-exact content id surviving reformatting),
|
||||
and auditable (content-addressed, referentially checkable, honest
|
||||
diagnostics) — with no compile and no run. The shortfall concentrates in the
|
||||
authoring on-ramp (envelope invisible to introspection, no worked example, the
|
||||
`content:`-vs-bare id trap) and the terminal wall: the campaign+process pair
|
||||
fully specifies a sweep→gate→walk-forward the engine already runs, yet nothing
|
||||
consumes it. The single most valuable next capability is an executor
|
||||
(`aura campaign run <id>` driving the existing family machinery) — the one
|
||||
addition that turns persisted intent into a result and retroactively gives
|
||||
every id, store, axis, and gate its reason to exist.
|
||||
|
||||
Counts: bug 1 (F2), friction 4 (F1/F3/F4/F5), doc-gap 1 (F6), spec-gap 1 (F7),
|
||||
working 3 (F8/F9/F10).
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"format_version": 1,
|
||||
"name": "robust-sweep-v1",
|
||||
"stages": [
|
||||
{ "block": "std::sweep", "metric": "sqn", "select": "argmax", "deflate": true },
|
||||
{ "block": "std::gate", "all": [ { "metric": "expectancy_r", "cmp": "gt", "value": 0.0 } ] },
|
||||
{ "block": "std::walk_forward", "folds": 4, "in_sample_bars": 5000, "out_of_sample_bars": 1000, "metric": "sqn", "select": "argmax" }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"format_version": 1,
|
||||
"kind": "process",
|
||||
"name": "robust-sweep-v1",
|
||||
"stages": [
|
||||
{ "block": "std::sweep", "metric": "sqn", "select": "argmax", "deflate": true },
|
||||
{ "block": "std::gate", "all": [ { "metric": "expectancy_r", "cmp": "gt", "value": 0.0 } ] },
|
||||
{ "block": "std::walk_forward", "folds": 4, "in_sample_bars": 5000, "out_of_sample_bars": 1000, "metric": "sqn", "select": "argmax" }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"format_version": 1,
|
||||
"kind": "process",
|
||||
"name": "robust-sweep-v1",
|
||||
"description": "Sweep with trials-deflation, gate on positive expectancy, then walk-forward confirm.",
|
||||
"pipeline": [
|
||||
{ "block": "std::sweep", "metric": "sqn", "select": "argmax", "deflate": true },
|
||||
{ "block": "std::gate", "all": [ { "metric": "expectancy_r", "cmp": "gt", "value": 0.0 } ] },
|
||||
{ "block": "std::walk_forward", "folds": 4, "in_sample_bars": 5000, "out_of_sample_bars": 1000, "metric": "sqn", "select": "argmax" }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"format_version": 1,
|
||||
"kind": "process",
|
||||
"name": "robust-sweep-v1",
|
||||
"pipeline": [
|
||||
{ "block": "std::sweep", "metric": "sqn", "select": "argmax" },
|
||||
{ "block": "std::gate", "all": [] }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"kind": "process",
|
||||
"format_version": 1,
|
||||
"name": "robust-sweep-v1",
|
||||
"description": "Sweep with trials-deflation, gate on positive expectancy, then walk-forward confirm.",
|
||||
"pipeline": [
|
||||
{
|
||||
"select": "argmax",
|
||||
"block": "std::sweep",
|
||||
"deflate": true,
|
||||
"metric": "sqn"
|
||||
},
|
||||
{ "block": "std::gate", "all": [ { "cmp": "gt", "metric": "expectancy_r", "value": 0 } ] },
|
||||
{
|
||||
"block": "std::walk_forward",
|
||||
"folds": 4,
|
||||
"in_sample_bars": 5000,
|
||||
"out_of_sample_bars": 1000,
|
||||
"metric": "sqn",
|
||||
"select": "argmax"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"format_version": 1,
|
||||
"kind": "campaign",
|
||||
"name": "sma-cross-length-sweep",
|
||||
"data": { "instruments": ["EURUSD"], "windows": [ { "from_ms": 1704067200000, "to_ms": 1711929600000 } ] },
|
||||
"strategy": {
|
||||
"ref": { "content_id": "573262f2650fd2749427adaec41c148744597c6923fd7e829cb46ceccd105777" },
|
||||
"axes": {
|
||||
"fast.length": { "kind": "I64", "values": [4, 8, 12] },
|
||||
"slow.length": { "kind": "I64", "values": [16, 24, 32] }
|
||||
}
|
||||
},
|
||||
"process_ref": { "ref": { "content_id": "aac13f6e9eecda09364d263ead9174161efb8292d10f15e2f8846b4ee94fd10f" } },
|
||||
"presentation": { "persist_taps": ["bias", "equity"], "emit": ["family_table", "selection_report"] }
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"format_version": 1,
|
||||
"kind": "campaign",
|
||||
"name": "sma-cross-length-sweep",
|
||||
"seed": 42,
|
||||
"data": { "instruments": ["EURUSD"], "windows": [ { "from_ms": 1704067200000, "to_ms": 1711929600000 } ] },
|
||||
"strategies": [
|
||||
{
|
||||
"ref": { "content_id": "573262f2650fd2749427adaec41c148744597c6923fd7e829cb46ceccd105777" },
|
||||
"axes": {
|
||||
"fast.length": { "kind": "I64", "values": [4, 8, 12] },
|
||||
"slow.length": { "kind": "I64", "values": [16, 24, 32] }
|
||||
}
|
||||
}
|
||||
],
|
||||
"process": { "ref": { "content_id": "aac13f6e9eecda09364d263ead9174161efb8292d10f15e2f8846b4ee94fd10f" } },
|
||||
"presentation": { "persist_taps": ["bias", "equity"], "emit": ["family_table", "selection_report"] }
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"format_version": 1,
|
||||
"kind": "campaign",
|
||||
"name": "sma-cross-length-sweep",
|
||||
"seed": 42,
|
||||
"data": { "instruments": ["EURUSD"], "windows": [ { "from_ms": 1704067200000, "to_ms": 1711929600000 } ] },
|
||||
"strategies": [
|
||||
{
|
||||
"ref": {},
|
||||
"axes": {
|
||||
"fast.length": { "kind": "I64", "values": [4, 8, 12] }
|
||||
}
|
||||
}
|
||||
],
|
||||
"process": { "ref": {} },
|
||||
"presentation": { "persist_taps": ["bias"], "emit": ["family_table"] }
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"format_version": 1,
|
||||
"kind": "campaign",
|
||||
"name": "sma-cross-length-sweep",
|
||||
"seed": 42,
|
||||
"data": { "instruments": ["EURUSD"], "windows": [ { "from_ms": 1704067200000, "to_ms": 1711929600000 } ] },
|
||||
"strategies": [
|
||||
{
|
||||
"ref": { "content_id": "573262f2650fd2749427adaec41c148744597c6923fd7e829cb46ceccd105777" },
|
||||
"axes": {
|
||||
"fast.length": { "kind": "I64", "values": [4, 8, 12] },
|
||||
"slow.length": { "kind": "I64", "values": [16, 24, 32] }
|
||||
}
|
||||
}
|
||||
],
|
||||
"process": { "ref": { "content_id": "aac13f6e9eecda09364d263ead9174161efb8292d10f15e2f8846b4ee94fd10f" } },
|
||||
"presentation": { "persist_taps": ["bias", "equity"], "emit": ["family_table", "selection_report"] }
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"format_version": 1,
|
||||
"kind": "campaign",
|
||||
"name": "sma-cross-length-sweep",
|
||||
"seed": 42,
|
||||
"data": {
|
||||
"instruments": [
|
||||
"EURUSD"
|
||||
],
|
||||
"windows": [
|
||||
{
|
||||
"from_ms": 1704067200000,
|
||||
"to_ms": 1711929600000
|
||||
}
|
||||
]
|
||||
},
|
||||
"strategies": [
|
||||
{
|
||||
"ref": "573262f2650fd2749427adaec41c148744597c6923fd7e829cb46ceccd105777",
|
||||
"axes": {
|
||||
"fast.length": {
|
||||
"kind": "I64",
|
||||
"values": [
|
||||
4,
|
||||
8,
|
||||
12
|
||||
]
|
||||
},
|
||||
"slow.length": {
|
||||
"kind": "I64",
|
||||
"values": [
|
||||
16,
|
||||
24,
|
||||
32
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"process": {
|
||||
"ref": "aac13f6e9eecda09364d263ead9174161efb8292d10f15e2f8846b4ee94fd10f"
|
||||
},
|
||||
"presentation": {
|
||||
"persist_taps": [
|
||||
"bias",
|
||||
"equity"
|
||||
],
|
||||
"emit": [
|
||||
"family_table",
|
||||
"selection_report"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"format_version": 1,
|
||||
"kind": "campaign",
|
||||
"name": "sma-cross-length-sweep",
|
||||
"seed": 42,
|
||||
"data": {
|
||||
"instruments": [
|
||||
"EURUSD"
|
||||
],
|
||||
"windows": [
|
||||
{
|
||||
"from_ms": 1704067200000,
|
||||
"to_ms": 1711929600000
|
||||
}
|
||||
]
|
||||
},
|
||||
"strategies": [
|
||||
{
|
||||
"axes": {
|
||||
"fast.length": {
|
||||
"kind": "I64",
|
||||
"values": [
|
||||
4,
|
||||
8,
|
||||
12
|
||||
]
|
||||
},
|
||||
"slow.length": {
|
||||
"kind": "I64",
|
||||
"values": [
|
||||
16,
|
||||
24,
|
||||
32
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"process": {},
|
||||
"presentation": {
|
||||
"persist_taps": [
|
||||
"bias",
|
||||
"equity"
|
||||
],
|
||||
"emit": [
|
||||
"family_table",
|
||||
"selection_report"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"format_version": 1,
|
||||
"kind": "campaign",
|
||||
"name": "sma-cross-length-sweep",
|
||||
"seed": 42,
|
||||
"data": {
|
||||
"instruments": [
|
||||
"EURUSD"
|
||||
],
|
||||
"windows": [
|
||||
{
|
||||
"from_ms": 1704067200000,
|
||||
"to_ms": 1711929600000
|
||||
}
|
||||
]
|
||||
},
|
||||
"strategies": [
|
||||
{
|
||||
"ref": {
|
||||
"content_id": "content:573262f2650fd2749427adaec41c148744597c6923fd7e829cb46ceccd105777"
|
||||
},
|
||||
"axes": {
|
||||
"fast.length": {
|
||||
"kind": "I64",
|
||||
"values": [
|
||||
4,
|
||||
8,
|
||||
12
|
||||
]
|
||||
},
|
||||
"slow.length": {
|
||||
"kind": "I64",
|
||||
"values": [
|
||||
16,
|
||||
24,
|
||||
32
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"process": {
|
||||
"ref": {
|
||||
"content_id": "content:aac13f6e9eecda09364d263ead9174161efb8292d10f15e2f8846b4ee94fd10f"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"persist_taps": [
|
||||
"bias",
|
||||
"equity"
|
||||
],
|
||||
"emit": [
|
||||
"family_table",
|
||||
"selection_report"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"format_version": 1,
|
||||
"kind": "campaign",
|
||||
"name": "sma-cross-length-sweep",
|
||||
"seed": 42,
|
||||
"data": {
|
||||
"instruments": [
|
||||
"EURUSD"
|
||||
],
|
||||
"windows": [
|
||||
{
|
||||
"from_ms": 1704067200000,
|
||||
"to_ms": 1711929600000
|
||||
}
|
||||
]
|
||||
},
|
||||
"strategies": [
|
||||
{
|
||||
"ref": null,
|
||||
"axes": {
|
||||
"fast.length": {
|
||||
"kind": "I64",
|
||||
"values": [
|
||||
4,
|
||||
8,
|
||||
12
|
||||
]
|
||||
},
|
||||
"slow.length": {
|
||||
"kind": "I64",
|
||||
"values": [
|
||||
16,
|
||||
24,
|
||||
32
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"process": {
|
||||
"ref": null
|
||||
},
|
||||
"presentation": {
|
||||
"persist_taps": [
|
||||
"bias",
|
||||
"equity"
|
||||
],
|
||||
"emit": [
|
||||
"family_table",
|
||||
"selection_report"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
[
|
||||
{"op": "source", "role": "price", "kind": "F64"},
|
||||
{"op": "add", "type": "SMA", "name": "fast", "bind": {"length": {"I64": 8}}},
|
||||
{"op": "add", "type": "SMA", "name": "slow", "bind": {"length": {"I64": 16}}},
|
||||
{"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"},
|
||||
{"op": "connect", "from": "sub.value", "to": "bias.signal"},
|
||||
{"op": "expose", "from": "bias.bias", "as": "bias"}
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
{"format_version": 1, "kind": "campaign", "name": "sma-cross-length-sweep", "seed": 42, "data": {"instruments": ["EURUSD"], "windows": [{"from_ms": 1711929600000, "to_ms": 1704067200000}]}, "strategies": [{"ref": {"content_id": "573262f2650fd2749427adaec41c148744597c6923fd7e829cb46ceccd105777"}, "axes": {"fast.length": {"kind": "I64", "values": [4, 8, 12]}, "slow.length": {"kind": "I64", "values": [16, 24, 32]}}}], "process": {"ref": {"content_id": "aac13f6e9eecda09364d263ead9174161efb8292d10f15e2f8846b4ee94fd10f"}}, "presentation": {"persist_taps": ["bias", "equity"], "emit": ["family_table", "selection_report"]}}
|
||||
@@ -0,0 +1 @@
|
||||
{"format_version": 1, "kind": "process", "name": "robust-sweep-v1", "description": "Sweep with trials-deflation, gate on positive expectancy, then walk-forward confirm.", "pipeline": [{"block": "std::sweep", "metric": "sqn", "select": "argmax", "deflate": true}, {"block": "std::gate", "all": [{"metric": "expectancy_r", "cmp": "between", "value": 0.0}]}, {"block": "std::walk_forward", "folds": 4, "in_sample_bars": 5000, "out_of_sample_bars": 1000, "metric": "sqn", "select": "argmax"}]}
|
||||
@@ -0,0 +1 @@
|
||||
{"format_version": 1, "kind": "process", "name": "robust-sweep-v1", "description": "Sweep with trials-deflation, gate on positive expectancy, then walk-forward confirm.", "pipeline": [{"block": "std::sweep", "metric": "sqn", "select": "argmax", "deflate": true}, {"block": "std::gate", "all": [{"metric": "sharpe_ratio", "cmp": "gt", "value": 0.0}]}, {"block": "std::walk_forward", "folds": 4, "in_sample_bars": 5000, "out_of_sample_bars": 1000, "metric": "sqn", "select": "argmax"}]}
|
||||
@@ -0,0 +1 @@
|
||||
{"format_version": 1, "kind": "campaign", "name": "sma-cross-length-sweep", "seed": 42, "data": {"instruments": ["EURUSD"], "windows": [{"from_ms": 1704067200000, "to_ms": 1711929600000}]}, "strategies": [{"ref": {"content_id": "573262f2650fd2749427adaec41c148744597c6923fd7e829cb46ceccd105777"}, "axes": {"fast.length": {"kind": "I64", "values": []}, "slow.length": {"kind": "I64", "values": [16, 24, 32]}}}], "process": {"ref": {"content_id": "aac13f6e9eecda09364d263ead9174161efb8292d10f15e2f8846b4ee94fd10f"}}, "presentation": {"persist_taps": ["bias", "equity"], "emit": ["family_table", "selection_report"]}}
|
||||
@@ -0,0 +1 @@
|
||||
{"format_version": 1, "kind": "campaign", "name": "sma-cross-length-sweep", "seed": 42, "data": {"instruments": ["EURUSD"], "windows": [{"from_ms": 1704067200000, "to_ms": 1711929600000}]}, "strategies": [{"ref": {"content_id": "573262f2650fd2749427adaec41c148744597c6923fd7e829cb46ceccd105777"}, "axes": {"fast.length": {"kind": "I64", "values": [4, 8.5, 12]}, "slow.length": {"kind": "I64", "values": [16, 24, 32]}}}], "process": {"ref": {"content_id": "aac13f6e9eecda09364d263ead9174161efb8292d10f15e2f8846b4ee94fd10f"}}, "presentation": {"persist_taps": ["bias", "equity"], "emit": ["family_table", "selection_report"]}}
|
||||
@@ -0,0 +1 @@
|
||||
{"format_version": 1, "kind": "campaign", "name": "sma-cross-length-sweep", "seed": 42, "data": {"instruments": [], "windows": [{"from_ms": 1704067200000, "to_ms": 1711929600000}]}, "strategies": [{"ref": {"content_id": "573262f2650fd2749427adaec41c148744597c6923fd7e829cb46ceccd105777"}, "axes": {"fast.length": {"kind": "I64", "values": [4, 8, 12]}, "slow.length": {"kind": "I64", "values": [16, 24, 32]}}}], "process": {"ref": {"content_id": "aac13f6e9eecda09364d263ead9174161efb8292d10f15e2f8846b4ee94fd10f"}}, "presentation": {"persist_taps": ["bias", "equity"], "emit": ["family_table", "selection_report"]}}
|
||||
@@ -0,0 +1 @@
|
||||
{"format_version": 1, "kind": "campaign", "name": "sma-cross-length-sweep", "seed": 42, "data": {"instruments": ["EURUSD"], "windows": [{"from_ms": 1704067200000, "to_ms": 1711929600000}]}, "strategies": [{"ref": {"content_id": "573262f2650fd2749427adaec41c148744597c6923fd7e829cb46ceccd105777"}, "axes": {"fast.length": {"kind": "I64", "values": [4, 8, 12]}, "slow.length": {"kind": "I64", "values": [16, 24, 32]}}}], "process": {"ref": {"identity_id": "d15912eba3f8a9ac8e8f8e510717de92a98c6eb2d9fe5524746e97579cf37ed6"}}, "presentation": {"persist_taps": ["bias", "equity"], "emit": ["family_table", "selection_report"]}}
|
||||
@@ -0,0 +1,3 @@
|
||||
/target
|
||||
Cargo.lock
|
||||
/runs
|
||||
@@ -0,0 +1,3 @@
|
||||
# Static project context only (C17); paths only.
|
||||
[paths]
|
||||
runs = "runs"
|
||||
@@ -0,0 +1,10 @@
|
||||
# demo_project — an aura research project
|
||||
|
||||
This crate is an aura project: a cdylib of node/strategy blueprints the
|
||||
`aura` host loads during research (see the engine's docs/project-layout.md).
|
||||
|
||||
- Build: `cargo build` (the next `aura` invocation loads the fresh dylib)
|
||||
- Run: `aura run blueprints/signal.json` (from anywhere inside this dir)
|
||||
- Nodes live in `src/`; each is registered in `vocabulary()`/`type_ids()`
|
||||
under the `demo_project::` namespace prefix.
|
||||
- Topology is data (`blueprints/*.json`), node logic is Rust.
|
||||
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "demo_project"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
aura-core = { path = "/home/brummel/dev/aura/crates/aura-core" }
|
||||
|
||||
# Standalone workspace root: never a member of an enclosing workspace.
|
||||
[workspace]
|
||||
@@ -0,0 +1 @@
|
||||
{"format_version":1,"blueprint":{"name":"demo_project_signal","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}}]}},{"primitive":{"type":"demo_project::Identity"}}],"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},{"from":3,"to":4,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":4,"field":0,"name":"bias"}]}}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"format_version": 1,
|
||||
"kind": "campaign",
|
||||
"name": "sma-cross-length-sweep",
|
||||
"seed": 42,
|
||||
"data": { "instruments": ["EURUSD"], "windows": [ { "from_ms": 1704067200000, "to_ms": 1711929600000 } ] },
|
||||
"strategies": [
|
||||
{
|
||||
"ref": { "content_id": "573262f2650fd2749427adaec41c148744597c6923fd7e829cb46ceccd105777" },
|
||||
"axes": {
|
||||
"fast.length": { "kind": "I64", "values": [4, 8, 12] },
|
||||
"slow.length": { "kind": "I64", "values": [16, 24, 32] }
|
||||
}
|
||||
}
|
||||
],
|
||||
"process": { "ref": { "content_id": "aac13f6e9eecda09364d263ead9174161efb8292d10f15e2f8846b4ee94fd10f" } },
|
||||
"presentation": { "persist_taps": ["bias", "equity"], "emit": ["family_table", "selection_report"] }
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"format_version": 1, "kind": "campaign", "name": "sma-cross-length-sweep", "seed": 42, "data": {"instruments": ["EURUSD"], "windows": [{"from_ms": 1704067200000, "to_ms": 1711929600000}]}, "strategies": [{"ref": {"content_id": "573262f2650fd2749427adaec41c148744597c6923fd7e829cb46ceccd105777"}, "axes": {"fast.length": {"kind": "I64", "values": [4, 8, 12]}, "slow.length": {"kind": "I64", "values": [16, 24, 32]}}}], "process": {"ref": {"content_id": "content:aac13f6e9eecda09364d263ead9174161efb8292d10f15e2f8846b4ee94fd10f"}}, "presentation": {"persist_taps": ["bias", "equity"], "emit": ["family_table", "selection_report"]}}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"format_version": 1,
|
||||
"kind": "process",
|
||||
"name": "robust-sweep-v1",
|
||||
"description": "Sweep with trials-deflation, gate on positive expectancy, then walk-forward confirm.",
|
||||
"pipeline": [
|
||||
{ "block": "std::sweep", "metric": "sqn", "select": "argmax", "deflate": true },
|
||||
{ "block": "std::gate", "all": [ { "metric": "expectancy_r", "cmp": "gt", "value": 0.0 } ] },
|
||||
{ "block": "std::walk_forward", "folds": 4, "in_sample_bars": 5000, "out_of_sample_bars": 1000, "metric": "sqn", "select": "argmax" }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
//! demo_project — an aura research project: node logic + the exported vocabulary.
|
||||
//!
|
||||
//! Every node type this crate provides is registered in `vocabulary()` /
|
||||
//! `type_ids()` under the `demo_project::` namespace prefix; the `aura` host loads
|
||||
//! this cdylib per invocation and merges it with the std vocabulary.
|
||||
|
||||
use aura_core::{
|
||||
Cell, Ctx, FieldSpec, Firing, Node, NodeSchema, PortSpec, PrimitiveBuilder,
|
||||
ScalarKind,
|
||||
};
|
||||
|
||||
/// One-input f64 pass-through. Emits `None` until its input has a value.
|
||||
pub struct Identity {
|
||||
out: [Cell; 1],
|
||||
}
|
||||
|
||||
impl Identity {
|
||||
pub fn new() -> Self {
|
||||
Self { out: [Cell::from_f64(0.0)] }
|
||||
}
|
||||
pub fn builder() -> PrimitiveBuilder {
|
||||
PrimitiveBuilder::new(
|
||||
"demo_project::Identity",
|
||||
NodeSchema {
|
||||
inputs: vec![PortSpec {
|
||||
kind: ScalarKind::F64,
|
||||
firing: Firing::Any,
|
||||
name: "value".into(),
|
||||
}],
|
||||
output: vec![FieldSpec { name: "value".into(), kind: ScalarKind::F64 }],
|
||||
params: vec![],
|
||||
},
|
||||
|_| Box::new(Identity::new()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Identity {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for Identity {
|
||||
fn lookbacks(&self) -> Vec<usize> {
|
||||
vec![1]
|
||||
}
|
||||
fn eval(&mut self, ctx: Ctx<'_>) -> Option<&[Cell]> {
|
||||
let w = ctx.f64_in(0);
|
||||
if w.is_empty() {
|
||||
return None;
|
||||
}
|
||||
self.out[0] = Cell::from_f64(w[0]);
|
||||
Some(&self.out)
|
||||
}
|
||||
fn label(&self) -> String {
|
||||
"demo_project::Identity".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn vocabulary(type_id: &str) -> Option<PrimitiveBuilder> {
|
||||
match type_id {
|
||||
"demo_project::Identity" => Some(Identity::builder()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn type_ids() -> &'static [&'static str] {
|
||||
&["demo_project::Identity"]
|
||||
}
|
||||
|
||||
aura_core::aura_project! {
|
||||
namespace: "demo_project",
|
||||
vocabulary: vocabulary,
|
||||
type_ids: type_ids,
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"format_version":1,"kind":"campaign","name":"sma-cross-length-sweep","data":{"instruments":["EURUSD"],"windows":[{"from_ms":1704067200000,"to_ms":1711929600000}]},"strategies":[{"ref":{"content_id":"573262f2650fd2749427adaec41c148744597c6923fd7e829cb46ceccd105777"},"axes":{"fast.length":{"kind":"I64","values":[4,8,12]},"slow.length":{"kind":"I64","values":[16,24,32]}}}],"process":{"ref":{"content_id":"aac13f6e9eecda09364d263ead9174161efb8292d10f15e2f8846b4ee94fd10f"}},"seed":42,"presentation":{"persist_taps":["bias","equity"],"emit":["family_table","selection_report"]}}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"format_version":1,"kind":"process","name":"robust-sweep-v1","description":"Sweep with trials-deflation, gate on positive expectancy, then walk-forward confirm.","pipeline":[{"block":"std::sweep","metric":"sqn","select":"argmax","deflate":true},{"block":"std::gate","all":[{"metric":"expectancy_r","cmp":"gt","value":0.0}]},{"block":"std::walk_forward","folds":4,"in_sample_bars":5000,"out_of_sample_bars":1000,"metric":"sqn","select":"argmax"}]}
|
||||
Reference in New Issue
Block a user