ib-flex 0.2.2

Type-safe parser for Interactive Brokers (IBKR) FLEX XML statements: trades, positions, cash flows, corporate actions, and P&L.
Documentation

ib-flex

crates.io docs.rs License

Type-safe Rust parser for Interactive Brokers FLEX XML statements with comprehensive coverage and financial precision.

Features

  • Complete attribute coverage - Maps the full set of IB FLEX attributes (modeled against the mature ibflex reference), so you don't silently lose data
  • Robust on real data - Tolerates IB's real-world quirks: thousands separators, -/--/N/A sentinels, MULTI dates, Yes/No booleans, legacy date/time separators
  • Financial precision - rust_decimal for all monetary values (no floating-point error)
  • Type-safe - 19 enums covering asset classes, trade/cash/corporate-action types, transfer types, and more
  • Comprehensive sections - Trades, positions, cash, corporate actions, transfers, performance summaries, accruals, and 40+ FLEX sections in total
  • Edge cases covered - Warrants, T-Bills, CFDs, crypto, fractional shares, cancelled/corrected trades, cost-basis lots
  • Optional API client - Fetch FLEX statements programmatically from IB (api-client feature)

Installation

Add this to your Cargo.toml:

[dependencies]
ib-flex = "0.2"

Quick Start

Pull a NAV time series out of a multi-period Activity FLEX export (e.g. "Last 180 Calendar Days") and compute an annualized Sharpe ratio:

use ib_flex::parse_activity_flex_all;
use rust_decimal::prelude::ToPrimitive; // for Decimal::to_f64

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let xml = std::fs::read_to_string("flex_statement.xml")?;

    // NAV over time: the `total` of each daily equity-summary row, across all
    // statements, sorted by date. Every attribute is `Option`, so `?`-filter.
    let mut nav: Vec<(chrono::NaiveDate, f64)> = parse_activity_flex_all(&xml)?
        .iter()
        .flat_map(|stmt| stmt.equity_summary.items.iter())
        .filter_map(|e| Some((e.report_date?, e.total?.to_f64()?)))
        .collect();
    nav.sort_by_key(|(date, _)| *date);

    // Daily returns -> annualized Sharpe (risk-free = 0, ~252 trading days).
    let returns: Vec<f64> = nav.windows(2).map(|w| w[1].1 / w[0].1 - 1.0).collect();
    let mean = returns.iter().sum::<f64>() / returns.len() as f64;
    let variance = returns.iter().map(|r| (r - mean).powi(2)).sum::<f64>() / returns.len() as f64;
    let sharpe = mean / variance.sqrt() * 252f64.sqrt();

    println!("{} NAV points, annualized Sharpe: {sharpe:.2}", nav.len());
    Ok(())
}

Run it on your own export with cargo run --example nav_sharpe -- statement.xml.

Want trades or other sections instead? Parse a single statement and read any section directly — every attribute-mapped field is an Option<T> (real statements may omit any attribute):

use ib_flex::{detect_statement_type, parse_activity_flex, StatementType};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let xml = std::fs::read_to_string("flex_statement.xml")?;
    if let StatementType::Activity = detect_statement_type(&xml)? {
        let statement = parse_activity_flex(&xml)?;
        for trade in &statement.trades.items {
            println!(
                "{} {} @ {}",
                trade.symbol.as_deref().unwrap_or("?"),
                trade.quantity.unwrap_or_default(),
                trade.price.unwrap_or_default(),
            );
        }
    }
    Ok(())
}

Upgrading from 0.1.x? 0.2.0 makes all attribute-derived fields Option<T> so a statement that omits any attribute no longer fails to parse. Update call sites accordingly (e.g. statement.account_idstatement.account_id.as_deref()). See the CHANGELOG.

FLEX Query Setup

Interactive Brokers FLEX queries must be configured in the IB Client Portal:

  1. Navigate to: Reports → Flex Queries → Create Activity Flex Query
  2. Select required sections (Trades, Positions, Cash Transactions, etc.)
  3. Choose date format: ISO-8601 (yyyy-MM-dd) or compact (yyyyMMdd)
  4. Set output format to XML
  5. Save query and note the Query ID

