• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wboayue / rust-ibapi / 16253343776

13 Jul 2025 08:51PM UTC coverage: 74.507% (+1.0%) from 73.536%
16253343776

push

github

web-flow
Refactor contracts module to align with wsh patterns (#275)

This PR refactors the contracts module to follow the same clean architecture patterns established in the wsh module, improving code organization and reducing duplication between sync/async implementations.

51 of 58 new or added lines in 3 files covered. (87.93%)

1 existing line in 1 file now uncovered.

4869 of 6535 relevant lines covered (74.51%)

37.04 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.08
/src/client/sync.rs
1
//! Client implementation for connecting to and communicating with TWS and IB Gateway.
2
//!
3
//! The Client provides the main interface for establishing connections, sending requests,
4
//! and receiving responses from the Interactive Brokers API. It manages message routing,
5
//! subscriptions, and maintains the connection state.
6

7
use std::fmt::Debug;
8
use std::net::TcpStream;
9
use std::sync::Arc;
10
use std::time::Duration;
11

12
use log::debug;
13
use time::{Date, OffsetDateTime};
14
use time_tz::Tz;
15

16
use crate::accounts::types::{AccountGroup, AccountId, ContractId, ModelCode};
17
use crate::accounts::{AccountSummaryResult, AccountUpdate, AccountUpdateMulti, FamilyCode, PnL, PnLSingle, PositionUpdate, PositionUpdateMulti};
18
use crate::connection::{sync::Connection, ConnectionMetadata};
19
use crate::contracts::{Contract, OptionComputation, SecurityType};
20
use crate::errors::Error;
21
use crate::market_data::historical::{self, HistogramEntry};
22
use crate::market_data::realtime::{self, Bar, BarSize, DepthMarketDataDescription, MarketDepths, MidPoint, TickTypes, WhatToShow};
23
use crate::market_data::MarketDataType;
24
use crate::messages::{OutgoingMessages, RequestMessage};
25
use crate::news::NewsArticle;
26
use crate::orders::{CancelOrder, Executions, ExerciseOptions, Order, OrderUpdate, Orders, PlaceOrder};
27
use crate::scanner::ScannerData;
28
use crate::subscriptions::Subscription;
29
use crate::transport::{InternalSubscription, MessageBus, TcpMessageBus, TcpSocket};
30
use crate::wsh::AutoFill;
31
use crate::{accounts, contracts, market_data, news, orders, scanner, wsh};
32

33
use super::id_generator::ClientIdManager;
34

35
#[cfg(test)]
36
mod tests;
37

38
// Client
39

40
/// TWS API Client. Manages the connection to TWS or Gateway.
41
/// Tracks some global information such as server version and server time.
42
/// Supports generation of order ids.
43
pub struct Client {
44
    /// IB server version
45
    pub(crate) server_version: i32,
46
    pub(crate) connection_time: Option<OffsetDateTime>,
47
    pub(crate) time_zone: Option<&'static Tz>,
48
    pub(crate) message_bus: Arc<dyn MessageBus>,
49

50
    client_id: i32,              // ID of client.
51
    id_manager: ClientIdManager, // Manages request and order ID generation
52
}
53

54
impl Client {
55
    /// Establishes connection to TWS or Gateway
56
    ///
57
    /// Connects to server using the given connection string
58
    ///
59
    /// # Arguments
60
    /// * `address`   - address of server. e.g. 127.0.0.1:4002
61
    /// * `client_id` - id of client. e.g. 100
62
    ///
63
    /// # Examples
64
    ///
65
    /// ```no_run
66
    /// use ibapi::Client;
67
    ///
68
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
69
    ///
70
    /// println!("server_version: {}", client.server_version());
71
    /// println!("connection_time: {:?}", client.connection_time());
72
    /// println!("next_order_id: {}", client.next_order_id());
73
    /// ```
74
    pub fn connect(address: &str, client_id: i32) -> Result<Client, Error> {
×
75
        let stream = TcpStream::connect(address)?;
×
76
        let socket = TcpSocket::new(stream, address)?;
×
77

78
        let connection = Connection::connect(socket, client_id)?;
×
79
        let connection_metadata = connection.connection_metadata();
×
80

81
        let message_bus = Arc::new(TcpMessageBus::new(connection)?);
×
82

83
        // Starts thread to read messages from TWS
84
        message_bus.process_messages(connection_metadata.server_version, Duration::from_secs(1))?;
×
85

86
        Client::new(connection_metadata, message_bus)
×
87
    }
88

89
    fn new(connection_metadata: ConnectionMetadata, message_bus: Arc<dyn MessageBus>) -> Result<Client, Error> {
1✔
90
        let client = Client {
91
            server_version: connection_metadata.server_version,
2✔
92
            connection_time: connection_metadata.connection_time,
2✔
93
            time_zone: connection_metadata.time_zone,
2✔
94
            message_bus,
95
            client_id: connection_metadata.client_id,
2✔
96
            id_manager: ClientIdManager::new(connection_metadata.next_order_id),
1✔
97
        };
98

99
        Ok(client)
1✔
100
    }
101

102
    /// Returns the ID assigned to the [Client].
103
    pub fn client_id(&self) -> i32 {
1✔
104
        self.client_id
1✔
105
    }
106

107
    /// Returns the next request ID.
108
    pub fn next_request_id(&self) -> i32 {
91✔
109
        self.id_manager.next_request_id()
182✔
110
    }
111

112
    /// Returns and increments the order ID.
113
    ///
114
    /// The client maintains a sequence of order IDs. This function returns the next order ID in the sequence.
115
    pub fn next_order_id(&self) -> i32 {
×
116
        self.id_manager.next_order_id()
×
117
    }
118

119
    /// Gets the next valid order ID from the TWS server.
120
    ///
121
    /// Unlike [Self::next_order_id], this function requests the next valid order ID from the TWS server and updates the client's internal order ID sequence.
122
    /// This can be for ensuring that order IDs are unique across multiple clients.
123
    ///
124
    /// 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.
125
    ///
126
    /// # Examples
127
    ///
128
    /// ```no_run
129
    /// use ibapi::Client;
130
    ///
131
    /// // Connect to the TWS server at the given address with client ID.
132
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
133
    ///
134
    /// // Request the next valid order ID from the server.
135
    /// let next_valid_order_id = client.next_valid_order_id().expect("request failed");
136
    /// println!("next_valid_order_id: {next_valid_order_id}");
137
    /// ```
138
    pub fn next_valid_order_id(&self) -> Result<i32, Error> {
×
139
        orders::next_valid_order_id(self)
×
140
    }
141

142
    /// Sets the current value of order ID.
143
    pub(crate) fn set_next_order_id(&self, order_id: i32) {
1✔
144
        self.id_manager.set_order_id(order_id);
3✔
145
    }
146

147
    /// Returns the version of the TWS API server to which the client is connected.
148
    /// This version is determined during the initial connection handshake.
149
    ///
150
    /// # Examples
151
    ///
152
    /// ```no_run
153
    /// use ibapi::Client;
154
    ///
155
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
156
    /// let server_version = client.server_version();
157
    /// println!("Connected to TWS server version: {server_version:?}");
158
    /// ```
159
    pub fn server_version(&self) -> i32 {
174✔
160
        self.server_version
174✔
161
    }
162

163
    /// The time of the server when the client connected
164
    pub fn connection_time(&self) -> Option<OffsetDateTime> {
×
165
        self.connection_time
×
166
    }
167

168
    // === Accounts ===
169

170
    /// TWS's current time. TWS is synchronized with the server (not local computer) using NTP and this function will receive the current time in TWS.
171
    ///
172
    /// # Examples
173
    ///
174
    /// ```no_run
175
    /// use ibapi::Client;
176
    ///
177
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
178
    /// let server_time = client.server_time().expect("error requesting server time");
179
    /// println!("server time: {server_time:?}");
180
    /// ```
181
    pub fn server_time(&self) -> Result<OffsetDateTime, Error> {
9✔
182
        accounts::server_time(self)
18✔
183
    }
184

185
    /// Subscribes to [PositionUpdate]s for all accessible accounts.
186
    /// All positions sent initially, and then only updates as positions change.
187
    ///
188
    /// # Examples
189
    ///
190
    /// ```no_run
191
    /// use ibapi::Client;
192
    /// use ibapi::accounts::PositionUpdate;
193
    ///
194
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
195
    /// let subscription = client.positions().expect("error requesting positions");
196
    /// for position_response in subscription.iter() {
197
    ///     match position_response {
198
    ///         PositionUpdate::Position(position) => println!("{position:?}"),
199
    ///         PositionUpdate::PositionEnd => println!("initial set of positions received"),
200
    ///     }
201
    /// }
202
    /// ```
203
    pub fn positions(&self) -> Result<Subscription<PositionUpdate>, Error> {
4✔
204
        accounts::positions(self)
8✔
205
    }
206

207
    /// Subscribes to [PositionUpdateMulti] updates for account and/or model.
208
    /// Initially all positions are returned, and then updates are returned for any position changes in real time.
209
    ///
210
    /// # Arguments
211
    /// * `account`    - If an account Id is provided, only the account’s positions belonging to the specified model will be delivered.
212
    /// * `model_code` - The code of the model’s positions we are interested in.
213
    ///
214
    /// # Examples
215
    ///
216
    /// ```no_run
217
    /// use ibapi::Client;
218
    ///
219
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
220
    ///
221
    /// use ibapi::accounts::types::AccountId;
222
    ///
223
    /// let account = AccountId("U1234567".to_string());
224
    /// let subscription = client.positions_multi(Some(&account), None).expect("error requesting positions by model");
225
    /// for position in subscription.iter() {
226
    ///     println!("{position:?}")
227
    /// }
228
    /// ```
229
    pub fn positions_multi(&self, account: Option<&AccountId>, model_code: Option<&ModelCode>) -> Result<Subscription<PositionUpdateMulti>, Error> {
8✔
230
        accounts::positions_multi(self, account, model_code)
32✔
231
    }
232

233
    /// Creates subscription for real time daily PnL and unrealized PnL updates.
234
    ///
235
    /// # Arguments
236
    /// * `account`    - account for which to receive PnL updates
237
    /// * `model_code` - specify to request PnL updates for a specific model
238
    ///
239
    /// # Examples
240
    ///
241
    /// ```no_run
242
    /// use ibapi::Client;
243
    ///
244
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
245
    /// use ibapi::accounts::types::AccountId;
246
    ///
247
    /// let account = AccountId("account id".to_string());
248
    /// let subscription = client.pnl(&account, None).expect("error requesting pnl");
249
    /// for pnl in subscription.iter() {
250
    ///     println!("{pnl:?}")
251
    /// }
252
    /// ```
253
    pub fn pnl(&self, account: &AccountId, model_code: Option<&ModelCode>) -> Result<Subscription<PnL>, Error> {
11✔
254
        accounts::pnl(self, account, model_code)
44✔
255
    }
256

257
    /// Requests real time updates for daily PnL of individual positions.
258
    ///
259
    /// # Arguments
260
    /// * `account`     - Account in which position exists
261
    /// * `contract_id` - Contract ID of contract to receive daily PnL updates for. Note: does not return response if invalid conId is entered.
262
    /// * `model_code`  - Model in which position exists
263
    ///
264
    /// # Examples
265
    ///
266
    /// ```no_run
267
    /// use ibapi::Client;
268
    ///
269
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
270
    ///
271
    /// use ibapi::accounts::types::{AccountId, ContractId};
272
    ///
273
    /// let account = AccountId("<account id>".to_string());
274
    /// let contract_id = ContractId(1001);
275
    ///
276
    /// let subscription = client.pnl_single(&account, contract_id, None).expect("error requesting pnl");
277
    /// for pnl in &subscription {
278
    ///     println!("{pnl:?}")
279
    /// }
280
    /// ```
281
    pub fn pnl_single<'a>(
9✔
282
        &'a self,
283
        account: &AccountId,
284
        contract_id: ContractId,
285
        model_code: Option<&ModelCode>,
286
    ) -> Result<Subscription<'a, PnLSingle>, Error> {
287
        accounts::pnl_single(self, account, contract_id, model_code)
45✔
288
    }
