Login
The smallest program that opens a session: connect, read the order id, disconnect.
Two flavours are shown. Paper for everyday testing, live for read-only checks against the real account.
What this shows
- Reading credentials from environment variables.
EClient.connect(username=, password=, host=, paper=).- Starting
EClient.runon a daemon thread. - Reading the order id off the
next_valid_idcallback.
What comes back
connect() blocks until the session is up, and fires three callbacks on your
wrapper before it returns, on the calling thread: connect_ack,
managed_accounts, then next_valid_id.
next_valid_id is fired last on purpose. The venue names what the account has
already used just after the connection is made, and an id announced before that
lands is one a fill spent long ago. A program that trusted it would have its
first order refused as a duplicate.
So when connect() returns, the session is established and the id has already
been delivered. The wait() in the example is belt and braces, not a gate.
The daemon run() thread is what carries every callback after connect: quotes,
order status, bars, errors. Start it, or nothing further arrives.
Limits
A live login may push a second factor. Approve it on your authenticator;
connect() blocks until the gate clears, so give it a longer wall clock than a
paper login needs.
The live example is read-only: it logs in, prints, and disconnects. Send orders from the paper account.
Paper
Run it
IB_USERNAME=... IB_PASSWORD=... python examples/hello_login.py
Source
"""Hello-world recipe: connect, wait for next_valid_id, disconnect.
Usage:
IB_USERNAME=... IB_PASSWORD=... python examples/hello_login.py
"""
import os
import threading
from ib_dx import EClient, EWrapper
class LoginWrapper(EWrapper):
def __init__(self):
self.ready = threading.Event()
self.order_id = None
def next_valid_id(self, order_id):
self.order_id = order_id
self.ready.set()
w = LoginWrapper()
c = EClient(w)
c.connect(
username=os.environ["IB_USERNAME"],
password=os.environ["IB_PASSWORD"],
paper=True,
)
threading.Thread(target=c.run, daemon=True).start()
if not w.ready.wait(timeout=15):
raise RuntimeError("did not receive next_valid_id")
print(f"logged in. next_valid_id = {w.order_id}")
c.disconnect()
Live
Run it
IB_LIVE_USERNAME=... IB_LIVE_PASSWORD=... python examples/hello_login_live.py
Source
"""Live-account login recipe: connect with paper=False, wait for next_valid_id,
disconnect. Read-only — no orders, no market data.
When the live login triggers a second-factor push, approve it on your mobile
authenticator. The connect call blocks until the gate clears.
Usage:
IB_LIVE_USERNAME=... IB_LIVE_PASSWORD=... python examples/hello_login_live.py
"""
import os
import threading
from ib_dx import EClient, EWrapper
class LoginWrapper(EWrapper):
def __init__(self):
self.ready = threading.Event()
self.order_id = None
def next_valid_id(self, order_id):
self.order_id = order_id
self.ready.set()
w = LoginWrapper()
c = EClient(w)
c.connect(
username=os.environ["IB_LIVE_USERNAME"],
password=os.environ["IB_LIVE_PASSWORD"],
host=os.environ.get("IB_HOST", ""),
paper=False,
)
threading.Thread(target=c.run, daemon=True).start()
if not w.ready.wait(timeout=60):
raise RuntimeError("did not receive next_valid_id")
print(f"logged in LIVE. next_valid_id = {w.order_id}")
c.disconnect()