perf: intern stock-state symbols
This commit is contained in:
@@ -1327,6 +1327,7 @@ pub struct DataSet {
|
|||||||
market_series_end_positions_by_symbol_id: Arc<Option<SymbolSeriesEndPositionIndex>>,
|
market_series_end_positions_by_symbol_id: Arc<Option<SymbolSeriesEndPositionIndex>>,
|
||||||
benchmark_series_cache: Arc<BenchmarkPriceSeries>,
|
benchmark_series_cache: Arc<BenchmarkPriceSeries>,
|
||||||
symbol_id_by_code: Arc<AHashMap<String, u32>>,
|
symbol_id_by_code: Arc<AHashMap<String, u32>>,
|
||||||
|
symbol_by_id: Arc<Vec<Arc<str>>>,
|
||||||
eligible_universe_by_date: Arc<OnceLock<BTreeMap<NaiveDate, Vec<EligibleUniverseSnapshot>>>>,
|
eligible_universe_by_date: Arc<OnceLock<BTreeMap<NaiveDate, Vec<EligibleUniverseSnapshot>>>>,
|
||||||
benchmark_code: String,
|
benchmark_code: String,
|
||||||
futures_params_by_symbol: Arc<HashMap<String, Vec<FuturesTradingParameter>>>,
|
futures_params_by_symbol: Arc<HashMap<String, Vec<FuturesTradingParameter>>>,
|
||||||
@@ -1771,6 +1772,10 @@ impl DataSet {
|
|||||||
&factor_by_date,
|
&factor_by_date,
|
||||||
&candidate_by_date,
|
&candidate_by_date,
|
||||||
);
|
);
|
||||||
|
let mut symbol_by_id = vec![Arc::<str>::from(""); symbol_id_by_code.len()];
|
||||||
|
for (symbol, symbol_id) in &symbol_id_by_code {
|
||||||
|
symbol_by_id[*symbol_id as usize] = Arc::<str>::from(symbol.as_str());
|
||||||
|
}
|
||||||
let mut instruments_by_symbol_id = vec![None; symbol_id_by_code.len()];
|
let mut instruments_by_symbol_id = vec![None; symbol_id_by_code.len()];
|
||||||
for (symbol, instrument) in &instruments {
|
for (symbol, instrument) in &instruments {
|
||||||
if let Some(symbol_id) = symbol_id_by_code.get(symbol).copied() {
|
if let Some(symbol_id) = symbol_id_by_code.get(symbol).copied() {
|
||||||
@@ -1859,6 +1864,7 @@ impl DataSet {
|
|||||||
),
|
),
|
||||||
benchmark_series_cache: Arc::new(benchmark_series_cache),
|
benchmark_series_cache: Arc::new(benchmark_series_cache),
|
||||||
symbol_id_by_code: Arc::new(symbol_id_by_code),
|
symbol_id_by_code: Arc::new(symbol_id_by_code),
|
||||||
|
symbol_by_id: Arc::new(symbol_by_id),
|
||||||
eligible_universe_by_date: Arc::new(OnceLock::new()),
|
eligible_universe_by_date: Arc::new(OnceLock::new()),
|
||||||
benchmark_code,
|
benchmark_code,
|
||||||
futures_params_by_symbol: Arc::new(futures_params_by_symbol),
|
futures_params_by_symbol: Arc::new(futures_params_by_symbol),
|
||||||
@@ -1912,6 +1918,11 @@ impl DataSet {
|
|||||||
self.symbol_id_by_code.get(symbol).copied()
|
self.symbol_id_by_code.get(symbol).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn shared_symbol_by_id(&self, symbol_id: u32) -> Option<Arc<str>> {
|
||||||
|
let symbol = self.symbol_by_id.get(symbol_id as usize)?;
|
||||||
|
(!symbol.is_empty()).then(|| Arc::clone(symbol))
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn symbol_count(&self) -> usize {
|
pub(crate) fn symbol_count(&self) -> usize {
|
||||||
self.symbol_id_by_code.len()
|
self.symbol_id_by_code.len()
|
||||||
}
|
}
|
||||||
@@ -5047,6 +5058,10 @@ mod tests {
|
|||||||
.map(|row| row.symbol.as_str()),
|
.map(|row| row.symbol.as_str()),
|
||||||
Some(symbol)
|
Some(symbol)
|
||||||
);
|
);
|
||||||
|
let first_shared = data.shared_symbol_by_id(symbol_id).expect("shared symbol");
|
||||||
|
let second_shared = data.shared_symbol_by_id(symbol_id).expect("shared symbol");
|
||||||
|
assert_eq!(first_shared.as_ref(), symbol);
|
||||||
|
assert!(Arc::ptr_eq(&first_shared, &second_shared));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
data.market_by_symbol_id(date, symbol_id)
|
data.market_by_symbol_id(date, symbol_id)
|
||||||
.map(|row| row.symbol.as_str()),
|
.map(|row| row.symbol.as_str()),
|
||||||
|
|||||||
@@ -668,7 +668,7 @@ struct DayExpressionState {
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct StockExpressionState {
|
struct StockExpressionState {
|
||||||
symbol: String,
|
symbol: Arc<str>,
|
||||||
symbol_id: u32,
|
symbol_id: u32,
|
||||||
market_cap: f64,
|
market_cap: f64,
|
||||||
market_cap_bn: f64,
|
market_cap_bn: f64,
|
||||||
@@ -4012,6 +4012,13 @@ impl PlatformExprStrategy {
|
|||||||
symbol: symbol.to_string(),
|
symbol: symbol.to_string(),
|
||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
let shared_symbol = ctx.data.shared_symbol_by_id(symbol_id).ok_or_else(|| {
|
||||||
|
BacktestError::Data(crate::data::DataSetError::MissingSnapshot {
|
||||||
|
kind: "symbol_reverse_index",
|
||||||
|
date,
|
||||||
|
symbol: symbol.to_string(),
|
||||||
|
})
|
||||||
|
})?;
|
||||||
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) {
|
||||||
@@ -4216,7 +4223,7 @@ impl PlatformExprStrategy {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let state = Arc::new(StockExpressionState {
|
let state = Arc::new(StockExpressionState {
|
||||||
symbol: symbol.to_string(),
|
symbol: shared_symbol,
|
||||||
symbol_id,
|
symbol_id,
|
||||||
market_cap,
|
market_cap,
|
||||||
market_cap_bn,
|
market_cap_bn,
|
||||||
@@ -4685,7 +4692,7 @@ impl PlatformExprStrategy {
|
|||||||
stock.lower_limit,
|
stock.lower_limit,
|
||||||
stock.price_tick,
|
stock.price_tick,
|
||||||
);
|
);
|
||||||
scope.push("symbol", stock.symbol.clone());
|
scope.push("symbol", stock.symbol.to_string());
|
||||||
scope.push("market_cap", stock.market_cap);
|
scope.push("market_cap", stock.market_cap);
|
||||||
scope.push("market_cap_bn", stock.market_cap_bn);
|
scope.push("market_cap_bn", stock.market_cap_bn);
|
||||||
scope.push("free_float_cap", stock.free_float_cap);
|
scope.push("free_float_cap", stock.free_float_cap);
|
||||||
@@ -4784,7 +4791,7 @@ impl PlatformExprStrategy {
|
|||||||
scope.push("volume_ma100", stock.stock_volume_ma100);
|
scope.push("volume_ma100", stock.stock_volume_ma100);
|
||||||
if include_factors_map {
|
if include_factors_map {
|
||||||
let mut factors = Map::new();
|
let mut factors = Map::new();
|
||||||
factors.insert("symbol".into(), Dynamic::from(stock.symbol.clone()));
|
factors.insert("symbol".into(), Dynamic::from(stock.symbol.to_string()));
|
||||||
factors.insert("market_cap".into(), Dynamic::from(stock.market_cap));
|
factors.insert("market_cap".into(), Dynamic::from(stock.market_cap));
|
||||||
factors.insert("market_cap_bn".into(), Dynamic::from(stock.market_cap_bn));
|
factors.insert("market_cap_bn".into(), Dynamic::from(stock.market_cap_bn));
|
||||||
factors.insert("free_float_cap".into(), Dynamic::from(stock.free_float_cap));
|
factors.insert("free_float_cap".into(), Dynamic::from(stock.free_float_cap));
|
||||||
@@ -6841,7 +6848,7 @@ impl PlatformExprStrategy {
|
|||||||
let matched = ctx
|
let matched = ctx
|
||||||
.get_margin_stocks(&margin_type)
|
.get_margin_stocks(&margin_type)
|
||||||
.iter()
|
.iter()
|
||||||
.any(|symbol| symbol == &stock.symbol);
|
.any(|symbol| symbol.as_str() == stock.symbol.as_ref());
|
||||||
Ok(RuntimeHelperResolution::Boolean(matched))
|
Ok(RuntimeHelperResolution::Boolean(matched))
|
||||||
}
|
}
|
||||||
"dominant_future" | "get_dominant_future" => {
|
"dominant_future" | "get_dominant_future" => {
|
||||||
@@ -6911,14 +6918,14 @@ impl PlatformExprStrategy {
|
|||||||
let stock = stock.ok_or_else(|| {
|
let stock = stock.ok_or_else(|| {
|
||||||
BacktestError::Execution(format!("{helper} requires stock context"))
|
BacktestError::Execution(format!("{helper} requires stock context"))
|
||||||
})?;
|
})?;
|
||||||
return Ok((stock.symbol.clone(), default_lookback));
|
return Ok((stock.symbol.to_string(), default_lookback));
|
||||||
}
|
}
|
||||||
if args.len() == 1 {
|
if args.len() == 1 {
|
||||||
if let Ok(lookback) = Self::parse_positive_usize(&args[0]) {
|
if let Ok(lookback) = Self::parse_positive_usize(&args[0]) {
|
||||||
let stock = stock.ok_or_else(|| {
|
let stock = stock.ok_or_else(|| {
|
||||||
BacktestError::Execution(format!("{helper} requires stock context"))
|
BacktestError::Execution(format!("{helper} requires stock context"))
|
||||||
})?;
|
})?;
|
||||||
return Ok((stock.symbol.clone(), lookback));
|
return Ok((stock.symbol.to_string(), lookback));
|
||||||
}
|
}
|
||||||
return Ok((
|
return Ok((
|
||||||
Self::parse_string_or_identifier(&args[0])?,
|
Self::parse_string_or_identifier(&args[0])?,
|
||||||
@@ -12741,6 +12748,7 @@ mod tests {
|
|||||||
.stock_state(&second_context, dates[1], symbol)
|
.stock_state(&second_context, dates[1], symbol)
|
||||||
.expect("second state");
|
.expect("second state");
|
||||||
assert!(!Arc::ptr_eq(&first, &second));
|
assert!(!Arc::ptr_eq(&first, &second));
|
||||||
|
assert!(Arc::ptr_eq(&first.symbol, &second.symbol));
|
||||||
assert_eq!(second.close, 11.0);
|
assert_eq!(second.close, 11.0);
|
||||||
assert_eq!(*strategy.stock_state_cache_date.borrow(), Some(dates[1]));
|
assert_eq!(*strategy.stock_state_cache_date.borrow(), Some(dates[1]));
|
||||||
assert_eq!(strategy.stock_state_cache.borrow().len(), 1);
|
assert_eq!(strategy.stock_state_cache.borrow().len(), 1);
|
||||||
|
|||||||
Reference in New Issue
Block a user