289

290
    /// Requests a specific account’s summary. Subscribes to the account summary as presented in the TWS’ Account Summary tab. Data received is specified by using a specific tags value.
291
    ///
292
    /// # Arguments
293
    /// * `group` - Set to “All” to return account summary data for all accounts, or set to a specific Advisor Account Group name that has already been created in TWS Global Configuration.
294
    /// * `tags`  - List of the desired tags.
295
    ///
296
    /// # Examples
297
    ///
298
    /// ```no_run
299
    /// use ibapi::Client;
300
    /// use ibapi::accounts::AccountSummaryTags;
301
    /// use ibapi::accounts::types::AccountGroup;
302
    ///
303
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
304
    ///
305
    /// let group = AccountGroup("All".to_string());
306
    ///
307
    /// let subscription = client.account_summary(&group, &[AccountSummaryTags::ACCOUNT_TYPE]).expect("error requesting account summary");
308
    /// for summary in &subscription {
309
    ///     println!("{summary:?}")
310
    /// }
311
    /// ```
312
    pub fn account_summary<'a>(&'a self, group: &AccountGroup, tags: &[&str]) -> Result<Subscription<'a, AccountSummaryResult>, Error> {
10✔
313
        accounts::account_summary(self, group, tags)
40✔
314
    }
315

316
    /// Subscribes to a specific account’s information and portfolio.
317
    ///
318
    /// All account values and positions will be returned initially, and then there will only be updates when there is a change in a position, or to an account value every 3 minutes if it has changed. Only one account can be subscribed at a time.
319
    ///
320
    /// # Arguments
321
    /// * `account` - The account id (i.e. U1234567) for which the information is requested.
322
    ///
323
    /// # Examples
324
    ///
325
    /// ```no_run
326
    /// use ibapi::Client;
327
    /// use ibapi::accounts::AccountUpdate;
328
    ///
329
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
330
    ///
331
    /// use ibapi::accounts::types::AccountId;
332
    ///
333
    /// let account = AccountId("U1234567".to_string());
334
    ///
335
    /// let subscription = client.account_updates(&account).expect("error requesting account updates");
336
    /// for update in &subscription {
337
    ///     println!("{update:?}");
338
    ///
339
    ///     // stop after full initial update
340
    ///     if let AccountUpdate::End = update {
341
    ///         subscription.cancel();
342
    ///     }
343
    /// }
344
    /// ```
345
    pub fn account_updates<'a>(&'a self, account: &AccountId) -> Result<Subscription<'a, AccountUpdate>, Error> {
2✔
346
        accounts::account_updates(self, account)
6✔
347
    }
348

349
    /// Requests account updates for account and/or model.
350
    ///
351
    /// All account values and positions will be returned initially, and then there will only be updates when there is a change in a position, or to an account value every 3 minutes if it has changed. Only one account can be subscribed at a time.
352
    ///
353
    /// # Arguments
354
    /// * `account`        - Account values can be requested for a particular account.
355
    /// * `model_code`     - Account values can also be requested for a model.
356
    ///
357
    /// # Examples
358
    ///
359
    /// ```no_run
360
    /// use ibapi::Client;
361
    /// use ibapi::accounts::AccountUpdateMulti;
362
    ///
363
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
364
    ///
365
    /// use ibapi::accounts::types::AccountId;
366
    ///
367
    /// let account = AccountId("U1234567".to_string());
368
    ///
369
    /// let subscription = client.account_updates_multi(Some(&account), None).expect("error requesting account updates multi");
370
    /// for update in &subscription {
371
    ///     println!("{update:?}");
372
    ///
373
    ///     // stop after full initial update
374
    ///     if let AccountUpdateMulti::End = update {
375
    ///         subscription.cancel();
376
    ///     }
377
    /// }
378
    /// ```
379
    pub fn account_updates_multi<'a>(
2✔
380
        &'a self,
381
        account: Option<&AccountId>,
382
        model_code: Option<&ModelCode>,
383
    ) -> Result<Subscription<'a, AccountUpdateMulti>, Error> {
384
        accounts::account_updates_multi(self, account, model_code)
8✔
385
    }
386

387
    /// Requests the accounts to which the logged user has access to.
388
    ///
389
    /// # Examples
390
    ///
391
    /// ```no_run
392
    /// use ibapi::Client;
393
    ///
394
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
395
    ///
396
    /// let accounts = client.managed_accounts().expect("error requesting managed accounts");
397
    /// println!("managed accounts: {accounts:?}")
398
    /// ```
399
    pub fn managed_accounts(&self) -> Result<Vec<String>, Error> {
10✔
400
        accounts::managed_accounts(self)
20✔
401
    }
402

403
    // === Contracts ===
404

405
    /// Requests contract information.
406
    ///
407
    /// Provides all the contracts matching the contract provided. It can also be used to retrieve complete options and futures chains. Though it is now (in API version > 9.72.12) advised to use [Client::option_chain] for that purpose.
408
    ///
409
    /// # Arguments
410
    /// * `contract` - The [Contract] used as sample to query the available contracts. Typically, it will contain the [Contract]'s symbol, currency, security_type, and exchange.
411
    ///
412
    /// # Examples
413
    ///
414
    /// ```no_run
415
    /// use ibapi::Client;
416
    /// use ibapi::contracts::Contract;
417
    ///
418
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
419
    ///
420
    /// let contract = Contract::stock("TSLA");
421
    /// let results = client.contract_details(&contract).expect("request failed");
422
    /// for contract_detail in results {
423
    ///     println!("contract: {contract_detail:?}");
424
    /// }
425
    /// ```
426
    pub fn contract_details(&self, contract: &Contract) -> Result<Vec<contracts::ContractDetails>, Error> {
6✔
427
        contracts::contract_details(self, contract)
18✔
428
    }
429

430
    /// Get current [FamilyCode]s for all accessible accounts.
431
    pub fn family_codes(&self) -> Result<Vec<FamilyCode>, Error> {
4✔
432
        accounts::family_codes(self)
8✔
433
    }
434

435
    /// Requests details about a given market rule
436
    ///
437
    /// The market rule for an instrument on a particular exchange provides details about how the minimum price increment changes with price.
438
    /// A list of market rule ids can be obtained by invoking [Self::contract_details()] for a particular contract.
439
    /// The returned market rule ID list will provide the market rule ID for the instrument in the correspond valid exchange list in [contracts::ContractDetails].
440
    pub fn market_rule(&self, market_rule_id: i32) -> Result<contracts::MarketRule, Error> {
2✔
441
        contracts::market_rule(self, market_rule_id)
6✔
442
    }
443

444
    /// Requests matching stock symbols.
445
    ///
446
    /// # Arguments
447
    /// * `pattern` - Either start of ticker symbol or (for larger strings) company name.
448
    ///
449
    /// # Examples
450
    ///
451
    /// ```no_run
452
    /// use ibapi::Client;
453
    ///
454
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
455
    ///
456
    /// let contracts = client.matching_symbols("IB").expect("request failed");
457
    /// for contract in contracts {
458
    ///     println!("contract: {contract:?}");
459
    /// }
460
    /// ```
461
    pub fn matching_symbols(&self, pattern: &str) -> Result<impl Iterator<Item = contracts::ContractDescription>, Error> {
1✔
462
        Ok(contracts::matching_symbols(self, pattern)?.into_iter())
4✔
463
    }
464

465
    /// Calculates an option’s price based on the provided volatility and its underlying’s price.
466
    ///
467
    /// # Arguments
468
    /// * `contract`        - The [Contract] object representing the option for which the calculation is being requested.
469
    /// * `volatility`      - Hypothetical volatility as a percentage (e.g., 20.0 for 20%).
470
    /// * `underlying_price` - Hypothetical price of the underlying asset.
471
    ///
472
    /// # Examples
473
    ///
474
    /// ```no_run
475
    /// use ibapi::Client;
476
    /// use ibapi::contracts::Contract;
477
    ///
478
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
479
    ///
480
    /// let contract = Contract::option("AAPL", "20251219", 150.0, "C");
481
    /// let calculation = client.calculate_option_price(&contract, 100.0, 235.0).expect("request failed");
482
    /// println!("calculation: {calculation:?}");
483
    /// ```
484
    pub fn calculate_option_price(&self, contract: &Contract, volatility: f64, underlying_price: f64) -> Result<OptionComputation, Error> {
2✔
485
        contracts::calculate_option_price(self, contract, volatility, underlying_price)
10✔
486
    }
487

488
    /// Calculates the implied volatility based on the hypothetical option price and underlying price.
489
    ///
490
    /// # Arguments
491
    /// * `contract`        - The [Contract] object representing the option for which the calculation is being requested.
492
    /// * `option_price`    - Hypothetical option price.
493
    /// * `underlying_price` - Hypothetical price of the underlying asset.
494
    ///
495
    /// # Examples
496
    ///
497
    /// ```no_run
498
    /// use ibapi::Client;
499
    /// use ibapi::contracts::Contract;
500
    ///
501
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
502
    ///
503
    /// let contract = Contract::option("AAPL", "20230519", 150.0, "C");
