pub trait OrderStatusClient: ExecutionClient {
// Required method
fn fetch_ended_orders(
&self,
orders: &[UnindexedOrderKey],
) -> impl Future<Output = Result<Vec<UnindexedInactiveOrder>, UnindexedClientError>> + Send;
// Provided method
fn fetch_order_status(
&self,
key: &UnindexedOrderKey,
) -> impl Future<Output = Result<VenueOrderStatus, UnindexedClientError>> + Send
where Self: Sync { ... }
}Expand description
Extension trait for clients that can say how an order ended at the venue.
A client that loses its account stream recovers the fills it missed when it reconnects, but not an order that was cancelled, expired or rejected in the meantime: the event that said so went out while nothing was listening. The engine then keeps such an order as live until a complete account snapshot no longer lists it, and even then cannot tell how it ended. This trait is the lookup that answers that: given the orders a caller still holds as live, it returns how each one that has ended did end.
§By order, not by time
The obvious alternative, asking the venue for every order closed since the disconnect, does not
work. Binance’s allOrders and Alpaca’s GET /v2/orders?status=closed both filter their time
window on when an order was created, so a window opening at the disconnect cannot see an
order placed before it and cancelled during it — which is the order that matters, because the
caller is tracking it. Looking each order up by its client order id finds it whenever it was
placed.
§Type-Level Capability
A supertrait of ExecutionClient, for the reason BracketOrderClient is one: a client that
cannot look orders up does not implement it, rather than carrying a method that fails at run
time. A caller that needs the lookup bounds on ExecutionClient + OrderStatusClient.
Required Methods§
Sourcefn fetch_ended_orders(
&self,
orders: &[UnindexedOrderKey],
) -> impl Future<Output = Result<Vec<UnindexedInactiveOrder>, UnindexedClientError>> + Send
fn fetch_ended_orders( &self, orders: &[UnindexedOrderKey], ) -> impl Future<Output = Result<Vec<UnindexedInactiveOrder>, UnindexedClientError>> + Send
How each of orders ended, for those that have.
§Contract
- One entry per order that has ended, in no particular order:
FullyFilled,Cancelledcarrying what filled before it,Expired, orOpenFailedfor an order the venue accepted for processing and later rejected (BinanceREJECTED, Alpacarejected). An order rejected in the response to its open request was answered there and is not expected here. - An order still live is omitted, and so is one the venue does not know. Neither is an error. A caller compares the result with what it asked for.
- An empty
ordersreturns an empty list. UnlikeExecutionClient::fetch_open_orders’s empty slice, it does not mean “all”. - An order is found by its
cid, and each returned order carries the key it was asked for, so astrategythe venue does not record survives. - A key whose
instrumentis not the one the order traded is treated as unknown. Theexchangeis not checked: a client answers for its own venue. - A
cidasked about more than once is reported at most once, under the first key that finds it.
§Errors
Err if the venue could not answer for every order. A partial list is never returned,
because an order missing from it would read as still live.
Provided Methods§
Sourcefn fetch_order_status(
&self,
key: &UnindexedOrderKey,
) -> impl Future<Output = Result<VenueOrderStatus, UnindexedClientError>> + Sendwhere
Self: Sync,
fn fetch_order_status(
&self,
key: &UnindexedOrderKey,
) -> impl Future<Output = Result<VenueOrderStatus, UnindexedClientError>> + Sendwhere
Self: Sync,
What the venue knows of the order under key: live, ended, or nothing.
This is how an open request whose outcome is unknown is settled (see
OrderError::is_outcome_unknown).
§Contract
VenueOrderStatus::Unknownmeans the venue lists the order neither as live nor as ended, under the key’sinstrument. A venue may take a moment to list an order it has just accepted, so oneUnknowndoes not prove the order was never placed. A caller asks again before it concludes that.- The order is found by its
cid, and a found order carrieskey.
The provided method asks fetch_ended_orders about key
first, then lists the open orders of its instrument with
ExecutionClient::fetch_open_orders. In that order, an order that is placed between
the two reads is listed as live, so it is not missed. An order that ends between them is
found by neither, and is reported Unknown once; the next lookup finds it ended. A client
whose venue answers in one request may override it.
§Errors
Err if either read fails. A failed read is never reported as Unknown.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".
Implementors§
impl OrderStatusClient for AlpacaClient
impl OrderStatusClient for BinanceMargin
Looks each order up with GET /sapi/v1/margin/order by symbol and client order id, in the
cross or isolated account as BinanceMarginConfig::is_isolated says, weight 10 each, up to
eight at a time. Binance’s FILLED is reported with its average price, CANCELED with what
filled before it, EXPIRED and EXPIRED_IN_MATCH (self-trade prevention) as expired, and
REJECTED as OpenFailed. An order
Binance does not know under the key’s symbol (-2013, or -1121 for a symbol that does not
exist) is omitted. A row whose status this version does not know is omitted too, with a
warning, as is a PENDING_CANCEL one.
In isolated mode a key’s symbol is asked about whether or not it is one of
isolated_symbols, since an order can be placed on any
pair. Any error other than those two codes fails the call, including one Binance gives for a
pair with no isolated account.
The account stream runs the same lookup itself after a reconnect; see
account_stream.
impl OrderStatusClient for BinanceSpot
Looks each order up with GET /api/v3/order by symbol and client order id, weight 4 each, up
to eight at a time. Binance’s FILLED is reported with its average price, CANCELED with
what filled before it, EXPIRED and EXPIRED_IN_MATCH (self-trade prevention) as expired, and
REJECTED as OpenFailed. An order
Binance does not know under the key’s symbol (-2013, or -1121 for a symbol that does not
exist) is omitted. A row whose status this version does not know is omitted too, with a
warning, as is a PENDING_CANCEL one, which Binance does not use.
The account stream runs the same lookup itself after a reconnect; see
account_stream.
impl OrderStatusClient for HyperliquidClient
Looks each order up with orderStatus (weight 2), by the cloid it was placed with, or by its
oid for an order placed without one, which this client reports under its oid. Up to 8 run
at once.
filled is reported as fully filled, without an average price, which the record does not
carry; every status ending in Canceled, and scheduledCancel, as cancelled with what filled
before it; and every status ending in Rejected as
OpenFailed. Hyperliquid has no expiry.
An order the venue does not know, one on another instrument than the key’s, and an id that is
neither a canonical UUID nor an oid are unknown. An oid whose order carries a cloid is
unknown too, since this client reports that order under its cloid.
The account stream runs the same lookup itself after a reconnect; see
account_stream.
impl OrderStatusClient for HyperliquidSpotClient
Looks each order up with orderStatus (weight 2), as
HyperliquidClient does; see its implementation for how each
status is reported. An order on a spot coin missing from spotMeta even after it is read again
(see Spot coins) fails the call, since it cannot be told apart from an order
on the key’s instrument.
The account stream runs the same lookup itself after a reconnect; see
account_stream.
impl OrderStatusClient for IbkrClient
Looks each order up in IB’s listing of the account’s completed orders, by the order reference that carries the client order id it was placed with, so it finds an order this client placed before a restart too. One listing is read per call, and, when an order ended cancelled or expired, one read of the last seven days’ executions for its fill.
A filled order is reported fully filled, without an average price; a cancelled or expired one
with the fill its executions show, which is nothing when they show none of it and the order
was placed through this client under five days ago, since the read then covers its whole life,
and otherwise unknown (None); a placement IB accepted and then rejected, or left Inactive,
as OpenFailed. A cancelled order is
reported expired by the rule ExecutionClient::account_stream applies. An order IB lists
under no client order id, such as one entered in TWS, cannot be found.
IB lists the orders completed before a Gateway restart under API client id 0, so a completed order is matched by its client order id whichever of this client’s id and 0 it is listed under. Client order ids must therefore be unique across the API clients on the account.
When IB lists several completed orders under one id, the one that completed last is reported. One that completed more than 5 seconds before this client began tracking the order now under that id is an earlier order’s, under a reused id, and the order is not reported ended, with a warning: should IB’s clock lag this host’s by more, an order that did end is held as live.
The account stream runs the same lookup itself after a gap in event delivery; see
ExecutionClient::account_stream.
§Errors
UnindexedClientError::Internal when a listing fails, IB sends nothing for 10 seconds
before the end of one, or the connection drops during it.
impl<FnTime> OrderStatusClient for MockExecution<FnTime>
Answered from the venue’s own ledger: an order it filled, cancelled or retired at its deadline
is reported, and one still open or never held is omitted. The venue rejects an order only on
arrival, in the response to its open request, so it never reports OpenFailed.