perf: stream selection candidates by symbol id
This commit is contained in:
@@ -4007,6 +4007,35 @@ impl PlatformExprStrategy {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn selection_stock_state_with_factor_date_from_views_by_symbol_id<'a>(
|
||||||
|
&self,
|
||||||
|
ctx: &StrategyContext<'a>,
|
||||||
|
date: NaiveDate,
|
||||||
|
factor_date: NaiveDate,
|
||||||
|
symbol_id: u32,
|
||||||
|
symbol: &str,
|
||||||
|
execution_day: &DailySnapshotView<'a>,
|
||||||
|
factor_day: &DailySnapshotView<'a>,
|
||||||
|
) -> Result<Arc<StockExpressionState>, BacktestError> {
|
||||||
|
let source = ViewStockStateSnapshotSource {
|
||||||
|
execution: execution_day,
|
||||||
|
factor: factor_day,
|
||||||
|
same_date: factor_date == date,
|
||||||
|
};
|
||||||
|
let use_intraday_quote = self.selection_quote_usage != StockFilterQuoteUsage::DailyOnly;
|
||||||
|
self.stock_state_with_factor_date_and_time_from_source_by_symbol_id(
|
||||||
|
ctx,
|
||||||
|
date,
|
||||||
|
factor_date,
|
||||||
|
symbol_id,
|
||||||
|
symbol,
|
||||||
|
None,
|
||||||
|
use_intraday_quote,
|
||||||
|
&source,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn stock_decision_rolling_mean(
|
fn stock_decision_rolling_mean(
|
||||||
&self,
|
&self,
|
||||||
ctx: &StrategyContext<'_>,
|
ctx: &StrategyContext<'_>,
|
||||||
@@ -4149,6 +4178,33 @@ impl PlatformExprStrategy {
|
|||||||
symbol: symbol.to_string(),
|
symbol: symbol.to_string(),
|
||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
self.stock_state_with_factor_date_and_time_from_source_by_symbol_id(
|
||||||
|
ctx,
|
||||||
|
date,
|
||||||
|
factor_date,
|
||||||
|
symbol_id,
|
||||||
|
symbol,
|
||||||
|
execution_time,
|
||||||
|
use_intraday_quote,
|
||||||
|
source,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn stock_state_with_factor_date_and_time_from_source_by_symbol_id<'a, S>(
|
||||||
|
&self,
|
||||||
|
ctx: &StrategyContext<'a>,
|
||||||
|
date: NaiveDate,
|
||||||
|
factor_date: NaiveDate,
|
||||||
|
symbol_id: u32,
|
||||||
|
symbol: &str,
|
||||||
|
execution_time: Option<NaiveTime>,
|
||||||
|
use_intraday_quote: bool,
|
||||||
|
source: &S,
|
||||||
|
) -> Result<Arc<StockExpressionState>, BacktestError>
|
||||||
|
where
|
||||||
|
S: StockStateSnapshotSource<'a>,
|
||||||
|
{
|
||||||
let shared_symbol = ctx.data.shared_symbol_by_id(symbol_id).ok_or_else(|| {
|
let shared_symbol = ctx.data.shared_symbol_by_id(symbol_id).ok_or_else(|| {
|
||||||
BacktestError::Data(crate::data::DataSetError::MissingSnapshot {
|
BacktestError::Data(crate::data::DataSetError::MissingSnapshot {
|
||||||
kind: "symbol_reverse_index",
|
kind: "symbol_reverse_index",
|
||||||
@@ -9371,13 +9427,17 @@ impl PlatformExprStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn field_value(&self, row: &EligibleUniverseSnapshot) -> f64 {
|
fn field_value(&self, row: &EligibleUniverseSnapshot) -> f64 {
|
||||||
match self.config.market_cap_field.as_str() {
|
self.field_value_from_caps(row.market_cap_bn, row.free_float_cap_bn)
|
||||||
"market_cap_bn" => row.market_cap_bn,
|
|
||||||
"free_float_cap" | "free_float_market_cap" => {
|
|
||||||
Self::market_cap_storage_to_strategy_unit(row.free_float_cap_bn)
|
|
||||||
}
|
}
|
||||||
"free_float_cap_bn" => row.free_float_cap_bn,
|
|
||||||
_ => Self::market_cap_storage_to_strategy_unit(row.market_cap_bn),
|
fn field_value_from_caps(&self, market_cap_bn: f64, free_float_cap_bn: f64) -> f64 {
|
||||||
|
match self.config.market_cap_field.as_str() {
|
||||||
|
"market_cap_bn" => market_cap_bn,
|
||||||
|
"free_float_cap" | "free_float_market_cap" => {
|
||||||
|
Self::market_cap_storage_to_strategy_unit(free_float_cap_bn)
|
||||||
|
}
|
||||||
|
"free_float_cap_bn" => free_float_cap_bn,
|
||||||
|
_ => Self::market_cap_storage_to_strategy_unit(market_cap_bn),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9421,7 +9481,39 @@ impl PlatformExprStrategy {
|
|||||||
selection_risk_deferral: SelectionRiskDeferral,
|
selection_risk_deferral: SelectionRiskDeferral,
|
||||||
collect_risk_decisions: bool,
|
collect_risk_decisions: bool,
|
||||||
) -> (Vec<EligibleUniverseSnapshot>, Vec<FidcRiskDecisionAudit>) {
|
) -> (Vec<EligibleUniverseSnapshot>, Vec<FidcRiskDecisionAudit>) {
|
||||||
let mut rows = Vec::new();
|
let (symbol_ids, decisions) = self.selection_symbol_ids_and_risk_decisions_with_options(
|
||||||
|
ctx,
|
||||||
|
date,
|
||||||
|
factor_date,
|
||||||
|
selection_risk_deferral,
|
||||||
|
collect_risk_decisions,
|
||||||
|
);
|
||||||
|
let factor_day = ctx.data.daily_snapshot_view(factor_date);
|
||||||
|
let rows = symbol_ids
|
||||||
|
.into_iter()
|
||||||
|
.map(|symbol_id| {
|
||||||
|
let factor = factor_day
|
||||||
|
.factor(symbol_id)
|
||||||
|
.expect("market-cap order references missing factor row");
|
||||||
|
EligibleUniverseSnapshot {
|
||||||
|
symbol: factor.symbol.clone(),
|
||||||
|
market_cap_bn: decision_market_cap_bn(factor),
|
||||||
|
free_float_cap_bn: decision_free_float_cap_bn(factor),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
(rows, decisions)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn selection_symbol_ids_and_risk_decisions_with_options(
|
||||||
|
&self,
|
||||||
|
ctx: &StrategyContext<'_>,
|
||||||
|
date: NaiveDate,
|
||||||
|
factor_date: NaiveDate,
|
||||||
|
selection_risk_deferral: SelectionRiskDeferral,
|
||||||
|
collect_risk_decisions: bool,
|
||||||
|
) -> (Vec<u32>, Vec<FidcRiskDecisionAudit>) {
|
||||||
|
let mut symbol_ids = Vec::new();
|
||||||
let mut decisions = Vec::new();
|
let mut decisions = Vec::new();
|
||||||
let mut eligible_symbols = vec![false; ctx.data.symbol_count()];
|
let mut eligible_symbols = vec![false; ctx.data.symbol_count()];
|
||||||
let execution_day = ctx.data.daily_snapshot_view(date);
|
let execution_day = ctx.data.daily_snapshot_view(date);
|
||||||
@@ -9504,16 +9596,9 @@ impl PlatformExprStrategy {
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let factor = factor_day
|
symbol_ids.push(symbol_id);
|
||||||
.factor(symbol_id)
|
|
||||||
.expect("market-cap order references missing factor row");
|
|
||||||
rows.push(EligibleUniverseSnapshot {
|
|
||||||
symbol: factor.symbol.clone(),
|
|
||||||
market_cap_bn: decision_market_cap_bn(factor),
|
|
||||||
free_float_cap_bn: decision_free_float_cap_bn(factor),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
(rows, decisions)
|
(symbol_ids, decisions)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -9657,6 +9742,21 @@ impl PlatformExprStrategy {
|
|||||||
candidate: &EligibleUniverseSnapshot,
|
candidate: &EligibleUniverseSnapshot,
|
||||||
stock: &StockExpressionState,
|
stock: &StockExpressionState,
|
||||||
field: &str,
|
field: &str,
|
||||||
|
) -> Option<f64> {
|
||||||
|
self.stock_numeric_field_value_from_caps(
|
||||||
|
candidate.market_cap_bn,
|
||||||
|
candidate.free_float_cap_bn,
|
||||||
|
stock,
|
||||||
|
field,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stock_numeric_field_value_from_caps(
|
||||||
|
&self,
|
||||||
|
candidate_market_cap_bn: f64,
|
||||||
|
candidate_free_float_cap_bn: f64,
|
||||||
|
stock: &StockExpressionState,
|
||||||
|
field: &str,
|
||||||
) -> Option<f64> {
|
) -> Option<f64> {
|
||||||
match field {
|
match field {
|
||||||
"market_cap" => Some(stock.market_cap),
|
"market_cap" => Some(stock.market_cap),
|
||||||
@@ -9722,13 +9822,13 @@ impl PlatformExprStrategy {
|
|||||||
"is_one_yuan" => Some(if stock.is_one_yuan { 1.0 } else { 0.0 }),
|
"is_one_yuan" => Some(if stock.is_one_yuan { 1.0 } else { 0.0 }),
|
||||||
"is_new_listing" => Some(if stock.is_new_listing { 1.0 } else { 0.0 }),
|
"is_new_listing" => Some(if stock.is_new_listing { 1.0 } else { 0.0 }),
|
||||||
"candidate_market_cap" => Some(Self::market_cap_storage_to_strategy_unit(
|
"candidate_market_cap" => Some(Self::market_cap_storage_to_strategy_unit(
|
||||||
candidate.market_cap_bn,
|
candidate_market_cap_bn,
|
||||||
)),
|
)),
|
||||||
"candidate_market_cap_bn" => Some(candidate.market_cap_bn),
|
"candidate_market_cap_bn" => Some(candidate_market_cap_bn),
|
||||||
"candidate_free_float_cap" => Some(Self::market_cap_storage_to_strategy_unit(
|
"candidate_free_float_cap" => Some(Self::market_cap_storage_to_strategy_unit(
|
||||||
candidate.free_float_cap_bn,
|
candidate_free_float_cap_bn,
|
||||||
)),
|
)),
|
||||||
"candidate_free_float_cap_bn" => Some(candidate.free_float_cap_bn),
|
"candidate_free_float_cap_bn" => Some(candidate_free_float_cap_bn),
|
||||||
other => stock.extra_factors.get(other).copied(),
|
other => stock.extra_factors.get(other).copied(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9737,20 +9837,38 @@ impl PlatformExprStrategy {
|
|||||||
&self,
|
&self,
|
||||||
candidate: &EligibleUniverseSnapshot,
|
candidate: &EligibleUniverseSnapshot,
|
||||||
stock: &StockExpressionState,
|
stock: &StockExpressionState,
|
||||||
|
) -> f64 {
|
||||||
|
self.selection_field_value_from_caps(
|
||||||
|
candidate.market_cap_bn,
|
||||||
|
candidate.free_float_cap_bn,
|
||||||
|
stock,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn selection_field_value_from_caps(
|
||||||
|
&self,
|
||||||
|
market_cap_bn: f64,
|
||||||
|
free_float_cap_bn: f64,
|
||||||
|
stock: &StockExpressionState,
|
||||||
) -> f64 {
|
) -> f64 {
|
||||||
match self.config.market_cap_field.as_str() {
|
match self.config.market_cap_field.as_str() {
|
||||||
"market_cap" => {
|
"market_cap" => {
|
||||||
return Self::market_cap_storage_to_strategy_unit(candidate.market_cap_bn);
|
return Self::market_cap_storage_to_strategy_unit(market_cap_bn);
|
||||||
}
|
}
|
||||||
"market_cap_bn" => return candidate.market_cap_bn,
|
"market_cap_bn" => return market_cap_bn,
|
||||||
"free_float_cap" | "free_float_market_cap" => {
|
"free_float_cap" | "free_float_market_cap" => {
|
||||||
return Self::market_cap_storage_to_strategy_unit(candidate.free_float_cap_bn);
|
return Self::market_cap_storage_to_strategy_unit(free_float_cap_bn);
|
||||||
}
|
}
|
||||||
"free_float_cap_bn" => return candidate.free_float_cap_bn,
|
"free_float_cap_bn" => return free_float_cap_bn,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
self.stock_numeric_field_value(candidate, stock, self.config.market_cap_field.as_str())
|
self.stock_numeric_field_value_from_caps(
|
||||||
.unwrap_or_else(|| self.field_value(candidate))
|
market_cap_bn,
|
||||||
|
free_float_cap_bn,
|
||||||
|
stock,
|
||||||
|
self.config.market_cap_field.as_str(),
|
||||||
|
)
|
||||||
|
.unwrap_or_else(|| self.field_value_from_caps(market_cap_bn, free_float_cap_bn))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rank_value(
|
fn rank_value(
|
||||||
@@ -9800,7 +9918,7 @@ impl PlatformExprStrategy {
|
|||||||
ctx: &StrategyContext<'_>,
|
ctx: &StrategyContext<'_>,
|
||||||
date: NaiveDate,
|
date: NaiveDate,
|
||||||
day: &DayExpressionState,
|
day: &DayExpressionState,
|
||||||
candidate: &EligibleUniverseSnapshot,
|
symbol: &str,
|
||||||
stock: &StockExpressionState,
|
stock: &StockExpressionState,
|
||||||
diagnostics: &mut Vec<String>,
|
diagnostics: &mut Vec<String>,
|
||||||
) -> Result<bool, BacktestError> {
|
) -> Result<bool, BacktestError> {
|
||||||
@@ -9808,13 +9926,13 @@ impl PlatformExprStrategy {
|
|||||||
&& let Some(reason) = self.stock_selection_limit_rejection_reason(stock)
|
&& let Some(reason) = self.stock_selection_limit_rejection_reason(stock)
|
||||||
{
|
{
|
||||||
if diagnostics.len() < 12 {
|
if diagnostics.len() < 12 {
|
||||||
diagnostics.push(format!("{} rejected by {}", candidate.symbol, reason));
|
diagnostics.push(format!("{} rejected by {}", symbol, reason));
|
||||||
}
|
}
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
if !self.stock_passes_expr(ctx, day, stock)? {
|
if !self.stock_passes_expr(ctx, day, stock)? {
|
||||||
if diagnostics.len() < 12 {
|
if diagnostics.len() < 12 {
|
||||||
diagnostics.push(format!("{} rejected by stock_expr", candidate.symbol));
|
diagnostics.push(format!("{} rejected by stock_expr", symbol));
|
||||||
}
|
}
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
@@ -9822,13 +9940,13 @@ impl PlatformExprStrategy {
|
|||||||
== PlatformStopTakeReferencePriceMode::SignalDayPostAdjustedClose
|
== PlatformStopTakeReferencePriceMode::SignalDayPostAdjustedClose
|
||||||
&& ctx
|
&& ctx
|
||||||
.data
|
.data
|
||||||
.market_latest_back_adjusted_close(date, &candidate.symbol)
|
.market_latest_back_adjusted_close(date, symbol)
|
||||||
.is_none()
|
.is_none()
|
||||||
{
|
{
|
||||||
if diagnostics.len() < 12 {
|
if diagnostics.len() < 12 {
|
||||||
diagnostics.push(format!(
|
diagnostics.push(format!(
|
||||||
"{} rejected by missing signal-day post-adjusted close",
|
"{} rejected by missing signal-day post-adjusted close",
|
||||||
candidate.symbol
|
symbol
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
@@ -9968,7 +10086,8 @@ impl PlatformExprStrategy {
|
|||||||
// Selection policy is evaluated on the signal day. Lagged execution only
|
// 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
|
// defers buy/sell risk to the actual execution bar; it must not disable an
|
||||||
// explicitly configured signal-day universe filter.
|
// explicitly configured signal-day universe filter.
|
||||||
let (universe, risk_decisions) = self.selection_universe_and_risk_decisions_with_options(
|
let (universe_symbol_ids, risk_decisions) = self
|
||||||
|
.selection_symbol_ids_and_risk_decisions_with_options(
|
||||||
ctx,
|
ctx,
|
||||||
date,
|
date,
|
||||||
universe_factor_date,
|
universe_factor_date,
|
||||||
@@ -9982,6 +10101,7 @@ impl PlatformExprStrategy {
|
|||||||
5,
|
5,
|
||||||
);
|
);
|
||||||
let execution_day = ctx.data.daily_snapshot_view(date);
|
let execution_day = ctx.data.daily_snapshot_view(date);
|
||||||
|
let universe_factor_day = ctx.data.daily_snapshot_view(universe_factor_date);
|
||||||
let factor_day = ctx.data.daily_snapshot_view(stock_factor_date);
|
let factor_day = ctx.data.daily_snapshot_view(stock_factor_date);
|
||||||
|
|
||||||
// The universe is already stably ordered by market cap. When the
|
// The universe is already stably ordered by market cap. When the
|
||||||
@@ -9989,17 +10109,26 @@ impl PlatformExprStrategy {
|
|||||||
// complete ranking for replacement limiting, select directly from the
|
// complete ranking for replacement limiting, select directly from the
|
||||||
// ordered stream instead of materializing a second candidate vector.
|
// ordered stream instead of materializing a second candidate vector.
|
||||||
if self.rank_reuses_market_cap_order() && self.config.daily_replacement_limit == 0 {
|
if self.rank_reuses_market_cap_order() && self.config.daily_replacement_limit == 0 {
|
||||||
let mut selected = Vec::with_capacity(limit.min(universe.len()));
|
let mut selected = Vec::with_capacity(limit.min(universe_symbol_ids.len()));
|
||||||
for candidate in universe {
|
for symbol_id in universe_symbol_ids {
|
||||||
let stock = self.selection_stock_state_with_factor_date_from_views(
|
let factor = universe_factor_day
|
||||||
|
.factor(symbol_id)
|
||||||
|
.expect("market-cap order references missing factor row");
|
||||||
|
let symbol = factor.symbol.as_str();
|
||||||
|
let stock = self.selection_stock_state_with_factor_date_from_views_by_symbol_id(
|
||||||
ctx,
|
ctx,
|
||||||
date,
|
date,
|
||||||
stock_factor_date,
|
stock_factor_date,
|
||||||
&candidate.symbol,
|
symbol_id,
|
||||||
|
symbol,
|
||||||
&execution_day,
|
&execution_day,
|
||||||
&factor_day,
|
&factor_day,
|
||||||
)?;
|
)?;
|
||||||
let field_value = self.selection_field_value(&candidate, &stock);
|
let field_value = self.selection_field_value_from_caps(
|
||||||
|
decision_market_cap_bn(factor),
|
||||||
|
decision_free_float_cap_bn(factor),
|
||||||
|
&stock,
|
||||||
|
);
|
||||||
if !field_value.is_finite() || field_value < band_low || field_value > band_high {
|
if !field_value.is_finite() || field_value < band_low || field_value > band_high {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -10007,11 +10136,11 @@ impl PlatformExprStrategy {
|
|||||||
ctx,
|
ctx,
|
||||||
date,
|
date,
|
||||||
day,
|
day,
|
||||||
&candidate,
|
symbol,
|
||||||
&stock,
|
&stock,
|
||||||
&mut diagnostics,
|
&mut diagnostics,
|
||||||
)? {
|
)? {
|
||||||
selected.push(candidate.symbol);
|
selected.push(factor.symbol.clone());
|
||||||
if selected.len() >= limit {
|
if selected.len() >= limit {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -10020,6 +10149,20 @@ impl PlatformExprStrategy {
|
|||||||
return Ok((selected, diagnostics, risk_decisions));
|
return Ok((selected, diagnostics, risk_decisions));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let universe = universe_symbol_ids
|
||||||
|
.into_iter()
|
||||||
|
.map(|symbol_id| {
|
||||||
|
let factor = universe_factor_day
|
||||||
|
.factor(symbol_id)
|
||||||
|
.expect("market-cap order references missing factor row");
|
||||||
|
EligibleUniverseSnapshot {
|
||||||
|
symbol: factor.symbol.clone(),
|
||||||
|
market_cap_bn: decision_market_cap_bn(factor),
|
||||||
|
free_float_cap_bn: decision_free_float_cap_bn(factor),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
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();
|
||||||
@@ -10114,7 +10257,7 @@ impl PlatformExprStrategy {
|
|||||||
ctx,
|
ctx,
|
||||||
date,
|
date,
|
||||||
day,
|
day,
|
||||||
&candidate,
|
&candidate.symbol,
|
||||||
&stock,
|
&stock,
|
||||||
&mut diagnostics,
|
&mut diagnostics,
|
||||||
)? {
|
)? {
|
||||||
@@ -13920,6 +14063,16 @@ mod tests {
|
|||||||
.selection_risk_decisions(&ctx, date, date)
|
.selection_risk_decisions(&ctx, date, date)
|
||||||
.is_empty()
|
.is_empty()
|
||||||
);
|
);
|
||||||
|
let (default_symbol_ids, default_direct_decisions) = default_strategy
|
||||||
|
.selection_symbol_ids_and_risk_decisions_with_options(
|
||||||
|
&ctx,
|
||||||
|
date,
|
||||||
|
date,
|
||||||
|
SelectionRiskDeferral::None,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
assert_eq!(default_symbol_ids, vec![data.symbol_id(symbol).unwrap()]);
|
||||||
|
assert!(default_direct_decisions.is_empty());
|
||||||
let mut selection_cfg = PlatformExprStrategyConfig::microcap_rotation();
|
let mut selection_cfg = PlatformExprStrategyConfig::microcap_rotation();
|
||||||
selection_cfg.risk_config.static_rules.reject_kcb_selection = true;
|
selection_cfg.risk_config.static_rules.reject_kcb_selection = true;
|
||||||
let selection_strategy = PlatformExprStrategy::new(selection_cfg);
|
let selection_strategy = PlatformExprStrategy::new(selection_cfg);
|
||||||
@@ -13937,6 +14090,20 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert!(risk_diagnostics.is_empty(), "{risk_diagnostics:?}");
|
assert!(risk_diagnostics.is_empty(), "{risk_diagnostics:?}");
|
||||||
let risk_decisions = selection_strategy.selection_risk_decisions(&ctx, date, date);
|
let risk_decisions = selection_strategy.selection_risk_decisions(&ctx, date, date);
|
||||||
|
let (rejected_symbol_ids, direct_risk_decisions) = selection_strategy
|
||||||
|
.selection_symbol_ids_and_risk_decisions_with_options(
|
||||||
|
&ctx,
|
||||||
|
date,
|
||||||
|
date,
|
||||||
|
SelectionRiskDeferral::None,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
assert!(rejected_symbol_ids.is_empty());
|
||||||
|
assert_eq!(direct_risk_decisions.len(), risk_decisions.len());
|
||||||
|
assert_eq!(
|
||||||
|
direct_risk_decisions[0].rule_code,
|
||||||
|
risk_decisions[0].rule_code
|
||||||
|
);
|
||||||
let risk_diagnostics = PlatformExprStrategy::selection_risk_decision_diagnostics(
|
let risk_diagnostics = PlatformExprStrategy::selection_risk_decision_diagnostics(
|
||||||
&risk_decisions,
|
&risk_decisions,
|
||||||
date,
|
date,
|
||||||
|
|||||||
Reference in New Issue
Block a user