perf: avoid caching transient selection states

This commit is contained in:
boris
2026-09-05 04:59:50 +08:00
parent 0af4cd7f68
commit 29faf7932e
+77 -22
View File
@@ -4008,7 +4008,7 @@ impl PlatformExprStrategy {
}
#[allow(clippy::too_many_arguments)]
fn selection_stock_state_with_factor_date_from_views_by_symbol_id<'a>(
fn uncached_selection_stock_state_from_views_by_symbol_id<'a>(
&self,
ctx: &StrategyContext<'a>,
date: NaiveDate,
@@ -4017,14 +4017,15 @@ impl PlatformExprStrategy {
symbol: &str,
execution_day: &DailySnapshotView<'a>,
factor_day: &DailySnapshotView<'a>,
) -> Result<Arc<StockExpressionState>, BacktestError> {
) -> Result<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(
let calendar_index = self.prepare_stock_state_cache_date(ctx, date);
self.build_stock_state_with_factor_date_and_time_from_source_by_symbol_id(
ctx,
date,
factor_date,
@@ -4032,6 +4033,7 @@ impl PlatformExprStrategy {
symbol,
None,
use_intraday_quote,
calendar_index,
&source,
)
}
@@ -4202,6 +4204,61 @@ impl PlatformExprStrategy {
use_intraday_quote: bool,
source: &S,
) -> Result<Arc<StockExpressionState>, BacktestError>
where
S: StockStateSnapshotSource<'a>,
{
let calendar_index = self.prepare_stock_state_cache_date(ctx, date);
let cache_key = (factor_date, symbol_id, execution_time, use_intraday_quote);
if let Some(state) = self.stock_state_cache.borrow().get(&cache_key) {
return Ok(Arc::clone(state));
}
let state = Arc::new(
self.build_stock_state_with_factor_date_and_time_from_source_by_symbol_id(
ctx,
date,
factor_date,
symbol_id,
symbol,
execution_time,
use_intraday_quote,
calendar_index,
source,
)?,
);
self.stock_state_cache
.borrow_mut()
.insert(cache_key, Arc::clone(&state));
Ok(state)
}
fn prepare_stock_state_cache_date(
&self,
ctx: &StrategyContext<'_>,
date: NaiveDate,
) -> Option<usize> {
let mut cache_date = self.stock_state_cache_date.borrow_mut();
if *cache_date != Some(date) {
self.stock_state_cache.borrow_mut().clear();
*cache_date = Some(date);
*self.stock_state_cache_calendar_index.borrow_mut() = ctx.data.calendar_index(date);
}
*self.stock_state_cache_calendar_index.borrow()
}
#[allow(clippy::too_many_arguments)]
fn build_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,
calendar_index: Option<usize>,
source: &S,
) -> Result<StockExpressionState, BacktestError>
where
S: StockStateSnapshotSource<'a>,
{
@@ -4212,19 +4269,6 @@ impl PlatformExprStrategy {
symbol: symbol.to_string(),
})
})?;
let calendar_index = {
let mut cache_date = self.stock_state_cache_date.borrow_mut();
if *cache_date != Some(date) {
self.stock_state_cache.borrow_mut().clear();
*cache_date = Some(date);
*self.stock_state_cache_calendar_index.borrow_mut() = ctx.data.calendar_index(date);
}
*self.stock_state_cache_calendar_index.borrow()
};
let cache_key = (factor_date, symbol_id, execution_time, use_intraday_quote);
if let Some(state) = self.stock_state_cache.borrow().get(&cache_key) {
return Ok(Arc::clone(state));
}
let market = source.execution_market(symbol_id).ok_or_else(|| {
BacktestError::Data(crate::data::DataSetError::MissingSnapshot {
@@ -4406,7 +4450,7 @@ impl PlatformExprStrategy {
BTreeMap::new()
};
let state = Arc::new(StockExpressionState {
let state = StockExpressionState {
symbol: shared_symbol,
symbol_id,
market_cap,
@@ -4483,10 +4527,7 @@ impl PlatformExprStrategy {
} else {
BTreeMap::new()
},
});
self.stock_state_cache
.borrow_mut()
.insert(cache_key, Arc::clone(&state));
};
Ok(state)
}
@@ -10115,7 +10156,7 @@ impl PlatformExprStrategy {
.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(
let stock = self.uncached_selection_stock_state_from_views_by_symbol_id(
ctx,
date,
stock_factor_date,
@@ -13101,6 +13142,20 @@ mod tests {
};
let strategy = PlatformExprStrategy::new(PlatformExprStrategyConfig::microcap_rotation());
let first_context = context(dates[0], 0);
let first_day = data.daily_snapshot_view(dates[0]);
let uncached = strategy
.uncached_selection_stock_state_from_views_by_symbol_id(
&first_context,
dates[0],
dates[0],
data.symbol_id(symbol).unwrap(),
symbol,
&first_day,
&first_day,
)
.expect("uncached first state");
assert_eq!(uncached.close, 10.0);
assert!(strategy.stock_state_cache.borrow().is_empty());
let first = strategy
.stock_state(&first_context, dates[0], symbol)
.expect("first state");