fix: preserve missing numeric factors and reject nonfinite truthiness

This commit is contained in:
boris
2026-09-09 02:51:26 +08:00
parent 078839b0f3
commit dd6b37be16
+19 -9
View File
@@ -4687,7 +4687,7 @@ impl PlatformExprStrategy {
} else if intraday_same_day_factor { } else if intraday_same_day_factor {
f64::NAN f64::NAN
} else { } else {
factor.extra_factors.get("amount").copied().unwrap_or(0.0) factor.extra_factors.get("amount").copied().unwrap_or(f64::NAN)
}; };
let market_cap_bn = decision_market_cap_bn(factor); let market_cap_bn = decision_market_cap_bn(factor);
let free_float_cap_bn = decision_free_float_cap_bn(factor); let free_float_cap_bn = decision_free_float_cap_bn(factor);
@@ -4769,8 +4769,8 @@ impl PlatformExprStrategy {
minute_volume: market.minute_volume as i64, minute_volume: market.minute_volume as i64,
bid1_volume: market.bid1_volume as i64, bid1_volume: market.bid1_volume as i64,
ask1_volume: market.ask1_volume as i64, ask1_volume: market.ask1_volume as i64,
turnover_ratio: factor.turnover_ratio.unwrap_or(0.0), turnover_ratio: factor.turnover_ratio.unwrap_or(f64::NAN),
effective_turnover_ratio: factor.effective_turnover_ratio.unwrap_or(0.0), effective_turnover_ratio: factor.effective_turnover_ratio.unwrap_or(f64::NAN),
open: feature_market.day_open, open: feature_market.day_open,
high: expression_high, high: expression_high,
low: expression_low, low: expression_low,
@@ -8359,7 +8359,7 @@ impl PlatformExprStrategy {
if let Some(value) = self.eval_numeric_vm(ctx, expr, day, stock, position)? { if let Some(value) = self.eval_numeric_vm(ctx, expr, day, stock, position)? {
return match value { return match value {
NumericVmValue::Boolean(boolean) => Ok(boolean), NumericVmValue::Boolean(boolean) => Ok(boolean),
NumericVmValue::Number(number) => Ok(number != 0.0), NumericVmValue::Number(number) => Ok(number.is_finite() && number != 0.0),
}; };
} }
let value = self.eval_dynamic(ctx, expr, day, stock, position)?; let value = self.eval_dynamic(ctx, expr, day, stock, position)?;
@@ -8367,7 +8367,7 @@ impl PlatformExprStrategy {
return Ok(boolean); return Ok(boolean);
} }
if let Some(number) = value.clone().try_cast::<f64>() { if let Some(number) = value.clone().try_cast::<f64>() {
return Ok(number != 0.0); return Ok(number.is_finite() && number != 0.0);
} }
if let Some(number) = value.try_cast::<i64>() { if let Some(number) = value.try_cast::<i64>() {
return Ok(number != 0); return Ok(number != 0);
@@ -16524,8 +16524,8 @@ mod tests {
market_cap_bn: 12.0, market_cap_bn: 12.0,
free_float_cap_bn: 10.0, free_float_cap_bn: 10.0,
pe_ttm: 8.0, pe_ttm: 8.0,
turnover_ratio: Some(1.0), turnover_ratio: None,
effective_turnover_ratio: Some(1.0), effective_turnover_ratio: None,
adjustment_factor_backward1: None, adjustment_factor_backward1: None,
extra_factors: BTreeMap::new(), extra_factors: BTreeMap::new(),
}, },
@@ -16535,8 +16535,8 @@ mod tests {
market_cap_bn: 12.0, market_cap_bn: 12.0,
free_float_cap_bn: 10.0, free_float_cap_bn: 10.0,
pe_ttm: 8.0, pe_ttm: 8.0,
turnover_ratio: Some(1.0), turnover_ratio: Some(0.0),
effective_turnover_ratio: Some(1.0), effective_turnover_ratio: Some(0.0),
adjustment_factor_backward1: None, adjustment_factor_backward1: None,
extra_factors: BTreeMap::from([ extra_factors: BTreeMap::from([
("model_score".into(), 2.0), ("model_score".into(), 2.0),
@@ -16601,6 +16601,16 @@ mod tests {
.expect("present stock state"); .expect("present stock state");
assert!(day.available_factor_names.contains("model_score")); assert!(day.available_factor_names.contains("model_score"));
assert!(missing_stock.turnover_ratio.is_nan());
assert!(missing_stock.effective_turnover_ratio.is_nan());
assert_eq!(present_stock.turnover_ratio, 0.0);
assert!(!strategy.eval_bool(&ctx, "model_score", &day, Some(&missing_stock), None).unwrap());
assert!(strategy.eval_bool(&ctx, "model_score", &day, Some(&present_stock), None).unwrap());
for field in ["turnover_ratio", "effective_turnover_ratio"] {
let predicate = format!("{field} < 1.0");
assert!(!strategy.eval_bool(&ctx, &predicate, &day, Some(&missing_stock), None).unwrap());
assert!(strategy.eval_bool(&ctx, &predicate, &day, Some(&present_stock), None).unwrap());
}
assert!( assert!(
!strategy !strategy
.stock_passes_expr(&ctx, &day, &missing_stock) .stock_passes_expr(&ctx, &day, &missing_stock)