Skip to main content

IbkrClient

Struct IbkrClient 

Source
pub struct IbkrClient { /* private fields */ }
Expand description

Interactive Brokers execution client.

§Clone Behavior

Cloning creates a shallow copy with shared Arc references to the underlying IB connection, contract registry, order ID map, and execution buffer. All clones share the same TWS/Gateway connection and state.

Implementations§

Source§

impl IbkrClient

Source

pub fn connect_sync(config: IbkrConfig) -> Result<Self, IbkrConnectError>

Connect to TWS/Gateway and register every contract in IbkrConfig::contracts (sync, blocking).

Each contract is built from its config, resolved as resolve_contract does and registered as register_contract does. If any of them fails, the connection is dropped and IbkrConnectError::Contracts lists every contract that failed, not just the first, so that a client never starts without a contract its caller configured. To connect anyway and get the failures back, use connect_sync_lenient.

The library does not retry. If every contract in IbkrConnectError::Contracts has a reason that is_transient, connecting again may succeed. Otherwise fix the config, or connect with connect_sync_lenient.

§Errors
Source

pub fn connect_sync_lenient( config: IbkrConfig, ) -> Result<ConnectOutcome, UnindexedClientError>

Connect to TWS/Gateway as connect_sync does, but without each configured contract that cannot be registered, returning those in ConnectOutcome::skipped.

Orders for a skipped contract’s instrument are refused as for any unregistered instrument, so check skipped. To retry one, call resolve_contract and then register_contract on the connected client.

Blocking, like connect_sync: it waits for the connection and for one contract details request per configured contract, each of which can take up to 10 seconds if IB stops answering. From async code, call it inside tokio::task::spawn_blocking.

§Errors

Returns an error if TWS/Gateway cannot be reached or refuses the connection.

Source

pub async fn resolve_contract( &self, contract: &Contract, ) -> Result<Contract, ResolveContractError>

Resolve a contract description through IB’s contract details, on this client’s connection, into the one IB contract it matches.

A contract built locally, such as stock_contract("AAPL", "SMART", "USD"), has no IB contract id until it is resolved, and register_contract refuses it. Resolve it here first:

use rustrade_execution::client::ibkr::contract::stock_contract;

let aapl = client
    .resolve_contract(&stock_contract("AAPL", "SMART", "USD"))
    .await?;
client.register_contract("AAPL".into(), aapl)?;
§Errors

ResolveContractError::is_transient tells the failures worth retrying from the rest.

Source

pub fn register_contract( &self, name: InstrumentNameExchange, contract: Contract, ) -> Result<(), ContractRegistryError>

Register a contract IB has resolved for an instrument, so that orders can be placed on it and everything IB reports for it (fills, order listings, positions) is attributed to name.

Resolve the contract first with resolve_contract, or list it in IbkrConfig::contracts for connect_sync to resolve.

§Errors

Leaves the registry unchanged and returns:

Source

pub fn contract_registry(&self) -> &ContractRegistry

Get the contract registry.

Source

pub fn pending_execution_count(&self) -> usize

Get the number of pending executions awaiting commission reports.

Useful for monitoring whether commission reports are being received. A growing count may indicate IB connection issues or delayed reports.

Source

pub fn clear_stale_executions(&self, max_age: Duration) -> usize

Clear stale executions older than the given duration.

Returns the number of cleared entries.

Call this periodically to prevent unbounded memory growth if commission reports are delayed or lost. A reasonable interval is 5-10 minutes with a max_age of 1 hour.

Source

pub fn clear_stale_order_ids(&self, max_age: Duration) -> usize

Clear order ID mappings older than the given duration.

Returns the number of cleared entries.

§Why This Is Needed

IB does not guarantee event ordering between OrderStatus("Filled") and ExecutionData/CommissionReport. For fast-filling orders (especially market orders), execution data may arrive AFTER the filled status — or the filled status may not arrive at all. So a Filled status frees the order’s client order id but keeps its IB order ID’s entry, and an order whose terminal status never arrives keeps both: its id cannot name a new order (see open_order’s “Client order ids”) until this clears it.

