ib-dx

ib-dx: direct connection engine for Interactive Brokers ib-dx: direct connection engine for Interactive Brokers

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.

Get started Python recipes Rust recipes GitHub

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 ibapi package 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.

FeatureWhat it adds
pythonThe PyO3 bindings
extension-moduleTells PyO3 not to link libpython. Right for the wheel, wrong for cargo test. maturin sets it; you do not
dev-toolsThe 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.

LanguageSurfacePick it when
Pythonib_dx.EClient / EWrapperYour program is written against ibapi. Change ibapi to ib_dx in its imports, and its connect call to the one above.
RustEClient / WrapperYou 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

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

PythonRust
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.run on a daemon thread.
  • Reading the order id off the next_valid_id callback.

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 Contract from symbol, sec_type, exchange and currency.
  • req_contract_details(req_id, contract) and the contract_details callback.
  • Driving the callback loop on a daemon thread with EClient.run.
  • Reading con_id, primary_exchange and trading_class off the returned ContractDetails.

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 Contract that carries con_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), with format_date, keep_up_to_date and chart_options left 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, where tick_type names 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 foryou receive
100call and put option volume, on tick_size 29 and 30
101call and put open interest, on tick_size 27 and 28
104 512historical volatility, on tick_generic 23
105average option volume, the two sides added, on tick_size 87
106option implied volatility, on tick_generic 24
162the premium of an index over the future written on it, on tick_generic 31
165the day's volume and the 13, 26 and 52-week extremes, on tick_size 21 and tick_price 15 to 20
220the mark the venue keeps, on tick_price 78
221 232the mark, on tick_price 37
225auction volume and imbalance on tick_size 34 and 36, auction price on tick_price 35, the regulatory imbalance on tick_size 61
233the trade tape, on tick_string 48: price;size;time;volume;vwap;single
236shortability on tick_generic 46, and the borrowable share count on tick_size 89
258company ratios, on tick_string 47
292 292:BRFG+DJNLnews, on tick_news
293 294 295trade count, trade rate and volume rate, on tick_generic 54, 55 and 56
318last regular-session trade, on tick_price 57
375the trade-report tape, on tick_string 77
411real-time historical volatility, on tick_generic 58
456what the contract pays out, on tick_string 59
460bond factor multiplier, on tick_generic 60
499borrow fee rate, on tick_price 111
577 623an ETF's net asset value, last and frozen, on tick_price 96 and 97
586the estimated IPO midpoint on tick_generic 101, and what it opened at on 102
588futures open interest, on tick_size 86
595the 3, 5 and 10-minute volumes, on tick_size 63, 64 and 65
614an ETF's net asset value high and low, on tick_price 98 and 99
619the slow mark, on tick_price 79
787the 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_l2 events 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:

fieldvalues
operation0 insert, 1 update, 2 delete
side0 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), where subscription is any object carrying the right attributes. The example defines a small class; the reference client's own ScannerSubscription works too.
  • Reading scanner_data rows, each a rank and a ContractDetails, until scanner_data_end.
  • cancel_scanner_subscription(req_id) before disconnecting.

What the subscription object is read for

The scan itself:

attributeabsent means
instrumentempty
locationCodeempty
scanCodeempty
numberOfRowsthe 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_pnl and realized_pnl off the pnl callback.
  • 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, which connect() fires before it returns. c.next_order_id() reserves one directly if you would rather ask.
  • An Order with order_type = "LMT" and lmt_price set. outside_rth = True is what lets it be acknowledged outside regular hours.
  • order_status callbacks. PreSubmitted then Submitted while it rests, Cancelled once the cancel lands.
  • cancel_order(order_id, order_cancel). The second argument is what the withdrawal states about itself: an OrderCancel (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 on error; 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, which connect() fires before it returns.
  • An Order with order_type = "STP" and the trigger price in aux_price. lmt_price is unused for a plain stop.
  • tif = "GTC", so it survives the close rather than expiring with the day.
  • order_status callbacks: PreSubmitted then Submitted while it rests, Cancelled once 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_id and 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 Submitted off order_status.
  • req_open_orders(), and finding the order in the open_order callbacks.
  • 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 Cancelled terminal 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: true for the paper account, paper: false for 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 Contract from symbol, sec_type, exchange and currency.
  • req_contract_details(req_id, &contract), and reading answers off the contract_details callback.
  • Pumping the callbacks with process_msgs until contract_details_end fires.
  • Reading con_id, primary_exchange and trading_class out of ContractDetails.

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 Contract that carries con_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_msgs until historical_data_end lands, 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, where tick_type names 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 foryou receive
100call and put option volume, on tick_size 29 and 30
101call and put open interest, on tick_size 27 and 28
104 512historical volatility, on tick_generic 23
105average option volume, the two sides added, on tick_size 87
106option implied volatility, on tick_generic 24
162the premium of an index over the future written on it, on tick_generic 31
165the day's volume and the 13, 26 and 52-week extremes, on tick_size 21 and tick_price 15 to 20
220the mark the venue keeps, on tick_price 78
221 232the mark, on tick_price 37
225auction volume and imbalance on tick_size 34 and 36, auction price on tick_price 35, the regulatory imbalance on tick_size 61
233the trade tape, on tick_string 48: price;size;time;volume;vwap;single
236shortability on tick_generic 46, and the borrowable share count on tick_size 89
258company ratios, on tick_string 47
292 292:BRFG+DJNLnews, on tick_news
293 294 295trade count, trade rate and volume rate, on tick_generic 54, 55 and 56
318last regular-session trade, on tick_price 57
375the trade-report tape, on tick_string 77
411real-time historical volatility, on tick_generic 58
456what the contract pays out, on tick_string 59
460bond factor multiplier, on tick_generic 60
499borrow fee rate, on tick_price 111
577 623an ETF's net asset value, last and frozen, on tick_price 96 and 97
586the estimated IPO midpoint on tick_generic 101, and what it opened at on 102
588futures open interest, on tick_size 86
595the 3, 5 and 10-minute volumes, on tick_size 63, 64 and 65
614an ETF's net asset value high and low, on tick_price 98 and 99
619the slow mark, on tick_price 79
787the 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_l2 events 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:

fieldvalues
operation0 insert, 1 update, 2 delete
side0 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), with scanner_setting_pairs "" when there are none.
  • Filters as TagValue pairs. The tag names are the ones req_scanner_parameters publishes, for example priceAbove = "5" or stkTypes = "inc:ETF". They are carried to the venue, not dropped.
  • Reading scanner_data rows, each a rank and a ContractDetails, until scanner_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, which connect populates.
  • req_pnl(req_id, account, model_code).
  • Reading daily_pnl, unrealized_pnl and realized_pnl off the pnl callback.
  • 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 Order with order_type: "LMT" and lmt_price set. outside_rth: true is what lets it be acknowledged outside regular hours.
  • order_status callbacks. PreSubmitted then Submitted while it rests, Cancelled once the cancel lands.
  • cancel_order(order_id, order_cancel). The second argument is what the withdrawal states about itself: an OrderCancel (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 on error; 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 Order with order_type: "STP" and the trigger price in aux_price. lmt_price is unused for a plain stop.
  • tif: "GTC", so it survives the close rather than expiring with the day.
  • order_status callbacks: PreSubmitted then Submitted while it rests, Cancelled once 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

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)
ParameterTypeDescription
wrapperPy<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)
ParameterTypeDescription
hoststrServer hostname.
portintPort number (unused — ib-dx connects directly).
client_idintAPI client ID for order ownership and the saved order-id counter.
usernamestrAccount username.
passwordstrAccount password.
paperboolIf true, connect to paper trading. If false, connect blocks on the live second-factor approval window (see method note).
core_idusize or NoneCPU core affinity for the hot loop thread. Use a distinct value per engine when running several in one process.
ib_key_timeout_secsint or NoneLive 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_typestr or NoneFallback second-factor token sub-type (default "2a"), used only when the server states none for the session; ignored for paper.
code_providerPy<PyAny> or NoneCallable (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.
readonlybool

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)
ParameterTypeDescription
optsstrConnect 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)
ParameterTypeDescription
opt_capabstr

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)
ParameterTypeDescription
orderOrderOrder 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)
ParameterTypeDescription
attached_ordersOrderOrder 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)
ParameterTypeDescription
hoststrServer 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)
ParameterTypeDescription
originErrorOriginWhat 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.
codeint
msgstr

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)
ParameterTypeDescription
timeoutfloatThe 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)
ParameterTypeDescription
con_idintContract 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)
ParameterTypeDescription
keystrAccount 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)
ParameterTypeDescription
contractContractContract 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)
ParameterTypeDescription
contractContractContract specification (symbol, secType, exchange, currency, etc.).
start_datestr
end_datestr

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)
ParameterTypeDescription
contractContractContract specification (symbol, secType, exchange, currency, etc.).
end_date_timestrEnd date/time in "YYYYMMDD HH:MM:SS" format, or empty for now.
duration_strstrDuration string, e.g. "1 D", "1 W", "1 M", "1 Y".
bar_size_settingstrBar size: "1 min", "5 mins", "1 hour", "1 day", etc.
what_to_showstrData type: "TRADES", "MIDPOINT", "BID", "ASK", "BID_ASK", etc.
use_rthintIf 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)
ParameterTypeDescription
contractContractContract specification (symbol, secType, exchange, currency, etc.).
what_to_showstrData type: "TRADES", "MIDPOINT", "BID", "ASK", "BID_ASK", etc.
use_rthintIf true, only return data from Regular Trading Hours.

