perf: reuse daily snapshot views in stock selection
This commit is contained in:
@@ -522,6 +522,7 @@ pub struct YieldCurvePoint {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct EligibleUniverseSnapshot {
|
pub struct EligibleUniverseSnapshot {
|
||||||
pub symbol: String,
|
pub symbol: String,
|
||||||
|
pub symbol_id: u32,
|
||||||
pub market_cap_bn: f64,
|
pub market_cap_bn: f64,
|
||||||
pub free_float_cap_bn: f64,
|
pub free_float_cap_bn: f64,
|
||||||
}
|
}
|
||||||
@@ -1357,8 +1358,7 @@ impl<'a, T> DailySymbolRows<'a, T> {
|
|||||||
/// the already indexed slices once and keeps all lookups read-only.
|
/// the already indexed slices once and keeps all lookups read-only.
|
||||||
pub(crate) struct DailySnapshotView<'a> {
|
pub(crate) struct DailySnapshotView<'a> {
|
||||||
market: DailySymbolRows<'a, DailyMarketSnapshot>,
|
market: DailySymbolRows<'a, DailyMarketSnapshot>,
|
||||||
factor_rows: &'a [DailyFactorSnapshot],
|
factors: DailySymbolRows<'a, DailyFactorSnapshot>,
|
||||||
factor_symbol_ids: &'a [u32],
|
|
||||||
candidates: DailySymbolRows<'a, CandidateEligibility>,
|
candidates: DailySymbolRows<'a, CandidateEligibility>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1371,12 +1371,16 @@ impl<'a> DailySnapshotView<'a> {
|
|||||||
self.candidates.get(symbol_id)
|
self.candidates.get(symbol_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn factor(&self, symbol_id: u32) -> Option<&'a DailyFactorSnapshot> {
|
||||||
|
self.factors.get(symbol_id)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn factor_rows(&self) -> &'a [DailyFactorSnapshot] {
|
pub(crate) fn factor_rows(&self) -> &'a [DailyFactorSnapshot] {
|
||||||
self.factor_rows
|
self.factors.rows
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn factor_symbol_ids(&self) -> &'a [u32] {
|
pub(crate) fn factor_symbol_ids(&self) -> &'a [u32] {
|
||||||
self.factor_symbol_ids
|
self.factors.symbol_ids
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1950,16 +1954,12 @@ impl DataSet {
|
|||||||
&self.market_symbol_ids_by_date,
|
&self.market_symbol_ids_by_date,
|
||||||
&self.market_row_positions_by_date,
|
&self.market_row_positions_by_date,
|
||||||
),
|
),
|
||||||
factor_rows: self
|
factors: rows_on(
|
||||||
.factor_by_date
|
date,
|
||||||
.get(&date)
|
&self.factor_by_date,
|
||||||
.map(Vec::as_slice)
|
&self.factor_symbol_ids_by_date,
|
||||||
.unwrap_or(&[]),
|
&self.factor_row_positions_by_date,
|
||||||
factor_symbol_ids: self
|
),
|
||||||
.factor_symbol_ids_by_date
|
|
||||||
.get(&date)
|
|
||||||
.map(Vec::as_slice)
|
|
||||||
.unwrap_or(&[]),
|
|
||||||
candidates: rows_on(
|
candidates: rows_on(
|
||||||
date,
|
date,
|
||||||
&self.candidate_by_date,
|
&self.candidate_by_date,
|
||||||
@@ -3650,14 +3650,25 @@ impl DataSet {
|
|||||||
|
|
||||||
pub fn eligible_universe_on(&self, date: NaiveDate) -> &[EligibleUniverseSnapshot] {
|
pub fn eligible_universe_on(&self, date: NaiveDate) -> &[EligibleUniverseSnapshot] {
|
||||||
self.eligible_universe_by_date
|
self.eligible_universe_by_date
|
||||||
.get_or_init(|| build_eligible_universe(&self.factor_by_date, &self.market_by_date))
|
.get_or_init(|| {
|
||||||
|
build_eligible_universe(
|
||||||
|
&self.factor_by_date,
|
||||||
|
&self.market_by_date,
|
||||||
|
&self.symbol_id_by_code,
|
||||||
|
)
|
||||||
|
})
|
||||||
.get(&date)
|
.get(&date)
|
||||||
.map(Vec::as_slice)
|
.map(Vec::as_slice)
|
||||||
.unwrap_or(&[])
|
.unwrap_or(&[])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fundamental_universe_on(&self, date: NaiveDate) -> Vec<EligibleUniverseSnapshot> {
|
pub fn fundamental_universe_on(&self, date: NaiveDate) -> Vec<EligibleUniverseSnapshot> {
|
||||||
build_fundamental_universe_for_date(date, &self.factor_by_date, &self.market_by_date)
|
build_fundamental_universe_for_date(
|
||||||
|
date,
|
||||||
|
&self.factor_by_date,
|
||||||
|
&self.market_by_date,
|
||||||
|
&self.symbol_id_by_code,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eligible_universe_on_with_risk_config(
|
pub fn eligible_universe_on_with_risk_config(
|
||||||
@@ -3671,6 +3682,7 @@ impl DataSet {
|
|||||||
&self.candidate_by_date,
|
&self.candidate_by_date,
|
||||||
&self.market_by_date,
|
&self.market_by_date,
|
||||||
&self.instruments,
|
&self.instruments,
|
||||||
|
&self.symbol_id_by_code,
|
||||||
risk_config,
|
risk_config,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -4450,11 +4462,17 @@ fn build_order_book_depth_index(
|
|||||||
fn build_eligible_universe(
|
fn build_eligible_universe(
|
||||||
factor_by_date: &BTreeMap<NaiveDate, Vec<DailyFactorSnapshot>>,
|
factor_by_date: &BTreeMap<NaiveDate, Vec<DailyFactorSnapshot>>,
|
||||||
market_by_date: &BTreeMap<NaiveDate, Vec<DailyMarketSnapshot>>,
|
market_by_date: &BTreeMap<NaiveDate, Vec<DailyMarketSnapshot>>,
|
||||||
|
symbol_id_by_code: &AHashMap<String, u32>,
|
||||||
) -> BTreeMap<NaiveDate, Vec<EligibleUniverseSnapshot>> {
|
) -> BTreeMap<NaiveDate, Vec<EligibleUniverseSnapshot>> {
|
||||||
let mut per_date = BTreeMap::<NaiveDate, Vec<EligibleUniverseSnapshot>>::new();
|
let mut per_date = BTreeMap::<NaiveDate, Vec<EligibleUniverseSnapshot>>::new();
|
||||||
|
|
||||||
for date in factor_by_date.keys() {
|
for date in factor_by_date.keys() {
|
||||||
let rows = build_fundamental_universe_for_date(*date, factor_by_date, market_by_date);
|
let rows = build_fundamental_universe_for_date(
|
||||||
|
*date,
|
||||||
|
factor_by_date,
|
||||||
|
market_by_date,
|
||||||
|
symbol_id_by_code,
|
||||||
|
);
|
||||||
per_date.insert(*date, rows);
|
per_date.insert(*date, rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4465,12 +4483,16 @@ fn build_fundamental_universe_for_date(
|
|||||||
date: NaiveDate,
|
date: NaiveDate,
|
||||||
factor_by_date: &BTreeMap<NaiveDate, Vec<DailyFactorSnapshot>>,
|
factor_by_date: &BTreeMap<NaiveDate, Vec<DailyFactorSnapshot>>,
|
||||||
market_by_date: &BTreeMap<NaiveDate, Vec<DailyMarketSnapshot>>,
|
market_by_date: &BTreeMap<NaiveDate, Vec<DailyMarketSnapshot>>,
|
||||||
|
symbol_id_by_code: &AHashMap<String, u32>,
|
||||||
) -> Vec<EligibleUniverseSnapshot> {
|
) -> Vec<EligibleUniverseSnapshot> {
|
||||||
let mut rows = Vec::new();
|
let mut rows = Vec::new();
|
||||||
let Some(factors) = factor_by_date.get(&date) else {
|
let Some(factors) = factor_by_date.get(&date) else {
|
||||||
return rows;
|
return rows;
|
||||||
};
|
};
|
||||||
for factor in factors {
|
for factor in factors {
|
||||||
|
let Some(symbol_id) = symbol_id_by_code.get(&factor.symbol).copied() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
if market_by_date
|
if market_by_date
|
||||||
.get(&date)
|
.get(&date)
|
||||||
.and_then(|rows| find_by_symbol(rows, &factor.symbol, |row| row.symbol.as_str()))
|
.and_then(|rows| find_by_symbol(rows, &factor.symbol, |row| row.symbol.as_str()))
|
||||||
@@ -4484,6 +4506,7 @@ fn build_fundamental_universe_for_date(
|
|||||||
}
|
}
|
||||||
rows.push(EligibleUniverseSnapshot {
|
rows.push(EligibleUniverseSnapshot {
|
||||||
symbol: factor.symbol.clone(),
|
symbol: factor.symbol.clone(),
|
||||||
|
symbol_id,
|
||||||
market_cap_bn,
|
market_cap_bn,
|
||||||
free_float_cap_bn: decision_free_float_cap_bn(factor),
|
free_float_cap_bn: decision_free_float_cap_bn(factor),
|
||||||
});
|
});
|
||||||
@@ -4503,6 +4526,7 @@ fn build_eligible_universe_for_date(
|
|||||||
candidate_by_date: &BTreeMap<NaiveDate, Vec<CandidateEligibility>>,
|
candidate_by_date: &BTreeMap<NaiveDate, Vec<CandidateEligibility>>,
|
||||||
market_by_date: &BTreeMap<NaiveDate, Vec<DailyMarketSnapshot>>,
|
market_by_date: &BTreeMap<NaiveDate, Vec<DailyMarketSnapshot>>,
|
||||||
instruments: &HashMap<String, Instrument>,
|
instruments: &HashMap<String, Instrument>,
|
||||||
|
symbol_id_by_code: &AHashMap<String, u32>,
|
||||||
risk_config: &FidcRiskControlConfig,
|
risk_config: &FidcRiskControlConfig,
|
||||||
) -> Vec<EligibleUniverseSnapshot> {
|
) -> Vec<EligibleUniverseSnapshot> {
|
||||||
factor_by_date
|
factor_by_date
|
||||||
@@ -4514,6 +4538,7 @@ fn build_eligible_universe_for_date(
|
|||||||
candidate_by_date,
|
candidate_by_date,
|
||||||
market_by_date,
|
market_by_date,
|
||||||
instruments,
|
instruments,
|
||||||
|
symbol_id_by_code,
|
||||||
risk_config,
|
risk_config,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -4526,10 +4551,14 @@ fn build_eligible_universe_for_date_from_factors(
|
|||||||
candidate_by_date: &BTreeMap<NaiveDate, Vec<CandidateEligibility>>,
|
candidate_by_date: &BTreeMap<NaiveDate, Vec<CandidateEligibility>>,
|
||||||
market_by_date: &BTreeMap<NaiveDate, Vec<DailyMarketSnapshot>>,
|
market_by_date: &BTreeMap<NaiveDate, Vec<DailyMarketSnapshot>>,
|
||||||
instruments: &HashMap<String, Instrument>,
|
instruments: &HashMap<String, Instrument>,
|
||||||
|
symbol_id_by_code: &AHashMap<String, u32>,
|
||||||
risk_config: &FidcRiskControlConfig,
|
risk_config: &FidcRiskControlConfig,
|
||||||
) -> Vec<EligibleUniverseSnapshot> {
|
) -> Vec<EligibleUniverseSnapshot> {
|
||||||
let mut rows = Vec::new();
|
let mut rows = Vec::new();
|
||||||
for factor in factors {
|
for factor in factors {
|
||||||
|
let Some(symbol_id) = symbol_id_by_code.get(&factor.symbol).copied() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
if factor.market_cap_bn <= 0.0 || !factor.market_cap_bn.is_finite() {
|
if factor.market_cap_bn <= 0.0 || !factor.market_cap_bn.is_finite() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -4567,6 +4596,7 @@ fn build_eligible_universe_for_date_from_factors(
|
|||||||
let free_float_cap_bn = decision_free_float_cap_bn(factor);
|
let free_float_cap_bn = decision_free_float_cap_bn(factor);
|
||||||
rows.push(EligibleUniverseSnapshot {
|
rows.push(EligibleUniverseSnapshot {
|
||||||
symbol: factor.symbol.clone(),
|
symbol: factor.symbol.clone(),
|
||||||
|
symbol_id,
|
||||||
market_cap_bn,
|
market_cap_bn,
|
||||||
free_float_cap_bn,
|
free_float_cap_bn,
|
||||||
});
|
});
|
||||||
@@ -5005,6 +5035,10 @@ mod tests {
|
|||||||
.map(|row| row.symbol.as_str()),
|
.map(|row| row.symbol.as_str()),
|
||||||
Some(symbol)
|
Some(symbol)
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
day.factor(symbol_id).map(|row| row.symbol.as_str()),
|
||||||
|
Some(symbol)
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
data.candidate_by_symbol_id(date, symbol_id)
|
data.candidate_by_symbol_id(date, symbol_id)
|
||||||
.map(|row| row.symbol.as_str()),
|
.map(|row| row.symbol.as_str()),
|
||||||
@@ -5024,6 +5058,7 @@ mod tests {
|
|||||||
Some("000300.SH")
|
Some("000300.SH")
|
||||||
);
|
);
|
||||||
assert!(data.factor_by_symbol_id(date, signal_id).is_none());
|
assert!(data.factor_by_symbol_id(date, signal_id).is_none());
|
||||||
|
assert!(day.factor(signal_id).is_none());
|
||||||
assert!(data.candidate_by_symbol_id(date, signal_id).is_none());
|
assert!(data.candidate_by_symbol_id(date, signal_id).is_none());
|
||||||
assert!(day.candidate(signal_id).is_none());
|
assert!(day.candidate(signal_id).is_none());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ use rhai::{AST, Dynamic, Engine, Map, Scope};
|
|||||||
use crate::broker::{MatchingType, RebalanceCashMode, SlippageModel};
|
use crate::broker::{MatchingType, RebalanceCashMode, SlippageModel};
|
||||||
use crate::cost::ChinaAShareCostModel;
|
use crate::cost::ChinaAShareCostModel;
|
||||||
use crate::data::{
|
use crate::data::{
|
||||||
DailyMarketSnapshot, EligibleUniverseSnapshot, PriceField, decision_free_float_cap_bn,
|
DailyMarketSnapshot, DailySnapshotView, EligibleUniverseSnapshot, PriceField,
|
||||||
decision_market_cap_bn,
|
decision_free_float_cap_bn, decision_market_cap_bn,
|
||||||
};
|
};
|
||||||
use crate::engine::BacktestError;
|
use crate::engine::BacktestError;
|
||||||
use crate::events::OrderSide;
|
use crate::events::OrderSide;
|
||||||
@@ -3883,6 +3883,7 @@ impl PlatformExprStrategy {
|
|||||||
self.stock_state_with_factor_date_and_time(ctx, date, factor_date, symbol, None, true)
|
self.stock_state_with_factor_date_and_time(ctx, date, factor_date, symbol, None, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
fn selection_stock_state_with_factor_date(
|
fn selection_stock_state_with_factor_date(
|
||||||
&self,
|
&self,
|
||||||
ctx: &StrategyContext<'_>,
|
ctx: &StrategyContext<'_>,
|
||||||
@@ -3901,6 +3902,29 @@ impl PlatformExprStrategy {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn selection_stock_state_with_factor_date_from_views(
|
||||||
|
&self,
|
||||||
|
ctx: &StrategyContext<'_>,
|
||||||
|
date: NaiveDate,
|
||||||
|
factor_date: NaiveDate,
|
||||||
|
candidate: &EligibleUniverseSnapshot,
|
||||||
|
execution_day: &DailySnapshotView<'_>,
|
||||||
|
factor_day: &DailySnapshotView<'_>,
|
||||||
|
) -> Result<Arc<StockExpressionState>, BacktestError> {
|
||||||
|
let use_intraday_quote = self.selection_quote_usage != StockFilterQuoteUsage::DailyOnly;
|
||||||
|
self.stock_state_with_factor_date_and_time_by_symbol_id(
|
||||||
|
ctx,
|
||||||
|
date,
|
||||||
|
factor_date,
|
||||||
|
candidate.symbol_id,
|
||||||
|
&candidate.symbol,
|
||||||
|
None,
|
||||||
|
use_intraday_quote,
|
||||||
|
Some(execution_day),
|
||||||
|
Some(factor_day),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn stock_decision_rolling_mean(
|
fn stock_decision_rolling_mean(
|
||||||
&self,
|
&self,
|
||||||
ctx: &StrategyContext<'_>,
|
ctx: &StrategyContext<'_>,
|
||||||
@@ -4013,6 +4037,32 @@ impl PlatformExprStrategy {
|
|||||||
symbol: symbol.to_string(),
|
symbol: symbol.to_string(),
|
||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
self.stock_state_with_factor_date_and_time_by_symbol_id(
|
||||||
|
ctx,
|
||||||
|
date,
|
||||||
|
factor_date,
|
||||||
|
symbol_id,
|
||||||
|
symbol,
|
||||||
|
execution_time,
|
||||||
|
use_intraday_quote,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn stock_state_with_factor_date_and_time_by_symbol_id(
|
||||||
|
&self,
|
||||||
|
ctx: &StrategyContext<'_>,
|
||||||
|
date: NaiveDate,
|
||||||
|
factor_date: NaiveDate,
|
||||||
|
symbol_id: u32,
|
||||||
|
symbol: &str,
|
||||||
|
execution_time: Option<NaiveTime>,
|
||||||
|
use_intraday_quote: bool,
|
||||||
|
execution_day: Option<&DailySnapshotView<'_>>,
|
||||||
|
factor_day: Option<&DailySnapshotView<'_>>,
|
||||||
|
) -> Result<Arc<StockExpressionState>, BacktestError> {
|
||||||
let calendar_index = {
|
let calendar_index = {
|
||||||
let mut cache_date = self.stock_state_cache_date.borrow_mut();
|
let mut cache_date = self.stock_state_cache_date.borrow_mut();
|
||||||
if *cache_date != Some(date) {
|
if *cache_date != Some(date) {
|
||||||
@@ -4033,9 +4083,9 @@ impl PlatformExprStrategy {
|
|||||||
return Ok(Arc::clone(state));
|
return Ok(Arc::clone(state));
|
||||||
}
|
}
|
||||||
|
|
||||||
let market = ctx
|
let market = execution_day
|
||||||
.data
|
.and_then(|view| view.market(symbol_id))
|
||||||
.market_by_symbol_id(date, symbol_id)
|
.or_else(|| ctx.data.market_by_symbol_id(date, symbol_id))
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
BacktestError::Data(crate::data::DataSetError::MissingSnapshot {
|
BacktestError::Data(crate::data::DataSetError::MissingSnapshot {
|
||||||
kind: "market",
|
kind: "market",
|
||||||
@@ -4043,9 +4093,9 @@ impl PlatformExprStrategy {
|
|||||||
symbol: symbol.to_string(),
|
symbol: symbol.to_string(),
|
||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
let candidate = ctx
|
let candidate = execution_day
|
||||||
.data
|
.and_then(|view| view.candidate(symbol_id))
|
||||||
.candidate_by_symbol_id(date, symbol_id)
|
.or_else(|| ctx.data.candidate_by_symbol_id(date, symbol_id))
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
BacktestError::Data(crate::data::DataSetError::MissingSnapshot {
|
BacktestError::Data(crate::data::DataSetError::MissingSnapshot {
|
||||||
kind: "candidate",
|
kind: "candidate",
|
||||||
@@ -4056,13 +4106,14 @@ impl PlatformExprStrategy {
|
|||||||
let feature_market = if factor_date == date {
|
let feature_market = if factor_date == date {
|
||||||
market
|
market
|
||||||
} else {
|
} else {
|
||||||
ctx.data
|
factor_day
|
||||||
.market_by_symbol_id(factor_date, symbol_id)
|
.and_then(|view| view.market(symbol_id))
|
||||||
|
.or_else(|| ctx.data.market_by_symbol_id(factor_date, symbol_id))
|
||||||
.unwrap_or(market)
|
.unwrap_or(market)
|
||||||
};
|
};
|
||||||
let factor = ctx
|
let factor = factor_day
|
||||||
.data
|
.and_then(|view| view.factor(symbol_id))
|
||||||
.factor_by_symbol_id(factor_date, symbol_id)
|
.or_else(|| ctx.data.factor_by_symbol_id(factor_date, symbol_id))
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
BacktestError::Data(crate::data::DataSetError::MissingSnapshot {
|
BacktestError::Data(crate::data::DataSetError::MissingSnapshot {
|
||||||
kind: "factor",
|
kind: "factor",
|
||||||
@@ -9315,6 +9366,7 @@ impl PlatformExprStrategy {
|
|||||||
let free_float_cap_bn = decision_free_float_cap_bn(factor);
|
let free_float_cap_bn = decision_free_float_cap_bn(factor);
|
||||||
rows.push(EligibleUniverseSnapshot {
|
rows.push(EligibleUniverseSnapshot {
|
||||||
symbol: factor.symbol.clone(),
|
symbol: factor.symbol.clone(),
|
||||||
|
symbol_id,
|
||||||
market_cap_bn,
|
market_cap_bn,
|
||||||
free_float_cap_bn,
|
free_float_cap_bn,
|
||||||
});
|
});
|
||||||
@@ -9793,6 +9845,8 @@ impl PlatformExprStrategy {
|
|||||||
universe_factor_date,
|
universe_factor_date,
|
||||||
5,
|
5,
|
||||||
);
|
);
|
||||||
|
let execution_day = ctx.data.daily_snapshot_view(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
|
||||||
// strategy asks for that exact ascending order and does not need a
|
// strategy asks for that exact ascending order and does not need a
|
||||||
@@ -9801,11 +9855,13 @@ impl PlatformExprStrategy {
|
|||||||
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.len()));
|
||||||
for candidate in universe {
|
for candidate in universe {
|
||||||
let stock = self.selection_stock_state_with_factor_date(
|
let stock = self.selection_stock_state_with_factor_date_from_views(
|
||||||
ctx,
|
ctx,
|
||||||
date,
|
date,
|
||||||
stock_factor_date,
|
stock_factor_date,
|
||||||
&candidate.symbol,
|
&candidate,
|
||||||
|
&execution_day,
|
||||||
|
&factor_day,
|
||||||
)?;
|
)?;
|
||||||
let field_value = self.selection_field_value(&candidate, &stock);
|
let field_value = self.selection_field_value(&candidate, &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 {
|
||||||
@@ -9832,11 +9888,13 @@ impl PlatformExprStrategy {
|
|||||||
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();
|
||||||
for candidate in universe {
|
for candidate in universe {
|
||||||
let stock = self.selection_stock_state_with_factor_date(
|
let stock = self.selection_stock_state_with_factor_date_from_views(
|
||||||
ctx,
|
ctx,
|
||||||
date,
|
date,
|
||||||
stock_factor_date,
|
stock_factor_date,
|
||||||
&candidate.symbol,
|
&candidate,
|
||||||
|
&execution_day,
|
||||||
|
&factor_day,
|
||||||
)?;
|
)?;
|
||||||
let field_value = self.selection_field_value(&candidate, &stock);
|
let field_value = self.selection_field_value(&candidate, &stock);
|
||||||
if !field_value.is_finite() {
|
if !field_value.is_finite() {
|
||||||
@@ -10517,13 +10575,17 @@ impl PlatformExprStrategy {
|
|||||||
universe_factor_date,
|
universe_factor_date,
|
||||||
selection_risk_deferral,
|
selection_risk_deferral,
|
||||||
);
|
);
|
||||||
|
let execution_day = ctx.data.daily_snapshot_view(date);
|
||||||
|
let factor_day = ctx.data.daily_snapshot_view(stock_factor_date);
|
||||||
let quote_candidate_limit = self.quote_plan_candidate_limit(selection_limit);
|
let quote_candidate_limit = self.quote_plan_candidate_limit(selection_limit);
|
||||||
for candidate in universe {
|
for candidate in universe {
|
||||||
let stock = self.selection_stock_state_with_factor_date(
|
let stock = self.selection_stock_state_with_factor_date_from_views(
|
||||||
ctx,
|
ctx,
|
||||||
date,
|
date,
|
||||||
stock_factor_date,
|
stock_factor_date,
|
||||||
&candidate.symbol,
|
&candidate,
|
||||||
|
&execution_day,
|
||||||
|
&factor_day,
|
||||||
)?;
|
)?;
|
||||||
let field_value = self.selection_field_value(&candidate, &stock);
|
let field_value = self.selection_field_value(&candidate, &stock);
|
||||||
if !field_value.is_finite() {
|
if !field_value.is_finite() {
|
||||||
@@ -18395,11 +18457,16 @@ mod tests {
|
|||||||
.expect("bjse state");
|
.expect("bjse state");
|
||||||
let star_candidate = EligibleUniverseSnapshot {
|
let star_candidate = EligibleUniverseSnapshot {
|
||||||
symbol: star_st_symbol.to_string(),
|
symbol: star_st_symbol.to_string(),
|
||||||
|
symbol_id: ctx
|
||||||
|
.data
|
||||||
|
.symbol_id(star_st_symbol)
|
||||||
|
.expect("star st symbol id"),
|
||||||
market_cap_bn: 1.0,
|
market_cap_bn: 1.0,
|
||||||
free_float_cap_bn: 1.0,
|
free_float_cap_bn: 1.0,
|
||||||
};
|
};
|
||||||
let bjse_candidate = EligibleUniverseSnapshot {
|
let bjse_candidate = EligibleUniverseSnapshot {
|
||||||
symbol: bjse_symbol.to_string(),
|
symbol: bjse_symbol.to_string(),
|
||||||
|
symbol_id: ctx.data.symbol_id(bjse_symbol).expect("bjse symbol id"),
|
||||||
market_cap_bn: 2.0,
|
market_cap_bn: 2.0,
|
||||||
free_float_cap_bn: 2.0,
|
free_float_cap_bn: 2.0,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user