Call this periodically alongside clear_stale_executions(). A reasonable interval is 5-10 minutes with a max_age of 1 hour. An order still working past max_age is cleared too, and its id freed.

Source

pub fn clear_stale_pending_cancels(&self, max_age: Duration) -> usize

Clear pending cancel entries older than the given duration.

Returns the number of cleared entries.

Pending cancels are tracked to differentiate user-initiated cancellation from time-based expiration. If a cancel request is submitted but the order never receives terminal status (e.g., network disconnect), the entry would remain indefinitely. Call this alongside other stale cleanup methods.

Source

pub fn disconnect(&self)

Disconnect from IB Gateway.

Shuts the ibapi client down for every clone sharing it: requests in flight fail, any active account_stream() ends with StreamTerminated, and later requests are refused.

§Client ID release

This closes the TCP connection, even while other clones of this client (IbkrClient implements Clone and shares one connection) or their subscriptions are still alive. TWS/Gateway frees the API client ID once it has processed the close, which can take a moment: connect_sync under the same ID right after this call may be refused with IB error 326 (“client id is already in use”), and succeeds on a retry. The clones need not be dropped first. A clone that outlives the call is shut down, not reconnected.

This is idempotent — calling it multiple times is safe.

Source

pub async fn open_bracket_order( &self, request: BracketOrderRequest, ) -> BracketOrderResult

Place a bracket order (entry + take-profit + stop-loss) with OCA linking.

A bracket order consists of three linked orders:

  1. Entry: Limit order to enter the position
  2. Take Profit: Limit order to exit at profit target (OCA-linked to SL)
  3. Stop Loss: Stop order to exit at loss limit (OCA-linked to TP)
§OCA (One-Cancels-All) Behavior

When either exit order fills, IB automatically cancels the other. This prevents the dangerous scenario where take-profit fills but stop-loss remains open, potentially opening an unintended opposing position.

§Failure Semantics

If any leg is rejected by IB (e.g., insufficient margin, invalid price), or cannot be sent, this method cancels every leg already sent and returns the legs as Inactive, except for legs of unknown fate (below).

The same all-legs cancellation applies if any leg fails to report a status within the placement timeout, or the transport drops while waiting (an unknown/no-status outcome): leaving part of a bracket working while the rest is in an unknown state is unsafe, so every leg is cancelled and an error is returned.

If any leg’s client order id (the parent’s, or the _tp/_sl id derived from it) is held by a live order, nothing is sent and every leg comes back Inactive with ApiError::DuplicateClientOrderId; the live order is unaffected. See ExecutionClient::open_order’s “Client order ids” for when an id is held.

§Legs of Unknown Fate

Cancelling does not settle a leg whose fate is unknown: a leg that reported no status, or whose rollback cancel could not be sent (ibapi refuses sends while its transport is down), may still be live or held at TWS. Such a leg comes back Open with zero fill and keeps its order-id mapping, as a no-status single order does, so the account stream can report how it ends. The other legs come back Inactive with the error. A leg IB rejected is settled even if its cancel was refused. So a failed bracket can return a mix of Open and Inactive legs, and every leg can be Open when none reported a status: an all-Open result is then indistinguishable from success, and the failure shows only in the log and on the account stream. Treat an Open leg as working until the account stream or ExecutionClient::fetch_open_orders says otherwise.

§Cancellation Safety

This future registers order ID mappings before submitting to IB. If cancelled mid-flight, orders may still be submitted and mappings will leak. Avoid cancelling this future; use IB’s native order timeout via TimeInForce if timeout behavior is needed.

§Example
ⓘ
let request = BracketOrderRequest {
    instrument: "AAPL".into(),
    strategy: StrategyId::new("my-strategy"),
    parent_cid: ClientOrderId::new("bracket-001"),
    side: Side::Buy,
    quantity: dec!(100),
    entry_price: dec!(150.00),
    take_profit_price: dec!(160.00),  // +$10 profit target
    stop_loss_price: dec!(145.00),    // -$5 stop loss
    time_in_force: TimeInForce::GoodUntilCancelled { post_only: false },
};
let result = client.open_bracket_order(request).await;

