What the session states, and the API does not forward
This client speaks the protocol a gateway speaks. The gateway receives more than it forwards: some of what the venue states at logon and on a contract has no message in the API a gateway offers its own clients, so a program written against that API has no way to ask for it.
Everything below is stated by the venue on an ordinary session. The figures are
from one paper session and will differ with the account; what does not differ is
that none of it is reachable through ib_async or the TWS API.
The examples are Python. Every one of these has a Rust EClient equivalent —
fields and Option-wrapped returns included — see the
table at the end.
What the account may do
The venue states its grants at logon — one token per capability.
grants = client.enabled_features() # 302 on the session this was written from
"ISLAND2NASDAQ" in grants # True
They decide behaviour. island_for_nasdaq — whether a US stock trading on
Nasdaq is named by the older spelling — takes this grant as well as the
setting, which is how it is decided: the same token is read off
the same list at logon and holds it beside the setting.
Which orders the venue will take
Stated at logon, per security type, before a single order is sent.
perms = client.order_permissions() # 21 security types
len(perms["STK"]) # 92 order types and attributes
client.permitted_order_types("FUT") # or None, if the type is not permitted at all
Through the API a program discovers this by being refused. An order for a security type absent from this map is refused before it is sent, with the reason on the error callback.
Which algorithms this account may use
Keyed by provider and security type: the documents that define them, as the venue lists them. The list is asked for the first time an order names an algorithm, as a gateway asks it, and is empty until then.
client.algorithms() # 13 keys: 'IBALGO/STK', 'FOXRIVER/STK', …
client.algorithms_for("STK") # ['FOXRIVER-AE', 'IBALGO-AE', 'JONES-AE', …]
An order is held to the definitions the list names for its contract (see an order through an algorithm).
Which order defaults the account holds
The venue keeps a set of order defaults per security type and fills parts of an order the caller left unstated from them: the size a compete order competes with and the offset it competes by, where neither was named. So the same call on two accounts is not the same order.
client.order_presets() # [('s=CASH', 'v=1&a=1'), ('s=FUT', 'v=1&a=1'), ('s=STK', 'v=1&a=1'), …]
The key is the venue's own, and a currency can split one: s=CASH&tc=EUR sits
beside s=CASH. The second figure is the set's attributes as the venue
writes them: v= names its variant and a=1 marks it active, so
('s=CASH&tc=EUR', 'v=1') is a set that is not active. The values inside a
set are asked for separately and are not carried here.
What the session itself is
client.get_account_id() # the account this session acts for
client.next_order_id() # the first id it may use
client.ccp_session_id() # what a web endpoint expects as a header
client.misc_url("region_dam") # hosts the venue pushed at logon
How far away the venue is
client.req_ping()
client.last_rtt_ms() # 10.96 on the session this was written from
The API has no notion of this. A program that wants to know whether it is close to the venue has to time a request that does something else.
What the venue states about a contract, beyond its quote
A quote subscription can carry far more than prices. The venue publishes dozens of series against a contract, each under a number of its own, and the API forwards a handful of them. Ask for one by putting its number in the generic tick list, then read what it stated.
Some carry the venue's own named fields as text — analyst ratings, ownership, fund terms, screening scores, margin, technical readings, company accounts:
client.req_mkt_data(1, contract, "454,505,631,678,705", False, False, [])
client.company_data_series(265598) # [434, 454, 505, 548, 631, 678, 699, 705, …]
client.company_data(265598, 454) # [('IITPCHL', '47.7348'), … ('IISHOS', '14594180000')]
client.company_data(756733, 505) # [('EXPRATIO', '0.0945'), ('TOTALASSETVALUE', '811937033817.46'), …]
client.company_data(265598, 705) # [('TRESGS', '7.10404'), ('TRESGCS', '3.65619'), …]
The keys are the venue's own and are handed on unchanged, so a field it adds arrives with the rest instead of being dropped for not being recognised here. One of these series answered with 212 fields on a single contract.
Others carry figures rather than text — a bond's accrued interest, what one contract delivers, volatility, the volume a contract usually opens and closes on, what margin a future takes:
client.stated_figures_series(1) # [398, 399, 402, 459, 493, 504, 527, 584, …]
client.stated_figures(1, 527) # [0.0154349] volatility over twenty days
client.stated_figures(1, 504) # [383820.0, 1.0] what one contract delivers
client.stated_figures(1, 407) # [24340.8, 18338.7, 34772.6, 26198.1]
Six of them state the volatility a contract has shown over 10, 50, 75, 100,
150 and 200 days, one figure each: 511 and 513 to 517 (stated_figures(1, 511)
and on).
The figures arrive in the venue's order and at its widths, and a figure it holds nothing for arrives as the largest number its field carries. Nothing is republished under a tick number of this client's own choosing.
Four series number their figures themselves, in two tables — whole numbers and fractional ones:
client.numbered_figures(1, 562, fractional=False) # [(7, 20260911.0), (30, 20260814.0), … (1826, 20210917.0)]
client.numbered_figures(1, 562, fractional=True) # [(7, 765.96), (30, 776.34), … (1826, 419.586)]
client.numbered_figures(1, 757, fractional=True) # [(30, 766.106)] the average close over thirty days
The first of those is a price history: the number is a span of days, the whole table holds the date each was taken and the fractional table the price on it, back to five years — from a quote subscription, with no historical-data request.
Two more state a run of paired figures, and one carries the venue's option model as the contract closed rather than as it stands — every greek it states, including several the API has no field for:
client.paired_figures(1, 546) # [(coordinate, volatility), …]
client.closing_option_model(1) # {'delta': …, 'rho': …, 'fugit': …, …}
Others state rows of three figures, and what the three are is the series' own: on 547 a quantity, what it is offered at and a second price where the form states one; on 320 a packed quote's field, its figure and how far its decimal point moves.
client.stated_rows_series(1) # [320, 547] which series stated rows
client.stated_rows(1, 547) # [(quantity, price, second price), …]
Two more state what the venue's option model works an underlying's chain from, per class of its options and per expiry: 687 as it stands, and 691 as the chain closed. Each is asked for on a subscription on the underlying, and this client asks the venue for it on the option model's name, as a gateway does:
client.req_mkt_data(1, underlying, "687", False, False, [])
client.chain_model_parameters(1, 687) # [{'productId': …, 'classes': [('SPY', [100.0])],
# 'underlyingPrice': …, 'dividends': [(yyyymmdd, amount)],
# 'terms': [{'lastTradeDate': …, 'modelYield': …,
# 'interestRate': …, 'forward': …,
# 'callAtmVol': …, 'putAtmVol': …}], …}]
A gateway reads both for its own model and hands none of it on. The two volatilities are per trading day; the venue states no unit for the yield and the rate. What a series stated goes when the series is withdrawn, as a gateway forgets it.
Series that are acknowledged and state nothing
Some series are acknowledged and then say nothing: the venue assigns the subscription a tag and states nothing on it, with no error to read. On the session this was written from, six behaved that way: 490, 546, 669, 700, 726 and 733. The same six on the same account, minutes apart, over both a paper and a live login.
An empty result from any of the calls above can mean two things: a series the venue has nothing to say about for that contract, or one this account is not entitled to. The subscription list in account management is what tells them apart.
Why this is not in the API
The API is a protocol between a gateway and a program on the same machine. What the gateway does with any of this is its own and not visible from here; what is visible is that none of it was ever framed as a message to forward. A client that speaks the venue's protocol receives it directly, so it is here.
Two consequences worth stating plainly:
- None of this is derived. Every figure above is stated by the venue, read off the session. Nothing here computes what the venue did not say.
- A gateway has no message for these. A program that calls one runs here and not against a gateway, so a program that has to run against both leaves them alone.
The same calls in Rust
| Python | Rust |
|---|---|
client.enabled_features() | client.enabled_features() |
client.order_permissions() | client.order_permissions() |
client.permitted_order_types(sec_type) | client.permitted_order_types(sec_type) |
client.algorithms() | client.algorithms() |
client.algorithms_for(sec_type) | client.algorithms_for(sec_type) |
client.get_account_id() | client.account_id (a field) |
client.next_order_id() | client.next_order_id() |
client.ccp_session_id() | client.ccp_session_id() |
client.misc_url(key) | client.misc_url(key) |
client.company_data(con_id, series) | client.company_data(con_id, series) |
client.company_data_series(con_id) | client.company_data_series(con_id) |
client.stated_figures(req_id, series) | client.stated_figures(req_id, series) |
client.stated_figures_series(req_id) | client.stated_figures_series(req_id) |
client.numbered_figures(req_id, series, fractional) | client.numbered_figures(req_id, series, fractional) |
client.numbered_figures_series(req_id) | client.numbered_figures_series(req_id) |
client.paired_figures(req_id, series) | client.paired_figures(req_id, series) |
client.paired_figures_series(req_id) | client.paired_figures_series(req_id) |
client.stated_rows(req_id, series) | client.stated_rows(req_id, series) |
client.stated_rows_series(req_id) | client.stated_rows_series(req_id) |
client.closing_option_model(req_id) | client.closing_option_model(req_id) |
client.chain_model_parameters(req_id, series) | client.chain_model_parameters(req_id, series) |
client.req_ping() | client.req_ping() |
client.last_rtt_ms() | client.last_rtt(), an Option<Duration> (delegating to client.shared_state().last_ccp_rtt()) |