Skip to content

Commit

Permalink
fix: fix the rejection response.
Browse files Browse the repository at this point in the history
  • Loading branch information
nkaz001 committed Jul 29, 2024
1 parent 90bb7b6 commit c6f61d7
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 29 deletions.
39 changes: 30 additions & 9 deletions hftbacktest/src/backtest/proc/l3_local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,24 @@ where
// Applies the received order response to the local orders.
match self.orders.entry(order.order_id) {
Entry::Occupied(mut entry) => {
*entry.get_mut() = order;
let local_order = entry.get_mut();
if order.req == Status::Rejected {
if order.local_timestamp == local_order.local_timestamp {
if local_order.req == Status::New {
local_order.req = Status::None;
local_order.status = Status::Expired;
} else {
local_order.req = Status::None;
}
}
} else {
local_order.update(&order);
}
}
Entry::Vacant(entry) => {
entry.insert(order);
if order.req != Status::Rejected {
entry.insert(order);
}
}
}
Ok(())
Expand Down Expand Up @@ -148,6 +162,7 @@ where
// notification.
if order_entry_latency < 0 {
// Rejects the order.
order.req = Status::Rejected;
let rej_recv_timestamp = current_timestamp - order_entry_latency;
self.orders_from.append(order, rej_recv_timestamp);
} else {
Expand All @@ -168,15 +183,16 @@ where
}

order.req = Status::Canceled;
order.local_timestamp = current_timestamp;
let order_entry_latency = self.order_latency.entry(current_timestamp, order);
// Negative latency indicates that the order is rejected for technical reasons, and its
// value represents the latency that the local experiences when receiving the rejection
// notification.
if order_entry_latency < 0 {
// Rejects the order.
let mut order_ = order.clone();
order_.req = Status::Rejected;
let rej_recv_timestamp = current_timestamp - order_entry_latency;
self.orders_from.append(order.clone(), rej_recv_timestamp);
self.orders_from.append(order_, rej_recv_timestamp);
} else {
let exch_recv_timestamp = current_timestamp + order_entry_latency;
self.orders_to.append(order.clone(), exch_recv_timestamp);
Expand Down Expand Up @@ -248,11 +264,11 @@ where
let ev = &self.data[self.row_num];
// Processes a depth event
if ev.is(LOCAL_BID_DEPTH_CLEAR_EVENT) {
self.depth.clear_depth(BUY_EVENT);
self.depth.clear_depth(Side::Buy);
} else if ev.is(LOCAL_ASK_DEPTH_CLEAR_EVENT) {
self.depth.clear_depth(SELL_EVENT);
self.depth.clear_depth(Side::Sell);
} else if ev.is(LOCAL_DEPTH_CLEAR_EVENT) {
self.depth.clear_depth(0);
self.depth.clear_depth(Side::None);
} else if ev.is(LOCAL_BID_ADD_ORDER_EVENT) {
self.depth
.add_buy_order(ev.order_id, ev.px, ev.qty, ev.local_ts)?;
Expand Down Expand Up @@ -314,8 +330,13 @@ where
if timestamp == recv_timestamp {
let (order, _) = self.orders_from.pop_front().unwrap();

self.last_order_latency =
Some((order.local_timestamp, order.exch_timestamp, recv_timestamp));
// Updates the order latency only if it has a valid exchange timestamp. When the
// order is rejected before it reaches the matching engine, it has no exchange
// timestamp. This situation occurs in crypto exchanges.
if order.exch_timestamp > 0 {
self.last_order_latency =
Some((order.local_timestamp, order.exch_timestamp, recv_timestamp));
}

if let Some(wait_resp_order_id) = wait_resp_order_id {
if order.order_id == wait_resp_order_id {
Expand Down
20 changes: 9 additions & 11 deletions hftbacktest/src/backtest/proc/l3_nopartialfillexchange.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ where
}
}

fn ack_cancel(&mut self, mut order: Order, timestamp: i64) -> Result<i64, BacktestError> {
fn ack_cancel(&mut self, mut order: Order, timestamp: i64) -> Result<(), BacktestError> {
match self
.queue_model
.cancel_order(L3OrderId::Backtest(order.order_id))
Expand All @@ -333,17 +333,15 @@ where
timestamp + self.order_latency.response(timestamp, &exch_order);
self.orders_to
.append(exch_order.clone(), local_recv_timestamp);
Ok(local_recv_timestamp)
Ok(())
}
Err(BacktestError::OrderNotFound) => {
order.status = Status::Expired;
order.req = Status::Rejected;
order.exch_timestamp = timestamp;
let local_recv_timestamp =
timestamp + self.order_latency.response(timestamp, &order);
// It can overwrite another existing order on the local side if order_id is the same.
// So, commented out.
// self.orders_to.append(order.copy(), local_recv_timestamp)
return Ok(local_recv_timestamp);
self.orders_to.append(order, local_recv_timestamp);
return Ok(());
}
Err(e) => Err(e),
}
Expand Down Expand Up @@ -375,11 +373,11 @@ where
fn process_data(&mut self) -> Result<(i64, i64), BacktestError> {
let row_num = self.row_num;
if self.data[row_num].is(EXCH_BID_DEPTH_CLEAR_EVENT) {
self.depth.clear_depth(BUY_EVENT);
self.depth.clear_depth(Side::Buy);
} else if self.data[row_num].is(EXCH_ASK_DEPTH_CLEAR_EVENT) {
self.depth.clear_depth(SELL_EVENT);
self.depth.clear_depth(Side::Sell);
} else if self.data[row_num].is(EXCH_DEPTH_CLEAR_EVENT) {
self.depth.clear_depth(0);
self.depth.clear_depth(Side::None);
} else if self.data[row_num].is(EXCH_BID_ADD_ORDER_EVENT) {
let (prev_best_bid_tick, best_bid_tick) = self.depth.add_buy_order(
self.data[row_num].order_id,
Expand Down Expand Up @@ -415,7 +413,7 @@ where
self.data[row_num].qty,
self.data[row_num].exch_ts,
)?;
if side == BUY_EVENT {
if side == Side::Buy {
if best_tick > prev_best_tick {
self.on_best_bid_update(prev_best_tick, best_tick, self.data[row_num].exch_ts)?;
}
Expand Down
14 changes: 5 additions & 9 deletions hftbacktest/src/backtest/proc/partialfillexchange.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,12 +606,10 @@ where
};

if exch_order.is_none() {
order.status = Status::Expired;
order.req = Status::Rejected;
order.exch_timestamp = timestamp;
// let local_recv_timestamp = timestamp + self.order_latency.response(timestamp, &order);
// It can overwrite another existing order on the local side if order_id is the same.
// So, commented out.
// self.orders_to.append(order.copy(), local_recv_timestamp)
let local_recv_timestamp = timestamp + self.order_latency.response(timestamp, &order);
self.orders_to.append(order, local_recv_timestamp);
return Ok(());
}

Expand Down Expand Up @@ -646,13 +644,11 @@ where
//
// // The order can be already deleted due to fill or expiration.
// if exch_order.is_none() {
// order.status = Status::Expired;
// order.req = Status::Rejected;
// order.exch_timestamp = timestamp;
// let local_recv_timestamp =
// timestamp + self.order_latency.response(timestamp, &order);
// // It can overwrite another existing order on the local side if order_id is the
// // same. So, commented out.
// // self.orders_to.append(order.copy(), local_recv_timestamp)
// self.orders_to.append(order, local_recv_timestamp);
// return Ok(local_recv_timestamp);
// }
//
Expand Down

0 comments on commit c6f61d7

Please sign in to comment.