Skip to main content

ibapi/orders/
sync.rs

1use std::sync::Arc;
2use std::time::{Duration, Instant};
3
4use super::common::fill::{completed_status, lists_order, lookup_wait, FillTracker};
5use super::common::{decoders, encoders, verify};
6use super::{
7    CancelOrder, ClientBound, ExecutionFilter, Executions, ExerciseOptionsBuilder, OrderBuilder, OrderOutcome, OrderStatus, OrderUpdate, Orders,
8    PlaceOrder,
9};
10use crate::client::blocking::Subscription;
11use crate::common::request_helpers::{self, expect_proto};
12use crate::contracts::Contract;
13use crate::messages::OutgoingMessages;
14use crate::orders::OrderId;
15use crate::subscriptions::SubscriptionItem;
16use crate::{client::sync::Client, Error};
17
18impl Client {
19    /// Start building an order for the given contract
20    ///
21    /// This is the primary API for creating orders, providing a fluent interface
22    /// that guides you through the order creation process.
23    ///
24    /// # Examples
25    ///
26    /// ```no_run
27    /// use ibapi::client::blocking::Client;
28    /// use ibapi::contracts::Contract;
29    ///
30    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
31    /// let contract = Contract::stock("AAPL").build();
32    ///
33    /// let order_id = client.order(&contract)
34    ///     .buy(100)
35    ///     .limit(50.0)
36    ///     .submit().expect("order submission failed");
37    /// ```
38    pub fn order<'a>(&'a self, contract: &'a Contract) -> OrderBuilder<ClientBound<'a, Self>> {
39        OrderBuilder::new(self, contract)
40    }
41
42    /// Requests all *current* open orders in associated accounts at the current moment.
43    /// Open orders are returned once; this function does not initiate a subscription.
44    ///
45    /// # Examples
46    ///
47    /// ```no_run
48    /// use ibapi::client::blocking::Client;
49    ///
50    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
51    ///
52    /// let subscription = client.all_open_orders().expect("request failed");
53    /// for order_data in subscription.iter_data() {
54    ///     match order_data {
55    ///         Ok(order_data) => println!("{order_data:?}"),
56    ///         Err(e) => eprintln!("error: {e}"),
57    ///     }
58    /// }
59    /// ```
60    pub fn all_open_orders(&self) -> Result<Subscription<Orders>, Error> {
61        let request = encoders::encode_all_open_orders()?;
62        let subscription = self.send_shared_request(OutgoingMessages::RequestAllOpenOrders, request)?;
63
64        Ok(Subscription::new(Arc::clone(&self.message_bus), subscription, self.decoder_context()))
65    }
66
67    /// Requests status updates about future orders placed from TWS. Can only be used with client ID 0.
68    ///
69    /// # Arguments
70    /// * `auto_bind` - if set to true, the newly created orders will be assigned an API order ID and implicitly associated with this client. If set to false, future orders will not be.
71    ///
72    /// # Examples
73    ///
74    /// ```no_run
75    /// use ibapi::client::blocking::Client;
76    ///
77    /// let client = Client::connect("127.0.0.1:4002", 0).expect("connection failed");
78    ///
79    /// let subscription = client.auto_open_orders(false).expect("request failed");
80    /// for order_data in subscription.iter_data() {
81    ///     match order_data {
82    ///         Ok(order_data) => println!("{order_data:?}"),
83    ///         Err(e) => eprintln!("error: {e}"),
84    ///     }
85    /// }
86    /// ```
87    pub fn auto_open_orders(&self, auto_bind: bool) -> Result<Subscription<Orders>, Error> {
88        let request = encoders::encode_auto_open_orders(auto_bind)?;
89        let subscription = self.send_shared_request(OutgoingMessages::RequestAutoOpenOrders, request)?;
90
91        Ok(Subscription::new(Arc::clone(&self.message_bus), subscription, self.decoder_context()))
92    }
93
94    /// Cancels an active [`crate::orders::Order`] placed by the same API client ID.
95    ///
96    /// The confirmation (TWS code 202) arrives as a non-terminal
97    /// [`SubscriptionItem::Notice`](crate::subscriptions::SubscriptionItem);
98    /// the subscription stays open until dropped, so break once cancellation
99    /// is observed.
100    ///
101    /// # Arguments
102    /// * `order_id` - ID of the [`crate::orders::Order`] to cancel.
103    /// * `manual_order_cancel_time` - Optional timestamp to specify the cancellation time. Use an empty string to use the current time.
104    ///
105    /// # Errors
106    /// [`Error::OrderIdInRequestRange`] if `order_id` is at or above 1,500,000,000 (reserved for request ids).
107    ///
108    /// # Examples
109    ///
110    /// ```no_run
111    /// use ibapi::client::blocking::Client;
112    /// use ibapi::subscriptions::SubscriptionItem;
113    ///
114    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
115    ///
116    /// let order_id = 15;
117    /// let subscription = client.cancel_order(order_id, "").expect("request failed");
118    /// for item in &subscription {
119    ///     match item {
120    ///         Ok(SubscriptionItem::Data(status)) => println!("status: {status:?}"),
121    ///         Ok(SubscriptionItem::Notice(n)) if n.is_cancellation() => {
122    ///             println!("cancelled: {n}");
123    ///             break;
124    ///         }
125    ///         Ok(SubscriptionItem::Notice(n)) => println!("notice: {n}"),
126    ///         Err(e) => { eprintln!("cancel err: {e:?}"); break; }
127    ///     }
128    /// }
129    /// ```
130    pub fn cancel_order(&self, order_id: impl Into<OrderId>, manual_order_cancel_time: &str) -> Result<Subscription<CancelOrder>, Error> {
131        let order_id = order_id.into().checked()?;
132        let request = encoders::encode_cancel_order(order_id.value(), manual_order_cancel_time)?;
133        let subscription = self.send_order(order_id, request)?;
134
135        Ok(Subscription::new(Arc::clone(&self.message_bus), subscription, self.decoder_context()))
136    }
137
138    /// Requests completed [`crate::orders::Order`]s.
139    ///
140    /// A completed order's `total_quantity` is the quantity left unfilled, not the size
141    /// ordered: `0` for a filled order. The size ordered is `total_quantity + filled_quantity`.
142    ///
143    /// # Arguments
144    /// * `api_only` - request only orders placed by the API.
145    ///
146    /// # Examples
147    ///
148    /// ```no_run
149    /// use ibapi::client::blocking::Client;
150    ///
151    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
152    ///
153    /// let subscription = client.completed_orders(false).expect("request failed");
154    /// for order_data in subscription.iter_data() {
155    ///     match order_data {
156    ///         Ok(order_data) => println!("{order_data:?}"),
157    ///         Err(e) => eprintln!("error: {e}"),
158    ///     }
159    /// }
160    /// ```
161    pub fn completed_orders(&self, api_only: bool) -> Result<Subscription<Orders>, Error> {
162        let request = encoders::encode_completed_orders(api_only)?;
163        let subscription = self.send_shared_request(OutgoingMessages::RequestCompletedOrders, request)?;
164
165        Ok(Subscription::new(Arc::clone(&self.message_bus), subscription, self.decoder_context()))
166    }
167
168    /// Requests executions matching the filter.
169    ///
170    /// Covers the current day (since midnight) by default; set
171    /// [`ExecutionFilter::last_n_days`] or [`ExecutionFilter::specific_dates`]
172    /// to reach earlier days.
173    /// Along with the [`crate::orders::ExecutionData`], the [`crate::orders::CommissionReport`] will also be returned.
174    /// Join a commission to its execution by `execution_id` (see the
175    /// [`CommissionReport`](crate::orders::CommissionReport) docs) — the commission follows its execution.
176    /// When requesting executions, a filter can be specified to receive only a subset of them
177    ///
178    /// # Arguments
179    /// * `filter` - filter criteria used to determine which execution reports are returned
180    ///
181    /// # Examples
182    ///
183    /// ```no_run
184    /// use ibapi::client::blocking::Client;
185    /// use ibapi::orders::{ExecutionFilter, ExecutionFilterSide};
186    ///
187    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
188    ///
189    /// let filter = ExecutionFilter {
190    ///    side: Some(ExecutionFilterSide::Buy),
191    ///    ..ExecutionFilter::default()
192    /// };
193    ///
194    /// let subscription = client.executions(filter).expect("request failed");
195    /// for execution_data in subscription.iter_data() {
196    ///     match execution_data {
197    ///         Ok(execution_data) => println!("{execution_data:?}"),
198    ///         Err(e) => eprintln!("error: {e}"),
199    ///     }
200    /// }
201    /// ```
202    pub fn executions(&self, filter: ExecutionFilter) -> Result<Subscription<Executions>, Error> {
203        let request_id = self.mint_request_id();
204
205        let request = encoders::encode_executions(request_id.raw(), &filter)?;
206        let subscription = self.send_request(request_id, request)?;
207
208        Ok(Subscription::new(Arc::clone(&self.message_bus), subscription, self.decoder_context()))
209    }
210
211    /// Cancels all open [`crate::orders::Order`]s.
212    ///
213    /// # Examples
214    ///
215    /// ```no_run
216    /// use ibapi::client::blocking::Client;
217    ///
218    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
219    ///
220    /// client.global_cancel().expect("request failed");
221    /// ```
222    pub fn global_cancel(&self) -> Result<(), Error> {
223        let message = encoders::encode_global_cancel()?;
224        self.send_message(message)?;
225
226        Ok(())
227    }
228
229    /// Gets the next valid order ID from the TWS server.
230    ///
231    /// Unlike [Self::next_order_id], this function requests the next valid order ID from the TWS server.
232    /// This can be for ensuring that order IDs are unique across multiple clients.
233    ///
234    /// The returned value also raises the client's order-ID generator to at
235    /// least that value — monotonically, never lowering it below locally
236    /// allocated order IDs, including IDs whose order has not yet reached the
237    /// server.
238    ///
239    /// Use this method when coordinating order IDs across multiple client instances or when you need to synchronize with the server's order ID sequence at the start of a session.
240    ///
241    /// # Errors
242    /// - [`Error::OrderIdInRequestRange`] if TWS returns an id at or above 1,500,000,000 (reserved for request ids).
243    /// - [`Error::Timeout`] if TWS sends no answer within 30 seconds, or within [`one_shot_timeout`](crate::client::blocking::ClientBuilder::one_shot_timeout) when set.
244    ///
245    /// # Examples
246    ///
247    /// ```no_run
248    /// use ibapi::client::blocking::Client;
249    ///
250    /// // Connect to the TWS server at the given address with client ID.
251    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
252    ///
253    /// // Request the next valid order ID from the server.
254    /// let next_valid_order_id = client.next_valid_order_id().expect("request failed");
255    /// println!("next_valid_order_id: {next_valid_order_id}");
256    /// ```
257    pub fn next_valid_order_id(&self) -> Result<i32, Error> {
258        let next_order_id = request_helpers::blocking::one_shot_shared(
259            self,
260            OutgoingMessages::RequestIds,
261            encoders::encode_next_valid_order_id,
262            expect_proto(decoders::decode_next_valid_id_proto),
263        )?;
264
265        let next_order_id = OrderId::from(next_order_id).checked()?;
266        self.raise_next_order_id(next_order_id);
267        Ok(next_order_id.value())
268    }
269
270    /// Requests all open orders places by this specific API client (identified by the API client id).
271    /// For client ID 0, this will bind previous manual TWS orders.
272    ///
273    /// # Examples
274    ///
275    /// ```no_run
276    /// use ibapi::client::blocking::Client;
277    ///
278    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
279    ///
280    /// let subscription = client.open_orders().expect("request failed");
281    /// for order_data in subscription.iter_data() {
282    ///     match order_data {
283    ///         Ok(order_data) => println!("{order_data:?}"),
284    ///         Err(e) => eprintln!("error: {e}"),
285    ///     }
286    /// }
287    /// ```
288    pub fn open_orders(&self) -> Result<Subscription<Orders>, Error> {
289        let request = encoders::encode_open_orders()?;
290        let subscription = self.send_shared_request(OutgoingMessages::RequestOpenOrders, request)?;
291
292        Ok(Subscription::new(Arc::clone(&self.message_bus), subscription, self.decoder_context()))
293    }
294
295    /// Places or modifies an [`crate::orders::Order`].
296    ///
297    /// Submits an [`crate::orders::Order`] using [Client] for the given [Contract].
298    /// Upon successful submission, the client will start receiving events related to the order's activity via the subscription, including order status updates and execution reports.
299    ///
300    /// # Arguments
301    /// * `order_id` - ID for [`crate::orders::Order`]. Get next valid ID using [Client::next_order_id].
302    /// * `contract` - [Contract] to submit order for.
303    /// * `order` - [`crate::orders::Order`] to submit.
304    ///
305    /// # Errors
306    /// [`Error::OrderIdInRequestRange`] if `order_id`, or a `parent_id` or preset attached-order id on `order`, is at or above 1,500,000,000 (reserved for request ids).
307    ///
308    /// # Examples
309    ///
310    /// ```no_run
311    /// use ibapi::client::blocking::Client;
312    /// use ibapi::contracts::Contract;
313    /// use ibapi::orders::PlaceOrder;
314    ///
315    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
316    ///
317    /// let contract = Contract::stock("MSFT").build();
318    /// let order = client.order(&contract)
319    ///     .buy(100)
320    ///     .market()
321    ///     .build_order()
322    ///     .expect("failed to build order");
323    /// let order_id = client.next_order_id();
324    ///
325    /// let events = client.place_order(order_id, &contract, &order).expect("request failed");
326    ///
327    /// for event in events.iter_data() {
328    ///     match event? {
329    ///         PlaceOrder::OrderStatus(order_status) => {
330    ///             println!("order status: {order_status:?}")
331    ///         }
332    ///         PlaceOrder::OpenOrder(open_order) => println!("open order: {open_order:?}"),
333    ///         PlaceOrder::ExecutionData(execution) => println!("execution: {execution:?}"),
334    ///         PlaceOrder::CommissionReport(report) => println!("commission report: {report:?}"),
335    ///    }
336    /// }
337    /// # Ok::<(), ibapi::Error>(())
338    /// ```
339    pub fn place_order(&self, order_id: impl Into<OrderId>, contract: &Contract, order: &super::Order) -> Result<Subscription<PlaceOrder>, Error> {
340        let checked_id = verify::verify_order_ids(order_id.into(), order)?;
341        verify::verify_order(self.server_version(), order)?;
342
343        let request = encoders::encode_place_order(checked_id.value(), contract, order)?;
344        let subscription = self.send_order(checked_id, request)?;
345
346        Ok(Subscription::new(Arc::clone(&self.message_bus), subscription, self.decoder_context()))
347    }
348
349    /// Submits or modifies an [`crate::orders::Order`] without returning a subscription.
350    ///
351    /// This is a fire-and-forget method that submits an [`crate::orders::Order`] for the given [Contract]
352    /// but does not return a subscription for order updates. To receive order status updates,
353    /// fills, and commission reports, use the [`order_update_stream`](Client::order_update_stream) method
354    /// or use [`place_order`](Client::place_order) instead which returns a subscription.
355    ///
356    /// # Arguments
357    /// * `order_id` - ID for [`crate::orders::Order`]. Get next valid ID using [Client::next_order_id].
358    /// * `contract` - [Contract] to submit order for.
359    /// * `order` - [`crate::orders::Order`] to submit.
360    ///
361    /// # Returns
362    /// * `Ok(())` if the order was successfully sent
363    /// * `Err(Error)` if validation failed or sending failed
364    ///
365    /// # Errors
366    /// [`Error::OrderIdInRequestRange`] if `order_id`, or a `parent_id` or preset attached-order id on `order`, is at or above 1,500,000,000 (reserved for request ids).
367    ///
368    /// # Examples
369    ///
370    /// ```no_run
371    /// use ibapi::client::blocking::Client;
372    /// use ibapi::contracts::Contract;
373    ///
374    /// # fn main() -> Result<(), ibapi::Error> {
375    ///
376    /// let client = Client::connect("127.0.0.1:4002", 100)?;
377    ///
378    /// let contract = Contract::stock("MSFT").build();
379    /// let order = client.order(&contract)
380    ///     .buy(100)
381    ///     .market()
382    ///     .build_order()?;
383    /// let order_id = client.next_order_id();
384    ///
385    /// // Submit order without waiting for confirmation
386    /// client.submit_order(order_id, &contract, &order)?;
387    ///
388    /// // Monitor all order updates via the order update stream
389    /// // This will receive updates for ALL orders, not just this one
390    /// use ibapi::orders::OrderUpdate;
391    /// for event in client.order_update_stream()?.iter_data() {
392    ///     match event? {
393    ///         OrderUpdate::OrderStatus(status) => println!("Order Status: {status:?}"),
394    ///         OrderUpdate::ExecutionData(exec) => println!("Execution: {exec:?}"),
395    ///         OrderUpdate::CommissionReport(report) => println!("Commission: {report:?}"),
396    ///         OrderUpdate::OrderBound(binding) => println!("Order binding: {binding:?}"),
397    ///         _ => {}
398    ///     }
399    /// }
400    ///
401    /// # Ok(())
402    /// # }
403    /// ```
404    pub fn submit_order(&self, order_id: impl Into<OrderId>, contract: &Contract, order: &super::Order) -> Result<(), Error> {
405        let checked_id = verify::verify_order_ids(order_id.into(), order)?;
406        verify::verify_order(self.server_version(), order)?;
407
408        let request = encoders::encode_place_order(checked_id.value(), contract, order)?;
409        self.send_message(request)?;
410
411        Ok(())
412    }
413
414    /// Submit multiple OCA (One-Cancels-All) orders
415    ///
416    /// When one order in the group is filled, all others are automatically cancelled.
417    ///
418    /// # Examples
419    ///
420    /// ```no_run
421    /// use ibapi::client::blocking::Client;
422    /// use ibapi::contracts::Contract;
423    /// use ibapi::orders::OcaType;
424    ///
425    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
426    ///
427    /// let contract1 = Contract::stock("AAPL").build();
428    /// let contract2 = Contract::stock("MSFT").build();
429    ///
430    /// let order1 = client.order(&contract1)
431    ///     .buy(100)
432    ///     .limit(50.0)
433    ///     .oca_group("MyOCA", OcaType::CancelWithBlock)
434    ///     .build_order().expect("order build failed");
435    ///     
436    /// let order2 = client.order(&contract2)
437    ///     .buy(100)
438    ///     .limit(45.0)
439    ///     .oca_group("MyOCA", OcaType::CancelWithBlock)
440    ///     .build_order().expect("order build failed");
441    ///
442    /// let order_ids = client.submit_oca_orders(
443    ///     vec![(contract1, order1), (contract2, order2)]
444    /// ).expect("OCA submission failed");
445    /// ```
446    pub fn submit_oca_orders(&self, orders: Vec<(Contract, crate::orders::Order)>) -> Result<Vec<OrderId>, Error> {
447        let mut order_ids = Vec::new();
448
449        for (contract, mut order) in orders.into_iter() {
450            let order_id = self.next_order_id();
451            order.order_id = order_id;
452            order_ids.push(OrderId::new(order_id));
453            self.submit_order(order_id, &contract, &order)?;
454        }
455
456        Ok(order_ids)
457    }
458
459    /// Creates a subscription stream for receiving real-time order updates.
460    ///
461    /// This method establishes a stream that receives all order-related events including:
462    /// - Order status updates (e.g., submitted, filled, cancelled)
463    /// - Open order information
464    /// - Execution data for trades
465    /// - Commission reports
466    /// - Order-related messages and notices
467    ///
468    /// The stream will receive updates for all orders placed through this client connection,
469    /// including both new orders submitted after creating the stream and existing orders.
470    ///
471    /// # Returns
472    ///
473    /// Returns a `Subscription<OrderUpdate>` that yields `OrderUpdate` enum variants containing:
474    /// - `OrderStatus`: Current status of an order (filled amount, average price, etc.)
475    /// - `OpenOrder`: Complete order details including contract and order parameters
476    /// - `ExecutionData`: Details about individual trade executions
477    /// - `CommissionReport`: Commission information for executed trades
478    ///
479    /// Order-bound TWS errors and warnings (e.g. rejections, code 399 order
480    /// messages) arrive as [`SubscriptionItem::Notice`](crate::subscriptions::SubscriptionItem),
481    /// not as `OrderUpdate` variants. They surface via `iter()` / `next()` as
482    /// below; `iter_data()` drops them (logging at `warn!` level), so match on
483    /// notices explicitly when monitoring fire-and-forget orders for rejection.
484    ///
485    /// # Errors
486    ///
487    /// Returns an error if the subscription cannot be created, typically due to
488    /// connection issues or internal errors.
489    ///
490    /// # Examples
491    ///
492    /// ```no_run
493    /// use ibapi::client::blocking::Client;
494    /// use ibapi::orders::OrderUpdate;
495    /// use ibapi::subscriptions::SubscriptionItem;
496    ///
497    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
498    ///
499    /// // Create order update stream
500    /// let updates = client.order_update_stream().expect("failed to create stream");
501    ///
502    /// // `iter()` surfaces both data and notices; `iter_data()` would drop notices.
503    /// for item in updates.iter() {
504    ///     match item? {
505    ///         SubscriptionItem::Data(OrderUpdate::OrderStatus(status)) => {
506    ///             println!("Order {} status: {} - filled: {}/{}",
507    ///                 status.order_id, status.status, status.filled, status.remaining);
508    ///         }
509    ///         SubscriptionItem::Data(update) => println!("update: {update:?}"),
510    ///         SubscriptionItem::Notice(notice) if notice.is_error() => {
511    ///             eprintln!("order {:?} rejected: {}", notice.request_id, notice.message);
512    ///         }
513    ///         SubscriptionItem::Notice(notice) => println!("notice: {}", notice.message),
514    ///     }
515    /// }
516    /// # Ok::<(), ibapi::Error>(())
517    /// ```
518    ///
519    /// # Note
520    ///
521    /// This stream provides updates for all orders, not just a specific order.
522    /// To track a specific order, filter the updates by order ID.
523    ///
524    /// To pair a [`CommissionReport`](crate::orders::CommissionReport) with the
525    /// [`ExecutionData`](crate::orders::ExecutionData) it belongs to, join on
526    /// `execution_id` — the commission follows its execution and shares that key. See
527    /// the [`CommissionReport`](crate::orders::CommissionReport) docs for the idiom.
528    ///
529    /// # Gaps and recovery
530    ///
531    /// TWS does not replay order events, so two situations leave a gap the
532    /// stream cannot fill on its own:
533    ///
534    /// - **Reconnect.** The stream survives the client's automatic reconnects:
535    ///   the same subscription keeps delivering once the connection returns,
536    ///   but updates TWS emitted during the outage are lost and no marker
537    ///   appears in the stream. Watch [`Self::notice_stream`] for the
538    ///   connectivity notices (codes 1100 connectivity lost, 1101 restored
539    ///   with data lost, 1102 restored with data maintained, 1300 socket reset).
540    /// - **Error.** An `Err` item ends the stream; every later call returns
541    ///   `None`. A frame that fails to decode is a bug in TWS or this crate,
542    ///   so it is surfaced rather than skipped.
543    ///
544    /// Client shutdown (disconnect, drop, or a reconnect that gives up) also
545    /// ends the stream; after it, `order_update_stream` returns
546    /// [`Error::Shutdown`].
547    ///
548    /// After an error, drop the ended subscription and call
549    /// `order_update_stream` again; while the old one is held and not
550    /// cancelled, a second call returns [`Error::AlreadySubscribed`].
551    /// After a reconnect, the existing subscription is still live. In both
552    /// cases, rebuild state from snapshots:
553    ///
554    /// 1. [`Self::all_open_orders`]: every open order and its status.
555    /// 2. [`Self::completed_orders`]`(false)`: orders that filled or were
556    ///    cancelled during the gap.
557    /// 3. [`Self::executions`] with a default [`ExecutionFilter`]: fills and
558    ///    their commissions. The default covers the current day only; set
559    ///    `last_n_days` when the gap may span midnight, as the daily gateway
560    ///    reset does.
561    ///
562    /// The snapshots cover every client's orders, while this stream reports
563    /// only this client's. Keep the entries whose `order.client_id` /
564    /// `execution.client_id` matches, or state will hold orders the stream
565    /// never updates.
566    ///
567    /// Take the snapshots after the stream is live again, so no event falls
568    /// between the two. An event can then arrive both ways; apply updates
569    /// idempotently, keyed by `perm_id` for orders and `execution_id` for fills.
570    pub fn order_update_stream(&self) -> Result<Subscription<OrderUpdate>, Error> {
571        let subscription = self.create_order_update_subscription()?;
572        Ok(Subscription::new(Arc::clone(&self.message_bus), subscription, self.decoder_context()))
573    }
574
575    /// Streams one order's status updates.
576    ///
577    /// Starts with the order's latest status, if this client has seen one
578    /// (the latest statuses of the 10,000 most recent orders are kept), then
579    /// yields each new [`OrderStatus`] as TWS sends it. A reconnect keeps only
580    /// statuses of orders that ended: TWS does not replay what happened while
581    /// disconnected, so a working order's stream starts empty until its next
582    /// status. If TWS refuses the
583    /// order with an error before any status (e.g. 110, a price off the
584    /// minimum tick), the stream yields that error as [`Error::Notice`] and
585    /// ends; a stream opened later starts with it. Otherwise the stream does
586    /// not end on its own when the order does; stop reading, or see
587    /// [`wait_for_fill`](Self::wait_for_fill) for the common case.
588    ///
589    /// Takes nothing from [`order_update_stream`](Self::order_update_stream) or
590    /// a [`place_order`](Self::place_order) subscription: all may be open at
591    /// once, as may several streams on one order. Nothing is sent to TWS.
592    ///
593    /// # Errors
594    ///
595    /// [`Error::OrderIdInRequestRange`] if `order_id` is at or above 1,500,000,000 (reserved for request ids).
596    ///
597    /// # Examples
598    ///
599    /// ```no_run
600    /// use ibapi::client::blocking::Client;
601    /// use ibapi::contracts::Contract;
602    ///
603    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
604    /// let contract = Contract::stock("AAPL").build();
605    /// let order_id = client.order(&contract).buy(100).limit(150.0).submit().expect("order submission failed");
606    ///
607    /// let statuses = client.order_status_stream(order_id).expect("stream failed");
608    /// for status in statuses.iter_data() {
609    ///     let status = status.expect("stream error");
610    ///     println!("{}: {}/{} filled", status.status, status.filled, status.filled + status.remaining);
611    ///     if status.status.is_terminal() {
612    ///         break;
613    ///     }
614    /// }
615    /// ```
616    pub fn order_status_stream(&self, order_id: impl Into<OrderId>) -> Result<Subscription<OrderStatus>, Error> {
617        let subscription = self.message_bus.create_order_status_subscription(order_id.into().checked()?)?;
618        Ok(Subscription::new(Arc::clone(&self.message_bus), subscription, self.decoder_context()))
619    }
620
621    /// Waits until an order fills or ends, or `timeout` elapses.
622    ///
623    /// Returns [`OrderOutcome::Filled`] when the order filled completely,
624    /// [`OrderOutcome::Ended`] when it was cancelled or became inactive first,
625    /// [`OrderOutcome::Rejected`] with TWS's error when TWS refused the order
626    /// without a status (e.g. 110, a price off the minimum tick), and
627    /// [`OrderOutcome::TimedOut`] with the last status seen when time runs
628    /// out. A timeout leaves the order working; cancel it yourself if needed.
629    ///
630    /// Built on [`order_status_stream`](Self::order_status_stream), so an
631    /// order that already finished before the call is reported at once, as
632    /// long as this client saw its status since connecting. When it has seen
633    /// none, the wait first looks the order up: an order this client's open
634    /// orders ([`open_orders`](Self::open_orders)) list is still working, and
635    /// the wait goes on; otherwise one found among
636    /// [`completed_orders`](Self::completed_orders) ends the wait at once,
637    /// with a status built from the completed order, whose fill prices are
638    /// `None` (see [`executions`](Self::executions) for them). Each lookup
639    /// reads its replies to the end, which can add a round trip to `timeout`.
640    /// With client id 0, the open-orders request also binds manual TWS orders
641    /// to this client. An order placed by another client, or one in neither
642    /// reply, waits out the timeout.
643    ///
644    /// # Errors
645    ///
646    /// [`Error::OrderIdInRequestRange`] if `order_id` is at or above 1,500,000,000
647    /// (reserved for request ids), [`Error::ConnectionReset`] if the connection is
648    /// down or drops during the wait, [`Error::Shutdown`] if the client shuts down.
649    ///
650    /// # Examples
651    ///
652    /// ```no_run
653    /// use std::time::Duration;
654    ///
655    /// use ibapi::client::blocking::Client;
656    /// use ibapi::contracts::Contract;
657    /// use ibapi::orders::OrderOutcome;
658    ///
659    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
660    /// let contract = Contract::stock("AAPL").build();
661    ///
662    /// let order_id = client.order(&contract).buy(100).market().submit().expect("order submission failed");
663    ///
664    /// match client.wait_for_fill(order_id, Duration::from_secs(30)).expect("wait failed") {
665    ///     OrderOutcome::Filled(status) => println!("filled {} @ {:?}", status.filled, status.average_fill_price),
666    ///     OrderOutcome::Ended(status) => println!("ended {:?} after {} filled", status.status, status.filled),
667    ///     OrderOutcome::TimedOut(status) => println!("still working: {status:?}"),
668    ///     OrderOutcome::Rejected(notice) => println!("rejected: {notice}"),
669    /// }
670    /// ```
671    pub fn wait_for_fill(&self, order_id: impl Into<OrderId>, timeout: Duration) -> Result<OrderOutcome, Error> {
672        let order_id = order_id.into();
673        let statuses = self.order_status_stream(order_id)?;
674        let mut tracker = FillTracker::default();
675        // `None` for a timeout too large to represent: wait without one.
676        let deadline = Instant::now().checked_add(timeout);
677        // Nothing queued: this client has no status for the order (e.g.
678        // placed on an earlier connection). Look it up.
679        let mut first = statuses.try_next();
680        if first.is_none() {
681            if let Some(status) = self.find_finished(order_id, deadline)? {
682                if let Some(outcome) = tracker.observe_completed(status) {
683                    return Ok(outcome);
684                }
685            }
686        }
687        loop {
688            let item = match (first.take(), deadline) {
689                (Some(item), _) => Some(item),
690                (None, Some(deadline)) => statuses.next_timeout(deadline.saturating_duration_since(Instant::now())),
691                (None, None) => statuses.next(),
692            };
693            match item {
694                Some(Ok(SubscriptionItem::Data(status))) => {
695                    if let Some(outcome) = tracker.observe(status) {
696                        return Ok(outcome);
697                    }
698                }
699                Some(Ok(SubscriptionItem::Notice(_))) => {}
700                Some(Err(error)) => return FillTracker::error(error),
701                // `next_timeout` gives `None` at the deadline; before it, the
702                // stream ended.
703                None if deadline.is_some_and(|deadline| Instant::now() >= deadline) => return Ok(tracker.timed_out()),
704                None => return Err(Error::UnexpectedEndOfStream),
705            }
706        }
707    }
708
709    /// Look up an order this client has no status for: `None` when this
710    /// client's open orders list it (their statuses reach the order's status
711    /// streams like any other) or neither reply does; otherwise its status
712    /// from completed orders. Each reply is read to its end marker.
713    fn find_finished(&self, order_id: OrderId, deadline: Option<Instant>) -> Result<Option<OrderStatus>, Error> {
714        let read = |subscription: Subscription<Orders>| match deadline {
715            Some(deadline) => subscription.collect_to_end_within(lookup_wait(deadline)),
716            None => subscription.collect_to_end(),
717        };
718        // A reply cut short (deadline, dropped connection) proves nothing:
719        // keep waiting; the status stream reports a dropped connection.
720        let Ok(open) = read(self.open_orders()?) else {
721            return Ok(None);
722        };
723        if lists_order(&open, order_id) {
724            return Ok(None);
725        }
726        let Ok(completed) = read(self.completed_orders(false)?) else {
727            return Ok(None);
728        };
729        Ok(completed_status(&completed, self.client_id(), order_id))
730    }
731
732    /// Exercise or lapse an option position.
733    ///
734    /// Terminal: [`ExerciseOptionsBuilder::submit`]. Pick [`exercise`](ExerciseOptionsBuilder::exercise)
735    /// or [`lapse`](ExerciseOptionsBuilder::lapse); [`account`](ExerciseOptionsBuilder::account),
736    /// [`override_natural_action`](ExerciseOptionsBuilder::override_natural_action) and
737    /// [`manual_order_time`](ExerciseOptionsBuilder::manual_order_time) are optional.
738    ///
739    /// # Examples
740    ///
741    /// ```no_run
742    /// use ibapi::client::blocking::Client;
743    /// use ibapi::contracts::{Contract, OptionRight};
744    ///
745    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
746    /// let contract = Contract::option("AAPL", "20251219", 150.0, OptionRight::Call);
747    /// let subscription = client
748    ///     .exercise_options(&contract)
749    ///     .exercise(1)
750    ///     .override_natural_action()
751    ///     .submit()
752    ///     .expect("exercise_options failed");
753    /// ```
754    pub fn exercise_options<'a>(&'a self, contract: &'a Contract) -> ExerciseOptionsBuilder<'a, Self> {
755        ExerciseOptionsBuilder::new(self, contract)
756    }
757}
758
759#[cfg(test)]
760#[path = "sync_tests.rs"]
761mod tests;