IBKR JSONL Schema Reference
Updated: 2026-04-12
Scope: ibkr_underlying_quote_v1 (current) and ibkr_spread_quote_v1 (planned for Phase 6.1)
1. Versioning Policy
schema field is the breaking-change boundary.
- Field addition, rename, deletion, type change, unit change, or nested-shape change requires a new schema name such as
ibkr_underlying_quote_v2.
- Writer and downstream analysis scripts must branch on
record["schema"] first, not on filename.
- Nullable payload fields (
bid, ask, price など) may be null without forcing a version bump, as long as the field name and unit stay the same.
2. Current Schema: ibkr_underlying_quote_v1
Source of truth:
- AEGIS/WORK_LOG/2026-04-12_ibkr_phase3b_phase6_mvp.md
- aegis_v3/aegis-bt-rs/src/bin/lt_ibkr_quote_collector.rs
Fields
| Field |
Type |
Unit |
Notes |
schema |
string |
n/a |
Fixed value: ibkr_underlying_quote_v1 |
ts_utc |
string |
ISO-8601 UTC |
Collector write timestamp |
ts_ms |
integer |
milliseconds since epoch |
Same timestamp in integer form |
symbol |
string |
ticker |
Example: SPY |
conid |
integer |
IBKR contract id |
Underlying conid |
bid |
number or null |
USD/share |
NBBO bid snapshot |
ask |
number or null |
USD/share |
NBBO ask snapshot |
last |
number or null |
USD/share |
Last trade price |
delayed |
boolean |
n/a |
True when IBKR marks fields with delayed prefix |
authenticated |
boolean |
n/a |
Collector auth state at write time |
source |
string |
n/a |
Fixed writer id: lt-ibkr-quote-collector |
Example
{
"schema": "ibkr_underlying_quote_v1",
"ts_utc": "2026-04-12T14:30:00.123Z",
"ts_ms": 1744471800123,
"symbol": "SPY",
"conid": 756733,
"bid": 680.65,
"ask": 680.73,
"last": 680.65,
"delayed": false,
"authenticated": true,
"source": "lt-ibkr-quote-collector"
}
3. Planned Schema: ibkr_spread_quote_v1
This schema is not emitted yet. It is the Phase 6.1 contract draft based on:
- AEGIS/WORK_LOG/2026-04-12_ibkr_phase3b_phase6_mvp.md
- aegis_v3/aegis-bt-rs/src/broker/ibkr/quote.rs (IbkrSpreadQuote)
- aegis_v3/aegis-bt-rs/src/broker/ibkr/execution.rs (IbkrWhatIfResponse)
Planned fields
| Field |
Type |
Unit |
Notes |
schema |
string |
n/a |
Planned fixed value: ibkr_spread_quote_v1 |
ts_utc |
string |
ISO-8601 UTC |
Collector write timestamp |
ts_ms |
integer |
milliseconds since epoch |
Integer timestamp for joins |
symbol |
string |
ticker |
Underlying symbol |
underlying_conid |
integer |
IBKR contract id |
Underlying conid used for option lookup |
short_leg |
object |
n/a |
IbkrLegSnapshot: conid, bid, ask, last, delayed |
long_leg |
object |
n/a |
Same shape as short_leg |
net_bid |
number or null |
USD/spread |
short_leg.bid - long_leg.ask |
net_ask |
number or null |
USD/spread |
short_leg.ask - long_leg.bid |
net_mid |
number or null |
USD/spread |
Midpoint of net_bid and net_ask |
has_quote |
boolean |
n/a |
True only when both legs have bid/ask |
captured_at_ms |
integer |
milliseconds since epoch |
From IbkrSpreadQuote |
what_if |
object or null |
n/a |
Planned IbkrWhatIfResponse payload |
source |
string |
n/a |
Planned writer id, expected collector identifier |
Planned short_leg / long_leg object
| Field |
Type |
Unit |
conid |
integer |
IBKR contract id |
bid |
number or null |
USD/contract |
ask |
number or null |
USD/contract |
last |
number or null |
USD/contract |
delayed |
boolean |
n/a |
Planned what_if object
| Field |
Type |
Unit |
Notes |
amount |
object or null |
broker-defined |
Raw IBKR response fragment |
equity |
object or null |
broker-defined |
Raw IBKR response fragment |
initial |
object or null |
broker-defined |
Raw IBKR response fragment |
maintenance |
object or null |
broker-defined |
Raw IBKR response fragment |
initMarginChange |
string or null |
broker-defined |
Explicitly typed in Rust |
price |
number or null |
USD/spread |
Expected fill / preview price |
extra |
object |
n/a |
Unmodeled IBKR fields retained verbatim |
Example (planned)
{
"schema": "ibkr_spread_quote_v1",
"ts_utc": "2026-04-13T13:35:12.456Z",
"ts_ms": 1776087312456,
"symbol": "SPY",
"underlying_conid": 756733,
"short_leg": {
"conid": 111111,
"bid": 1.82,
"ask": 1.88,
"last": 1.85,
"delayed": false
},
"long_leg": {
"conid": 222222,
"bid": 0.94,
"ask": 0.98,
"last": 0.96,
"delayed": false
},
"net_bid": 0.84,
"net_ask": 0.94,
"net_mid": 0.89,
"has_quote": true,
"captured_at_ms": 1776087312451,
"what_if": {
"initMarginChange": "211.00",
"price": 0.89,
"amount": null,
"equity": null,
"initial": null,
"maintenance": null,
"extra": {}
},
"source": "lt-ibkr-quote-collector"
}
4. Downstream Expectations
aegis_v3/scripts/join_saxo_ibkr_quotes.py currently assumes ibkr_underlying_quotes.jsonl with top-level symbol, ts_ms, bid, ask, last, conid, source.
- When Phase 6.1 lands, spread-parity tooling should consume
ibkr_spread_quote_v1 directly rather than trying to reconstruct combo pricing from underlying-only rows.
- Any collector that emits both underlying and spread records should keep them in separate files or separate by
schema, never by filename convention alone.