Important: European date formats (dd/MM/yyyy) are NOT supported by the IB FLEX API.

📘 For comprehensive setup instructions, see FLEX_SETUP.md which covers all 21 recommended sections, field selections, and configuration options.

FLEX Web Service API Client (Optional)

The api-client feature provides programmatic access to fetch FLEX statements directly from Interactive Brokers without manual downloads.

Installation with API Client

[dependencies]
ib-flex = { version = "0.2", features = ["api-client"] }

API Setup

  1. Log in to IB Account Management
  2. Navigate to: Reports → Settings → FlexWeb Service
  3. Generate a FLEX Web Service token (keep it secure!)
  4. Note your FLEX Query ID from the Flex Queries page

Usage Example

use ib_flex::api::FlexApiClient;
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create client with your token
    let client = FlexApiClient::new("YOUR_TOKEN");

    // Step 1: Send request with your query ID
    let reference_code = client.send_request("123456").await?;

    // Step 2: Get statement with automatic retry
    let xml = client.get_statement_with_retry(
        &reference_code,
        10,                           // max retries
        Duration::from_secs(2)        // delay between retries
    ).await?;

    // Step 3: Parse the statement
    let statement = ib_flex::parse_activity_flex(&xml)?;
    println!("Trades: {}", statement.trades.items.len());

    Ok(())
}

API Examples

Run the API examples (requires IB credentials):

export IB_FLEX_TOKEN="your_token"
export IB_FLEX_QUERY_ID="your_query_id"
cargo run --example fetch_flex_statement --features api-client
cargo run --example api_simple_usage --features api-client
cargo run --example api_with_retry --features api-client

Supported FLEX Sections

Activity FLEX - Core Types

  • Trades - Executions with 90+ fields including P&L, commissions, dates, IDs, security details
  • Trade sub-rows - <Lot> cost-basis detail and <Order>/<SymbolSummary>/<AssetSummary> roll-ups captured from inside <Trades>
  • Open Positions - Current holdings
  • Cash Transactions - Deposits, withdrawals, interest, fees, dividends
  • Corporate Actions - Splits, mergers, spinoffs, dividends
  • Securities Info - Reference data for all traded instruments
  • FX Conversion Rates - Currency conversion rates for multi-currency accounts

Extended FLEX Sections

  • Account Information, Change in NAV, Equity Summary, Cash Report
  • Trade Confirmations - Real-time trade execution confirmations
  • Option EAE - Option exercises, assignments, and expirations
  • FX Transactions & FX Lots - Foreign exchange conversions and lots
  • Dividend Accruals (open + change) and Interest Accruals (incl. tier detail)
  • Transfers - Transfers, trade transfers, unsettled transfers (ACATS, ATON, FOP, internal, OTC, ...)
  • Performance summaries - MTM, FIFO, and MTD/YTD
  • Securities lending - SLB activity, fees, hard-to-borrow, and open contracts
  • Fees & commissions - Client fees, unbundled commission detail, transaction taxes, sales tax
  • Other - Net stock positions, prior-period positions, statement of funds, change-in-position value, debit-card activity, CFD charges, stock-grant activity

Asset Classes Supported

  • Stocks (STK) - Including fractional shares
  • Options (OPT) - Calls, puts, assignments, expirations
  • Futures (FUT) - All major contracts (ES, NQ, CL, GC, etc.)
  • Forex (CASH) - FX trades and positions
  • Bonds (BOND) - Treasuries, corporate, municipal
  • Treasury Bills (BILL) - With maturity handling
  • Warrants (WAR) - Equity warrants
  • CFDs (CFD / FXCFD) - Contract for difference
  • Crypto (CRYPTO) - Cryptocurrency
  • Funds, Commodities, and more - 22 asset categories total

Trade Confirmation FLEX

  • Trade Confirmations - Real-time trade execution data
  • All trade fields - Full support for all trade attributes
  • Automatic detection - Detect statement type from XML

Performance

Benchmarked on M1 MacBook Pro:

Statement Type Transactions Parse Time
Minimal 1 trade ~6.5 µs
Options 4 trades ~65 µs
Cash 15 transactions ~71 µs

The full multi-statement test corpus (142 statements, ~2,600 trades) parses in a few milliseconds.

