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, UnindexedClientError>

Connect to TWS/Gateway and initialize the client (sync, blocking).

§Errors

Returns error if connection fails or contract resolution fails.

Source

pub fn register_contract( &self, name: InstrumentNameExchange, contract: Contract, )

Register a contract for an instrument.

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. Removing mappings on terminal status would cause data loss.

Call this periodically alongside clear_stale_executions(). A reasonable interval is 5-10 minutes with a max_age of 1 hour.

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.

Signals the ibapi client to shut down and releases the client ID for reuse.

IbkrClient implements Clone and has no Drop impl: the underlying connection is released automatically when the last Arc<Client> reference is dropped. Calling disconnect() explicitly terminates the connection immediately for all clones sharing this client.

Any active account_stream() iterators will receive errors on their next iteration attempt.

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.

§All-or-Nothing Semantics

If any leg is rejected by IB (e.g., insufficient margin, invalid price), this method cancels all other legs and returns all three as Inactive. You will never receive a mix of active and inactive legs.

The same all-legs cancellation applies if any leg fails to report a terminal status within the placement timeout (an unknown/no-status outcome): leaving part of a bracket working while the rest is in an unknown state is unsafe, so all three legs are cancelled and an error is returned. This is intentionally stricter than single-order placement (open_order), where a no-status outcome retains the order and defers resolution to the account stream.

§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) -> IbkrClient

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 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 connection fails. The ExecutionClient trait doesn’t allow new() to return Result. Use IbkrClient::connect_sync() directly for fallible construction.

Source§

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

Fetch account snapshot (balances and positions).

§Limitations
  • The orders field in each InstrumentAccountSnapshot is always empty. IB’s positions endpoint returns position data only, not open orders. Use fetch_open_orders() or account_stream() for order state.

  • Position quantity and average cost from IB are not carried in the returned InstrumentAccountSnapshot. The struct only indicates which instruments have positions, not the position sizes. This is a limitation of the InstrumentAccountSnapshot type, not the IB API.

§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

Uses 5-second timeout between position updates. If IB stalls mid-stream (e.g., due to the ibapi PositionEnd routing bug), returns partial results after 5s of inactivity rather than blocking indefinitely.

For accounts with no positions, this method waits the full 5-second timeout before returning an empty position list.

Source§

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

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

§Thread Lifecycle

Spawns a background thread to read from the blocking IB subscription. The thread terminates when:

  • The returned BoxStream is dropped (channel closes)
  • The IB subscription ends (disconnect)

Important: If IB is stalled (no events flowing), the thread blocks on the iterator. Dropping the stream signals termination, but the thread won’t observe it until the next IB event arrives. For graceful shutdown during stalls, the caller should disconnect the IB connection.

§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.

§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>, ) -> Option<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>, ) -> Option<Order<ExchangeId, InstrumentNameExchange, UnindexedOrderState>>

Submit an order to IB.

§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

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.

Source§

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

Fetch open orders.

§Limitations
  • The filled_qty in returned orders is set to zero. IB’s open orders endpoint returns order definitions, not fill status. For accurate filled quantities, use account_stream which provides OrderStatus events with fill progress.
  • The time_in_force defaults to GoodUntilCancelled. IB’s open orders endpoint does not return the original TIF setting.
  • This method blocks on IB’s subscription until IB sends an end-of-data marker. If IB is stalled, this will block indefinitely.
Source§

async fn fetch_trades( &self, time_since: DateTime<Utc>, instruments: &[InstrumentNameExchange], ) -> Result<Vec<Trade<AssetNameExchange, InstrumentNameExchange>>, UnindexedClientError>

Fetch historical trades (executions).

§Limitations
  • IB only returns executions from the current trading day. The time_since parameter is applied client-side to filter within that day. For historical executions beyond today, use IB’s Flex Query or Activity Statements.
  • Fees are always zero. IB’s executions endpoint doesn’t include commission data. For trades with accurate fees, use account_stream() which pairs ExecutionData with CommissionReport events.
  • This method blocks on IB’s executions subscription until IB sends an end-of-data marker. If IB is stalled, this will block indefinitely.
Source§

const EXCHANGE: ExchangeId = ExchangeId::Ibkr

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 = Option<UnindexedOrderResponseCancel>> + Send

Source§

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

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 = Infallible

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

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

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