修正当前滚动因子运行语义
This commit is contained in:
@@ -754,6 +754,29 @@ fn precomputed_stock_rolling_mean(
|
||||
}
|
||||
}
|
||||
|
||||
fn precomputed_stock_current_rolling_mean(
|
||||
extra_factors: &BTreeMap<String, f64>,
|
||||
field: &str,
|
||||
lookback: usize,
|
||||
) -> Option<f64> {
|
||||
if lookback == 0 {
|
||||
return None;
|
||||
}
|
||||
let value_for = |key: &str| {
|
||||
extra_factors
|
||||
.get(key)
|
||||
.copied()
|
||||
.filter(|value| value.is_finite())
|
||||
};
|
||||
match field.trim().to_ascii_lowercase().as_str() {
|
||||
"close" | "prev_close" | "stock_close" | "price" => {
|
||||
value_for(&format!("ma{lookback}_current_close"))
|
||||
}
|
||||
"volume" | "stock_volume" => value_for(&format!("avg_volume{lookback}_current")),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PlatformExprStrategy {
|
||||
config: PlatformExprStrategyConfig,
|
||||
engine: Engine,
|
||||
@@ -3307,7 +3330,7 @@ impl PlatformExprStrategy {
|
||||
field: &str,
|
||||
lookback: usize,
|
||||
) -> Option<f64> {
|
||||
let precomputed = precomputed_stock_rolling_mean(extra_factors, field, lookback);
|
||||
let precomputed = precomputed_stock_current_rolling_mean(extra_factors, field, lookback);
|
||||
let computed = || {
|
||||
ctx.data
|
||||
.market_current_numeric_moving_average(date, symbol, field, lookback)
|
||||
@@ -8213,9 +8236,12 @@ impl PlatformExprStrategy {
|
||||
return true;
|
||||
}
|
||||
if stock_rolling_helpers_require_extra
|
||||
&& identifiers
|
||||
.iter()
|
||||
.any(|name| matches!(name.as_str(), "rolling_mean" | "sma" | "ma" | "vma"))
|
||||
&& identifiers.iter().any(|name| {
|
||||
matches!(
|
||||
name.as_str(),
|
||||
"rolling_mean" | "rolling_mean_current" | "sma" | "ma" | "vma"
|
||||
)
|
||||
})
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -8361,6 +8387,11 @@ impl PlatformExprStrategy {
|
||||
) {
|
||||
let compact = Self::compact_expr(expr);
|
||||
Self::require_stock_rollings_for_named_helper(requirements, &compact, "rolling_mean");
|
||||
Self::require_stock_rollings_for_named_helper(
|
||||
requirements,
|
||||
&compact,
|
||||
"rolling_mean_current",
|
||||
);
|
||||
Self::require_stock_rollings_for_named_helper(requirements, &compact, "sma");
|
||||
Self::require_stock_rollings_for_named_helper(requirements, &compact, "ma");
|
||||
Self::require_stock_rollings_for_vma_helper(requirements, &compact);
|
||||
@@ -28285,7 +28316,11 @@ mod tests {
|
||||
let symbol = "300001.SZ";
|
||||
let mut extra_factors = BTreeMap::new();
|
||||
extra_factors.insert("ma5_prev_close".to_string(), 99.0);
|
||||
extra_factors.insert("ma10_prev_close".to_string(), 98.0);
|
||||
extra_factors.insert("ma30_prev_close".to_string(), 97.0);
|
||||
extra_factors.insert("avg_volume5".to_string(), 88.0);
|
||||
extra_factors.insert("avg_volume100".to_string(), 99.0);
|
||||
extra_factors.insert("adjustment_factor_backward1".to_string(), 1.0);
|
||||
let data = DataSet::from_components(
|
||||
vec![Instrument {
|
||||
symbol: symbol.to_string(),
|
||||
@@ -28322,16 +28357,23 @@ mod tests {
|
||||
price_tick: 0.01,
|
||||
})
|
||||
.collect(),
|
||||
vec![DailyFactorSnapshot {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
market_cap_bn: 20.0,
|
||||
free_float_cap_bn: 20.0,
|
||||
pe_ttm: 0.0,
|
||||
turnover_ratio: None,
|
||||
effective_turnover_ratio: None,
|
||||
extra_factors,
|
||||
}],
|
||||
dates
|
||||
.into_iter()
|
||||
.map(|factor_date| DailyFactorSnapshot {
|
||||
date: factor_date,
|
||||
symbol: symbol.to_string(),
|
||||
market_cap_bn: 20.0,
|
||||
free_float_cap_bn: 20.0,
|
||||
pe_ttm: 0.0,
|
||||
turnover_ratio: None,
|
||||
effective_turnover_ratio: None,
|
||||
extra_factors: if factor_date == date {
|
||||
extra_factors.clone()
|
||||
} else {
|
||||
BTreeMap::from([("adjustment_factor_backward1".to_string(), 1.0)])
|
||||
},
|
||||
})
|
||||
.collect(),
|
||||
vec![CandidateEligibility {
|
||||
date,
|
||||
symbol: symbol.to_string(),
|
||||
@@ -28376,7 +28418,7 @@ mod tests {
|
||||
let mut cfg = PlatformExprStrategyConfig::microcap_rotation();
|
||||
cfg.signal_symbol = symbol.to_string();
|
||||
cfg.prefer_precomputed_rolling_factors = true;
|
||||
cfg.stock_filter_expr = "stock_ma5 > 0 && stock_volume_ma5 > 0".to_string();
|
||||
cfg.stock_filter_expr = "rolling_mean(\"close\", 5) > rolling_mean(\"close\", 10) && rolling_mean(\"close\", 10) > rolling_mean(\"close\", 30) && rolling_mean(\"volume\", 5) < rolling_mean(\"volume\", 100)".to_string();
|
||||
let strategy = PlatformExprStrategy::new(cfg);
|
||||
let stock = strategy
|
||||
.stock_state_with_factor_date(&ctx, date, date, symbol)
|
||||
@@ -28384,19 +28426,51 @@ mod tests {
|
||||
assert_eq!(stock.stock_ma5, 99.0);
|
||||
assert_eq!(stock.stock_volume_ma5, 88.0);
|
||||
let day = strategy.day_state(&ctx, date).expect("day state");
|
||||
assert!(
|
||||
strategy
|
||||
.stock_passes_expr(&ctx, &day, &stock)
|
||||
.expect("precomputed decision rolling filter")
|
||||
);
|
||||
assert_eq!(
|
||||
strategy
|
||||
.resolve_current_rolling_mean(&ctx, &day, Some(&stock), "close", 5)
|
||||
.expect("precomputed current close rolling mean"),
|
||||
.resolve_rolling_mean(&ctx, &day, Some(&stock), "close", 5)
|
||||
.expect("precomputed decision close rolling mean"),
|
||||
99.0
|
||||
);
|
||||
assert_eq!(
|
||||
strategy
|
||||
.resolve_current_rolling_mean(&ctx, &day, Some(&stock), "volume", 5)
|
||||
.expect("precomputed current volume rolling mean"),
|
||||
.resolve_rolling_mean(&ctx, &day, Some(&stock), "volume", 5)
|
||||
.expect("precomputed decision volume rolling mean"),
|
||||
88.0
|
||||
);
|
||||
|
||||
let mut cfg = PlatformExprStrategyConfig::microcap_rotation();
|
||||
cfg.signal_symbol = symbol.to_string();
|
||||
cfg.prefer_precomputed_rolling_factors = true;
|
||||
cfg.stock_filter_expr = "rolling_mean_current(\"close\", 5) == 10.0 && rolling_mean_current(\"volume\", 5) == 1000.0".to_string();
|
||||
let strategy = PlatformExprStrategy::new(cfg);
|
||||
let stock = strategy
|
||||
.stock_state_with_factor_date(&ctx, date, date, symbol)
|
||||
.expect("stock state");
|
||||
let day = strategy.day_state(&ctx, date).expect("day state");
|
||||
assert!(
|
||||
strategy
|
||||
.stock_passes_expr(&ctx, &day, &stock)
|
||||
.expect("current rolling filter")
|
||||
);
|
||||
assert_eq!(
|
||||
strategy
|
||||
.resolve_current_rolling_mean(&ctx, &day, Some(&stock), "close", 5)
|
||||
.expect("current close rolling mean"),
|
||||
10.0
|
||||
);
|
||||
assert_eq!(
|
||||
strategy
|
||||
.resolve_current_rolling_mean(&ctx, &day, Some(&stock), "volume", 5)
|
||||
.expect("current volume rolling mean"),
|
||||
1_000.0
|
||||
);
|
||||
|
||||
let mut cfg = PlatformExprStrategyConfig::microcap_rotation();
|
||||
cfg.stock_filter_expr = "stock_ma5 > 0 && stock_volume_ma5 > 0".to_string();
|
||||
let strategy = PlatformExprStrategy::new(cfg);
|
||||
|
||||
Reference in New Issue
Block a user