diff --git a/crates/fidc-core/src/data.rs b/crates/fidc-core/src/data.rs index 8c1b639..22e2731 100644 --- a/crates/fidc-core/src/data.rs +++ b/crates/fidc-core/src/data.rs @@ -161,6 +161,8 @@ impl DailyMarketSnapshot { } } +pub type NumericFactorMap = BTreeMap, f64>; + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DailyFactorSnapshot { #[serde(with = "date_format")] @@ -172,7 +174,7 @@ pub struct DailyFactorSnapshot { pub turnover_ratio: Option, pub effective_turnover_ratio: Option, #[serde(default)] - pub extra_factors: BTreeMap, + pub extra_factors: NumericFactorMap, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -3264,11 +3266,15 @@ fn normalize_factor_snapshots(factors: Vec) -> Vec= 0.5 { - extra_factors.insert("daily_volume".to_string(), volumes[index] as f64); + extra_factors.insert("daily_volume".into(), volumes[index] as f64); } } DailyFactorSnapshot { @@ -4140,10 +4145,7 @@ mod tests { pe_ttm: 10.0, turnover_ratio: None, effective_turnover_ratio: None, - extra_factors: BTreeMap::from([( - "adjustment_factor_backward1".to_string(), - factor, - )]), + extra_factors: BTreeMap::from([("adjustment_factor_backward1".into(), factor)]), }) .collect(), Vec::new(), @@ -4243,7 +4245,7 @@ mod tests { extra_factors: if *date == dates[3] { BTreeMap::new() } else { - BTreeMap::from([("adjustment_factor_backward1".to_string(), 1.0)]) + BTreeMap::from([("adjustment_factor_backward1".into(), 1.0)]) }, }) .collect(), diff --git a/crates/fidc-core/src/lib.rs b/crates/fidc-core/src/lib.rs index a2de0c6..957054c 100644 --- a/crates/fidc-core/src/lib.rs +++ b/crates/fidc-core/src/lib.rs @@ -31,8 +31,8 @@ pub use data::{ BenchmarkSnapshot, CandidateEligibility, CorporateAction, DailyFactorSnapshot, DailyMarketSnapshot, DailySnapshotBundle, DataSet, DataSetError, DividendRecord, EligibleUniverseSnapshot, FactorTextValue, FactorValue, IntradayExecutionQuote, - IntradayOrderBookDepthLevel, PriceBar, PriceField, SecuritiesMarginRecord, SplitRecord, - YieldCurvePoint, + IntradayOrderBookDepthLevel, NumericFactorMap, PriceBar, PriceField, SecuritiesMarginRecord, + SplitRecord, YieldCurvePoint, }; pub use engine::{ AnalyzerMonthlyReturnRow, AnalyzerPositionRow, AnalyzerReport, AnalyzerRiskSummary, diff --git a/crates/fidc-core/src/platform_expr_strategy.rs b/crates/fidc-core/src/platform_expr_strategy.rs index 0ff1c64..1002c51 100644 --- a/crates/fidc-core/src/platform_expr_strategy.rs +++ b/crates/fidc-core/src/platform_expr_strategy.rs @@ -3669,7 +3669,7 @@ impl PlatformExprStrategy { ctx.data .factor_snapshots_on(date) .into_iter() - .flat_map(|row| row.extra_factors.keys().cloned()) + .flat_map(|row| row.extra_factors.keys().map(|key| key.to_string())) .collect() } else { BTreeSet::new() @@ -3993,7 +3993,11 @@ impl PlatformExprStrategy { }; let extra_factors = if self.stock_extra_factors_required { - factor.extra_factors.clone() + factor + .extra_factors + .iter() + .map(|(field, value)| (field.to_string(), *value)) + .collect() } else { BTreeMap::new() }; @@ -13686,7 +13690,7 @@ mod tests { pe_ttm: 8.0, turnover_ratio: Some(1.0), effective_turnover_ratio: Some(1.0), - extra_factors: BTreeMap::from([("model_score".to_string(), 2.0)]), + extra_factors: BTreeMap::from([("model_score".into(), 2.0)]), }, ], symbols @@ -16173,10 +16177,7 @@ mod tests { pe_ttm: 8.0, turnover_ratio: Some(3.0), effective_turnover_ratio: Some(3.0), - extra_factors: BTreeMap::from([( - "adjustment_factor_backward1".to_string(), - 1.0, - )]), + extra_factors: BTreeMap::from([("adjustment_factor_backward1".into(), 1.0)]), }) .collect(), vec![CandidateEligibility { @@ -17125,7 +17126,7 @@ mod tests { pe_ttm: 8.0, turnover_ratio: Some(1.2), effective_turnover_ratio: Some(1.2), - extra_factors: BTreeMap::from([("touched_upper_limit".to_string(), 1.0)]), + extra_factors: BTreeMap::from([("touched_upper_limit".into(), 1.0)]), }, DailyFactorSnapshot { date, @@ -17239,7 +17240,7 @@ mod tests { pe_ttm: 8.0, turnover_ratio: Some(1.0), effective_turnover_ratio: Some(1.0), - extra_factors: BTreeMap::from([("touched_upper_limit".to_string(), 1.0)]), + extra_factors: BTreeMap::from([("touched_upper_limit".into(), 1.0)]), }], vec![CandidateEligibility { date, @@ -17976,7 +17977,7 @@ mod tests { pe_ttm: 8.0, turnover_ratio: Some(1.0), effective_turnover_ratio: Some(1.0), - extra_factors: BTreeMap::from([("adjustment_factor_backward1".to_string(), 1.0)]), + extra_factors: BTreeMap::from([("adjustment_factor_backward1".into(), 1.0)]), }) .collect(); let benchmark_rows: Vec = market_rows @@ -20825,7 +20826,7 @@ mod tests { pe_ttm: 8.0, turnover_ratio: Some(1.0), effective_turnover_ratio: Some(1.0), - extra_factors: BTreeMap::from([("amount".to_string(), 30_000_000.0)]), + extra_factors: BTreeMap::from([("amount".into(), 30_000_000.0)]), }, DailyFactorSnapshot { date: decision_date, @@ -22054,17 +22055,17 @@ mod tests { turnover_ratio: Some(22.0), effective_turnover_ratio: Some(18.0), extra_factors: BTreeMap::from([ - ("custom_alpha".to_string(), 7.0), - ("margin_all".to_string(), 1.0), - ("yield_curve_1y".to_string(), 0.02), - ("total_shares".to_string(), 123.0), - ("stock_connect_north_bound".to_string(), 1.0), - ("industry_citics_l1".to_string(), 10.0), - ("fundamental_net_profit".to_string(), 99.0), - ("financial_revenue".to_string(), 188.0), - ("pit_financial_eps".to_string(), 0.88), - ("current_performance_roe".to_string(), 12.0), - ("amount".to_string(), 12_345_678.0), + ("custom_alpha".into(), 7.0), + ("margin_all".into(), 1.0), + ("yield_curve_1y".into(), 0.02), + ("total_shares".into(), 123.0), + ("stock_connect_north_bound".into(), 1.0), + ("industry_citics_l1".into(), 10.0), + ("fundamental_net_profit".into(), 99.0), + ("financial_revenue".into(), 188.0), + ("pit_financial_eps".into(), 0.88), + ("current_performance_roe".into(), 12.0), + ("amount".into(), 12_345_678.0), ]), }], vec![CandidateEligibility { @@ -22835,8 +22836,8 @@ mod tests { turnover_ratio: Some(1.0), effective_turnover_ratio: Some(1.0), extra_factors: BTreeMap::from([ - ("Mixed_Factor".to_string(), index as f64 + 5.0), - ("adjustment_factor_backward1".to_string(), 1.0), + ("Mixed_Factor".into(), index as f64 + 5.0), + ("adjustment_factor_backward1".into(), 1.0), ]), }) .collect::>(); @@ -22989,7 +22990,7 @@ mod tests { pe_ttm: 8.0, turnover_ratio: Some(1.0), effective_turnover_ratio: Some(1.0), - extra_factors: BTreeMap::from([("adjustment_factor_backward1".to_string(), 1.0)]), + extra_factors: BTreeMap::from([("adjustment_factor_backward1".into(), 1.0)]), }) .collect::>(); let candidate_rows = dates @@ -25352,7 +25353,7 @@ mod tests { pe_ttm: 8.0, turnover_ratio: Some(1.0), effective_turnover_ratio: Some(1.0), - extra_factors: BTreeMap::from([("adjustment_factor_backward1".to_string(), 1.0)]), + extra_factors: BTreeMap::from([("adjustment_factor_backward1".into(), 1.0)]), }) .chain(symbols.iter().map(|symbol| DailyFactorSnapshot { date, @@ -25368,7 +25369,7 @@ mod tests { pe_ttm: 8.0, turnover_ratio: Some(1.0), effective_turnover_ratio: Some(1.0), - extra_factors: BTreeMap::from([("adjustment_factor_backward1".to_string(), 1.0)]), + extra_factors: BTreeMap::from([("adjustment_factor_backward1".into(), 1.0)]), })) .collect(), symbols @@ -26020,7 +26021,7 @@ mod tests { pe_ttm: 8.0, turnover_ratio: Some(1.0), effective_turnover_ratio: Some(1.0), - extra_factors: BTreeMap::from([("adjustment_factor_backward1".to_string(), 1.0)]), + extra_factors: BTreeMap::from([("adjustment_factor_backward1".into(), 1.0)]), }; let data = DataSet::from_components( vec![Instrument { @@ -29884,7 +29885,7 @@ mod tests { let date = dates[5]; let symbol = "300001.SZ"; let mut extra_factors = BTreeMap::new(); - extra_factors.insert("adjustment_factor_backward1".to_string(), 1.0); + extra_factors.insert("adjustment_factor_backward1".into(), 1.0); let data = DataSet::from_components( vec![Instrument { symbol: symbol.to_string(), @@ -29934,7 +29935,7 @@ mod tests { extra_factors: if factor_date == date { extra_factors.clone() } else { - BTreeMap::from([("adjustment_factor_backward1".to_string(), 1.0)]) + BTreeMap::from([("adjustment_factor_backward1".into(), 1.0)]) }, }) .collect(), @@ -30088,9 +30089,9 @@ mod tests { turnover_ratio: None, effective_turnover_ratio: None, extra_factors: BTreeMap::from([ - ("adjustment_factor_backward1".to_string(), 1.0), - ("ma5_current_back_adjusted_close".to_string(), current_close), - ("avg_volume5_current".to_string(), current_volume), + ("adjustment_factor_backward1".into(), 1.0), + ("ma5_current_back_adjusted_close".into(), current_close), + ("avg_volume5_current".into(), current_volume), ]), }, ) @@ -30239,7 +30240,7 @@ mod tests { pe_ttm: 0.0, turnover_ratio: None, effective_turnover_ratio: None, - extra_factors: BTreeMap::from([("ma5_prev_close".to_string(), 10.0)]), + extra_factors: BTreeMap::from([("ma5_prev_close".into(), 10.0)]), }], vec![CandidateEligibility { date: current, @@ -32539,10 +32540,7 @@ let target_exposure = csi_ready ? dynamic_exposure : 0.0; pe_ttm: 8.0, turnover_ratio: Some(22.0), effective_turnover_ratio: Some(18.0), - extra_factors: BTreeMap::from([( - "adjustment_factor_backward1".to_string(), - 1.0, - )]), + extra_factors: BTreeMap::from([("adjustment_factor_backward1".into(), 1.0)]), }) .collect(), dates @@ -32755,7 +32753,7 @@ let target_exposure = csi_ready ? dynamic_exposure : 0.0; extra_factors: if *symbol == "000002.SZ" { BTreeMap::new() } else { - BTreeMap::from([("model_score".to_string(), 4.0 - index as f64)]) + BTreeMap::from([("model_score".into(), 4.0 - index as f64)]) }, }) .collect(), diff --git a/crates/fidc-core/tests/engine_hooks.rs b/crates/fidc-core/tests/engine_hooks.rs index 863010c..e2db4e3 100644 --- a/crates/fidc-core/tests/engine_hooks.rs +++ b/crates/fidc-core/tests/engine_hooks.rs @@ -10,11 +10,11 @@ use fidc_core::{ ChinaEquityRuleHooks, DailyFactorSnapshot, DailyMarketSnapshot, DataSet, ExecutionQuoteRequest, FuturesAccountState, FuturesCommissionType, FuturesContractSpec, FuturesDirection, FuturesOrderIntent, FuturesPositionEffect, FuturesTradingParameter, FuturesValidationConfig, - Instrument, IntradayExecutionQuote, IntradayOrderBookDepthLevel, MatchingType, OpenOrderView, - OrderIntent, OrderSide, OrderStatus, PlatformExprStrategy, PlatformExprStrategyConfig, - PlatformTradeAction, PortfolioState, PriceField, ProcessEvent, ProcessEventBus, - ProcessEventKind, ScheduleRule, ScheduleStage, ScheduleTimeRule, Strategy, StrategyContext, - StrategyDecision, + Instrument, IntradayExecutionQuote, IntradayOrderBookDepthLevel, MatchingType, + NumericFactorMap, OpenOrderView, OrderIntent, OrderSide, OrderStatus, PlatformExprStrategy, + PlatformExprStrategyConfig, PlatformTradeAction, PortfolioState, PriceField, ProcessEvent, + ProcessEventBus, ProcessEventKind, ScheduleRule, ScheduleStage, ScheduleTimeRule, Strategy, + StrategyContext, StrategyDecision, }; fn d(year: i32, month: u32, day: u32) -> NaiveDate { @@ -133,7 +133,7 @@ fn market_row(date: NaiveDate, symbol: &str, open: f64, close: f64) -> DailyMark fn factor_row( date: NaiveDate, symbol: &str, - extra_factors: BTreeMap, + extra_factors: NumericFactorMap, ) -> DailyFactorSnapshot { DailyFactorSnapshot { date, @@ -209,26 +209,26 @@ fn two_day_futures_data() -> DataSet { d1, "000001.SZ", BTreeMap::from([ - ("custom_alpha".to_string(), 7.0), - ("margin_all".to_string(), 1.0), - ("yield_curve_1y".to_string(), 0.02), - ("total_shares".to_string(), 123.0), - ("stock_connect_north_bound".to_string(), 1.0), - ("industry_citics_l1".to_string(), 10.0), - ("fundamental_net_profit".to_string(), 99.0), + ("custom_alpha".into(), 7.0), + ("margin_all".into(), 1.0), + ("yield_curve_1y".into(), 0.02), + ("total_shares".into(), 123.0), + ("stock_connect_north_bound".into(), 1.0), + ("industry_citics_l1".into(), 10.0), + ("fundamental_net_profit".into(), 99.0), ]), ), factor_row( d2, "000001.SZ", BTreeMap::from([ - ("custom_alpha".to_string(), 8.0), - ("margin_all".to_string(), 1.0), - ("yield_curve_1y".to_string(), 0.021), - ("total_shares".to_string(), 124.0), - ("stock_connect_north_bound".to_string(), 1.0), - ("industry_citics_l1".to_string(), 10.0), - ("fundamental_net_profit".to_string(), 101.0), + ("custom_alpha".into(), 8.0), + ("margin_all".into(), 1.0), + ("yield_curve_1y".into(), 0.021), + ("total_shares".into(), 124.0), + ("stock_connect_north_bound".into(), 1.0), + ("industry_citics_l1".into(), 10.0), + ("fundamental_net_profit".into(), 101.0), ]), ), ],