walk-forward: space on the family, tag-free chosen_params, schema-checked param_stability #75
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Last cycle (Scalar-as-enum +
RunManifest.paramslift) keptWindowRun.chosen_paramsas
Vec<Scalar>— an explicit out-of-scope decision ("Fork A"), justified by "Scalar isnow serializable, so a
WindowRunis a self-describing record."That defense does not hold:
WalkForwardResultandWindowRuncarry no serde derives(
walkforward.rs:123, 147). They are never serialized — only the embeddedoos_report(a
RunReport, already typed via the lift) is. Sochosen_paramsis a pure in-memorysubstrate for
param_stability, andVec<Scalar>makes it C7-impure: it carries thekind N×M times at the value, where C11 makes the kind per-slot constant. The lone
reader (
param_stability,:243) then re-asks "is this slot numeric?" once perwindow-value via a per-value
unreachable!.SweepFamilyalready shows the C7-clean form in the same crate: kinds live once onthe family (
space: Vec<ParamSpec>), points are tag-freeVec<Cell>, names recombined ondemand via
zip_params. Walk-forward is the lone asymmetry.Change (three coupled parts)
WalkForwardResultgainsspace: Vec<ParamSpec>;WindowRun.chosen_params:Vec<Scalar>→Vec<Cell>. Shares the(space, tag-free Cell points)idiom withSweepFamily(+zip_paramsfor the namedview) — distinct types, not a unified container (Sweep's axis is the param-space, an
input coordinate; WF's axis is time,
chosen_paramsis the inner optimizer's output,and WF additionally carries
bounds+oos_equity+stitched_oos_equity).walk_forwardseam.walk_forward(roller, space, run_window); theFn(WindowBounds) -> WindowRun + Syncgeneric is untouched — space is schemametadata passed beside the closure (by value, moved onto the result), never captured, so
run_indexedis untouched. The caller computes the space once (C11: window-invariant)via
param_space(); the closure simplifies tochosen_params: best.params(drops thefrom_cellreconstruction).param_stabilitybuilds a per-slot coercerVec<fn(Cell) -> f64>fromresult.spacebefore a type-blind reduction loop.Bool -> 0/1keeps the coercer total over the knob kinds;Timestampis never a knob(C20) -> a single schema-level assert.
scalar_as_f64+ its per-valueunreachable!aredeleted. Public signature unchanged (reads
result.space).Notes
{kind, cell}Scalar struct lastcycle: "it works" does not beat "it fits structurally."
param_stabilityyields identicalMetricStatsover theexisting fixtures (
I64->f64/F64coercions are value-identical; theBoolarm is newand unexercised by built-in grids).
specify->planner->implement->audit(design settled — no fork).