ibapi 5.4.0

An unofficial Rust client for the Interactive Brokers TWS API: market data, orders, and account data, with async (Tokio) and blocking clients for TWS and IB Gateway.
Documentation
# Build and Test Guide

> **`--features sync` is not the sync-only build.** `default = ["async"]` and cargo features
> are additive, so `--features sync` gives you sync **and** async. Every sync command below
> therefore uses `--no-default-features --features sync`. See
> [docs/rules/parity/feature-matrix.md]rules/parity/feature-matrix.md.

## Build Commands

### Basic Build
```bash
# Build with async support (async is the default feature)
cargo build

# Build with sync support
cargo build --no-default-features --features sync

# Release build with optimizations
cargo build --release
cargo build --release --no-default-features --features sync

# Build all targets including examples
cargo build --all-targets
cargo build --all-targets --no-default-features --features sync
```

### Running Tests

```bash
# Run async tests (async is the default feature)
cargo test

# Run sync tests
cargo test --no-default-features --features sync

# Run specific test
cargo test test_name --no-default-features --features sync

# Test specific module
cargo test --package ibapi module_name:: --no-default-features --features sync

# Run with output
cargo test --no-default-features --features sync -- --nocapture

# Run doctests only
cargo test --doc --no-default-features --features sync
```

### Code Quality

```bash
# Format code
cargo fmt

# Check formatting
cargo fmt --check

# Run clippy — one leg per feature configuration, all with --all-targets.
# `--features sync` alone leaves the default async feature on, so it is not
# the sync-only build. Same three legs as the pre-PR gate below.
cargo clippy --all-targets -- -D warnings
cargo clippy --all-targets --no-default-features --features sync -- -D warnings
cargo clippy --all-targets --all-features -- -D warnings

# Generate coverage report (nightly is required for --doctests)
cargo +nightly llvm-cov --all-features --doctests --html --open
# or using just
just cover
```

## Testing Patterns

See [docs/testing-patterns.md](testing-patterns.md) for the full fixture stratification (`MessageBusStub` for domain logic, `MemoryStream` for transport/connection, `spawn_handshake_listener` for `Client::connect*`). The short version: pick the lightest fixture that exercises the seam.

### Domain test pattern (`MessageBusStub`)

```rust
let message_bus = Arc::new(MessageBusStub::with_ordered_responses(vec![proto_response(
    IncomingMessages::OrderStatus,
    order_status().order_id(1).status(OrderStatusKind::Submitted).encode_proto(),
)]));
let client = Client::stubbed(message_bus.clone(), server_versions::SIZE_RULES);
let result = client.some_method()?;
// assert request_messages records the encoded request
// assert result decoded the scripted response
```

Responses are proto-framed: `proto_response(...)` takes bytes from a field-minimal builder in `src/testdata/builders/<domain>.rs`. A text-framed fixture reaching a proto-only decoder fails the subscription with `Error::UnexpectedWireFormat`.

### Table-Driven Tests

Use shared test tables for comprehensive coverage:

```rust
// common/test_tables.rs
pub fn market_rule_test_cases() -> Vec<MarketRuleTestCase> {
    vec![
        MarketRuleTestCase { name: "valid_request", market_rule_id: 26, ordered_responses: vec![/* … */], /* … */ },
        // more cases...
    ]
}

// In sync_tests.rs and async_tests.rs
#[test]
fn market_rule() {
    for case in market_rule_test_cases() {
        // run the request, assert on the result
    }
}
```

### Testing Outgoing Requests

Compare the whole encoded request against a request builder from `src/testdata/builders/`:

```rust
#[test]
fn market_rule_request() {
    let (client, message_bus) = create_test_client_with_ordered_proto_responses(vec![proto_response(
        IncomingMessages::MarketRule,
        market_rule(26).encode_proto(),
    )]);
    client.market_rule(26).expect("market_rule failed");

    assert_request(&message_bus, 0, &market_rule_request().market_rule_id(26));
}
```

## Running Tests for Every Configuration

Three configurations, not two — async-only, sync-only, and both-plus-`utoipa-6`. A type or impl
can compile in two of them and fail the third:

```bash
# Using just command (runs all three)
just test

# Or manually
cargo test                                          # async only (default)
cargo test --no-default-features --features sync     # sync only
cargo test --all-features                            # sync + async + utoipa-6

# Test everything (tests + clippy + fmt)
cargo fmt --check && \
cargo clippy --all-targets -- -D warnings && \
cargo clippy --all-targets --no-default-features --features sync -- -D warnings && \
cargo clippy --all-targets --all-features -- -D warnings && \
cargo test && \
cargo test --no-default-features --features sync && \
cargo test --all-features
```

This chain does not cover intra-doc links or the integration crates. For the full pre-PR gate,
see [pre-PR checks](rules/workflow/pre-pr-checks.md).

## Continuous Integration

`ci.yml` runs one matrix leg per configuration — `async`, `sync`, and `all-features` — and each
leg runs the full sequence:

1. **Formatting**: `cargo fmt --check`
2. **Linting**: `cargo clippy --all-targets … -- -D warnings`
3. **Tests**: `cargo test`
4. **Documentation**: `cargo doc --no-deps` with `RUSTDOCFLAGS=-D warnings`, so a broken
   intra-doc link fails the leg; see [pre-PR checks]rules/workflow/pre-pr-checks.md
5. **Examples**: `cargo build --examples`
6. **Benches**: `cargo check --benches` (non-blocking)

The legs spell their flags out rather than interpolating a feature name, because
`--features sync` would silently leave the async client enabled — the bug that let a sync-only
break sit unnoticed through 11 merges (#658 → #671).

## Performance Testing

For performance-critical code:

```bash
# Run benchmarks
cargo bench --no-default-features --features sync

# Profile with flamegraph
cargo flamegraph --no-default-features --features sync --example market_data
```

## Debugging

### Enable Debug Logging
```bash
RUST_LOG=debug cargo test --no-default-features --features sync -- --nocapture
RUST_LOG=ibapi=trace cargo run --example connect
```

### Record TWS Messages
```bash
IBAPI_RECORDING_DIR=/tmp/tws-messages cargo run --example market_data
```

This creates timestamped files with all TWS communication for debugging protocol issues.