补齐回测交易日期审计字段

This commit is contained in:
boris
2026-07-05 08:47:52 +08:00
parent 8543c3ab6d
commit ba2470aefe
4 changed files with 330 additions and 14 deletions
+190 -1
View File
@@ -46,6 +46,8 @@ struct ExecutionFill {
#[derive(Debug, Clone)]
struct OpenOrder {
order_id: u64,
decision_date: Option<NaiveDate>,
order_created_date: Option<NaiveDate>,
symbol: String,
side: OrderSide,
requested_quantity: u32,
@@ -176,6 +178,8 @@ pub struct BrokerSimulator<C, R> {
intraday_execution_start_time: Option<NaiveTime>,
runtime_intraday_start_time: Cell<Option<NaiveTime>>,
runtime_intraday_end_time: Cell<Option<NaiveTime>>,
runtime_decision_date: Cell<Option<NaiveDate>>,
runtime_order_created_date: Cell<Option<NaiveDate>>,
next_order_id: Cell<u64>,
open_orders: RefCell<Vec<OpenOrder>>,
}
@@ -201,6 +205,8 @@ impl<C, R> BrokerSimulator<C, R> {
intraday_execution_start_time: None,
runtime_intraday_start_time: Cell::new(None),
runtime_intraday_end_time: Cell::new(None),
runtime_decision_date: Cell::new(None),
runtime_order_created_date: Cell::new(None),
next_order_id: Cell::new(1),
open_orders: RefCell::new(Vec::new()),
}
@@ -230,6 +236,8 @@ impl<C, R> BrokerSimulator<C, R> {
intraday_execution_start_time: None,
runtime_intraday_start_time: Cell::new(None),
runtime_intraday_end_time: Cell::new(None),
runtime_decision_date: Cell::new(None),
runtime_order_created_date: Cell::new(None),
next_order_id: Cell::new(1),
open_orders: RefCell::new(Vec::new()),
}
@@ -640,6 +648,37 @@ where
portfolio: &mut PortfolioState,
data: &DataSet,
decision: &StrategyDecision,
) -> Result<BrokerExecutionReport, BacktestError> {
self.execute_with_event_dates(date, date, date, portfolio, data, decision)
}
pub fn execute_with_event_dates(
&self,
date: NaiveDate,
decision_date: NaiveDate,
order_created_date: NaiveDate,
portfolio: &mut PortfolioState,
data: &DataSet,
decision: &StrategyDecision,
) -> Result<BrokerExecutionReport, BacktestError> {
let previous_decision_date = self.runtime_decision_date.get();
let previous_order_created_date = self.runtime_order_created_date.get();
self.runtime_decision_date.set(Some(decision_date));
self.runtime_order_created_date
.set(Some(order_created_date));
let result = self.execute_with_runtime_dates(date, portfolio, data, decision);
self.runtime_decision_date.set(previous_decision_date);
self.runtime_order_created_date
.set(previous_order_created_date);
result
}
fn execute_with_runtime_dates(
&self,
date: NaiveDate,
portfolio: &mut PortfolioState,
data: &DataSet,
decision: &StrategyDecision,
) -> Result<BrokerExecutionReport, BacktestError> {
let mut report = BrokerExecutionReport::default();
let mut intraday_turnover = BTreeMap::<String, u32>::new();
@@ -769,12 +808,35 @@ where
decision: &StrategyDecision,
start_time: Option<NaiveTime>,
end_time: Option<NaiveTime>,
) -> Result<BrokerExecutionReport, BacktestError> {
self.execute_between_with_event_dates(
date, date, date, portfolio, data, decision, start_time, end_time,
)
}
pub fn execute_between_with_event_dates(
&self,
date: NaiveDate,
decision_date: NaiveDate,
order_created_date: NaiveDate,
portfolio: &mut PortfolioState,
data: &DataSet,
decision: &StrategyDecision,
start_time: Option<NaiveTime>,
end_time: Option<NaiveTime>,
) -> Result<BrokerExecutionReport, BacktestError> {
let previous_start_time = self.runtime_intraday_start_time.get();
let previous_end_time = self.runtime_intraday_end_time.get();
self.runtime_intraday_start_time.set(start_time);
self.runtime_intraday_end_time.set(end_time);
let result = self.execute(date, portfolio, data, decision);
let result = self.execute_with_event_dates(
date,
decision_date,
order_created_date,
portfolio,
data,
decision,
);
self.runtime_intraday_start_time.set(previous_start_time);
self.runtime_intraday_end_time.set(previous_end_time);
result
@@ -1389,6 +1451,34 @@ where
open_orders.push(open_order);
}
fn current_decision_date(&self, date: NaiveDate) -> NaiveDate {
self.runtime_decision_date.get().unwrap_or(date)
}
fn current_order_created_date(&self, date: NaiveDate) -> NaiveDate {
self.runtime_order_created_date.get().unwrap_or(date)
}
fn annotate_report_range(
report: &mut BrokerExecutionReport,
order_event_start: usize,
fill_event_start: usize,
decision_date: NaiveDate,
order_created_date: NaiveDate,
execution_date: NaiveDate,
) {
for event in &mut report.order_events[order_event_start..] {
event.decision_date.get_or_insert(decision_date);
event.order_created_date.get_or_insert(order_created_date);
event.execution_date.get_or_insert(execution_date);
}
for fill in &mut report.fill_events[fill_event_start..] {
fill.decision_date.get_or_insert(decision_date);
fill.order_created_date.get_or_insert(order_created_date);
fill.execution_date.get_or_insert(execution_date);
}
}
fn clear_open_order(&self, order_id: u64) {
self.open_orders
.borrow_mut()
@@ -1461,6 +1551,8 @@ where
std::mem::take(&mut *open_orders)
};
for order in pending_orders {
let order_event_start = report.order_events.len();
let fill_event_start = report.fill_events.len();
let signed_quantity = if order.side == OrderSide::Buy {
order.remaining_quantity as i32
} else {
@@ -1482,6 +1574,14 @@ where
commission_state,
report,
)?;
Self::annotate_report_range(
report,
order_event_start,
fill_event_start,
order.decision_date.unwrap_or(date),
order.order_created_date.unwrap_or(date),
date,
);
}
Ok(())
}
@@ -1597,6 +1697,9 @@ where
);
report.order_events.push(OrderEvent {
date,
decision_date: order.decision_date,
order_created_date: order.order_created_date,
execution_date: Some(date),
order_id: Some(order.order_id),
symbol: order.symbol.clone(),
side: order.side,
@@ -1632,6 +1735,9 @@ where
);
report.order_events.push(OrderEvent {
date,
decision_date: order.decision_date,
order_created_date: order.order_created_date,
execution_date: Some(date),
order_id: Some(order.order_id),
symbol: order.symbol.clone(),
side: order.side,
@@ -1692,6 +1798,9 @@ where
);
report.order_events.push(OrderEvent {
date,
decision_date: None,
order_created_date: None,
execution_date: None,
order_id: Some(order_id),
symbol: symbol.to_string(),
side,
@@ -1738,6 +1847,9 @@ where
}
report.order_events.push(OrderEvent {
date,
decision_date: None,
order_created_date: None,
execution_date: None,
order_id: Some(order_id),
symbol: symbol.to_string(),
side,
@@ -2639,6 +2751,9 @@ where
};
report.order_events.push(OrderEvent {
date,
decision_date: None,
order_created_date: None,
execution_date: None,
order_id: Some(order_id),
symbol: symbol.to_string(),
side: OrderSide::Sell,
@@ -2704,6 +2819,8 @@ where
if allow_pending_limit {
self.upsert_open_order(OpenOrder {
order_id,
decision_date: Some(self.current_decision_date(date)),
order_created_date: Some(self.current_order_created_date(date)),
symbol: symbol.to_string(),
side: OrderSide::Sell,
requested_quantity: requested_qty,
@@ -2714,6 +2831,9 @@ where
});
report.order_events.push(OrderEvent {
date,
decision_date: None,
order_created_date: None,
execution_date: None,
order_id: Some(order_id),
symbol: symbol.to_string(),
side: OrderSide::Sell,
@@ -2735,6 +2855,9 @@ where
}
report.order_events.push(OrderEvent {
date,
decision_date: None,
order_created_date: None,
execution_date: None,
order_id: Some(order_id),
symbol: symbol.to_string(),
side: OrderSide::Sell,
@@ -2765,6 +2888,8 @@ where
.unwrap_or("no sellable quantity");
self.upsert_open_order(OpenOrder {
order_id,
decision_date: Some(self.current_decision_date(date)),
order_created_date: Some(self.current_order_created_date(date)),
symbol: symbol.to_string(),
side: OrderSide::Sell,
requested_quantity: requested_qty,
@@ -2775,6 +2900,9 @@ where
});
report.order_events.push(OrderEvent {
date,
decision_date: None,
order_created_date: None,
execution_date: None,
order_id: Some(order_id),
symbol: symbol.to_string(),
side: OrderSide::Sell,
@@ -2796,6 +2924,9 @@ where
}
report.order_events.push(OrderEvent {
date,
decision_date: None,
order_created_date: None,
execution_date: None,
order_id: Some(order_id),
symbol: symbol.to_string(),
side: OrderSide::Sell,
@@ -2891,6 +3022,8 @@ where
if allow_pending_limit && Self::limit_order_can_remain_open(Some(detail)) {
self.upsert_open_order(OpenOrder {
order_id,
decision_date: Some(self.current_decision_date(date)),
order_created_date: Some(self.current_order_created_date(date)),
symbol: symbol.to_string(),
side: OrderSide::Sell,
requested_quantity: requested_qty,
@@ -2901,6 +3034,9 @@ where
});
report.order_events.push(OrderEvent {
date,
decision_date: None,
order_created_date: None,
execution_date: None,
order_id: Some(order_id),
symbol: symbol.to_string(),
side: OrderSide::Sell,
@@ -2922,6 +3058,9 @@ where
}
report.order_events.push(OrderEvent {
date,
decision_date: None,
order_created_date: None,
execution_date: None,
order_id: Some(order_id),
symbol: symbol.to_string(),
side: OrderSide::Sell,
@@ -2973,6 +3112,9 @@ where
report.fill_events.push(FillEvent {
date,
decision_date: None,
order_created_date: None,
execution_date: None,
order_id: Some(order_id),
symbol: symbol.to_string(),
side: OrderSide::Sell,
@@ -3032,6 +3174,8 @@ where
if keep_open {
self.upsert_open_order(OpenOrder {
order_id,
decision_date: Some(self.current_decision_date(date)),
order_created_date: Some(self.current_order_created_date(date)),
symbol: symbol.to_string(),
side: OrderSide::Sell,
requested_quantity: requested_qty,
@@ -3081,6 +3225,9 @@ where
report.order_events.push(OrderEvent {
date,
decision_date: None,
order_created_date: None,
execution_date: None,
order_id: Some(order_id),
symbol: symbol.to_string(),
side: OrderSide::Sell,
@@ -3126,6 +3273,9 @@ where
if current_qty == 0 {
report.order_events.push(OrderEvent {
date,
decision_date: None,
order_created_date: None,
execution_date: None,
order_id: None,
symbol: symbol.to_string(),
side: OrderSide::Sell,
@@ -3210,6 +3360,9 @@ where
} else {
report.order_events.push(OrderEvent {
date,
decision_date: None,
order_created_date: None,
execution_date: None,
order_id: None,
symbol: symbol.to_string(),
side: if current_qty > 0 {
@@ -3269,6 +3422,9 @@ where
if current_qty == 0 {
report.order_events.push(OrderEvent {
date,
decision_date: None,
order_created_date: None,
execution_date: None,
order_id: None,
symbol: symbol.to_string(),
side: OrderSide::Sell,
@@ -3323,6 +3479,9 @@ where
} else {
report.order_events.push(OrderEvent {
date,
decision_date: None,
order_created_date: None,
execution_date: None,
order_id: None,
symbol: symbol.to_string(),
side: if current_qty > 0 {
@@ -3419,6 +3578,9 @@ where
} else {
report.order_events.push(OrderEvent {
date,
decision_date: None,
order_created_date: None,
execution_date: None,
order_id: None,
symbol: symbol.to_string(),
side: if current_qty > 0 {
@@ -4279,6 +4441,9 @@ where
};
report.order_events.push(OrderEvent {
date,
decision_date: None,
order_created_date: None,
execution_date: None,
order_id: Some(order_id),
symbol: symbol.to_string(),
side: OrderSide::Buy,
@@ -4333,6 +4498,8 @@ where
if allow_pending_limit {
self.upsert_open_order(OpenOrder {
order_id,
decision_date: Some(self.current_decision_date(date)),
order_created_date: Some(self.current_order_created_date(date)),
symbol: symbol.to_string(),
side: OrderSide::Buy,
requested_quantity: requested_qty,
@@ -4343,6 +4510,9 @@ where
});
report.order_events.push(OrderEvent {
date,
decision_date: None,
order_created_date: None,
execution_date: None,
order_id: Some(order_id),
symbol: symbol.to_string(),
side: OrderSide::Buy,
@@ -4364,6 +4534,9 @@ where
}
report.order_events.push(OrderEvent {
date,
decision_date: None,
order_created_date: None,
execution_date: None,
order_id: Some(order_id),
symbol: symbol.to_string(),
side: OrderSide::Buy,
@@ -4523,6 +4696,8 @@ where
if allow_pending_limit && Self::limit_order_can_remain_open(Some(detail)) {
self.upsert_open_order(OpenOrder {
order_id,
decision_date: Some(self.current_decision_date(date)),
order_created_date: Some(self.current_order_created_date(date)),
symbol: symbol.to_string(),
side: OrderSide::Buy,
requested_quantity: requested_qty,
@@ -4533,6 +4708,9 @@ where
});
report.order_events.push(OrderEvent {
date,
decision_date: None,
order_created_date: None,
execution_date: None,
order_id: Some(order_id),
symbol: symbol.to_string(),
side: OrderSide::Buy,
@@ -4554,6 +4732,9 @@ where
}
report.order_events.push(OrderEvent {
date,
decision_date: None,
order_created_date: None,
execution_date: None,
order_id: Some(order_id),
symbol: symbol.to_string(),
side: OrderSide::Buy,
@@ -4609,6 +4790,9 @@ where
report.fill_events.push(FillEvent {
date,
decision_date: None,
order_created_date: None,
execution_date: None,
order_id: Some(order_id),
symbol: symbol.to_string(),
side: OrderSide::Buy,
@@ -4666,6 +4850,8 @@ where
if keep_open {
self.upsert_open_order(OpenOrder {
order_id,
decision_date: Some(self.current_decision_date(date)),
order_created_date: Some(self.current_order_created_date(date)),
symbol: symbol.to_string(),
side: OrderSide::Buy,
requested_quantity: requested_qty,
@@ -4715,6 +4901,9 @@ where
report.order_events.push(OrderEvent {
date,
decision_date: None,
order_created_date: None,
execution_date: None,
order_id: Some(order_id),
symbol: symbol.to_string(),
side: OrderSide::Buy,