504
    /// let calculation = client.calculate_implied_volatility(&contract, 25.0, 235.0).expect("request failed");
505
    /// println!("calculation: {calculation:?}");
506
    /// ```
507
    pub fn calculate_implied_volatility(&self, contract: &Contract, option_price: f64, underlying_price: f64) -> Result<OptionComputation, Error> {
2✔
508
        contracts::calculate_implied_volatility(self, contract, option_price, underlying_price)
10✔
509
    }
510

511
    /// Requests security definition option parameters for viewing a contract’s option chain.
512
    ///
513
    /// # Arguments
514
    /// `symbol`   - Contract symbol of the underlying.
515
    /// `exchange` - The exchange on which the returned options are trading. Can be set to the empty string for all exchanges.
516
    /// `security_type` - The type of the underlying security, i.e. STK
517
    /// `contract_id`   - The contract ID of the underlying security.
518
    ///
519
    /// # Examples
520
    ///
521
    /// ```no_run
522
    /// use ibapi::{contracts::SecurityType, Client};
523
    ///
524
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
525
    ///
526
    /// let symbol = "AAPL";
527
    /// let exchange = ""; // all exchanges
528
    /// let security_type = SecurityType::Stock;
529
    /// let contract_id = 265598;
530
    ///
531
    /// let subscription = client
532
    ///     .option_chain(symbol, exchange, security_type, contract_id)
533
    ///     .expect("request option chain failed!");
534
    ///
535
    /// for option_chain in &subscription {
536
    ///     println!("{option_chain:?}")
537
    /// }
538
    /// ```
539
    pub fn option_chain(
2✔
540
        &self,
541
        symbol: &str,
542
        exchange: &str,
543
        security_type: SecurityType,
544
        contract_id: i32,
545
    ) -> Result<Subscription<contracts::OptionChain>, Error> {
546
        contracts::option_chain(self, symbol, exchange, security_type, contract_id)
12✔
547
    }
548

549
    // === Orders ===
550

551
    /// Requests all *current* open orders in associated accounts at the current moment.
552
    /// Open orders are returned once; this function does not initiate a subscription.
553
    ///
554
    /// # Examples
555
    ///
556
    /// ```no_run
557
    /// use ibapi::Client;
558
    ///
559
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
560
    ///
561
    /// let subscription = client.all_open_orders().expect("request failed");
562
    /// for order_data in &subscription {
563
    ///    println!("{order_data:?}")
564
    /// }
565
    /// ```
566
    pub fn all_open_orders(&self) -> Result<Subscription<Orders>, Error> {
1✔
567
        orders::all_open_orders(self)
2✔
568
    }
569

570
    /// Requests status updates about future orders placed from TWS. Can only be used with client ID 0.
571
    ///
572
    /// # Arguments
573
    /// * `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.
574
    ///
575
    /// # Examples
576
    ///
577
    /// ```no_run
578
    /// use ibapi::Client;
579
    ///
580
    /// let client = Client::connect("127.0.0.1:4002", 0).expect("connection failed");
581
    ///
582
    /// let subscription = client.auto_open_orders(false).expect("request failed");
583
    /// for order_data in &subscription {
584
    ///    println!("{order_data:?}")
585
    /// }
586
    /// ```
587
    pub fn auto_open_orders(&self, auto_bind: bool) -> Result<Subscription<Orders>, Error> {
1✔
588
        orders::auto_open_orders(self, auto_bind)
3✔
589
    }
590

591
    /// Cancels an active [Order] placed by the same API client ID.
592
    ///
593
    /// # Arguments
594
    /// * `order_id` - ID of the [Order] to cancel.
595
    /// * `manual_order_cancel_time` - Optional timestamp to specify the cancellation time. Use an empty string to use the current time.
596
    ///
597
    /// # Examples
598
    ///
599
    /// ```no_run
600
    /// use ibapi::Client;
601
    ///
602
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
603
    ///
604
    /// let order_id = 15;
605
    /// let subscription = client.cancel_order(order_id, "").expect("request failed");
606
    /// for result in subscription {
607
    ///    println!("{result:?}");
608
    /// }
609
    /// ```
610
    pub fn cancel_order(&self, order_id: i32, manual_order_cancel_time: &str) -> Result<Subscription<CancelOrder>, Error> {
1✔
611
        orders::cancel_order(self, order_id, manual_order_cancel_time)
4✔
612
    }
613

614
    /// Requests completed [Order]s.
615
    ///
616
    /// # Arguments
617
    /// * `api_only` - request only orders placed by the API.
618
    ///
619
    /// # Examples
620
    ///
621
    /// ```no_run
622
    /// use ibapi::Client;
623
    ///
624
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
625
    ///
626
    /// let subscription = client.completed_orders(false).expect("request failed");
627
    /// for order_data in &subscription {
628
    ///    println!("{order_data:?}")
629
    /// }
630
    /// ```
631
    pub fn completed_orders(&self, api_only: bool) -> Result<Subscription<Orders>, Error> {
×
632
        orders::completed_orders(self, api_only)
×
633
    }
634

635
    /// Requests current day's (since midnight) executions matching the filter.
636
    ///
637
    /// Only the current day's executions can be retrieved.
638
    /// Along with the [orders::ExecutionData], the [orders::CommissionReport] will also be returned.
639
    /// When requesting executions, a filter can be specified to receive only a subset of them
640
    ///
641
    /// # Arguments
642
    /// * `filter` - filter criteria used to determine which execution reports are returned
643
    ///
644
    /// # Examples
645
    ///
646
    /// ```no_run
647
    /// use ibapi::Client;
648
    /// use ibapi::orders::ExecutionFilter;
649
    ///
650
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
651
    ///
652
    /// let filter = ExecutionFilter{
653
    ///    side: "BUY".to_owned(),
654
    ///    ..ExecutionFilter::default()
655
    /// };
656
    ///
657
    /// let subscription = client.executions(filter).expect("request failed");
658
    /// for execution_data in &subscription {
659
    ///    println!("{execution_data:?}")
660
    /// }
661
    /// ```
662
    pub fn executions(&self, filter: orders::ExecutionFilter) -> Result<Subscription<Executions>, Error> {
1✔
663
        orders::executions(self, filter)
3✔
664
    }
665

666
    /// Cancels all open [Order]s.
667
    ///
668
    /// # Examples
669
    ///
670
    /// ```no_run
671
    /// use ibapi::Client;
672
    ///
673
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
674
    ///
675
    /// client.global_cancel().expect("request failed");
676
    /// ```
677
    pub fn global_cancel(&self) -> Result<(), Error> {
×
678
        orders::global_cancel(self)
×
679
    }
680

681
    /// Requests all open orders places by this specific API client (identified by the API client id).
682
    /// For client ID 0, this will bind previous manual TWS orders.
683
    ///
684
    /// # Examples
685
    ///
686
    /// ```no_run
687
    /// use ibapi::Client;
688
    ///
689
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
690
    ///
691
    /// let subscription = client.open_orders().expect("request failed");
692
    /// for order_data in &subscription {
693
    ///    println!("{order_data:?}")
694
    /// }
695
    /// ```
696
    pub fn open_orders(&self) -> Result<Subscription<Orders>, Error> {
×
697
        orders::open_orders(self)
×
698
    }
699

700
    /// Places or modifies an [Order].
701
    ///
702
    /// Submits an [Order] using [Client] for the given [Contract].
703
    /// 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.
704
    ///
705
    /// # Arguments
706
    /// * `order_id` - ID for [Order]. Get next valid ID using [Client::next_order_id].
707
    /// * `contract` - [Contract] to submit order for.
708
    /// * `order` - [Order] to submit.
709
    ///
710
    /// # Examples
711
    ///
712
    /// ```no_run
713
    /// use ibapi::Client;
714
    /// use ibapi::contracts::Contract;
715
    /// use ibapi::orders::{order_builder, Action, PlaceOrder};
716
    ///
717
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
718
    ///
719
    /// let contract = Contract::stock("MSFT");
720
    /// let order = order_builder::market_order(Action::Buy, 100.0);
721
    /// let order_id = client.next_order_id();
722
    ///
723
    /// let events = client.place_order(order_id, &contract, &order).expect("request failed");
724
    ///
725
    /// for event in &events {
726
    ///     match event {
727
    ///         PlaceOrder::OrderStatus(order_status) => {
728
    ///             println!("order status: {order_status:?}")
729
    ///         }
730
    ///         PlaceOrder::OpenOrder(open_order) => println!("open order: {open_order:?}"),
731
    ///         PlaceOrder::ExecutionData(execution) => println!("execution: {execution:?}"),
732
    ///         PlaceOrder::CommissionReport(report) => println!("commission report: {report:?}"),
733
    ///         PlaceOrder::Message(message) => println!("message: {message:?}"),
734
    ///    }
735
    /// }
736
    /// ```
737
    pub fn place_order(&self, order_id: i32, contract: &Contract, order: &Order) -> Result<Subscription<PlaceOrder>, Error> {
3✔
738
        orders::place_order(self, order_id, contract, order)
15✔
739
    }
740

741
    /// Submits or modifies an [Order] without returning a subscription.
742
    ///
743
    /// This is a fire-and-forget method that submits an [Order] for the given [Contract]
744
    /// but does not return a subscription for order updates. To receive order status updates,
745
    /// fills, and commission reports, use the [`order_update_stream`](Client::order_update_stream) method
746
    /// or use [`place_order`](Client::place_order) instead which returns a subscription.
747
    ///
748
    /// # Arguments
749
    /// * `order_id` - ID for [Order]. Get next valid ID using [Client::next_order_id].
750
    /// * `contract` - [Contract] to submit order for.
751
    /// * `order` - [Order] to submit.
752
    ///
753
    /// # Returns
754
    /// * `Ok(())` if the order was successfully sent
755
    /// * `Err(Error)` if validation failed or sending failed
756
    ///
757
    /// # Examples
758
    ///
759
    /// ```no_run
760
    /// use ibapi::Client;
761
    /// use ibapi::contracts::Contract;
762
    /// use ibapi::orders::{order_builder, Action};
763
    ///
764
    /// # fn main() -> Result<(), ibapi::Error> {
765
    ///
766
    /// let client = Client::connect("127.0.0.1:4002", 100)?;
767
    ///
768
    /// let contract = Contract::stock("MSFT");
769
    /// let order = order_builder::market_order(Action::Buy, 100.0);
770
    /// let order_id = client.next_order_id();
771
    ///
772
    /// // Submit order without waiting for confirmation
773
    /// client.submit_order(order_id, &contract, &order)?;
