fix(aura-strategy): charge-basis-specific cost-node one-liners
The three cost nodes (ConstantCost, CarryCost, VolSlippageCost) shared a byte-identical generic doc line, so `aura graph introspect --vocabulary` could not tell them apart — C29's per-entry meaning was formally met but not entry-specific (binary-only M1 fieldtest finding). cost_node_builder now takes the factor-specific one-line doc as a parameter (the shared string is gone from the single schema home); each builder states its own charge basis. RED-first: cost_node_descriptions_are_pairwise_distinct failed against the shared string before the fix. closes #330
This commit is contained in:
@@ -29,6 +29,7 @@ impl CarryCost {
|
||||
"CarryCost",
|
||||
Vec::new(),
|
||||
vec![ParamSpec { name: "carry_per_cycle".into(), kind: ScalarKind::F64 }],
|
||||
"cost-model node: cost accrued per held cycle (param carry_per_cycle)",
|
||||
|p| Box::new(CarryCost::new(p[0].f64())),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ impl ConstantCost {
|
||||
"ConstantCost",
|
||||
Vec::new(),
|
||||
vec![ParamSpec { name: "cost_per_trade".into(), kind: ScalarKind::F64 }],
|
||||
"cost-model node: fixed cost per trade (param cost_per_trade), charged at close",
|
||||
|p| Box::new(ConstantCost::new(p[0].f64())),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -214,26 +214,20 @@ impl<F: CostNode> Node for CostRunner<F> {
|
||||
}
|
||||
|
||||
/// Assemble a cost-node `PrimitiveBuilder`: the 4 geometry inputs ++ the factor's
|
||||
/// extra inputs, the standard 3-field cost output, the given params, and a build
|
||||
/// closure. The single home for the cost-node schema shape.
|
||||
/// extra inputs, the standard 3-field cost output, the given params, a
|
||||
/// factor-specific one-line `doc` (C29 — each cost node names its own charge
|
||||
/// basis rather than sharing one generic sentence, #330), and a build closure.
|
||||
/// The single home for the cost-node schema shape.
|
||||
pub fn cost_node_builder(
|
||||
name: &'static str,
|
||||
extra_inputs: Vec<PortSpec>,
|
||||
params: Vec<ParamSpec>,
|
||||
doc: &'static str,
|
||||
build: impl Fn(&[Cell]) -> Box<dyn Node> + 'static,
|
||||
) -> PrimitiveBuilder {
|
||||
let mut inputs = geometry_input_ports();
|
||||
inputs.extend(extra_inputs);
|
||||
PrimitiveBuilder::new(
|
||||
name,
|
||||
NodeSchema {
|
||||
inputs,
|
||||
output: cost_output_fields(),
|
||||
params,
|
||||
doc: "cost-model node: charges its cost in R from position geometry, at close or accrued per held cycle",
|
||||
},
|
||||
build,
|
||||
)
|
||||
PrimitiveBuilder::new(name, NodeSchema { inputs, output: cost_output_fields(), params, doc }, build)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -501,6 +495,7 @@ mod tests {
|
||||
"Probe",
|
||||
extra.clone(),
|
||||
params.clone(),
|
||||
"cost-model node: probe factor for the builder-assembly test",
|
||||
|_p| -> Box<dyn Node> { Box::new(CostRunner::new(StubCost(0.0))) },
|
||||
);
|
||||
let schema = builder.schema();
|
||||
@@ -537,6 +532,21 @@ mod tests {
|
||||
assert_eq!(a, "volatility");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cost_node_descriptions_are_pairwise_distinct() {
|
||||
// C29: `aura graph introspect --vocabulary` must be able to tell the
|
||||
// three cost nodes apart by their charge basis (flat-per-trade vs
|
||||
// per-held-cycle-accrual vs volatility-scaled), not read one
|
||||
// identical generic sentence three times (#330).
|
||||
use crate::{CarryCost, ConstantCost, VolSlippageCost};
|
||||
let constant_doc = ConstantCost::builder().schema().doc;
|
||||
let carry_doc = CarryCost::builder().schema().doc;
|
||||
let vol_slippage_doc = VolSlippageCost::builder().schema().doc;
|
||||
assert_ne!(constant_doc, carry_doc, "ConstantCost vs CarryCost doc must differ");
|
||||
assert_ne!(constant_doc, vol_slippage_doc, "ConstantCost vs VolSlippageCost doc must differ");
|
||||
assert_ne!(carry_doc, vol_slippage_doc, "CarryCost vs VolSlippageCost doc must differ");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn producer_and_aggregator_share_the_triple() {
|
||||
// The structural lockstep: producer output and aggregator output/inputs all
|
||||
|
||||
@@ -43,6 +43,7 @@ impl VolSlippageCost {
|
||||
"VolSlippageCost",
|
||||
volatility_input_ports(),
|
||||
vec![ParamSpec { name: "slip_vol_mult".into(), kind: ScalarKind::F64 }],
|
||||
"cost-model node: slippage proportional to volatility (slip_vol_mult × volatility input), charged at close",
|
||||
|p| Box::new(VolSlippageCost::new(p[0].f64())),
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user