matching_symbols

Contracts whose symbol or name matches a pattern.

def matching_symbols(pattern)
ParameterTypeDescription
patternstrSymbol 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)
ParameterTypeDescription
con_idintContract ID. Unique per instrument.
provider_codesstrPipe-separated news provider codes.
start_date_timestrStart date/time for tick query.
end_date_timestrEnd date/time in "YYYYMMDD HH:MM:SS" format, or empty for now.
total_resultsintMaximum 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)
ParameterTypeDescription
contractContractContract specification (symbol, secType, exchange, currency, etc.).
end_date_timestrEnd date/time in "YYYYMMDD HH:MM:SS" format, or empty for now.
duration_strstrDuration string, e.g. "1 D", "1 W", "1 M", "1 Y".
use_rthboolIf 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)
ParameterTypeDescription
underlying_symbolstrUnderlying symbol (e.g. "AAPL").
fut_fop_exchangestrExchange for futures/FOP options.
underlying_sec_typestrUnderlying security type (e.g. "STK").
underlying_con_idintUnderlying 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")
ParameterTypeDescription
contractContractContract specification (symbol, secType, exchange, currency, etc.).
use_rthboolIf true, only return data from Regular Trading Hours.
time_periodstrHistogram time period.

fundamental_data

A fundamental report on a contract, as the venue supplies it.

def fundamental_data(contract, report_type)
ParameterTypeDescription
contractContractContract specification (symbol, secType, exchange, currency, etc.).
report_typestrReport 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)
ParameterTypeDescription
contractContractContract specification (symbol, secType, exchange, currency, etc.).
orderOrderOrder 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)
ParameterTypeDescription
instrumentstrInstrument type for scanner (e.g. "STK", "FUT").
location_codestrScanner location (e.g. "STK.US.MAJOR").
scan_codestrScanner code (e.g. "TOP_PERC_GAIN", "HIGH_OPT_IMP_VOLAT").
mostintMaximum 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)
ParameterTypeDescription
con_idintContract 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)
ParameterTypeDescription
contractContractContract 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)
ParameterTypeDescription
contractslistContract 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="")
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
accountstrAccount ID.
model_codestrModel portfolio code (empty for default).

cancel_pnl

Cancel P&L subscription.

def cancel_pnl(req_id)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
accountstrAccount ID.
model_codestrModel portfolio code (empty for default).
con_idintContract ID. Unique per instrument.

cancel_pnl_single

Cancel single-position P&L subscription.

def cancel_pnl_single(req_id)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
group_namestrAccount group name (e.g. "All").
tagsstrComma-separated account tags: "NetLiquidation,BuyingPower,...".

cancel_account_summary

Cancel account summary.

def cancel_account_summary(req_id)
ParameterTypeDescription
req_idintRequest 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="")
ParameterTypeDescription
subscribebooltrue to start updates, false to stop.
acct_codestrAccount 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
accountstrAccount ID.
model_codestrModel portfolio code (empty for default).
ledger_and_nlvboolIf 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)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
accountstrAccount ID.
model_codestrModel portfolio code (empty for default).

cancel_positions_multi

Cancel multi-account positions.

def cancel_positions_multi(req_id)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
heldstrWhich 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)
ParameterTypeDescription
order_idintOrder identifier. Must be unique per session.
contractContractContract specification (symbol, secType, exchange, currency, etc.).
orderOrderOrder 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
contractContractContract specification (symbol, secType, exchange, currency, etc.).
exercise_actionint1=exercise, 2=lapse.
exercise_quantityintNumber of contracts to exercise.
accountstrAccount ID.
overrideintOverride flag for exercise.
manual_order_timestr
customer_accountstr
professional_customerbool

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)
ParameterTypeDescription
order_idintOrder identifier. Must be unique per session.
order_cancelPy<PyAny> or NoneWhat 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)
ParameterTypeDescription
order_cancelPy<PyAny> or NoneWhat 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)
ParameterTypeDescription
num_idsintNumber 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)
ParameterTypeDescription
b_auto_bindboolIf 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
exec_filterPy<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)
ParameterTypeDescription
api_onlybool

Market Data

set_news_providers

Set news provider codes for per-contract news ticks (e.g. "BRFG*BRFUPDN").

def set_news_providers(providers)
ParameterTypeDescription
providersstrNews 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
contractContractContract specification (symbol, secType, exchange, currency, etc.).
generic_tick_liststrComma-separated generic tick IDs (e.g. "233" for RT volume).
snapshotboolIf true, delivers one quote then auto-cancels.
regulatory_snapshotboolIf true, request a regulatory snapshot (additional fees may apply).
mkt_data_optionslist

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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
contractContractContract specification (symbol, secType, exchange, currency, etc.).
generic_tick_liststrComma-separated generic tick IDs (e.g. "233" for RT volume).
snapshotboolIf true, delivers one quote then auto-cancels.
regulatory_snapshotboolIf true, request a regulatory snapshot (additional fees may apply).
mode_9887int
mkt_data_optionslist

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)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
contractContractContract specification (symbol, secType, exchange, currency, etc.).
tick_typestrTick type ID or tick-by-tick type string.
number_of_ticksintMaximum number of ticks to return.
ignore_sizeboolIf 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)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
market_data_typeint1=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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
contractContractContract specification (symbol, secType, exchange, currency, etc.).
num_rowsintNumber of order book rows to subscribe to.
is_smart_depthboolIf true, aggregate depth from multiple exchanges via SMART.
mkt_depth_optionslist

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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
is_smart_depthboolIf 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
contractContractContract specification (symbol, secType, exchange, currency, etc.).
bar_sizeintBar size: "1 min", "5 mins", "1 hour", "1 day", etc.
what_to_showstrData type: "TRADES", "MIDPOINT", "BID", "ASK", "BID_ASK", etc.
use_rthintIf true, only return data from Regular Trading Hours.
real_time_bars_optionslist

cancel_real_time_bars

Cancel real-time bars.

def cancel_real_time_bars(req_id)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
instrumentintInstrument 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)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
instrumentintInstrument 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)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
instrumentintInstrument 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
seriesint

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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
seriesint
fractionalbool

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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
seriesint

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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
contractContractContract specification (symbol, secType, exchange, currency, etc.).
scanSpreadScanWhat 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)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
seriesint

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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
seriesint

stated_rows_series

Which series have stated rows for a subscription, in order.

def stated_rows_series(req_id)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
instrumentintInstrument 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)
ParameterTypeDescription
instrumentintInstrument 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
contractContractContract specification (symbol, secType, exchange, currency, etc.).
end_date_timestrEnd date/time in "YYYYMMDD HH:MM:SS" format, or empty for now.
duration_strstrDuration string, e.g. "1 D", "1 W", "1 M", "1 Y".
bar_size_settingstrBar size: "1 min", "5 mins", "1 hour", "1 day", etc.
what_to_showstrData type: "TRADES", "MIDPOINT", "BID", "ASK", "BID_ASK", etc.
use_rthintIf true, only return data from Regular Trading Hours.
format_dateintDate format: 1="YYYYMMDD HH:MM:SS", 2=Unix seconds.
keep_up_to_dateboolIf true, continue receiving updates after initial history.
chart_optionslist

cancel_historical_data

Cancel historical data.

def cancel_historical_data(req_id)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
contractContractContract specification (symbol, secType, exchange, currency, etc.).
what_to_showstrData type: "TRADES", "MIDPOINT", "BID", "ASK", "BID_ASK", etc.
use_rthintIf true, only return data from Regular Trading Hours.
format_dateintDate format: 1="YYYYMMDD HH:MM:SS", 2=Unix seconds.

cancel_head_time_stamp

Cancel head timestamp request.

def cancel_head_time_stamp(req_id)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.

req_contract_details

Request contract details.

def req_contract_details(req_id, contract)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
contractContractContract 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)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
patternstrSymbol 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
underlying_symbolstrUnderlying symbol (e.g. "AAPL").
fut_fop_exchangestrExchange for futures/FOP options.
underlying_sec_typestrUnderlying security type (e.g. "STK").
underlying_con_idintUnderlying 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
subscriptionPy<PyAny>Scanner subscription parameters.
scanner_subscription_optionslist
scanner_subscription_filter_optionslistScanner filter tags from req_scanner_parameters, e.g. priceAbove = "10".

cancel_scanner_subscription

Cancel scanner subscription.

def cancel_scanner_subscription(req_id)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
provider_codestrNews provider code (e.g. "BRFG").
article_idstrNews article identifier.
news_article_optionslist

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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
con_idintContract ID. Unique per instrument.
provider_codesstrPipe-separated news provider codes.
start_date_timestrStart date/time for tick query.
end_date_timestrEnd date/time in "YYYYMMDD HH:MM:SS" format, or empty for now.
total_resultsintMaximum number of news results.
historical_news_optionslist

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)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
con_idintContract ID. Unique per instrument.
sec_typestr
exchangestrExchange name.
start_datestr
end_datestr

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)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
contractContractContract specification (symbol, secType, exchange, currency, etc.).
report_typestrReport type: "ReportSnapshot", "ReportsFinSummary", "RESC", etc.
fundamental_data_optionslist

