From 4283baef8ef8959326b61da921a7db51f3b7b32d Mon Sep 17 00:00:00 2001 From: Wil Boayue Date: Sun, 20 Sep 2026 16:49:42 -0700 Subject: [PATCH 1/2] orders: rename good_till_cancel to good_till_canceled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The setter sets TimeInForce::GoodTillCanceled and sits next to day_till_canceled(); it was the one TIF setter whose name matched neither. #822 spent the breaking change on this spelling everywhere else, so finish it in the same release rather than pay for it again after 4.0 ships. Updates the four in-tree call sites, the README and docs/order-types.md snippets (which nothing compiles), and migration §13. --- CHANGELOG.md | 1 + README.md | 2 +- docs/migration-4.0.md | 4 ++-- docs/order-types.md | 4 ++-- src/orders/async_tests.rs | 2 +- src/orders/builder/order_builder.rs | 6 +++--- src/orders/builder/order_builder/tests.rs | 4 ++-- src/orders/sync_tests.rs | 2 +- 8 files changed, 13 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a95fcce..410fb6d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `orders::TimeInForce` variants are spelled "till", matching IB's `goodTillDate`, `Order.good_till_date` and the builder setters: `GoodTilCanceled` → `GoodTillCanceled`, `GoodTilDate` → `GoodTillDate`, `DayTilCanceled` → `DayTillCanceled`. Wire strings are unchanged (#822). - `OrderBuilder::time_in_force` takes `orders::TimeInForce`; exhaustive matches on `orders::TimeInForce` need `GoodTillCrossing` and `Unknown(raw)` arms. See `docs/migration-4.0.md` §13 (#822). +- `OrderBuilder::good_till_cancel()` is renamed `good_till_canceled()`, matching the `TimeInForce::GoodTillCanceled` variant it sets and the neighbouring `day_till_canceled()` (#825). - `orders::TimeInForce` serializes as the TWS wire string (`"GTC"`) rather than the variant name (`"GoodTilCanceled"`), in both directions, and its `utoipa` schema is a plain string — matching `OrderStatusKind`. Stored JSON and downstream consumers that read the old variant names need updating (#822). ### Removed diff --git a/README.md b/README.md index 5f7b1923..f40740a2 100644 --- a/README.md +++ b/README.md @@ -371,7 +371,7 @@ pub fn main() { let order_id = client.order(&contract) .sell(50) .limit(150.00) - .good_till_cancel() + .good_till_canceled() .outside_rth() .submit() .expect("order submission failed!"); diff --git a/docs/migration-4.0.md b/docs/migration-4.0.md index f3d34d9d..0e886092 100644 --- a/docs/migration-4.0.md +++ b/docs/migration-4.0.md @@ -368,7 +368,7 @@ Rename the variants from either 3.x enum: | `Auction` | `Auction` | `Auction` | | `OpeningAuction` | `OnOpen` | `OnOpen` | -The named setters (`.day_order()`, `.good_till_cancel()`, `.good_till_date(..)`, `.fill_or_kill()`, `.immediate_or_cancel()`) are unchanged, and `.good_till_crossing()` and `.day_till_canceled()` are added. `.time_in_force(TimeInForce::GoodTillDate)` without a date still fails `build()` with `ValidationError::MissingRequiredField("good_till_date")`. The builder keeps the GTD date only in the field written by `.good_till_date(..)` / `.good_till_time(..)`; in 3.x a date carried in the `GoodTillDate { date }` payload took precedence over one set through those methods; now the last one set wins. +The named setters (`.day_order()`, `.good_till_date(..)`, `.fill_or_kill()`, `.immediate_or_cancel()`) are unchanged; `.good_till_cancel()` is renamed `.good_till_canceled()`, matching the variant it sets and its `.day_till_canceled()` neighbour, and `.good_till_crossing()` and `.day_till_canceled()` are added. `.time_in_force(TimeInForce::GoodTillDate)` without a date still fails `build()` with `ValidationError::MissingRequiredField("good_till_date")`. The builder keeps the GTD date only in the field written by `.good_till_date(..)` / `.good_till_time(..)`; in 3.x a date carried in the `GoodTillDate { date }` payload took precedence over one set through those methods; now the last one set wins. `orders::TimeInForce` is also an open enum now, like `OrderStatusKind` in [§9](#9-orderstatuskind-gains-unknownstring): a TIF string this crate does not model decodes as `Unknown(raw)` carrying the value TWS sent, instead of being coerced to `Day`. Sending an `Unknown` back — through `.time_in_force(..)` or `Order.tif` — puts the raw string on the wire unchanged, so an order read from TWS round-trips. Matching is exact and case-sensitive, so a case-variant such as `"gtc"` lands in `Unknown` rather than being coerced to the nearest known variant. @@ -406,7 +406,7 @@ No code changes required, but observable at runtime: 10. If you serialize market-data types to JSON, update downstream consumers: sizes are now `number | null` instead of `integer`, and notices may carry `request_id`. 11. Add an `OrderUpdate::OrderBound(binding)` arm to exhaustive matches on order updates, and read bindings from `order_update_stream()` — they never reach `place_order` subscriptions; see [§11](#11-orderupdate-gains-orderbound). 12. Replace any `Subscription::new(rx)` over your own channel with `tokio_stream::wrappers::UnboundedReceiverStream::new(rx)` (add `tokio-stream` to your dependencies), and give any generic helper over the async `Subscription` a concrete item type — see [§12](#12-the-async-subscriptionnewreceiver-constructor-is-removed). -13. Use `ibapi::orders::TimeInForce` everywhere (`ibapi::orders::builder::TimeInForce` is gone) and spell the variants "till": `GoodTilCanceled` → `GoodTillCanceled`, `GoodTilDate` → `GoodTillDate`, `DayTilCanceled` → `DayTillCanceled`; from the builder enum, `GoodTillCancel` → `GoodTillCanceled`, `OpeningAuction` → `OnOpen`, `GoodTillDate { date }` → `GoodTillDate` plus `.good_till_date(date)`. Add `GoodTillCrossing` and `Unknown(raw)` arms to exhaustive matches, and re-read any stored JSON — the field is the wire string now — see [§13](#13-one-timeinforce-ordersbuildertimeinforce-is-removed-and-the-variants-are-spelled-till). +13. Use `ibapi::orders::TimeInForce` everywhere (`ibapi::orders::builder::TimeInForce` is gone) and spell the variants "till": `GoodTilCanceled` → `GoodTillCanceled`, `GoodTilDate` → `GoodTillDate`, `DayTilCanceled` → `DayTillCanceled`; from the builder enum, `GoodTillCancel` → `GoodTillCanceled`, `OpeningAuction` → `OnOpen`, `GoodTillDate { date }` → `GoodTillDate` plus `.good_till_date(date)`. Rename `.good_till_cancel()` calls to `.good_till_canceled()`, add `GoodTillCrossing` and `Unknown(raw)` arms to exhaustive matches, and re-read any stored JSON — the field is the wire string now — see [§13](#13-one-timeinforce-ordersbuildertimeinforce-is-removed-and-the-variants-are-spelled-till). 14. Re-run `cargo fmt`, `cargo clippy --all-targets --all-features -- -D warnings`, and your test suite for each feature flag you support. ## Need help? diff --git a/docs/order-types.md b/docs/order-types.md index cc3d99ea..1fbf4d46 100644 --- a/docs/order-types.md +++ b/docs/order-types.md @@ -564,7 +564,7 @@ let order_id = client.order(&contract) .and_condition(volume(265598, "SMART").greater_than(80_000_000)) .and_condition(time().greater_than("20251230 10:00:00 US/Eastern")) .and_condition(margin().greater_than(40)) - .good_till_cancel() + .good_till_canceled() .submit()?; ``` @@ -1217,7 +1217,7 @@ Control how long an order remains active: let order_id = client.order(&contract) .buy(100) .limit(150.00) - .good_till_cancel() + .good_till_canceled() .submit()?; // Good till specific date diff --git a/src/orders/async_tests.rs b/src/orders/async_tests.rs index 28c76aae..f305b1d8 100644 --- a/src/orders/async_tests.rs +++ b/src/orders/async_tests.rs @@ -744,7 +744,7 @@ async fn submit_all_reserves_three_ids_and_wires_the_bracket() { let ids = client .order(&contract) .buy(100) - .good_till_cancel() + .good_till_canceled() .bracket() .entry_limit(50.0) .take_profit(55.0) diff --git a/src/orders/builder/order_builder.rs b/src/orders/builder/order_builder.rs index 3418dd82..62699cac 100644 --- a/src/orders/builder/order_builder.rs +++ b/src/orders/builder/order_builder.rs @@ -422,8 +422,8 @@ impl<'a, C> OrderBuilder<'a, C> { self } - /// Good till cancelled order - pub fn good_till_cancel(mut self) -> Self { + /// Good till canceled order + pub fn good_till_canceled(mut self) -> Self { self.time_in_force = TimeInForce::GoodTillCanceled; self } @@ -441,7 +441,7 @@ impl<'a, C> OrderBuilder<'a, C> { self } - /// Day till cancelled order + /// Day till canceled order pub fn day_till_canceled(mut self) -> Self { self.time_in_force = TimeInForce::DayTillCanceled; self diff --git a/src/orders/builder/order_builder/tests.rs b/src/orders/builder/order_builder/tests.rs index 98d4598b..5ecaaa14 100644 --- a/src/orders/builder/order_builder/tests.rs +++ b/src/orders/builder/order_builder/tests.rs @@ -190,7 +190,7 @@ fn test_time_conditions() { assert_eq!(order.tif, TimeInForce::Day); // Test Good Till Cancel - let builder = OrderBuilder::new(&client, &contract).buy(100).market().good_till_cancel(); + let builder = OrderBuilder::new(&client, &contract).buy(100).market().good_till_canceled(); let order = builder.build().unwrap(); assert_eq!(order.tif, TimeInForce::GoodTillCanceled); @@ -1447,7 +1447,7 @@ fn bracket_order_propagates_tif() { let orders = OrderBuilder::new(&client, &contract) .buy(100) - .good_till_cancel() + .good_till_canceled() .bracket() .entry_limit(50.0) .take_profit(55.0) diff --git a/src/orders/sync_tests.rs b/src/orders/sync_tests.rs index 230c7d10..d486febf 100644 --- a/src/orders/sync_tests.rs +++ b/src/orders/sync_tests.rs @@ -808,7 +808,7 @@ fn submit_all_reserves_three_ids_and_wires_the_bracket() { let ids = client .order(&contract) .buy(100) - .good_till_cancel() + .good_till_canceled() .bracket() .entry_limit(50.0) .take_profit(55.0) From b4f1b5dec0655babd04e6c398c7380f26276e809 Mon Sep 17 00:00:00 2001 From: Wil Boayue Date: Sun, 20 Sep 2026 16:49:57 -0700 Subject: [PATCH 2/2] changelog: correct PR reference to #826 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 410fb6d7..54b9ac7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `orders::TimeInForce` variants are spelled "till", matching IB's `goodTillDate`, `Order.good_till_date` and the builder setters: `GoodTilCanceled` → `GoodTillCanceled`, `GoodTilDate` → `GoodTillDate`, `DayTilCanceled` → `DayTillCanceled`. Wire strings are unchanged (#822). - `OrderBuilder::time_in_force` takes `orders::TimeInForce`; exhaustive matches on `orders::TimeInForce` need `GoodTillCrossing` and `Unknown(raw)` arms. See `docs/migration-4.0.md` §13 (#822). -- `OrderBuilder::good_till_cancel()` is renamed `good_till_canceled()`, matching the `TimeInForce::GoodTillCanceled` variant it sets and the neighbouring `day_till_canceled()` (#825). +- `OrderBuilder::good_till_cancel()` is renamed `good_till_canceled()`, matching the `TimeInForce::GoodTillCanceled` variant it sets and the neighbouring `day_till_canceled()` (#826). - `orders::TimeInForce` serializes as the TWS wire string (`"GTC"`) rather than the variant name (`"GoodTilCanceled"`), in both directions, and its `utoipa` schema is a plain string — matching `OrderStatusKind`. Stored JSON and downstream consumers that read the old variant names need updating (#822). ### Removed