Type Safety

All financial values use rust_decimal::Decimal for exact arithmetic without floating-point error. The public enums include:

  • AssetCategory (22 variants) - STK, OPT, FUT, FOP, CASH, BOND, BILL, CFD, FXCFD, WAR, FUND, CMDTY, CRYPTO, FSFOP, FSOPT, ...
  • BuySell, OpenClose, PutCall, LongShort - trade direction and option basics
  • TradeType - ExchTrade, BookTrade, TradeCancel, TradeCorrect, FracShare, ...
  • OrderType - market, limit, stop, stop-limit, trailing, ...
  • CashTransactionType - dividends, interest, fees, deposits/withdrawals, ...
  • SecurityIdType - CUSIP, ISIN, FIGI, SEDOL
  • SubCategory - ETF, ADR, REIT, Preferred, Common, ...
  • LevelOfDetail - Summary, Detail, Execution, Lot
  • DerivativeInfo - consolidated option/future/warrant metadata via Trade::derivative()

Examples

The repository includes several complete example programs:

Parsing Examples

  1. nav_sharpe.rs - NAV time series + annualized Sharpe ratio (the Quick Start example)
  2. parse_activity_statement.rs - Basic Activity FLEX parsing and display
  3. filter_trades.rs - Filter by asset class, side, symbol, quantity, P&L, date range
  4. calculate_commissions.rs - Analyze commission costs by category
  5. parse_trade_confirmation.rs - Trade Confirmation FLEX parsing

Run parsing examples:

cargo run --example parse_activity_statement
cargo run --example parse_trade_confirmation
cargo run --example filter_trades
cargo run --example calculate_commissions

Historical Backfill Example

  1. backfill_summary.rs - Parse multi-statement FLEX XML and display comprehensive summary

To use this example:

  1. Create a FLEX query with Period: Last 180 Calendar Days (or similar) in IBKR Client Portal - see FLEX_SETUP.md
  2. Download the XML file manually or via API
  3. Run the summary:
cargo run --example backfill_summary -- path/to/your/backfill.xml

Output includes:

  • NAV over time with returns, high/low, drawdown
  • Trading activity by symbol with P&L attribution
  • Cash transaction breakdown (dividends, interest, fees)
  • Current positions from latest statement
  • Monthly returns table

API Client Examples (requires api-client feature)

  1. fetch_flex_statement.rs - Complete API workflow with detailed output
  2. api_simple_usage.rs - Minimal API client usage
  3. api_with_retry.rs - API client with automatic retry logic

Run API examples:

export IB_FLEX_TOKEN="your_token"
export IB_FLEX_QUERY_ID="your_query_id"
cargo run --example fetch_flex_statement --features api-client

Development

Build

cargo build

Test

cargo test

Benchmark

cargo bench

Format

cargo fmt

Lint

cargo clippy --all-targets -- -D warnings

Testing

The library has comprehensive test coverage including:

  • Integration tests covering all asset classes and edge cases
  • Extended types tests for FLEX sections
  • Error tests for malformed XML and invalid data
  • Unit tests for custom deserializers
  • Doc tests in inline documentation
  • XML fixtures including extended types, warrants, T-Bills, CFDs, fractional shares, cancelled trades

Reliability Testing

The library includes comprehensive reliability tests using:

  • Property-based testing with proptest for random inputs
  • Stress tests for large XML files
  • Concurrency tests for thread safety
  • Memory efficiency tests for repeated parsing
  • Edge case fuzzing for malformed inputs

Run tests with:

cargo test           # All tests
cargo test --doc     # Documentation tests only

Contributing

Contributions welcome! This is an open-source project designed to benefit the Rust trading community.

Before submitting a PR:

  1. Ensure all tests pass: cargo test
  2. Run clippy: cargo clippy --all-targets -- -D warnings
  3. Format code: cargo fmt
  4. Add tests for new features
  5. Update CHANGELOG.md

Bug reports and feature requests are appreciated. When reporting bugs, please include:

  • Anonymized XML sample demonstrating the issue
  • Expected vs actual behavior
  • Rust version and platform

Documentation

Resources

License

Licensed under the MIT License.

Acknowledgments