cancel_fundamental_data

Cancel fundamental data.

def cancel_fundamental_data(req_id)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
contractContractContract specification (symbol, secType, exchange, currency, etc.).
start_date_timestrStart date/time for tick query.
end_date_timestrEnd date/time in "YYYYMMDD HH:MM:SS" format, or empty for now.
number_of_ticksintMaximum number of ticks to return.
what_to_showstrData type: "TRADES", "MIDPOINT", "BID", "ASK", "BID_ASK", etc.
use_rthintIf true, only return data from Regular Trading Hours.
ignore_sizeboolIf true, asks that a bid/ask change moving only a size be left out.
misc_optionslist

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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.

req_market_rule

Request market rule details.

def req_market_rule(market_rule_id)
ParameterTypeDescription
market_rule_idintMarket rule ID.

req_histogram_data

Request histogram data.

def req_histogram_data(req_id, contract, use_rth, time_period)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
contractContractContract specification (symbol, secType, exchange, currency, etc.).
use_rthboolIf true, only return data from Regular Trading Hours.
time_periodstrHistogram time period.

cancel_histogram_data

Cancel histogram data.

def cancel_histogram_data(req_id)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
contractContractContract specification (symbol, secType, exchange, currency, etc.).
end_date_timestrEnd date/time in "YYYYMMDD HH:MM:SS" format, or empty for now.
duration_strstrDuration string, e.g. "1 D", "1 W", "1 M", "1 Y".
use_rthboolIf 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)
ParameterTypeDescription
config_request_protoBound<'_, 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)
ParameterTypeDescription
update_config_request_protoBound<'_, 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)
ParameterTypeDescription
sec_typestr

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)
ParameterTypeDescription
sec_typestr

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)
ParameterTypeDescription
con_idintContract ID. Unique per instrument.
seriesint

company_data_series

Which of those series have been stated for a contract.

def company_data_series(con_id)
ParameterTypeDescription
con_idintContract 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
contractContractContract specification (symbol, secType, exchange, currency, etc.).
option_pricefloatOption market price.
under_pricefloatUnderlying asset price.
implied_vol_optionslist

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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
contractContractContract specification (symbol, secType, exchange, currency, etc.).
volatilityfloatImplied volatility.
under_pricefloatUnderlying asset price.
opt_prc_optionslist

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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.

cancel_calculate_option_price

As for cancel_calculate_implied_volatility.

def cancel_calculate_option_price(req_id)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
all_msgsboolIf 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)
ParameterTypeDescription
fa_data_typeintFA 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
fa_data_typeintFA data type (1=Groups, 2=Profiles, 3=Aliases).
cxmlstrFA XML configuration data.

query_display_groups

Ask which display groups exist. Answered on display_group_list.

def query_display_groups(req_id)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
group_idintDisplay group ID.

unsubscribe_from_group_events

Stop watching a display group.

def unsubscribe_from_group_events(req_id)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
contract_infostrDisplay 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)
ParameterTypeDescription
api_namestr
api_versionstr

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)
ParameterTypeDescription
api_namestr
api_versionstr
opaque_isv_keystr

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)
ParameterTypeDescription
api_datastr

verify_and_auth_message

As verify_message: api_data and xyz_response reach nothing.

def verify_and_auth_message(api_data, xyz_response)
ParameterTypeDescription
api_datastr
xyz_responsestr

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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
bbo_exchangestrBBO 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)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
log_levelintA 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)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest 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)
ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
wsh_event_dataPy<PyAny> or None

EWrapper Callbacks

new

Create a new EClient (or EWrapper) instance.

ParameterTypeDescription
argsBound<'_, pyo3::types::PyTuple>
kwargsBound<'_, 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.

ParameterTypeDescription
order_idintOrder 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.

ParameterTypeDescription
accounts_liststrComma-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.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
error_timeintWhen 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_codeintError code.
error_stringstrError message.
advanced_order_reject_jsonstrJSON 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.

ParameterTypeDescription
originErrorOriginWhat 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_timeintWhen 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_codeintError code.
error_stringstrError message.
advanced_order_reject_jsonstrJSON with advanced rejection details.

current_time

The venue clock, in seconds since the epoch.

ParameterTypeDescription
timeintTick 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.

ParameterTypeDescription
time_in_millisint

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.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
tick_typeintTick type ID or tick-by-tick type string.
pricefloatTick price.
attribPy<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.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
tick_typeintTick type ID or tick-by-tick type string.
sizePy<PyAny>Tick size.

tick_string

A quote's value that is not a number — a timestamp, an exchange map, a set of ids.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
tick_typeintTick type ID or tick-by-tick type string.
valuestrAccount 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.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
tick_typeintTick type ID or tick-by-tick type string.
valuefloatAccount 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.

ParameterTypeDescription
req_idintRequest 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.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
market_data_typeint1=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.

ParameterTypeDescription
order_idintOrder identifier. Must be unique per session.
statusstrOrder status string ("Submitted", "Filled", "Cancelled", etc.).
filledPy<PyAny>Cumulative filled quantity.
remainingPy<PyAny>Remaining quantity.
avg_fill_pricefloatAverage fill price.
perm_idintThe order's permanent id: the number it goes to the venue under.
parent_idintParent order ID (0 if no parent).
last_fill_pricefloatPrice of the last fill.
client_idintAPI client ID for order ownership and the saved order-id counter.
why_heldstrReason the order is held (e.g. "locate").
mkt_cap_pricefloatMarket 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.

ParameterTypeDescription
order_idintOrder identifier. Must be unique per session.
contractPy<PyAny>Contract specification (symbol, secType, exchange, currency, etc.).
orderPy<PyAny>Order parameters (action, quantity, type, price, TIF, etc.).
order_statePy<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.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
contractPy<PyAny>Contract specification (symbol, secType, exchange, currency, etc.).
executionPy<PyAny>Execution details (exec_id, time, price, shares, etc.).

exec_details_end

Every execution answering this request has been stated.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.

commission_and_fees_report

What a fill cost, matched to it by execution id.

ParameterTypeDescription
commission_and_fees_reportPy<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.

ParameterTypeDescription
keystrAccount value key (e.g. "NetLiquidation", "BuyingPower").
valuestrAccount value.
currencystrCurrency code (e.g. "USD").
account_namestrAccount identifier.

update_portfolio

One position, as the venue values it now.

ParameterTypeDescription
contractPy<PyAny>Contract specification (symbol, secType, exchange, currency, etc.).
positionPy<PyAny>Book position (row index) or position size.
market_pricefloatCurrent market price.
market_valuefloatCurrent market value of position.
average_costfloatAverage cost basis.
unrealized_pnlfloatUnrealized profit/loss.
realized_pnlfloatRealized profit/loss.
account_namestrAccount identifier.

update_account_time

When the account figures above were last stated.

ParameterTypeDescription
timestampstrTimestamp string.

account_download_end

The account has been fully stated. Fires once the venue has stopped adding to it, not on the first figure.

ParameterTypeDescription
accountstrAccount ID.

account_summary

One figure answering req_account_summary, in the currency the venue states it in.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
accountstrAccount ID.
tagstrAccount tag name (e.g. "NetLiquidation").
valuestrAccount value.
currencystrCurrency code (e.g. "USD").

account_summary_end

Every figure answering this request has been stated.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.

position

One position held, on any account this login may act for.

ParameterTypeDescription
accountstrAccount ID.
contractPy<PyAny>Contract specification (symbol, secType, exchange, currency, etc.).
posPy<PyAny>Position size (decimal shares).
avg_costfloatAverage 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.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
daily_pnlfloatDaily profit/loss.
unrealized_pnlfloatUnrealized profit/loss.
realized_pnlfloatRealized profit/loss.

pnl_single

The same for one position, with the size held.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
posPy<PyAny>Position size (decimal shares).
daily_pnlfloatDaily profit/loss.
unrealized_pnlfloatUnrealized profit/loss.
realized_pnlfloatRealized profit/loss.
valuefloatAccount 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.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
barPy<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.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
startstrPeriod start date/time.
endstrPeriod 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.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
barPy<PyAny>Bar data (date, open, high, low, close, volume, wap, bar_count).

head_timestamp

The earliest moment the venue holds data for a contract.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
head_timestampstrEarliest 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.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
contract_detailsPy<PyAny>

contract_details_end

Every contract matching this request has been stated.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.

symbol_samples

Contracts whose symbol or name matches a pattern, across venues.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
contract_descriptionsPy<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.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
tick_typeintTick type ID or tick-by-tick type string.
timeintTick timestamp (Unix seconds).
pricefloatTick price.
sizePy<PyAny>Tick size.
tick_attrib_lastPy<PyAny>
exchangestrExchange name.
special_conditionsstrSpecial trade conditions.

tick_by_tick_bid_ask

One change to the top of the book, as it happens.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
timeintTick timestamp (Unix seconds).
bid_pricefloatBid price.
ask_pricefloatAsk price.
bid_sizePy<PyAny>Bid size.
ask_sizePy<PyAny>Ask size.
tick_attrib_bid_askPy<PyAny>

tick_by_tick_mid_point

One change to the midpoint, as it happens.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
timeintTick timestamp (Unix seconds).
mid_pointfloatMidpoint price.

