An Interactive Brokers client with no gateway. No JVM, no window, no process to keep alive.
An independent project: not an official Interactive Brokers product, and not endorsed by Interactive Brokers.
Tip
For ib_async, use ib_async-dx, which runs it on this engine.
Start here — Introduction · Why this exists · Installation · Quick start
What you get — Beyond the documented API · Capabilities · Calls · Callbacks · Beyond the canonical list
Moving across — Running an existing program
Under the hood — How it works · Configuration
The honest parts — What is not covered · Questions · Testing
Working on it — Contributing · Security · License · Credits
ib-dx implements the IBKR client protocol directly. It authenticates, maintains the market-data, trading, historical and security-definition connections, and exposes the same API a program would otherwise reach through IB Gateway — with no gateway process, JVM, or local socket in between. The default connection identifies as gateway 10.51.1b.
The API has the TWS API's shape: EClient for requests, and EWrapper (in
Rust, Wrapper) for what comes back. Every call and callback on the canonical
list of the TWS API's requests and callbacks is present on both surfaces — the
Capabilities table counts them from the source — and the same
EClient answers what the gateway never forwarded; see
Beyond the documented API.
Moving an existing Python program across changes its imports (ibapi becomes
ib_dx) and its connect call:
- client.connect("127.0.0.1", 4001, clientId=1) # requires a running gateway
+ client.connect(username="...", password="...") # no external processTip
The calls, callbacks and order objects keep their names and shapes. Where an answer differs from a gateway's, Limits names the case, and what a gateway answers the same way is under Venue behaviour.
A program that trades through Interactive Brokers normally talks to a local process — IB Gateway or Trader Workstation — which talks to the venue. That process is a Java application. It holds a heap, it holds a window unless you fight it, it has to be logged in, and it has to stay alive for as long as your program does.
That arrangement costs you four things:
- An operational dependency. Something has to start it, watch it, restart it, and log in again when it drops. In a container or over ssh that is work you did not want.
- A second failure mode. Your program can be healthy while the thing it depends on is wedged, and the two do not agree about it.
- A ceiling. The heap is finite and bulk historical data is what finds the edge of it.
- A narrower view than the venue gives. The gateway receives far more from the venue than it forwards. Whatever has no message in the documented API simply never reaches you.
This client speaks the venue's own protocol, so none of those apply. It authenticates, holds the connections a session runs on, and answers the same calls a program already makes — and because it sits where the gateway sat, what the gateway kept to itself is reachable too.
- The gateway process — nothing to install, launch, log into, or restart
- The JVM — no Java runtime to install, and no heap to size
- The localhost socket — ticks are delivered in-process
- The window — runs headless, in a container, over ssh
- An Interactive Brokers account, paper or live
- Rust 1.89+, which both install routes build with
- Python 3.11+ for the bindings
No IB software is required. The ibapi package is not needed either.
Important
A live login enters the venue's second-factor approval, which waits on a device. Paper logins do not. Each program opens its own session, and a login holds one session at a time: a second program on the same login takes the first one's session, and the venue names the host that took it. Give each program that runs at the same time a login of its own.
Both routes build from the repository.
pip install "git+https://github.com/userFRM/ib-dx"The package imports as ib_dx. pip compiles the extension, which needs a
Rust toolchain at 1.89 or newer and, on Linux, the OpenSSL headers and
pkg-config (libssl-dev on Debian and Ubuntu, openssl-devel on Fedora and
RHEL). It builds for CPython 3.11 to 3.14 and the free-threaded 3.14t; the
free-threaded 3.13t is not supported.
[dependencies]
ib-dx = { git = "https://github.com/userFRM/ib-dx" }The Rust client needs no feature turned on. Rust 1.89 is the minimum supported
version (rust-version), and on Linux the build needs the OpenSSL headers and
pkg-config as above.
For working on the client itself:
git clone https://github.com/userFRM/ib-dx
cd ib-dx
uv venv .venv
source .venv/bin/activate # .venv\Scripts\activate on Windows
uv pip install maturin
maturin developpyproject.toml names the features the extension is built with, so
maturin develop needs none. A Rust program can depend on the checkout with
ib-dx = { path = "../ib-dx" }.
import threading
import time
from ib_dx import EWrapper, EClient, Contract
class App(EWrapper):
def __init__(self):
super().__init__()
self.ready = threading.Event()
def next_valid_id(self, order_id):
self.next_id = order_id
self.ready.set()
def tick_price(self, req_id, tick_type, price, attrib):
print(f"tick {tick_type}: {price}")
app = App()
client = EClient(app)
client.connect(username="your_user", password="your_pass", paper=True)
threading.Thread(target=client.run, daemon=True).start()
app.ready.wait(timeout=10)
aapl = Contract(symbol="AAPL", secType="STK", exchange="SMART", currency="USD")
client.req_mkt_data(1, aapl, "", False)
time.sleep(5)
client.cancel_mkt_data(1)
client.disconnect()Callbacks arrive on the thread that runs client.run(), or on the one that
calls client.poll(). The three that
connect() announces — connect_ack, managed_accounts and next_valid_id —
are fired on the calling thread before it returns. In Rust, process_msgs
delivers callbacks on the thread that calls it; after connect, it delivers
next_valid_id once the venue has named the orders the account is working.
Requests and cancels return after admission; their answers and refusals arrive
in session order. See
Requests, delivery and shutdown
for admission, delivery order, error origins and shutdown.
In Rust:
use std::time::{Duration, Instant};
use ib_dx::api::{Contract, Decimal, EClient, EClientConfig, TickAttrib, Wrapper};
struct App;
impl Wrapper for App {
fn tick_price(&mut self, _req_id: i64, tick_type: i32, price: f64, _attrib: &TickAttrib) {
println!("tick {tick_type}: {price}");
}
fn tick_size(&mut self, _req_id: i64, tick_type: i32, size: Decimal) {
println!("size {tick_type}: {size}");
}
}
let client = EClient::connect(&EClientConfig {
username: std::env::var("IB_USERNAME").unwrap_or_default(),
password: std::env::var("IB_PASSWORD").unwrap_or_default(),
paper: true,
..Default::default()
})?;
let aapl = Contract {
symbol: "AAPL".into(),
sec_type: "STK".into(),
exchange: "SMART".into(),
currency: "USD".into(),
..Default::default()
};
client.req_mkt_data(1, &aapl, "", false, false, &[]);
let mut app = App;
let until = Instant::now() + Duration::from_secs(5);
while Instant::now() < until {
client.process_msgs(&mut app);
std::thread::sleep(Duration::from_millis(50));
}
client.cancel_mkt_data(1);
client.disconnect();A quote subscription carries far more than prices. The venue publishes well over a hundred series against a contract, and the documented API forwards a handful of them — the rest have no message, so a program had no way to ask. This client sits where the gateway sat and reads them.
Everything below is stated by the venue on an ordinary session. The figures are
from one paper session and will differ with the account and the day; what does
not differ is that none of it is reachable through ibapi or ib_async.
Each is a call on EClient, beside the TWS API's own. The examples are Python;
the Rust EClient carries the same calls, spelled as
this table
gives them.
client.req_mkt_data(1, aapl, "454,678,705", False, False, [])
client.company_data(265598, 678) # 212 fields, among them:
# FLOAT = 1.4352417904E10 the float, to the share
# FLOAT_DATE = 20260901
client.company_data(265598, 454)
# IISHOS = 14594180000 shares on issue
# IITPCHL = 47.7348 percent held by institutions
client.company_data(265598, 705)
# TRESGS = 7.10404 how it scores against screening principlesTip
The float is the example worth giving: the venue states it outright, to the share, on a series the documented API never named.
client.numbered_figures(1, 661, fractional=True)
# [(13, 56.155), (26, 56.155), (52, 56.155)] implied-volatility rank
client.numbered_figures(1, 664, fractional=True)
# [(13, 2.436), (26, 6.450), (52, 31.749)] the same for realisedThe number each figure is kept under is weeks — a quarter, a half year, a
year. Two of these series state a high and a low, and there the window is
written negative for the low and positive for the high, so a year's range is the
pair numbered -52 and 52.
client.numbered_figures(1, 562, fractional=False) # the date of each point
client.numbered_figures(1, 562, fractional=True) # the price on it
# [(7, 765.96), (30, 776.34), … (1826, 419.586)]The key is a span of days: a week, a month, a quarter, a year, three years, five. It arrives on a quote subscription with no historical-data request behind it at all.
client.stated_figures(1, 504) # [383820.0, 1.0] what one contract delivers
client.stated_figures(1, 527) # [0.0154349] volatility over twenty days
client.stated_figures(1, 407) # four margin figures for a futureNote
On a future, what one contract delivers came back as its multiplier times its own modelled price; on an option, a hundred times the underlying; on a share, the price itself. That is the kind of check every reader here is held to — a figure is only claimed once the wire agrees with it.
On tick 13, and on 83 where the feed is delayed, tick_option_computation
carries the venue's model as a gateway builds it: the venue's greeks and option
price, the first of its model, mid and last volatilities that stands as the
implied volatility, over a year of 252 trading days, the underlying's price the
chain parameters on the underlying state for the option's expiry, and the
present value of the dividends the option's life covers. Ticks 10, 11 and 12,
and the delayed 80, 81 and 82, carry the model against the bid, the ask and the
last, worked out the way a gateway works it: at the volatility the venue states
for that side, with that side's price, and greeks from this client's own option
model. They are worked from the underlying's price the chain parameters state,
where a gateway takes the underlying's own quote when it holds one, so their
underlying's price and greeks are not a gateway's.
Limits
says what they do not carry.
The venue states more on the model's record, and it is all here: rho, fugit, the exercise boundary, the forward coefficient, the model and bridge yields — and the same model again as of the close, kept apart from the standing one.
m = client.option_model(1)
m["rho"], m["fugit"], m["exerciseBoundary"], m["modelYield"]
client.closing_option_model(1) # the same, worked out as the contract closedscan = ib_dx.SpreadScan(version=6, under_con_id=265598,
account="DU1234567", min_delta=0.25)
client.req_spread_scan(1, aapl, scan)
for s in client.scanned_strategies(1):
s["legs"], s["figures"], s["breakEvens"]The same fields in Rust:
let aapl = client.qualify_contract(&Contract {
symbol: "AAPL".into(),
sec_type: "STK".into(),
exchange: "SMART".into(),
currency: "USD".into(),
..Default::default()
})?;
let scan = ib_dx::types::SpreadScan {
version: 6, under_con_id: aapl.con_id,
account: "DU1234567".into(), min_delta: Some(0.25),
..Default::default()
};
client.req_spread_scan(1, &aapl, &scan);
for s in client.scanned_strategies(1) {
println!("{:?} {:?} {:?}", s.legs, s.figures, s.break_evens);
}client.enabled_features() # what the venue permits this account
client.order_permissions() # which order types, per security type
client.algorithms_for("STK") # which algorithms it may use
client.order_presets() # the defaults it fills an order's blanks from
client.req_ping(); client.last_rtt_ms()The full list, with what each returns, is in Beyond the API.
Tip
The calls under Beyond the canonical list are the point of this client. The venue states all of it on an ordinary session and the documented API never gave it a message, so a program had no way to ask — a contract's float, where its volatility stands against its own year, what margin it takes, five years of its price with the date of each point. They are reached here the way every other call is.
One row per capability, one column per client — every one of the 87 calls and 90 callbacks on the canonical list of the TWS API's requests and callbacks, read from each client rather than recalled.
| Client | Calls | Callbacks | |
|---|---|---|---|
| TWS API | 87 / 87 | 90 / 90 | nothing missing |
| ibapi | 78 / 87 | 85 / 90 | 9 absent, 5 callbacks absent |
| ib_async | 84 / 87 | 78 / 90 | 3 absent, 12 callbacks absent |
| ib-dx Rust | 87 / 87 | 88 / 90 | 2 callbacks declared, not fired |
| ib-dx Python | 87 / 87 | 88 / 90 | 2 callbacks declared, not fired |
Every call and callback on that list is present on both surfaces. 2 callbacks are declared and not fired: a gateway reroutes a request to another contract for a contract for difference whose definition asks for it, and this client does not read a definition's flags for that before subscribing: the request goes to the venue as asked. Each says so where it is declared, so a program that implements one still compiles and runs. 7 more are declared by the TWS API and never fire on a gateway — the four steps of the verification handshake, the exchange-for-physical quote, the delta-neutral validation, which a gateway never sends, and win_error, which no message on the wire carries — so they fire here exactly as often as there: never.
And 166 more beyond that list. The venue states more on a session than the documented calls ask for — what it permits this account, which algorithms it offers, the order defaults it fills an order's blanks from, what it says about an issuer, which session holds the account — and this client answers for those too, beside helpers and instrumentation of its own. The table under Beyond the canonical list says which each is, and which a reference client also names.
Every figure here is read from the client it names, on the machine that generated it. A client that is not installed is left out rather than filled in from memory.
The whole table — every call, every callback, and the calls beyond them
One row per capability, one column per client. This client exists to be put where a gateway was, so the question is not what it can do but whether anything you already use is missing — which is a question about the columns, not about the rows.
The first two tables are the canonical list of the TWS API's calls and callbacks, which are the rows a port has to match. The last is what this client answers beyond them.
Presence and behaviour are marked apart. A reference client's mark says a method exists, read by importing the package and listing its methods; it says nothing about what the method does. This client's mark says what the call does, and the evidence column says how that was established.
| Mark | Meaning |
|---|---|
| ● | Present. For ib-dx, also served: a call does what it names; a callback is fired whenever what it reports arrives |
| ◐ | Present and not served: a call reports why on the error callback; a callback is declared and not fired here, although a gateway sends it |
| · | Absent |
| — | Beyond the canonical list: not on this surface by design. The call is the other surface's own convenience, not one this surface lacks |
Each column is read from the client it names, on the machine that generated this page:
- TWS API — the canonical list this repository keeps of the TWS API's requests and callbacks, in
scripts/gen_api_docs.py. Beyond that list, a call is marked here where the TWS API's own client, or ib_async's transport, has a method by that name; a helper of ib_async's facade is not. - ibapi — IBKR's own Python client, version
9.81.1-1, imported and enumerated. This is the copy published to PyPI; the version IBKR distributes directly is numbered 10.x and names calls this one predates, so a gap in this column is a gap in the copy that was read and not necessarily in the client you have. - ib_async — version
2.1.0, imported and enumerated across both the transport and the facade, because it carries some calls on one and some on the other. - ib-dx Rust and ib-dx Python — this client's two surfaces, from the coverage matrix
scripts/gen_api_docs.pygenerates from the source. A mark here says what the call does, not only that it exists. - Evidence — how this client's status for a call was established: named by a suite that opens a session, named only by the offline suites, or not named by a test.
- Fires on a gateway — whether the callback fires at all for a program on a gateway. The TWS API declares seven that never do.
- Answered from — beyond the canonical list, whether a call asks the venue or reads what it stated, or answers from this client itself: its own state, a measurement it takes, or a helper.
A client that is not installed is left out of the table rather than filled in from memory. A mark here is a thing that was read.
What a program asks the venue for.
| Category | Call | TWS API | ibapi | ib_async | ib-dx Rust | ib-dx Python | Evidence |
|---|---|---|---|---|---|---|---|
| Connection | connect |
● | ● | ● | ● | ● | Live session |
disconnect |
● | ● | ● | ● | ● | Live session | |
is_connected |
● | ● | ● | ● | ● | Live session | |
start_api |
● | ● | ● | ● | ● | Offline suites | |
set_server_log_level |
● | ● | ● | ● | ● | Live session | |
req_current_time |
● | ● | ● | ● | ● | Live session | |
req_current_time_in_millis |
● | · | · | ● | ● | Live session | |
verify_request |
● | ● | ● | ● | ● | Offline suites | |
verify_message |
● | ● | ● | ● | ● | Offline suites | |
verify_and_auth_request |
● | ● | ● | ● | ● | Offline suites | |
verify_and_auth_message |
● | ● | ● | ● | ● | Offline suites | |
| Market Data | req_mkt_data |
● | ● | ● | ● | ● | Live session |
cancel_mkt_data |
● | ● | ● | ● | ● | Live session | |
req_market_data_type |
● | ● | ● | ● | ● | Live session | |
req_tick_by_tick_data |
● | ● | ● | ● | ● | Live session | |
cancel_tick_by_tick_data |
● | ● | ● | ● | ● | Live session | |
req_mkt_depth |
● | ● | ● | ● | ● | Live session | |
cancel_mkt_depth |
● | ● | ● | ● | ● | Live session | |
req_mkt_depth_exchanges |
● | ● | ● | ● | ● | Live session | |
req_smart_components |
● | ● | ● | ● | ● | Live session | |
req_real_time_bars |
● | ● | ● | ● | ● | Live session | |
cancel_real_time_bars |
● | ● | ● | ● | ● | Live session | |
| Historical Data | req_historical_data |
● | ● | ● | ● | ● | Live session |
cancel_historical_data |
● | ● | ● | ● | ● | Live session | |
req_head_time_stamp |
● | ● | ● | ● | ● | Live session | |
cancel_head_time_stamp |
● | ● | ● | ● | ● | Live session | |
req_historical_ticks |
● | ● | ● | ● | ● | Live session | |
cancel_historical_ticks |
● | · | · | ● | ● | Offline suites | |
req_histogram_data |
● | ● | ● | ● | ● | Live session | |
cancel_histogram_data |
● | ● | ● | ● | ● | Live session | |
req_historical_schedule |
● | · | ● | ● | ● | Live session | |
| Orders | place_order |
● | ● | ● | ● | ● | Live session |
cancel_order |
● | ● | ● | ● | ● | Live session | |
req_open_orders |
● | ● | ● | ● | ● | Live session | |
req_all_open_orders |
● | ● | ● | ● | ● | Live session | |
req_auto_open_orders |
● | ● | ● | ● | ● | Live session | |
req_ids |
● | ● | ● | ● | ● | Live session | |
req_global_cancel |
● | ● | ● | ● | ● | Live session | |
req_completed_orders |
● | ● | ● | ● | ● | Live session | |
| Executions | req_executions |
● | ● | ● | ● | ● | Live session |
| Account | req_account_updates |
● | ● | ● | ● | ● | Live session |
req_account_summary |
● | ● | ● | ● | ● | Live session | |
cancel_account_summary |
● | ● | ● | ● | ● | Live session | |
req_positions |
● | ● | ● | ● | ● | Live session | |
cancel_positions |
● | ● | ● | ● | ● | Live session | |
req_pnl |
● | ● | ● | ● | ● | Live session | |
cancel_pnl |
● | ● | ● | ● | ● | Live session | |
req_pnl_single |
● | ● | ● | ● | ● | Live session | |
cancel_pnl_single |
● | ● | ● | ● | ● | Live session | |
req_managed_accts |
● | ● | ● | ● | ● | Live session | |
req_account_updates_multi |
● | ● | ● | ● | ● | Live session | |
cancel_account_updates_multi |
● | ● | ● | ● | ● | Live session | |
req_positions_multi |
● | ● | ● | ● | ● | Live session | |
cancel_positions_multi |
● | ● | ● | ● | ● | Live session | |
| Contract | req_contract_details |
● | ● | ● | ● | ● | Live session |
cancel_contract_data |
● | · | · | ● | ● | Offline suites | |
req_matching_symbols |
● | ● | ● | ● | ● | Live session | |
req_market_rule |
● | ● | ● | ● | ● | Live session | |
| Scanner | req_scanner_parameters |
● | ● | ● | ● | ● | Live session |
req_scanner_subscription |
● | ● | ● | ● | ● | Live session | |
cancel_scanner_subscription |
● | ● | ● | ● | ● | Live session | |
| News | req_news_providers |
● | ● | ● | ● | ● | Live session |
req_news_article |
● | ● | ● | ● | ● | Live session | |
req_historical_news |
● | ● | ● | ● | ● | Live session | |
req_news_bulletins |
● | ● | ● | ● | ● | Live session | |
cancel_news_bulletins |
● | ● | ● | ● | ● | Live session | |
| Fundamental | req_fundamental_data |
● | ● | ● | ● | ● | Live session |
cancel_fundamental_data |
● | ● | ● | ● | ● | Live session | |
| Options | calculate_implied_volatility |
● | ● | ● | ● | ● | Live session |
cancel_calculate_implied_volatility |
● | ● | ● | ● | ● | Live session | |
calculate_option_price |
● | ● | ● | ● | ● | Live session | |
cancel_calculate_option_price |
● | ● | ● | ● | ● | Live session | |
exercise_options |
● | ● | ● | ● | ● | Live session | |
req_sec_def_opt_params |
● | ● | ● | ● | ● | Live session | |
| Reference | req_soft_dollar_tiers |
● | ● | ● | ● | ● | Live session |
req_family_codes |
● | ● | ● | ● | ● | Live session | |
req_user_info |
● | · | ● | ● | ● | Live session | |
| Financial Advisor | request_fa |
● | ● | ● | ● | ● | Offline suites |
replace_fa |
● | ● | ● | ● | ● | Offline suites | |
| Display Groups | query_display_groups |
● | ● | ● | ● | ● | Live session |
subscribe_to_group_events |
● | ● | ● | ● | ● | Live session | |
unsubscribe_from_group_events |
● | ● | ● | ● | ● | Live session | |
update_display_group |
● | ● | ● | ● | ● | Live session | |
| WSH | req_wsh_meta_data |
● | · | ● | ● | ● | Live session |
cancel_wsh_meta_data |
● | · | ● | ● | ● | Live session | |
req_wsh_event_data |
● | · | ● | ● | ● | Live session | |
cancel_wsh_event_data |
● | · | ● | ● | ● | Live session |
What the venue says back. ib_async delivers these as events as well as methods, so a mark here says the method exists on its wrapper, not that the information is unavailable by another route.
| Category | Call | TWS API | Fires on a gateway | ibapi | ib_async | ib-dx Rust | ib-dx Python |
|---|---|---|---|---|---|---|---|
| Connection | connect_ack |
● | yes | ● | ● | ● | ● |
connection_closed |
● | yes | ● | ● | ● | ● | |
next_valid_id |
● | yes | ● | ● | ● | ● | |
managed_accounts |
● | yes | ● | ● | ● | ● | |
error |
● | yes | ● | ● | ● | ● | |
current_time |
● | yes | ● | ● | ● | ● | |
current_time_in_millis |
● | yes | · | · | ● | ● | |
| Market Data | tick_price |
● | yes | ● | · | ● | ● |
tick_size |
● | yes | ● | ● | ● | ● | |
tick_string |
● | yes | ● | ● | ● | ● | |
tick_generic |
● | yes | ● | ● | ● | ● | |
tick_snapshot_end |
● | yes | ● | ● | ● | ● | |
market_data_type |
● | yes | ● | ● | ● | ● | |
tick_req_params |
● | yes | ● | ● | ● | ● | |
| Orders | order_status |
● | yes | ● | ● | ● | ● |
open_order |
● | yes | ● | ● | ● | ● | |
open_order_end |
● | yes | ● | ● | ● | ● | |
order_bound |
● | yes | ● | ● | ● | ● | |
| Executions | exec_details |
● | yes | ● | ● | ● | ● |
exec_details_end |
● | yes | ● | ● | ● | ● | |
commission_and_fees_report |
● | yes | ● | ● | ● | ● | |
| Account | update_account_value |
● | yes | ● | ● | ● | ● |
update_portfolio |
● | yes | ● | ● | ● | ● | |
update_account_time |
● | yes | ● | ● | ● | ● | |
account_download_end |
● | yes | ● | ● | ● | ● | |
account_summary |
● | yes | ● | ● | ● | ● | |
account_summary_end |
● | yes | ● | ● | ● | ● | |
position |
● | yes | ● | ● | ● | ● | |
position_end |
● | yes | ● | ● | ● | ● | |
pnl |
● | yes | ● | ● | ● | ● | |
pnl_single |
● | yes | ● | ● | ● | ● | |
position_multi |
● | yes | ● | ● | ● | ● | |
position_multi_end |
● | yes | ● | ● | ● | ● | |
account_update_multi |
● | yes | ● | ● | ● | ● | |
account_update_multi_end |
● | yes | ● | ● | ● | ● | |
| Contract | contract_details |
● | yes | ● | ● | ● | ● |
contract_details_end |
● | yes | ● | ● | ● | ● | |
bond_contract_details |
● | yes | ● | ● | ● | ● | |
symbol_samples |
● | yes | ● | ● | ● | ● | |
| Historical Data | historical_data |
● | yes | ● | ● | ● | ● |
historical_data_end |
● | yes | ● | ● | ● | ● | |
historical_data_update |
● | yes | ● | ● | ● | ● | |
head_timestamp |
● | yes | ● | ● | ● | ● | |
historical_ticks |
● | yes | ● | ● | ● | ● | |
historical_ticks_bid_ask |
● | yes | ● | ● | ● | ● | |
historical_ticks_last |
● | yes | ● | ● | ● | ● | |
histogram_data |
● | yes | ● | ● | ● | ● | |
historical_schedule |
● | yes | · | ● | ● | ● | |
| Market Depth | update_mkt_depth |
● | yes | ● | ● | ● | ● |
update_mkt_depth_l2 |
● | yes | ● | ● | ● | ● | |
mkt_depth_exchanges |
● | yes | ● | ● | ● | ● | |
| Tick-by-Tick | tick_by_tick_all_last |
● | yes | ● | ● | ● | ● |
tick_by_tick_bid_ask |
● | yes | ● | ● | ● | ● | |
tick_by_tick_mid_point |
● | yes | ● | ● | ● | ● | |
| Scanner | scanner_data |
● | yes | ● | ● | ● | ● |
scanner_data_end |
● | yes | ● | ● | ● | ● | |
scanner_parameters |
● | yes | ● | ● | ● | ● | |
| News | news_providers |
● | yes | ● | ● | ● | ● |
news_article |
● | yes | ● | ● | ● | ● | |
historical_news |
● | yes | ● | ● | ● | ● | |
historical_news_end |
● | yes | ● | ● | ● | ● | |
tick_news |
● | yes | ● | ● | ● | ● | |
update_news_bulletin |
● | yes | ● | ● | ● | ● | |
| Real-Time Bars | real_time_bar |
● | yes | ● | ● | ● | ● |
| Fundamental | fundamental_data |
● | yes | ● | ● | ● | ● |
| Market Rules | market_rule |
● | yes | ● | ● | ● | ● |
| Completed Orders | completed_order |
● | yes | ● | ● | ● | ● |
completed_orders_end |
● | yes | ● | ● | ● | ● | |
| Options | tick_option_computation |
● | yes | ● | ● | ● | ● |
security_definition_option_parameter |
● | yes | ● | ● | ● | ● | |
security_definition_option_parameter_end |
● | yes | ● | ● | ● | ● | |
| Reference | smart_components |
● | yes | ● | ● | ● | ● |
soft_dollar_tiers |
● | yes | ● | ● | ● | ● | |
family_codes |
● | yes | ● | ● | ● | ● | |
user_info |
● | yes | · | ● | ● | ● | |
| FA | receive_fa |
● | yes | ● | ● | ● | ● |
replace_fa_end |
● | yes | ● | · | ● | ● | |
| Display Groups | display_group_list |
● | yes | ● | · | ● | ● |
display_group_updated |
● | yes | ● | · | ● | ● | |
| Other | delta_neutral_validation |
● | no | ● | ● | ● | ● |
| WSH | wsh_meta_data |
● | yes | · | ● | ● | ● |
wsh_event_data |
● | yes | · | ● | ● | ● | |
| Market Data | reroute_mkt_data_req |
● | yes | ● | · | ◐ | ◐ |
reroute_mkt_depth_req |
● | yes | ● | · | ◐ | ◐ | |
tick_efp |
● | no | ● | ● | ● | ● | |
| Connection | verify_message_api |
● | no | ● | · | ● | ● |
verify_completed |
● | no | ● | · | ● | ● | |
verify_and_auth_message_api |
● | no | ● | · | ● | ● | |
verify_and_auth_completed |
● | no | ● | · | ● | ● | |
win_error |
● | no | ● | · | ● | ● |
What this client answers that the canonical list does not name. Three kinds, told apart by the Answered from column and by the reference columns: what the venue states that no documented call asks for (what it permits this account, which algorithms it offers, the order defaults it holds, what it says about an issuer, which session holds the account); a question answered in one call rather than delivered on a callback; and this client's own state, helpers and instrumentation, which are about the client and not the venue.
A mark against a reference client here means it names the same thing; a mark under TWS API means the TWS API's own client, or ib_async's transport, has a method by that name.
| Call | Answered from | TWS API | ibapi | ib_async | ib-dx Rust | ib-dx Python |
|---|---|---|---|---|---|---|
account / account_snapshot |
venue | · | · | · | ● | ● |
account_id / get_account_id |
venue | · | · | · | ● | ● |
adjustments |
venue | · | · | · | ● | · |
adjustmentsFor |
venue | · | · | · | ● | ● |
algorithms |
venue | · | · | · | ● | ● |
algorithmsFor |
venue | · | · | · | ● | ● |
await_order |
venue | · | · | · | ● | — |
calculateImpliedVolatilityProtoBuf |
venue | · | · | · | · | ● |
calculateOptionPriceProtoBuf |
venue | · | · | · | · | ● |
calendarEvents |
venue | · | · | · | ● | ● |
calendarSchema |
venue | · | · | · | ● | ● |
cancelAccountSummaryProtoBuf |
venue | · | · | · | · | ● |
cancelAccountUpdatesMultiProtoBuf |
venue | · | · | · | · | ● |
cancelAdjustments |
venue | · | · | · | ● | ● |
cancelCalculateImpliedVolatilityProtoBuf |
venue | · | · | · | · | ● |
cancelCalculateOptionPriceProtoBuf |
venue | · | · | · | · | ● |
cancelContractDataProtoBuf |
venue | · | · | · | · | ● |
cancelHeadTimestampProtoBuf |
venue | · | · | · | · | ● |
cancelHistogramDataProtoBuf |
venue | · | · | · | · | ● |
cancelHistoricalDataProtoBuf |
venue | · | · | · | · | ● |
cancelHistoricalNews |
venue | · | · | · | ● | ● |
cancelHistoricalTicksProtoBuf |
venue | · | · | · | · | ● |
cancelMarketDataProtoBuf |
venue | · | · | · | · | ● |
cancelMarketDepthProtoBuf |
venue | · | · | · | · | ● |
cancelNewsBulletinsProtoBuf |
venue | · | · | · | · | ● |
cancelOrderProtoBuf |
venue | · | · | · | · | ● |
cancelPnLProtoBuf |
venue | · | · | · | · | ● |
cancelPnLSingleProtoBuf |
venue | · | · | · | · | ● |
cancelPositionsMultiProtoBuf |
venue | · | · | · | · | ● |
cancelPositionsProtoBuf |
venue | · | · | · | · | ● |
cancelRealTimeBarsProtoBuf |
venue | · | · | · | · | ● |
cancelScannerSubscriptionProtoBuf |
venue | · | · | · | · | ● |
cancelTickByTickProtoBuf |
venue | · | · | · | · | ● |
cancelWshEventDataProtoBuf |
venue | · | · | · | · | ● |
cancelWshMetaDataProtoBuf |
venue | · | · | · | · | ● |
ccpSessionId |
venue | · | · | · | ● | ● |
chainModelParameters |
venue | · | · | · | ● | ● |
checkConnected |
this client | · | · | · | — | ● |
closingOptionModel |
venue | · | · | · | ● | ● |
closingOptionModelByInstrument |
venue | · | · | · | ● | ● |
companyData |
venue | · | · | · | ● | ● |
companyDataSeries |
venue | · | · | · | ● | ● |
competingSession |
venue | · | · | · | ● | ● |
contractFigures |
venue | · | · | · | ● | ● |
contractFiguresByInstrument |
venue | · | · | · | ● | ● |
corporateActions |
venue | · | · | · | ● | ● |
enabledFeatures |
venue | · | · | · | ● | ● |
error_from |
this client | · | · | · | ● | ● |
eventsLost |
this client | · | · | · | ● | ● |
exerciseOptionsProtoBuf |
venue | · | · | · | · | ● |
instrumentOf |
this client | · | · | · | ● | ● |
last_rtt / last_rtt_ms |
this client | · | · | · | ● | ● |
matchingSymbols |
venue | · | · | · | ● | ● |
miscUrl |
venue | · | · | · | ● | ● |
newsHeadlines |
venue | · | · | · | ● | ● |
nextOrderId |
this client | · | · | · | ● | ● |
nextSharedId |
this client | · | · | · | ● | ● |
next_shared_id_within |
venue | · | · | · | ● | · |
numberedFigures |
venue | · | · | · | ● | ● |
numberedFiguresSeries |
venue | · | · | · | ● | ● |
on_data |
venue | · | · | · | ● | · |
option_chain / option_chains |
venue | · | · | · | ● | ● |
optionModel |
venue | · | · | · | ● | ● |
optionModelByInstrument |
venue | · | · | · | ● | ● |
order_id_floor |
venue | · | · | · | ● | · |
orderPermissions |
venue | · | · | · | ● | ● |
orderPresets |
venue | · | · | · | ● | ● |
pairedFigures |
venue | · | · | · | ● | ● |
pairedFiguresSeries |
venue | · | · | · | ● | ● |
parse_algo_params |
this client | · | · | · | ● | — |
permittedOrderTypes |
venue | · | · | · | ● | ● |
placeOrderProtoBuf |
venue | · | · | · | · | ● |
poll |
this client | · | · | · | · | ● |
positions |
venue | · | · | ● | ● | ● |
positionsElsewhere |
venue | · | · | · | ● | ● |
qualifyContract |
venue | · | · | · | ● | ● |
qualifyContracts |
venue | · | · | ● | ● | ● |
queryDisplayGroupsProtoBuf |
venue | · | · | · | · | ● |
question_retired |
this client | · | · | · | ● | — |
quote |
venue | · | · | · | ● | ● |
quoteByInstrument |
venue | · | · | · | ● | ● |
refuse |
this client | · | · | · | ● | ● |
replaceFAProtoBuf |
venue | · | · | · | · | ● |
reqAccountSummaryProtoBuf |
venue | · | · | · | · | ● |
reqAccountUpdatesMultiProtoBuf |
venue | · | · | · | · | ● |
reqAccountUpdatesProtoBuf |
venue | · | · | · | · | ● |
reqAdjustments |
venue | · | · | · | ● | ● |
reqAllOpenOrdersProtoBuf |
venue | · | · | · | · | ● |
reqAutoOpenOrdersProtoBuf |
venue | · | · | · | · | ● |
reqCompletedOrdersProtoBuf |
venue | · | · | · | · | ● |
req_config / req_config_proto_buf |
this client | · | · | · | ◐ | ◐ |
reqContractDataProtoBuf |
venue | · | · | · | · | ● |
reqCurrentTimeInMillisProtoBuf |
venue | · | · | · | · | ● |
reqCurrentTimeProtoBuf |
venue | · | · | · | · | ● |
reqExecutionsProtoBuf |
venue | · | · | · | · | ● |
reqFamilyCodesProtoBuf |
venue | · | · | · | · | ● |
reqFAProtoBuf |
venue | · | · | · | · | ● |
reqGlobalCancelProtoBuf |
venue | · | · | · | · | ● |
reqHeadTimestampProtoBuf |
venue | · | · | · | · | ● |
reqHistogramDataProtoBuf |
venue | · | · | · | · | ● |
reqHistoricalDataProtoBuf |
venue | · | · | · | · | ● |
reqHistoricalNewsProtoBuf |
venue | · | · | · | · | ● |
reqHistoricalTicksProtoBuf |
venue | · | · | · | · | ● |
reqIdsProtoBuf |
venue | · | · | · | · | ● |
reqManagedAcctsProtoBuf |
venue | · | · | · | · | ● |
reqMarketDataProtoBuf |
venue | · | · | · | · | ● |
reqMarketDataTypeProtoBuf |
venue | · | · | · | · | ● |
reqMarketDepthExchangesProtoBuf |
venue | · | · | · | · | ● |
reqMarketDepthProtoBuf |
venue | · | · | · | · | ● |
reqMarketRuleProtoBuf |
venue | · | · | · | · | ● |
reqMatchingSymbolsProtoBuf |
venue | · | · | · | · | ● |
reqMktDataEx |
venue | · | · | · | ● | ● |
reqNewsArticleProtoBuf |
venue | · | · | · | · | ● |
reqNewsBulletinsProtoBuf |
venue | · | · | · | · | ● |
reqNewsProvidersProtoBuf |
venue | · | · | · | · | ● |
reqOpenOrdersProtoBuf |
venue | · | · | · | · | ● |
reqPing |
venue | · | · | · | ● | ● |
reqPnLProtoBuf |
venue | · | · | · | · | ● |
reqPnLSingleProtoBuf |
venue | · | · | · | · | ● |
reqPositionsMultiProtoBuf |
venue | · | · | · | · | ● |
reqPositionsProtoBuf |
venue | · | · | · | · | ● |
reqRealTimeBarsProtoBuf |
venue | · | · | · | · | ● |
reqScannerParametersProtoBuf |
venue | · | · | · | · | ● |
reqScannerSubscriptionProtoBuf |
venue | · | · | · | · | ● |
reqSecDefOptParamsProtoBuf |
venue | · | · | · | · | ● |
reqSmartComponentsProtoBuf |
venue | · | · | · | · | ● |
reqSoftDollarTiersProtoBuf |
venue | · | · | · | · | ● |
reqSpreadScan |
venue | · | · | · | ● | ● |
reqTickByTickDataProtoBuf |
venue | · | · | · | · | ● |
reqUserInfoProtoBuf |
venue | · | · | · | · | ● |
reqWshEventDataProtoBuf |
venue | · | · | · | · | ● |
reqWshMetaDataProtoBuf |
venue | · | · | · | · | ● |
reset |
this client | ● | ● | ● | · | ● |
run |
this client | ● | ● | ● | · | ● |
scan |
venue | · | · | · | ● | ● |
scannedStrategies |
venue | · | · | · | ● | ● |
schedule / trading_schedule |
venue | · | · | · | ● | ● |
serverVersion |
this client | ● | ● | ● | ● | ● |
sessionOver |
this client | · | · | · | ● | ● |
setConnectionOptions |
this client | ● | ● | ● | · | ◐ |
setNewsProviders |
venue | · | · | · | ● | ● |
setOptionalCapabilities |
this client | · | · | · | — | ◐ |
setServerLogLevelProtoBuf |
venue | · | · | · | · | ● |
shortSaleRestricted |
venue | · | · | · | ● | ● |
shortSaleRestrictedByInstrument |
venue | · | · | · | ● | ● |
startApiProtoBuf |
venue | · | · | · | · | ● |
statedFigures |
venue | · | · | · | ● | ● |
statedFiguresSeries |
venue | · | · | · | ● | ● |
statedRows |
venue | · | · | · | ● | ● |
statedRowsSeries |
venue | · | · | · | ● | ● |
subscribeToGroupEventsProtoBuf |
venue | · | · | · | · | ● |
traffic |
venue | · | · | · | ● | ● |
twsConnectionTime |
venue | ● | ● | · | ● | ● |
unreadWire |
this client | · | · | · | ● | ● |
unsubscribeFromGroupEventsProtoBuf |
venue | · | · | · | · | ● |
update_config / update_config_proto_buf |
this client | · | · | · | ◐ | ◐ |
updateDisplayGroupProtoBuf |
venue | · | · | · | · | ● |
useProtoBuf |
venue | · | · | · | · | ● |
validateAttachedOrdersParameters |
this client | · | · | · | — | ● |
validateInvalidSymbols |
this client | · | · | · | — | ● |
validateOrderParameters |
this client | · | · | · | — | ● |
valuesElsewhere |
venue | · | · | · | ● | ● |
verifyMessageProtoBuf |
venue | · | · | · | · | ● |
verifyRequestProtoBuf |
venue | · | · | · | · | ● |
waitForData |
this client | · | · | · | ● | ● |
whatIfOrder |
venue | · | · | ● | ● | ● |
| Client | Present ● | Present, not served ◐ | Absent · |
|---|---|---|---|
| TWS API | 87 | 0 | 0 |
| ibapi | 78 | 0 | 9 |
| ib_async | 84 | 0 | 3 |
| ib-dx Rust | 87 | 0 | 0 |
| ib-dx Python | 87 | 0 | 0 |
The same table stands on its own in docs/capabilities.md, and what each claim rests on is in docs/evidence.md.
Everything this client reads is listed above. This is the other half of that list, so nobody has to discover it by finding an empty result.
Note
Six series are read but have never been seen. 490, 546, 669, 700,
726 and 733 — the price-distribution weights, the volatility curve, the
historical ratios, the two screening dashboards, and the option model as of
the close. Each subscribes cleanly: the venue acknowledges it, assigns it a
tag, and then states nothing. Measured the same on a paper and a live login
for one account, minutes apart. The readers are written and tested; they fill
in the moment the data flows. The same is true of the spread scan.
Note
Some readers have not met their instrument. The series for bonds, municipals, warrants and perpetuals are read from the record's own shape and have not been exercised against one of those contracts. Where a reader has been run against live data, it says so in its own documentation.
Important
An empty result means one of two things: the venue holds nothing for that
contract, or the account cannot see that series. tick_req_params carries
what the venue states for each request: the exchange the best bid and offer
come from, as written (with the contract's security type appended as four hex
digits where the name is four characters or fewer, as a gateway writes it),
and the permission number the venue gives the request (0 nothing stated, 1 no
top of book, 2 snapshots, 3 real-time top of book, 4 snapshots not available
through the API; any other number is not kept, as a gateway keeps none). The
exchange is the name req_smart_components answers to. Where the
acknowledgement names no exchange, as a currency's, a crypto's or a bond's
does, the exchange is empty. The number is 0 there, on a bond, a bill, a
fixed-income contract or a combination, and while the frozen quote is served
in place of the live one, whatever the venue stated, as a gateway states it. Whether that number differs between a series the account
is not entitled to and one with nothing to say has not been seen yet, and a
gateway may report 0 for some other contracts whatever the venue stated;
until a capture settles both, the subscription list in account management is
what separates them.
And two things this client does not read or fire:
| Why | |
|---|---|
| One series | 230, a portfolio figure, is declared by the venue and read by neither this client nor a gateway: a gateway has no reader for it and refuses it in a generic tick list. What arrives on it is kept as unread rather than dropped. |
| Two callbacks | Declared so a program written against the TWS API still compiles, and not fired: a gateway reroutes a request for a contract for difference whose definition asks for its underlying's data, and this client does not read a definition's flags for that before subscribing — the request goes to the venue as asked. |
A program written against the TWS API keeps its calls, its callbacks and its
order objects; its imports and its connect call change — the Python
Quick start is one. In Python, both naming conventions resolve
on EClient, EWrapper and the records a call or callback hands over:
reqMktData and req_mkt_data, secType and sec_type, conId and
con_id; the classes a program only builds, such as ExecutionFilter, carry
the TWS API's spelling alone. The TWS API's module layout resolves under
ib_dx (ib_dx.client, ib_dx.wrapper, ib_dx.contract, …).
A wrapper is called as the ibapi release it was written for calls it. An
error declared as (reqId, errorCode, errorString) (ibapi 9.81), with
advancedOrderRejectJson after them, or with errorTime second as the current
release has it receives those arguments, and a wrapper that declares
commissionReport and not commissionAndFeesReport receives each charge there.
Where this client answers differently from a gateway, Limits says so, case by case.
For ib_async, use ib_async-dx, which runs it on this engine.
There is no process between your program and the venue. The client opens the connections a session runs on and keeps them:
| Connection | Carries |
|---|---|
| Trading | Orders, executions, positions, account values, news bulletins |
| Market data | Quotes, depth, the extra series, tick-by-tick |
| Historical | Bars, historical ticks, head timestamps |
| Security definition | Contract details, option chains, matching symbols |
Each is authenticated at logon, kept alive, and rebuilt on its own if it drops — a subscription the venue was serving is asked for again, under the same request the caller made.
Note
Reconnection is per connection, not per session. A market-data connection that drops does not take the trading connection with it, and an order in flight is not orphaned by a quote feed reconnecting.
A quote is a value, not a stream of events. The client keeps the current state of every contract it watches in a lock-free table and hands you a snapshot when you ask. Callers who want events get them too — the point is that reading a quote does not mean draining a queue first.
The figures are the venue's. A price, size, greek or series figure a caller
reads is the one the venue stated, and a figure the venue holds nothing for
comes back as the largest number its field carries — the venue's own way of
saying so — rather than as a zero that reads like a price. Where this client
works something out itself, the call that does it says so: the P&L figures,
worked out from the venue's figures against the session's prices; the bars
that continue a keepUpToDate request, formed from the venue's five-second
bars on the history's last bar, a week and a month on the calendar; a book kept
at the size asked for; and the answer to a price or a volatility the caller
supplies, solved against the venue's published model.
Important
A live login enters the venue's second-factor approval and waits on a device. Paper logins do not. One program per login: each program opens its own session, and a login holds one session at a time, so a second program on the same login takes the first one's session, and the venue names the host that took it.
Within a running program, a dropped connection is rebuilt on the session
already open, with no second factor. A restarted program logs in again, second
factor included. Offering the saved session (EClientConfig::resume, or
session_file) has not spared a login. See
Login.
The gateway's configuration file is replaced by settings on the client: announced build, time zone, execution-report scope, and others — 23 in total, readable at runtime. Fourteen gateway settings are not settings here, and each says why or names what stands in for it (no window geometry, no local listening socket, no JVM heap, and no message pacing: a gateway paces requests at the rate its logon states (fifty a second where it states none) unless it is set to reject them instead, and nothing here does either).
Rust: EClientConfig.gateway. Python: ib_dx.configure() or per-session
connect(settings={...}).
download_open_orders_on_connection is on by default. After nextValidId,
the initial connection sends openOrder and orderStatus for numbered working
orders belonging to this API client, or every client when this is the configured
master. A nonempty download ends with openOrderEnd; an empty one sends no end.
Unnumbered manual orders, local placements not yet submitted, previews, and option
exercise or lapse instructions are excluded; ordinary option orders remain
eligible. Working orders named PendingSubmit and working orders with a held
transmit=false modification remain eligible. Initial reports update local
status and quantities before the snapshot; both callbacks state the current
filled quantity. The download waits for order naming to finish even if the
next-ID wait has timed out.
Set it to false to skip this initial delivery; explicit open-order requests
still work. Reports of orders working at connection keep their execution
history without fill callbacks; the program's own orders, placed while the
download waits, are told as they happen. Later reports and transport reconnects
retain their existing callback behavior. This setting does not add automatic
binding or change order IDs. Whatever it is set to, an order this session is
told of but did not place is stated with openOrder beside its status, under
its API number and with its parent under the parent's.
Client 0 binds an order placed elsewhere, one with no API number of its own,
as a gateway does. reqOpenOrders binds each such order working when it is
asked; after reqAutoOpenOrders(True) each new one is bound as the venue first
reports it, once the venue has named what was already working, and
reqAutoOpenOrders(False) stops that and keeps the numbers already given.
reqAllOpenOrders binds nothing. use_negative_auto_range is on by default: a
bound order takes the next number from -2 down, counted apart from the order IDs
and kept beside them in the order-ID file. Turned off, it takes the next order
ID. The order's callbacks carry that number, and a cancel reaches the order
under it. The venue is then asked to bind it: orderBound follows the venue's
acceptance, and a refusal is error 10198. Naming an order at connection is not
a binding and says no orderBound. An OMS container or a child of one is bound
here without the venue being asked. The setting is also read from
IB_DX_USE_NEGATIVE_AUTO_RANGE.
include_fx_positions is on by default. Turn it off to omit CASH rows from
position, positionMulti, and updatePortfolio.
send_zero_positions_for_todays_opening_only is off by default. Turn it on to
omit flat rows whose opening quantity the venue states as zero. Flat rows with
nonzero or unknown opening quantities still arrive, as do all nonzero holdings.
Both settings apply to initial answers and later updates, including other
accounts, using each account's own opening quantities. An empty cached security
type does not override the holding's known type. End callbacks still arrive when
every row is filtered. Stored holdings and P&L calculations are unchanged.
The environment names are
IB_DX_INCLUDE_FX_POSITIONS and
IB_DX_SEND_ZERO_POSITIONS_FOR_TODAYS_OPENING_ONLY.
Order IDs survive restarts in ib-dx/order-ids.json under the user's data
directory: $XDG_DATA_HOME (or ~/.local/share) on Linux,
~/Library/Application Support on macOS, and %APPDATA% on Windows.
The file keeps a separate next ID for each account and API client ID (the one
stated at connect), keyed by the SHA-256 digest of the account rather than the
account number; a file keyed by account numbers is rewritten that way when a
session opens it. next_order_id() saves a reservation under an exclusive lock
before returning; a fresh session starts above that counter and venue replay.
A storage failure warns once in the log and leaves trading available with the
session's in-memory counter and replay floor.
Set order_id_file through EClientConfig.gateway, Python
connect(settings={"order_id_file": "/path/order-ids.json"}), or
ib_dx.configure(order_id_file="/path/order-ids.json"); the environment name
is IB_DX_ORDER_ID_FILE. An empty string disables persistence. To move it,
stop every process using it, move the JSON file, and configure the same new
path in each process. The .lock and .tmp files beside it are maintained by
the client. See order IDs
for the reservation scope.
Registration is held by the engine and does not wait at the call.
Configuration read and update requests through reqConfigProtoBuf and
updateConfigProtoBuf (Rust: req_config and update_config) report 10357
on every connected session. Configuration payloads are never applied; see
Limits.
- The book — guides, recipes and the generated API reference
- Capabilities — one row per capability, one column per client
- Evidence — what each claim rests on, and the session that produced it
- Notebooks — seven walkthroughs of
EClientandEWrapper: connecting and the account, bars, contract details, depth, orders, scanners and ticks - Examples — 42 runnable single-file programs, 27 in Rust and 15 in Python
- Beyond the API — what the session states that no documented call asks for
- Venue behaviour — what a program meets here that is the venue's answer or the TWS API's definition
- Limits — what this client will not do, and why
Do I still need IB Gateway or TWS installed?
No. Nothing is installed, launched or logged into. The ibapi package is not
needed either — this client provides that surface itself.
What changes in an existing program?
The imports and the connect call. The calls, the callbacks and the order objects keep their names and shapes. Where an answer differs from a gateway's, Limits names the case. See Running an existing program.
Can I run two programs, or this and a gateway, at the same time?
On two logins, yes. On one, no: each program opens its own session, and a login holds one session at a time, so the second takes the first one's session, and the venue names the host that took it. The same holds for a gateway on that login.
I asked for a series and got nothing back. Is it broken?
Probably not. An empty result means one of two things: the venue holds nothing
for that contract, or the account is not entitled to that series. Such a
subscription is acknowledged and then nothing is stated, with no error to read,
not even tick_req_params: it fires once per request, including a follower
sharing another request's subscription, with the first record the request is
served, as a gateway sends it. It reads the contract's latest stored parameters
when delivered: the price increment, the exchange the best bid and offer come from,
and the permission number the venue gives the request (0
nothing stated, 1 no top of book, 2 snapshots, 3 real-time top of book, 4
snapshots not available through the API), which is 0 where the venue names no
such exchange, as for a currency or a crypto, on a bond, a bill, a
fixed-income contract or a combination, and while the frozen quote is served in
place of the live one, whatever the venue stated, as a gateway states it.
Whether that number differs between a series the account is not entitled to
and one with nothing to say has not been seen yet, and a
gateway may report 0 for some other contracts whatever the venue stated; until
a capture settles both, the subscription list in account management is what
separates them. See
What is not covered.
Is paper different from live?
Both run on this engine, with the same calls and the same protocol; a live login also enters the second-factor approval, which paper does not. What each answers depends on that account's subscriptions and permissions. The verification here is on a paper account, with one order round trip on a funded account — see Evidence.
What happens when a connection drops?
It is rebuilt on its own and the subscriptions it was serving are asked for again, under the request the caller made, save what a gateway ends at the drop: a bar request kept up to date ends on 10182, as a gateway ends it. Connections are independent: a quote feed reconnecting does not disturb an order in flight.
As on a gateway, the caller hears 1100 as the trading connection drops, and
again for each connection opened in its place that goes before its return is
said, one a reconnect opened and let go on the way included; and 1102 once the
connection that replaced it has named the working orders, with the data farms
as they stand: at once where every one is connected, and otherwise at a look
each second, once they are or thirty seconds have passed, naming the ones that
are not. The words name the broker and the product as the logon names them,
"Connectivity between IBKR and Trader Workstation has been lost." for a login
with Interactive Brokers. A data farm dropping on its own is said under its own
numbers, not 1100. The session stays connected throughout, as a program's
socket to a gateway does: is_connected() reads true between the 1100 and the
1102, and false only once the session is over. A request made meanwhile is
carried when the connection comes back; a question of what is working is
answered once the venue has named it to its end. Working orders keep the status
they were last given: as on a gateway, nothing is said of them at the drop, and
the venue restates each one after the reconnect. An order sent and not yet
answered when the connection went is recovered as a gateway recovers it: once
the venue has named what is working, it is asked what it has finished today,
and an order it names in either answer takes the state it gives; one it names
in neither is held as inactive and not sent again, and where a recovery had
already sent it out once, the caller is told under 106 that it could not be
sent. Placements not yet sent are listed after the 1100 under 1104, 1105 and
1106 and go out once that is over, where the logon offers the recovery; where it
does not, every placement the drop leaves not yet sent is cancelled under
10328, whether it was built or not. A question of what is
working asked meanwhile is answered once it is over. Any other order the venue
does not restate keeps its last status.
Why does a figure come back as 1.7976931348623157e308?
That is the largest number a double carries, and it is how the venue says it holds nothing for that field. It is passed through rather than turned into a zero, because zero is a real price and a real greek.
Rust or Python — is one behind the other?
Not on the canonical list of the TWS API's calls and callbacks. Every one is on
both, and the generated matrix counts the ones on which the two differ: zero. scripts/conformance.py --compare holds ten server responses to the same
answer on both. Beyond that list, each language spells some calls its own way
and a few are on one side only; the
Beyond the canonical list table has a column for
each.
Attached stop-loss and profit-taking orders are constructed from the selected
account preset through the existing Order fields. The engine loads the
preset and holds the family until it transmits, sending parent, stop loss and
profit taker in order. Percentage-allocation sizing from group or model
holdings is not carried: use explicitly sized parent and child orders when
those holdings determine their quantities. The advertised level remains 217;
226 is the highest level a gateway announces. See
attached orders
for fields, refusals and the remaining venue evidence.
Individually placed children with parentId / parent_id share the known
parent's cancellation group, including children added to a bracket helper's
family. A modification the venue refuses leaves the original working order
and what has filled on it, and a cancellation refused on a report leaves the
order in the state it held before the cancel went out. As on a gateway, the
open-orders view keeps the terms a refused modification sent, and takes the
terms and the size a refusal states only where the refusal is an ordinary
report stating some of the order left. A refusal on the venue's own
cancel-reject message reaches no callback, and neither does a report's reason
for restating an order. open_order states an order's total as a gateway
does: what has filled, and what is left of the order's own size, which only
the reports a gateway takes a size from restate. Hedge pricing instructions are
retained on replacement. Returned orders enable Sweep to Fill, Hidden, Auto
Cancel Parent and Imbalance Only when the respective reported value starts with
ASCII 1, and Continuous Update when its value starts with ASCII 1 or 2.
Other stated values, including empty, true and y, disable the respective
field.
See order behaviour.
Claims here rest on tests, and the tests are counted rather than described:
| Suite | Count | Needs a session |
|---|---|---|
| Rust, unit and integration | 3,337 | No |
| Python | 1,481 | No |
| Rust, live | 10 | Yes |
| Python, live | 122 | Yes |
| Paper compatibility, 154 phases | 50 | Yes |
Every published count is checked against what is actually there, so a number in this file cannot drift from the suite that produced it. What each claim rests on is in Evidence.
Readers of the venue's own records are held to a harder standard than passing: a test that would pass against a broken reader is not a test. Each is checked by breaking the reader deliberately and confirming the test fails.
Issues and pull requests are welcome.
Tip
The fastest way to be useful is a wire observation: a contract, a request, and what the venue answered with. A reader is only as good as the records it has been held against, and the ones that have met the fewest live records are named in What is not covered.
Before opening a pull request, run the same gate the workflow runs. It builds both surfaces, runs every suite, regenerates the documentation and checks that what is published matches what is there:
python scripts/gate.pyCaution
Credentials are the account. Never commit them, never paste them into an issue, and never put them in a file the repository tracks.
Pass them at connect time from the environment or a secret store. A resumed session is written to disk encrypted with a password you supply and never in the clear. Nothing in this repository ships credentials, and nothing that fabricates a session is compiled into the published wheel.
If you find a security problem, please report it privately through the repository's security advisories rather than in a public issue.
ib-dx began as a fork of ibx by DeepEntropy and Odyssée, at its v0.7.1 release (deepentropy/ibx@9367845, 25 July 2026), under the same AGPL-3.0 licence. That history is the first commit here, and its full log is in the original repository. Everything after it was written for ib-dx, from 28 July 2026 on. Thank you to both of them for the foundation.
- ibx: Copyright (C) 2026 DeepEntropy and Odyssée
- ib-dx: Copyright (C) 2026 userFRM
Interactive Brokers®, IBKR®, Trader Workstation®, and IB Gateway® are registered trademarks of Interactive Brokers Group, Inc. This project is not affiliated with, endorsed by, or supported by Interactive Brokers.
ib-dx is an independent, open-source project provided "as is", without warranty of any kind.
Caution
This client places orders against a real account. Test against a paper login first, and satisfy yourself that an order reads the way you meant it before pointing it at money.
- No warranty. ib-dx is provided "as is", without warranty of any kind. See LICENSE for full terms.
- Use at your own risk. Users are solely responsible for ensuring their use of ib-dx complies with Interactive Brokers' Terms of Service, Customer Agreement, and any applicable laws or regulations. Using ib-dx may carry risks including but not limited to account restriction or termination by IB.
- Orders are real. ib-dx places, modifies and cancels orders on the account it logs into, and is at an early version. It is not intended as a replacement for officially supported IB software in production trading environments. The authors accept no liability for financial losses, missed trades, account issues, or any other damages arising from the use of this software.
- Protocol stability. ib-dx relies on an undocumented protocol that IB may change at any time without notice. There is no guarantee of continued functionality.