ib-dx
Talk directly to IBKR. The TWS API your program already uses, with no IB Gateway, no Trader Workstation and no JVM between you and the venue.
For ib_async, use ib_async-dx, which runs it on this engine.
One line changes
A program written against the TWS API talks to IB Gateway or Trader Workstation over a socket on localhost, and that process talks to the venue. ib-dx takes the gateway's place: it logs in, holds the trading, market-data, historical and security-definition connections open, and gives your program the same calls and the same callbacks.
- client.connect("127.0.0.1", 4001, clientId=1) # needs a gateway running
+ client.connect(username="...", password="...") # no external process
port is still accepted, and client.port reads it back while the session is
held, but nothing connects on it. There is no local process to point it at.
What goes away
The process
Nothing to install, launch, log into on a schedule, or restart.
The JVM
No Java runtime to install, and no heap to size.
The localhost socket
Callbacks are delivered in-process, on the thread that runs the dispatch: run() or poll() in Python, process_msgs in Rust.
The window
It runs headless: in a container, over ssh, on a machine with no display.
What you get
The shape you already have
EClient and EWrapper in Python, EClient and Wrapper in Rust, with the TWS API's method names and callbacks.
One engine, two languages
The Python module is the Rust core through PyO3, not a second implementation. A gate on every commit fails if the two surfaces stop agreeing on a call or a callback the TWS API documents.
Nothing in between
The engine runs on a thread of its own and can be pinned to a core. Quotes are published through a seqlock: the writer never blocks, and a reader takes a whole quote without a lock.
More than the API forwards
The session states things a gateway never passes on: the account's grants, the order types it will take, its algorithms, and data series with no documented call. See what.
Honest about its limits
A call the protocol cannot carry says why, instead of returning as though it acted. The limits are written down, over a coverage matrix regenerated from the source on every commit, and what a gateway answers the same way is on venue behaviour.
Where to go next
Status
Under active development. Every capability claim in this repository is assigned from a named artifact, a test, a script or a recorded server response, never from reading the code. The matrix is in capabilities.md, its counts are recomputed on every commit, and the build fails if one moves.
Both install routes build from the repository: see Getting started.
Getting started
What you need
- An Interactive Brokers account, paper or live. No IB software, and not the
ibapipackage either. - Rust 1.89 or newer. Both install routes compile the engine.
- Python 3.11 or newer, for the Python surface.
Install
Both routes build from the repository.
Rust
[dependencies]
ib-dx = { git = "https://github.com/userFRM/ib-dx" }
Python
pip install "git+https://github.com/userFRM/ib-dx"
The package imports as ib_dx. The build backend is
maturin, which compiles the Rust core into the
extension module, and pyproject.toml names the features it needs, so there is
nothing to pass. It builds for CPython 3.11 to 3.14 and the free-threaded
3.14t; the free-threaded 3.13t is not supported.
Either route compiles the engine, so it needs Rust 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).
From the source
Working on the client itself, build it in place:
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 develop
maturin develop replaces the installed module. A local pytest run tests
whatever was built last, so rebuild before running one.
Feature flags
default = []. The Rust client needs nothing turned on.
| Feature | What it adds |
|---|---|
python | The PyO3 bindings |
extension-module | Tells PyO3 not to link libpython. Right for the wheel, wrong for cargo test. maturin sets it; you do not |
dev-tools | The binaries under src/bin — the benchmarks, and the capture tools this repository is developed with. Most of them read credentials and open a session |
The features not in that table exist for this repository's own test suites. They are off by default and should stay off in anything you install.
Credentials
The credentials go in the connect call. There is no configuration file and no process holding a login on your behalf.
client.connect(username="your_user", password="your_pass", paper=True)
let client = EClient::connect(&Config {
username: "your_user".into(),
password: "your_pass".into(),
paper: true,
..Default::default()
})?;
Do not name a host. Left empty, the client knocks on one of the venue's regional doors and the venue answers by naming the server this account actually lives on; the session moves there. A host is worth stating only to knock at a particular region.
paper. true skips the live second-factor approval gate — a paper
session presents no second factor. false enters that gate on connect, and
that call blocks until it is answered.
Which factor an account presents is stated by the venue when the gate opens,
and this client handles two: an authenticator code, which has nothing to fall
back on and needs a code_provider, and an IBKey push, which does not. Nothing
below has been exercised against a live account from here, so treat the timing
and the shape of the prompt as the venue's to state rather than as described.
Use a paper account while you are writing something. A live account is a live account.
One program per login. 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.
One place reads the environment instead of the call: the programs under
examples/ read IB_USERNAME and IB_PASSWORD:
export IB_USERNAME="your_username"
export IB_PASSWORD="your_password"
Pick a surface
Every surface below drives the same engine. Pick by the program you have, not by capability.
| Language | Surface | Pick it when |
|---|---|---|
| Python | ib_dx.EClient / EWrapper | Your program is written against ibapi. Change ibapi to ib_dx in its imports, and its connect call to the one above. |
| Rust | EClient / Wrapper | You are porting a TWS API program and want its callbacks. |
For ib_async, use ib_async-dx, which runs it on this engine.
Hello, world
Both files below are the ones in the repository, included rather than copied,
so this page cannot drift from what actually runs. The Rust one is compiled by
CI along with the rest of examples/. Each subscribes to SPY for five seconds
and prints the last quote it saw.
Rust
//! Recipe: stream SPY market data for 5 seconds, print latest bid/ask/last.
//!
//! Usage: IB_USERNAME=... IB_PASSWORD=... cargo run --example hello_tick_data
use std::env;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use ib_dx::api::client::{Contract, EClient, EClientConfig};
use ib_dx::api::types::TickAttrib;
use ib_dx::api::wrapper::Wrapper;
#[derive(Default)]
struct State {
bid: f64,
ask: f64,
last: f64,
ticks: u64,
}
struct TickWrapper {
state: Arc<Mutex<State>>,
}
impl Wrapper for TickWrapper {
fn tick_price(&mut self, _req_id: i64, tick_type: i32, price: f64, _: &TickAttrib) {
let mut s = self.state.lock().unwrap();
s.ticks += 1;
match tick_type {
1 => s.bid = price,
2 => s.ask = price,
4 => s.last = price,
_ => {}
}
}
fn error(&mut self, req_id: i64, _error_time: i64, code: i64, msg: &str, _adv: &str) {
if !matches!(code, 2104 | 2106 | 2158) {
eprintln!("[error] req_id={req_id} code={code} msg={msg}");
}
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = EClient::connect(&EClientConfig {
username: env::var("IB_USERNAME")?,
password: env::var("IB_PASSWORD")?,
paper: true,
..Default::default()
})?;
let state = Arc::new(Mutex::new(State::default()));
let mut wrapper = TickWrapper { state: state.clone() };
let spy = Contract {
con_id: 756733,
symbol: "SPY".into(),
sec_type: "STK".into(),
exchange: "SMART".into(),
currency: "USD".into(),
..Default::default()
};
let req_id = 1;
println!("streaming SPY for 5s…");
client.req_mkt_data(req_id, &spy, "", false, false, &[]);
let deadline = Instant::now() + Duration::from_secs(5);
while Instant::now() < deadline {
client.process_msgs(&mut wrapper);
std::thread::sleep(Duration::from_millis(20));
}
client.cancel_mkt_data(req_id);
let s = state.lock().unwrap();
println!("ticks: {} bid={:.2} ask={:.2} last={:.2}", s.ticks, s.bid, s.ask, s.last);
drop(s);
client.disconnect();
Ok(())
}
IB_USERNAME=... IB_PASSWORD=... cargo run --example hello_tick_data
Python
"""Recipe: stream SPY market data for 5 seconds, print latest bid/ask/last.
Usage:
IB_USERNAME=... IB_PASSWORD=... python examples/hello_tick_data.py
"""
import os
import threading
import time
from ib_dx import EClient, EWrapper, Contract
class TickWrapper(EWrapper):
def __init__(self):
self.bid = 0.0
self.ask = 0.0
self.last = 0.0
self.ticks = 0
def tick_price(self, req_id, tick_type, price, attrib):
self.ticks += 1
if tick_type == 1:
self.bid = price
elif tick_type == 2:
self.ask = price
elif tick_type == 4:
self.last = price
def tick_size(self, req_id, tick_type, size):
self.ticks += 1
def error(self, req_id, error_time, code, msg, advanced=""):
if code not in (2104, 2106, 2158):
print(f"[error] {code}: {msg}")
w = TickWrapper()
c = EClient(w)
c.connect(
username=os.environ["IB_USERNAME"],
password=os.environ["IB_PASSWORD"],
paper=True,
)
threading.Thread(target=c.run, daemon=True).start()
spy = Contract()
spy.con_id = 756733
spy.symbol = "SPY"
spy.sec_type = "STK"
spy.exchange = "SMART"
spy.currency = "USD"
req_id = 1
print("streaming SPY for 5s…")
c.req_mkt_data(req_id, spy, "", False, False)
deadline = time.monotonic() + 5
while time.monotonic() < deadline:
time.sleep(0.1)
c.cancel_mkt_data(req_id)
print(f"ticks: {w.ticks} bid={w.bid:.2f} ask={w.ask:.2f} last={w.last:.2f}")
c.disconnect()
IB_USERNAME=... IB_PASSWORD=... python examples/hello_tick_data.py
Next steps
- Login — connect, take the first order id, disconnect
- Streaming ticks · L2 depth
- Order lifecycle — place, modify, cancel, fill
- Limits — read this before you depend on a call
- Requests, delivery and shutdown — request signatures, ordered callbacks, error origins and the shutdown result
What the session states, and the API does not forward
This client speaks the protocol a gateway speaks. The gateway receives more than it forwards: some of what the venue states at logon and on a contract has no message in the API a gateway offers its own clients, so a program written against that API has no way to ask for it.
Everything below is stated by the venue on an ordinary session. The figures are
from one paper session and will differ with the account; what does not differ is
that none of it is reachable through ib_async or the TWS API.
The examples are Python. Every one of these has a Rust EClient equivalent —
fields and Option-wrapped returns included — see the
table at the end.
What the account may do
The venue states its grants at logon — one token per capability.
grants = client.enabled_features() # 302 on the session this was written from
"ISLAND2NASDAQ" in grants # True
They decide behaviour. island_for_nasdaq — whether a US stock trading on
Nasdaq is named by the older spelling — takes this grant as well as the
setting, which is how it is decided: the same token is read off
the same list at logon and holds it beside the setting.
Which orders the venue will take
Stated at logon, per security type, before a single order is sent.
perms = client.order_permissions() # 21 security types
len(perms["STK"]) # 92 order types and attributes
client.permitted_order_types("FUT") # or None, if the type is not permitted at all
Through the API a program discovers this by being refused. An order for a security type absent from this map is refused before it is sent, with the reason on the error callback.
Which algorithms this account may use
Keyed by provider and security type: the documents that define them, as the venue lists them. The list is asked for the first time an order names an algorithm, as a gateway asks it, and is empty until then.
client.algorithms() # 13 keys: 'IBALGO/STK', 'FOXRIVER/STK', …
client.algorithms_for("STK") # ['FOXRIVER-AE', 'IBALGO-AE', 'JONES-AE', …]
An order is held to the definitions the list names for its contract (see an order through an algorithm).
Which order defaults the account holds
The venue keeps a set of order defaults per security type and fills parts of an order the caller left unstated from them: the size a compete order competes with and the offset it competes by, where neither was named. So the same call on two accounts is not the same order.
client.order_presets() # [('s=CASH', 'v=1&a=1'), ('s=FUT', 'v=1&a=1'), ('s=STK', 'v=1&a=1'), …]
The key is the venue's own, and a currency can split one: s=CASH&tc=EUR sits
beside s=CASH. The second figure is the set's attributes as the venue
writes them: v= names its variant and a=1 marks it active, so
('s=CASH&tc=EUR', 'v=1') is a set that is not active. The values inside a
set are asked for separately and are not carried here.
What the session itself is
client.get_account_id() # the account this session acts for
client.next_order_id() # the first id it may use
client.ccp_session_id() # what a web endpoint expects as a header
client.misc_url("region_dam") # hosts the venue pushed at logon
How far away the venue is
client.req_ping()
client.last_rtt_ms() # 10.96 on the session this was written from
The API has no notion of this. A program that wants to know whether it is close to the venue has to time a request that does something else.
What the venue states about a contract, beyond its quote
A quote subscription can carry far more than prices. The venue publishes dozens of series against a contract, each under a number of its own, and the API forwards a handful of them. Ask for one by putting its number in the generic tick list, then read what it stated.
Some carry the venue's own named fields as text — analyst ratings, ownership, fund terms, screening scores, margin, technical readings, company accounts:
client.req_mkt_data(1, contract, "454,505,631,678,705", False, False, [])
client.company_data_series(265598) # [434, 454, 505, 548, 631, 678, 699, 705, …]
client.company_data(265598, 454) # [('IITPCHL', '47.7348'), … ('IISHOS', '14594180000')]
client.company_data(756733, 505) # [('EXPRATIO', '0.0945'), ('TOTALASSETVALUE', '811937033817.46'), …]
client.company_data(265598, 705) # [('TRESGS', '7.10404'), ('TRESGCS', '3.65619'), …]
The keys are the venue's own and are handed on unchanged, so a field it adds arrives with the rest instead of being dropped for not being recognised here. One of these series answered with 212 fields on a single contract.
Others carry figures rather than text — a bond's accrued interest, what one contract delivers, volatility, the volume a contract usually opens and closes on, what margin a future takes:
client.stated_figures_series(1) # [398, 399, 402, 459, 493, 504, 527, 584, …]
client.stated_figures(1, 527) # [0.0154349] volatility over twenty days
client.stated_figures(1, 504) # [383820.0, 1.0] what one contract delivers
client.stated_figures(1, 407) # [24340.8, 18338.7, 34772.6, 26198.1]
Six of them state the volatility a contract has shown over 10, 50, 75, 100,
150 and 200 days, one figure each: 511 and 513 to 517 (stated_figures(1, 511)
and on).
The figures arrive in the venue's order and at its widths, and a figure it holds nothing for arrives as the largest number its field carries. Nothing is republished under a tick number of this client's own choosing.
Four series number their figures themselves, in two tables — whole numbers and fractional ones:
client.numbered_figures(1, 562, fractional=False) # [(7, 20260911.0), (30, 20260814.0), … (1826, 20210917.0)]
client.numbered_figures(1, 562, fractional=True) # [(7, 765.96), (30, 776.34), … (1826, 419.586)]
client.numbered_figures(1, 757, fractional=True) # [(30, 766.106)] the average close over thirty days
The first of those is a price history: the number is a span of days, the whole table holds the date each was taken and the fractional table the price on it, back to five years — from a quote subscription, with no historical-data request.
Two more state a run of paired figures, and one carries the venue's option model as the contract closed rather than as it stands — every greek it states, including several the API has no field for:
client.paired_figures(1, 546) # [(coordinate, volatility), …]
client.closing_option_model(1) # {'delta': …, 'rho': …, 'fugit': …, …}
Others state rows of three figures, and what the three are is the series' own: on 547 a quantity, what it is offered at and a second price where the form states one; on 320 a packed quote's field, its figure and how far its decimal point moves.
client.stated_rows_series(1) # [320, 547] which series stated rows
client.stated_rows(1, 547) # [(quantity, price, second price), …]
Two more state what the venue's option model works an underlying's chain from, per class of its options and per expiry: 687 as it stands, and 691 as the chain closed. Each is asked for on a subscription on the underlying, and this client asks the venue for it on the option model's name, as a gateway does:
client.req_mkt_data(1, underlying, "687", False, False, [])
client.chain_model_parameters(1, 687) # [{'productId': …, 'classes': [('SPY', [100.0])],
# 'underlyingPrice': …, 'dividends': [(yyyymmdd, amount)],
# 'terms': [{'lastTradeDate': …, 'modelYield': …,
# 'interestRate': …, 'forward': …,
# 'callAtmVol': …, 'putAtmVol': …}], …}]
A gateway reads both for its own model and hands none of it on. The two volatilities are per trading day; the venue states no unit for the yield and the rate. What a series stated goes when the series is withdrawn, as a gateway forgets it.
Series that are acknowledged and state nothing
Some series are acknowledged and then say nothing: the venue assigns the subscription a tag and states nothing on it, with no error to read. On the session this was written from, six behaved that way: 490, 546, 669, 700, 726 and 733. The same six on the same account, minutes apart, over both a paper and a live login.
An empty result from any of the calls above can mean two things: a series the venue has nothing to say about for that contract, or one this account is not entitled to. The subscription list in account management is what tells them apart.
Why this is not in the API
The API is a protocol between a gateway and a program on the same machine. What the gateway does with any of this is its own and not visible from here; what is visible is that none of it was ever framed as a message to forward. A client that speaks the venue's protocol receives it directly, so it is here.
Two consequences worth stating plainly:
- None of this is derived. Every figure above is stated by the venue, read off the session. Nothing here computes what the venue did not say.
- A gateway has no message for these. A program that calls one runs here and not against a gateway, so a program that has to run against both leaves them alone.
The same calls in Rust
| Python | Rust |
|---|---|
client.enabled_features() | client.enabled_features() |
client.order_permissions() | client.order_permissions() |
client.permitted_order_types(sec_type) | client.permitted_order_types(sec_type) |
client.algorithms() | client.algorithms() |
client.algorithms_for(sec_type) | client.algorithms_for(sec_type) |
client.get_account_id() | client.account_id (a field) |
client.next_order_id() | client.next_order_id() |
client.ccp_session_id() | client.ccp_session_id() |
client.misc_url(key) | client.misc_url(key) |
client.company_data(con_id, series) | client.company_data(con_id, series) |
client.company_data_series(con_id) | client.company_data_series(con_id) |
client.stated_figures(req_id, series) | client.stated_figures(req_id, series) |
client.stated_figures_series(req_id) | client.stated_figures_series(req_id) |
client.numbered_figures(req_id, series, fractional) | client.numbered_figures(req_id, series, fractional) |
client.numbered_figures_series(req_id) | client.numbered_figures_series(req_id) |
client.paired_figures(req_id, series) | client.paired_figures(req_id, series) |
client.paired_figures_series(req_id) | client.paired_figures_series(req_id) |
client.stated_rows(req_id, series) | client.stated_rows(req_id, series) |
client.stated_rows_series(req_id) | client.stated_rows_series(req_id) |
client.closing_option_model(req_id) | client.closing_option_model(req_id) |
client.chain_model_parameters(req_id, series) | client.chain_model_parameters(req_id, series) |
client.req_ping() | client.req_ping() |
client.last_rtt_ms() | client.last_rtt(), an Option<Duration> (delegating to client.shared_state().last_ccp_rtt()) |
Login
The smallest program that opens a session: connect, read the order id, disconnect.
Two flavours are shown. Paper for everyday testing, live for read-only checks against the real account.
What this shows
- Reading credentials from environment variables.
EClient.connect(username=, password=, host=, paper=).- Starting
EClient.runon a daemon thread. - Reading the order id off the
next_valid_idcallback.
What comes back
connect() blocks until the session is up, and fires three callbacks on your
wrapper before it returns, on the calling thread: connect_ack,
managed_accounts, then next_valid_id.
next_valid_id is fired last on purpose. The venue names what the account has
already used just after the connection is made, and an id announced before that
lands is one a fill spent long ago. A program that trusted it would have its
first order refused as a duplicate.
So when connect() returns, the session is established and the id has already
been delivered. The wait() in the example is belt and braces, not a gate.
The daemon run() thread is what carries every callback after connect: quotes,
order status, bars, errors. Start it, or nothing further arrives.
Limits
A live login may push a second factor. Approve it on your authenticator;
connect() blocks until the gate clears, so give it a longer wall clock than a
paper login needs.
The live example is read-only: it logs in, prints, and disconnects. Send orders from the paper account.
Paper
Run it
IB_USERNAME=... IB_PASSWORD=... python examples/hello_login.py
Source
"""Hello-world recipe: connect, wait for next_valid_id, disconnect.
Usage:
IB_USERNAME=... IB_PASSWORD=... python examples/hello_login.py
"""
import os
import threading
from ib_dx import EClient, EWrapper
class LoginWrapper(EWrapper):
def __init__(self):
self.ready = threading.Event()
self.order_id = None
def next_valid_id(self, order_id):
self.order_id = order_id
self.ready.set()
w = LoginWrapper()
c = EClient(w)
c.connect(
username=os.environ["IB_USERNAME"],
password=os.environ["IB_PASSWORD"],
paper=True,
)
threading.Thread(target=c.run, daemon=True).start()
if not w.ready.wait(timeout=15):
raise RuntimeError("did not receive next_valid_id")
print(f"logged in. next_valid_id = {w.order_id}")
c.disconnect()
Live
Run it
IB_LIVE_USERNAME=... IB_LIVE_PASSWORD=... python examples/hello_login_live.py
Source
"""Live-account login recipe: connect with paper=False, wait for next_valid_id,
disconnect. Read-only — no orders, no market data.
When the live login triggers a second-factor push, approve it on your mobile
authenticator. The connect call blocks until the gate clears.
Usage:
IB_LIVE_USERNAME=... IB_LIVE_PASSWORD=... python examples/hello_login_live.py
"""
import os
import threading
from ib_dx import EClient, EWrapper
class LoginWrapper(EWrapper):
def __init__(self):
self.ready = threading.Event()
self.order_id = None
def next_valid_id(self, order_id):
self.order_id = order_id
self.ready.set()
w = LoginWrapper()
c = EClient(w)
c.connect(
username=os.environ["IB_LIVE_USERNAME"],
password=os.environ["IB_LIVE_PASSWORD"],
host=os.environ.get("IB_HOST", ""),
paper=False,
)
threading.Thread(target=c.run, daemon=True).start()
if not w.ready.wait(timeout=60):
raise RuntimeError("did not receive next_valid_id")
print(f"logged in LIVE. next_valid_id = {w.order_id}")
c.disconnect()
Request Contract Details
Resolve a symbol: send a partial description, collect every contract the venue
lists under it, stop when the end marker lands. This is how you turn "AAPL"
into a con_id you can quote and trade on.
What this shows
- Building a partial
Contractfromsymbol,sec_type,exchangeandcurrency. req_contract_details(req_id, contract)and thecontract_detailscallback.- Driving the callback loop on a daemon thread with
EClient.run. - Reading
con_id,primary_exchangeandtrading_classoff the returnedContractDetails.
What comes back
One contract_details callback per matching contract, then one
contract_details_end for the request id. A description that matches several
listings answers with all of them, so more than one row means an ambiguous
description rather than an error.
The shorter form
Two blocking methods do the same work with no wrapper and no callbacks:
rows = c.contract_details(aapl) # list, every match
one = c.qualify_contract(aapl) # a single Contract
qualify_contract refuses an ambiguous description rather than handing back
whichever the venue listed first, which would be a different contract from the
one you asked about. c.qualify_contracts([...]) takes several at once.
Nothing that answers these calls reaches your wrapper, even what arrives after
the call has returned.
These send, wait and return. They take their answer off the queue by request id
and release the interpreter lock while waiting, so they need no run() thread
of their own. Do not run both styles on one client: run() drains every queue
rather than only its own, so a dispatch loop beside a blocking call competes
with it for the answer. Pick the callbacks or pick these.
Lookups by contract id
A lookup by con_id includes the requested exchange. Leaving the exchange
empty asks for the preferred definition. The same rule applies when a market
data request needs to name a contract given only by id.
Contract details reuse exchange rule scopes already returned during the session. A first lookup can still need a separate request for each missing exchange; later lookups reuse those answers. CORPACT is excluded because it does not name a trading venue.
Run it
IB_USERNAME=... IB_PASSWORD=... python examples/hello_contract_details.py
Source
"""Recipe: req_contract_details for AAPL on paper, print con_id and primary exchange.
Usage:
IB_USERNAME=... IB_PASSWORD=... python examples/hello_contract_details.py
"""
import os
import threading
from ib_dx import EClient, EWrapper, Contract
class DetailsWrapper(EWrapper):
def __init__(self):
self.rows = []
self.done = threading.Event()
def contract_details(self, req_id, details):
self.rows.append(details)
def contract_details_end(self, req_id):
self.done.set()
def error(self, req_id, error_time, code, msg, advanced=""):
if code not in (2104, 2106, 2158):
print(f"[error] {code}: {msg}")
w = DetailsWrapper()
c = EClient(w)
c.connect(
username=os.environ["IB_USERNAME"],
password=os.environ["IB_PASSWORD"],
paper=True,
)
threading.Thread(target=c.run, daemon=True).start()
aapl = Contract()
aapl.symbol = "AAPL"
aapl.sec_type = "STK"
aapl.exchange = "SMART"
aapl.currency = "USD"
c.req_contract_details(1, aapl)
if not w.done.wait(timeout=15):
raise RuntimeError("contract_details_end not received")
print(f"matches: {len(w.rows)}")
for d in w.rows:
print(f" con_id={d.contract.con_id:>8} "
f"primary={d.contract.primary_exchange:<10} "
f"trading_class={d.contract.trading_class}")
c.disconnect()
Historical Bars
Fetch one trading day of 5-minute SPY bars and print the first and last. Use this whenever you need a finished series rather than a live feed.
What this shows
- Building a
Contractthat carriescon_id, so the venue is not asked to guess which listing you meant. req_historical_data(req_id, contract, end_date_time, duration_str, bar_size_setting, what_to_show, use_rth), withformat_date,keep_up_to_dateandchart_optionsleft at their defaults.- Driving the callback loop on a daemon thread with
EClient.run. - Reading OHLCV off the bar object.
An empty end_date_time means now. use_rth=1 keeps the series inside regular
hours.
What comes back
One historical_data callback per bar, in time order, then one
historical_data_end carrying the first and last timestamps of the range that
was served. A request whose bars are refused once it has been handed over is
told why on error, and nothing follows it: a gateway states no end after a
refusal, so a program waiting for the end stops on the error too. A request
kept up to date whose five-second stream the venue refuses is told nothing, as
a gateway tells it nothing: a history still to come arrives with its end, and
no update follows. When the historical connection drops, a request kept up to
date ends on 10182, and one still arriving is told 165 and asked again once the
connection is back, as a gateway does. A 165 is a notice and ends nothing: the
call that waits for the bars waits through it.
With keep_up_to_date=True the bar still forming goes on from the history's
last bar, as a gateway's does: the live five-second bars inside it are folded
into the open, high, low and volume the venue stated for it. Five-second bars
are sent as the stream sends them. One that arrives before the history is in
is held, as a gateway holds it, and folded into the history's last bar with the
next one after it; one from before the bar in hand opened is passed over. So
nothing of the bar still forming is heard before historical_data_end, and
every update is heard on historical_data_update, whatever else the request's
number has done: a live bar stream under the same number is heard on
real_time_bar, and a second request under the number, refused while this
one answers, leaves this one's dates as it asked for them. A daily session supplied with the history
keeps its opening and end across UTC midnight. Its updates use the session
end's date on the series' timezone, as the history does, under both
date-format settings. Timed daily history is returned as yyyyMMdd; explicit
weekly and monthly date strings are preserved.
When that session ends, the next five-second bar opens the next bar at midnight UTC of its own day, as a gateway opens it. That bar ends at the next midnight UTC, or at the close the history stated where that falls between the two, and is dated by where it ends on the series' timezone; a bar that has ended is opened again by the next five-second bar. So a session that opens before midnight UTC, as a future's evening session does, is dated by the day the history closed until midnight UTC, and by the next one after it. On a timezone east of UTC the next midnight UTC falls on the following day there, so the bar a session there rolls over to is dated by the day after that session, as a gateway dates it. Intraday bars open on whole multiples of their length from the epoch; a week opens on Monday and a month on its first day at midnight UTC. Updates to these calendar bars remain dates under both date-format settings.
The shorter form
One blocking method does the same request and returns the list:
bars = c.historical_data(spy, "", "1 D", "5 mins", "TRADES", use_rth=1)
It sends, waits and returns, taking its answer off the queue by request id and
releasing the interpreter lock while it waits. Do not run run() beside it on
the same client: run() drains every queue rather than only its own, so the two
compete for the answer. Pick the callbacks or pick this.
Cancelling historical data also withdraws the corporate-actions query held by that request. A separate corporate-actions request keeps running, even when it uses the same caller request id.
Limits
bar_size_setting and what_to_show are checked before anything is sent, so a
misspelling is refused here rather than answered with a different series. With
keep_up_to_date=True the size must be one this client can form from
the five-second bars the venue keeps sending: five seconds up to a day in
whole multiples of five seconds, a week, or a month. A second is shorter than
what arrives and is refused. A request kept up to date is refused, as a gateway
refuses it, with an end date, on a combination, or on a series other than
TRADES, MIDPOINT, BID, ASK and the two option open-interest series — an empty
series name included.
How far back a series goes, and which bar sizes pair with which durations, are
the venue's rules. A request outside them comes back as a stated refusal on the
error callback rather than as empty bars.
Run it
IB_USERNAME=... IB_PASSWORD=... python examples/hello_bar_data.py
Source
"""Recipe: fetch 1 day of 5-minute SPY bars, print first/last bar.
Usage:
IB_USERNAME=... IB_PASSWORD=... python examples/hello_bar_data.py
"""
import os
import threading
from ib_dx import EClient, EWrapper, Contract
class BarsWrapper(EWrapper):
def __init__(self):
self.bars = []
self.done = threading.Event()
def historical_data(self, req_id, bar):
self.bars.append(bar)
def historical_data_end(self, req_id, start, end):
self.done.set()
def error(self, req_id, error_time, code, msg, advanced=""):
if code not in (2104, 2106, 2158):
print(f"[error] {code}: {msg}")
# A refused request is told the error alone: no end follows it.
if req_id == 1:
self.done.set()
w = BarsWrapper()
c = EClient(w)
c.connect(
username=os.environ["IB_USERNAME"],
password=os.environ["IB_PASSWORD"],
paper=True,
)
threading.Thread(target=c.run, daemon=True).start()
spy = Contract()
spy.con_id = 756733
spy.symbol = "SPY"
spy.sec_type = "STK"
spy.exchange = "SMART"
spy.currency = "USD"
c.req_historical_data(
1, spy,
end_date_time="",
duration_str="1 D",
bar_size_setting="5 mins",
what_to_show="TRADES",
use_rth=1,
)
if not w.done.wait(timeout=30):
raise RuntimeError("the bar request was not answered")
print(f"bars: {len(w.bars)}")
if w.bars:
first, last = w.bars[0], w.bars[-1]
print(f" first: {first.date} O={first.open} H={first.high} L={first.low} C={first.close} V={first.volume}")
print(f" last : {last.date} O={last.open} H={last.high} L={last.low} C={last.close} V={last.volume}")
c.disconnect()
Streaming Ticks
Subscribe to top-of-book on SPY, collect for five seconds, print the latest bid, ask and last.
What this shows
req_mkt_data(req_id, contract, generic_tick_list, snapshot, regulatory_snapshot).- Reading prices off
tick_price, wheretick_typenames which price it is: 1 bid, 2 ask, 4 last, 9 close. cancel_mkt_data(req_id)before disconnecting.
What comes back
tick_req_params (tickReqParams in Python) states the contract's price
increment, exchange and snapshot permission once per request, with the first
record the request is served and ahead of its ticks, as a gateway states it; a
request served no record is told nothing. It reads the latest stored
parameters when the callback is delivered. Where the venue names
no best-bid-and-offer exchange, as for a currency, a crypto or a bond, the
exchange is empty. The permission 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. On a
closed market whose frozen quote is served, that is the record the parameters
go out with, so the permission is 0. A follower sharing the subscription gets
its own callback; cancelling and reusing its request id starts a new request. A
request cancelled before delivery receives no callback.
tick_price and tick_size for as long as the subscription runs.
tick_generic fires for the halt state the venue states on tick 49: 0 while the
contract is trading, 1 once it has stopped.
With snapshot=True you get the first available quote, then
tick_snapshot_end, and this client cancels the subscription for you.
regulatory_snapshot=True is the venue's own one-shot snapshot. It is a
different request type, and an account without the
entitlement is refused by the venue.
Limits
Each number in generic_tick_list is asked for, as a subscription of its own
beside the prices. The number you state is the number the venue knows the series
by, so there is nothing to translate. "292" additionally subscribes to news for
that contract, and "292:BRFG+DJNL" asks for it from the providers it names; an
entry that is not a number is reported rather than sent.
Every number the protocol will accept in that list is read here, and each arrives under the number the reference client publishes it under:
| you ask for | you receive |
|---|---|
100 | call and put option volume, on tick_size 29 and 30 |
101 | call and put open interest, on tick_size 27 and 28 |
104 512 | historical volatility, on tick_generic 23 |
105 | average option volume, the two sides added, on tick_size 87 |
106 | option implied volatility, on tick_generic 24 |
162 | the premium of an index over the future written on it, on tick_generic 31 |
165 | the day's volume and the 13, 26 and 52-week extremes, on tick_size 21 and tick_price 15 to 20 |
220 | the mark the venue keeps, on tick_price 78 |
221 232 | the mark, on tick_price 37 |
225 | auction volume and imbalance on tick_size 34 and 36, auction price on tick_price 35, the regulatory imbalance on tick_size 61 |
233 | the trade tape, on tick_string 48: price;size;time;volume;vwap;single |
236 | shortability on tick_generic 46, and the borrowable share count on tick_size 89 |
258 | company ratios, on tick_string 47 |
292 292:BRFG+DJNL | news, on tick_news |
293 294 295 | trade count, trade rate and volume rate, on tick_generic 54, 55 and 56 |
318 | last regular-session trade, on tick_price 57 |
375 | the trade-report tape, on tick_string 77 |
411 | real-time historical volatility, on tick_generic 58 |
456 | what the contract pays out, on tick_string 59 |
460 | bond factor multiplier, on tick_generic 60 |
499 | borrow fee rate, on tick_price 111 |
577 623 | an ETF's net asset value, last and frozen, on tick_price 96 and 97 |
586 | the estimated IPO midpoint on tick_generic 101, and what it opened at on 102 |
588 | futures open interest, on tick_size 86 |
595 | the 3, 5 and 10-minute volumes, on tick_size 63, 64 and 65 |
614 | an ETF's net asset value high and low, on tick_price 98 and 99 |
619 | the slow mark, on tick_price 79 |
787 | the odd lot: both prices on tick_price 105 and 106, their sizes on tick_size 107 and 108, where each is quoted on tick_string 109 and 110 |
Any other number is asked for the same way, as a series of its own. What the venue states on series the TWS API has no tick for is read through the calls under Beyond the documented API.
One subscription per contract. To change the mode on a contract, cancel first.
Every subscription starts live, whatever the type set on
req_market_data_type. As a gateway takes the types, 2 turns frozen data on,
3 and 4 turn delayed data on (4 with delayed-frozen, 3 without), and only 1
turns frozen data off, so a 2 asked after a 3 or a 4 still leaves the delayed
feed to fall back to. With delayed data on, a bid/ask refusal that states
delayed data is available switches to the delayed feed and reports 10167
without ending the request, and market_data_type reports 3. With nothing to
fall back to, the refusal ends the request as a gateway ends it: under 354,
"Requested market data is not subscribed.", followed by "Delayed market data
is available." where the refusal says so; or under 10089, where the services
the refusal names for the API reach past those it names for the data. The
venue's own words follow either, as sent. Under type 2 alone the request ends
there, and no frozen quote is asked for.
Where the logon enables frozen data, the market's status is watched, as a gateway watches it, for a subscription made with frozen data on while it is served live, and for one that has fallen back to delayed data with delayed-frozen data on. While the status says the market is closed, the frozen quote is asked for beside the live one and served, reported as 2; beside a delayed one the delayed-frozen quote is asked for too, and served from its first record, reported as 4, the delayed one until then. The quote asked for first is kept up meanwhile, and once the status says the market is open the frozen quotes are withdrawn and that quote is served again as it stands, reported as 1 or 3. A refusal of the live quote ends the watch on it without a word: the frozen quote beside it is withdrawn, and so is the status, unless the fallback watches it. So a login entitled to live data is served live under type 4 alone, and on a closed market its quote states what the live feed states, zero yields included; under type 2, alone or before a 4, it is served the frozen quote. A calculation's own subscription falls back to nothing: it asks the delayed feed directly where delayed data is on, and watches no status.
req_mkt_data_ex selects a feed directly: mode_9887 is 0 realtime, 1 delayed,
2 frozen, or 3 delayed-frozen. Frozen keeps thinly traded names quoting after
hours, when the realtime feed is silent.
A refusal naming several requests reaches each affected caller. The bid/ask and last entries of one quote produce one subscription error. Cancelling a tick-by-tick stream uses its query identifier before acknowledgement and its assigned stream identifier afterwards; another caller sharing that stream keeps receiving data until it cancels too.
req_mkt_data_ex takes mkt_data_options=None after mode_9887, with the
same option checks as req_mkt_data. With no session, both report 504,
Not connected, before checking the request.
The list accepts manual=0 or manual=1. An unknown key is refused under
10337, an invalid value under 10338, and a malformed list under 320.
NOAPIMISCVLD at logon lifts the key and value checks; the later check that
manual is zero or one remains.
Reading without the callbacks
c.quote(req_id) returns a dict of bid, ask, last, bid_size,
ask_size, last_size, volume, high, low, open, close for a running
subscription, or None if that request id is not subscribed. No wrapper state
to keep, no lock to take.
Run it
IB_USERNAME=... IB_PASSWORD=... python examples/hello_tick_data.py
Source
"""Recipe: stream SPY market data for 5 seconds, print latest bid/ask/last.
Usage:
IB_USERNAME=... IB_PASSWORD=... python examples/hello_tick_data.py
"""
import os
import threading
import time
from ib_dx import EClient, EWrapper, Contract
class TickWrapper(EWrapper):
def __init__(self):
self.bid = 0.0
self.ask = 0.0
self.last = 0.0
self.ticks = 0
def tick_price(self, req_id, tick_type, price, attrib):
self.ticks += 1
if tick_type == 1:
self.bid = price
elif tick_type == 2:
self.ask = price
elif tick_type == 4:
self.last = price
def tick_size(self, req_id, tick_type, size):
self.ticks += 1
def error(self, req_id, error_time, code, msg, advanced=""):
if code not in (2104, 2106, 2158):
print(f"[error] {code}: {msg}")
w = TickWrapper()
c = EClient(w)
c.connect(
username=os.environ["IB_USERNAME"],
password=os.environ["IB_PASSWORD"],
paper=True,
)
threading.Thread(target=c.run, daemon=True).start()
spy = Contract()
spy.con_id = 756733
spy.symbol = "SPY"
spy.sec_type = "STK"
spy.exchange = "SMART"
spy.currency = "USD"
req_id = 1
print("streaming SPY for 5s…")
c.req_mkt_data(req_id, spy, "", False, False)
deadline = time.monotonic() + 5
while time.monotonic() < deadline:
time.sleep(0.1)
c.cancel_mkt_data(req_id)
print(f"ticks: {w.ticks} bid={w.bid:.2f} ask={w.ask:.2f} last={w.last:.2f}")
c.disconnect()
Streaming L2 Market Depth
Subscribe to Level-2 depth on two contracts at once, keep a bid and ask book per contract from the update stream, and print top-of-book at the end.
What this shows
req_mkt_depth(req_id, contract, num_rows=5), one request id per contract.- Applying
update_mkt_depth_l2events to a book keyed by level position. cancel_mkt_depth(req_id, is_smart_depth)for each subscription before disconnecting, naming the mode the book was asked in.
What comes back
update_mkt_depth_l2(req_id, position, market_maker, operation, side, price, size, is_smart_depth) on every level change:
| field | values |
|---|---|
operation | 0 insert, 1 update, 2 delete |
side | 0 ask, 1 bid |
A level carries the maker the venue names on it. An exchange-level book often
names none — many venues quote no makers — and then the field is empty; on the
aggregated book the exchange stands in where the venue names no maker. That is
why the example keeps market_maker alongside price and size.
The example collects for the whole window and prints once at the end, not on
every update. DURATION_SECS sets that window; the default is 15.
After a reconnect the venue restates the book from the top. The client reports
that on error as 317, the TWS API's notice that a book was reset, before the
first new level arrives — as a gateway does. Empty the book on it, or the
levels that follow land on top of the old ones.
Limits
A depth subscription the venue will not serve is refused, and the refusal
arrives on the error callback. The example prints an em dash for a side with
no levels rather than failing, so a book that never started shows up as zero
updates rather than as an error.
A contract whose security type and exchange are both left off is sent as it stands, and an unnamed exchange is read as the smart destination. Naming a security type that does not match the contract asks for one book as another's, which the venue refuses.
Run it
IB_USERNAME=... IB_PASSWORD=... python examples/hello_l2.py
Collect for longer:
DURATION_SECS=30 python examples/hello_l2.py
Source
"""Recipe: stream L2 market depth on AAPL + TSLA, print top-of-book on update.
Usage:
IB_USERNAME=... IB_PASSWORD=... python examples/hello_l2.py
"""
import os
import threading
import time
from ib_dx import EClient, EWrapper, Contract
class Book:
def __init__(self, symbol):
self.symbol = symbol
self.bids = {} # position -> (price, size, mm)
self.asks = {}
self.updates = 0
def apply(self, position, mm, operation, side, price, size):
self.updates += 1
book = self.bids if side == 1 else self.asks
if operation in (0, 1):
book[position] = (price, size, mm)
elif operation == 2:
book.pop(position, None)
def top(self):
bid = max(self.bids.values(), default=None, key=lambda v: v[0])
ask = min(self.asks.values(), default=None, key=lambda v: v[0])
return bid, ask
class L2Wrapper(EWrapper):
def __init__(self, books):
self.books = books # req_id -> Book
def update_mkt_depth_l2(self, req_id, position, market_maker,
operation, side, price, size, is_smart_depth):
if req_id in self.books:
self.books[req_id].apply(position, market_maker, operation, side, price, size)
def error(self, req_id, error_time, code, msg, advanced=""):
if code not in (2104, 2106, 2158):
print(f"[error] {code}: {msg}")
def make_stk(con_id, symbol):
c = Contract()
c.con_id = con_id
c.symbol = symbol
c.sec_type = "STK"
c.exchange = "SMART"
c.currency = "USD"
return c
SUBSCRIPTIONS = [
(1, make_stk(265598, "AAPL")),
(2, make_stk(76792991, "TSLA")),
]
DURATION_SECS = int(os.environ.get("DURATION_SECS", "15"))
books = {req_id: Book(c.symbol) for req_id, c in SUBSCRIPTIONS}
w = L2Wrapper(books)
c = EClient(w)
c.connect(
username=os.environ["IB_USERNAME"],
password=os.environ["IB_PASSWORD"],
paper=True,
)
threading.Thread(target=c.run, daemon=True).start()
for req_id, contract in SUBSCRIPTIONS:
c.req_mkt_depth(req_id, contract, num_rows=5)
print(f"streaming L2 for {DURATION_SECS}s…")
time.sleep(DURATION_SECS)
for req_id, _ in SUBSCRIPTIONS:
c.cancel_mkt_depth(req_id)
for book in books.values():
bid, ask = book.top()
bid_s = f"{bid[0]:.2f} x {bid[1]:.0f}" if bid else "—"
ask_s = f"{ask[0]:.2f} x {ask[1]:.0f}" if ask else "—"
print(f" {book.symbol:<5} updates={book.updates:>5} bid={bid_s:>14} ask={ask_s:<14}")
c.disconnect()
Market Scanner
Run the TOP_PERC_GAIN scan over US major stocks, filtered to names above $5,
and print the top ten.
What this shows
req_scanner_subscription(req_id, subscription), wheresubscriptionis any object carrying the right attributes. The example defines a small class; the reference client's ownScannerSubscriptionworks too.- Reading
scanner_datarows, each a rank and aContractDetails, untilscanner_data_end. cancel_scanner_subscription(req_id)before disconnecting.
What the subscription object is read for
The scan itself:
| attribute | absent means |
|---|---|
instrument | empty |
locationCode | empty |
scanCode | empty |
numberOfRows | the venue picks |
Then the filters, each read by attribute and sent under the venue's own tag
name: abovePrice, belowPrice, aboveVolume, marketCapAbove,
marketCapBelow, moodyRatingAbove, moodyRatingBelow, spRatingAbove,
spRatingBelow, maturityDateAbove, maturityDateBelow, couponRateAbove,
couponRateBelow, averageOptionVolumeAbove, plus excludeConvertible and
stockTypeFilter.
An attribute you leave off takes its default. One that is present and cannot be read is refused rather than run as a different scan under your request id.
What comes back
One scanner_data callback per row, then scanner_data_end for the request id.
Rows are ranked; the example sorts by rank because it collects them into a list
first.
A scanner subscription keeps answering until it is cancelled. The example cancels; if you leave one running it goes on delivering into a session nobody is reading.
Limits
Which scan codes and locations are valid is the venue's list.
req_scanner_parameters() fetches it, and an unknown code comes back as a
stated refusal on the error callback. When the historical connection drops, a
running scan is told 165 as it drops, at each attempt to bring it back that
fails and once it is back, and is then subscribed again and goes on under its
number, as a gateway does; a 165 ends nothing.
Run it
IB_USERNAME=... IB_PASSWORD=... python examples/hello_scanner.py
Source
"""Recipe: subscribe to TOP_PERC_GAIN scanner, print first 10 results.
Usage:
IB_USERNAME=... IB_PASSWORD=... python examples/hello_scanner.py
"""
import os
import threading
from ib_dx import EClient, EWrapper
class ScannerSubscription:
def __init__(self):
self.instrument = "STK"
self.locationCode = "STK.US.MAJOR"
self.scanCode = "TOP_PERC_GAIN"
self.numberOfRows = 25
self.abovePrice = 5.0
class ScannerWrapper(EWrapper):
def __init__(self):
self.rows = []
self.done = threading.Event()
def scanner_data(self, req_id, rank, contract_details,
distance, benchmark, projection, legs_str):
self.rows.append((rank, contract_details))
def scanner_data_end(self, req_id):
self.done.set()
def error(self, req_id, error_time, code, msg, advanced=""):
if code not in (2104, 2106, 2158):
print(f"[error] {code}: {msg}")
w = ScannerWrapper()
c = EClient(w)
c.connect(
username=os.environ["IB_USERNAME"],
password=os.environ["IB_PASSWORD"],
paper=True,
)
threading.Thread(target=c.run, daemon=True).start()
req_id = 1
print("subscribing TOP_PERC_GAIN, STK.US.MAJOR…")
c.req_scanner_subscription(req_id, ScannerSubscription())
if not w.done.wait(timeout=30):
print(f"(scanner_data_end not received; got {len(w.rows)} rows so far)")
w.rows.sort(key=lambda r: r[0])
print(f"results: {len(w.rows)}")
for rank, d in w.rows[:10]:
print(f" #{rank:<3} {d.contract.symbol:<8} {d.contract.primary_exchange:<6} con_id={d.contract.con_id}")
c.cancel_scanner_subscription(req_id)
c.disconnect()
Request Account PnL
Subscribe to the account-level PnL stream, take the first update, cancel and disconnect.
What this shows
c.get_account_id(), which names the account the session opened under.req_pnl(req_id, account).- Reading
daily_pnl,unrealized_pnlandrealized_pnloff thepnlcallback. cancel_pnl(req_id)before disconnecting.
account empty means the account this session opened under.
What comes back
The venue states what each holding was worth at midnight and what it has
realised since. The three figures on the pnl callback are worked out from
those against the prices this session is being told, so they move as the
session's quotes move.
Limits
account is checked as a gateway checks it, in its order and words: a blank
one is refused (Account must not be empty, 321); one the login does not hold
is refused (Invalid account code, 321), and so is All on a login holding one
account or where the logon says the login may not ask for every account; All
on a login the venue adds accounts to is refused (This API request for All is
not supported for Dynamic Account Addition, 321). Another account the login
holds, and All where a gateway would take it, are refused too, on both
clients: the figures are worked out from one account's midnight seeds and
holdings, the one this session opened under. Nothing is subscribed for a
refused request.
model_code is taken and not applied. There is no model portfolio to name here,
so leave it at its default.
cancel_pnl stops the updates. The venue has no message withdrawing the
subscription itself, on a gateway as here, so the updates stopping is what the
call does.
An account with no position reports zeros. Pair this with the limit-order recipe if you want to watch the numbers move.
Run it
IB_USERNAME=... IB_PASSWORD=... python examples/hello_pnl.py
Source
"""Recipe: subscribe to account-level PnL, take one update, then cancel.
Usage:
IB_USERNAME=... IB_PASSWORD=... python examples/hello_pnl.py
"""
import os
import threading
from ib_dx import EClient, EWrapper
class PnlWrapper(EWrapper):
def __init__(self):
self.got_pnl = threading.Event()
self.pnl_data = None
def pnl(self, req_id, daily_pnl, unrealized_pnl, realized_pnl):
self.pnl_data = (daily_pnl, unrealized_pnl, realized_pnl)
self.got_pnl.set()
def error(self, req_id, error_time, code, msg, advanced=""):
if code not in (2104, 2106, 2158):
print(f"[error] {code}: {msg}")
w = PnlWrapper()
c = EClient(w)
c.connect(
username=os.environ["IB_USERNAME"],
password=os.environ["IB_PASSWORD"],
paper=True,
)
threading.Thread(target=c.run, daemon=True).start()
account = c.get_account_id()
print(f"account: {account}")
req_id = 1
c.req_pnl(req_id, account)
if w.got_pnl.wait(timeout=15):
daily, unrealized, realized = w.pnl_data
print(f"daily={daily:.2f} unrealized={unrealized:.2f} realized={realized:.2f}")
else:
print("no pnl update within 15s — try again with an open position")
c.cancel_pnl(req_id)
c.disconnect()
Send a Limit Order
Place a BUY LMT on SPY far below market, watch it acknowledge, then cancel it. Connect, take an id, place, read status, cancel, disconnect.
What this shows
- Taking the order id off
next_valid_id, whichconnect()fires before it returns.c.next_order_id()reserves one directly if you would rather ask. - An
Orderwithorder_type = "LMT"andlmt_priceset.outside_rth = Trueis what lets it be acknowledged outside regular hours. order_statuscallbacks.PreSubmittedthenSubmittedwhile it rests,Cancelledonce the cancel lands.cancel_order(order_id, order_cancel). The second argument is what the withdrawal states about itself: anOrderCancel(who is withdrawing it, whether a person entered it, a manual time), or a time alone;""states nothing. The operator and who entered it travel on the cancel. A time does not, and the caller is told so onerror; a time a gateway cannot read is refused under 10301 and the order keeps working.
What comes back
order_status on every change and again on each fill. filled and remaining
are quantities, avg_fill_price is the average of what has filled so far. An
order that rests will sit at Submitted until you cancel it.
The perm_id on that callback is the number the order goes to the venue
under, as a gateway states it, which for this order is its own order id. It is
stated from the moment the order is sent and stays the same for the life of the
order, including across a modify.
Limits
Order ids count from one past the highest id the account is working an order under, which the venue names at every connect, from every session. So one taken here does not collide with an order placed elsewhere.
tif is checked against what the venue carries: DAY, GTC, IOC, FOK,
OPG, GTD, GTX, DTC, AUC, NMIN, spelled exactly. An unrecognised value is
refused here rather than sent as DAY and quietly expiring at the close.
Paper account only. The price is set far below market so it will not fill.
Run it
IB_USERNAME=... IB_PASSWORD=... python examples/hello_limit_order.py
Source
"""Recipe: place a non-marketable BUY LMT on SPY, watch Submitted, then cancel.
Usage:
IB_USERNAME=... IB_PASSWORD=... python examples/hello_limit_order.py
"""
import os
import threading
from ib_dx import EClient, EWrapper, Contract, Order
class OrderWrapper(EWrapper):
def __init__(self):
self.next_id = None
self.submitted = threading.Event()
self.cancelled = threading.Event()
def next_valid_id(self, order_id):
self.next_id = order_id
def order_status(self, order_id, status, filled, remaining,
avg_fill_price, perm_id, parent_id,
last_fill_price, client_id, why_held, mkt_cap_price):
print(f"[status] oid={order_id} status={status}")
if status == "Submitted":
self.submitted.set()
if status == "Cancelled":
self.cancelled.set()
def error(self, req_id, error_time, code, msg, advanced=""):
if code not in (2104, 2106, 2158):
print(f"[error] {code}: {msg}")
w = OrderWrapper()
c = EClient(w)
c.connect(
username=os.environ["IB_USERNAME"],
password=os.environ["IB_PASSWORD"],
paper=True,
)
threading.Thread(target=c.run, daemon=True).start()
order_id = w.next_id
spy = Contract()
spy.con_id = 756733
spy.symbol = "SPY"
spy.sec_type = "STK"
spy.exchange = "SMART"
spy.currency = "USD"
order = Order()
order.action = "BUY"
order.total_quantity = 1
order.order_type = "LMT"
order.lmt_price = 1.00
order.tif = "DAY"
order.outside_rth = True
print(f"placing BUY 1 SPY LMT 1.00 (oid={order_id})")
c.place_order(order_id, spy, order)
w.submitted.wait(timeout=15)
print(f"cancelling oid={order_id}")
c.cancel_order(order_id, "")
w.cancelled.wait(timeout=15)
c.disconnect()
Send a Stop Order
Place a BUY STOP on SPY far above market. The trigger never fires, so the order rests. Watch it acknowledge, then cancel it.
What this shows
- Taking the order id off
next_valid_id, whichconnect()fires before it returns. - An
Orderwithorder_type = "STP"and the trigger price inaux_price.lmt_priceis unused for a plain stop. tif = "GTC", so it survives the close rather than expiring with the day.order_statuscallbacks:PreSubmittedthenSubmittedwhile it rests,Cancelledonce the cancel lands.cancel_order(order_id, "").
What comes back
The same order_status stream as any other order. A resting stop shows no
fills: filled stays 0 and remaining stays at the full quantity until the
trigger is touched.
Limits
A stop is held until its trigger is touched, then goes to the market as a market order. This example sets the trigger far above market so that never happens. On a real stop, the fill price is whatever the market gives you at that moment, not the trigger.
Paper account only.
Run it
IB_USERNAME=... IB_PASSWORD=... python examples/hello_stop_order.py
Source
"""Recipe: place a BUY STP on SPY far above market, watch Submitted, then cancel.
Usage:
IB_USERNAME=... IB_PASSWORD=... python examples/hello_stop_order.py
"""
import os
import threading
from ib_dx import EClient, EWrapper, Contract, Order
class OrderWrapper(EWrapper):
def __init__(self):
self.next_id = None
self.submitted = threading.Event()
self.cancelled = threading.Event()
def next_valid_id(self, order_id):
self.next_id = order_id
def order_status(self, order_id, status, filled, remaining,
avg_fill_price, perm_id, parent_id,
last_fill_price, client_id, why_held, mkt_cap_price):
print(f"[status] oid={order_id} status={status}")
if status == "Submitted":
self.submitted.set()
if status == "Cancelled":
self.cancelled.set()
def error(self, req_id, error_time, code, msg, advanced=""):
if code not in (2104, 2106, 2158):
print(f"[error] {code}: {msg}")
w = OrderWrapper()
c = EClient(w)
c.connect(
username=os.environ["IB_USERNAME"],
password=os.environ["IB_PASSWORD"],
paper=True,
)
threading.Thread(target=c.run, daemon=True).start()
order_id = w.next_id
spy = Contract()
spy.con_id = 756733
spy.symbol = "SPY"
spy.sec_type = "STK"
spy.exchange = "SMART"
spy.currency = "USD"
order = Order()
order.action = "BUY"
order.total_quantity = 1
order.order_type = "STP"
order.aux_price = 9999.0
order.tif = "GTC"
order.outside_rth = True
print(f"placing BUY 1 SPY STP 9999.00 (oid={order_id})")
c.place_order(order_id, spy, order)
w.submitted.wait(timeout=15)
print(f"cancelling oid={order_id}")
c.cancel_order(order_id, "")
w.cancelled.wait(timeout=15)
c.disconnect()
Order Lifecycle
One order walked through every state it can reach on SPY: place, modify, fill, cancel, and the executions query afterwards. Use this when you are changing order handling and want each transition checked, rather than as a template to copy into a strategy.
It prints a pass/fail line per step and exits non-zero if any step failed.
What this shows
- Taking the first order id off
next_valid_idand counting up from it. The example keeps its own counter so it can hold two ids at once;c.next_order_id()reserves one directly. - Placing a limit order far from market and reading
Submittedofforder_status. req_open_orders(), and finding the order in theopen_ordercallbacks.- Placing again under the same order id at a new price, which is a modify, and checking the permId did not move.
- Walking the price to market to trigger a fill, then
req_executions(req_id). - Placing a second order, cancelling it, and reading the
Cancelledterminal status. - Flattening whatever filled before disconnecting.
What comes back
req_open_orders() waits up to three seconds for the replay the venue sends
unprompted after a connect, then fires open_order and order_status for each
working order. It waits because answering before that replay lands reports none
of them, and a caller who reads "nothing working" places the same order twice.
req_all_open_orders() is the same call.
req_executions(req_id) replays the day's executions — the fills this
session has seen and the ones the venue restated when the session opened — then
fires exec_details_end. A restarted program is answered with the fills it made
before it restarted, including fills on orders that had already completed. What
is not in it is the account's history: the venue restates a day, not a life, and
this protocol has no question that asks for more. The fill in step 6 happens
inside this session, which is why the query finds it either way.
An exec_filter is read by attribute: symbol, secType, exchange, side.
A side of BUY or SELL is translated to the venue's own word before
matching, so a filter written the way you write an order action works.
permId
The perm_id on order_status is the number the order goes to the venue
under, as a gateway states it: for an order placed here, the number this client
sent it under, and for one another session placed, the number that session sent
it under. It is stable for the life of the order, which is what step 5 checks: a
modify keeps the same order, so it keeps the same permId. A modify or a cancel
of an order another session placed keeps that session's number too. It is not a
key on its own: a number whose order was withdrawn or refused is free again at
the venue, so orders the account finished under a number used twice share a
permId, and completed_order delivers each of them.
Limits
Paper account only. Step 6 deliberately crosses the spread, so it moves real paper position; the script flattens it at the end.
Steps 6 and 7 need an open market. With the market shut, price discovery falls
back to SPY_PRICE (default 630.0), nothing fills, and both steps report
failure rather than passing on a market that never traded.
Run it
IB_USERNAME=... IB_PASSWORD=... python examples/order_lifecycle.py
Source
"""Order lifecycle on SPY: place, modify, cancel, fill, executions.
Walks an order through every state it can reach and checks each one, so a
change to order handling shows up here rather than in a live account.
1. Take the next order id.
2. Place SPY 100 shares, LMT far from market.
3. req_open_orders() — the order is there, under a permId.
4. Place again under the same id, at a new price — a modify.
5. The permId is the same across the modify.
6. Place again at market — trigger a fill.
7. req_executions() — the fill is reported with its quantity and price.
8. Place another LMT far from market, cancel it, see Cancelled.
Ends by flattening whatever step 6 filled.
Usage:
IB_USERNAME=... IB_PASSWORD=... python examples/order_lifecycle.py
"""
import os
import sys
import time
import threading
from ib_dx import EWrapper, EClient, Contract, Order
SPY_CON_ID = 756733
class Wrapper(EWrapper):
def __init__(self):
super().__init__()
self.next_id = 0
self.order_statuses = [] # (order_id, status, filled, remaining, perm_id)
self.perm_ids = {} # order_id -> perm_id
self.open_orders = [] # (order_id, contract, order)
self.executions = [] # (req_id, contract, execution)
self.got_status = threading.Event()
self.got_fill = threading.Event()
self.got_cancelled = threading.Event()
self.got_open_order_end = threading.Event()
self.got_exec_end = threading.Event()
# Price discovery
self.last_price = 0.0
self.got_tick = threading.Event()
def next_valid_id(self, order_id):
self.next_id = order_id
def managed_accounts(self, accounts_list):
pass
def connect_ack(self):
pass
def tick_price(self, req_id, tick_type, price, attrib):
if price > 0 and tick_type in (1, 2, 4):
self.last_price = price
self.got_tick.set()
def tick_size(self, req_id, tick_type, size):
pass
def order_status(self, order_id, status, filled, remaining,
avg_fill_price, perm_id, parent_id,
last_fill_price, client_id, why_held, mkt_cap_price):
self.order_statuses.append((order_id, status, filled, remaining, perm_id))
self.perm_ids[order_id] = perm_id
print(f" [status] oid={order_id} status={status} filled={filled} "
f"remaining={remaining} permId={perm_id}")
self.got_status.set()
if status == "Filled" or filled > 0:
self.got_fill.set()
if status == "Cancelled":
self.got_cancelled.set()
def open_order(self, order_id, contract, order, order_state):
self.open_orders.append((order_id, contract, order))
def open_order_end(self):
self.got_open_order_end.set()
def exec_details(self, req_id, contract, execution):
self.executions.append((req_id, contract, execution))
print(f" [exec] symbol={contract.symbol} exec={execution}")
def exec_details_end(self, req_id):
self.got_exec_end.set()
def commission_and_fees_report(self, commission_and_fees_report):
pass
def error(self, req_id, error_time, error_code, error_string, advanced_order_reject_json=""):
if error_code not in (2104, 2106, 2158, 202):
print(f" [error] {error_code}: {error_string}")
def make_spy():
c = Contract()
c.con_id = SPY_CON_ID
c.symbol = "SPY"
c.sec_type = "STK"
c.exchange = "SMART"
c.currency = "USD"
return c
def alloc_id(wrapper):
oid = wrapper.next_id
wrapper.next_id += 1
return oid
def get_price(client, wrapper):
spy = make_spy()
client.req_mkt_data(9999, spy, "", False)
got = wrapper.got_tick.wait(timeout=30)
client.cancel_mkt_data(9999)
if not got or wrapper.last_price <= 0:
return None
return wrapper.last_price
def last_status_for(wrapper, oid):
"""Return last (status, remaining) for a given order_id, or (None, None)."""
matches = [s for s in wrapper.order_statuses if s[0] == oid]
if matches:
return matches[-1][1], matches[-1][3]
return None, None
def run_example():
username = os.environ.get("IB_USERNAME", "")
password = os.environ.get("IB_PASSWORD", "")
if not username or not password:
print("Set IB_USERNAME and IB_PASSWORD")
sys.exit(1)
w = Wrapper()
c = EClient(w)
c.connect(username=username, password=password,
host=os.environ.get("IB_HOST", ""), paper=True)
t = threading.Thread(target=c.run, daemon=True)
t.start()
print(f"Connected. nextValidId={w.next_id}")
spy = make_spy()
results = {} # step -> pass/fail
filled_qty = 0 # how many shares are left to flatten
# ── Price discovery ──────────────────────────────────────────────────
price = get_price(c, w)
if price is None:
# Use last known close as fallback when market is closed
price = float(os.environ.get("SPY_PRICE", "630.0"))
print(f"No live price — using fallback: {price}")
else:
print(f"SPY last price: {price}")
# ══════════════════════════════════════════════════════════════════════
# Step 1: reqIds — get next valid orderId
# ══════════════════════════════════════════════════════════════════════
print("\n=== Step 1: reqIds ===")
oid1 = alloc_id(w)
print(f" orderId={oid1}")
results["1_reqIds"] = "PASS"
# ══════════════════════════════════════════════════════════════════════
# Step 2: placeOrder — SPY 100 shares, LMT far from market
# ══════════════════════════════════════════════════════════════════════
print("\n=== Step 2: placeOrder (100 shares LMT far from market) ===")
order1 = Order()
order1.action = "BUY"
order1.total_quantity = 100
order1.order_type = "LMT"
order1.lmt_price = round(price * 0.80, 2) # 20% below market
order1.tif = "GTC"
order1.outside_rth = True
c.place_order(oid1, spy, order1)
assert w.got_status.wait(timeout=30), "No order status received"
perm_id_place = w.perm_ids.get(oid1, 0)
assert perm_id_place > 0, f"permId should be positive, got {perm_id_place}"
print(f" permId on place: {perm_id_place}")
print(f" lmt_price={order1.lmt_price} (20% below market)")
results["2_place"] = "PASS"
# ══════════════════════════════════════════════════════════════════════
# Step 3: reqOpenOrders — verify order appears with correct permId
# ══════════════════════════════════════════════════════════════════════
print("\n=== Step 3: reqOpenOrders ===")
c.req_open_orders()
assert w.got_open_order_end.wait(timeout=15), "open_order_end not received"
placed = [o for o in w.open_orders if o[0] == oid1]
assert len(placed) > 0, "the order should appear in open orders"
print(f" Found {len(placed)} open order(s) for oid={oid1}")
results["3_reqOpenOrders"] = "PASS"
# ══════════════════════════════════════════════════════════════════════
# Step 4: placeOrder (same orderId, new price) — modify
# ══════════════════════════════════════════════════════════════════════
print("\n=== Step 4: Modify order (same orderId, price closer to market) ===")
w.got_status.clear()
order1.lmt_price = round(price * 0.90, 2) # 10% below — still won't fill
c.place_order(oid1, spy, order1)
assert w.got_status.wait(timeout=15), "No status on modify"
status_after_modify, remaining_after_modify = last_status_for(w, oid1)
modify_ok = status_after_modify == "Submitted" and remaining_after_modify == 100.0
print(f" new lmt_price={order1.lmt_price} (10% below market)")
print(f" status after modify: {status_after_modify}, remaining: {remaining_after_modify}")
if modify_ok:
results["4_modify"] = "PASS"
else:
results["4_modify"] = f"FAIL (expected Submitted/100, got {status_after_modify}/{remaining_after_modify})"
# ══════════════════════════════════════════════════════════════════════
# Step 5: Verify permId stable across modify
# ══════════════════════════════════════════════════════════════════════
print("\n=== Step 5: Verify permId stability ===")
perm_id_modify = w.perm_ids[oid1]
perm_stable = perm_id_place == perm_id_modify
print(f" permId after modify: {perm_id_modify} (stable={perm_stable})")
results["5_permId_stable"] = "PASS" if perm_stable else f"FAIL ({perm_id_place} → {perm_id_modify})"
# ══════════════════════════════════════════════════════════════════════
# Step 6: placeOrder (same orderId, price at market) — trigger fill
# ══════════════════════════════════════════════════════════════════════
print("\n=== Step 6: Modify to market price (trigger fill) ===")
time.sleep(2) # let the venue settle the first modify
w.got_status.clear()
w.got_fill.clear()
# Re-fetch price in case it moved
w.got_tick.clear()
c.req_mkt_data(9998, spy, "", False)
if w.got_tick.wait(timeout=10) and w.last_price > 0:
price = w.last_price
c.cancel_mkt_data(9998)
order1.lmt_price = round(price * 1.02, 2) # slightly above market
c.place_order(oid1, spy, order1)
got_fill = w.got_fill.wait(timeout=30)
if got_fill:
fill_statuses = [s for s in w.order_statuses
if s[0] == oid1 and (s[1] == "Filled" or s[2] > 0)]
if fill_statuses:
filled_qty = int(fill_statuses[-1][2]) # how much was filled
perm_id_fill = w.perm_ids[oid1]
print(f" Fill confirmed: qty={filled_qty}, permId={perm_id_fill}")
results["6_fill_via_modify"] = "PASS"
else:
results["6_fill_via_modify"] = "FAIL (got_fill but no fill status)"
else:
status_now, _ = last_status_for(w, oid1)
print(f" No fill via modify (status={status_now})")
results["6_fill_via_modify"] = f"FAIL (no fill, status={status_now})"
# ══════════════════════════════════════════════════════════════════════
# Step 7: reqExecutions — verify execDetails
# ══════════════════════════════════════════════════════════════════════
print("\n=== Step 7: reqExecutions ===")
c.req_executions(8001)
w.got_exec_end.wait(timeout=15)
spy_execs = [e for e in w.executions if e[1].symbol == "SPY"]
if spy_execs:
print(f" Found {len(spy_execs)} SPY execution(s)")
results["7_reqExecutions"] = "PASS"
else:
print(" No SPY executions (expected if step 6 failed)")
results["7_reqExecutions"] = "FAIL (no executions)" if got_fill else "SKIP (no fill in step 6)"
# ══════════════════════════════════════════════════════════════════════
# Step 8: Place another LMT far from market, then cancelOrder
# ══════════════════════════════════════════════════════════════════════
print("\n=== Step 8: Place another LMT far, then cancelOrder ===")
w.got_status.clear()
w.got_cancelled.clear()
oid2 = alloc_id(w)
order2 = Order()
order2.action = "BUY"
order2.total_quantity = 100
order2.order_type = "LMT"
order2.lmt_price = round(price * 0.80, 2) # 20% below
order2.tif = "GTC"
order2.outside_rth = True
c.place_order(oid2, spy, order2)
assert w.got_status.wait(timeout=30), "No status for cancel-test order"
submitted = [s for s in w.order_statuses if s[0] == oid2 and s[1] == "Submitted"]
assert len(submitted) > 0, "Cancel-test order should be Submitted"
print(f" Order {oid2} submitted, permId={w.perm_ids.get(oid2, 0)}")
c.cancel_order(oid2, "")
got_cancel = w.got_cancelled.wait(timeout=15)
if got_cancel:
cancel_statuses = [s for s in w.order_statuses if s[0] == oid2 and s[1] == "Cancelled"]
assert len(cancel_statuses) > 0, "Should have Cancelled status"
print(f" Order {oid2} cancelled OK")
results["8_cancel"] = "PASS"
else:
results["8_cancel"] = "FAIL (cancel not confirmed within 15s)"
# ══════════════════════════════════════════════════════════════════════
# Cleanup: flatten any position from step 6
# ══════════════════════════════════════════════════════════════════════
if filled_qty > 0:
print(f"\n=== Cleanup: Sell {filled_qty} to flatten ===")
oid3 = alloc_id(w)
sell = Order()
sell.action = "SELL"
sell.total_quantity = filled_qty
sell.order_type = "MKT"
sell.tif = "DAY"
c.place_order(oid3, spy, sell)
time.sleep(5)
c.disconnect()
t.join(timeout=5)
# ══════════════════════════════════════════════════════════════════════
# Summary
# ══════════════════════════════════════════════════════════════════════
print("\n" + "=" * 60)
print("RESULTS SUMMARY")
print("=" * 60)
for step, result in results.items():
mark = "PASS" if result == "PASS" else "FAIL"
print(f" [{mark}] {step}: {result}")
passed = sum(1 for r in results.values() if r == "PASS")
total = len(results)
print(f"\n {passed}/{total} steps passed")
print("=" * 60)
if passed == total:
print("\n✓ Example complete — all steps passed")
else:
print(f"\n✗ Example complete — {total - passed} step(s) failed")
sys.exit(1)
if __name__ == "__main__":
run_example()
Login
The smallest program that opens a session: connect, read the order id this client would place under, disconnect.
Two flavours are shown. Paper for everyday testing, live for read-only checks against the real account.
What this shows
- Reading credentials from environment variables into
EClientConfig. paper: truefor the paper account,paper: falsefor the live one.client.account_id, which names the account the session opened under.req_ids, which reports the next order id.
What comes back
EClient::connect blocks until the session is up. When it returns, the session
is established and client.account_id is populated. There is nothing further to
wait for.
connect states the next order id once, as a gateway does: the first
process_msgs read after the venue has named what the account is working calls
next_valid_id on your wrapper, with one past the highest id the account is
working an order under. req_ids states it again without asking the venue
anything. The venue names that working set unprompted at every connect, from
every session, not only this one. Nothing is reserved by either; the
reservation happens when you place.
Limits
A live login may push a second factor. Approve it on your authenticator; the
connect call blocks until the gate clears, so give it a longer wall clock than
a paper login needs.
The live example is read-only: it logs in, prints, and disconnects. Send orders from the paper account.
Paper
Run it
IB_USERNAME=... IB_PASSWORD=... cargo run --example hello_login
Source
//! Hello-world recipe: connect, request next_valid_id, disconnect.
//!
//! Usage: IB_USERNAME=... IB_PASSWORD=... cargo run --example hello_login
use std::env;
use ib_dx::api::client::{EClient, EClientConfig};
use ib_dx::api::wrapper::Wrapper;
#[derive(Default)]
struct LoginWrapper {
next_id: Option<i64>,
}
impl Wrapper for LoginWrapper {
fn next_valid_id(&mut self, order_id: i64) {
self.next_id = Some(order_id);
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = EClient::connect(&EClientConfig {
username: env::var("IB_USERNAME")?,
password: env::var("IB_PASSWORD")?,
paper: true,
..Default::default()
})?;
let mut wrapper = LoginWrapper::default();
client.req_ids(1); client.process_msgs(&mut wrapper);
let next_id = wrapper.next_id.ok_or("did not receive next_valid_id")?;
println!("logged in. account = {}, next_valid_id = {next_id}", client.account_id);
client.disconnect();
Ok(())
}
Live
Run it
IB_LIVE_USERNAME=... IB_LIVE_PASSWORD=... cargo run --example hello_login_live
Source
//! Live-account login recipe: connect with `paper: false`, request next_valid_id,
//! Disconnect. Read-only — no orders, no market data.
//!
//! When the live login triggers a second-factor push, approve it on your mobile
//! Authenticator. The connect call blocks until the gate clears.
//!
//! Usage: IB_LIVE_USERNAME=... IB_LIVE_PASSWORD=... cargo run --example hello_login_live
use std::env;
use ib_dx::api::client::{EClient, EClientConfig};
use ib_dx::api::wrapper::Wrapper;
#[derive(Default)]
struct LoginWrapper {
next_id: Option<i64>,
}
impl Wrapper for LoginWrapper {
fn next_valid_id(&mut self, order_id: i64) {
self.next_id = Some(order_id);
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = EClient::connect(&EClientConfig {
username: env::var("IB_LIVE_USERNAME")?,
password: env::var("IB_LIVE_PASSWORD")?,
host: env::var("IB_HOST").unwrap_or_default(),
paper: false,
..Default::default()
})?;
let mut wrapper = LoginWrapper::default();
client.req_ids(1); client.process_msgs(&mut wrapper);
let next_id = wrapper.next_id.ok_or("did not receive next_valid_id")?;
println!("logged in LIVE. account = {}, next_valid_id = {next_id}", client.account_id);
client.disconnect();
Ok(())
}
Request Contract Details
Resolve a symbol: send a partial description, collect every contract the venue
lists under it, stop when the end marker lands. This is how you turn "AAPL"
into a con_id you can quote and trade on.
What this shows
- Building a partial
Contractfrom symbol, sec_type, exchange and currency. req_contract_details(req_id, &contract), and reading answers off thecontract_detailscallback.- Pumping the callbacks with
process_msgsuntilcontract_details_endfires. - Reading
con_id,primary_exchangeandtrading_classout ofContractDetails.
What comes back
One contract_details callback per matching contract, then one
contract_details_end for the request id. A description that matches several
listings answers with all of them, so rows.len() above one is an ambiguous
description rather than an error.
The shorter form
req_contract_details is the request-and-callback shape. Two blocking calls do
the same work without the wrapper:
let rows = client.contract_details(&aapl)?; // every match
let one = client.qualify_contract(&aapl)?; // the single match
qualify_contract refuses an ambiguous description rather than handing back
whichever the venue listed first, which would be a different contract from the
one you asked about. Nothing that answers either call reaches the wrapper
process_msgs is handed, even what arrives after the call has returned.
Lookups by contract id
A lookup by con_id includes the requested exchange. Leaving the exchange
empty asks for the preferred definition. The same rule applies when a market
data request needs to name a contract given only by id.
Contract details reuse exchange rule scopes already returned during the session. A first lookup can still need a separate request for each missing exchange; later lookups reuse those answers. CORPACT is excluded because it does not name a trading venue.
Run it
IB_USERNAME=... IB_PASSWORD=... cargo run --example hello_contract_details
Source
//! Recipe: req_contract_details for AAPL on paper, print con_id and primary exchange.
//!
//! Usage: IB_USERNAME=... IB_PASSWORD=... cargo run --example hello_contract_details
use std::env;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use ib_dx::api::client::{Contract, EClient, EClientConfig};
use ib_dx::api::types::ContractDetails;
use ib_dx::api::wrapper::Wrapper;
#[derive(Default)]
struct State {
rows: Vec<ContractDetails>,
end_seen: bool,
}
struct DetailsWrapper {
state: Arc<Mutex<State>>,
}
impl Wrapper for DetailsWrapper {
fn contract_details(&mut self, _req_id: i64, details: &ContractDetails) {
self.state.lock().unwrap().rows.push(details.clone());
}
fn contract_details_end(&mut self, _req_id: i64) {
self.state.lock().unwrap().end_seen = true;
}
fn error(&mut self, req_id: i64, _error_time: i64, code: i64, msg: &str, _adv: &str) {
eprintln!("[error] req_id={req_id} code={code} msg={msg}");
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = EClient::connect(&EClientConfig {
username: env::var("IB_USERNAME")?,
password: env::var("IB_PASSWORD")?,
paper: true,
..Default::default()
})?;
let state = Arc::new(Mutex::new(State::default()));
let mut wrapper = DetailsWrapper { state: state.clone() };
let aapl = Contract {
symbol: "AAPL".into(),
sec_type: "STK".into(),
exchange: "SMART".into(),
currency: "USD".into(),
..Default::default()
};
client.req_contract_details(1, &aapl);
let deadline = Instant::now() + Duration::from_secs(15);
while Instant::now() < deadline {
client.process_msgs(&mut wrapper);
if state.lock().unwrap().end_seen { break; }
std::thread::sleep(Duration::from_millis(20));
}
let s = state.lock().unwrap();
println!("matches: {}", s.rows.len());
for d in &s.rows {
println!(
" con_id={:>8} primary={:<10} trading_class={}",
d.contract.con_id, d.contract.primary_exchange, d.contract.trading_class,
);
}
drop(s);
client.disconnect();
Ok(())
}
Historical Bars
Fetch one trading day of 5-minute SPY bars and print the first and last. Use this whenever you need a finished series rather than a live feed.
What this shows
- Building a
Contractthat carriescon_id, so the venue is not asked to guess which listing you meant. req_historical_data, which takes every argument the request carries:req_id, contract,end_date_time,duration,bar_size,what_to_show,use_rth,format_date,keep_up_to_date.- Pumping the callbacks with
process_msgsuntilhistorical_data_endlands, or the request's refusal does. - Reading OHLCV out of
BarData.
An empty end_date_time means now. use_rth: true keeps the series inside
regular hours. format_date: 1 asks for the date written out rather than as
seconds. keep_up_to_date: false asks for closed bars only.
What comes back
One historical_data callback per bar, in time order, then one
historical_data_end carrying the first and last timestamps of the range that
was served. A request whose bars are refused once it has been handed over is
told why on error, and nothing follows it: a gateway states no end after a
refusal, so a program waiting for the end stops on the error too. A request
kept up to date whose five-second stream the venue refuses is told nothing, as
a gateway tells it nothing: a history still to come arrives with its end, and
no update follows. When the historical connection drops, a request kept up to
date ends on 10182, and one still arriving is told 165 and asked again once the
connection is back, as a gateway does. A 165 is a notice and ends nothing: the
call that waits for the bars waits through it.
With keep_up_to_date: true the bar still forming goes on from the history's
last bar, as a gateway's does: the live five-second bars inside it are folded
into the open, high, low and volume the venue stated for it. Five-second bars
are sent as the stream sends them. One that arrives before the history is in
is held, as a gateway holds it, and folded into the history's last bar with the
next one after it; one from before the bar in hand opened is passed over. So
nothing of the bar still forming is heard before historical_data_end, and
every update is heard on historical_data_update, whatever else the request's
number has done: a live bar stream under the same number is heard on
real_time_bar, and a second request under the number, refused while this
one answers, leaves this one's dates as it asked for them. A daily session supplied with the history
keeps its opening and end across UTC midnight. Its updates use the session
end's date on the series' timezone, as the history does, under both
date-format settings. Timed daily history is returned as yyyyMMdd; explicit
weekly and monthly date strings are preserved.
When that session ends, the next five-second bar opens the next bar at midnight UTC of its own day, as a gateway opens it. That bar ends at the next midnight UTC, or at the close the history stated where that falls between the two, and is dated by where it ends on the series' timezone; a bar that has ended is opened again by the next five-second bar. So a session that opens before midnight UTC, as a future's evening session does, is dated by the day the history closed until midnight UTC, and by the next one after it. On a timezone east of UTC the next midnight UTC falls on the following day there, so the bar a session there rolls over to is dated by the day after that session, as a gateway dates it. Intraday bars open on whole multiples of their length from the epoch; a week opens on Monday and a month on its first day at midnight UTC. Updates to these calendar bars remain dates under both date-format settings.
The shorter form
Two blocking calls do the same request without a wrapper:
let bars = client.historical_data(&spy, "", "1 D", "5 mins", "TRADES", true)?;
let same = client.bars(&spy, "1 D", "5 mins")?;
bars fills in the arguments most callers state the same way: trades, regular
hours, ending now. It asks for MIDPOINT instead of TRADES on instruments that
are quoted rather than traded (CASH, CFD, CMDTY), which answer a TRADES
request with no history at all.
Cancelling historical data also withdraws the corporate-actions query held by that request. A separate corporate-actions request keeps running, even when it uses the same caller request id.
Limits
bar_size and what_to_show are checked before anything is sent, so a
misspelling is refused here rather than answered with a different series. With
keep_up_to_date: true the size must be one this client can form from
the five-second bars the venue keeps sending: five seconds up to a day in
whole multiples of five seconds, a week, or a month. A second is shorter than
what arrives and is refused. A request kept up to date is refused, as a gateway
refuses it, with an end date, on a combination, or on a series other than
TRADES, MIDPOINT, BID, ASK and the two option open-interest series — an empty
series name included.
How far back a series goes, and which bar sizes pair with which durations, are
the venue's rules. A request outside them comes back as a stated refusal on the
error callback rather than as empty bars.
Run it
IB_USERNAME=... IB_PASSWORD=... cargo run --example hello_bar_data
Source
//! Recipe: fetch 1 day of 5-minute SPY bars, print first/last bar.
//!
//! Usage: IB_USERNAME=... IB_PASSWORD=... cargo run --example hello_bar_data
use std::env;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use ib_dx::api::client::{Contract, EClient, EClientConfig};
use ib_dx::api::types::BarData;
use ib_dx::api::wrapper::Wrapper;
#[derive(Default)]
struct State {
bars: Vec<BarData>,
done: bool,
}
struct BarsWrapper {
state: Arc<Mutex<State>>,
}
impl Wrapper for BarsWrapper {
fn historical_data(&mut self, _req_id: i64, bar: &BarData) {
self.state.lock().unwrap().bars.push(bar.clone());
}
fn historical_data_end(&mut self, _req_id: i64, _start: &str, _end: &str) {
self.state.lock().unwrap().done = true;
}
fn error(&mut self, req_id: i64, _error_time: i64, code: i64, msg: &str, _adv: &str) {
eprintln!("[error] req_id={req_id} code={code} msg={msg}");
// A refused request is told the error alone: no end follows it.
if req_id == 1 {
self.state.lock().unwrap().done = true;
}
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = EClient::connect(&EClientConfig {
username: env::var("IB_USERNAME")?,
password: env::var("IB_PASSWORD")?,
paper: true,
..Default::default()
})?;
let state = Arc::new(Mutex::new(State::default()));
let mut wrapper = BarsWrapper { state: state.clone() };
let spy = Contract {
con_id: 756733,
symbol: "SPY".into(),
sec_type: "STK".into(),
exchange: "SMART".into(),
currency: "USD".into(),
..Default::default()
};
client.req_historical_data(1, &spy, "", "1 D", "5 mins", "TRADES", true, 1, false);
let deadline = Instant::now() + Duration::from_secs(30);
while Instant::now() < deadline {
client.process_msgs(&mut wrapper);
if state.lock().unwrap().done { break; }
std::thread::sleep(Duration::from_millis(20));
}
let s = state.lock().unwrap();
println!("bars: {}", s.bars.len());
if let (Some(first), Some(last)) = (s.bars.first(), s.bars.last()) {
println!(" first: {} O={} H={} L={} C={} V={}",
first.date, first.open, first.high, first.low, first.close, first.volume);
println!(" last : {} O={} H={} L={} C={} V={}",
last.date, last.open, last.high, last.low, last.close, last.volume);
}
drop(s);
client.disconnect();
Ok(())
}
Streaming Ticks
Subscribe to top-of-book on SPY, collect for five seconds, print the latest bid, ask and last.
What this shows
req_mkt_data(req_id, &contract, generic_tick_list, snapshot, regulatory_snapshot, mkt_data_options).- Reading prices off
tick_price, wheretick_typenames which price it is: 1 bid, 2 ask, 4 last, 9 close. cancel_mkt_data(req_id)before disconnecting.
What comes back
tick_req_params (tickReqParams in Python) states the contract's price
increment, exchange and snapshot permission once per request, with the first
record the request is served and ahead of its ticks, as a gateway states it; a
request served no record is told nothing. It reads the latest stored
parameters when the callback is delivered. Where the venue names
no best-bid-and-offer exchange, as for a currency, a crypto or a bond, the
exchange is empty. The permission 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. On a
closed market whose frozen quote is served, that is the record the parameters
go out with, so the permission is 0. A follower sharing the subscription gets
its own callback; cancelling and reusing its request id starts a new request. A
request cancelled before delivery receives no callback.
tick_price and tick_size for as long as the subscription runs.
tick_generic fires for the halt state the venue states on tick 49: 0 while the
contract is trading, 1 once it has stopped.
With snapshot: true you get the first available quote, then
tick_snapshot_end, and this client cancels the subscription for you. That is
this client ending a subscription, not a separate request.
regulatory_snapshot: true is the venue's own one-shot snapshot. It is a
different request type, and an account without the
entitlement is refused by the venue. It also ends on tick_snapshot_end.
Limits
Each number in generic_tick_list is asked for, as a subscription of its own
beside the prices. The number you state is the number the venue knows the series
by, so there is nothing to translate. "292" additionally subscribes to news for
that contract, and "292:BRFG+DJNL" asks for it from the providers it names; an
entry that is not a number is reported rather than sent.
Every number the protocol will accept in that list is read here, and each arrives under the number the reference client publishes it under:
| you ask for | you receive |
|---|---|
100 | call and put option volume, on tick_size 29 and 30 |
101 | call and put open interest, on tick_size 27 and 28 |
104 512 | historical volatility, on tick_generic 23 |
105 | average option volume, the two sides added, on tick_size 87 |
106 | option implied volatility, on tick_generic 24 |
162 | the premium of an index over the future written on it, on tick_generic 31 |
165 | the day's volume and the 13, 26 and 52-week extremes, on tick_size 21 and tick_price 15 to 20 |
220 | the mark the venue keeps, on tick_price 78 |
221 232 | the mark, on tick_price 37 |
225 | auction volume and imbalance on tick_size 34 and 36, auction price on tick_price 35, the regulatory imbalance on tick_size 61 |
233 | the trade tape, on tick_string 48: price;size;time;volume;vwap;single |
236 | shortability on tick_generic 46, and the borrowable share count on tick_size 89 |
258 | company ratios, on tick_string 47 |
292 292:BRFG+DJNL | news, on tick_news |
293 294 295 | trade count, trade rate and volume rate, on tick_generic 54, 55 and 56 |
318 | last regular-session trade, on tick_price 57 |
375 | the trade-report tape, on tick_string 77 |
411 | real-time historical volatility, on tick_generic 58 |
456 | what the contract pays out, on tick_string 59 |
460 | bond factor multiplier, on tick_generic 60 |
499 | borrow fee rate, on tick_price 111 |
577 623 | an ETF's net asset value, last and frozen, on tick_price 96 and 97 |
586 | the estimated IPO midpoint on tick_generic 101, and what it opened at on 102 |
588 | futures open interest, on tick_size 86 |
595 | the 3, 5 and 10-minute volumes, on tick_size 63, 64 and 65 |
614 | an ETF's net asset value high and low, on tick_price 98 and 99 |
619 | the slow mark, on tick_price 79 |
787 | the odd lot: both prices on tick_price 105 and 106, their sizes on tick_size 107 and 108, where each is quoted on tick_string 109 and 110 |
Any other number is asked for the same way, as a series of its own. What the venue states on series the TWS API has no tick for is read through the calls under Beyond the documented API.
One subscription per contract. To change the mode on a contract, cancel first.
Every subscription starts live, whatever the type set on
req_market_data_type. As a gateway takes the types, 2 turns frozen data on,
3 and 4 turn delayed data on (4 with delayed-frozen, 3 without), and only 1
turns frozen data off, so a 2 asked after a 3 or a 4 still leaves the delayed
feed to fall back to. With delayed data on, a bid/ask refusal that states
delayed data is available switches to the delayed feed and reports 10167
without ending the request, and market_data_type reports 3. With nothing to
fall back to, the refusal ends the request as a gateway ends it: under 354,
"Requested market data is not subscribed.", followed by "Delayed market data
is available." where the refusal says so; or under 10089, where the services
the refusal names for the API reach past those it names for the data. The
venue's own words follow either, as sent. Under type 2 alone the request ends
there, and no frozen quote is asked for.
Where the logon enables frozen data, the market's status is watched, as a gateway watches it, for a subscription made with frozen data on while it is served live, and for one that has fallen back to delayed data with delayed-frozen data on. While the status says the market is closed, the frozen quote is asked for beside the live one and served, reported as 2; beside a delayed one the delayed-frozen quote is asked for too, and served from its first record, reported as 4, the delayed one until then. The quote asked for first is kept up meanwhile, and once the status says the market is open the frozen quotes are withdrawn and that quote is served again as it stands, reported as 1 or 3. A refusal of the live quote ends the watch on it without a word: the frozen quote beside it is withdrawn, and so is the status, unless the fallback watches it. So a login entitled to live data is served live under type 4 alone, and on a closed market its quote states what the live feed states, zero yields included; under type 2, alone or before a 4, it is served the frozen quote. A calculation's own subscription falls back to nothing: it asks the delayed feed directly where delayed data is on, and watches no status.
req_mkt_data_ex selects a feed directly: mode_9887 is 0 realtime, 1 delayed,
2 frozen, or 3 delayed-frozen. Frozen keeps thinly traded names quoting after
hours, when the realtime feed is silent.
A refusal naming several requests reaches each affected caller. The bid/ask and last entries of one quote produce one subscription error. Cancelling a tick-by-tick stream uses its query identifier before acknowledgement and its assigned stream identifier afterwards; another caller sharing that stream keeps receiving data until it cancels too.
Both quote requests take mkt_data_options: &[TagValue], in
req_mkt_data_ex after mode_9887. Pass &[] when there are no options.
With no session, both calls report 504, Not connected, before checking the
request.
The list accepts manual=0 or manual=1. An unknown key is refused under
10337, an invalid value under 10338, and a malformed list under 320.
NOAPIMISCVLD at logon lifts the key and value checks; the later check that
manual is zero or one remains.
Reading without the callback loop
quote(req_id) returns the latest bid, ask, last and sizes for a running
subscription, with no wrapper and no lock. quote_of(&contract) does the same
by contract. Both return None until the first tick has landed.
Prices and sizes on Quote are integers scaled by PRICE_SCALE and QTY_SCALE
(both 108). Divide when you display; the tick_price callback hands
you f64 already.
Run it
IB_USERNAME=... IB_PASSWORD=... cargo run --example hello_tick_data
Source
//! Recipe: stream SPY market data for 5 seconds, print latest bid/ask/last.
//!
//! Usage: IB_USERNAME=... IB_PASSWORD=... cargo run --example hello_tick_data
use std::env;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use ib_dx::api::client::{Contract, EClient, EClientConfig};
use ib_dx::api::types::TickAttrib;
use ib_dx::api::wrapper::Wrapper;
#[derive(Default)]
struct State {
bid: f64,
ask: f64,
last: f64,
ticks: u64,
}
struct TickWrapper {
state: Arc<Mutex<State>>,
}
impl Wrapper for TickWrapper {
fn tick_price(&mut self, _req_id: i64, tick_type: i32, price: f64, _: &TickAttrib) {
let mut s = self.state.lock().unwrap();
s.ticks += 1;
match tick_type {
1 => s.bid = price,
2 => s.ask = price,
4 => s.last = price,
_ => {}
}
}
fn error(&mut self, req_id: i64, _error_time: i64, code: i64, msg: &str, _adv: &str) {
if !matches!(code, 2104 | 2106 | 2158) {
eprintln!("[error] req_id={req_id} code={code} msg={msg}");
}
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = EClient::connect(&EClientConfig {
username: env::var("IB_USERNAME")?,
password: env::var("IB_PASSWORD")?,
paper: true,
..Default::default()
})?;
let state = Arc::new(Mutex::new(State::default()));
let mut wrapper = TickWrapper { state: state.clone() };
let spy = Contract {
con_id: 756733,
symbol: "SPY".into(),
sec_type: "STK".into(),
exchange: "SMART".into(),
currency: "USD".into(),
..Default::default()
};
let req_id = 1;
println!("streaming SPY for 5s…");
client.req_mkt_data(req_id, &spy, "", false, false, &[]);
let deadline = Instant::now() + Duration::from_secs(5);
while Instant::now() < deadline {
client.process_msgs(&mut wrapper);
std::thread::sleep(Duration::from_millis(20));
}
client.cancel_mkt_data(req_id);
let s = state.lock().unwrap();
println!("ticks: {} bid={:.2} ask={:.2} last={:.2}", s.ticks, s.bid, s.ask, s.last);
drop(s);
client.disconnect();
Ok(())
}
Streaming L2 Market Depth
Subscribe to Level-2 depth on two contracts at once, keep a bid and ask book per contract from the update stream, and print both books at the end.
What this shows
req_mkt_depth(req_id, &contract, num_rows, is_smart_depth), one request id per contract.- Applying
update_mkt_depth_l2events to a book keyed by level position. cancel_mkt_depth(req_id, is_smart_depth)for each subscription before disconnecting, naming the mode the book was asked in.
What comes back
update_mkt_depth_l2 per level change, carrying the level position, the market
maker, and:
| field | values |
|---|---|
operation | 0 insert, 1 update, 2 delete |
side | 0 ask, 1 bid |
A level carries the maker the venue names on it. An exchange-level book often
names none — many venues quote no makers — and then the field is empty; on the
aggregated book the exchange stands in where the venue names no maker. That is
why the example keeps market_maker alongside price and size.
The example collects for the whole window and prints each book once at the end,
not on every update. DURATION_SECS sets that window; the default is 15.
After a reconnect the venue restates the book from the top. The client reports
that on error as 317, the TWS API's notice that a book was reset, before the
first new level arrives — as a gateway does. Empty the book on it, or the
levels that follow land on top of the old ones.
Limits
A depth subscription the venue will not serve is refused, and the refusal
arrives on the error callback. A book that stays empty means the subscription
never started, not that the market is empty.
The example asserts at the end that both books received updates and that neither is empty, so it panics rather than reporting success on a feed that never delivered. Run it while the market is open.
Run it
IB_USERNAME=... IB_PASSWORD=... cargo run --example l2_aapl_tsla
Collect for longer:
DURATION_SECS=30 cargo run --example l2_aapl_tsla
Source
//! Subscribe to L2 market depth for AAPL + TSLA and print the order book.
//!
//! Usage:
//! IB_USERNAME=user IB_PASSWORD=pass cargo run --example l2_aapl_tsla
//!
//! Optional env vars:
//! IB_HOST — the venue host to log in to (default: the venue names it)
//! DURATION_SECS — how long to collect data (default: 15)
use std::collections::HashMap;
use std::env;
use std::time::{Duration, Instant};
use ib_dx::api::client::{EClient, EClientConfig, Contract};
use ib_dx::api::wrapper::Wrapper;
// ── Book entry ──
#[derive(Debug, Clone)]
struct BookLevel {
price: f64,
size: f64,
market_maker: String,
}
// ── Per-ticker book ──
struct TickerBook {
symbol: String,
bids: HashMap<i32, BookLevel>, // position → level
asks: HashMap<i32, BookLevel>,
update_count: u64,
}
impl TickerBook {
fn new(symbol: &str) -> Self {
Self {
symbol: symbol.into(),
bids: HashMap::new(),
asks: HashMap::new(),
update_count: 0,
}
}
fn apply(&mut self, position: i32, market_maker: &str, operation: i32, side: i32, price: f64, size: f64) {
self.update_count += 1;
let book = if side == 1 { &mut self.bids } else { &mut self.asks };
match operation {
0 | 1 => { book.insert(position, BookLevel { price, size, market_maker: market_maker.into() }); }
2 => { book.remove(&position); }
_ => {}
}
}
fn print_summary(&self) {
let mut bids: Vec<_> = self.bids.values().collect();
let mut asks: Vec<_> = self.asks.values().collect();
bids.sort_by(|a, b| b.price.partial_cmp(&a.price).unwrap());
asks.sort_by(|a, b| a.price.partial_cmp(&b.price).unwrap());
println!("\n── {} ── ({} updates)", self.symbol, self.update_count);
println!(" Top 5 Asks:");
for level in asks.iter().take(5).rev() {
println!(" {:>10.2} x {:<10.0} {}", level.price, level.size, level.market_maker);
}
println!(" ─────────────────────────────");
println!(" Top 5 Bids:");
for level in bids.iter().take(5) {
println!(" {:>10.2} x {:<10.0} {}", level.price, level.size, level.market_maker);
}
}
}
// ── Wrapper that collects depth for 2 tickers ──
struct DepthWrapper {
books: HashMap<i64, TickerBook>, // req_id → book
errors: Vec<(i64, i64, String)>,
}
impl DepthWrapper {
fn new(tickers: &[(i64, &str)]) -> Self {
let mut books = HashMap::new();
for &(req_id, symbol) in tickers {
books.insert(req_id, TickerBook::new(symbol));
}
Self { books, errors: Vec::new() }
}
fn total_updates(&self) -> u64 {
self.books.values().map(|b| b.update_count).sum()
}
}
impl Wrapper for DepthWrapper {
fn error(&mut self, req_id: i64, _error_time: i64, error_code: i64, error_string: &str, _: &str) {
eprintln!(" error req_id={req_id} code={error_code} msg={error_string}");
self.errors.push((req_id, error_code, error_string.into()));
}
fn update_mkt_depth(
&mut self, req_id: i64, position: i32, operation: i32,
side: i32, price: f64, size: ib_dx::api::Decimal,
) {
if let Some(book) = self.books.get_mut(&req_id) {
book.apply(position, "", operation, side, price, size.into());
}
}
fn update_mkt_depth_l2(
&mut self, req_id: i64, position: i32, market_maker: &str,
operation: i32, side: i32, price: f64, size: ib_dx::api::Decimal, _is_smart_depth: bool,
) {
if let Some(book) = self.books.get_mut(&req_id) {
book.apply(position, market_maker, operation, side, price, size.into());
}
}
}
// ── Contracts ──
fn aapl() -> Contract {
Contract {
con_id: 265598,
symbol: "AAPL".into(),
sec_type: "STK".into(),
exchange: "SMART".into(),
currency: "USD".into(),
..Default::default()
}
}
fn tsla() -> Contract {
Contract {
con_id: 76792991,
symbol: "TSLA".into(),
sec_type: "STK".into(),
exchange: "SMART".into(),
currency: "USD".into(),
..Default::default()
}
}
fn main() {
let username = env::var("IB_USERNAME").expect("IB_USERNAME required");
let password = env::var("IB_PASSWORD").expect("IB_PASSWORD required");
let host = env::var("IB_HOST").unwrap_or_default();
let duration_secs: u64 = env::var("DURATION_SECS")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(15);
println!("Connecting to {host}...");
let client = EClient::connect(&EClientConfig {
username,
password,
host,
paper: true,
..Default::default()
}).expect("Failed to connect");
println!("Connected.");
// Subscribe to L2 depth for both tickers (5 rows, SmartDepth)
let num_rows = 5;
let is_smart = true;
let aapl_id: i64 = 1;
let tsla_id: i64 = 2;
println!("Subscribing AAPL (req_id={aapl_id})");
client.req_mkt_depth(aapl_id, &aapl(), num_rows, is_smart);
println!("Subscribing TSLA (req_id={tsla_id})");
client.req_mkt_depth(tsla_id, &tsla(), num_rows, is_smart);
// Poll for updates
let mut wrapper = DepthWrapper::new(&[(aapl_id, "AAPL"), (tsla_id, "TSLA")]);
let start = Instant::now();
let timeout = Duration::from_secs(duration_secs);
println!("Collecting depth data for {duration_secs}s...");
while start.elapsed() < timeout {
client.process_msgs(&mut wrapper);
std::thread::sleep(Duration::from_millis(50));
}
// Print results
for book in wrapper.books.values() {
book.print_summary();
}
let total = wrapper.total_updates();
println!("\nTotal depth updates: {total}");
// Cancel subscriptions
client.cancel_mkt_depth(aapl_id, is_smart);
client.cancel_mkt_depth(tsla_id, is_smart);
// Validate
for (req_id, book) in &wrapper.books {
assert!(
book.update_count > 0,
"No depth updates received for {} (req_id={})", book.symbol, req_id
);
assert!(
!book.bids.is_empty() || !book.asks.is_empty(),
"Empty book for {} (req_id={})", book.symbol, req_id
);
println!("✓ {} — {} updates, {} bid levels, {} ask levels",
book.symbol, book.update_count, book.bids.len(), book.asks.len());
}
assert!(total > 0, "Expected depth updates but got none");
println!("\nAll validations passed.");
}
Market Scanner
Run the TOP_PERC_GAIN scan over US major stocks, filtered to names above $5,
and print the top ten.
What this shows
req_scanner_subscription(req_id, instrument, location_code, scan_code, max_items, filters, scanner_setting_pairs), withscanner_setting_pairs""when there are none.- Filters as
TagValuepairs. The tag names are the onesreq_scanner_parameterspublishes, for examplepriceAbove="5"orstkTypes="inc:ETF". They are carried to the venue, not dropped. - Reading
scanner_datarows, each a rank and aContractDetails, untilscanner_data_end. cancel_scanner_subscription(req_id)before disconnecting.
What comes back
One scanner_data callback per row, then scanner_data_end for the request id.
Rows are ranked; the example sorts by rank before printing because it collects
them into a Vec first.
A scanner subscription keeps answering until it is cancelled. The example cancels; if you leave one running it goes on delivering into a session nobody is reading.
The shorter form
scan runs the scan, waits, and withdraws the subscription before returning:
let rows = client.scan("STK", "STK.US.MAJOR", "TOP_PERC_GAIN", 25)?;
It sends no filters. Use req_scanner_subscription when the scan needs them.
Limits
Which scan codes and locations are valid is the venue's list.
req_scanner_parameters fetches it, and an unknown code comes back as a stated
refusal on the error callback. When the historical connection drops, a
running scan is told 165 as it drops, at each attempt to bring it back that
fails and once it is back, and is then subscribed again and goes on under its
number, as a gateway does; a 165 ends nothing.
Run it
IB_USERNAME=... IB_PASSWORD=... cargo run --example hello_scanner
Source
//! Recipe: subscribe to TOP_PERC_GAIN scanner, print first 10 results.
//!
//! Usage: IB_USERNAME=... IB_PASSWORD=... cargo run --example hello_scanner
use std::env;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use ib_dx::api::client::{EClient, EClientConfig};
use ib_dx::api::types::{ContractDetails, TagValue};
use ib_dx::api::wrapper::Wrapper;
#[derive(Default)]
struct State {
rows: Vec<(i32, ContractDetails)>,
end_seen: bool,
}
struct ScannerWrapper {
state: Arc<Mutex<State>>,
}
impl Wrapper for ScannerWrapper {
fn scanner_data(
&mut self, _req_id: i64, rank: i32, details: &ContractDetails,
_distance: &str, _benchmark: &str, _projection: &str, _legs: &str,
) {
self.state.lock().unwrap().rows.push((rank, details.clone()));
}
fn scanner_data_end(&mut self, _req_id: i64) {
self.state.lock().unwrap().end_seen = true;
}
fn error(&mut self, req_id: i64, _error_time: i64, code: i64, msg: &str, _adv: &str) {
if !matches!(code, 2104 | 2106 | 2158) {
eprintln!("[error] req_id={req_id} code={code} msg={msg}");
}
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = EClient::connect(&EClientConfig {
username: env::var("IB_USERNAME")?,
password: env::var("IB_PASSWORD")?,
paper: true,
..Default::default()
})?;
let state = Arc::new(Mutex::new(State::default()));
let mut wrapper = ScannerWrapper { state: state.clone() };
let req_id = 1;
println!("subscribing TOP_PERC_GAIN, STK.US.MAJOR…");
let filters = [TagValue { tag: "priceAbove".into(), value: "5".into() }];
client.req_scanner_subscription(req_id, "STK", "STK.US.MAJOR", "TOP_PERC_GAIN", 25, &filters, "");
let deadline = Instant::now() + Duration::from_secs(30);
while Instant::now() < deadline {
client.process_msgs(&mut wrapper);
if state.lock().unwrap().end_seen { break; }
std::thread::sleep(Duration::from_millis(20));
}
let mut s = state.lock().unwrap();
s.rows.sort_by_key(|(rank, _)| *rank);
println!("results: {}", s.rows.len());
for (rank, d) in s.rows.iter().take(10) {
println!(" #{rank:<3} {:<8} {:<6} con_id={}", d.contract.symbol, d.contract.primary_exchange, d.contract.con_id);
}
drop(s);
client.cancel_scanner_subscription(req_id);
client.disconnect();
Ok(())
}
Request Account PnL
Subscribe to the account-level PnL stream, take the first update, cancel and disconnect.
What this shows
- Reading the account name off
client.account_id, whichconnectpopulates. req_pnl(req_id, account, model_code).- Reading
daily_pnl,unrealized_pnlandrealized_pnloff thepnlcallback. cancel_pnl(req_id)before disconnecting.
account empty means the account this session opened under.
What comes back
The venue states what each holding was worth at midnight and what it has
realised since. The three figures on the pnl callback are worked out from
those against the prices this session is being told, so they move as the
session's quotes move.
Limits
account is checked as a gateway checks it, in its order and words: a blank
one is refused (Account must not be empty, 321); one the login does not hold
is refused (Invalid account code, 321), and so is All on a login holding one
account or where the logon says the login may not ask for every account; All
on a login the venue adds accounts to is refused (This API request for All is
not supported for Dynamic Account Addition, 321). Another account the login
holds, and All where a gateway would take it, are refused too, on both
clients: the figures are worked out from one account's midnight seeds and
holdings, the one this session opened under. Nothing is subscribed for a
refused request.
model_code is taken and not applied. There is no model portfolio to name here,
so pass "".
cancel_pnl stops the updates. The venue has no message withdrawing the
subscription itself, on a gateway as here, so the updates stopping is what the
call does.
An account with no position reports zeros. Pair this with the limit-order recipe if you want to watch the numbers move.
Run it
IB_USERNAME=... IB_PASSWORD=... cargo run --example hello_pnl
Source
//! Recipe: subscribe to account-level PnL, take one update, then cancel.
//!
//! Usage: IB_USERNAME=... IB_PASSWORD=... cargo run --example hello_pnl
use std::env;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use ib_dx::api::client::{EClient, EClientConfig};
use ib_dx::api::wrapper::Wrapper;
#[derive(Default)]
struct State {
pnl: Option<(f64, f64, f64)>,
}
struct PnlWrapper {
state: Arc<Mutex<State>>,
}
impl Wrapper for PnlWrapper {
fn pnl(&mut self, _req_id: i64, daily: f64, unrealized: f64, realized: f64) {
self.state.lock().unwrap().pnl = Some((daily, unrealized, realized));
}
fn error(&mut self, req_id: i64, _error_time: i64, code: i64, msg: &str, _adv: &str) {
eprintln!("[error] req_id={req_id} code={code} msg={msg}");
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = EClient::connect(&EClientConfig {
username: env::var("IB_USERNAME")?,
password: env::var("IB_PASSWORD")?,
paper: true,
..Default::default()
})?;
let account = client.account_id.clone();
println!("account: {account}");
let state = Arc::new(Mutex::new(State::default()));
let mut wrapper = PnlWrapper { state: state.clone() };
let req_id = 1;
client.req_pnl(req_id, &account, "");
let deadline = Instant::now() + Duration::from_secs(15);
while Instant::now() < deadline {
client.process_msgs(&mut wrapper);
if state.lock().unwrap().pnl.is_some() { break; }
std::thread::sleep(Duration::from_millis(20));
}
match state.lock().unwrap().pnl {
Some((daily, unrealized, realized)) => {
println!("daily={daily:.2} unrealized={unrealized:.2} realized={realized:.2}");
}
None => println!("no pnl update within 15s — try again with an open position"),
}
client.cancel_pnl(req_id);
client.disconnect();
Ok(())
}
Send a Limit Order
Place a BUY LMT on SPY far below market, watch it acknowledge, then cancel it. Connect, take an id, place, read status, cancel, disconnect.
What this shows
client.next_order_id(), which reserves one id.- An
Orderwithorder_type: "LMT"andlmt_priceset.outside_rth: trueis what lets it be acknowledged outside regular hours. order_statuscallbacks.PreSubmittedthenSubmittedwhile it rests,Cancelledonce the cancel lands.cancel_order(order_id, order_cancel). The second argument is what the withdrawal states about itself: anOrderCancel(who is withdrawing it, whether a person entered it, a manual time), or a time alone;""states nothing. The operator and who entered it travel on the cancel. A time does not, and the caller is told so onerror; a time a gateway cannot read is refused under 10301 and the order keeps working.
What comes back
order_status on every change and again on each fill. filled and remaining
are quantities, avg_fill_price is the average of what has filled so far. An
order that rests will sit at Submitted until you cancel it.
The perm_id on that callback is the number the order goes to the venue
under, as a gateway states it, which for this order is its own order id. It is
stated from the moment the order is sent and stays the same for the life of the
order, including across a modify.
Limits
next_order_id counts from one past the highest id the account is working an
order under, which the venue names at every connect, from every session. So it
does not collide with an order placed elsewhere.
tif is checked against what the venue carries: DAY, GTC, IOC, FOK,
OPG, GTD, GTX, DTC, AUC, NMIN, spelled exactly. An unrecognised value is
refused here rather than sent as DAY and quietly expiring at the close.
Paper account only. The price is set far below market so it will not fill.
The shorter form
place takes the id itself, sends the order, and waits up to five seconds for
the venue to settle it:
let report = client.place(&spy, &order)?;
println!("{} {} filled", report.status, report.filled);
It reports what is known either way. An order still working is reported as working; it is not cancelled.
Run it
IB_USERNAME=... IB_PASSWORD=... cargo run --example hello_limit_order
Source
//! Recipe: place a non-marketable BUY LMT on SPY, watch Submitted, then cancel.
//!
//! Usage: IB_USERNAME=... IB_PASSWORD=... cargo run --example hello_limit_order
use std::env;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use ib_dx::api::client::{Contract, EClient, EClientConfig, Order};
use ib_dx::api::wrapper::Wrapper;
#[derive(Default)]
struct State {
statuses: Vec<(i64, String)>,
}
struct OrderWrapper {
state: Arc<Mutex<State>>,
}
impl Wrapper for OrderWrapper {
fn order_status(
&mut self, order_id: i64, status: &str, _filled: ib_dx::api::Decimal, _remaining: ib_dx::api::Decimal,
_avg_fill: f64, _perm_id: i64, _parent_id: i64, _last_fill: f64,
_client_id: i64, _why_held: &str, _mkt_cap_price: f64,
) {
println!("[status] oid={order_id} status={status}");
self.state.lock().unwrap().statuses.push((order_id, status.into()));
}
fn error(&mut self, req_id: i64, _error_time: i64, code: i64, msg: &str, _adv: &str) {
eprintln!("[error] req_id={req_id} code={code} msg={msg}");
}
}
fn pump_until<F: Fn(&State) -> bool>(
client: &EClient, wrapper: &mut OrderWrapper, state: &Arc<Mutex<State>>,
timeout: Duration, done: F,
) -> bool {
let deadline = Instant::now() + timeout;
while Instant::now() < deadline {
client.process_msgs(wrapper);
if done(&state.lock().unwrap()) { return true; }
std::thread::sleep(Duration::from_millis(20));
}
false
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = EClient::connect(&EClientConfig {
username: env::var("IB_USERNAME")?,
password: env::var("IB_PASSWORD")?,
paper: true,
..Default::default()
})?;
let order_id = client.next_order_id();
let state = Arc::new(Mutex::new(State::default()));
let mut wrapper = OrderWrapper { state: state.clone() };
let spy = Contract {
con_id: 756733,
symbol: "SPY".into(),
sec_type: "STK".into(),
exchange: "SMART".into(),
currency: "USD".into(),
..Default::default()
};
let order = Order {
action: "BUY".into(),
total_quantity: 1.0,
order_type: "LMT".into(),
lmt_price: 1.00,
tif: "DAY".into(),
outside_rth: true,
..Default::default()
};
println!("placing BUY 1 SPY LMT 1.00 (oid={order_id})");
client.place_order(order_id, &spy, &order);
pump_until(&client, &mut wrapper, &state, Duration::from_secs(15),
|s| s.statuses.iter().any(|(id, st)| *id == order_id && st == "Submitted"));
println!("cancelling oid={order_id}");
client.cancel_order(order_id, "");
pump_until(&client, &mut wrapper, &state, Duration::from_secs(15),
|s| s.statuses.iter().any(|(id, st)| *id == order_id && st == "Cancelled"));
client.disconnect();
Ok(())
}
Send a Stop Order
Place a BUY STOP on SPY far above market. The trigger never fires, so the order rests. Watch it acknowledge, then cancel it.
What this shows
client.next_order_id(), which reserves one id.- An
Orderwithorder_type: "STP"and the trigger price inaux_price.lmt_priceis unused for a plain stop. tif: "GTC", so it survives the close rather than expiring with the day.order_statuscallbacks:PreSubmittedthenSubmittedwhile it rests,Cancelledonce the cancel lands.cancel_order(order_id, "").
What comes back
The same order_status stream as any other order. A resting stop shows no
fills: filled stays 0 and remaining stays at the full quantity until the
trigger is touched.
Limits
A stop is held until its trigger is touched, then goes to the market as a market order. This example sets the trigger far above market so that never happens. On a real stop, the fill price is whatever the market gives you at that moment, not the trigger.
Paper account only.
Run it
IB_USERNAME=... IB_PASSWORD=... cargo run --example hello_stop_order
Source
//! Recipe: place a BUY STP on SPY far above market, watch Submitted, then cancel.
//!
//! Usage: IB_USERNAME=... IB_PASSWORD=... cargo run --example hello_stop_order
use std::env;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use ib_dx::api::client::{Contract, EClient, EClientConfig, Order};
use ib_dx::api::wrapper::Wrapper;
#[derive(Default)]
struct State {
statuses: Vec<(i64, String)>,
}
struct OrderWrapper {
state: Arc<Mutex<State>>,
}
impl Wrapper for OrderWrapper {
fn order_status(
&mut self, order_id: i64, status: &str, _filled: ib_dx::api::Decimal, _remaining: ib_dx::api::Decimal,
_avg_fill: f64, _perm_id: i64, _parent_id: i64, _last_fill: f64,
_client_id: i64, _why_held: &str, _mkt_cap_price: f64,
) {
println!("[status] oid={order_id} status={status}");
self.state.lock().unwrap().statuses.push((order_id, status.into()));
}
fn error(&mut self, req_id: i64, _error_time: i64, code: i64, msg: &str, _adv: &str) {
eprintln!("[error] req_id={req_id} code={code} msg={msg}");
}
}
fn pump_until<F: Fn(&State) -> bool>(
client: &EClient, wrapper: &mut OrderWrapper, state: &Arc<Mutex<State>>,
timeout: Duration, done: F,
) -> bool {
let deadline = Instant::now() + timeout;
while Instant::now() < deadline {
client.process_msgs(wrapper);
if done(&state.lock().unwrap()) { return true; }
std::thread::sleep(Duration::from_millis(20));
}
false
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = EClient::connect(&EClientConfig {
username: env::var("IB_USERNAME")?,
password: env::var("IB_PASSWORD")?,
paper: true,
..Default::default()
})?;
let order_id = client.next_order_id();
let state = Arc::new(Mutex::new(State::default()));
let mut wrapper = OrderWrapper { state: state.clone() };
let spy = Contract {
con_id: 756733,
symbol: "SPY".into(),
sec_type: "STK".into(),
exchange: "SMART".into(),
currency: "USD".into(),
..Default::default()
};
let order = Order {
action: "BUY".into(),
total_quantity: 1.0,
order_type: "STP".into(),
aux_price: 9999.0,
tif: "GTC".into(),
outside_rth: true,
..Default::default()
};
println!("placing BUY 1 SPY STP 9999.00 (oid={order_id})");
client.place_order(order_id, &spy, &order);
pump_until(&client, &mut wrapper, &state, Duration::from_secs(15),
|s| s.statuses.iter().any(|(id, st)| *id == order_id && st == "Submitted"));
println!("cancelling oid={order_id}");
client.cancel_order(order_id, "");
pump_until(&client, &mut wrapper, &state, Duration::from_secs(15),
|s| s.statuses.iter().any(|(id, st)| *id == order_id && st == "Cancelled"));
client.disconnect();
Ok(())
}
Python API
ib_dx.EClient / ib_dx.EWrapper is the TWS API shape: a request under an id, an answer later on a callback.
The same EClient also carries the calls that answer and
the calls beyond the documented API.
On EClient, EWrapper and the records a call or callback hands over, both
naming conventions resolve: reqMktData and req_mkt_data, secType and
sec_type, conId and con_id. dir() lists both, so an editor or a notebook
completing from it offers either. The plain Python classes a program only
builds — ExecutionFilter, ScannerSubscription, WshEventData, OrderCancel,
RealTimeBar, ScanData, FamilyCode and HistoricalSession — carry the
reference client's spelling alone, as that client's own do; the family codes and
sessions a callback hands over answer to both.
A wrapper is called with the shape the ibapi release it was written for calls.
error receives reqId, errorCode, errorString where it declares three
parameters (ibapi 9.81), advancedOrderRejectJson after them where it declares
four, and reqId, errorTime, errorCode, errorString, advancedOrderRejectJson,
the current release's shape, otherwise. A wrapper whose class declares
commissionReport, the name before the fees were reported beside the
commission, and neither commissionAndFeesReport nor
commission_and_fees_report, receives each charge on commissionReport. Both
are read once, when EClient.__init__ binds the wrapper.
A new Order starts each figure the reference client's Order starts unset —
the limit and auxiliary prices, the trailing percentage, the cash quantity, the
trigger and adjusted prices, the filled quantity, the minimum quantity, the
volatility and reference price kinds — at UNSET_DOUBLE or UNSET_INTEGER, as
that client's does, so a program comparing with them reads what it would
there, and the Rust Order starts them at the same values. A price left there
is sent as none, as a gateway sends it; see
a price the order does not state.
ib_dx.configure() carries what a gateway would hold in a configuration file:
the session time zone, the build announced at logon, which executions arrive
when a session opens, and the rest. ib_dx.describe() lists them, and
ib_dx.UNAVAILABLE names the gateway settings that mean nothing without a local
process — a port to listen on, the addresses allowed to reach it, how much heap
the runtime may take — so a program being migrated finds an answer rather than
silence.
Settings given to connect(settings=...) belong to that session; those given to
configure() belong to the process and are what a session that states none
falls back to. They are read when a session opens, so set them before
connect().
order_id_file selects the durable counter shared by sessions with the same
account and API client ID; an empty path disables it. See
order IDs across sessions
for its default location, reservations and moving the file.
Logging is settled at import: a process has one logger and importing
ib_dx installs it, reading IB_DX_LOG_LEVEL (with or without a log
directory; one that is not a level is said in the log), IB_DX_LOG_DIR and
IB_DX_LOG_QUEUE. The level can be moved afterwards:
configure(log_level=...) and connect(settings={"log_level": ...}) move
the logger ib_dx installed, and raise where the program installed its
own; configure(log_level=None) or "" moves it back to the level it was
installed at. A connect refused for another of its settings leaves the level
where it was. Where the logger writes and how much it buffers are fixed once
it runs, so log_dir and log_queue belong in the environment before the
import, and both ways in refuse them afterwards.
The reference
Below is generated by scripts/gen_api_docs.py from the same source the Rust
surface is generated from. CI regenerates it on every push and fails if what is
committed has drifted.
Python API Reference (v0.1.0)
Auto-generated from source — do not edit.
Table of Contents
- EClient: Connection
- EClient: Calls That Answer
- EClient: Account & Portfolio
- EClient: Orders
- EClient: Market Data
- EClient: Reference Data
- EClient: Gateway-Local & Stubs
- EWrapper Callbacks
Connection
__init__
Bind the wrapper the callbacks are delivered to. EClient(wrapper) lands here through the interpreter; a subclass with a constructor of its own calls EClient.__init__(self, wrapper), as the reference sample does with wrapper=self. Bound once: a second, different wrapper is refused, since callbacks may already be on their way to the first.
def __init__(wrapper)
| Parameter | Type | Description |
|---|---|---|
wrapper | Py<PyAny> | Wrapper callback receiver for synchronous delivery. |
connect
Connect to IB and start the engine. Live logins (paper=False) enter a second-factor approval window and block until the factor is approved (mobile push) or the deadline fires (ib_key_timeout_secs, default ~18 min). This is a human approval gate, not a hang. To bound or avoid it: use paper=True, pass a smaller ib_key_timeout_secs, or run connect() on a worker thread with your own timeout. Paper logins skip the gate entirely. Set RUST_LOG=info to see a log line when the wait begins. code_provider answers that factor with a typed code instead: code_provider(factor, display_id, avth_url) -> str, where factor is "ibkey" (return the code shown for display_id) or "authenticator" (return the account's current code; display_id and avth_url are empty). An authenticator account has no push to fall back to and cannot log in without this. It is called once, on a thread of its own, and holds the GIL while it runs — return the code, don't block on input. It is asked once and the login carries whatever it returns; what the venue does with a wrong code has not been exercised from here. Multiple EClient instances can run concurrently in one process; each owns its own state, sockets, and engine thread, and connect() does not serialize across instances. If you pin engines via core_id, give each a distinct value. port is kept and read back on port, as the reference client keeps it. The session connects to the venue directly, so there is no local socket for it to open.
def connect(host, port=0, client_id=0, username="", password="", paper=True, core_id=None, ib_key_timeout_secs=None, ib_key_token_sub_type=None, code_provider=None, readonly=False, settings=None, session_file=None, *, clientId=None)
| Parameter | Type | Description |
|---|---|---|
host | str | Server hostname. |
port | int | Port number (unused — ib-dx connects directly). |
client_id | int | API client ID for order ownership and the saved order-id counter. |
username | str | Account username. |
password | str | Account password. |
paper | bool | If true, connect to paper trading. If false, connect blocks on the live second-factor approval window (see method note). |
core_id | usize or None | CPU core affinity for the hot loop thread. Use a distinct value per engine when running several in one process. |
ib_key_timeout_secs | int or None | Live second-factor approval timeout in seconds (default ~18 min). Lower it to fail fast on unattended live logins; ignored for paper. |
ib_key_token_sub_type | str or None | Fallback second-factor token sub-type (default "2a"), used only when the server states none for the session; ignored for paper. |
code_provider | Py<PyAny> or None | Callable (factor, display_id, avth_url) -> str returning the second-factor code. factor is "ibkey" (the code shown for display_id) or "authenticator" (the account's current code). Required for authenticator accounts, which have no push to fall back to; ignored for paper. |
readonly | bool |
disconnect
Disconnect from IB.
def disconnect()
is_connected
Whether there is a session to make requests on. False before connect and after disconnect, and false from the moment the engine gives the session up — which it writes down itself. True while a lost connection to the venue is being rebuilt, between the 1100 and the 1102, as a program connected to a gateway reads it: the gateway's socket stays up. The record saying so is delivered only by a read, and a program that drives its own loop, or none at all, is told nowhere else: it read connected on a session that was over, and went on issuing requests into it. The Rust surface answers this the same way.
def is_connected()
conn_state
Which of DISCONNECTED, CONNECTING and CONNECTED this client is in. CONNECTING is the span of connect, as read from another thread: the connection is claimed at the top of that call and the session's state handed over at the bottom, and is_connected reads true for the whole of it. A session that ended without being closed is connecting again from the moment connect is called on it, though what it held stays in place until the logon answers. A session the engine has given up is DISCONNECTED from the moment it writes that down, however the caller drives its loop — read from the cached flags alone it stayed CONNECTED for as long as the program went without dispatching.
client.conn_state # read-only attribute
client_id
The number this session connected under, as the caller gave it, and None when there is no session.
client.client_id # read-only attribute
host
The server this session connected to, and None when there is no session. The one it knocked on, which is the one the caller named — the venue may then send it elsewhere, and the rest of that list is where. The reference client holds what its caller passed here too.
client.host # read-only attribute
port
The port connect was given, and None when there is no session. Kept as the caller gave it, as the reference client keeps it. This session speaks to the venue's servers on the ports the venue names, so the number opens nothing; a program that reads it back gets what it passed.
client.port # read-only attribute
conn
The client itself while a session is held, and None without one. The reference client keeps its connection here, and what a program reads off it is isConnected(), host and port — which this client answers, following the session. Its socket is not here: this client's connections are opened, read and kept alive inside its engine.
client.conn # read-only attribute
asynchronous
False: there is no asynchronous mode. The reference client sets this nowhere either; its sample program reads it in connect_ack to decide whether to start the exchange itself. Here connect returns with the session up and its engine running, and connect_ack is announced from inside it, so there is nothing left for a caller to start.
client.asynchronous # read-only attribute
server_version
The protocol level this client implements: 217, the reference client's MIN_SERVER_VER_ADDITIONAL_ORDER_PARAMS_2. None before a session and after one, as the reference client answers before its greeting — and held while a lost connection is recovered, which the reference client rides out holding the number. In the reference architecture this number is the API level of the process a program is talking to. That process was a gateway, which announced it and had every request gated on it; here it is this client, so the number is a statement about this client and not a reading off the venue, whose logon names no such level. Attached orders (218) load the selected account preset and construct the parent and children. Percentage allocations still need positions by account and model and the applicable allocation-group state, so the level remains 217. A percentage allocation cannot yet determine the quantities of its attached children here; supply explicitly sized parent and child orders when those quantities depend on group or model holdings. Configuration requests (219, 221) and the last price and size stated to their precision (222, 224) are absent. hedgeMaxSize (223) is sent on a beta hedge, and odd-lot quotes (225) are served: generic tick 787 is asked for and its prices, sizes and venues delivered. conditionsIncludeOvernight (226) is sent with an order's conditions, and refused under 10371 when the order is placed where the logon does not enable it or the contract trades on no overnight venue. 226 is the highest level a gateway announces. Below it, a program that believes the number is wrong about the following, and each is said on use rather than passed over: * An order field this client does not carry, refused by name on error under 321 when the order is placed: smartComboRoutingParams (57). * A withdrawal stating a manual time (169): the withdrawal goes, with its operator and who entered it, and the caller is told on error that the time did not travel. Every other gate at or below 217 names a request, field or callback that is here and does what it does through a gateway.
def server_version()
tws_connection_time
When the venue says this session logged in, by its own clock and in its own spelling; None when there is no session, and held while a lost connection is recovered, as the level is. The reference client answers the time its gateway stamped on its greeting. The venue stamps every message it sends with the time it sent it, the answer to the logon included, and this is that stamp — the clock competing_session reads the other session's logon off. Where the venue stamped none, connect holds this machine's clock instead and says so in the log.
def tws_connection_time()
set_connect_options
The reference client carries these on its greeting to its gateway, which reads them; there is no gateway between this client and the venue to read them, so an option a caller states cannot be carried. Said rather than swallowed. Every one of these options changes how the session behaves — how fast it may ask, what it is told — and a caller who set one and heard nothing has a session that is not the one they asked for and no way to know it. Stating none is stating nothing, which is what the reference client's own is on an ordinary call.
def set_connect_options(opts)
| Parameter | Type | Description |
|---|---|---|
opts | str | Connect options, as the reference client takes them. Not applied. |
set_optional_capabilities
The capabilities a program asks its gateway to switch on, which the reference client keeps and sends on its handshake. There is no gateway between this client and the venue to read them, so capabilities stated cannot be carried — answered as set_connect_options answers, and for the same reason said rather than swallowed.
def set_optional_capabilities(opt_capab)
| Parameter | Type | Description |
|---|---|---|
opt_capab | str |
validate_order_parameters
The parameter an order states that a session at the level this client answers cannot carry, named as the reference client names it, or None where the order states none. Its own check ported (ibapi/client.py:2819): a field counts as stated where it carries something — a flag set, an option present, a number off the unset value it is born with — and the gates are the levels the reference client gates them at: 216 for the first block of additional parameters, 217 for the second, 223 for hedgeMaxSize and 226 for conditionsIncludeOvernight. Before a session the level reads as nought, as it does there, and every gate asks.
def validate_order_parameters(order)
| Parameter | Type | Description |
|---|---|---|
order | Order | Order parameters (action, quantity, type, price, TIF, etc.). |
validate_attached_orders_parameters
The attached-order field a session below level 218 cannot carry, named as the reference client names it, or None. Its own check ported (ibapi/client.py:2853), read against the order's own fields: an id counts as stated when it is off the unset value it is born with and a type when it is not empty.
def validate_attached_orders_parameters(attached_orders)
| Parameter | Type | Description |
|---|---|---|
attached_orders | Order | Order parameters (action, quantity, type, price, TIF, etc.). |
validate_invalid_symbols
The reference client's own check (ibapi/client.py:352) of a string before it goes on the wire, as [host_carriable] runs it — and where it fails, said on error under 579 naming the string, rather than raised: this client states its refusals on the wrapper. The options that client checks beside the host are never kept here, so there is nothing else to check. connect runs it on the host and returns on the refusal, as the reference client's connect does.
def validate_invalid_symbols(host)
| Parameter | Type | Description |
|---|---|---|
host | str | Server hostname. |
start_api
Nothing to start, once there is a session. The reference client sends its client id here and its gateway begins the exchange on receiving it. Here the id is kept on the client and never sent — one session holds the account — and connect announces connect_ack, the accounts and the next order id itself before it returns. Before a session exists this is reported the way the reference client reports it: on error, under 504.
def start_api()
check_connected
Raises when there is a session, with the words the reference client raises its own check under. Nothing otherwise.
def check_connected()
reset
Forget the session — which here is closing it. The reference client drops its hold on the socket and leaves the socket to its reader thread; a session here is an engine that stays logged in at the venue until it is stopped, so this is disconnect. Nothing to forget is fine.
def reset()
events_lost
How many engine events this session's channel discarded: none. This surface reads every engine callback through the session's one order, which keeps each record until a read delivers it, and attaches no event channel that could drop one.
def events_lost()
refuse
Put a refusal the program makes itself into the session's order. Delivered by a pass on error, or error_from, under the number origin names: after everything the session queued before this call and before everything after it, as a gateway's rejection arrives after everything the gateway wrote before it. Said at the call instead, it reaches the wrapper ahead of the refusals of requests made before it. origin is an ErrorOrigin, ErrorOrigin("Request", 7) for a request's number. With no session, or one that is over, there is no order to put it in and nothing is delivered.
def refuse(origin, code, msg)
| Parameter | Type | Description |
|---|---|---|
origin | ErrorOrigin | What the error is about: a request, an order and the operation on it, a request with no number of its own, the session, or a lookup this client made for itself. |
code | int | |
msg | str |
poll
Deliver everything waiting, once, and return. run owns the thread it is called on, which a program with an event loop of its own cannot give it: an asyncio framework has to drive the callbacks from its own loop, and a blocking loop leaves it nowhere to stand. This is one pass of the same dispatch.
def poll()
run
Deliver callbacks until the session ends. Blocks the calling thread. Everything a program receives arrives from here, so it runs on a thread of its own or is the last call a program makes. poll does one pass instead, for a program that owns its loop.
def run()
wait_for_data
Wait for the engine to signal, for at most timeout seconds: True when it signalled, False when the wait ran out. The engine signals at the end of each pass of its loop, and when a connection goes or comes back. One waiter takes each signal. A thread that wakes a loop of its own waits here and then has that loop call poll: True is a reason to poll, not a promise that the poll delivers anything. Waits with the interpreter released, so other threads run meanwhile. Raises when there is no session.
def wait_for_data(timeout)
| Parameter | Type | Description |
|---|---|---|
timeout | float | The longest to wait. |
get_account_id
Get the account ID.
def get_account_id()
session_over
Whether this session is finished rather than merely disconnected: closed by disconnect(), or given up on by the engine. A loss the engine is still working on is neither — is_connected() reads true between the 1100 and the 1102, and a request made then is carried when the transports come back.
def session_over()
instrument_of
Which slot a contract holds on this session, if it holds one. Read through the lookup every other reader uses, which drops what the engine has given back, so a slot that has gone to the next contract is not named.
def instrument_of(con_id)
| Parameter | Type | Description |
|---|---|---|
con_id | int | Contract ID. Unique per instrument. |
unread_wire
What the venue sent this session that nothing here reads, as pairs of the connection and what arrived: each kind of message named once, the first time it arrives. With IB_DX_CAPTURE_WIRE set, every frame is kept here as well, whole and as sent.
def unread_wire()
competing_session
Another session that already held this account when this one connected. None when this session is alone. Otherwise where the other one connected from, when it logged in, and whether this session is held to reading only because the other has the account. Worth asking before starting work: the venue permits one logon at a time and takes the account from the older session without saying which it dropped, so a second client reads as data that stops arriving.
def competing_session()
ccp_session_id
Session ID surfaced to webapp REST clients as x-ccp-session-id.
def ccp_session_id()
misc_url
Logical-name → host URL lookup from the MiscUrls block of the logon response (e.g. region_dam). None when the logon did not carry the key.
def misc_url(key)
| Parameter | Type | Description |
|---|---|---|
key | str | Account value key (e.g. "NetLiquidation", "BuyingPower"). |
Calls That Answer
contract_details
Everything the venue knows about the contracts matching a description. Sends the lookup, waits for the venue to say it has finished, and hands back every match. A description matching nothing returns an empty list; a venue that refuses the lookup raises with the reason it gave.
def contract_details(contract)
| Parameter | Type | Description |
|---|---|---|
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
corporate_actions
A contract's corporate actions, asked for and waited on. One dict per action, stating what the venue stated: its kind as the two-letter name the venue uses, the day it takes effect, its value, and the dates and dividend descriptions the kind carries. A field the kind does not carry is empty rather than invented. contract must carry the venue's id for it. Days are YYYYMMDD.
def corporate_actions(contract, start_date, end_date)
| Parameter | Type | Description |
|---|---|---|
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
start_date | str | |
end_date | str |
historical_data
Bars for a contract over a period, handed back rather than delivered a bar at a time to a callback. The venue has no adjusted series to pass through: what it serves is raw bars, and the two series the vendor states as adjusted — TRADES and ADJUSTED_LAST — are those folded with the contract's own actions. For a stock or a fund, whatever the series, the actions are asked for first, as a gateway asks them, once a day for each contract, and their answer states the ids the contract traded under: the bars are asked one id at a time, the newest first, each for the days the contract traded as it — a week or a month split at every split as well, and its two parts joined into one bar — and joined into one series. The fold is made once the series is whole, before a bar is handed to anyone, with the actions dated up to the day it is made, that day on UTC's calendar included, as a gateway folds them: a series priced as the contract trades is put on the scale of its splits, stock dividends and spin-offs, and every series has the bars before a rights offer multiplied by the value it states. Any other kind of contract is asked as the request was made, and its bars handed back as the venue served them. ADJUSTED_LAST also has each cash dividend taken off the bars before it, as a gateway takes it off: the amount restated on the scale of the splits, spin-offs and rights offers listed after it, to four places, taken off the last bar before the one dated its day, and every bar before that one multiplied by what it took off that bar's close. This call waits and hands the series back in one piece; reqHistoricalData delivers the same bars one at a time on its callbacks. Both ask for the actions by the venue's id for the contract, which the venue is asked for first where the contract is named some other way.
def historical_data(contract, end_date_time, duration_str, bar_size_setting, what_to_show, use_rth=1)
| Parameter | Type | Description |
|---|---|---|
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
end_date_time | str | End date/time in "YYYYMMDD HH:MM:SS" format, or empty for now. |
duration_str | str | Duration string, e.g. "1 D", "1 W", "1 M", "1 Y". |
bar_size_setting | str | Bar size: "1 min", "5 mins", "1 hour", "1 day", etc. |
what_to_show | str | Data type: "TRADES", "MIDPOINT", "BID", "ASK", "BID_ASK", etc. |
use_rth | int | If true, only return data from Regular Trading Hours. |
head_timestamp
The earliest moment the venue holds data for a contract.
def head_timestamp(contract, what_to_show="TRADES", use_rth=1)
| Parameter | Type | Description |
|---|---|---|
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
what_to_show | str | Data type: "TRADES", "MIDPOINT", "BID", "ASK", "BID_ASK", etc. |
use_rth | int | If true, only return data from Regular Trading Hours. |
matching_symbols
Contracts whose symbol or name matches a pattern.
def matching_symbols(pattern)
| Parameter | Type | Description |
|---|---|---|
pattern | str | Symbol search pattern. |
news_headlines
The headlines the venue holds for a contract. Answers rather than reporting through the wrapper, because a program written against the reference client reads the return value.
def news_headlines(con_id, provider_codes, start_date_time, end_date_time, total_results)
| Parameter | Type | Description |
|---|---|---|
con_id | int | Contract ID. Unique per instrument. |
provider_codes | str | Pipe-separated news provider codes. |
start_date_time | str | Start date/time for tick query. |
end_date_time | str | End date/time in "YYYYMMDD HH:MM:SS" format, or empty for now. |
total_results | int | Maximum number of news results. |
trading_schedule
When a contract trades, over a stretch of days. Each session is its opening, its close, and the day it belongs to; the time zone they are stated in comes with them.
def trading_schedule(contract, end_date_time, duration_str, use_rth)
| Parameter | Type | Description |
|---|---|---|
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
end_date_time | str | End date/time in "YYYYMMDD HH:MM:SS" format, or empty for now. |
duration_str | str | Duration string, e.g. "1 D", "1 W", "1 M", "1 Y". |
use_rth | bool | If true, only return data from Regular Trading Hours. |
option_chains
Every venue's option chain for an underlying, returned rather than delivered on a callback: expiries and strikes, per venue and trading class.
def option_chains(underlying_symbol, fut_fop_exchange, underlying_sec_type, underlying_con_id)
| Parameter | Type | Description |
|---|---|---|
underlying_symbol | str | Underlying symbol (e.g. "AAPL"). |
fut_fop_exchange | str | Exchange for futures/FOP options. |
underlying_sec_type | str | Underlying security type (e.g. "STK"). |
underlying_con_id | int | Underlying contract ID. |
histogram_data
How a contract's traded volume is spread across prices over a period.
def histogram_data(contract, use_rth=True, time_period="3 days")
| Parameter | Type | Description |
|---|---|---|
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
use_rth | bool | If true, only return data from Regular Trading Hours. |
time_period | str | Histogram time period. |
fundamental_data
A fundamental report on a contract, as the venue supplies it.
def fundamental_data(contract, report_type)
| Parameter | Type | Description |
|---|---|---|
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
report_type | str | Report type: "ReportSnapshot", "ReportsFinSummary", "RESC", etc. |
positions
Every holding in the account: one tuple per holding, its account, its contract, the position and its average cost — what position states, handed back rather than delivered. Read once the account has finished stating its holdings, as req_positions reads them; where it had not within the wait, what this session already held is answered and the log says so. A holding named by id alone is given a moment for its definition to land, as there. Nothing is subscribed: asking again reads again.
def positions()
what_if_order
What the venue says an order would cost, without placing it: the order's own placement with the question marked on it, answered with the state the venue states for it. Numbered in the band these calls take, so the answer is this call's and the dispatch loop leaves it, with anything said about it. A placement this client refuses raises at once, with the refusal's words.
def what_if_order(contract, order)
| Parameter | Type | Description |
|---|---|---|
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
order | Order | Order parameters (action, quantity, type, price, TIF, etc.). |
scan
Run a scan and hand back what it found: one tuple per row, its rank and the contract's details, then the distance, benchmark and projection the venue states beside it, empty where it states none. The subscription is withdrawn before this returns: a scan asked for once is a question, and left running it keeps answering into a session nobody is reading. A scan the venue will not run raises with its words.
def scan(instrument, location_code, scan_code, most)
| Parameter | Type | Description |
|---|---|---|
instrument | str | Instrument type for scanner (e.g. "STK", "FUT"). |
location_code | str | Scanner location (e.g. "STK.US.MAJOR"). |
scan_code | str | Scanner code (e.g. "TOP_PERC_GAIN", "HIGH_OPT_IMP_VOLAT"). |
most | int | Maximum number of scanner results. |
calendar_schema
What the corporate-events calendar says it carries, as the venue's JSON.
def calendar_schema()
calendar_events
The calendar's events for one contract, as the venue's JSON.
def calendar_events(con_id)
| Parameter | Type | Description |
|---|---|---|
con_id | int | Contract ID. Unique per instrument. |
qualify_contract
Fill in what the venue knows about a contract, above all its id. Most of what this client sends carries a contract, and a contract with an id is worth more than one without: market data is answered only for a contract named by id, and an order carrying one needs to state nothing else. A description matching more than one contract is refused rather than resolved to whichever came back first — the same symbol on the same venue exists in more than one currency, and picking one silently is how an order reaches the wrong one.
def qualify_contract(contract)
| Parameter | Type | Description |
|---|---|---|
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
qualify_contracts
Fill in a whole list of contracts, keeping their order. One that cannot be resolved fails the call rather than being dropped: a list quietly shorter than it was asked for is how a program trades something other than what it named.
def qualify_contracts(contracts)
| Parameter | Type | Description |
|---|---|---|
contracts | list | Contract specification (symbol, secType, exchange, currency, etc.). |
Account & Portfolio
req_pnl
Subscribe to the named account's profit. The account is checked as a gateway checks it. Each request has its own subscription; a repeated active request number is refused under 102. A model is taken and not applied, with a log notice once per session.
def req_pnl(req_id, account, model_code="")
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
account | str | Account ID. |
model_code | str | Model portfolio code (empty for default). |
cancel_pnl
Cancel P&L subscription.
def cancel_pnl(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
req_pnl_single
Subscribe to a position's profit in the named account. The account is checked as for the account-level profit. A model is taken and not applied, with a log notice once per session.
def req_pnl_single(req_id, account, model_code, con_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
account | str | Account ID. |
model_code | str | Model portfolio code (empty for default). |
con_id | int | Contract ID. Unique per instrument. |
cancel_pnl_single
Cancel single-position P&L subscription.
def cancel_pnl_single(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
req_account_summary
Request an account summary. All answers for every account the login holds. Account groups and AllNonProp are taken and not applied, with a log notice once per session. Validation and the limit of two standing summary requests follow a gateway.
def req_account_summary(req_id, group_name, tags)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
group_name | str | Account group name (e.g. "All"). |
tags | str | Comma-separated account tags: "NetLiquidation,BuyingPower,...". |
cancel_account_summary
Cancel account summary.
def cancel_account_summary(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
req_positions
Request all positions. Before a session exists this is reported on the error callback and the call returns, as every other request made before connecting is. A program written against the reference client has no exception handling around a request, because that client does not raise there. include_fx_positions (default true) controls CASH rows. send_zero_positions_for_todays_opening_only (default false) omits flat rows only when the venue states a zero opening quantity. A nonzero or unknown opening quantity still passes. Both settings apply to the initial answer and later updates; end callbacks still arrive.
def req_positions()
cancel_positions
Cancel positions.
def cancel_positions()
req_account_updates
Subscribe to the named account's figures and holdings, or withdraw the subscription. A single-account login ignores the name as a gateway does. Subscribing asks the venue to restate that account now; the engine holds the answer until its download ends or the existing wait expires. include_fx_positions (default true) controls CASH rows. send_zero_positions_for_todays_opening_only (default false) omits flat rows only when the venue states a zero opening quantity. A nonzero or unknown opening quantity still passes. Both settings apply to the initial answer and later updates; end callbacks still arrive.
def req_account_updates(subscribe, acct_code="")
| Parameter | Type | Description |
|---|---|---|
subscribe | bool | true to start updates, false to stop. |
acct_code | str | Account code (e.g. "DU1234567"). |
req_managed_accts
Request managed accounts list. Answered with every account this login holds, comma separated, matching the reference client. Before a session exists there are no accounts to name, and an empty list reads as a login holding none rather than as a question asked too early.
def req_managed_accts()
req_account_updates_multi
Subscribe to the named account's figures under this request number. ledger_and_nlv selects the per-currency ledger and net liquidation. A model is taken and not applied, with a log notice once per session. The initial batch ends with account_update_multi_end; changes keep arriving until the request is cancelled.
def req_account_updates_multi(req_id, account, model_code, ledger_and_nlv=False)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
account | str | Account ID. |
model_code | str | Model portfolio code (empty for default). |
ledger_and_nlv | bool | If true, only the per-currency ledger: each currency's cash, market values and NetLiquidationByCurrency. |
cancel_account_updates_multi
Cancel multi-account updates. The request stops being reported to. The venue keeps the account current whether or not anyone is listening; what stops is the reporting — a figure that moves after this is no longer delivered on accountUpdateMulti for this request. A request the engine still holds is withdrawn, never answered; one it answered stops where the withdrawal stands, after its answer.
def cancel_account_updates_multi(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
req_positions_multi
Subscribe to holdings of the named account under this request number. A model is taken and not applied, with a log notice once per session. include_fx_positions (default true) controls CASH rows. send_zero_positions_for_todays_opening_only (default false) omits flat rows only when the venue states a zero opening quantity. A nonzero or unknown opening quantity still passes. Both settings apply to the initial answer and later updates; end callbacks still arrive.
def req_positions_multi(req_id, account, model_code)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
account | str | Account ID. |
model_code | str | Model portfolio code (empty for default). |
cancel_positions_multi
Cancel multi-account positions.
def cancel_positions_multi(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
account_snapshot
Read account state snapshot. Returns a dict with all account values.
def account_snapshot()
positions_elsewhere
Holdings the venue reports that this broker does not hold itself: positions held away at another broker, and rows it marks as shown but not held. Kept apart from req_positions, which answers what the account itself holds. The reference client has no call for these — its own front end shows them in a separate table — so this is the only way to reach them. One dict per holding: con_id, symbol, sec_type, currency, position, avg_cost, and held, which is "Away" for a position held at another broker, "DisplayOnly" for a row shown but not held, and "Aside" for one reported apart without saying why. Empty with no session.
def positions_elsewhere()
values_elsewhere
The account figures describing one of the sets of holdings the account does not hold itself, as name, value and the currency each is stated in. A figure stated in two currencies is two figures. held names the set as positions_elsewhere does: "Away", "DisplayOnly" or "Aside". The venue states these the same way it states the account's own, and mixing them in would overstate what the account is worth, so they are kept where the holdings they describe are kept. Empty with no session.
def values_elsewhere(held)
| Parameter | Type | Description |
|---|---|---|
held | str | Which set of holdings kept elsewhere: Away, DisplayOnly or Aside. |
Orders
place_order
Place an order. A request the client will not send is reported under the number the reference client reports it under, and the call returns. A program moved from that client has an error handler and no exception handling around a request, because nothing it was written against raises there. A send the engine can no longer take is reported the same way, stating what has already reached the engine and what has not. slOrderId / slOrderType and ptOrderId / ptOrderType construct children from the selected account preset. State each child id with PRESET (case-insensitive). Preset loading and any quote wait run in the engine; the call returns without waiting. The parent goes first, then stop loss and profit taker. transmit=False holds the family until a parent or child transmits it. Replacing the parent preserves its existing children. Percentage-allocation sizing from group or model holdings is not carried; use explicitly sized parent and child orders where their quantities depend on it.
def place_order(order_id, contract, order)
| Parameter | Type | Description |
|---|---|---|
order_id | int | Order identifier. Must be unique per session. |
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
order | Order | Order parameters (action, quantity, type, price, TIF, etc.). |
exercise_options
Exercise or lapse a long option position.
def exercise_options(req_id, contract, exercise_action, exercise_quantity, account, override, manual_order_time, customer_account, professional_customer)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
exercise_action | int | 1=exercise, 2=lapse. |
exercise_quantity | int | Number of contracts to exercise. |
account | str | Account ID. |
override | int | Override flag for exercise. |
manual_order_time | str | |
customer_account | str | |
professional_customer | bool |
cancel_order
Cancel an order. The second argument is what the reference client states about the withdrawal itself — when a person entered it, on whose authority, and whether a person entered it at all. It is taken as that object or as the time alone. Who is withdrawing it and whether a person entered it travel on the cancel, as a gateway writes them: from the withdrawal, not from the placement. A time does not travel. A gateway sends it only where the venue has turned that record on for the login, and this client does not read whether it has. The cancel goes anyway and the caller is told the time did not: refused outright, a live order would be left standing over a record this client does not send. Taken silently it would be withdrawn without the record while the caller had given one, so it is said. A time a gateway cannot read is refused as a gateway refuses it, under 10301, and nothing is withdrawn. Nothing is said as the cancel goes out, as a gateway says nothing then: asked for, the order reads PendingCancel, and the venue's next report on it states it.
def cancel_order(order_id, order_cancel=None)
| Parameter | Type | Description |
|---|---|---|
order_id | int | Order identifier. Must be unique per session. |
order_cancel | Py<PyAny> or None | What the withdrawal states about itself: an OrderCancel, or a manual time alone. "" states nothing, and so does None from Python. |
req_global_cancel
Cancel every order the account is working. This wire carries no request to withdraw everything, so it is composed here: one cancel for each order held, which is what a caller asking for everything back is asking for. What is held is what the venue named as working at connect and what this session placed since. The venue names the former after the connect returns, so a global cancel issued straight away waits for that naming, as asking for the open orders does, and covers what was named. Where the naming does not finish within the wait, what had been named is still withdrawn and the call says so rather than returning as though every order were covered: a partial cancel that reads as one beats the same cancel in silence, which reads as a complete answer. What the withdrawal states — who is withdrawing and whether a person entered it — travels on every cancel, as a gateway states it on every order it withdraws. A time does not: the reference client writes none on a withdrawal of everything, so a gateway never reads one, and one stated here goes the same way.
def req_global_cancel(order_cancel=None)
| Parameter | Type | Description |
|---|---|---|
order_cancel | Py<PyAny> or None | What the withdrawal states about itself: an OrderCancel, or a manual time alone. "" states nothing, and so does None from Python. |
req_ids
Request next valid order ID. num_ids has no effect, as on a gateway: the next valid id is answered whatever number is asked for. Before a session exists there is no counter to answer from: the id an account may next use is the venue's to state. Answering announces zero, which names no order the venue will hold and is refused on placement. Reported the way the reference client reports every request made before connecting.
def req_ids(num_ids=1)
| Parameter | Type | Description |
|---|---|---|
num_ids | int | Number of IDs to reserve (unused). |
next_order_id
Reserve the next order ID above the saved counter and venue replay. The account and API client's counter is written under an exclusive lock before returning. A storage failure warns once in the log and leaves allocation using session memory and venue replay.
def next_order_id()
next_shared_id
The first id past everything the account has used that a request can also carry. A caller that numbers its orders and its requests out of one counter — which is how ib_async is written — needs both at once: clear of every id an order has spent, and inside the numbers a request can carry. An account that has been given a wider order id than that has no such number above it, so this answers with one past the widest the account has used that a request can carry, and the counting goes on from there. After a connect it waits, for at most three seconds in all, for the venue to name the orders the account is working. Raises RuntimeError where even that is not a number a request can carry. Answers 1 where there is no session.
def next_shared_id()
req_open_orders
Request the open orders of this client. The answer is this client's own orders alone, as a gateway narrows it. An order the venue states held is among them only where this session placed it: a gateway holds an order it sent working whatever the venue states of it later, and leaves out one it first learns of held.
def req_open_orders()
req_all_open_orders
Request all open orders across all clients. Every client's orders on the account, where req_open_orders answers with this client's own, as a gateway narrows that one.
def req_all_open_orders()
req_auto_open_orders
Binding orders entered elsewhere to this client. For client 0, b_auto_bind turns binding on or off, as on a gateway: while it is on, each order placed elsewhere that the venue states after it has named what was already working is bound to client 0, under the next negative number, or the next order id where use_negative_auto_range is off. Turning it off keeps the numbers already given. The venue is asked to bind each one, and order_bound follows when it accepts. Any other client is refused, as a gateway refuses a client other than 0: asked to bind, with 321, as a request that fails validation; asked not to, with 327.
def req_auto_open_orders(b_auto_bind)
| Parameter | Type | Description |
|---|---|---|
b_auto_bind | bool | If true, auto-bind future orders to this client. |
req_executions
Request execution reports. Before a session exists this is reported on the error callback, as every other request made before connecting is. Answered instead, the answer waits for a dispatch pass no session is there to make, and the caller hears nothing at all. lastNDays and specificDates select days as a gateway selects them, counted on the session's time zone. A date that does not read as a number, or is not a day of the calendar, is refused under 320, as a gateway refuses it. The executions answered from reach back to midnight six days before the logon in UTC, or to the logon's own day for a session set to today's executions, so the earliest days asked for can be missing some; those days are named on error under 321 ahead of the answer, which still comes. acctCode is ignored on a login holding one account and refused on one holding several where the login does not hold it, as a gateway does both.
def req_executions(req_id, exec_filter=None)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
exec_filter | Py<PyAny> or None |
req_completed_orders
Request completed orders. api_only asks for the orders entered through an API rather than by hand. The venue states no origin beside a finished order, and it does number the ones an API placed: an order that went out through one carries the number that API gave it, and one typed in carries none. So true is answered with the orders the venue numbered.
def req_completed_orders(api_only=False)
| Parameter | Type | Description |
|---|---|---|
api_only | bool |
Market Data
set_news_providers
Set news provider codes for per-contract news ticks (e.g. "BRFG*BRFUPDN").
def set_news_providers(providers)
| Parameter | Type | Description |
|---|---|---|
providers | str | News provider list. |
req_mkt_data
Request market data for a contract. With snapshot, tickSnapshotEnd follows once the snapshot is whole, as a gateway ends one: when the venue has stated the bid, the ask, the last, the open and the close; on a contract of a type a gateway marks as an option (OPT, FOP, IOPT, WAR, EC) also the option model, 13 (83 delayed); on a delayed feed also the last trade's time, 88; or eleven seconds after the request. Ticks 10, 11 and 12 (80, 81 and 82 delayed), the bid's, the ask's and the last's option computations, are not stated by the venue: a gateway works them out with an option model of its own, and so does this client, for an option on a share once its inputs are in hand. A gateway's snapshot of an option also waits for them; this client's does not. mkt_data_options is checked as a gateway checks it: manual, 0 or 1, is taken and changes nothing a gateway sends; any other key is refused under 10337, another value under 10338, and an entry not written key=value under 320. Where the venue has lifted the key checks, a manual that does not read as the number nought or one is refused under 321.
def req_mkt_data(req_id, contract, generic_tick_list="", snapshot=False, regulatory_snapshot=False, mkt_data_options=None)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
generic_tick_list | str | Comma-separated generic tick IDs (e.g. "233" for RT volume). |
snapshot | bool | If true, delivers one quote then auto-cancels. |
regulatory_snapshot | bool | If true, request a regulatory snapshot (additional fees may apply). |
mkt_data_options | list |
req_mkt_data_ex
Like req_mkt_data, but names the market-data mode on the request itself (0=realtime, 1=delayed, 2=frozen, 3=delayed-frozen) rather than taking the one the session is set to. The frozen one keeps thinly-traded names quoting after hours, when the realtime feed is silent. A contract holds one subscription at a time, so this states the mode for that subscription rather than adding a second alongside it: a later request for a contract already subscribed follows the one that is up and is handed its quotes. To compare two modes on one contract, withdraw between them. regulatory_snapshot asks for the venue's own chargeable one-shot snapshot: a request type of its own rather than a mode on an ordinary quote. It needs the entitlement, and an account without it is refused by the venue, which names the request type back through error. It ends the way an ordinary snapshot does, so tickSnapshotEnd fires either way.
def req_mkt_data_ex(req_id, contract, generic_tick_list="", snapshot=False, regulatory_snapshot=False, mode_9887=0, mkt_data_options=None)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
generic_tick_list | str | Comma-separated generic tick IDs (e.g. "233" for RT volume). |
snapshot | bool | If true, delivers one quote then auto-cancels. |
regulatory_snapshot | bool | If true, request a regulatory snapshot (additional fees may apply). |
mode_9887 | int | |
mkt_data_options | list |
cancel_mkt_data
Cancel market data. A number a gateway cannot read, past four bytes signed, is refused under -1 with 320, as a gateway refuses it, and withdraws nothing.
def cancel_mkt_data(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
req_tick_by_tick_data
Request tick-by-tick data. number_of_ticks and ignore_size are sent as stated: a count of past ticks goes out as the length of the run the stream opens with, and the size filter as the query's filter term. Neither goes out at its default — no prelude, sizes included — which is what the venue does on its own. Whether the venue honours the size filter is the venue's, and what it sends is passed on as it stands, as a gateway passes it: one session saw size-only changes still arrive on a stream that asked to leave them out.
def req_tick_by_tick_data(req_id, contract, tick_type, number_of_ticks=0, ignore_size=False)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
tick_type | str | Tick type ID or tick-by-tick type string. |
number_of_ticks | int | Maximum number of ticks to return. |
ignore_size | bool | If true, asks that a bid/ask change moving only a size be left out. |
cancel_tick_by_tick_data
Cancel tick-by-tick data.
def cancel_tick_by_tick_data(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
req_ping
Request an auth-connection round-trip time sample: sends a lightweight liveness probe with no side effects on subscriptions, contract caches, or pacing budgets. Poll last_rtt_ms() after a moment for the result.
def req_ping()
last_rtt_ms
Last measured auth-connection round-trip time in milliseconds, or None if never measured. A gauge, not a benchmark req_ping. Also sampled automatically by the engine's own liveness probes.
def last_rtt_ms()
req_market_data_type
Name the feeds every subscription after this one may be served: 1 live, 2 frozen, 3 delayed, 4 delayed-frozen. A type turns feeds on, as a gateway takes it: 2 turns frozen data on; 3 and 4 turn delayed data on, 4 with delayed-frozen and 3 without; only 1 turns frozen data off, and it turns all three off. A subscription starts live whatever the type, falls back to delayed data on a refusal where delayed data is on, and is served the frozen or delayed-frozen quote while the market is closed, where the logon enables frozen data; the market_data_type callback reports the type served. A number naming no type is refused under -1 with 321, Invalid market data type, as a gateway refuses it, and leaves the feeds as they were. req_mkt_data_ex names a feed per request, which allows two feeds on one contract at once.
def req_market_data_type(market_data_type)
| Parameter | Type | Description |
|---|---|---|
market_data_type | int | 1=live, 2=frozen, 3=delayed, 4=delayed-frozen. |
req_mkt_depth
Request market depth (L2 order book). mkt_depth_options is checked as a gateway checks it: manual, 0 or 1, is taken and changes nothing a gateway sends; any other key is refused under 10337, another value under 10338, and an entry not written key=value under 320. Where the venue has lifted the key checks, a manual that does not read as the number nought or one is refused under 321.
def req_mkt_depth(req_id, contract, num_rows=5, is_smart_depth=False, mkt_depth_options=None)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
num_rows | int | Number of order book rows to subscribe to. |
is_smart_depth | bool | If true, aggregate depth from multiple exchanges via SMART. |
mkt_depth_options | list |
cancel_mkt_depth
Cancel market depth. is_smart_depth names which of the two books under the number is withdrawn — the smart one or the regular one — as it does for a gateway: the two are held apart, both run, and the withdrawal takes the book of the mode it names. A mode no book was asked in is refused under 310 rather than answered with the other mode's book.
def cancel_mkt_depth(req_id, is_smart_depth=False)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
is_smart_depth | bool | If true, aggregate depth from multiple exchanges via SMART. |
req_real_time_bars
Request real-time 5-second bars. bar_size has no effect, as on a gateway: a real-time bar is five seconds, and the venue's request carries no bar size. A gateway reads the number and does not use it. Requests for the same bars of one contract — this call's, or the stream a historical request kept up to date rides — read one stream, as on a gateway: the venue serves it under one number, each request is handed every bar under its own id, and a cancel withdraws its own request alone. The stream is withdrawn when the last of them leaves. real_time_bars_options is checked as a gateway checks it: manual, 0 or 1, is taken and changes nothing a gateway sends; any other key is refused under 10337, another value under 10338, and an entry not written key=value under 320. Where the venue has lifted the key checks, a manual that does not read as the number nought or one is refused under 321.
def req_real_time_bars(req_id, contract, bar_size=5, what_to_show="TRADES", use_rth=0, real_time_bars_options=None)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
bar_size | int | Bar size: "1 min", "5 mins", "1 hour", "1 day", etc. |
what_to_show | str | Data type: "TRADES", "MIDPOINT", "BID", "ASK", "BID_ASK", etc. |
use_rth | int | If true, only return data from Regular Trading Hours. |
real_time_bars_options | list |
cancel_real_time_bars
Cancel real-time bars.
def cancel_real_time_bars(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
quote
Zero-copy SeqLock quote read by req_id. Returns a dict with bid, ask, last, bid_size, ask_size, last_size, volume, high, low, open, close, or None if the req_id is not mapped.
def quote(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
quote_by_instrument
Zero-copy SeqLock quote read by InstrumentId. Returns a dict with bid, ask, last, bid_size, ask_size, last_size, volume, high, low, open, close, and whether the venue has halted the contract and why — or None if not connected, or for an id past every slot the instrument table holds. Whether it is restricting short sales in it is shortSaleRestrictedByInstrument, which is stated on the same record and kept off this one.
def quote_by_instrument(instrument)
| Parameter | Type | Description |
|---|---|---|
instrument | int | Instrument type for scanner (e.g. "STK", "FUT"). |
option_model
What the venue's own model last made of an option, whole. tickOptionComputation carries its greeks and its price, beside the volatility and the underlying's price a gateway takes from the venue's other series. The venue states eighteen figures on this record, and every one is in this dict, its own volatility and underlying price among them. A figure the venue did not state is this API's own unset double; zero is a real greek. None where the request names no subscription, or the venue has not stated a model for it yet.
def option_model(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
short_sale_restricted
Whether the venue is restricting short sales in the contract a request is watching. The circuit breaker a venue puts on a contract that has fallen far enough in a day, which stops a short from resting below the bid. Stated on the same record as the halt, and with no field anywhere in the documented API. Not the same question as whether the contract can be borrowed, which ticks 46 and 89 already answer beside it.
def short_sale_restricted(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
short_sale_restricted_by_instrument
The same, by InstrumentId, for callers who track them themselves.
def short_sale_restricted_by_instrument(instrument)
| Parameter | Type | Description |
|---|---|---|
instrument | int | Instrument type for scanner (e.g. "STK", "FUT"). |
contract_figures
What the venue says about the contract itself, beside its prices: how many shares are on issue, and what it opened at a year ago. Both arrive on the tick carrying the price extremes and neither has a tick of its own in the documented API — a share count is a fundamentals request there, and a year-ago open has no call at all.
def contract_figures(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
contract_figures_by_instrument
The same, by InstrumentId, for callers who track them themselves.
def contract_figures_by_instrument(instrument)
| Parameter | Type | Description |
|---|---|---|
instrument | int | Instrument type for scanner (e.g. "STK", "FUT"). |
stated_figures
What one of the venue's own series last stated for a subscription, in the order the series states it. The venue runs dozens of series the documented API has no call for: a bond's analytics, an option's model volatility, the volume a contract usually opens on, what margin a future takes. Ask for one by its own number in the generic tick list, and read what it stated here. A figure the venue holds nothing for comes as the largest number its field carries.
def stated_figures(req_id, series)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
series | int |
numbered_figures
What one of the venue's numbered-table series last stated for a subscription: its figures under the venue's own numbering. Four series state their figures as two tables, whole numbers and fractional ones, each entry naming what it is before stating it. fractional picks the table; the venue numbers the two separately, so the same number in each is not the same figure.
def numbered_figures(req_id, series, fractional=False)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
series | int | |
fractional | bool |
paired_figures
The run of paired figures one series last stated for a subscription. Two series state their figures as a count and then that many pairs: the volatility the venue's own model puts on each point of a curve, and the weight it puts on each price a contract might reach.
def paired_figures(req_id, series)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
series | int |
req_spread_scan
Ask the venue to scan an underlying for strategies worth putting on. The scan goes out beside a subscription for the series the venue states its answer on, because that is how it is asked for: the series carries the answer and the scan tells the venue what to look for. Read the answer with scanned_strategies under the same request, and withdraw it with cancel_mkt_data. The documented API has no call for this at all. What the scan states about each strategy is the venue's own, in the venue's own words, and nothing here translates them.
def req_spread_scan(req_id, contract, scan)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
scan | SpreadScan | What to scan the underlying for. |
scanned_strategies
The strategies a spread scan stated for a request, as the venue stated them. Each is a dict of its legs, which shape of strategy it is and how pressing, the thirteen figures the venue states about it, where it comes out even, and one figure behind those. Empty until a scan has been asked for and answered. The documented API has no call for this.
def scanned_strategies(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
stated_rows
The rows of three figures one series last stated for a subscription. What the three are is the series' own: | Series | The three figures | | --- | --- | | 547 | A quantity, what it is offered at, and a second price where the form states one | | 491 | Which strategy the leg belongs to, the contract it names, and its size | | 320, 376, 530, 532 | The venue's number for a field of a packed quote, its figure, and how far that figure's decimal point moves | On the packed quotes the venue numbers its fields itself: nought and one are the two sides of the quote and four and five the size behind each, read off a live session. A side the venue is not standing behind reads as minus one hundred. Those figures are counted in the contract's own increments, as every packed figure is — 75815 against an increment of a hundredth is 758.15 — and no scale is put on them here, because which fields are prices and which are counts is the venue's to change. f64::MAX stands where a form states no third figure. The documented API has no call for any of these.
def stated_rows(req_id, series)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
series | int |
chain_model_parameters
What one of the option model's chain series last stated for a subscription on an underlying. A list with one dict per class of the underlying's options: the underlying's price, the dividends expected, and per expiry the yield, the interest rate, the forward and the at-the-money volatilities the model works from. Ask for series 687 for the standing set, or 691 for the set as the chain closed, in the generic tick list of a subscription on the underlying. The documented API has no call for either: a gateway reads them for its own option model and hands none of it on. Empty where the request names no subscription, or the series has stated nothing for it.
def chain_model_parameters(req_id, series)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
series | int |
stated_rows_series
Which series have stated rows for a subscription, in order.
def stated_rows_series(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
paired_figures_series
Which series have stated paired figures for a subscription, in order.
def paired_figures_series(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
numbered_figures_series
Which series have stated numbered figures for a subscription, in order.
def numbered_figures_series(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
stated_figures_series
Which series have stated figures for a subscription, in order.
def stated_figures_series(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
closing_option_model
What the venue's model made of an option as it closed. The same model as option_model and in the same shape — every greek it states, the ones the documented API has no field for included — but worked out as the contract closed rather than as it stands. The documented API has no call for it at all. Ask for it by the venue's own number for the series in the generic tick list.
def closing_option_model(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
closing_option_model_by_instrument
The same, by InstrumentId, for callers who track them themselves.
def closing_option_model_by_instrument(instrument)
| Parameter | Type | Description |
|---|---|---|
instrument | int | Instrument type for scanner (e.g. "STK", "FUT"). |
option_model_by_instrument
The same, by InstrumentId, for callers who track them themselves.
def option_model_by_instrument(instrument)
| Parameter | Type | Description |
|---|---|---|
instrument | int | Instrument type for scanner (e.g. "STK", "FUT"). |
Reference Data
req_historical_data
Request historical bar data. chart_options is checked as a gateway checks it: manual, 0 or 1, is taken and changes nothing a gateway sends; any other key is refused under 10337, another value under 10338, and an entry not written key=value under 320. Where the venue has lifted the key checks, a manual that does not read as the number nought or one is refused under 321.
def req_historical_data(req_id, contract, end_date_time, duration_str, bar_size_setting, what_to_show, use_rth, format_date=1, keep_up_to_date=False, chart_options=None)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
end_date_time | str | End date/time in "YYYYMMDD HH:MM:SS" format, or empty for now. |
duration_str | str | Duration string, e.g. "1 D", "1 W", "1 M", "1 Y". |
bar_size_setting | str | Bar size: "1 min", "5 mins", "1 hour", "1 day", etc. |
what_to_show | str | Data type: "TRADES", "MIDPOINT", "BID", "ASK", "BID_ASK", etc. |
use_rth | int | If true, only return data from Regular Trading Hours. |
format_date | int | Date format: 1="YYYYMMDD HH:MM:SS", 2=Unix seconds. |
keep_up_to_date | bool | If true, continue receiving updates after initial history. |
chart_options | list |
cancel_historical_data
Cancel historical data.
def cancel_historical_data(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
req_head_time_stamp
Request head timestamp.
def req_head_time_stamp(req_id, contract, what_to_show, use_rth, format_date=1)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
what_to_show | str | Data type: "TRADES", "MIDPOINT", "BID", "ASK", "BID_ASK", etc. |
use_rth | int | If true, only return data from Regular Trading Hours. |
format_date | int | Date format: 1="YYYYMMDD HH:MM:SS", 2=Unix seconds. |
cancel_head_time_stamp
Cancel head timestamp request.
def cancel_head_time_stamp(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
req_contract_details
Request contract details.
def req_contract_details(req_id, contract)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
cancel_contract_data
Withdraw a contract lookup. Nothing is sent and nothing answers: there is nothing to withdraw. A gateway asks the venue nothing for this either: it only stops re-sending a lookup it held back while its connection to the venue was down, and this client holds none back — a lookup made with no connection is refused there and then. A lookup already asked for is still answered, as it is through a gateway.
def cancel_contract_data(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
req_mkt_depth_exchanges
Request available exchanges for market depth.
def req_mkt_depth_exchanges()
req_matching_symbols
Search for matching symbols.
def req_matching_symbols(req_id, pattern)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
pattern | str | Symbol search pattern. |
req_sec_def_opt_params
Request option chain parameters. Every argument is stated by the caller, as the reference client requires them to be. With underlying_sec_type defaulted to stocks, a caller who left it off asked about the chains of a stock by that name rather than being told they had left it off.
def req_sec_def_opt_params(req_id, underlying_symbol, fut_fop_exchange, underlying_sec_type, underlying_con_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
underlying_symbol | str | Underlying symbol (e.g. "AAPL"). |
fut_fop_exchange | str | Exchange for futures/FOP options. |
underlying_sec_type | str | Underlying security type (e.g. "STK"). |
underlying_con_id | int | Underlying contract ID. |
req_scanner_subscription
Request scanner subscription. scanner_subscription_options is checked as a gateway checks it: manual, 0 or 1, is taken and changes nothing a gateway sends; any other key is refused under 10337, another value under 10338, and an entry not written key=value under 320. Where the venue has lifted the key checks, a manual that does not read as the number nought or one is refused under 321.
def req_scanner_subscription(req_id, subscription, scanner_subscription_options=None, scanner_subscription_filter_options=None)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
subscription | Py<PyAny> | Scanner subscription parameters. |
scanner_subscription_options | list | |
scanner_subscription_filter_options | list | Scanner filter tags from req_scanner_parameters, e.g. priceAbove = "10". |
cancel_scanner_subscription
Cancel scanner subscription.
def cancel_scanner_subscription(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
req_scanner_parameters
Request scanner parameters XML.
def req_scanner_parameters()
req_news_article
Request a news article. news_article_options is checked as a gateway checks it: manual, 0 or 1, is taken and changes nothing a gateway sends; any other key is refused under 10337, another value under 10338, and an entry not written key=value under 320. Where the venue has lifted the key checks, a manual that does not read as the number nought or one is refused under 321.
def req_news_article(req_id, provider_code, article_id, news_article_options=None)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
provider_code | str | News provider code (e.g. "BRFG"). |
article_id | str | News article identifier. |
news_article_options | list |
req_historical_news
Request historical news. A nonempty start requests newer headlines from that bound; otherwise the end requests older headlines. The selected bound is sent as stated, without parsing it locally. When both are empty, the request asks for the latest headlines. historical_news_options is checked as a gateway checks it: manual, 0 or 1, is taken and changes nothing a gateway sends; any other key is refused under 10337, another value under 10338, and an entry not written key=value under 320. Where the venue has lifted the key checks, a manual that does not read as the number nought or one is refused under 321.
def req_historical_news(req_id, con_id, provider_codes, start_date_time, end_date_time, total_results, historical_news_options=None)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
con_id | int | Contract ID. Unique per instrument. |
provider_codes | str | Pipe-separated news provider codes. |
start_date_time | str | Start date/time for tick query. |
end_date_time | str | End date/time in "YYYYMMDD HH:MM:SS" format, or empty for now. |
total_results | int | Maximum number of news results. |
historical_news_options | list |
cancel_historical_news
Withdraw a historical news query. The TWS API has no call for this; the venue has a message for it. One message carrying the number the query went out under, sent for a query still waiting on its answer. Nothing of a query is kept once it is answered, as a gateway keeps nothing of one: withdrawn after its answer, it is refused as naming nothing.
def cancel_historical_news(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
req_adjustments
Ask for a contract's corporate actions over a range of days. No callback carries the answer; a refusal arrives on error under this id, as any request's does, and gives the request up: nothing is held for it after, and there is nothing to withdraw. The answer is held under the id until adjustments_for takes it or cancel_adjustments gives it up, so a request that is neither taken nor withdrawn holds its answer for the rest of the session. corporate_actions asks and waits in one call; this is the request on its own.
def req_adjustments(req_id, con_id, sec_type, exchange, start_date, end_date)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
con_id | int | Contract ID. Unique per instrument. |
sec_type | str | |
exchange | str | Exchange name. |
start_date | str | |
end_date | str |
adjustments_for
The corporate actions answering a req_adjustments under this id, once they have arrived: one dict per action, as corporate_actions states them. Taken rather than read: the answer is handed over once and the request holds nothing after it. None until the answer arrives, and for a request this session is not holding one for. A contract the venue states nothing for answers with an empty list, which is an answer.
def adjustments_for(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
cancel_adjustments
Give up on a req_adjustments: whatever it holds is let go of, and the venue is told to stop serving the query. For a request whose answer has not come and is no longer wanted: the venue serves the query until it is withdrawn. A withdrawal naming no query this client is waiting on, one already answered included, is reported on error under 300.
def cancel_adjustments(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
req_fundamental_data
Request fundamental data. The report is asked about the stock the venue's id names: a contract given by its description is looked up first, as a gateway looks it up. A contract not stated as a stock is refused, as a gateway refuses it. fundamental_data_options is taken and nothing in it is checked or applied, as through a gateway: a gateway reads no option list on this request.
def req_fundamental_data(req_id, contract, report_type, fundamental_data_options=None)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
report_type | str | Report type: "ReportSnapshot", "ReportsFinSummary", "RESC", etc. |
fundamental_data_options | list |
cancel_fundamental_data
Cancel fundamental data.
def cancel_fundamental_data(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
req_historical_ticks
Request historical tick data. ignore_size asks the venue to leave out a bid/ask change that moves only a size, and what it answers is passed on as it stands, as a gateway passes it: nothing is filtered here. A gateway asks for midpoint ticks that way whatever the caller asked, and so does this client; for trades it is not asked. One session saw the venue answer the same with the filter as without it. misc_options is checked as a gateway checks it: manual, 0 or 1, is taken and changes nothing a gateway sends; any other key is refused under 10337, another value under 10338, and an entry not written key=value under 320. Where the venue has lifted the key checks, a manual that does not read as the number nought or one is refused under 321.
def req_historical_ticks(req_id, contract, start_date_time="", end_date_time="", number_of_ticks=1000, what_to_show="TRADES", use_rth=1, ignore_size=False, misc_options=None)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
start_date_time | str | Start date/time for tick query. |
end_date_time | str | End date/time in "YYYYMMDD HH:MM:SS" format, or empty for now. |
number_of_ticks | int | Maximum number of ticks to return. |
what_to_show | str | Data type: "TRADES", "MIDPOINT", "BID", "ASK", "BID_ASK", etc. |
use_rth | int | If true, only return data from Regular Trading Hours. |
ignore_size | bool | If true, asks that a bid/ask change moving only a size be left out. |
misc_options | list |
cancel_historical_ticks
Withdraw a historical ticks request. Nothing is sent and nothing answers: there is nothing to withdraw, as for cancel_contract_data. A gateway only stops re-sending a request it held back while its connection to the venue was down, which this client never does. Ticks already asked for still arrive, and a request waiting for its contract to be named still goes once it is, as through a gateway.
def cancel_historical_ticks(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
req_market_rule
Request market rule details.
def req_market_rule(market_rule_id)
| Parameter | Type | Description |
|---|---|---|
market_rule_id | int | Market rule ID. |
req_histogram_data
Request histogram data.
def req_histogram_data(req_id, contract, use_rth, time_period)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
use_rth | bool | If true, only return data from Regular Trading Hours. |
time_period | str | Histogram time period. |
cancel_histogram_data
Cancel histogram data.
def cancel_histogram_data(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
req_historical_schedule
Request historical trading schedule.
def req_historical_schedule(req_id, contract, end_date_time="", duration_str="1 M", use_rth=True)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
end_date_time | str | End date/time in "YYYYMMDD HH:MM:SS" format, or empty for now. |
duration_str | str | Duration string, e.g. "1 D", "1 W", "1 M", "1 Y". |
use_rth | bool | If true, only return data from Regular Trading Hours. |
Gateway-Local & Stubs
req_config_proto_buf
Request configuration. Reports 10357 on the request's error callback.
def req_config_proto_buf(config_request_proto)
| Parameter | Type | Description |
|---|---|---|
config_request_proto | Bound<'_, PyAny> |
update_config_proto_buf
Request a configuration update. Reports 10357 on the request's error callback.
def update_config_proto_buf(update_config_request_proto)
| Parameter | Type | Description |
|---|---|---|
update_config_request_proto | Bound<'_, PyAny> |
order_permissions
Security type → the order types the venue permits for it, as stated at logon. Empty until the session is up.
def order_permissions()
permitted_order_types
The order types permitted for one security type, or None when the type is not permitted at all. A combination is named COMB.
def permitted_order_types(sec_type)
| Parameter | Type | Description |
|---|---|---|
sec_type | str |
enabled_features
Feature tokens the venue enables for this account.
def enabled_features()
algorithms
Which algorithms the venue offers, keyed PROVIDER/SECTYPE.
def algorithms()
algorithms_for
The algorithms offered for one security type, across every provider.
def algorithms_for(sec_type)
| Parameter | Type | Description |
|---|---|---|
sec_type | str |
order_presets
The sets of order defaults this account holds, as (key, attributes, when it last changed). The venue keeps one per security type and fills parts of an order the caller left unstated from them, so the same call on two accounts is not the same order. The key is the venue's own. The attributes are as the venue writes them, & between them: v= names the set's variant and a=1 marks it active. The values in a set are asked for separately. The moment says when a set last changed, which is what tells a caller whether an order it sent at a given time was filled in from the old defaults or the new. Empty where the venue stated none.
def order_presets()
company_data
What the venue states about a contract's company or its terms on one series, as the pairs it wrote. Ask for the series on the market data request by the venue's own number for it. Seventeen of them carry this text: 386 is what the company has coming and when; 434 and 548 are the two analyst ratings; 454 is how much of the company institutions and insiders hold, on what date each was counted, and how many shares are on issue, from which a float is worked out rather than stated; 505 is what a fund will take part in; 628 and 633 are a fund family's figures and the same by financial year; 631 the technical readings taken on the contract; 669 the ratios the venue keeps a history of; 678 and 699 two scores worked out from what is written about the company; 700 and 705 how it scores against the principles an account can screen on; 703 what margin dealing in it takes; 726 the lens the venue publishes over its accounts; 750 the price the venue holds it against for reference; and 752 whether it passes a religious screen. The keys are the venue's own, unchanged.
def company_data(con_id, series)
| Parameter | Type | Description |
|---|---|---|
con_id | int | Contract ID. Unique per instrument. |
series | int |
company_data_series
Which of those series have been stated for a contract.
def company_data_series(con_id)
| Parameter | Type | Description |
|---|---|---|
con_id | int | Contract ID. Unique per instrument. |
calculate_implied_volatility
What volatility a price implies for an option, under the model the venue publishes for that contract. Answered on tick_option_computation. implied_vol_options is checked as a gateway checks it: this request takes no key, so any is refused under 10337, and an entry not written key=value under 320. Where the venue has lifted the key checks, a list that reads is taken. Nothing in it is sent: this client answers the calculation itself, from the venue's model.
def calculate_implied_volatility(req_id, contract, option_price, under_price, implied_vol_options=None)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
option_price | float | Option market price. |
under_price | float | Underlying asset price. |
implied_vol_options | list |
calculate_option_price
What an option is worth at a stated volatility, under the same model. Answered on tick_option_computation. opt_prc_options is checked as a gateway checks it: this request takes no key, so any is refused under 10337, and an entry not written key=value under 320. Where the venue has lifted the key checks, a list that reads is taken. Nothing in it is sent: this client answers the calculation itself, from the venue's model.
def calculate_option_price(req_id, contract, volatility, under_price, opt_prc_options=None)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
contract | Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
volatility | float | Implied volatility. |
under_price | float | Underlying asset price. |
opt_prc_options | list |
cancel_calculate_implied_volatility
Stop waiting on an implied-volatility request. A question answered in the call it was asked in leaves nothing to withdraw. One that opened a watch is holding a subscription the caller never asked for by name, and this is what releases it.
def cancel_calculate_implied_volatility(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
cancel_calculate_option_price
As for cancel_calculate_implied_volatility.
def cancel_calculate_option_price(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
req_news_bulletins
Ask for the notices the venue broadcasts to everyone. Answered on update_news_bulletin. all_msgs asks for the day's bulletins as well as the ones still to come. Nothing is sent to the venue: it broadcasts these unasked and has been doing so since the session opened, so the day's are answered from what is cached — without consuming it: the cache is the day's, a gateway clears it only at day rollover, and asking again restates it. Asking only for what follows starts the subscription where the call is made and leaves the cache to a later ask. What cannot be had either way is anything from before the session existed, because there is no request to ask for it with. The last NEWS_BULLETIN_LIMIT are kept for a caller who has not asked yet.
def req_news_bulletins(all_msgs=True)
| Parameter | Type | Description |
|---|---|---|
all_msgs | bool | If true, receive all existing bulletins on subscribe. |
cancel_news_bulletins
Stop receiving broadcast notices.
def cancel_news_bulletins()
req_current_time
Ask for the venue's own clock. Answered on current_time. Before a session exists this is reported on error, the way every request made before connecting is: an answer waits for a dispatch pass, and with no session there is nothing to make one.
def req_current_time()
req_current_time_in_millis
Ask for the venue's own clock in milliseconds. Answered on current_time_in_millis. The same clock req_current_time reports and worked out the same way. What differs is the precision kept: asking in seconds throws away the fraction this one keeps. Before a session exists this is reported on error, as req_current_time is.
def req_current_time_in_millis()
request_fa
Ask the venue for a partition of the advisor's own configuration. The reference client names the partition by a number: its groups, its allocation profiles, its aliases. The allocation profiles are refused at the door under 585, with nothing sent, as the reference client refuses them. For the rest, the venue names the partition by a word, so the number is turned into the word it stands for; a number that stands for nothing draws the intake's own refusal rather than being sent as an empty partition. The venue's answer reaches receive_fa under the same number the partition was asked for by.
def request_fa(fa_data_type)
| Parameter | Type | Description |
|---|---|---|
fa_data_type | int | FA data type (1=Groups, 2=Profiles, 3=Aliases). |
replace_fa
Replace a partition of the advisor's configuration with the one given. The allocation profiles are refused at the door under 585, as the reference client refuses them. replace_fa_end fires with req_id once the venue has taken it, and a venue that refuses states why on error under the same number.
def replace_fa(req_id, fa_data_type, cxml)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
fa_data_type | int | FA data type (1=Groups, 2=Profiles, 3=Aliases). |
cxml | str | FA XML configuration data. |
query_display_groups
Ask which display groups exist. Answered on display_group_list.
def query_display_groups(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
subscribe_to_group_events
Watch what a display group is showing. Answered on display_group_updated.
def subscribe_to_group_events(req_id, group_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
group_id | int | Display group ID. |
unsubscribe_from_group_events
Stop watching a display group.
def unsubscribe_from_group_events(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
update_display_group
Tell a display group what to show.
def update_display_group(req_id, contract_info)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
contract_info | str | Display group contract info string. |
verify_request
Answered as the reference client answers it: on error, under 508 and no request, because intent to authenticate is stated on the initial connect and was not. Nothing is sent, so api_name and api_version reach nothing.
def verify_request(api_name, api_version)
| Parameter | Type | Description |
|---|---|---|
api_name | str | |
api_version | str |
verify_and_auth_request
As verify_request: api_name, api_version and opaque_isv_key reach nothing.
def verify_and_auth_request(api_name, api_version, opaque_isv_key)
| Parameter | Type | Description |
|---|---|---|
api_name | str | |
api_version | str | |
opaque_isv_key | str |
verify_message
Nothing is sent and nothing answers. A gateway reads this message and discards it, so a program on one is answered by nothing either, and api_data reaches nothing there or here.
def verify_message(api_data)
| Parameter | Type | Description |
|---|---|---|
api_data | str |
verify_and_auth_message
As verify_message: api_data and xyz_response reach nothing.
def verify_and_auth_message(api_data, xyz_response)
| Parameter | Type | Description |
|---|---|---|
api_data | str | |
xyz_response | str |
req_smart_components
Ask which venue each bit of a quote's exchange mask refers to. The venue states the map beside the quote, so a quote has to have been asked for first. Answered on smart_components. bbo_exchange names the map: the one tick_req_params states for the contract. The venue states a map per BBO exchange and security type, so one contract's venues are not another's. A BBO exchange no subscription named is refused as a gateway refuses it; one whose map has not arrived yet is waited for up to two seconds, as a gateway waits, and answered from the dispatch loop rather than by holding this call.
def req_smart_components(req_id, bbo_exchange)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
bbo_exchange | str | BBO exchange for smart component lookup (e.g. "SMART"). |
req_news_providers
Ask which news providers this account may read. Answered on news_providers.
def req_news_providers()
req_soft_dollar_tiers
Ask which soft dollar tiers this account may direct commission to. Answered on soft_dollar_tiers.
def req_soft_dollar_tiers(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
req_family_codes
Ask which account families this login belongs to. Answered on family_codes.
def req_family_codes()
set_server_log_level
How much to log about this session, 1 to 5. 1 to 5 are a gateway's System, Error, Warning, Info and Detail, and set this client's logger to error, error, warn, info and trace. A gateway applies the level to its own log; this client, which serves the caller in its place, applies it to the logger it installed. Nothing goes to the venue, which has no message for it. Where the program installed a logger of its own, the call says so on error rather than reporting a level it did not set. A level outside 1 to 5 is refused rather than reported back as warn, which would tell a caller they had a level that does not exist.
def set_server_log_level(log_level=2)
| Parameter | Type | Description |
|---|---|---|
log_level | int | A gateway's level: 1=System, 2=Error, 3=Warning, 4=Info, 5=Detail; this client's logger goes to error, error, warn, info and trace. |
req_user_info
Ask what this login is entitled to. Answered on user_info.
def req_user_info(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
req_wsh_meta_data
What event types the corporate-events calendar carries. Answered on wshMetaData.
def req_wsh_meta_data(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
cancel_wsh_meta_data
Stop waiting on the event types. The query is one message and one answer, so there is nothing at the venue to withdraw: what is withdrawn is the answer, which would otherwise reach a caller who has said they are done with it. A cancel naming no waiting request says so rather than returning as though it acted.
def cancel_wsh_meta_data(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
cancel_wsh_event_data
Stop waiting on the calendar's events. As above.
def cancel_wsh_event_data(req_id)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
req_wsh_event_data
The calendar's events. Answered on wshEventData. wsh_event_data is the object the public API takes: a contract id, or a filter the caller writes, plus the window and what to fill from.
def req_wsh_event_data(req_id, wsh_event_data=None)
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
wsh_event_data | Py<PyAny> or None |
EWrapper Callbacks
new
Create a new EClient (or EWrapper) instance.
| Parameter | Type | Description |
|---|---|---|
args | Bound<'_, pyo3::types::PyTuple> | |
kwargs | Bound<'_, pyo3::types::PyDict> or None |
connect_ack
The session is open. Nothing has been asked for yet.
connection_closed
The session is over, because this client ended it. A session that went away instead is reported on error under 1100.
next_valid_id
The first order id this session may use. Each order needs one higher than the last.
| Parameter | Type | Description |
|---|---|---|
order_id | int | Order identifier. Must be unique per session. |
managed_accounts
Every account this login may act for, separated by commas. One for most logins; an advisor has several.
| Parameter | Type | Description |
|---|---|---|
accounts_list | str | Comma-separated account IDs. |
error
What the venue said about a request, under the number it says it with. Codes from 2100 to 2200 are notices about a connection rather than failures. req_id is -1 for anything that answers no particular request. A request this client will not send is reported here too, under the same numbers the reference client uses: 321 for a request that fails validation, 200 for a contract description that matches nothing, 504 for a call made with no session. error_time is the reference client's second parameter, in milliseconds since the epoch. A gateway stamps every error it sends, and so does this client: what the venue said about an order on a report, a refusal or a message, with the second the report states it was sent, and every other error with the clock as it is delivered.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
error_time | int | When the venue sent the report it comes from, to the second, for what the venue said about an order; otherwise when this client delivered it. In milliseconds since the epoch. |
error_code | int | Error code. |
error_string | str | Error message. |
advanced_order_reject_json | str | JSON with advanced rejection details. |
error_from
An error, with what it is about: a request and whether nothing more follows for it, an order and the operation it answers, a request that carries no number, the session, or a lookup this client made for itself. The number error carries can be any of these and does not say which; origin says. Every error reaches a subclass of this class here. By default it goes on to error, under the number origin.id states it under, so a subclass that overrides only error is told exactly what it was told before. A wrapper that is not a subclass and has no method of this name is called on error, as the reference client calls it.
| Parameter | Type | Description |
|---|---|---|
origin | ErrorOrigin | What the error is about: a request, an order and the operation on it, a request with no number of its own, the session, or a lookup this client made for itself. |
error_time | int | When the venue sent the report it comes from, to the second, for what the venue said about an order; otherwise when this client delivered it. In milliseconds since the epoch. |
error_code | int | Error code. |
error_string | str | Error message. |
advanced_order_reject_json | str | JSON with advanced rejection details. |
current_time
The venue clock, in seconds since the epoch.
| Parameter | Type | Description |
|---|---|---|
time | int | Tick timestamp (Unix seconds). |
current_time_in_millis
The venue clock, in milliseconds since the epoch. The same clock current_time reports, at the precision the venue stated it in. The stamp can carry a fraction of a second and this reads it where it does, but the stamps measured against this venue carried none, so the answer lands on a whole second.
| Parameter | Type | Description |
|---|---|---|
time_in_millis | int |
tick_price
One price of a quote, and which price it is. tick_type names it — 1 bid, 2 ask, 4 last, 9 close — and attrib says whether it can be traded against and whether it is past its limit. A size arrives on tick_size under the type that belongs to it.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
tick_type | int | Tick type ID or tick-by-tick type string. |
price | float | Tick price. |
attrib | Py<PyAny> | Tick attributes. |
tick_size
One size of a quote, and which size it is: 0 bid, 3 ask, 5 last, 8 the day's volume.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
tick_type | int | Tick type ID or tick-by-tick type string. |
size | Py<PyAny> | Tick size. |
tick_string
A quote's value that is not a number — a timestamp, an exchange map, a set of ids.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
tick_type | int | Tick type ID or tick-by-tick type string. |
value | str | Account value. |
tick_generic
A quote's value that is a number and is not a price or a size — an implied volatility, an index future's premium, a halt.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
tick_type | int | Tick type ID or tick-by-tick type string. |
value | float | Account value. |
tick_snapshot_end
A snapshot has stated everything it is going to. Only for a subscription asked for as a snapshot; a streaming one never ends.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
market_data_type
Which feed a subscription is being served from: 1 live, 2 frozen, 3 delayed, 4 delayed and frozen.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
market_data_type | int | 1=live, 2=frozen, 3=delayed, 4=delayed-frozen. |
order_status
Where an order stands now: stated as the venue reports on the order, again on each fill, and after the order's open_order when open orders are asked for. Sending a cancel states nothing, as a gateway states nothing then: asked for, the order reads PendingCancel, and the venue's next report on it states it so. filled and remaining are shares, avg_fill_price the average of what has filled so far.
| Parameter | Type | Description |
|---|---|---|
order_id | int | Order identifier. Must be unique per session. |
status | str | Order status string ("Submitted", "Filled", "Cancelled", etc.). |
filled | Py<PyAny> | Cumulative filled quantity. |
remaining | Py<PyAny> | Remaining quantity. |
avg_fill_price | float | Average fill price. |
perm_id | int | The order's permanent id: the number it goes to the venue under. |
parent_id | int | Parent order ID (0 if no parent). |
last_fill_price | float | Price of the last fill. |
client_id | int | API client ID for order ownership and the saved order-id counter. |
why_held | str | Reason the order is held (e.g. "locate"). |
mkt_cap_price | float | Market cap price for the order. |
open_order
An order as the venue holds it, and the state it is in. Fires beside every order_status, when open orders are asked for, and once for a preview — where the state carries what the order would cost and no status follows, because a preview is not an order.
| Parameter | Type | Description |
|---|---|---|
order_id | int | Order identifier. Must be unique per session. |
contract | Py<PyAny> | Contract specification (symbol, secType, exchange, currency, etc.). |
order | Py<PyAny> | Order parameters (action, quantity, type, price, TIF, etc.). |
order_state | Py<PyAny> | Order state (status, margin, commission info). |
open_order_end
Every open order has been stated.
exec_details
One fill, against the order and contract it filled. What it cost arrives separately, on commission_and_fees_report.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
contract | Py<PyAny> | Contract specification (symbol, secType, exchange, currency, etc.). |
execution | Py<PyAny> | Execution details (exec_id, time, price, shares, etc.). |
exec_details_end
Every execution answering this request has been stated.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
commission_and_fees_report
What a fill cost, matched to it by execution id.
| Parameter | Type | Description |
|---|---|---|
commission_and_fees_report | Py<PyAny> |
update_account_value
One figure the venue states about an account, in the currency it states it in. An account is stated in several currencies at once, so the same key arrives more than once.
| Parameter | Type | Description |
|---|---|---|
key | str | Account value key (e.g. "NetLiquidation", "BuyingPower"). |
value | str | Account value. |
currency | str | Currency code (e.g. "USD"). |
account_name | str | Account identifier. |
update_portfolio
One position, as the venue values it now.
| Parameter | Type | Description |
|---|---|---|
contract | Py<PyAny> | Contract specification (symbol, secType, exchange, currency, etc.). |
position | Py<PyAny> | Book position (row index) or position size. |
market_price | float | Current market price. |
market_value | float | Current market value of position. |
average_cost | float | Average cost basis. |
unrealized_pnl | float | Unrealized profit/loss. |
realized_pnl | float | Realized profit/loss. |
account_name | str | Account identifier. |
update_account_time
When the account figures above were last stated.
| Parameter | Type | Description |
|---|---|---|
timestamp | str | Timestamp string. |
account_download_end
The account has been fully stated. Fires once the venue has stopped adding to it, not on the first figure.
| Parameter | Type | Description |
|---|---|---|
account | str | Account ID. |
account_summary
One figure answering req_account_summary, in the currency the venue states it in.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
account | str | Account ID. |
tag | str | Account tag name (e.g. "NetLiquidation"). |
value | str | Account value. |
currency | str | Currency code (e.g. "USD"). |
account_summary_end
Every figure answering this request has been stated.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
position
One position held, on any account this login may act for.
| Parameter | Type | Description |
|---|---|---|
account | str | Account ID. |
contract | Py<PyAny> | Contract specification (symbol, secType, exchange, currency, etc.). |
pos | Py<PyAny> | Position size (decimal shares). |
avg_cost | float | Average cost per share. |
position_end
Every position has been stated.
pnl
An account's running profit: today's, what is unrealised, and what has been realised.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
daily_pnl | float | Daily profit/loss. |
unrealized_pnl | float | Unrealized profit/loss. |
realized_pnl | float | Realized profit/loss. |
pnl_single
The same for one position, with the size held.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
pos | Py<PyAny> | Position size (decimal shares). |
daily_pnl | float | Daily profit/loss. |
unrealized_pnl | float | Unrealized profit/loss. |
realized_pnl | float | Realized profit/loss. |
value | float | Account value. |
historical_data
One bar answering a historical request. bar.date is a day for a daily bar and a moment for anything shorter, in the zone the bar carries.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
bar | Py<PyAny> | Bar data (date, open, high, low, close, volume, wap, bar_count). |
historical_data_end
Every bar answering this request has been stated, and the window they cover.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
start | str | Period start date/time. |
end | str | Period end date/time. |
historical_data_update
A bar that continues a keep_up_to_date request, after its first batch completed. The bar still forming is restated as it changes.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
bar | Py<PyAny> | Bar data (date, open, high, low, close, volume, wap, bar_count). |
head_timestamp
The earliest moment the venue holds data for a contract.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
head_timestamp | str | Earliest available data timestamp string. |
contract_details
One contract matching a description, with everything the venue states about it. A description can match more than one.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
contract_details | Py<PyAny> |
contract_details_end
Every contract matching this request has been stated.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
symbol_samples
Contracts whose symbol or name matches a pattern, across venues.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
contract_descriptions | Py<PyAny> |
tick_by_tick_all_last
One trade, as it happens. tick_attrib_last says whether it was past a limit and whether it goes unreported to the tape.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
tick_type | int | Tick type ID or tick-by-tick type string. |
time | int | Tick timestamp (Unix seconds). |
price | float | Tick price. |
size | Py<PyAny> | Tick size. |
tick_attrib_last | Py<PyAny> | |
exchange | str | Exchange name. |
special_conditions | str | Special trade conditions. |
tick_by_tick_bid_ask
One change to the top of the book, as it happens.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
time | int | Tick timestamp (Unix seconds). |
bid_price | float | Bid price. |
ask_price | float | Ask price. |
bid_size | Py<PyAny> | Bid size. |
ask_size | Py<PyAny> | Ask size. |
tick_attrib_bid_ask | Py<PyAny> |
tick_by_tick_mid_point
One change to the midpoint, as it happens.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
time | int | Tick timestamp (Unix seconds). |
mid_point | float | Midpoint price. |
scanner_data
One row of a scan, in rank order.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
rank | int | Scanner result rank (0-based). |
contract_details | Py<PyAny> | |
distance | str | Scanner distance metric. |
benchmark | str | Scanner benchmark. |
projection | str | Scanner projection. |
legs_str | str | Combo legs description. |
scanner_data_end
Every row of this scan has been stated.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
scanner_parameters
Every scan the venue offers and what each can be filtered by, as the XML the venue publishes.
| Parameter | Type | Description |
|---|---|---|
xml | str | XML string. |
news_providers
Every news provider this account may read.
| Parameter | Type | Description |
|---|---|---|
news_providers | Py<PyAny> |
news_article
The body of one article. article_type is 0 for text and 1 for a binary document.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
article_type | int | Article type: 0=plain text, 1=HTML. |
article_text | str | Full article body. |
historical_news
One headline from the archive.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
time | str | Tick timestamp (Unix seconds). |
provider_code | str | News provider code (e.g. "BRFG"). |
article_id | str | News article identifier. |
headline | str | News headline text. |
historical_news_end
Every headline answering this request has been stated, and whether the archive holds more.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
has_more | bool | If true, more results available. |
tick_news
A headline about a contract being watched, as it is published.
| Parameter | Type | Description |
|---|---|---|
ticker_id | int | Ticker/request ID. |
time_stamp | int | Timestamp string. |
provider_code | str | News provider code (e.g. "BRFG"). |
article_id | str | News article identifier. |
headline | str | News headline text. |
extra_data | str | Additional tick data. |
update_mkt_depth
One level of a book that names no venue. operation is 0 to insert, 1 to update, 2 to delete; side is 0 ask, 1 bid.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
position | int | Book position (row index) or position size. |
operation | int | Book operation: 0=insert, 1=update, 2=delete. |
side | int | Book side: 0=ask, 1=bid. Or order side "BOT"/"SLD". |
price | float | Tick price. |
size | Py<PyAny> | Tick size. |
update_mkt_depth_l2
One level of a book that names the venue it stands on. Every level from this client names one.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
position | int | Book position (row index) or position size. |
market_maker | str | Market maker ID. |
operation | int | Book operation: 0=insert, 1=update, 2=delete. |
side | int | Book side: 0=ask, 1=bid. Or order side "BOT"/"SLD". |
price | float | Tick price. |
size | Py<PyAny> | Tick size. |
is_smart_depth | bool | If true, aggregate depth from multiple exchanges via SMART. |
mkt_depth_exchanges
Every exchange the venue names, in the two sections it names them in: shares and derivatives.
| Parameter | Type | Description |
|---|---|---|
depth_mkt_data_descriptions | Py<PyAny> |
real_time_bar
One five-second bar of a live stream.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
date | int | Bar date string. |
open | float | Open price. |
high | float | High price. |
low | float | Low price. |
close | float | Close price. |
volume | Py<PyAny> | Volume. |
wap | Py<PyAny> | Volume-weighted average price. |
count | int | Trade count. |
historical_ticks
Historical midpoints, in batches, until done.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
ticks | Py<PyAny> | Historical tick data. |
done | bool | If true, all ticks have been delivered. |
historical_ticks_bid_ask
Historical quotes, in batches, until done.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
ticks | Py<PyAny> | Historical tick data. |
done | bool | If true, all ticks have been delivered. |
historical_ticks_last
Historical trades, in batches, until done.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
ticks | Py<PyAny> | Historical tick data. |
done | bool | If true, all ticks have been delivered. |
tick_option_computation
The venue's model for an option: the volatility its price implies, the greeks, and the modelled value of the option and its underlying. Every figure is None where the venue stated none. The reference client hands a caller None for those, so this surface does too, and a wrapper that has not overridden this is handed the same. Declared as a number, the default refused the call and the exception left the caller's reading loop: a caller who watched an option and did not write this method lost the session on the first model the venue did not fill in.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
tick_type | int | Tick type ID or tick-by-tick type string. |
tick_attrib | int | |
implied_vol | float or None | Implied volatility. |
delta | float or None | Option delta. |
opt_price | float or None | Option theoretical price. |
pv_dividend | float or None | Present value of dividends. |
gamma | float or None | Option gamma. |
vega | float or None | Option vega. |
theta | float or None | Option theta. |
und_price | float or None | Underlying price. |
security_definition_option_parameter
One venue's option chain for an underlying and trading class: the expiries and strikes it lists for that class.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
exchange | str | Exchange name. |
underlying_con_id | int | Underlying contract ID. |
trading_class | str | Trading class. |
multiplier | str | Contract multiplier. |
expirations | Py<PyAny> | Available expiration dates. |
strikes | Py<PyAny> | Available strike prices. |
security_definition_option_parameter_end
Every venue's chain has been stated.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
fundamental_data
A fundamental report, as the XML the venue publishes.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
data | str | Raw data string (XML/JSON). |
update_news_bulletin
A notice the venue broadcasts to everyone — an exchange unavailable, a system message.
| Parameter | Type | Description |
|---|---|---|
msg_id | int | Bulletin message ID. |
msg_type | int | Bulletin message type (1=regular, 2=exchange). |
message | str | Bulletin message text. |
orig_exchange | str | Originating exchange. |
receive_fa
A partition of an advisor's configuration, as the XML the venue holds it in.
| Parameter | Type | Description |
|---|---|---|
fa_data_type | int | FA data type (1=Groups, 2=Profiles, 3=Aliases). |
xml | str | XML string. |
replace_fa_end
An advisor configuration has been replaced.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
text | str | Informational text. |
position_multi
One position, for a request naming an account or a model.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
account | str | Account ID. |
model_code | str | Model portfolio code (empty for default). |
contract | Py<PyAny> | Contract specification (symbol, secType, exchange, currency, etc.). |
pos | Py<PyAny> | Position size (decimal shares). |
avg_cost | float | Average cost per share. |
position_multi_end
Every position answering this request has been stated.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
account_update_multi
One account figure, for a request naming an account or a model.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
account | str | Account ID. |
model_code | str | Model portfolio code (empty for default). |
key | str | Account value key (e.g. "NetLiquidation", "BuyingPower"). |
value | str | Account value. |
currency | str | Currency code (e.g. "USD"). |
account_update_multi_end
Every figure answering this request has been stated.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
display_group_list
Which display groups exist, as the venue numbers them.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
groups | str | FA group definitions. |
display_group_updated
What a display group is now showing.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
contract_info | str | Display group contract info string. |
market_rule
The price ladder a contract trades on: each step, and what the price moves in above it.
| Parameter | Type | Description |
|---|---|---|
market_rule_id | int | Market rule ID. |
price_increments | Py<PyAny> | Price increment rules [{low_edge, increment}]. |
smart_components
Which venue each bit of a quote's exchange mask refers to, and the letter that venue is named by.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
smart_component_map | Py<PyAny> |
soft_dollar_tiers
The soft dollar tiers this account may direct commission to.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
tiers | Py<PyAny> | Soft dollar tier list. |
family_codes
The account families this login belongs to.
| Parameter | Type | Description |
|---|---|---|
family_codes | Py<PyAny> |
histogram_data
How much traded at each price over a window.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
items | Py<PyAny> | Histogram entries [(price, count)]. |
user_info
What the login is entitled to, as the venue states it.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
white_branding_id | str | White branding ID (empty for standard accounts). |
wsh_meta_data
What the corporate-events calendar carries: its event types and the fields each one has, as the JSON the venue publishes.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
data_json | str |
wsh_event_data
Events from the corporate-events calendar, as the JSON the venue publishes. Events themselves need a Wall Street Horizon subscription; a login without one is answered with an empty set.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
data_json | str |
completed_order
An order that is done — filled, cancelled or expired — as the venue holds it.
| Parameter | Type | Description |
|---|---|---|
contract | Py<PyAny> | Contract specification (symbol, secType, exchange, currency, etc.). |
order | Py<PyAny> | Order parameters (action, quantity, type, price, TIF, etc.). |
order_state | Py<PyAny> | Order state (status, margin, commission info). |
completed_orders_end
Every completed order has been stated.
order_bound
An order placed elsewhere has been bound to this session, so its changes arrive here.
| Parameter | Type | Description |
|---|---|---|
order_id | int | Order identifier. Must be unique per session. |
api_client_id | int | |
api_order_id | int |
tick_req_params
What a subscription was given: the increment its prices move in, which venues it is served from, and which feed answered. Sent once per request, with the first record it is served, as a gateway sends it; a request served none is sent none.
| Parameter | Type | Description |
|---|---|---|
ticker_id | int | Ticker/request ID. |
min_tick | float | Minimum tick size. |
bbo_exchange | str | BBO exchange for smart component lookup (e.g. "SMART"). |
snapshot_permissions | int | What the venue says this request may be given: 0 nothing stated, 1 no top of book, 2 snapshots, 3 real-time top of book, 4 snapshots not available through the API. 0 where the venue names no BBO 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. |
bond_contract_details
One bond matching a description, with its terms: what it pays, how and when, whether it can be called or put, whether it converts, what it is rated.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
contract_details | Py<PyAny> |
reroute_mkt_data_req
The contract a market-data request should be asked for under instead. A gateway sends it, and asks the venue nothing, for a contract for difference whose own definition asks for its underlying's data, where the account's logon permissions allow that: the underlying's contract and the venue to ask on. This client does not read a definition's flags for asking on the underlying before subscribing: the request goes to the venue as asked, and this never fires.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
con_id | int | Contract ID. Unique per instrument. |
exchange | str | Exchange name. |
reroute_mkt_depth_req
The same, for a request for the book rather than the quote.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
con_id | int | Contract ID. Unique per instrument. |
exchange | str | Exchange name. |
delta_neutral_validation
The contract the venue paired with a delta-neutral order. Declared by the TWS API and never fired on a gateway: a gateway never sends it. It fires here as it does there: never.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
delta_neutral_contract | Py<PyAny> |
tick_efp
An exchange-for-physical quote. Declared by the TWS API and never fired on a gateway, so it fires here as it does there: never.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
tick_type | int | Tick type ID or tick-by-tick type string. |
basis_points | float | |
formatted_basis_points | str | |
implied_future | float | |
hold_days | int | |
future_last_trade_date | str | |
dividend_impact | float | |
dividends_to_last_trade_date | float |
verify_message_api
A step in the TWS API's verification handshake. Declared by the TWS API and never fired on a gateway, so these four fire here as they do there: never.
| Parameter | Type | Description |
|---|---|---|
api_data | str |
verify_completed
Whether that handshake was accepted.
| Parameter | Type | Description |
|---|---|---|
is_successful | bool | |
error_text | str |
verify_and_auth_message_api
The same handshake, where the terminal also authenticates the program.
| Parameter | Type | Description |
|---|---|---|
api_data | str | |
xyz_challenge | str |
verify_and_auth_completed
Whether that one was accepted.
| Parameter | Type | Description |
|---|---|---|
is_successful | bool | |
error_text | str |
win_error
Declared by the TWS API as winError. No message on the wire carries it, and the TWS API's Python client declares it and never raises it, so it fires here as it does there: never. Here, trouble on a connection reaches a caller on the error callback.
| Parameter | Type | Description |
|---|---|---|
text | str | Informational text. |
last_error | int |
historical_schedule
When a contract's venue was open over a window, session by session, in the zone the venue keeps.
| Parameter | Type | Description |
|---|---|---|
req_id | int | Request identifier. Used to match responses to requests. |
start_date_time | str | Start date/time for tick query. |
end_date_time | str | End date/time in "YYYYMMDD HH:MM:SS" format, or empty for now. |
time_zone | str | Timezone string (e.g. "US/Eastern"). |
sessions | Py<PyAny> | Trading sessions [(ref_date, open, close)]. |
Rust API
| Type | What it is |
|---|---|
ib_dx::EClient / ib_dx::Wrapper | The reference client's shape: a request under an id, and an answer later on a callback you implement |
ib_dx::Config | What a session is opened with. The same type EClient takes |
The same EClient also carries the calls that answer and
the calls beyond the documented API.
Contract and Order are at ib_dx::api::client::{Contract, Order}.
EClientConfig.gateway.order_id_file selects the durable order-id counter;
Some(String::new()) disables persistence. See
order IDs across sessions
for its default location, reservations and moving the file.
The full reference
Build the full reference from the source:
cargo doc --no-deps --open
That is the full API, not a summary. The crate carries #![deny(missing_docs)],
so a public item with nothing written on it fails the build rather than shipping
bare, and CI builds the documentation with -D warnings, so an intra-doc link
that stops resolving fails too.
Snapshot of this release
Below is a flat snapshot of the same surface — grouped by area, with signatures
and field tables — for reading without a checkout. It is generated by
scripts/gen_api_docs.py from the source; CI regenerates it on every push and
fails if what is committed has drifted.
Rust API Reference (v0.1.0)
Auto-generated from source — do not edit.
Table of Contents
- EClient: Connection
- EClient: Calls That Answer
- EClient: Account & Portfolio
- EClient: Orders
- EClient: Market Data
- EClient: Reference Data
- EClient: Gateway-Local & Stubs
- Wrapper Callbacks
Connection
connect
Connect to IB and start the engine. The session states next_valid_id once, as a gateway states it to a client that has just connected: the process_msgs read after the venue has named what the account is working delivers it.
pub fn connect(config: &EClientConfig) -> Result<Self, Box<dyn std::error::Error>>
| Parameter | Type | Description |
|---|---|---|
config | &EClientConfig | Connection configuration (username, password, host, paper, client_id, core_id). |
Returns: Result<Self, Box<dyn std::error::Error>>
connect_with_events
Connect to IB and start the engine with an event channel attached. A second, optional delivery path for a program that would rather own a queue than be called back. It is bounded, and an event arriving at a full one is discarded rather than made to wait — a session that stalled on a slow reader would stop carrying market data. Read events_lost to learn whether that happened. One reader, and it is told what it drains and nothing else. For a program that wants none of it dropped, drive process_msgs with a Wrapper instead: its callbacks are called with the message rather than sent a copy of it, so there is no queue to fill and nothing to fall out of one. This is a second, optional delivery path that runs alongside process_msgs — it does not replace it, and nothing is removed from the wrapper callbacks when it is in use. The channel is bounded by capacity; the engine never blocks on it, so a consumer that falls behind loses events rather than slowing the hot loop. Drain it from a thread that is not the one calling process_msgs(), or keep capacity generous. Attaching a channel makes the engine build events it would otherwise skip, which for bar batches and contract definitions means one deep copy each. Use connect() when you only need the wrapper callbacks.
pub fn connect_with_events( config: &EClientConfig, capacity: usize, ) -> Result<(Self, Receiver<Event>), Box<dyn std::error::Error>>
| Parameter | Type | Description |
|---|---|---|
config | &EClientConfig | Connection configuration (username, password, host, paper, client_id, core_id). |
capacity | usize |
Returns: Result<(Self, Receiver<Event>), Box<dyn std::error::Error>>
traffic
What this session has sent and received on the venue's connections since it opened: bytes and messages, each way, across every connection it has held, those a reconnect opened included. Bytes are the protocol bytes read or written on the established connections, before TLS encryption and after decryption; messages are whole frames, including heartbeats. Authentication before a connection is established is outside these counts. What a TWS client reads as its connection's statistics.
pub fn traffic(&self) -> Traffic
Returns: Traffic
events_lost
How many events the channel from connect_with_events discarded. The engine never waits on a reader — a session that stalled on one would stop carrying market data — so an event arriving at a full channel is dropped. A program that acted on every fill it saw needs to know the difference between that and every fill there was. Zero for a session with no channel attached, and for one whose reader kept up.
pub fn events_lost(&self) -> u64
Returns: u64
is_connected
False after disconnect(), after the engine has ended the session, and after a process_msgs() call that observed the engine stopping. True while a lost connection to the venue is being rebuilt, between the 1100 and the 1102, as a program connected to a gateway reads it: the gateway's socket stays up. The engine records an ended session itself. A shape that never pumps process_msgs hears of it nowhere else, and kept saying connected after the session underneath it was over.
pub fn is_connected(&self) -> bool
Returns: bool
session_over
Whether this session is finished rather than merely disconnected: closed by disconnect(), or given up on by the engine, which records why. A loss the engine is still working on is neither — is_connected() reads true between the 1100 and the 1102, and a request made then is carried when the transports come back. Refused under 504 there, a withdrawal was left unapplied and the feed came back with the session; the reference client serves that window.
pub fn session_over(&self) -> bool
Returns: bool
server_version
The protocol level this client implements: 217, the reference client's MIN_SERVER_VER_ADDITIONAL_ORDER_PARAMS_2. None once the session is over, as the reference client answers before its greeting — and not while a lost connection is being recovered, which the reference client rides out holding the number. In the reference architecture this number is the API level of the process a program is talking to. That process was a gateway, which announced it and had every request gated on it; here it is this client, so the number is a statement about this client and not a reading off the venue, whose logon names no such level. Attached orders (218) load the selected account preset and construct the parent and children. Percentage allocations still need positions by account and model and the applicable allocation-group state, so the level remains 217. A percentage allocation cannot yet determine the quantities of its attached children here; supply explicitly sized parent and child orders when those quantities depend on group or model holdings. Configuration requests (219, 221) and the last price and size stated to their precision (222, 224) are absent. hedgeMaxSize (223) is sent on a beta hedge, and odd-lot quotes (225) are served: generic tick 787 is asked for and its prices, sizes and venues delivered. conditionsIncludeOvernight (226) is sent with an order's conditions, and refused under 10371 when the order is placed where the logon does not enable it or the contract trades on no overnight venue. 226 is the highest level a gateway announces. Below it, a program that believes the number is wrong about the following, and each is said on use rather than passed over: * An order field this client does not carry, refused by name on error under 321 when the order is placed: smartComboRoutingParams (57). * A withdrawal stating a manual time (169): the withdrawal goes, with its operator and who entered it, and the caller is told on error that the time did not travel. Every other gate at or below 217 names a request, field or callback that is here and does what it does through a gateway.
pub fn server_version(&self) -> Option<i32>
Returns: Option<i32>
tws_connection_time
When the venue says this session logged in, by its own clock and in its own spelling; None once the session is over, and held while a lost connection is recovered, as the level is. The reference client answers the time its gateway stamped on its greeting. The venue stamps every message it sends with the time it sent it, the answer to the logon included, and this is that stamp — the clock competing_session reads the other session's logon off. Where the venue stamped none, the connect holds this machine's clock instead and says so in the log.
pub fn tws_connection_time(&self) -> Option<String>
Returns: Option<String>
start_api
Nothing to start. The reference client sends its client id here and its gateway begins the exchange on receiving it; here the session is up and its engine running by the time connect returns, so there is nothing left to begin. Once the session is over this is reported the way the reference client reports a call with no session: on error, under 504 and no request.
pub fn start_api(&self)
refuse
Push a refusal into the session's order, at the call. For a value the caller cannot hand the engine because the engine's types cannot carry it: the refusal then takes its place in the session's one order, after everything pushed before this call, as a gateway's rejection arrives after everything the gateway wrote before it. Delivered by process_msgs on error, under the number origin names.
pub fn refuse(&self, origin: crate::types::model::ErrorOrigin, code: i64, msg: &str)
| Parameter | Type | Description |
|---|---|---|
origin | crate::types::model::ErrorOrigin | What the error is about: a request, an order and the operation on it, a request with no number of its own, the session, or a lookup this client made for itself. |
code | i64 | |
msg | &str |
wait_for_data
Wait for the engine to signal, for at most timeout: true when it signalled, false when the wait ran out. The engine signals at the end of each pass of its loop, and when a connection goes or comes back. One waiter takes each signal. A thread that reads the session only when there may be something to read waits here and then calls process_msgs: true is a reason to read, not a promise that the read delivers anything.
pub fn wait_for_data(&self, timeout: std::time::Duration) -> bool
| Parameter | Type | Description |
|---|---|---|
timeout | std::time::Duration | The longest to wait. |
Returns: bool
on_data
Be called when this session has something to read. The hook runs on the engine's own thread, after a pass of its loop that pushed a record or wrote a quote, a holding or an account figure, and at most once until the next process_msgs begins: an idle session never calls it, and a busy one calls it once per read however much arrives. It is called holding none of the engine's locks, so it may take a lock the engine takes, but it must return at once: the loop that reads the venue's sockets waits for it. A hook replaces the one before it, and None removes it. A hook that panics is caught, logged and removed, and the engine goes on.
pub fn on_data(&self, hook: Option<Arc<dyn Fn() + Send + Sync>>)
| Parameter | Type | Description |
|---|---|---|
hook | Option<Arc<dyn Fn( |
keep_record
Deliver to record everything a call that answers reads, its own answer under its own number included. A call that answers rather than delivers — contract_details, historical_data and the others that hand back what they asked for — holds the session's turn while it waits. With a record kept here, each of its pumps is a whole read of the session, delivered to the record and to the call's collector together: the record receives everything in the session's order, the call's own answer in its place under a number from the range those calls take, which no request of the caller's carries. With none, the call takes only the records under the numbers it holds and leaves everything else where it is for process_msgs. Keep a record that writes into the state the program's own process_msgs loop writes into: what reaches it here is not delivered to that loop again, the notice that a connection went or came back included. Never hold this record's lock across process_msgs. A call locks the record inside the turn it already holds, so a loop that locks the record and then waits for the turn waits on a call that is waiting on it, and neither ever returns. Hand process_msgs a wrapper of its own that locks the shared state on each callback, as the record does: the turn first, then the state, on both sides. Replaces any record kept before.
pub fn keep_record(&self, record: Arc<Mutex<dyn Wrapper + Send>>)
| Parameter | Type | Description |
|---|---|---|
record | Arc<Mutex<dyn Wrapper + Send>> | The record fed everything an answering call reads, its own answer under its own number included. |
disconnect
Disconnect from IB. Sends Shutdown to the hot loop, waits for the background thread to exit, and marks the client as disconnected. Returns only once the engine's thread has ended, however many callers stop it at once: a TWS client knows its socket is closed when disconnect returns, and a program that must not open a second session on the account needs the same of the first. It never panics. The engine's wake hook must return at once, so it cannot call this blocking method or drop the client's last owner. The engine's last record is delivered by the next process_msgs, after everything the session queued before it, as connection_closed. What this side keeps about the session's requests is kept until then, so a final read still delivers each quote and callback under the request it belongs to.
pub fn disconnect(&self) -> Shutdown
Returns: Shutdown
instrument_of
Which slot a contract holds on this session, if it holds one.
pub fn instrument_of(&self, con_id: i64) -> Option<crate::types::InstrumentId>
| Parameter | Type | Description |
|---|---|---|
con_id | i64 | Contract ID. Unique per instrument. |
Returns: Option<crate::types::InstrumentId>
shared_state
The session's own state, for reading what has arrived.
pub fn shared_state(&self) -> &Arc<SharedState>
Returns: &Arc<SharedState>
adjustments
What the venue last stated for a contract, and the contract as it named it. Not every action of the contract's life: each question replaces what the one before it left, so this states the answer to the last range asked about. The venue serves no adjusted series of its own: asked for one by name it answers that it has no such data, and the trades it does serve are raw. A series that crosses a split steps by the split's ratio with nothing in it saying so, which is a wrong number rather than a missing one. This is what a caller adjusts with. scale_before turns a date and these actions into the factor a price from that date carries, so a caller can put a series on one scale. Three kinds move a price: a stock dividend, a split and a spin-off. The factor is the value the action states, and a spin-off states its reciprocal — established against a contract that split ten for one, where the closes either side were 1208.88 and 121.79, and every close before it folds to a tenth. A cash dividend moves no price on that scale, and neither does a rights offer; a future rollover carries no value to move anything by. A series folded here is then multiplied, before each rights offer, by the value the offer states, and ADJUSTED_LAST has each cash dividend taken off the bars before it as well, as a gateway does; a factor for a date alone states neither: see historical_data. Empty until the venue has stated them for the contract, which it does on the first historical request for it each day.
pub fn adjustments(&self, con_id: &str) -> Option<(AdjustedContract, Vec<Adjustment>)>
| Parameter | Type | Description |
|---|---|---|
con_id | &str | Contract ID. Unique per instrument. |
Returns: Option<(AdjustedContract, Vec<Adjustment>)>
unread_wire
What the venue sent this session that nothing here reads, by connection: each kind of message named once, the first time it arrives. With IB_DX_CAPTURE_WIRE set, every frame is kept here as well, whole and as sent — a reading checked only against frames this client made up says nothing about the ones that arrive.
pub fn unread_wire(&self) -> Vec<(&'static str, String)>
Returns: Vec<(&'static str, String)>
competing_session
Another session that already held this account when this one connected. None when this session is alone. Otherwise where the other one connected from, when it logged in — GMT, as the venue writes it: yyyyMMdd-HH:mm:ss — and whether this session is held to reading only because the other has the account. Worth asking before starting work: the venue permits one logon at a time and takes the account from the older session without saying which it dropped, so a second client reads as data that stops arriving.
pub fn competing_session(&self) -> Option<(String, String, bool)>
Returns: Option<(String, String, bool)>
ccp_session_id
Session ID surfaced to webapp REST clients as x-ccp-session-id.
pub fn ccp_session_id(&self) -> String
Returns: String
misc_url
Logical-name → host URL lookup from the gateway logon MiscUrls push (e.g. region_dam). Returns None when the gateway did not push this key.
pub fn misc_url(&self, key: &str) -> Option<String>
| Parameter | Type | Description |
|---|---|---|
key | &str | Account value key (e.g. "NetLiquidation", "BuyingPower"). |
Returns: Option<String>
session_token_bytes
Canonical big-endian session-token bytes (leading zeros stripped) captured at connect. Round-trips through BigUint::from_bytes_be to the SRP shared secret K and is the second SHA-1 input for SSO Authenticate-TWS bodies.
pub fn session_token_bytes(&self) -> &[u8]
Returns: &[u8]
session
The session this connection established, for a caller that wants to resume from it later. Hand it back through EClientConfig::resume on a subsequent connect. Keep it wherever the process keeps secrets — it is a credential, and where it lives is the caller's decision, which is why nothing here writes it anywhere by default.
pub fn session(&self) -> &crate::auth::resume::ResumableSession
Returns: &crate::auth::resume::ResumableSession
Calls That Answer
historical_data
Bars for a contract, as req_historical_data asks for them. The venue has no adjusted series to pass through: what it serves is raw bars, and the two series the vendor states as adjusted — TRADES and ADJUSTED_LAST — are those folded with the contract's own actions. For a stock or a fund, whatever the series, the actions are asked for first, as a gateway asks them, once a day for each contract, and their answer states the ids the contract traded under: the bars are asked one id at a time, the newest first, each for the days the contract traded as it — a week or a month split at every split as well, and its two parts joined into one bar — and joined into one series. The fold is made once the series is whole, before a bar is handed to anyone, with the actions dated up to the day it is made, that day on UTC's calendar included, as a gateway folds them: a series priced as the contract trades is put on the scale of its splits, stock dividends and spin-offs, and every series has the bars before a rights offer multiplied by the value it states. Any other kind of contract is asked as the request was made, and its bars handed back as the venue served them. ADJUSTED_LAST also has each cash dividend taken off the bars before it, as a gateway takes it off: the amount restated on the scale of the splits, spin-offs and rights offers listed after it, to four places, taken off the last bar before the one dated its day, and every bar before that one multiplied by what it took off that bar's close. This call waits and hands the series back in one piece; req_historical_data delivers the same bars one at a time on its callbacks. Both ask for the actions by the venue's id for the contract, which the venue is asked for first where the contract is named some other way.
pub fn historical_data( &self, contract: &Contract, end_date_time: &str, duration: &str, bar_size: &str, what_to_show: &str, use_rth: bool, ) -> Result<Vec<BarData>, Refusal>
| Parameter | Type | Description |
|---|---|---|
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
end_date_time | &str | End date/time in "YYYYMMDD HH:MM:SS" format, or empty for now. |
duration | &str | Duration string, e.g. "1 D", "1 W", "1 M", "1 Y". |
bar_size | &str | Bar size: "1 min", "5 mins", "1 hour", "1 day", etc. |
what_to_show | &str | Data type: "TRADES", "MIDPOINT", "BID", "ASK", "BID_ASK", etc. |
use_rth | bool | If true, only return data from Regular Trading Hours. |
Returns: Result<Vec<BarData>, Refusal>
corporate_actions
A contract's corporate actions, asked for and waited on. The venue answers these per contract rather than per request, which is enough to file an answer and not enough to know whose question it answers: two questions about one contract over different ranges are answered by two replies naming the same contract. The id the request went out under is carried through, and this takes only the answer to its own. A contract the venue states nothing for answers empty, which is an answer: it is how a contract that has never split says so. contract must carry the venue's id for it, which qualify_contract supplies. Days are YYYYMMDD.
pub fn corporate_actions( &self, contract: &Contract, start_date: &str, end_date: &str, ) -> Result<Vec<crate::control::adjustments::Adjustment>, Refusal>
| Parameter | Type | Description |
|---|---|---|
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
start_date | &str | |
end_date | &str |
Returns: Result<Vec<crate::control::adjustments::Adjustment>, Refusal>
option_chain
Every expiration and strike each venue lists for an underlying. underlying must carry the id of the contract the options are on — the stock, not the option — which qualify_contract supplies.
pub fn option_chain( &self, underlying: &Contract, ) -> Result<Vec<OptionChain>, Refusal>
| Parameter | Type | Description |
|---|---|---|
underlying | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
Returns: Result<Vec<OptionChain>, Refusal>
head_timestamp
The earliest moment the venue holds data for a contract. The same question req_head_time_stamp asks.
pub fn head_timestamp( &self, contract: &Contract, what_to_show: &str, use_rth: bool, ) -> Result<String, Refusal>
| Parameter | Type | Description |
|---|---|---|
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
what_to_show | &str | Data type: "TRADES", "MIDPOINT", "BID", "ASK", "BID_ASK", etc. |
use_rth | bool | If true, only return data from Regular Trading Hours. |
Returns: Result<String, Refusal>
matching_symbols
Contracts whose name or symbol matches a pattern.
pub fn matching_symbols( &self, pattern: &str, ) -> Result<Vec<crate::types::model::ContractDescription>, Refusal>
| Parameter | Type | Description |
|---|---|---|
pattern | &str | Symbol search pattern. |
Returns: Result<Vec<crate::types::model::ContractDescription>, Refusal>
news_headlines
The headlines the venue holds for a contract, up to the number asked for. Each is the time, the provider's code, the article's id and the headline itself. Reading an article needs the first two. The venue states whether it holds more than it sent, and that is reported through the log rather than in the returned rows: what comes back is a page, and a full one is not evidence there is no next one. Ask for more, or narrow the window, to see the rest.
pub fn news_headlines( &self, con_id: i64, provider_codes: &str, start_date_time: &str, end_date_time: &str, total_results: i32, ) -> Result<Vec<Headline>, Refusal>
| Parameter | Type | Description |
|---|---|---|
con_id | i64 | Contract ID. Unique per instrument. |
provider_codes | &str | Pipe-separated news provider codes. |
start_date_time | &str | Start date/time for tick query. |
end_date_time | &str | End date/time in "YYYYMMDD HH:MM:SS" format, or empty for now. |
total_results | i32 | Maximum number of news results. |
Returns: Result<Vec<Headline>, Refusal>
histogram_data
How a contract's trades were spread across prices.
pub fn histogram_data( &self, contract: &Contract, use_rth: bool, period: &str, ) -> Result<Vec<(f64, i64)>, Refusal>
| Parameter | Type | Description |
|---|---|---|
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
use_rth | bool | If true, only return data from Regular Trading Hours. |
period | &str | Histogram period, e.g. "1week", "1month". |
Returns: Result<Vec<(f64, i64)>, Refusal>
fundamental_data
A fundamental document about a contract, as the venue writes it.
pub fn fundamental_data( &self, contract: &Contract, report_type: &str, ) -> Result<String, Refusal>
| Parameter | Type | Description |
|---|---|---|
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
report_type | &str | Report type: "ReportSnapshot", "ReportsFinSummary", "RESC", etc. |
Returns: Result<String, Refusal>
what_if_order
What the venue says an order would cost, without placing it. The order is marked as a question rather than an instruction, so nothing reaches the market. The preview is the order's own placement with the question marked on it, as a gateway sends one: its type, its prices and the instruction that separates the types sharing a name — trailing, relative and the two pegs all go as P, told apart by their ExecInst — so a security that refuses a type refuses the preview of it rather than answering about an order that was not asked about. A name that is not an order type is refused, as it is when placing.
pub fn what_if_order( &self, contract: &Contract, order: &crate::types::model::Order, ) -> Result<crate::types::model::OrderState, Refusal>
| Parameter | Type | Description |
|---|---|---|
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
order | &crate::types::model::Order | Order parameters (action, quantity, type, price, TIF, etc.). |
Returns: Result<crate::types::model::OrderState, Refusal>
positions
Every holding in the account, read as it stands. Read, not subscribed, as the Python call reads it: nothing is left standing to deliver later moves to a program that did not ask for them, or to take the moves its own per-request watchers wait on.
pub fn positions(&self) -> Result<Vec<PositionRow>, Refusal>
Returns: Result<Vec<PositionRow>, Refusal>
account_summary
The account values named by tags, as req_account_summary asks for them. tags is a comma-separated list, or All.
pub fn account_summary(&self, tags: &str) -> Result<Vec<AccountValue>, Refusal>
| Parameter | Type | Description |
|---|---|---|
tags | &str | Comma-separated account tags: "NetLiquidation,BuyingPower,...". |
Returns: Result<Vec<AccountValue>, Refusal>
await_order
Wait for an order to reach a state the venue will not move it from. Placing an order says only that it was sent. What happened to it arrives later, on a callback, spread across a status and possibly a refusal. This waits for the venue to finish with it and reports where it landed — including the refusal, which is the part a caller most needs and the part most easily missed. A wait that runs out is not a failure of the order: it says only that the venue had not finished, and the order is still working.
pub fn await_order( &self, order_id: i64, timeout: Duration, ) -> Result<OrderReport, Refusal>
| Parameter | Type | Description |
|---|---|---|
order_id | i64 | Order identifier. Must be unique per session. |
timeout | Duration | The longest to wait. |
Returns: Result<OrderReport, Refusal>
contract_details
Every contract matching the one described. The same question req_contract_details asks, answered here instead of on a callback.
pub fn contract_details(&self, contract: &Contract) -> Result<Vec<ContractDetails>, Refusal>
| Parameter | Type | Description |
|---|---|---|
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
Returns: Result<Vec<ContractDetails>, Refusal>
qualify_contract
Fill in what the venue knows about a contract, above all its id. Most of what this client sends carries a contract, and a contract with an id is worth more than one without: market data is answered only for a contract named by id, and an order that carries one needs to state nothing else. Ask this first and pass the result around. A description matching more than one contract is refused rather than resolved to whichever came back first — the same symbol on the same venue exists in more than one currency, and picking one silently is how an order ends up on the wrong one.
pub fn qualify_contract(&self, contract: &Contract) -> Result<Contract, Refusal>
| Parameter | Type | Description |
|---|---|---|
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
Returns: Result<Contract, Refusal>
qualify_contracts
Fill in a batch of contracts, keeping the caller's order. Stops at the first that cannot be named, because a caller building a basket wants to know which one is wrong, not to trade the rest.
pub fn qualify_contracts(&self, contracts: &[Contract]) -> Result<Vec<Contract>, Refusal>
| Parameter | Type | Description |
|---|---|---|
contracts | &[Contract] | Contract specification (symbol, secType, exchange, currency, etc.). |
Returns: Result<Vec<Contract>, Refusal>
scan
Run a scan and hand back what it found. The subscription is withdrawn before this returns: a scan asked for once is a question, and left running it keeps answering into a session nobody is reading.
pub fn scan( &self, instrument: &str, location: &str, scan_code: &str, most: u32, ) -> Result<Vec<ScanRow>, Refusal>
| Parameter | Type | Description |
|---|---|---|
instrument | &str | Instrument type for scanner (e.g. "STK", "FUT"). |
location | &str | |
scan_code | &str | Scanner code (e.g. "TOP_PERC_GAIN", "HIGH_OPT_IMP_VOLAT"). |
most | u32 | Maximum number of scanner results. |
Returns: Result<Vec<ScanRow>, Refusal>
schedule
When a contract trades, over a window ending now.
pub fn schedule(&self, contract: &Contract, duration: &str) -> Result<Schedule, Refusal>
| Parameter | Type | Description |
|---|---|---|
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
duration | &str | Duration string, e.g. "1 D", "1 W", "1 M", "1 Y". |
Returns: Result<Schedule, Refusal>
calendar_schema
What the corporate-events calendar says it carries. As the venue's JSON: it states a schema of its own that changes without notice, and a shape imposed here would be a shape to keep in step with it.
pub fn calendar_schema(&self) -> Result<String, Refusal>
Returns: Result<String, Refusal>
calendar_events
The calendar's events for one contract, as the venue's JSON.
pub fn calendar_events(&self, con_id: i64) -> Result<String, Refusal>
| Parameter | Type | Description |
|---|---|---|
con_id | i64 | Contract ID. Unique per instrument. |
Returns: Result<String, Refusal>
Account & Portfolio
req_positions
Request positions. Answered where the account has stated what it holds, which the venue does as a session opens: every holding and position_end, stated as the account stands where the answer is delivered, and each move after it on position. The engine holds the question until then, and nothing waits here. An account that says nothing within ten seconds is answered with what this session holds — which reads the same as an account holding nothing, so it is said on error ahead of the answer. include_fx_positions (default true) controls CASH rows. send_zero_positions_for_todays_opening_only (default false) omits flat rows only when the venue states a zero opening quantity. A nonzero or unknown opening quantity still passes. Both settings apply to the initial answer and later updates; end callbacks still arrive.
pub fn req_positions(&self)
req_pnl
Subscribe to the named account's profit. The account is checked as a gateway checks it. Each request has its own subscription; a repeated active request number is refused under 102. A model is taken and not applied, with a log notice once per session.
pub fn req_pnl(&self, req_id: i64, account: &str, model_code: &str)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
account | &str | Account ID. |
model_code | &str | Model portfolio code (empty for default). |
cancel_pnl
Cancel PnL subscription. The updates stop. The venue has no message withdrawing the subscription itself, on a gateway as here, so the updates stopping is what the call does.
pub fn cancel_pnl(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
req_pnl_single
Subscribe to a position's profit in the named account. The account is checked as for the account-level profit. A model is taken and not applied, with a log notice once per session.
pub fn req_pnl_single(&self, req_id: i64, account: &str, model_code: &str, con_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
account | &str | Account ID. |
model_code | &str | Model portfolio code (empty for default). |
con_id | i64 | Contract ID. Unique per instrument. |
cancel_pnl_single
Cancel single-position PnL subscription.
pub fn cancel_pnl_single(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
req_account_summary
Request an account summary. All answers for every account the login holds. Account groups and AllNonProp are taken and not applied, with a log notice once per session. Validation and the limit of two standing summary requests follow a gateway.
pub fn req_account_summary(&self, req_id: i64, group: &str, tags: &str)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
group | &str | Account group name (e.g. "All"). |
tags | &str | Comma-separated account tags: "NetLiquidation,BuyingPower,...". |
cancel_account_summary
Cancel account summary.
pub fn cancel_account_summary(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
req_account_updates
Subscribe to the named account's figures and holdings, or withdraw the subscription. A single-account login ignores the name as a gateway does. Subscribing asks the venue to restate that account now; the engine holds the answer until its download ends or the existing wait expires. include_fx_positions (default true) controls CASH rows. send_zero_positions_for_todays_opening_only (default false) omits flat rows only when the venue states a zero opening quantity. A nonzero or unknown opening quantity still passes. Both settings apply to the initial answer and later updates; end callbacks still arrive.
pub fn req_account_updates(&self, subscribe: bool, acct_code: &str)
| Parameter | Type | Description |
|---|---|---|
subscribe | bool | true to start updates, false to stop. |
acct_code | &str | Account code (e.g. "DU1234567"). |
cancel_positions
Cancel positions subscription. Nothing is withdrawn from the venue: it pushes what the account holds as the session opens and keeps it current whether or not anyone is listening. What stops is the reporting — a holding that moves after this is no longer delivered on position. A req_positions the engine still holds is withdrawn, never answered. The cancel is confirmed on question_retired where it stands, after everything the question was answered with.
pub fn cancel_positions(&self)
req_managed_accts
Request managed accounts. Answered with every account this login holds, comma separated, which is the shape the reference client answers in. A login with one account is answered with that one account and no comma.
pub fn req_managed_accts(&self)
req_account_updates_multi
Subscribe to the named account's figures under this request number. ledger_and_nlv selects the per-currency ledger and net liquidation. A model is taken and not applied, with a log notice once per session. The initial batch ends with account_update_multi_end; changes keep arriving until the request is cancelled.
pub fn req_account_updates_multi( &self, req_id: i64, account: &str, model_code: &str, ledger_and_nlv: bool, )
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
account | &str | Account ID. |
model_code | &str | Model portfolio code (empty for default). |
ledger_and_nlv | bool | If true, only the per-currency ledger: each currency's cash, market values and NetLiquidationByCurrency. |
cancel_account_updates_multi
Cancel multi-account updates. The request stops being reported to. The venue keeps the account current whether or not anyone is listening, as for cancel_account_updates; what stops is the reporting — a figure that moves after this is no longer delivered on account_update_multi for this request. A request the engine still holds is withdrawn, never answered; one it answered stops where the withdrawal stands, after its answer.
pub fn cancel_account_updates_multi(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
req_positions_multi
Subscribe to holdings of the named account under this request number. A model is taken and not applied, with a log notice once per session. include_fx_positions (default true) controls CASH rows. send_zero_positions_for_todays_opening_only (default false) omits flat rows only when the venue states a zero opening quantity. A nonzero or unknown opening quantity still passes. Both settings apply to the initial answer and later updates; end callbacks still arrive.
pub fn req_positions_multi(&self, req_id: i64, account: &str, model_code: &str)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
account | &str | Account ID. |
model_code | &str | Model portfolio code (empty for default). |
cancel_positions_multi
pub fn cancel_positions_multi(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
positions_elsewhere
Holdings the venue reports that this broker does not hold itself: positions held away at another broker, and rows it marks as shown but not held. Kept apart from positions, which answers what the account itself holds. The reference client has no call for these — its own front end shows them in a separate table — so this is the only way to reach them.
pub fn positions_elsewhere(&self) -> Vec<crate::types::PositionElsewhere>
Returns: Vec<crate::types::PositionElsewhere>
values_elsewhere
The account figures describing one of the sets of holdings the account does not hold itself, as name, value and the currency each is stated in. A figure stated in two currencies is two figures. The venue states these the same way it states the account's own, and mixing them in would overstate what the account is worth, so they are kept where the holdings they describe are kept.
pub fn values_elsewhere(&self, held: crate::types::HeldElsewhere) -> Vec<(String, String, String)>
| Parameter | Type | Description |
|---|---|---|
held | crate::types::HeldElsewhere | Which set of holdings kept elsewhere: Away, DisplayOnly or Aside. |
Returns: Vec<(String, String, String)>
account
The account as the venue last stated it whole, or nothing while a download is running. Answered from the struct alone, a caller read all zeros before the first download and the pre-drop figures after a drop, with nothing to say so; the other surface answers None for both.
pub fn account(&self) -> Option<AccountState>
Returns: Option<AccountState>
Orders
order_permissions
Security type → the order types the venue permits for it, as stated at logon. Empty until the session is up.
pub fn order_permissions(&self) -> std::collections::HashMap<String, Vec<String>>
Returns: std::collections::HashMap<String, Vec<String>>
permitted_order_types
The order types permitted for one security type, or None when the type is not permitted at all. A combination is named COMB.
pub fn permitted_order_types(&self, sec_type: &str) -> Option<Vec<String>>
| Parameter | Type | Description |
|---|---|---|
sec_type | &str |
Returns: Option<Vec<String>>
enabled_features
Feature tokens the venue enables for this account: those stated at logon, and those the account configuration adds afterwards.
pub fn enabled_features(&self) -> Vec<String>
Returns: Vec<String>
algorithms
Which algorithms the venue offers, keyed PROVIDER/SECTYPE. Stated on the session rather than per contract. An algorithm absent here is one this account may not use, and an order naming it is refused by the venue.
pub fn algorithms(&self) -> std::collections::HashMap<String, Vec<String>>
Returns: std::collections::HashMap<String, Vec<String>>
algorithms_for
The algorithms offered for one security type, across every provider.
pub fn algorithms_for(&self, sec_type: &str) -> Vec<String>
| Parameter | Type | Description |
|---|---|---|
sec_type | &str |
Returns: Vec<String>
order_presets
The sets of order defaults this account holds, as (key, attributes, when it last changed). The venue keeps one per security type and fills parts of an order the caller left unstated from them: the size a compete order competes with and the offset it competes by, where neither was named. So the same call on two accounts is not the same order, and the reference client's surface has no way to say which sets are in force. The key is the venue's own — s=STK, or s=CASH&tc=EUR where a currency splits it. The attributes are as the venue writes them, & between them: v= names the set's variant and a=1 marks it active, so v=1&a=1 is an active set and v=1 one that is not. The values in a set are asked for separately and are not carried here. The moment says when a set last changed, which is what tells a caller whether an order it sent at a given time was filled in from the old defaults or the new. Empty where the venue stated none.
pub fn order_presets(&self) -> Vec<(String, String, String)>
Returns: Vec<(String, String, String)>
place_order
Place an order. An order names its contract by the venue's id. A caller who states a description instead of an id — which every example written against the reference client does — has it named by the engine, once the order itself is known to be one the venue would take: an order that names no contract is one the venue has nothing to match, and answers with nothing at all. Once per description: the answer is kept, and later orders on the same contract are sent without asking again. Nothing waits here. What needs no venue is checked at the call and a refusal of it is delivered in its place in the session's order; the engine then names and registers the contract, checks the order against what this session placed and what the venue is working — a number already finished, a replace naming another contract, a change a gateway refuses — builds it, and sends it or keeps it, and refuses it under its own number where it will not. A change of an order the engine has not sent yet goes after it, and its withdrawal withdraws it. sl_order_id / sl_order_type and pt_order_id / pt_order_type construct stop-loss and profit-taking children from the selected account preset. State the child id with PRESET (case-insensitive). The engine loads the preset, builds the children once and sends the parent, stop loss and profit taker in that order. transmit = false holds the family until a parent or child transmits it. Replacing a parent changes that order and preserves its existing children. Percentage-allocation sizing from group or model holdings is not carried; use explicitly sized parent and child orders where their quantities depend on it.
pub fn place_order(&self, order_id: i64, contract: &Contract, order: &Order)
| Parameter | Type | Description |
|---|---|---|
order_id | i64 | Order identifier. Must be unique per session. |
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
order | &Order | Order parameters (action, quantity, type, price, TIF, etc.). |
exercise_options
Exercise or lapse a long option position. exercise_action is 1 to exercise and 2 to lapse; anything else is refused. account is the account the exercise is taken on. A login holding several has to name one it holds; a login holding one takes it on its own, and an account other than its own holds no position here, which is answered under 322 as a gateway answers it. The instruction is checked as a gateway checks it, by the engine, which holds it while it does: the account's position in the option must be above nothing, or it is refused under 322 ("No unlapsed position exists in this option in account ..."), and no more than the position goes. The option's in-the-money figure (generic tick 493) is asked for whatever override_ says: one already held is used, and otherwise the engine watches for one, with no bound, as a gateway does. With override_ false, an exercise of an option not in the money and a lapse of one in it are refused under 322, as a gateway refuses them; with true they go. override_ itself travels on no tag: it names this check, which is made before the order is built. The position and quantity are checked in the account named.
pub fn exercise_options( &self, req_id: i64, contract: &Contract, exercise_action: i32, exercise_quantity: i32, account: &str, override_: bool, stated: crate::client_core::ExerciseStates, )
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
exercise_action | i32 | 1=exercise, 2=lapse. |
exercise_quantity | i32 | Number of contracts to exercise. |
account | &str | Account ID. |
override_ | bool | |
stated | crate::client_core::ExerciseStates |
cancel_order
Cancel an order. The second argument is what the withdrawal states about itself — OrderCancel, or a time alone; "" states nothing. Who is withdrawing it and whether a person entered it travel on the cancel, as a gateway writes them: from the withdrawal, not from the placement. A time does not travel. A gateway sends it only where the venue has turned that record on for the login, and this client does not read whether it has. The cancel goes anyway and the caller is told the time did not: a live order left standing because a regulatory annotation has nowhere to go is the worse of the two. Taken in silence, the order would come back without the record while the caller had given one. A time a gateway cannot read is refused as a gateway refuses it, under 10301, and nothing is withdrawn. Nothing is said as the cancel goes out, as a gateway says nothing then: asked for, the order reads PendingCancel, and the venue's next report on it states it.
pub fn cancel_order( &self, order_id: i64, order_cancel: impl Into<crate::types::model::OrderCancel>, )
| Parameter | Type | Description |
|---|---|---|
order_id | i64 | Order identifier. Must be unique per session. |
order_cancel | impl Into<crate::types::model::OrderCancel> | What the withdrawal states about itself: an OrderCancel, or a manual time alone. "" states nothing, and so does None from Python. |
req_global_cancel
Cancel every order the account is working. This wire carries no request to withdraw everything, so it is composed here: one cancel for each order held, which is what a caller asking for everything back is asking for. What is held is what the venue named as working at connect and what this session placed since. The venue names the former after the connect returns, so a global cancel issued straight away waits for that naming, as asking for the open orders does, and covers what was named. Where the naming does not finish within the wait, what had been named is still withdrawn and the call says so rather than returning as though every order were covered: a partial cancel that reads as one beats the same cancel in silence, which reads as a complete answer. What the withdrawal states — who is withdrawing and whether a person entered it — travels on every cancel, as a gateway states it on every order it withdraws. A time does not: the reference client writes none on a withdrawal of everything, so a gateway never reads one, and one stated here goes the same way.
pub fn req_global_cancel( &self, order_cancel: impl Into<crate::types::model::OrderCancel>, )
| Parameter | Type | Description |
|---|---|---|
order_cancel | impl Into<crate::types::model::OrderCancel> | What the withdrawal states about itself: an OrderCancel, or a manual time alone. "" states nothing, and so does None from Python. |
req_ids
Request next valid order ID. Answered on next_valid_id in its place in the session's order. The venue names what the account is working after the connect returns, and the id is floored above it, so the engine holds the question until the naming is over; nothing waits here. num_ids has no effect, as on a gateway: the next valid id is answered whatever number is asked for.
pub fn req_ids(&self, num_ids: i32)
| Parameter | Type | Description |
|---|---|---|
num_ids | i32 | Number of IDs to reserve (unused). |
next_order_id
Reserve the next order ID above the saved counter and venue replay. The counter is saved under the account and API client before this returns. Sessions sharing the configured file reserve under an exclusive lock. A storage failure warns once and leaves allocation using session memory and venue replay. Zero where there is no id to give, which every placement path refuses. A number carries no reason with it, so the reason goes out on the channel a caller already watches as well as to the log: told only in the log, a caller reads a zero and has nowhere to learn why.
pub fn next_order_id(&self) -> i64
Returns: i64
next_shared_id
The first id past everything the account has used that a request can also carry. A caller that numbers its orders and its requests out of one counter needs both at once: clear of every id an order has spent, and inside the numbers a request can carry. An account that has been given a wider order id than that has no such number above it, so this answers with one past the widest the account has used that a request can carry, and the counting goes on from there. A read, not a reservation: asked twice, it answers the same until the venue names a wider id. After a connect it waits, for at most three seconds in all, for the venue to name the orders the account is working. Refused where even that is not a number a request can carry.
pub fn next_shared_id(&self) -> Result<i64, Refusal>
Returns: Result<i64, Refusal>
next_shared_id_within
next_shared_id, with its wait for the replay also bounded by timeout and by the config's cancel, both read at each 10 ms step of the wait. A gateway gives its client the next valid id once it has read the account's orders. A program bounding that wait, as it bounds the handshake, is answered Refusal::no_answer when timeout passes first, and the same, saying so, when the connect is taken back. None is the replay's own bound alone, as next_shared_id waits.
pub fn next_shared_id_within( &self, timeout: Option<std::time::Duration>, ) -> Result<i64, Refusal>
| Parameter | Type | Description |
|---|---|---|
timeout | Option<std::time::Duration> | The longest to wait. |
Returns: Result<i64, Refusal>
order_id_floor
The id next_valid_id would state now, read without waiting. A gateway gives its client the next valid id only once it has read the account's orders, and raises it past every new order. This is that floor as it stands at the read: it rises as the venue names what the account is working, before the read that delivers those orders, and again after every reconnect. A caller allocating ids clears it at each allocation. The saved counter also raises it at connect; it is one where neither the saved counter nor the venue names a prior id. A read, not a reservation. next_shared_id answers the floor a request can also carry.
pub fn order_id_floor(&self) -> i64
Returns: i64
req_open_orders
Request open orders for this client. Answers with this client's own orders alone, each on open_order and then its status on order_status, as a gateway narrows this answer; req_all_open_orders answers with every client's, each stated the same way. An order the venue states held is among them only where this session placed it: a gateway holds an order it sent working whatever the venue states of it later, and leaves out one it first learns of held.
pub fn req_open_orders(&self)
req_all_open_orders
Request all open orders.
pub fn req_all_open_orders(&self)
req_completed_orders
Request completed orders. Asks the venue, and answers where the end of what it states stands in the session's order: every completed order this session has archived, then completed_orders_end. Nothing waits here. api_only asks for the orders entered through an API rather than by hand. The venue states no origin beside a finished order, and it does number the ones an API placed: an order that went out through one carries the number that API gave it, and one typed in carries none. So true is answered with the orders the venue numbered.
pub fn req_completed_orders(&self, api_only: bool)
| Parameter | Type | Description |
|---|---|---|
api_only | bool |
req_auto_open_orders
Automatically bind future orders to this client. For client 0, b_auto_bind turns binding on or off, as on a gateway: while it is on, each order placed elsewhere that the venue states after it has named what was already working is bound to client 0, under the next negative number, or the next order id where use_negative_auto_range is off. Turning it off keeps the numbers already given. The venue is asked to bind each one, and Wrapper::order_bound follows when it accepts. Any client but 0 is refused, as a gateway refuses one: asked to bind, with 321, as a request that fails validation; asked not to, with 327.
pub fn req_auto_open_orders(&self, b_auto_bind: bool)
| Parameter | Type | Description |
|---|---|---|
b_auto_bind | bool | If true, auto-bind future orders to this client. |
req_executions
Request execution reports. Replays stored executions (optionally filtered), firing exec_details + commission_and_fees_report for each, then exec_details_end, where the answer stands in the session's order: every fill delivered before it is in it. last_n_days and specific_dates select days as a gateway selects them, counted on the session's time zone; a date that is not a day of the calendar is refused under 320, as a gateway refuses it. The executions answered from reach back to midnight six days before the logon in UTC, or to the logon's own day for a session set to today's executions, so the earliest days asked for can be missing some; those days are named on error under 321 ahead of the answer, which still comes. acct_code is ignored on a login holding one account and refused on one holding several where the login does not hold it, as a gateway does both. A refused request is told so on error and nothing else, as a gateway tells it.
pub fn req_executions(&self, req_id: i64, filter: &ExecutionFilter)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
filter | &ExecutionFilter | Execution filter (client_id, acct_code, time, symbol, sec_type, exchange, side, last_n_days, specific_dates). |
parse_algo_params
Parse algo strategy and TagValue params into internal AlgoParams. A key the caller never set is not stated: the venue's own default for it is not known here, and a value sent in its place — 0, an empty time, Neutral — is a claim the caller did not make. A strategy modelled here is re-encoded from the fields it names, and a key it does not name has no field to be re-encoded into. That is a limit of the re-encoding, not of the protocol: there is no tag per parameter — a name and a value travel as a pair in a repeating group, and the venue reads a pair whose name this client never modelled exactly as it reads one it did. So a list carrying such a key is forwarded whole, as the caller wrote it and in the order they wrote it, rather than refused or quietly shortened. A value is checked, not re-spelled. A parameter is text on the wire, and the text the caller wrote is what reaches the venue, as the reference client forwards it; the parse here is this client's own check that it reads. Two kinds are sent in the venue's spelling instead, each said where it is read: a known flag goes as 1/0, and a known riskAversion as the venue names it. Other spellings travel as written.
pub fn parse_algo_params(strategy: &str, params: &[TagValue]) -> Result<AlgoParams, Refusal>
| Parameter | Type | Description |
|---|---|---|
strategy | &str | Algo strategy name (e.g. "Vwap", "Twap"). |
params | &[TagValue] | Algo parameter list. |
Returns: Result<AlgoParams, Refusal>
Market Data
req_spread_scan
Ask the venue to scan an underlying for strategies worth putting on. The scan goes out beside a subscription for the series the venue states its answer on, because that is how it is asked for: the series carries the answer and the scan tells the venue what to look for. Read the answer with Self::scanned_strategies under the same request. The documented API has no call for this at all. What the scan states about each strategy is the venue's own, in the venue's own words, and nothing here translates them.
pub fn req_spread_scan( &self, req_id: i64, contract: &Contract, scan: &crate::types::SpreadScan, )
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
scan | &crate::types::SpreadScan | What to scan the underlying for. |
scanned_strategies
The strategies a spread scan stated for a request, as the venue stated them. Empty until a scan has been asked for and answered. A scan the venue refuses answers with nothing rather than with strategies.
pub fn scanned_strategies(&self, req_id: i64) -> Vec<crate::types::ScannedStrategy>
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
Returns: Vec<crate::types::ScannedStrategy>
req_mkt_data
Subscribe to market data. When snapshot is true, the quote is delivered as it arrives, and tick_snapshot_end follows once the snapshot is whole, as a gateway ends one: when the venue has stated the bid, the ask, the last, the open and the close; on a contract of a type a gateway marks as an option (OPT, FOP, IOPT, WAR, EC) also the option model, 13 (83 delayed); on a delayed feed also the last trade's time, 88; or eleven seconds after the request, whichever comes first. The subscription is then withdrawn. That is a subscription this client ends, not a request of its own: the venue's own one-shot snapshot is the chargeable one, asked for with regulatory_snapshot on req_mkt_data_ex. Ticks 10, 11 and 12 (80, 81 and 82 delayed), the bid's, the ask's and the last's option computations, are not stated by the venue: a gateway works them out with an option model of its own, and so does this client, for an option on a share once its inputs are in hand. A gateway's snapshot of an option also waits for them; this client's does not. generic_tick_list goes out with the subscription, and what comes back reaches the caller on the callback the series belongs to. These are read: * 100, 101, 105 — option volume, open interest and the average volume, calls before puts. * 104, 106, 411 — volatility: the historical figure, the implied one, and the one the venue restrikes through the session. * 162, 165 — an index's premium over its future; the extremes of the last quarter, half-year and year with the ordinary day's volume. * 220, 221, 232, 619 — the mark the venue keeps, which is not a trade, under each of the numbers it is asked for by, and the slow one beside it. * 225 — the auction: what is crossing, which way, at what price, and the imbalance the venue must publish. * 233, 375 — everything that traded, and what traded on a trade report, each stated as a trade rather than as the totals it is read from. * 236 — whether it can be borrowed, and how much of it. * 258 (or 47) — the company ratios, as the venue writes them. * 292 — news for the contract, from the providers the session names; 292:BRFG+DJNL names the providers to ask instead. A contract's headlines are asked for once, by the first request that wants them. * 293, 294, 295 — how fast it is trading. * 318 — what last traded in the regular session. * 456 (or 59) — what it pays out. * 460 — the factor a redemption changes. * 577, 614, 623 — a fund's value per share: last, the day's extremes, and the frozen one. * 586 — what a share is expected to open at, and what it did. * 588 — a future's open interest. * 595 — what has traded over the last three, five and ten minutes. * 787 — the odd lot: the two prices nobody has to deal in round lots at, their sizes, and where each is quoted. The list is read whole before anything is subscribed, as a gateway reads one: an entry that is not a number the venue knows a series by, or one no series of the request's type is legal under, refuses the whole request under 321, in a standing text that names every series legal for the type. A list of mdoff alone resolves to nothing and is refused with the rest; a number the venue also answers an older number by is read as the series it names. Some numbers the venue knows are legal for no quote request at all — the borrow cost among them — and are refused under that reading whatever the type. A code legal for the type but not among those read here still goes to the venue, and a reading of it arrives and is recorded rather than delivered: the shape it is written in is the series' own, and nothing here can read one it has not been taught. tick_generic also fires for the halt the venue states on its own tick: tick 49, 0 while a contract is trading and 1 once it has stopped. Every type starts live. Types 3 and 4 switch to delayed data after a bid/ask refusal says delayed data is available, which reports 10167 without ending the request. Types 2 and 4 are served the frozen or the delayed-frozen quote while the market is closed, where the logon enables frozen data. market_data_type names the feed served. req_mkt_data_ex selects its feed directly. mkt_data_options is checked as a gateway checks it — manual, 0 or 1, is the key it takes — and an entry it cannot read refuses the request under its own id before anything is subscribed. The wire writes the list as it writes it for the request naming its mode: checked, not carried.
pub fn req_mkt_data( &self, req_id: i64, contract: &Contract, generic_tick_list: &str, snapshot: bool, regulatory_snapshot: bool, mkt_data_options: &[crate::types::model::TagValue], )
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
generic_tick_list | &str | Comma-separated generic tick IDs (e.g. "233" for RT volume). |
snapshot | bool | If true, delivers one quote then auto-cancels. |
regulatory_snapshot | bool | If true, request a regulatory snapshot (additional fees may apply). |
mkt_data_options | &[crate::types::model::TagValue] |
req_mkt_data_ex
Like req_mkt_data, but names the market-data mode on the request itself, through FIX field 9887, rather than taking the one the session is set to: | mode_9887 | mode | wire shape | |-------------|------------------|---| | 0 | REALTIME | 264=442 (BID_ASK) + 264=443 (LAST), no 9887 | | 1 | DELAYED | 264=442 + 264=443, each with 9887=1 | | 2 | FROZEN | 264=442 + 264=443, each with 9887=2 | | 3 | DELAYED_FROZEN | 264=442 + 264=443, each with 9887=3 | The frozen mode keeps thinly-traded names quoting after-hours, when the realtime feed is silent. A contract holds one subscription at a time, so this states the mode for that subscription rather than adding a parallel one — to compare modes on one contract, cancel between them. To set the mode for every subscription instead of naming it per request, call req_market_data_type. regulatory_snapshot asks for the venue's own chargeable one-shot snapshot: a request type of its own rather than a mode on an ordinary quote, asked for under the snapshot action and with no feed named beside it. It needs the entitlement — an account without it is refused by the venue, which names the request type back. Whether it also costs something is between the account and the broker, and is not on this wire. It ends the way an ordinary snapshot does, so a caller hears tick_snapshot_end either way. Its default is false.
pub fn req_mkt_data_ex( &self, req_id: i64, contract: &Contract, generic_tick_list: &str, snapshot: bool, regulatory_snapshot: bool, mode_9887: i32, mkt_data_options: &[crate::types::model::TagValue], )
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
generic_tick_list | &str | Comma-separated generic tick IDs (e.g. "233" for RT volume). |
snapshot | bool | If true, delivers one quote then auto-cancels. |
regulatory_snapshot | bool | If true, request a regulatory snapshot (additional fees may apply). |
mode_9887 | i32 | |
mkt_data_options | &[crate::types::model::TagValue] |
cancel_mkt_data
Cancel market data. A number a gateway cannot read, past four bytes signed, is refused under -1 with 320, as a gateway refuses it, and withdraws nothing; a stream watch opened is withdrawn under the number it handed back.
pub fn cancel_mkt_data(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
req_tick_by_tick_data
Subscribe to every trade or every quote change on a contract. The feed rides the historical farm, registered there under the name TickByTick beside the five-second bars. No separate service is involved. A missing entitlement arrives as the venue's refusal rather than as silence. number_of_ticks and ignore_size are sent as stated: a count of past ticks goes out as the length of the run the stream opens with, and the size filter as the query's filter term. Neither goes out at its default — no prelude, sizes included — which is what the venue does on its own. Whether the venue honours the size filter is the venue's, and what it sends is passed on as it stands, as a gateway passes it: one session saw size-only changes still arrive on a stream that asked to leave them out.
pub fn req_tick_by_tick_data( &self, req_id: i64, contract: &Contract, tick_type: &str, number_of_ticks: i32, ignore_size: bool, )
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
tick_type | &str | Tick type ID or tick-by-tick type string. |
number_of_ticks | i32 | Maximum number of ticks to return. |
ignore_size | bool | If true, asks that a bid/ask change moving only a size be left out. |
cancel_tick_by_tick_data
Cancel tick-by-tick data.
pub fn cancel_tick_by_tick_data(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
req_mkt_depth
Subscribe to market depth (L2 order book). Refused as a gateway refuses it, before anything is sent: a contract naming no exchange, a contract description a gateway refuses — no identifier, a type that resolves to nothing, a name the wire cannot carry, an option or futures type missing the fields its description needs — a combination, and a book of no rows. Substituting a stock here asks for a future's book as a stock's, which the venue refuses as a book it does not serve.
pub fn req_mkt_depth( &self, req_id: i64, contract: &Contract, num_rows: i32, is_smart_depth: bool, )
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
num_rows | i32 | Number of order book rows to subscribe to. |
is_smart_depth | bool | If true, aggregate depth from multiple exchanges via SMART. |
cancel_mkt_depth
Cancel market depth. The mode names which of the two books under the number is withdrawn — the smart one or the regular one — as it does for a gateway, and the other one runs on.
pub fn cancel_mkt_depth(&self, req_id: i64, is_smart_depth: bool)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
is_smart_depth | bool | If true, aggregate depth from multiple exchanges via SMART. |
req_real_time_bars
Subscribe to real-time 5-second bars. bar_size has no effect, as on a gateway: a real-time bar is five seconds, and the venue's request carries no bar size. A gateway reads the number and does not use it. Requests for the same bars of one contract — this call's, or the stream a historical request kept up to date rides — read one stream, as on a gateway: the venue serves it under one number, each request is handed every bar under its own id, and a cancel withdraws its own request alone. The stream is withdrawn when the last of them leaves.
pub fn req_real_time_bars( &self, req_id: i64, contract: &Contract, _bar_size: i32, what_to_show: &str, use_rth: bool, )
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
bar_size | i32 | Bar size: "1 min", "5 mins", "1 hour", "1 day", etc. |
what_to_show | &str | Data type: "TRADES", "MIDPOINT", "BID", "ASK", "BID_ASK", etc. |
use_rth | bool | If true, only return data from Regular Trading Hours. |
cancel_real_time_bars
Cancel real-time bars.
pub fn cancel_real_time_bars(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
req_ping
Request an auth-connection round-trip time sample: sends a lightweight liveness probe with no side effects on subscriptions, contract caches, or pacing budgets. The result lands asynchronously — poll last_rtt() after a moment. No-op while a probe is already in flight or the connection is down.
pub fn req_ping(&self)
last_rtt
Last measured auth-connection round-trip time, if any. A gauge, not a benchmark: the sample is the interval from a probe to the first inbound traffic that followed it, which on an active feed can undercount by racing data already in flight. Also sampled automatically whenever liveness sends its own probe.
pub fn last_rtt(&self) -> Option<std::time::Duration>
Returns: Option<std::time::Duration>
req_market_data_type
Which feeds the subscriptions after this one may be served: 1 live, 2 frozen, 3 delayed, 4 delayed and frozen. A type turns feeds on, as a gateway takes it: 2 turns frozen data on; 3 and 4 turn delayed data on, 4 with delayed-frozen and 3 without; only 1 turns frozen data off, and it turns all three off. A subscription starts live whatever the type, falls back to delayed data on a refusal where delayed data is on, and is served the frozen or delayed-frozen quote while the market is closed, where the logon enables frozen data; the market_data_type callback reports the type served. To name a feed for one request instead, req_mkt_data_ex takes it. A number naming no type is refused under -1 with 321, Invalid market data type, as a gateway refuses it, and leaves the feeds as they were.
pub fn req_market_data_type(&self, market_data_type: i32)
| Parameter | Type | Description |
|---|---|---|
market_data_type | i32 | 1=live, 2=frozen, 3=delayed, 4=delayed-frozen. |
set_news_providers
Set news provider codes for per-contract news ticks.
pub fn set_news_providers(&self, providers: &str)
| Parameter | Type | Description |
|---|---|---|
providers | &str | News provider list. |
quote
Zero-copy SeqLock quote read. Maps reqId → InstrumentId → SeqLock. Returns None if the reqId is not mapped to a subscription.
pub fn quote(&self, req_id: i64) -> Option<crate::types::model::QuoteSnapshot>
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
Returns: Option<crate::types::model::QuoteSnapshot>
quote_by_instrument
Direct SeqLock read by InstrumentId (for callers who track IDs themselves). Returns None for an id past every slot the instrument table holds.
pub fn quote_by_instrument(&self, instrument: InstrumentId) -> Option<crate::types::model::QuoteSnapshot>
| Parameter | Type | Description |
|---|---|---|
instrument | InstrumentId | Instrument type for scanner (e.g. "STK", "FUT"). |
Returns: Option<crate::types::model::QuoteSnapshot>
option_model
What the venue's own model last made of an option, whole. Wrapper::tick_option_computation carries its greeks and its price, beside the volatility and the underlying's price a gateway takes from the venue's other series. The venue states eighteen figures on this record, and the ones the callback has no room for — its own volatility and underlying price, the rate greek, the expected time to exercise and the price that triggers it, the forward coefficient, two yields, the time value, the days the model counted, the rate it discounted at, and which kind of volatility it priced on — are on the record this returns. A figure the venue did not state is f64::MAX, as everywhere else on this record; zero is a real greek. None where the request names no subscription, or the venue has not stated a model for it yet.
pub fn option_model(&self, req_id: i64) -> Option<crate::types::OptionComputation>
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
Returns: Option<crate::types::OptionComputation>
short_sale_restricted
Whether the venue is restricting short sales in the contract a request is watching. The circuit breaker a venue puts on a contract that has fallen far enough in a day, which stops a short from resting below the bid. The venue states it on the same record as the halt, and it has no field anywhere in the documented API. Not the same question as whether the contract can be borrowed, which Wrapper::tick_generic already answers beside it: a contract can be freely borrowable and still restricted. false where the request names no subscription, as it is for a contract the venue has not restricted.
pub fn short_sale_restricted(&self, req_id: i64) -> bool
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
Returns: bool
short_sale_restricted_by_instrument
The same, by InstrumentId, for callers who track them themselves.
pub fn short_sale_restricted_by_instrument(&self, instrument: InstrumentId) -> bool
| Parameter | Type | Description |
|---|---|---|
instrument | InstrumentId | Instrument type for scanner (e.g. "STK", "FUT"). |
Returns: bool
contract_figures
What the venue says about the contract itself, beside its prices. How many shares the company has on issue — the multiplier that turns a price into a market capitalisation — and what the contract opened at a year ago, which gives a trailing return without asking for a year of history. Both arrive on the tick that carries the price extremes, on every subscription that asks for them, and both were read past. The documented API reaches neither from a quote: a share count is a fundamentals request of its own there, and a year-ago open has no call at all. So they are read here rather than sent as ticks — a tick number of this client's own choosing, where a caller reads the venue's, is not something this client invents. None where the request names no subscription, or the venue has stated neither figure for it yet; f64::MAX for a figure it has not stated.
pub fn contract_figures(&self, req_id: i64) -> Option<crate::types::ContractFigures>
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
Returns: Option<crate::types::ContractFigures>
contract_figures_by_instrument
The same, by InstrumentId, for callers who track them themselves.
pub fn contract_figures_by_instrument( &self, instrument: InstrumentId, ) -> Option<crate::types::ContractFigures>
| Parameter | Type | Description |
|---|---|---|
instrument | InstrumentId | Instrument type for scanner (e.g. "STK", "FUT"). |
Returns: Option<crate::types::ContractFigures>
stated_figures
What one of the venue's own series last stated for a subscription, in the order the series states it. The venue runs dozens of series the documented API has no call for: a bond's analytics, an option's model volatility, the volume a contract usually opens on, what margin a future takes. Ask for one by its own number in the generic tick list, and read what it stated here. The figures come as the venue stated them — its order, its widths — and a figure it holds nothing for comes as the largest number its field carries. Empty where the request names no subscription, or that series has stated nothing for it. Read here rather than sent as ticks for the same reason the contract figures are: a tick number of this client's own choosing, where a caller reads the venue's, is not something this client invents.
pub fn stated_figures(&self, req_id: i64, series: u32) -> Vec<f64>
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
series | u32 |
Returns: Vec<f64>
numbered_figures
What one of the venue's numbered-table series last stated for a subscription: its figures under the venue's own numbering. Four series state their figures as two tables, whole numbers and fractional ones, each entry naming what it is before stating it. Two of those numbers have a documented call to arrive on and the rest have none; the rest are here, under the number the venue gave them. fractional picks the table. The venue numbers the two separately, so the same number in each is not the same figure.
pub fn numbered_figures( &self, req_id: i64, series: u32, fractional: bool, ) -> Vec<(i32, f64)>
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
series | u32 | |
fractional | bool |
Returns: Vec<(i32, f64)>
paired_figures
The run of paired figures one series last stated for a subscription. Two series state their figures as a count and then that many pairs: the volatility the venue's own model puts on each point of a curve, and the weight it puts on each price a contract might reach. Neither has a documented call to arrive on.
pub fn paired_figures(&self, req_id: i64, series: u32) -> Vec<(f64, f64)>
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
series | u32 |
Returns: Vec<(f64, f64)>
stated_rows
The rows of three figures one series last stated for a subscription. What the three are is the series' own: | Series | The three figures | | --- | --- | | 547 | A quantity, what it is offered at, and a second price where the form states one | | 491 | Which strategy the leg belongs to, the contract it names, and its size | | 320, 376, 530, 532 | The venue's number for a field of a packed quote, its figure, and how far that figure's decimal point moves | On the packed quotes the venue numbers its fields itself: nought and one are the two sides of the quote and four and five the size behind each, read off a live session. A side the venue is not standing behind reads as minus one hundred. Those figures are counted in the contract's own increments, as every packed figure is — 75815 against an increment of a hundredth is 758.15 — and no scale is put on them here, because which fields are prices and which are counts is the venue's to change. f64::MAX stands where a form states no third figure. The documented API has no call for any of these.
pub fn stated_rows(&self, req_id: i64, series: u32) -> Vec<(f64, f64, f64)>
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
series | u32 |
Returns: Vec<(f64, f64, f64)>
chain_model_parameters
What one of the option model's chain series last stated for a subscription on an underlying. Per class of the underlying's options: the underlying's price, the dividends expected, and per expiry the yield, the interest rate, the forward and the at-the-money volatilities the model works from. Ask for series 687 for the standing set, or 691 for the set as the chain closed, in the generic tick list of a subscription on the underlying. The documented API has no call for either: a gateway reads them for its own option model and hands none of it on. Empty where the request names no subscription, or the series has stated nothing for it.
pub fn chain_model_parameters( &self, req_id: i64, series: u32, ) -> Vec<crate::types::ChainModelParameters>
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
series | u32 |
Returns: Vec<crate::types::ChainModelParameters>
stated_rows_series
Which series have stated rows for a subscription, in order.
pub fn stated_rows_series(&self, req_id: i64) -> Vec<u32>
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
Returns: Vec<u32>
paired_figures_series
Which series have stated paired figures for a subscription, in order.
pub fn paired_figures_series(&self, req_id: i64) -> Vec<u32>
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
Returns: Vec<u32>
numbered_figures_series
Which series have stated numbered figures for a subscription, in order.
pub fn numbered_figures_series(&self, req_id: i64) -> Vec<u32>
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
Returns: Vec<u32>
stated_figures_series
Which series have stated figures for a subscription, in order.
pub fn stated_figures_series(&self, req_id: i64) -> Vec<u32>
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
Returns: Vec<u32>
company_data
What the venue states about a contract's company or its terms on one series, as the pairs it wrote. Seventeen series carry this text, each asked for by the venue's own number for it in the generic tick list: the analyst ratings, what institutions and insiders hold, the shares on issue and the float, fund terms, the screening scores, margin, the technical readings, the company's accounts and what it has coming. The keys are the venue's own and are handed on unchanged. Held against the venue's id for the contract rather than the request, because it is a fact about the contract and outlives the subscription that fetched it — so it is read by con_id, not by request number, and it is still there after the watch ends — for four thousand and ninety-six contracts, the one heard of longest ago making way. Empty where that series has stated nothing for the contract.
pub fn company_data(&self, con_id: u32, series: u32) -> Vec<(String, String)>
| Parameter | Type | Description |
|---|---|---|
con_id | u32 | Contract ID. Unique per instrument. |
series | u32 |
Returns: Vec<(String, String)>
company_data_series
Which of those series have been stated for a contract, in order.
pub fn company_data_series(&self, con_id: u32) -> Vec<u32>
| Parameter | Type | Description |
|---|---|---|
con_id | u32 | Contract ID. Unique per instrument. |
Returns: Vec<u32>
closing_option_model
What the venue's model made of an option as it closed. The same model as Self::option_model and in the same shape — every greek it states, the ones the documented API has no field for included — but worked out as the contract closed rather than as it stands. The documented API has no call for it at all. Ask for it by the venue's own number for the series in the generic tick list.
pub fn closing_option_model(&self, req_id: i64) -> Option<crate::types::OptionComputation>
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
Returns: Option<crate::types::OptionComputation>
closing_option_model_by_instrument
The same, by InstrumentId, for callers who track them themselves.
pub fn closing_option_model_by_instrument( &self, instrument: InstrumentId, ) -> Option<crate::types::OptionComputation>
| Parameter | Type | Description |
|---|---|---|
instrument | InstrumentId | Instrument type for scanner (e.g. "STK", "FUT"). |
Returns: Option<crate::types::OptionComputation>
option_model_by_instrument
The same, by InstrumentId, for callers who track them themselves.
pub fn option_model_by_instrument( &self, instrument: InstrumentId, ) -> Option<crate::types::OptionComputation>
| Parameter | Type | Description |
|---|---|---|
instrument | InstrumentId | Instrument type for scanner (e.g. "STK", "FUT"). |
Returns: Option<crate::types::OptionComputation>
Reference Data
req_historical_data
Request historical data. With keep_up_to_date, the bar still forming is folded here from the stream the venue sends, going on from the history's last bar as a gateway's does, and a new one opens on a whole multiple of its own length counted from the epoch. For every size up to an hour that is the clock boundary a caller expects. A 1 day bar is the one the history last stated until that ends; the next five-second bar then opens a bar at midnight UTC of its own day, as a gateway opens it, ending at the next midnight UTC or at the close the history stated where that falls between the two, and a bar that has ended is opened again by the next five-second bar. Each is dated by where it ends, on the series' zone. A week opens on its Monday and a month on its first day, both at midnight UTC, on the calendar as a gateway folds them. Bars already closed are the venue's own and are not folded here.
pub fn req_historical_data( &self, req_id: i64, contract: &Contract, end_date_time: &str, duration: &str, bar_size: &str, what_to_show: &str, use_rth: bool, format_date: i32, keep_up_to_date: bool, )
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
end_date_time | &str | End date/time in "YYYYMMDD HH:MM:SS" format, or empty for now. |
duration | &str | Duration string, e.g. "1 D", "1 W", "1 M", "1 Y". |
bar_size | &str | Bar size: "1 min", "5 mins", "1 hour", "1 day", etc. |
what_to_show | &str | Data type: "TRADES", "MIDPOINT", "BID", "ASK", "BID_ASK", etc. |
use_rth | bool | If true, only return data from Regular Trading Hours. |
format_date | i32 | Date format: 1="YYYYMMDD HH:MM:SS", 2=Unix seconds. |
keep_up_to_date | bool | If true, continue receiving updates after initial history. |
cancel_historical_data
Cancel historical data.
pub fn cancel_historical_data(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
req_head_time_stamp
Request head timestamp.
pub fn req_head_time_stamp( &self, req_id: i64, contract: &Contract, what_to_show: &str, use_rth: bool, format_date: i32, )
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
what_to_show | &str | Data type: "TRADES", "MIDPOINT", "BID", "ASK", "BID_ASK", etc. |
use_rth | bool | If true, only return data from Regular Trading Hours. |
format_date | i32 | Date format: 1="YYYYMMDD HH:MM:SS", 2=Unix seconds. |
req_contract_details
Request contract details.
pub fn req_contract_details(&self, req_id: i64, contract: &Contract)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
cancel_contract_data
Withdraw a contract lookup. Nothing is sent and nothing answers, and req_id names nothing to withdraw. A gateway asks the venue nothing for this either: it only stops re-sending a lookup it held back while its connection to the venue was down, and this client holds none back — a lookup made with no connection is refused there and then. A lookup already asked for is still answered, as it is through a gateway.
pub fn cancel_contract_data(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
req_mkt_depth_exchanges
Request available exchanges for market depth.
pub fn req_mkt_depth_exchanges(&self)
req_matching_symbols
Request matching symbols.
pub fn req_matching_symbols(&self, req_id: i64, pattern: &str)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
pattern | &str | Symbol search pattern. |
req_wsh_meta_data
Ask what event types the corporate-events calendar carries. The events depend on this, not the other way: a gateway keeps the calendar metadata in a cache written only by this request's answer, and an event request in a session the cache is empty in is refused under 10282 rather than sent. A second metadata request in a session the cache is full in is answered from it at once rather than sent.
pub fn req_wsh_meta_data(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
cancel_wsh_meta_data
Stop waiting on the event types. Both cancels do the same thing, a gateway quirk this client keeps: either frees a metadata request that is on the wire — silently, whatever number it names — and says nothing at all. Neither touches a pending events request, whose answer still reaches the caller, and an answer for the freed metadata request is dropped when it arrives. The venue is never told: the query is one message and one answer.
pub fn cancel_wsh_meta_data(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
cancel_wsh_event_data
Stop waiting on the calendar's events. As above: frees the metadata slot, not the events request — the gateway's quirk, kept as it is.
pub fn cancel_wsh_event_data(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
req_wsh_event_data
Ask the corporate-events calendar for events. Exactly one scope is a request a gateway takes: a named contract — any id but the field's unset marker — or a non-empty filter the caller wrote. Naming both, or neither, is refused under 10309. The filter goes to the venue as written: the venue validates it, and rewriting it here would change what was asked. A metadata request answered this session must precede it; until one has been, the request is refused under 10282 rather than sent.
pub fn req_wsh_event_data( &self, req_id: i64, query: crate::types::CalendarQuery, )
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
query | crate::types::CalendarQuery |
req_sec_def_opt_params
Request option chain parameters. fut_fop_exchange names the venue for a futures option chain and is empty for an equity or index one.
pub fn req_sec_def_opt_params( &self, req_id: i64, underlying_symbol: &str, fut_fop_exchange: &str, underlying_sec_type: &str, underlying_con_id: i64, )
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
underlying_symbol | &str | Underlying symbol (e.g. "AAPL"). |
fut_fop_exchange | &str | Exchange for futures/FOP options. |
underlying_sec_type | &str | Underlying security type (e.g. "STK"). |
underlying_con_id | i64 | Underlying contract ID. |
cancel_head_time_stamp
Cancel head timestamp request.
pub fn cancel_head_time_stamp(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
req_market_rule
The price increments a market rule states. Any rule this client holds is answered; a rule it does not hold is refused as a gateway refuses one, under -1, which is not the rule's number — a rule's number is no request number. The rule table a gateway keeps of its own is not ported here: rules arrive with the details of a contract that uses them, and the limits page says so.
pub fn req_market_rule(&self, market_rule_id: i32)
| Parameter | Type | Description |
|---|---|---|
market_rule_id | i32 | Market rule ID. |
req_news_bulletins
Subscribe to news bulletins. all_msgs asks for the day's bulletins as well as the ones still to come. The subscription carries no field asking the venue for them, but the venue has been broadcasting them at this session since it opened and they are still cached, so a caller asking for every message of the day is answered from those — without consuming them: the cache is the day's, a gateway clears it only at day rollover, and asking again restates it. Asking only for what follows starts the subscription where the call is made and leaves the cache to a later ask.
pub fn req_news_bulletins(&self, all_msgs: bool)
| Parameter | Type | Description |
|---|---|---|
all_msgs | bool | If true, receive all existing bulletins on subscribe. |
cancel_news_bulletins
Cancel news bulletin subscription.
pub fn cancel_news_bulletins(&self)
req_scanner_parameters
Request scanner parameters XML.
pub fn req_scanner_parameters(&self)
req_scanner_subscription
Subscribe to a market scanner. filters are the scanner filter tags named by req_scanner_parameters, e.g. priceAbove = "10" or stkTypes = "inc:ETF". scanner_setting_pairs is taken and not carried to the venue, with a warning once when a caller states it.
pub fn req_scanner_subscription( &self, req_id: i64, instrument: &str, location_code: &str, scan_code: &str, max_items: u32, filters: &[TagValue], scanner_setting_pairs: &str, )
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
instrument | &str | Instrument type for scanner (e.g. "STK", "FUT"). |
location_code | &str | Scanner location (e.g. "STK.US.MAJOR"). |
scan_code | &str | Scanner code (e.g. "TOP_PERC_GAIN", "HIGH_OPT_IMP_VOLAT"). |
max_items | u32 | Maximum number of scanner results. |
filters | &[TagValue] | Scanner filter tags from req_scanner_parameters, e.g. priceAbove = "10". |
scanner_setting_pairs | &str |
cancel_scanner_subscription
Cancel a scanner subscription.
pub fn cancel_scanner_subscription(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
req_historical_news
Request historical news headlines. A nonempty start requests newer headlines from that bound; otherwise the end requests older headlines. The selected bound is sent as stated, without parsing it locally. When both are empty, the request asks for the latest headlines. No more than three hundred are asked for however many are wanted. A gateway caps total_results there before the request goes out, so a bigger number is one the venue is never asked, and it passes a smaller positive one on as stated; a count below one it refuses with 321 before the venue is asked anything.
pub fn req_historical_news( &self, req_id: i64, con_id: i64, provider_codes: &str, start_time: &str, end_time: &str, total_results: i32, )
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
con_id | i64 | Contract ID. Unique per instrument. |
provider_codes | &str | Pipe-separated news provider codes. |
start_time | &str | Start date/time for news query. |
end_time | &str | End date/time for news query. |
total_results | i32 | Maximum number of news results. |
req_news_article
Request a news article by provider and article ID.
pub fn req_news_article(&self, req_id: i64, provider_code: &str, article_id: &str)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
provider_code | &str | News provider code (e.g. "BRFG"). |
article_id | &str | News article identifier. |
req_adjustments
Ask for a contract's corporate actions over a range of days. No callback carries the answer; a refusal arrives on error under this id, as any request's does, and gives the request up: nothing is held for it after, and there is nothing to withdraw. The answer is held under the id until adjustments_for takes it or cancel_adjustments gives it up, so a request that is neither taken nor withdrawn holds its answer for the rest of the session. It is also filed against the contract it names, where EClient::adjustments reads the last answer about that contract whoever asked, and corporate_actions asks and waits in one call. start_date and end_date are days, as YYYYMMDD.
pub fn req_adjustments( &self, req_id: i64, con_id: i64, sec_type: &str, exchange: &str, start_date: &str, end_date: &str, )
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
con_id | i64 | Contract ID. Unique per instrument. |
sec_type | &str | |
exchange | &str | Exchange name. |
start_date | &str | |
end_date | &str |
adjustments_for
The corporate actions answering a req_adjustments under this id, once they have arrived. Taken rather than read: the answer is handed over once and the request holds nothing after it. None until the answer arrives, and for a request this session is not holding one for. A contract the venue states nothing for answers with an empty list, which is an answer.
pub fn adjustments_for(&self, req_id: i64) -> Option<Vec<Adjustment>>
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
Returns: Option<Vec<Adjustment>>
cancel_adjustments
Give up on a req_adjustments: whatever it holds is let go of, and the venue is told to stop serving the query. For a request whose answer has not come and is no longer wanted: the venue serves the query until it is withdrawn. A withdrawal naming no query this client is waiting on, one already answered included, is reported on error under 300.
pub fn cancel_adjustments(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
req_fundamental_data
Request fundamental data. Three reports, which are the three the venue states: ReportSnapshot, RESC for what analysts expect, and CalendarReport for what the issuer has coming. The report is asked about the stock the venue's id names: a contract given by its description is looked up first, as a gateway looks it up. A contract not stated as a stock is refused, as a gateway refuses it.
pub fn req_fundamental_data(&self, req_id: i64, contract: &Contract, report_type: &str)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
report_type | &str | Report type: "ReportSnapshot", "ReportsFinSummary", "RESC", etc. |
cancel_fundamental_data
Cancel fundamental data.
pub fn cancel_fundamental_data(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
cancel_historical_news
Withdraw a historical news query. The TWS API has no call for this; the venue has a message for it. One message carrying the id the query went out under, which is the whole of what a withdrawal states, sent for a query still waiting on its answer. Nothing of a query is kept once it is answered, as a gateway keeps nothing of one: withdrawn after its answer, it is refused as naming nothing.
pub fn cancel_historical_news(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
req_histogram_data
Request price histogram data. Asked about the contract as the venue names it, as req_fundamental_data is.
pub fn req_histogram_data(&self, req_id: i64, contract: &Contract, use_rth: bool, period: &str)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
use_rth | bool | If true, only return data from Regular Trading Hours. |
period | &str | Histogram period, e.g. "1week", "1month". |
cancel_histogram_data
Cancel histogram data.
pub fn cancel_histogram_data(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
req_historical_ticks
Request historical tick data. Named from one end and counted from there: give start_date_time for the ticks after a moment or end_date_time for the ones before it, and number_of_ticks says how far it reaches. Where both are named the start wins and the ticks run forward from it, as at a gateway; where neither is, the query still goes — the gateway's epoch-anchored forward window — and the venue's own answer or refusal is relayed. ignore_size asks the venue to leave out a bid/ask change that moves only a size, and what it answers is passed on as it stands, as a gateway passes it: nothing is filtered here. A gateway asks for midpoint ticks that way whatever the caller asked, and so does this client; for trades it is not asked. One session saw the venue answer the same with the filter as without it.
pub fn req_historical_ticks( &self, req_id: i64, contract: &Contract, start_date_time: &str, end_date_time: &str, number_of_ticks: i32, what_to_show: &str, use_rth: bool, ignore_size: bool, )
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
start_date_time | &str | Start date/time for tick query. |
end_date_time | &str | End date/time in "YYYYMMDD HH:MM:SS" format, or empty for now. |
number_of_ticks | i32 | Maximum number of ticks to return. |
what_to_show | &str | Data type: "TRADES", "MIDPOINT", "BID", "ASK", "BID_ASK", etc. |
use_rth | bool | If true, only return data from Regular Trading Hours. |
ignore_size | bool | If true, asks that a bid/ask change moving only a size be left out. |
cancel_historical_ticks
Withdraw a historical ticks request. Nothing is sent and nothing answers, and req_id names nothing to withdraw, as for cancel_contract_data: a gateway only stops re-sending a request it held back while its connection to the venue was down, which this client never does. Ticks already asked for still arrive, and a request waiting for its contract to be named still goes once it is, as through a gateway.
pub fn cancel_historical_ticks(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
req_historical_schedule
Request historical trading schedule.
pub fn req_historical_schedule( &self, req_id: i64, contract: &Contract, end_date_time: &str, duration: &str, use_rth: bool, )
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
end_date_time | &str | End date/time in "YYYYMMDD HH:MM:SS" format, or empty for now. |
duration | &str | Duration string, e.g. "1 D", "1 W", "1 M", "1 Y". |
use_rth | bool | If true, only return data from Regular Trading Hours. |
Gateway-Local & Stubs
req_config
Request configuration. Reports 10357 through Wrapper::error.
pub fn req_config(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
update_config
Request a configuration update. Reports 10357 through Wrapper::error.
pub fn update_config(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
req_smart_components
Request smart routing components for a BBO exchange. Answered from the map of venues the venue stated beside the subscription whose acknowledgement named that BBO exchange — the one tick_req_params states. The venue states a map per BBO exchange and security type, so one contract's venues are not another's. A BBO exchange no subscription named is refused as a gateway refuses it. One whose map has not arrived yet is waited for up to two seconds, as a gateway waits, and answered from process_msgs rather than by holding this call.
pub fn req_smart_components(&self, req_id: i64, bbo_exchange: &str)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
bbo_exchange | &str | BBO exchange for smart component lookup (e.g. "SMART"). |
req_news_providers
Request available news providers. Gateway-local — returns provider list from init data.
pub fn req_news_providers(&self)
req_current_time
The venue's clock, as reqCurrentTime reports it. The venue is never asked. There is no request for this on the wire, so the answer is worked out here: this machine's clock, shifted by what the venue has stated about its own — on the logon it stamps, and in the clock it pushes afterwards. A session that has been told nothing is shifted by nothing and answers this machine's clock, which is what a caller who asks before the venue has said anything gets. This is a question that always has an answer, and never a refusal.
pub fn req_current_time(&self)
req_current_time_in_millis
The venue's clock in milliseconds, as reqCurrentTimeInMillis reports it. The same clock req_current_time reports and worked out the same way. What differs is the precision kept: asking in seconds throws away the fraction this one keeps.
pub fn req_current_time_in_millis(&self)
request_fa
Ask the venue for a partition of the advisor's own configuration. The reference client names the partition by a number: its groups, its allocation profiles, its aliases. The allocation profiles are refused at the door under 585, with nothing sent, as the reference client refuses them. For the rest, the venue names the partition by a word, so the number is turned into the word it stands for; a number that stands for nothing draws the intake's own refusal rather than being sent as an empty partition. The venue's answer reaches Wrapper::receive_fa under the same number the partition was asked for by.
pub fn request_fa(&self, fa_data_type: i32)
| Parameter | Type | Description |
|---|---|---|
fa_data_type | i32 | FA data type (1=Groups, 2=Profiles, 3=Aliases). |
replace_fa
Replace a partition of the advisor's configuration with the one given. The allocation profiles are refused at the door under 585, as the reference client refuses them, and a number naming no partition draws the intake's own refusal; either way nothing is sent and the caller hears it under req_id. Wrapper::replace_fa_end fires with req_id once the venue has taken it, and a venue that refuses states why on Wrapper::error under the same number.
pub fn replace_fa(&self, req_id: i64, fa_data_type: i32, cxml: &str)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
fa_data_type | i32 | FA data type (1=Groups, 2=Profiles, 3=Aliases). |
cxml | &str | FA XML configuration data. |
calculate_implied_volatility
What volatility a price implies, under the venue's model. This protocol carries no request for it, so the value is computed here, anchored to the venue's last stated model output for this contract, and delivered on tick_option_computation under 53 in its place in the session's order. Where the venue has stated no model, the contract is watched so it states one, and the engine answers where the model arrives, right behind it — rather than the question being refused for having been asked first. Answered over a year, which is the scale tick_option_computation reports the venue's own volatility on, so the two read against each other.
pub fn calculate_implied_volatility( &self, req_id: i64, contract: &super::Contract, option_price: f64, under_price: f64, )
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
contract | &super::Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
option_price | f64 | Option market price. |
under_price | f64 | Underlying asset price. |
calculate_option_price
What price a volatility implies, under that same model.
pub fn calculate_option_price( &self, req_id: i64, contract: &super::Contract, volatility: f64, under_price: f64, )
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
contract | &super::Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
volatility | f64 | Implied volatility. |
under_price | f64 | Underlying asset price. |
cancel_calculate_implied_volatility
Withdraw a question that was waiting on the venue to state a model. A question answered from a model already stated started nothing and stops nothing. One that opened a watch to get an answer withdraws it here, so a caller that changes its mind is not left watching a contract it no longer asks about.
pub fn cancel_calculate_implied_volatility(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
cancel_calculate_option_price
As for cancel_calculate_implied_volatility.
pub fn cancel_calculate_option_price(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
query_display_groups
Query display groups. The display groups on offer. Answered on display_group_list. A display group is a way for several callers on one session to agree on a contract. Nothing about one crosses this wire, so they are kept here and served to callers from here.
pub fn query_display_groups(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
subscribe_to_group_events
Follow a display group. Answered on display_group_updated, at once with what the group holds and again whenever it changes.
pub fn subscribe_to_group_events(&self, req_id: i64, group_id: i32)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
group_id | i32 | Display group ID. |
unsubscribe_from_group_events
Stop following a display group.
pub fn unsubscribe_from_group_events(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
update_display_group
Put a contract in the group this request follows, stated as conId@exchange, or none to empty it. Every follower of that group is told, including this one.
pub fn update_display_group(&self, req_id: i64, contract_info: &str)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
contract_info | &str | Display group contract info string. |
verify_request
Answered as the reference client answers it: on error, under 508 and no request, because intent to authenticate is stated on the initial connect and was not. Nothing is sent, so api_name and api_version reach nothing.
pub fn verify_request(&self, api_name: &str, api_version: &str)
| Parameter | Type | Description |
|---|---|---|
api_name | &str | |
api_version | &str |
verify_and_auth_request
As verify_request: api_name, api_version and opaque_isv_key reach nothing.
pub fn verify_and_auth_request(&self, api_name: &str, api_version: &str, opaque_isv_key: &str)
| Parameter | Type | Description |
|---|---|---|
api_name | &str | |
api_version | &str | |
opaque_isv_key | &str |
verify_message
Nothing is sent and nothing answers. A gateway reads this message and discards it, so a program on one is answered by nothing either, and api_data reaches nothing there or here.
pub fn verify_message(&self, api_data: &str)
| Parameter | Type | Description |
|---|---|---|
api_data | &str |
verify_and_auth_message
As verify_message: api_data and xyz_response reach nothing.
pub fn verify_and_auth_message(&self, api_data: &str, xyz_response: &str)
| Parameter | Type | Description |
|---|---|---|
api_data | &str | |
xyz_response | &str |
req_soft_dollar_tiers
Request soft dollar tiers. Gateway-local — returns tiers parsed from CCP logon tag 6560.
pub fn req_soft_dollar_tiers(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
req_family_codes
Request family codes. Gateway-local — returns codes parsed from CCP logon tag 6823.
pub fn req_family_codes(&self)
set_server_log_level
Set server log level. 1 to 5 are a gateway's System, Error, Warning, Info and Detail, and set this client's logger to error, error, warn, info and trace. A gateway applies the level to its own log; this client, which serves the caller in its place, applies it to the logger it installed. Nothing goes to the venue, which has no message for it. Where the program installed a logger of its own, that logger's level is the program's, and the call says so on the error callback rather than reporting a level it did not set. A level outside 1 to 5 is refused the same way.
pub fn set_server_log_level(&self, log_level: i32)
| Parameter | Type | Description |
|---|---|---|
log_level | i32 | A gateway's level: 1=System, 2=Error, 3=Warning, 4=Info, 5=Detail; this client's logger goes to error, error, warn, info and trace. |
req_user_info
Request user info. Gateway-local — returns whiteBrandingId from CCP logon.
pub fn req_user_info(&self, req_id: i64)
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
Wrapper Callbacks
connect_ack
The session is open. Nothing has been asked for yet.
connection_closed
The session is over, because this client ended it. A session that went away instead is reported on error under 1100.
next_valid_id
The first order id this session may use. Each order needs one higher than the last.
| Parameter | Type | Description |
|---|---|---|
order_id | i64 | Order identifier. Must be unique per session. |
managed_accounts
Every account this login may act for, separated by commas. One for most logins; an advisor has several.
| Parameter | Type | Description |
|---|---|---|
accounts_list | &str | Comma-separated account IDs. |
error
What the venue said about a request, under the number it says it with. Codes from 2100 to 2200 are notices about a connection rather than failures. req_id is -1 for anything that answers no particular request. A request this client will not send is reported here too, under the same numbers the reference client uses: 321 for a request that fails validation, 200 for a contract description that matches nothing, 504 for a call made with no session. error_time is the reference client's second parameter, in milliseconds since the epoch. A gateway stamps every error it sends, and so does this client: what the venue said about an order on a report, a refusal or a message, with the second the report states it was sent, and every other error with the clock as it is delivered.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
error_time | i64 | When the venue sent the report it comes from, to the second, for what the venue said about an order; otherwise when this client delivered it. In milliseconds since the epoch. |
error_code | i64 | Error code. |
error_string | &str | Error message. |
advanced_order_reject_json | &str | JSON with advanced rejection details. |
error_from
An error, with what it is about: a request and whether nothing more follows for it, an order and the operation it answers, a request that carries no number, the session, or a lookup this client made for itself. The number error carries can be any of these and does not say which; this says. Every error is delivered here. By default it goes on to error, under the number ErrorOrigin::id states it under, so a wrapper that implements only error sees exactly what it saw before this existed.
| Parameter | Type | Description |
|---|---|---|
origin | ErrorOrigin | What the error is about: a request, an order and the operation on it, a request with no number of its own, the session, or a lookup this client made for itself. |
error_time | i64 | When the venue sent the report it comes from, to the second, for what the venue said about an order; otherwise when this client delivered it. In milliseconds since the epoch. |
error_code | i64 | Error code. |
error_string | &str | Error message. |
advanced_order_reject_json | &str | JSON with advanced rejection details. |
current_time
The venue's clock, in seconds since the epoch.
| Parameter | Type | Description |
|---|---|---|
time | i64 | Tick timestamp (Unix seconds). |
current_time_in_millis
The venue's clock, in milliseconds since the epoch. The same clock current_time reports, at the precision the venue stated it in. The stamp can carry a fraction of a second and this reads it where it does — but on the sessions measured here the venue stated none, so the answer lands on a whole second and is the other call's thousandfold. Read the precision off the number rather than assuming this one has more of it.
| Parameter | Type | Description |
|---|---|---|
time_in_millis | i64 |
tick_price
One price of a quote, and which price it is. tick_type names it — 1 bid, 2 ask, 4 last, 9 close — and attrib says whether it can be traded against and whether it is past its limit. A size arrives on tick_size under the type that belongs to it.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
tick_type | i32 | Tick type ID or tick-by-tick type string. |
price | f64 | Tick price. |
attrib | &TickAttrib | Tick attributes. |
tick_size
One size of a quote, and which size it is: 0 bid, 3 ask, 5 last, 8 the day's volume.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
tick_type | i32 | Tick type ID or tick-by-tick type string. |
size | Decimal | Tick size. |
tick_string
A quote's value that is not a number — a timestamp, an exchange map, a set of ids.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
tick_type | i32 | Tick type ID or tick-by-tick type string. |
value | &str | Account value. |
tick_generic
A quote's value that is a number and is not a price or a size — an implied volatility, an index future's premium, a halt.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
tick_type | i32 | Tick type ID or tick-by-tick type string. |
value | f64 | Account value. |
tick_snapshot_end
A snapshot has stated everything it is going to. Only for a subscription asked for as a snapshot; a streaming one never ends.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
market_data_type
Which feed a subscription is being served from: 1 live, 2 frozen, 3 delayed, 4 delayed and frozen.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
market_data_type | i32 | 1=live, 2=frozen, 3=delayed, 4=delayed-frozen. |
order_status
Where an order stands now: stated as the venue reports on the order, again on each fill, and after the order's open_order when open orders are asked for. Sending a cancel states nothing, as a gateway states nothing then: asked for, the order reads PendingCancel, and the venue's next report on it states it so. filled and remaining are shares, avg_fill_price the average of what has filled so far.
| Parameter | Type | Description |
|---|---|---|
order_id | i64 | Order identifier. Must be unique per session. |
status | &str | Order status string ("Submitted", "Filled", "Cancelled", etc.). |
filled | Decimal | Cumulative filled quantity. |
remaining | Decimal | Remaining quantity. |
avg_fill_price | f64 | Average fill price. |
perm_id | i64 | The order's permanent id: the number it goes to the venue under. |
parent_id | i64 | Parent order ID (0 if no parent). |
last_fill_price | f64 | Price of the last fill. |
client_id | i64 | API client ID for order ownership and the saved order-id counter. |
why_held | &str | Reason the order is held (e.g. "locate"). |
mkt_cap_price | f64 | Market cap price for the order. |
open_order
An order as the venue holds it, and the state it is in. Fires beside every order_status, when open orders are asked for, and once for a preview — where the state carries what the order would cost and no status follows, because a preview is not an order.
| Parameter | Type | Description |
|---|---|---|
order_id | i64 | Order identifier. Must be unique per session. |
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
order | &Order | Order parameters (action, quantity, type, price, TIF, etc.). |
order_state | &OrderState | Order state (status, margin, commission info). |
open_order_end
Every open order has been stated.
exec_details
One fill, against the order and contract it filled. What it cost arrives separately, on commission_and_fees_report.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
execution | &Execution | Execution details (exec_id, time, price, shares, etc.). |
exec_details_end
Every execution answering this request has been stated.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
commission_and_fees_report
What a fill cost, matched to it by execution id.
| Parameter | Type | Description |
|---|---|---|
report | &CommissionAndFeesReport | Commission report (exec_id, commission, currency, realized P&L). |
update_account_value
One figure the venue states about an account, in the currency it states it in. An account is stated in several currencies at once, so the same key arrives more than once.
| Parameter | Type | Description |
|---|---|---|
key | &str | Account value key (e.g. "NetLiquidation", "BuyingPower"). |
value | &str | Account value. |
currency | &str | Currency code (e.g. "USD"). |
account_name | &str | Account identifier. |
update_portfolio
One position, as the venue values it now.
| Parameter | Type | Description |
|---|---|---|
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
position | Decimal | Book position (row index) or position size. |
market_price | f64 | Current market price. |
market_value | f64 | Current market value of position. |
average_cost | f64 | Average cost basis. |
unrealized_pnl | f64 | Unrealized profit/loss. |
realized_pnl | f64 | Realized profit/loss. |
account_name | &str | Account identifier. |
update_account_time
When the account figures above were last stated.
| Parameter | Type | Description |
|---|---|---|
timestamp | &str | Timestamp string. |
account_download_end
The account has been fully stated. Fires once the venue has stopped adding to it, not on the first figure.
| Parameter | Type | Description |
|---|---|---|
account | &str | Account ID. |
account_summary
One figure answering req_account_summary, in the currency the venue states it in.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
account | &str | Account ID. |
tag | &str | Account tag name (e.g. "NetLiquidation"). |
value | &str | Account value. |
currency | &str | Currency code (e.g. "USD"). |
account_summary_end
Every figure answering this request has been stated.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
position
One position held, on any account this login may act for.
| Parameter | Type | Description |
|---|---|---|
account | &str | Account ID. |
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
pos | Decimal | Position size (decimal shares). |
avg_cost | f64 | Average cost per share. |
position_end
Every position has been stated.
question_retired
A question's cancel, confirmed where it stands: cancel_positions or req_account_updates(false, ..), the only questions with one. It follows every callback of the exchange it ends, and nothing of that exchange follows it: a caller that serializes its questions moves on here. A gateway says nothing at a cancel, so nothing reaches a caller that does not implement this.
| Parameter | Type | Description |
|---|---|---|
q | Question |
position_multi
A holding, answering req_positions_multi. Separate from position: a caller asks per account or model and is answered per request.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
account | &str | Account ID. |
model_code | &str | Model portfolio code (empty for default). |
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
pos | Decimal | Position size (decimal shares). |
avg_cost | f64 | Average cost per share. |
position_multi_end
Every position answering this request has been stated.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
account_update_multi
An account value, answering req_account_updates_multi.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
account | &str | Account ID. |
model_code | &str | Model portfolio code (empty for default). |
key | &str | Account value key (e.g. "NetLiquidation", "BuyingPower"). |
value | &str | Account value. |
currency | &str | Currency code (e.g. "USD"). |
account_update_multi_end
Every figure answering this request has been stated.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
pnl
An account's running profit: today's, what is unrealised, and what has been realised.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
daily_pnl | f64 | Daily profit/loss. |
unrealized_pnl | f64 | Unrealized profit/loss. |
realized_pnl | f64 | Realized profit/loss. |
pnl_single
The same for one position, with the size held.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
pos | Decimal | Position size (decimal shares). |
daily_pnl | f64 | Daily profit/loss. |
unrealized_pnl | f64 | Unrealized profit/loss. |
realized_pnl | f64 | Realized profit/loss. |
value | f64 | Account value. |
historical_data
One bar answering a historical request. bar.date is a day for a daily bar and a moment for anything shorter, in the zone the bar carries.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
bar | &BarData | Bar data (date, open, high, low, close, volume, wap, bar_count). |
historical_data_end
Every bar answering this request has been stated, and the window they cover.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
start | &str | Period start date/time. |
end | &str | Period end date/time. |
historical_data_update
A bar that continues a keep_up_to_date request, after its first batch completed. The bar still forming is restated as it changes.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
bar | &BarData | Bar data (date, open, high, low, close, volume, wap, bar_count). |
head_timestamp
The earliest moment the venue holds data for a contract.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
head_timestamp | &str | Earliest available data timestamp string. |
contract_details
One contract matching a description, with everything the venue states about it. A description can match more than one.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
details | &ContractDetails | Contract details object. |
contract_details_end
Every contract matching this request has been stated.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
symbol_samples
Contracts whose symbol or name matches a pattern, across venues.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
descriptions | &[ContractDescription] | Array of matching contract descriptions. |
tick_by_tick_all_last
One trade, as it happens. tick_attrib_last says whether it was past a limit and whether it goes unreported to the tape.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
tick_type | i32 | Tick type ID or tick-by-tick type string. |
time | i64 | Tick timestamp (Unix seconds). |
price | f64 | Tick price. |
size | Decimal | Tick size. |
attrib | &TickAttribLast | Tick attributes. |
exchange | &str | Exchange name. |
special_conditions | &str | Special trade conditions. |
tick_by_tick_bid_ask
One change to the top of the book, as it happens.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
time | i64 | Tick timestamp (Unix seconds). |
bid_price | f64 | Bid price. |
ask_price | f64 | Ask price. |
bid_size | Decimal | Bid size. |
ask_size | Decimal | Ask size. |
attrib | &TickAttribBidAsk | Tick attributes. |
tick_by_tick_mid_point
One change to the midpoint, as it happens.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
time | i64 | Tick timestamp (Unix seconds). |
mid_point | f64 | Midpoint price. |
scanner_data
One row of a scan, in rank order.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
rank | i32 | Scanner result rank (0-based). |
details | &ContractDetails | Contract details object. |
distance | &str | Scanner distance metric. |
benchmark | &str | Scanner benchmark. |
projection | &str | Scanner projection. |
legs_str | &str | Combo legs description. |
scanner_data_end
Every row of this scan has been stated.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
scanner_parameters
Every scan the venue offers and what each can be filtered by, as the XML the venue publishes.
| Parameter | Type | Description |
|---|---|---|
xml | &str | XML string. |
update_news_bulletin
A notice the venue broadcasts to everyone — an exchange unavailable, a system message.
| Parameter | Type | Description |
|---|---|---|
msg_id | i64 | Bulletin message ID. |
msg_type | i32 | Bulletin message type (1=regular, 2=exchange). |
message | &str | Bulletin message text. |
orig_exchange | &str | Originating exchange. |
tick_news
A headline about a contract being watched, as it is published.
| Parameter | Type | Description |
|---|---|---|
ticker_id | i64 | Ticker/request ID. |
timestamp | i64 | Timestamp string. |
provider_code | &str | News provider code (e.g. "BRFG"). |
article_id | &str | News article identifier. |
headline | &str | News headline text. |
extra_data | &str | Additional tick data. |
historical_news
One headline from the archive.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
time | &str | Tick timestamp (Unix seconds). |
provider_code | &str | News provider code (e.g. "BRFG"). |
article_id | &str | News article identifier. |
headline | &str | News headline text. |
historical_news_end
Every headline answering this request has been stated, and whether the archive holds more.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
has_more | bool | If true, more results available. |
news_article
The body of one article. article_type is 0 for text and 1 for a binary document.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
article_type | i32 | Article type: 0=plain text, 1=HTML. |
article_text | &str | Full article body. |
real_time_bar
One five-second bar of a live stream.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
date | i64 | Bar date string. |
open | f64 | Open price. |
high | f64 | High price. |
low | f64 | Low price. |
close | f64 | Close price. |
volume | Decimal | Volume. |
wap | Decimal | Volume-weighted average price. |
count | i32 | Trade count. |
historical_ticks
Historical midpoints, in batches, until done.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
ticks | &HistoricalTickData | Historical tick data. |
done | bool | If true, all ticks have been delivered. |
historical_ticks_bid_ask
Historical quotes, in batches, until done.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
ticks | &HistoricalTickData | Historical tick data. |
done | bool | If true, all ticks have been delivered. |
historical_ticks_last
Historical trades, in batches, until done.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
ticks | &HistoricalTickData | Historical tick data. |
done | bool | If true, all ticks have been delivered. |
tick_option_computation
The venue's model for an option: the volatility its price implies, the greeks, and what the model says the option and its underlying are worth.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
tick_type | i32 | Tick type ID or tick-by-tick type string. |
tick_attrib | i32 | |
implied_vol | f64 | Implied volatility. |
delta | f64 | Option delta. |
opt_price | f64 | Option theoretical price. |
pv_dividend | f64 | Present value of dividends. |
gamma | f64 | Option gamma. |
vega | f64 | Option vega. |
theta | f64 | Option theta. |
und_price | f64 | Underlying price. |
display_group_list
The display groups this client offers, |-separated.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
groups | &str | FA group definitions. |
display_group_updated
The contract a display group now holds, as conId@exchange, or none.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
contract_info | &str | Display group contract info string. |
bond_contract_details
A bond's contract details, answering req_contract_details for fixed income: a bond, a bill, and the type the venue spells FIXED. Every other type answers on contract_details.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
details | &ContractDetails | Contract details object. |
order_bound
The permanent id an order was given, paired with the id this client used.
| Parameter | Type | Description |
|---|---|---|
perm_id | i64 | The order's permanent id: the number it goes to the venue under. |
client_id | i64 | API client ID for order ownership and the saved order-id counter. |
order_id | i64 | Order identifier. Must be unique per session. |
receive_fa
An advisor's allocation groups, profiles or aliases, as XML.
| Parameter | Type | Description |
|---|---|---|
fa_data_type | i32 | FA data type (1=Groups, 2=Profiles, 3=Aliases). |
cxml | &str | FA XML configuration data. |
replace_fa_end
The end of a replace_fa exchange.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
text | &str | Informational text. |
wsh_meta_data
What the event calendar can answer about, as JSON.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
data_json | &str |
wsh_event_data
Calendar events, as JSON.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
data_json | &str |
security_definition_option_parameter
One venue's option chain for an underlying and trading class: the expiries and strikes it lists for that class.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
exchange | &str | Exchange name. |
underlying_con_id | i64 | Underlying contract ID. |
trading_class | &str | Trading class. |
multiplier | &str | Contract multiplier. |
expirations | &[String] | Available expiration dates. |
strikes | &[f64] | Available strike prices. |
security_definition_option_parameter_end
Every venue's chain has been stated.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
reroute_mkt_data_req
The contract a market-data request should be asked for under instead. A gateway sends it, and asks the venue nothing, for a contract for difference whose own definition asks for its underlying's data, where the account's logon permissions allow that: the underlying's contract and the venue to ask on. This client does not read a definition's flags for asking on the underlying before subscribing: the request goes to the venue as asked, and this never fires.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
con_id | i64 | Contract ID. Unique per instrument. |
exchange | &str | Exchange name. |
reroute_mkt_depth_req
The same, for a request for the book rather than the quote.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
con_id | i64 | Contract ID. Unique per instrument. |
exchange | &str | Exchange name. |
delta_neutral_validation
The contract the venue paired with a delta-neutral order. Declared by the TWS API and never fired on a gateway: a gateway never sends it. It fires here as it does there: never.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
con_id | i64 | Contract ID. Unique per instrument. |
delta | f64 | Option delta. |
price | f64 | Tick price. |
tick_efp
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
tick_type | i32 | Tick type ID or tick-by-tick type string. |
basis_points | f64 | |
formatted_basis_points | &str | |
implied_future | f64 | |
hold_days | i32 | |
future_last_trade_date | &str | |
dividend_impact | f64 | |
dividends_to_last_trade_date | f64 |
verify_message_api
A step in the TWS API's verification handshake. Declared by the TWS API and never fired on a gateway, so these four fire here as they do there: never.
| Parameter | Type | Description |
|---|---|---|
api_data | &str |
verify_completed
Whether that handshake was accepted.
| Parameter | Type | Description |
|---|---|---|
is_successful | bool | |
error_text | &str |
verify_and_auth_message_api
The same handshake, where the terminal also authenticates the program.
| Parameter | Type | Description |
|---|---|---|
api_data | &str | |
xyz_challenge | &str |
verify_and_auth_completed
Whether that one was accepted.
| Parameter | Type | Description |
|---|---|---|
is_successful | bool | |
error_text | &str |
win_error
Declared by the TWS API as winError. No message on the wire carries it, and the TWS API's Python client declares it and never raises it, so it fires here as it does there: never. Here, trouble on a connection reaches a caller on the error callback, with the reason the transport gave.
| Parameter | Type | Description |
|---|---|---|
text | &str | Informational text. |
last_error | i32 |
histogram_data
How much traded at each price over a window.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
items | &[(f64, Decimal | Histogram entries [(price, count)]. |
market_rule
The price ladder a contract trades on: each step, and what the price moves in above it.
| Parameter | Type | Description |
|---|---|---|
market_rule_id | i64 | Market rule ID. |
price_increments | &[PriceIncrement] | Price increment rules [{low_edge, increment}]. |
completed_order
An order that is done — filled, cancelled or expired — as the venue holds it.
| Parameter | Type | Description |
|---|---|---|
contract | &Contract | Contract specification (symbol, secType, exchange, currency, etc.). |
order | &Order | Order parameters (action, quantity, type, price, TIF, etc.). |
order_state | &OrderState | Order state (status, margin, commission info). |
completed_orders_end
Every completed order has been stated.
historical_schedule
When a contract's venue was open over a window, session by session, in the zone the venue keeps.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
start_date_time | &str | Start date/time for tick query. |
end_date_time | &str | End date/time in "YYYYMMDD HH:MM:SS" format, or empty for now. |
time_zone | &str | Timezone string (e.g. "US/Eastern"). |
sessions | &[(String, String, String | Trading sessions [(ref_date, open, close)]. |
fundamental_data
A fundamental report, as the XML the venue publishes.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
data | &str | Raw data string (XML/JSON). |
update_mkt_depth
One level of a book that names no venue. operation is 0 to insert, 1 to update, 2 to delete; side is 0 ask, 1 bid.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
position | i32 | Book position (row index) or position size. |
operation | i32 | Book operation: 0=insert, 1=update, 2=delete. |
side | i32 | Book side: 0=ask, 1=bid. Or order side "BOT"/"SLD". |
price | f64 | Tick price. |
size | Decimal | Tick size. |
update_mkt_depth_l2
One level of a book that names the venue it stands on. Every level from this client names one.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
position | i32 | Book position (row index) or position size. |
market_maker | &str | Market maker ID. |
operation | i32 | Book operation: 0=insert, 1=update, 2=delete. |
side | i32 | Book side: 0=ask, 1=bid. Or order side "BOT"/"SLD". |
price | f64 | Tick price. |
size | Decimal | Tick size. |
is_smart_depth | bool | If true, aggregate depth from multiple exchanges via SMART. |
mkt_depth_exchanges
Every exchange the venue names, in the two sections it names them in: shares and derivatives.
| Parameter | Type | Description |
|---|---|---|
descriptions | &[crate::types::DepthMktDataDescription] | Array of matching contract descriptions. |
tick_req_params
What a subscription was given: the increment its prices move in, which venues it is served from, and which feed answered. Sent once per request, with the first record it is served, as a gateway sends it; a request served none is sent none.
| Parameter | Type | Description |
|---|---|---|
ticker_id | i64 | Ticker/request ID. |
min_tick | f64 | Minimum tick size. |
bbo_exchange | &str | BBO exchange for smart component lookup (e.g. "SMART"). |
snapshot_permissions | i64 | What the venue says this request may be given: 0 nothing stated, 1 no top of book, 2 snapshots, 3 real-time top of book, 4 snapshots not available through the API. 0 where the venue names no BBO 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. |
smart_components
Which venue each bit of a quote's exchange mask refers to, and the letter that venue is named by.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
components | &[crate::types::SmartComponent] | Smart routing component exchanges. |
news_providers
Every news provider this account may read.
| Parameter | Type | Description |
|---|---|---|
providers | &[crate::types::NewsProvider] | News provider list. |
soft_dollar_tiers
The soft dollar tiers this account may direct commission to.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
tiers | &[crate::types::SoftDollarTier] | Soft dollar tier list. |
family_codes
The account families this login belongs to.
| Parameter | Type | Description |
|---|---|---|
codes | &[crate::types::FamilyCode] | Family code list. |
user_info
What the login is entitled to, as the venue states it.
| Parameter | Type | Description |
|---|---|---|
req_id | i64 | Request identifier. Used to match responses to requests. |
white_branding_id | &str | White branding ID (empty for standard accounts). |
Requests, delivery and shutdown
Requests and cancels return after admission. The engine holds work while it
waits for contract naming, order replay, account download, an option's model,
or an earlier exchange to finish. A cancel withdraws its own kind of request,
including one still waiting, except cancel_contract_data and
cancel_historical_ticks, which withdraw nothing, as on a gateway (see
Venue behaviour). Cancelling a question that is ready follows its
answer; cancelling one still waiting prevents that answer. A request for a
contract the venue names no single contract for is over, as it is at a gateway:
its number holds nothing, a cancel under it is refused as one of nothing held,
and it can be asked under again.
Requests and callbacks
Rust request and cancel methods return (). Their refusals arrive on
Wrapper::error_from or Wrapper::error. Request methods take no wrapper:
pass the wrapper to process_msgs to receive their answers. The answering
conveniences, such as historical_data, contract_details and
qualify_contract, wait and return their results.
Records are delivered in session order. Refusals, immediate answers, connection
notices, registrations and order reports share that order. Quotes, positions,
account figures and other conflated state follow the records of a read. A
record arriving during a read waits for the next read. A fill and its status
from one report are delivered as order_status, then exec_details; the
commission report follows where the venue states it. It carries the realized
P&L and a bond's yield where the venue states them beside the charge, and the
unset value where it states none, states nan or, for the P&L, zero. The
commission is unset in the same way. A record stating neither the charge nor
the other amount beside it is not delivered, and neither is one stating a figure
or a sending time that cannot be read. The venue states the day's executions and their charges
again at every reconnect. An execution is delivered once for the session,
however many the day has. A charge is delivered once for the day its record was
sent on, counted on the session's time zone, as a gateway keeps it: only a
later revision of one is delivered again, with the P&L the one before it
realized added to its own, and one stated again after midnight there is
delivered again.
Python request refusals also wait for poll() or run(). A 504 about a feed or
trading connection that ended within an admitted session follows the earlier
records. With no session, “not connected” is reported inside the call, as in
the reference client.
A Rust answering convenience with keep_record delivers a whole read through
that record and its answer collector. Without a kept record, it takes only its
own answers and leaves other callbacks for process_msgs.
Error origins
Every error reaches error_from. Its default forwards to the error callback,
so a wrapper that implements only error receives every error there. Rust
ErrorOrigin distinguishes:
| Origin | Meaning |
|---|---|
Request { id, ends } | A numbered request; ends distinguishes a refusal from a notice followed by more answers |
Order { id, op } | An order's placement, modification, cancellation, exercise, or a venue report |
Question { q, ends } | A request without an id, such as positions, open orders or scanner parameters |
Session | A connection notice or another session-wide outcome |
Internal(id) | An engine lookup |
Python ErrorOrigin exposes kind, id, ends, op and question.
ends is None for origins other than requests and questions; op and
question are None when inapplicable. Ordinary ibapi wrappers receive
error with the original number and code, with the arguments the wrapper's
error declares: reqId, errorCode, errorString as ibapi 9.81 calls it,
advancedOrderRejectJson after them as the releases before errorTime did,
or errorTime second as the current release does. An error declared with
three or four is called directly, without error_from.
A call that answers asks under a number this client took for that call. What answers it after the call has returned — the end of a lookup that was refused, an answer that came after the wait ran out — reaches no wrapper on either surface.
refuse(origin, code, msg) adds a caller-side refusal to the same stream, on
both surfaces. Python makes the origin from the fields it reads back:
ErrorOrigin(kind, id=-1, ends=True, op=None, question=None).
question_retired(Question) marks the ordered cancellation of positions
or account updates; its default does nothing. Python delivers no cancellation
callback for these questions.
Shutdown
Python disconnect() finishes the engine's wire work, calls connectionClosed
before returning, and discards queued callbacks.
Rust disconnect() returns Shutdown { logout_sent, engine }. The engine
result is EngineEnd::Ended or EngineEnd::Panicked; either confirms that the
engine thread ended. Concurrent callers wait for the same end. Call
process_msgs afterwards to receive the remaining records and final state,
followed by connection_closed. Nothing from that session follows the close.
Orders already admitted finish before logout or are refused under their order
numbers. Orders kept with transmit = false are not sent.
EClientConfig.cancel can take back a connect during socket operations,
second-factor polling and client construction. Name resolution and a supplied
code provider must return before cancellation completes. Shutdown waits for
recovery workers to finish, including those calls, and logs out a session that
opened while cancellation was underway.
Admission and wakeups
Admission uses an unbounded channel: a call hands the engine its command and returns, however far behind the loop is. Released work and new commands share a limit of 64 commands per engine lap. A caller that limits admission must still allow the transmitting order or cancellation that releases its staged orders.
Rust on_data(Some(hook)) installs a wake hook; None removes it. The hook runs
on the engine thread, outside engine locks, after a record or state change, at
most once until the next process_msgs begins. It must return immediately and
must not call disconnect or drop the last client owner. A panicking hook is
logged and removed. Bound an idle wait when relying on completions whose
deadlines are evaluated by process_msgs, such as snapshots and smart
components.
Both surfaces expose traffic(): bytes_sent, bytes_received,
messages_sent and messages_received for the session. Bytes are protocol
frames before TLS encryption and after decryption. Complete frames count as
messages, including one message per compressed outer frame. Initial
authentication is outside these counts; replacement connections preserve them.
Order IDs
Both surfaces state next_valid_id once a session has connected, after the
venue has named the orders the account is working: Python before connect()
returns, Rust on the first process_msgs read after that naming.
Rust order_id_floor() reads the ID next_valid_id would state, without
waiting; next_shared_id() answers the request-compatible one.
next_shared_id_within(timeout) adds a bound to the replay wait and observes
EClientConfig.cancel.
next_order_id() is an answering call; an exercise
that asks the engine to assign its number returns immediately and is numbered
after replay.
The order-id file keeps a separate next ID for each account and API client ID
across sessions; the order_id_file setting selects it. See
order IDs across sessions
for its default location, reservations and moving the file.
Settings and request values
There are 17 carried settings, on Rust EClientConfig.gateway and Python
ib_dx.configure(), and 15 settings recorded as inapplicable here.
Registration is held by the engine and does not wait on a caller thread.
Historical news takes signed total_results: i32. Values above 300 are sent as
300; zero and negative values pass through. Historical ticks carry
ignore_size. Order-condition margin percentages and price trigger methods
are signed i32 and are sent unchanged.
Rust req_scanner_subscription takes a final scanner_setting_pairs: &str;
pass "" when none are stated. Both surfaces accept these pairs and warn once
that they are not carried to the venue. Scanner subscriptions first load
permission metadata. Missing permissions produce error 490 and prevent the
subscription, or warning 492 while the scan continues. Notices name the scan
and requested filters; range names omit the trailing “below” regardless of
letter case. A range name containing an ellipsis is given as the venue states
it, where a gateway may shorten it to the words before the ellipsis.
Metadata fetched for a subscription produces no unsolicited
scanner_parameters callback.
When a real-time bar ticker is reassigned to a different contract, its bars reach the new contract's subscriptions. Existing subscriptions retain their assigned cancellation number. Readers of the same contract share its stream and price and size increments.
Bond order messages use Govt, Muni, Corp and Agency issuer labels. An unknown issuer category is omitted. A replacement preserves a venue-provided external instruction identifier, including nonnumeric identifiers.
Accounts, exercises and snapshots
Named-account updates, positions, profit and multi requests keep the account
they name. An All summary includes every account held by the login.
Concurrent profit requests have separate subscriptions. Multi answers echo
the requested model label on the initial and subsequent rows. Model selection,
AllNonProp membership and advisor groups are not applied and produce a
warning once per selection and session.
An exercise requires a positive position in the selected account. It requests
tick 493 and waits for its in-the-money figure when necessary, even with
override. Without override it applies the exercise/lapse check. Its quantity
is limited to the whole-contract position captured before waiting. Shutdown
refuses an exercise still waiting for that figure. The alternate exercise
transport is not implemented.
Snapshot registration follows contract naming, including options given only by contract id. Its eleven-second bound starts when the engine takes the named subscription. A snapshot is sent each kind of tick once, the first the quote states, and the bid's, ask's and last's sizes only beside their prices, each right after its price. The snapshot waits for bid, ask, last, open and close; OPT, FOP, IOPT, WAR and EC also wait for model computation 13 and the bid's, ask's and last's computations 10 to 12, or 83 and 80 to 82 for delayed data. Delayed snapshots also wait for tick string 88. As a gateway does, a snapshot is sent no open, and does not wait for one, on any type but STK, FUT, CMDTY and CRYPTO; no volume or last size on CASH, IND, FUND, CMDTY, FIXED and EC; and no last or last exchange on CASH, CMDTY, FIXED and EC; nor, where a contract's definition says it is quoted at its midpoint, a volume or a last. Each computation is sent to a snapshot once and only with all eight figures stated; at the end it is sent each one it has not been sent where any figure is stated. On a frozen or delayed-frozen feed the model is also sent once whatever it states, and at the end a side only with all eight figures stated and the model again where it was not sent that way. A snapshot of an option on an index or a future, or of one whose definition the venue has not answered, is sent no 10 to 12 here and so runs the full eleven seconds.
Lower-level Rust users
from_parts, engine constructors and core command senders use
std::sync::mpsc::Sender<ControlCommand>. Order, market-data and question
commands carry their inputs to the engine. MarketDataTaken carries the slot
generation and registration time. Order records share immutable cached reports.
Ask and Answer multi-account variants carry the account and model label.
A global cancellation is one order-buffer request. An automatically numbered
exercise carries its caller's shared allocator until replay completes.
SharedState.portfolio is an Arc, and named accounts have separate stores.
Profit request and cancellation commands distinguish account and single-position
subscriptions. Connection::new takes TlsStream<LogonSocket>; explicit socket
construction wraps the TCP stream with LogonSocket::new. Established
connections keep their ordinary timeouts.
Per-client order views, outcomes for children of refused placements and changes behind a refused or withdrawn placement, complete option greeks, account-group and model application, alternate exercise transport and scanner settings-pair carriage are limited as Limits describes.
Venue behaviour
What a program meets here that is how the venue answers, or how the TWS API is defined, rather than something this client chose. They are written down because a program meeting one for the first time reads it as a fault in the client, and checking the wrong thing costs a session. Where a gateway is known to answer the same way, the section says so.
What this client does differently from a gateway is on Limits.
Quotes and books
A bid of -1 is the venue saying there is none
Some instruments carry no bid and no ask. The venue says so by sending -1 on
both, and this client passes it on as sent, as a gateway does, rather than
turning it into a zero.
Which instruments those are is the venue's to say and not something this client can work out. Indices are where it was met: most of the ones below carry no quote, and one of them does, so the kind of instrument is not the rule.
Three answers look alike at first, and are worth telling apart before concluding a subscription is missing:
- No ticks at all, and no
market_data_typecallback either. Nothing arrives to say what feed you are on. Asking for delayed data then answers normally. -1on bid and ask, no bid or ask size, and amarket_data_typeof 1 saying the feed is real time, while the last, high, low, close and open arrive and move. The instrument has no quote.- A real bid and ask. It is quoted.
Measured together with the market open: SPX on CBOE quotes; VIX on the
same exchange and the same feed does not, and reads -1 while its volume ticks
up on a real-time feed. RUT, INDU and TICK-NYSE read -1 the same way.
NDX real time is the first case — silence, no feed stated — and it answers as
soon as delayed data is asked for.
Delayed data carries no index quote at all: on market_data_type 3 every one
of them reads -1, SPX included. A quote that is on the real-time feed and
not the delayed one is a property of the feed rather than of the account.
A closed market and its frozen quote
On a closed market the live feed states what it states, and the venue serves the quote the market closed on as a feed of its own. Measured on a bond at night: the live record states a zero bid, ask and yields, and the frozen one the bid, ask and yields the market closed on.
A gateway asks the venue whether a contract's market is closed, on a series of its own, beside the quote of a subscription made with frozen data on, and of one that has fallen back to delayed data with delayed-frozen data on, where the logon enables frozen data, and serves the frozen quote while the market is closed. This client does the same; the tick data recipe says what a program is sent. Measured on a Saturday, the venue acknowledges the series and answers it at once with a single four-byte figure: 1 for a stock, an option and the bond above, whose markets were closed, and 2 for a crypto contract, whose market trades around the clock. Only 1 says the market is closed, as a gateway reads it; on that answer the bond was served its frozen bid, ask and yields. Where the series goes unanswered, a closed market is served what the live feed states. A frozen quote the venue refuses the login is not reported to the program, as a gateway reports nothing of it.
A book restarts after a reconnect
After a connection is rebuilt the venue restates a book from the top. This client reports that on the error callback as 317, the TWS API's notice that a book was reset, before the first new level arrives. Empty the book on it, or the levels that follow land on top of the old ones.
Which venue a quote's exchange mask names
The map from a quote's exchange-mask bits to venues is answered at regular trading hours. Outside those hours the venue states none, to a gateway as to this client, and a venue's letter is empty until it does.
The fourth integer on a trading-status record
The trading-status record (generic tick 437) carries a status mask, a timestamp and a status index, and then a fourth integer. This client reads the first three and does not interpret the fourth, which has no stated meaning; a gateway assigns it none either.
Dividends arrive as tick 59
Ask for 456 in the generic tick list and what a contract pays out arrives on
tick_string 59, which is how the TWS API delivers dividends and how a gateway
delivers them.
Finer prices and odd lots
Every entry of a quote request states tag 9839, as a gateway states it: 2
on the last-trade entry where the logon offers PRCEXTRAPREC, and on the
chargeable snapshot of a US share or warrant where it offers ODDLOTBIDASK;
1 on every other entry. Whether a contract is a US share or warrant is read
from its definition's market classification, and where the logon offers
odd lots the snapshot of a share or warrant whose definition is not held
waits while it is looked up. A withdrawal states each entry as it was asked
for. What the venue sends back is read the same way either way, and its
prices and sizes reach tickPrice and tickSize as it states them.
Callbacks that never fire on a gateway
The TWS API declares seven callbacks that never fire for a program on a
gateway: the four steps of the verification handshake (verify_message_api,
verify_completed, verify_and_auth_message_api,
verify_and_auth_completed), the exchange-for-physical quote (tick_efp), the
delta-neutral validation (delta_neutral_validation), which a gateway never
sends, and win_error, which no message on the wire carries; the TWS API's
Python client declares it and never raises it. Each is declared here, so a
program implementing one compiles and runs, and each fires here as often as it
does on a gateway: never. Trouble on a connection reaches the error callback.
Requests
Requests a gateway answers itself, or not at all
verify_request and verify_and_auth_request are answered by the reference
client itself, under 508 (Bad message Intent to authenticate needs to be
expressed during initial connect request.), and never reach a gateway; they
are answered the same way here. verify_message and
verify_and_auth_message reach a gateway, which reads and discards them, so
here they send nothing and nothing answers. cancel_contract_data and
cancel_historical_ticks ask the venue nothing on a gateway either: they only
stop a gateway re-sending a request it held back while its connection to the
venue was down, which this client never does. A lookup or ticks already asked
for still arrive. Without a session — before one exists, or once it is over —
each is refused under 504.
A broad lookup takes longer than one contract
A lookup naming a whole class is a different question from one naming a single
contract. SPY options across every expiry is 13,580 definitions over nineteen
exchanges, and the venue takes about ten seconds to send the first of them.
The wait measures silence, not the length of an answer: it is reset each time the venue speaks, so an answer that runs for as long as that one does is not cut off part-way through. A lookup that does run out says how many definitions arrived before it did, since a partial answer and no answer are different facts and only the first says to ask a narrower question — by naming an expiry, or a single venue.
An ambiguous description
req_contract_details answers a description that matches several listings
with every one of them, as a gateway does.
Looking a contract up
- By identifier:
CUSIP,SEDOL,ISIN,RIC,FIGIandBB_SYMBOL, spelled exactly so. A gateway reads any other name — a lower-case one included — as no identifier, and looks the contract up by its symbol with the identifier left out; so does this client. The venue, primary exchange and currency ride beside the identifier, and an ISIN asked about anywhere but SMART is asked of any type. Nothing of an issuer is stated beside an identifier. - Only a details request looks a contract up by identifier or issuer. Market data, depth, bars, head timestamps, histograms, fundamental reports, ticks and the rest carry neither to a gateway, which looks their contract up by its description, whatever else the contract holds. This client looks a contract given without the venue's id up by its description too, and one given by the id with no type or no exchange beside it by that id — except on a quote request naming no exchange, which a gateway refuses before any lookup under 321, Please enter exchange, with no trailing period, which the book refusal has. Only a contract of the news type is exempt, as headlines name providers and no venue; so does this client refuse it, and exempt the same type. One stating the id, its type and its exchange goes out as it stands, and so does a fundamental report stating the id, which states nothing else of the contract. One stating neither the id nor a symbol names nothing to look up, and is refused as a contract not found (200), as a gateway's lookup refuses it.
- A head timestamp states the contract's type and exchange as a gateway states
them, however they were spelled: a stock is
STK, given asCSor in lower case, and a Nasdaq listing isNASDAQ, given asISLAND. - A bar request, a head timestamp or a histogram asked under the number of a bar request still answering is refused as a duplicate under 322, Error processing request:-'' : cause - Duplicate ticker ID for API historical data query, as a gateway refuses it, and the bar request goes on answering. A scan asked under a number already running one is refused alike under 322, Duplicate ticker ID for API scanner subscription.
- A news or article request under the number of one still answering is asked as well, as a gateway asks it, and each is answered under the number.
- When the historical connection drops, a bar request kept up to date ends on 10182, Failed to request live updates (disconnected), as a gateway ends it, whether its history was still arriving or it was on its updates. One not kept up to date, or a trading schedule, still arriving is told 165, Historical Market Data Service query message:HMDS server disconnect occurred. Attempting reconnection..., told again at each attempt to bring the connection back that fails, and once it is back is told HMDS server connection was successful. and asked again from the start; its answer then arrives with its end. A scan is told the same and is subscribed again under the name it ran under, going on under its number. A 165 is a notice and ends nothing, and the calls that wait for an answer wait through it. A head timestamp, a histogram and historical ticks are told nothing, as a gateway tells them nothing; a histogram and ticks asked on the connection that went are not answered, as on a gateway. A fundamentals, news or article request and a question for the scanner's parameters are not on a gateway's historical connection, so its drop tells them nothing either: each goes on waiting and is asked again, as it was asked, once the connection is back — a question for the scanner's parameters one at a time, as always. A contract's corporate actions are this client's own question and are failed at the drop under 504. A connection this client stops trying for fails the requests waiting to be asked again, the scans, the queries kept waiting through the drop and the scanner's questions, under 504.
- A head timestamp the venue has not answered in five seconds ends on 162, Historical Market Data Service error message:Request Timed Out, as a gateway ends it.
- A difficulty stated for a historical query is told under the number and the words the kind of request is told in, as a gateway tells it: 162 with Historical Market Data Service error message: prefixed, whether the service refused the query or its answer did not read; a histogram failure under 10188, Failed to request histogram data: followed by the difficulty; and a historical-ticks failure under 10187, Failed to request historical ticks:.
- A fundamental report is asked about a stock alone. A gateway refuses one on a contract stated as any other type, or stating none, before looking it up, with Please enter a valid security type under 321; so does this client. It is asked about a stock in dollars, whatever currency the contract states, as a gateway asks every one.
- A venue or currency left empty is not stated on the lookup.
includeExpiredis stated on every lookup by description a details request makes, and a bars, head-timestamp or ticks request carries it to the venue as the contract states it. A details lookup by identifier or by contract id does not state it.FUT+CONTFUT(orCONTFUT+FUT): the continuous future is asked for first and the listed months once it is answered, answer or refusal; the months come back first and the continuous contract after them underCONTFUT, with the lead month's id. Where the venue names no listed month the request is answered as a contract not found (200), whatever the continuous lookup named.CONTFUTalone comes back asCONTFUT, one per venue and multiplier. Named by an identifier, both lookups ask by that identifier.- A continuous future asked for by contract id is looked up by that id and handed back as the venue names it. A gateway goes on to ask for the continuous contract as well, and this client does not.
A contract's least size
min_size and size_increment are one figure, as a gateway states them, and
worked out in this order: a contract whose size rule deals in fractions of a
unit takes the rule's finest band; one whose definition states a least size
behind the flag that admits it takes that size, or a ten-thousandth where the
flag stands alone; one whose order types on its exchange name a board lot or
a default lot, on an exchange it lists, takes the lot; any other takes one
unit. The lot is the band a size of nothing falls in, no less than the rule's
whole unit for a share or a warrant listed in the United States, or nought
where the definition states no rule; and no less than a size the venue
suggests above nought — which on a routed exchange stands on its own. A bond's
least size is where its size table starts, and its step is the lot.
suggested_size_increment is the larger of the rule's finest band and the size
the venue suggests.
Size-only changes
ignoreSize on req_historical_ticks and ignore_size on
req_tick_by_tick_data ask the venue to leave out a bid/ask change that moves
only a size. A gateway sends the filter and passes on what the venue answers
as it stands, and so does this client: nothing is filtered here. On historical
ticks a gateway asks for midpoint ticks that way whatever the flag says, and so
does this client; for trades and aggregated trades it is not asked. One
session saw the venue answer the same with the filter as without it, on
historical bid/ask ticks and on a bid/ask stream.
Arguments that change nothing
Two arguments the TWS API defines have no effect on a gateway, and have none here:
| Call | Argument | Why |
|---|---|---|
req_ids | num_ids | The next valid id is answered whatever number is asked for |
req_real_time_bars | bar_size | A real-time bar is five seconds. The venue's request carries no bar size; a gateway reads the number and does not use it |
Stopping profit and loss
cancel_pnl stops the updates. The venue has no message withdrawing the
subscription itself, on a gateway as here, so the updates stopping is what the
call does.
Implied volatility and option price
The venue computes its option model and publishes it per option, on a subscription of its own, beside the volatilities it states the model from. The model tick, 13 or 83 on a delayed feed, is built from those as a gateway builds it:
- The greeks and the option price are the venue's model's. The price of a
warrant or a structured product (
WAR,IOPT) is multiplied by its multiplier. - The implied volatility is the first of the venue's model, mid and last volatilities that stands, carried from a trading day to a year of 252. Until one stands none is stated: a gateway falls back to the volatility curve of the option's expiry at its strike, and with no analytics settings saved, as on a gateway, that curve states no volatility.
tickAttribis 1 where the mid's volatility was worked from prices.- The underlying's price is the one the chain parameters on the underlying (687, asked for once per underlying on each connection) state for the option's trading class, multiplier and last trading day, and where they state none, the mark the model holds from the underlying's own quote (below). An option on a future is worked from the future's own record alone: where it holds no price, no side is worked, and the chain parameters stand in for no future.
- The present value of dividends is that of the payments the underlying's
schedule states over the option's life, discounted at the currency's rate
for the option's term. A payment the venue marks special — as an attribute
on the entry its answer states — is skipped, as a gateway skips it. An
index states its schedule in cumulative amounts, and its present value is
the last one inside the option's life discounted, less the last before
today grown to that day, as a gateway reads an index's. A future pays
nothing out: the model of an option on one takes no dividends and grows it
at the rate as its yield instead, and the value its computation states
comes from the future's own schedule — nought, once the rate is in hand,
where the venue answered none. The currency's rates are the venue's answer
to the query a contract's payments are asked with, naming the currency
(
div USD): each entry is written as a payment is, its date the day the rate runs to and its amount the percentage, and a gateway reads it that way.
It is rebuilt at most once a second, on the clock's seconds, and a request is sent it when any figure differs from the last one that request was sent. A request that starts on an option the model already works out is sent the model tick as it stands at once, as a gateway sends it when a request starts. What was stated stands through a reconnect until the venue states it again, so a tick owed when the connection drops is built from what was stated before it.
The bid's, the ask's and the last's computations, 10, 11 and 12 (80, 81 and 82 on a delayed feed), are a gateway's and not the venue's. This client works them out the way a gateway does, with an option model of its own, from the inputs below; the underlying's price it works them from is not always the one a gateway takes, as Limits says.
- The implied volatility is the one the venue states for the side — the bid's
and the ask's together (736), the last's on its own (737) — carried to a year
of 252 trading days.
tickAttribis 0 on every one of them, as a gateway sends it: it sends each side with the attribute of the last of that side it sent, which starts unset. - The option price is the side's price as the model takes it in, to single precision: a bid of 4.74 is 4.739999771118164. A warrant's or a structured product's is per contract. The model takes a bid or an ask in only with a size, and a last only where it or its size is above nought, so a side the quote has nothing on states no price: a frozen quote states one as -1 with no size, or a last of nought.
- The greeks are this client's option model's at that volatility, the model for volatilities worked from prices where any the venue states for the option, or its chain parameters, say so. They are worked from the underlying's price the model holds from the underlying's own quote (below) — its mark, else the midpoint of its sides, else its last, else its close — and, until the model holds one, from the price the chain parameters state — the first set for the option's class at its multiplier, or, where no set names the class, the only set or else the first for its last trading day at its multiplier — and from the time to the option's expiry, the currency's rate for its term and the dividends above. A side's greeks are replaced only by four that can be worked out. Its theta is held to no more than its time value, its price less its intrinsic value, as a gateway holds it; a side with no price has no time value to hold it to, so where its theta falls its greeks are not replaced: it keeps those it had, or states none. The time runs to the option's last trading day at the time of day its definition states (read as a gateway reads it, so 2400 is the next day's midnight), or at the end of that day's last liquid session, on the zone its sessions are stated on, or, where neither states it, to that date alone, which the model reads as four in the afternoon on that zone. The venue states the time of day as four digits (1615 on SPY, 1500 central time on SPXW), on SPY's and AAPL's options only for an expiry two to three weeks off or nearer, and the sessions about a week ahead, so a later SPY expiry runs to 16:00 and not to its 16:15 close, on a gateway as here. The model reads the clock once a minute. The currency's rates are counted from the day they were taken in.
- They are rebuilt at most once every two seconds, for the options something new was stated for. A side is put to every request watching the option when its volatility moves, and all three again on every change to the option's quote. Each figure a side does not state is the last one of that side the request was sent, and a request is sent a side when that differs from the last.
- What a request is sent of a computation is gated by what the program may see of the quotes it is worked from, as a gateway gates it. The underlying's price goes only once every listing of the underlying whose definition the venue has answered this session is subscribed, the venue has stated the access to one of them and none of them is restricted. A restricted listing refuses it to the request for good, told under 10091 where a subscription would lift the restriction and 10090 where nothing would, in the service's own words with the contract, the kind of feed and the side or sides named, and whether delayed data is offered; a listing not subscribed, or one nothing has stated yet, leaves the price held and nothing said, and the decision waits. A side's own price goes only once the access to the option's own bid and ask — for a bid's or an ask's — or its last — for a last's — is stated as allowed. A delayed feed is gated by nothing.
- What a program may see of a quote is what the acknowledgement of its live subscription states on its seventh field: nothing stated is allowed; a list of the subscriptions it takes, by a comma or a hash past its first character, or one by its number, requires one; anything else, a dash included, is refused. A refused bid and ask is no subscription on the listing at all; a withdrawn subscription leaves no listing stated behind, and a connection lost puts every listing back to unstated until it is acknowledged again.
A gateway's option model watches each modelled option's underlying's own quote itself, and so does this client, under a request of its own that nothing is said of to a program: on the listing a gateway prefers for the contract, which is the one the venue names when asked for the contract alone (the smart one for a share), or, for an index whose definition names another contract it is priced off, on that contract, each of its prices multiplied by the figure the definition states beside it. The watch holds a quote line as any subscription does, shares a subscription a program holds on the same listing, and is withdrawn with the last option on the underlying. The model marks the underlying from that quote as a gateway marks it: at the venue's mark for the contract where something asked for it (221); else, where trading has halted or the subscription stands with nothing quoted, at the market price the account's portfolio states for it; else an index at its last, and anything else at its last, at the midpoint of its sides where it trades no last to be marked at, or at its close where the close's attributes do not say it does not stand; a mark outside the sides is held to them unless they cross, and a quote with no mark is marked at the midpoint of its sides, or its bid; and a mark that is not a price the contract may take is replaced by the last. The model takes nothing from the quote until the mark first stands, then its bid, ask, last and close to single precision and the mark at once, and afterwards each part again as it changes, with the mark as it stands then.
A snapshot of an option is held, as a gateway holds it, until it has been sent the model and the bid's, the ask's and the last's computations as well as the bid, the ask, the last and the close, or until eleven seconds after it was asked for: an option's snapshot is sent no open, and does not wait for one. It is sent each computation once, and only one stating all eight figures, whether or not it moved; at its end it is sent each one it has not been sent, as it last stood for it, where any figure is stated: the bid's, the ask's and the last's, then the model. On a frozen or a delayed-frozen feed it is also sent the model once whatever it states, the first time it changes, which does not complete it; each later confirmation of the frozen feed re-arms it, and a model that changes after one is sent to it again, whatever it states. At its end a side is sent only stating every figure, and the model again where it was not sent that way, whatever it states.
What is not carried yet is on Limits.
What the protocol carries no request for is the inversion: an option price or a volatility the caller supplies, for the venue to work back from. A gateway solves that against the venue's published model for the same contract, and so does this client. Where the venue has published no model, nothing is answered, on a gateway as here. How long either waits for the model is on Limits.
Orders
Order IDs across sessions
The next order ID is retained per account and API client ID: the one stated at
connect, EClientConfig::client_id in Rust and client_id / clientId in
Python, zero on both unless stated. The saved counter raises next_valid_id
and Rust order_id_floor, which reads the same ID, at connect, and venue
replay can raise them further. Orders absent from that replay still leave their
saved counter behind.
Both surfaces state next_valid_id once a session has connected, after the
venue has named the orders the account is working: Python before connect()
returns, Rust on the first process_msgs read after that naming. req_ids
states it again.
next_order_id() reserves under an exclusive file lock and writes the raised
counter before returning. Bracket helpers reserve their consecutive IDs in one
operation. A positive ID supplied to place_order() or exercise_options()
raises the saved counter before the call returns. IDs the engine assigns
itself, to attached children and to exercises stated without one, are counted
in memory and saved with the session's next placement or reservation.
As through a gateway, a new order under an ID at or below the counter saved before the session opened, or at or below the ID this client gave a working order the venue names for it, is refused with error 103, "Duplicate order id". An order the session holds under that ID is modified as usual. Another client's working orders raise nothing here. Previews are neither counted nor refused against the saved counter.
Every order a program places states the program's order ID and client ID, as a gateway states them on every order a program places, and no transact time, which a gateway does not state. An order ID past what a gateway carries, a signed 32-bit number, is not stated; this client numbers the previews it asks for on its own account past it.
next_valid_id states a floor; it does not reserve it. Programs sharing an
account, client ID and file should use next_order_id() for each allocation
rather than incrementing separate counters from that callback. The file does
not coordinate different client IDs, separate files or different machines.
The default file is ib-dx/order-ids.json under:
| Platform | Data directory |
|---|---|
| Linux | $XDG_DATA_HOME, or ~/.local/share when unset |
| macOS | ~/Library/Application Support |
| Windows | %APPDATA% |
Rust sets EClientConfig.gateway.order_id_file = Some(path.into()). Python sets
connect(settings={"order_id_file": path}) for one session, or
ib_dx.configure(order_id_file=path) before connecting. Both read
IB_DX_ORDER_ID_FILE when the session leaves it unstated. An empty string
disables persistence; Rust None and Python configure(order_id_file=None)
restore the environment/default selection.
Entries are keyed by the SHA-256 digest of the account, in lowercase hex, so the file holds no account number. A file keyed by the account itself is rewritten under the digests when a session opens it, keeping every counter. The digest is not a secret: anyone holding the file can find an account's entry by digesting candidate account numbers.
To move the counter, stop all processes using it, move the JSON file and set
all of them to its new path. The client creates parent directories and keeps
.lock and .tmp siblings for locking and replacement. Writes are synced
before rename, and the containing directory is synced on Unix. A read, lock
or write failure warns once in the session's log and leaves it allocating from
memory and venue replay; a counter that could be read still floors the
session. The file then provides no reservation guarantee for that session.
Removing the file or disabling persistence also removes the floor for orders
the venue no longer replays.
One number, more than one order
A number whose order the venue withdrew or refused is free again at the venue, so a program that numbers its orders from the same point more than once sends a later order under an earlier one's number, and the account then holds several orders under one number. The venue names each with a name of its own, which every report on the order repeats, and each is kept as the order it is:
reqCompletedOrdersanswers with every order the venue states finished, each carrying the number as its permanent id, so two of them can share one. While that answer is open, every report the venue marks as restating the past is filed with it unless it is about an order this session holds, whatever this session once sent under its number.- A report the venue marks as restating the past is about the order this session holds under its number where it states one of the names the venue has given that order on its reports of what is happening to it, and about another order where it states another name. One stating no name is read as though the venue had not named the order. Before the venue has named an order this session sent, as it does not name a market-on-close order in regular hours until it is withdrawn, the report is about another order where the venue counts that order at or below the highest count it had stated when this session sent its own, or where every time it states for the order and the event is more than two seconds before this session sent it, on the venue's clock. The venue counts the orders it creates from one count that only rises, the third segment of its name for an order under the first two, so an order counted there was created before this one went out. A count above that says nothing: an order that finished before the session opened is not restated then, so it can count above every order the venue had named by the send. Under a number this session holds no order under, such a report outside that answer is another order's where it names another order than the one this session saw finish under the number in the last five minutes.
- A report of what is happening now is about the order this session holds under its number unless it states none of the names the venue has given that order and the venue counts it at or below the highest count it had stated when this session sent it, which makes it an order created before.
- A report about another order is filed with the completed orders while that answer is open, or, outside it, kept as one of the day's executions where it states one. It reaches the order under the number in neither case.
- An order the venue names as working under a number an earlier order finished under is taken as that number's order.
- A new order under a number an earlier order used is refused with error 103,
Duplicate order id: N, only as a gateway refuses one: a gateway refuses under 103 a new order whose number is not above a mark it keeps for each client from one session to the next, raised by every order that client places and every working order it states to it, and not by the executions the venue restates. An order the session holds under that number is modified as usual.
Attached cancellation groups
A child placed with parent_id / parentId uses the known parent's decimal
wire order number as its OCA group. The default is reduce-on-fill without
block. This applies to separately transmitted children and to families
released by the last child's transmit flag. The bracket helper uses the same
group, so individually added children join it. The parent has no OCA group;
scale-generated children retain their scale group. Replacement retains the
original parent reference and group.
Which account preset an order takes
Attached children and an order stated in cash take their terms from one of
the account's order presets, chosen as a gateway chooses it. A gateway holds
a preset of its own, at its defaults, for each security type the logon lets
the account trade, and lays the account's list over them: an entry for a
security type takes its place, and one for a symbol sits beneath its type.
An order takes the entry for its symbol (for a currency pair, its local
symbol first), else its type's, and where the list names nothing for the
type, the defaults a gateway holds for it, for which the venue is asked nothing.
Where the session's PRESETS feature is on, the entry the list made active
stands for its type: of strategies, the first active one in the list, since
each clears the strategies beside it; of other entries, the last. A strategy
whose values, once read, say it is not active gives way to the preset for
every type (u=ANY), or to the gateway's defaults where the list has none.
A modification in flight
A replacement reads PendingSubmit once it is sent, as a gateway holds an
order it has sent and the venue has not acknowledged; nothing is said at the
send itself. The venue's pending report follows, as a status report, and a
gateway reads it as the order acknowledged: the caller is told PreSubmitted,
and the working status again once the venue accepts the change. The report
ahead of a change the venue makes itself (the exits of a bracket once its
parent fills) reads the same way, and so does a report stating the venue's
other code for a change on its way, E. A pending report for a revision earlier
than the one last sent, or on an order this session is withdrawing, changes no
status, and one sent as an execution report rather than a status report
changes none either: the order is stated again as it stands. An order whose
state this session lost with a dropped connection takes its status from the
report instead, and one the venue names at connect or states in the finished
orders with a change on its way reads PreSubmitted, as a gateway restores it.
Refused modifications
A gateway writes a modification's terms into the order it holds before it sends the modification, and puts nothing back when the venue refuses it. So the open-orders view keeps the terms a refused modification sent, and the engine's own book goes back to the terms the venue holds. A cancel states the size the order is stated at — after a modification the venue refused restating nothing, the one the modification went out with — as a gateway's cancel does.
A rejection that names the original order reports error 201 under that order's
ID with the venue's words. It reports a rejected modification; it does not
declare the original cancelled or inactive, and fills and cancellation progress
already received are retained. The venue states it on an ordinary report that
states the terms it holds, and where such a report states some of the order
left, the open-orders view takes the type, the limit, the trigger of a stop or
a touch, and the size from it, as a gateway does; the time in force stays the
one sent. A rejection on a status report, or one stating nothing left, changes
none of the terms. After the error, the order is restated through open_order
and order_status in the state it held before the modification went out, as a
gateway restates it.
A modification refused on the venue's own cancel-reject message is told to nobody, as a cancellation refused there is (below), and the terms it sent stand in the open-orders view.
Replacement carries useAutoPriceForHedge under the same order type, hedge,
opt-out and HDGLMT feature conditions as placement. Changing hedgeMaxSize
keeps the pricing instruction.
Refused cancellations
A cancel the venue refuses on a report naming the cancel, as it refuses one
for an order another session is already withdrawing ("Order is already being
cancelled by another user"), reports error 201 under the order's ID with the
venue's words. The order stays in the state it held before the cancel went
out, in the open-orders view and under its own permId: the refusal is about the
cancel, not the order. After the error, the order is restated through
open_order and order_status in that state, as a gateway restates it.
A refusal on the venue's own cancel-reject message is told to nobody, whatever
it refuses and whatever reason it states: as on a gateway, no error, no text and
no status reaches the program's callbacks. The order reads as it did before the
cancel went out until the venue states another, and stays in the book and the
open-orders view: a gateway retires no order on a refusal. The event channel
connect_with_events returns, which a gateway does not have, still carries the
engine's Event::CancelReject for it, with the status the engine's own book
went back to.
A cancel names this client's orders only, each by the number this client knows it by. Under a number an order of another API client is held by, or under the number one of this client's own orders went to the venue under where that is not its API number, it finds none and is refused under 10147, OrderId N that needs to be cancelled is not found., as a gateway refuses it, whether or not that other order has finished; nothing is withdrawn. An order whose reports name no client is this client's to withdraw, and once withdrawn here what the venue says of it is told to this session. An order entered by hand states an API number of 0, which is none: client 0 withdraws it under the number it went to the venue under. A cancel that waits, behind a placement still being built, names the order its number names when it is taken.
A cancel going out
A gateway says nothing to a program when a cancel goes out, and neither does
this client. The order is held as being withdrawn: asked for the open orders,
a program reads it PendingCancel, and the venue's next report on the order
states it so, or states it Cancelled where that report is the venue's answer.
An order the venue answers at once goes from its working status straight to
Cancelled. The event channel connect_with_events returns, which a gateway
does not have, still carries the engine's Event::OrderUpdate as the cancel
goes out.
A report's reason for restating an order
A report can state a reason the venue restates an order, among them a revision or a cancel it will not make. A gateway reads no reason on a report, and nothing of it reaches the program: no error, no refusal. A report of the order replaced is taken as the replacement, whatever reason it gives. An ordinary report restating the order, and an acknowledgement of a cancel on one, are skipped and move nothing of an order the session holds; each still restates the order to the program in the state the book holds, as every report with an order restates it — a duplicate of a status the book already holds, and a report the book's own order refuses, restate it as much as one that moved it, each with the status the book holds and the average its booked fills last stated, not the report's own figures. A status report restating an order is read as any status report is.
An order's total
open_order states an order's total quantity as a gateway states it: what has
filled, and what is left of the order's own size. The size is the one the order
was placed or last replaced with, and a gateway takes it again from the leaves
quantity of an ordinary report of the order pending, filled, replaced at the
revision last sent, refused with some of it left, or new in a one-cancels-all
group, and from the report an order it did not place is first stated from; a
report of a cash quantity that states no leaves states none left. Every other report, status reports among
them, leaves the size as it was, whatever quantity it states for the order, so
a status report stating more filled states a larger total. order_status
states as the remaining what is left of the order's own size, as a gateway
states it — not the report's leaves quantity, which a cancel states as none.
A report stating an order inactive
A report stating an order inactive moves nothing of an order this session holds: a gateway keeps the status it holds for it, and says nothing of the report's reason. An order first learned of from such a report is held inactive, as a gateway holds it, and is left out of the open orders unless this session placed it.
Reports behind a withdrawal
A cancel takes the order to a revision of its own, past every one the order
was named under, and a gateway ignores a status report stating an earlier
revision unless it states the order cancelled. So once this session has sent a
cancel, a status report naming the order rather than the cancel changes
nothing: the order reads PendingCancel until the venue answers the cancel,
and is stated again as it stands. Nor does an acknowledgement, the acceptance
of a change or a new order's report move an order being withdrawn, or one this
session did not place that the venue named inactive: a gateway takes those
only from an order that is working or on its way. A gateway does not hold an
order it has sent inactive on the venue's word, so one this session placed is
working again on any of them. A status report stating the order working at
its own revision, after a withdrawal this session did not send, states it
working again.
Returned orders read Sweep to Fill (sweep_to_fill in Rust and Python, also
sweepToFill in Python) from tag 6102, and Continuous Update (continuous_update,
also continuousUpdate in Python) from tag 6275. Hidden (hidden) uses tag
6135, Auto Cancel Parent (auto_cancel_parent, also autoCancelParent in
Python) tag 6965, and Imbalance Only (imbalance_only, also imbalanceOnly
in Python) tag 6737. Sweep to Fill, Hidden, Auto Cancel Parent and Imbalance
Only are enabled only when their stated value starts with ASCII 1 on ordinary
and completed reports; Continuous Update accepts ASCII 1 or 2. Suffixes
are allowed. Other stated values, including empty, Y, y, true, leading
whitespace and non-ASCII digits, disable the respective field. In this client’s
report reader, an omitted field leaves the supplied order alone: ordinary
snapshots start with false, while completed orders can keep earlier terms.
Orders across a lost connection
The venue ends what it names after a logon, and each answer to what it has
finished, with a report stating its contract as *; on an account working
nothing that report follows the day's executions directly. A question of what
is working is answered there, and one asked from a drop until then is held, as
a gateway holds it.
An order sent and not yet answered when the trading connection goes is
recovered as a gateway recovers it. Once the connection that replaced it has
named what is working, the venue is asked what it has finished today, where
the logon offers a question bounded in time (1DAYSORDER) from a second before
the earliest such order went out, or from the last time a report stated where
that is earlier, though not from before the start of yesterday. An order the venue names in
either answer takes the state it gives, and the program is told as for any
report. One it names in neither is held as inactive and is not sent again: a
gateway sends no order of a program's twice. Where a recovery had already sent
it out once, the program is told under 106, Can't transmit order id:<id>, <order>, with the order described as a message beside its status describes
it. The recovery is given up a minute after the logon that began it, and a
connection that replaced one mid-recovery leaves it to that minute.
Where the logon offers the recovery (APINTLRCV, or APINTLRCV-WB for a
partner's login; a gateway's setting for it is on unless changed, and this
client has none), the placements a drop leaves not yet sent are listed after
the 1100: not yet built under 1104, Pending to create 2 orders: 5,6, built
and not sent under 1105, previews under 1106, ten numbers at most followed by
(and 3 more). They, and placements made while the connection is down, go out
once the recovery behind the next naming is over. Where the logon does not
offer it, every placement the drop leaves not yet sent reads ApiCancelled
and is refused under 10328, Connection lost, order data could not be resolved, whether it was built or not.
A message beside an order's status
The venue can state a message beside an order's status: an order that will not reach the exchange before the open is acknowledged with a warning saying so. On a status report at the order's revision or a later one, other than one stating the order cancelled, the message reaches the program that placed the order as error 399 under the order's ID, ahead of the status, the way a gateway words it: Order Message:, the order as a gateway's display describes it, and the message.
Order Message: BUY 100 AAPL NASDAQ.NMS Warning: your order will not be placed at the exchange until 2026-09-25 09:30:00 US/Eastern
The error's three parts are on lines of their own, and a gateway writes each
line break of an error's text as a space. The description is the side (BUY,
SELL, SSHORT), the size, and the contract, which reads by kind:
| Kind | Reads as |
|---|---|
| Share | AAPL NASDAQ.NMS: where it is listed, under the venue's name for the market (NASDAQ where a program is handed ISLAND), and the segment of that market |
| Option | SPY OCT 16 '26 765 Call; a trading class other than the underlying's in brackets after the symbol, SPX (SPXW) OCT 16 '26 6000 Put; where the month the option is for is not its last day's, the month, DEC26 |
| Future | MES DEC'26, and (DP) after a dividend-protected one |
| Future's option | ES DEC'26 6000 Call Fut. Option, and a trading class other than the symbol in brackets |
| Currency pair | EUR.USD Forex |
| Crypto currency, commodity | BTC Crypto (BTC), XAUUSD Commodity (XAUUSD): the trading class in brackets |
| Contract for difference | AAPL CFD (AAPL), or EUR.USD CFD on a currency pair |
| Warrant, structured product | DAX DEC'26 18000 Call (FWB,DB,0.010): where it is listed, its issuer, its multiplier as the venue writes it, each where stated |
| Event contract | FF DEC'26 4.5 NO Event (FFE): as a future's option, its right as YES or NO; where the venue files it as an event, its strike as the text the venue states for it, and none for a strike of nought |
| Combination | SPY Combo: its symbol and Combo |
| Bond, bill | US-T GOVT Notes 4.250 Nov05'35 91282CKX0 AA+/Aaa: its class, its kind, its coupon as stated, its maturity (Perpetual for one with none), its CUSIP or else its ISIN, and its ratings, each where stated |
Where the venue's own name for the contract is not its symbol, it follows in
brackets and a space, (SPY 261016C00765000) , except for a currency pair,
a bond and a combination. Sizes are whole units grouped by thousands
(1,500); parts of a unit written out in full on a contract dealt in them
(0.5), and on any other rounded to the places of the least size the venue
states for it, or, where it states none, to four places, or to the logon's
part of a unit where that is finer (tag 8079); a currency pair's in thousands
and millions (25K, 1.5M, 1,250K); and a bond's as the face value it
comes to in thousands of its currency ($5K), or as the number of bonds where
the venue states no face value. A ladder sized by its components is described
by the most it holds, written as the contract writes a size.
An order for an amount of money is described by the amount and the currency
where a gateway takes it by amount: a crypto currency's; a currency pair's
where the venue takes an amount for it, to three places and from the side of
the currency it is priced in; and a share's where the logon lists the order's
type among those it takes amounts on (tag 8351) and the share is one it takes
an amount for: the venue takes an amount on it (CASHQTY) and either the logon
lists a market, limit or stop type there or the share's rule deals in parts of
a share, or the venue states a least size for it and the logon takes amounts on
shares (tag 8334) for the account (tag 8335, 6130 or a crypto permission). An
amount other than a currency pair's
is written to the cent where the logon's product defaults (tag 6052) list its
currency at a precision below a unit, and in whole units otherwise or where the
logon states NOCASHQTYPRECISION. Any other order for an amount is described
by its quantity.
The message is laid out as a gateway lays it out: broken before the word that
would carry a line past 110 characters, the space ahead of the break kept, so
a program reads two spaces there. A reference to one of the venue's answers
(FAQ and eight digits) is made a link where the logon names where the answers
are. Characters beyond seven-bit ASCII are written as \uXXXX. A closed fund
is stated in the venue's words for a closed fund rather than as an order
message, and a price-cap message on an order a program placed is not sent: a
gateway shows it in its own window.
Where the logon asks for them (tag 6130), and does not ask for them not to be
(NOORDERSTATUSREJECT), a rejection the venue states on a status report
reaches the program the same way, with the venue's reason as the message, and
under 10212 with the venue's price-management question where the rejection is
a price cap. A pattern-day-trader rejection is not: a gateway shows it in its
own window.
An order stated in cash
An order that states cashQty goes with the amount on tag 152. Where the
venue works the size out from it — a crypto's always, a share's where the
logon offers DISABLECASHQTYOVEREST and a currency pair's where it offers
DISABLEFXCASHQTYOVEREST — it states no size, as a gateway sends it, and
the venue states the size it worked out on its reports; an order it cannot
size is refused, "Order size would be zero". A fund is bought by its
amount and states a size of one.
A gateway refuses, before sending anything, an order that cannot carry an amount, and this client refuses it the same way, in the same order:
| 10292 | "Stop Buy order is not allowed for this instrument": a stop to buy a crypto |
| 10318 | "This order doesn't support fractional quantity trading": a currency pair stating part of a unit as its size, or an amount with parts of a unit where its currency moves in whole units or the logon waives the currency's least amount (NOCASHQTYPRECISION) |
| 10317 | "The Cash Quantity size of … does not conform to minimum variation of … for this contract": an amount that is not a whole number of its currency's least amount; a fund bought by an amount with more places than the fund's own least amount, where the logon offers MFCASHQTYINCR |
| 10244 | "Cash Quantity cannot be used for this order": a crypto as a stop, a limit or a sale at market; a currency pair whose order types do not list CASHQTY; a fund sold by an amount; a share unless it goes through an algorithm whose definition takes an amount, on an order type the logon takes amounts on, allocated only where the logon permits it, on a contract the venue sizes by an amount; any other contract |
| 10203 | a fund bought with a quantity, or without an amount |
| 10204 | a fund sold with an amount, or without a quantity |
| 10207 | a fund sold by a size of more places than a fund's size is stated to: three, or, where the fund's rule deals in parts of a unit and the logon does not keep funds whole (NOMFFRACMR), the most its rule shows a size to, and no limit where it shows none |
| 10206 | an amount of more than two places, where the currency's least amount is waived or is a whole unit |
| 10293 | "Cryptocurrency Cash Quantity order cannot specify size": a crypto stating a size beside its amount |
| 10241 | a replace of an order stated by an amount, where the account may trade crypto or the logon takes amounts on market, limit, stop or stop-limit orders |
A currency's least amount is the one the logon's product defaults state for
it under CASH (a hundredth where they state none, a yen whole); a gateway
holds its own list of thirteen currencies until the logon states any, and a
currency the logon states a fixed rate for moves in hundredths. A gateway
states 10207, 10206 and the 10317 on an amount without stopping its checks,
and drops the order all the same: only the first of them is stated, with
10293 beside it where a crypto states a size.
The size a gateway works out for the order is the order's size, which it reports until the venue states its own, and is the size tag 38 states where the venue does not work it out. A size the caller states passes through on a currency pair, and on any other contract the venue sizes by an amount. The amount converts at the market's midpoint, last trade or close, then at the record as it stands, then, for a currency pair, at the account's rate between its two currencies; a buy no higher than its limit and a sale no lower. On a contract dealt in parts of a unit the size is the amount over the order's own price — its limit, stop, trailing stop or starting price, or the market's for an order at market — rounded to the places of the finest size, and a gateway keeps fewer places the larger the size's whole part; otherwise it is rounded up to a whole unit where parts of a unit are not open to the order, and to a lot where the order's venue deals in them. Where the venue does not work the size out, it is raised by the margin the account preset states (a quarter where it states none). A replace goes with the caller's size, and a gateway then resizes its own record of an order whose size it worked out to the new amount, on a contract dealt in parts of a unit rounded up to the most places its rule shows a size to; where the rule shows none, the record keeps the replace's size.
A share's order through an algorithm takes an amount only where the algorithm's own definition names one: of the venue's share algorithms, every one but accumulate-distribute does.
An order through an algorithm
A gateway puts no algorithm on a fund traded at its settlement (tag 8077
ETMF), on the settlement venue (tag 207 CFETAS), or on a contract priced
other than in currency (a rule on tag 6858 other than factor or etmf). It
refuses such an order, 10015, with its words for each, and leaves the order's
number taken: placed again under that number, the order is refused, 2102
"Unable to modify this order as its still being processed." So does this
client.
The venue defines each algorithm an order can name, and a gateway holds an
order to that definition before it checks anything else about it. The first
time an order names an algorithm it asks for the list of definitions
(35=U 6040=80, answered on tag 6597), then each definition the list names
for the contract's provider group (tag 6599 of its definition) and security
type (6040=53, answered by 6040=54 with the definition on tag 6118); the
order waits on them, and they are held for the session. So does this client,
and it refuses, with a gateway's codes and words:
| 442 | "Specified algorithm is not allowed for this order.": a contract naming no provider group, or definitions that make no set; an order trading overnight through an algorithm the overnight session does not take |
| 439 | "Order processing failed. Algorithm definition not found": an algorithm not defined under the name the order gives, which is read as stated |
| 443 | "Order processing failed. Unknown algo attribute :…": a parameter the algorithm does not take |
| 10205 | "Cash Quantity cannot be send in monetaryValue field in Algo . Please try sending in Cash Quantity field.": an amount stated as the algorithm's own parameter rather than as the order's cash quantity |
| 10314 | "…: The date, time, or time-zone entered is invalid. …": a moment in a form a gateway does not read — it reads yyyymmdd-hh:mm:ss in UTC, or hh:mm:ss with an eight-digit date before it and a zone after it where they are stated; it never reads the zones CST, IST, BST, AST, GST or AMT |
| 441 | "Algo attributes validation failed:…": a number that does not read, as tag=value; a parameter the contract's primary exchange does not take; and every required parameter missing and every number outside its bounds, one line each, in the order a gateway's hash map of the parameters reads them, not the order stated. A number is read in the units its definition states, so a percentage of volume of 0.1 is ten |
| 145 | "Error in validating entry fields -…": a text that is not one of its legal strings |
A moment stated without a zone is read on the local clock, and a gateway warns of it once for each connection, 2174, before it reads it. The warning is the same here.
An order's warning is described by its amount, where it is stated in cash and goes through one of those algorithms, where the algorithm's definition takes one.
An order for part of a share
A login that may trade parts of a unit or crypto — the account may trade crypto, or it takes parts of a unit (8335) or has refusals told (6130) and the logon's list for parts of a unit (8334) names a market, limit, stop or stop-limit order — cannot place an order for part of a share or a warrant through the API: a gateway refuses it, 10243 "Fractional-sized order cannot be placed via API. Please use desktop version to place this order.", and a replace where the order or the replace states part of a unit, 10242 "Fractional-sized order cannot be modified via API. Please use desktop version to revise this order." So does this client.
What the account may trade
The venue states at logon which security types this account may trade, and
order_permissions() lists them. The venue answers an order for a type it has
not permitted by returning the order inactive, with no text. This client
refuses such an order before it is sent instead, with the reason on the error
callback.
Conditions in the overnight session
conditionsIncludeOvernight asks for an order's conditions to be measured in
the overnight session too. It travels with the conditions, beside
conditionsIgnoreRth and conditionsCancelOrder, each stated on or off as a
gateway writes them, and states nothing on an order that has none. As a
gateway does, this client refuses it under 10371 when the order is placed,
"Conditions include overnight is not supported for this instrument or is not
enabled for this account.", unless the logon enables CONDINCOVN and the
contract's valid exchanges include OVERNIGHT or IBEOS; a contract whose
definition is not yet held is looked up first. A replace is sent without that
check. A report that states the conditions is read for all three flags, one it
leaves out being off, as a gateway reads it, and open_order carries them.
What a crypto order needs
A crypto is quoted around the clock and priced and sized differently from a share. These are the venue's rules, and a gateway meets the same refusals.
| Time in force | Immediate-or-cancel, or the one measured in minutes. A day order is refused: "The crypto buy order must be Minutes or IOC" |
| Price | On the venue's grid. One that is not is refused as a price, "Invalid Price", rather than rounded — this client sends prices as they were given |
| Quantity | A fraction, counted in hundred-millionths. A thousandth of a coin is an ordinary size |
Account values
Account values and per-currency ledger values are separate rows, even when
their key and currency match. A full account subscription can receive both;
req_account_updates_multi with ledger_and_nlv=True receives only ledger
rows. An account-only change does not update a ledger-only subscription.
Unchanged rows are not repeated.
An AccountCode used only to identify an account delta does not overwrite the
account value. A frame that states AccountType before AccountCode supplies
the account value, including an explicitly empty one.
Sessions
What authenticates a farm connection
A market-data or trading connection is opened with a request carrying the session's own token, and the venue may answer it in one of two ways: by asking the session to authenticate in full, or by acknowledging the logon against the token it was already given. Both are the venue accepting a credential.
Where it asks in full, the answer it sends back is checked: the venue states a proof of the session key that only a party holding the account's verifier can compute, and a logon whose proof does not match is refused. The group that exchange runs in is this venue's own and no other, because a peer names it before it has proved anything.
The logon runs beside the channel rather than inside it, so the protocol does not bind the party holding the channel keys to the party that answered the logon. That is a property of the venue's protocol, and a gateway's connections are opened the same way.
Name-service versions
A connect request states the name-service versions a gateway of this build
speaks, 51 to 52, and so does the key exchange ahead of it; a data-farm logon
states the same range on tag 8285, 51..52. The venue answers at 52. Every
message after AUTH_START states the version it agreed, and those before it
state 51.
At 52 the venue may state when it published the session's tokens
(NS_PUBLISH_ST_RESPONSE). The time is kept for each token type. Where a
reconnect or a resumed session presents the session's token it states the
time beside the token's hash, and every data-farm logon states it on tag 8611
behind the hash on 8483; a saved session keeps it for the next connect. Where
the venue has stated none, nothing is added.
Contract expiry validation
Malformed lastTradeDateOrContractMonth is refused with code 10372 before
contract lookup or subscription state changes. Both surfaces check contract
details, market data, depth, tick-by-tick data, real-time bars, historical
bars and schedules, historical ticks, head timestamps, histograms, option
calculations and option exercise. Depth reports an empty exchange first.
Fundamental reports do not apply this check.
Accepted values are empty, case-insensitive NOEXP, yyyyMM, or a valid
calendar yyyyMMdd, with years 1978 through 3000 inclusive. Accepted text is
sent unchanged. Callback errors retain the caller's request id. The error
message is:
lastTradeDateOrContractMonth: The date entered is invalid. The correct format is yyyyMM for a contract month or yyyyMMdd for a date. E.g.: 202607 or 20260724.
Position filters
The session includes CASH positions by default. Set include_fx_positions=false
in Rust's EClientConfig.gateway, or include_fx_positions=False in Python's
connect(settings={...}) or ib_dx.configure(), to omit them from position,
positionMulti, and updatePortfolio.
An empty security type in a cached contract does not override the holding's
known type.
send_zero_positions_for_todays_opening_only defaults to false. When enabled,
a flat holding is omitted only if its opening quantity is known to be zero.
An opening long or short position closed today still arrives with quantity zero;
so does a flat row whose opening quantity is unknown. A holding opened and closed
intraday is omitted when its opening quantity is stated as zero. Nonzero holdings,
including fractional ones, pass this setting.
The opening quantity comes from the venue's daily position figures, rather than from the first quantity seen by this process. Each account uses its own opening quantity, including when the portfolio subscription switches accounts. Both settings apply to initial answers and subsequent updates on every account. Their end callbacks are still delivered for empty answers. The filters do not remove stored positions or change P&L calculations.
Open orders on initial connection
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.
Limits
What a caller has to know before writing against this client: what the account decides, what is not settled here, and where this client answers differently from a gateway. What a gateway answers the same way is on Venue behaviour.
Nothing here is a call that returns as though it acted. A call this protocol cannot carry reports why.
What the account decides
Market depth depends on the entitlement
A book is asked for at a named venue, and a level carries the maker the venue names on it — empty where it names none, which is the ordinary shape on a venue that quotes no makers. Which venues answer is the account's entitlement, not this client's: a venue the account is not entitled to refuses by name, and the refusal reaches the caller.
Measured on one account with the market open: a share on ARCA, a share on
ISLAND and a future on CME are each refused by name, while EUR.USD,
GBP.USD and USD.JPY on IDEALPRO all deliver, and a twenty-level book
delivers as readily as a five-level one.
A book of the size asked for stays that size. The venue sends every level it holds and says nothing about the one that moves when a level inside the asked size goes away, so this client keeps the book and states that move itself: a withdrawal inside the book is followed by the level that came up into the place it left, and a level arriving inside it is preceded by the withdrawal of the one it pushed out. A caller applying the operations in the order they arrive holds the size it asked for.
Things an entitlement decides, not this client
- News headlines need a news subscription. Without one, the providers list is returned and every query comes back empty.
- Corporate events content needs a Wall Street Horizon subscription. The calendar's schema and event types are delivered either way; the events themselves come back empty without it.
Settings a gateway lets its user change
A gateway's user sets how its API behaves: its API settings, its order
precautions, its smart-routing defaults and its warning messages. This client
offers some of them: a read-only session (readonly), the older Nasdaq
spelling (island_for_nasdaq), the time zone and date format, the logging
level, the master API client id (master_client_id), the initial open-order
download (download_open_orders_on_connection), the numbers bound orders take
(use_negative_auto_range), and the position filters
(include_fx_positions and send_zero_positions_for_todays_opening_only). The others, among
them the order-precaution bypasses and the smart-routing defaults, are not configurable here yet, and
req_config and update_config are refused with 10357. The full list is in
#273.
Not settled here
Something the protocol may well carry, which no session has established. Each says what would settle it. None of them is a call that returns as though it acted: a request this client will not send says so.
What a replace states, and what it does not yet
A modify is the caller's order stated whole: its type, its prices and everything it carries go out as its own placement would state them, which is what a gateway's replace does. An order is replaced as itself whatever it is — relative, pegged, trailing, midpoint, snap, limit-if-touched, an adjustable stop, an algo, a conditional order, one with a minimum quantity — and a change of type states the new type whole and nothing of the old one. A trail moved between a percentage and an amount states the unit it is now in, and a trail naming both is refused under 320, Error reading request: Cannot specify Trailing Amount and Trailing Percent at the same time, as a gateway refuses it while it reads the order — a gateway answers every order it cannot read under 320, this one included. A ladder stated as a table is stated on its placement alone, as a gateway states it, and a replace states its restart instead. A ladder sized by its components states the most it holds, the order's quantity in whole units, on its placement, as a gateway states it; the venue refuses a ladder placed without it (Message must contain field # 6534). A replace states it again as the placement stated it, as a gateway does: it keeps the figure on the order and does not work it out again. A gateway leaves every field of a ladder off an order whose contract, on its exchange, does not take a ladder among its order types; this client holds no definition when it places an order and states them whatever the contract takes.
What a gateway refuses in a replace is refused here in its words: a new one-cancels-all group on an order that has one, under 10326 (OCA group revision is not allowed), and a new way for the group to cancel, under 10327 (OCA group type revision is not allowed), unless the venue lifts both at logon. The way is compared whether or not either names a group, and a way that is not one of the four reads as the default, reduce on fill without block: an order placed with the first way and replaced naming none is refused, and one replaced from the default to a way outside the four is not. A parent or a group named on an order that had none is taken, and the order goes on under the links it was placed with.
Four things are not settled:
- The links on a replace. A gateway's replace states neither the parent nor the group. This client's restates the ones the order was placed with, because a session once saw a bracket leg replaced without them leave its bracket — by a replace that also stated none of the order's other attributes. A leg replaced without them on a paper session, and then left to see whether its group still cancels it, would settle which is needed.
- A relative order's replace. One session saw a replace of one draw no answer, and a withdrawal after it none either. That replace stated a trigger a gateway does not state for this type; it states none now, and has not been measured since.
- A change into a type the contract does not take. A gateway refuses it before sending, under 329 and Order modify failed. Cannot change to the new order type: followed by the type's name, reading the order types the contract takes on its exchange. This client sends it and the venue answers: how a definition keys its lists of order types to each exchange is not established here, and a definition captured on a session would settle it.
- A preview under the number of a working order. A gateway prices it as a new order and leaves the working one alone. This client keys an order's record and its revisions on the number, so a preview there would stand in for the working order; it is refused under 329 until the two are kept apart.
A modify of an order this session did not place — one the venue named at connect — is restated from the caller's own statement of it, links included: there is no placement here to restate them from.
Market-on-close acknowledgement
A market-on-close order placed during regular hours draws nothing from the
venue until it is withdrawn: no acknowledgement and no status. A program sees
no PreSubmitted or Submitted for it while it works, because the venue
states none. A gateway states an order to a
program when the venue reports on it, when the venue names it at connect and
when a program asks for the open orders; it neither asks the venue about an
order nor states one on a clock of its own, so it says nothing in that wait,
and neither does this client. Asked for the open orders meanwhile, both state
the order on open_order and then order_status, as PendingSubmit, under the
number it went out under as its permanent id.
Withdrawn, nothing is said as the cancel goes out, and the order reads
PendingCancel to a program asking for its open orders. The venue then
acknowledges the order ahead of answering the withdrawal; a gateway takes an
acknowledgement only from an order not yet withdrawn, so that report states the
order PendingCancel, then the venue's answer Cancelled, and so does this
client. The order a gateway sends for it differs from a market order in its
type alone, and so does the order this client sends: nothing about the close
travels beside it.
Placed before the open, the same order is acknowledged at once with the venue's warning that it will not reach the exchange until the open, which reaches the program as error 399, as it does from a gateway; see a message beside an order's status.
A trailing stop limit by percentage
A gateway takes a trailing stop limit whose trail is a percentage. What it states as such an order's trigger is not established here, so this client refuses one rather than put a price on it the caller did not ask for, and takes the trail as an amount. A gateway placing one, read on the wire, would settle it.
The order types a contract takes on its exchange
A gateway refuses a midpoint peg on an exchange whose order types include
neither form of it, and a peg to best where they do not include the midpoint
form it rests on, under 387 and Unsupported order type for this exchange and
security type. This client sends both and the venue answers. The lists are
read here, each contract under the one its own exchange's record names, as
order_types states it, and an order is not checked against them.
A contract's least size
min_size and size_increment are what a gateway states, as
Venue behaviour describes, except in three corners a
gateway decides on a setting or a figure this client does not read: a fund
held to whole units despite a fractional rule; a contract on SMART or
ZERO whose suggested size is stated as nought, which a gateway states as one
under a logon setting; and a contract for difference whose rule states no
sizes, which a gateway sizes by its underlying's listing.
Alternate exercise transport
An exercise checks the selected account's positive position and waits for its in-the-money figure, including when override is set. Override bypasses the natural exercise/lapse check. The quantity is limited to the whole-contract position captured before the wait. The alternate exercise transport named by some logins is not implemented.
Option computation ticks
Every numbered market-data tick that reaches a caller of the reference client reaches one here. The option computations, 10 to 13 and the delayed 80 to 83, are built from what the venue states as a gateway builds them, as Venue behaviour describes, except as follows.
On all of them:
- Where a program asks the venue's real-time close series (459) on an option's underlying, a gateway may mark the underlying at a close it works out from that series; this client marks it from the quote alone.
- An option is worked from its definition as the venue has answered it to this session, by a contract-details request or by a lookup of this client's own. One this session has not been answered is modelled without it: no underlying's price, a warrant's price per unit, and no 10 to 12.
- A restriction is reported with the offer of delayed data where the venue's refusal of the subscription offers one; the model's own refusal states the offer always, as a gateway's does where delayed data is available for the listing — which, on the feeds this client is served, it is.
- On a frozen feed a gateway works them from a model it keeps for the frozen quote, and what that model states is not established here. Here 13 and 83 are the live model's, and 10 to 12 (80 to 82) take the frozen quote's sides where it states any.
On 10 to 12 and 80 to 82, and the present value of dividends on every one of them:
- A volatility option in the VIX style, a quanto's or a forex index's yield, and a premium settled futures-style are read by a gateway from reference data it is served; this client is not served them. An option on an index is worked in the index style with no yield, and an option on a future as one whose premium is not settled futures-style — for a volatility option, a quanto, or a future whose premium settles that way, the figures can differ from a gateway's. An option whose definition the venue has not answered is sent no 10 to 12 and no dividends' present value, and a snapshot of one waits for them as a gateway does, and so runs its eleven seconds, where a gateway ends it once its own 10 to 12 are sent.
- They wait on the option's sessions, for when its time runs out, which this client asks for by the key the option's definition names, once for every option on the key and again each day, as a gateway asks; on the underlying's schedule of payments; and, for the rate, the dividends' present value and the greeks, on the currency's rates, asked for once. The rate and the present value are worked out when these arrive and kept, as a gateway keeps them until they are stated again.
- A payment the model's tree takes is one whose ex-date ends a whole day or more from now on this machine's own calendar, which is how a gateway counts it on its own: on the time zone its settings name, or its machine's where they name none. No zone is named to this client, so it counts on its machine's.
- The model takes no dividend yield for the underlying. A gateway takes one only where a trader workstation on the same machine, under the same login, saved one for it; with none saved, it takes none either.
Where this client behaves differently
Not a gap in what it can ask for. A program written against the other behaviour will still be wrong, which is why they are here.
An order's warning on some contracts
A message the venue states beside an order's status reaches the program as a gateway words it, with the order described as a gateway's display describes it (see a message beside an order's status). For some orders that description is not written here, and the message is not sent: a fund, a combination whose definition this session has not been answered, a structured product of a kind the venue names, and an order for an amount of money that carries an algorithm whose definition this session does not hold yet. A gateway sends each of them. A hedge the venue holds until its parent fills is described without its size by a gateway; here its size is written. A gateway follows the strike of a derivative whose underlying trades in another currency with that currency, once it has looked the underlying up; here the strike is written alone.
Two fields of a status are fixed here
Every order_status names the last price an order filled at, beside two
fields this client states as fixed values. A gateway reads its market-cap
price off an attribute it carries for the order — asked under tag 6591, its
value answered back — and this client carries no attributes, so
mkt_cap_price is always 0.0. A gateway says why it holds an order where it
holds one for a reason; this client keeps no such string, so why_held is
always empty.
Callbacks nothing fires
Two callbacks exist so a program written against the TWS API compiles and runs, and nothing here fires them.
| Callback | Why |
|---|---|
reroute_mkt_data_req | The contract a market-data request should be asked for under instead — a contract for difference standing for a share. A gateway sends it, and asks the venue nothing, for a contract for difference whose own definition asks for its underlying's data, where the account's logon permissions allow that. This client does not read a definition's flags for asking on the underlying before subscribing: the request goes to the venue as asked, and this never fires |
reroute_mkt_depth_req | The same, for a request for the book rather than the quote |
Seven more never fire for a program on a gateway, and fire here as often: never. They are on Venue behaviour.
Every other call and callback on the canonical list is served on both languages. The call-by-call matrix is generated from the source.
The synchronous wrapper its samples import
The reference client publishes a second client beside the callback one: a
synchronous wrapper its samples import (from ibapi.sync_wrapper import TWSSyncWrapper), which runs an event loop of its own and blocks on each
request's answer. No module of that name is published here. The wrapper is a
second engine rather than a view of this client's surface, so the one-rename
promise does not reach it: a program written against the callback client
ports by the rename, and a program written against the synchronous wrapper
is rewritten in the callback style.
Bars along a contract's id history
A contract can trade under more than one id, ticker or listing over its life,
and the venue states which beside the contract's corporate actions. For every
bar request on a stock or a fund, whatever the series, this client asks the
actions first, as a gateway does, and then the bars one stretch of that history
at a time, the newest first, each under the id it traded as and for its own
days; the stretches are one series, folded across the joins with the one set of
actions. Weeks and months are asked with the history split at every split,
stock dividend and spin-off as well, and the two parts of a week or a month
that two stretches answered for are joined into one bar, as a gateway joins
them. The logon token NOINEFFECTCONCQUERY changes where the stretches are
cut, as it does on a gateway.
A series of days, weeks or months asked along more than one stretch counts the days each answer holds, those of its bars and those of its session opens, and asks nothing more once the request's bars are all in. Its last stretch is cut at the day the request reaches back to, as a gateway cuts it, and the venue answers that day whole: such a series can hold one day more at its far end than the same request holds where the contract kept one id.
The actions are held for the session, as a gateway holds them. A request for a contract whose actions were answered the same day on UTC's calendar is asked along them without asking again, and an answer on a later day adds to what is held the rows it states that are not already held. Everything held is let go of when the day turns on the machine's own calendar. A gateway lets go of it when its own day turns; which clock that turn follows is not established here.
A stretch the venue refuses fails the request: the caller is told why, and the bars of the stretches already in are not delivered. A request that cannot be asked along the history — a day in it that cannot be read, or an end or a length this client cannot count — is asked as it was made, under the id named, and filed as the venue serves it, not folded, as a gateway sends the query it was given where it cannot cut the history. Where nothing of the history falls within the request, the caller is told the query returned no data; a gateway says so where the history is one stretch, and answers nothing where it is more.
A gateway also asks a request for another kind of contract this way where a gateway's lookup names that contract; where the venue turns a feature on for the session, it asks a request that starts less than a day before now as it was made; and it leaves out of a stretch a listing the logon names. This client asks a request for any other kind of contract as it was made, and leaves out only the listings a gateway leaves out where the logon names none.
Smart depth is one request
A book is refused before anything is sent where a gateway refuses it, in its words and in both languages: no exchange (Please enter exchange.), a combination (Market depth does not support combos.) and no rows (Market depth rows requested must be greater than zero.), each under 321. A book on a market the routing table names for the top of the book only — a share on the smart destination asked for without smart depth, as the table this account is given reads — is refused before the venue is asked, under 10092, Deep market data is not supported for this combination of security type/exchange, as a gateway refuses it. Smart depth is not refused on a type a gateway gathers a book for from each venue the contract trades on: shares, contracts for difference, options, futures, currencies, bonds, crypto and the rest.
What differs is how smart depth is asked for. A gateway gathers one book from each venue the contract trades on; this client asks for it as one request on the smart destination, and on a share the smart destination serves no book, so nothing arrives and no error follows. A gateway also matches a smart row by the contract's listing exchange and aggregate group, so a share listed on PINK in group 1 is served a book on the smart destination; this client does not know those when it asks, and refuses every share on the smart destination without smart depth.
A regulatory snapshot is asked for whatever the permission
A regulatory snapshot (regulatory_snapshot on req_mkt_data_ex) is answered
by the venue in one message, which this client reads and hands to the
snapshot's own request, as a gateway does. Where the venue states the
contract's snapshot as chargeable, the moment the answer was read is stamped on
a clock corrected to the venue's and delivered as text on 85, in milliseconds;
a contract the account sees in real time is answered without it. Like a
gateway, this client waits up to two seconds for the contract's map of venues
before publishing, so the exchange letters on 32, 33, 84, 109 and 110 are the
contract's own, and the snapshot ends as soon as its answer is published.
Where the map never comes it publishes none of the answer, as a gateway
publishes none; the error a gateway reports then is not reproduced, as its
words are not settled here. On an account without the entitlement the venue
refuses the request, so no answer has been read here from a session.
What differs is what happens before and after. A gateway reads the contract's snapshot permission first and refuses, without asking the venue, a snapshot the permission does not allow — nothing stated, no top of book, or not available through the API — and gives up on the data after seven seconds. This client asks the venue whatever the permission, and waits up to eleven seconds.
Binding a new order placed elsewhere
req_auto_open_orders(true) turns binding on for client 0, as on a gateway,
and a client other than 0 is refused as a gateway refuses one: with 321 asked
to bind, as a request that fails validation, and with 327 asked not to. A
gateway numbers a new order placed elsewhere as the order is created. This
client learns of the order from the venue's first report of it, binds it then,
and asks the venue to bind it with the request a gateway sends for an order
req_open_orders binds. That request and the handling of the venue's answer
are checked on a local socket; the venue's acceptance has not yet been read
from a session.
Configuration requests
reqConfigProtoBuf(configRequestProto) and
updateConfigProtoBuf(updateConfigRequestProto) report 10357 under the request
id on every connected session: Configuration access via API is not available.
Please refer to the application interface to view or update your settings.
Their snake spellings are req_config_proto_buf(config_request_proto) and
update_config_proto_buf(update_config_request_proto). None makes no request;
configuration payloads are never applied. A request without a session reports
504.
Rust's req_config(req_id) and update_config(req_id) take only the request
id and deliver the same refusal through process_msgs and Wrapper::error.
They accept no configuration payload.
The protobuf encoding's family
Beside every text request the reference client publishes a second spelling
that takes a message of a protobuf encoding, serializes it and sends it on a
socket: the req*ProtoBuf and cancel*ProtoBuf family, useProtoBuf(msgId)
to ask which encoding a session speaks, and a *ProtoBuf stub on its wrapper
for every answer of that encoding. There is no socket between a program and
this client — the engine runs in the same process — and no protobuf encoder
exists or is needed here.
useProtoBuf(msgId) answers False under both spellings, so a sample program
written against that client asks, is told no, and takes its text path. Every
req*ProtoBuf(requestProto) routes to the text request carrying the same
intent: it states what its message states, and a field the message does not
state falls to the text request's own default, while an argument the text
request requires but the message omits arrives as the encoding's own zero for
it — what a gateway reads off the wire for an unstated field. A None message
is the reference client's own first line and sends nothing. The two
configuration calls above are the exception: they report 10357 rather than
routing, and the family leaves them to the Rust surface that does.
Two requests carry a field the text surface has no place for.
placeOrderProtoBuf(placeOrderRequestProto) routes orderId, contract and
order; its attachedOrders is dropped, as a stop-loss and take-profit pair
travels here on the order's own fields, not beside it. startApiProtoBuf
routes to the text start_api, whose session this client establishes without
the clientId and optionalCapabilities the message may state. The transport
primitive sendMsgProtoBuf is absent: there is no socket to serialize onto,
and a program that reaches for it is reaching for the encoding this client
does not speak.
printProtoSingleLine(header, message) is in the utils module, the
one-line display helper a callback in that encoding prints what arrived with.
Where the protobuf library is installed it renders a message on one line as
that library renders one; where it is not, the message's own text is collapsed
to one line, so the call displays and never dies on the import a program's
first line made. The 81 *ProtoBuf wrapper stubs exist to be found: a
program's override calls super() into one, and the base answers as every
other stub here answers — nothing, as this client's engine delivers its
answers under the text encoding. The whole family is installed from Python
(ib_dx/_protobuf_shapes.py), and the reference spellings are generated for
it as for any other method, so both reqMarketDataProtoBuf and
req_market_data_proto_buf name one call.
Connect options
Python's set_connect_options (also setConnectOptions, and ibapi's
setConnectionOptions; the Rust client has no such call) answers a caller that states options on error, under
321. The reference client hands these to its gateway on the greeting for the
gateway to read, and there is no gateway between this client and the venue.
Stating none is stating nothing.
set_optional_capabilities is of the same standing and answers the same way:
the reference client names the capabilities in the same greeting, for the
gateway to switch on, and nothing here reads them. A caller that states any
hears it on error under 321 rather than getting a session that is not the
one it asked for.
The reference client's client-side validators are ported as pure functions of
their arguments, answering what they answer there: the parameter an order
states that a session at the level this client answers cannot carry, by the
name that client gives it, and a string a connection names that the wire
cannot carry, said on error under 579. The level a session answers here is
217, so a field the reference gates above 217 is named even where a gateway at
a higher level would carry it.
A withdrawal's time
A withdrawal states who is withdrawing the order and whether a person entered it, and both travel on the cancel as a gateway writes them: from the withdrawal, not the placement. A cancel that states neither carries neither. An operator holding the byte that separates fields is refused under 321 and nothing is withdrawn.
A manual cancel time is read as a gateway reads it — yyyymmdd-hh:mm:ss in
UTC, or yyyymmdd hh:mm:ss with the date and a zone optional — and one it
cannot read is refused under 10301 in a gateway's words, and the order keeps
working, as it does through a gateway. A time holding a character outside
ASCII is let through unread. A time that is read does not travel: a gateway
sends one only where the venue has turned that record on for the login, and
this client does not read whether it has. The withdrawal goes, and the caller
is told on error that the time did not. A gateway also refuses a time it can
read but not place — one in a zone that is neither UTC nor that of the machine
it runs on — and this client does not.
A withdrawal of every order carries no time: the reference client writes none, and one stated here goes nowhere, as there. From Python, each field is written as its text, as the reference client writes it, and an indicator that does not read as a whole number is refused under 320 (Error reading request: Unable to parse field: 'Manual Order Indicator' for input string: '…').
Request ids
Every request reads its number as a gateway reads it, four bytes signed: one
that does not fit — every id at and above 0x8000_0000, the ones this client
keeps at and above 0xC000_0000 for the questions it asks itself among them —
is refused under -1 with 320, Error reading request: Unable to parse field:
'Client Req Id' for input string: '…', as a gateway refuses it, whatever else
the request states. A withdrawal refused so withdraws nothing; a stream watch
opened is withdrawn under the number it handed back. A negative id is carried
whole by a market-data request, as a gateway reads it, and refused by the
requests that carry the number as four bytes unsigned.
The interface this client mirrors
encourages one counter for orders and requests, and a program that keeps one
meets this once the account's order ids reach 0x8000_0000: the next valid id
is then one no request can be stated under.
A preview asked with what_if_order spends no order id. The calls that answer
take their numbers from that band, and the venue's answer to a preview is not
counted as naming an order, so the ids handed out after a preview go on from
where they were.
A calculation asked for before the model has arrived
calculate_implied_volatility and calculate_option_price are answered from
the venue's model for the contract, and asking opens the subscription that
carries it. A gateway gives up after five seconds. This client waits until the
model arrives or the call is cancelled.
Market rules arrive with contracts
A gateway keeps a market-rule table of its own and answers reqMarketRule
from it for any rule it knows, in a fresh session included. This client
learns a rule only with the details of a contract that uses it: a rule asked
for before any contract has brought it in is answered the way a gateway
answers one it does not hold — against -1, under 322, Market rule with id =
260 is missing. Asking for a contract that uses the rule brings it in, and a
request after that is answered.
The fundamentals series is not gated at the intake
A gateway serves the company ratios on a market-data subscription — generic
tick 258, or its older number 47 — only where a flag of its own feature
service says the account may have them, and refuses the request under 10358,
Fundamentals data is not allowed, where it does not. The flag is state a
gateway reads from a service of its own; nothing on the wire this session
reads corresponds to it, so this client does not gate the series: the
subscription goes out as any other legal series goes out, and the ratios
arrive where the venue serves them.
A frozen quote is the subscription's
A gateway decides for each request whether it is served the frozen quote, by the type in force when that request began, and applies a type changed while requests run to them as well. This client holds one subscription per contract, and every request on it is served what the subscription was made with; a type set later applies to the subscriptions made after it. The market's status of a contract directed to an exchange other than the smart route is asked for on its preferred market, or on the smart route where it is listed there, as a gateway asks it, where this session holds the contract's definition; where it does not, on the contract's own exchange.
A gateway states the type a request is served at the moment it changes, also where no frozen record follows it; this client states it with the next tick it delivers for the request. And where the quote served changes between the live one and a frozen one, a gateway sends the whole quote again, and this client sends the fields that differ.
Order fields
An order carries 159 fields. 128 go out under a tag. 23 are taken and not
sent: a gateway reads each and sends nothing for it on the orders this client
places, and neither does this client. 1 is not carried by this client, and
it says so on itself rather than being quietly dropped. 6 more are what the
venue fills in on the way back, which an order being placed does not carry
out. The last, transmit, is acted on here rather than sent: an order held
back is kept until one in its family transmits, which is what a gateway does
with it.
The 23 include the three retired instructions described below, a basis-point
offset and its kind, a bond's accrued interest, an auction strategy, a
shareholder, a parent's permanent id and the percentage constraints an order would set aside — which a gateway reads and sends nothing
for — together with the order options, the prices on a combination's legs,
the choice to decline smart routing and the kind of preview, which it checks
and sends nothing for, in the same words: an unknown option under 10337, a bad
value of manual under 10338, an entry not written key=value under 320
(Error reading request:Please use 'Key=Value' format for Misc Options), a
leg's price as Prices on a combination's legs below sets out, declining
smart routing refused under 10348 where the venue withdrew it and warned about
under 2181 otherwise — ahead of anything the venue says about the order, as a
gateway says it before the order goes out — and a preview kind other than the
ordinary one refused. A gateway reads the kind only from an
order in the protobuf encoding; the text encoding ib_async uses has no field
for it. The delta, the price randomisation and the hedging leg's
clearing, settling, short-sale and designated-location fields go out only on
order types this client does not place — a pegged-to-stock order, a
volatility order and the hedge a gateway builds for one — so on every order it
does place, a gateway sends nothing for them either. The hedging leg's short
sale is refused on an order that is itself a short sale naming a hedging
order type, where what a gateway makes of it is not established here.
A combination's routing parameters are not carried: a gateway checks them against the combination in ways not all established here.
Attached orders
place_order constructs stop-loss and profit-taking children from the selected
account preset. Rust uses sl_order_id / sl_order_type and pt_order_id /
pt_order_type; Python uses slOrderId / slOrderType and ptOrderId /
ptOrderType. State a child id with PRESET, compared without case. The default
integer leaves the id unstated. A child id is numbered as the order id is, so an
id from next_order_id() or next_valid_id serves a child as it serves a
parent; as with a parent, a new child under an id at or below one already used
is refused with 103. These are fields of Order; the call gains no argument.
The engine loads the contract and preset while unrelated requests continue.
order_presets() returns the list's key, attributes and last-change triples;
it does not return the separately requested values. Answers are correlated to
their request and selected key, and list changes invalidate older values.
Missing answers and venue errors are distinct from disabled attachment flags.
Smart-routing keys (sr=) and keys naming no selector are not selected as order
presets. The values request rebuilds its key in sr, s, u, tc, m2, f order and
escapes its values; the list retains the keys as the venue stated them.
The parent goes first, then stop loss, then profit taker. Children reverse the
parent's side and receive the applicable quantity, prices, parent link and
sibling OCA terms. TIF, trailing and adjusted prices, scale terms, RTH flags
and parent-trade-price offsets follow the preset and contract. A stop child
does not retain OPG unchanged. A contract without OCA support can omit a
collected multiple-child family while allowing the parent through.
A family with transmit=false stays in the engine until a parent or child
transmits it. It stays unsent at disconnect. A transmitting family finishes its
waits and goes out in order before logout. Children are built once: placing an
existing parent again changes that order, preserves changes already made to
its children and does not reload the preset. API order ids remain the ids for
callbacks, modification and cancellation even when venue ids differ. An API
number equal to another order's venue number still identifies its own order;
another client's API number does not address this client's orders. Parent
links carry the parent's current venue revision.
Initial child prices use the available order, quote and portfolio information
with the contract's price rules. There is no extra snapshot wait to choose
those prices. A parent's limit is read as a gateway reads it off the order: a
type that takes a limit is priced from its own, and one that takes none, a
market parent among them, from the preset's primary limit as the parent is
created, which unless the preset states otherwise is the ask to buy and the bid
to sell. A limit the program states on a market order prices nothing.
Transmission can subsequently wait for an ordinary quote or mark data without
recalculating the children. Changes and cancellation apply to the held family;
releasing its market-data observer leaves caller subscriptions active. Price
and trailing units accept 0 (amount), 1 (ticks), 100 (percent), or the
labels amt, ticks, %. Other numeric units are unavailable.
Combination attachments use confirmed contracts, normalized legs and their price rules. Distinct combinations retain distinct quote slots. Fresh quantity scaling differs from replacement quantity handling: an explicit group-allocation total becomes an integer on creation, while a replacement preserves the quantity it states. Ratio factors, parent-price offsets and a combination's multiplier are written to two through eight decimal places.
Refusals follow this order:
- Attached fields are read first.
NOAPISLPTSGLgives 320,Error reading request: Attaching stop-loss or profit-taker is not allowed as part of a single placeOrder request, please submit such orders separately.This includes an empty attached message. Otherwise a mismatched id andPRESETgives 320,Error reading request: Invalid value for Stop Loss order-id or order-type, with Profit Taker checked next. A non-PRESETtype with no id is not refused merely for that type. - Order-field checks precede the shared contract-expiry check. An invalid
expiry gives 10372. Empty,
NOEXP, a validyyyyMMmonth oryyyyMMddday in years 1978–3000 are accepted. - Numbers are checked parent, profit taker, stop loss. An unassigned 0,
INT_MAXorINT_MINgives 10149,Invalid order id: N. A number no higher than the last placed gives 103,Duplicate order id: N, unless it names an existing order or an advanced rejection allowed reuse. Equal ids within the family give 103,Duplicate order id. - A requested attachment disabled in the loaded preset gives 10355,
Cannot auto-attach Profit Taker. Preset is not defined., checking Stop Loss next.
The advertised level remains 217. Attached orders are constructed, but
level 218 also needs percentage-allocation sizing from positions by account
and model and the applicable allocation group. The selected account's holdings
cannot supply those quantities. Requests are not refused locally for this
limit; callers whose child quantities depend on group or model holdings must
supply explicitly sized parent and child orders. 226 is the highest level
a gateway announces; conditionsIncludeOvernight at that level is carried.
The complete preset-values answer and an entire attached family still need venue confirmation. Offline tests cover the readers, construction, pricing, identities and engine holds; they do not establish measured venue acceptance.
Two fields a gateway sends are refused by the venue by name, and this client sends them the same way so the caller receives that answer: a caller's own name for an algo — "Invalid value in field # 8016" — and how much of a ladder's first component is already filled — "Can not contain field # 6486". The second goes, as a gateway sends it, only on a ladder that steps its price, as do the ladder's price adjustment, profit offset, restart, varied sizes and starting position.
None is silently dropped, and that is checked rather than claimed:
python scripts/gen_order_field_reach.py recounts every figure from the
order builders and exits non-zero if any field becomes settable and unread.
Retired order instructions
Both Order surfaces accept e_trade_only, firm_quote_only and
nbbo_price_cap; Python also accepts eTradeOnly, firmQuoteOnly and
nbboPriceCap. Their defaults are false, false and f64::MAX (UNSET_DOUBLE
in Python). An unset or non-finite cap states no instruction; zero and other
finite values do.
With DEPRETFQNC enabled at logon, the first stated instruction is refused
under 10268, 10269 or 10270, in that order. Otherwise each produces warning
2168, 2169 or 2170 and the accepted order goes without it. The caller's order
object keeps its values. Preview and algorithm orders follow the same rule.
The option list is read first, then the order fields are validated, then the
retired instructions are checked. A stop order with no stop price and
e_trade_only=true therefore receives 321 before 10268. NOAPIMISCVLD
lifts option key and value checks while reading, but the later numeric check
on manual remains after preview and trailing-percent validation.
Declining smart routing is checked after these three instructions. If that
produces refusal 10348 after earlier warnings, Python delivers the warnings
before the refusal. Rust returns the refusal from place_order and queues
the earlier warnings under the order id for Wrapper::error on the next
process_msgs; a negative order id receives no queued warnings.
A price of nought on an order
A gateway reads some prices of nought as no price and carries others as stated, and so does this client:
- A limit of nought on a type that takes a limit is no limit, except on a combination that is not a relative order, where it is a price and is sent.
- A trailing order's amount of nought beside a percentage is no amount, and the percentage is the trail; stated alone, it is a trail of nought.
- A trailing stop's starting trigger is carried wherever it is stated, nought and below included.
- A stock range of nought is no bound, and is not sent.
- A discretionary amount below nought is refused under 168, Discretionary amount does not conform to the minimum price variation for this contract.
A price the order does not state
Both Order surfaces start each figure the reference client starts unset at
its unset value: the limit and auxiliary prices, the trailing percentage, the
cash quantity, the trigger and adjusted prices and the filled quantity at
f64::MAX (UNSET_DOUBLE in Python), and the minimum quantity and the
volatility and reference price kinds at i32::MAX (UNSET_INTEGER). A price
left there reaches the wire as none: the order goes without its tag, as a
gateway sends it, and the venue answers it. An order whose type cannot go
without the price is refused as a gateway refuses it, under 321, previews
included:
- a stop, a stop limit or a protected stop with no
aux_price: Please enter a stop price; - a trailing stop limit with no
trail_stop_price: Please enter a trailing stop price; - a market-if-touched or limit-if-touched order with no
aux_price: Please enter a trigger price; - a trailing stop or trailing stop limit with neither an amount nor a
percentage, or with an amount below nought: You must enter a Trailing Amount
for TRAIL Order. (or
TRAIL LIMIT Order.); - an adjustable stop with no
adjusted_stop_price: Invalid Adjusted Stop Price, and one becoming a stop limit with noadjusted_stop_limit_price: Invalid Adjusted Stop Limit Price.
Prices on a combination's legs
A gateway prices a combination by its legs only where it is a non-guaranteed combination of two legs routed by SMART. This client carries no routing parameter that makes a combination non-guaranteed, so it places none priced that way; the prices are checked as a gateway checks them and answered as it answers them:
- A priced leg on any order but a limit is refused under 321, the leg named (The combo details for leg '0' are invalid. - … Only LMT or REL+LMT order allows using per-leg prices.).
- An unpriced leg after a priced first leg is refused the same way (… All leg prices are needed when specifying per-leg prices.).
- A limit of the combination's own beside a priced first leg is refused under 10054, Can't specify combo price when using per-leg prices.
- A combination with more than two legs and no overall limit is refused under 10369 for LMT, STP LMT, PEG MKT, PEG MID, PEG BEST, LOC, TRAIL LIMIT and LIT: Limit price is required for combo orders with more than 2 legs. Zero is a valid combination limit. This check follows 10054 and precedes 10058.
- Otherwise, a combination priced on every leg is refused under 10058, Combo per-leg prices are only supported for non-guaranteed smart combo with two legs and feature "IECOMBOPERLEGPRICE" enabled.
- Prices on later legs alone are read and not sent, as a gateway sends none; the combination goes at its own limit.
The legs a combination's quote states
A quote request stating the BAG type and no legs is refused as a gateway
refuses it, under 321, Security type 'BAG' requires combo leg details.,
whether or not the contract states the venue's id for the combination.
Legs stated on the request are dropped at the API boundary: the lookup behind a subscription carries no legs, so nothing downstream can state them, and a gateway carries them to the venue — each leg's contract, its ratio, its side and its venue — naming a combination the venue has not numbered. Here the request is answered only where the venue has numbered the combination: the subscription goes out under the id the contract states. A combination the venue has not numbered cannot be described, and what the venue is asked for is the id alone.
A request's option list
Every request that carries a free-form option list has it checked the way an
order's is: market data, a book, historical bars, a scan, real-time bars, a
news article, historical news and historical ticks. manual, 0 or 1, is
taken and changes nothing a gateway sends. Any other key is refused under
10337, another value under 10338, and an entry that is not key=value under
320. Two entries that could each be refused are checked in the order a
gateway's table holds them, and a key named twice keeps its last value. Where
the venue has lifted the checks at logon, a value of manual that is not the
number nought or one is refused under 321, after Market data: or
Historical data:; a value written in another script's digits is let
through, since a gateway reads those digits and this client does not.
The two option calculations, calculate_implied_volatility and
calculate_option_price, take no key at all: any key is refused under 10337
with no key named as valid (*Misc options key=foo is invalid in
ReqCalcImpliedVolatility(54) request. Valid keys are: *), and an entry that is
not key=value under 320. Where the venue has lifted the checks, any list
that reads is taken. Nothing in either list is sent: this client answers the
calculations itself, from the model the venue states for the contract. A
request for a fundamental report takes a list and a gateway reads none of it;
neither does this client, and nothing in it is checked or sent.
The Rust client takes a free-form list on an order and on req_mkt_data_ex,
whose final argument is mkt_data_options: &[TagValue]. Pass &[] for no
options. Python's req_mkt_data_ex appends mkt_data_options=None after
mode_9887 and checks it as req_mkt_data does. From Python, None is no
list, and an entry that is not a tag and a value is written as its own text,
on an order as on a request, as the reference client writes it — refused
under 320 where that text is not key=value.
The checks are the ones a gateway makes on a list written in the text
encoding ib_async uses. On the protobuf encoding the reference client moves
these requests to from level 203 on, a gateway checks only the value of
manual; this client makes every check whichever client a program was
written against.
A refusal made while an order is checked names no request
Where a gateway refuses an order while it validates it — a login holding
several accounts naming none, a preview of a kind it does not take, a trailing
percentage outside what one can be, a value of manual that is not a number
nought or one where the venue has lifted the option checks — it answers under
321 and puts the name of the request it was validating in front of the reason.
This client answers under the same number with the same reason, without the
name. An option under an unknown key, and a value of manual other than 0
or 1 where the checks stand, are answered under their own numbers, 10337 and
10338.
The account an order is for
On a login holding one account, a gateway states that account on every order whatever the order names, and so does this client; the open order reads back that account from the moment the order is sent, as a gateway's does. On a login holding several, the order goes out on the account it names, and so do its replacements and its withdrawal; one naming none is refused, You must specify an account. The withdrawal of an order the venue named at connect goes out on the account the venue states it is on. An exercise on such a login is refused naming none (The account code is required for this operation.) or one the login does not hold (Invalid account code 'U9'.).
A login holds several accounts, as a gateway decides it, where the venue lets accounts be added to it as it runs, where the first account its logon names is an introducing broker's master, or where its logon names more than one account that is not a group. The accounts a family links to the login are not counted. Which list a gateway counts is read from the logon, and this client counts the accounts the logon names as the login's own; a login holding several accounts, placing an order naming none, would settle that the two agree.
An advisor's login is not refused an order naming no account: the order is allocated across accounts. Where an advisor's order does name one, a gateway allocates it to that account rather than stating it on tag 1; this client states it on tag 1, and what the venue makes of that is not established here.
An exercise naming another account on a login holding one is refused under 322, No unlapsed position exists in this option in account followed by the account, which is what a gateway answers when it looks the position up there. For the login's own account a gateway checks the position before sending, and reduces a request for more than is held to what is held; this client sends it and the venue answers, under 399, You have not got the number of options requested to be exercised.
An order states who it is for twice
A gateway states who originated an order on tag 6122 and nothing on 204. This client states both: 6122 from the order's origin, and 204 as a customer's order, which the venue once refused an order for leaving out when it stated neither. An order placed with 6122 alone on a paper session would settle whether 204 can go.
Order-type names
A gateway takes each order type under several names, in any case, and so does
this client: LIMIT is LMT, STOP LIMIT is STP LMT, PEG PRIM is a
relative order. An order is stated back under its type's own name, as a
gateway states it: one placed as LIMIT reads back LMT, and one placed as
STOP LIMIT reads back STP LMT; a relative order reads back REL. A name
that is no order type is refused as a gateway refuses it, under 10051 and
Invalid order type, and so is a name a gateway does not take.
Order types a gateway takes are not all placed by this client: trailing
market-if-touched (TRAIL MIT) and limit-if-touched (TRAIL LIT), and the
two joined to a market order (TRAIL LMT + MKT, TRAIL REL + MKT); a limit
or a relative order joined to a market order and a relative order joined to a
limit (LMT + MKT, REL + MKT, REL + LMT); the retail price improvement
order (RPI), the volatility order (VOL) and the four volatility pegs
(PEGMIDVOL, PEGMKTVOL, PEGPRIMVOL, PEGSURFVOL, each also under its
spaced name and its short one, such as PEG MID VOL and PDV); the
pegged-to-stock order (PEG STK), FIX PEGGED, FUNARI, QUOTE and
IBALGO as a type name.
Each carries prices or companions not yet established here, and is refused by
name under 387, Unsupported order type: followed by the name and that it is
not an order type this client places.
Account groups and models
reqAccountSummary is checked as a gateway checks it and refused in its
words, before anything is taken: empty tags (Tags cannot be null), an empty
group (Group name cannot be null), and on a login that is not an advisor's
any group but All or AllNonProp (Group name is invalid), all under 321.
All is refused where the logon says the login may not ask for every account
(ALL account is not supported, 321) and, on a login the venue adds accounts
to, under 10200. A third summary while two are open is refused under 322, as a
gateway refuses it. The tag All is what asks for every figure; a group of
All with empty tags is refused.
An All summary includes every account held by the login. Named-account
updates, positions and profit subscriptions answer for the selected account.
AllNonProp, advisor group membership and model selection are not applied;
they are accepted with a warning once per selection and session. Multi-account
answers retain the caller's model label without applying that model.
Executions and fills are the ones the venue restated, not the account's history
reqExecutions() answers with the executions this session has seen and the
ones the venue restated when the session opened.
The venue restates the account's recent executions at every logon. Those are filed for a caller that asks and announced to nobody — a restarted program is answered with the fills it made before it restarted, including fills on orders that had already completed, which it never tracked and knows nothing else about.
A filter's lastNDays (1 to 7, counting today) and specificDates (within
the last seven days) select days the way a gateway selects them: anything
else asks for no window, a date of 8 or less is dropped, a date outside the
week is dropped and said in the log rather than refused, a date named twice
is one day, and a window that asks for no more than today is answered with
the executions held. A date that does not read as a whole number, or is not a
day of the calendar (the thirty-first of February), refuses the request under
320, as a gateway refuses it while reading it. Days are counted on the
session's time zone. What is answered from is what the venue restated at
logon, which reaches back to midnight six days before the logon in UTC (to
the logon's own day for a session set to today's executions); a gateway asks
the venue for each day the window names. So on a clock east of UTC, or a
session set to today's executions, the earliest days a window asks for can be
missing executions; the days concerned are named on error under 321 ahead
of the answer, which still comes. acctCode is ignored on a login holding
one account, as a gateway ignores it; on a login holding several, one the
login does not hold is refused.
A refused execution request is told so on error. The Rust client sends
nothing else, as a gateway sends nothing else; the Python client also ends it
with execDetailsEnd, as it ends every request it refuses, so a program
waiting on the end is not left waiting.
reqCompletedOrders is not in that position. It asks the venue, which answers
with what it has finished rather than with what this session watched finish,
and the call waits for that answer before handing it back.
Contracts held at a time
Nothing here refuses a contract for want of room. The tables that hold a slot per contract are made for 4,096 and grow past that, and a slot's entry never moves while a caller reads it; a slot handed out reads as an empty quote until its first tick, wherever it falls. What a caller meets is the venue's allowance of quote lines, stated on the logon and counted against open streams: a subscription past it is refused under 101, Max number of tickers has been reached (100 lines on a paper login). The series an option's model asks for beside a caller's — five on each option, one on its underlying — take no line: on a paper login the venue served five hundred of them beside a hundred option lines and refused none. Whether a gateway counts them against its own allowance is not established. Orders, fills, holdings and news each take a slot as well, and nothing bounds how many of those a session holds.
Slots are reused: cancelling a market-data subscription gives its slot back, and a contract nothing holds any more frees its own.
What the company series state about a contract is kept for 4,096 contracts past their subscriptions, the one heard of longest ago making way — a bound of this client's own, so a scanner swept across thousands of contracts is not kept whole for a session.
A recovery attempt outlives the call that stops it, and opens nothing
disconnect stops the engine and returns. An attempt to reopen a connection
that was already dialling when it did is told to stop, and the trading
connection's attempt is stopped as a gateway stops a connection: its socket is
closed from the stopping thread, so a key exchange, an authentication or a
wait for a second factor in flight returns at once rather than waiting out its
timeout. This holds wherever recovery is taken back — a stop, a halt, a spent
budget — not only as the engine ends. What closing the socket cannot cut short
is a dial not yet connected (the host being resolved, the socket being
opened), which reads the flag as soon as its socket exists and opens nothing,
and a logon already written, which is let finish so the session it opens can
be told goodbye rather than dropped. An engine that is ending waits for the
trading attempt at most five seconds, the time a gateway gives a connection's
thread it is stopping; a session the attempt lands after that is told goodbye
where it lands.
Nothing it opens is used. The trading connection's attempt is waited for, within that bound, and a session that landed after the stop is logged out rather than dropped: on this protocol an authenticated session is a session open at the venue, and somebody may have approved a second factor for it. The other three are not waited for. Their socket closes when the receiver that would have taken it is gone, and the engine refuses to install a connection that arrives after the stop.
What the window costs a live login has not been measured. A paper session presents no second factor, so nothing on this account reaches the case where it would.
The protocol
The protocol is not published
This client speaks a protocol the venue does not document and can change without notice. That is the standing risk of not running IB Gateway, and no amount of testing removes it. What the repository does about it is regenerate the coverage matrices from the source on every commit and fail the build when a claim stops matching the code.
Endpoint Coverage
Every call and callback on the canonical list of the TWS API's surface, and
what this client does with each. The table below is generated by scripts/gen_api_docs.py from
the source: CI regenerates it on every push and fails if what is committed has
drifted from the code.
Three things to read it with:
- The status is per call, not per feature.
Ymeans the call is served on that surface.STUBmeans it exists with the expected signature and is not served; for a callback, that it is declared and not fired here although a gateway sends it. What each stub is and why is on Limits. The callbacks that never fire on a gateway are marked in a column of their own, and are on Venue behaviour. - The evidence column is derived. A call is credited to the live session only when a suite that opens a session names it, and to the offline suites only when a test names it. Nothing in that column is hand-maintained, so nothing in it can go quietly out of date — and nothing in it is a promise that the call was exercised in the way you intend to use it.
- Coverage is not correctness. A call that reaches the venue can still be the wrong call for what you are doing. The behaviour notes are in capabilities.md.
For which messages this client speaks, rather than which method names it carries, see Wire Coverage — taken from the dispatch tables and the message builders themselves.
API Coverage Matrix (v0.1.0)
Auto-generated from source — do not edit.
Canonical IB API methods vs ib-dx implementation status.
- Y = Implemented: a call is served; a callback is declared and fired whenever what it reports arrives
- STUB = 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
- - = Not present
The callback table also says whether each callback fires at all for a program on a gateway. Seven declared by the TWS API never do, so they fire neither there nor here.
The evidence column says how each status was established, and is derived rather than asserted: a call is credited to the live session only when a suite that runs against a real account names it, and to the offline suites only when a test names it.
Summary
| IB API | Rust | Python | |
|---|---|---|---|
| EClient methods | 87 | 87 impl, 0 stub | 87 impl, 0 stub |
| EWrapper callbacks | 90 | 88 impl, 2 stub | 88 impl, 2 stub |
EClient Methods
| Category | IB API Method | C++ Name | Rust | Python | Evidence |
|---|---|---|---|---|---|
| Connection | connect | eConnect | Y | Y | Live session |
disconnect | eDisconnect | Y | Y | Live session | |
is_connected | isConnected | Y | Y | Live session | |
start_api | startApi | Y | Y | Offline suites | |
set_server_log_level | setServerLogLevel | Y | Y | Live session | |
req_current_time | reqCurrentTime | Y | Y | Live session | |
req_current_time_in_millis | reqCurrentTimeInMillis | Y | Y | Live session | |
verify_request | verifyRequest | Y | Y | Offline suites | |
verify_message | verifyMessage | Y | Y | Offline suites | |
verify_and_auth_request | verifyAndAuthRequest | Y | Y | Offline suites | |
verify_and_auth_message | verifyAndAuthMessage | Y | Y | Offline suites | |
| Market Data | req_mkt_data | reqMktData | Y | Y | Live session |
cancel_mkt_data | cancelMktData | Y | Y | Live session | |
req_market_data_type | reqMarketDataType | Y | Y | Live session | |
req_tick_by_tick_data | reqTickByTickData | Y | Y | Live session | |
cancel_tick_by_tick_data | cancelTickByTickData | Y | Y | Live session | |
req_mkt_depth | reqMktDepth | Y | Y | Live session | |
cancel_mkt_depth | cancelMktDepth | Y | Y | Live session | |
req_mkt_depth_exchanges | reqMktDepthExchanges | Y | Y | Live session | |
req_smart_components | reqSmartComponents | Y | Y | Live session | |
req_real_time_bars | reqRealTimeBars | Y | Y | Live session | |
cancel_real_time_bars | cancelRealTimeBars | Y | Y | Live session | |
| Historical Data | req_historical_data | reqHistoricalData | Y | Y | Live session |
cancel_historical_data | cancelHistoricalData | Y | Y | Live session | |
req_head_time_stamp | reqHeadTimeStamp | Y | Y | Live session | |
cancel_head_time_stamp | cancelHeadTimestamp | Y | Y | Live session | |
req_historical_ticks | reqHistoricalTicks | Y | Y | Live session | |
cancel_historical_ticks | cancelHistoricalTicks | Y | Y | Offline suites | |
req_histogram_data | reqHistogramData | Y | Y | Live session | |
cancel_histogram_data | cancelHistogramData | Y | Y | Live session | |
req_historical_schedule | reqHistoricalSchedule | Y | Y | Live session | |
| Orders | place_order | placeOrder | Y | Y | Live session |
cancel_order | cancelOrder | Y | Y | Live session | |
req_open_orders | reqOpenOrders | Y | Y | Live session | |
req_all_open_orders | reqAllOpenOrders | Y | Y | Live session | |
req_auto_open_orders | reqAutoOpenOrders | Y | Y | Live session | |
req_ids | reqIds | Y | Y | Live session | |
req_global_cancel | reqGlobalCancel | Y | Y | Live session | |
req_completed_orders | reqCompletedOrders | Y | Y | Live session | |
| Executions | req_executions | reqExecutions | Y | Y | Live session |
| Account | req_account_updates | reqAccountUpdates | Y | Y | Live session |
req_account_summary | reqAccountSummary | Y | Y | Live session | |
cancel_account_summary | cancelAccountSummary | Y | Y | Live session | |
req_positions | reqPositions | Y | Y | Live session | |
cancel_positions | cancelPositions | Y | Y | Live session | |
req_pnl | reqPnL | Y | Y | Live session | |
cancel_pnl | cancelPnL | Y | Y | Live session | |
req_pnl_single | reqPnLSingle | Y | Y | Live session | |
cancel_pnl_single | cancelPnLSingle | Y | Y | Live session | |
req_managed_accts | reqManagedAccts | Y | Y | Live session | |
req_account_updates_multi | reqAccountUpdatesMulti | Y | Y | Live session | |
cancel_account_updates_multi | cancelAccountUpdatesMulti | Y | Y | Live session | |
req_positions_multi | reqPositionsMulti | Y | Y | Live session | |
cancel_positions_multi | cancelPositionsMulti | Y | Y | Live session | |
| Contract | req_contract_details | reqContractDetails | Y | Y | Live session |
cancel_contract_data | cancelContractData | Y | Y | Offline suites | |
req_matching_symbols | reqMatchingSymbols | Y | Y | Live session | |
req_market_rule | reqMarketRule | Y | Y | Live session | |
| Scanner | req_scanner_parameters | reqScannerParameters | Y | Y | Live session |
req_scanner_subscription | reqScannerSubscription | Y | Y | Live session | |
cancel_scanner_subscription | cancelScannerSubscription | Y | Y | Live session | |
| News | req_news_providers | reqNewsProviders | Y | Y | Live session |
req_news_article | reqNewsArticle | Y | Y | Live session | |
req_historical_news | reqHistoricalNews | Y | Y | Live session | |
req_news_bulletins | reqNewsBulletins | Y | Y | Live session | |
cancel_news_bulletins | cancelNewsBulletins | Y | Y | Live session | |
| Fundamental | req_fundamental_data | reqFundamentalData | Y | Y | Live session |
cancel_fundamental_data | cancelFundamentalData | Y | Y | Live session | |
| Options | calculate_implied_volatility | calculateImpliedVolatility | Y | Y | Live session |
cancel_calculate_implied_volatility | cancelCalculateImpliedVolatility | Y | Y | Live session | |
calculate_option_price | calculateOptionPrice | Y | Y | Live session | |
cancel_calculate_option_price | cancelCalculateOptionPrice | Y | Y | Live session | |
exercise_options | exerciseOptions | Y | Y | Live session | |
req_sec_def_opt_params | reqSecDefOptParams | Y | Y | Live session | |
| Reference | req_soft_dollar_tiers | reqSoftDollarTiers | Y | Y | Live session |
req_family_codes | reqFamilyCodes | Y | Y | Live session | |
req_user_info | reqUserInfo | Y | Y | Live session | |
| Financial Advisor | request_fa | requestFA | Y | Y | Offline suites |
replace_fa | replaceFA | Y | Y | Offline suites | |
| Display Groups | query_display_groups | queryDisplayGroups | Y | Y | Live session |
subscribe_to_group_events | subscribeToGroupEvents | Y | Y | Live session | |
unsubscribe_from_group_events | unsubscribeFromGroupEvents | Y | Y | Live session | |
update_display_group | updateDisplayGroup | Y | Y | Live session | |
| WSH | req_wsh_meta_data | reqWshMetaData | Y | Y | Live session |
cancel_wsh_meta_data | cancelWshMetaData | Y | Y | Live session | |
req_wsh_event_data | reqWshEventData | Y | Y | Live session | |
cancel_wsh_event_data | cancelWshEventData | Y | Y | Live session |
EWrapper Callbacks
| Category | Callback | Rust | Python | Fires on a gateway |
|---|---|---|---|---|
| Connection | connect_ack | Y | Y | yes |
connection_closed | Y | Y | yes | |
next_valid_id | Y | Y | yes | |
managed_accounts | Y | Y | yes | |
error | Y | Y | yes | |
current_time | Y | Y | yes | |
current_time_in_millis | Y | Y | yes | |
| Market Data | tick_price | Y | Y | yes |
tick_size | Y | Y | yes | |
tick_string | Y | Y | yes | |
tick_generic | Y | Y | yes | |
tick_snapshot_end | Y | Y | yes | |
market_data_type | Y | Y | yes | |
tick_req_params | Y | Y | yes | |
| Orders | order_status | Y | Y | yes |
open_order | Y | Y | yes | |
open_order_end | Y | Y | yes | |
order_bound | Y | Y | yes | |
| Executions | exec_details | Y | Y | yes |
exec_details_end | Y | Y | yes | |
commission_and_fees_report | Y | Y | yes | |
| Account | update_account_value | Y | Y | yes |
update_portfolio | Y | Y | yes | |
update_account_time | Y | Y | yes | |
account_download_end | Y | Y | yes | |
account_summary | Y | Y | yes | |
account_summary_end | Y | Y | yes | |
position | Y | Y | yes | |
position_end | Y | Y | yes | |
pnl | Y | Y | yes | |
pnl_single | Y | Y | yes | |
position_multi | Y | Y | yes | |
position_multi_end | Y | Y | yes | |
account_update_multi | Y | Y | yes | |
account_update_multi_end | Y | Y | yes | |
| Contract | contract_details | Y | Y | yes |
contract_details_end | Y | Y | yes | |
bond_contract_details | Y | Y | yes | |
symbol_samples | Y | Y | yes | |
| Historical Data | historical_data | Y | Y | yes |
historical_data_end | Y | Y | yes | |
historical_data_update | Y | Y | yes | |
head_timestamp | Y | Y | yes | |
historical_ticks | Y | Y | yes | |
historical_ticks_bid_ask | Y | Y | yes | |
historical_ticks_last | Y | Y | yes | |
histogram_data | Y | Y | yes | |
historical_schedule | Y | Y | yes | |
| Market Depth | update_mkt_depth | Y | Y | yes |
update_mkt_depth_l2 | Y | Y | yes | |
mkt_depth_exchanges | Y | Y | yes | |
| Tick-by-Tick | tick_by_tick_all_last | Y | Y | yes |
tick_by_tick_bid_ask | Y | Y | yes | |
tick_by_tick_mid_point | Y | Y | yes | |
| Scanner | scanner_data | Y | Y | yes |
scanner_data_end | Y | Y | yes | |
scanner_parameters | Y | Y | yes | |
| News | news_providers | Y | Y | yes |
news_article | Y | Y | yes | |
historical_news | Y | Y | yes | |
historical_news_end | Y | Y | yes | |
tick_news | Y | Y | yes | |
update_news_bulletin | Y | Y | yes | |
| Real-Time Bars | real_time_bar | Y | Y | yes |
| Fundamental | fundamental_data | Y | Y | yes |
| Market Rules | market_rule | Y | Y | yes |
| Completed Orders | completed_order | Y | Y | yes |
completed_orders_end | Y | Y | yes | |
| Options | tick_option_computation | Y | Y | yes |
security_definition_option_parameter | Y | Y | yes | |
security_definition_option_parameter_end | Y | Y | yes | |
| Reference | smart_components | Y | Y | yes |
soft_dollar_tiers | Y | Y | yes | |
family_codes | Y | Y | yes | |
user_info | Y | Y | yes | |
| FA | receive_fa | Y | Y | yes |
replace_fa_end | Y | Y | yes | |
| Display Groups | display_group_list | Y | Y | yes |
display_group_updated | Y | Y | yes | |
| Other | delta_neutral_validation | Y | Y | no |
| WSH | wsh_meta_data | Y | Y | yes |
wsh_event_data | Y | Y | yes | |
| Market Data | reroute_mkt_data_req | STUB | STUB | yes |
reroute_mkt_depth_req | STUB | STUB | yes | |
tick_efp | Y | Y | no | |
| Connection | verify_message_api | Y | Y | no |
verify_completed | Y | Y | no | |
verify_and_auth_message_api | Y | Y | no | |
verify_and_auth_completed | Y | Y | no | |
win_error | Y | Y | no |
Wire coverage
Auto-generated from source — do not edit.
Which messages this client speaks. A claim to stand where a gateway stood is a claim about messages rather than about method names, so this is taken from the dispatch tables and the message builders themselves, and CI regenerates it and fails if it has drifted.
Test code is excluded: a fixture that builds a message is not this client sending one.
What this does not establish: a type absent here is one this client neither sends nor handles, which is not the same as one the venue never sends. Settling that needs a record of everything the venue can send.
How it is read: the four connections' dispatch tables, the subtypes written beside the tag that carries them under either of its names, and the constants named as sub-protocols. A subtype reaching the wire by some other shape would be missing from this page, and one has been: the calendar's, passed as a named constant rather than spelled out, which is why constants are read too.
Sent
Message types
| Type | Meaning |
|---|---|
0 | Heartbeat |
1 | Test request |
5 | Logout |
A | Logon |
D | New order |
F | Order cancel |
G | Order replace |
H | Order mass status request |
U | User message |
V | Market data request |
W | Chart request |
Z | Chart cancel |
c | Security definition request |
User-message subtypes
A user message carries what it is for on tag 6040.
| Subtype |
|---|
6 |
27 |
72 |
74 |
76 |
91 |
101 |
106 |
112 |
116 |
138 |
142 |
155 |
185 |
193 |
209 |
10001 |
10003 |
10004 |
10010 |
10011 |
10020 |
10021 |
10030 |
10031 |
Handled
During the logon exchange, before the dispatch tables run
| Type | Meaning |
|---|---|
3 | Session reject |
A | Logon |
U | User message |
On the trading connection
| Type | Meaning |
|---|---|
0 | Heartbeat |
1 | Test request |
3 | Session reject |
8 | Execution report |
9 | Cancel reject |
B | News |
U | User message |
d | Security definition |
AL | Account update, holdings held away |
AP | Position update, held away |
DO | Position update, shown but not held |
DP | Position update, reported apart |
EB | End of a batch |
RL | Account update |
UL | Account update, holdings reported apart |
UM | Account update |
UP | Position update |
UT | Account update |
User-message subtypes on the trading connection
| Subtype |
|---|
7 |
18 |
20 |
36 |
42 |
54 |
60 |
75 |
77 |
81 |
107 |
110 |
117 |
139 |
143 |
152 |
154 |
186 |
192 |
194 |
210 |
278 |
On the market data connection
| Type | Meaning |
|---|---|
0 | Heartbeat |
1 | Test request |
3 | Session reject |
G | Tick payload, binary |
L | Ticker setup |
P | Tick |
Q | Subscription ack |
Y | Subscription reject |
On the historical connection
| Type | Meaning |
|---|---|
0 | Heartbeat |
1 | Test request |
E | Historical payload |
G | Bar payload |
U | User message |
W | Chart response |
User-message subtypes on the historical connection
| Subtype |
|---|
10002 |
10005 |
10012 |
10022 |
10032 |
On the security definition connection
| Type | Meaning |
|---|---|
0 | Heartbeat |
1 | Test request |
3 | Session reject |
U | User message |