774
    ///
775
    /// // Monitor all order updates via the order update stream
776
    /// // This will receive updates for ALL orders, not just this one
777
    /// use ibapi::orders::OrderUpdate;
778
    /// for event in client.order_update_stream()? {
779
    ///     match event {
780
    ///         OrderUpdate::OrderStatus(status) => println!("Order Status: {status:?}"),
781
    ///         OrderUpdate::ExecutionData(exec) => println!("Execution: {exec:?}"),
782
    ///         OrderUpdate::CommissionReport(report) => println!("Commission: {report:?}"),
783
    ///         _ => {}
784
    ///     }
785
    /// }
786
    ///
787
    /// # Ok(())
788
    /// # }
789
    /// ```
790
    pub fn submit_order(&self, order_id: i32, contract: &Contract, order: &Order) -> Result<(), Error> {
1✔
791
        orders::submit_order(self, order_id, contract, order)
5✔
792
    }
793

794
    /// Creates a subscription stream for receiving real-time order updates.
795
    ///
796
    /// This method establishes a stream that receives all order-related events including:
797
    /// - Order status updates (e.g., submitted, filled, cancelled)
798
    /// - Open order information
799
    /// - Execution data for trades
800
    /// - Commission reports
801
    /// - Order-related messages and notices
802
    ///
803
    /// The stream will receive updates for all orders placed through this client connection,
804
    /// including both new orders submitted after creating the stream and existing orders.
805
    ///
806
    /// # Returns
807
    ///
808
    /// Returns a `Subscription<OrderUpdate>` that yields `OrderUpdate` enum variants containing:
809
    /// - `OrderStatus`: Current status of an order (filled amount, average price, etc.)
810
    /// - `OpenOrder`: Complete order details including contract and order parameters
811
    /// - `ExecutionData`: Details about individual trade executions
812
    /// - `CommissionReport`: Commission information for executed trades
813
    /// - `Message`: Notices or error messages related to orders
814
    ///
815
    /// # Errors
816
    ///
817
    /// Returns an error if the subscription cannot be created, typically due to
818
    /// connection issues or internal errors.
819
    ///
820
    /// # Examples
821
    ///
822
    /// ```no_run
823
    /// use ibapi::Client;
824
    /// use ibapi::orders::OrderUpdate;
825
    ///
826
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
827
    ///
828
    /// // Create order update stream
829
    /// let updates = client.order_update_stream().expect("failed to create stream");
830
    ///
831
    /// // Process order updates
832
    /// for update in updates {
833
    ///     match update {
834
    ///         OrderUpdate::OrderStatus(status) => {
835
    ///             println!("Order {} status: {} - filled: {}/{}",
836
    ///                 status.order_id, status.status, status.filled, status.remaining);
837
    ///         },
838
    ///         OrderUpdate::OpenOrder(order_data) => {
839
    ///             println!("Open order {}: {} {} @ {}",
840
    ///                 order_data.order.order_id,
841
    ///                 order_data.order.action,
842
    ///                 order_data.order.total_quantity,
843
    ///                 order_data.order.limit_price.unwrap_or(0.0));
844
    ///         },
845
    ///         OrderUpdate::ExecutionData(exec) => {
846
    ///             println!("Execution: {} {} @ {} on {}",
847
    ///                 exec.execution.side,
848
    ///                 exec.execution.shares,
849
    ///                 exec.execution.price,
850
    ///                 exec.execution.exchange);
851
    ///         },
852
    ///         OrderUpdate::CommissionReport(report) => {
853
    ///             println!("Commission: ${} for execution {}",
854
    ///                 report.commission, report.execution_id);
855
    ///         },
856
    ///         OrderUpdate::Message(notice) => {
857
    ///             println!("Order message: {}", notice.message);
858
    ///         }
859
    ///     }
860
    /// }
861
    /// ```
862
    ///
863
    /// # Note
864
    ///
865
    /// This stream provides updates for all orders, not just a specific order.
866
    /// To track a specific order, filter the updates by order ID.
867
    pub fn order_update_stream(&self) -> Result<Subscription<OrderUpdate>, Error> {
3✔
868
        orders::order_update_stream(self)
6✔
869
    }
870

871
    /// Exercises an options contract.
872
    ///
873
    /// Note: this function is affected by a TWS setting which specifies if an exercise request must be finalized.
874
    ///
875
    /// # Arguments
876
    /// * `contract`          - The option [Contract] to be exercised.
877
    /// * `exercise_action`   - Exercise option. ExerciseAction::Exercise or ExerciseAction::Lapse.
878
    /// * `exercise_quantity` - Number of contracts to be exercised.
879
    /// * `account`           - Destination account.
880
    /// * `ovrd`              - Specifies whether your setting will override the system’s natural action. For example, if your action is "exercise" and the option is not in-the-money, by natural action the option would not exercise. If you have override set to true the natural action would be overridden and the out-of-the money option would be exercised.
881
    /// * `manual_order_time` - Specify the time at which the options should be exercised. If `None`, the current time will be used. Requires TWS API 10.26 or higher.
882
    pub fn exercise_options<'a>(
×
883
        &'a self,
884
        contract: &Contract,
885
        exercise_action: orders::ExerciseAction,
886
        exercise_quantity: i32,
887
        account: &str,
888
        ovrd: bool,
889
        manual_order_time: Option<OffsetDateTime>,
890
    ) -> Result<Subscription<'a, ExerciseOptions>, Error> {
891
        orders::exercise_options(self, contract, exercise_action, exercise_quantity, account, ovrd, manual_order_time)
×
892
    }
893

894
    // === Historical Market Data ===
895

896
    /// Returns the timestamp of earliest available historical data for a contract and data type.
897
    ///
898
    /// ```no_run
899
    /// use ibapi::Client;
900
    /// use ibapi::contracts::Contract;
901
    /// use ibapi::market_data::historical::{self, WhatToShow};
902
    ///
903
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
904
    ///
905
    /// let contract = Contract::stock("MSFT");
906
    /// let what_to_show = WhatToShow::Trades;
907
    /// let use_rth = true;
908
    ///
909
    /// let result = client.head_timestamp(&contract, what_to_show, use_rth).expect("head timestamp failed");
910
    ///
911
    /// print!("head_timestamp: {result:?}");
912
    /// ```
913
    pub fn head_timestamp(&self, contract: &Contract, what_to_show: historical::WhatToShow, use_rth: bool) -> Result<OffsetDateTime, Error> {
1✔
914
        historical::head_timestamp(self, contract, what_to_show, use_rth)
5✔
915
    }
916

917
    /// Requests interval of historical data ending at specified time for [Contract].
918
    ///
919
    /// # Arguments
920
    /// * `contract`     - [Contract] to retrieve [historical::HistoricalData] for.
921
    /// * `interval_end` - optional end date of interval to retrieve [historical::HistoricalData] for. If `None` current time or last trading of contract is implied.
922
    /// * `duration`     - duration of interval to retrieve [historical::HistoricalData] for.
923
    /// * `bar_size`     - [historical::BarSize] to return.
924
    /// * `what_to_show` - requested bar type: [historical::WhatToShow].
925
    /// * `use_rth`      - use regular trading hours.
926
    ///
927
    /// # Examples
928
    ///
929
    /// ```no_run
930
    /// use time::macros::datetime;
931
    ///
932
    /// use ibapi::contracts::Contract;
933
    /// use ibapi::Client;
934
    /// use ibapi::market_data::historical::{BarSize, ToDuration, WhatToShow};
935
    ///
936
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
937
    ///
938
    /// let contract = Contract::stock("TSLA");
939
    ///
940
    /// let historical_data = client
941
    ///     .historical_data(&contract, Some(datetime!(2023-04-15 0:00 UTC)), 7.days(), BarSize::Day, WhatToShow::Trades, true)
942
    ///     .expect("historical data request failed");
943
    ///
944
    /// println!("start_date: {}, end_date: {}", historical_data.start, historical_data.end);
945
    ///
946
    /// for bar in &historical_data.bars {
947
    ///     println!("{bar:?}");
948
    /// }
949
    /// ```
950
    pub fn historical_data(
6✔
951
        &self,
952
        contract: &Contract,
953
        interval_end: Option<OffsetDateTime>,
954
        duration: historical::Duration,
955
        bar_size: historical::BarSize,
956
        what_to_show: historical::WhatToShow,
957
        use_rth: bool,
958
    ) -> Result<historical::HistoricalData, Error> {
959
        historical::historical_data(self, contract, interval_end, duration, bar_size, Some(what_to_show), use_rth)
48✔
960
    }
961

962
    /// Requests [Schedule](historical::Schedule) for an interval of given duration
963
    /// ending at specified date.
964
    ///
965
    /// # Arguments
966
    /// * `contract`     - [Contract] to retrieve [Schedule](historical::Schedule) for.
967
    /// * `interval_end` - end date of interval to retrieve [Schedule](historical::Schedule) for.
968
    /// * `duration`     - duration of interval to retrieve [Schedule](historical::Schedule) for.
969
    ///
970
    /// # Examples
971
    ///
972
    /// ```no_run
973
    /// use time::macros::datetime;
974
    /// use ibapi::contracts::Contract;
975
    /// use ibapi::Client;
976
    /// use ibapi::market_data::historical::ToDuration;
977
    ///
978
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
979
    ///
980
    /// let contract = Contract::stock("GM");
981
    ///
982
    /// let historical_data = client
983
    ///     .historical_schedules(&contract, datetime!(2023-04-15 0:00 UTC), 30.days())
984
    ///     .expect("historical schedule request failed");
985
    ///
986
    /// println!("start: {:?}, end: {:?}", historical_data.start, historical_data.end);
987
    ///
988
    /// for session in &historical_data.sessions {
989
    ///     println!("{session:?}");
990
    /// }
991
    /// ```
992
    pub fn historical_schedules(
1✔
993
        &self,
994
        contract: &Contract,
995
        interval_end: OffsetDateTime,
996
        duration: historical::Duration,
997
    ) -> Result<historical::Schedule, Error> {
998
        historical::historical_schedule(self, contract, Some(interval_end), duration)
5✔
999
    }
1000

1001
    /// Requests [historical::Schedule] for interval ending at current time.
1002
    ///
1003
    /// # Arguments
1004
    /// * `contract` - [Contract] to retrieve [historical::Schedule] for.
1005
    /// * `duration` - [historical::Duration] for interval to retrieve [historical::Schedule] for.
1006
    ///
1007
    /// # Examples
1008
    ///
1009
    /// ```no_run
1010
    /// use ibapi::contracts::Contract;
1011
    /// use ibapi::Client;
