严格校验回测撮合类型

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, value: &Value,
) -> Result<PlatformExprStrategyConfig, serde_json::Error> { ) -> Result<PlatformExprStrategyConfig, serde_json::Error> {
if value.is_null() || value.as_object().is_some_and(|object| object.is_empty()) { if value.is_null() || value.as_object().is_some_and(|object| object.is_empty()) {
return Ok(platform_expr_config_from_spec( return platform_expr_config_from_spec(strategy_id, signal_symbol, None)
strategy_id, .map_err(platform_config_error);
signal_symbol,
None,
));
} }
let spec = serde_json::from_value::<StrategyRuntimeSpec>(value.clone())?; let spec = serde_json::from_value::<StrategyRuntimeSpec>(value.clone())?;
Ok(platform_expr_config_from_spec( platform_expr_config_from_spec(strategy_id, signal_symbol, Some(&spec))
strategy_id, .map_err(platform_config_error)
signal_symbol, }
Some(&spec),
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('-', "_") value.trim().to_ascii_lowercase().replace('-', "_")
} }
fn parse_matching_type(value: Option<&str>) -> Option<MatchingType> { fn parse_matching_type(value: Option<&str>) -> Result<Option<MatchingType>, String> {
match normalize_model_name(value?).as_str() { let Some(raw) = value.map(str::trim).filter(|item| !item.is_empty()) else {
"current_bar_close" => Some(MatchingType::CurrentBarClose), return Ok(None);
"next_bar_open" => Some(MatchingType::NextBarOpen), };
"minute_last" => Some(MatchingType::MinuteLast), match normalize_model_name(raw).as_str() {
_ => None, "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_volatility_coefficient: Option<f64>,
slippage_max_value: Option<f64>, slippage_max_value: Option<f64>,
strict_value_budget: Option<bool>, strict_value_budget: Option<bool>,
) { ) -> Result<(), String> {
if let Some(matching_type) = parse_matching_type(matching_type) { if let Some(matching_type) = parse_matching_type(matching_type)? {
cfg.matching_type = matching_type; cfg.matching_type = matching_type;
} }
if slippage_model.is_some() if slippage_model.is_some()
@@ -497,6 +503,7 @@ fn apply_execution_behavior_overrides(
if let Some(enabled) = strict_value_budget { if let Some(enabled) = strict_value_budget {
cfg.strict_value_budget = enabled; cfg.strict_value_budget = enabled;
} }
Ok(())
} }
fn parse_usize_after(text: &str, start: usize) -> Option<(usize, usize)> { 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, strategy_id: &str,
signal_symbol: &str, signal_symbol: &str,
strategy_spec: Option<&StrategyRuntimeSpec>, strategy_spec: Option<&StrategyRuntimeSpec>,
) -> PlatformExprStrategyConfig { ) -> Result<PlatformExprStrategyConfig, String> {
let mut cfg = PlatformExprStrategyConfig::microcap_rotation(); let mut cfg = PlatformExprStrategyConfig::microcap_rotation();
cfg.strategy_name = strategy_id.to_string(); cfg.strategy_name = strategy_id.to_string();
if !signal_symbol.trim().is_empty() { if !signal_symbol.trim().is_empty() {
@@ -634,7 +641,7 @@ pub fn platform_expr_config_from_spec(
} }
let Some(spec) = strategy_spec else { let Some(spec) = strategy_spec else {
return cfg; return Ok(cfg);
}; };
let mut benchmark_short_explicit = false; let mut benchmark_short_explicit = false;
let mut benchmark_long_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_volatility_coefficient,
engine.slippage_max_value, engine.slippage_max_value,
engine.strict_value_budget, engine.strict_value_budget,
); )?;
} }
if let Some(spec_signal_symbol) = spec if let Some(spec_signal_symbol) = spec
@@ -1151,7 +1158,7 @@ pub fn platform_expr_config_from_spec(
execution.slippage_volatility_coefficient, execution.slippage_volatility_coefficient,
execution.slippage_max_value, execution.slippage_max_value,
execution.strict_value_budget, execution.strict_value_budget,
); )?;
} }
if cfg.aiquant_transaction_cost if cfg.aiquant_transaction_cost
&& cfg && cfg
@@ -1161,7 +1168,7 @@ pub fn platform_expr_config_from_spec(
cfg.minimum_commission = None; cfg.minimum_commission = None;
} }
cfg Ok(cfg)
} }
fn parse_platform_rebalance_schedule( 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}"
);
} }
} }