The V2 Contract Builder API provides a type-safe, ergonomic way to create contracts for various financial instruments. It leverages Rust's type system to prevent invalid contracts at compile time while offering an intuitive, discoverable interface.
- Key Features
- Quick Start
- Contract Types
- Strong Types
- Type-State Pattern
- Error Handling
- Migration from V1
- Best Practices
- Type Safety: Required fields are enforced at compile time
- Zero Invalid States: Impossible to build incomplete contracts
- Smart Defaults: Sensible defaults for exchanges, currencies, and multipliers
- Discoverable API: IDE autocomplete guides you through the process
- Strongly Typed: No more string typos for exchanges, currencies, or option rights
use ibapi::contracts::Contract;
// Simple stock contract
let aapl = Contract::stock("AAPL").build();
// Stock with customization
let toyota = Contract::stock("7203")
.on_exchange("TSEJ")
.in_currency("JPY")
.build();The simplest contract type. Defaults to SMART routing and USD currency.
use ibapi::contracts::Contract;
// Basic stock - uses SMART routing and USD
let stock = Contract::stock("AAPL").build();
// European stock - disambiguate which listing we want
let european_stock = Contract::stock("SAN")
.on_exchange("SMART") // Use SMART routing for best execution
.primary("IBIS") // But we want the IBIS listing (not Madrid, etc.)
.in_currency("EUR")
.build();
// Stock with trading class
let stock_with_class = Contract::stock("IBKR")
.trading_class("NMS")
.build();on_exchange()- Where to route your order (e.g., SMART, NASDAQ, NYSE)primary()- Which listing of the stock you want (for disambiguation)
Common patterns:
// US stock - usually no primary_exchange needed
let us_stock = Contract::stock("AAPL")
.on_exchange("SMART") // Route via SMART (default)
.build();
// Dual-listed stock - specify which listing
let dual_listed = Contract::stock("BMW")
.on_exchange("SMART") // Route via SMART for best price
.primary("IBIS") // We want the German listing, not another
.in_currency("EUR")
.build();
// Direct routing to specific exchange
let direct_route = Contract::stock("BMW")
.on_exchange("IBIS") // Route directly to IBIS
.primary("IBIS") // And it's the IBIS listing we want
.in_currency("EUR")
.build();Options require strike price and expiration date. The builder enforces these at compile time.
use ibapi::contracts::{Contract, ExpirationDate};
// Call option
let call = Contract::call("AAPL")
.strike(150.0) // Validates positive strike price
.expires_on(2024, 12, 20)
.build();
// Put option with custom exchange
let put = Contract::put("SPY")
.strike(450.0)
.expires(ExpirationDate::new(2024, 3, 15))
.on_exchange("CBOE")
.multiplier(100)
.build();
// Weekly options (expires next Friday)
let weekly_call = Contract::call("QQQ")
.strike(400.0)
.expires_weekly()
.build();
// Monthly options (third Friday of the month)
let monthly_put = Contract::put("IWM")
.strike(200.0)
.expires_monthly()
.build();
// Note: These won't compile (type safety!)
// let invalid = Contract::call("AAPL").build(); // Missing strike and expiry
// let invalid = Contract::call("AAPL").strike(150.0).build(); // Missing expiryFutures contracts with flexible expiration options.
use ibapi::contracts::{Contract, ContractMonth};
// Front month contract (next expiring)
let es_front = Contract::futures("ES")
.front_month()
.build();
// Next quarterly expiration (Mar/Jun/Sep/Dec)
let nq_quarter = Contract::futures("NQ")
.next_quarter()
.build();
// Futures with specific expiration
let cl_futures = Contract::futures("CL")
.expires_in(ContractMonth::new(2024, 6))
.build();
// Custom futures on specific exchange
let zc_futures = Contract::futures("ZC")
.expires_in(ContractMonth::new(2024, 12))
.on_exchange("ECBOT")
.build();Note: The futures builder leaves the multiplier field empty by default, allowing TWS to determine the correct value. Use .multiplier() only when needed for non-standard contracts.
Continuous futures cannot be used with real time data or to place orders, but only for historical data.
use ibapi::contracts::Contract;
// ES contract
let es_front = Contract::continuous_futures("ES")
.on_exchange("CME")
.build();
// Custom futures on specific exchange
let zc_futures = Contract::continuous_futures("ZC")
.on_exchange("ECBOT")
.build();Foreign exchange pairs with automatic pair formatting.
use ibapi::contracts::Contract;
// EUR/USD pair
let eur_usd = Contract::forex("EUR", "USD").build();
// GBP/JPY with custom exchange
let gbp_jpy = Contract::forex("GBP", "JPY")
.on_exchange("IDEALPRO")
.build();Digital assets with Paxos as the default exchange.
use ibapi::contracts::Contract;
// Bitcoin
let btc = Contract::crypto("BTC")
.build(); // Defaults to PAXOS exchange and USD
// Ethereum with custom settings
let eth = Contract::crypto("ETH")
.in_currency("EUR")
.on_exchange("PAXOS")
.build();Market indices with smart exchange and currency defaults.
use ibapi::contracts::Contract;
// S&P 500 - automatically uses CBOE exchange and USD
let spx = Contract::index("SPX");
// DAX - automatically uses EUREX exchange and EUR
let dax = Contract::index("DAX");
// FTSE - automatically uses FTSE exchange and GBP
let ftse = Contract::index("FTSE");
// Custom index
let custom = Contract::index("VIX"); // Defaults to SMART/USDBond contracts can be created using CUSIP or ISIN identifiers.
use ibapi::contracts::Contract;
// US Treasury bond by CUSIP
let treasury = Contract::bond_cusip("912810RN0");
// European bond by ISIN
let euro_bond = Contract::bond_isin("DE0001102309");
// Corporate bond by CUSIP
let corporate = Contract::bond_cusip("037833100"); // Apple bondThe bond builder automatically:
- Sets the correct security ID type (CUSIP or ISIN)
- Determines currency based on ISIN country code
- Uses SMART exchange routing
Complex multi-leg strategies with type-safe leg construction.
use ibapi::contracts::{Contract, LegAction};
// Vertical spread
let vertical = Contract::spread()
.vertical(11111, 22222) // Long and short contract IDs
.build()?;
// Iron condor using convenience method
let iron_condor = Contract::spread()
.iron_condor(
10001, // Long put
10002, // Short put
10003, // Short call
10004 // Long call
)
.build()?;
// Custom butterfly spread
let butterfly = Contract::spread()
.add_leg(30001, LegAction::Buy) // Buy 1 lower strike
.ratio(1)
.done()
.add_leg(30002, LegAction::Sell) // Sell 2 middle strike
.ratio(2)
.done()
.add_leg(30003, LegAction::Buy) // Buy 1 higher strike
.ratio(1)
.done()
.build()?;The V2 API uses strong types instead of strings to prevent errors:
IBKR supports 160+ exchanges worldwide. The Exchange type provides constants for common exchanges and supports any exchange code.
use ibapi::contracts::Exchange;
// Create exchanges from string literals
let exchange = Exchange::from("SMART"); // Smart routing
let exchange = Exchange::from("NASDAQ"); // NASDAQ
let exchange = Exchange::from("NYSE"); // NYSE
let exchange = Exchange::from("CBOE"); // CBOE
let exchange = Exchange::from("GLOBEX"); // CME Globex
let exchange = Exchange::from("IDEALPRO"); // Forex
let exchange = Exchange::from("PAXOS"); // Crypto
// Any other exchange code
let exchange = Exchange::from("ARCA"); // NYSE Arca
let exchange = Exchange::from("IBIS"); // XETRA
let exchange = Exchange::from("SEHK"); // Hong Kong
// Builder methods accept string literals directly
let contract = Contract::stock("AAPL")
.on_exchange("NASDAQ") // String literal converted automatically
.build();IBKR supports trading in many currencies. The Currency type provides constants for major currencies and supports any currency code.
use ibapi::contracts::Currency;
// Create currencies from string literals
let currency = Currency::from("USD"); // US Dollar
let currency = Currency::from("EUR"); // Euro
let currency = Currency::from("GBP"); // British Pound
let currency = Currency::from("JPY"); // Japanese Yen
let currency = Currency::from("CHF"); // Swiss Franc
let currency = Currency::from("CAD"); // Canadian Dollar
let currency = Currency::from("AUD"); // Australian Dollar
let currency = Currency::from("HKD"); // Hong Kong Dollar
let currency = Currency::from("CNH"); // Offshore RMB
// Any other currency code
let currency = Currency::from("SEK"); // Swedish Krona
let currency = Currency::from("NOK"); // Norwegian Krone
let currency = Currency::from("INR"); // Indian Rupee
// Builder methods accept string literals directly
let contract = Contract::stock("7203")
.in_currency("JPY") // String literal converted automatically
.build();use ibapi::contracts::OptionRight;
let right = OptionRight::Call; // Converts to "C"
let right = OptionRight::Put; // Converts to "P"The builders use Rust's type system to track required fields:
use ibapi::contracts::{Contract, Missing, Symbol, Strike, ExpirationDate};
// The option builder tracks which fields are set using phantom types
let builder1: OptionBuilder<Symbol, Missing, Missing> = Contract::call("AAPL");
let builder2: OptionBuilder<Symbol, Strike, Missing> = builder1.strike(150.0);
let builder3: OptionBuilder<Symbol, Strike, ExpirationDate> = builder2.expires_on(2024, 12, 20);
let contract = builder3.build(); // Build only available when all required fields are setThe API validates inputs and returns errors for invalid data:
use ibapi::contracts::{Contract, Strike};
// Strike price validation
let result = Strike::new(-10.0);
assert!(result.is_err()); // Negative strikes are invalid
// Spread validation
let empty_spread = Contract::spread().build();
assert!(empty_spread.is_err()); // Spreads need at least one leg
// Option validation happens at compile time
// This won't compile:
// let invalid = Contract::call("AAPL").build(); // Missing required fieldsIf you're upgrading from the old ContractBuilder API:
use ibapi::contracts::{ContractBuilder, SecurityType};
let contract = ContractBuilder::new()
.symbol("AAPL")
.security_type(SecurityType::Stock)
.exchange("SMART")
.currency("USD")
.build()?;use ibapi::contracts::Contract;
let contract = Contract::stock("AAPL").build();- Entry Points: Use
Contract::stock(),Contract::call(), etc. instead ofContractBuilder::new() - Type Safety: Required fields enforced at compile time
- Strong Types: Use
Exchange::from("SMART")for type safety or pass string literals directly to builder methods - Smart Defaults: Less boilerplate for common cases
- No Runtime Validation: Invalid states prevented by the type system
- Use specific entry points: Start with
Contract::stock(),Contract::call(), etc. for clarity - Let the compiler help: Missing required fields will be caught at compile time
- Flexible types: Builder methods accept string literals directly (e.g.,
.on_exchange("SMART")), or useExchange::from("SMART")for explicit type creation - Leverage defaults: Don't specify values that match the defaults
- Handle errors appropriately: Strike validation and spread building return
Result