Trait Implementations§

Source§

impl BracketOrderClient for IbkrClient

Source§

async fn open_bracket_order( &self, request: UnifiedBracketOrderRequest<ExchangeId, &InstrumentNameExchange>, ) -> UnifiedBracketOrderResult

Place a bracket order (entry + take-profit + stop-loss). Read more
Source§

impl Clone for IbkrClient

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for IbkrClient

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl ExecutionClient for IbkrClient

Source§

fn validate_config( config: &Self::Config, instruments: &[ClientInstrument<'_>], ) -> Result<(), String>

Rejects a ContractConfig that connect_sync would refuse as invalid, or whose security_type contradicts the kind of the instrument it is keyed to by name: STK or CASH for a Spot, FUT for a Future, OPT for an Option. Every problem found is reported, not just the first.

Spot covers both a stock and a currency pair, so STK and CASH cannot be told apart here: either is accepted for any Spot instrument.

An entry keyed to no instrument on this exchange, and an instrument with no entry, are both accepted: a contract can also be registered later with register_contract, which this cannot see.

Source§

fn new(config: Self::Config) -> Self

Create a new IBKR client by connecting to TWS/Gateway.

§Blocking

This method performs blocking TCP I/O and IB API handshake. If called from an async context, wrap in tokio::task::spawn_blocking or call from a dedicated thread.

§Panics

Panics if IbkrClient::connect_sync fails: when TWS/Gateway cannot be reached or refuses the connection, or when any contract in IbkrConfig::contracts cannot be registered. The ExecutionClient trait doesn’t allow new() to return Result. Call IbkrClient::connect_sync or IbkrClient::connect_sync_lenient directly for fallible construction.

Source§

async fn account_snapshot( &self, assets: &[AssetNameExchange], instruments: &[InstrumentNameExchange], ) -> Result<UnindexedAccountSnapshot, UnindexedClientError>

Fetch account snapshot: balances, and the position and open orders of each registered instrument IB reports either in.

§Positions

Each instrument IB reports a position or an open order of this API client’s in, and that is registered (and in instruments, when that is not empty), gets an InstrumentAccountSnapshot. Its position is PositionReport::Open, carrying:

  • quantity: IB’s signed position, negative when short, in shares for a stock and in contracts for a future or an option. ibapi hands it over as an f64, converted with Decimal::try_from, which rounds to the float’s precision of about 15 significant digits (0.1 stays 0.1) rather than keeping its exact binary expansion.
  • entry_price: IB’s average cost divided by the contract multiplier, so a future or an option is quoted as its orders are priced. It includes commissions. None when IB sends no average cost, or no multiplier for a contract other than a stock or a forex pair.
  • time_exchange: the time of the call, since IB does not timestamp positions.

unrealized_pnl and the margin fields are None: IB’s positions subscription does not carry them.

A zero quantity, which IB reports for a position closed today, is PositionReport::Flat. So is each instrument in instruments that is registered with its contract ID but that IB did not list, provided IB marked the end of its listing (PositionEnd) during the read. Without that marker such instruments are left out, with a warning, since the listing may be incomplete. A requested instrument registered without a contract ID, or whose ID was registered again under another name, cannot be matched to IB’s reports and is left out too.

Several accounts. This client reads the positions of every account the login manages, one account at a time. When more than one account holds the same instrument, the first account to report a non-zero quantity is kept and the others are dropped with a warning; they are never summed. A consumer comparing the reported quantity with its own, as the rustrade engine’s position drift check does, therefore compares against that one account. Which account comes first depends on the order IB lists the login’s accounts in, so a caller holding the same instrument in several accounts should not rely on it.

An instrument with an open order whose position the read could not establish, because IB did not mark the end of its listing, reports it PositionReport::Unreported.

§Orders

Each instrument’s orders are this API client’s open orders in it, read as fetch_open_orders reads them, from one listing of the account’s open orders. An order this client does not track but placed under a client order id, such as one placed before a restart, is listed under that id, and tracked from then on.

orders_complete is true when IB listed every open order to the end of the listing and each of the instrument’s was read back under the client order id it was placed with and with its status. So it is false on an instrument with an order of this API client’s that carries no client order id, which is listed under its IB order id: one placed by an earlier version of this client, or, for a client connected as 0, one entered in TWS. So it is too with an order whose shape this client never sends, one IB listed without its status, or one whose client order id names another order this client tracks. When the listing cannot be read, no instrument’s orders are complete, with a warning, and the snapshot is still returned.

§Errors
  • ConnectivityError::Timeout if IB does not answer the request for the login’s accounts within 30 seconds. Transient: retry.
  • UnindexedClientError::Internal if a position quantity does not convert to a Decimal, if the request for the login’s accounts fails otherwise or yields none (as it does when the session ends during it), if a positions subscription fails, or as fetch_balances.
§Known Issue: ibapi Decode Errors

You may see log errors like "error decoding message: error occurred: unexpected message: Error". This is an upstream ibapi limitation where IB Error messages (type 4) on subscription channels aren’t properly routed — they’re logged but don’t affect functionality.

§Timeout

Each account’s positions read ends at IB’s end-of-listing marker. If IB sends nothing for 5 seconds before it, that account’s read gives up and the next one starts, so a login with N accounts can wait up to N × 5 seconds. The positions received are returned rather than blocking indefinitely, without the flat reports that need every account’s listing to be complete.

Each read uses IB’s positions-multi request for that account, whose replies carry a request ID, so concurrent calls on clones of this client never read each other’s listing.

Source§

async fn account_stream( &self, _assets: &[AssetNameExchange], _instruments: &[InstrumentNameExchange], ) -> Result<Self::AccountStream, UnindexedClientError>

Stream account events (order updates, fills, commissions).

§Thread Lifecycle

Spawns two background threads: ibkr-order-stream reads the blocking IB subscription, and ibkr-recovery watches for gaps in event delivery (see below). The watcher exits within about a second of the returned BoxStream being dropped or the stream ending, or once the recovery or order check in progress then ends. The reader exits when:

  • The returned BoxStream is dropped (channel closes), or the stream has ended, and the next IB event arrives
  • The IB subscription ends

Important: If IB is stalled (no events flowing), the reader thread blocks on the iterator. Dropping the stream signals termination, but the thread won’t observe it until the next IB event arrives. A shutdown does end the subscription, so the thread exits then.

§Reconnects and Recovery

ibapi reconnects its socket to TWS/Gateway by itself, and this stream stays open across that: a transient drop produces neither an error nor StreamTerminated. TWS does not resend what it sent while the socket was down, and TWS losing its own link to IB’s servers (notice 1100) opens the same kind of gap. Once delivery is restored, this stream asks TWS for the last seven days of executions and emits the ones from the gap as Trade events. The gap is taken to start up to a poll interval before the drop was noticed, minus a lookback margin. Trades the stream already delivered are not emitted twice. The log line Recovered IBKR fills after a gap in event delivery marks one.

When that read fails three times for a reason other than the transport dropping again, the gap is given up: the stream sends one AccountEventKind::FillRecoveryGaveUp covering every instrument, from the start of the gap to when it gave up, and stays open. Read that span with ExecutionClient::fetch_trades.

A gap that starts before the span IB surely holds (see fetch_trades’ held_since) is reported too, even though the read succeeds: after the recovered trades, one FillRecoveryGaveUp with FillRecoveryFailure::VenueRetention covers the part before that span, ending the moment before held_since. fetch_trades cannot read that part either, so reconcile it from another source.

Orders that ended during the gap. After the fills, recovered or given up, the stream checks how each order the client holds as live ended: those placed through it, listed by fetch_open_orders or account_snapshot, or reported live on this stream, and not yet seen to end. It lists the open orders once, and looks up each held order the listing no longer shows in IB’s completed orders, as OrderStatusClient::fetch_ended_orders does. Each that ended is sent as an OrderSnapshot of its inactive state, under StrategyId::unknown, and its client order id is freed, as when the stream reports an order ending itself. An order IB lists as neither is dropped from the set with a warning. A check that fails is retried after 1, 2, 4, 8 and 16 minutes while connected, then given up with an error log: an order that ended then stays live in engine state until the next gap’s check, so reconcile with fetch_open_orders. The client holds at most 4,096 orders as live, forgetting the oldest beyond that. The check reads IB’s listings on the recovery thread, each bounded by a 10-second stall timeout, and the thread reads notices again once it ends.

Call this within a Tokio runtime with its time driver enabled, as #[tokio::main] and Runtime::new build: the recovery thread runs the order check on the runtime this was called on.

While TWS reports its link to IB’s servers lost (1100), nothing marks the gap on this stream until the link is restored and the recovered fills arrive.

The stream ends with StreamTerminated when the client shuts down for good, because ibapi gave up reconnecting, IbkrClient::disconnect was called, or TWS/Gateway ended the API session. Called on a client that has already shut down, this method fails, so replace the client.

§Duplicate Events

Orders submitted via open_order() will emit OrderSnapshot events on this stream in addition to the response from open_order() itself. This is inherent to IB’s API — both the per-order subscription and the global order update stream receive the same OrderStatus events. Callers should deduplicate if needed.

Trade events are delivered once each. Executions that answer an executions request, such as ExecutionClient::fetch_trades, are not reported here as fills.

§Corrections

IB reports a corrected execution as a further execution whose id differs only in the digits after the final period (….01.02 corrects ….01.01). It is sent as AccountEventKind::TradeAmended, Corrected, with the correction as the replacement Trade, once its commission report arrives. Its original is the revision this stream delivered before, or, for an execution from before the stream, the revision the correction’s id says it corrects. A revision older than one already delivered is dropped. Fills recovered after a gap are delivered originals first, so a correction recovered with them comes after every original in the gap, not in time order. Each correction is logged at warn! when it arrives, so one that never gets a commission report is still seen. IB documents no busts.

The revisions delivered are remembered in a bounded cache, like the dedup cache. A correction of an execution the cache has forgotten names the previous revision by its id, which is the original unless an intermediate revision was delivered and forgotten too.

One case loses a fill: a correction that arrives live after a reconnect, before recovery has delivered its original from the gap. The correction is sent naming the original, and recovery then drops the original as older than a revision already delivered, so the trade reaches the stream only as the amendment.

§Filter Parameters

The assets and instruments parameters are currently ignored. IB’s order_update_stream() returns events for all orders on the account. Callers subscribing for a specific instrument will receive events for all instruments.

Source§

async fn cancel_order( &self, request: OrderRequestCancel<ExchangeId, &InstrumentNameExchange>, ) -> UnindexedOrderResponseCancel

Cancel an order.

§Async Cancel Semantics

Returns Ok(Cancelled) when the cancel request is submitted, not when the order is confirmed cancelled. The actual cancellation confirmation comes via account_stream as an OrderStatus::Cancelled event.

The order ID mapping is retained until terminal status is received via account_stream, ensuring fill events arriving between cancel request and confirmation are not lost.

Source§

async fn open_order( &self, request: OrderRequestOpen<ExchangeId, &InstrumentNameExchange>, ) -> Order<ExchangeId, InstrumentNameExchange, UnindexedOrderState>

Submit an order to IB.

§Client order ids

The client order id is sent as the order’s IB order reference, so it must be at most 128 ASCII characters; any other is refused before anything is sent. TWS does not check the reference’s uniqueness, so this client keeps each id unique itself: an open under an id that a live order holds is refused before anything is sent, as ApiError::DuplicateClientOrderId, and the live order is unaffected. An order stops holding its id when the account stream reports it Filled, Cancelled or Inactive, or when clear_stale_order_ids reaps it, or when the account stream’s check after a gap finds it ended. Without a running account_stream, no order is reported ended, and an id is held until reaped.

An open whose outcome is unknown (OrderError::is_outcome_unknown) keeps holding its id, as TWS may hold the order. If the order was never placed, the id is held until reaped, so retry such an open under a new id.

A cancel names the live order under its id, so cancelling a filled order’s id once it is freed is refused locally as not found, rather than at TWS.

§Cancellation Safety

This future registers the order ID mapping before submitting to IB. If the future is cancelled (e.g., via tokio::select! timeout) after registration but before completion:

  • The order may still be submitted to IB
  • The order ID mapping will leak (not cleaned up)
  • Subsequent fills will be processed via account_stream
  • The client order id stays held, so a retry under it is refused as ApiError::DuplicateClientOrderId until the account stream reports the order ended or clear_stale_order_ids reaps it. Retry under a fresh id. The same holds when this returns Open without a status from TWS.

Callers should avoid cancelling this future mid-flight. If timeout behavior is needed, prefer setting IB’s native order timeout via TimeInForce instead.

Source§

async fn fetch_balances( &self, assets: &[AssetNameExchange], ) -> Result<Vec<AssetBalance<AssetNameExchange>>, UnindexedClientError>

Fetch account balances.

§Limitations

The time_exchange field in returned balances uses Utc::now(), not the actual IB server timestamp. IB’s account summary endpoint does not provide timestamps per balance update.

§Errors

UnindexedClientError::Internal if the request fails, IB answers with an error, IB sends nothing for 10 seconds before the end of the listing, or the connection drops during it.

Source§

async fn fetch_open_orders( &self, instruments: &[InstrumentNameExchange], ) -> Result<Vec<Order<ExchangeId, InstrumentNameExchange, Open>>, UnindexedClientError>

Fetch the open orders this API client placed.

IB numbers orders per API client, so orders other clients placed, and orders entered in TWS, are left out: their ids could name an order of this client’s. IB reports orders entered in TWS under API client id 0, so a client connected as 0 keeps them.

Each order is returned under the client order id it was placed with: the one this client tracks it under, with the kind, price and time in force it was placed with, or else the one its IB order reference carries, with those read back from IB’s listing. An order this client does not track but placed under a client order id, such as one placed before a restart, is tracked from then on, so its fills and status reach the account stream and it can be cancelled. Every listed order of this client’s is adopted so, whatever instruments asks for. An order without a reference, such as one placed by an earlier version of this client, or, for a client connected as 0, one entered in TWS, is returned under its IB order id; so is one whose reference names another order this client tracks. One whose order type or time in force this client never sends is left out, with a warning. Every order’s filled quantity is the one IB reports with it.

Each returned order named by the id it was placed with is held as live, for the account stream’s check of how it ended after a gap.

IB lists every client’s open orders to every request for them, so this client and its clones read the listing one at a time.

§Errors

UnindexedClientError::Internal if the request fails, IB answers with an error, IB sends nothing for 10 seconds before the end of the listing, or the connection drops during it.

Source§

async fn fetch_trades( &self, start: DateTime<Utc>, end: DateTime<Utc>, instruments: &[InstrumentNameExchange], ) -> Result<TradesRead<AssetNameExchange, InstrumentNameExchange>, UnindexedClientError>

Fetch historical trades (executions).

§Limitations
  • IB lists executions from the last 7 days at most, counted in a time zone of its own. One request reads them all, the span is applied client-side, and the read is always complete (resume: None) for what IB returned. A span starting more than 5 days ago has held_since set to 5 days ago, the most the read is sure to reach: fills before it may be missing. For older executions, use IB’s Flex Query or Activity Statements.
  • Only the executions of orders this API client placed are returned, plus any execution IB flags as an IB-initiated liquidation, whoever placed its order, as a Liquidation trade. With IbkrConfig::other_clients_fills set, every other client’s execution IB sends is returned too, as an External trade under IB’s permanent id for its order. An execution in a contract this client has not registered is skipped either way.
  • Each trade’s fees come from the commission report IB sends with its execution (see Fill fees for what that figure includes). An execution whose report IB did not send is returned with a zero fee in UNKNOWN_FEE_ASSET, with a warning.
  • Trades are returned in time_exchange order, then by id. A corrected execution keeps the position of its first revision.
§Errors

UnindexedClientError::Internal if the request fails, IB answers with an error, IB sends nothing for 10 seconds before the end of the listing, or the connection drops during it.

§Corrections

IB reports a corrected execution as a further execution whose id differs only in the digits after the final period. Each execution is returned once, at its latest revision, under that revision’s id: the replacement a TradeAmended on ExecutionClient::account_stream names.

Source§

const EXCHANGE: ExchangeId = ExchangeId::Ibkr

Source§

const SUPPORTED_KINDS: &'static [InstrumentKindDiscriminant]

The InstrumentKindDiscriminants this client can trade. Read more
Source§

type Config = IbkrConfig

Source§

type AccountStream = Pin<Box<dyn Stream<Item = AccountEvent<ExchangeId, AssetNameExchange, InstrumentNameExchange>> + Send>>

Source§

fn cancel_orders<'a>( &self, requests: impl IntoIterator<Item = OrderRequestCancel<ExchangeId, &'a InstrumentNameExchange>>, ) -> impl Stream<Item = UnindexedOrderResponseCancel> + Send

Source§

fn open_orders<'a>( &self, requests: impl IntoIterator<Item = OrderRequestOpen<ExchangeId, &'a InstrumentNameExchange>>, ) -> impl Stream<Item = Order<ExchangeId, InstrumentNameExchange, UnindexedOrderState>> + Send

