Skip to main content

ibapi/
lib.rs

1//! [![github]](https://github.com/wboayue/rust-ibapi) [![crates-io]](https://crates.io/crates/ibapi) [![license]](https://opensource.org/licenses/MIT)
2//!
3//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
4//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
5//! [license]: https://img.shields.io/badge/License-MIT-blue.svg?style=for-the-badge&labelColor=555555
6//!
7//! <br>
8//!
9//! A comprehensive Rust implementation of the Interactive Brokers TWS API, providing a robust and
10//! user-friendly interface for TWS and IB Gateway. Designed with simplicity in mind, it integrates smoothly into trading systems.
11//!
12//! **API Documentation:**
13//! * [TWS API Reference](https://interactivebrokers.github.io/tws-api/introduction.html) - Detailed technical documentation
14//! * [IBKR Campus](https://ibkrcampus.com/ibkr-api-page/trader-workstation-api/) - IB's official learning platform
15//!
16//! This fully featured API enables the retrieval of account information, access to real-time and historical market data, order management,
17//! market scanning, and access to news and Wall Street Horizons (WSH) event data. Future updates will focus on bug fixes,
18//! maintaining parity with the official API, and enhancing usability.
19//!
20//! # Example
21//!
22//! Connect to TWS / IB Gateway and place a market order:
23//!
24#![cfg_attr(
25    feature = "async",
26    doc = r#"```no_run
27use ibapi::prelude::*;
28
29#[tokio::main]
30async fn main() {
31    let client = Client::connect("127.0.0.1:4002", 100)
32        .await
33        .expect("connection failed");
34
35    let contract = Contract::stock("AAPL").build();
36    let order_id = client
37        .order(&contract)
38        .buy(100)
39        .market()
40        .submit()
41        .await
42        .expect("order submission failed");
43    println!("submitted order id: {order_id}");
44}
45```"#
46)]
47#![cfg_attr(
48    not(feature = "async"),
49    doc = r#"```no_run
50use ibapi::prelude::*;
51
52fn main() {
53    let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
54
55    let contract = Contract::stock("AAPL").build();
56    let order_id = client
57        .order(&contract)
58        .buy(100)
59        .market()
60        .submit()
61        .expect("order submission failed");
62    println!("submitted order id: {order_id}");
63}
64```"#
65)]
66//!
67//! For broader usage — quick start, examples, migration from v2, full API tour — see the
68//! [README](https://github.com/wboayue/rust-ibapi/blob/main/README.md) and the
69//! [`docs/`](https://github.com/wboayue/rust-ibapi/tree/main/docs) directory.
70
71#![warn(missing_docs)]
72// Allow octal-looking escapes in string literals (used in test data)
73#![allow(clippy::octal_escapes)]
74#![allow(clippy::bool_assert_comparison)]
75#![allow(clippy::useless_format)]
76#![allow(clippy::uninlined_format_args)]
77#![allow(clippy::assertions_on_constants)]
78
79// Feature guards
80#[cfg(not(any(feature = "sync", feature = "async")))]
81compile_error!(
82    "You must enable at least one of the 'sync' or 'async' features to use this crate.\n\
83     The 'async' feature is enabled by default; if you disabled default features, be sure to\n\
84     opt back into either API:\n\
85         ibapi = { version = \"4.0\", default-features = false, features = [\"sync\"] }\n\
86         ibapi = { version = \"4.0\", default-features = false, features = [\"async\"] }\n\
87     You may also enable both to access the synchronous API under `client::blocking`."
88);
89
90#[macro_use]
91mod macros;
92
93/// Describes items present in an account.
94pub mod accounts;
95
96/// TWS API Client.
97///
98/// The Client establishes the connection to TWS or the Gateway.
99/// It manages the routing of messages between TWS and the application.
100pub mod client;
101
102pub(crate) mod transport;
103
104/// Connection management
105pub(crate) mod connection;
106
107/// Typed handshake-time messages delivered to [`ClientBuilder::startup_callback`].
108///
109/// When TWS emits unsolicited `OpenOrder`, `OrderStatus`, account-update, or
110/// other frames during the handshake, the startup callback receives them as
111/// typed [`StartupMessage`] values instead of having them discarded.
112///
113/// # Example
114///
115#[cfg_attr(
116    feature = "async",
117    doc = r#"```no_run
118use ibapi::{Client, StartupMessage};
119use std::sync::{Arc, Mutex};
120
121#[tokio::main]
122async fn main() {
123    let order_ids = Arc::new(Mutex::new(Vec::new()));
124    let order_ids_clone = order_ids.clone();
125
126    let client = Client::builder()
127        .address("127.0.0.1:4002")
128        .client_id(100)
129        .startup_callback(move |msg| if let StartupMessage::OpenOrder(o) = msg {
130            order_ids_clone.lock().unwrap().push(o.order_id);
131        })
132        .connect()
133        .await
134        .expect("connection failed");
135
136    println!("Received {} startup open-orders", order_ids.lock().unwrap().len());
137    drop(client);
138}
139```"#
140)]
141#[cfg_attr(
142    not(feature = "async"),
143    doc = r#"```no_run
144use ibapi::{Client, StartupMessage};
145use std::sync::{Arc, Mutex};
146
147fn main() {
148    let order_ids = Arc::new(Mutex::new(Vec::new()));
149    let order_ids_clone = order_ids.clone();
150
151    let client = Client::builder()
152        .address("127.0.0.1:4002")
153        .client_id(100)
154        .startup_callback(move |msg| if let StartupMessage::OpenOrder(o) = msg {
155            order_ids_clone.lock().unwrap().push(o.order_id);
156        })
157        .connect()
158        .expect("connection failed");
159
160    println!("Received {} startup open-orders", order_ids.lock().unwrap().len());
161    drop(client);
162}
163```"#
164)]
165pub use connection::StartupMessage;
166
167/// Common utilities shared across modules
168pub(crate) mod common;
169
170pub use common::timezone::register_timezone_alias;
171
172/// Display groups subscription support
173pub mod display_groups;
174
175/// Subscription types for streaming data
176pub mod subscriptions;
177
178/// APIs for reading TWS/Gateway configuration (API, precautions, orders, lock-and-exit settings).
179pub mod config;
180/// A [Contract](crate::contracts::Contract) object represents trading instruments such as a stocks, futures or options.
181///
182/// Every time a new request that requires a contract (i.e. market data, order placing, etc.) is sent to the API, the system will try to match the provided contract object with a single candidate. If there is more than one contract matching the same description, the API will return an error notifying you there is an ambiguity. In these cases the API needs further information to narrow down the list of contracts matching the provided description to a single element.
183pub mod contracts;
184// Describes primary data structures used by the model.
185pub mod errors;
186/// APIs for retrieving market data
187pub mod market_data;
188pub(crate) mod messages;
189/// APIs for retrieving news data including articles, bulletins, and providers
190pub mod news;
191/// Data types for building and placing orders.
192pub mod orders;
193/// APIs for working with the market scanner.
194pub mod scanner;
195/// APIs for working with Wall Street Horizon: Earnings Calendar & Event Data.
196pub mod wsh;
197
198/// Server interaction tracing for debugging and monitoring
199pub mod trace;
200
201/// A prelude module for convenient importing of commonly used types.
202pub mod prelude;
203
204/// Protocol version checking and constants for TWS API features.
205pub mod protocol;
206
207/// Generated protobuf message types for the TWS API wire protocol.
208pub(crate) mod proto;
209
210mod server_versions;
211
212#[doc(inline)]
213pub use errors::Error;
214
215#[doc(inline)]
216pub use client::Client;
217#[doc(inline)]
218pub use client::ClientBuilder;
219
220#[doc(inline)]
221pub use messages::{ConnectivityStatus, IncomingMessages, Notice, NoticeCategory, OutgoingMessages};
222
223#[doc(inline)]
224pub use messages::{
225    DATA_ADVISORY_CODES, HANDSHAKE_DECODE_FAILURE_CODE, HANDSHAKE_UNKNOWN_FRAME_CODE, NOTICE_STREAM_LAG_CODE, ORDER_CANCELLED_CODE,
226    ORDER_MESSAGE_CODE, ORDER_REJECTION_CODE_RANGE, SUBSCRIPTION_LAG_CODE, SYSTEM_MESSAGE_CODES, TRANSPORT_RECONNECT_CODE, UNKNOWN_MESSAGE_TYPE_CODE,
227    WARNING_CODE_RANGE,
228};
229
230#[doc(hidden)]
231pub use messages::parser_registry;
232use std::sync::LazyLock;
233use time::{
234    format_description::{self, BorrowedFormatItem},
235    Date,
236};
237
238#[cfg(test)]
239pub(crate) mod stubs;
240
241#[cfg(test)]
242pub(crate) mod tests;
243
244#[cfg(test)]
245#[path = "lib_tests.rs"]
246mod lib_tests;
247
248#[cfg(test)]
249pub(crate) mod testdata;
250
251// ToField
252
253pub(crate) trait ToField {
254    fn to_field(&self) -> String;
255}
256
257impl ToField for bool {
258    fn to_field(&self) -> String {
259        if *self {
260            String::from("1")
261        } else {
262            String::from("0")
263        }
264    }
265}
266
267impl ToField for String {
268    fn to_field(&self) -> String {
269        self.clone()
270    }
271}
272
273impl ToField for Option<String> {
274    fn to_field(&self) -> String {
275        encode_option_field(self)
276    }
277}
278
279impl ToField for &str {
280    fn to_field(&self) -> String {
281        <&str>::clone(self).to_string()
282    }
283}
284
285impl ToField for Option<&str> {
286    fn to_field(&self) -> String {
287        encode_option_field(self)
288    }
289}
290
291impl ToField for usize {
292    fn to_field(&self) -> String {
293        self.to_string()
294    }
295}
296
297impl ToField for i32 {
298    fn to_field(&self) -> String {
299        self.to_string()
300    }
301}
302
303impl ToField for Option<i32> {
304    fn to_field(&self) -> String {
305        encode_option_field(self)
306    }
307}
308
309impl ToField for f64 {
310    fn to_field(&self) -> String {
311        self.to_string()
312    }
313}
314
315impl ToField for Option<f64> {
316    fn to_field(&self) -> String {
317        encode_option_field(self)
318    }
319}
320
321fn date_format() -> Vec<BorrowedFormatItem<'static>> {
322    format_description::parse_borrowed::<2>("[year][month][day]").unwrap()
323}
324
325static DATE_FORMAT: LazyLock<Vec<BorrowedFormatItem<'static>>> = LazyLock::new(date_format);
326
327impl ToField for Date {
328    fn to_field(&self) -> String {
329        self.format(&DATE_FORMAT).unwrap()
330    }
331}
332
333impl ToField for Option<Date> {
334    fn to_field(&self) -> String {
335        encode_option_field(self)
336    }
337}
338
339fn encode_option_field<T: ToField>(val: &Option<T>) -> String {
340    match val {
341        Some(val) => val.to_field(),
342        None => String::from(""),
343    }
344}