按有序市值流提前停止选股
This commit is contained in:
@@ -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<String>,
|
||||||
|
) -> Result<bool, BacktestError> {
|
||||||
|
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 {
|
fn can_sell_position(&self, ctx: &StrategyContext<'_>, date: NaiveDate, symbol: &str) -> bool {
|
||||||
self.can_sell_position_at_time(ctx, date, symbol, None)
|
self.can_sell_position_at_time(ctx, date, symbol, None)
|
||||||
}
|
}
|
||||||
@@ -9373,6 +9414,41 @@ impl PlatformExprStrategy {
|
|||||||
universe_factor_date,
|
universe_factor_date,
|
||||||
5,
|
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 candidates = Vec::new();
|
||||||
let mut missing_rank_count = 0usize;
|
let mut missing_rank_count = 0usize;
|
||||||
let mut missing_rank_examples = Vec::new();
|
let mut missing_rank_examples = Vec::new();
|
||||||
@@ -9461,33 +9537,14 @@ impl PlatformExprStrategy {
|
|||||||
|
|
||||||
let mut selected = Vec::new();
|
let mut selected = Vec::new();
|
||||||
for (candidate, stock, _) in candidates {
|
for (candidate, stock, _) in candidates {
|
||||||
if !ctx.is_lagged_execution()
|
if !self.selection_candidate_passes_filters(
|
||||||
&& let Some(reason) = self.stock_selection_limit_rejection_reason(&stock)
|
ctx,
|
||||||
{
|
date,
|
||||||
if diagnostics.len() < 12 {
|
day,
|
||||||
diagnostics.push(format!("{} rejected by {}", candidate.symbol, reason));
|
&candidate,
|
||||||
}
|
&stock,
|
||||||
continue;
|
&mut diagnostics,
|
||||||
}
|
)? {
|
||||||
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
|
|
||||||
));
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
selected.push(candidate.symbol.clone());
|
selected.push(candidate.symbol.clone());
|
||||||
|
|||||||
Reference in New Issue
Block a user