diff --git a/crates/fidc-core/src/engine.rs b/crates/fidc-core/src/engine.rs index a1a6ab5..255834c 100644 --- a/crates/fidc-core/src/engine.rs +++ b/crates/fidc-core/src/engine.rs @@ -1935,14 +1935,14 @@ where .ok_or(BacktestError::MissingBenchmark { date: execution_date, })?; - let notes = corporate_action_notes.join(" | "); - let diagnostics = std::iter::once(format!( - "decision_lag_warmup lag_days={} execution_index={}", - self.config.decision_lag_trading_days, execution_idx - )) - .chain(broker_diagnostics.into_iter()) - .collect::>() - .join(" | "); + let notes = join_text_parts(corporate_action_notes.into_iter()); + let diagnostics = join_text_parts( + std::iter::once(format!( + "decision_lag_warmup lag_days={} execution_index={}", + self.config.decision_lag_trading_days, execution_idx + )) + .chain(broker_diagnostics.into_iter()), + ); let holdings_for_day = portfolio.holdings_summary(execution_date); let holding_start = result.daily_holdings.len(); let holding_count = holdings_for_day.len(); @@ -3012,17 +3012,17 @@ where .ok_or(BacktestError::MissingBenchmark { date: execution_date, })?; - let notes = corporate_action_notes - .into_iter() - .chain(decision.notes.into_iter()) - .collect::>() - .join(" | "); - let diagnostics = decision - .diagnostics - .into_iter() - .chain(broker_diagnostics.into_iter()) - .collect::>() - .join(" | "); + let notes = join_text_parts( + corporate_action_notes + .into_iter() + .chain(decision.notes.into_iter()), + ); + let diagnostics = join_text_parts( + decision + .diagnostics + .into_iter() + .chain(broker_diagnostics.into_iter()), + ); let holdings_for_day = portfolio.holdings_summary(execution_date); let holding_start = result.daily_holdings.len(); let holding_count = holdings_for_day.len(); @@ -4303,6 +4303,22 @@ fn futures_limit_satisfied(side: OrderSide, price: f64, limit_price: Option } } +fn join_text_parts(parts: I) -> String +where + I: IntoIterator, +{ + let mut iterator = parts.into_iter(); + let Some(first) = iterator.next() else { + return String::new(); + }; + let mut result = first; + for part in iterator { + result.push_str(" | "); + result.push_str(&part); + } + result +} + fn futures_cancel_report( date: NaiveDate, order: FuturesOpenOrder, @@ -4392,6 +4408,15 @@ mod tests { const SYMBOL: &str = "000001.SZ"; + #[test] + fn join_text_parts_matches_vec_join_contract() { + assert_eq!(super::join_text_parts(Vec::::new()), ""); + assert_eq!( + super::join_text_parts(vec!["a".to_string(), "".to_string(), "c".to_string()]), + "a | | c" + ); + } + #[derive(Debug)] struct BuyWhenDecisionDateStrategy { decision_date: NaiveDate,