From d5265619f376b72961747deafc52f24d97cf33d8 Mon Sep 17 00:00:00 2001 From: boris Date: Sat, 29 Aug 2026 07:50:09 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8C=89=E6=9C=89=E5=BA=8F=E5=B8=82=E5=80=BC?= =?UTF-8?q?=E6=B5=81=E6=8F=90=E5=89=8D=E5=81=9C=E6=AD=A2=E9=80=89=E8=82=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fidc-core/src/platform_expr_strategy.rs | 111 +++++++++++++----- 1 file changed, 84 insertions(+), 27 deletions(-) diff --git a/crates/fidc-core/src/platform_expr_strategy.rs b/crates/fidc-core/src/platform_expr_strategy.rs index 0b9a982..14be2ce 100644 --- a/crates/fidc-core/src/platform_expr_strategy.rs +++ b/crates/fidc-core/src/platform_expr_strategy.rs @@ -9228,6 +9228,47 @@ impl PlatformExprStrategy { ) } + fn selection_candidate_passes_filters( + &self, + ctx: &StrategyContext<'_>, + date: NaiveDate, + day: &DayExpressionState, + candidate: &EligibleUniverseSnapshot, + stock: &StockExpressionState, + diagnostics: &mut Vec, + ) -> Result { + if !ctx.is_lagged_execution() + && let Some(reason) = self.stock_selection_limit_rejection_reason(stock) + { + if diagnostics.len() < 12 { + diagnostics.push(format!("{} rejected by {}", candidate.symbol, reason)); + } + return Ok(false); + } + if !self.stock_passes_expr(ctx, day, stock)? { + if diagnostics.len() < 12 { + diagnostics.push(format!("{} rejected by stock_expr", candidate.symbol)); + } + return Ok(false); + } + if self.config.stop_take_reference_price_mode + == PlatformStopTakeReferencePriceMode::SignalDayPostAdjustedClose + && ctx + .data + .market_latest_back_adjusted_close(date, &candidate.symbol) + .is_none() + { + if diagnostics.len() < 12 { + diagnostics.push(format!( + "{} rejected by missing signal-day post-adjusted close", + candidate.symbol + )); + } + return Ok(false); + } + Ok(true) + } + fn can_sell_position(&self, ctx: &StrategyContext<'_>, date: NaiveDate, symbol: &str) -> bool { self.can_sell_position_at_time(ctx, date, symbol, None) } @@ -9373,6 +9414,41 @@ impl PlatformExprStrategy { universe_factor_date, 5, ); + + // The universe is already stably ordered by market cap. When the + // strategy asks for that exact ascending order and does not need a + // complete ranking for replacement limiting, select directly from the + // ordered stream instead of materializing a second candidate vector. + if self.rank_reuses_market_cap_order() && self.config.daily_replacement_limit == 0 { + let mut selected = Vec::with_capacity(limit.min(universe.len())); + for candidate in universe { + let stock = self.selection_stock_state_with_factor_date( + ctx, + date, + stock_factor_date, + &candidate.symbol, + )?; + let field_value = self.selection_field_value(&candidate, &stock); + if !field_value.is_finite() || field_value < band_low || field_value > band_high { + continue; + } + if self.selection_candidate_passes_filters( + ctx, + date, + day, + &candidate, + &stock, + &mut diagnostics, + )? { + selected.push(candidate.symbol); + if selected.len() >= limit { + break; + } + } + } + return Ok((selected, diagnostics, risk_decisions)); + } + let mut candidates = Vec::new(); let mut missing_rank_count = 0usize; let mut missing_rank_examples = Vec::new(); @@ -9461,33 +9537,14 @@ impl PlatformExprStrategy { let mut selected = Vec::new(); for (candidate, stock, _) in candidates { - if !ctx.is_lagged_execution() - && let Some(reason) = self.stock_selection_limit_rejection_reason(&stock) - { - if diagnostics.len() < 12 { - diagnostics.push(format!("{} rejected by {}", candidate.symbol, reason)); - } - continue; - } - if !self.stock_passes_expr(ctx, day, &stock)? { - if diagnostics.len() < 12 { - diagnostics.push(format!("{} rejected by stock_expr", candidate.symbol)); - } - continue; - } - if self.config.stop_take_reference_price_mode - == PlatformStopTakeReferencePriceMode::SignalDayPostAdjustedClose - && ctx - .data - .market_latest_back_adjusted_close(date, &candidate.symbol) - .is_none() - { - if diagnostics.len() < 12 { - diagnostics.push(format!( - "{} rejected by missing signal-day post-adjusted close", - candidate.symbol - )); - } + if !self.selection_candidate_passes_filters( + ctx, + date, + day, + &candidate, + &stock, + &mut diagnostics, + )? { continue; } selected.push(candidate.symbol.clone());