scanner_data

One row of a scan, in rank order.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
rankintScanner result rank (0-based).
contract_detailsPy<PyAny>
distancestrScanner distance metric.
benchmarkstrScanner benchmark.
projectionstrScanner projection.
legs_strstrCombo legs description.

scanner_data_end

Every row of this scan has been stated.

ParameterTypeDescription
req_idintRequest 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.

ParameterTypeDescription
xmlstrXML string.

news_providers

Every news provider this account may read.

ParameterTypeDescription
news_providersPy<PyAny>

news_article

The body of one article. article_type is 0 for text and 1 for a binary document.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
article_typeintArticle type: 0=plain text, 1=HTML.
article_textstrFull article body.

historical_news

One headline from the archive.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
timestrTick timestamp (Unix seconds).
provider_codestrNews provider code (e.g. "BRFG").
article_idstrNews article identifier.
headlinestrNews headline text.

historical_news_end

Every headline answering this request has been stated, and whether the archive holds more.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
has_moreboolIf true, more results available.

tick_news

A headline about a contract being watched, as it is published.

ParameterTypeDescription
ticker_idintTicker/request ID.
time_stampintTimestamp string.
provider_codestrNews provider code (e.g. "BRFG").
article_idstrNews article identifier.
headlinestrNews headline text.
extra_datastrAdditional 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.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
positionintBook position (row index) or position size.
operationintBook operation: 0=insert, 1=update, 2=delete.
sideintBook side: 0=ask, 1=bid. Or order side "BOT"/"SLD".
pricefloatTick price.
sizePy<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.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
positionintBook position (row index) or position size.
market_makerstrMarket maker ID.
operationintBook operation: 0=insert, 1=update, 2=delete.
sideintBook side: 0=ask, 1=bid. Or order side "BOT"/"SLD".
pricefloatTick price.
sizePy<PyAny>Tick size.
is_smart_depthboolIf 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.

ParameterTypeDescription
depth_mkt_data_descriptionsPy<PyAny>

real_time_bar

One five-second bar of a live stream.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
dateintBar date string.
openfloatOpen price.
highfloatHigh price.
lowfloatLow price.
closefloatClose price.
volumePy<PyAny>Volume.
wapPy<PyAny>Volume-weighted average price.
countintTrade count.

historical_ticks

Historical midpoints, in batches, until done.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
ticksPy<PyAny>Historical tick data.
doneboolIf true, all ticks have been delivered.

historical_ticks_bid_ask

Historical quotes, in batches, until done.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
ticksPy<PyAny>Historical tick data.
doneboolIf true, all ticks have been delivered.

historical_ticks_last

Historical trades, in batches, until done.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
ticksPy<PyAny>Historical tick data.
doneboolIf 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.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
tick_typeintTick type ID or tick-by-tick type string.
tick_attribint
implied_volfloat or NoneImplied volatility.
deltafloat or NoneOption delta.
opt_pricefloat or NoneOption theoretical price.
pv_dividendfloat or NonePresent value of dividends.
gammafloat or NoneOption gamma.
vegafloat or NoneOption vega.
thetafloat or NoneOption theta.
und_pricefloat or NoneUnderlying 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.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
exchangestrExchange name.
underlying_con_idintUnderlying contract ID.
trading_classstrTrading class.
multiplierstrContract multiplier.
expirationsPy<PyAny>Available expiration dates.
strikesPy<PyAny>Available strike prices.

security_definition_option_parameter_end

Every venue's chain has been stated.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.

fundamental_data

A fundamental report, as the XML the venue publishes.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
datastrRaw data string (XML/JSON).

update_news_bulletin

A notice the venue broadcasts to everyone — an exchange unavailable, a system message.

ParameterTypeDescription
msg_idintBulletin message ID.
msg_typeintBulletin message type (1=regular, 2=exchange).
messagestrBulletin message text.
orig_exchangestrOriginating exchange.

receive_fa

A partition of an advisor's configuration, as the XML the venue holds it in.

ParameterTypeDescription
fa_data_typeintFA data type (1=Groups, 2=Profiles, 3=Aliases).
xmlstrXML string.

replace_fa_end

An advisor configuration has been replaced.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
textstrInformational text.

position_multi

One position, for a request naming an account or a model.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
accountstrAccount ID.
model_codestrModel portfolio code (empty for default).
contractPy<PyAny>Contract specification (symbol, secType, exchange, currency, etc.).
posPy<PyAny>Position size (decimal shares).
avg_costfloatAverage cost per share.

position_multi_end

Every position answering this request has been stated.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.

account_update_multi

One account figure, for a request naming an account or a model.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
accountstrAccount ID.
model_codestrModel portfolio code (empty for default).
keystrAccount value key (e.g. "NetLiquidation", "BuyingPower").
valuestrAccount value.
currencystrCurrency code (e.g. "USD").

account_update_multi_end

Every figure answering this request has been stated.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.

display_group_list

Which display groups exist, as the venue numbers them.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
groupsstrFA group definitions.

display_group_updated

What a display group is now showing.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
contract_infostrDisplay group contract info string.

market_rule

The price ladder a contract trades on: each step, and what the price moves in above it.

ParameterTypeDescription
market_rule_idintMarket rule ID.
price_incrementsPy<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.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
smart_component_mapPy<PyAny>

soft_dollar_tiers

The soft dollar tiers this account may direct commission to.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
tiersPy<PyAny>Soft dollar tier list.

family_codes

The account families this login belongs to.

ParameterTypeDescription
family_codesPy<PyAny>

histogram_data

How much traded at each price over a window.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
itemsPy<PyAny>Histogram entries [(price, count)].

user_info

What the login is entitled to, as the venue states it.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
white_branding_idstrWhite 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.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
data_jsonstr

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.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
data_jsonstr

completed_order

An order that is done — filled, cancelled or expired — as the venue holds it.

ParameterTypeDescription
contractPy<PyAny>Contract specification (symbol, secType, exchange, currency, etc.).
orderPy<PyAny>Order parameters (action, quantity, type, price, TIF, etc.).
order_statePy<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.

ParameterTypeDescription
order_idintOrder identifier. Must be unique per session.
api_client_idint
api_order_idint

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.

ParameterTypeDescription
ticker_idintTicker/request ID.
min_tickfloatMinimum tick size.
bbo_exchangestrBBO exchange for smart component lookup (e.g. "SMART").
snapshot_permissionsintWhat 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.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
contract_detailsPy<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.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
con_idintContract ID. Unique per instrument.
exchangestrExchange name.

reroute_mkt_depth_req

The same, for a request for the book rather than the quote.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
con_idintContract ID. Unique per instrument.
exchangestrExchange 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.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
delta_neutral_contractPy<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.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
tick_typeintTick type ID or tick-by-tick type string.
basis_pointsfloat
formatted_basis_pointsstr
implied_futurefloat
hold_daysint
future_last_trade_datestr
dividend_impactfloat
dividends_to_last_trade_datefloat

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.

ParameterTypeDescription
api_datastr

verify_completed

Whether that handshake was accepted.

ParameterTypeDescription
is_successfulbool
error_textstr

verify_and_auth_message_api

The same handshake, where the terminal also authenticates the program.

ParameterTypeDescription
api_datastr
xyz_challengestr

verify_and_auth_completed

Whether that one was accepted.

ParameterTypeDescription
is_successfulbool
error_textstr

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.

ParameterTypeDescription
textstrInformational text.
last_errorint

historical_schedule

When a contract's venue was open over a window, session by session, in the zone the venue keeps.

ParameterTypeDescription
req_idintRequest identifier. Used to match responses to requests.
start_date_timestrStart date/time for tick query.
end_date_timestrEnd date/time in "YYYYMMDD HH:MM:SS" format, or empty for now.
time_zonestrTimezone string (e.g. "US/Eastern").
sessionsPy<PyAny>Trading sessions [(ref_date, open, close)].

Rust API

