fix: accept scoped blacklist runtime context

This commit is contained in:
boris
2026-08-31 09:40:49 +08:00
parent d0ca09d4d8
commit cf1b60c996
+62 -18
View File
@@ -336,6 +336,15 @@ pub struct StrategyRiskPolicySpec {
alias = "blacklist"
)]
pub blacklisted_symbols: Vec<String>,
/// Account- and strategy-scoped blacklist facts are injected by the
/// trading platform before runtime execution. They are typed here so the
/// shared contract validator accepts the context without silently
/// discarding malformed values; the trading risk layer applies the
/// account/strategy match with the actual execution identity.
#[serde(default, alias = "accountBlacklistedInstruments")]
pub account_blacklisted_instruments: BTreeMap<String, BTreeSet<String>>,
#[serde(default, alias = "strategyBlacklistedInstruments")]
pub strategy_blacklisted_instruments: BTreeMap<String, BTreeSet<String>>,
#[serde(
default,
alias = "volume_limit_enabled",
@@ -675,8 +684,14 @@ const STRATEGY_ALIAS_GROUPS: &[(&str, &[&str])] = &[
("matchingType", &["matching_type"]),
("slippageModel", &["slippage_model"]),
("slippageValue", &["slippage_value"]),
("slippageImpactCoefficient", &["slippage_impact_coefficient"]),
("slippageVolatilityCoefficient", &["slippage_volatility_coefficient"]),
(
"slippageImpactCoefficient",
&["slippage_impact_coefficient"],
),
(
"slippageVolatilityCoefficient",
&["slippage_volatility_coefficient"],
),
(
"slippageMaxValue",
&["slippage_max_value", "slippage_max_rate"],
@@ -688,7 +703,10 @@ const STRATEGY_ALIAS_GROUPS: &[(&str, &[&str])] = &[
),
("transferFeeRate", &["transfer_fee_rate", "transferFeeRate"]),
("stampTaxRate", &["stamp_tax_rate"]),
("stampTaxRateBeforeChange", &["stamp_tax_rate_before_change"]),
(
"stampTaxRateBeforeChange",
&["stamp_tax_rate_before_change"],
),
("stampTaxRateAfterChange", &["stamp_tax_rate_after_change"]),
("stampTaxChangeDate", &["stamp_tax_change_date"]),
("volumeLimit", &["volume_limit"]),
@@ -1080,6 +1098,10 @@ pub fn validate_strategy_risk_policy_fields(value: &Value) -> Result<(), String>
"blacklistedInstruments",
"blacklisted_instruments",
"blacklist",
"accountBlacklistedInstruments",
"account_blacklisted_instruments",
"strategyBlacklistedInstruments",
"strategy_blacklisted_instruments",
// Legacy execution aliases are accepted by StrategyExecutionSpec and
// normalized into the same shared switches.
"volumeLimit",
@@ -1295,18 +1317,12 @@ fn apply_risk_policy_overrides(
let Some(policy) = policy else {
return Ok(());
};
let max_order_quantity = valid_positive_limit(
policy.max_order_quantity,
"riskPolicy.maxOrderQuantity",
)?;
let max_order_notional = valid_positive_limit(
policy.max_order_notional,
"riskPolicy.maxOrderNotional",
)?;
let max_symbol_position = valid_positive_limit(
policy.max_symbol_position,
"riskPolicy.maxSymbolPosition",
)?;
let max_order_quantity =
valid_positive_limit(policy.max_order_quantity, "riskPolicy.maxOrderQuantity")?;
let max_order_notional =
valid_positive_limit(policy.max_order_notional, "riskPolicy.maxOrderNotional")?;
let max_symbol_position =
valid_positive_limit(policy.max_symbol_position, "riskPolicy.maxSymbolPosition")?;
if let Some(value) = max_order_quantity {
cfg.risk_config.trading_constraints.max_order_quantity = value;
}
@@ -3433,9 +3449,18 @@ mod tests {
);
assert!(cfg.risk_config.trading_constraints.volume_limit_enabled);
assert!(cfg.risk_config.trading_constraints.liquidity_limit_enabled);
assert_eq!(cfg.risk_config.trading_constraints.max_order_quantity, 8000.0);
assert_eq!(cfg.risk_config.trading_constraints.max_order_notional, 2_000_000.0);
assert_eq!(cfg.risk_config.trading_constraints.max_symbol_position, 12_000.0);
assert_eq!(
cfg.risk_config.trading_constraints.max_order_quantity,
8000.0
);
assert_eq!(
cfg.risk_config.trading_constraints.max_order_notional,
2_000_000.0
);
assert_eq!(
cfg.risk_config.trading_constraints.max_symbol_position,
12_000.0
);
assert!((cfg.risk_config.trading_constraints.volume_percent - 0.25).abs() < 1e-12);
assert_eq!(
cfg.risk_config
@@ -3647,6 +3672,25 @@ mod tests {
assert_eq!(cfg.risk_config.trading_constraints.minimum_commission, 5.0);
}
#[test]
fn accepts_scoped_blacklist_context_in_runtime_risk_policy() {
let spec = serde_json::json!({
"execution": {
"riskPolicy": {
"account_blacklisted_instruments": {
"2075773": ["000001.SZ"]
},
"strategyBlacklistedInstruments": {
"live-gt-2075773-20260829": ["600000.SH"]
}
}
}
});
platform_expr_config_from_value("105", "932000.CSI", &spec)
.expect("scoped blacklist context is part of the shared runtime contract");
}
#[test]
fn rejects_conflicting_risk_policy_alias_values() {
let bool_conflict = serde_json::json!({