Align order lifecycle with rqalpha close semantics

This commit is contained in:
boris
2026-04-23 03:41:49 -07:00
parent 8906490a40
commit b657205103
5 changed files with 314 additions and 27 deletions

View File

@@ -43,6 +43,8 @@ struct OpenOrder {
order_id: u64,
symbol: String,
side: OrderSide,
requested_quantity: u32,
filled_quantity: u32,
remaining_quantity: u32,
limit_price: f64,
reason: String,
@@ -947,7 +949,7 @@ where
}
};
if let Some(order) = canceled {
self.emit_canceled_open_order(date, order, reason, report);
self.emit_user_canceled_open_order(date, order, reason, report);
}
}
@@ -973,7 +975,7 @@ where
canceled
};
for order in canceled {
self.emit_canceled_open_order(date, order, reason, report);
self.emit_user_canceled_open_order(date, order, reason, report);
}
}
@@ -988,38 +990,87 @@ where
std::mem::take(&mut *open_orders)
};
for order in canceled {
self.emit_canceled_open_order(date, order, reason, report);
self.emit_user_canceled_open_order(date, order, reason, report);
}
}
fn emit_canceled_open_order(
fn emit_user_canceled_open_order(
&self,
date: NaiveDate,
order: OpenOrder,
reason: &str,
report: &mut BrokerExecutionReport,
) {
Self::emit_order_process_event(
report,
date,
ProcessEventKind::OrderPendingCancel,
order.order_id,
&order.symbol,
order.side,
format!("reason={reason}"),
);
report.order_events.push(OrderEvent {
date,
order_id: Some(order.order_id),
symbol: order.symbol.clone(),
side: order.side,
requested_quantity: order.remaining_quantity,
filled_quantity: 0,
requested_quantity: order.requested_quantity,
filled_quantity: order.filled_quantity,
status: OrderStatus::Canceled,
reason: format!("{reason}: canceled open order"),
reason: format!("{reason}: canceled by user"),
});
Self::emit_order_process_event(
report,
date,
ProcessEventKind::OrderUnsolicitedUpdate,
ProcessEventKind::OrderCancellationPass,
order.order_id,
&order.symbol,
order.side,
"status=Canceled reason=canceled open order",
format!(
"status=Canceled requested_quantity={} filled_quantity={}",
order.requested_quantity, order.filled_quantity
),
);
}
pub fn after_trading(&self, date: NaiveDate) -> BrokerExecutionReport {
let mut report = BrokerExecutionReport::default();
let pending = {
let mut open_orders = self.open_orders.borrow_mut();
std::mem::take(&mut *open_orders)
};
for order in pending {
let market_close_reason = format!(
"Order Rejected: {} can not match. Market close.",
order.symbol
);
report.order_events.push(OrderEvent {
date,
order_id: Some(order.order_id),
symbol: order.symbol.clone(),
side: order.side,
requested_quantity: order.requested_quantity,
filled_quantity: order.filled_quantity,
status: OrderStatus::Rejected,
reason: market_close_reason.clone(),
});
Self::emit_order_process_event(
&mut report,
date,
ProcessEventKind::OrderUnsolicitedUpdate,
order.order_id,
&order.symbol,
order.side,
format!(
"status=Rejected requested_quantity={} filled_quantity={} reason={market_close_reason}",
order.requested_quantity, order.filled_quantity
),
);
}
report
}
fn emit_order_process_event(
report: &mut BrokerExecutionReport,
date: NaiveDate,
@@ -1583,6 +1634,8 @@ where
order_id,
symbol: symbol.to_string(),
side: OrderSide::Sell,
requested_quantity: requested_qty,
filled_quantity: 0,
remaining_quantity: requested_qty,
limit_price: limit_price.expect("limit price for pending limit sell"),
reason: reason.to_string(),
@@ -1642,6 +1695,8 @@ where
order_id,
symbol: symbol.to_string(),
side: OrderSide::Sell,
requested_quantity: requested_qty,
filled_quantity: 0,
remaining_quantity: requested_qty,
limit_price: limit_price.expect("limit price for pending limit sell"),
reason: reason.to_string(),
@@ -1748,6 +1803,8 @@ where
order_id,
symbol: symbol.to_string(),
side: OrderSide::Sell,
requested_quantity: requested_qty,
filled_quantity: 0,
remaining_quantity: requested_qty,
limit_price: limit_price.expect("limit price for pending limit sell"),
reason: reason.to_string(),
@@ -1883,6 +1940,8 @@ where
order_id,
symbol: symbol.to_string(),
side: OrderSide::Sell,
requested_quantity: requested_qty,
filled_quantity: filled_qty,
remaining_quantity: remaining_qty,
limit_price: limit_price.expect("limit price for pending limit sell"),
reason: reason.to_string(),
@@ -2639,6 +2698,8 @@ where
order_id,
symbol: symbol.to_string(),
side: OrderSide::Buy,
requested_quantity: requested_qty,
filled_quantity: 0,
remaining_quantity: requested_qty,
limit_price: limit_price.expect("limit price for pending limit buy"),
reason: reason.to_string(),
@@ -2770,6 +2831,8 @@ where
order_id,
symbol: symbol.to_string(),
side: OrderSide::Buy,
requested_quantity: requested_qty,
filled_quantity: 0,
remaining_quantity: requested_qty,
limit_price: limit_price.expect("limit price for pending limit buy"),
reason: reason.to_string(),
@@ -2902,6 +2965,8 @@ where
order_id,
symbol: symbol.to_string(),
side: OrderSide::Buy,
requested_quantity: requested_qty,
filled_quantity: filled_qty,
remaining_quantity: remaining_qty,
limit_price: limit_price.expect("limit price for pending limit buy"),
reason: reason.to_string(),

View File

@@ -330,11 +330,6 @@ where
ProcessEventKind::PostOnDay,
"on_day:post",
);
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();
self.extend_result(&mut result, report);
portfolio.update_prices(execution_date, &self.data, PriceField::Close)?;
@@ -358,6 +353,13 @@ where
ProcessEventKind::AfterTrading,
"after_trading",
);
let mut close_report = self.broker.after_trading(execution_date);
process_events.append(&mut close_report.process_events);
report.order_events.extend(close_report.order_events);
report.fill_events.extend(close_report.fill_events);
report.position_events.extend(close_report.position_events);
report.account_events.extend(close_report.account_events);
report.diagnostics.extend(close_report.diagnostics);
push_phase_event(
&mut process_events,
execution_date,
@@ -383,6 +385,11 @@ where
ProcessEventKind::PostSettlement,
"settlement:post",
);
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();
self.extend_result(&mut result, report);
let benchmark =
self.data

View File

@@ -110,6 +110,8 @@ pub enum ProcessEventKind {
PostSettlement,
OrderPendingNew,
OrderCreationPass,
OrderPendingCancel,
OrderCancellationPass,
OrderUnsolicitedUpdate,
Trade,
}