diff --git a/crates/fidc-core/src/platform_strategy_spec.rs b/crates/fidc-core/src/platform_strategy_spec.rs index 985780e..2a0fb89 100644 --- a/crates/fidc-core/src/platform_strategy_spec.rs +++ b/crates/fidc-core/src/platform_strategy_spec.rs @@ -1119,7 +1119,19 @@ fn apply_execution_behavior_overrides( "sellThenBuyDelaySlippageRate must be a finite number in [0, 1)".to_string(), ); } - cfg.sell_then_buy_delay_slippage_rate = rate; + if cfg.rebalance_cash_mode != RebalanceCashMode::SellThenBuy { + if rate != 0.0 { + return Err( + "sellThenBuyDelaySlippageRate can only be set when rebalanceCashMode is sell_then_buy" + .to_string(), + ); + } + cfg.sell_then_buy_delay_slippage_rate = 0.0; + } else { + cfg.sell_then_buy_delay_slippage_rate = rate; + } + } else if cfg.rebalance_cash_mode != RebalanceCashMode::SellThenBuy { + cfg.sell_then_buy_delay_slippage_rate = 0.0; } Ok(()) } @@ -2684,15 +2696,43 @@ mod tests { let spec = serde_json::json!({ "execution": { "matchingType": "next_bar_open", - "rebalanceCashMode": "pre_open_cash", - "sellThenBuyDelaySlippageRate": 0.003 + "rebalanceCashMode": "pre_open_cash" } }); let cfg = platform_expr_config_from_value("", "", &spec).expect("config"); assert_eq!(cfg.matching_type, MatchingType::NextBarOpen); assert_eq!(cfg.rebalance_cash_mode, RebalanceCashMode::PreOpenCash); - assert_eq!(cfg.sell_then_buy_delay_slippage_rate, 0.003); + assert_eq!(cfg.sell_then_buy_delay_slippage_rate, 0.0); + + let invalid_delay_spec = serde_json::json!({ + "execution": { + "matchingType": "next_bar_open", + "rebalanceCashMode": "pre_open_cash", + "sellThenBuyDelaySlippageRate": 0.003 + } + }); + let invalid_delay = platform_expr_config_from_value("", "", &invalid_delay_spec) + .expect_err("delay slippage should be rejected outside sell_then_buy"); + assert!(invalid_delay.to_string().contains("can only be set")); + + let override_to_pre_open_spec = serde_json::json!({ + "engineConfig": { + "rebalanceCashMode": "sell_then_buy", + "sellThenBuyDelaySlippageRate": 0.003 + }, + "execution": { + "matchingType": "next_bar_open", + "rebalanceCashMode": "pre_open_cash" + } + }); + let override_to_pre_open = + platform_expr_config_from_value("", "", &override_to_pre_open_spec).expect("config"); + assert_eq!( + override_to_pre_open.rebalance_cash_mode, + RebalanceCashMode::PreOpenCash + ); + assert_eq!(override_to_pre_open.sell_then_buy_delay_slippage_rate, 0.0); let minute_spec = serde_json::json!({ "execution": {