ibapi 5.6.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
use crate::common::test_utils::helpers::proto_response;
use crate::messages::IncomingMessages;
use crate::messages::ResponseMessage;
use crate::testdata::builders::market_data::historical_schedule_response;
use crate::testdata::builders::ResponseProtoEncoder;

fn schedule_response() -> Vec<ResponseMessage> {
    vec![proto_response(
        IncomingMessages::HistoricalSchedule,
        historical_schedule_response().encode_proto(),
    )]
}

#[cfg(feature = "sync")]
mod sync_tests {
    use std::sync::Arc;
    use time::macros::datetime;

    use super::schedule_response;
    use crate::client::blocking::Client;
    use crate::common::test_utils::helpers::{assert_request, request_message_count};
    use crate::contracts::Contract;
    use crate::market_data::historical::{BarSize, Duration, ToDuration, WhatToShow};
    use crate::market_data::TradingHours;
    use crate::server_versions;
    use crate::stubs::MessageBusStub;
    use crate::testdata::builders::market_data::historical_data_request;

    fn client_with_schedule() -> (Client, Arc<MessageBusStub>) {
        let bus = Arc::new(MessageBusStub::with_ordered_responses(schedule_response()));
        let client = Client::stubbed(bus.clone(), server_versions::PROTOBUF_HISTORICAL_DATA);
        (client, bus)
    }

    #[test]
    fn defaults_anchor_at_now() {
        let (client, bus) = client_with_schedule();
        let contract = Contract::stock("AAPL").build();

        client.historical_schedules(&contract, 7.days()).fetch().expect("fetch failed");

        assert_eq!(request_message_count(&bus), 1);
        assert_request(
            &bus,
            0,
            &historical_data_request()
                .contract(&contract)
                .end_date(None)
                .duration(Duration::days(7))
                .bar_size(BarSize::Day)
                .what_to_show(WhatToShow::Schedule)
                .use_rth(true),
        );
    }

    #[test]
    fn ending_anchors_at_explicit_date() {
        let (client, bus) = client_with_schedule();
        let contract = Contract::stock("AAPL").build();
        let end = datetime!(2026-04-15 0:00 UTC);

        client
            .historical_schedules(&contract, 30.days())
            .ending(end)
            .fetch()
            .expect("fetch failed");

        assert_request(
            &bus,
            0,
            &historical_data_request()
                .contract(&contract)
                .end_date(Some(end))
                .duration(Duration::days(30))
                .bar_size(BarSize::Day)
                .what_to_show(WhatToShow::Schedule)
                .use_rth(true),
        );
    }

    #[test]
    fn trading_hours_sets_use_rth() {
        for (name, trading_hours, use_rth) in [
            ("explicit Regular", TradingHours::Regular, true),
            ("Extended", TradingHours::Extended, false),
        ] {
            let (client, bus) = client_with_schedule();
            let contract = Contract::stock("AAPL").build();

            client
                .historical_schedules(&contract, 7.days())
                .trading_hours(trading_hours)
                .fetch()
                .unwrap_or_else(|e| panic!("{name}: fetch failed: {e}"));

            assert_request(
                &bus,
                0,
                &historical_data_request()
                    .contract(&contract)
                    .end_date(None)
                    .duration(Duration::days(7))
                    .bar_size(BarSize::Day)
                    .what_to_show(WhatToShow::Schedule)
                    .use_rth(use_rth),
            );
        }
    }
}

#[cfg(feature = "async")]
mod async_tests {
    use std::sync::Arc;
    use time::macros::datetime;

    use super::schedule_response;
    use crate::client::r#async::Client;
    use crate::common::test_utils::helpers::assert_request;
    use crate::contracts::Contract;
    use crate::market_data::historical::{BarSize, Duration, ToDuration, WhatToShow};
    use crate::market_data::TradingHours;
    use crate::server_versions;
    use crate::stubs::MessageBusStub;
    use crate::testdata::builders::market_data::historical_data_request;

    fn client_with_schedule() -> (Client, Arc<MessageBusStub>) {
        let bus = Arc::new(MessageBusStub::with_ordered_responses(schedule_response()));
        let client = Client::stubbed(bus.clone(), server_versions::PROTOBUF_HISTORICAL_DATA);
        (client, bus)
    }

    #[tokio::test]
    async fn defaults_anchor_at_now() {
        let (client, bus) = client_with_schedule();
        let contract = Contract::stock("AAPL").build();

        client.historical_schedules(&contract, 7.days()).fetch().await.expect("fetch failed");

        assert_request(
            &bus,
            0,
            &historical_data_request()
                .contract(&contract)
                .end_date(None)
                .duration(Duration::days(7))
                .bar_size(BarSize::Day)
                .what_to_show(WhatToShow::Schedule)
                .use_rth(true),
        );
    }

    #[tokio::test]
    async fn ending_anchors_at_explicit_date() {
        let (client, bus) = client_with_schedule();
        let contract = Contract::stock("AAPL").build();
        let end = datetime!(2026-04-15 0:00 UTC);

        client
            .historical_schedules(&contract, 30.days())
            .ending(end)
            .fetch()
            .await
            .expect("fetch failed");

        assert_request(
            &bus,
            0,
            &historical_data_request()
                .contract(&contract)
                .end_date(Some(end))
                .duration(Duration::days(30))
                .bar_size(BarSize::Day)
                .what_to_show(WhatToShow::Schedule)
                .use_rth(true),
        );
    }

    #[tokio::test]
    async fn trading_hours_sets_use_rth() {
        for (name, trading_hours, use_rth) in [
            ("explicit Regular", TradingHours::Regular, true),
            ("Extended", TradingHours::Extended, false),
        ] {
            let (client, bus) = client_with_schedule();
            let contract = Contract::stock("AAPL").build();

            client
                .historical_schedules(&contract, 7.days())
                .trading_hours(trading_hours)
                .fetch()
                .await
                .unwrap_or_else(|e| panic!("{name}: fetch failed: {e}"));

            assert_request(
                &bus,
                0,
                &historical_data_request()
                    .contract(&contract)
                    .end_date(None)
                    .duration(Duration::days(7))
                    .bar_size(BarSize::Day)
                    .what_to_show(WhatToShow::Schedule)
                    .use_rth(use_rth),
            );
        }
    }
}