aether_quant.cycle¶
The research cycle: generations sharing a book, the admission rule, decay, the forward record, the glide's calibration, pre-registered rounds and holdout cells.
The research cycle: generations sharing a book, the admission rule, decay, the forward record, the glide's calibration, pre-registered rounds and holdout cells.
AdmissionEvidence
dataclass
¶
What the admission rule (R-ADMIT1) reads about a generation: its selection, hold and edge passes, forward record, decay state and guard.
mean_diff_vs_incumbent
class-attribute
instance-attribute
¶
mean_diff_vs_incumbent: float | None = None
Cell
dataclass
¶
One research cell: a universe side and a date window, holdout or free, that trials are scored on.
DecayConfig
dataclass
¶
Settings of the CUSUM decay monitor: the alarm threshold, the watch fraction and the minimum sessions before a verdict.
DecayReport
dataclass
¶
The decay monitor's state for one generation (healthy, watch, decaying), with its statistic and the sessions it saw.
Generation
dataclass
¶
One model generation in the registry: its kind, admission tier, lifecycle stage, definition and admission evidence.
GlidePolicy
dataclass
¶
How capital moves between generations: the glide length, a newcomer's entry share, the retirement floor and the paper caps.
MissingInput ¶
Bases: FileNotFoundError
A builder's input isn't on this host.
NoSessionToRecord ¶
Bases: RuntimeError
The run found no session it may write (an override that is not a session, or an empty calendar). A scheduled run must fail loudly on this: a silent exit is how a record loses a session.
RoundSpec
dataclass
¶
A pre-registered research round: its name, cell, side, trials, rules file and the bar a trial must clear.
admitted_to_shadow ¶
admitted_to_shadow(evidence: AdmissionEvidence) -> bool
Selection and the one holdout read; or, once the holdouts are spent, selection and a year of forward sessions.
allocation_row ¶
allocation_row(generations: Mapping[str, Generation], shares_before: Mapping[str, float], states: Mapping[str, Mapping], caps: Mapping[str, float], rows_by_generation: Mapping[str, Mapping[str, float]], policy: GlidePolicy, *, decided_today: bool, sessions_since_blend: int, records: Mapping[str, Mapping] | None = None) -> dict[str, Any]
Today's shares, by the policy's mode, and a blended row when one is due. A row retargets every name, so it isn't written daily.
append_event ¶
append_event(path: Path, *, event: str, session: str, candidate: str, payload: Mapping[str, Any], created_at: str) -> dict[str, Any]
Links a row to the chain. The session is in the hashed payload, so rewriting a date breaks it.
append_lifecycle ¶
append_lifecycle(path: Path, *, event: str, payload: Mapping[str, Any], created_at: str) -> dict[str, Any]
A hash-chained line for each change of stage or vintage, so the record of how a generation reached the book can't be edited later without showing.
append_row ¶
append_row(path: Path, *, session: str, candidate: str, payload: Mapping[str, Any], created_at: str) -> dict[str, Any]
Links a row to the chain and appends it. The row's own session is in the payload, so a rewrite of a date breaks the hash.
apply_gate ¶
apply_gate(table: DataFrame, gate: str | None, cell: WideCell) -> pd.DataFrame
An optional gate on the finished table. regime: no signal while BTC is below its 120-session average.
assert_cell_open ¶
assert_cell_open(cell_name: str, freeze_path: Path | None, freeze_digest: str | None) -> Cell
The gate itself. A later cell needs the digest of the freeze file, and that file must be the one on disk.
average_run_lengths ¶
average_run_lengths(*, expected_sharpe: float, config: DecayConfig | None = None, paths: int = 2000, seed: int = 1, horizon: int = 2520, decayed_sharpe: float | None = None) -> dict[str, Any]
Simulated sessions to the first decaying alarm: with the alpha intact (a false alarm) and after it falls to decayed_sharpe, default zero. Paths that never alarm count at horizon.
blank_stale ¶
blank_stale(known: DataFrame, closes: DataFrame, *, rows: int = STALE_ROWS) -> pd.DataFrame
known_closes carries the last price forward, so a name whose bars stopped would keep a signal forever. A cell with no bar over
the last rows rows goes NaN. Looks backward only, and it never reads why the bars stopped.
blend_due ¶
blend_due(*, decided_today: bool, sessions_since_blend: int, guard_tripped: bool, policy: GlidePolicy) -> bool
Whether to write a blended row. A row retargets every name and resets drift, so not daily: on a generation's decision day, every blend_every sessions, or on a guard trip.
blend_generation_rows ¶
blend_generation_rows(rows_by_generation: Mapping[str, Mapping[str, float]], shares: Mapping[str, float]) -> dict[str, float]
The book: each generation's weights times its share, summed per name. A generation with no row counts as flat; a name that nets to zero leaves the row.
build_trial_table ¶
build_trial_table(builder: str, params: Mapping[str, Any] | None, cell: WideCell) -> pd.DataFrame
One builder's table on this cell. cross_asset_trend takes its tickers from the params.
cap_lifted ¶
cap_lifted(evidence: AdmissionEvidence) -> bool
A tier-2 generation takes over only on forward alpha, read at fixed checkpoints so the looks are countable.
caps_for ¶
caps_for(generations: Mapping[str, Generation], evidence: Mapping[str, AdmissionEvidence], policy: GlidePolicy) -> dict[str, float]
Each generation's share cap under the admission rule, from its tier, evidence and the policy's paper cap.
clause_overrides ¶
clause_overrides(spec: RoundSpec) -> dict[str, dict]
Per trial: its own minimum of live names, and whether it has no IC clause. Empty for a round that sets neither, so earlier rounds recompute unchanged.
coin_side ¶
coin_side(ticker: str, project_coins: Iterable[str] = ()) -> str
The same split for coins, by base asset: BTCUSD and BTCUSDT are one coin.
count_unrun_trials ¶
count_unrun_trials(spec: RoundSpec, unscored: Sequence[str], *, reason: str = 'did not run') -> list[str]
A pre-registered trial counts in N whether it ran or not. Appends a row only for those the ledger doesn't hold yet.
cusum_lower ¶
cusum_lower(returns: ndarray, *, expected_sharpe: float, threshold: float, slack: float = 0.5) -> float
Today's statistic as a share of the threshold: 1.0 is an alarm.
cusum_path ¶
cusum_path(returns: ndarray, *, expected_sharpe: float, slack: float = 0.5) -> np.ndarray
The running statistic: it rises while returns fall short and rests at zero, so old good years can't hide today's loss.
decay_state ¶
decay_state(returns: Sequence[float], *, expected_sharpe: float, previous: str = 'healthy', config: DecayConfig | None = None) -> DecayReport
A generation's state today. previous carries the hysteresis, so a state can't flicker on one good week.
decide ¶
decide(definition: Mapping[str, Any], known: DataFrame, date: str) -> dict[str, float]
The frozen rule's weights at date, through the research pipeline. known holds only what was public by that session's close.
decide_generation ¶
decide_generation(definition: Mapping[str, Any], panel: ResearchPanel, date: str) -> dict[str, float]
The frozen rule's weights at date. Unlike forward_ledger.decide, it passes a score table and top_k through, so a cross-sectional rule can't silently become a different one.
default_configs ¶
default_configs() -> list[dict]
The grid of simulated worlds (Sharpe, alpha half-life, correlation, zero-alpha probability) the glide calibration runs over.
default_policies ¶
default_policies() -> list[dict]
The handover policies the glide calibration compares: fixed glides of several lengths and the evidence-driven variants.
definition_sha256 ¶
definition_sha256(definition: Mapping[str, Any]) -> str
SHA-256 of a forward definition's canonical JSON (sorted keys, no spaces): the identity a ledger row is bound to.
due_actions ¶
due_actions(generations: Mapping[str, Mapping], judged: Mapping[str, Any], *, session: str | None, sessions_behind: int = 0, sessions_since_retrain: int | None = None, broker_keys: bool = False, policy_enabled: bool = False, record_started: bool = True) -> list[dict]
What a person or a job should do next, most urgent first.
dynamic_generation_shares ¶
dynamic_generation_shares(shares: Mapping[str, float], stages: Mapping[str, str], states: Mapping[str, str], evidence: Mapping[str, Mapping], caps: Mapping[str, float], policy: GlidePolicy, *, sessions: int = 1) -> dict[str, float]
The dynamic mode. evidence holds, per generation, ew_sharpe, sessions and prior. A generation out of a holding stage, dead or tripped drops to zero at once, whatever its score;
winding_down aims for zero. With no positive score and no generation past min_evidence_sessions, nothing is known yet, so shares hold.
dynamic_targets ¶
dynamic_targets(scores: Mapping[str, float], caps: Mapping[str, float], policy: GlidePolicy) -> dict[str, float]
Each generation's target: proportional to its score above the floor, for the best max_generations. What a capped generation can't hold goes to the others, and what none may hold stays in T-bills.
evaluate_policies ¶
evaluate_policies(configs: Sequence[Mapping], policies: Sequence[Mapping], *, paths: int = 2000, seed: int = 1, years: int = 8, workers: int = 1) -> dict[str, Any]
{policy: median regret, time above half of capital on a zero-alpha generation, generations holding capital, turnover}. Every policy meets the same fixed-seed worlds, across all the configs.
evidence_for ¶
evidence_for(generation: Generation, judged: Mapping[str, Any], *, incumbent_returns: Mapping[str, float] | None, own_returns: Mapping[str, float], holdouts_spent: bool, state: Mapping[str, Any]) -> AdmissionEvidence
The admission rule's inputs from the record, with the flags a round's verdict set. The mean difference is on sessions both have.
ew_sharpe ¶
ew_sharpe(state: tuple[float, float, float, int], *, sessions_per_year: float = 252.0) -> float | None
The annualised Sharpe of that record. None until it holds EW_MIN_SESSIONS or if it has no variance.
ew_update ¶
ew_update(state: tuple[float, float, float, int], value: float, half_life: float = EW_HALF_LIFE_SESSIONS) -> tuple[float, float, float, int]
One more session of an exponentially weighted record: (weight, weighted sum, weighted squares, sessions). A non-finite return leaves it as it is.
family_of ¶
family_of(spec: RoundSpec) -> str
The holdout family of a round's cell: crypto for the crypto universe, equity otherwise.
freeze_digest_of ¶
freeze_digest_of(path: Path) -> str
SHA-256 of a freeze file's bytes, the digest a holdout read must quote.
gate_report ¶
gate_report(scores: Mapping[str, Mapping], placebos: Mapping[str, Mapping], daily: DataFrame, *, trials: Sequence[str], min_live_names: int = 150, overrides: Mapping[str, Mapping] | None = None) -> dict[str, Any]
R-SLEEVE3 from stored scores: a pure function, so a past round can be recomputed and compared.
generation_state ¶
generation_state(generation: Generation, mine: Sequence[Mapping], *, config: DecayConfig | None = None, guard_max_drawdown: float = GUARD_MAX_DRAWDOWN) -> dict[str, Any]
The decay report, with the guard first: a drawdown past the owner's ceiling is tripped, whatever the statistic says, and it latches.
glide2_policies ¶
glide2_policies() -> list[dict]
R-GLIDE2's grid: the dynamic mode, and the two it must be judged against. Four generations may hold capital at once.
has_session ¶
has_session(rows: Sequence[dict], candidate: str, session: str) -> bool
Whether the ledger already holds a row for this candidate and session.
held_by_lag ¶
held_by_lag(mine: Sequence[Mapping], classes: Mapping[str, str], initial: Mapping[str, float], *, lags: Mapping[str, int] = FILL_LAG_SESSIONS) -> dict[str, float]
The weights in place at this session's start, class by class: each class holds what it decided as long ago as its order takes to fill.
holdouts_spent ¶
holdouts_spent(path: Path) -> list[str]
Cells any freeze has read, from the once-only ledger.
install_command ¶
install_command(platform: str, *, task_file: Path) -> list[str]
What registers the rendered job. Returned, not run, so a test can read it and a person can check it first.
is_rebalance ¶
is_rebalance(sessions: Sequence[str], date: str, *, anchor: str, every: int) -> bool
Whether date is a decision day, counting sessions from the frozen file's last decision so the phase carries on unchanged.
judge_candidate ¶
judge_candidate(rows: Sequence[dict], candidate: str) -> dict[str, Any]
Sessions recorded, Sharpe, alpha t against the benchmarks each row carries, and what this many sessions can and can't show.
judge_generations ¶
judge_generations(rows: Sequence[Mapping]) -> dict[str, Any]
Chain first: a broken chain makes every figure meaningless.
judge_ledger ¶
judge_ledger(rows: Sequence[dict], *, today_sessions: Sequence[str] = ()) -> dict[str, Any]
Chain check first: a broken chain makes every figure meaningless. today_sessions warns of a schedule that stopped.
last_row_for ¶
last_row_for(rows: Sequence[dict], candidate: str) -> dict[str, Any] | None
The newest ledger row of a candidate, or None.
lifecycle_is_intact ¶
lifecycle_is_intact(path: Path) -> tuple[bool, int | None]
Verify the hash chain of the lifecycle log: (valid, index of the first broken row or None).
live_rows ¶
live_rows(signal: DataFrame, entry: Mapping[str, Any]) -> pd.DataFrame
The rows live names are counted on. A gated trial holds nothing while its gate is off, so the median over all rebalances would read the gate as a lack of names: it is counted on the rows where the gate is on (those with any value). An ungated trial counts every row, as before.
load_registry ¶
load_registry(path: Path) -> dict[str, Generation]
Read ml/generations/registry.json into Generation records by name, refusing an unknown stage.
load_round ¶
load_round(path: Path, *, root: Path = Path('.')) -> RoundSpec
The round, after its hash check. A changed file stops the round: no stage reads rules other than the ones recorded.
membership_mask ¶
membership_mask(table: Mapping[str, list[tuple[str, str]]], sessions: list[str], columns: list[str]) -> pd.DataFrame
Session x ticker, True while a member. Names outside the membership table are False.
name_side ¶
name_side(ticker: str, project_issuers: Iterable[str] = ()) -> str
A or B, fixed by a hash of the issuer: share classes and renames share a side, and it can't be tuned. A name any earlier phase has read is always A.
names_in_cell ¶
names_in_cell(cell: Cell, tickers: Iterable[str], project: Iterable[str]) -> list[str]
The tickers a cell may trade: all of them for side AB, else those on the cell's side of the split.
next_stage ¶
next_stage(generation: Generation, evidence: AdmissionEvidence) -> str
R-ADMIT1. Moves forward only on evidence, off the book at once on a guard trip or dead state, and never by more than the rule says. Non-finite evidence holds.
no_data_row ¶
no_data_row(generation: Generation, rows: Sequence[Mapping], *, reason: str, benchmarks: Mapping[str, float], definition_sha: str = '') -> dict[str, Any]
A failed fetch is a row with its reason, never a gap. The held weights don't change.
oracle_daily ¶
oracle_daily(world: Mapping[str, Any]) -> np.ndarray
The daily return of an oracle that always holds the generations with the best true Sharpe, the calibration's upper bound.
passes_earlier_clauses ¶
passes_earlier_clauses(score: Mapping[str, Any], *, min_live_names: int, no_ic: bool = False) -> bool
Everything in R-SLEEVE3 before the placebos: live names, the Sharpe clauses, and the rank-IC t (not for a trial the round lists in no_ic_clause).
pbo_report ¶
pbo_report(frames: Sequence[DataFrame], columns: Sequence[str], *, window: tuple[str, str], blocks: int = 16) -> dict[str, Any]
The probability of backtest overfitting over the named columns. A trial with no return on a date reads as flat, as a trial that didn't exist yet would be.
pick_glide2 ¶
pick_glide2(results: Mapping[str, Mapping], *, baseline: str = 'fixed_252', tie: float = 0.005) -> dict[str, Any]
R-GLIDE2: the dynamic policy with the lowest median regret among those no worse than the baseline on zero-alpha time, and it replaces the baseline only if its regret is no worse than the
baseline's, within tie. Ties among dynamic policies go to less turnover. The owner asked for the dynamic form, so no worse is enough: it holds several generations, and the ladder can't.
pick_policy ¶
pick_policy(results: Mapping[str, Mapping], *, max_zero_alpha_share: float = 0.2, tie: float = 0.005) -> dict[str, Any]
R-GLIDE1: the lowest median regret among policies whose time mostly on a zero-alpha generation is at most 20%. Ties go to fewer parameters. None qualifying: fixed 252.
r_hold1 ¶
r_hold1(reads: Mapping[str, Mapping]) -> dict[str, Any]
R-HOLD1's clauses: V3 Sharpe >= 0.50 and alpha t >= 2.0; V1 and V2 Sharpe > 0. A missing figure fails.
r_hold1_crypto ¶
r_hold1_crypto(reads: Mapping[str, Mapping]) -> dict[str, Any]
R-HOLD1 for a crypto candidate: CV Sharpe >= 0.50 and alpha t >= 2.0. A missing figure fails.
ram_preflight ¶
ram_preflight(host: str, *, total_bytes: int | None = None, min_gb: float = 14.0) -> dict[str, Any]
Whether this host may train. A local machine below the minimum refuses with the numbers: a retrain killed for memory hours in costs more than a clear no. Unknown RAM refuses too.
read_json_or ¶
read_json_or(path: Path, default: Any) -> Any
A JSON file's content, or default when the file does not exist.
read_ledger ¶
read_ledger(path: Path) -> list[dict]
The ledger's rows (one JSON object per line), or [] when the file does not exist.
read_lifecycle ¶
read_lifecycle(path: Path) -> list[dict]
The lifecycle log's rows, or [] when the file does not exist.
record_holdout_read ¶
record_holdout_read(path: Path, *, candidate: str, cells: tuple[str, ...], digest: str, created_at: str) -> None
The once-only ledger of holdout reads. write_freeze only refuses its own path, so a second freeze file would otherwise reopen a cell.
Written before the read: a crash after it still counts as spent.
record_of ¶
record_of(returns: Iterable[float], *, prior: float) -> dict[str, Any]
What the allocator reads about one generation: its exponentially weighted Sharpe, its sessions of record, and what it claimed at admission.
render_schedule ¶
render_schedule(platform: str, *, repo: Path, python: str = 'python', host: str = 'local') -> str
The text of a weekly retrain-if-due job, Sunday 03:00. It renders only: aq cycle schedule install is what registers it, on a machine that passes the preflight.
repair_shares ¶
repair_shares(shares: Mapping[str, float]) -> dict[str, float]
An allocation that is a valid one: a NaN or negative share is zero, and a total above one is scaled down to one
(formal/AetherFormal/DynamicTargets.lean, repair_valid). V6.6.7 (#375): an invalid allocation used to be returned
as it was, so a tripped or dead generation kept its share, and an over-allocated book stayed over-allocated.
replay_dynamic ¶
replay_dynamic(rows_by_generation: Mapping[str, Mapping[str, Mapping[str, float]]], returns_by_generation: Mapping[str, Mapping[str, float]], sessions: Sequence[str], *, born: Mapping[str, str], priors: Mapping[str, float], policy: GlidePolicy, initial: Mapping[str, float] | None = None) -> dict[str, Any]
The blended file the dynamic mode would have written, reading only returns known by each session. Used by the running functions, so a backtest of it tests what would trade.
{"rows", "shares", "scores"}, with shares and scores on each session a row is written.
replay_handover ¶
replay_handover(rows_by_generation: Mapping[str, Mapping[str, Mapping[str, float]]], sessions: Sequence[str], *, incumbent: str, successor: str, start: str, policy: GlidePolicy) -> dict[str, Any]
The blended weight file a handover would have written: incumbent holds the book until start, then the ladder moves it to successor. Uses the running functions, so a
backtest of this file tests what would trade. {"rows": {date: weights}, "shares": {date: shares}}.
resolve_glide_policy ¶
resolve_glide_policy(raw: Mapping | None) -> GlidePolicy
A GlidePolicy from config, every share clipped to [0, 1] and every missing or non-finite value at its default.
resolve_session ¶
resolve_session(sessions: Sequence[str], now_date: str, override: str | None = None) -> str | None
The session a run records. An override must be a real session. Otherwise the newest session on or before today, so a schedule delayed past midnight UTC, or a weekend dispatch, still records the session it was for. Never a future date, and never an older gap: only the newest session can be written.
retrain_due ¶
retrain_due(*, sessions_since_retrain: int | None, decay_states: Mapping[str, str], every_sessions: int = RETRAIN_EVERY_SESSIONS) -> dict[str, Any]
By the calendar, or because the book's generation is decaying. It reports; it starts nothing.
retrain_stages ¶
retrain_stages(*, data_present: bool) -> list[str]
A fresh checkout builds its own data first, from public sources and the user's own SEC contact: this is what lets anyone retrain.
row_for_generation ¶
row_for_generation(generation: Generation, rows: Sequence[Mapping], session: str, sessions: Sequence[str], marks: DataFrame, benchmarks: Mapping[str, float], *, decision: Mapping[str, float] | None = None, definition_sha: str = '') -> dict[str, Any]
One session's payload. A rule with no needs decides here, on the runner; one with needs reads the decision a host dated earlier.
rows_for ¶
rows_for(rows: Sequence[Mapping], generation: str) -> list[dict]
The payloads of one generation's ledger rows, oldest first.
rules_sha_path ¶
rules_sha_path(path: Path) -> Path
The .sha256 file that pins a round's rules file.
run_policy ¶
run_policy(world: Mapping[str, Any], policy: Mapping[str, Any]) -> dict[str, Any]
A path of shares and what they earned, net of turnover cost, with blended rows only when due and the deadband on.
save_registry ¶
save_registry(path: Path, generations: Mapping[str, Generation]) -> None
Atomic, sorted, LF: one registry has one hash, and a crash can't leave half a file.
score_file_table ¶
score_file_table(path: Path, index: Sequence[str] | Index, columns: Sequence[str] | Index) -> pd.DataFrame | None
A model's walk-forward scores as a session x ticker table, or None when the file isn't there.
session_for_outage ¶
session_for_outage(now: datetime) -> str
With no calendar (the fetch failed), the last weekday whose US close has passed. Dating an outage row by the calendar day would let a run at 02:00 UTC use up that day's own session.
session_return ¶
session_return(held: Mapping[str, float], marks: DataFrame, previous: str, date: str, *, cost_bps: float, traded: float = 0.0) -> float | None
What the weights held earned between two sessions' marks, less the cost of today's trade. None if a held name has no mark.
session_return_covered ¶
session_return_covered(held: Mapping[str, float], marks: DataFrame, previous: str, date: str, *, classes: Mapping[str, str], traded: Mapping[str, float], cost_by_class: Mapping[str, float] = COST_BPS_BY_CLASS, min_coverage: float = MIN_COVERAGE) -> dict[str, Any]
What the held weights earned between two marks, less the cost of what traded. One delisted holding doesn't blank every row until the next decision: the rest counts while
at least min_coverage of the gross has a mark. Below that the session is no_data, never a guess.
sessions_since_retrain ¶
sessions_since_retrain(run_root: Path, sessions: Sequence[str]) -> int | None
Sessions recorded since the newest real retrain finished. A smoke run registers no procedure, so it never counts. None with none on record.
share_cap ¶
share_cap(generation: Generation, evidence: AdmissionEvidence, tier2_cap: float = 0.3) -> float
What the generation may hold of the paper book: all of it with a full pass or as the installed book, the tier-2 cap until its forward alpha lifts it, otherwise nothing.
A tier-none generation is observed only and holds nothing under any evidence (Problems #335): a lifted cap is a tier-2 privilege.
shrunk_sharpe ¶
shrunk_sharpe(observed: float | None, sessions: int, prior: float, prior_sessions: float) -> float
What a generation's Sharpe is believed to be: its record and its claim, weighted by sessions of record against prior_sessions. No usable record leaves the claim.
simulate_generations ¶
simulate_generations(config: Mapping[str, Any], seed: int, *, years: int = 8) -> dict[str, Any]
{"true_sharpe": sessions x generations, "returns": sessions x generations, "born": first session of each}. A generation exists from its birth.
spec_of ¶
spec_of(spec: RoundSpec, trial: str, universe: Sequence[str]) -> SleeveSpec
The SleeveSpec a round's trial runs as, from its registered entry.
stage_freeze ¶
stage_freeze(spec: RoundSpec, candidate: Mapping[str, Any], *, holdout_ledger: Path, code_digests: Mapping[str, str] | None = None) -> tuple[Path, str]
Freezes one candidate, by rule. Refuses once any holdout has been read: the same data can't back a second candidate. Returns (path, digest).
stage_gate ¶
stage_gate(spec: RoundSpec) -> dict[str, Any]
Apply the round's selection gate to the scored and placebo-tested trials, and write the survivors.
stage_hold ¶
stage_hold(spec: RoundSpec, trial: str, *, root: Path, freeze_path: Path, digest: str, holdout_ledger: Path, created_at: str, score_dir: Path, read_cell: Callable[[str], object] | None = None, alpha_fn: Callable[[Series], Mapping] | None = None) -> dict[str, Any]
The one read of the holdouts. It records the read before opening anything: a crash after this still counts as spent.
stage_pbo ¶
stage_pbo(spec: RoundSpec, *, root: Path = Path('.')) -> dict[str, Any]
PBO by CSCV over the round's trials (and prior rounds' daily returns on the same cell), written beside the gate.
stage_placebo ¶
stage_placebo(spec: RoundSpec, *, cell: WideCell, score_dir: Path, draws: int = 100, force: bool = False, emit: Callable[[str], None] = print) -> dict[str, Any]
R-SLEEVE3's placebos, only for trials that pass every earlier clause.
stage_report ¶
stage_report(spec: RoundSpec, *, alpha_fn: Callable[[Series], Mapping] | None = None) -> dict[str, Any]
The verdict in plain figures: survivors, PBO, and what this round can and can't exclude, per trial and at the best trial.
stage_score ¶
stage_score(spec: RoundSpec, *, cell: WideCell, score_dir: Path, only: Sequence[str] = (), force: bool = False, ic_fn: Callable[..., Any] | None = None, emit: Callable[[str], None] = print) -> dict[str, Any]
Scores each trial not yet scored, saving after each one. A table that can't be built stays unscored and is named by the gate.
status_view ¶
status_view(generations: Mapping[str, Mapping], judged: Mapping[str, Any], actions: Sequence[Mapping], *, session: str | None, policy: Mapping[str, Any]) -> dict[str, Any]
The one view aq cycle status, the API and the dashboard all read. No secret and no path outside the repo.
step_toward ¶
step_toward(shares: Mapping[str, float], targets: Mapping[str, float], fraction: float, retire_share: float) -> dict[str, float]
A fixed fraction of the way to the targets, so nothing jumps. A blend of two allocations that each sum to at most one sums to at most one (proved in Lean). A generation headed to nothing
is gone below retire_share, as it couldn't trade its residue.
total_memory_bytes ¶
total_memory_bytes() -> int | None
This machine's RAM, or None if it can't be read. Stdlib only, so the check works before anything heavy is installed.
trial_table ¶
trial_table(spec: RoundSpec, trial: str, cell: WideCell, built: dict[str, DataFrame], *, score_dir: Path, score_suffix: str = '') -> pd.DataFrame | None
One trial's table on this cell, or None when it can't be built yet (a model that didn't run). built holds earlier trials, which a composite
may name. Masked to the cell's members, so nothing outside them can be held.
turnover ¶
turnover(before: Mapping[str, float], after: Mapping[str, float]) -> float
Sum of absolute weight changes between two books, the traded fraction of NAV.
universe_for ¶
universe_for(spec: RoundSpec, trial: str) -> tuple[str, ...]
What a trial trades: its own universe, else the coins of a crypto cell, else the eleven sectors.
update_generation_shares ¶
update_generation_shares(shares: Mapping[str, float], stages: Mapping[str, str], states: Mapping[str, str], caps: Mapping[str, float], policy: GlidePolicy, *, sessions: int = 1) -> dict[str, float]
The shares after sessions sessions. A generation out of a holding stage, or dead, or with a guard trip (state tripped), drops to zero at once; a new ramping
generation enters at entry_share; below retire_share a generation is gone, and what it held passes to the ramping one up to its cap. What no one may hold stays in T-bills.
window_rows ¶
window_rows(index: Iterable[str], cell: Cell, *, warmup: int = WARMUP_SESSIONS) -> list[str]
The cell's sessions plus the warmup before them, which give a signal its history. A warm-up row can never hold a position:
weights start at cell.start. Nothing after cell.end exists in the panel at all.
write_freeze ¶
write_freeze(path: Path, payload: Mapping[str, Any]) -> str
Writes the freeze file once and returns its digest. Refuses to overwrite: a second freeze would be a second look.
write_rules ¶
write_rules(payload: Mapping[str, Any], path: Path) -> str
Writes a round's rule file once, with its hash beside it. A second write refuses: a changed rule needs a dated amendment.