fix: keep decision estimates independent and use timed capacity in fill fixtures
This commit is contained in:
@@ -7725,7 +7725,7 @@ where
|
||||
} else {
|
||||
remaining_qty.min(available_qty)
|
||||
};
|
||||
if !(side == OrderSide::Sell && allow_odd_lot_sell && take_qty == remaining_qty) {
|
||||
if !(side == OrderSide::Sell && allow_odd_lot_sell) {
|
||||
take_qty =
|
||||
self.round_buy_quantity(take_qty, minimum_order_quantity, order_step_size);
|
||||
}
|
||||
@@ -8397,6 +8397,7 @@ mod tests {
|
||||
vec![dated_limit_test_benchmark(first), dated_limit_test_benchmark(second)],
|
||||
).unwrap();
|
||||
let broker = BrokerSimulator::new(ChinaAShareCostModel::default(), ChinaEquityRuleHooks)
|
||||
.with_volume_capacity_mode(crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit)
|
||||
.with_matching_type(MatchingType::CurrentBarClose);
|
||||
let mut portfolio = PortfolioState::new(100_000.0);
|
||||
broker.execute(first, &mut portfolio, &data, &next_open_buy_decision()).unwrap();
|
||||
@@ -8423,6 +8424,7 @@ mod tests {
|
||||
let data = DataSet::from_components(vec![limit_test_instrument()], vec![limit_test_snapshot()],
|
||||
Vec::new(), vec![limit_test_candidate(true, true)], vec![limit_test_benchmark()]).unwrap();
|
||||
let broker = BrokerSimulator::new(ChinaAShareCostModel::default(), ChinaEquityRuleHooks)
|
||||
.with_volume_capacity_mode(crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit)
|
||||
.with_matching_type(MatchingType::CurrentBarClose);
|
||||
broker.upsert_open_order(test_open_order(99));
|
||||
let mut decision = StrategyDecision::default();
|
||||
@@ -8450,6 +8452,7 @@ mod tests {
|
||||
dated_limit_test_candidate(second, false, false, true, true)],
|
||||
vec![dated_limit_test_benchmark(first), dated_limit_test_benchmark(second)]).unwrap();
|
||||
let broker = BrokerSimulator::new(ChinaAShareCostModel::default(), ChinaEquityRuleHooks)
|
||||
.with_volume_capacity_mode(crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit)
|
||||
.with_matching_type(MatchingType::NextBarOpen);
|
||||
let mut portfolio = PortfolioState::new(1_000_000.0);
|
||||
let mut initial = StrategyDecision::default();
|
||||
|
||||
@@ -7028,6 +7028,7 @@ mod tests {
|
||||
let third = d(2025, 1, 6);
|
||||
let fourth = d(2025, 1, 7);
|
||||
let broker = scheduled_next_open_broker(FidcRiskControlConfig::default())
|
||||
.with_volume_capacity_mode(crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit)
|
||||
.with_volume_limit(true)
|
||||
.with_volume_percent(0.25);
|
||||
let result = run_scheduled_round_trip_next_open_with_dataset_and_broker(
|
||||
@@ -7055,12 +7056,13 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn next_bar_open_sell_volume_limit_rejects_execution_day_zero_volume() {
|
||||
fn next_bar_open_session_audit_flags_zero_volume_without_rewriting_fills() {
|
||||
let first = d(2025, 1, 2);
|
||||
let second = d(2025, 1, 3);
|
||||
let third = d(2025, 1, 6);
|
||||
let fourth = d(2025, 1, 7);
|
||||
let broker = scheduled_next_open_broker(FidcRiskControlConfig::default())
|
||||
.with_volume_capacity_mode(crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit)
|
||||
.with_volume_limit(true)
|
||||
.with_volume_percent(0.25);
|
||||
let result = run_scheduled_round_trip_next_open_with_dataset_and_broker(
|
||||
@@ -7081,7 +7083,10 @@ mod tests {
|
||||
broker,
|
||||
);
|
||||
|
||||
assert_round_trip_sell_canceled_with_reason(&result, "daily volume limit");
|
||||
assert!(result.fills.iter().any(|fill| fill.side == OrderSide::Sell && fill.date == fourth));
|
||||
assert_eq!(result.capacity_audit.audit_passed, Some(false));
|
||||
assert_eq!(result.capacity_audit.failed_symbol_sessions, 1);
|
||||
assert!(result.process_events.iter().any(|event| event.kind == ProcessEventKind::SessionCapacityAudit));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -3157,10 +3157,16 @@ impl PlatformExprStrategy {
|
||||
allow_odd_lot_sell: bool,
|
||||
current_fill_quantity: u32,
|
||||
execution_state: &ProjectedExecutionState,
|
||||
future_execution: bool,
|
||||
) -> Result<Option<u32>, BacktestError> {
|
||||
if requested_qty == 0 {
|
||||
return Ok(Some(0));
|
||||
}
|
||||
if future_execution {
|
||||
// A decision-day estimate cannot use tomorrow's liquidity to
|
||||
// change the orders created today.
|
||||
return Ok(Some(requested_qty));
|
||||
}
|
||||
|
||||
let constraints = self.config.risk_config.trading_constraints;
|
||||
let mut max_fill = requested_qty;
|
||||
@@ -3333,6 +3339,7 @@ impl PlatformExprStrategy {
|
||||
allow_odd_lot_sell,
|
||||
filled_qty,
|
||||
execution_state,
|
||||
Self::defer_projection_execution_risk(ctx, date),
|
||||
)?
|
||||
.unwrap_or(0);
|
||||
if available_qty == 0 {
|
||||
@@ -3521,6 +3528,7 @@ impl PlatformExprStrategy {
|
||||
sellable_qty >= current_qty,
|
||||
0,
|
||||
execution_state,
|
||||
Self::defer_projection_execution_risk(ctx, date),
|
||||
)?.filter(|quantity| *quantity > 0)
|
||||
{
|
||||
fill = Some(ProjectedExecutionFill {
|
||||
@@ -4153,6 +4161,7 @@ impl PlatformExprStrategy {
|
||||
false,
|
||||
0,
|
||||
execution_state,
|
||||
Self::defer_projection_execution_risk(ctx, date),
|
||||
)?.filter(|quantity| *quantity > 0)
|
||||
{
|
||||
fill = Some(ProjectedExecutionFill {
|
||||
@@ -15072,6 +15081,7 @@ mod tests {
|
||||
order_events:&[],fills:&[],
|
||||
};
|
||||
let mut cfg=PlatformExprStrategyConfig::generic();
|
||||
cfg.risk_config.trading_constraints.volume_limit_enabled=false;
|
||||
cfg.signal_symbol=symbol.into();
|
||||
cfg.rotation_enabled=false;
|
||||
cfg.signal_book=Some(book);
|
||||
@@ -15190,6 +15200,7 @@ mod tests {
|
||||
let rows = Arc::new(Mutex::new(Vec::new()));
|
||||
let strategy = Capture { inner: PlatformExprStrategy::new(config), first, rows: Arc::clone(&rows) };
|
||||
let broker = BrokerSimulator::new(ChinaAShareCostModel::default(), ChinaEquityRuleHooks)
|
||||
.with_volume_capacity_mode(crate::execution_capacity::VolumeCapacityMode::SessionCapacityAudit)
|
||||
.with_matching_type(MatchingType::CurrentBarClose);
|
||||
let mut engine = BacktestEngine::new(data, strategy, broker, BacktestConfig {
|
||||
initial_cash: 10_000.0, benchmark_code: "000852.SH".to_owned(), start_date: Some(first),
|
||||
@@ -18046,6 +18057,7 @@ mod tests {
|
||||
false,
|
||||
0,
|
||||
&execution_state,
|
||||
false,
|
||||
).expect("valid volume capacity"),
|
||||
Some(2_500)
|
||||
);
|
||||
@@ -18066,6 +18078,7 @@ mod tests {
|
||||
false,
|
||||
0,
|
||||
&execution_state,
|
||||
false,
|
||||
).expect("valid remaining volume capacity"),
|
||||
Some(100)
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user