ibapi 5.2.0

An unofficial Rust client for the Interactive Brokers TWS API. Async (Tokio) and blocking clients for TWS and IB Gateway.
Documentation
use futures::StreamExt;

use super::order_builder::{AttachedOrdersBuilder, BracketOrderBuilder, ClientBound, OrderBuilder};
use super::types::{AttachedOrderIds, BracketOrderIds};
use crate::client::r#async::Client;
use crate::errors::Error;
use crate::orders::OrderId;
use crate::orders::PlaceOrder;
use crate::subscriptions::SubscriptionItemStreamExt;

impl OrderBuilder<ClientBound<'_, Client>> {
    /// Submit the order asynchronously
    /// Returns the order ID assigned to the submitted order
    pub async fn submit(self) -> Result<OrderId, Error> {
        let ClientBound { client, contract } = self.target;
        let order_id = client.next_order_id();
        let order = self.build()?;
        client.submit_order(order_id, contract, &order).await?;
        Ok(OrderId::new(order_id))
    }

    /// Analyze order for margin/commission (what-if)
    pub async fn analyze(mut self) -> Result<crate::orders::OrderState, Error> {
        self.what_if = true;
        let ClientBound { client, contract } = self.target;
        let order_id = client.next_order_id();
        let order = self.build()?;

        // Submit what-if order and get the response
        let subscription = client.place_order(order_id, contract, &order).await?;

        // Look for the order state in the responses. `?` propagates a rejected
        // what-if order; the earlier `while let Some(Ok(..))` read discarded it by
        // ending the loop, so the caller saw `UnexpectedEndOfStream` (#735).
        let mut data = subscription.filter_data();
        while let Some(response) = data.next().await {
            if let PlaceOrder::OpenOrder(order_data) = response? {
                if order_data.order_id == order_id {
                    return Ok(order_data.order_state);
                }
            }
        }

        Err(Error::UnexpectedEndOfStream)
    }
}

impl BracketOrderBuilder<'_, Client> {
    /// Submit bracket orders asynchronously
    /// Returns BracketOrderIds containing all three order IDs
    pub async fn submit_all(self) -> Result<BracketOrderIds, Error> {
        let ClientBound { client, contract } = self.parent_builder.target;
        let orders = self.build()?;

        // Reserve all order IDs upfront to prevent collisions
        let parent_id = client.next_order_id();
        let tp_id = client.next_order_id();
        let sl_id = client.next_order_id();
        let reserved_ids = [parent_id, tp_id, sl_id];

        for (i, mut order) in orders.into_iter().enumerate() {
            let order_id = reserved_ids[i];
            order.order_id = order_id;

            // Update parent_id for child orders
            if i > 0 {
                order.parent_id = parent_id;
            }

            // Only transmit the last order
            if i == 2 {
                order.transmit = true;
            }

            client.submit_order(order_id, contract, &order).await?;
        }

        Ok(BracketOrderIds::new(parent_id, tp_id, sl_id))
    }
}

impl AttachedOrdersBuilder<'_, Client> {
    /// Submit the order with its preset children asynchronously.
    ///
    /// Allocates the parent id, then one id per requested child, and sends a single
    /// place-order request. Fire-and-forget: whether TWS attached the children shows up only
    /// on the order update stream — see [`Order::preset_stop_loss_order_id`](crate::orders::Order::preset_stop_loss_order_id).
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use ibapi::contracts::Contract;
    /// use ibapi::Client;
    ///
    /// #[tokio::main]
    /// async fn main() {
    ///     let client = Client::connect("127.0.0.1:4002", 100).await.expect("connection failed");
    ///     let contract = Contract::stock("AAPL").build();
    ///     let ids = client
    ///         .order(&contract)
    ///         .buy(100)
    ///         .limit(150.0)
    ///         .preset_stop_loss()
    ///         .preset_profit_taker()
    ///         .submit()
    ///         .await
    ///         .expect("submit failed");
    ///     println!("parent {} stop-loss {:?} profit-taker {:?}", ids.parent, ids.stop_loss, ids.profit_taker);
    /// }
    /// ```
    pub async fn submit(self) -> Result<AttachedOrderIds, Error> {
        let ClientBound { client, contract } = self.parent_builder.target;
        let (order, ids) = self.build_with_ids(|| client.next_order_id())?;
        client.submit_order(order.order_id, contract, &order).await?;
        Ok(ids)
    }
}

/// Submitting several OCA orders in one call.
impl Client {
    /// Submit multiple OCA (One-Cancels-All) orders
    ///
    /// When one order in the group is filled, all others are automatically cancelled.
    ///
    /// # Example
    /// ```no_run
    /// use ibapi::Client;
    /// use ibapi::contracts::Contract;
    /// use ibapi::orders::OcaType;
    ///
    /// #[tokio::main]
    /// async fn main() {
    ///     let client = Client::connect("127.0.0.1:4002", 100).await.expect("connection failed");
    ///     
    ///     let contract1 = Contract::stock("AAPL").build();
    ///     let contract2 = Contract::stock("MSFT").build();
    ///
    ///     let order1 = client.order(&contract1)
    ///         .buy(100)
    ///         .limit(50.0)
    ///         .oca_group("MyOCA", OcaType::CancelWithBlock)
    ///         .build_order().expect("order build failed");
    ///         
    ///     let order2 = client.order(&contract2)
    ///         .buy(100)
    ///         .limit(45.0)
    ///         .oca_group("MyOCA", OcaType::CancelWithBlock)
    ///         .build_order().expect("order build failed");
    ///
    ///     let order_ids = client.submit_oca_orders(
    ///         vec![(contract1, order1), (contract2, order2)]
    ///     ).await.expect("OCA submission failed");
    /// }
    /// ```
    pub async fn submit_oca_orders(&self, orders: Vec<(crate::contracts::Contract, crate::orders::Order)>) -> Result<Vec<OrderId>, Error> {
        let mut order_ids = Vec::new();

        for (contract, mut order) in orders.into_iter() {
            let order_id = self.next_order_id();
            order.order_id = order_id;
            order_ids.push(OrderId::new(order_id));
            self.submit_order(order_id, &contract, &order).await?;
        }

        Ok(order_ids)
    }
}