diff --git a/crates/fidc-core/src/platform_expr_strategy.rs b/crates/fidc-core/src/platform_expr_strategy.rs index 019b859..bef979a 100644 --- a/crates/fidc-core/src/platform_expr_strategy.rs +++ b/crates/fidc-core/src/platform_expr_strategy.rs @@ -1531,7 +1531,7 @@ impl PlatformExprStrategy { symbol, execution_state, execution_time, - ) && !ctx.data.has_execution_quotes_on_date(date) + ) && ctx.data.execution_quotes_on(date, symbol).is_empty() { Some(ProjectedExecutionFill { price: self.projected_execution_price(market, OrderSide::Sell), @@ -1887,7 +1887,7 @@ impl PlatformExprStrategy { ) .or_else(|| { if !self.has_execution_quote_at_or_before(ctx, date, symbol, execution_state) - && !ctx.data.has_execution_quotes_on_date(date) + && ctx.data.execution_quotes_on(date, symbol).is_empty() { Some(ProjectedExecutionFill { price: sizing_price, @@ -15146,60 +15146,73 @@ mod tests { fn platform_aiquant_projection_uses_snapshot_before_execution_quotes_are_loaded() { let date = d(2025, 5, 14); let symbol = "000003.SZ"; - let data = DataSet::from_components( - vec![Instrument { - symbol: symbol.to_string(), - name: symbol.to_string(), - board: "SZ".to_string(), - round_lot: 100, - listed_at: Some(d(2020, 1, 1)), - delisted_at: None, - status: "active".to_string(), - }], - vec![DailyMarketSnapshot { - date, - symbol: symbol.to_string(), - timestamp: Some("2025-05-14 14:59:00".to_string()), - day_open: 10.0, - open: 10.0, - high: 10.5, - low: 9.8, - close: 10.0, - last_price: 10.0, - bid1: 10.0, - ask1: 10.0, - prev_close: 9.9, - volume: 1_000_000, - tick_volume: 10_000, - bid1_volume: 2_000, - ask1_volume: 2_000, - trading_phase: Some("continuous".to_string()), - paused: false, - upper_limit: 11.0, - lower_limit: 9.0, - price_tick: 0.01, - }], - vec![DailyFactorSnapshot { - date, - symbol: symbol.to_string(), - market_cap_bn: 10.0, - free_float_cap_bn: 10.0, - pe_ttm: 8.0, - turnover_ratio: Some(1.0), - effective_turnover_ratio: Some(1.0), - extra_factors: BTreeMap::new(), - }], - vec![CandidateEligibility { - date, - symbol: symbol.to_string(), - is_st: false, - is_new_listing: false, - is_paused: false, - allow_buy: true, - allow_sell: true, - is_kcb: false, - is_one_yuan: false, - }], + let other_symbol = "000004.SZ"; + let data = DataSet::from_components_with_actions_and_quotes( + [symbol, other_symbol] + .into_iter() + .map(|item| Instrument { + symbol: item.to_string(), + name: item.to_string(), + board: "SZ".to_string(), + round_lot: 100, + listed_at: Some(d(2020, 1, 1)), + delisted_at: None, + status: "active".to_string(), + }) + .collect(), + [symbol, other_symbol] + .into_iter() + .map(|item| DailyMarketSnapshot { + date, + symbol: item.to_string(), + timestamp: Some("2025-05-14 14:59:00".to_string()), + day_open: 10.0, + open: 10.0, + high: 10.5, + low: 9.8, + close: 10.0, + last_price: 10.0, + bid1: 10.0, + ask1: 10.0, + prev_close: 9.9, + volume: 1_000_000, + tick_volume: 10_000, + bid1_volume: 2_000, + ask1_volume: 2_000, + trading_phase: Some("continuous".to_string()), + paused: false, + upper_limit: 11.0, + lower_limit: 9.0, + price_tick: 0.01, + }) + .collect(), + [symbol, other_symbol] + .into_iter() + .map(|item| DailyFactorSnapshot { + date, + symbol: item.to_string(), + market_cap_bn: 10.0, + free_float_cap_bn: 10.0, + pe_ttm: 8.0, + turnover_ratio: Some(1.0), + effective_turnover_ratio: Some(1.0), + extra_factors: BTreeMap::new(), + }) + .collect(), + [symbol, other_symbol] + .into_iter() + .map(|item| CandidateEligibility { + date, + symbol: item.to_string(), + is_st: false, + is_new_listing: false, + is_paused: false, + allow_buy: true, + allow_sell: true, + is_kcb: false, + is_one_yuan: false, + }) + .collect(), vec![BenchmarkSnapshot { date, benchmark: "000852.SH".to_string(), @@ -15208,6 +15221,20 @@ mod tests { prev_close: 998.0, volume: 1_000_000, }], + Vec::new(), + vec![IntradayExecutionQuote { + date, + symbol: other_symbol.to_string(), + timestamp: date.and_hms_opt(10, 18, 0).unwrap(), + last_price: 10.0, + bid1: 10.0, + ask1: 10.0, + bid1_volume: 1_000, + ask1_volume: 1_000, + volume_delta: 100, + amount_delta: 1_000.0, + trading_phase: Some("continuous".to_string()), + }], ) .expect("dataset"); @@ -15236,6 +15263,7 @@ mod tests { let mut execution_state = super::ProjectedExecutionState::default(); assert!(ctx.data.execution_quotes_on(date, symbol).is_empty()); + assert!(!ctx.data.execution_quotes_on(date, other_symbol).is_empty()); let filled = strategy.project_order_value( &ctx, &mut projected,