Repository navigation
v5.0.0
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).