ibapi/scanner/sync.rs
1//! Synchronous implementation of scanner functionality
2
3use super::common::{decoders, encoders};
4use super::*;
5use crate::client::blocking::{ClientRequestBuilders, Subscription};
6use crate::client::sync::Client;
7use crate::common::request_helpers::{self, expect_proto};
8use crate::contracts::TagValue;
9use crate::messages::OutgoingMessages;
10use crate::Error;
11
12impl Client {
13 /// Requests an XML list of scanner parameters valid in TWS.
14 ///
15 /// # Errors
16 /// [`Error::Timeout`] if TWS sends no answer within 30 seconds, or within [`one_shot_timeout`](crate::client::blocking::ClientBuilder::one_shot_timeout) when set.
17 ///
18 /// # Examples
19 ///
20 /// ```no_run
21 /// use ibapi::client::blocking::Client;
22 ///
23 /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
24 /// let xml = client.scanner_parameters().expect("scanner_parameters failed");
25 /// println!("scanner parameters: {} chars", xml.len());
26 /// ```
27 pub fn scanner_parameters(&self) -> Result<String, Error> {
28 request_helpers::blocking::one_shot_shared(
29 self,
30 OutgoingMessages::RequestScannerParameters,
31 encoders::encode_scanner_parameters,
32 expect_proto(decoders::decode_scanner_parameters_proto),
33 )
34 }
35
36 /// Starts a subscription to market scan results based on the provided parameters.
37 ///
38 /// # Examples
39 ///
40 /// ```no_run
41 /// use ibapi::client::blocking::Client;
42 /// use ibapi::scanner::ScannerSubscription;
43 /// use ibapi::contracts::TagValue;
44 ///
45 /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
46 ///
47 /// let mut sub = ScannerSubscription::default();
48 /// sub.instrument = Some("STK".to_string());
49 /// sub.location_code = Some("STK.US.MAJOR".to_string());
50 /// sub.scan_code = Some("TOP_PERC_GAIN".to_string());
51 /// // Further customize the subscription object as needed, for example:
52 /// // sub.above_price = Some(1.0);
53 /// // sub.below_price = Some(100.0);
54 /// // sub.number_of_rows = 20;
55 ///
56 /// // Filter options are advanced and not always needed. Pass an empty Vec if not used.
57 /// let mut filter_options: Vec<TagValue> = Vec::new();
58 /// // Example of adding a filter:
59 /// // filter_options.push(TagValue { tag: "marketCapAbove".to_string(), value: "1000000000".to_string() });
60 ///
61 /// match client.scanner_subscription(&sub, &filter_options) {
62 /// Ok(subscription) => {
63 /// // Iterate over received scanner data.
64 /// // Note: Scanner subscriptions can be continuous or return a snapshot.
65 /// // This example just takes the first batch if available.
66 /// match subscription.iter_data().next() {
67 /// Some(Ok(scanner_results_vec)) => {
68 /// println!("Scanner Results (first batch):");
69 /// for data in scanner_results_vec {
70 /// println!(" Rank: {}, Symbol: {}",
71 /// data.rank,
72 /// data.contract_details.contract.symbol);
73 /// }
74 /// }
75 /// Some(Err(e)) => eprintln!("Scanner error: {e:?}"),
76 /// None => println!("No scanner results received in the first check."),
77 /// }
78 /// // In a real application, you might continuously iterate or handle updates.
79 /// // Remember to cancel the subscription when no longer needed if it's continuous.
80 /// // subscription.cancel();
81 /// }
82 /// Err(e) => {
83 /// eprintln!("Failed to start scanner subscription: {e:?}");
84 /// }
85 /// };
86 /// ```
87 pub fn scanner_subscription(&self, subscription: &ScannerSubscription, filter: &[TagValue]) -> Result<Subscription<Vec<ScannerData>>, Error> {
88 let builder = self.request();
89 let request = encoders::encode_scanner_subscription(builder.request_id(), subscription, filter)?;
90 builder.send(request)
91 }
92}
93
94#[cfg(test)]
95#[path = "sync_tests.rs"]
96mod tests;