缓存平台策略表达式元数据
This commit is contained in:
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user