Login

The smallest possible IBX program: connect, wait for next_valid_id, disconnect.

Two flavors are shown below — paper for everyday testing and live for read-only validation against your real account.

Per the project rules, never send orders from a live account. Use live only for read-only checks (login, contract details).

What this shows

  • Reading credentials from environment variables.
  • Building an EClientConfig with paper: true (paper) or paper: false (live).
  • Receiving next_valid_id — the signal that the session is fully established and ready for requests.

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 ibx::api::client::{EClient, EClientConfig};
use ibx::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")?,
        host: "cdc1.ibllc.com".into(),
        paper: true,
        core_id: None,
    })?;

    let mut wrapper = LoginWrapper::default();
    client.req_ids(&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

The live login may trigger a second-factor push. Approve it on your mobile authenticator when prompted — the connect call blocks until the gate clears.

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 ibx::api::client::{EClient, EClientConfig};
use ibx::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_else(|_| "cdc1.ibllc.com".into()),
        paper: false,
        core_id: None,
    })?;

    let mut wrapper = LoginWrapper::default();
    client.req_ids(&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(())
}