1012
    /// use ibapi::market_data::historical::ToDuration;
1013
    ///
1014
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1015
    ///
1016
    /// let contract = Contract::stock("GM");
1017
    ///
1018
    /// let historical_data = client
1019
    ///     .historical_schedules_ending_now(&contract, 30.days())
1020
    ///     .expect("historical schedule request failed");
1021
    ///
1022
    /// println!("start: {:?}, end: {:?}", historical_data.start, historical_data.end);
1023
    ///
1024
    /// for session in &historical_data.sessions {
1025
    ///     println!("{session:?}");
1026
    /// }
1027
    /// ```
1028
    pub fn historical_schedules_ending_now(&self, contract: &Contract, duration: historical::Duration) -> Result<historical::Schedule, Error> {
×
1029
        historical::historical_schedule(self, contract, None, duration)
×
1030
    }
1031

1032
    /// Requests historical time & sales data (Bid/Ask) for an instrument.
1033
    ///
1034
    /// # Arguments
1035
    /// * `contract` - [Contract] object that is subject of query
1036
    /// * `start`    - Start time. Either start time or end time is specified.
1037
    /// * `end`      - End time. Either start time or end time is specified.
1038
    /// * `number_of_ticks` - Number of distinct data points. Max currently 1000 per request.
1039
    /// * `use_rth`         - Data from regular trading hours (true), or all available hours (false)
1040
    /// * `ignore_size`     - A filter only used when the source price is Bid_Ask
1041
    ///
1042
    /// # Examples
1043
    ///
1044
    /// ```no_run
1045
    /// use time::macros::datetime;
1046
    ///
1047
    /// use ibapi::contracts::Contract;
1048
    /// use ibapi::Client;
1049
    ///
1050
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1051
    ///
1052
    /// let contract = Contract::stock("TSLA");
1053
    ///
1054
    /// let ticks = client
1055
    ///     .historical_ticks_bid_ask(&contract, Some(datetime!(2023-04-15 0:00 UTC)), None, 100, true, false)
1056
    ///     .expect("historical ticks request failed");
1057
    ///
1058
    /// for tick in ticks {
1059
    ///     println!("{tick:?}");
1060
    /// }
1061
    /// ```
1062
    pub fn historical_ticks_bid_ask(
2✔
1063
        &self,
1064
        contract: &Contract,
1065
        start: Option<OffsetDateTime>,
1066
        end: Option<OffsetDateTime>,
1067
        number_of_ticks: i32,
1068
        use_rth: bool,
1069
        ignore_size: bool,
1070
    ) -> Result<historical::TickSubscription<historical::TickBidAsk>, Error> {
1071
        historical::historical_ticks_bid_ask(self, contract, start, end, number_of_ticks, use_rth, ignore_size)
16✔
1072
    }
1073

1074
    /// Requests historical time & sales data (Midpoint) for an instrument.
1075
    ///
1076
    /// # Arguments
1077
    /// * `contract` - [Contract] object that is subject of query
1078
    /// * `start`    - Start time. Either start time or end time is specified.
1079
    /// * `end`      - End time. Either start time or end time is specified.
1080
    /// * `number_of_ticks` - Number of distinct data points. Max currently 1000 per request.
1081
    /// * `use_rth`         - Data from regular trading hours (true), or all available hours (false)
1082
    ///
1083
    /// # Examples
1084
    ///
1085
    /// ```no_run
1086
    /// use time::macros::datetime;
1087
    ///
1088
    /// use ibapi::contracts::Contract;
1089
    /// use ibapi::Client;
1090
    ///
1091
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1092
    ///
1093
    /// let contract = Contract::stock("TSLA");
1094
    ///
1095
    /// let ticks = client
1096
    ///     .historical_ticks_mid_point(&contract, Some(datetime!(2023-04-15 0:00 UTC)), None, 100, true)
1097
    ///     .expect("historical ticks request failed");
1098
    ///
1099
    /// for tick in ticks {
1100
    ///     println!("{tick:?}");
1101
    /// }
1102
    /// ```
1103
    pub fn historical_ticks_mid_point(
2✔
1104
        &self,
1105
        contract: &Contract,
1106
        start: Option<OffsetDateTime>,
1107
        end: Option<OffsetDateTime>,
1108
        number_of_ticks: i32,
1109
        use_rth: bool,
1110
    ) -> Result<historical::TickSubscription<historical::TickMidpoint>, Error> {
1111
        historical::historical_ticks_mid_point(self, contract, start, end, number_of_ticks, use_rth)
14✔
1112
    }
1113

1114
    /// Requests historical time & sales data (Trades) for an instrument.
1115
    ///
1116
    /// # Arguments
1117
    /// * `contract` - [Contract] object that is subject of query
1118
    /// * `start`    - Start time. Either start time or end time is specified.
1119
    /// * `end`      - End time. Either start time or end time is specified.
1120
    /// * `number_of_ticks` - Number of distinct data points. Max currently 1000 per request.
1121
    /// * `use_rth`         - Data from regular trading hours (true), or all available hours (false)
1122
    ///
1123
    /// # Examples
1124
    ///
1125
    /// ```no_run
1126
    /// use time::macros::datetime;
1127
    ///
1128
    /// use ibapi::contracts::Contract;
1129
    /// use ibapi::Client;
1130
    ///
1131
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1132
    ///
1133
    /// let contract = Contract::stock("TSLA");
1134
    ///
1135
    /// let ticks = client
1136
    ///     .historical_ticks_trade(&contract, Some(datetime!(2023-04-15 0:00 UTC)), None, 100, true)
1137
    ///     .expect("historical ticks request failed");
1138
    ///
1139
    /// for tick in ticks {
1140
    ///     println!("{tick:?}");
1141
    /// }
1142
    /// ```
1143
    pub fn historical_ticks_trade(
4✔
1144
        &self,
1145
        contract: &Contract,
1146
        start: Option<OffsetDateTime>,
1147
        end: Option<OffsetDateTime>,
1148
        number_of_ticks: i32,
1149
        use_rth: bool,
1150
    ) -> Result<historical::TickSubscription<historical::TickLast>, Error> {
1151
        historical::historical_ticks_trade(self, contract, start, end, number_of_ticks, use_rth)
28✔
1152
    }
1153

1154
    /// Requests data histogram of specified contract.
1155
    ///
1156
    /// # Arguments
1157
    /// * `contract`  - [Contract] to retrieve [Histogram Entries](historical::HistogramEntry) for.
1158
    /// * `use_rth`   - Data from regular trading hours (true), or all available hours (false).
1159
    /// * `period`    - The time period of each histogram bar (e.g., `BarSize::Day`, `BarSize::Week`, `BarSize::Month`).
1160
    ///
1161
    /// # Examples
1162
    ///
1163
    /// ```no_run
1164
    /// use time::macros::datetime;
1165
    //
1166
    /// use ibapi::contracts::Contract;
1167
    /// use ibapi::Client;
1168
    /// use ibapi::market_data::historical::BarSize;
1169
    ///
1170
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1171
    ///
1172
    /// let contract = Contract::stock("GM");
1173
    ///
1174
    /// let histogram = client
1175
    ///     .histogram_data(&contract, true, BarSize::Week)
1176
    ///     .expect("histogram request failed");
1177
    ///
1178
    /// for item in &histogram {
1179
    ///     println!("{item:?}");
1180
    /// }
1181
    /// ```
1182
    pub fn histogram_data(&self, contract: &Contract, use_rth: bool, period: historical::BarSize) -> Result<Vec<HistogramEntry>, Error> {
1✔
1183
        historical::histogram_data(self, contract, use_rth, period)
5✔
1184
    }
1185

1186
    // === Realtime Market Data ===
1187

1188
    /// Requests realtime bars.
1189
    ///
1190
    /// # Arguments
1191
    /// * `contract` - The [Contract] used as sample to query the available contracts. Typically, it will contain the [Contract]'s symbol, currency, security_type, and exchange.
1192
    ///
1193
    /// # Examples
1194
    ///
1195
    /// ```no_run
1196
    /// use ibapi::Client;
1197
    /// use ibapi::contracts::Contract;
1198
    /// use ibapi::market_data::realtime::{BarSize, WhatToShow};
1199
    ///
1200
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1201
    ///
1202
    /// let contract = Contract::stock("TSLA");
1203
    /// let subscription = client.realtime_bars(&contract, BarSize::Sec5, WhatToShow::Trades, false).expect("request failed");
1204
    ///
1205
    /// for (i, bar) in subscription.iter().enumerate().take(60) {
1206
    ///     println!("bar[{i}]: {bar:?}");
1207
    /// }
1208
    /// ```
1209
    pub fn realtime_bars<'a>(
2✔
1210
        &'a self,
1211
        contract: &Contract,
1212
        bar_size: BarSize,
1213
        what_to_show: WhatToShow,
1214
        use_rth: bool,
1215
    ) -> Result<Subscription<'a, Bar>, Error> {
1216
        realtime::realtime_bars(self, contract, &bar_size, &what_to_show, use_rth, Vec::default())
14✔
1217
    }
1218

1219
    /// Requests tick by tick AllLast ticks.
1220
    ///
1221
    /// # Arguments
1222
    /// * `contract`        - The [Contract] for which to request tick-by-tick data.
1223
    /// * `number_of_ticks` - The number of ticks to retrieve. TWS usually limits this to 1000.
1224
    /// * `ignore_size`     - Specifies if tick sizes should be ignored.
1225
    ///
1226
    /// # Examples
1227
    ///
1228
    /// ```no_run
1229
    /// use ibapi::Client;
1230
    /// use ibapi::contracts::Contract;
1231
    ///
1232
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1233
    ///
1234
    /// let contract = Contract::stock("AAPL");
1235
    /// let number_of_ticks = 10; // Request a small number of ticks for the example
1236
    /// let ignore_size = false;
1237
    ///
1238
    /// let subscription = client.tick_by_tick_all_last(&contract, number_of_ticks, ignore_size)
1239
    ///     .expect("tick-by-tick all last data request failed");
1240
    ///
1241
    /// for tick in subscription.iter().take(number_of_ticks as usize) { // Take to limit example output
1242
    ///     println!("All Last Tick: {tick:?}");
1243
    /// }
1244
    /// ```
1245
    pub fn tick_by_tick_all_last<'a>(
1✔
1246
        &'a self,
1247
        contract: &Contract,
1248
        number_of_ticks: i32,
1249
        ignore_size: bool,
1250
    ) -> Result<Subscription<'a, realtime::Trade>, Error> {
1251
        realtime::tick_by_tick_all_last(self, contract, number_of_ticks, ignore_size)
