修正精确分钟执行价回退逻辑
This commit is contained in:
@@ -1285,17 +1285,21 @@ where
|
|||||||
|| self.quote_has_executable_liquidity(quote, side, matching_type))
|
|| self.quote_has_executable_liquidity(quote, side, matching_type))
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
quotes
|
let latest = quotes
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|quote| {
|
.filter(|quote| {
|
||||||
quote.timestamp <= cursor
|
quote.timestamp <= cursor
|
||||||
&& self
|
&& self
|
||||||
.select_quote_reference_price(snapshot, quote, side, matching_type)
|
.select_quote_reference_price(snapshot, quote, side, matching_type)
|
||||||
.is_some()
|
.is_some()
|
||||||
&& (!require_executable_liquidity
|
|
||||||
|| self.quote_has_executable_liquidity(quote, side, matching_type))
|
|
||||||
})
|
})
|
||||||
.max_by_key(|quote| quote.timestamp)
|
.max_by_key(|quote| quote.timestamp)?;
|
||||||
|
if require_executable_liquidity
|
||||||
|
&& !self.quote_has_executable_liquidity(latest, side, matching_type)
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(latest)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_limit_shares(
|
fn process_limit_shares(
|
||||||
@@ -6171,6 +6175,45 @@ mod tests {
|
|||||||
assert_eq!(fill.legs[0].price, 10.0);
|
assert_eq!(fill.legs[0].price, 10.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn minute_last_exact_time_does_not_fallback_to_older_liquid_quote() {
|
||||||
|
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(true);
|
||||||
|
let mut quote_0931 = limit_test_quote(10.0, 9.99, 10.01);
|
||||||
|
quote_0931.timestamp = date.and_hms_opt(9, 31, 0).expect("valid timestamp");
|
||||||
|
quote_0931.volume_delta = 10_000;
|
||||||
|
quote_0931.ask1_volume = 10_000;
|
||||||
|
quote_0931.bid1_volume = 10_000;
|
||||||
|
let mut quote_1015 = limit_test_quote(10.2, 10.19, 10.21);
|
||||||
|
quote_1015.timestamp = date.and_hms_opt(10, 15, 0).expect("valid timestamp");
|
||||||
|
quote_1015.volume_delta = 0;
|
||||||
|
quote_1015.ask1_volume = 10_000;
|
||||||
|
quote_1015.bid1_volume = 10_000;
|
||||||
|
|
||||||
|
let fill = broker.select_execution_fill(
|
||||||
|
&limit_test_snapshot(),
|
||||||
|
&[quote_0931, quote_1015],
|
||||||
|
OrderSide::Sell,
|
||||||
|
MatchingType::MinuteLast,
|
||||||
|
Some(date.and_hms_opt(10, 15, 0).expect("valid timestamp")),
|
||||||
|
Some(date.and_hms_opt(10, 15, 0).expect("valid timestamp")),
|
||||||
|
1_000,
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(fill.is_none(), "{fill:?}");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn minute_last_volume_limit_rejects_sub_lot_quote_volume() {
|
fn minute_last_volume_limit_rejects_sub_lot_quote_volume() {
|
||||||
let date = chrono::NaiveDate::from_ymd_opt(2025, 1, 2).expect("valid date");
|
let date = chrono::NaiveDate::from_ymd_opt(2025, 1, 2).expect("valid date");
|
||||||
|
|||||||
Reference in New Issue
Block a user