移除引擎历史事件重复复制

This commit is contained in:
boris
2026-08-25 08:34:06 +08:00
parent 5482c8a52d
commit 85cfdca14c
+169 -78
View File
@@ -319,6 +319,7 @@ pub struct BacktestDayProgress {
pub total_return: f64,
pub benchmark_close: f64,
pub daily_fill_count: usize,
pub daily_order_count: usize,
pub cumulative_trade_count: usize,
pub holding_count: usize,
pub notes: String,
@@ -1716,11 +1717,20 @@ where
}
pub fn run(&mut self) -> Result<BacktestResult, BacktestError> {
self.run_with_progress(|_| {})
self.run_with_progress_options(false, false, |_| {})
}
pub fn run_with_progress<F>(
pub fn run_with_progress<F>(&mut self, on_progress: F) -> Result<BacktestResult, BacktestError>
where
F: FnMut(&BacktestDayProgress),
{
self.run_with_progress_options(true, true, on_progress)
}
pub fn run_with_progress_options<F>(
&mut self,
include_progress_details: bool,
include_progress_diagnostics: bool,
mut on_progress: F,
) -> Result<BacktestResult, BacktestError>
where
@@ -1866,6 +1876,8 @@ where
execution_date,
execution_date,
);
let day_order_start = result.order_events.len();
let day_fill_start = result.fills.len();
let decision_slot = decision_slots.get(execution_idx).copied().flatten();
let Some((decision_index, decision_date)) = decision_slot else {
@@ -1884,13 +1896,13 @@ where
let futures_expiration_report = self.settle_futures_expirations(execution_date);
merge_broker_report(&mut report, futures_expiration_report);
let daily_fill_count = report.fill_events.len();
let day_orders = report.order_events.clone();
let day_fills = report.fill_events.clone();
let broker_diagnostics = report.diagnostics.clone();
let execution_risk_decisions = risk_decisions_from_order_events(&day_orders);
let broker_diagnostics = std::mem::take(&mut report.diagnostics);
let execution_risk_decisions =
risk_decisions_from_order_events(&report.order_events);
self.extend_result(&mut result, report, execution_date, execution_date);
result.risk_decisions.extend(execution_risk_decisions);
let daily_fill_count = result.fills.len() - day_fill_start;
let daily_order_count = result.order_events.len() - day_order_start;
let benchmark =
self.data
@@ -1907,7 +1919,11 @@ where
.collect::<Vec<_>>()
.join(" | ");
let holdings_for_day = portfolio.holdings_summary(execution_date);
let day_process_events = process_events.clone();
let holding_start = result.daily_holdings.len();
let holding_count = holdings_for_day.len();
result.daily_holdings.extend(holdings_for_day);
let progress_process_start = result.process_events.len();
result.process_events.append(&mut process_events);
let aggregate_cash = self.aggregate_cash(&portfolio);
let aggregate_market_value = self.aggregate_market_value(&portfolio);
let aggregate_total_equity = self.aggregate_total_equity(&portfolio);
@@ -1928,7 +1944,6 @@ where
notes,
diagnostics,
});
result.daily_holdings.extend(holdings_for_day.clone());
let latest = result
.equity_curve
.last()
@@ -1943,16 +1958,28 @@ where
total_return: latest.unit_nav - 1.0,
benchmark_close: latest.benchmark_close,
daily_fill_count,
daily_order_count,
cumulative_trade_count: result.fills.len(),
holding_count: holdings_for_day.len(),
notes: latest.notes.clone(),
diagnostics: latest.diagnostics.clone(),
orders: day_orders,
fills: day_fills,
holdings: holdings_for_day,
process_events: day_process_events,
holding_count,
notes: include_progress_diagnostics
.then(|| latest.notes.clone())
.unwrap_or_default(),
diagnostics: include_progress_diagnostics
.then(|| latest.diagnostics.clone())
.unwrap_or_default(),
orders: include_progress_details
.then(|| result.order_events[day_order_start..].to_vec())
.unwrap_or_default(),
fills: include_progress_details
.then(|| result.fills[day_fill_start..].to_vec())
.unwrap_or_default(),
holdings: include_progress_details
.then(|| result.daily_holdings[holding_start..].to_vec())
.unwrap_or_default(),
process_events: include_progress_details
.then(|| result.process_events[progress_process_start..].to_vec())
.unwrap_or_default(),
});
result.process_events.append(&mut process_events);
stock_equity_by_date.insert(execution_date, portfolio.total_equity());
continue;
};
@@ -2683,18 +2710,13 @@ where
)?;
let post_trade_open_orders = self.open_order_views();
let visible_order_events = result
.order_events
.iter()
.cloned()
.chain(report.order_events.iter().cloned())
.collect::<Vec<_>>();
let visible_fills = result
.fills
.iter()
.cloned()
.chain(report.fill_events.iter().cloned())
.collect::<Vec<_>>();
let mut broker_diagnostics = std::mem::take(&mut report.diagnostics);
self.extend_result(
&mut result,
std::mem::take(&mut report),
decision_date,
execution_date,
);
publish_phase_event(
&mut self.strategy,
&mut self.process_event_bus,
@@ -2728,8 +2750,8 @@ where
decision_date,
default_stage_time(ScheduleStage::AfterTrading),
),
order_events: visible_order_events.as_slice(),
fills: visible_fills.as_slice(),
order_events: result.order_events.as_slice(),
fills: result.fills.as_slice(),
})?;
publish_phase_event(
&mut self.strategy,
@@ -2765,8 +2787,8 @@ where
&mut process_events,
&mut self.process_event_bus,
default_stage_time(ScheduleStage::AfterTrading),
visible_order_events.as_slice(),
visible_fills.as_slice(),
result.order_events.as_slice(),
result.fills.as_slice(),
)?;
self.apply_strategy_directives(
execution_date,
@@ -2800,18 +2822,13 @@ where
report.account_events.extend(close_report.account_events);
report.diagnostics.extend(close_report.diagnostics);
let post_close_open_orders = self.open_order_views();
let visible_order_events_after_close = result
.order_events
.iter()
.cloned()
.chain(report.order_events.iter().cloned())
.collect::<Vec<_>>();
let visible_fills_after_close = result
.fills
.iter()
.cloned()
.chain(report.fill_events.iter().cloned())
.collect::<Vec<_>>();
broker_diagnostics.append(&mut report.diagnostics);
self.extend_result(
&mut result,
std::mem::take(&mut report),
decision_date,
execution_date,
);
publish_phase_event(
&mut self.strategy,
&mut self.process_event_bus,
@@ -2862,8 +2879,8 @@ where
decision_date,
default_stage_time(ScheduleStage::Settlement),
),
order_events: visible_order_events_after_close.as_slice(),
fills: visible_fills_after_close.as_slice(),
order_events: result.order_events.as_slice(),
fills: result.fills.as_slice(),
})?;
publish_phase_event(
&mut self.strategy,
@@ -2899,8 +2916,8 @@ where
&mut process_events,
&mut self.process_event_bus,
default_stage_time(ScheduleStage::Settlement),
visible_order_events_after_close.as_slice(),
visible_fills_after_close.as_slice(),
result.order_events.as_slice(),
result.fills.as_slice(),
)?;
self.apply_strategy_directives(
execution_date,
@@ -2927,8 +2944,8 @@ where
dynamic_universe_snapshot.as_ref(),
&subscriptions_snapshot,
&mut process_events,
visible_order_events_after_close.as_slice(),
visible_fills_after_close.as_slice(),
result.order_events.as_slice(),
result.fills.as_slice(),
)?;
merge_broker_report(&mut directive_report, management_fee_report);
publish_phase_event(
@@ -2949,12 +2966,17 @@ where
"settlement:post",
)?;
merge_broker_report(&mut report, directive_report);
let daily_fill_count = report.fill_events.len();
let day_orders = report.order_events.clone();
let day_fills = report.fill_events.clone();
let broker_diagnostics = report.diagnostics.clone();
let execution_risk_decisions = risk_decisions_from_order_events(&day_orders);
self.extend_result(&mut result, report, decision_date, execution_date);
broker_diagnostics.append(&mut report.diagnostics);
self.extend_result(
&mut result,
std::mem::take(&mut report),
decision_date,
execution_date,
);
let daily_fill_count = result.fills.len() - day_fill_start;
let daily_order_count = result.order_events.len() - day_order_start;
let execution_risk_decisions =
risk_decisions_from_order_events(&result.order_events[day_order_start..]);
result.risk_decisions.extend(decision.risk_decisions);
result.risk_decisions.extend(execution_risk_decisions);
@@ -2976,7 +2998,11 @@ where
.collect::<Vec<_>>()
.join(" | ");
let holdings_for_day = portfolio.holdings_summary(execution_date);
let day_process_events = process_events.clone();
let holding_start = result.daily_holdings.len();
let holding_count = holdings_for_day.len();
result.daily_holdings.extend(holdings_for_day);
let progress_process_start = result.process_events.len();
result.process_events.append(&mut process_events);
let aggregate_cash = self.aggregate_cash(&portfolio);
let aggregate_market_value = self.aggregate_market_value(&portfolio);
let aggregate_total_equity = self.aggregate_total_equity(&portfolio);
@@ -2997,7 +3023,6 @@ where
notes,
diagnostics,
});
result.daily_holdings.extend(holdings_for_day.clone());
let latest = result
.equity_curve
.last()
@@ -3012,16 +3037,28 @@ where
total_return: latest.unit_nav - 1.0,
benchmark_close: latest.benchmark_close,
daily_fill_count,
daily_order_count,
cumulative_trade_count: result.fills.len(),
holding_count: holdings_for_day.len(),
notes: latest.notes.clone(),
diagnostics: latest.diagnostics.clone(),
orders: day_orders,
fills: day_fills,
holdings: holdings_for_day,
process_events: day_process_events,
holding_count,
notes: include_progress_diagnostics
.then(|| latest.notes.clone())
.unwrap_or_default(),
diagnostics: include_progress_diagnostics
.then(|| latest.diagnostics.clone())
.unwrap_or_default(),
orders: include_progress_details
.then(|| result.order_events[day_order_start..].to_vec())
.unwrap_or_default(),
fills: include_progress_details
.then(|| result.fills[day_fill_start..].to_vec())
.unwrap_or_default(),
holdings: include_progress_details
.then(|| result.daily_holdings[holding_start..].to_vec())
.unwrap_or_default(),
process_events: include_progress_details
.then(|| result.process_events[progress_process_start..].to_vec())
.unwrap_or_default(),
});
result.process_events.extend(process_events);
stock_equity_by_date.insert(execution_date, portfolio.total_equity());
}
@@ -3045,16 +3082,13 @@ where
mut report: BrokerExecutionReport,
decision_date: NaiveDate,
execution_date: NaiveDate,
) -> BrokerExecutionReport {
) {
annotate_broker_report_dates(&mut report, decision_date, decision_date, execution_date);
result.order_events.extend(report.order_events.clone());
result.fills.extend(report.fill_events.clone());
result
.position_events
.extend(report.position_events.clone());
result.account_events.extend(report.account_events.clone());
result.process_events.extend(report.process_events.clone());
report
result.order_events.append(&mut report.order_events);
result.fills.append(&mut report.fill_events);
result.position_events.append(&mut report.position_events);
result.account_events.append(&mut report.account_events);
result.process_events.append(&mut report.process_events);
}
fn apply_corporate_actions(
@@ -4872,11 +4906,12 @@ mod tests {
.expect("dataset")
}
fn run_with_matching(
fn engine_with_matching(
matching_type: MatchingType,
execution_price_field: PriceField,
decision_lag_trading_days: usize,
) -> super::BacktestResult {
) -> BacktestEngine<BuyWhenDecisionDateStrategy, ChinaAShareCostModel, ChinaEquityRuleHooks>
{
let first = d(2025, 1, 2);
let broker = BrokerSimulator::new_with_execution_price(
ChinaAShareCostModel::default(),
@@ -4904,10 +4939,66 @@ mod tests {
broker,
config,
)
}
fn run_with_matching(
matching_type: MatchingType,
execution_price_field: PriceField,
decision_lag_trading_days: usize,
) -> super::BacktestResult {
engine_with_matching(
matching_type,
execution_price_field,
decision_lag_trading_days,
)
.run()
.expect("backtest run")
}
#[test]
fn compact_progress_keeps_counts_without_event_payload_clones() {
let mut engine = engine_with_matching(MatchingType::CurrentBarClose, PriceField::Close, 0);
let mut progress = Vec::new();
let result = engine
.run_with_progress_options(false, false, |event| {
progress.push((
event.daily_order_count,
event.daily_fill_count,
event.orders.len(),
event.fills.len(),
event.holdings.len(),
event.process_events.len(),
event.notes.len(),
event.diagnostics.len(),
));
})
.expect("compact progress run");
assert!(!progress.is_empty());
assert!(
progress
.iter()
.any(|(orders, fills, ..)| *orders > 0 && *fills > 0)
);
assert!(progress.iter().all(
|(_, _, orders, fills, holdings, process_events, notes, diagnostics)| {
*orders == 0
&& *fills == 0
&& *holdings == 0
&& *process_events == 0
&& *notes == 0
&& *diagnostics == 0
}
));
assert_eq!(
progress.iter().map(|value| value.0).sum::<usize>(),
result.order_events.len()
);
assert_eq!(
progress.iter().map(|value| value.1).sum::<usize>(),
result.fills.len()
);
}
fn full_day_coverage_engine(
data: DataSet,
date: NaiveDate,