diff --git a/crates/fidc-core/src/platform_expr_strategy.rs b/crates/fidc-core/src/platform_expr_strategy.rs index 76af552..ba01a91 100644 --- a/crates/fidc-core/src/platform_expr_strategy.rs +++ b/crates/fidc-core/src/platform_expr_strategy.rs @@ -27,6 +27,7 @@ use crate::numeric_expr_vm::{ self, EvalError as NumericVmEvalError, Program as NumericVmProgram, Scratch as NumericVmScratch, Value as NumericVmValue, ValueType as NumericVmValueType, }; +use crate::numeric_factors::NumericFactorMap; use crate::portfolio::PortfolioState; use crate::holding_policy::{AutomaticTradeProtection, AutomaticTradePermission, HoldingLifecycleEvidence}; @@ -1010,7 +1011,7 @@ struct StockExpressionState { stock_volume_ma60: f64, stock_volume_ma100: f64, current_series_end: Option, - extra_factors: BTreeMap, + extra_factors: NumericFactorMap, extra_text_factors: BTreeMap, } @@ -4390,11 +4391,14 @@ impl PlatformExprStrategy { .factor_snapshot_rows_on(date) .iter() .flat_map(|row| { - row.extra_factors.keys().map(|key| key.to_string()).chain( + row.extra_factors.keys().map(|key| key.as_ref()).chain( row.adjustment_factor_backward1 - .map(|_| BACKWARD_ADJUSTMENT_FACTOR_FIELD.to_string()), + .map(|_| BACKWARD_ADJUSTMENT_FACTOR_FIELD), ) }) + .collect::>() + .into_iter() + .map(str::to_owned) .collect() } else { BTreeSet::new() @@ -4403,7 +4407,10 @@ impl PlatformExprStrategy { ctx.data .factor_text_rows_on(date) .iter() - .map(|row| row.field.clone()) + .map(|row| row.field.as_str()) + .collect::>() + .into_iter() + .map(str::to_owned) .collect() } else { BTreeSet::new() @@ -4896,10 +4903,10 @@ impl PlatformExprStrategy { self.stock_extra_factor_map_required || self.stock_extra_factor_identifiers.contains(field.as_ref()) }) - .map(|(field, value)| (field.to_string(), *value)) + .map(|(field, value)| (field.clone(), *value)) .collect() } else { - BTreeMap::new() + NumericFactorMap::new() }; if !self.config.completed_session_factor_fields.is_empty() { let visible_date = completed_session_factor_date( @@ -4914,7 +4921,7 @@ impl PlatformExprStrategy { .and_then(|row| row.extra_factors.get(field.as_str())) .copied() .unwrap_or(f64::NAN); - extra_factors.insert(field.clone(), value); + extra_factors.insert(field.clone().into(), value); } } } @@ -4925,7 +4932,7 @@ impl PlatformExprStrategy { .contains(BACKWARD_ADJUSTMENT_FACTOR_FIELD)) && let Some(value) = factor.adjustment_factor_backward1 { - extra_factors.insert(BACKWARD_ADJUSTMENT_FACTOR_FIELD.to_string(), value); + extra_factors.insert(BACKWARD_ADJUSTMENT_FACTOR_FIELD.into(), value); } let state = StockExpressionState { @@ -5642,7 +5649,7 @@ impl PlatformExprStrategy { Dynamic::from(stock.stock_volume_ma100), ); for (key, value) in &stock.extra_factors { - factors.insert(key.clone().into(), Dynamic::from(*value)); + factors.insert(key.as_ref().into(), Dynamic::from(*value)); } for (key, value) in &stock.extra_text_factors { factors.insert(key.clone().into(), Dynamic::from(value.clone())); @@ -14546,6 +14553,7 @@ mod tests { use std::sync::Arc; use chrono::{NaiveDate, NaiveTime}; + use rhai::{Dynamic, Map}; use super::{ CompiledRuntimeHelperArgs, PlatformAccountActionKind, PlatformExplicitActionStage, @@ -17542,6 +17550,18 @@ mod tests { .stock_state_with_factor_date(&ctx, date, date, present_symbol) .expect("factor map stock state"); assert!(map_stock.extra_factors.contains_key("unused_factor")); + let mut numeric_state = (*map_stock).clone(); + numeric_state.extra_factors.insert("negative_zero".into(), -0.0); + numeric_state.extra_factors.insert("undefined_value".into(), f64::NAN); + let copied_state = numeric_state.clone(); + assert_eq!(copied_state.extra_factors["negative_zero"].to_bits(), (-0.0_f64).to_bits()); + assert!(copied_state.extra_factors["undefined_value"].is_nan()); + let exposed = copied_state.extra_factors.iter() + .map(|(key, value)| (key.as_ref().into(), Dynamic::from(*value))) + .collect::(); + assert_eq!(exposed["negative_zero"].as_float().unwrap().to_bits(), (-0.0_f64).to_bits()); + assert!(exposed["undefined_value"].as_float().unwrap().is_nan()); + assert!(!exposed.contains_key("missing_factor")); let map_day = map_strategy .day_state(&ctx, date) .expect("factor map day state");