From f5de3a2c29a727ea0e33561784a0cf076e75fce6 Mon Sep 17 00:00:00 2001 From: boris Date: Sun, 6 Sep 2026 13:35:52 +0800 Subject: [PATCH] Skip market buys below one lot --- crates/fidc-core/src/broker.rs | 50 ++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/crates/fidc-core/src/broker.rs b/crates/fidc-core/src/broker.rs index e5cfd4c..e3e7ccb 100644 --- a/crates/fidc-core/src/broker.rs +++ b/crates/fidc-core/src/broker.rs @@ -5796,6 +5796,9 @@ where self.minimum_order_quantity(data, symbol), self.order_step_size(data, symbol), ); + if requested_qty == 0 { + return Ok(()); + } self.process_buy( date, portfolio, @@ -10345,6 +10348,53 @@ mod tests { ); } + #[test] + fn market_share_buy_skips_quantity_that_rounds_below_one_lot() { + let date = chrono::NaiveDate::from_ymd_opt(2025, 1, 2).expect("valid date"); + let data = DataSet::from_components_with_actions_and_quotes( + vec![limit_test_instrument()], + vec![limit_test_snapshot()], + Vec::new(), + vec![limit_test_candidate(true, true)], + vec![limit_test_benchmark()], + Vec::new(), + Vec::new(), + ) + .expect("valid dataset"); + let broker = BrokerSimulator::new_with_execution_price( + ChinaAShareCostModel::default(), + ChinaEquityRuleHooks, + PriceField::Open, + ) + .with_volume_limit(false) + .with_liquidity_limit(false) + .with_inactive_limit(false); + let mut portfolio = PortfolioState::new(20_000.0); + let mut report = BrokerExecutionReport::default(); + + broker + .process_shares( + date, + &mut portfolio, + &data, + "000001.SZ", + 1, + "sub_lot_target_delta", + &mut BTreeMap::new(), + &mut IntradayExecutionLedger::default(), + &mut None, + &mut BTreeMap::new(), + None, + &mut report, + ) + .expect("sub-lot buy must be a no-op"); + + assert!(report.order_events.is_empty()); + assert!(report.fill_events.is_empty()); + assert!(portfolio.position("000001.SZ").is_none()); + report.validate().expect("empty report remains valid"); + } + #[test] fn target_value_zero_rejects_sell_when_market_snapshot_missing() { let trade_date = chrono::NaiveDate::from_ymd_opt(2025, 1, 2).expect("valid date");