Tighten market order residual fill semantics

This commit is contained in:
boris
2026-04-21 08:12:21 -07:00
parent 3652f80d85
commit c67932a2c9
2 changed files with 0 additions and 104 deletions

View File

@@ -716,7 +716,6 @@ where
let mut filled_qty = 0_u32; let mut filled_qty = 0_u32;
let mut gross_amount = 0.0_f64; let mut gross_amount = 0.0_f64;
let mut last_timestamp = None; let mut last_timestamp = None;
let mut last_quote_price = None;
for quote in quotes { for quote in quotes {
if start_cursor.is_some_and(|cursor| quote.timestamp < cursor) { if start_cursor.is_some_and(|cursor| quote.timestamp < cursor) {
@@ -728,10 +727,6 @@ where
} else { } else {
quote.buy_price() quote.buy_price()
}; };
if fallback_quote_price.is_some() {
last_quote_price = fallback_quote_price;
last_timestamp = Some(quote.timestamp);
}
if quote.volume_delta == 0 { if quote.volume_delta == 0 {
continue; continue;
@@ -784,35 +779,6 @@ where
} }
} }
if filled_qty < requested_qty {
let remaining_qty = requested_qty.saturating_sub(filled_qty);
let mut residual_qty = self.round_buy_quantity(remaining_qty, lot);
if residual_qty > 0 {
if let Some(residual_price) = last_quote_price {
if let Some(cash) = cash_limit {
while residual_qty > 0 {
let candidate_gross =
gross_amount + residual_price * residual_qty as f64;
if gross_limit.is_some_and(|limit| candidate_gross > limit + 1e-6) {
residual_qty = residual_qty.saturating_sub(lot);
continue;
}
let candidate_cost =
self.cost_model.calculate(OrderSide::Buy, candidate_gross);
if candidate_gross + candidate_cost.total() <= cash + 1e-6 {
break;
}
residual_qty = residual_qty.saturating_sub(lot);
}
}
if residual_qty > 0 {
gross_amount += residual_price * residual_qty as f64;
filled_qty += residual_qty;
}
}
}
}
if filled_qty == 0 { if filled_qty == 0 {
return None; return None;
} }
@@ -1177,22 +1143,12 @@ where
let mut filled_qty = 0_u32; let mut filled_qty = 0_u32;
let mut gross_amount = 0.0_f64; let mut gross_amount = 0.0_f64;
let mut last_timestamp = None; let mut last_timestamp = None;
let mut last_quote_price = None;
for quote in quotes { for quote in quotes {
if start_cursor.is_some_and(|cursor| quote.timestamp < cursor) { if start_cursor.is_some_and(|cursor| quote.timestamp < cursor) {
continue; continue;
} }
let fallback_quote_price = match side {
OrderSide::Buy => quote.buy_price(),
OrderSide::Sell => quote.sell_price(),
};
if fallback_quote_price.is_some() {
last_quote_price = fallback_quote_price;
last_timestamp = Some(quote.timestamp);
}
// Approximate JoinQuant market-order fills with the evolving L1 book after // Approximate JoinQuant market-order fills with the evolving L1 book after
// the decision time instead of trade VWAP. This keeps quantities/prices // the decision time instead of trade VWAP. This keeps quantities/prices
// closer to the observed 10:18 execution logs. // closer to the observed 10:18 execution logs.
@@ -1256,37 +1212,6 @@ where
} }
} }
if filled_qty < requested_qty {
let remaining_qty = requested_qty.saturating_sub(filled_qty);
let mut residual_qty = self.round_buy_quantity(remaining_qty, lot);
if residual_qty > 0 {
if let Some(residual_price) = last_quote_price {
if let Some(cash) = cash_limit {
while residual_qty > 0 {
let candidate_gross =
gross_amount + residual_price * residual_qty as f64;
if gross_limit.is_some_and(|limit| candidate_gross > limit + 1e-6) {
residual_qty = residual_qty.saturating_sub(lot);
continue;
}
if candidate_gross <= cash + 1e-6 {
break;
}
residual_qty = residual_qty.saturating_sub(lot);
}
}
if residual_qty > 0 {
let execution_price = match side {
OrderSide::Buy => residual_price,
OrderSide::Sell => residual_price,
};
gross_amount += execution_price * residual_qty as f64;
filled_qty += residual_qty;
}
}
}
}
if filled_qty == 0 { if filled_qty == 0 {
return None; return None;
} }

View File

@@ -820,7 +820,6 @@ impl JqMicroCapStrategy {
let mut filled_qty = 0_u32; let mut filled_qty = 0_u32;
let mut gross_amount = 0.0_f64; let mut gross_amount = 0.0_f64;
let mut last_timestamp = None; let mut last_timestamp = None;
let mut last_quote_price = None;
for quote in quotes { for quote in quotes {
if start_cursor.is_some_and(|cursor| quote.timestamp < cursor) { if start_cursor.is_some_and(|cursor| quote.timestamp < cursor) {
@@ -838,7 +837,6 @@ impl JqMicroCapStrategy {
OrderSide::Sell => quote.sell_price(), OrderSide::Sell => quote.sell_price(),
}; };
if fallback_quote_price.is_some() { if fallback_quote_price.is_some() {
last_quote_price = fallback_quote_price;
last_timestamp = Some(quote.timestamp); last_timestamp = Some(quote.timestamp);
} }
@@ -893,33 +891,6 @@ impl JqMicroCapStrategy {
} }
} }
if filled_qty < requested_qty {
let remaining_qty = requested_qty.saturating_sub(filled_qty);
let mut residual_qty = self.round_lot_quantity(remaining_qty, lot);
if residual_qty > 0 {
if let Some(residual_price) = last_quote_price {
if let Some(cash) = cash_limit {
while residual_qty > 0 {
let candidate_gross =
gross_amount + residual_price * residual_qty as f64;
if gross_limit.is_some_and(|limit| candidate_gross > limit + 1e-6) {
residual_qty = residual_qty.saturating_sub(lot);
continue;
}
if candidate_gross <= cash + 1e-6 {
break;
}
residual_qty = residual_qty.saturating_sub(lot);
}
}
if residual_qty > 0 {
gross_amount += residual_price * residual_qty as f64;
filled_qty += residual_qty;
}
}
}
}
if filled_qty == 0 { if filled_qty == 0 {
return None; return None;
} }