feat: publish typed native indicator parameter domains
This commit is contained in:
@@ -178,11 +178,11 @@ pub fn catalog() -> Value {
|
||||
let indicators: Vec<Value> = abstract_api::funcs().map(|f| json!({
|
||||
"name":f.name, "group":format!("{:?}",f.group), "description":f.hint,
|
||||
"inputs":f.inputs.iter().map(|p|json!({"name":p.param_name,"kind":format!("{:?}",p.kind),"flags":p.flags.0})).collect::<Vec<_>>(),
|
||||
"parameters":f.opt_inputs.iter().map(|p|json!({"name":p.param_name,"label":p.display_name,"description":p.hint,"domain":format!("{:?}",p.kind)})).collect::<Vec<_>>(),
|
||||
"parameters":f.opt_inputs.iter().map(|p|json!({"name":p.param_name,"label":p.display_name,"description":p.hint,"domain":parameter_domain(p.kind)})).collect::<Vec<_>>(),
|
||||
"outputs":f.outputs.iter().enumerate().map(|(i,p)|json!({"index":i,"name":p.param_name,"kind":format!("{:?}",p.kind)})).collect::<Vec<_>>(),
|
||||
"unstable_period":format!("{:?}",f.unst_id), "production_eligible":false,
|
||||
})).collect();
|
||||
json!({"contract":CONTRACT,"expression_kernel_sha256":implementation_sha256,"library":{"name":"TA-Lib native Rust","revision":TA_REV,"license":"BSD-3-Clause"},
|
||||
json!({"contract":CONTRACT,"parameter_domain_contract":"fidc.indicator-parameter-domain/v1","expression_kernel_sha256":implementation_sha256,"library":{"name":"TA-Lib native Rust","revision":TA_REV,"license":"BSD-3-Clause"},
|
||||
"execution_context_contract":crate::pattern_context::CONTRACT,
|
||||
"execution_context_fields":crate::pattern_context::CONTEXT_FIELDS,
|
||||
"market_event_context_contract":crate::market_event_context::CONTRACT,
|
||||
@@ -196,6 +196,26 @@ pub fn catalog() -> Value {
|
||||
"minute_execution":"strictly_after_completed_bar","cross_section":"requires_separate_complete_universe_contract"}})
|
||||
}
|
||||
|
||||
fn parameter_domain(kind: OptInputType) -> Value {
|
||||
match kind {
|
||||
OptInputType::IntegerRange { min, max, default, .. } => json!({
|
||||
"value_type":"integer", "minimum":min, "maximum":max, "default":default,
|
||||
}),
|
||||
OptInputType::RealRange { min, max, default, precision, .. } => json!({
|
||||
"value_type":"number", "minimum":min, "maximum":max,
|
||||
"default":default, "display_precision":precision,
|
||||
}),
|
||||
OptInputType::IntegerList { values, default } => json!({
|
||||
"value_type":"integer", "default":default,
|
||||
"choices":values.iter().map(|(value,label)|json!({"value":value,"label":label})).collect::<Vec<_>>(),
|
||||
}),
|
||||
OptInputType::RealList { values, default } => json!({
|
||||
"value_type":"number", "default":default,
|
||||
"choices":values.iter().map(|(value,label)|json!({"value":value,"label":label})).collect::<Vec<_>>(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
pub fn validate(&self) -> Result<(), String> {
|
||||
let n = self.timestamps.len();
|
||||
@@ -960,6 +980,46 @@ fn operator(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn every_parameter_domain_is_structured_and_matches_native_defaults() {
|
||||
for function in abstract_api::funcs() {
|
||||
let handle = abstract_api::get_func_handle(function.name).unwrap();
|
||||
let core = Core::new();
|
||||
let mut call = handle.new_call(&core);
|
||||
for (index, parameter) in function.opt_inputs.iter().enumerate() {
|
||||
let domain = parameter_domain(parameter.kind);
|
||||
let default = domain["default"].as_f64().unwrap();
|
||||
assert!(default.is_finite(), "{} {}", function.name, parameter.param_name);
|
||||
if let Some(choices) = domain.get("choices") {
|
||||
assert!(choices.as_array().unwrap().iter().any(|v| v["value"].as_f64() == Some(default)));
|
||||
} else {
|
||||
assert!(default >= domain["minimum"].as_f64().unwrap());
|
||||
assert!(default <= domain["maximum"].as_f64().unwrap());
|
||||
}
|
||||
if domain["value_type"] == "integer" {
|
||||
assert_eq!(default.fract(), 0.0);
|
||||
call.set_opt(index, default as i32).unwrap();
|
||||
} else {
|
||||
call.set_opt(index, default).unwrap();
|
||||
}
|
||||
}
|
||||
assert!(call.lookback().is_ok(), "{}", function.name);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parameter_domains_keep_enumeration_labels_without_debug_string_parsing() {
|
||||
let catalog = catalog();
|
||||
assert_eq!(catalog["parameter_domain_contract"], "fidc.indicator-parameter-domain/v1");
|
||||
let indicators = catalog["indicators"].as_array().unwrap();
|
||||
let rsi = indicators.iter().find(|v| v["name"] == "RSI").unwrap();
|
||||
assert_eq!(rsi["parameters"][0]["domain"]["minimum"], 2);
|
||||
let stoch = indicators.iter().find(|v| v["name"] == "STOCH").unwrap();
|
||||
let ma_type = stoch["parameters"].as_array().unwrap().iter().find(|p| p["name"] == "optInSlowK_MAType").unwrap();
|
||||
assert!(ma_type["domain"]["choices"].as_array().unwrap().iter().any(|v| v["label"] == "EMA" && v["value"] == 1));
|
||||
}
|
||||
|
||||
fn frame(values: Vec<Option<f64>>) -> Frame {
|
||||
let start = DateTime::parse_from_rfc3339("2026-09-01T15:30:00+08:00").unwrap();
|
||||
let times = (0..values.len())
|
||||
|
||||
Reference in New Issue
Block a user