diff --git a/examples/switch_market_data_type.rs b/examples/switch_market_data_type.rs new file mode 100644 index 00000000..8a9c0dab --- /dev/null +++ b/examples/switch_market_data_type.rs @@ -0,0 +1,13 @@ +use ibapi::market_data::MarketDataType; +use ibapi::Client; + +fn main() { + env_logger::init(); + + let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed"); + + let market_data_type = MarketDataType::Live; + + client.switch_market_data_type(market_data_type).expect("request failed"); + println!("market data switched: {:?}", market_data_type); +} diff --git a/src/client.rs b/src/client.rs index 39e43af1..a7fff1ee 100644 --- a/src/client.rs +++ b/src/client.rs @@ -13,11 +13,12 @@ use crate::contracts::{Contract, OptionComputation}; use crate::errors::Error; use crate::market_data::historical::{self, HistogramEntry}; use crate::market_data::realtime::{self, Bar, BarSize, MidPoint, WhatToShow}; +use crate::market_data::MarketDataType; use crate::messages::{IncomingMessages, OutgoingMessages}; use crate::messages::{RequestMessage, ResponseMessage}; use crate::orders::{Order, OrderDataResult, OrderNotification}; use crate::transport::{Connection, ConnectionMetadata, InternalSubscription, MessageBus, TcpMessageBus}; -use crate::{accounts, contracts, orders}; +use crate::{accounts, contracts, market_data, orders}; // Client @@ -1057,6 +1058,27 @@ impl Client { realtime::tick_by_tick_midpoint(self, contract, number_of_ticks, ignore_size) } + /// Switches market data type returned from request_market_data requests to Live, Frozen, Delayed, or FrozenDelayed. + /// + /// # Arguments + /// * `market_data_type` - Type of market data to retrieve. + /// + /// # Examples + /// + /// ```no_run + /// use ibapi::Client; + /// use ibapi::market_data::{MarketDataType}; + /// + /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed"); + /// + /// let market_data_type = MarketDataType::Live; + /// client.switch_market_data_type(market_data_type).expect("request failed"); + /// println!("market data switched: {:?}", market_data_type); + /// ``` + pub fn switch_market_data_type(&self, market_data_type: MarketDataType) -> Result<(), Error> { + market_data::switch_market_data_type(self, market_data_type) + } + // == Internal Use == #[cfg(test)] diff --git a/src/market_data.rs b/src/market_data.rs index e0bc2a46..a5a9b40a 100644 --- a/src/market_data.rs +++ b/src/market_data.rs @@ -1,2 +1,96 @@ +use crate::{messages::OutgoingMessages, server_versions, Client, Error}; + pub mod historical; pub mod realtime; + +/// By default only Realtime market data is enabled sending. +#[derive(Debug, Clone, Copy)] +pub enum MarketDataType { + /// Disables frozen, delayed and delayed-frozen market data sending. + Live = 1, + /// Enables frozen market data sending. + Frozen = 2, + /// Enables delayed and disables delayed-frozen market data sending. + Delayed = 3, + /// Enables delayed and delayed-frozen market data. + DelayedFrozen = 4, +} + +pub(crate) fn switch_market_data_type(client: &Client, market_data_type: MarketDataType) -> Result<(), Error> { + client.check_server_version(server_versions::REQ_MARKET_DATA_TYPE, "It does not support market data type requests.")?; + + let message = encoders::encode_request_market_data_type(market_data_type)?; + let _ = client.send_shared_request(OutgoingMessages::RequestMarketDataType, message)?; + + Ok(()) +} + +mod encoders { + use crate::messages::{OutgoingMessages, RequestMessage}; + use crate::Error; + + use super::MarketDataType; + + pub(super) fn encode_request_market_data_type(market_data_type: MarketDataType) -> Result { + const VERSION: i32 = 1; + + let mut message = RequestMessage::new(); + + message.push_field(&OutgoingMessages::RequestMarketDataType); + message.push_field(&VERSION); + message.push_field(&(market_data_type as i32)); + + Ok(message) + } + + #[cfg(test)] + mod tests { + use super::*; + use crate::{market_data::MarketDataType, ToField}; + + #[test] + fn test_encode_request_market_data_type() { + let market_data_types = vec![ + MarketDataType::Live, + MarketDataType::Frozen, + MarketDataType::Delayed, + MarketDataType::DelayedFrozen, + ]; + + for market_data_type in market_data_types { + let result = encode_request_market_data_type(market_data_type); + assert!(result.is_ok()); + + let message = result.unwrap(); + + assert_eq!(message[0], OutgoingMessages::RequestMarketDataType.to_field()); + assert_eq!(message[1], "1"); // VERSION + assert_eq!(message[2], (market_data_type as i32).to_string()); + } + } + } +} + +#[cfg(test)] +mod tests { + use std::sync::{Arc, RwLock}; + + use crate::{market_data::MarketDataType, server_versions, stubs::MessageBusStub, Client}; + + #[test] + fn test_switch_market_data_type() { + let message_bus = Arc::new(MessageBusStub { + request_messages: RwLock::new(vec![]), + response_messages: vec![], + }); + + let client = Client::stubbed(message_bus, server_versions::SIZE_RULES); + + let market_data_type = MarketDataType::Delayed; + client.switch_market_data_type(market_data_type).expect("switch market data type failed"); + + let request_messages = client.message_bus.request_messages(); + + assert_eq!(request_messages[0].encode_simple(), "59|1|3|"); + } +} diff --git a/src/messages/shared_channel_configuration.rs b/src/messages/shared_channel_configuration.rs index 2ff963c6..b1ed7cc0 100644 --- a/src/messages/shared_channel_configuration.rs +++ b/src/messages/shared_channel_configuration.rs @@ -44,4 +44,8 @@ pub(crate) const CHANNEL_MAPPINGS: &[ChannelMapping] = &[ IncomingMessages::AccountUpdateTime, ], }, + ChannelMapping { + request: OutgoingMessages::RequestMarketDataType, + responses: &[IncomingMessages::MarketDataType], + }, ]; diff --git a/src/transport.rs b/src/transport.rs index 7e86bcdb..5d095bfa 100644 --- a/src/transport.rs +++ b/src/transport.rs @@ -95,9 +95,10 @@ impl SharedChannels { // Get receiver for specified message type. Panics if receiver not found. pub fn get_receiver(&self, message_type: OutgoingMessages) -> Arc>> { - let receiver = self.receivers.get(&message_type).unwrap_or_else(|| { - panic!("unsupported request message {message_type:?}. check mapping in SharedChannels::new() located in transport.rs") - }); + let receiver = self + .receivers + .get(&message_type) + .unwrap_or_else(|| panic!("unsupported request message {message_type:?}. check mapping in messages::shared_channel_configuration")); Arc::clone(receiver) }