减少滚动窗口重复索引开销

This commit is contained in:
boris
2026-08-24 03:36:07 +08:00
parent 2013314e4f
commit ea79fdae46
2 changed files with 57 additions and 104 deletions
+38 -76
View File
@@ -1,3 +1,4 @@
use std::borrow::Cow;
use std::collections::{BTreeMap, HashMap, HashSet};
use std::sync::{Arc, OnceLock};
@@ -959,7 +960,7 @@ impl SymbolPriceSeries {
}
fn numeric_value_at(&self, index: usize, field: &str) -> Option<f64> {
match normalize_field(field).as_str() {
match normalized_field(field).as_ref() {
"day_open" | "dayopen" => Some(self.day_opens[index]),
"open" => Some(self.opens[index]),
"high" => Some(self.highs[index]),
@@ -1405,12 +1406,14 @@ impl DataSet {
.and_then(|rows| find_arc_by_symbol(rows, symbol, |row| row.symbol.as_str()))
}
fn market_series(&self, symbol: &str) -> Option<Arc<SymbolPriceSeries>> {
self.market_series_by_symbol.get(symbol).cloned()
fn market_series(&self, symbol: &str) -> Option<&SymbolPriceSeries> {
self.market_series_by_symbol.get(symbol).map(Arc::as_ref)
}
fn adjusted_close_series(&self, symbol: &str) -> Option<Arc<AdjustedCloseSeries>> {
self.adjusted_close_series_by_symbol.get(symbol).cloned()
fn adjusted_close_series(&self, symbol: &str) -> Option<&AdjustedCloseSeries> {
self.adjusted_close_series_by_symbol
.get(symbol)
.map(Arc::as_ref)
}
pub fn factor(&self, date: NaiveDate, symbol: &str) -> Option<&DailyFactorSnapshot> {
@@ -2452,19 +2455,14 @@ impl DataSet {
field: &str,
lookback: usize,
) -> Option<f64> {
let field = normalize_field(field);
match field.as_str() {
let field = normalized_field(field);
match field.as_ref() {
"close" | "prev_close" | "stock_close" | "price" => self
.adjusted_close_series(symbol)
.and_then(|series| series.decision_moving_average(date, lookback)),
"volume" | "stock_volume" => {
if !self.source_daily_volume_window_available(date, symbol, lookback, false) {
None
} else {
self.market_series(symbol)
.and_then(|series| series.decision_volume_moving_average(date, lookback))
}
}
"volume" | "stock_volume" => self
.market_series(symbol)
.and_then(|series| series.decision_volume_moving_average(date, lookback)),
"day_open" | "dayopen" => {
self.market_moving_average(date, symbol, lookback, PriceField::DayOpen)
}
@@ -2483,22 +2481,14 @@ impl DataSet {
field: &str,
lookback: usize,
) -> Option<f64> {
let field = normalize_field(field);
match field.as_str() {
let field = normalized_field(field);
match field.as_ref() {
"close" | "prev_close" | "stock_close" | "price" => self
.adjusted_close_series(symbol)
.and_then(|series| series.current_moving_average(date, lookback)),
"volume" | "stock_volume" => {
if !self.source_daily_volume_window_available(date, symbol, lookback, true) {
None
} else {
self.market_series(symbol)
.and_then(|series| series.current_volume_moving_average(date, lookback))
.or_else(|| {
self.factor_moving_average(date, symbol, "daily_volume", lookback)
})
}
}
"volume" | "stock_volume" => self
.market_series(symbol)
.and_then(|series| series.current_volume_moving_average(date, lookback)),
"day_open" | "dayopen" => {
self.market_moving_average(date, symbol, lookback, PriceField::DayOpen)
}
@@ -2525,21 +2515,16 @@ impl DataSet {
if lookback == 0 {
return Vec::new();
}
let field = normalize_field(field);
match field.as_str() {
let field = normalized_field(field);
match field.as_ref() {
"close" | "prev_close" | "stock_close" | "price" => self
.adjusted_close_series(symbol)
.map(|series| series.values(date, lookback, false))
.unwrap_or_default(),
"volume" | "stock_volume" => {
if !self.source_daily_volume_window_available(date, symbol, lookback, false) {
Vec::new()
} else {
self.market_series(symbol)
.and_then(|series| series.decision_volume_values(date, lookback))
.unwrap_or_default()
}
}
"volume" | "stock_volume" => self
.market_series(symbol)
.and_then(|series| series.decision_volume_values(date, lookback))
.unwrap_or_default(),
"day_open" | "dayopen" => self
.market_series(symbol)
.map(|series| series.trailing_values(date, lookback, PriceField::DayOpen))
@@ -2563,14 +2548,9 @@ impl DataSet {
field: &str,
lookback: usize,
) -> Vec<f64> {
let field = normalize_field(field);
if matches!(field.as_str(), "volume" | "stock_volume")
&& !self.source_daily_volume_window_available(date, symbol, lookback, true)
{
return Vec::new();
}
let field = normalized_field(field);
if matches!(
field.as_str(),
field.as_ref(),
"close" | "prev_close" | "stock_close" | "price"
) {
return self
@@ -2578,40 +2558,17 @@ impl DataSet {
.map(|series| series.values(date, lookback, true))
.unwrap_or_default();
}
if matches!(field.as_str(), "volume" | "stock_volume") {
if matches!(field.as_ref(), "volume" | "stock_volume") {
return self
.market_series(symbol)
.and_then(|series| series.current_volume_values(date, lookback))
.unwrap_or_default();
}
self.market_series(symbol)
.map(|series| series.trailing_numeric_values(date, lookback, &field, true))
.map(|series| series.trailing_numeric_values(date, lookback, field.as_ref(), true))
.unwrap_or_default()
}
fn source_daily_volume_window_available(
&self,
date: NaiveDate,
symbol: &str,
lookback: usize,
include_now: bool,
) -> bool {
if lookback == 0 {
return false;
}
let Some(series) = self.market_series(symbol) else {
return false;
};
let end_index = if include_now {
series.end_index(date)
} else {
series.previous_completed_end_index(date)
};
end_index
.and_then(|end| series.valid_volume_window(end, lookback))
.is_some()
}
pub fn factor_numeric_values(
&self,
date: NaiveDate,
@@ -3034,11 +2991,16 @@ fn intraday_quote_price_bar(snapshot: &IntradayExecutionQuote) -> PriceBar {
}
fn normalize_field(field: &str) -> String {
field
.trim()
.trim_matches('"')
.trim_matches('\'')
.to_ascii_lowercase()
normalized_field(field).into_owned()
}
fn normalized_field(field: &str) -> Cow<'_, str> {
let trimmed = field.trim().trim_matches('"').trim_matches('\'');
if trimmed.bytes().all(|byte| !byte.is_ascii_uppercase()) {
Cow::Borrowed(trimmed)
} else {
Cow::Owned(trimmed.to_ascii_lowercase())
}
}
fn normalize_factor_snapshots(factors: Vec<DailyFactorSnapshot>) -> Vec<DailyFactorSnapshot> {