优化回测数据集内存并修复rolling依赖识别
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user