Compare commits

..

1 Commits

Author SHA1 Message Date
boris 2f62d82420 优化回测数据集内存并修复rolling依赖识别 2026-06-21 03:31:45 +08:00
2 changed files with 106 additions and 10 deletions
+15 -10
View File
@@ -1,7 +1,7 @@
use std::collections::{BTreeMap, HashMap, HashSet}; use std::collections::{BTreeMap, HashMap, HashSet};
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::{Arc, OnceLock};
use chrono::{NaiveDate, NaiveDateTime}; use chrono::{NaiveDate, NaiveDateTime};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -997,7 +997,7 @@ pub struct DataSet {
benchmark_by_date: BTreeMap<NaiveDate, BenchmarkSnapshot>, benchmark_by_date: BTreeMap<NaiveDate, BenchmarkSnapshot>,
market_series_by_symbol: HashMap<String, SymbolPriceSeries>, market_series_by_symbol: HashMap<String, SymbolPriceSeries>,
benchmark_series_cache: BenchmarkPriceSeries, benchmark_series_cache: BenchmarkPriceSeries,
eligible_universe_by_date: BTreeMap<NaiveDate, Vec<EligibleUniverseSnapshot>>, eligible_universe_by_date: Arc<OnceLock<BTreeMap<NaiveDate, Vec<EligibleUniverseSnapshot>>>>,
benchmark_code: String, benchmark_code: String,
futures_params_by_symbol: HashMap<String, Vec<FuturesTradingParameter>>, futures_params_by_symbol: HashMap<String, Vec<FuturesTradingParameter>>,
} }
@@ -1303,12 +1303,6 @@ impl DataSet {
let market_series_by_symbol = build_market_series(&market_by_date); let market_series_by_symbol = build_market_series(&market_by_date);
let benchmark_series_cache = let benchmark_series_cache =
BenchmarkPriceSeries::new(&benchmark_by_date.values().cloned().collect::<Vec<_>>()); BenchmarkPriceSeries::new(&benchmark_by_date.values().cloned().collect::<Vec<_>>());
let eligible_universe_by_date = build_eligible_universe(
&factor_by_date,
&candidate_index,
&market_index,
&instruments,
);
let futures_params_by_symbol = build_futures_params_index(futures_params); let futures_params_by_symbol = build_futures_params_index(futures_params);
Ok(Self { Ok(Self {
@@ -1328,7 +1322,7 @@ impl DataSet {
benchmark_by_date, benchmark_by_date,
market_series_by_symbol, market_series_by_symbol,
benchmark_series_cache, benchmark_series_cache,
eligible_universe_by_date, eligible_universe_by_date: Arc::new(OnceLock::new()),
benchmark_code, benchmark_code,
futures_params_by_symbol, futures_params_by_symbol,
}) })
@@ -2625,6 +2619,14 @@ 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.candidate_index,
&self.market_index,
&self.instruments,
)
})
.get(&date) .get(&date)
.map(Vec::as_slice) .map(Vec::as_slice)
.unwrap_or(&[]) .unwrap_or(&[])
@@ -3595,7 +3597,10 @@ fn build_market_series(
let mut grouped = HashMap::<String, Vec<&DailyMarketSnapshot>>::new(); let mut grouped = HashMap::<String, Vec<&DailyMarketSnapshot>>::new();
for rows in market_by_date.values() { for rows in market_by_date.values() {
for row in rows { for row in rows {
grouped.entry(row.symbol.clone()).or_default().push(row.as_ref()); grouped
.entry(row.symbol.clone())
.or_default()
.push(row.as_ref());
} }
} }
@@ -6374,6 +6374,7 @@ impl PlatformExprStrategy {
return requirements; return requirements;
} }
Self::require_stock_rollings_for_identifiers(&mut requirements, config, &normalized); Self::require_stock_rollings_for_identifiers(&mut requirements, config, &normalized);
Self::require_stock_rollings_for_helper_calls(&mut requirements, &normalized);
} }
Self::require_stock_rollings_for_fast_filter( Self::require_stock_rollings_for_fast_filter(
&mut requirements, &mut requirements,
@@ -6612,6 +6613,96 @@ impl PlatformExprStrategy {
} }
} }
fn require_stock_rollings_for_helper_calls(
requirements: &mut StockRollingRequirements,
expr: &str,
) {
let compact = Self::compact_expr(expr);
Self::require_stock_rollings_for_named_helper(requirements, &compact, "rolling_mean");
Self::require_stock_rollings_for_named_helper(requirements, &compact, "sma");
Self::require_stock_rollings_for_named_helper(requirements, &compact, "ma");
Self::require_stock_rollings_for_vma_helper(requirements, &compact);
}
fn require_stock_rollings_for_named_helper(
requirements: &mut StockRollingRequirements,
compact: &str,
helper: &str,
) {
let needle = format!("{helper}(");
let mut offset = 0usize;
while let Some(relative) = compact[offset..].find(&needle) {
let args_start = offset + relative + needle.len();
let args = &compact[args_start..];
let Some((field, rest)) = Self::parse_quoted_arg(args) else {
offset = args_start;
continue;
};
let Some(rest) = rest.strip_prefix(',') else {
offset = args_start;
continue;
};
let Some(lookback) = Self::parse_usize_prefix(rest) else {
offset = args_start;
continue;
};
let normalized_field = field.trim().to_ascii_lowercase();
match normalized_field.as_str() {
"close" | "prev_close" | "stock_close" | "price" => {
requirements.require(StockRollingField::Close, lookback)
}
"volume" | "stock_volume" => {
requirements.require(StockRollingField::Volume, lookback)
}
_ => {}
}
offset = args_start;
}
}
fn require_stock_rollings_for_vma_helper(
requirements: &mut StockRollingRequirements,
compact: &str,
) {
let needle = "vma(";
let mut offset = 0usize;
while let Some(relative) = compact[offset..].find(needle) {
let args_start = offset + relative + needle.len();
if let Some(lookback) = Self::parse_usize_prefix(&compact[args_start..]) {
requirements.require(StockRollingField::Volume, lookback);
}
offset = args_start;
}
}
fn parse_quoted_arg(input: &str) -> Option<(String, &str)> {
let mut chars = input.char_indices();
let (_, quote) = chars.next()?;
if quote != '"' && quote != '\'' {
return None;
}
for (idx, ch) in chars {
if ch == quote {
let value = input[1..idx].to_string();
let rest = &input[idx + ch.len_utf8()..];
return Some((value, rest));
}
}
None
}
fn parse_usize_prefix(input: &str) -> Option<usize> {
let end = input
.char_indices()
.take_while(|(_, ch)| ch.is_ascii_digit())
.map(|(idx, ch)| idx + ch.len_utf8())
.last()?;
input[..end]
.parse::<usize>()
.ok()
.filter(|value| *value > 0)
}
fn require_stock_rollings_for_fast_filter( fn require_stock_rollings_for_fast_filter(
requirements: &mut StockRollingRequirements, requirements: &mut StockRollingRequirements,
config: &PlatformExprStrategyConfig, config: &PlatformExprStrategyConfig,