Skip to content

v4.2.0

Choose a tag to compare

@wboayue wboayue released this 21 Sep 19:24
9c65a11

What's New

OrderBuilder reaches every enum-typed Order field (#832, #833)

New setters trigger_method, origin, short_sale_slot, designated_location, volatility_type, reference_price_type, rule_80_a and open_close — fields that reached TWS but could only be set by hand-building Order. oca_group now takes OcaType instead of a bare i32.

use ibapi::orders::{OcaType, Rule80A};
use ibapi::orders::conditions::TriggerMethod;

let order = client
    .order(&contract)
    .sell(100)
    .stop(140.0)
    .trigger_method(TriggerMethod::Last)
    .rule_80_a(Rule80A::Agency)
    .oca_group("MyOCA", OcaType::CancelWithBlock)
    .submit()
    .await?;

One TimeInForce, with GTX and round-tripping of unknown values (#822, #826)

orders::builder::TimeInForce is merged into orders::TimeInForce; variants are spelled "till" (GoodTillCanceled, GoodTillDate, DayTillCanceled). Adds GoodTillCrossing (GTX) and Unknown(String), so a TIF this crate doesn't model survives a read/write round trip instead of becoming DAY. good_till_cancel() is renamed good_till_canceled().

let order = client
    .order(&contract)
    .buy(100)
    .limit(50.0)
    .good_till_crossing()
    .submit()
    .await?;

Thanks to @tradatious for the contribution.

Order enums preserve values IB adds later (#825, #829)

Rule80A and OrderOpenClose gain Unknown(String); OcaType, OrderOrigin, ShortSaleSlot, VolatilityType, ReferencePriceType and TriggerMethod gain Unknown(i32). String enums parse through FromStr; variant as i32 becomes i32::from(variant).

use ibapi::orders::{OcaType, Rule80A};

let rule: Rule80A = "Z".parse()?;          // Rule80A::Unknown("Z")
let code = i32::from(OcaType::CancelWithBlock); // 1

Thanks to @tradatious for the contribution.

Async Subscription<T> dispatches statically (#823)

The async Subscription::new(receiver) constructor is removed; every async subscription is built by Client methods and decodes through T directly, as the blocking one does. clone() no longer has a panicking path.

Thanks to @tradatious for the contribution.

Bug Fixes

historical_data(..).between(start, end) returns the requested range (#835)

It sent the wall-clock span as trading seconds, so a one-day range returned ~3.7 sessions and anything over a day was rejected with code 321. Bars outside [start, end) are now dropped.

let bars = client
    .historical_data(&contract, HistoricalBarSize::Hour)
    .between(datetime!(2023-04-08 0:00 UTC), datetime!(2023-04-15 0:00 UTC))
    .fetch()
    .await?;

Thanks to @Johan3F for the report.

Dead subscriptions fail at the socket drop (#816, #817, #818)

In-flight subscriptions and one-shots fail with Error::ConnectionReset as soon as the socket read fails, instead of hanging until the reconnect finished (up to 7.5 min). Sends while the session is down are refused, and TRANSPORT_RECONNECT_CODE is published once the new session is live.

while let Some(item) = subscription.next().await {
    match item {
        Ok(bar) => println!("{bar:?}"),
        Err(Error::ConnectionReset) => break, // resubscribe on TRANSPORT_RECONNECT_CODE
        Err(e) => return Err(e.into()),
    }
}

Thanks to @tradatious for the report and the contribution.

time minimum raised to 0.3.47 (#820)

Blocks resolution of versions affected by RUSTSEC-2026-0009.


Breaking changes are covered in docs/migration-4.0.md §12–§15. Full changelog: CHANGELOG.md.