perf(strategy): retain compact factor values in expression contexts

This commit is contained in:
boris
2026-09-13 00:41:51 +08:00
committed by boris
parent fe7243bbc3
commit 07b7b181b6
+29 -9
View File
@@ -27,6 +27,7 @@ use crate::numeric_expr_vm::{
self, EvalError as NumericVmEvalError, Program as NumericVmProgram, self, EvalError as NumericVmEvalError, Program as NumericVmProgram,
Scratch as NumericVmScratch, Value as NumericVmValue, ValueType as NumericVmValueType, Scratch as NumericVmScratch, Value as NumericVmValue, ValueType as NumericVmValueType,
}; };
use crate::numeric_factors::NumericFactorMap;
use crate::portfolio::PortfolioState; use crate::portfolio::PortfolioState;
use crate::holding_policy::{AutomaticTradeProtection, AutomaticTradePermission, HoldingLifecycleEvidence}; use crate::holding_policy::{AutomaticTradeProtection, AutomaticTradePermission, HoldingLifecycleEvidence};
@@ -1010,7 +1011,7 @@ struct StockExpressionState {
stock_volume_ma60: f64, stock_volume_ma60: f64,
stock_volume_ma100: f64, stock_volume_ma100: f64,
current_series_end: Option<usize>, current_series_end: Option<usize>,
extra_factors: BTreeMap<String, f64>, extra_factors: NumericFactorMap,
extra_text_factors: BTreeMap<String, String>, extra_text_factors: BTreeMap<String, String>,
} }
@@ -4390,11 +4391,14 @@ impl PlatformExprStrategy {
.factor_snapshot_rows_on(date) .factor_snapshot_rows_on(date)
.iter() .iter()
.flat_map(|row| { .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 row.adjustment_factor_backward1
.map(|_| BACKWARD_ADJUSTMENT_FACTOR_FIELD.to_string()), .map(|_| BACKWARD_ADJUSTMENT_FACTOR_FIELD),
) )
}) })
.collect::<BTreeSet<_>>()
.into_iter()
.map(str::to_owned)
.collect() .collect()
} else { } else {
BTreeSet::new() BTreeSet::new()
@@ -4403,7 +4407,10 @@ impl PlatformExprStrategy {
ctx.data ctx.data
.factor_text_rows_on(date) .factor_text_rows_on(date)
.iter() .iter()
.map(|row| row.field.clone()) .map(|row| row.field.as_str())
.collect::<BTreeSet<_>>()
.into_iter()
.map(str::to_owned)
.collect() .collect()
} else { } else {
BTreeSet::new() BTreeSet::new()
@@ -4896,10 +4903,10 @@ impl PlatformExprStrategy {
self.stock_extra_factor_map_required self.stock_extra_factor_map_required
|| self.stock_extra_factor_identifiers.contains(field.as_ref()) || self.stock_extra_factor_identifiers.contains(field.as_ref())
}) })
.map(|(field, value)| (field.to_string(), *value)) .map(|(field, value)| (field.clone(), *value))
.collect() .collect()
} else { } else {
BTreeMap::new() NumericFactorMap::new()
}; };
if !self.config.completed_session_factor_fields.is_empty() { if !self.config.completed_session_factor_fields.is_empty() {
let visible_date = completed_session_factor_date( let visible_date = completed_session_factor_date(
@@ -4914,7 +4921,7 @@ impl PlatformExprStrategy {
.and_then(|row| row.extra_factors.get(field.as_str())) .and_then(|row| row.extra_factors.get(field.as_str()))
.copied() .copied()
.unwrap_or(f64::NAN); .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)) .contains(BACKWARD_ADJUSTMENT_FACTOR_FIELD))
&& let Some(value) = factor.adjustment_factor_backward1 && 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 { let state = StockExpressionState {
@@ -5642,7 +5649,7 @@ impl PlatformExprStrategy {
Dynamic::from(stock.stock_volume_ma100), Dynamic::from(stock.stock_volume_ma100),
); );
for (key, value) in &stock.extra_factors { 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 { for (key, value) in &stock.extra_text_factors {
factors.insert(key.clone().into(), Dynamic::from(value.clone())); factors.insert(key.clone().into(), Dynamic::from(value.clone()));
@@ -14546,6 +14553,7 @@ mod tests {
use std::sync::Arc; use std::sync::Arc;
use chrono::{NaiveDate, NaiveTime}; use chrono::{NaiveDate, NaiveTime};
use rhai::{Dynamic, Map};
use super::{ use super::{
CompiledRuntimeHelperArgs, PlatformAccountActionKind, PlatformExplicitActionStage, CompiledRuntimeHelperArgs, PlatformAccountActionKind, PlatformExplicitActionStage,
@@ -17542,6 +17550,18 @@ mod tests {
.stock_state_with_factor_date(&ctx, date, date, present_symbol) .stock_state_with_factor_date(&ctx, date, date, present_symbol)
.expect("factor map stock state"); .expect("factor map stock state");
assert!(map_stock.extra_factors.contains_key("unused_factor")); 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::<Map>();
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 let map_day = map_strategy
.day_state(&ctx, date) .day_state(&ctx, date)
.expect("factor map day state"); .expect("factor map day state");