减少运行时辅助函数参数分配

This commit is contained in:
boris
2026-08-29 21:03:42 +08:00
parent ccac2a2399
commit cdc60f616f
+37 -8
View File
@@ -6158,7 +6158,7 @@ impl PlatformExprStrategy {
) -> Result<RuntimeHelperResolution, BacktestError> { ) -> Result<RuntimeHelperResolution, BacktestError> {
match helper { match helper {
"factor" => { "factor" => {
let key = Self::normalize_runtime_factor_key(&Self::parse_string_or_identifier( let key = Self::normalize_runtime_factor_key(Self::parse_string_or_identifier_ref(
args.first().map(String::as_str).unwrap_or_default(), args.first().map(String::as_str).unwrap_or_default(),
)?); )?);
if let Some((field, lookback)) = framework_stock_rolling_factor_requirement(&key) { if let Some((field, lookback)) = framework_stock_rolling_factor_requirement(&key) {
@@ -6190,7 +6190,7 @@ impl PlatformExprStrategy {
))) )))
} }
"day_factor" => { "day_factor" => {
let key = Self::normalize_runtime_factor_key(&Self::parse_string_or_identifier( let key = Self::normalize_runtime_factor_key(Self::parse_string_or_identifier_ref(
args.first().map(String::as_str).unwrap_or_default(), args.first().map(String::as_str).unwrap_or_default(),
)?); )?);
Ok(RuntimeHelperResolution::Expression(format!( Ok(RuntimeHelperResolution::Expression(format!(
@@ -6204,7 +6204,7 @@ impl PlatformExprStrategy {
"{helper} expects 2 arguments" "{helper} expects 2 arguments"
))); )));
} }
let field = Self::parse_string_or_identifier(&args[0])?; let field = Self::parse_string_or_identifier_ref(&args[0])?;
let lookback = Self::parse_positive_usize(&args[1])?; let lookback = Self::parse_positive_usize(&args[1])?;
let value = self.resolve_rolling_mean(ctx, day, stock, &field, lookback)?; let value = self.resolve_rolling_mean(ctx, day, stock, &field, lookback)?;
Ok(RuntimeHelperResolution::Number(value)) Ok(RuntimeHelperResolution::Number(value))
@@ -6215,7 +6215,7 @@ impl PlatformExprStrategy {
"rolling_mean_current expects 2 arguments".to_string(), "rolling_mean_current expects 2 arguments".to_string(),
)); ));
} }
let field = Self::parse_string_or_identifier(&args[0])?; let field = Self::parse_string_or_identifier_ref(&args[0])?;
let lookback = Self::parse_positive_usize(&args[1])?; let lookback = Self::parse_positive_usize(&args[1])?;
let value = self.resolve_current_rolling_mean(ctx, day, stock, &field, lookback)?; let value = self.resolve_current_rolling_mean(ctx, day, stock, &field, lookback)?;
Ok(RuntimeHelperResolution::Number(value)) Ok(RuntimeHelperResolution::Number(value))
@@ -6226,7 +6226,7 @@ impl PlatformExprStrategy {
"rolling_max_current expects field and lookback".to_string(), "rolling_max_current expects field and lookback".to_string(),
)); ));
} }
let field = Self::parse_string_or_identifier(&args[0])?; let field = Self::parse_string_or_identifier_ref(&args[0])?;
let lookback = Self::parse_positive_usize(&args[1])?; let lookback = Self::parse_positive_usize(&args[1])?;
let values = let values =
self.resolve_current_rolling_values(ctx, day, stock, &field, lookback)?; self.resolve_current_rolling_values(ctx, day, stock, &field, lookback)?;
@@ -6239,7 +6239,7 @@ impl PlatformExprStrategy {
"rolling_return_stddev_current expects field and return count".to_string(), "rolling_return_stddev_current expects field and return count".to_string(),
)); ));
} }
let field = Self::parse_string_or_identifier(&args[0])?; let field = Self::parse_string_or_identifier_ref(&args[0])?;
let return_count = Self::parse_positive_usize(&args[1])?; let return_count = Self::parse_positive_usize(&args[1])?;
let values = self.resolve_current_rolling_values( let values = self.resolve_current_rolling_values(
ctx, ctx,
@@ -6278,7 +6278,7 @@ impl PlatformExprStrategy {
"{helper} expects field and lookback" "{helper} expects field and lookback"
))); )));
} }
let field = Self::parse_string_or_identifier(&args[0])?; let field = Self::parse_string_or_identifier_ref(&args[0])?;
let lookback = Self::parse_positive_usize(&args[1])?; let lookback = Self::parse_positive_usize(&args[1])?;
let values = self.resolve_rolling_values(ctx, day, stock, &field, lookback)?; let values = self.resolve_rolling_values(ctx, day, stock, &field, lookback)?;
let value = match helper { let value = match helper {
@@ -6297,7 +6297,7 @@ impl PlatformExprStrategy {
"pct_change expects field and lookback".to_string(), "pct_change expects field and lookback".to_string(),
)); ));
} }
let field = Self::parse_string_or_identifier(&args[0])?; let field = Self::parse_string_or_identifier_ref(&args[0])?;
let lookback = Self::parse_positive_usize(&args[1])?; let lookback = Self::parse_positive_usize(&args[1])?;
let values = self.resolve_rolling_values( let values = self.resolve_rolling_values(
ctx, ctx,
@@ -7083,6 +7083,35 @@ impl PlatformExprStrategy {
))) )))
} }
/// Parse the literal/identifier forms used by runtime helpers without
/// allocating a new String on every stock evaluation. The owned parser
/// above remains the boundary for helpers that must retain the value.
fn parse_string_or_identifier_ref(raw: &str) -> Result<&str, BacktestError> {
let trimmed = raw.trim();
if trimmed.is_empty() {
return Err(BacktestError::Execution(
"platform helper argument cannot be empty".to_string(),
));
}
if let Some(value) = trimmed
.strip_prefix('"')
.and_then(|value| value.strip_suffix('"'))
.or_else(|| {
trimmed
.strip_prefix('\'')
.and_then(|value| value.strip_suffix('\''))
})
{
return Ok(value);
}
if Self::is_expression_identifier(trimmed) {
return Ok(trimmed);
}
Err(BacktestError::Execution(format!(
"platform helper expects a factor name or string literal, got {trimmed}"
)))
}
fn normalize_runtime_factor_key(key: &str) -> String { fn normalize_runtime_factor_key(key: &str) -> String {
key.trim() key.trim()
.trim_matches('"') .trim_matches('"')