缓存平台策略表达式元数据
This commit is contained in:
@@ -511,6 +511,12 @@ pub struct PlatformExprStrategy {
|
|||||||
/// 命中计数与未命中计数,便于在 unit test 中验证缓存生效;非生产指标。
|
/// 命中计数与未命中计数,便于在 unit test 中验证缓存生效;非生产指标。
|
||||||
cache_hits: RefCell<u64>,
|
cache_hits: RefCell<u64>,
|
||||||
cache_misses: 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)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
@@ -589,6 +595,15 @@ impl PlatformExprStrategy {
|
|||||||
engine.register_fn("trim", |value: &str| value.trim().to_string());
|
engine.register_fn("trim", |value: &str| value.trim().to_string());
|
||||||
engine.register_fn("strlen", |value: &str| value.chars().count() as i64);
|
engine.register_fn("strlen", |value: &str| value.chars().count() as i64);
|
||||||
engine.register_fn("code_number", code_number_value);
|
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 {
|
Self {
|
||||||
config,
|
config,
|
||||||
engine,
|
engine,
|
||||||
@@ -599,6 +614,12 @@ impl PlatformExprStrategy {
|
|||||||
compiled_cache: RefCell::new(HashMap::new()),
|
compiled_cache: RefCell::new(HashMap::new()),
|
||||||
cache_hits: RefCell::new(0),
|
cache_hits: RefCell::new(0),
|
||||||
cache_misses: 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>,
|
position: Option<&PositionExpressionState>,
|
||||||
) -> Result<Dynamic, BacktestError> {
|
) -> Result<Dynamic, BacktestError> {
|
||||||
let normalized_expr = Self::normalize_expr(expr);
|
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 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")
|
let include_day_factors = normalized_identifiers.contains("day_factors")
|
||||||
|| normalized_identifiers.contains("day_factor")
|
|| normalized_identifiers.contains("day_factor")
|
||||||
|| prelude_identifiers.contains("day_factors");
|
|| prelude_identifiers.contains("day_factors");
|
||||||
@@ -3046,11 +3066,10 @@ impl PlatformExprStrategy {
|
|||||||
);
|
);
|
||||||
let expanded_expr =
|
let expanded_expr =
|
||||||
self.expand_runtime_helpers(ctx, day, stock, &normalized_expr, &mut scope)?;
|
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 {
|
if let Some(item) = stock {
|
||||||
for identifier in Self::extract_identifier_candidates(&expanded_expr) {
|
for identifier in Self::extract_identifier_candidates(&expanded_expr) {
|
||||||
if Self::is_reserved_scope_name(identifier.as_str())
|
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_factor_names.contains(&identifier)
|
||||||
&& !day.available_text_factor_names.contains(&identifier))
|
&& !day.available_text_factor_names.contains(&identifier))
|
||||||
{
|
{
|
||||||
@@ -3064,11 +3083,11 @@ impl PlatformExprStrategy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut script_parts = Vec::new();
|
let mut script_parts = Vec::with_capacity(2);
|
||||||
if !prelude.trim().is_empty() {
|
if !self.normalized_prelude.trim().is_empty() {
|
||||||
script_parts.push(prelude);
|
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");
|
let script = script_parts.join("\n");
|
||||||
self.eval_with_cache(&mut scope, &script)
|
self.eval_with_cache(&mut scope, &script)
|
||||||
}
|
}
|
||||||
@@ -3114,7 +3133,12 @@ impl PlatformExprStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn prelude_numeric_constant(&self, name: &str) -> Option<f64> {
|
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 trimmed = line.trim();
|
||||||
let Some(body) = trimmed.strip_prefix("let ") else {
|
let Some(body) = trimmed.strip_prefix("let ") else {
|
||||||
continue;
|
continue;
|
||||||
@@ -3122,15 +3146,12 @@ impl PlatformExprStrategy {
|
|||||||
let Some((lhs, rhs)) = body.split_once('=') else {
|
let Some((lhs, rhs)) = body.split_once('=') else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if lhs.trim() != name {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let rhs = rhs.trim().trim_end_matches(';').trim();
|
let rhs = rhs.trim().trim_end_matches(';').trim();
|
||||||
if let Ok(value) = rhs.parse::<f64>() {
|
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 {
|
fn normalize_prelude_for_eval(prelude: &str) -> String {
|
||||||
@@ -5642,7 +5663,7 @@ impl PlatformExprStrategy {
|
|||||||
_day: &DayExpressionState,
|
_day: &DayExpressionState,
|
||||||
stock: &StockExpressionState,
|
stock: &StockExpressionState,
|
||||||
) -> Option<bool> {
|
) -> 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);
|
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" {
|
if compact == "stock_ma_short>stock_ma_mid*ma_ratio&&stock_ma_mid>stock_ma_long" {
|
||||||
return Some(
|
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 =
|
let requires_min_listed_days =
|
||||||
if let Some(rest) = filter_body.strip_prefix("listed_days>=min_listed_days&&") {
|
if let Some(rest) = filter_body.strip_prefix("listed_days>=min_listed_days&&") {
|
||||||
filter_body = rest;
|
filter_body = rest;
|
||||||
@@ -6157,7 +6178,10 @@ impl PlatformExprStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn stock_filter_quote_usage(&self) -> StockFilterQuoteUsage {
|
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;
|
let mut usage = StockFilterQuoteUsage::DailyOnly;
|
||||||
for name in Self::extract_identifier_candidates(&expr) {
|
for name in Self::extract_identifier_candidates(&expr) {
|
||||||
match name.as_str() {
|
match name.as_str() {
|
||||||
|
|||||||
Reference in New Issue
Block a user