TypeWhat it is
ib_dx::EClient / ib_dx::WrapperThe reference client's shape: a request under an id, and an answer later on a callback you implement
ib_dx::ConfigWhat 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

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>>
ParameterTypeDescription
config&EClientConfigConnection 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>>
ParameterTypeDescription
config&EClientConfigConnection configuration (username, password, host, paper, client_id, core_id).
capacityusize

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)
ParameterTypeDescription
origincrate::types::model::ErrorOriginWhat 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.
codei64
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
ParameterTypeDescription
timeoutstd::time::DurationThe 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>>)
ParameterTypeDescription
hookOption<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>>)
ParameterTypeDescription
recordArc<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>
ParameterTypeDescription
con_idi64Contract 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>)>
ParameterTypeDescription
con_id&strContract 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>
ParameterTypeDescription
key&strAccount 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>
ParameterTypeDescription
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
end_date_time&strEnd date/time in "YYYYMMDD HH:MM:SS" format, or empty for now.
duration&strDuration string, e.g. "1 D", "1 W", "1 M", "1 Y".
bar_size&strBar size: "1 min", "5 mins", "1 hour", "1 day", etc.
what_to_show&strData type: "TRADES", "MIDPOINT", "BID", "ASK", "BID_ASK", etc.
use_rthboolIf 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>
ParameterTypeDescription
contract&ContractContract 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>
ParameterTypeDescription
underlying&ContractContract 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>
ParameterTypeDescription
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
what_to_show&strData type: "TRADES", "MIDPOINT", "BID", "ASK", "BID_ASK", etc.
use_rthboolIf 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>
ParameterTypeDescription
pattern&strSymbol 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>
ParameterTypeDescription
con_idi64Contract ID. Unique per instrument.
provider_codes&strPipe-separated news provider codes.
start_date_time&strStart date/time for tick query.
end_date_time&strEnd date/time in "YYYYMMDD HH:MM:SS" format, or empty for now.
total_resultsi32Maximum 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>
ParameterTypeDescription
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
use_rthboolIf true, only return data from Regular Trading Hours.
period&strHistogram 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>
ParameterTypeDescription
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
report_type&strReport 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>
ParameterTypeDescription
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
order&crate::types::model::OrderOrder 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>
ParameterTypeDescription
tags&strComma-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>
ParameterTypeDescription
order_idi64Order identifier. Must be unique per session.
timeoutDurationThe 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>
ParameterTypeDescription
contract&ContractContract 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>
ParameterTypeDescription
contract&ContractContract 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>
ParameterTypeDescription
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>
ParameterTypeDescription
instrument&strInstrument type for scanner (e.g. "STK", "FUT").
location&str
scan_code&strScanner code (e.g. "TOP_PERC_GAIN", "HIGH_OPT_IMP_VOLAT").
mostu32Maximum 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>
ParameterTypeDescription
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
duration&strDuration 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>
ParameterTypeDescription
con_idi64Contract 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)
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
account&strAccount ID.
model_code&strModel 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)
ParameterTypeDescription
req_idi64Request 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)
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
account&strAccount ID.
model_code&strModel portfolio code (empty for default).
con_idi64Contract ID. Unique per instrument.

cancel_pnl_single

Cancel single-position PnL subscription.

pub fn cancel_pnl_single(&self, req_id: i64)
ParameterTypeDescription
req_idi64Request 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)
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
group&strAccount group name (e.g. "All").
tags&strComma-separated account tags: "NetLiquidation,BuyingPower,...".

cancel_account_summary

Cancel account summary.

pub fn cancel_account_summary(&self, req_id: i64)
ParameterTypeDescription
req_idi64Request 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)
ParameterTypeDescription
subscribebooltrue to start updates, false to stop.
acct_code&strAccount 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, )
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
account&strAccount ID.
model_code&strModel portfolio code (empty for default).
ledger_and_nlvboolIf 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)
ParameterTypeDescription
req_idi64Request 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)
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
account&strAccount ID.
model_code&strModel portfolio code (empty for default).

cancel_positions_multi

pub fn cancel_positions_multi(&self, req_id: i64)
ParameterTypeDescription
req_idi64Request 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)>
ParameterTypeDescription
heldcrate::types::HeldElsewhereWhich 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>>
ParameterTypeDescription
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>
ParameterTypeDescription
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)
ParameterTypeDescription
order_idi64Order identifier. Must be unique per session.
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
order&OrderOrder 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, )
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
exercise_actioni321=exercise, 2=lapse.
exercise_quantityi32Number of contracts to exercise.
account&strAccount ID.
override_bool
statedcrate::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>, )
ParameterTypeDescription
order_idi64Order identifier. Must be unique per session.
order_cancelimpl 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>, )
ParameterTypeDescription
order_cancelimpl 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)
ParameterTypeDescription
num_idsi32Number 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>
ParameterTypeDescription
timeoutOption<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)
ParameterTypeDescription
api_onlybool

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)
ParameterTypeDescription
b_auto_bindboolIf 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)
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
filter&ExecutionFilterExecution 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>
ParameterTypeDescription
strategy&strAlgo 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, )
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
scan&crate::types::SpreadScanWhat 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>
ParameterTypeDescription
req_idi64Request 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], )
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
generic_tick_list&strComma-separated generic tick IDs (e.g. "233" for RT volume).
snapshotboolIf true, delivers one quote then auto-cancels.
regulatory_snapshotboolIf 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], )
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
generic_tick_list&strComma-separated generic tick IDs (e.g. "233" for RT volume).
snapshotboolIf true, delivers one quote then auto-cancels.
regulatory_snapshotboolIf true, request a regulatory snapshot (additional fees may apply).
mode_9887i32
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)
ParameterTypeDescription
req_idi64Request 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, )
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
tick_type&strTick type ID or tick-by-tick type string.
number_of_ticksi32Maximum number of ticks to return.
ignore_sizeboolIf 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)
ParameterTypeDescription
req_idi64Request 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, )
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
num_rowsi32Number of order book rows to subscribe to.
is_smart_depthboolIf 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)
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
is_smart_depthboolIf 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, )
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
bar_sizei32Bar size: "1 min", "5 mins", "1 hour", "1 day", etc.
what_to_show&strData type: "TRADES", "MIDPOINT", "BID", "ASK", "BID_ASK", etc.
use_rthboolIf 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)
ParameterTypeDescription
req_idi64Request 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)
ParameterTypeDescription
market_data_typei321=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)
ParameterTypeDescription
providers&strNews 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>
ParameterTypeDescription
req_idi64Request 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>
ParameterTypeDescription
instrumentInstrumentIdInstrument 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>
ParameterTypeDescription
req_idi64Request 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
ParameterTypeDescription
req_idi64Request 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
ParameterTypeDescription
instrumentInstrumentIdInstrument 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>
ParameterTypeDescription
req_idi64Request 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>
ParameterTypeDescription
instrumentInstrumentIdInstrument 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>
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
seriesu32

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)>
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
seriesu32
fractionalbool

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)>
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
seriesu32

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)>
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
seriesu32

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>
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
seriesu32

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>
ParameterTypeDescription
req_idi64Request 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>
ParameterTypeDescription
req_idi64Request 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>
ParameterTypeDescription
req_idi64Request 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>
ParameterTypeDescription
req_idi64Request 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)>
ParameterTypeDescription
con_idu32Contract ID. Unique per instrument.
seriesu32

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>
ParameterTypeDescription
con_idu32Contract 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>
ParameterTypeDescription
req_idi64Request 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>
ParameterTypeDescription
instrumentInstrumentIdInstrument 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>
ParameterTypeDescription
instrumentInstrumentIdInstrument 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, )
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
end_date_time&strEnd date/time in "YYYYMMDD HH:MM:SS" format, or empty for now.
duration&strDuration string, e.g. "1 D", "1 W", "1 M", "1 Y".
bar_size&strBar size: "1 min", "5 mins", "1 hour", "1 day", etc.
what_to_show&strData type: "TRADES", "MIDPOINT", "BID", "ASK", "BID_ASK", etc.
use_rthboolIf true, only return data from Regular Trading Hours.
format_datei32Date format: 1="YYYYMMDD HH:MM:SS", 2=Unix seconds.
keep_up_to_dateboolIf true, continue receiving updates after initial history.

cancel_historical_data

Cancel historical data.

pub fn cancel_historical_data(&self, req_id: i64)
ParameterTypeDescription
req_idi64Request 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, )
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
what_to_show&strData type: "TRADES", "MIDPOINT", "BID", "ASK", "BID_ASK", etc.
use_rthboolIf true, only return data from Regular Trading Hours.
format_datei32Date 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)
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
contract&ContractContract 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)
ParameterTypeDescription
req_idi64Request 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)
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
pattern&strSymbol 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)
ParameterTypeDescription
req_idi64Request 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)
ParameterTypeDescription
req_idi64Request 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)
ParameterTypeDescription
req_idi64Request 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, )
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
querycrate::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, )
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
underlying_symbol&strUnderlying symbol (e.g. "AAPL").
fut_fop_exchange&strExchange for futures/FOP options.
underlying_sec_type&strUnderlying security type (e.g. "STK").
underlying_con_idi64Underlying contract ID.

cancel_head_time_stamp

Cancel head timestamp request.

pub fn cancel_head_time_stamp(&self, req_id: i64)
ParameterTypeDescription
req_idi64Request 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)
ParameterTypeDescription
market_rule_idi32Market 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)
ParameterTypeDescription
all_msgsboolIf 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, )
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
instrument&strInstrument type for scanner (e.g. "STK", "FUT").
location_code&strScanner location (e.g. "STK.US.MAJOR").
scan_code&strScanner code (e.g. "TOP_PERC_GAIN", "HIGH_OPT_IMP_VOLAT").
max_itemsu32Maximum 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)
ParameterTypeDescription
req_idi64Request 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, )
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
con_idi64Contract ID. Unique per instrument.
provider_codes&strPipe-separated news provider codes.
start_time&strStart date/time for news query.
end_time&strEnd date/time for news query.
total_resultsi32Maximum 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)
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
provider_code&strNews provider code (e.g. "BRFG").
article_id&strNews 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, )
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
con_idi64Contract ID. Unique per instrument.
sec_type&str
exchange&strExchange 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>>
ParameterTypeDescription
req_idi64Request 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)
ParameterTypeDescription
req_idi64Request 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)
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
report_type&strReport type: "ReportSnapshot", "ReportsFinSummary", "RESC", etc.

cancel_fundamental_data

Cancel fundamental data.

