Tighten market order residual fill semantics
This commit is contained in:
@@ -716,7 +716,6 @@ where
|
||||
let mut filled_qty = 0_u32;
|
||||
let mut gross_amount = 0.0_f64;
|
||||
let mut last_timestamp = None;
|
||||
let mut last_quote_price = None;
|
||||
|
||||
for quote in quotes {
|
||||
if start_cursor.is_some_and(|cursor| quote.timestamp < cursor) {
|
||||
@@ -728,10 +727,6 @@ where
|
||||
} else {
|
||||
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 {
|
||||
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 {
|
||||
return None;
|
||||
}
|
||||
@@ -1177,22 +1143,12 @@ where
|
||||
let mut filled_qty = 0_u32;
|
||||
let mut gross_amount = 0.0_f64;
|
||||
let mut last_timestamp = None;
|
||||
let mut last_quote_price = None;
|
||||
|
||||
for quote in quotes {
|
||||
if start_cursor.is_some_and(|cursor| quote.timestamp < cursor) {
|
||||
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
|
||||
// the decision time instead of trade VWAP. This keeps quantities/prices
|
||||
// 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 {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -820,7 +820,6 @@ impl JqMicroCapStrategy {
|
||||
let mut filled_qty = 0_u32;
|
||||
let mut gross_amount = 0.0_f64;
|
||||
let mut last_timestamp = None;
|
||||
let mut last_quote_price = None;
|
||||
|
||||
for quote in quotes {
|
||||
if start_cursor.is_some_and(|cursor| quote.timestamp < cursor) {
|
||||
@@ -838,7 +837,6 @@ impl JqMicroCapStrategy {
|
||||
OrderSide::Sell => quote.sell_price(),
|
||||
};
|
||||
if fallback_quote_price.is_some() {
|
||||
last_quote_price = fallback_quote_price;
|
||||
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 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user