diff --git a/crates/fidc-core/src/platform_expr_strategy.rs b/crates/fidc-core/src/platform_expr_strategy.rs index ba01a91..9f9198d 100644 --- a/crates/fidc-core/src/platform_expr_strategy.rs +++ b/crates/fidc-core/src/platform_expr_strategy.rs @@ -21251,6 +21251,37 @@ mod tests { assert_eq!(stock.market_cap, 8.0); assert_eq!(stock.market_cap_bn, 8.0); assert!(stock.touched_upper_limit); + + let missing_feature_day = DataSet::from_components( + vec![data.instrument(symbol).unwrap().clone()], + data.market_snapshot_rows_on(date).to_vec(), + data.factor_snapshot_rows_on(factor_date).iter() + .chain(data.factor_snapshot_rows_on(date)).cloned().collect(), + data.candidate_snapshot_rows_on(date).to_vec(), + vec![data.benchmark(date).unwrap().clone()], + ).unwrap(); + let gap_ctx = StrategyContext {data: &missing_feature_day, decision_date: factor_date, ..ctx}; + let mut gap_config = PlatformExprStrategyConfig::generic(); + gap_config.matching_type = MatchingType::NextBarOpen; + gap_config.stock_filter_expr = "close > 10.0".into(); + let gap_strategy = PlatformExprStrategy::new(gap_config); + let indexed = gap_strategy.stock_state_with_factor_date(&gap_ctx, date, factor_date, symbol); + assert!(matches!(&indexed, Err(BacktestError::Data(crate::data::DataSetError::MissingSnapshot { + kind: "feature_market", date: missing_date, symbol: missing_symbol, + })) if *missing_date == factor_date && missing_symbol == symbol), + "missing decision-date OHLCV must not become execution-date values: {indexed:?}"); + let execution_view = missing_feature_day.daily_snapshot_view(date); + let feature_view = missing_feature_day.daily_snapshot_view(factor_date); + let viewed = gap_strategy.uncached_selection_stock_state_from_views_by_symbol_id( + &gap_ctx, date, factor_date, missing_feature_day.symbol_id(symbol).unwrap(), symbol, + &execution_view, &feature_view, + ); + assert!(matches!(&viewed, Err(BacktestError::Data(crate::data::DataSetError::MissingSnapshot { + kind: "feature_market", date: missing_date, .. + })) if *missing_date == factor_date), "daily views must preserve the same missing-date boundary: {viewed:?}"); + assert!(gap_strategy.stock_state_cache.borrow().is_empty()); + let same_day = gap_strategy.stock_state_with_factor_date(&gap_ctx, date, date, symbol).unwrap(); + assert_eq!(same_day.close, 20.0); } #[test]