减少滚动窗口重复索引开销
This commit is contained in:
@@ -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> {
|
||||
|
||||
Reference in New Issue
Block a user