Source§

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.

Source§

async fn fetch_ended_orders( &self, orders: &[UnindexedOrderKey], ) -> Result<Vec<UnindexedInactiveOrder>, UnindexedClientError>

How each of orders ended, for those that have. Read more
Source§

fn fetch_order_status( &self, key: &UnindexedOrderKey, ) -> impl Future<Output = Result<VenueOrderStatus, UnindexedClientError>> + Send
where Self: Sync,

What the venue knows of the order under key: live, ended, or nothing. Read more
Source§

impl ShortabilityClient for IbkrClient

Source§

async fn fetch_shortability( &self, instrument: &InstrumentNameExchange, ) -> Result<Shortability, UnindexedClientError>

Reads IB’s shortability ticks with a streaming market data request for generic tick 236, which IB answers with:

  • tick 46, a level: above 2.5, at least 1000 shares are available to borrow, so the stock is shortable and easy_to_borrow; above 1.5, it is shortable once shares are located, so not easy to borrow; 1.5 or below, it is not shortable, and easy_to_borrow is left unknown.
  • tick 89, the shares IB has to lend, as available.

A stock that is shortable but not easy_to_borrow needs a locate. IB accepts a short sale of it and holds the order while it looks for shares (IB’s code 404, which this client reports as a working order, not a rejection), and may never find them. So shortable says a short sale may be tried, not that shares are on hand.

