1#![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#![warn(missing_docs)]
72#![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#[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
93pub mod accounts;
95
96pub mod client;
101
102pub(crate) mod transport;
103
104pub(crate) mod connection;
106
107#[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
167pub(crate) mod common;
169
170pub use common::timezone::register_timezone_alias;
171
172pub mod display_groups;
174
175pub mod subscriptions;
177
178pub mod config;
180pub mod contracts;
184pub mod errors;
186pub mod market_data;
188pub(crate) mod messages;
189pub mod news;
191pub mod orders;
193pub mod scanner;
195pub mod wsh;
197
198pub mod trace;
200
201pub mod prelude;
203
204pub mod protocol;
206
207pub(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
251pub(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}