perf: type static current rolling helpers
This commit is contained in:
@@ -3454,31 +3454,13 @@ impl DataSet {
|
||||
let field = normalized_field(field);
|
||||
match field.as_ref() {
|
||||
"close" | "prev_close" | "stock_close" | "price" => self
|
||||
.adjusted_close_series_by_symbol_id(symbol_id)
|
||||
.and_then(|series| {
|
||||
series_end
|
||||
.map(|end| series.moving_average_at_end(end, lookback))
|
||||
.unwrap_or_else(|| series.current_moving_average(date, lookback))
|
||||
}),
|
||||
"volume" | "stock_volume" => {
|
||||
self.market_series_by_symbol_id(symbol_id)
|
||||
.and_then(|series| {
|
||||
series_end
|
||||
.map(|end| {
|
||||
series
|
||||
.valid_volume_window(end, lookback)
|
||||
.map(|(start, end)| {
|
||||
normalize_rolling_factor(
|
||||
(series.valid_volume_sum_prefix[end]
|
||||
- series.valid_volume_sum_prefix[start])
|
||||
/ lookback as f64,
|
||||
12,
|
||||
)
|
||||
})
|
||||
})
|
||||
.unwrap_or_else(|| series.current_volume_moving_average(date, lookback))
|
||||
})
|
||||
}
|
||||
.market_current_close_moving_average_with_end_by_symbol_id(
|
||||
date, symbol_id, lookback, series_end,
|
||||
),
|
||||
"volume" | "stock_volume" => self
|
||||
.market_current_volume_moving_average_with_end_by_symbol_id(
|
||||
date, symbol_id, lookback, series_end,
|
||||
),
|
||||
"day_open" | "dayopen" => {
|
||||
self.market_series_by_symbol_id(symbol_id)
|
||||
.and_then(|series| {
|
||||
@@ -3514,6 +3496,47 @@ impl DataSet {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn market_current_close_moving_average_with_end_by_symbol_id(
|
||||
&self,
|
||||
date: NaiveDate,
|
||||
symbol_id: u32,
|
||||
lookback: usize,
|
||||
series_end: Option<usize>,
|
||||
) -> Option<f64> {
|
||||
self.adjusted_close_series_by_symbol_id(symbol_id)
|
||||
.and_then(|series| {
|
||||
series_end
|
||||
.map(|end| series.moving_average_at_end(end, lookback))
|
||||
.unwrap_or_else(|| series.current_moving_average(date, lookback))
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn market_current_volume_moving_average_with_end_by_symbol_id(
|
||||
&self,
|
||||
date: NaiveDate,
|
||||
symbol_id: u32,
|
||||
lookback: usize,
|
||||
series_end: Option<usize>,
|
||||
) -> Option<f64> {
|
||||
self.market_series_by_symbol_id(symbol_id)
|
||||
.and_then(|series| {
|
||||
series_end
|
||||
.map(|end| {
|
||||
series
|
||||
.valid_volume_window(end, lookback)
|
||||
.map(|(start, end)| {
|
||||
normalize_rolling_factor(
|
||||
(series.valid_volume_sum_prefix[end]
|
||||
- series.valid_volume_sum_prefix[start])
|
||||
/ lookback as f64,
|
||||
12,
|
||||
)
|
||||
})
|
||||
})
|
||||
.unwrap_or_else(|| series.current_volume_moving_average(date, lookback))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn market_latest_back_adjusted_close(&self, date: NaiveDate, symbol: &str) -> Option<f64> {
|
||||
self.adjusted_close_series(symbol)
|
||||
.and_then(|series| series.latest_back_adjusted_close(date))
|
||||
|
||||
@@ -972,6 +972,10 @@ enum CompiledRuntimeHelperArgs {
|
||||
lookback: usize,
|
||||
current: bool,
|
||||
},
|
||||
StockCurrentRollingMean {
|
||||
field: StockRollingField,
|
||||
lookback: usize,
|
||||
},
|
||||
RollingMaxCurrent {
|
||||
field: String,
|
||||
lookback: usize,
|
||||
@@ -5124,6 +5128,41 @@ impl PlatformExprStrategy {
|
||||
};
|
||||
Ok(RuntimeHelperResolution::Number(value))
|
||||
}
|
||||
CompiledRuntimeHelperArgs::StockCurrentRollingMean { field, lookback } => {
|
||||
let stock = stock.ok_or_else(|| {
|
||||
BacktestError::Execution(format!(
|
||||
"rolling_mean_current requires stock context for lookback {lookback}"
|
||||
))
|
||||
})?;
|
||||
let value = match field {
|
||||
StockRollingField::Close => ctx
|
||||
.data
|
||||
.market_current_close_moving_average_with_end_by_symbol_id(
|
||||
day.date,
|
||||
stock.symbol_id,
|
||||
*lookback,
|
||||
stock.current_series_end,
|
||||
),
|
||||
StockRollingField::Volume => ctx
|
||||
.data
|
||||
.market_current_volume_moving_average_with_end_by_symbol_id(
|
||||
day.date,
|
||||
stock.symbol_id,
|
||||
*lookback,
|
||||
stock.current_series_end,
|
||||
),
|
||||
}
|
||||
.ok_or_else(|| {
|
||||
BacktestError::Execution(format!(
|
||||
"missing current rolling mean for field {} with lookback {lookback}",
|
||||
match field {
|
||||
StockRollingField::Close => "close",
|
||||
StockRollingField::Volume => "volume",
|
||||
}
|
||||
))
|
||||
})?;
|
||||
Ok(RuntimeHelperResolution::Number(value))
|
||||
}
|
||||
CompiledRuntimeHelperArgs::RollingMaxCurrent { field, lookback } => {
|
||||
let values =
|
||||
self.resolve_current_rolling_values(ctx, day, stock, field, *lookback)?;
|
||||
@@ -6216,6 +6255,19 @@ impl PlatformExprStrategy {
|
||||
}
|
||||
"rolling_mean_current" => {
|
||||
let (field, lookback) = field_lookback()?;
|
||||
let stock_field = match field.trim().to_ascii_lowercase().as_str() {
|
||||
"close" | "prev_close" | "stock_close" | "price" => {
|
||||
Some(StockRollingField::Close)
|
||||
}
|
||||
"volume" | "stock_volume" => Some(StockRollingField::Volume),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(field) = stock_field {
|
||||
return Some(CompiledRuntimeHelperArgs::StockCurrentRollingMean {
|
||||
field,
|
||||
lookback,
|
||||
});
|
||||
}
|
||||
Some(CompiledRuntimeHelperArgs::RollingMean {
|
||||
field,
|
||||
lookback,
|
||||
@@ -12675,7 +12727,7 @@ mod tests {
|
||||
PlatformExprStrategyConfig, PlatformPortfolioDrawdownControlConfig,
|
||||
PlatformPortfolioDrawdownController, PlatformRebalanceSchedule, PlatformScheduleFrequency,
|
||||
PlatformStopTakeReferencePriceMode, PlatformTradeAction, PlatformUniverseActionKind,
|
||||
RuntimeHelperResolution, SelectionRiskDeferral, StockFilterQuoteUsage,
|
||||
RuntimeHelperResolution, SelectionRiskDeferral, StockFilterQuoteUsage, StockRollingField,
|
||||
StockSnapshotFieldRequirements, framework_stock_rolling_factor_requirement,
|
||||
scheduled_position_exposure,
|
||||
};
|
||||
@@ -33593,6 +33645,31 @@ let target_exposure = csi_ready ? dynamic_exposure : 0.0;
|
||||
.all(|binding| binding.compiled_args.is_some()),
|
||||
"all static numeric helper arguments must be parsed once at plan compilation"
|
||||
);
|
||||
assert!(bindings.iter().any(|binding| {
|
||||
if binding.name != "rolling_mean_current" {
|
||||
return false;
|
||||
}
|
||||
matches!(
|
||||
binding.compiled_args.as_ref(),
|
||||
Some(CompiledRuntimeHelperArgs::StockCurrentRollingMean {
|
||||
field: StockRollingField::Volume,
|
||||
lookback: 10,
|
||||
})
|
||||
)
|
||||
}));
|
||||
|
||||
let signal_args = vec!["\"signal_close\"".to_string(), "20".to_string()];
|
||||
assert!(matches!(
|
||||
PlatformExprStrategy::compile_runtime_helper_args(
|
||||
"rolling_mean_current",
|
||||
&signal_args
|
||||
),
|
||||
Some(CompiledRuntimeHelperArgs::RollingMean {
|
||||
field,
|
||||
lookback: 20,
|
||||
current: true,
|
||||
}) if field == "signal_close"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user