5✔
1252
    }
1253

1254
    /// Requests tick by tick BidAsk ticks.
1255
    ///
1256
    /// # Arguments
1257
    /// * `contract`        - The [Contract] for which to request tick-by-tick data.
1258
    /// * `number_of_ticks` - The number of ticks to retrieve. TWS usually limits this to 1000.
1259
    /// * `ignore_size`     - Specifies if tick sizes should be ignored. (typically true for BidAsk ticks to get changes based on price).
1260
    ///
1261
    /// # Examples
1262
    ///
1263
    /// ```no_run
1264
    /// use ibapi::Client;
1265
    /// use ibapi::contracts::Contract;
1266
    ///
1267
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1268
    ///
1269
    /// let contract = Contract::stock("AAPL");
1270
    /// let number_of_ticks = 10; // Request a small number of ticks for the example
1271
    /// let ignore_size = false;
1272
    ///
1273
    /// let subscription = client.tick_by_tick_bid_ask(&contract, number_of_ticks, ignore_size)
1274
    ///     .expect("tick-by-tick bid/ask data request failed");
1275
    ///
1276
    /// for tick in subscription.iter().take(number_of_ticks as usize) { // Take to limit example output
1277
    ///     println!("BidAsk Tick: {tick:?}");
1278
    /// }
1279
    /// ```
1280
    pub fn tick_by_tick_bid_ask<'a>(
1✔
1281
        &'a self,
1282
        contract: &Contract,
1283
        number_of_ticks: i32,
1284
        ignore_size: bool,
1285
    ) -> Result<Subscription<'a, realtime::BidAsk>, Error> {
1286
        realtime::tick_by_tick_bid_ask(self, contract, number_of_ticks, ignore_size)
5✔
1287
    }
1288

1289
    /// Requests tick by tick Last ticks.
1290
    ///
1291
    /// # Arguments
1292
    /// * `contract`        - The [Contract] for which to request tick-by-tick data.
1293
    /// * `number_of_ticks` - The number of ticks to retrieve. TWS usually limits this to 1000.
1294
    /// * `ignore_size`     - Specifies if tick sizes should be ignored (typically false for Last ticks).
1295
    ///
1296
    /// # Examples
1297
    ///
1298
    /// ```no_run
1299
    /// use ibapi::Client;
1300
    /// use ibapi::contracts::Contract;
1301
    ///
1302
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1303
    ///
1304
    /// let contract = Contract::stock("AAPL");
1305
    /// let number_of_ticks = 10; // Request a small number of ticks for the example
1306
    /// let ignore_size = false;
1307
    ///
1308
    /// let subscription = client.tick_by_tick_last(&contract, number_of_ticks, ignore_size)
1309
    ///     .expect("tick-by-tick last data request failed");
1310
    ///
1311
    /// for tick in subscription.iter().take(number_of_ticks as usize) { // Take to limit example output
1312
    ///     println!("Last Tick: {tick:?}");
1313
    /// }
1314
    /// ```
1315
    pub fn tick_by_tick_last<'a>(
1✔
1316
        &'a self,
1317
        contract: &Contract,
1318
        number_of_ticks: i32,
1319
        ignore_size: bool,
1320
    ) -> Result<Subscription<'a, realtime::Trade>, Error> {
1321
        realtime::tick_by_tick_last(self, contract, number_of_ticks, ignore_size)
5✔
1322
    }
1323

1324
    /// Requests tick by tick MidPoint ticks.
1325
    ///
1326
    /// # Arguments
1327
    /// * `contract`        - The [Contract] for which to request tick-by-tick data.
1328
    /// * `number_of_ticks` - The number of ticks to retrieve. TWS usually limits this to 1000.
1329
    /// * `ignore_size`     - Specifies if tick sizes should be ignored.
1330
    ///
1331
    /// # Examples
1332
    ///
1333
    /// ```no_run
1334
    /// use ibapi::Client;
1335
    /// use ibapi::contracts::Contract;
1336
    ///
1337
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1338
    ///
1339
    /// let contract = Contract::stock("AAPL");
1340
    /// let number_of_ticks = 10; // Request a small number of ticks for the example
1341
    /// let ignore_size = false;
1342
    ///
1343
    /// let subscription = client.tick_by_tick_bid_ask(&contract, number_of_ticks, ignore_size)
1344
    ///     .expect("tick-by-tick mid-point data request failed");
1345
    ///
1346
    /// for tick in subscription.iter().take(number_of_ticks as usize) { // Take to limit example output
1347
    ///     println!("MidPoint Tick: {tick:?}");
1348
    /// }
1349
    /// ```
1350
    pub fn tick_by_tick_midpoint<'a>(
1✔
1351
        &'a self,
1352
        contract: &Contract,
1353
        number_of_ticks: i32,
1354
        ignore_size: bool,
1355
    ) -> Result<Subscription<'a, MidPoint>, Error> {
1356
        realtime::tick_by_tick_midpoint(self, contract, number_of_ticks, ignore_size)
5✔
1357
    }
1358

1359
    /// Switches market data type returned from request_market_data requests to Live, Frozen, Delayed, or FrozenDelayed.
1360
    ///
1361
    /// # Arguments
1362
    /// * `market_data_type` - Type of market data to retrieve.
1363
    ///
1364
    /// # Examples
1365
    ///
1366
    /// ```no_run
1367
    /// use ibapi::Client;
1368
    /// use ibapi::market_data::{MarketDataType};
1369
    ///
1370
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1371
    ///
1372
    /// let market_data_type = MarketDataType::Live;
1373
    /// client.switch_market_data_type(market_data_type).expect("request failed");
1374
    /// println!("market data switched: {market_data_type:?}");
1375
    /// ```
1376
    pub fn switch_market_data_type(&self, market_data_type: MarketDataType) -> Result<(), Error> {
1✔
1377
        market_data::switch_market_data_type(self, market_data_type)
3✔
1378
    }
1379

1380
    /// Requests the contract's market depth (order book).
1381
    ///
1382
    /// # Arguments
1383
    ///
1384
    /// * `contract` - The Contract for which the depth is being requested.
1385
    /// * `number_of_rows` - The number of rows on each side of the order book.
1386
    /// * `is_smart_depth` - Flag indicates that this is smart depth request.
1387
    ///
1388
    /// # Examples
1389
    ///
1390
    /// ```no_run
1391
    /// use ibapi::Client;
1392
    /// use ibapi::contracts::Contract;
1393
    ///
1394
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1395
    ///
1396
    /// let contract = Contract::stock("AAPL");
1397
    ///
1398
    /// let subscription = client.market_depth(&contract, 5, true).expect("error requesting market depth");
1399
    /// for row in &subscription {
1400
    ///     println!("row: {row:?}");
1401
    /// }
1402
    ///
1403
    /// if let Some(error) = subscription.error() {
1404
    ///     println!("error: {error:?}");
1405
    /// }
1406
    /// ```
1407
    pub fn market_depth<'a>(
1✔
1408
        &'a self,
1409
        contract: &Contract,
1410
        number_of_rows: i32,
1411
        is_smart_depth: bool,
1412
    ) -> Result<Subscription<'a, MarketDepths>, Error> {
1413
        realtime::market_depth(self, contract, number_of_rows, is_smart_depth)
5✔
1414
    }
1415

1416
    /// Requests venues for which market data is returned to market_depth (those with market makers)
1417
    ///
1418
    /// # Examples
1419
    ///
1420
    /// ```no_run
1421
    /// use ibapi::Client;
1422
    ///
1423
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1424
    /// let exchanges = client.market_depth_exchanges().expect("error requesting market depth exchanges");
1425
    /// for exchange in &exchanges {
1426
    ///     println!("{exchange:?}");
1427
    /// }
1428
    /// ```
1429
    pub fn market_depth_exchanges(&self) -> Result<Vec<DepthMarketDataDescription>, Error> {
1✔
1430
        realtime::market_depth_exchanges(self)
2✔
1431
    }
1432

1433
    /// Requests real time market data.
1434
    ///
1435
    /// Returns market data for an instrument either in real time or 10-15 minutes delayed data.
1436
    ///
1437
    /// # Arguments
1438
    ///
1439
    /// * `contract` - Contract for which the data is being requested.
1440
    /// * `generic_ticks` - IDs of the available generic ticks:
1441
    ///   - 100 Option Volume (currently for stocks)
1442
    ///   - 101 Option Open Interest (currently for stocks)
1443
    ///   - 104 Historical Volatility (currently for stocks)
1444
    ///   - 105 Average Option Volume (currently for stocks)
1445
    ///   - 106 Option Implied Volatility (currently for stocks)
1446
    ///   - 162 Index Future Premium
1447
    ///   - 165 Miscellaneous Stats
1448
    ///   - 221 Mark Price (used in TWS P&L computations)
1449
    ///   - 225 Auction values (volume, price and imbalance)
1450
    ///   - 233 RTVolume - contains the last trade price, last trade size, last trade time, total volume, VWAP, and single trade flag.
1451
    ///   - 236 Shortable
1452
    ///   - 256 Inventory
1453
    ///   - 258 Fundamental Ratios
1454
    ///   - 411 Realtime Historical Volatility
1455
    ///   - 456 IBDividends
1456
    /// * `snapshot` - for users with corresponding real time market data subscriptions. A true value will return a one-time snapshot, while a false value will provide streaming data.
1457
    /// * `regulatory_snapshot` - snapshot for US stocks requests NBBO snapshots for users which have "US Securities Snapshot Bundle" subscription but not corresponding Network A, B, or C subscription necessary for streaming market data. One-time snapshot of current market price that will incur a fee of 1 cent to the account per snapshot.
1458
    ///
1459
    /// # Examples
1460
    ///
1461
    /// ```no_run
1462
    /// use ibapi::{contracts::Contract, market_data::realtime::TickTypes, Client};
1463
    ///
1464
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1465
    ///
1466
    /// let contract = Contract::stock("AAPL");
1467
    ///
1468
    /// // https://www.interactivebrokers.com/campus/ibkr-api-page/twsapi-doc/#available-tick-types
1469
    /// let generic_ticks = &["233", "293"];
1470
    /// let snapshot = false;
1471
    /// let regulatory_snapshot = false;
1472
    ///
1473
    /// let subscription = client
1474
    ///     .market_data(&contract, generic_ticks, snapshot, regulatory_snapshot)
1475
    ///     .expect("error requesting market data");
1476
    ///
1477
    /// for tick in &subscription {
1478
    ///     match tick {
1479
    ///         TickTypes::Price(tick_price) => println!("{tick_price:?}"),
1480
    ///         TickTypes::Size(tick_size) => println!("{tick_size:?}"),
