修复分钟成交量限制撮合

This commit is contained in:
boris
2026-07-06 08:05:59 +08:00
parent a3415095a7
commit 7c867f1788
+80 -1
View File
@@ -5589,7 +5589,7 @@ where
if remaining_qty == 0 {
break;
}
let available_qty = if quote_quantity_limited {
let mut available_qty = if quote_quantity_limited {
let top_level_liquidity = match side {
OrderSide::Buy => quote.ask1_volume,
OrderSide::Sell => quote.bid1_volume,
@@ -5600,6 +5600,15 @@ where
} else {
remaining_qty
};
if self.volume_limit {
let raw_limit = ((quote.volume_delta as f64) * self.volume_percent).floor() as u32;
let volume_limited = if side == OrderSide::Sell && allow_odd_lot_sell {
raw_limit
} else {
self.round_buy_quantity(raw_limit, minimum_order_quantity, order_step_size)
};
available_qty = available_qty.min(volume_limited);
}
if available_qty == 0 {
continue;
}
@@ -6076,6 +6085,76 @@ mod tests {
assert!(liquidity_limited.quote_quantity_limited(MatchingType::MinuteLast));
}
#[test]
fn minute_last_volume_limit_caps_quote_volume_delta_without_liquidity_limit() {
let date = chrono::NaiveDate::from_ymd_opt(2025, 1, 2).expect("valid date");
let broker = BrokerSimulator::new(ChinaAShareCostModel::default(), ChinaEquityRuleHooks)
.with_matching_type(MatchingType::MinuteLast)
.with_volume_limit(true)
.with_volume_percent(0.25)
.with_liquidity_limit(false);
let mut quote = limit_test_quote(10.0, 9.99, 10.01);
quote.timestamp = date.and_hms_opt(10, 15, 0).expect("valid timestamp");
quote.volume_delta = 4_900;
quote.ask1_volume = 0;
quote.bid1_volume = 0;
let fill = broker
.select_execution_fill(
&limit_test_snapshot(),
&[quote],
OrderSide::Buy,
MatchingType::MinuteLast,
Some(date.and_hms_opt(10, 15, 0).expect("valid timestamp")),
None,
3_200,
100,
100,
100,
false,
None,
None,
None,
)
.expect("minute last fill");
assert_eq!(fill.quantity, 1_200);
}
#[test]
fn minute_last_volume_limit_rejects_sub_lot_quote_volume() {
let date = chrono::NaiveDate::from_ymd_opt(2025, 1, 2).expect("valid date");
let broker = BrokerSimulator::new(ChinaAShareCostModel::default(), ChinaEquityRuleHooks)
.with_matching_type(MatchingType::MinuteLast)
.with_volume_limit(true)
.with_volume_percent(0.25)
.with_liquidity_limit(false);
let mut quote = limit_test_quote(10.0, 9.99, 10.01);
quote.timestamp = date.and_hms_opt(10, 15, 0).expect("valid timestamp");
quote.volume_delta = 100;
quote.ask1_volume = 10_000;
quote.bid1_volume = 10_000;
let fill = broker.select_execution_fill(
&limit_test_snapshot(),
&[quote],
OrderSide::Buy,
MatchingType::MinuteLast,
Some(date.and_hms_opt(10, 15, 0).expect("valid timestamp")),
None,
3_200,
100,
100,
100,
false,
None,
None,
None,
);
assert!(fill.is_none());
}
#[test]
fn daily_matching_does_not_require_intraday_quote_quantity() {
let broker = BrokerSimulator::new(ChinaAShareCostModel::default(), ChinaEquityRuleHooks)