diff --git a/crates/fidc-core/src/platform_expr_strategy.rs b/crates/fidc-core/src/platform_expr_strategy.rs index 7bb14df..0e4272d 100644 --- a/crates/fidc-core/src/platform_expr_strategy.rs +++ b/crates/fidc-core/src/platform_expr_strategy.rs @@ -9,8 +9,8 @@ use rhai::{AST, Dynamic, Engine, Map, Scope}; use crate::broker::{MatchingType, RebalanceCashMode, SlippageModel}; use crate::cost::ChinaAShareCostModel; use crate::data::{ - DailyMarketSnapshot, EligibleUniverseSnapshot, NumericFactorMap, PriceField, - decision_free_float_cap_bn, decision_market_cap_bn, + DailyMarketSnapshot, EligibleUniverseSnapshot, PriceField, decision_free_float_cap_bn, + decision_market_cap_bn, }; use crate::engine::BacktestError; use crate::events::OrderSide; @@ -424,11 +424,6 @@ pub struct PlatformExprStrategyConfig { pub matching_type: MatchingType, pub quote_quantity_limit: bool, pub current_day_precomputed_factors: bool, - /// Use audited Strategy Factory Source Lake rolling fields when the - /// runtime intentionally loads only the latest decision rows. This is - /// opt-in; ordinary backtests always recompute rolling values from the - /// canonical market series. - pub prefer_precomputed_rolling_factors: bool, pub intraday_execution_time: Option, pub delayed_limit_open_exit_enabled: bool, pub delayed_limit_open_exit_time: Option, @@ -500,7 +495,6 @@ impl PlatformExprStrategyConfig { matching_type: MatchingType::CurrentBarClose, quote_quantity_limit: true, current_day_precomputed_factors: false, - prefer_precomputed_rolling_factors: false, intraday_execution_time: None, delayed_limit_open_exit_enabled: false, delayed_limit_open_exit_time: None, @@ -808,42 +802,6 @@ fn framework_stock_rolling_factor_requirement(key: &str) -> Option<(&'static str .map(|window| (field, window)) } -fn precomputed_stock_rolling_mean(get: F, field: &str, lookback: usize) -> Option -where - F: Fn(&str) -> Option, -{ - if lookback == 0 { - return None; - } - let value_for = |key: String| get(&key).filter(|value| value.is_finite()); - match field.trim().to_ascii_lowercase().as_str() { - "close" | "prev_close" | "stock_close" | "price" => { - value_for(format!("ma{lookback}_prev_close")) - .or_else(|| value_for(format!("ma{lookback}"))) - } - "volume" | "stock_volume" => value_for(format!("avg_volume{lookback}")) - .or_else(|| value_for(format!("vma{lookback}"))), - _ => None, - } -} - -fn precomputed_stock_current_rolling_mean(get: F, field: &str, lookback: usize) -> Option -where - F: Fn(&str) -> Option, -{ - if lookback == 0 { - return None; - } - let value_for = |key: String| get(&key).filter(|value| value.is_finite()); - match field.trim().to_ascii_lowercase().as_str() { - "close" | "prev_close" | "stock_close" | "price" => { - value_for(format!("ma{lookback}_current_back_adjusted_close")) - } - "volume" | "stock_volume" => value_for(format!("avg_volume{lookback}_current")), - _ => None, - } -} - struct SelectiveExpressionScope<'a> { inner: Scope<'static>, required_identifiers: &'a AHashSet, @@ -3817,24 +3775,13 @@ impl PlatformExprStrategy { date: NaiveDate, symbol_id: u32, symbol: &str, - extra_factors: Option<&NumericFactorMap>, field: &str, lookback: usize, ) -> Option { - let computed = || { - ctx.data - .market_decision_numeric_moving_average_by_symbol_id( - date, symbol_id, symbol, field, lookback, - ) - }; - let precomputed = extra_factors.and_then(|factors| { - precomputed_stock_rolling_mean(|key| factors.get(key).copied(), field, lookback) - }); - if self.config.prefer_precomputed_rolling_factors { - precomputed.or_else(computed) - } else { - computed().or(precomputed) - } + ctx.data + .market_decision_numeric_moving_average_by_symbol_id( + date, symbol_id, symbol, field, lookback, + ) } fn stock_current_rolling_mean( @@ -3843,23 +3790,12 @@ impl PlatformExprStrategy { date: NaiveDate, symbol_id: u32, symbol: &str, - extra_factors: Option<&NumericFactorMap>, field: &str, lookback: usize, ) -> Option { - let computed = || { - ctx.data.market_current_numeric_moving_average_by_symbol_id( - date, symbol_id, symbol, field, lookback, - ) - }; - let precomputed = extra_factors.and_then(|factors| { - precomputed_stock_current_rolling_mean(|key| factors.get(key).copied(), field, lookback) - }); - if self.config.prefer_precomputed_rolling_factors { - precomputed.or_else(computed) - } else { - computed().or(precomputed) - } + ctx.data.market_current_numeric_moving_average_by_symbol_id( + date, symbol_id, symbol, field, lookback, + ) } fn stock_state_at_time( @@ -3984,16 +3920,8 @@ impl PlatformExprStrategy { if !self.stock_rolling_requirements.requires(field, lookback) { return f64::NAN; } - self.stock_decision_rolling_mean( - ctx, - date, - symbol_id, - symbol, - Some(&factor.extra_factors), - field, - lookback, - ) - .unwrap_or(f64::NAN) + self.stock_decision_rolling_mean(ctx, date, symbol_id, symbol, field, lookback) + .unwrap_or(f64::NAN) }; let stock_ma_short = rolling("close", self.config.stock_short_ma_days); let stock_ma_mid = rolling("close", self.config.stock_mid_ma_days); @@ -6109,9 +6037,6 @@ impl PlatformExprStrategy { day.date, stock.symbol_id, &stock.symbol, - ctx.data - .factor_by_symbol_id(day.date, stock.symbol_id) - .map(|factor| &factor.extra_factors), field, lookback, ) @@ -6767,9 +6692,6 @@ impl PlatformExprStrategy { day.date, stock.symbol_id, &stock.symbol, - ctx.data - .factor_by_symbol_id(day.date, stock.symbol_id) - .map(|factor| &factor.extra_factors), other, lookback, ) @@ -6819,9 +6741,6 @@ impl PlatformExprStrategy { day.date, stock.symbol_id, &stock.symbol, - ctx.data - .factor_by_symbol_id(day.date, stock.symbol_id) - .map(|factor| &factor.extra_factors), other, lookback, ) @@ -12173,7 +12092,6 @@ mod tests { PlatformRebalanceSchedule, PlatformScheduleFrequency, PlatformStopTakeReferencePriceMode, PlatformTradeAction, PlatformUniverseActionKind, RuntimeHelperResolution, SelectionRiskDeferral, StockFilterQuoteUsage, framework_stock_rolling_factor_requirement, - precomputed_stock_current_rolling_mean, precomputed_stock_rolling_mean, }; use crate::{ AlgoOrderStyle, BenchmarkSnapshot, CandidateEligibility, CorporateAction, @@ -12241,39 +12159,6 @@ mod tests { assert_eq!(framework_stock_rolling_factor_requirement("alpha001"), None); } - #[test] - fn source_lake_precomputed_rolling_aliases_use_only_canonical_fields() { - let factors = BTreeMap::from([ - ("ma5_prev_close".to_string(), 11.0), - ("ma5".to_string(), 12.0), - ("ma5_current_close".to_string(), 99.0), - ("ma5_current_back_adjusted_close".to_string(), 13.0), - ("avg_volume5".to_string(), 100.0), - ("avg_volume5_current".to_string(), 120.0), - ]); - let get = |key: &str| factors.get(key).copied(); - assert_eq!(precomputed_stock_rolling_mean(get, "close", 5), Some(11.0)); - assert_eq!( - precomputed_stock_current_rolling_mean(get, "close", 5), - Some(13.0) - ); - assert_eq!( - precomputed_stock_rolling_mean(get, "volume", 5), - Some(100.0) - ); - assert_eq!( - precomputed_stock_current_rolling_mean(get, "volume", 5), - Some(120.0) - ); - // A source row that only has a raw current-close alias must not be - // mistaken for the adjusted rolling value. - let raw_only = BTreeMap::from([("ma5_current_close".to_string(), 99.0)]); - assert_eq!( - precomputed_stock_current_rolling_mean(|key| raw_only.get(key).copied(), "close", 5,), - None - ); - } - #[test] fn typed_runtime_numbers_preserve_legacy_rhai_formatting() { let RuntimeHelperResolution::Number(value) =