缓存平台策略表达式元数据

This commit is contained in:
boris
2026-06-21 00:20:56 +08:00
parent beebc5fa58
commit 0f982887a3
+41 -17
View File
@@ -511,6 +511,12 @@ pub struct PlatformExprStrategy {
/// 命中计数与未命中计数,便于在 unit test 中验证缓存生效;非生产指标。
cache_hits: RefCell<u64>,
cache_misses: RefCell<u64>,
normalized_prelude: String,
prelude_identifier_candidates: BTreeSet<String>,
prelude_declared_identifiers: BTreeSet<String>,
prelude_numeric_constants: HashMap<String, f64>,
compact_stock_filter_expr: String,
stock_filter_quote_usage: StockFilterQuoteUsage,
}
#[derive(Debug, Clone, PartialEq)]
@@ -589,6 +595,15 @@ impl PlatformExprStrategy {
engine.register_fn("trim", |value: &str| value.trim().to_string());
engine.register_fn("strlen", |value: &str| value.chars().count() as i64);
engine.register_fn("code_number", code_number_value);
let normalized_prelude = Self::normalize_prelude_for_eval(&config.prelude);
let prelude_identifier_candidates =
Self::extract_identifier_candidates(&normalized_prelude);
let prelude_declared_identifiers = Self::declared_prelude_identifiers(&config.prelude);
let prelude_numeric_constants = Self::parse_prelude_numeric_constants(&normalized_prelude);
let normalized_stock_filter_expr = Self::normalize_expr(&config.stock_filter_expr);
let compact_stock_filter_expr = Self::compact_expr(&normalized_stock_filter_expr);
let stock_filter_quote_usage =
Self::stock_filter_quote_usage_for_expr(&normalized_stock_filter_expr);
Self {
config,
engine,
@@ -599,6 +614,12 @@ impl PlatformExprStrategy {
compiled_cache: RefCell::new(HashMap::new()),
cache_hits: RefCell::new(0),
cache_misses: RefCell::new(0),
normalized_prelude,
prelude_identifier_candidates,
prelude_declared_identifiers,
prelude_numeric_constants,
compact_stock_filter_expr,
stock_filter_quote_usage,
}
}
@@ -3024,9 +3045,8 @@ impl PlatformExprStrategy {
position: Option<&PositionExpressionState>,
) -> Result<Dynamic, BacktestError> {
let normalized_expr = Self::normalize_expr(expr);
let prelude = Self::normalize_prelude_for_eval(&self.config.prelude);
let normalized_identifiers = Self::extract_identifier_candidates(&normalized_expr);
let prelude_identifiers = Self::extract_identifier_candidates(&prelude);
let prelude_identifiers = &self.prelude_identifier_candidates;
let include_day_factors = normalized_identifiers.contains("day_factors")
|| normalized_identifiers.contains("day_factor")
|| prelude_identifiers.contains("day_factors");
@@ -3046,11 +3066,10 @@ impl PlatformExprStrategy {
);
let expanded_expr =
self.expand_runtime_helpers(ctx, day, stock, &normalized_expr, &mut scope)?;
let prelude_declared_identifiers = Self::declared_prelude_identifiers(&self.config.prelude);
if let Some(item) = stock {
for identifier in Self::extract_identifier_candidates(&expanded_expr) {
if Self::is_reserved_scope_name(identifier.as_str())
|| prelude_declared_identifiers.contains(&identifier)
|| self.prelude_declared_identifiers.contains(&identifier)
|| (!day.available_factor_names.contains(&identifier)
&& !day.available_text_factor_names.contains(&identifier))
{
@@ -3064,11 +3083,11 @@ impl PlatformExprStrategy {
}
}
}
let mut script_parts = Vec::new();
if !prelude.trim().is_empty() {
script_parts.push(prelude);
let mut script_parts = Vec::with_capacity(2);
if !self.normalized_prelude.trim().is_empty() {
script_parts.push(self.normalized_prelude.as_str());
}
script_parts.push(expanded_expr);
script_parts.push(expanded_expr.as_str());
let script = script_parts.join("\n");
self.eval_with_cache(&mut scope, &script)
}
@@ -3114,7 +3133,12 @@ impl PlatformExprStrategy {
}
fn prelude_numeric_constant(&self, name: &str) -> Option<f64> {
for line in Self::normalize_prelude_for_eval(&self.config.prelude).lines() {
self.prelude_numeric_constants.get(name).copied()
}
fn parse_prelude_numeric_constants(normalized_prelude: &str) -> HashMap<String, f64> {
let mut constants = HashMap::new();
for line in normalized_prelude.lines() {
let trimmed = line.trim();
let Some(body) = trimmed.strip_prefix("let ") else {
continue;
@@ -3122,15 +3146,12 @@ impl PlatformExprStrategy {
let Some((lhs, rhs)) = body.split_once('=') else {
continue;
};
if lhs.trim() != name {
continue;
}
let rhs = rhs.trim().trim_end_matches(';').trim();
if let Ok(value) = rhs.parse::<f64>() {
return Some(value);
constants.insert(lhs.trim().to_string(), value);
}
}
None
constants
}
fn normalize_prelude_for_eval(prelude: &str) -> String {
@@ -5642,7 +5663,7 @@ impl PlatformExprStrategy {
_day: &DayExpressionState,
stock: &StockExpressionState,
) -> Option<bool> {
let compact = Self::compact_expr(&Self::normalize_expr(&self.config.stock_filter_expr));
let compact = self.compact_stock_filter_expr.as_str();
let ma_ratio = self.prelude_numeric_constant("ma_ratio").unwrap_or(1.0);
if compact == "stock_ma_short>stock_ma_mid*ma_ratio&&stock_ma_mid>stock_ma_long" {
return Some(
@@ -5654,7 +5675,7 @@ impl PlatformExprStrategy {
);
}
let mut filter_body = compact.as_str();
let mut filter_body = compact;
let requires_min_listed_days =
if let Some(rest) = filter_body.strip_prefix("listed_days>=min_listed_days&&") {
filter_body = rest;
@@ -6157,7 +6178,10 @@ impl PlatformExprStrategy {
}
fn stock_filter_quote_usage(&self) -> StockFilterQuoteUsage {
let expr = Self::normalize_runtime_field_aliases(&self.config.stock_filter_expr);
self.stock_filter_quote_usage
}
fn stock_filter_quote_usage_for_expr(expr: &str) -> StockFilterQuoteUsage {
let mut usage = StockFilterQuoteUsage::DailyOnly;
for name in Self::extract_identifier_candidates(&expr) {
match name.as_str() {