pub fn cancel_fundamental_data(&self, req_id: i64)
ParameterTypeDescription
req_idi64Request 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)
ParameterTypeDescription
req_idi64Request 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)
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
use_rthboolIf true, only return data from Regular Trading Hours.
period&strHistogram period, e.g. "1week", "1month".

cancel_histogram_data

Cancel histogram data.

pub fn cancel_histogram_data(&self, req_id: i64)
ParameterTypeDescription
req_idi64Request 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, )
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
start_date_time&strStart date/time for tick query.
end_date_time&strEnd date/time in "YYYYMMDD HH:MM:SS" format, or empty for now.
number_of_ticksi32Maximum number of ticks to return.
what_to_show&strData type: "TRADES", "MIDPOINT", "BID", "ASK", "BID_ASK", etc.
use_rthboolIf true, only return data from Regular Trading Hours.
ignore_sizeboolIf 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)
ParameterTypeDescription
req_idi64Request 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, )
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
end_date_time&strEnd date/time in "YYYYMMDD HH:MM:SS" format, or empty for now.
duration&strDuration string, e.g. "1 D", "1 W", "1 M", "1 Y".
use_rthboolIf 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)
ParameterTypeDescription
req_idi64Request 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)
ParameterTypeDescription
req_idi64Request 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)
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
bbo_exchange&strBBO 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)
ParameterTypeDescription
fa_data_typei32FA 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)
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
fa_data_typei32FA data type (1=Groups, 2=Profiles, 3=Aliases).
cxml&strFA 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, )
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
contract&super::ContractContract specification (symbol, secType, exchange, currency, etc.).
option_pricef64Option market price.
under_pricef64Underlying 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, )
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
contract&super::ContractContract specification (symbol, secType, exchange, currency, etc.).
volatilityf64Implied volatility.
under_pricef64Underlying 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)
ParameterTypeDescription
req_idi64Request 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)
ParameterTypeDescription
req_idi64Request 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)
ParameterTypeDescription
req_idi64Request 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)
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
group_idi32Display group ID.

unsubscribe_from_group_events

Stop following a display group.

pub fn unsubscribe_from_group_events(&self, req_id: i64)
ParameterTypeDescription
req_idi64Request 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)
ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
contract_info&strDisplay 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)
ParameterTypeDescription
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)
ParameterTypeDescription
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)
ParameterTypeDescription
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)
ParameterTypeDescription
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)
ParameterTypeDescription
req_idi64Request 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)
ParameterTypeDescription
log_leveli32A 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)
ParameterTypeDescription
req_idi64Request 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.

ParameterTypeDescription
order_idi64Order 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.

ParameterTypeDescription
accounts_list&strComma-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.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
error_timei64When 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_codei64Error code.
error_string&strError message.
advanced_order_reject_json&strJSON 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.

ParameterTypeDescription
originErrorOriginWhat 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_timei64When 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_codei64Error code.
error_string&strError message.
advanced_order_reject_json&strJSON with advanced rejection details.

current_time

The venue's clock, in seconds since the epoch.

ParameterTypeDescription
timei64Tick 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.

ParameterTypeDescription
time_in_millisi64

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.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
tick_typei32Tick type ID or tick-by-tick type string.
pricef64Tick price.
attrib&TickAttribTick attributes.

tick_size

One size of a quote, and which size it is: 0 bid, 3 ask, 5 last, 8 the day's volume.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
tick_typei32Tick type ID or tick-by-tick type string.
sizeDecimalTick size.

tick_string

A quote's value that is not a number — a timestamp, an exchange map, a set of ids.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
tick_typei32Tick type ID or tick-by-tick type string.
value&strAccount 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.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
tick_typei32Tick type ID or tick-by-tick type string.
valuef64Account 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.

ParameterTypeDescription
req_idi64Request 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.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
market_data_typei321=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.

ParameterTypeDescription
order_idi64Order identifier. Must be unique per session.
status&strOrder status string ("Submitted", "Filled", "Cancelled", etc.).
filledDecimalCumulative filled quantity.
remainingDecimalRemaining quantity.
avg_fill_pricef64Average fill price.
perm_idi64The order's permanent id: the number it goes to the venue under.
parent_idi64Parent order ID (0 if no parent).
last_fill_pricef64Price of the last fill.
client_idi64API client ID for order ownership and the saved order-id counter.
why_held&strReason the order is held (e.g. "locate").
mkt_cap_pricef64Market 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.

ParameterTypeDescription
order_idi64Order identifier. Must be unique per session.
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
order&OrderOrder parameters (action, quantity, type, price, TIF, etc.).
order_state&OrderStateOrder 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.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
execution&ExecutionExecution details (exec_id, time, price, shares, etc.).

exec_details_end

Every execution answering this request has been stated.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.

commission_and_fees_report

What a fill cost, matched to it by execution id.

ParameterTypeDescription
report&CommissionAndFeesReportCommission 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.

ParameterTypeDescription
key&strAccount value key (e.g. "NetLiquidation", "BuyingPower").
value&strAccount value.
currency&strCurrency code (e.g. "USD").
account_name&strAccount identifier.

update_portfolio

One position, as the venue values it now.

ParameterTypeDescription
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
positionDecimalBook position (row index) or position size.
market_pricef64Current market price.
market_valuef64Current market value of position.
average_costf64Average cost basis.
unrealized_pnlf64Unrealized profit/loss.
realized_pnlf64Realized profit/loss.
account_name&strAccount identifier.

update_account_time

When the account figures above were last stated.

ParameterTypeDescription
timestamp&strTimestamp string.

account_download_end

The account has been fully stated. Fires once the venue has stopped adding to it, not on the first figure.

ParameterTypeDescription
account&strAccount ID.

account_summary

One figure answering req_account_summary, in the currency the venue states it in.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
account&strAccount ID.
tag&strAccount tag name (e.g. "NetLiquidation").
value&strAccount value.
currency&strCurrency code (e.g. "USD").

account_summary_end

Every figure answering this request has been stated.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.

position

One position held, on any account this login may act for.

ParameterTypeDescription
account&strAccount ID.
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
posDecimalPosition size (decimal shares).
avg_costf64Average 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.

ParameterTypeDescription
qQuestion

position_multi

A holding, answering req_positions_multi. Separate from position: a caller asks per account or model and is answered per request.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
account&strAccount ID.
model_code&strModel portfolio code (empty for default).
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
posDecimalPosition size (decimal shares).
avg_costf64Average cost per share.

position_multi_end

Every position answering this request has been stated.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.

account_update_multi

An account value, answering req_account_updates_multi.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
account&strAccount ID.
model_code&strModel portfolio code (empty for default).
key&strAccount value key (e.g. "NetLiquidation", "BuyingPower").
value&strAccount value.
currency&strCurrency code (e.g. "USD").

account_update_multi_end

Every figure answering this request has been stated.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.

pnl

An account's running profit: today's, what is unrealised, and what has been realised.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
daily_pnlf64Daily profit/loss.
unrealized_pnlf64Unrealized profit/loss.
realized_pnlf64Realized profit/loss.

pnl_single

The same for one position, with the size held.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
posDecimalPosition size (decimal shares).
daily_pnlf64Daily profit/loss.
unrealized_pnlf64Unrealized profit/loss.
realized_pnlf64Realized profit/loss.
valuef64Account 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.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
bar&BarDataBar 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.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
start&strPeriod start date/time.
end&strPeriod 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.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
bar&BarDataBar data (date, open, high, low, close, volume, wap, bar_count).

head_timestamp

The earliest moment the venue holds data for a contract.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
head_timestamp&strEarliest 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.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
details&ContractDetailsContract details object.

contract_details_end

Every contract matching this request has been stated.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.

symbol_samples

Contracts whose symbol or name matches a pattern, across venues.

ParameterTypeDescription
req_idi64Request 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.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
tick_typei32Tick type ID or tick-by-tick type string.
timei64Tick timestamp (Unix seconds).
pricef64Tick price.
sizeDecimalTick size.
attrib&TickAttribLastTick attributes.
exchange&strExchange name.
special_conditions&strSpecial trade conditions.

tick_by_tick_bid_ask

One change to the top of the book, as it happens.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
timei64Tick timestamp (Unix seconds).
bid_pricef64Bid price.
ask_pricef64Ask price.
bid_sizeDecimalBid size.
ask_sizeDecimalAsk size.
attrib&TickAttribBidAskTick attributes.

tick_by_tick_mid_point

One change to the midpoint, as it happens.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
timei64Tick timestamp (Unix seconds).
mid_pointf64Midpoint price.

scanner_data

One row of a scan, in rank order.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
ranki32Scanner result rank (0-based).
details&ContractDetailsContract details object.
distance&strScanner distance metric.
benchmark&strScanner benchmark.
projection&strScanner projection.
legs_str&strCombo legs description.

scanner_data_end

Every row of this scan has been stated.

ParameterTypeDescription
req_idi64Request 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.

ParameterTypeDescription
xml&strXML string.

update_news_bulletin

A notice the venue broadcasts to everyone — an exchange unavailable, a system message.

ParameterTypeDescription
msg_idi64Bulletin message ID.
msg_typei32Bulletin message type (1=regular, 2=exchange).
message&strBulletin message text.
orig_exchange&strOriginating exchange.

tick_news

A headline about a contract being watched, as it is published.