1481
    ///         TickTypes::PriceSize(tick_price_size) => println!("{tick_price_size:?}"),
1482
    ///         TickTypes::Generic(tick_generic) => println!("{tick_generic:?}"),
1483
    ///         TickTypes::String(tick_string) => println!("{tick_string:?}"),
1484
    ///         TickTypes::EFP(tick_efp) => println!("{tick_efp:?}"),
1485
    ///         TickTypes::OptionComputation(option_computation) => println!("{option_computation:?}"),
1486
    ///         TickTypes::RequestParameters(tick_request_parameters) => println!("{tick_request_parameters:?}"),
1487
    ///         TickTypes::Notice(notice) => println!("{notice:?}"),
1488
    ///         TickTypes::SnapshotEnd => subscription.cancel(),
1489
    ///     }
1490
    /// }
1491
    /// ```
1492
    pub fn market_data(
5✔
1493
        &self,
1494
        contract: &Contract,
1495
        generic_ticks: &[&str],
1496
        snapshot: bool,
1497
        regulatory_snapshot: bool,
1498
    ) -> Result<Subscription<TickTypes>, Error> {
1499
        realtime::market_data(self, contract, generic_ticks, snapshot, regulatory_snapshot)
30✔
1500
    }
1501

1502
    // === News ===
1503

1504
    /// Requests news providers which the user has subscribed to.
1505
    ///
1506
    /// # Examples
1507
    ///
1508
    /// ```no_run
1509
    /// use ibapi::Client;
1510
    ///
1511
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1512
    ///
1513
    /// let news_providers = client.news_providers().expect("request news providers failed");
1514
    /// for news_provider in &news_providers {
1515
    ///   println!("news provider {news_provider:?}");
1516
    /// }
1517
    /// ```
1518
    pub fn news_providers(&self) -> Result<Vec<news::NewsProvider>, Error> {
×
1519
        news::news_providers(self)
×
1520
    }
1521

1522
    /// Subscribes to IB's News Bulletins.
1523
    ///
1524
    /// # Arguments
1525
    ///
1526
    /// * `all_messages` - If set to true, will return all the existing bulletins for the current day, set to false to receive only the new bulletins.
1527
    ///
1528
    /// # Examples
1529
    ///
1530
    /// ```no_run
1531
    /// use ibapi::Client;
1532
    ///
1533
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1534
    ///
1535
    /// let news_bulletins = client.news_bulletins(true).expect("request news providers failed");
1536
    /// for news_bulletin in &news_bulletins {
1537
    ///   println!("news bulletin {news_bulletin:?}");
1538
    /// }
1539
    /// ```
1540
    pub fn news_bulletins(&self, all_messages: bool) -> Result<Subscription<news::NewsBulletin>, Error> {
×
1541
        news::news_bulletins(self, all_messages)
×
1542
    }
1543

1544
    /// Requests historical news headlines.
1545
    ///
1546
    /// # Arguments
1547
    ///
1548
    /// * `contract_id`    - Contract ID of ticker. See [contract_details](Client::contract_details) for how to retrieve contract ID.
1549
    /// * `provider_codes` - A list of provider codes.
1550
    /// * `start_time`     - Marks the (exclusive) start of the date range.
1551
    /// * `end_time`       - Marks the (inclusive) end of the date range.
1552
    /// * `total_results`  - The maximum number of headlines to fetch (1 – 300)
1553
    ///
1554
    /// # Examples
1555
    ///
1556
    /// ```no_run
1557
    /// use ibapi::Client;
1558
    /// use ibapi::contracts::Contract; // Or remove if conId is always known
1559
    /// use time::macros::datetime;
1560
    ///
1561
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1562
    ///
1563
    /// // Example: Fetch historical news for a known contract ID (e.g., AAPL's conId)
1564
    /// let contract_id = 265598;
1565
    /// let provider_codes = &["DJNL", "BRFG"]; // Example provider codes
1566
    /// // Define a past date range for the news query
1567
    /// let start_time = datetime!(2023-01-01 0:00 UTC);
1568
    /// let end_time = datetime!(2023-01-02 0:00 UTC);
1569
    /// let total_results = 5u8; // Request a small number of articles for the example
1570
    ///
1571
    /// let articles_subscription = client.historical_news(
1572
    ///     contract_id,
1573
    ///     provider_codes,
1574
    ///     start_time,
1575
    ///     end_time,
1576
    ///     total_results,
1577
    /// ).expect("request historical news failed");
1578
    ///
1579
    /// println!("Requested historical news articles:");
1580
    /// for article in articles_subscription.iter().take(total_results as usize) {
1581
    ///     println!("- Headline: {}, ID: {}, Provider: {}, Time: {}",
1582
    ///              article.headline, article.article_id, article.provider_code, article.time);
1583
    /// }
1584
    /// ```
1585
    pub fn historical_news(
×
1586
        &self,
1587
        contract_id: i32,
1588
        provider_codes: &[&str],
1589
        start_time: OffsetDateTime,
1590
        end_time: OffsetDateTime,
1591
        total_results: u8,
1592
    ) -> Result<Subscription<news::NewsArticle>, Error> {
1593
        news::historical_news(self, contract_id, provider_codes, start_time, end_time, total_results)
×
1594
    }
1595

1596
    /// Requests news article body given articleId.
1597
    ///
1598
    /// # Arguments
1599
    ///
1600
    /// * `provider_code` - Short code indicating news provider, e.g. FLY.
1601
    /// * `article_id`    - ID of the specific article.
1602
    ///
1603
    /// # Examples
1604
    ///
1605
    /// ```no_run
1606
    /// use ibapi::Client;
1607
    ///
1608
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1609
    ///
1610
    /// // can get these using the historical_news method
1611
    /// let provider_code = "DJ-N";
1612
    /// let article_id = "DJ-N$1915168d";
1613
    ///
1614
    /// let article = client.news_article(provider_code, article_id).expect("request news article failed");
1615
    /// println!("{article:?}");
1616
    /// ```
1617
    pub fn news_article(&self, provider_code: &str, article_id: &str) -> Result<news::NewsArticleBody, Error> {
×
1618
        news::news_article(self, provider_code, article_id)
×
1619
    }
1620

1621
    /// Requests realtime contract specific news
1622
    ///
1623
    /// # Arguments
1624
    ///
1625
    /// * `contract`       - Contract for which news is being requested.
1626
    /// * `provider_codes` - Short codes indicating news providers, e.g. DJ-N.
1627
    ///
1628
    /// # Examples
1629
    ///
1630
    /// ```no_run
1631
    /// use ibapi::Client;
1632
    /// use ibapi::contracts::Contract;
1633
    ///
1634
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1635
    ///
1636
    /// let contract = Contract::stock("AAPL");
1637
    /// let provider_codes = ["DJ-N"];
1638
    ///
1639
    /// let subscription = client.contract_news(&contract, &provider_codes).expect("request contract news failed");
1640
    /// for article in &subscription {
1641
    ///     println!("{article:?}");
1642
    /// }
1643
    /// ```
1644
    pub fn contract_news(&self, contract: &Contract, provider_codes: &[&str]) -> Result<Subscription<NewsArticle>, Error> {
×
1645
        news::contract_news(self, contract, provider_codes)
×
1646
    }
1647

1648
    /// Requests realtime BroadTape News
1649
    ///
1650
    /// # Arguments
1651
    ///
1652
    /// * `provider_code` - Short codes indicating news provider, e.g. DJ-N.
1653
    ///
1654
    /// # Examples
1655
    ///
1656
    /// ```no_run
1657
    /// use ibapi::Client;
1658
    ///
1659
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1660
    ///
1661
    /// let provider_code = "BRFG";
1662
    ///
1663
    /// let subscription = client.broad_tape_news(provider_code).expect("request broad tape news failed");
1664
    /// for article in &subscription {
1665
    ///     println!("{article:?}");
1666
    /// }
1667
    /// ```
1668
    pub fn broad_tape_news(&self, provider_code: &str) -> Result<Subscription<NewsArticle>, Error> {
×
1669
        news::broad_tape_news(self, provider_code)
×
1670
    }
1671

1672
    // === Scanner ===
1673

1674
    /// Requests an XML list of scanner parameters valid in TWS.
1675
    ///
1676
    /// # Examples
1677
    ///
1678
    /// ```no_run
1679
    /// use ibapi::Client;
1680
    /// use ibapi::scanner::ScannerSubscription;
1681
    /// use ibapi::orders::TagValue; // Or ensure common::TagValue is the correct path
1682
    ///
1683
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1684
    ///
1685
    /// let mut sub = ScannerSubscription::default();
1686
    /// sub.instrument = Some("STK".to_string());
1687
    /// sub.location_code = Some("STK.US.MAJOR".to_string());
1688
    /// sub.scan_code = Some("TOP_PERC_GAIN".to_string());
1689
    /// // Further customize the subscription object as needed, for example:
1690
    /// // sub.above_price = Some(1.0);
1691
    /// // sub.below_price = Some(100.0);
1692
    /// // sub.number_of_rows = Some(20);
1693
    ///
1694
    /// // Filter options are advanced and not always needed. Pass an empty Vec if not used.
1695
    /// let filter_options: Vec<TagValue> = Vec::new();
1696
    /// // Example of adding a filter:
1697
    /// // filter_options.push(TagValue { tag: "marketCapAbove".to_string(), value: "1000000000".to_string() });
1698
    ///
1699
    /// match client.scanner_subscription(&sub, &filter_options) {
1700
    ///     Ok(subscription) => {
1701
    ///         // Iterate over received scanner data.
1702
    ///         // Note: Scanner subscriptions can be continuous or return a snapshot.
1703
    ///         // This example just takes the first batch if available.
1704
    ///         if let Some(scanner_results_vec) = subscription.iter().next() {
1705
    ///             println!("Scanner Results (first batch):");
1706
    ///             for data in scanner_results_vec {
1707
    ///                 println!("  Rank: {}, Symbol: {}",
1708
    ///                          data.rank,
1709
    ///                          data.contract_details.contract.symbol);
1710
    ///             }
1711
    ///         } else {
1712
    ///             println!("No scanner results received in the first check.");
1713
    ///         }
1714
    ///         // In a real application, you might continuously iterate or handle updates.
1715
    ///         // Remember to cancel the subscription when no longer needed if it's continuous.
1716
    ///         // subscription.cancel();
1717
    ///     }
1718
    ///     Err(e) => {
1719
    ///         eprintln!("Failed to start scanner subscription: {e:?}");
1720
    ///     }
1721
    /// };
1722
    /// ```
