修复开盘与跨日ETF执行时钟及资金阻断原因
This commit is contained in:
@@ -944,9 +944,17 @@ impl<C, R> BrokerSimulator<C, R> {
|
||||
}
|
||||
|
||||
fn new_open_order_submission_time(&self) -> Option<NaiveTime> {
|
||||
if self.matching_type == MatchingType::NextBarOpen && !self.runtime_stock_pool_followup.get() {
|
||||
NaiveTime::from_hms_opt(9, 30, 0)
|
||||
} else { self.order_origin().1 }
|
||||
if self.runtime_resting_order_origin.get().is_some() {
|
||||
return self.order_origin().1;
|
||||
}
|
||||
if self.matching_type == MatchingType::NextBarOpen
|
||||
&& !self.runtime_stock_pool_followup.get()
|
||||
{
|
||||
let open = NaiveTime::from_hms_opt(9, 30, 0).unwrap();
|
||||
Some(self.execution_clock().map_or(open, |clock| clock.max(open)))
|
||||
} else {
|
||||
self.execution_clock().or(self.order_origin().1)
|
||||
}
|
||||
}
|
||||
|
||||
fn resting_order_session_close(&self, date: NaiveDate, order: &OpenOrder) -> NaiveTime {
|
||||
@@ -8025,22 +8033,31 @@ where
|
||||
&& origin.accepted_date == date
|
||||
&& let Some(submitted) = origin.submission_time
|
||||
{
|
||||
Some(start_cursor.map_or(date.and_time(submitted), |cursor| cursor.max(date.and_time(submitted))))
|
||||
} else { start_cursor };
|
||||
let start_cursor = if algo_request.is_some() {
|
||||
match (start_cursor, self.execution_clock().map(|time| date.and_time(time))) {
|
||||
(Some(declared), Some(clock)) => Some(declared.max(clock)),
|
||||
(start, _) => start,
|
||||
}
|
||||
} else { start_cursor };
|
||||
let end_cursor = post_close_window.map(|window| {
|
||||
runtime_end_time.map_or(window.1, |end| window.1.min(date.and_time(end)))
|
||||
}).or_else(|| {
|
||||
algo_request
|
||||
.and_then(|request| request.end_time)
|
||||
.or(runtime_end_time)
|
||||
.map(|end_time| date.and_time(end_time))
|
||||
});
|
||||
Some(start_cursor.map_or(date.and_time(submitted), |cursor| {
|
||||
cursor.max(date.and_time(submitted))
|
||||
}))
|
||||
} else {
|
||||
start_cursor
|
||||
};
|
||||
// A configured session start is not the current submission clock.
|
||||
// Coarse callbacks and resting-order retries cannot execute backwards
|
||||
// into an earlier quote, even when they are not algorithm orders.
|
||||
let start_cursor = match (
|
||||
start_cursor,
|
||||
self.execution_clock().map(|time| date.and_time(time)),
|
||||
) {
|
||||
(Some(declared), Some(clock)) => Some(declared.max(clock)),
|
||||
(None, Some(clock)) => Some(clock),
|
||||
(start, None) => start,
|
||||
};
|
||||
let end_cursor = post_close_window
|
||||
.map(|window| runtime_end_time.map_or(window.1, |end| window.1.min(date.and_time(end))))
|
||||
.or_else(|| {
|
||||
algo_request
|
||||
.and_then(|request| request.end_time)
|
||||
.or(runtime_end_time)
|
||||
.map(|end_time| date.and_time(end_time))
|
||||
});
|
||||
let end_cursor = if end_cursor.is_none()
|
||||
&& matching_type == MatchingType::CurrentBarClose
|
||||
&& self.matching_type_uses_intraday_quotes()
|
||||
@@ -8253,6 +8270,7 @@ where
|
||||
let mut last_timestamp = None;
|
||||
let mut legs = Vec::new();
|
||||
let mut budget_block_reason = None;
|
||||
let mut budget_block_timestamp = None;
|
||||
let mut execution_block_reason = None;
|
||||
let mut execution_block_timestamp = None;
|
||||
let mut saw_non_blocked_execution_price = false;
|
||||
@@ -8393,6 +8411,7 @@ where
|
||||
self.quote_execution_price(snapshot, side, raw_quote_price, Some(take_qty), calibration)?;
|
||||
if !quote_price.is_finite() || quote_price <= 0.0 {
|
||||
budget_block_reason = Some("invalid execution price");
|
||||
budget_block_timestamp = Some(execution_at);
|
||||
take_qty = 0;
|
||||
break;
|
||||
}
|
||||
@@ -8411,6 +8430,7 @@ where
|
||||
.is_some_and(|limit| !Self::fixed_cash_fits(candidate_gross, limit))
|
||||
{
|
||||
budget_block_reason = Some("value budget limit");
|
||||
budget_block_timestamp = Some(execution_at);
|
||||
take_qty = self.decrement_order_quantity(
|
||||
take_qty,
|
||||
minimum_order_quantity,
|
||||
@@ -8436,6 +8456,7 @@ where
|
||||
break;
|
||||
}
|
||||
budget_block_reason = Some("insufficient cash after fees");
|
||||
budget_block_timestamp = Some(execution_at);
|
||||
take_qty = self.decrement_order_quantity(
|
||||
take_qty,
|
||||
minimum_order_quantity,
|
||||
@@ -8516,6 +8537,16 @@ where
|
||||
unfilled_reason: Some(reason),
|
||||
}));
|
||||
}
|
||||
if let Some(reason) = budget_block_reason {
|
||||
return Ok(Some(ExecutionFill {
|
||||
quantity: 0,
|
||||
next_cursor: budget_block_timestamp.expect("budget-blocked quote timestamp")
|
||||
+ Duration::seconds(1),
|
||||
legs: Vec::new(),
|
||||
liquidity_consumption: Vec::new(),
|
||||
unfilled_reason: Some(reason),
|
||||
}));
|
||||
}
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
@@ -8717,6 +8748,31 @@ mod tests {
|
||||
|
||||
include!("broker_stock_pool_batch_tests.rs");
|
||||
|
||||
#[test]
|
||||
fn queued_order_retains_the_real_creation_clock_when_retried() {
|
||||
let date = chrono::NaiveDate::from_ymd_opt(2026, 6, 1).unwrap();
|
||||
let open = NaiveTime::from_hms_opt(9, 30, 0).unwrap();
|
||||
let created = NaiveTime::from_hms_opt(9, 31, 0).unwrap();
|
||||
let later = NaiveTime::from_hms_opt(10, 0, 0).unwrap();
|
||||
for matching in [MatchingType::NextBarOpen, MatchingType::MinuteLast] {
|
||||
let broker =
|
||||
BrokerSimulator::new(ChinaAShareCostModel::default(), ChinaEquityRuleHooks)
|
||||
.with_matching_type(matching)
|
||||
.with_intraday_execution_start_time(open);
|
||||
broker.runtime_execution_clock.set(Some(created));
|
||||
assert_eq!(broker.new_open_order_submission_time(), Some(created));
|
||||
broker
|
||||
.runtime_resting_order_origin
|
||||
.set(Some(super::RestingOrderOrigin {
|
||||
created_date: Some(date),
|
||||
submission_time: Some(created),
|
||||
accepted_date: date,
|
||||
}));
|
||||
broker.runtime_execution_clock.set(Some(later));
|
||||
assert_eq!(broker.new_open_order_submission_time(), Some(created));
|
||||
}
|
||||
}
|
||||
|
||||
fn test_open_order(order_id: u64) -> OpenOrder {
|
||||
OpenOrder {
|
||||
order_id,
|
||||
|
||||
+1199
-349
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user