共享选股状态并合并风控扫描
This commit is contained in:
@@ -952,7 +952,7 @@ pub struct PlatformExprStrategy {
|
||||
stock_text_factors_required: bool,
|
||||
stock_state_cache_date: RefCell<Option<NaiveDate>>,
|
||||
stock_state_cache: RefCell<
|
||||
AHashMap<(NaiveDate, NaiveDate, u32, Option<NaiveTime>, bool), StockExpressionState>,
|
||||
AHashMap<(NaiveDate, NaiveDate, u32, Option<NaiveTime>, bool), Arc<StockExpressionState>>,
|
||||
>,
|
||||
}
|
||||
|
||||
@@ -3155,7 +3155,7 @@ impl PlatformExprStrategy {
|
||||
ctx,
|
||||
execution_date,
|
||||
symbol,
|
||||
&self.stock_state(ctx, execution_date, symbol)?,
|
||||
self.stock_state(ctx, execution_date, symbol)?.as_ref(),
|
||||
)?
|
||||
.is_some()
|
||||
{
|
||||
@@ -3614,7 +3614,7 @@ impl PlatformExprStrategy {
|
||||
ctx: &StrategyContext<'_>,
|
||||
date: NaiveDate,
|
||||
symbol: &str,
|
||||
) -> Result<StockExpressionState, BacktestError> {
|
||||
) -> Result<Arc<StockExpressionState>, BacktestError> {
|
||||
self.stock_state_with_factor_date(ctx, date, date, symbol)
|
||||
}
|
||||
|
||||
@@ -3624,7 +3624,7 @@ impl PlatformExprStrategy {
|
||||
date: NaiveDate,
|
||||
factor_date: NaiveDate,
|
||||
symbol: &str,
|
||||
) -> Result<StockExpressionState, BacktestError> {
|
||||
) -> Result<Arc<StockExpressionState>, BacktestError> {
|
||||
self.stock_state_with_factor_date_and_time(ctx, date, factor_date, symbol, None, true)
|
||||
}
|
||||
|
||||
@@ -3634,7 +3634,7 @@ impl PlatformExprStrategy {
|
||||
date: NaiveDate,
|
||||
factor_date: NaiveDate,
|
||||
symbol: &str,
|
||||
) -> Result<StockExpressionState, BacktestError> {
|
||||
) -> Result<Arc<StockExpressionState>, BacktestError> {
|
||||
let use_intraday_quote = self.selection_quote_usage != StockFilterQuoteUsage::DailyOnly;
|
||||
self.stock_state_with_factor_date_and_time(
|
||||
ctx,
|
||||
@@ -3681,7 +3681,7 @@ impl PlatformExprStrategy {
|
||||
date: NaiveDate,
|
||||
symbol: &str,
|
||||
execution_time: Option<NaiveTime>,
|
||||
) -> Result<StockExpressionState, BacktestError> {
|
||||
) -> Result<Arc<StockExpressionState>, BacktestError> {
|
||||
self.stock_state_with_factor_date_and_time(ctx, date, date, symbol, execution_time, true)
|
||||
}
|
||||
|
||||
@@ -3745,7 +3745,7 @@ impl PlatformExprStrategy {
|
||||
symbol: &str,
|
||||
execution_time: Option<NaiveTime>,
|
||||
use_intraday_quote: bool,
|
||||
) -> Result<StockExpressionState, BacktestError> {
|
||||
) -> Result<Arc<StockExpressionState>, BacktestError> {
|
||||
let symbol_id = ctx.data.symbol_id(symbol).ok_or_else(|| {
|
||||
BacktestError::Data(crate::data::DataSetError::MissingSnapshot {
|
||||
kind: "symbol_index",
|
||||
@@ -3768,7 +3768,7 @@ impl PlatformExprStrategy {
|
||||
use_intraday_quote,
|
||||
);
|
||||
if let Some(state) = self.stock_state_cache.borrow().get(&cache_key) {
|
||||
return Ok(state.clone());
|
||||
return Ok(Arc::clone(state));
|
||||
}
|
||||
|
||||
let market = ctx
|
||||
@@ -3921,7 +3921,7 @@ impl PlatformExprStrategy {
|
||||
BTreeMap::new()
|
||||
};
|
||||
|
||||
let state = StockExpressionState {
|
||||
let state = Arc::new(StockExpressionState {
|
||||
symbol: symbol.to_string(),
|
||||
symbol_id,
|
||||
market_cap,
|
||||
@@ -3997,10 +3997,10 @@ impl PlatformExprStrategy {
|
||||
} else {
|
||||
BTreeMap::new()
|
||||
},
|
||||
};
|
||||
});
|
||||
self.stock_state_cache
|
||||
.borrow_mut()
|
||||
.insert(cache_key, state.clone());
|
||||
.insert(cache_key, Arc::clone(&state));
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
@@ -7752,7 +7752,7 @@ impl PlatformExprStrategy {
|
||||
ctx: &StrategyContext<'_>,
|
||||
date: NaiveDate,
|
||||
symbol: Option<&str>,
|
||||
) -> Result<Option<StockExpressionState>, BacktestError> {
|
||||
) -> Result<Option<Arc<StockExpressionState>>, BacktestError> {
|
||||
let Some(symbol) = symbol else {
|
||||
return Ok(None);
|
||||
};
|
||||
@@ -7802,7 +7802,7 @@ impl PlatformExprStrategy {
|
||||
if !self.action_when_matches(
|
||||
ctx,
|
||||
day,
|
||||
stock_state.as_ref(),
|
||||
stock_state.as_deref(),
|
||||
when_expr.as_deref(),
|
||||
)? {
|
||||
continue;
|
||||
@@ -7817,7 +7817,7 @@ impl PlatformExprStrategy {
|
||||
match kind {
|
||||
PlatformExplicitOrderKind::Shares => {
|
||||
let quantity =
|
||||
self.eval_i32(ctx, amount_expr, day, stock_state.as_ref(), None)?;
|
||||
self.eval_i32(ctx, amount_expr, day, stock_state.as_deref(), None)?;
|
||||
if quantity == 0 {
|
||||
continue;
|
||||
}
|
||||
@@ -7829,7 +7829,7 @@ impl PlatformExprStrategy {
|
||||
}
|
||||
PlatformExplicitOrderKind::LimitShares => {
|
||||
let quantity =
|
||||
self.eval_i32(ctx, amount_expr, day, stock_state.as_ref(), None)?;
|
||||
self.eval_i32(ctx, amount_expr, day, stock_state.as_deref(), None)?;
|
||||
if quantity == 0 {
|
||||
continue;
|
||||
}
|
||||
@@ -7837,7 +7837,7 @@ impl PlatformExprStrategy {
|
||||
ctx,
|
||||
limit_price_expr.as_deref().unwrap_or_default(),
|
||||
day,
|
||||
stock_state.as_ref(),
|
||||
stock_state.as_deref(),
|
||||
None,
|
||||
)?;
|
||||
intents.push(OrderIntent::LimitShares {
|
||||
@@ -7849,7 +7849,7 @@ impl PlatformExprStrategy {
|
||||
}
|
||||
PlatformExplicitOrderKind::Lots => {
|
||||
let lots =
|
||||
self.eval_i32(ctx, amount_expr, day, stock_state.as_ref(), None)?;
|
||||
self.eval_i32(ctx, amount_expr, day, stock_state.as_deref(), None)?;
|
||||
if lots == 0 {
|
||||
continue;
|
||||
}
|
||||
@@ -7861,7 +7861,7 @@ impl PlatformExprStrategy {
|
||||
}
|
||||
PlatformExplicitOrderKind::LimitLots => {
|
||||
let lots =
|
||||
self.eval_i32(ctx, amount_expr, day, stock_state.as_ref(), None)?;
|
||||
self.eval_i32(ctx, amount_expr, day, stock_state.as_deref(), None)?;
|
||||
if lots == 0 {
|
||||
continue;
|
||||
}
|
||||
@@ -7869,7 +7869,7 @@ impl PlatformExprStrategy {
|
||||
ctx,
|
||||
limit_price_expr.as_deref().unwrap_or_default(),
|
||||
day,
|
||||
stock_state.as_ref(),
|
||||
stock_state.as_deref(),
|
||||
None,
|
||||
)?;
|
||||
intents.push(OrderIntent::LimitLots {
|
||||
@@ -7881,7 +7881,7 @@ impl PlatformExprStrategy {
|
||||
}
|
||||
PlatformExplicitOrderKind::TargetShares => {
|
||||
let target_quantity =
|
||||
self.eval_i32(ctx, amount_expr, day, stock_state.as_ref(), None)?;
|
||||
self.eval_i32(ctx, amount_expr, day, stock_state.as_deref(), None)?;
|
||||
intents.push(OrderIntent::TargetShares {
|
||||
symbol: symbol.clone(),
|
||||
target_quantity,
|
||||
@@ -7890,12 +7890,12 @@ impl PlatformExprStrategy {
|
||||
}
|
||||
PlatformExplicitOrderKind::LimitTargetShares => {
|
||||
let target_quantity =
|
||||
self.eval_i32(ctx, amount_expr, day, stock_state.as_ref(), None)?;
|
||||
self.eval_i32(ctx, amount_expr, day, stock_state.as_deref(), None)?;
|
||||
let limit_price = self.eval_float(
|
||||
ctx,
|
||||
limit_price_expr.as_deref().unwrap_or_default(),
|
||||
day,
|
||||
stock_state.as_ref(),
|
||||
stock_state.as_deref(),
|
||||
None,
|
||||
)?;
|
||||
intents.push(OrderIntent::LimitTargetShares {
|
||||
@@ -7906,8 +7906,13 @@ impl PlatformExprStrategy {
|
||||
});
|
||||
}
|
||||
PlatformExplicitOrderKind::Value => {
|
||||
let value =
|
||||
self.eval_float(ctx, amount_expr, day, stock_state.as_ref(), None)?;
|
||||
let value = self.eval_float(
|
||||
ctx,
|
||||
amount_expr,
|
||||
day,
|
||||
stock_state.as_deref(),
|
||||
None,
|
||||
)?;
|
||||
if value.abs() <= f64::EPSILON {
|
||||
continue;
|
||||
}
|
||||
@@ -7919,21 +7924,38 @@ impl PlatformExprStrategy {
|
||||
}
|
||||
PlatformExplicitOrderKind::VwapValue
|
||||
| PlatformExplicitOrderKind::TwapValue => {
|
||||
let value =
|
||||
self.eval_float(ctx, amount_expr, day, stock_state.as_ref(), None)?;
|
||||
let value = self.eval_float(
|
||||
ctx,
|
||||
amount_expr,
|
||||
day,
|
||||
stock_state.as_deref(),
|
||||
None,
|
||||
)?;
|
||||
if value.abs() <= f64::EPSILON {
|
||||
continue;
|
||||
}
|
||||
let start_time = start_time_expr
|
||||
.as_deref()
|
||||
.map(|expr| {
|
||||
self.eval_time_expr(ctx, expr, day, stock_state.as_ref(), None)
|
||||
self.eval_time_expr(
|
||||
ctx,
|
||||
expr,
|
||||
day,
|
||||
stock_state.as_deref(),
|
||||
None,
|
||||
)
|
||||
})
|
||||
.transpose()?;
|
||||
let end_time = end_time_expr
|
||||
.as_deref()
|
||||
.map(|expr| {
|
||||
self.eval_time_expr(ctx, expr, day, stock_state.as_ref(), None)
|
||||
self.eval_time_expr(
|
||||
ctx,
|
||||
expr,
|
||||
day,
|
||||
stock_state.as_deref(),
|
||||
None,
|
||||
)
|
||||
})
|
||||
.transpose()?;
|
||||
intents.push(OrderIntent::AlgoValue {
|
||||
@@ -7950,8 +7972,13 @@ impl PlatformExprStrategy {
|
||||
});
|
||||
}
|
||||
PlatformExplicitOrderKind::LimitValue => {
|
||||
let value =
|
||||
self.eval_float(ctx, amount_expr, day, stock_state.as_ref(), None)?;
|
||||
let value = self.eval_float(
|
||||
ctx,
|
||||
amount_expr,
|
||||
day,
|
||||
stock_state.as_deref(),
|
||||
None,
|
||||
)?;
|
||||
if value.abs() <= f64::EPSILON {
|
||||
continue;
|
||||
}
|
||||
@@ -7959,7 +7986,7 @@ impl PlatformExprStrategy {
|
||||
ctx,
|
||||
limit_price_expr.as_deref().unwrap_or_default(),
|
||||
day,
|
||||
stock_state.as_ref(),
|
||||
stock_state.as_deref(),
|
||||
None,
|
||||
)?;
|
||||
intents.push(OrderIntent::LimitValue {
|
||||
@@ -7970,8 +7997,13 @@ impl PlatformExprStrategy {
|
||||
});
|
||||
}
|
||||
PlatformExplicitOrderKind::Percent => {
|
||||
let percent =
|
||||
self.eval_float(ctx, amount_expr, day, stock_state.as_ref(), None)?;
|
||||
let percent = self.eval_float(
|
||||
ctx,
|
||||
amount_expr,
|
||||
day,
|
||||
stock_state.as_deref(),
|
||||
None,
|
||||
)?;
|
||||
if percent.abs() <= f64::EPSILON {
|
||||
continue;
|
||||
}
|
||||
@@ -7983,21 +8015,38 @@ impl PlatformExprStrategy {
|
||||
}
|
||||
PlatformExplicitOrderKind::VwapPercent
|
||||
| PlatformExplicitOrderKind::TwapPercent => {
|
||||
let percent =
|
||||
self.eval_float(ctx, amount_expr, day, stock_state.as_ref(), None)?;
|
||||
let percent = self.eval_float(
|
||||
ctx,
|
||||
amount_expr,
|
||||
day,
|
||||
stock_state.as_deref(),
|
||||
None,
|
||||
)?;
|
||||
if percent.abs() <= f64::EPSILON {
|
||||
continue;
|
||||
}
|
||||
let start_time = start_time_expr
|
||||
.as_deref()
|
||||
.map(|expr| {
|
||||
self.eval_time_expr(ctx, expr, day, stock_state.as_ref(), None)
|
||||
self.eval_time_expr(
|
||||
ctx,
|
||||
expr,
|
||||
day,
|
||||
stock_state.as_deref(),
|
||||
None,
|
||||
)
|
||||
})
|
||||
.transpose()?;
|
||||
let end_time = end_time_expr
|
||||
.as_deref()
|
||||
.map(|expr| {
|
||||
self.eval_time_expr(ctx, expr, day, stock_state.as_ref(), None)
|
||||
self.eval_time_expr(
|
||||
ctx,
|
||||
expr,
|
||||
day,
|
||||
stock_state.as_deref(),
|
||||
None,
|
||||
)
|
||||
})
|
||||
.transpose()?;
|
||||
intents.push(OrderIntent::AlgoPercent {
|
||||
@@ -8014,8 +8063,13 @@ impl PlatformExprStrategy {
|
||||
});
|
||||
}
|
||||
PlatformExplicitOrderKind::LimitPercent => {
|
||||
let percent =
|
||||
self.eval_float(ctx, amount_expr, day, stock_state.as_ref(), None)?;
|
||||
let percent = self.eval_float(
|
||||
ctx,
|
||||
amount_expr,
|
||||
day,
|
||||
stock_state.as_deref(),
|
||||
None,
|
||||
)?;
|
||||
if percent.abs() <= f64::EPSILON {
|
||||
continue;
|
||||
}
|
||||
@@ -8023,7 +8077,7 @@ impl PlatformExprStrategy {
|
||||
ctx,
|
||||
limit_price_expr.as_deref().unwrap_or_default(),
|
||||
day,
|
||||
stock_state.as_ref(),
|
||||
stock_state.as_deref(),
|
||||
None,
|
||||
)?;
|
||||
intents.push(OrderIntent::LimitPercent {
|
||||
@@ -8034,8 +8088,13 @@ impl PlatformExprStrategy {
|
||||
});
|
||||
}
|
||||
PlatformExplicitOrderKind::TargetValue => {
|
||||
let target_value =
|
||||
self.eval_float(ctx, amount_expr, day, stock_state.as_ref(), None)?;
|
||||
let target_value = self.eval_float(
|
||||
ctx,
|
||||
amount_expr,
|
||||
day,
|
||||
stock_state.as_deref(),
|
||||
None,
|
||||
)?;
|
||||
intents.push(OrderIntent::TargetValue {
|
||||
symbol: symbol.clone(),
|
||||
target_value,
|
||||
@@ -8043,13 +8102,18 @@ impl PlatformExprStrategy {
|
||||
});
|
||||
}
|
||||
PlatformExplicitOrderKind::LimitTargetValue => {
|
||||
let target_value =
|
||||
self.eval_float(ctx, amount_expr, day, stock_state.as_ref(), None)?;
|
||||
let target_value = self.eval_float(
|
||||
ctx,
|
||||
amount_expr,
|
||||
day,
|
||||
stock_state.as_deref(),
|
||||
None,
|
||||
)?;
|
||||
let limit_price = self.eval_float(
|
||||
ctx,
|
||||
limit_price_expr.as_deref().unwrap_or_default(),
|
||||
day,
|
||||
stock_state.as_ref(),
|
||||
stock_state.as_deref(),
|
||||
None,
|
||||
)?;
|
||||
intents.push(OrderIntent::LimitTargetValue {
|
||||
@@ -8060,8 +8124,13 @@ impl PlatformExprStrategy {
|
||||
});
|
||||
}
|
||||
PlatformExplicitOrderKind::TargetPercent => {
|
||||
let target_percent =
|
||||
self.eval_float(ctx, amount_expr, day, stock_state.as_ref(), None)?;
|
||||
let target_percent = self.eval_float(
|
||||
ctx,
|
||||
amount_expr,
|
||||
day,
|
||||
stock_state.as_deref(),
|
||||
None,
|
||||
)?;
|
||||
intents.push(OrderIntent::TargetPercent {
|
||||
symbol: symbol.clone(),
|
||||
target_percent,
|
||||
@@ -8069,13 +8138,18 @@ impl PlatformExprStrategy {
|
||||
});
|
||||
}
|
||||
PlatformExplicitOrderKind::LimitTargetPercent => {
|
||||
let target_percent =
|
||||
self.eval_float(ctx, amount_expr, day, stock_state.as_ref(), None)?;
|
||||
let target_percent = self.eval_float(
|
||||
ctx,
|
||||
amount_expr,
|
||||
day,
|
||||
stock_state.as_deref(),
|
||||
None,
|
||||
)?;
|
||||
let limit_price = self.eval_float(
|
||||
ctx,
|
||||
limit_price_expr.as_deref().unwrap_or_default(),
|
||||
day,
|
||||
stock_state.as_ref(),
|
||||
stock_state.as_deref(),
|
||||
None,
|
||||
)?;
|
||||
intents.push(OrderIntent::LimitTargetPercent {
|
||||
@@ -8098,7 +8172,7 @@ impl PlatformExprStrategy {
|
||||
if !self.action_when_matches(
|
||||
ctx,
|
||||
day,
|
||||
stock_state.as_ref(),
|
||||
stock_state.as_deref(),
|
||||
when_expr.as_deref(),
|
||||
)? {
|
||||
continue;
|
||||
@@ -8109,7 +8183,7 @@ impl PlatformExprStrategy {
|
||||
ctx,
|
||||
order_id_expr.as_deref().unwrap_or_default(),
|
||||
day,
|
||||
stock_state.as_ref(),
|
||||
stock_state.as_deref(),
|
||||
None,
|
||||
)?;
|
||||
if order_id == 0 {
|
||||
@@ -8503,6 +8577,7 @@ impl PlatformExprStrategy {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn selectable_universe_on(
|
||||
&self,
|
||||
ctx: &StrategyContext<'_>,
|
||||
@@ -8524,14 +8599,30 @@ impl PlatformExprStrategy {
|
||||
factor_date: NaiveDate,
|
||||
selection_risk_deferral: SelectionRiskDeferral,
|
||||
) -> Vec<EligibleUniverseSnapshot> {
|
||||
self.selection_universe_and_risk_decisions_with_options(
|
||||
ctx,
|
||||
date,
|
||||
factor_date,
|
||||
selection_risk_deferral,
|
||||
false,
|
||||
)
|
||||
.0
|
||||
}
|
||||
|
||||
fn selection_universe_and_risk_decisions_with_options(
|
||||
&self,
|
||||
ctx: &StrategyContext<'_>,
|
||||
date: NaiveDate,
|
||||
factor_date: NaiveDate,
|
||||
selection_risk_deferral: SelectionRiskDeferral,
|
||||
collect_risk_decisions: bool,
|
||||
) -> (Vec<EligibleUniverseSnapshot>, Vec<FidcRiskDecisionAudit>) {
|
||||
let mut rows = Vec::new();
|
||||
let mut decisions = Vec::new();
|
||||
let factor_rows = ctx.data.factor_snapshot_rows_on(factor_date);
|
||||
let factor_symbol_ids = ctx.data.factor_symbol_ids_on(factor_date);
|
||||
debug_assert_eq!(factor_rows.len(), factor_symbol_ids.len());
|
||||
for (factor, symbol_id) in factor_rows.iter().zip(factor_symbol_ids.iter().copied()) {
|
||||
if factor.market_cap_bn <= 0.0 || !factor.market_cap_bn.is_finite() {
|
||||
continue;
|
||||
}
|
||||
if ctx.has_dynamic_universe() && !ctx.dynamic_universe_contains(&factor.symbol) {
|
||||
continue;
|
||||
}
|
||||
@@ -8547,12 +8638,43 @@ impl PlatformExprStrategy {
|
||||
let Some(market) = ctx.data.market_by_symbol_id(date, symbol_id) else {
|
||||
continue;
|
||||
};
|
||||
if let Some(_reason) =
|
||||
self.selection_risk_rejection_reason(ctx, date, &factor.symbol, candidate, market)
|
||||
let (reject_from_universe, selection_decision) = if collect_risk_decisions {
|
||||
let decision = ChinaAShareRiskControl::selection_rejection_decision_with_config(
|
||||
date,
|
||||
candidate,
|
||||
market,
|
||||
ctx.data.instrument(&factor.symbol),
|
||||
&self.config.risk_config,
|
||||
);
|
||||
let rejected = decision.as_ref().is_some_and(|decision| {
|
||||
!selection_risk_deferral.should_defer_rejection(&decision.rule_code)
|
||||
});
|
||||
(rejected, decision)
|
||||
} else {
|
||||
let reason = ChinaAShareRiskControl::selection_rejection_reason_with_config(
|
||||
date,
|
||||
candidate,
|
||||
market,
|
||||
ctx.data.instrument(&factor.symbol),
|
||||
&self.config.risk_config,
|
||||
);
|
||||
(
|
||||
reason.is_some_and(|reason| {
|
||||
!selection_risk_deferral.should_defer_rejection(reason)
|
||||
}),
|
||||
None,
|
||||
)
|
||||
};
|
||||
if let Some(decision) = selection_decision
|
||||
&& !selection_risk_deferral.should_suppress_diagnostic(&decision.rule_code)
|
||||
{
|
||||
if !selection_risk_deferral.should_defer_rejection(_reason) {
|
||||
decisions.push(decision);
|
||||
}
|
||||
if reject_from_universe {
|
||||
continue;
|
||||
}
|
||||
if factor.market_cap_bn <= 0.0 || !factor.market_cap_bn.is_finite() {
|
||||
continue;
|
||||
}
|
||||
if !self.stock_passes_universe_exclude(candidate, market) {
|
||||
continue;
|
||||
@@ -8574,9 +8696,10 @@ impl PlatformExprStrategy {
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
.then_with(|| left.symbol.cmp(&right.symbol))
|
||||
});
|
||||
rows
|
||||
(rows, decisions)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn selection_risk_decisions(
|
||||
&self,
|
||||
ctx: &StrategyContext<'_>,
|
||||
@@ -8591,6 +8714,7 @@ impl PlatformExprStrategy {
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn selection_risk_decisions_with_options(
|
||||
&self,
|
||||
ctx: &StrategyContext<'_>,
|
||||
@@ -8598,40 +8722,14 @@ impl PlatformExprStrategy {
|
||||
factor_date: NaiveDate,
|
||||
selection_risk_deferral: SelectionRiskDeferral,
|
||||
) -> Vec<FidcRiskDecisionAudit> {
|
||||
let mut decisions = Vec::new();
|
||||
let factor_rows = ctx.data.factor_snapshot_rows_on(factor_date);
|
||||
let factor_symbol_ids = ctx.data.factor_symbol_ids_on(factor_date);
|
||||
debug_assert_eq!(factor_rows.len(), factor_symbol_ids.len());
|
||||
for (factor, symbol_id) in factor_rows.iter().zip(factor_symbol_ids.iter().copied()) {
|
||||
if ctx.has_dynamic_universe() && !ctx.dynamic_universe_contains(&factor.symbol) {
|
||||
continue;
|
||||
}
|
||||
let synthetic_candidate;
|
||||
let candidate =
|
||||
if let Some(candidate) = ctx.data.candidate_by_symbol_id(date, symbol_id) {
|
||||
candidate
|
||||
} else {
|
||||
synthetic_candidate =
|
||||
crate::data::missing_candidate_risk_state(date, &factor.symbol);
|
||||
&synthetic_candidate
|
||||
};
|
||||
let Some(market) = ctx.data.market_by_symbol_id(date, symbol_id) else {
|
||||
continue;
|
||||
};
|
||||
if let Some(decision) = ChinaAShareRiskControl::selection_rejection_decision_with_config(
|
||||
self.selection_universe_and_risk_decisions_with_options(
|
||||
ctx,
|
||||
date,
|
||||
candidate,
|
||||
market,
|
||||
ctx.data.instrument(&factor.symbol),
|
||||
&self.config.risk_config,
|
||||
) {
|
||||
if selection_risk_deferral.should_suppress_diagnostic(&decision.rule_code) {
|
||||
continue;
|
||||
}
|
||||
decisions.push(decision);
|
||||
}
|
||||
}
|
||||
decisions
|
||||
factor_date,
|
||||
selection_risk_deferral,
|
||||
true,
|
||||
)
|
||||
.1
|
||||
}
|
||||
|
||||
fn selection_risk_decision_diagnostics(
|
||||
@@ -8667,23 +8765,6 @@ impl PlatformExprStrategy {
|
||||
diagnostics
|
||||
}
|
||||
|
||||
fn selection_risk_rejection_reason(
|
||||
&self,
|
||||
ctx: &StrategyContext<'_>,
|
||||
date: NaiveDate,
|
||||
symbol: &str,
|
||||
candidate: &crate::data::CandidateEligibility,
|
||||
market: &DailyMarketSnapshot,
|
||||
) -> Option<&'static str> {
|
||||
ChinaAShareRiskControl::selection_rejection_reason_with_config(
|
||||
date,
|
||||
candidate,
|
||||
market,
|
||||
ctx.data.instrument(symbol),
|
||||
&self.config.risk_config,
|
||||
)
|
||||
}
|
||||
|
||||
fn stock_selection_limit_rejection_reason(
|
||||
&self,
|
||||
stock: &StockExpressionState,
|
||||
@@ -9013,8 +9094,13 @@ impl PlatformExprStrategy {
|
||||
// Selection policy is evaluated on the signal day. Lagged execution only
|
||||
// defers buy/sell risk to the actual execution bar; it must not disable an
|
||||
// explicitly configured signal-day universe filter.
|
||||
let universe = self.selectable_universe_on(ctx, date, universe_factor_date);
|
||||
let risk_decisions = self.selection_risk_decisions(ctx, date, universe_factor_date);
|
||||
let (universe, risk_decisions) = self.selection_universe_and_risk_decisions_with_options(
|
||||
ctx,
|
||||
date,
|
||||
universe_factor_date,
|
||||
SelectionRiskDeferral::None,
|
||||
true,
|
||||
);
|
||||
let mut diagnostics = Self::selection_risk_decision_diagnostics(
|
||||
&risk_decisions,
|
||||
date,
|
||||
@@ -11534,7 +11620,7 @@ impl Strategy for PlatformExprStrategy {
|
||||
ctx,
|
||||
execution_date,
|
||||
symbol,
|
||||
&self.stock_state(ctx, execution_date, symbol)?,
|
||||
self.stock_state(ctx, execution_date, symbol)?.as_ref(),
|
||||
)?
|
||||
.is_some()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user