fee_rate is unknown: IB reports no borrow fee to the API. delisting_at is unknown too.

The request is read until both ticks have arrived, or for IbkrConfig::shortability_timeout (10 s by default); one that arrives alone is returned on its own. Once tick 46 has arrived, tick 89 is waited for at most 2 s more: IB sends both together for a stock it has shares of, and can send no tick 89 at all for one it has few or none of, which then comes back with available unknown. While it runs it holds one of the account’s market data lines, and it is cancelled when the read ends. Calls made at once each hold their own line, so checking many stocks together can fill the account’s lines. The read runs on a blocking thread, so dropping the returned future does not end it early: it runs to its end, holding the line until then.

The request asks for IB’s delayed market data, which IB serves only where the account has no live subscription for the stock, so an account without one still gets the ticks. IB does not say how far behind delayed shortability ticks are.

§Errors

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> Conv for T

Source§

fn conv<T>(self) -> T
where Self: Into<T>,

Converts self into T using Into<T>. Read more
Source§

impl<T> FmtForward for T

Source§

fn fmt_binary(self) -> FmtBinary<Self>
where Self: Binary,

Causes self to use its Binary implementation when Debug-formatted.
Source§

fn fmt_display(self) -> FmtDisplay<Self>
where Self: Display,

Causes self to use its Display implementation when Debug-formatted.
Source§

