perf: type static current rolling helpers
This commit is contained in:
@@ -3454,31 +3454,13 @@ impl DataSet {
|
|||||||
let field = normalized_field(field);
|
let field = normalized_field(field);
|
||||||
match field.as_ref() {
|
match field.as_ref() {
|
||||||
"close" | "prev_close" | "stock_close" | "price" => self
|
"close" | "prev_close" | "stock_close" | "price" => self
|
||||||
.adjusted_close_series_by_symbol_id(symbol_id)
|
.market_current_close_moving_average_with_end_by_symbol_id(
|
||||||
.and_then(|series| {
|
date, symbol_id, lookback, series_end,
|
||||||
series_end
|
),
|
||||||
.map(|end| series.moving_average_at_end(end, lookback))
|
"volume" | "stock_volume" => self
|
||||||
.unwrap_or_else(|| series.current_moving_average(date, lookback))
|
.market_current_volume_moving_average_with_end_by_symbol_id(
|
||||||
}),
|
date, symbol_id, lookback, series_end,
|
||||||
"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))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
"day_open" | "dayopen" => {
|
"day_open" | "dayopen" => {
|
||||||
self.market_series_by_symbol_id(symbol_id)
|
self.market_series_by_symbol_id(symbol_id)
|
||||||
.and_then(|series| {
|
.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> {
|
pub fn market_latest_back_adjusted_close(&self, date: NaiveDate, symbol: &str) -> Option<f64> {
|
||||||
self.adjusted_close_series(symbol)
|
self.adjusted_close_series(symbol)
|
||||||
.and_then(|series| series.latest_back_adjusted_close(date))
|
.and_then(|series| series.latest_back_adjusted_close(date))
|
||||||
|
|||||||
@@ -972,6 +972,10 @@ enum CompiledRuntimeHelperArgs {
|
|||||||
lookback: usize,
|
lookback: usize,
|
||||||
current: bool,
|
current: bool,
|
||||||
},
|
},
|
||||||
|
StockCurrentRollingMean {
|
||||||
|
field: StockRollingField,
|
||||||
|
lookback: usize,
|
||||||
|
},
|
||||||
RollingMaxCurrent {
|
RollingMaxCurrent {
|
||||||
field: String,
|
field: String,
|
||||||
lookback: usize,
|
lookback: usize,
|
||||||
@@ -5124,6 +5128,41 @@ impl PlatformExprStrategy {
|
|||||||
};
|
};
|
||||||
Ok(RuntimeHelperResolution::Number(value))
|
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 } => {
|
CompiledRuntimeHelperArgs::RollingMaxCurrent { field, lookback } => {
|
||||||
let values =
|
let values =
|
||||||
self.resolve_current_rolling_values(ctx, day, stock, field, *lookback)?;
|
self.resolve_current_rolling_values(ctx, day, stock, field, *lookback)?;
|
||||||
@@ -6216,6 +6255,19 @@ impl PlatformExprStrategy {
|
|||||||
}
|
}
|
||||||
"rolling_mean_current" => {
|
"rolling_mean_current" => {
|
||||||
let (field, lookback) = field_lookback()?;
|
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 {
|
Some(CompiledRuntimeHelperArgs::RollingMean {
|
||||||
field,
|
field,
|
||||||
lookback,
|
lookback,
|
||||||
@@ -12675,7 +12727,7 @@ mod tests {
|
|||||||
PlatformExprStrategyConfig, PlatformPortfolioDrawdownControlConfig,
|
PlatformExprStrategyConfig, PlatformPortfolioDrawdownControlConfig,
|
||||||
PlatformPortfolioDrawdownController, PlatformRebalanceSchedule, PlatformScheduleFrequency,
|
PlatformPortfolioDrawdownController, PlatformRebalanceSchedule, PlatformScheduleFrequency,
|
||||||
PlatformStopTakeReferencePriceMode, PlatformTradeAction, PlatformUniverseActionKind,
|
PlatformStopTakeReferencePriceMode, PlatformTradeAction, PlatformUniverseActionKind,
|
||||||
RuntimeHelperResolution, SelectionRiskDeferral, StockFilterQuoteUsage,
|
RuntimeHelperResolution, SelectionRiskDeferral, StockFilterQuoteUsage, StockRollingField,
|
||||||
StockSnapshotFieldRequirements, framework_stock_rolling_factor_requirement,
|
StockSnapshotFieldRequirements, framework_stock_rolling_factor_requirement,
|
||||||
scheduled_position_exposure,
|
scheduled_position_exposure,
|
||||||
};
|
};
|
||||||
@@ -33593,6 +33645,31 @@ let target_exposure = csi_ready ? dynamic_exposure : 0.0;
|
|||||||
.all(|binding| binding.compiled_args.is_some()),
|
.all(|binding| binding.compiled_args.is_some()),
|
||||||
"all static numeric helper arguments must be parsed once at plan compilation"
|
"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]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user