复用策略表达式解析计划

This commit is contained in:
boris
2026-08-01 21:44:57 +08:00
parent 6f2c39aaf2
commit d51d324977
+41 -14
View File
@@ -1,5 +1,6 @@
use std::cell::RefCell;
use std::collections::{BTreeMap, BTreeSet, HashMap};
use std::sync::Arc;
use chrono::{Datelike, Duration, NaiveDate, NaiveDateTime, NaiveTime};
use rhai::{AST, Dynamic, Engine, Map, Scope};
@@ -835,6 +836,11 @@ impl<'a> SelectiveExpressionScope<'a> {
}
}
struct ExpressionEvalPlan {
normalized: String,
identifiers: BTreeSet<String>,
}
pub struct PlatformExprStrategy {
config: PlatformExprStrategyConfig,
engine: Engine,
@@ -857,6 +863,7 @@ pub struct PlatformExprStrategy {
/// 命中计数与未命中计数,便于在 unit test 中验证缓存生效;非生产指标。
cache_hits: RefCell<u64>,
cache_misses: RefCell<u64>,
expression_plan_cache: RefCell<HashMap<String, Arc<ExpressionEvalPlan>>>,
normalized_prelude: String,
prelude_identifier_candidates: BTreeSet<String>,
prelude_declared_identifiers: BTreeSet<String>,
@@ -1177,6 +1184,7 @@ impl PlatformExprStrategy {
compiled_cache: RefCell::new(HashMap::new()),
cache_hits: RefCell::new(0),
cache_misses: RefCell::new(0),
expression_plan_cache: RefCell::new(HashMap::new()),
normalized_prelude,
prelude_identifier_candidates,
prelude_declared_identifiers,
@@ -4453,8 +4461,9 @@ impl PlatformExprStrategy {
stock: Option<&StockExpressionState>,
position: Option<&PositionExpressionState>,
) -> Result<Dynamic, BacktestError> {
let normalized_expr = Self::normalize_expr(expr);
let normalized_identifiers = Self::extract_identifier_candidates(&normalized_expr);
let expression_plan = self.expression_eval_plan(expr);
let normalized_expr = expression_plan.normalized.as_str();
let normalized_identifiers = &expression_plan.identifiers;
let prelude_identifiers = &self.prelude_identifier_candidates;
let include_day_factors = normalized_identifiers.contains("day_factors")
|| normalized_identifiers.contains("day_factor")
@@ -4481,27 +4490,30 @@ impl PlatformExprStrategy {
let expanded_expr =
self.expand_runtime_helpers(ctx, day, stock, &normalized_expr, &mut scope)?;
if let Some(item) = stock {
let mut expanded_identifiers = Self::extract_identifier_candidates(&expanded_prelude);
expanded_identifiers.extend(Self::extract_identifier_candidates(&expanded_expr));
for identifier in expanded_identifiers {
let factor_identifiers = normalized_identifiers.iter().chain(
prelude_identifiers
.iter()
.filter(|identifier| !normalized_identifiers.contains(*identifier)),
);
for identifier in factor_identifiers {
if Self::is_reserved_scope_name(identifier.as_str())
|| self.prelude_declared_identifiers.contains(&identifier)
|| (!self.stock_extra_factor_identifiers.contains(&identifier)
&& !item.extra_factors.contains_key(&identifier)
&& !day.available_factor_names.contains(&identifier)
&& !day.available_text_factor_names.contains(&identifier))
|| self.prelude_declared_identifiers.contains(identifier)
|| (!self.stock_extra_factor_identifiers.contains(identifier)
&& !item.extra_factors.contains_key(identifier)
&& !day.available_factor_names.contains(identifier)
&& !day.available_text_factor_names.contains(identifier))
{
continue;
}
if let Some(value) = item.extra_text_factors.get(&identifier) {
scope.push_dynamic(identifier, Dynamic::from(value.clone()));
if let Some(value) = item.extra_text_factors.get(identifier) {
scope.push_dynamic(identifier.clone(), Dynamic::from(value.clone()));
} else {
let value = item
.extra_factors
.get(&identifier)
.get(identifier)
.copied()
.unwrap_or(f64::NAN);
scope.push_dynamic(identifier, Dynamic::from(value));
scope.push_dynamic(identifier.clone(), Dynamic::from(value));
}
}
}
@@ -4514,6 +4526,21 @@ impl PlatformExprStrategy {
self.eval_with_cache(&mut scope, &script)
}
fn expression_eval_plan(&self, expr: &str) -> Arc<ExpressionEvalPlan> {
if let Some(plan) = self.expression_plan_cache.borrow().get(expr).cloned() {
return plan;
}
let normalized = Self::normalize_expr(expr);
let plan = Arc::new(ExpressionEvalPlan {
identifiers: Self::extract_identifier_candidates(&normalized),
normalized,
});
self.expression_plan_cache
.borrow_mut()
.insert(expr.to_string(), Arc::clone(&plan));
plan
}
fn normalize_expr(expr: &str) -> String {
let expr = Self::normalize_runtime_field_aliases(expr.trim());
let expr = Self::normalize_python_numeric_division(&expr);