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 7e19133 commit 90bb7b6
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 25 deletions.
4 changes: 4 additions & 0 deletions hftbacktest/src/backtest/backtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,8 @@ where
)?;
self.evs
.update_exch_order(asset_no, local.earliest_send_order_timestamp());
self.evs
.update_local_order(asset_no, local.earliest_recv_order_timestamp());

if wait {
return self.goto::<false>(
Expand All @@ -831,6 +833,8 @@ where
local.cancel(order_id, self.cur_ts)?;
self.evs
.update_exch_order(asset_no, local.earliest_send_order_timestamp());
self.evs
.update_local_order(asset_no, local.earliest_recv_order_timestamp());

if wait {
return self.goto::<false>(
Expand Down
36 changes: 27 additions & 9 deletions hftbacktest/src/backtest/proc/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,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 @@ -145,8 +159,7 @@ where
// notification.
if order_entry_latency < 0 {
// Rejects the order.
order.req = Status::None;
order.status = Status::Expired;
order.req = Status::Rejected;
let rej_recv_timestamp = current_timestamp - order_entry_latency;
self.orders_from.append(order, rej_recv_timestamp);
} else {
Expand All @@ -167,16 +180,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.
order.req = Status::None;
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 @@ -301,8 +314,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
22 changes: 9 additions & 13 deletions hftbacktest/src/backtest/proc/nopartialfillexchange.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,20 +406,18 @@ 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> {
let exch_order = {
let mut order_borrowed = self.orders.borrow_mut();
order_borrowed.remove(&order.order_id)
};

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)
return Ok(local_recv_timestamp);
self.orders_to.append(order, local_recv_timestamp);
return Ok(());
}

// Deletes the order.
Expand All @@ -442,7 +440,7 @@ where
let local_recv_timestamp = timestamp + self.order_latency.response(timestamp, &exch_order);
self.orders_to
.append(exch_order.clone(), local_recv_timestamp);
Ok(local_recv_timestamp)
Ok(())
}

fn ack_modify(&mut self, mut order: Order, timestamp: i64) -> Result<(), BacktestError> {
Expand All @@ -452,13 +450,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)
let local_recv_timestamp =
timestamp + self.order_latency.response(timestamp, &order);
self.orders_to.append(order, local_recv_timestamp);
return Ok(());
}

Expand Down
7 changes: 4 additions & 3 deletions hftbacktest/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ pub enum Status {
Filled = 3,
Canceled = 4,
PartiallyFilled = 5,
Rejected = 6,
/// This occurs when the [`Connector`](`crate::connector::Connector`) receives an order status
/// value that does not have a corresponding enum value.
Unsupported = 255,
Expand Down Expand Up @@ -521,9 +522,9 @@ impl Order {
self.exch_timestamp = order.exch_timestamp;
}
self.status = order.status;
if order.local_timestamp > 0 {
self.local_timestamp = order.local_timestamp;
}
// if order.local_timestamp > 0 {
// self.local_timestamp = order.local_timestamp;
// }
self.req = order.req;
self.exec_price_tick = order.exec_price_tick;
self.exec_qty = order.exec_qty;
Expand Down

0 comments on commit 90bb7b6

Please sign in to comment.