feat(aura-research, aura-runner): RiskRegime::Fixed — the fixed stop is campaign-reachable
Harvest sweep, batch 3 of 6 (second half).
The third externally-tagged variant ('{"fixed":{"distance":..}}',
additive — stored Vol/VolTf documents keep their bytes): doc-tier
validation mirrors FixedStop::new's distance>0 assert (NaN refused)
so the assert is unreachable from a document; stop_rule_for_regime
binds the shipped FixedStop composite; run_blueprint_member stamps
stop_distance beside the vol knobs' precedent and
stop_rule_from_params re-derives Fixed from it (stamps are mutually
exclusive; the pre-#233 no-knob fallback is unchanged). BadRegime
prose widened to name the distance rule. C10's risk-axis paragraph
updates from 'additive when needed' to shipped; glossary gains the
fixed variant.
closes #338
This commit is contained in:
@@ -161,7 +161,7 @@ pub(crate) fn doc_fault_prose(f: &DocFault) -> String {
|
||||
format!("data.windows[{index}]: from_ms must be earlier than to_ms")
|
||||
}
|
||||
DocFault::BadRegime { index } => {
|
||||
format!("risk[{index}]: stop length must be >= 1 and k must be > 0")
|
||||
format!("risk[{index}]: stop length must be >= 1, k must be > 0, and a fixed-stop distance must be > 0")
|
||||
}
|
||||
DocFault::BadCost { index } => {
|
||||
format!("cost[{index}]: the component's price-unit knob must be finite and >= 0")
|
||||
|
||||
@@ -662,7 +662,7 @@ fn campaign_validate_refuses_bad_risk_regime_prose_exit_1() {
|
||||
let (out, code) = run_code_in(&dir, &["campaign", "validate", "risk-bad.campaign.json"]);
|
||||
assert_eq!(code, Some(1), "stdout/stderr: {out}");
|
||||
assert!(
|
||||
out.contains("risk[0]: stop length must be >= 1 and k must be > 0"),
|
||||
out.contains("risk[0]: stop length must be >= 1, k must be > 0, and a fixed-stop distance must be > 0"),
|
||||
"stdout/stderr: {out}"
|
||||
);
|
||||
assert!(!out.contains("BadRegime"), "Debug leak: {out}");
|
||||
@@ -1192,7 +1192,7 @@ fn campaign_register_refuses_invalid_risk_section_and_writes_nothing() {
|
||||
assert_eq!(code, Some(1), "stdout/stderr: {out}");
|
||||
assert!(out.contains("refusing to register:"), "stdout/stderr: {out}");
|
||||
assert!(
|
||||
out.contains("risk[0]: stop length must be >= 1 and k must be > 0"),
|
||||
out.contains("risk[0]: stop length must be >= 1, k must be > 0, and a fixed-stop distance must be > 0"),
|
||||
"stdout/stderr: {out}"
|
||||
);
|
||||
assert!(
|
||||
@@ -3086,6 +3086,68 @@ fn campaign_run_real_e2e_non_default_regime_stamps_its_own_stop() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Property (#338, the `campaign_run_real_e2e_non_default_regime_stamps_its_own_stop`
|
||||
/// precedent above, `Fixed` edition): a campaign document's `RiskRegime::Fixed`
|
||||
/// reaches the real `CliMemberRunner::run_member` path and IS the stop actually
|
||||
/// stamped into each emitted member's manifest as `stop_distance` — making the
|
||||
/// shipped `FixedStop` composite campaign-reachable end to end, not merely
|
||||
/// intrinsically valid.
|
||||
#[test]
|
||||
fn campaign_run_real_e2e_fixed_regime_stamps_its_own_distance() {
|
||||
let (dir, _fixture) = fresh_project();
|
||||
let runs_dir = dir.join("runs");
|
||||
std::fs::remove_dir_all(&runs_dir).ok();
|
||||
let _cleanup = ScratchGuard(vec![
|
||||
ScratchPath::Dir(runs_dir.clone()),
|
||||
ScratchPath::File(dir.join("fixed_regime.process.json")),
|
||||
ScratchPath::File(dir.join("fixed_regime.campaign.json")),
|
||||
]);
|
||||
let bp_id = seed_blueprint(&dir, "campaign-run-fixed-regime-seed");
|
||||
let proc_id = register_process_doc(&dir, "fixed_regime.process.json", SWEEP_ONLY_PROCESS_DOC);
|
||||
let base = campaign_doc_json(
|
||||
&bp_id,
|
||||
&proc_id,
|
||||
(1725148800000, 1727740799999),
|
||||
"",
|
||||
"\"family_table\"",
|
||||
);
|
||||
let with_regime = base.replacen(
|
||||
"\"seed\": 7,",
|
||||
"\"seed\": 7,\n \"risk\": [ { \"fixed\": { \"distance\": 12.0 } } ],",
|
||||
1,
|
||||
);
|
||||
assert_ne!(with_regime, base, "replacen must actually match the fixture's seed field");
|
||||
write_doc(&dir, "fixed_regime.campaign.json", &with_regime);
|
||||
let (out, code) = run_code_in(&dir, &["exec", "fixed_regime.campaign.json"]);
|
||||
|
||||
if code == Some(3)
|
||||
&& (out.contains("no recorded geometry") || out.contains("no data for instrument"))
|
||||
{
|
||||
eprintln!("skip: no local GER40 data for the fixed-regime campaign e2e");
|
||||
return;
|
||||
}
|
||||
assert_eq!(code, Some(0), "stdout/stderr: {out}");
|
||||
let member_lines: Vec<&str> =
|
||||
out.lines().filter(|l| l.starts_with("{\"family_id\":")).collect();
|
||||
assert!(!member_lines.is_empty(), "family_table member lines emitted: {out}");
|
||||
for line in &member_lines {
|
||||
let v: serde_json::Value = serde_json::from_str(line).expect("member line parses as JSON");
|
||||
let params = v["report"]["manifest"]["params"]
|
||||
.as_array()
|
||||
.expect("manifest.params is an array");
|
||||
let get = |name: &str| params.iter().find(|p| p[0].as_str() == Some(name));
|
||||
assert_eq!(
|
||||
get("stop_distance").and_then(|p| p[1]["F64"].as_f64()),
|
||||
Some(12.0),
|
||||
"the campaign's own fixed distance is stamped, not the R_SMA vol-stop default: {line}"
|
||||
);
|
||||
assert!(
|
||||
get("stop_length").is_none() && get("stop_k").is_none(),
|
||||
"a Fixed stop stamps no vol knobs: {line}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Property (#210 T3/T4 — per-cell regime resolution, not a shared last-write):
|
||||
/// a campaign with TWO distinct non-default risk regimes stamps EACH regime's
|
||||
/// OWN stop into ITS OWN cell's members, never the other regime's values and
|
||||
|
||||
@@ -513,16 +513,17 @@ pub struct Axis {
|
||||
}
|
||||
|
||||
/// A protective-stop regime: a serializable, content-addressable mirror of the
|
||||
/// runtime `StopRule` structural axis (C10/C20). The sole implemented variant is
|
||||
/// the vol-stop; the shipped fixed-stop rule is admitted as a future additive
|
||||
/// variant (it runs as a composite today but is not yet campaign-reachable).
|
||||
/// Externally tagged so adding `Fixed` is additive — no content-id churn on
|
||||
/// stored `Vol` regimes.
|
||||
/// runtime `StopRule` structural axis (C10/C20). `Fixed{distance}` (#338) makes
|
||||
/// the shipped `FixedStop` composite campaign-reachable, beside the vol-stop
|
||||
/// family — `distance` is the same price-unit knob `FixedStop`'s own `distance`
|
||||
/// param carries. Externally tagged so each variant was additive on arrival —
|
||||
/// no content-id churn on stored `Vol`/`VolTf` regimes.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields, rename_all = "snake_case")]
|
||||
pub enum RiskRegime {
|
||||
Vol { length: i64, k: f64 },
|
||||
VolTf { period_minutes: i64, length: i64, k: f64 },
|
||||
Fixed { distance: f64 },
|
||||
}
|
||||
|
||||
/// One component of the campaign's cost model (#234): a closed, additive
|
||||
@@ -973,6 +974,14 @@ pub fn validate_campaign(doc: &CampaignDoc) -> Vec<DocFault> {
|
||||
faults.push(DocFault::BadRegime { index: i });
|
||||
}
|
||||
}
|
||||
RiskRegime::Fixed { distance } => {
|
||||
// Mirrors `FixedStop::new`'s own `distance > 0.0` assert
|
||||
// (`aura-strategy`) — refused gracefully here instead of
|
||||
// reaching that assert and panicking.
|
||||
if *distance <= 0.0 || distance.is_nan() {
|
||||
faults.push(DocFault::BadRegime { index: i });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i, c) in doc.cost.iter().enumerate() {
|
||||
@@ -1666,6 +1675,18 @@ mod tests {
|
||||
assert_eq!(back, r);
|
||||
}
|
||||
|
||||
/// #338: the third variant's wire form follows the same externally-tagged
|
||||
/// convention as `vol`/`vol_tf` — `fixed` snake_case tag, one named field
|
||||
/// (the `FixedStop` composite's own `distance` param).
|
||||
#[test]
|
||||
fn risk_regime_round_trips_as_externally_tagged_fixed() {
|
||||
let r = RiskRegime::Fixed { distance: 10.0 };
|
||||
let j = serde_json::to_string(&r).unwrap();
|
||||
assert_eq!(j, r#"{"fixed":{"distance":10.0}}"#);
|
||||
let back: RiskRegime = serde_json::from_str(&j).unwrap();
|
||||
assert_eq!(back, r);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn campaign_absent_and_empty_risk_omit_from_canonical_bytes() {
|
||||
let doc: CampaignDoc = serde_json::from_str(CAMPAIGN_FIXTURE).unwrap();
|
||||
@@ -1724,6 +1745,23 @@ mod tests {
|
||||
assert!(!faults.contains(&DocFault::BadRegime { index: 0 }), "valid regime not flagged: {faults:?}");
|
||||
}
|
||||
|
||||
/// #338: a non-positive fixed-stop distance must be refused as a graceful
|
||||
/// `DocFault::BadRegime` at the doc tier — otherwise it reaches
|
||||
/// `FixedStop::new`'s `distance > 0.0` assert and panics instead of a
|
||||
/// validation error (the `validate_campaign_flags_non_positive_period_minutes_regime`
|
||||
/// precedent above, `Fixed` edition).
|
||||
#[test]
|
||||
fn validate_campaign_flags_non_positive_fixed_distance_regime() {
|
||||
let mut doc: CampaignDoc = serde_json::from_str(CAMPAIGN_FIXTURE).unwrap();
|
||||
doc.risk = vec![
|
||||
RiskRegime::Fixed { distance: 10.0 }, // 0: valid
|
||||
RiskRegime::Fixed { distance: 0.0 }, // 1: distance <= 0
|
||||
];
|
||||
let faults = validate_campaign(&doc);
|
||||
assert!(faults.contains(&DocFault::BadRegime { index: 1 }), "{faults:?}");
|
||||
assert!(!faults.contains(&DocFault::BadRegime { index: 0 }), "valid regime not flagged: {faults:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_campaign_accepts_a_valid_risk_section() {
|
||||
let mut doc: CampaignDoc = serde_json::from_str(CAMPAIGN_FIXTURE).unwrap();
|
||||
|
||||
@@ -649,9 +649,10 @@ pub fn run_blueprint_member(
|
||||
h.run_bound(key_supply(binding, sources))
|
||||
.expect("sources opened against `binding` key-match that binding's own roles by construction");
|
||||
let mut named = zip_params(space, point); // by-name params for the manifest record
|
||||
// `match` (not an irrefutable `let`): `StopRule` also has a `Fixed` variant,
|
||||
// which stamps no vol knobs. The campaign/single-run paths only pass `Vol`
|
||||
// or `VolTf`, so the `Fixed` arm is inert here.
|
||||
// One arm's knobs per member (#233 Vol/VolTf, #338 Fixed) — the manifest
|
||||
// stamp `stop_rule_from_params` reads back (`translate.rs`), so a
|
||||
// campaign's `RiskRegime::Fixed` cell reproduces its own distance, not
|
||||
// the baked default vol-stop.
|
||||
match stop {
|
||||
StopRule::Vol { length, k } => {
|
||||
named.push(("stop_length".to_string(), Scalar::i64(length)));
|
||||
@@ -662,7 +663,9 @@ pub fn run_blueprint_member(
|
||||
named.push(("stop_length".to_string(), Scalar::i64(length)));
|
||||
named.push(("stop_k".to_string(), Scalar::f64(k)));
|
||||
}
|
||||
StopRule::Fixed(_) => {}
|
||||
StopRule::Fixed(distance) => {
|
||||
named.push(("stop_distance".to_string(), Scalar::f64(distance)));
|
||||
}
|
||||
}
|
||||
// Stamp the cost model the member ran under, beside the stop knobs (#234,
|
||||
// the #233 pattern): one `cost[k].<knob>` param per component, in
|
||||
@@ -1694,6 +1697,49 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// #338 write-side: `run_blueprint_member` given `StopRule::Fixed` actually
|
||||
/// bootstraps and runs the member (the `risk_executor`/`FixedStop` arm,
|
||||
/// end-to-end over a real synthetic realization) and stamps `stop_distance`
|
||||
/// into the manifest under the key `stop_rule_from_params`'s round-trip
|
||||
/// reads back — the `run_blueprint_member_stamps_the_vol_tf_stop_knobs`
|
||||
/// precedent above, `Fixed` edition.
|
||||
#[test]
|
||||
fn run_blueprint_member_stamps_the_fixed_stop_distance() {
|
||||
let env = Env::std();
|
||||
let doc = blueprint_to_json(&load_closed_r_sma()).expect("serializes");
|
||||
let reload = || blueprint_from_json(&doc, &|t| std_vocabulary(t)).expect("loads");
|
||||
let space = blueprint_axis_probe(&doc, &env).param_space();
|
||||
let binding = crate::binding::resolve_binding("fixedstamp", reload().input_roles(), &BTreeMap::new())
|
||||
.expect("the price role resolves");
|
||||
let stop = StopRule::Fixed(10.0);
|
||||
let (sources, window, pip) = resolve_run_data(&RunData::Synthetic, &env, &binding);
|
||||
let run = run_blueprint_member(
|
||||
reload(),
|
||||
&[],
|
||||
&space,
|
||||
sources,
|
||||
window,
|
||||
0,
|
||||
pip,
|
||||
"topo",
|
||||
&env,
|
||||
stop,
|
||||
&binding,
|
||||
&[],
|
||||
"GER40",
|
||||
);
|
||||
assert!(
|
||||
run.manifest.params.contains(&("stop_distance".to_string(), Scalar::f64(10.0))),
|
||||
"manifest must stamp stop_distance: {:?}",
|
||||
run.manifest.params
|
||||
);
|
||||
assert!(
|
||||
!run.manifest.params.iter().any(|(k, _)| k == "stop_length" || k == "stop_k"),
|
||||
"a Fixed stop stamps no vol knobs: {:?}",
|
||||
run.manifest.params
|
||||
);
|
||||
}
|
||||
|
||||
/// A fresh project root so a run's `runs/` lands in a temp dir.
|
||||
fn temp_project_env(name: &str) -> (Env, PathBuf) {
|
||||
let root = std::env::temp_dir()
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
//! the cost model) must still round-trip through a manifest, so a stored
|
||||
//! member (family, reproduce) re-derives bit-identically. These translators
|
||||
//! are the single source for each such write<->read pair — the manifest
|
||||
//! `stop_length`/`stop_k`/`stop_period_minutes` <-> [`StopRule`] binding, and
|
||||
//! the `cost[k].<knob>` <-> [`aura_research::CostSpec`] binding — so the two
|
||||
//! halves of each pair cannot drift out of sync between the run/campaign
|
||||
//! paths and reproduce/persist.
|
||||
//! `stop_length`/`stop_k`/`stop_period_minutes`/`stop_distance` <->
|
||||
//! [`StopRule`] binding, and the `cost[k].<knob>` <->
|
||||
//! [`aura_research::CostSpec`] binding — so the two halves of each pair
|
||||
//! cannot drift out of sync between the run/campaign paths and
|
||||
//! reproduce/persist.
|
||||
|
||||
use aura_composites::StopRule;
|
||||
use aura_core::{PrimitiveBuilder, Scalar};
|
||||
@@ -23,11 +24,14 @@ pub const R_SMA_STOP_LENGTH: i64 = 3;
|
||||
pub const R_SMA_STOP_K: f64 = 2.0;
|
||||
|
||||
/// Re-derive the `StopRule` a member was minted under from its manifest params
|
||||
/// (`stop_length`/`stop_k`/`stop_period_minutes`, stamped by `run_blueprint_member`):
|
||||
/// if `stop_period_minutes` is present alongside `stop_length`/`stop_k`, this
|
||||
/// re-derives `VolTf`; otherwise falls back to `Vol`, and to the default
|
||||
/// vol-stop regime when the manifest carries no stop knobs at all (pre-#233
|
||||
/// members), mirroring [`stop_rule_for_regime`]'s `None` arm for the same
|
||||
/// (`stop_length`/`stop_k`/`stop_period_minutes`/`stop_distance`, stamped by
|
||||
/// `run_blueprint_member`): if `stop_period_minutes` is present alongside
|
||||
/// `stop_length`/`stop_k`, this re-derives `VolTf`; `stop_length`/`stop_k`
|
||||
/// alone re-derives `Vol`; `stop_distance` alone (#338) re-derives `Fixed` —
|
||||
/// the three stamps are mutually exclusive (`run_blueprint_member` stamps
|
||||
/// exactly one arm's knobs per member). Falls back to the default vol-stop
|
||||
/// regime when the manifest carries no stop knobs at all (pre-#233 members),
|
||||
/// mirroring [`stop_rule_for_regime`]'s `None` arm for the same
|
||||
/// one-directional widening `point_from_params` already applies to missing
|
||||
/// manifest params.
|
||||
pub fn stop_rule_from_params(params: &[(String, Scalar)]) -> StopRule {
|
||||
@@ -35,11 +39,13 @@ pub fn stop_rule_from_params(params: &[(String, Scalar)]) -> StopRule {
|
||||
params.iter().find(|(n, _)| n == "stop_period_minutes").map(|(_, s)| s.as_i64());
|
||||
let length = params.iter().find(|(n, _)| n == "stop_length").map(|(_, s)| s.as_i64());
|
||||
let k = params.iter().find(|(n, _)| n == "stop_k").map(|(_, s)| s.as_f64());
|
||||
match (period_minutes, length, k) {
|
||||
(Some(period_minutes), Some(length), Some(k)) => {
|
||||
let distance = params.iter().find(|(n, _)| n == "stop_distance").map(|(_, s)| s.as_f64());
|
||||
match (period_minutes, length, k, distance) {
|
||||
(Some(period_minutes), Some(length), Some(k), _) => {
|
||||
StopRule::VolTf { period_minutes, length, k }
|
||||
}
|
||||
(None, Some(length), Some(k)) => StopRule::Vol { length, k },
|
||||
(None, Some(length), Some(k), _) => StopRule::Vol { length, k },
|
||||
(None, None, None, Some(distance)) => StopRule::Fixed(distance),
|
||||
_ => StopRule::Vol { length: R_SMA_STOP_LENGTH, k: R_SMA_STOP_K },
|
||||
}
|
||||
}
|
||||
@@ -94,6 +100,7 @@ pub fn cost_specs_from_params(
|
||||
/// default vol-stop, `Some(RiskRegime::Vol { .. })` binds that regime's own
|
||||
/// params. Single-sourced so the persist-side re-run structurally cannot
|
||||
/// diverge from the run-side binding again (the #219 divergence class).
|
||||
/// `Fixed { distance }` (#338) binds the shipped `FixedStop` composite.
|
||||
pub fn stop_rule_for_regime(regime: Option<aura_research::RiskRegime>) -> aura_composites::StopRule {
|
||||
match regime {
|
||||
None => aura_composites::StopRule::Vol { length: R_SMA_STOP_LENGTH, k: R_SMA_STOP_K },
|
||||
@@ -103,6 +110,7 @@ pub fn stop_rule_for_regime(regime: Option<aura_research::RiskRegime>) -> aura_c
|
||||
Some(aura_research::RiskRegime::VolTf { period_minutes, length, k }) => {
|
||||
aura_composites::StopRule::VolTf { period_minutes, length, k }
|
||||
}
|
||||
Some(aura_research::RiskRegime::Fixed { distance }) => aura_composites::StopRule::Fixed(distance),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,6 +186,15 @@ mod tests {
|
||||
assert_eq!(stop_rule_from_params(&vol), StopRule::Vol { length: 3, k: 2.0 });
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// #338 round-trip: a manifest carrying ONLY `stop_distance` (no vol
|
||||
/// knobs) re-derives `StopRule::Fixed` — the `stop_rule_from_params_round_trips_the_vol_tf_stamp`
|
||||
/// precedent above, `Fixed` edition.
|
||||
fn stop_rule_from_params_round_trips_the_fixed_distance_stamp() {
|
||||
let fixed = vec![("stop_distance".to_string(), Scalar::f64(10.0))];
|
||||
assert_eq!(stop_rule_from_params(&fixed), StopRule::Fixed(10.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// #262: `stop_rule_for_regime` binds `RiskRegime::VolTf` to
|
||||
/// `StopRule::VolTf` field-for-field — the resolve-side half of the
|
||||
@@ -193,6 +210,17 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// #338: `stop_rule_for_regime` binds `RiskRegime::Fixed` to
|
||||
/// `StopRule::Fixed` — the resolve-side half of the write/resolve pair the
|
||||
/// manifest round-trip test above covers from the stamp side (the
|
||||
/// `stop_rule_for_regime_binds_vol_tf_field_for_field` precedent, `Fixed`
|
||||
/// edition).
|
||||
fn stop_rule_for_regime_binds_fixed_distance() {
|
||||
let regime = Some(aura_research::RiskRegime::Fixed { distance: 10.0 });
|
||||
assert_eq!(stop_rule_for_regime(regime), aura_composites::StopRule::Fixed(10.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// #234: the one CostSpec -> builder binding maps each component to its
|
||||
/// shipped cost node with the knob BOUND — a bound component adds no open
|
||||
|
||||
@@ -284,10 +284,11 @@ with like; costed families reproduce bit-identically (incl. `Carry`).
|
||||
curves; a cost-less doc requesting it keeps a remedy-naming skip notice. There are
|
||||
no `--cost-*` run-path flags (removed #221/#234); cost travels in the document.
|
||||
|
||||
**Risk regime as a structural campaign axis.** The `StopRule{Fixed, Vol}` axis is
|
||||
realized at the campaign-document level as `CampaignDoc.risk: [RiskRegime]`
|
||||
(`aura-research`, variants `Vol{length, k}` and `VolTf{period_minutes, length, k}`
|
||||
(#262), the fixed-stop rule additive when needed) — a kept-separate matrix axis,
|
||||
**Risk regime as a structural campaign axis.** The `StopRule{Fixed, Vol, VolTf}`
|
||||
axis is realized at the campaign-document level as `CampaignDoc.risk:
|
||||
[RiskRegime]` (`aura-research`, variants `Vol{length, k}`,
|
||||
`VolTf{period_minutes, length, k}` (#262), and `Fixed{distance}` (#338, binding
|
||||
the shipped `FixedStop` composite)) — a kept-separate matrix axis,
|
||||
peer of instruments and windows. The executor keys the nominee map by `(strategy,
|
||||
window, regime)`, so `generalize` aggregates *within* a regime, never across.
|
||||
Regimes are **compared** at presentation, never argmax-**selected** across (a
|
||||
|
||||
+9
-7
@@ -245,13 +245,15 @@ A node that converts a finer stream to a coarser bar stream, emitting a complete
|
||||
**Avoid:** risk section
|
||||
One entry of a campaign document's structural risk axis (`risk`): a
|
||||
serializable protective-stop regime (variants `vol{length,k}` — per-cycle —
|
||||
and `vol_tf{period_minutes,length,k}` — per completed time bucket) the matrix
|
||||
runs every cell under, so cells differ by execution discipline, never by
|
||||
signal. Absent or empty = one implicit default regime; the regime's stop
|
||||
defines the risk unit R — in `vol{length,k}` (stop = k·√EMA(Δ², length) over
|
||||
m1 cycles) `length` only smooths the vol estimator while `k` scales the stop
|
||||
distance, so the stop's timescale stays one cycle (`vol_tf` sets the stop's
|
||||
timescale via `period_minutes`).
|
||||
`vol_tf{period_minutes,length,k}` — per completed time bucket — and
|
||||
`fixed{distance}` — a constant price-unit distance, binding the shipped
|
||||
`FixedStop` composite) the matrix runs every cell under, so cells differ by
|
||||
execution discipline, never by signal. Absent or empty = one implicit default
|
||||
regime; the regime's stop defines the risk unit R — in `vol{length,k}` (stop =
|
||||
k·√EMA(Δ², length) over m1 cycles) `length` only smooths the vol estimator
|
||||
while `k` scales the stop distance, so the stop's timescale stays one cycle
|
||||
(`vol_tf` sets the stop's timescale via `period_minutes`; `fixed` has no
|
||||
timescale — the distance never adapts).
|
||||
|
||||
### run
|
||||
**Avoid:** —
|
||||
|
||||
Reference in New Issue
Block a user