ParameterTypeDescription
ticker_idi64Ticker/request ID.
timestampi64Timestamp string.
provider_code&strNews provider code (e.g. "BRFG").
article_id&strNews article identifier.
headline&strNews headline text.
extra_data&strAdditional tick data.

historical_news

One headline from the archive.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
time&strTick timestamp (Unix seconds).
provider_code&strNews provider code (e.g. "BRFG").
article_id&strNews article identifier.
headline&strNews headline text.

historical_news_end

Every headline answering this request has been stated, and whether the archive holds more.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
has_moreboolIf true, more results available.

news_article

The body of one article. article_type is 0 for text and 1 for a binary document.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
article_typei32Article type: 0=plain text, 1=HTML.
article_text&strFull article body.

real_time_bar

One five-second bar of a live stream.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
datei64Bar date string.
openf64Open price.
highf64High price.
lowf64Low price.
closef64Close price.
volumeDecimalVolume.
wapDecimalVolume-weighted average price.
counti32Trade count.

historical_ticks

Historical midpoints, in batches, until done.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
ticks&HistoricalTickDataHistorical tick data.
doneboolIf true, all ticks have been delivered.

historical_ticks_bid_ask

Historical quotes, in batches, until done.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
ticks&HistoricalTickDataHistorical tick data.
doneboolIf true, all ticks have been delivered.

historical_ticks_last

Historical trades, in batches, until done.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
ticks&HistoricalTickDataHistorical tick data.
doneboolIf 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.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
tick_typei32Tick type ID or tick-by-tick type string.
tick_attribi32
implied_volf64Implied volatility.
deltaf64Option delta.
opt_pricef64Option theoretical price.
pv_dividendf64Present value of dividends.
gammaf64Option gamma.
vegaf64Option vega.
thetaf64Option theta.
und_pricef64Underlying price.

display_group_list

The display groups this client offers, |-separated.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
groups&strFA group definitions.

display_group_updated

The contract a display group now holds, as conId@exchange, or none.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
contract_info&strDisplay 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.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
details&ContractDetailsContract details object.

order_bound

The permanent id an order was given, paired with the id this client used.

ParameterTypeDescription
perm_idi64The order's permanent id: the number it goes to the venue under.
client_idi64API client ID for order ownership and the saved order-id counter.
order_idi64Order identifier. Must be unique per session.

receive_fa

An advisor's allocation groups, profiles or aliases, as XML.

ParameterTypeDescription
fa_data_typei32FA data type (1=Groups, 2=Profiles, 3=Aliases).
cxml&strFA XML configuration data.

replace_fa_end

The end of a replace_fa exchange.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
text&strInformational text.

wsh_meta_data

What the event calendar can answer about, as JSON.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
data_json&str

wsh_event_data

Calendar events, as JSON.

ParameterTypeDescription
req_idi64Request 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.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
exchange&strExchange name.
underlying_con_idi64Underlying contract ID.
trading_class&strTrading class.
multiplier&strContract multiplier.
expirations&[String]Available expiration dates.
strikes&[f64]Available strike prices.

security_definition_option_parameter_end

Every venue's chain has been stated.

ParameterTypeDescription
req_idi64Request 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.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
con_idi64Contract ID. Unique per instrument.
exchange&strExchange name.

reroute_mkt_depth_req

The same, for a request for the book rather than the quote.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
con_idi64Contract ID. Unique per instrument.
exchange&strExchange 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.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
con_idi64Contract ID. Unique per instrument.
deltaf64Option delta.
pricef64Tick price.

tick_efp

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
tick_typei32Tick type ID or tick-by-tick type string.
basis_pointsf64
formatted_basis_points&str
implied_futuref64
hold_daysi32
future_last_trade_date&str
dividend_impactf64
dividends_to_last_trade_datef64

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.

ParameterTypeDescription
api_data&str

verify_completed

Whether that handshake was accepted.

ParameterTypeDescription
is_successfulbool
error_text&str

verify_and_auth_message_api

The same handshake, where the terminal also authenticates the program.

ParameterTypeDescription
api_data&str
xyz_challenge&str

verify_and_auth_completed

Whether that one was accepted.

ParameterTypeDescription
is_successfulbool
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.

ParameterTypeDescription
text&strInformational text.
last_errori32

histogram_data