1723
    pub fn scanner_parameters(&self) -> Result<String, Error> {
1✔
1724
        scanner::scanner_parameters(self)
2✔
1725
    }
1726

1727
    /// Starts a subscription to market scan results based on the provided parameters.
1728
    ///
1729
    /// # Examples
1730
    ///
1731
    /// ```no_run
1732
    /// use ibapi::Client;
1733
    /// use ibapi::scanner::ScannerSubscription;
1734
    /// use ibapi::orders::TagValue;
1735
    ///
1736
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1737
    ///
1738
    /// let mut sub = ScannerSubscription::default();
1739
    /// sub.instrument = Some("STK".to_string());
1740
    /// sub.location_code = Some("STK.US.MAJOR".to_string());
1741
    /// sub.scan_code = Some("TOP_PERC_GAIN".to_string());
1742
    /// // Further customize the subscription object as needed, for example:
1743
    /// // sub.above_price = Some(1.0);
1744
    /// // sub.below_price = Some(100.0);
1745
    /// // sub.number_of_rows = Some(20);
1746
    ///
1747
    /// // Filter options are advanced and not always needed. Pass an empty Vec if not used.
1748
    /// let mut filter_options: Vec<TagValue> = Vec::new();
1749
    /// // Example of adding a filter:
1750
    /// // filter_options.push(TagValue { tag: "marketCapAbove".to_string(), value: "1000000000".to_string() });
1751
    ///
1752
    /// match client.scanner_subscription(&sub, &filter_options) {
1753
    ///     Ok(subscription) => {
1754
    ///         // Iterate over received scanner data.
1755
    ///         // Note: Scanner subscriptions can be continuous or return a snapshot.
1756
    ///         // This example just takes the first batch if available.
1757
    ///         if let Some(scanner_results_vec) = subscription.iter().next() {
1758
    ///             println!("Scanner Results (first batch):");
1759
    ///             for data in scanner_results_vec {
1760
    ///                 println!("  Rank: {}, Symbol: {}",
1761
    ///                          data.rank,
1762
    ///                          data.contract_details.contract.symbol);
1763
    ///             }
1764
    ///         } else {
1765
    ///             println!("No scanner results received in the first check.");
1766
    ///         }
1767
    ///         // In a real application, you might continuously iterate or handle updates.
1768
    ///         // Remember to cancel the subscription when no longer needed if it's continuous.
1769
    ///         // subscription.cancel();
1770
    ///     }
1771
    ///     Err(e) => {
1772
    ///         eprintln!("Failed to start scanner subscription: {e:?}");
1773
    ///     }
1774
    /// };
1775
    /// ```
1776
    pub fn scanner_subscription(
1✔
1777
        &self,
1778
        subscription: &scanner::ScannerSubscription,
1779
        filter: &Vec<orders::TagValue>,
1780
    ) -> Result<Subscription<Vec<ScannerData>>, Error> {
1781
        scanner::scanner_subscription(self, subscription, filter)
4✔
1782
    }
1783

1784
    // == Wall Street Horizon
1785

1786
    /// Requests metadata from the WSH calendar.
1787
    ///
1788
    /// # Examples
1789
    ///
1790
    /// ```no_run
1791
    /// use ibapi::Client;
1792
    ///
1793
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1794
    ///
1795
    /// let metadata = client.wsh_metadata().expect("request wsh metadata failed");
1796
    /// println!("{metadata:?}");
1797
    /// ```
1798
    pub fn wsh_metadata(&self) -> Result<wsh::WshMetadata, Error> {
×
1799
        wsh::wsh_metadata(self)
×
1800
    }
1801

1802
    /// Requests event data for a specified contract from the Wall Street Horizons (WSH) calendar.
1803
    ///
1804
    /// # Arguments
1805
    ///
1806
    /// * `contract_id` - Contract identifier for the event request.
1807
    /// * `start_date`  - Start date of the event request.
1808
    /// * `end_date`    - End date of the event request.
1809
    /// * `limit`       - Maximum number of events to return. Maximum of 100.
1810
    /// * `auto_fill`   - Fields to automatically fill in. See [AutoFill] for more information.
1811
    ///
1812
    /// # Examples
1813
    ///
1814
    /// ```no_run
1815
    /// use ibapi::Client;
1816
    ///
1817
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1818
    ///
1819
    /// let contract_id = 76792991; // TSLA
1820
    /// let event_data = client.wsh_event_data_by_contract(contract_id, None, None, None, None).expect("request wsh event data failed");
1821
    /// println!("{event_data:?}");
1822
    /// ```
1823
    pub fn wsh_event_data_by_contract(
×
1824
        &self,
1825
        contract_id: i32,
1826
        start_date: Option<Date>,
1827
        end_date: Option<Date>,
1828
        limit: Option<i32>,
1829
        auto_fill: Option<AutoFill>,
1830
    ) -> Result<wsh::WshEventData, Error> {
1831
        wsh::wsh_event_data_by_contract(self, contract_id, start_date, end_date, limit, auto_fill)
×
1832
    }
1833

1834
    /// Requests event data from the Wall Street Horizons (WSH) calendar using a JSON filter.
1835
    ///
1836
    /// # Arguments
1837
    ///
1838
    /// * `filter`    - Json-formatted string containing all filter values.
1839
    /// * `limit`     - Maximum number of events to return. Maximum of 100.
1840
    /// * `auto_fill` - Fields to automatically fill in. See [AutoFill] for more information.
1841
    ///
1842
    /// # Examples
1843
    ///
1844
    /// ```no_run
1845
    /// use ibapi::Client;
1846
    ///
1847
    /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
1848
    ///
1849
    /// let filter = ""; // see https://www.interactivebrokers.com/campus/ibkr-api-page/twsapi-doc/#wsheventdata-object
1850
    /// let event_data = client.wsh_event_data_by_filter(filter, None, None).expect("request wsh event data failed");
1851
    /// println!("{event_data:?}");
1852
    /// ```
1853
    pub fn wsh_event_data_by_filter(
×
1854
        &self,
1855
        filter: &str,
1856
        limit: Option<i32>,
1857
        auto_fill: Option<AutoFill>,
1858
    ) -> Result<Subscription<wsh::WshEventData>, Error> {
1859
        wsh::wsh_event_data_by_filter(self, filter, limit, auto_fill)
×
1860
    }
1861

1862
    // == Internal Use ==
1863

1864
    #[cfg(test)]
1865
    pub(crate) fn stubbed(message_bus: Arc<dyn MessageBus>, server_version: i32) -> Client {
157✔
1866
        Client {
1867
            server_version,
1868
            connection_time: None,
1869
            time_zone: None,
1870
            message_bus,
1871
            client_id: 100,
1872
            id_manager: ClientIdManager::new(-1),
157✔
1873
        }
1874
    }
1875

1876
    pub(crate) fn send_request(&self, request_id: i32, message: RequestMessage) -> Result<InternalSubscription, Error> {
87✔
1877
        debug!("send_message({request_id:?}, {message:?})");
87✔
1878
        self.message_bus.send_request(request_id, &message)
261✔
1879
    }
1880

1881
    pub(crate) fn send_order(&self, order_id: i32, message: RequestMessage) -> Result<InternalSubscription, Error> {
5✔
1882
        debug!("send_order({order_id:?}, {message:?})");
5✔
1883
        self.message_bus.send_order_request(order_id, &message)
15✔
1884
    }
1885

1886
    pub(crate) fn send_message(&self, message: RequestMessage) -> Result<(), Error> {
1✔
1887
        debug!("send_message({message:?})");
1✔
1888
        self.message_bus.send_message(&message)
2✔
1889
    }
1890

1891
    /// Creates a subscription for order updates if one is not already active.
1892
    pub(crate) fn create_order_update_subscription(&self) -> Result<InternalSubscription, Error> {
3✔
1893
        self.message_bus.create_order_update_subscription()
3✔
1894
    }
1895

1896
    /// Sends request for the next valid order id.
1897
    pub(crate) fn send_shared_request(&self, message_id: OutgoingMessages, message: RequestMessage) -> Result<InternalSubscription, Error> {
43✔
1898
        self.message_bus.send_shared_request(message_id, &message)
129✔
1899
    }
1900

1901
    pub(crate) fn check_server_version(&self, version: i32, message: &str) -> Result<(), Error> {
8✔
1902
        if version <= self.server_version {
8✔
1903
            Ok(())
8✔
1904
        } else {
UNCOV
1905
            Err(Error::ServerVersion(version, self.server_version, message.into()))
×
1906
        }
1907
    }
1908
}
1909

1910
impl Drop for Client {
1911
    fn drop(&mut self) {
158✔
1912
        debug!("dropping basic client");
158✔
1913
        self.message_bus.ensure_shutdown();
158✔
1914
    }
1915
}
1916

1917
impl Debug for Client {
1918
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
1919
        f.debug_struct("Client")
×
1920
            .field("server_version", &self.server_version)
×
1921
            .field("server_time", &self.connection_time)
×
1922
            .field("client_id", &self.client_id)
×
1923
            .finish()
1924
    }
1925
}
1926

1927
/// Subscriptions facilitate handling responses from TWS that may be delayed or delivered periodically.
1928
///
1929
/// They offer both blocking and non-blocking methods for retrieving data.
1930
///
1931
/// In the simplest case a subscription can be implicitly converted to blocking iterator
1932
/// that cancels the subscription when it goes out of scope.
1933
///
1934
/// ```no_run
1935
/// use ibapi::contracts::Contract;
1936
/// use ibapi::market_data::realtime::{BarSize, WhatToShow};
1937
/// use ibapi::Client;
1938
///
1939
/// let connection_url = "127.0.0.1:4002";
1940
/// let client = Client::connect(connection_url, 100).expect("connection to TWS failed!");
1941
///
1942
/// // Request real-time bars data for AAPL with 5-second intervals
1943
/// let contract = Contract::stock("AAPL");
1944
/// let subscription = client
1945
///     .realtime_bars(&contract, BarSize::Sec5, WhatToShow::Trades, false)
1946
///     .expect("realtime bars request failed!");
1947
///
1948
/// // Use the subscription as a blocking iterator
1949
/// for bar in subscription {
1950
///     // Process each bar here (e.g., print or use in calculations)
1951
///     println!("Received bar: {bar:?}");
1952
/// }
1953
/// // The subscription goes out of scope and is automatically cancelled.
1954
/// ```
1955
///
1956
/// Subscriptions can be explicitly canceled using the [cancel](Subscription::cancel) method.
1957
///
1958
// Re-export SharesChannel trait from subscriptions module
1959
pub use crate::subscriptions::SharesChannel;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc