ibapi 3.0.1

A Rust implementation of the Interactive Brokers TWS API, providing a reliable and user friendly interface for TWS and IB Gateway. Designed with a focus on simplicity and performance.
Documentation
//! Market Data example
//!
//! This example demonstrates how to subscribe to market data using the builder API.
//!
//! # Usage
//!
//! ```bash
//! cargo run --features sync --example market_data
//! ```

use ibapi::client::blocking::Client;
use ibapi::prelude::*;

fn main() {
    env_logger::init();

    let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");

    let contract = Contract::stock("AAPL").build();

    println!("Market Data Examples for AAPL\n");
    println!("{}", "=".repeat(50));

    // Example 1: Basic streaming with specific tick types
    example_streaming_with_tick_types(&client, &contract);

    println!("\n{}\n", "=".repeat(50));

    // Example 2: Request a one-time snapshot
    example_snapshot(&client, &contract);

    println!("\n{}\n", "=".repeat(50));

    // Example 3: Demonstrating builder method chaining
    example_builder_chaining(&client, &contract);

    println!("\n{}\n", "=".repeat(50));

    // Example 4: Observing subscription notices via SubscriptionItem
    example_observe_notices(&client, &contract);

    println!("\nAll examples completed!");
}

fn example_streaming_with_tick_types(client: &Client, contract: &Contract) {
    println!("Example 1: Streaming with specific tick types\n");

    // https://www.interactivebrokers.com/campus/ibkr-api-page/twsapi-doc/#available-tick-types
    let subscription = client
        .market_data(contract)
        .generic_ticks(&["233", "236", "293"]) // RTVolume, Shortable, Trade Count
        .subscribe()
        .expect("Failed to subscribe to market data");

    let mut tick_count = 0;
    for tick in subscription.iter_data() {
        let tick = match tick {
            Ok(tick) => tick,
            Err(e) => {
                eprintln!("error: {e}");
                break;
            }
        };
        match tick {
            TickTypes::Price(price) => {
                println!("Price - Type: {:?}, Value: ${:.2}", price.tick_type, price.price);
            }
            TickTypes::Size(size) => {
                println!("Size - Type: {:?}, Value: {:.0}", size.tick_type, size.size);
            }
            TickTypes::PriceSize(price_size) => {
                println!(
                    "PriceSize - PriceType: {:?}, Price: ${:.2}, Size: {:.0}",
                    price_size.price_tick_type, price_size.price, price_size.size
                );
            }
            TickTypes::String(string) => {
                println!("String - Type: {:?}, Value: {}", string.tick_type, string.value);
            }
            TickTypes::Generic(generic) => {
                println!("Generic - Type: {:?}, Value: {:.2}", generic.tick_type, generic.value);
            }
            TickTypes::MarketDataType(data_type) => {
                println!("Active market data type: {data_type:?}");
            }
            _ => {}
        }

        tick_count += 1;
        if tick_count >= 10 {
            println!("\nReceived 10 ticks, cancelling subscription...");
            subscription.cancel();
            break;
        }
    }
}

fn example_snapshot(client: &Client, contract: &Contract) {
    println!("Example 2: One-time snapshot\n");

    let snapshot_subscription = client.market_data(contract).snapshot().subscribe().expect("Failed to request snapshot");

    for tick in snapshot_subscription.iter_data() {
        let tick = match tick {
            Ok(tick) => tick,
            Err(e) => {
                eprintln!("error: {e}");
                break;
            }
        };
        match tick {
            TickTypes::Price(price) => {
                println!("Snapshot Price - Type: {:?}, Value: ${:.2}", price.tick_type, price.price);
            }
            TickTypes::Size(size) => {
                println!("Snapshot Size - Type: {:?}, Value: {:.0}", size.tick_type, size.size);
            }
            TickTypes::SnapshotEnd => {
                println!("\nSnapshot completed!");
                break;
            }
            _ => {}
        }
    }
}

fn example_builder_chaining(client: &Client, contract: &Contract) {
    println!("Example 3: Builder method chaining\n");
    println!("Demonstrating how builder methods can be combined and overridden\n");

    // The builder pattern allows chaining multiple configuration methods.
    // Later method calls override earlier ones for the same setting.
    let subscription = client
        .market_data(contract)
        .generic_ticks(&["100", "101", "104"]) // Option Volume, Open Interest, Historical Vol
        .snapshot() // Initially set to snapshot mode
        .streaming() // Override to streaming mode (this takes precedence)
        .subscribe()
        .expect("Failed to subscribe");

    println!("This subscription is in streaming mode (not snapshot)");
    println!("Listening for generic ticks...\n");

    let mut tick_count = 0;
    for tick in subscription.iter_data() {
        let tick = match tick {
            Ok(tick) => tick,
            Err(e) => {
                eprintln!("error: {e}");
                break;
            }
        };
        if let TickTypes::Generic(generic) = tick {
            println!("Generic tick - Type: {:?}, Value: {:.2}", generic.tick_type, generic.value);
            tick_count += 1;
            if tick_count >= 5 {
                println!("\nReceived 5 generic ticks, cancelling...");
                subscription.cancel();
                break;
            }
        }
    }
}

// Demonstrates pattern-matching on `SubscriptionItem` directly. The earlier
// examples filter notices via `iter_data()`; here we use `iter()` so notices
// are surfaced as `SubscriptionItem::Notice(_)`. UIs that show connection /
// farm-status indicators want this shape.
fn example_observe_notices(client: &Client, contract: &Contract) {
    println!("Example 4: Observing notices via SubscriptionItem\n");

    let subscription = client
        .market_data(contract)
        .generic_ticks(&["233"])
        .subscribe()
        .expect("Failed to subscribe to market data");

    for event in subscription.iter().take(8) {
        match event {
            Ok(SubscriptionItem::Data(_)) => println!("data tick"),
            // Common codes: 2104 (Market data farm OK), 2107 (HMDS data farm
            // is inactive), 2108 (data farm connection broken).
            Ok(SubscriptionItem::Notice(n)) => println!("notice [{}] {}", n.code, n.message),
            Err(e) => {
                eprintln!("subscription terminated: {e}");
                break;
            }
        }
    }
}