fn fmt_lower_exp(self) -> FmtLowerExp<Self>
where Self: LowerExp,

Causes self to use its LowerExp implementation when Debug-formatted.
Source§

fn fmt_lower_hex(self) -> FmtLowerHex<Self>
where Self: LowerHex,

Causes self to use its LowerHex implementation when Debug-formatted.
Source§

fn fmt_octal(self) -> FmtOctal<Self>
where Self: Octal,

Causes self to use its Octal implementation when Debug-formatted.
Source§

fn fmt_pointer(self) -> FmtPointer<Self>
where Self: Pointer,

Causes self to use its Pointer implementation when Debug-formatted.
Source§

fn fmt_upper_exp(self) -> FmtUpperExp<Self>
where Self: UpperExp,

Causes self to use its UpperExp implementation when Debug-formatted.
Source§

fn fmt_upper_hex(self) -> FmtUpperHex<Self>
where Self: UpperHex,

Causes self to use its UpperHex implementation when Debug-formatted.
Source§

fn fmt_list(self) -> FmtList<Self>
where &'a Self: for<'a> IntoIterator,

Formats each item in a sequence. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> JsonSchemaMaybe for T

Source§

impl<T> Pipe for T
where T: ?Sized,

Source§

fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> R
where Self: Sized,

Pipes by value. This is generally the method you want to use. Read more
Source§

fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> R
where R: 'a,

Borrows self and passes that borrow into the pipe function. Read more
Source§

fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> R
where R: 'a,

Mutably borrows self and passes that borrow into the pipe function. Read more
Source§

fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
where Self: Borrow<B>, B: 'a + ?Sized, R: 'a,

Borrows self, then passes self.borrow() into the pipe function. Read more
Source§

fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
where Self: BorrowMut<B>, B: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.borrow_mut() into the pipe function. Read more
Source§

fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
where Self: AsRef<U>, U: 'a + ?Sized, R: 'a,

Borrows self, then passes self.as_ref() into the pipe function.
Source§

fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
where Self: AsMut<U>, U: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.as_mut() into the pipe function.
Source§

fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
where Self: Deref<Target = T>, T: 'a + ?Sized, R: 'a,

Borrows self, then passes self.deref() into the pipe function.
Source§

fn pipe_deref_mut<'a, T, R>( &'a mut self, func: impl FnOnce(&'a mut T) -> R, ) -> R
where Self: DerefMut<Target = T> + Deref, T: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.deref_mut() into the pipe function.
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> Tap for T

Source§

fn tap(self, func: impl FnOnce(&Self)) -> Self

Immutable access to a value. Read more
Source§

fn tap_mut(self, func: impl FnOnce(&mut Self)) -> Self

Mutable access to a value. Read more
Source§

fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Immutable access to the Borrow<B> of a value. Read more
Source§

fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Mutable access to the BorrowMut<B> of a value. Read more
Source§

fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Immutable access to the AsRef<R> view of a value. Read more
Source§

fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Mutable access to the AsMut<R> view of a value. Read more
Source§

fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Immutable access to the Deref::Target of a value. Read more
Source§

fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Mutable access to the Deref::Target of a value. Read more
Source§

fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self

Calls .tap() only in debug builds, and is erased in release builds.
Source§

fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self

Calls .tap_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Calls .tap_borrow() only in debug builds, and is erased in release builds.
Source§

fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Calls .tap_borrow_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Calls .tap_ref() only in debug builds, and is erased in release builds.
Source§

fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Calls .tap_ref_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Calls .tap_deref() only in debug builds, and is erased in release builds.
Source§

fn tap_deref_mut_dbg<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Calls .tap_deref_mut() only in debug builds, and is erased in release builds.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> TryConv for T

Source§

fn try_conv<T>(self) -> Result<T, Self::Error>
where Self: TryInto<T>,

Attempts to convert self into T using TryInto<T>. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more