Skip to content

Commit

Permalink
Implements request market data type (#138)
Browse files Browse the repository at this point in the history
  • Loading branch information
wboayue authored Oct 20, 2024
1 parent 60b148b commit f629b3c
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 4 deletions.
13 changes: 13 additions & 0 deletions examples/switch_market_data_type.rs
Original file line number Diff line number Diff line change
@@ -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);
}
24 changes: 23 additions & 1 deletion src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)]
Expand Down
94 changes: 94 additions & 0 deletions src/market_data.rs
Original file line number Diff line number Diff line change
@@ -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<RequestMessage, Error> {
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|");
}
}
4 changes: 4 additions & 0 deletions src/messages/shared_channel_configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,8 @@ pub(crate) const CHANNEL_MAPPINGS: &[ChannelMapping] = &[
IncomingMessages::AccountUpdateTime,
],
},
ChannelMapping {
request: OutgoingMessages::RequestMarketDataType,
responses: &[IncomingMessages::MarketDataType],
},
];
7 changes: 4 additions & 3 deletions src/transport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Receiver<Result<ResponseMessage, Error>>> {
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)
}
Expand Down

0 comments on commit f629b3c

Please sign in to comment.