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;