严格校验回测撮合类型

This commit is contained in:
boris
2026-06-28 02:41:39 +08:00
parent dd8783c8c1
commit 4009fe0899
+37 -24
View File
@@ -369,17 +369,18 @@ pub fn platform_expr_config_from_value(
value: &Value,
) -> Result<PlatformExprStrategyConfig, serde_json::Error> {
if value.is_null() || value.as_object().is_some_and(|object| object.is_empty()) {
return Ok(platform_expr_config_from_spec(
strategy_id,
signal_symbol,
None,
));
return platform_expr_config_from_spec(strategy_id, signal_symbol, None)
.map_err(platform_config_error);
}
let spec = serde_json::from_value::<StrategyRuntimeSpec>(value.clone())?;
Ok(platform_expr_config_from_spec(
strategy_id,
signal_symbol,
Some(&spec),
platform_expr_config_from_spec(strategy_id, signal_symbol, Some(&spec))
.map_err(platform_config_error)
}
fn platform_config_error(message: String) -> serde_json::Error {
serde_json::Error::io(std::io::Error::new(
std::io::ErrorKind::InvalidData,
message,
))
}
@@ -418,12 +419,17 @@ fn normalize_model_name(value: &str) -> String {
value.trim().to_ascii_lowercase().replace('-', "_")
}
fn parse_matching_type(value: Option<&str>) -> Option<MatchingType> {
match normalize_model_name(value?).as_str() {
"current_bar_close" => Some(MatchingType::CurrentBarClose),
"next_bar_open" => Some(MatchingType::NextBarOpen),
"minute_last" => Some(MatchingType::MinuteLast),
_ => None,
fn parse_matching_type(value: Option<&str>) -> Result<Option<MatchingType>, String> {
let Some(raw) = value.map(str::trim).filter(|item| !item.is_empty()) else {
return Ok(None);
};
match normalize_model_name(raw).as_str() {
"current_bar_close" => Ok(Some(MatchingType::CurrentBarClose)),
"next_bar_open" => Ok(Some(MatchingType::NextBarOpen)),
"minute_last" => Ok(Some(MatchingType::MinuteLast)),
_ => Err(format!(
"matchingType only supports current_bar_close, next_bar_open, minute_last: {raw}"
)),
}
}
@@ -474,8 +480,8 @@ fn apply_execution_behavior_overrides(
slippage_volatility_coefficient: Option<f64>,
slippage_max_value: Option<f64>,
strict_value_budget: Option<bool>,
) {
if let Some(matching_type) = parse_matching_type(matching_type) {
) -> Result<(), String> {
if let Some(matching_type) = parse_matching_type(matching_type)? {
cfg.matching_type = matching_type;
}
if slippage_model.is_some()
@@ -497,6 +503,7 @@ fn apply_execution_behavior_overrides(
if let Some(enabled) = strict_value_budget {
cfg.strict_value_budget = enabled;
}
Ok(())
}
fn parse_usize_after(text: &str, start: usize) -> Option<(usize, usize)> {
@@ -626,7 +633,7 @@ pub fn platform_expr_config_from_spec(
strategy_id: &str,
signal_symbol: &str,
strategy_spec: Option<&StrategyRuntimeSpec>,
) -> PlatformExprStrategyConfig {
) -> Result<PlatformExprStrategyConfig, String> {
let mut cfg = PlatformExprStrategyConfig::microcap_rotation();
cfg.strategy_name = strategy_id.to_string();
if !signal_symbol.trim().is_empty() {
@@ -634,7 +641,7 @@ pub fn platform_expr_config_from_spec(
}
let Some(spec) = strategy_spec else {
return cfg;
return Ok(cfg);
};
let mut benchmark_short_explicit = false;
let mut benchmark_long_explicit = false;
@@ -751,7 +758,7 @@ pub fn platform_expr_config_from_spec(
engine.slippage_volatility_coefficient,
engine.slippage_max_value,
engine.strict_value_budget,
);
)?;
}
if let Some(spec_signal_symbol) = spec
@@ -1151,7 +1158,7 @@ pub fn platform_expr_config_from_spec(
execution.slippage_volatility_coefficient,
execution.slippage_max_value,
execution.strict_value_budget,
);
)?;
}
if cfg.aiquant_transaction_cost
&& cfg
@@ -1161,7 +1168,7 @@ pub fn platform_expr_config_from_spec(
cfg.minimum_commission = None;
}
cfg
Ok(cfg)
}
fn parse_platform_rebalance_schedule(
@@ -1706,9 +1713,15 @@ mod tests {
}
});
let cfg = platform_expr_config_from_value("", "", &spec).expect("config");
let err =
platform_expr_config_from_value("", "", &spec).expect_err("invalid matchingType");
assert_eq!(cfg.matching_type, MatchingType::CurrentBarClose);
assert!(
err.to_string().contains(
"matchingType only supports current_bar_close, next_bar_open, minute_last"
),
"{err}"
);
}
}