From 053f880e3432b885c6d94c1b609b55e3f1010fc9 Mon Sep 17 00:00:00 2001 From: boris Date: Sat, 12 Sep 2026 06:39:34 +0800 Subject: [PATCH] test: declare historical audit assumptions in non-liquidity fixtures --- crates/fidc-core/src/broker.rs | 96 +++++++++++-------- .../fidc-core/src/platform_expr_strategy.rs | 26 +++++ .../tests/automatic_trade_protection.rs | 4 +- crates/fidc-core/tests/corporate_actions.rs | 4 +- .../fidc-core/tests/decision_quote_preload.rs | 10 +- crates/fidc-core/tests/delisting.rs | 4 +- crates/fidc-core/tests/engine_hooks.rs | 58 +++++------ 7 files changed, 124 insertions(+), 78 deletions(-) diff --git a/crates/fidc-core/src/broker.rs b/crates/fidc-core/src/broker.rs index 33b1cd0..1fab28a 100644 --- a/crates/fidc-core/src/broker.rs +++ b/crates/fidc-core/src/broker.rs @@ -1484,8 +1484,6 @@ where ) -> Result { if self.volume_limit { self.volume_rate.map_err(|error| BacktestError::Execution(error.to_string()))?; - self.volume_capacity_mode.validate(true, self.matching_type_uses_intraday_quotes()) - .map_err(|error| BacktestError::Execution(error.to_string()))?; } let mut session = std::mem::take(&mut *self.execution_session.borrow_mut()); session.activate(date); @@ -4252,12 +4250,8 @@ where .saturating_sub(self.reserved_open_sell_quantity(symbol, None)); match self.market_fillable_quantity( snapshot, - OrderSide::Sell, sellable.min(current_qty), - minimum_order_quantity, - order_step_size, - 0, - sellable >= current_qty, + false, ) { Ok(quantity) => { let quantity = quantity.min(sellable).min(current_qty); @@ -4300,11 +4294,7 @@ where } match self.market_fillable_quantity( snapshot, - OrderSide::Buy, u32::MAX, - minimum_order_quantity, - order_step_size, - 0, false, ) { Ok(quantity) => { @@ -4573,14 +4563,12 @@ where } else { None }; + self.volume_capacity_mode.validate(self.volume_limit, algo_request.is_some() || self.matching_type_uses_intraday_quotes()) + .map_err(|error| BacktestError::Execution(error.to_string()))?; let market_limited_qty = self.market_fillable_quantity( snapshot, - OrderSide::Sell, requested_qty.min(sellable), - self.minimum_order_quantity(data, symbol), - self.order_step_size(data, symbol), - *intraday_turnover.get(symbol).unwrap_or(&0), - requested_qty >= position.quantity && sellable >= position.quantity, + algo_request.is_some(), ); let fillable_qty = match market_limited_qty { Ok(quantity) => { @@ -6382,14 +6370,12 @@ where } let mut partial_fill_reason = None; + self.volume_capacity_mode.validate(self.volume_limit, algo_request.is_some() || self.matching_type_uses_intraday_quotes()) + .map_err(|error| BacktestError::Execution(error.to_string()))?; let market_limited_qty = self.market_fillable_quantity( snapshot, - OrderSide::Buy, requested_qty, - self.minimum_order_quantity(data, symbol), - self.order_step_size(data, symbol), - *intraday_turnover.get(symbol).unwrap_or(&0), - false, + algo_request.is_some(), ); let constrained_qty = match market_limited_qty { Ok(quantity) => { @@ -7286,20 +7272,15 @@ where fn market_fillable_quantity( &self, snapshot: &crate::data::DailyMarketSnapshot, - side: OrderSide, requested_qty: u32, - minimum_order_quantity: u32, - order_step_size: u32, - consumed_turnover: u32, - allow_odd_lot_sell: bool, + algorithmic_order: bool, ) -> Result { if requested_qty == 0 { return Ok(0); } - let _ = (side, minimum_order_quantity, order_step_size, consumed_turnover, allow_odd_lot_sell); if self.inactive_limit && snapshot.paused { return Err("paused".into()); } - self.volume_capacity_mode.validate(self.volume_limit, self.matching_type_uses_intraday_quotes()) + self.volume_capacity_mode.validate(self.volume_limit, algorithmic_order || self.matching_type_uses_intraday_quotes()) .map_err(|error| error.to_string())?; // Per-observation limits are applied to each actual quote below. The // session-audit model must never size this order from the day's total. @@ -9412,7 +9393,42 @@ mod tests { } #[test] - fn current_bar_close_volume_limit_uses_daily_volume_when_minute_volume_missing() { + fn daily_session_volume_changes_only_audit_not_opening_fills() { + use crate::execution_capacity::VolumeCapacityMode; + let run = |volume: u64, mode: VolumeCapacityMode| { + let mut market = limit_test_snapshot(); + market.volume = volume; + let date = market.date; + let data = DataSet::from_components_with_actions_and_quotes( + vec![limit_test_instrument()], vec![market], vec![], + vec![limit_test_candidate(true, true)], vec![limit_test_benchmark()], vec![], vec![], + ).unwrap(); + let mut portfolio = PortfolioState::new(100_000.0); + let broker = BrokerSimulator::new(ChinaAShareCostModel::default(), ChinaEquityRuleHooks) + .with_volume_capacity_mode(mode).with_liquidity_limit(false); + let decision = StrategyDecision { order_intents: vec![OrderIntent::Shares { + symbol: "000001.SZ".into(), quantity: 1_000, reason: "capacity_test".into(), + }], ..StrategyDecision::default() }; + let before = portfolio.cash(); + let outcome = broker.execute(date, &mut portfolio, &data, &decision); + if outcome.is_err() { assert_eq!(portfolio.cash(), before); } + let audit = broker.audit_completed_session_capacity(date, &data).unwrap(); + (outcome, portfolio.cash(), audit) + }; + let (strict, _, _) = run(1_000_000, VolumeCapacityMode::ExecutionObservation); + assert!(strict.unwrap_err().to_string().contains("execution-time capacity is missing")); + let (a, cash_a, audit_a) = run(100, VolumeCapacityMode::SessionCapacityAudit); + let (b, cash_b, audit_b) = run(1_000_000, VolumeCapacityMode::SessionCapacityAudit); + let a = a.unwrap(); let b = b.unwrap(); + assert_eq!(a.fill_events.len(), 1); + assert_eq!(serde_json::to_value(&a.fill_events).unwrap(), serde_json::to_value(&b.fill_events).unwrap()); + assert_eq!(cash_a, cash_b); + assert_eq!(audit_a[0].filled_shares, 1_000); + assert!(!audit_a[0].passed); assert!(audit_b[0].passed); + } + + #[test] + fn daily_capacity_requires_a_timed_observation_instead_of_falling_back_to_total_volume() { let mut snapshot = limit_test_snapshot(); snapshot.minute_volume = 0; snapshot.volume = 1_000_000; @@ -9428,13 +9444,13 @@ mod tests { .with_liquidity_limit(true); let fillable = - broker.market_fillable_quantity(&snapshot, OrderSide::Buy, 5_000, 100, 100, 0, false); + broker.market_fillable_quantity(&snapshot, 5_000, false); - assert_eq!(fillable, Ok(5_000)); + assert!(fillable.unwrap_err().contains("daily session volume cannot size an earlier fill")); } #[test] - fn volume_limit_uses_floor_for_odd_lot_sell() { + fn session_capacity_audit_never_caps_an_early_odd_lot_sell() { let mut snapshot = limit_test_snapshot(); snapshot.minute_volume = 0; snapshot.volume = 3; @@ -9444,18 +9460,19 @@ mod tests { PriceField::Close, ) .with_matching_type(MatchingType::CurrentBarClose) + .with_volume_capacity_mode(crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit) .with_volume_limit(true) .with_volume_percent(0.5) .with_liquidity_limit(false); let fillable = - broker.market_fillable_quantity(&snapshot, OrderSide::Sell, 10, 100, 100, 0, true); + broker.market_fillable_quantity(&snapshot, 10, false); - assert_eq!(fillable, Ok(1)); + assert_eq!(fillable, Ok(10)); } #[test] - fn current_bar_close_volume_limit_rejects_daily_zero_volume() { + fn session_audit_does_not_infer_an_opening_suspension_from_future_zero_volume() { let mut snapshot = limit_test_snapshot(); snapshot.minute_volume = 0; snapshot.volume = 0; @@ -9465,13 +9482,16 @@ mod tests { PriceField::Close, ) .with_matching_type(MatchingType::CurrentBarClose) + .with_volume_capacity_mode(crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit) .with_volume_limit(true) .with_liquidity_limit(false); let fillable = - broker.market_fillable_quantity(&snapshot, OrderSide::Buy, 5_000, 100, 100, 0, false); + broker.market_fillable_quantity(&snapshot, 5_000, false); - assert_eq!(fillable, Err("daily no volume".to_string())); + assert_eq!(fillable, Ok(5_000)); + snapshot.paused = true; + assert_eq!(broker.market_fillable_quantity(&snapshot, 5_000, false), Err("paused".into())); } #[test] @@ -9492,7 +9512,7 @@ mod tests { .with_liquidity_limit(false); let fillable = - broker.market_fillable_quantity(&snapshot, OrderSide::Buy, 5_000, 100, 100, 0, false); + broker.market_fillable_quantity(&snapshot, 5_000, false); assert_eq!(fillable, Ok(5_000)); } diff --git a/crates/fidc-core/src/platform_expr_strategy.rs b/crates/fidc-core/src/platform_expr_strategy.rs index 8bec4bb..06c520f 100644 --- a/crates/fidc-core/src/platform_expr_strategy.rs +++ b/crates/fidc-core/src/platform_expr_strategy.rs @@ -14527,6 +14527,7 @@ mod tests { active_datetime: None, order_events: &[], fills: &[], }; let mut cfg = PlatformExprStrategyConfig::microcap_rotation(); + cfg.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; cfg.signal_symbol = symbol.to_string(); cfg.max_positions = 1; cfg.refresh_rate = 1; @@ -14560,6 +14561,7 @@ mod tests { active_datetime: None, order_events: &[], fills: &[], }; let mut cfg = PlatformExprStrategyConfig::generic(); + cfg.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; cfg.signal_symbol = symbol.into(); cfg.stock_filter_expr = "close > 0".into(); cfg.hold_until_exit_enabled = true; @@ -15175,6 +15177,7 @@ mod tests { } let data = DataSet::from_components(parts.instruments, parts.market, parts.factors, parts.candidates, parts.benchmarks).unwrap(); let mut config = PlatformExprStrategyConfig::generic(); + config.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; config.universe_include = Some(BTreeSet::from(["000001.SZ".to_owned()])); config.signal_symbol = "000001.SZ".to_owned(); config.benchmark_symbol = "000852.SH".to_owned(); @@ -15419,6 +15422,7 @@ mod tests { fills: &[], }; let mut config = PlatformExprStrategyConfig::microcap_rotation(); + config.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; config.signal_symbol = symbol.to_string(); config.refresh_rate = 1; config.max_positions = 1; @@ -15670,6 +15674,7 @@ mod tests { fills: &[], }; let mut cfg = PlatformExprStrategyConfig::microcap_rotation(); + cfg.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; cfg.signal_symbol = "000001.SZ".to_string(); cfg.refresh_rate = 1; cfg.max_positions = 3; @@ -22375,6 +22380,7 @@ mod tests { fills: &[], }; let mut cfg = PlatformExprStrategyConfig::microcap_rotation(); + cfg.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; cfg.signal_symbol = symbol.to_string(); cfg.exposure_expr = "1.0".to_string(); cfg.selection_limit_expr = "40".to_string(); @@ -22749,6 +22755,7 @@ mod tests { fills: &[], }; let mut cfg = PlatformExprStrategyConfig::microcap_rotation(); + cfg.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; cfg.rotation_enabled = false; cfg.daily_top_up_enabled = false; cfg.signal_symbol = symbol.to_string(); @@ -23705,6 +23712,7 @@ mod tests { fills: &[], }; let mut cfg = PlatformExprStrategyConfig::microcap_rotation(); + cfg.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; cfg.signal_symbol = symbol.to_string(); cfg.exposure_expr = "0.5".to_string(); cfg.selection_limit_expr = "40".to_string(); @@ -24961,6 +24969,7 @@ mod tests { fills: &[], }; let mut cfg = PlatformExprStrategyConfig::microcap_rotation(); + cfg.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; cfg.intraday_execution_time = Some(NaiveTime::from_hms_opt(9, 33, 0).unwrap()); cfg.signal_symbol = signal.to_string(); cfg.max_positions = 1; @@ -27428,6 +27437,7 @@ mod tests { fills: &[], }; let mut cfg = PlatformExprStrategyConfig::microcap_rotation(); + cfg.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; cfg.signal_symbol = "000001.SZ".to_string(); cfg.refresh_rate = 99; cfg.max_positions = 2; @@ -27569,6 +27579,7 @@ mod tests { fills: &[], }; let mut cfg = PlatformExprStrategyConfig::microcap_rotation(); + cfg.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; cfg.signal_symbol = "000001.SZ".to_string(); cfg.refresh_rate = 99; cfg.max_positions = 2; @@ -28359,6 +28370,7 @@ mod tests { fills: &[], }; let mut cfg = PlatformExprStrategyConfig::microcap_rotation(); + cfg.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; cfg.signal_symbol = "000001.SZ".to_string(); cfg.refresh_rate = 99; cfg.max_positions = 2; @@ -28678,6 +28690,7 @@ mod tests { .expect("dataset"); let mut cfg = PlatformExprStrategyConfig::microcap_rotation(); + cfg.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; cfg.signal_symbol = "000001.SZ".to_string(); cfg.refresh_rate = 20; cfg.max_positions = 2; @@ -28726,6 +28739,7 @@ mod tests { ); let mut dynamic_cfg = PlatformExprStrategyConfig::microcap_rotation(); + dynamic_cfg.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; dynamic_cfg.signal_symbol = "000001.SZ".to_string(); dynamic_cfg.refresh_rate = 20; dynamic_cfg.refresh_rate_expr = "year >= 2024 ? 5 : 20".to_string(); @@ -28751,6 +28765,7 @@ mod tests { ); let mut signal_dates_cfg = PlatformExprStrategyConfig::microcap_rotation(); + signal_dates_cfg.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; signal_dates_cfg.signal_symbol = "000001.SZ".to_string(); signal_dates_cfg.refresh_rate = 20; signal_dates_cfg.max_positions = 2; @@ -28786,6 +28801,7 @@ mod tests { ); let mut no_retry_cfg = PlatformExprStrategyConfig::microcap_rotation(); + no_retry_cfg.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; no_retry_cfg.signal_symbol = "000001.SZ".to_string(); no_retry_cfg.refresh_rate = 15; no_retry_cfg.max_positions = 2; @@ -28953,6 +28969,7 @@ mod tests { .expect("dataset"); let mut cfg = PlatformExprStrategyConfig::microcap_rotation(); + cfg.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; cfg.signal_symbol = "000001.SZ".to_string(); cfg.refresh_rate = 10; cfg.max_positions = 2; @@ -29130,6 +29147,7 @@ mod tests { fills: &[], }; let mut cfg = PlatformExprStrategyConfig::microcap_rotation(); + cfg.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; cfg.signal_symbol = "000001.SZ".to_string(); cfg.refresh_rate = 20; cfg.max_positions = 2; @@ -30343,6 +30361,7 @@ mod tests { fills: &[], }; let mut cfg = PlatformExprStrategyConfig::microcap_rotation(); + cfg.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; cfg.rotation_enabled = false; cfg.hold_until_exit_enabled = true; cfg.signal_symbol = symbol.to_string(); @@ -31605,6 +31624,7 @@ mod tests { fills: &[], }; let mut cfg = PlatformExprStrategyConfig::microcap_rotation(); + cfg.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; cfg.signal_symbol = "000001.SZ".to_string(); cfg.refresh_rate = 99; cfg.max_positions = 2; @@ -31758,6 +31778,7 @@ mod tests { fills: &[], }; let mut cfg = PlatformExprStrategyConfig::microcap_rotation(); + cfg.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; cfg.signal_symbol = "000001.SZ".to_string(); cfg.refresh_rate = 99; cfg.max_positions = 2; @@ -31922,6 +31943,7 @@ mod tests { fills: &[], }; let mut cfg = PlatformExprStrategyConfig::microcap_rotation(); + cfg.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; cfg.signal_symbol = "000001.SZ".to_string(); cfg.refresh_rate = 99; cfg.max_positions = 2; @@ -33943,6 +33965,7 @@ mod tests { fills: &[], }; let mut cfg = PlatformExprStrategyConfig::microcap_rotation(); + cfg.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; cfg.signal_symbol = "000001.SZ".to_string(); cfg.refresh_rate = 99; cfg.max_positions = 1; @@ -34120,6 +34143,7 @@ mod tests { fills: &[], }; let mut cfg = PlatformExprStrategyConfig::microcap_rotation(); + cfg.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; cfg.signal_symbol = "000001.SZ".to_string(); cfg.refresh_rate = 99; cfg.max_positions = 1; @@ -34162,6 +34186,7 @@ mod tests { ); let mut filtered_cfg = PlatformExprStrategyConfig::microcap_rotation(); + filtered_cfg.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; filtered_cfg.signal_symbol = "000001.SZ".to_string(); filtered_cfg.refresh_rate = 99; filtered_cfg.max_positions = 1; @@ -36485,6 +36510,7 @@ mod tests { fills: &[], }; let mut cfg = PlatformExprStrategyConfig::microcap_rotation(); + cfg.volume_capacity_mode = crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit; cfg.signal_symbol = "000001.SZ".to_string(); cfg.rotation_enabled = false; cfg.benchmark_short_ma_days = 1; diff --git a/crates/fidc-core/tests/automatic_trade_protection.rs b/crates/fidc-core/tests/automatic_trade_protection.rs index 20c5af5..eb85ee8 100644 --- a/crates/fidc-core/tests/automatic_trade_protection.rs +++ b/crates/fidc-core/tests/automatic_trade_protection.rs @@ -119,7 +119,7 @@ fn run(policy: AutomaticTradeProtection) -> fidc_core::BacktestResult { action("-100", "decision_date >= \"2026-09-14\""), ]; config.matching_type = MatchingType::CurrentBarClose; - let broker = BrokerSimulator::new(ChinaAShareCostModel::default(), ChinaEquityRuleHooks) + let broker = BrokerSimulator::new(ChinaAShareCostModel::default(), ChinaEquityRuleHooks).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit) .with_matching_type(MatchingType::CurrentBarClose); BacktestEngine::new( data(), @@ -294,7 +294,7 @@ fn locked_holding_keeps_its_slot_even_when_cash_can_buy_the_next_candidate() { }], ..Default::default() }; - let broker = BrokerSimulator::new(ChinaAShareCostModel::default(), ChinaEquityRuleHooks) + let broker = BrokerSimulator::new(ChinaAShareCostModel::default(), ChinaEquityRuleHooks).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit) .with_matching_type(MatchingType::CurrentBarClose); let result = BacktestEngine::new( dataset, diff --git a/crates/fidc-core/tests/corporate_actions.rs b/crates/fidc-core/tests/corporate_actions.rs index 27ed34a..0d5d37d 100644 --- a/crates/fidc-core/tests/corporate_actions.rs +++ b/crates/fidc-core/tests/corporate_actions.rs @@ -394,7 +394,7 @@ fn engine_reinvests_dividend_receivable_in_round_lots() { .with_minimum_commission(0.0), ChinaEquityRuleHooks::default(), PriceField::Open, - ), + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit), BacktestConfig { initial_cash: 11_008.0, benchmark_code: "000300.SH".to_string(), @@ -475,7 +475,7 @@ fn engine_settles_same_day_dividend_after_split_for_aiquant_semantics() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Open, - ), + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit), BacktestConfig { initial_cash: 11_008.0, benchmark_code: "000300.SH".to_string(), diff --git a/crates/fidc-core/tests/decision_quote_preload.rs b/crates/fidc-core/tests/decision_quote_preload.rs index e08db24..07802bf 100644 --- a/crates/fidc-core/tests/decision_quote_preload.rs +++ b/crates/fidc-core/tests/decision_quote_preload.rs @@ -170,7 +170,7 @@ fn engine_uses_preplanned_decision_symbols_without_recomputing_strategy_plan() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks, PriceField::Close, - ) + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit) .with_matching_type(MatchingType::CurrentBarClose); let config = BacktestConfig { initial_cash: 10_000.0, @@ -235,7 +235,7 @@ fn engine_skips_decision_quote_symbol_plan_without_loader() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks, PriceField::Close, - ) + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit) .with_matching_type(MatchingType::CurrentBarClose); let config = BacktestConfig { initial_cash: 10_000.0, @@ -391,7 +391,7 @@ fn engine_preloads_declared_decision_quotes_for_current_positions() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks, PriceField::Last, - ) + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit) .with_matching_type(MatchingType::MinuteLast) .with_intraday_execution_start_time(t(10, 40, 0)); let config = BacktestConfig { @@ -590,7 +590,7 @@ fn engine_reuses_preloaded_decision_quotes_without_loader_call() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks, PriceField::Last, - ) + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit) .with_matching_type(MatchingType::MinuteLast) .with_intraday_execution_start_time(t(10, 40, 0)); let config = BacktestConfig { @@ -796,7 +796,7 @@ fn engine_loads_distinct_decision_quote_times_on_same_day() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks, PriceField::Last, - ) + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit) .with_matching_type(MatchingType::MinuteLast) .with_intraday_execution_start_time(t(10, 40, 0)); let config = BacktestConfig { diff --git a/crates/fidc-core/tests/delisting.rs b/crates/fidc-core/tests/delisting.rs index 96d9bc1..55fcaf0 100644 --- a/crates/fidc-core/tests/delisting.rs +++ b/crates/fidc-core/tests/delisting.rs @@ -295,7 +295,7 @@ fn engine_keeps_unresolved_delisted_position_without_fabricating_a_fill() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Open, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( data, BuyThenHoldStrategy, @@ -548,7 +548,7 @@ fn engine_applies_successor_conversion_before_unresolved_delisting_audit() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Open, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( data, BuyThenHoldStrategy, diff --git a/crates/fidc-core/tests/engine_hooks.rs b/crates/fidc-core/tests/engine_hooks.rs index 3b1e925..07bb37c 100644 --- a/crates/fidc-core/tests/engine_hooks.rs +++ b/crates/fidc-core/tests/engine_hooks.rs @@ -1219,7 +1219,7 @@ fn engine_runs_strategy_hooks_in_daily_order() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Open, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( data, strategy, @@ -1260,7 +1260,7 @@ fn engine_runs_strategy_hooks_in_daily_order() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Open, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut compact_engine = BacktestEngine::new( compact_data, compact_strategy, @@ -1401,7 +1401,7 @@ fn engine_executes_open_auction_decisions_before_on_day() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::DayOpen, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( data, strategy, @@ -1497,7 +1497,7 @@ fn engine_executes_futures_order_intents_against_future_account() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Open, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( data, FuturesOrderStrategy, @@ -1569,7 +1569,7 @@ fn platform_runtime_actions_execute_generic_futures_open_and_close() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Open, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( two_day_futures_data(), PlatformExprStrategy::new(cfg), @@ -1609,7 +1609,7 @@ fn engine_settles_configured_futures_expiration_at_settlement() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Open, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( single_day_anchor_data(date), FuturesOrderStrategy, @@ -1657,7 +1657,7 @@ fn engine_aggregates_futures_account_into_nav_and_metrics() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Open, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( single_day_anchor_data(date), FuturesOrderStrategy, @@ -1700,7 +1700,7 @@ fn engine_matches_pending_futures_limit_order_with_data_driven_costs() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Open, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( two_day_futures_data(), FuturesLimitOrderStrategy, @@ -1753,7 +1753,7 @@ fn engine_reports_pending_futures_order_at_backtest_boundary() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Open, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( two_day_futures_data(), FuturesLimitOrderStrategy, @@ -1805,7 +1805,7 @@ fn engine_rejects_futures_limit_orders_not_aligned_to_tick() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Open, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( two_day_futures_data(), FuturesInvalidTickLimitStrategy, @@ -1836,7 +1836,7 @@ fn engine_allows_disabling_futures_limit_tick_validation() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Open, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( two_day_futures_data(), FuturesInvalidTickLimitStrategy, @@ -1883,7 +1883,7 @@ fn engine_rejects_futures_limit_orders_outside_price_limits() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Open, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( two_day_futures_data(), FuturesAboveUpperLimitStrategy, @@ -1958,7 +1958,7 @@ fn engine_rejects_futures_orders_when_trading_phase_is_closed() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Open, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( data, FuturesClosedPhaseOrderStrategy, @@ -2066,7 +2066,7 @@ fn engine_sweeps_futures_order_book_depth_when_available() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Last, - ) + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit) .with_matching_type(MatchingType::MinuteBestCounterparty); let mut engine = BacktestEngine::new( data, @@ -2111,7 +2111,7 @@ fn strategy_context_exposes_advanced_data_helpers() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Open, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( two_day_futures_data(), AdvancedDataApiProbeStrategy { @@ -2265,7 +2265,7 @@ fn engine_runs_minute_hooks_and_executes_minute_orders() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Last, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let loader_requests = Arc::new(Mutex::new(Vec::::new())); let loader_requests_for_callback = Arc::clone(&loader_requests); let mut engine = BacktestEngine::new( @@ -2381,7 +2381,7 @@ fn engine_skips_empty_platform_style_minute_callbacks_between_schedule_times() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Last, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( data, strategy, @@ -2579,7 +2579,7 @@ fn strategy_context_exposes_engine_native_data_helpers() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Open, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( data, strategy, @@ -2683,7 +2683,7 @@ fn strategy_context_exposes_final_order_runtime_view() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Close, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( data, strategy, @@ -2959,7 +2959,7 @@ fn engine_applies_account_cash_flow_and_financing_intents() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Close, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( data, AccountFlowStrategy, @@ -3157,7 +3157,7 @@ fn engine_expires_pending_day_limit_orders_at_market_close() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Open, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let strategy = LimitCarryStrategy { issued: false }; let mut engine = BacktestEngine::new( data, @@ -3394,7 +3394,7 @@ fn engine_runs_scheduled_rules_for_daily_weekly_and_monthly_triggers() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::DayOpen, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( data, strategy, @@ -3649,7 +3649,7 @@ fn engine_dispatches_process_events_to_external_bus_listeners() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::DayOpen, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( data, strategy, @@ -3741,7 +3741,7 @@ fn engine_installs_process_mods_on_event_bus() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::DayOpen, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( data, HookProbeStrategy { @@ -3778,7 +3778,7 @@ fn engine_installs_enabled_process_mods_from_loader() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::DayOpen, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( data, HookProbeStrategy { @@ -3981,7 +3981,7 @@ fn engine_applies_dynamic_universe_and_subscription_directives() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Open, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( data, strategy, @@ -4105,7 +4105,7 @@ fn engine_exposes_current_process_context_to_strategies() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Last, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( data, strategy, @@ -4215,7 +4215,7 @@ fn engine_rejects_an_unexplained_missing_holding_close() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Open, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( data, BuyMissingRowThenHoldStrategy, @@ -4314,7 +4314,7 @@ fn platform_strategy_cannot_hide_missing_valuation_by_skipping_stop_take() { ChinaAShareCostModel::default(), ChinaEquityRuleHooks::default(), PriceField::Open, - ); + ).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit); let mut engine = BacktestEngine::new( data, PlatformExprStrategy::new(config),