How much traded at each price over a window.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
items&[(f64, DecimalHistogram entries [(price, count)].

market_rule

The price ladder a contract trades on: each step, and what the price moves in above it.

ParameterTypeDescription
market_rule_idi64Market 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.

ParameterTypeDescription
contract&ContractContract specification (symbol, secType, exchange, currency, etc.).
order&OrderOrder parameters (action, quantity, type, price, TIF, etc.).
order_state&OrderStateOrder 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.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
start_date_time&strStart date/time for tick query.
end_date_time&strEnd date/time in "YYYYMMDD HH:MM:SS" format, or empty for now.
time_zone&strTimezone string (e.g. "US/Eastern").
sessions&[(String, String, StringTrading sessions [(ref_date, open, close)].

fundamental_data

A fundamental report, as the XML the venue publishes.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
data&strRaw 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.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
positioni32Book position (row index) or position size.
operationi32Book operation: 0=insert, 1=update, 2=delete.
sidei32Book side: 0=ask, 1=bid. Or order side "BOT"/"SLD".
pricef64Tick price.
sizeDecimalTick size.

update_mkt_depth_l2

One level of a book that names the venue it stands on. Every level from this client names one.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
positioni32Book position (row index) or position size.
market_maker&strMarket maker ID.
operationi32Book operation: 0=insert, 1=update, 2=delete.
sidei32Book side: 0=ask, 1=bid. Or order side "BOT"/"SLD".
pricef64Tick price.
sizeDecimalTick size.
is_smart_depthboolIf 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.

ParameterTypeDescription
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.

ParameterTypeDescription
ticker_idi64Ticker/request ID.
min_tickf64Minimum tick size.
bbo_exchange&strBBO exchange for smart component lookup (e.g. "SMART").
snapshot_permissionsi64What 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.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
components&[crate::types::SmartComponent]Smart routing component exchanges.

news_providers

Every news provider this account may read.

ParameterTypeDescription
providers&[crate::types::NewsProvider]News provider list.

soft_dollar_tiers

The soft dollar tiers this account may direct commission to.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
tiers&[crate::types::SoftDollarTier]Soft dollar tier list.

family_codes

The account families this login belongs to.

ParameterTypeDescription
codes&[crate::types::FamilyCode]Family code list.

user_info

What the login is entitled to, as the venue states it.

ParameterTypeDescription
req_idi64Request identifier. Used to match responses to requests.
white_branding_id&strWhite 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:

OriginMeaning
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
SessionA 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_type callback either. Nothing arrives to say what feed you are on. Asking for delayed data then answers normally.
  • -1 on bid and ask, no bid or ask size, and a market_data_type of 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, FIGI and BB_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 as CS or in lower case, and a Nasdaq listing is NASDAQ, given as ISLAND.
  • 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.
  • includeExpired is 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 (or CONTFUT+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 under CONTFUT, 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. CONTFUT alone comes back as CONTFUT, 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:

CallArgumentWhy
req_idsnum_idsThe next valid id is answered whatever number is asked for
req_real_time_barsbar_sizeA 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.
  • tickAttrib is 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. tickAttrib is 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:

PlatformData 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:

  • reqCompletedOrders answers 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:

KindReads as
ShareAAPL 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
OptionSPY 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
FutureMES DEC'26, and (DP) after a dividend-protected one
Future's optionES DEC'26 6000 Call Fut. Option, and a trading class other than the symbol in brackets
Currency pairEUR.USD Forex
Crypto currency, commodityBTC Crypto (BTC), XAUUSD Commodity (XAUUSD): the trading class in brackets
Contract for differenceAAPL CFD (AAPL), or EUR.USD CFD on a currency pair
Warrant, structured productDAX 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 contractFF 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
CombinationSPY Combo: its symbol and Combo
Bond, billUS-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
10203a fund bought with a quantity, or without an amount
10204a fund sold with an amount, or without a quantity
10207a 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
10206an 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
10241a 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 forceImmediate-or-cancel, or the one measured in minutes. A day order is refused: "The crypto buy order must be Minutes or IOC"
PriceOn 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
QuantityA 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.

CallbackWhy
reroute_mkt_data_reqThe 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_reqThe 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:

  1. Attached fields are read first. NOAPISLPTSGL gives 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 and PRESET gives 320, Error reading request: Invalid value for Stop Loss order-id or order-type, with Profit Taker checked next. A non-PRESET type with no id is not refused merely for that type.
  2. Order-field checks precede the shared contract-expiry check. An invalid expiry gives 10372. Empty, NOEXP, a valid yyyyMM month or yyyyMMdd day in years 1978–3000 are accepted.
  3. Numbers are checked parent, profit taker, stop loss. An unassigned 0, INT_MAX or INT_MIN gives 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.
  4. 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 no adjusted_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. Y means the call is served on that surface. STUB means 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 APIRustPython
EClient methods8787 impl, 0 stub87 impl, 0 stub
EWrapper callbacks9088 impl, 2 stub88 impl, 2 stub

EClient Methods

CategoryIB API MethodC++ NameRustPythonEvidence
ConnectionconnecteConnectYYLive session
disconnecteDisconnectYYLive session
is_connectedisConnectedYYLive session
start_apistartApiYYOffline suites
set_server_log_levelsetServerLogLevelYYLive session
req_current_timereqCurrentTimeYYLive session
req_current_time_in_millisreqCurrentTimeInMillisYYLive session
verify_requestverifyRequestYYOffline suites
verify_messageverifyMessageYYOffline suites
verify_and_auth_requestverifyAndAuthRequestYYOffline suites
verify_and_auth_messageverifyAndAuthMessageYYOffline suites
Market Datareq_mkt_datareqMktDataYYLive session
cancel_mkt_datacancelMktDataYYLive session
req_market_data_typereqMarketDataTypeYYLive session
req_tick_by_tick_datareqTickByTickDataYYLive session
cancel_tick_by_tick_datacancelTickByTickDataYYLive session
req_mkt_depthreqMktDepthYYLive session
cancel_mkt_depthcancelMktDepthYYLive session
req_mkt_depth_exchangesreqMktDepthExchangesYYLive session
req_smart_componentsreqSmartComponentsYYLive session
req_real_time_barsreqRealTimeBarsYYLive session
cancel_real_time_barscancelRealTimeBarsYYLive session
Historical Datareq_historical_datareqHistoricalDataYYLive session
cancel_historical_datacancelHistoricalDataYYLive session
req_head_time_stampreqHeadTimeStampYYLive session
cancel_head_time_stampcancelHeadTimestampYYLive session
req_historical_ticksreqHistoricalTicksYYLive session
cancel_historical_tickscancelHistoricalTicksYYOffline suites
req_histogram_datareqHistogramDataYYLive session
cancel_histogram_datacancelHistogramDataYYLive session
req_historical_schedulereqHistoricalScheduleYYLive session
Ordersplace_orderplaceOrderYYLive session
cancel_ordercancelOrderYYLive session
req_open_ordersreqOpenOrdersYYLive session
req_all_open_ordersreqAllOpenOrdersYYLive session
req_auto_open_ordersreqAutoOpenOrdersYYLive session
req_idsreqIdsYYLive session
req_global_cancelreqGlobalCancelYYLive session
req_completed_ordersreqCompletedOrdersYYLive session
Executionsreq_executionsreqExecutionsYYLive session
Accountreq_account_updatesreqAccountUpdatesYYLive session
req_account_summaryreqAccountSummaryYYLive session
cancel_account_summarycancelAccountSummaryYYLive session
req_positionsreqPositionsYYLive session
cancel_positionscancelPositionsYYLive session
req_pnlreqPnLYYLive session
cancel_pnlcancelPnLYYLive session
req_pnl_singlereqPnLSingleYYLive session
cancel_pnl_singlecancelPnLSingleYYLive session
req_managed_acctsreqManagedAcctsYYLive session
req_account_updates_multireqAccountUpdatesMultiYYLive session
cancel_account_updates_multicancelAccountUpdatesMultiYYLive session
req_positions_multireqPositionsMultiYYLive session
cancel_positions_multicancelPositionsMultiYYLive session
Contractreq_contract_detailsreqContractDetailsYYLive session
cancel_contract_datacancelContractDataYYOffline suites
req_matching_symbolsreqMatchingSymbolsYYLive session
req_market_rulereqMarketRuleYYLive session
Scannerreq_scanner_parametersreqScannerParametersYYLive session
req_scanner_subscriptionreqScannerSubscriptionYYLive session
cancel_scanner_subscriptioncancelScannerSubscriptionYYLive session
Newsreq_news_providersreqNewsProvidersYYLive session
req_news_articlereqNewsArticleYYLive session
req_historical_newsreqHistoricalNewsYYLive session
req_news_bulletinsreqNewsBulletinsYYLive session
cancel_news_bulletinscancelNewsBulletinsYYLive session
Fundamentalreq_fundamental_datareqFundamentalDataYYLive session
cancel_fundamental_datacancelFundamentalDataYYLive session
Optionscalculate_implied_volatilitycalculateImpliedVolatilityYYLive session
cancel_calculate_implied_volatilitycancelCalculateImpliedVolatilityYYLive session
calculate_option_pricecalculateOptionPriceYYLive session
cancel_calculate_option_pricecancelCalculateOptionPriceYYLive session
exercise_optionsexerciseOptionsYYLive session
req_sec_def_opt_paramsreqSecDefOptParamsYYLive session
Referencereq_soft_dollar_tiersreqSoftDollarTiersYYLive session
req_family_codesreqFamilyCodesYYLive session
req_user_inforeqUserInfoYYLive session
Financial Advisorrequest_farequestFAYYOffline suites
replace_fareplaceFAYYOffline suites
Display Groupsquery_display_groupsqueryDisplayGroupsYYLive session
subscribe_to_group_eventssubscribeToGroupEventsYYLive session
unsubscribe_from_group_eventsunsubscribeFromGroupEventsYYLive session
update_display_groupupdateDisplayGroupYYLive session
WSHreq_wsh_meta_datareqWshMetaDataYYLive session
cancel_wsh_meta_datacancelWshMetaDataYYLive session
req_wsh_event_datareqWshEventDataYYLive session
cancel_wsh_event_datacancelWshEventDataYYLive session

EWrapper Callbacks

CategoryCallbackRustPythonFires on a gateway
Connectionconnect_ackYYyes
connection_closedYYyes
next_valid_idYYyes
managed_accountsYYyes
errorYYyes
current_timeYYyes
current_time_in_millisYYyes
Market Datatick_priceYYyes
tick_sizeYYyes
tick_stringYYyes
tick_genericYYyes
tick_snapshot_endYYyes
market_data_typeYYyes
tick_req_paramsYYyes
Ordersorder_statusYYyes
open_orderYYyes
open_order_endYYyes
order_boundYYyes
Executionsexec_detailsYYyes
exec_details_endYYyes
commission_and_fees_reportYYyes
Accountupdate_account_valueYYyes
update_portfolioYYyes
update_account_timeYYyes
account_download_endYYyes
account_summaryYYyes
account_summary_endYYyes
positionYYyes
position_endYYyes
pnlYYyes
pnl_singleYYyes
position_multiYYyes
position_multi_endYYyes
account_update_multiYYyes
account_update_multi_endYYyes
Contractcontract_detailsYYyes
contract_details_endYYyes
bond_contract_detailsYYyes
symbol_samplesYYyes
Historical Datahistorical_dataYYyes
historical_data_endYYyes
historical_data_updateYYyes
head_timestampYYyes
historical_ticksYYyes
historical_ticks_bid_askYYyes
historical_ticks_lastYYyes
histogram_dataYYyes
historical_scheduleYYyes
Market Depthupdate_mkt_depthYYyes
update_mkt_depth_l2YYyes
mkt_depth_exchangesYYyes
Tick-by-Ticktick_by_tick_all_lastYYyes
tick_by_tick_bid_askYYyes
tick_by_tick_mid_pointYYyes
Scannerscanner_dataYYyes
scanner_data_endYYyes
scanner_parametersYYyes
Newsnews_providersYYyes
news_articleYYyes
historical_newsYYyes
historical_news_endYYyes
tick_newsYYyes
update_news_bulletinYYyes
Real-Time Barsreal_time_barYYyes
Fundamentalfundamental_dataYYyes
Market Rulesmarket_ruleYYyes
Completed Orderscompleted_orderYYyes
completed_orders_endYYyes
Optionstick_option_computationYYyes
security_definition_option_parameterYYyes
security_definition_option_parameter_endYYyes
Referencesmart_componentsYYyes
soft_dollar_tiersYYyes
family_codesYYyes
user_infoYYyes
FAreceive_faYYyes
replace_fa_endYYyes
Display Groupsdisplay_group_listYYyes
display_group_updatedYYyes
Otherdelta_neutral_validationYYno
WSHwsh_meta_dataYYyes
wsh_event_dataYYyes
Market Datareroute_mkt_data_reqSTUBSTUByes
reroute_mkt_depth_reqSTUBSTUByes
tick_efpYYno
Connectionverify_message_apiYYno
verify_completedYYno
verify_and_auth_message_apiYYno
verify_and_auth_completedYYno
win_errorYYno

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

TypeMeaning
0Heartbeat
1Test request
5Logout
ALogon
DNew order
FOrder cancel
GOrder replace
HOrder mass status request
UUser message
VMarket data request
WChart request
ZChart cancel
cSecurity 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

TypeMeaning
3Session reject
ALogon
UUser message

On the trading connection

TypeMeaning
0Heartbeat
1Test request
3Session reject
8Execution report
9Cancel reject
BNews
UUser message
dSecurity definition
ALAccount update, holdings held away
APPosition update, held away
DOPosition update, shown but not held
DPPosition update, reported apart
EBEnd of a batch
RLAccount update
ULAccount update, holdings reported apart
UMAccount update
UPPosition update
UTAccount 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

TypeMeaning
0Heartbeat
1Test request
3Session reject
GTick payload, binary
LTicker setup
PTick
QSubscription ack
YSubscription reject

On the historical connection

TypeMeaning
0Heartbeat
1Test request
EHistorical payload
GBar payload
UUser message
WChart response

User-message subtypes on the historical connection

Subtype
10002
10005
10012
10022
10032

On the security definition connection

TypeMeaning
0Heartbeat
1Test request
3Session reject
UUser message