diff --git a/crates/fidc-core/src/broker.rs b/crates/fidc-core/src/broker.rs index 27573f5..e5b4b57 100644 --- a/crates/fidc-core/src/broker.rs +++ b/crates/fidc-core/src/broker.rs @@ -4680,9 +4680,8 @@ where let quote_quantity_limited = self.quote_quantity_limited_for_window(matching_type, start_cursor, end_cursor); let lot = round_lot.max(1); - let use_decision_time_quote = matching_type == MatchingType::NextTickLast - && start_cursor.is_some() - && end_cursor.is_none_or(|end| start_cursor.is_some_and(|start| end <= start)); + let use_decision_time_quote = + matching_type == MatchingType::NextTickLast && start_cursor.is_some(); let eligible_quotes: Vec<&IntradayExecutionQuote> = if use_decision_time_quote { self.latest_known_quote_at_or_before( quotes, diff --git a/crates/fidc-core/src/platform_expr_strategy.rs b/crates/fidc-core/src/platform_expr_strategy.rs index bccc92f..b95eb88 100644 --- a/crates/fidc-core/src/platform_expr_strategy.rs +++ b/crates/fidc-core/src/platform_expr_strategy.rs @@ -1064,7 +1064,8 @@ impl PlatformExprStrategy { ctx.data .execution_quotes_on(date, symbol) .iter() - .find(|quote| quote.timestamp >= start_cursor) + .filter(|quote| quote.timestamp <= start_cursor) + .max_by_key(|quote| quote.timestamp) } fn aiquant_scheduled_buy_price( @@ -1110,14 +1111,17 @@ impl PlatformExprStrategy { let start_cursor = self.projected_execution_start_cursor(ctx, date, symbol, execution_state); let quotes = ctx.data.execution_quotes_on(date, symbol); + let selected_quotes = quotes + .iter() + .filter(|quote| quote.timestamp <= start_cursor) + .max_by_key(|quote| quote.timestamp) + .into_iter() + .collect::>(); let mut filled_qty = 0_u32; let mut gross_amount = 0.0_f64; let mut last_timestamp = None; - for quote in quotes { - if quote.timestamp < start_cursor { - continue; - } + for quote in selected_quotes { let Some(quote_price) = self.projected_quote_execution_price(quote, side) else { continue; }; @@ -1191,7 +1195,7 @@ impl PlatformExprStrategy { }) } - fn has_execution_quote_at_or_after( + fn has_execution_quote_at_or_before( &self, ctx: &StrategyContext<'_>, date: NaiveDate, @@ -1203,7 +1207,7 @@ impl PlatformExprStrategy { ctx.data .execution_quotes_on(date, symbol) .iter() - .any(|quote| quote.timestamp >= start_cursor) + .any(|quote| quote.timestamp <= start_cursor) } fn project_target_zero( @@ -1241,7 +1245,7 @@ impl PlatformExprStrategy { execution_state, ) .or_else(|| { - if !self.has_execution_quote_at_or_after(ctx, date, symbol, execution_state) { + if !self.has_execution_quote_at_or_before(ctx, date, symbol, execution_state) { Some(ProjectedExecutionFill { price: self.projected_execution_price(market, OrderSide::Sell), quantity, @@ -1442,7 +1446,7 @@ impl PlatformExprStrategy { execution_state, ) .or_else(|| { - if !self.has_execution_quote_at_or_after(ctx, date, symbol, execution_state) { + if !self.has_execution_quote_at_or_before(ctx, date, symbol, execution_state) { Some(ProjectedExecutionFill { price: execution_price, quantity, @@ -7914,6 +7918,19 @@ mod tests { amount_delta: 1_000.0, trading_phase: Some("continuous".to_string()), }, + IntradayExecutionQuote { + date, + symbol: symbol.to_string(), + timestamp: date.and_hms_opt(14, 58, 59).expect("valid timestamp"), + last_price: 19.8, + bid1: 19.79, + ask1: 19.8, + bid1_volume: 1_000, + ask1_volume: 1_000, + volume_delta: 100, + amount_delta: 2_000.0, + trading_phase: Some("continuous".to_string()), + }, IntradayExecutionQuote { date, symbol: symbol.to_string(), @@ -7964,9 +7981,9 @@ mod tests { let strategy = PlatformExprStrategy::new(cfg); let quote = strategy .aiquant_scheduled_quote(&ctx, date, symbol) - .expect("quote at or after 14:59"); - assert_eq!(quote.timestamp, date.and_hms_opt(14, 59, 2).unwrap()); - assert_eq!(quote.last_price, 20.0); + .expect("latest known quote at or before 14:59"); + assert_eq!(quote.timestamp, date.and_hms_opt(14, 58, 59).unwrap()); + assert_eq!(quote.last_price, 19.8); } #[test]