Skip to content

v5.0.0

Choose a tag to compare

@wboayue wboayue released this 04 Oct 23:08
· 178 commits to main since this release
f43c646

5.0 is a major release: it removes APIs that compiled but were easy to misuse, and moves the order builders onto typed arguments. Every breaking change has a before/after in the 5.0 migration guide, with a checklist at the end. Minimum Rust is 1.88.

What's New

Streaming contract details (#876)

contract_details_stream hands back rows as they arrive, knows its request id before sending, and can cap unread rows with buffer_limit.

let builder = client.contract_details_stream(&contract).buffer_limit(1_000);
let request_id = builder.request_id();
let subscription = builder.subscribe()?;
while let Some(details) = subscription.next_data() {
    println!("{}", details?.contract.local_symbol);
}

Thanks to @hanseatica-io for the report.

Order::builder() without a client (#901)

The fluent builder now works without a client, and it covers the order fields and types it couldn't set before: pegged to stock, benchmark, market and midpoint, BOX TOP, cash quantity, and non-guaranteed combos.

let order = Order::builder()
    .buy(100)
    .trailing_stop(TrailBy::Amount(0.50), 95.0)
    .build()?;
client.place_order(order_id, &contract, &order)?;

Exercise options through a builder (#901)

let subscription = client.exercise_options(&option).exercise(1).account("DU1234567").submit()?;

IBKRATS pegs (#936)

client.order(&contract).buy(100).peg_mid(150.0, MidOffsets { at_whole: 0.01, at_half: 0.005 }).submit()?;

Preset attached orders (#842)

preset_stop_loss() / preset_profit_taker() attach children priced from your TWS order presets.

let ids = client.order(&contract).buy(100).limit(150.0).preset_stop_loss().preset_profit_taker().submit()?;

Cancel and wait for the end (#876)

Subscription::cancel_and_drain(deadline) sends the cancel and waits until TWS has finished the request.

match subscription.cancel_and_drain(Instant::now() + Duration::from_secs(5))? {
    Drained::Ended => {}
    Drained::Rejected(notice) => eprintln!("{notice}"),
    Drained::Unconfirmed => eprintln!("no end marker before the deadline"),
}

Typed, harder-to-swap arguments (#901, #935)

These now take typed arguments: TrailBy, BracketPrices, impl Into<OrderId> / impl Into<ContractId>. SpreadBuilder::calendar is split into long_calendar / short_calendar, because buying the old calendar combo was short the calendar.

let spread = Contract::spread().long_calendar(near_id, far_id).build()?;
let orders = order_builder::bracket_order(id, Action::Buy, 100.0, BracketPrices { entry: 50.0, take_profit: 55.0, stop_loss: 45.0 })?;

Bug Fixes

Shared streams no longer cancel or starve each other (#836, #848)

Dropping one of several positions / account_updates / news_bulletins subscriptions no longer ends the stream for the others. On the blocking client, every subscriber now receives every response. Thanks to @tradatious for the contribution.

Request ids and order ids no longer collide (#789)

Request ids start at 1,500,000,000, so an error frame can no longer be delivered to a subscription that happens to share an order's number. Thanks to @EAimTY for the report.

Blocking client: frames lost across the read timeout (#891)

A frame that straddled the 1 s socket read timeout used to corrupt every later frame. Partial reads are now kept.

Not-yet-computed option values are None (#941)

Not-yet-computed OptionComputation values are None, no longer the -1 / -2 sentinels TWS sends. Thanks to @tradatious for the contribution.

Scanner subscriptions survive IB 165 (#886)

Thanks to @mjarvel for the contribution.

Async: abandoned and cut-off writes (#874, #875)

A dropped or timed-out request no longer leaves its channel registered, and a write cut off mid-frame now triggers a reconnect. Thanks to @hanseatica-io for the contribution.

Blocking order_update_stream ends on shutdown (#871) and can be recreated right after a drop (#778, #893)

Thanks to @Niqnil and @EAimTY for the reports.

Gateway time zones resolve exactly; an unknown zone no longer fails connect (#809, #859)

Thanks to @EAimTY for the report.

Bond contract details are no longer empty

market_depth delivers RESET (317) as MarketDepths::Reset

filter_data() / iter_data() used to drop the RESET.

See CHANGELOG.md for the full list.

Thanks to @tradatious for the many 5.0 contributions: the FromStr parsing work, the removal of panicking conversions, and the decode hardening (#838, #845, #846, #854, #855, #863, #864, #868).