Skip to content

Commit

Permalink
Implements news bulletins (#147)
Browse files Browse the repository at this point in the history
  • Loading branch information
wboayue authored Oct 30, 2024
1 parent 5351e4e commit 15a4d65
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 3 deletions.
12 changes: 12 additions & 0 deletions examples/news_bulletins.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use ibapi::Client;

fn main() {
env_logger::init();

let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");

let news_bulletins = client.news_bulletins(true).expect("request news providers failed");
for news_bulletin in &news_bulletins {
println!("news bulletin {:?}", news_bulletin);
}
}
2 changes: 2 additions & 0 deletions examples/news_providers.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use ibapi::Client;

fn main() {
env_logger::init();

let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");

let news_providers = client.news_providers().expect("request news providers failed");
Expand Down
22 changes: 22 additions & 0 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1218,6 +1218,28 @@ impl Client {
news::news_providers(self)
}

/// Subscribes to IB's News Bulletins.
///
/// # Arguments
///
/// * `all_messages` - If set to true, will return all the existing bulletins for the current day, set to false to receive only the new bulletins.
///
/// # Examples
///
/// ```no_run
/// use ibapi::Client;
///
/// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
///
/// let news_bulletins = client.news_bulletins(true).expect("request news providers failed");
/// for news_bulletin in &news_bulletins {
/// println!("news bulletin {:?}", news_bulletin);
/// }
/// ```
pub fn news_bulletins(&self, all_messages: bool) -> Result<Subscription<news::NewsBulletin>, Error> {
news::news_bulletins(self, all_messages)
}

// == Internal Use ==

#[cfg(test)]
Expand Down
4 changes: 4 additions & 0 deletions src/messages/shared_channel_configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,8 @@ pub(crate) const CHANNEL_MAPPINGS: &[ChannelMapping] = &[
request: OutgoingMessages::RequestNewsProviders,
responses: &[IncomingMessages::NewsProviders],
},
ChannelMapping {
request: OutgoingMessages::RequestNewsBulletins,
responses: &[IncomingMessages::NewsBulletins],
},
];
47 changes: 45 additions & 2 deletions src/news.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
use crate::{messages::OutgoingMessages, server_versions, Client, Error};
use crate::{
client::{ResponseContext, SharesChannel, Subscribable, Subscription},
messages::{IncomingMessages, OutgoingMessages, RequestMessage, ResponseMessage},
server_versions, Client, Error,
};
use serde::{Deserialize, Serialize};

mod decoders;
mod encoders;

#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct NewsProvider {
pub code: String,
pub name: String,
Expand All @@ -29,3 +34,41 @@ pub fn news_providers(client: &Client) -> Result<Vec<NewsProvider>, Error> {
None => Err(Error::UnexpectedEndOfStream),
}
}

impl SharesChannel for Vec<NewsProvider> {}

/// IB News Bulletin
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct NewsBulletin {
/// The unique identifier of the news bulletin.
pub message_id: i32,
/// The type of the news bulletin. One of: 1 - Regular news bulletin 2 - Exchange no longer available for trading 3 - Exchange is available for trading.
pub message_type: i32,
/// The text of the news bulletin.
pub message: String,
/// The exchange from which this news bulletin originated.
pub exchange: String,
}

impl Subscribable<NewsBulletin> for NewsBulletin {
fn decode(_server_version: i32, message: &mut ResponseMessage) -> Result<NewsBulletin, Error> {
match message.message_type() {
IncomingMessages::NewsBulletins => Ok(decoders::decode_news_bulletin(message.clone())?),
_ => Err(Error::UnexpectedResponse(message.clone())),
}
}

fn cancel_message(_server_version: i32, _request_id: Option<i32>, _context: &ResponseContext) -> Result<RequestMessage, Error> {
encoders::encode_cancel_news_bulletin()
}
}

// Subscribes to IB's News Bulletins.
pub fn news_bulletins(client: &Client, all_messages: bool) -> Result<Subscription<NewsBulletin>, Error> {
let request = encoders::encode_request_news_bulletins(all_messages)?;
let subscription = client.send_shared_request(OutgoingMessages::RequestNewsBulletins, request)?;

Ok(Subscription::new(client, subscription, ResponseContext::default()))
}

impl SharesChannel for Subscription<'_, NewsBulletin> {}
14 changes: 13 additions & 1 deletion src/news/decoders.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{Error, NewsProvider};
use super::{Error, NewsBulletin, NewsProvider};
use crate::messages::ResponseMessage;

pub(super) fn decode_news_providers(mut message: ResponseMessage) -> Result<Vec<NewsProvider>, Error> {
Expand All @@ -16,3 +16,15 @@ pub(super) fn decode_news_providers(mut message: ResponseMessage) -> Result<Vec<

Ok(news_providers)
}

pub(super) fn decode_news_bulletin(mut message: ResponseMessage) -> Result<NewsBulletin, Error> {
message.skip(); // message type
message.skip(); // message version

Ok(NewsBulletin {
message_id: message.next_int()?,
message_type: message.next_int()?,
message: message.next_string()?,
exchange: message.next_string()?,
})
}
23 changes: 23 additions & 0 deletions src/news/encoders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,26 @@ pub(super) fn encode_request_news_providers() -> Result<RequestMessage, Error> {

Ok(message)
}

pub(super) fn encode_request_news_bulletins(all_messages: bool) -> Result<RequestMessage, Error> {
let mut message = RequestMessage::new();

const VERSION: i32 = 1;

message.push_field(&OutgoingMessages::RequestNewsBulletins);
message.push_field(&VERSION);
message.push_field(&all_messages);

Ok(message)
}

pub(super) fn encode_cancel_news_bulletin() -> Result<RequestMessage, Error> {
let mut message = RequestMessage::new();

const VERSION: i32 = 1;

message.push_field(&OutgoingMessages::CancelNewsBulletin);
message.push_field(&VERSION);

Ok(message)
}

0 comments on commit 15a4d65

Please sign in to comment.