1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//! Synchronous implementation of scanner functionality
use std::sync::Arc;
use super::common::{decoders, encoders};
use super::*;
use crate::client::blocking::Subscription;
use crate::client::sync::Client;
use crate::contracts::TagValue;
use crate::messages::OutgoingMessages;
use crate::{server_versions, Error};
impl Client {
/// Requests an XML list of scanner parameters valid in TWS.
///
/// # Examples
///
/// ```no_run
/// use ibapi::client::blocking::Client;
/// use ibapi::scanner::ScannerSubscription;
/// use ibapi::contracts::TagValue;
///
/// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
///
/// let mut sub = ScannerSubscription::default();
/// sub.instrument = Some("STK".to_string());
/// sub.location_code = Some("STK.US.MAJOR".to_string());
/// sub.scan_code = Some("TOP_PERC_GAIN".to_string());
/// // Further customize the subscription object as needed, for example:
/// // sub.above_price = Some(1.0);
/// // sub.below_price = Some(100.0);
/// // sub.number_of_rows = Some(20);
///
/// // Filter options are advanced and not always needed. Pass an empty Vec if not used.
/// let filter_options: Vec<TagValue> = Vec::new();
/// // Example of adding a filter:
/// // filter_options.push(TagValue { tag: "marketCapAbove".to_string(), value: "1000000000".to_string() });
///
/// match client.scanner_subscription(&sub, &filter_options) {
/// Ok(subscription) => {
/// // Iterate over received scanner data.
/// // Note: Scanner subscriptions can be continuous or return a snapshot.
/// // This example just takes the first batch if available.
/// match subscription.iter_data().next() {
/// Some(Ok(scanner_results_vec)) => {
/// println!("Scanner Results (first batch):");
/// for data in scanner_results_vec {
/// println!(" Rank: {}, Symbol: {}",
/// data.rank,
/// data.contract_details.contract.symbol);
/// }
/// }
/// Some(Err(e)) => eprintln!("Scanner error: {e:?}"),
/// None => println!("No scanner results received in the first check."),
/// }
/// // In a real application, you might continuously iterate or handle updates.
/// // Remember to cancel the subscription when no longer needed if it's continuous.
/// // subscription.cancel();
/// }
/// Err(e) => {
/// eprintln!("Failed to start scanner subscription: {e:?}");
/// }
/// };
/// ```
pub fn scanner_parameters(&self) -> Result<String, Error> {
let request = encoders::encode_scanner_parameters()?;
let subscription = self.send_shared_request(OutgoingMessages::RequestScannerParameters, request)?;
match subscription.next() {
Some(Ok(message)) => decoders::decode_scanner_parameters(&message),
Some(Err(Error::ConnectionReset)) => self.scanner_parameters(),
Some(Err(e)) => Err(e),
None => Err(Error::UnexpectedEndOfStream),
}
}
/// Starts a subscription to market scan results based on the provided parameters.
///
/// # Examples
///
/// ```no_run
/// use ibapi::client::blocking::Client;
/// use ibapi::scanner::ScannerSubscription;
/// use ibapi::contracts::TagValue;
///
/// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed");
///
/// let mut sub = ScannerSubscription::default();
/// sub.instrument = Some("STK".to_string());
/// sub.location_code = Some("STK.US.MAJOR".to_string());
/// sub.scan_code = Some("TOP_PERC_GAIN".to_string());
/// // Further customize the subscription object as needed, for example:
/// // sub.above_price = Some(1.0);
/// // sub.below_price = Some(100.0);
/// // sub.number_of_rows = Some(20);
///
/// // Filter options are advanced and not always needed. Pass an empty Vec if not used.
/// let mut filter_options: Vec<TagValue> = Vec::new();
/// // Example of adding a filter:
/// // filter_options.push(TagValue { tag: "marketCapAbove".to_string(), value: "1000000000".to_string() });
///
/// match client.scanner_subscription(&sub, &filter_options) {
/// Ok(subscription) => {
/// // Iterate over received scanner data.
/// // Note: Scanner subscriptions can be continuous or return a snapshot.
/// // This example just takes the first batch if available.
/// match subscription.iter_data().next() {
/// Some(Ok(scanner_results_vec)) => {
/// println!("Scanner Results (first batch):");
/// for data in scanner_results_vec {
/// println!(" Rank: {}, Symbol: {}",
/// data.rank,
/// data.contract_details.contract.symbol);
/// }
/// }
/// Some(Err(e)) => eprintln!("Scanner error: {e:?}"),
/// None => println!("No scanner results received in the first check."),
/// }
/// // In a real application, you might continuously iterate or handle updates.
/// // Remember to cancel the subscription when no longer needed if it's continuous.
/// // subscription.cancel();
/// }
/// Err(e) => {
/// eprintln!("Failed to start scanner subscription: {e:?}");
/// }
/// };
/// ```
pub fn scanner_subscription(&self, subscription: &ScannerSubscription, filter: &[TagValue]) -> Result<Subscription<Vec<ScannerData>>, Error> {
if !filter.is_empty() {
self.check_server_version(
server_versions::SCANNER_GENERIC_OPTS,
"It does not support API scanner subscription generic filter options.",
)?
}
let request_id = self.next_request_id();
let request = encoders::encode_scanner_subscription(request_id, subscription, filter)?;
let subscription = self.send_request(request_id, request)?;
Ok(Subscription::new(Arc::clone(&self.message_bus), subscription, self.decoder_context()))
}
}
#[cfg(test)]
#[path = "sync_tests.rs"]
mod tests;