跳过干净因子映射重复规范化

This commit is contained in:
boris
2026-08-25 22:59:30 +08:00
parent ac30d86b6a
commit afef38e45e
+45
View File
@@ -3311,6 +3311,16 @@ fn normalize_factor_snapshots(factors: Vec<DailyFactorSnapshot>) -> Vec<DailyFac
factors factors
.into_iter() .into_iter()
.map(|mut snapshot| { .map(|mut snapshot| {
let already_normalized = snapshot.extra_factors.iter().all(|(field, value)| {
let trimmed = field.as_ref().trim().trim_matches('"').trim_matches('\'');
!trimmed.is_empty()
&& trimmed == field.as_ref()
&& trimmed.bytes().all(|byte| !byte.is_ascii_uppercase())
&& value.is_finite()
});
if already_normalized {
return snapshot;
}
snapshot.extra_factors = snapshot snapshot.extra_factors = snapshot
.extra_factors .extra_factors
.into_iter() .into_iter()
@@ -3970,6 +3980,41 @@ mod tests {
assert_eq!(factor_numeric_value(&snapshot, "CUSTOM_FACTOR"), Some(3.5)); assert_eq!(factor_numeric_value(&snapshot, "CUSTOM_FACTOR"), Some(3.5));
} }
#[test]
fn factor_snapshot_normalization_moves_clean_maps_and_repairs_dirty_maps() {
let date = NaiveDate::parse_from_str("2025-01-02", "%Y-%m-%d").unwrap();
let clean = normalize_factor_snapshots(vec![DailyFactorSnapshot {
date,
symbol: "000001.SZ".to_string(),
market_cap_bn: 1.0,
free_float_cap_bn: 1.0,
pe_ttm: 1.0,
turnover_ratio: None,
effective_turnover_ratio: None,
extra_factors: BTreeMap::from([(Cow::Borrowed("amount"), 10.0)]),
}]);
assert!(matches!(
clean[0].extra_factors.keys().next(),
Some(Cow::Borrowed("amount"))
));
let dirty = normalize_factor_snapshots(vec![DailyFactorSnapshot {
date,
symbol: "000001.SZ".to_string(),
market_cap_bn: 1.0,
free_float_cap_bn: 1.0,
pe_ttm: 1.0,
turnover_ratio: None,
effective_turnover_ratio: None,
extra_factors: BTreeMap::from([
(Cow::Owned(" CUSTOM_FACTOR ".to_string()), 2.0),
(Cow::Borrowed("bad_nan"), f64::NAN),
]),
}]);
assert_eq!(dirty[0].extra_factors.get("custom_factor"), Some(&2.0));
assert!(!dirty[0].extra_factors.contains_key("bad_nan"));
}
#[test] #[test]
fn symbol_price_series_test_constructor_sorts_unsorted_rows() { fn symbol_price_series_test_constructor_sorts_unsorted_rows() {
let series = SymbolPriceSeries::new( let series = SymbolPriceSeries::new(