修正当前滚动因子运行语义
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 {
|
pub struct PlatformExprStrategy {
|
||||||
config: PlatformExprStrategyConfig,
|
config: PlatformExprStrategyConfig,
|
||||||
engine: Engine,
|
engine: Engine,
|
||||||
@@ -3307,7 +3330,7 @@ impl PlatformExprStrategy {
|
|||||||
field: &str,
|
field: &str,
|
||||||
lookback: usize,
|
lookback: usize,
|
||||||
) -> Option<f64> {
|
) -> 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 = || {
|
let computed = || {
|
||||||
ctx.data
|
ctx.data
|
||||||
.market_current_numeric_moving_average(date, symbol, field, lookback)
|
.market_current_numeric_moving_average(date, symbol, field, lookback)
|
||||||
@@ -8213,9 +8236,12 @@ impl PlatformExprStrategy {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if stock_rolling_helpers_require_extra
|
if stock_rolling_helpers_require_extra
|
||||||
&& identifiers
|
&& identifiers.iter().any(|name| {
|
||||||
.iter()
|
matches!(
|
||||||
.any(|name| matches!(name.as_str(), "rolling_mean" | "sma" | "ma" | "vma"))
|
name.as_str(),
|
||||||
|
"rolling_mean" | "rolling_mean_current" | "sma" | "ma" | "vma"
|
||||||
|
)
|
||||||
|
})
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -8361,6 +8387,11 @@ impl PlatformExprStrategy {
|
|||||||
) {
|
) {
|
||||||
let compact = Self::compact_expr(expr);
|
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");
|
||||||
|
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, "sma");
|
||||||
Self::require_stock_rollings_for_named_helper(requirements, &compact, "ma");
|
Self::require_stock_rollings_for_named_helper(requirements, &compact, "ma");
|
||||||
Self::require_stock_rollings_for_vma_helper(requirements, &compact);
|
Self::require_stock_rollings_for_vma_helper(requirements, &compact);
|
||||||
@@ -28285,7 +28316,11 @@ mod tests {
|
|||||||
let symbol = "300001.SZ";
|
let symbol = "300001.SZ";
|
||||||
let mut extra_factors = BTreeMap::new();
|
let mut extra_factors = BTreeMap::new();
|
||||||
extra_factors.insert("ma5_prev_close".to_string(), 99.0);
|
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_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(
|
let data = DataSet::from_components(
|
||||||
vec![Instrument {
|
vec![Instrument {
|
||||||
symbol: symbol.to_string(),
|
symbol: symbol.to_string(),
|
||||||
@@ -28322,16 +28357,23 @@ mod tests {
|
|||||||
price_tick: 0.01,
|
price_tick: 0.01,
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
vec![DailyFactorSnapshot {
|
dates
|
||||||
date,
|
.into_iter()
|
||||||
|
.map(|factor_date| DailyFactorSnapshot {
|
||||||
|
date: factor_date,
|
||||||
symbol: symbol.to_string(),
|
symbol: symbol.to_string(),
|
||||||
market_cap_bn: 20.0,
|
market_cap_bn: 20.0,
|
||||||
free_float_cap_bn: 20.0,
|
free_float_cap_bn: 20.0,
|
||||||
pe_ttm: 0.0,
|
pe_ttm: 0.0,
|
||||||
turnover_ratio: None,
|
turnover_ratio: None,
|
||||||
effective_turnover_ratio: None,
|
effective_turnover_ratio: None,
|
||||||
extra_factors,
|
extra_factors: if factor_date == date {
|
||||||
}],
|
extra_factors.clone()
|
||||||
|
} else {
|
||||||
|
BTreeMap::from([("adjustment_factor_backward1".to_string(), 1.0)])
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
vec![CandidateEligibility {
|
vec![CandidateEligibility {
|
||||||
date,
|
date,
|
||||||
symbol: symbol.to_string(),
|
symbol: symbol.to_string(),
|
||||||
@@ -28376,7 +28418,7 @@ mod tests {
|
|||||||
let mut cfg = PlatformExprStrategyConfig::microcap_rotation();
|
let mut cfg = PlatformExprStrategyConfig::microcap_rotation();
|
||||||
cfg.signal_symbol = symbol.to_string();
|
cfg.signal_symbol = symbol.to_string();
|
||||||
cfg.prefer_precomputed_rolling_factors = true;
|
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 strategy = PlatformExprStrategy::new(cfg);
|
||||||
let stock = strategy
|
let stock = strategy
|
||||||
.stock_state_with_factor_date(&ctx, date, date, symbol)
|
.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_ma5, 99.0);
|
||||||
assert_eq!(stock.stock_volume_ma5, 88.0);
|
assert_eq!(stock.stock_volume_ma5, 88.0);
|
||||||
let day = strategy.day_state(&ctx, date).expect("day state");
|
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!(
|
assert_eq!(
|
||||||
strategy
|
strategy
|
||||||
.resolve_current_rolling_mean(&ctx, &day, Some(&stock), "close", 5)
|
.resolve_rolling_mean(&ctx, &day, Some(&stock), "close", 5)
|
||||||
.expect("precomputed current close rolling mean"),
|
.expect("precomputed decision close rolling mean"),
|
||||||
99.0
|
99.0
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
strategy
|
strategy
|
||||||
.resolve_current_rolling_mean(&ctx, &day, Some(&stock), "volume", 5)
|
.resolve_rolling_mean(&ctx, &day, Some(&stock), "volume", 5)
|
||||||
.expect("precomputed current volume rolling mean"),
|
.expect("precomputed decision volume rolling mean"),
|
||||||
88.0
|
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();
|
let mut cfg = PlatformExprStrategyConfig::microcap_rotation();
|
||||||
cfg.stock_filter_expr = "stock_ma5 > 0 && stock_volume_ma5 > 0".to_string();
|
cfg.stock_filter_expr = "stock_ma5 > 0 && stock_volume_ma5 > 0".to_string();
|
||||||
let strategy = PlatformExprStrategy::new(cfg);
|
let strategy = PlatformExprStrategy::new(cfg);
|
||||||
|
|||||||
Reference in New Issue
Block a user