From 92322349d4859dbe3f5d35701a63049980a6420e Mon Sep 17 00:00:00 2001 From: boris Date: Fri, 4 Sep 2026 22:18:35 +0800 Subject: [PATCH] style: normalize fidc core formatting --- crates/fidc-core/src/broker.rs | 21 +- crates/fidc-core/src/data.rs | 223 ++++++++++-------- crates/fidc-core/src/engine.rs | 22 +- crates/fidc-core/src/lib.rs | 9 +- .../fidc-core/src/platform_expr_strategy.rs | 129 +++++----- .../fidc-core/src/platform_strategy_spec.rs | 3 +- crates/fidc-core/src/risk_control.rs | 6 +- crates/fidc-core/src/scheduler.rs | 33 ++- .../tests/intraday_history_performance.rs | 4 +- 9 files changed, 231 insertions(+), 219 deletions(-) diff --git a/crates/fidc-core/src/broker.rs b/crates/fidc-core/src/broker.rs index c7bc8cc..e5cfd4c 100644 --- a/crates/fidc-core/src/broker.rs +++ b/crates/fidc-core/src/broker.rs @@ -621,7 +621,7 @@ impl BrokerSimulator { self.runtime_order_created_date.get(), self.submission_time(), ) - .map(|(start, end)| (date.and_time(start), date.and_time(end))) + .map(|(start, end)| (date.and_time(start), date.and_time(end))) } fn post_close_execution_quote_window_for_submission( @@ -1208,8 +1208,7 @@ where matching_type: MatchingType, ) -> Option { if self.is_post_close_fixed_price(snapshot.date) { - return (snapshot.close.is_finite() && snapshot.close > 0.0) - .then_some(snapshot.close); + return (snapshot.close.is_finite() && snapshot.close > 0.0).then_some(snapshot.close); } let raw_price = match matching_type { MatchingType::MinuteBestOwn => match side { @@ -7684,8 +7683,7 @@ where match matching_type { MatchingType::OpenAuction | MatchingType::CurrentBarClose - | MatchingType::NextBarOpen - => false, + | MatchingType::NextBarOpen => false, MatchingType::MinuteLast => self.liquidity_limit, MatchingType::MinuteBestOwn | MatchingType::MinuteBestCounterparty @@ -8082,14 +8080,11 @@ mod tests { EquityExecutionPhase::ContinuousAuction ); - let next_open = BrokerSimulator::new( - ChinaAShareCostModel::default(), - ChinaEquityRuleHooks, - ) - .with_matching_type(MatchingType::NextBarOpen) - .with_intraday_execution_start_time( - NaiveTime::from_hms_opt(15, 0, 0).expect("valid signal time"), - ); + let next_open = BrokerSimulator::new(ChinaAShareCostModel::default(), ChinaEquityRuleHooks) + .with_matching_type(MatchingType::NextBarOpen) + .with_intraday_execution_start_time( + NaiveTime::from_hms_opt(15, 0, 0).expect("valid signal time"), + ); next_open .runtime_order_created_date .set(Some(date.pred_opt().expect("previous date"))); diff --git a/crates/fidc-core/src/data.rs b/crates/fidc-core/src/data.rs index b4f93f8..b3c7a06 100644 --- a/crates/fidc-core/src/data.rs +++ b/crates/fidc-core/src/data.rs @@ -885,12 +885,7 @@ impl SymbolPriceSeries { self.moving_average_at_end(end, lookback, field) } - fn moving_average_at_end( - &self, - end: usize, - lookback: usize, - field: PriceField, - ) -> Option { + fn moving_average_at_end(&self, end: usize, lookback: usize, field: PriceField) -> Option { if end < lookback { return None; } @@ -1813,7 +1808,8 @@ impl DataSet { let market_series_end_positions_by_symbol_id = build_symbol_series_end_positions(&market_series_by_symbol_id, &calendar); let execution_quotes_by_date = build_execution_quote_index(execution_quotes); - let mut execution_quote_dates = execution_quotes_by_date.keys().copied().collect::>(); + let mut execution_quote_dates = + execution_quotes_by_date.keys().copied().collect::>(); execution_quote_dates.sort_unstable(); let order_book_depth_index = build_order_book_depth_index(order_book_depth); @@ -1910,11 +1906,7 @@ impl DataSet { { return rows.get(index); } - find_by_symbol_id( - rows, - self.market_symbol_ids_by_date.get(&date)?, - symbol_id, - ) + find_by_symbol_id(rows, self.market_symbol_ids_by_date.get(&date)?, symbol_id) } pub(crate) fn daily_snapshot_view(&self, date: NaiveDate) -> DailySnapshotView<'_> { @@ -2021,11 +2013,7 @@ impl DataSet { { return rows.get(index); } - find_by_symbol_id( - rows, - self.factor_symbol_ids_by_date.get(&date)?, - symbol_id, - ) + find_by_symbol_id(rows, self.factor_symbol_ids_by_date.get(&date)?, symbol_id) } pub fn candidate(&self, date: NaiveDate, symbol: &str) -> Option<&CandidateEligibility> { @@ -2067,7 +2055,9 @@ impl DataSet { .map(|series| { series_end .map(|end| series.moving_averages_at_end(end, close_lookbacks)) - .unwrap_or_else(|| series.moving_averages(date, close_lookbacks, include_now)) + .unwrap_or_else(|| { + series.moving_averages(date, close_lookbacks, include_now) + }) }) .unwrap_or([None; 7]) } else { @@ -2243,10 +2233,7 @@ impl DataSet { if let Ok(index) = dates.binary_search(&date) { dates.remove(index); } - rows_by_symbol - .into_values() - .map(|rows| rows.len()) - .sum() + rows_by_symbol.into_values().map(|rows| rows.len()).sum() } pub fn snapshot_components(&self) -> DataSetSnapshotComponents { @@ -3249,31 +3236,39 @@ impl DataSet { .map(|end| series.moving_average_at_end(end, lookback)) .unwrap_or_else(|| series.decision_moving_average(date, lookback)) }), - "volume" | "stock_volume" => self - .market_series_by_symbol_id(symbol_id) - .and_then(|series| { - self.market_series_end_index_by_symbol_id(date, symbol_id, false) - .map(|end| { - series.valid_volume_window(end, lookback).map(|(start, end)| { - normalize_rolling_factor( - (series.valid_volume_sum_prefix[end] - - series.valid_volume_sum_prefix[start]) - / lookback as f64, - 12, - ) + "volume" | "stock_volume" => { + self.market_series_by_symbol_id(symbol_id) + .and_then(|series| { + self.market_series_end_index_by_symbol_id(date, symbol_id, false) + .map(|end| { + series + .valid_volume_window(end, lookback) + .map(|(start, end)| { + normalize_rolling_factor( + (series.valid_volume_sum_prefix[end] + - series.valid_volume_sum_prefix[start]) + / lookback as f64, + 12, + ) + }) }) - }) - .unwrap_or_else(|| series.decision_volume_moving_average(date, lookback)) - }), - "day_open" | "dayopen" => self - .market_series_by_symbol_id(symbol_id) - .and_then(|series| { - self.market_series_end_index_by_symbol_id(date, symbol_id, false) - .map(|end| series.moving_average_at_end(end, lookback, PriceField::DayOpen)) - .unwrap_or_else(|| { - series.moving_average(date, lookback, PriceField::DayOpen) - }) - }), + .unwrap_or_else(|| { + series.decision_volume_moving_average(date, lookback) + }) + }) + } + "day_open" | "dayopen" => { + self.market_series_by_symbol_id(symbol_id) + .and_then(|series| { + self.market_series_end_index_by_symbol_id(date, symbol_id, false) + .map(|end| { + series.moving_average_at_end(end, lookback, PriceField::DayOpen) + }) + .unwrap_or_else(|| { + series.moving_average(date, lookback, PriceField::DayOpen) + }) + }) + } "open" => self .market_series_by_symbol_id(symbol_id) .and_then(|series| { @@ -3281,13 +3276,18 @@ impl DataSet { .map(|end| series.moving_average_at_end(end, lookback, PriceField::Open)) .unwrap_or_else(|| series.moving_average(date, lookback, PriceField::Open)) }), - "last" | "last_price" => self - .market_series_by_symbol_id(symbol_id) - .and_then(|series| { - self.market_series_end_index_by_symbol_id(date, symbol_id, false) - .map(|end| series.moving_average_at_end(end, lookback, PriceField::Last)) - .unwrap_or_else(|| series.moving_average(date, lookback, PriceField::Last)) - }), + "last" | "last_price" => { + self.market_series_by_symbol_id(symbol_id) + .and_then(|series| { + self.market_series_end_index_by_symbol_id(date, symbol_id, false) + .map(|end| { + series.moving_average_at_end(end, lookback, PriceField::Last) + }) + .unwrap_or_else(|| { + series.moving_average(date, lookback, PriceField::Last) + }) + }) + } other => self.factor_moving_average(date, symbol, other, lookback), } } @@ -3335,31 +3335,37 @@ impl DataSet { .map(|end| series.moving_average_at_end(end, lookback)) .unwrap_or_else(|| series.current_moving_average(date, lookback)) }), - "volume" | "stock_volume" => self - .market_series_by_symbol_id(symbol_id) - .and_then(|series| { - self.market_series_end_index_by_symbol_id(date, symbol_id, true) - .map(|end| { - series.valid_volume_window(end, lookback).map(|(start, end)| { - normalize_rolling_factor( - (series.valid_volume_sum_prefix[end] - - series.valid_volume_sum_prefix[start]) - / lookback as f64, - 12, - ) + "volume" | "stock_volume" => { + self.market_series_by_symbol_id(symbol_id) + .and_then(|series| { + self.market_series_end_index_by_symbol_id(date, symbol_id, true) + .map(|end| { + series + .valid_volume_window(end, lookback) + .map(|(start, end)| { + normalize_rolling_factor( + (series.valid_volume_sum_prefix[end] + - series.valid_volume_sum_prefix[start]) + / lookback as f64, + 12, + ) + }) }) - }) - .unwrap_or_else(|| series.current_volume_moving_average(date, lookback)) - }), - "day_open" | "dayopen" => self - .market_series_by_symbol_id(symbol_id) - .and_then(|series| { - self.market_series_end_index_by_symbol_id(date, symbol_id, true) - .map(|end| series.moving_average_at_end(end, lookback, PriceField::DayOpen)) - .unwrap_or_else(|| { - series.moving_average(date, lookback, PriceField::DayOpen) - }) - }), + .unwrap_or_else(|| series.current_volume_moving_average(date, lookback)) + }) + } + "day_open" | "dayopen" => { + self.market_series_by_symbol_id(symbol_id) + .and_then(|series| { + self.market_series_end_index_by_symbol_id(date, symbol_id, true) + .map(|end| { + series.moving_average_at_end(end, lookback, PriceField::DayOpen) + }) + .unwrap_or_else(|| { + series.moving_average(date, lookback, PriceField::DayOpen) + }) + }) + } "open" => self .market_series_by_symbol_id(symbol_id) .and_then(|series| { @@ -3367,13 +3373,18 @@ impl DataSet { .map(|end| series.moving_average_at_end(end, lookback, PriceField::Open)) .unwrap_or_else(|| series.moving_average(date, lookback, PriceField::Open)) }), - "last" | "last_price" => self - .market_series_by_symbol_id(symbol_id) - .and_then(|series| { - self.market_series_end_index_by_symbol_id(date, symbol_id, true) - .map(|end| series.moving_average_at_end(end, lookback, PriceField::Last)) - .unwrap_or_else(|| series.moving_average(date, lookback, PriceField::Last)) - }), + "last" | "last_price" => { + self.market_series_by_symbol_id(symbol_id) + .and_then(|series| { + self.market_series_end_index_by_symbol_id(date, symbol_id, true) + .map(|end| { + series.moving_average_at_end(end, lookback, PriceField::Last) + }) + .unwrap_or_else(|| { + series.moving_average(date, lookback, PriceField::Last) + }) + }) + } other => self.factor_moving_average(date, symbol, other, lookback), } } @@ -4732,8 +4743,12 @@ mod tests { execution_quotes.clone(), ) .expect("flat dataset"); - let grouped = DataSet::from_daily_bundles_with_execution_quotes(instruments, bundles, execution_quotes) - .expect("daily bundle dataset"); + let grouped = DataSet::from_daily_bundles_with_execution_quotes( + instruments, + bundles, + execution_quotes, + ) + .expect("daily bundle dataset"); assert_eq!(flat.calendar().days(), grouped.calendar().days()); assert_eq!(flat.benchmark_code(), grouped.benchmark_code()); @@ -4901,7 +4916,8 @@ mod tests { let signal_id = data.symbol_id("000300.SH").unwrap(); let day = data.daily_snapshot_view(date); assert_eq!( - data.market_by_symbol_id(date, signal_id).map(|row| row.symbol.as_str()), + data.market_by_symbol_id(date, signal_id) + .map(|row| row.symbol.as_str()), Some("000300.SH") ); assert!(data.factor_by_symbol_id(date, signal_id).is_none()); @@ -4923,9 +4939,10 @@ mod tests { data.get_factor("000300.SH", date, date, "market_cap") .is_empty() ); - assert!(data - .get_factor("999999.SZ", date, date, "market_cap") - .is_empty()); + assert!( + data.get_factor("999999.SZ", date, date, "market_cap") + .is_empty() + ); } #[test] @@ -4955,11 +4972,7 @@ mod tests { .iter() .enumerate() .map(|(index, symbol)| { - let mut row = market_row( - "2025-01-02", - 10.0 + index as f64 / 1000.0, - 1_000_000, - ); + let mut row = market_row("2025-01-02", 10.0 + index as f64 / 1000.0, 1_000_000); row.symbol = symbol.clone(); row }) @@ -5407,7 +5420,11 @@ mod tests { .iter() .enumerate() .map(|(index, date)| { - market_row(&date.format("%Y-%m-%d").to_string(), 10.0 + index as f64, 100 + index as u64) + market_row( + &date.format("%Y-%m-%d").to_string(), + 10.0 + index as f64, + 100 + index as u64, + ) }) .collect(), dates @@ -5420,7 +5437,10 @@ mod tests { pe_ttm: 10.0, turnover_ratio: None, effective_turnover_ratio: None, - extra_factors: BTreeMap::from([(Cow::Borrowed(BACKWARD_ADJUSTMENT_FACTOR_FIELD), 1.0)]), + extra_factors: BTreeMap::from([( + Cow::Borrowed(BACKWARD_ADJUSTMENT_FACTOR_FIELD), + 1.0, + )]), }) .collect(), Vec::new(), @@ -5507,10 +5527,11 @@ mod tests { let symbol_id = data.symbol_id("000001.SZ").expect("symbol id"); let dates = data.calendar().days(); - assert!(data - .market_series_end_positions_by_symbol_id - .as_ref() - .is_some()); + assert!( + data.market_series_end_positions_by_symbol_id + .as_ref() + .is_some() + ); assert_eq!( data.market_series_end_index_by_symbol_id(dates[0], symbol_id, false), Some(0) diff --git a/crates/fidc-core/src/engine.rs b/crates/fidc-core/src/engine.rs index dc25d1c..030266a 100644 --- a/crates/fidc-core/src/engine.rs +++ b/crates/fidc-core/src/engine.rs @@ -2226,8 +2226,7 @@ where let coarse_schedule_rules = schedule_rules .iter() .filter(|rule| { - keep_timed_rules_on_coarse_stage - || !is_on_day_or_bar_physical_time_rule(rule) + keep_timed_rules_on_coarse_stage || !is_on_day_or_bar_physical_time_rule(rule) }) .cloned() .collect::>(); @@ -2774,9 +2773,7 @@ where ]); let minute_all_time_rules = intraday_schedule_rules .iter() - .filter(|rule| { - rule.stage == ScheduleStage::Minute && rule.time_rule.is_none() - }) + .filter(|rule| rule.stage == ScheduleStage::Minute && rule.time_rule.is_none()) .cloned() .collect::>(); let minute_schedule_all_times = !minute_all_time_rules.is_empty(); @@ -2798,10 +2795,9 @@ where loop { let next_quote_timestamp = minute_quotes.peek().map(|quote| quote.timestamp); let next_schedule_timestamp = minute_schedule_timestamps.peek().copied(); - let Some(minute_timestamp) = next_minute_event_timestamp( - next_quote_timestamp, - next_schedule_timestamp, - ) else { + let Some(minute_timestamp) = + next_minute_event_timestamp(next_quote_timestamp, next_schedule_timestamp) + else { break; }; let minute_time = minute_timestamp.time(); @@ -2816,8 +2812,7 @@ where .expect("peeked minute quote must be available"), ); } - let has_specific_schedule = - next_schedule_timestamp == Some(minute_timestamp); + let has_specific_schedule = next_schedule_timestamp == Some(minute_timestamp); if has_specific_schedule { minute_schedule_timestamps.next(); } @@ -5649,7 +5644,10 @@ mod tests { result.fills[0].execution_start_timestamp, date.and_hms_opt(10, 18, 0) ); - assert_eq!(result.fills[1].execution_timestamp, date.and_hms_opt(15, 10, 0)); + assert_eq!( + result.fills[1].execution_timestamp, + date.and_hms_opt(15, 10, 0) + ); assert_eq!(result.fills[1].price, 10.0); assert_eq!(result.fills[0].reason, "morning"); assert_eq!(result.fills[1].reason, "post_close"); diff --git a/crates/fidc-core/src/lib.rs b/crates/fidc-core/src/lib.rs index c2241db..b2ddcb1 100644 --- a/crates/fidc-core/src/lib.rs +++ b/crates/fidc-core/src/lib.rs @@ -75,11 +75,10 @@ pub use platform_strategy_spec::{ StrategyExpressionActionConfig, StrategyExpressionAllocationConfig, StrategyExpressionOrderingConfig, StrategyExpressionRiskConfig, StrategyExpressionScheduleConfig, StrategyExpressionSelectionConfig, - StrategyExpressionTradingConfig, StrategyPortfolioDrawdownControlConfig, - StrategyRebalanceSpec, StrategyRiskPolicySpec, StrategyRuntimeEnvironment, - StrategyRuntimeExpressions, StrategyRuntimeSpec, StrategyUniverseSpec, - platform_expr_config_from_spec, platform_expr_config_from_value, - validate_strategy_risk_policy_fields, + StrategyExpressionTradingConfig, StrategyPortfolioDrawdownControlConfig, StrategyRebalanceSpec, + StrategyRiskPolicySpec, StrategyRuntimeEnvironment, StrategyRuntimeExpressions, + StrategyRuntimeSpec, StrategyUniverseSpec, platform_expr_config_from_spec, + platform_expr_config_from_value, validate_strategy_risk_policy_fields, }; pub use portfolio::{CashReceivable, HoldingSummary, PendingCashFlow, PortfolioState, Position}; pub use risk_control::{ diff --git a/crates/fidc-core/src/platform_expr_strategy.rs b/crates/fidc-core/src/platform_expr_strategy.rs index 4f01139..2d6159d 100644 --- a/crates/fidc-core/src/platform_expr_strategy.rs +++ b/crates/fidc-core/src/platform_expr_strategy.rs @@ -1325,7 +1325,8 @@ impl PlatformExprStrategy { Self::stock_extra_factors_required_for_config(&config, &prelude_declared_identifiers); let stock_extra_factor_identifiers = Self::stock_extra_factor_identifiers_for_config(&config, &prelude_declared_identifiers); - let stock_extra_factor_map_required = Self::stock_extra_factor_map_required_for_config(&config); + let stock_extra_factor_map_required = + Self::stock_extra_factor_map_required_for_config(&config); let stock_text_factors_required = Self::stock_text_factors_required_for_config( &config, &normalized_stock_filter_expr, @@ -1503,8 +1504,7 @@ impl PlatformExprStrategy { when_expr, .. } => { - let target_field = - format!("explicit_actions[{index}].target_weights_expr"); + let target_field = format!("explicit_actions[{index}].target_weights_expr"); if !Self::collect_preflight_float_map_values( &target_field, target_weights_expr, @@ -4019,20 +4019,26 @@ impl PlatformExprStrategy { return Ok(Arc::clone(state)); } - let market = ctx.data.market_by_symbol_id(date, symbol_id).ok_or_else(|| { - BacktestError::Data(crate::data::DataSetError::MissingSnapshot { - kind: "market", - date, - symbol: symbol.to_string(), - }) - })?; - let candidate = ctx.data.candidate_by_symbol_id(date, symbol_id).ok_or_else(|| { - BacktestError::Data(crate::data::DataSetError::MissingSnapshot { - kind: "candidate", - date, - symbol: symbol.to_string(), - }) - })?; + let market = ctx + .data + .market_by_symbol_id(date, symbol_id) + .ok_or_else(|| { + BacktestError::Data(crate::data::DataSetError::MissingSnapshot { + kind: "market", + date, + symbol: symbol.to_string(), + }) + })?; + let candidate = ctx + .data + .candidate_by_symbol_id(date, symbol_id) + .ok_or_else(|| { + BacktestError::Data(crate::data::DataSetError::MissingSnapshot { + kind: "candidate", + date, + symbol: symbol.to_string(), + }) + })?; let feature_market = if factor_date == date { market } else { @@ -4040,13 +4046,16 @@ impl PlatformExprStrategy { .market_by_symbol_id(factor_date, symbol_id) .unwrap_or(market) }; - let factor = ctx.data.factor_by_symbol_id(factor_date, symbol_id).ok_or_else(|| { - BacktestError::Data(crate::data::DataSetError::MissingSnapshot { - kind: "factor", - date: factor_date, - symbol: symbol.to_string(), - }) - })?; + let factor = ctx + .data + .factor_by_symbol_id(factor_date, symbol_id) + .ok_or_else(|| { + BacktestError::Data(crate::data::DataSetError::MissingSnapshot { + kind: "factor", + date: factor_date, + symbol: symbol.to_string(), + }) + })?; let intraday_same_day_factor = self.uses_intraday_execution_quotes() && factor_date == date && !ctx.is_lagged_execution(); @@ -6353,13 +6362,7 @@ impl PlatformExprStrategy { args: &[String], ) -> Result { if let Some(compiled_args) = Self::compile_runtime_helper_args(helper, args) { - return self.resolve_compiled_runtime_helper( - ctx, - day, - stock, - helper, - &compiled_args, - ); + return self.resolve_compiled_runtime_helper(ctx, day, stock, helper, &compiled_args); } match helper { "factor" => { @@ -7665,8 +7668,8 @@ impl PlatformExprStrategy { &self.config.position_exposure_schedule, ctx.execution_date, ) - .unwrap_or(strategy_exposure) - .clamp(0.0, 1.0); + .unwrap_or(strategy_exposure) + .clamp(0.0, 1.0); let Some(controller) = self.portfolio_drawdown_controller.as_mut() else { return Ok(risk_on_exposure); }; @@ -9231,14 +9234,13 @@ impl PlatformExprStrategy { continue; } let synthetic_candidate; - let candidate = - if let Some(candidate) = execution_day.candidate(symbol_id) { - candidate - } else { - synthetic_candidate = - crate::data::missing_candidate_risk_state(date, &factor.symbol); - &synthetic_candidate - }; + let candidate = if let Some(candidate) = execution_day.candidate(symbol_id) { + candidate + } else { + synthetic_candidate = + crate::data::missing_candidate_risk_state(date, &factor.symbol); + &synthetic_candidate + }; let Some(market) = execution_day.market(symbol_id) else { continue; }; @@ -9579,10 +9581,7 @@ impl PlatformExprStrategy { fn rank_reuses_market_cap_order(&self) -> bool { self.config.rank_expr.trim().is_empty() && !self.config.rank_desc - && matches!( - self.config.rank_by.trim(), - "market_cap" | "market_cap_bn" - ) + && matches!(self.config.rank_by.trim(), "market_cap" | "market_cap_bn") } fn selection_candidate_passes_filters( @@ -12559,12 +12558,11 @@ mod tests { use super::{ CompiledRuntimeHelperArgs, PlatformAccountActionKind, PlatformExplicitActionStage, PlatformExplicitCancelKind, PlatformExplicitOrderKind, PlatformExprStrategy, - PlatformExprStrategyConfig, - PlatformPortfolioDrawdownControlConfig, PlatformPortfolioDrawdownController, - PlatformRebalanceSchedule, PlatformScheduleFrequency, PlatformStopTakeReferencePriceMode, - PlatformTradeAction, PlatformUniverseActionKind, RuntimeHelperResolution, - SelectionRiskDeferral, StockFilterQuoteUsage, framework_stock_rolling_factor_requirement, - scheduled_position_exposure, + PlatformExprStrategyConfig, PlatformPortfolioDrawdownControlConfig, + PlatformPortfolioDrawdownController, PlatformRebalanceSchedule, PlatformScheduleFrequency, + PlatformStopTakeReferencePriceMode, PlatformTradeAction, PlatformUniverseActionKind, + RuntimeHelperResolution, SelectionRiskDeferral, StockFilterQuoteUsage, + framework_stock_rolling_factor_requirement, scheduled_position_exposure, }; use crate::{ AlgoOrderStyle, BenchmarkSnapshot, CandidateEligibility, CorporateAction, @@ -12582,10 +12580,7 @@ mod tests { #[test] fn dated_position_exposure_uses_the_latest_effective_point() { - let schedule = BTreeMap::from([ - (d(2026, 8, 14), 0.6451), - (d(2026, 8, 20), 0.3225), - ]); + let schedule = BTreeMap::from([(d(2026, 8, 14), 0.6451), (d(2026, 8, 20), 0.3225)]); assert_eq!(scheduled_position_exposure(&schedule, d(2026, 8, 13)), None); assert_eq!( scheduled_position_exposure(&schedule, d(2026, 8, 14)), @@ -14429,7 +14424,9 @@ mod tests { .stock_state_with_factor_date(&ctx, date, date, present_symbol) .expect("factor map stock state"); assert!(map_stock.extra_factors.contains_key("unused_factor")); - let map_day = map_strategy.day_state(&ctx, date).expect("factor map day state"); + let map_day = map_strategy + .day_state(&ctx, date) + .expect("factor map day state"); assert!( map_strategy .stock_passes_expr(&ctx, &map_day, &map_stock) @@ -33310,11 +33307,7 @@ let target_exposure = csi_ready ? dynamic_exposure : 0.0; factor_value(\"quality_score\", 1)", ); let vm = plan.numeric_vm.as_ref().expect("numeric VM plan"); - let bindings = vm - .helper_bindings - .iter() - .flatten() - .collect::>(); + let bindings = vm.helper_bindings.iter().flatten().collect::>(); assert_eq!(bindings.len(), 12); assert!( @@ -33336,14 +33329,12 @@ let target_exposure = csi_ready ? dynamic_exposure : 0.0; let generic_started = std::time::Instant::now(); let mut generic_checksum = 0usize; for _ in 0..iterations { - let field = PlatformExprStrategy::parse_string_or_identifier(std::hint::black_box( - &args[0], - )) - .expect("field"); - let lookback = PlatformExprStrategy::parse_positive_usize(std::hint::black_box( - &args[1], - )) - .expect("lookback"); + let field = + PlatformExprStrategy::parse_string_or_identifier(std::hint::black_box(&args[0])) + .expect("field"); + let lookback = + PlatformExprStrategy::parse_positive_usize(std::hint::black_box(&args[1])) + .expect("lookback"); generic_checksum = generic_checksum.wrapping_add(field.len() + lookback); } let generic_seconds = generic_started.elapsed().as_secs_f64(); diff --git a/crates/fidc-core/src/platform_strategy_spec.rs b/crates/fidc-core/src/platform_strategy_spec.rs index d46d19c..6e2b331 100644 --- a/crates/fidc-core/src/platform_strategy_spec.rs +++ b/crates/fidc-core/src/platform_strategy_spec.rs @@ -4057,8 +4057,7 @@ mod tests { }); let cfg = platform_expr_config_from_value("", "", &spec).expect("config"); assert_eq!( - cfg.position_exposure_schedule - [&NaiveDate::from_ymd_opt(2026, 8, 14).unwrap()], + cfg.position_exposure_schedule[&NaiveDate::from_ymd_opt(2026, 8, 14).unwrap()], 0.6451, ); assert_eq!(cfg.position_exposure_schedule.len(), 2); diff --git a/crates/fidc-core/src/risk_control.rs b/crates/fidc-core/src/risk_control.rs index aa50e59..47f7f35 100644 --- a/crates/fidc-core/src/risk_control.rs +++ b/crates/fidc-core/src/risk_control.rs @@ -3,10 +3,10 @@ use std::collections::BTreeSet; use chrono::NaiveDate; use serde::{Deserialize, Serialize}; +use crate::OrderSide; use crate::data::{CandidateEligibility, DailyMarketSnapshot, PriceField}; use crate::instrument::Instrument; use crate::portfolio::Position; -use crate::OrderSide; #[derive(Debug, Clone, Copy, Default)] pub struct ChinaAShareRiskControl; @@ -560,7 +560,9 @@ impl ChinaAShareRiskControl { // lifecycle fact must still protect the sell path. Otherwise a // `inactive_or_delisted` candidate could fall through to a synthetic // sell price and violate the unresolved-delisted holding contract. - if let Some(reason) = candidate_active_status_rejection(candidate, config, RiskCheckScope::Sell) { + if let Some(reason) = + candidate_active_status_rejection(candidate, config, RiskCheckScope::Sell) + { return Some(reason); } if config.static_rules.reject_paused_sell && (market.paused || candidate.is_paused) { diff --git a/crates/fidc-core/src/scheduler.rs b/crates/fidc-core/src/scheduler.rs index 9671d7d..d22c8d6 100644 --- a/crates/fidc-core/src/scheduler.rs +++ b/crates/fidc-core/src/scheduler.rs @@ -284,18 +284,27 @@ mod tests { assert!(scheduler.is_due_on(d(2025, 1, 30), &daily)); assert!(scheduler.is_due_on(d(2025, 1, 31), &daily)); - assert!(scheduler.triggered_rules_at( - d(2025, 1, 30), - ScheduleStage::OnDay, - Some(NaiveTime::from_hms_opt(15, 0, 0).unwrap()), - std::slice::from_ref(&daily), - ).len() == 1); - assert!(scheduler.triggered_rules_at( - d(2025, 1, 30), - ScheduleStage::OnDay, - Some(NaiveTime::from_hms_opt(10, 18, 0).unwrap()), - std::slice::from_ref(&daily), - ).is_empty()); + assert!( + scheduler + .triggered_rules_at( + d(2025, 1, 30), + ScheduleStage::OnDay, + Some(NaiveTime::from_hms_opt(15, 0, 0).unwrap()), + std::slice::from_ref(&daily), + ) + .len() + == 1 + ); + assert!( + scheduler + .triggered_rules_at( + d(2025, 1, 30), + ScheduleStage::OnDay, + Some(NaiveTime::from_hms_opt(10, 18, 0).unwrap()), + std::slice::from_ref(&daily), + ) + .is_empty() + ); } #[test] diff --git a/crates/fidc-core/tests/intraday_history_performance.rs b/crates/fidc-core/tests/intraday_history_performance.rs index 9d4722a..f83179a 100644 --- a/crates/fidc-core/tests/intraday_history_performance.rs +++ b/crates/fidc-core/tests/intraday_history_performance.rs @@ -52,9 +52,7 @@ fn dataset(day_count: usize, bars_per_day: usize) -> (DataSet, Vec) { .collect::>(); let mut quotes = Vec::with_capacity(day_count * bars_per_day); for date in &dates { - let session_start = date - .and_hms_opt(9, 30, 0) - .expect("valid session start"); + let session_start = date.and_hms_opt(9, 30, 0).expect("valid session start"); for offset in 0..bars_per_day { let timestamp = session_start + Duration::minutes(offset as i64); quotes.push(IntradayExecutionQuote {