use serde::{Deserialize, Serialize};
use crate::{
client::{DataStream, ResponseContext, Subscription},
messages::{IncomingMessages, OutgoingMessages},
orders::TagValue,
server_versions, Client, Error,
};
pub(super) fn scanner_parameters(client: &Client) -> Result<String, Error> {
let request = encoders::encode_scanner_parameters()?;
let subscription = client.send_shared_request(OutgoingMessages::RequestScannerParameters, request)?;
match subscription.next() {
Some(Ok(message)) => decoders::decode_scanner_parameters(message),
Some(Err(Error::ConnectionReset)) => scanner_parameters(client),
Some(Err(e)) => Err(e),
None => Err(Error::UnexpectedEndOfStream),
}
}
pub struct ScannerSubscription {
pub number_of_rows: i32,
pub instrument: Option<String>,
pub location_code: Option<String>,
pub scan_code: Option<String>,
pub above_price: Option<f64>,
pub below_price: Option<f64>,
pub above_volume: Option<i32>,
pub average_option_volume_above: Option<i32>,
pub market_cap_above: Option<f64>,
pub market_cap_below: Option<f64>,
pub moody_rating_above: Option<String>,
pub moody_rating_below: Option<String>,
pub sp_rating_above: Option<String>,
pub sp_rating_below: Option<String>,
pub maturity_date_above: Option<String>,
pub maturity_date_below: Option<String>,
pub coupon_rate_above: Option<f64>,
pub coupon_rate_below: Option<f64>,
pub exclude_convertible: bool,
pub scanner_setting_pairs: Option<String>,
pub stock_type_filter: Option<String>,
}
impl Default for ScannerSubscription {
fn default() -> Self {
ScannerSubscription {
number_of_rows: -1,
instrument: None,
location_code: None,
scan_code: None,
above_price: None,
below_price: None,
above_volume: None,
average_option_volume_above: None,
market_cap_above: None,
market_cap_below: None,
moody_rating_above: None,
moody_rating_below: None,
sp_rating_above: None,
sp_rating_below: None,
maturity_date_above: None,
maturity_date_below: None,
coupon_rate_above: None,
coupon_rate_below: None,
exclude_convertible: false,
scanner_setting_pairs: None,
stock_type_filter: None,
}
}
}
impl DataStream<Vec<ScannerData>> for Vec<ScannerData> {
fn decode(_client: &Client, message: &mut crate::messages::ResponseMessage) -> Result<Vec<ScannerData>, Error> {
match message.message_type() {
IncomingMessages::ScannerData => Ok(decoders::decode_scanner_data(message.clone())?),
_ => Err(Error::UnexpectedResponse(message.clone())),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
pub struct ScannerData {
pub rank: i32,
pub contract_details: crate::contracts::ContractDetails,
pub leg: String,
}
pub(super) fn scanner_subscription<'a>(
client: &'a Client,
subscription: &ScannerSubscription,
filter: &Vec<TagValue>,
) -> Result<Subscription<'a, Vec<ScannerData>>, Error> {
if !filter.is_empty() {
client.check_server_version(
server_versions::SCANNER_GENERIC_OPTS,
"It does not support API scanner subscription generic filter options.",
)?
}
let request_id = client.next_request_id();
let request = encoders::encode_scanner_subscription(request_id, client.server_version, subscription, filter)?;
let subscription = client.send_request(request_id, request)?;
Ok(Subscription::new(client, subscription, ResponseContext::default()))
}
mod encoders {
use crate::messages::OutgoingMessages;
use crate::messages::RequestMessage;
use crate::orders::TagValue;
use crate::server_versions;
use crate::Error;
use super::ScannerSubscription;
pub(super) fn encode_scanner_parameters() -> Result<RequestMessage, Error> {
const VERSION: i32 = 1;
let mut message = RequestMessage::new();
message.push_field(&OutgoingMessages::RequestScannerParameters);
message.push_field(&VERSION);
Ok(message)
}
pub(super) fn encode_scanner_subscription(
request_id: i32,
server_version: i32,
subscription: &ScannerSubscription,
filter: &Vec<TagValue>,
) -> Result<RequestMessage, Error> {
const VERSION: i32 = 4;
let mut message = RequestMessage::new();
message.push_field(&OutgoingMessages::RequestScannerSubscription);
if server_version < server_versions::SCANNER_GENERIC_OPTS {
message.push_field(&VERSION);
}
message.push_field(&request_id);
message.push_field(&subscription.number_of_rows);
message.push_field(&subscription.instrument);
message.push_field(&subscription.location_code);
message.push_field(&subscription.scan_code);
message.push_field(&subscription.above_price);
message.push_field(&subscription.below_price);
message.push_field(&subscription.above_volume);
message.push_field(&subscription.market_cap_above);
message.push_field(&subscription.market_cap_below);
message.push_field(&subscription.moody_rating_above);
message.push_field(&subscription.moody_rating_below);
message.push_field(&subscription.sp_rating_above);
message.push_field(&subscription.sp_rating_below);
message.push_field(&subscription.maturity_date_above);
message.push_field(&subscription.maturity_date_below);
message.push_field(&subscription.coupon_rate_above);
message.push_field(&subscription.coupon_rate_below);
message.push_field(&subscription.exclude_convertible);
message.push_field(&subscription.average_option_volume_above);
message.push_field(&subscription.scanner_setting_pairs);
message.push_field(&subscription.stock_type_filter);
if server_version >= server_versions::SCANNER_GENERIC_OPTS {
message.push_field(filter);
}
if server_version >= server_versions::LINKING {
message.push_field(&""); }
Ok(message)
}
}
mod decoders {
use crate::contracts::SecurityType;
use crate::messages::ResponseMessage;
use crate::Error;
use super::ScannerData;
pub(super) fn decode_scanner_parameters(mut message: ResponseMessage) -> Result<String, Error> {
message.skip(); message.skip(); message.next_string()
}
pub(super) fn decode_scanner_data(mut message: ResponseMessage) -> Result<Vec<ScannerData>, Error> {
message.skip(); let message_version = message.next_int()?;
message.skip(); let number_of_elements = message.next_int()?;
let mut matches = Vec::with_capacity(number_of_elements as usize);
for _ in 0..number_of_elements {
let mut scanner_data = ScannerData {
rank: message.next_int()?,
..Default::default()
};
if message_version >= 3 {
scanner_data.contract_details.contract.contract_id = message.next_int()?;
}
scanner_data.contract_details.contract.symbol = message.next_string()?;
scanner_data.contract_details.contract.security_type = SecurityType::from(&message.next_string()?);
scanner_data.contract_details.contract.last_trade_date_or_contract_month = message.next_string()?;
scanner_data.contract_details.contract.strike = message.next_double()?;
scanner_data.contract_details.contract.right = message.next_string()?;
scanner_data.contract_details.contract.exchange = message.next_string()?;
scanner_data.contract_details.contract.currency = message.next_string()?;
scanner_data.contract_details.contract.local_symbol = message.next_string()?;
scanner_data.contract_details.market_name = message.next_string()?;
scanner_data.contract_details.contract.trading_class = message.next_string()?;
message.skip(); message.skip(); message.skip(); scanner_data.leg = if message_version >= 2 { message.next_string()? } else { "".to_string() };
matches.push(scanner_data);
}
Ok(matches)
}
}