修复分钟成交量限制撮合
This commit is contained in:
@@ -5589,7 +5589,7 @@ where
|
|||||||
if remaining_qty == 0 {
|
if remaining_qty == 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let available_qty = if quote_quantity_limited {
|
let mut available_qty = if quote_quantity_limited {
|
||||||
let top_level_liquidity = match side {
|
let top_level_liquidity = match side {
|
||||||
OrderSide::Buy => quote.ask1_volume,
|
OrderSide::Buy => quote.ask1_volume,
|
||||||
OrderSide::Sell => quote.bid1_volume,
|
OrderSide::Sell => quote.bid1_volume,
|
||||||
@@ -5600,6 +5600,15 @@ where
|
|||||||
} else {
|
} else {
|
||||||
remaining_qty
|
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 {
|
if available_qty == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -6076,6 +6085,76 @@ mod tests {
|
|||||||
assert!(liquidity_limited.quote_quantity_limited(MatchingType::MinuteLast));
|
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]
|
#[test]
|
||||||
fn daily_matching_does_not_require_intraday_quote_quantity() {
|
fn daily_matching_does_not_require_intraday_quote_quantity() {
|
||||||
let broker = BrokerSimulator::new(ChinaAShareCostModel::default(), ChinaEquityRuleHooks)
|
let broker = BrokerSimulator::new(ChinaAShareCostModel::default(), ChinaEquityRuleHooks)
|
||||||
|
|||||||
Reference in New Issue
Block a user