Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
ca57308
feat: Add update_display_group method to clients
AdAstraPerAsperaMX Jan 7, 2026
caa7a2d
Add time zone map to support China standard time (#364)
kingyond Jan 7, 2026
eef039c
feat: add Notice helper methods to distinguish message types (#366)
wboayue Jan 8, 2026
d560e48
chore: bump version to 2.6.0
wboayue Jan 8, 2026
15da483
fix: prevent scanner infinite hang on error messages (#371)
wboayue Jan 17, 2026
4cc4f8e
chore: bump version to 2.6.1
wboayue Jan 17, 2026
54280a8
fix: prevent bracket/OCA order ID collisions (#375)
wboayue Jan 23, 2026
3f14a76
fix: bracket child orders inherit outside_rth from parent (#373) (#376)
wboayue Jan 23, 2026
df9e0ad
feat: add entry_market() for bracket orders (#372) (#377)
wboayue Jan 23, 2026
7cc5f9e
docs: add DRY, SRP, and composition guidelines (#378)
wboayue Jan 24, 2026
b56466b
Add Sec10 bar size variant (#380)
wboayue Jan 25, 2026
8b0fb01
Update BarSize::from_str and tests. (#381)
yay Jan 26, 2026
5e1a6bd
fix: ForexBuilder sets symbol to base currency, currency to quote (#383)
wboayue Jan 26, 2026
7770ede
Add historical_data_streaming() with keepUpToDate=true support (#384)
mysyzygy Jan 31, 2026
d727fa3
Add sync historical_data_streaming(), address PR #384 feedback (#385)
wboayue Jan 31, 2026
0d3524f
Refactor: consolidate decoder context, add Client::decoder_context() …
wboayue Jan 31, 2026
86cb90f
Update README version to 2.7
wboayue Jan 31, 2026
d5b6e30
Enable timezone-aware execution timestamps (#387)
t00ts Feb 1, 2026
6d19c32
Merge branch 'wboayue:main' into main
AdAstraPerAsperaMX Feb 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/client/async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,40 @@ impl Client {
display_groups::r#async::subscribe_to_group_events(self, group_id).await
}

/// Updates the contract displayed in a TWS display group.
///
/// This function changes the contract shown in the specified display group within TWS.
/// You must first subscribe to the group using [`subscribe_to_group_events`](Self::subscribe_to_group_events)
/// before calling this function.
///
/// # Arguments
/// * `request_id` - The request ID from the subscription (use `subscription.request_id()`)
/// * `contract_info` - Contract to display:
/// - `"contractID@exchange"` for individual contracts (e.g., "265598@SMART")
/// - `"none"` for empty selection
/// - `"combo"` for combination contracts
///
/// # Examples
///
/// ```no_run
/// use ibapi::Client;
///
/// #[tokio::main]
/// async fn main() {
/// let client = Client::connect("127.0.0.1:7497", 100).await.expect("connection failed");
///
/// // First subscribe to the display group
/// let subscription = client.subscribe_to_group_events(1).await.expect("subscription failed");
/// let request_id = subscription.request_id().expect("no request ID");
///
/// // Update the display group to show AAPL
/// client.update_display_group(request_id, "265598@SMART").await.expect("update failed");
/// }
/// ```
pub async fn update_display_group(&self, request_id: i32, contract_info: &str) -> Result<(), Error> {
display_groups::r#async::update_display_group(self, request_id, contract_info).await
}

// === Market Data ===

/// Creates a market data subscription builder with a fluent interface.
Expand Down
31 changes: 31 additions & 0 deletions src/client/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,37 @@ impl Client {
display_groups::sync::subscribe_to_group_events(self, group_id)
}

/// Updates the contract displayed in a TWS display group.
///
/// This function changes the contract shown in the specified display group within TWS.
/// You must first subscribe to the group using [`subscribe_to_group_events`](Self::subscribe_to_group_events)
/// before calling this function.
///
/// # Arguments
/// * `request_id` - The request ID from the subscription (use `subscription.request_id()`)
/// * `contract_info` - Contract to display:
/// - `"contractID@exchange"` for individual contracts (e.g., "265598@SMART")
/// - `"none"` for empty selection
/// - `"combo"` for combination contracts
///
/// # Examples
///
/// ```no_run
/// use ibapi::client::blocking::Client;
///
/// let client = Client::connect("127.0.0.1:7497", 100).expect("connection failed");
///
/// // First subscribe to the display group
/// let subscription = client.subscribe_to_group_events(1).expect("subscription failed");
/// let request_id = subscription.request_id().expect("no request ID");
///
/// // Update the display group to show AAPL
/// client.update_display_group(request_id, "265598@SMART").expect("update failed");
/// ```
pub fn update_display_group(&self, request_id: i32, contract_info: &str) -> Result<(), Error> {
display_groups::sync::update_display_group(self, request_id, contract_info)
}

// === Contracts ===

/// Requests contract information.
Expand Down
19 changes: 19 additions & 0 deletions src/display_groups/async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,25 @@ pub async fn subscribe_to_group_events(client: &Client, group_id: i32) -> Result
builder.send::<DisplayGroupUpdate>(request).await
}

/// Updates the contract displayed in a TWS display group.
///
/// This function changes the contract shown in the specified display group within TWS.
/// You must first subscribe to the group using [`subscribe_to_group_events`] before
/// calling this function. The update will trigger a `DisplayGroupUpdated` callback
/// on the existing subscription.
///
/// # Arguments
/// * `client` - The connected client
/// * `request_id` - The request ID from the subscription (use `subscription.request_id()`)
/// * `contract_info` - Contract to display:
/// - `"contractID@exchange"` for individual contracts (e.g., "265598@SMART")
/// - `"none"` for empty selection
/// - `"combo"` for combination contracts
pub async fn update_display_group(client: &Client, request_id: i32, contract_info: &str) -> Result<(), Error> {
let request = encoders::encode_update_display_group(request_id, contract_info)?;
client.send_message(request).await
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
39 changes: 39 additions & 0 deletions src/display_groups/common/encoders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,21 @@ pub(crate) fn encode_unsubscribe_from_group_events(request_id: i32) -> Result<Re
Ok(message)
}

/// Encodes a request to update the contract displayed in a display group.
///
/// # Arguments
/// * `request_id` - The request ID (should match the subscription request ID)
/// * `contract_info` - Contract to display, format: "contractID@exchange" (e.g., "265598@SMART"),
/// "none" for empty selection, or "combo" for combination contracts
pub(crate) fn encode_update_display_group(request_id: i32, contract_info: &str) -> Result<RequestMessage, Error> {
let mut message = RequestMessage::new();
message.push_field(&OutgoingMessages::UpdateDisplayGroup);
message.push_field(&VERSION);
message.push_field(&request_id);
message.push_field(&contract_info);
Ok(message)
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -52,4 +67,28 @@ mod tests {
assert_eq!(message[1], "1"); // version
assert_eq!(message[2], request_id.to_field());
}

#[test]
fn test_encode_update_display_group() {
let request_id = 9000;
let contract_info = "265598@SMART";

let message = encode_update_display_group(request_id, contract_info).expect("encoding failed");

assert_eq!(message[0], OutgoingMessages::UpdateDisplayGroup.to_field());
assert_eq!(message[1], "1"); // version
assert_eq!(message[2], request_id.to_field());
assert_eq!(message[3], contract_info);
}

#[test]
fn test_encode_update_display_group_none() {
let request_id = 9000;
let contract_info = "none";

let message = encode_update_display_group(request_id, contract_info).expect("encoding failed");

assert_eq!(message[0], OutgoingMessages::UpdateDisplayGroup.to_field());
assert_eq!(message[3], "none");
}
}
19 changes: 19 additions & 0 deletions src/display_groups/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,22 @@ pub fn subscribe_to_group_events(client: &Client, group_id: i32) -> Result<Subsc
let request = encoders::encode_subscribe_to_group_events(builder.request_id(), group_id)?;
builder.send(request)
}

/// Updates the contract displayed in a TWS display group.
///
/// This function changes the contract shown in the specified display group within TWS.
/// You must first subscribe to the group using [`subscribe_to_group_events`] before
/// calling this function. The update will trigger a `DisplayGroupUpdated` callback
/// on the existing subscription.
///
/// # Arguments
/// * `client` - The connected client
/// * `request_id` - The request ID from the subscription (use `subscription.request_id()`)
/// * `contract_info` - Contract to display:
/// - `"contractID@exchange"` for individual contracts (e.g., "265598@SMART")
/// - `"none"` for empty selection
/// - `"combo"` for combination contracts
pub fn update_display_group(client: &Client, request_id: i32, contract_info: &str) -> Result<(), Error> {
let request = encoders::encode_update_display_group(request_id, contract_info)?;
client.send_message(request)
}
5 changes: 5 additions & 0 deletions src/subscriptions/async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,11 @@ impl<T> Subscription<T> {
SubscriptionInner::PreDecoded { receiver } => receiver.recv().await,
}
}

/// Get the request ID associated with this subscription
pub fn request_id(&self) -> Option<i32> {
self.request_id
}
}

impl<T> Subscription<T> {
Expand Down
5 changes: 5 additions & 0 deletions src/subscriptions/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ impl<T: StreamDecoder<T>> Subscription<T> {
}
}

/// Returns the request ID associated with this subscription.
pub fn request_id(&self) -> Option<i32> {
self.request_id
}

/// Returns the next available value, blocking if necessary until a value becomes available.
///
/// # Examples
Expand Down