1use std::env;
2use std::io::{self, Write};
3use std::str::FromStr;
4use std::time::Duration;
5
6use rust_decimal::Decimal;
7use thiserror::Error;
8
9use truefix_twsapi_client::client::{ClientConfig, TwsApiClient};
10use truefix_twsapi_client::events::Event;
11use truefix_twsapi_client::requests::{
12 CalculateImpliedVolatilityRequest, CalculateOptionPriceRequest, ContractDetailsRequest,
13 ExecutionRequest, ExerciseOptionsRequest, HeadTimestampRequest, HistogramDataRequest,
14 HistoricalDataRequest, HistoricalNewsRequest, HistoricalTicksRequest, MarketDataRequest,
15 MarketDepthRequest, PlaceOrderRequest, RealTimeBarsRequest, ScannerSubscriptionRequest,
16 TickByTickRequest, WshEventDataRequest,
17};
18use truefix_twsapi_client::types::{
19 Contract, ExecutionFilter, Order, ScannerSubscription, TagValue,
20};
21
22#[derive(Debug, Error)]
23enum CliError {
24 #[error("{0}")]
25 Usage(String),
26 #[error(transparent)]
27 Io(#[from] std::io::Error),
28 #[error(transparent)]
29 Tws(#[from] truefix_twsapi_client::error::TwsApiError),
30}
31
32enum Command {
33 CurrentTime,
34 CurrentTimeMillis,
35 RequestIds,
36 SetServerLogLevel,
37 MarketData { symbol: Option<String> },
38 MarketDepth,
39 PlaceOrder,
40 CancelOrder,
41 OpenOrders,
42 AutoOpenOrders,
43 AllOpenOrders,
44 GlobalCancel,
45 CompletedOrders,
46 AccountSummary,
47 Positions,
48 PositionsMulti,
49 AccountUpdatesMulti,
50 HistoricalData,
51 HistoricalTicks,
52 HeadTimestamp,
53 HistogramData,
54 ContractDetails,
55 BondContractDetails,
56 Executions,
57 Scanner,
58 ScannerParameters,
59 RealTimeBars,
60 TickByTick,
61 SmartComponents,
62 MarketRule,
63 CalculateImpliedVolatility,
64 CalculateOptionPrice,
65 ExerciseOptions,
66 Pnl,
67 PnlSingle,
68 NewsBulletins,
69 NewsArticle,
70 HistoricalNews,
71 ManagedAccounts,
72 Disconnect,
73 AccountUpdates,
74 MarketDepthExchanges,
75 SecDefOptParams,
76 SoftDollarTiers,
77 FamilyCodes,
78 MatchingSymbols,
79 QueryDisplayGroups,
80 SubscribeToGroupEvents,
81 UpdateDisplayGroup,
82 UnsubscribeFromGroupEvents,
83 RequestFa,
84 ReplaceFa,
85 WshMetaData,
86 WshEventData,
87 UserInfo,
88 NewsProviders,
89}
90
91#[tokio::main]
92async fn main() -> Result<(), CliError> {
93 let args = env::args().skip(1).collect::<Vec<_>>();
94 let mut client = connect().await?;
95 wait_until_api_ready(&mut client).await?;
96
97 if args.is_empty() {
98 print_help();
99 run_interactive(&mut client).await?;
100 } else {
101 let (command, extra_args) = parse_command(args)?;
102 run_command(&mut client, command).await?;
103 if !extra_args.is_empty() {
104 eprintln!("unused args: {}", extra_args.join(" "));
105 }
106 }
107
108 let _ = client.disconnect().await;
109 Ok(())
110}
111
112async fn run_command(client: &mut TwsApiClient, command: Command) -> Result<(), CliError> {
113 match command {
114 Command::CurrentTime => run_current_time(client).await?,
115 Command::CurrentTimeMillis => run_current_time_millis(client).await?,
116 Command::RequestIds => run_request_ids(client).await?,
117 Command::SetServerLogLevel => run_set_server_log_level(client).await?,
118 Command::MarketData { symbol } => run_market_data(client, symbol).await?,
119 Command::MarketDepth => run_market_depth(client).await?,
120 Command::PlaceOrder => run_place_order(client).await?,
121 Command::CancelOrder => run_cancel_order(client).await?,
122 Command::OpenOrders => run_open_orders(client).await?,
123 Command::AutoOpenOrders => run_auto_open_orders(client).await?,
124 Command::AllOpenOrders => run_all_open_orders(client).await?,
125 Command::GlobalCancel => run_global_cancel(client).await?,
126 Command::CompletedOrders => run_completed_orders(client).await?,
127 Command::AccountSummary => run_account_summary(client).await?,
128 Command::Positions => run_positions(client).await?,
129 Command::PositionsMulti => run_positions_multi(client).await?,
130 Command::AccountUpdatesMulti => run_account_updates_multi(client).await?,
131 Command::HistoricalData => run_historical_data(client).await?,
132 Command::HistoricalTicks => run_historical_ticks(client).await?,
133 Command::HeadTimestamp => run_head_timestamp(client).await?,
134 Command::HistogramData => run_histogram_data(client).await?,
135 Command::ContractDetails => run_contract_details(client).await?,
136 Command::BondContractDetails => run_bond_contract_details(client).await?,
137 Command::Executions => run_executions(client).await?,
138 Command::Scanner => run_scanner(client).await?,
139 Command::ScannerParameters => run_scanner_parameters(client).await?,
140 Command::RealTimeBars => run_real_time_bars(client).await?,
141 Command::TickByTick => run_tick_by_tick(client).await?,
142 Command::SmartComponents => run_smart_components(client).await?,
143 Command::MarketRule => run_market_rule(client).await?,
144 Command::CalculateImpliedVolatility => run_calculate_implied_volatility(client).await?,
145 Command::CalculateOptionPrice => run_calculate_option_price(client).await?,
146 Command::ExerciseOptions => run_exercise_options(client).await?,
147 Command::Pnl => run_pnl(client).await?,
148 Command::PnlSingle => run_pnl_single(client).await?,
149 Command::NewsBulletins => run_news_bulletins(client).await?,
150 Command::NewsArticle => run_news_article(client).await?,
151 Command::HistoricalNews => run_historical_news(client).await?,
152 Command::ManagedAccounts => run_managed_accounts(client).await?,
153 Command::Disconnect => run_disconnect(client).await?,
154 Command::AccountUpdates => run_account_updates(client).await?,
155 Command::MarketDepthExchanges => run_market_depth_exchanges(client).await?,
156 Command::SecDefOptParams => run_sec_def_opt_params(client).await?,
157 Command::SoftDollarTiers => run_soft_dollar_tiers(client).await?,
158 Command::FamilyCodes => run_family_codes(client).await?,
159 Command::MatchingSymbols => run_matching_symbols(client).await?,
160 Command::QueryDisplayGroups => run_query_display_groups(client).await?,
161 Command::SubscribeToGroupEvents => run_subscribe_to_group_events(client).await?,
162 Command::UpdateDisplayGroup => run_update_display_group(client).await?,
163 Command::UnsubscribeFromGroupEvents => run_unsubscribe_from_group_events(client).await?,
164 Command::RequestFa => run_request_fa(client).await?,
165 Command::ReplaceFa => run_replace_fa(client).await?,
166 Command::WshMetaData => run_wsh_meta_data(client).await?,
167 Command::WshEventData => run_wsh_event_data(client).await?,
168 Command::UserInfo => run_user_info(client).await?,
169 Command::NewsProviders => run_news_providers(client).await?,
170 }
171 Ok(())
172}
173
174async fn run_interactive(client: &mut TwsApiClient) -> Result<(), CliError> {
175 let stdin = io::stdin();
176 let mut line = String::new();
177
178 loop {
179 print!("twsapi> ");
180 io::stdout().flush()?;
181 line.clear();
182
183 if stdin.read_line(&mut line)? == 0 {
184 println!();
185 break;
186 }
187
188 let input = line.trim();
189 if input.is_empty() {
190 continue;
191 }
192 if matches!(input, "quit" | "exit" | "q") {
193 break;
194 }
195 if matches!(input, "help" | "h" | "?") {
196 print_help();
197 continue;
198 }
199
200 let parts = input
201 .split_whitespace()
202 .map(str::to_owned)
203 .collect::<Vec<_>>();
204 let (command, extra_args) = match parse_command(parts) {
205 Ok(parsed) => parsed,
206 Err(err) => {
207 eprintln!("{err}");
208 continue;
209 }
210 };
211 if let Err(err) = run_command(client, command).await {
212 eprintln!("{err}");
213 }
214 if !extra_args.is_empty() {
215 eprintln!("unused args: {}", extra_args.join(" "));
216 }
217 }
218
219 Ok(())
220}
221
222fn parse_command(mut args: Vec<String>) -> Result<(Command, Vec<String>), CliError> {
223 if args.is_empty() {
224 return Err(CliError::Usage("missing command".to_owned()));
225 }
226
227 let command = match args.remove(0).as_str() {
228 "time" | "current-time" => Command::CurrentTime,
229 "current-time-ms" | "time-ms" => Command::CurrentTimeMillis,
230 "request-ids" | "req-ids" => Command::RequestIds,
231 "set-server-log-level" => Command::SetServerLogLevel,
232 "market-data" => {
233 let symbol = args.first().cloned();
234 if !args.is_empty() {
235 args.remove(0);
236 }
237 Command::MarketData { symbol }
238 }
239 "market-depth" => Command::MarketDepth,
240 "place-order" => Command::PlaceOrder,
241 "cancel-order" => Command::CancelOrder,
242 "open-orders" => Command::OpenOrders,
243 "auto-open-orders" => Command::AutoOpenOrders,
244 "all-open-orders" => Command::AllOpenOrders,
245 "global-cancel" => Command::GlobalCancel,
246 "completed-orders" => Command::CompletedOrders,
247 "account-summary" => Command::AccountSummary,
248 "positions" => Command::Positions,
249 "positions-multi" => Command::PositionsMulti,
250 "account-updates-multi" => Command::AccountUpdatesMulti,
251 "historical-data" => Command::HistoricalData,
252 "historical-ticks" => Command::HistoricalTicks,
253 "head-timestamp" => Command::HeadTimestamp,
254 "histogram-data" => Command::HistogramData,
255 "contract-details" => Command::ContractDetails,
256 "bond-contract-details" => Command::BondContractDetails,
257 "executions" => Command::Executions,
258 "scanner" => Command::Scanner,
259 "scanner-params" => Command::ScannerParameters,
260 "real-time-bars" => Command::RealTimeBars,
261 "tick-by-tick" => Command::TickByTick,
262 "smart-components" => Command::SmartComponents,
263 "market-rule" => Command::MarketRule,
264 "calc-iv" | "calculate-implied-volatility" => Command::CalculateImpliedVolatility,
265 "calc-option-price" | "calculate-option-price" => Command::CalculateOptionPrice,
266 "exercise-options" => Command::ExerciseOptions,
267 "pnl" => Command::Pnl,
268 "pnl-single" => Command::PnlSingle,
269 "news-bulletins" => Command::NewsBulletins,
270 "news-article" => Command::NewsArticle,
271 "historical-news" => Command::HistoricalNews,
272 "managed-accounts" => Command::ManagedAccounts,
273 "disconnect" => Command::Disconnect,
274 "account-updates" => Command::AccountUpdates,
275 "market-depth-exchanges" => Command::MarketDepthExchanges,
276 "sec-def-opt-params" => Command::SecDefOptParams,
277 "soft-dollar-tiers" => Command::SoftDollarTiers,
278 "family-codes" => Command::FamilyCodes,
279 "matching-symbols" => Command::MatchingSymbols,
280 "query-display-groups" => Command::QueryDisplayGroups,
281 "subscribe-group-events" => Command::SubscribeToGroupEvents,
282 "update-display-group" => Command::UpdateDisplayGroup,
283 "unsubscribe-group-events" => Command::UnsubscribeFromGroupEvents,
284 "request-fa" => Command::RequestFa,
285 "replace-fa" => Command::ReplaceFa,
286 "wsh-meta-data" => Command::WshMetaData,
287 "wsh-event-data" => Command::WshEventData,
288 "user-info" => Command::UserInfo,
289 "news-providers" => Command::NewsProviders,
290 "help" | "-h" | "--help" => {
291 return Err(CliError::Usage("use `help` inside the client".to_owned()));
292 }
293 other => return Err(CliError::Usage(format!("unknown command: {other}"))),
294 };
295
296 Ok((command, args))
297}
298
299fn print_help() {
300 eprintln!(
301 "usage:\n\
302 \x20\x20interactive: cargo run -p truefix-twsapi-client --example twsapi\n\
303 \x20\x20one-shot: cargo run -p truefix-twsapi-client --example twsapi -- <command>\n\
304 \n\
305 commands:\n\
306 \x20\x20time | current-time\n\
307 \x20\x20current-time-ms | time-ms\n\
308 \x20\x20request-ids | req-ids\n\
309 \x20\x20set-server-log-level\n\
310 \x20\x20market-data\n\
311 \x20\x20market-depth\n\
312 \x20\x20place-order\n\
313 \x20\x20cancel-order\n\
314 \x20\x20open-orders\n\
315 \x20\x20auto-open-orders\n\
316 \x20\x20all-open-orders\n\
317 \x20\x20global-cancel\n\
318 \x20\x20completed-orders\n\
319 \x20\x20account-summary\n\
320 \x20\x20account-updates\n\
321 \x20\x20account-updates-multi\n\
322 \x20\x20positions\n\
323 \x20\x20positions-multi\n\
324 \x20\x20historical-data\n\
325 \x20\x20historical-ticks\n\
326 \x20\x20head-timestamp\n\
327 \x20\x20histogram-data\n\
328 \x20\x20contract-details\n\
329 \x20\x20bond-contract-details\n\
330 \x20\x20disconnect\n\
331 \x20\x20executions\n\
332 \x20\x20scanner\n\
333 \x20\x20scanner-params\n\
334 \x20\x20real-time-bars\n\
335 \x20\x20tick-by-tick\n\
336 \x20\x20smart-components\n\
337 \x20\x20market-rule\n\
338 \x20\x20calc-iv | calculate-implied-volatility\n\
339 \x20\x20calc-option-price | calculate-option-price\n\
340 \x20\x20exercise-options\n\
341 \x20\x20pnl\n\
342 \x20\x20pnl-single\n\
343 \x20\x20news-bulletins\n\
344 \x20\x20news-article\n\
345 \x20\x20historical-news\n\
346 \x20\x20managed-accounts\n\
347 \x20\x20market-depth-exchanges\n\
348 \x20\x20sec-def-opt-params\n\
349 \x20\x20soft-dollar-tiers\n\
350 \x20\x20family-codes\n\
351 \x20\x20matching-symbols\n\
352 \x20\x20query-display-groups\n\
353 \x20\x20subscribe-group-events\n\
354 \x20\x20update-display-group\n\
355 \x20\x20unsubscribe-group-events\n\
356 \x20\x20request-fa\n\
357 \x20\x20replace-fa\n\
358 \x20\x20wsh-meta-data\n\
359 \x20\x20wsh-event-data\n\
360 \x20\x20user-info\n\
361 \x20\x20news-providers\n\
362 \n\
363 common env:\n\
364 \x20\x20TWS_HOST=127.0.0.1 TWS_PORT=7497 TWS_CLIENT_ID=1002\n\
365 \x20\x20TWS_SYMBOL=AAPL TWS_SEC_TYPE=STK TWS_EXCHANGE=SMART TWS_CURRENCY=USD\n\
366 \x20\x20TWS_REQ_ID=9001 TWS_ACCOUNT=<acct> TWS_MODEL_CODE=<model>\n"
367 );
368}
369
370fn env_string(key: &str, default: &str) -> String {
371 env::var(key).unwrap_or_else(|_| default.to_owned())
372}
373
374fn env_opt_string(key: &str) -> Option<String> {
375 env::var(key).ok().filter(|value| !value.is_empty())
376}
377
378fn env_parse<T>(key: &str, default: T) -> Result<T, CliError>
379where
380 T: FromStr,
381 T::Err: std::fmt::Display,
382{
383 match env::var(key) {
384 Ok(value) if !value.is_empty() => value
385 .parse::<T>()
386 .map_err(|err| CliError::Usage(format!("{key}={value}: {err}"))),
387 _ => Ok(default),
388 }
389}
390
391fn env_bool(key: &str, default: bool) -> Result<bool, CliError> {
392 match env::var(key) {
393 Ok(value) if !value.is_empty() => match value.to_ascii_lowercase().as_str() {
394 "1" | "true" | "yes" | "y" => Ok(true),
395 "0" | "false" | "no" | "n" => Ok(false),
396 _ => Err(CliError::Usage(format!(
397 "{key} must be one of 1|0|true|false|yes|no|y|n"
398 ))),
399 },
400 _ => Ok(default),
401 }
402}
403
404fn env_decimal(key: &str) -> Result<Option<Decimal>, CliError> {
405 match env::var(key) {
406 Ok(value) if !value.is_empty() => value
407 .parse::<Decimal>()
408 .map(Some)
409 .map_err(|err| CliError::Usage(format!("{key}={value}: {err}"))),
410 _ => Ok(None),
411 }
412}
413
414fn env_tag_values(prefix: &str) -> Vec<TagValue> {
415 let mut values = Vec::new();
416 for idx in 1..=16 {
417 let tag_key = format!("{prefix}_{idx}_TAG");
418 let value_key = format!("{prefix}_{idx}_VALUE");
419 let Some(tag) = env_opt_string(&tag_key) else {
420 continue;
421 };
422 let value = env_opt_string(&value_key).unwrap_or_default();
423 values.push(TagValue { tag, value });
424 }
425 values
426}
427
428fn req_id(default: i32) -> Result<i32, CliError> {
429 env_parse("TWS_REQ_ID", default)
430}
431
432fn contract_from_env() -> Result<Contract, CliError> {
433 let mut contract = Contract::default();
434 contract.con_id = env_parse("TWS_CONID", 0i32)?;
435 contract.symbol = env_string("TWS_SYMBOL", "AAPL");
436 contract.sec_type = env_string("TWS_SEC_TYPE", "STK");
437 contract.last_trade_date_or_contract_month =
438 env_string("TWS_LAST_TRADE_DATE_OR_CONTRACT_MONTH", "");
439 contract.strike = env_parse("TWS_STRIKE", contract.strike)?;
440 contract.right = env_string("TWS_RIGHT", "");
441 contract.multiplier = env_string("TWS_MULTIPLIER", "");
442 contract.exchange = env_string("TWS_EXCHANGE", "SMART");
443 contract.primary_exchange = env_string("TWS_PRIMARY_EXCHANGE", "");
444 contract.currency = env_string("TWS_CURRENCY", "USD");
445 contract.local_symbol = env_string("TWS_LOCAL_SYMBOL", "");
446 contract.trading_class = env_string("TWS_TRADING_CLASS", "");
447 contract.include_expired = env_bool("TWS_INCLUDE_EXPIRED", false)?;
448 contract.sec_id_type = env_string("TWS_SEC_ID_TYPE", "");
449 contract.sec_id = env_string("TWS_SEC_ID", "");
450 contract.combo_legs_description = env_string("TWS_COMBO_LEGS_DESCRIPTION", "");
451 Ok(contract)
452}
453
454fn parse_order_kind() -> Result<String, CliError> {
455 Ok(env_string("TWS_ORDER_KIND", "limit").to_ascii_lowercase())
456}
457
458fn side(value: &str) -> Result<String, CliError> {
459 match value.to_ascii_uppercase().as_str() {
460 "BUY" | "SELL" => Ok(value.to_ascii_uppercase()),
461 other => Err(CliError::Usage(format!(
462 "TWS_ORDER_SIDE must be BUY or SELL, got {other}"
463 ))),
464 }
465}
466
467fn order_from_env() -> Result<Order, CliError> {
468 let mut order = Order::default();
469 order.order_id = req_id(5001)?;
470 order.action = side(&env_string("TWS_ORDER_SIDE", "BUY"))?;
471 order.total_quantity = env_decimal("TWS_ORDER_QTY")?.unwrap_or(Decimal::ONE);
472 order.account = env_string("TWS_ACCOUNT", "");
473 order.order_type = env_string("TWS_ORDER_TYPE", "");
474 order.tif = env_string("TWS_TIF", "DAY");
475 order.good_till_date = env_string("TWS_GOOD_TILL_DATE", "");
476 order.transmit = env_bool("TWS_TRANSMIT", true)?;
477 order.what_if = env_bool("TWS_WHAT_IF", false)?;
478 order.outside_rth = env_bool("TWS_OUTSIDE_RTH", false)?;
479 order.hidden = env_bool("TWS_HIDDEN", false)?;
480 order.solicited = env_bool("TWS_SOLICITED", false)?;
481 order.cash_qty = env_parse("TWS_CASH_QTY", order.cash_qty)?;
482 order.order_ref = env_string("TWS_ORDER_REF", "");
483 Ok(order)
484}
485
486fn order_kind_from_env(order: &mut Order) -> Result<(), CliError> {
487 match parse_order_kind()?.as_str() {
488 "market" => {
489 order.order_type = "MKT".to_owned();
490 }
491 "limit" => {
492 order.order_type = "LMT".to_owned();
493 order.limit_price = env_parse("TWS_LIMIT_PRICE", 0.0f64)?;
494 }
495 "stop" => {
496 order.order_type = "STP".to_owned();
497 order.trail_stop_price = env_parse("TWS_TRIGGER_PRICE", 0.0f64)?;
498 }
499 "stoplimit" => {
500 order.order_type = "STP LMT".to_owned();
501 order.limit_price = env_parse("TWS_LIMIT_PRICE", 0.0f64)?;
502 order.trail_stop_price = env_parse("TWS_TRIGGER_PRICE", 0.0f64)?;
503 }
504 other => {
505 return Err(CliError::Usage(format!(
506 "TWS_ORDER_KIND must be market|limit|stop|stoplimit, got {other}"
507 )));
508 }
509 }
510 Ok(())
511}
512
513async fn connect() -> Result<TwsApiClient, CliError> {
514 let host = env_string("TWS_HOST", "127.0.0.1");
515 let port = env_parse("TWS_PORT", 7497u16)?;
516 let client_id = env_parse("TWS_CLIENT_ID", 1002i32)?;
517 Ok(TwsApiClient::connect(ClientConfig::new(host, port, client_id)).await?)
518}
519
520async fn wait_until_api_ready(client: &mut TwsApiClient) -> Result<(), CliError> {
521 let result = tokio::time::timeout(Duration::from_secs(10), async {
522 while !client.api_ready() {
523 match client.read_event().await? {
524 Event::Error { code, message, .. } => {
525 eprintln!("TWS notice {code}: {message}");
526 }
527 other => {
528 print_event(&other);
529 }
530 }
531 }
532 truefix_twsapi_client::error::TwsApiResult::Ok(())
533 })
534 .await;
535
536 match result {
537 Ok(Ok(())) => Ok(()),
538 Ok(Err(err)) => Err(err.into()),
539 Err(_) => Err(CliError::Usage(
540 "timed out waiting for initial API callbacks".to_owned(),
541 )),
542 }
543}
544
545async fn read_until<F>(
546 client: &mut TwsApiClient,
547 timeout_secs: u64,
548 mut on_event: F,
549) -> Result<bool, CliError>
550where
551 F: FnMut(Event) -> bool,
552{
553 let result = tokio::time::timeout(Duration::from_secs(timeout_secs), async {
554 loop {
555 let event = client.read_event().await?;
556 if on_event(event) {
557 return truefix_twsapi_client::error::TwsApiResult::Ok(true);
558 }
559 }
560 })
561 .await;
562 match result {
563 Ok(Ok(done)) => Ok(done),
564 Ok(Err(err)) => Err(err.into()),
565 Err(_) => Ok(false),
566 }
567}
568
569fn print_event(event: &Event) {
570 match event {
571 Event::CurrentTime { time } => println!("current_time={time}"),
572 Event::CurrentTimeInMillis { time_in_millis } => {
573 println!("current_time_ms={time_in_millis}")
574 }
575 Event::MarketDataType {
576 req_id,
577 market_data_type,
578 } => println!("market_data_type req_id={req_id} type={market_data_type}"),
579 Event::TickPrice {
580 req_id,
581 tick_type,
582 price,
583 attrib,
584 } => println!(
585 "tick_price req_id={req_id} tick_type={tick_type} price={price} attrib={attrib}"
586 ),
587 Event::TickSize {
588 req_id,
589 tick_type,
590 size,
591 } => println!("tick_size req_id={req_id} tick_type={tick_type} size={size}"),
592 Event::TickString {
593 req_id,
594 tick_type,
595 value,
596 } => println!("tick_string req_id={req_id} tick_type={tick_type} value={value}"),
597 Event::TickOptionComputation {
598 req_id,
599 tick_type,
600 tick_attrib,
601 implied_vol,
602 delta,
603 opt_price,
604 pv_dividend,
605 gamma,
606 vega,
607 theta,
608 und_price,
609 } => println!(
610 "tick_option req_id={req_id} tick_type={tick_type} tick_attrib={tick_attrib} implied_vol={implied_vol} delta={delta} opt_price={opt_price} pv_dividend={pv_dividend} gamma={gamma} vega={vega} theta={theta} und_price={und_price}"
611 ),
612 Event::OrderStatus {
613 order_id,
614 status,
615 filled,
616 remaining,
617 avg_fill_price,
618 perm_id,
619 parent_id,
620 last_fill_price,
621 client_id,
622 why_held,
623 market_cap_price,
624 } => println!(
625 "order_status order_id={order_id} status={status} filled={filled} remaining={remaining} avg_fill_price={avg_fill_price} perm_id={perm_id} parent_id={parent_id} last_fill_price={last_fill_price} client_id={client_id} why_held={why_held} market_cap_price={market_cap_price}"
626 ),
627 Event::OpenOrder {
628 order_id,
629 contract,
630 order,
631 ..
632 } => println!(
633 "open_order order_id={order_id} symbol={} sec_type={} exchange={} action={} qty={} type={}",
634 contract.symbol,
635 contract.sec_type,
636 contract.exchange,
637 order.action,
638 order.total_quantity,
639 order.order_type
640 ),
641 Event::CompletedOrder {
642 contract,
643 order,
644 order_state,
645 } => println!(
646 "completed_order symbol={} sec_type={} exchange={} action={} qty={} status={} completed_status={}",
647 contract.symbol,
648 contract.sec_type,
649 contract.exchange,
650 order.action,
651 order.total_quantity,
652 order_state.status,
653 order_state.completed_status
654 ),
655 Event::ContractDetails { req_id, details } => println!(
656 "contract_details req_id={req_id} symbol={} sec_type={} exchange={} currency={}",
657 details.contract.symbol,
658 details.contract.sec_type,
659 details.contract.exchange,
660 details.contract.currency
661 ),
662 Event::BondContractDetails { req_id, details } => println!(
663 "bond_contract_details req_id={req_id} symbol={} sec_type={} exchange={} currency={}",
664 details.contract.symbol,
665 details.contract.sec_type,
666 details.contract.exchange,
667 details.contract.currency
668 ),
669 Event::ExecutionDetails {
670 req_id,
671 contract,
672 execution,
673 } => println!(
674 "execution req_id={req_id} symbol={} exec_id={} shares={} price={}",
675 contract.symbol, execution.exec_id, execution.shares, execution.price
676 ),
677 Event::HistoricalData { req_id, bar } => println!(
678 "historical_bar req_id={req_id} date={} open={} high={} low={} close={} volume={} wap={} count={}",
679 bar.date, bar.open, bar.high, bar.low, bar.close, bar.volume, bar.wap, bar.bar_count
680 ),
681 Event::HistoricalDataUpdate { req_id, bar } => println!(
682 "historical_update req_id={req_id} date={} open={} high={} low={} close={}",
683 bar.date, bar.open, bar.high, bar.low, bar.close
684 ),
685 Event::HistoricalDataEnd { req_id, start, end } => {
686 println!("historical_end req_id={req_id} start={start} end={end}")
687 }
688 Event::RealTimeBar { req_id, time, bar } => println!(
689 "real_time_bar req_id={req_id} time={time} open={} high={} low={} close={} volume={} wap={} count={}",
690 bar.open, bar.high, bar.low, bar.close, bar.volume, bar.wap, bar.bar_count
691 ),
692 Event::HeadTimestamp {
693 req_id,
694 head_timestamp,
695 } => println!("head_timestamp req_id={req_id} head_timestamp={head_timestamp}"),
696 Event::ScannerData { req_id, rows } => {
697 println!("scanner_data req_id={req_id} rows={}", rows.len());
698 for row in rows {
699 println!(
700 " rank={} symbol={} sec_type={} market={} distance={} benchmark={} projection={} combo_key={}",
701 row.rank,
702 row.contract.symbol,
703 row.contract.sec_type,
704 row.market_name,
705 row.distance,
706 row.benchmark,
707 row.projection,
708 row.combo_key
709 );
710 }
711 }
712 Event::ScannerDataEnd { req_id } => println!("scanner_data_end req_id={req_id}"),
713 Event::AccountSummary {
714 req_id,
715 account,
716 tag,
717 value,
718 currency,
719 } => println!(
720 "account_summary req_id={req_id} account={account} tag={tag} value={value} currency={currency}"
721 ),
722 Event::AccountValue {
723 key,
724 value,
725 currency,
726 account_name,
727 } => println!(
728 "account_value account={account_name} key={key} value={value} currency={currency}"
729 ),
730 Event::PortfolioValue {
731 contract,
732 position,
733 market_price,
734 market_value,
735 average_cost,
736 unrealized_pnl,
737 realized_pnl,
738 account_name,
739 } => println!(
740 "portfolio account={account_name} symbol={} position={} market_price={} market_value={} average_cost={} unrealized_pnl={} realized_pnl={}",
741 contract.symbol,
742 position,
743 market_price,
744 market_value,
745 average_cost,
746 unrealized_pnl,
747 realized_pnl
748 ),
749 Event::Position {
750 account,
751 contract,
752 position,
753 avg_cost,
754 } => println!(
755 "position account={account} symbol={} sec_type={} exchange={} position={} avg_cost={}",
756 contract.symbol, contract.sec_type, contract.exchange, position, avg_cost
757 ),
758 Event::PositionMulti {
759 req_id,
760 account,
761 model_code,
762 contract,
763 position,
764 avg_cost,
765 } => println!(
766 "position_multi req_id={req_id} account={account} model_code={model_code} symbol={} position={} avg_cost={}",
767 contract.symbol, position, avg_cost
768 ),
769 Event::Pnl {
770 req_id,
771 daily_pnl,
772 unrealized_pnl,
773 realized_pnl,
774 } => println!(
775 "pnl req_id={req_id} daily_pnl={daily_pnl} unrealized_pnl={unrealized_pnl} realized_pnl={realized_pnl}"
776 ),
777 Event::PnlSingle {
778 req_id,
779 position,
780 daily_pnl,
781 unrealized_pnl,
782 realized_pnl,
783 value,
784 } => println!(
785 "pnl_single req_id={req_id} position={position} daily_pnl={daily_pnl} unrealized_pnl={unrealized_pnl} realized_pnl={realized_pnl} value={value}"
786 ),
787 Event::ManagedAccounts { accounts } => println!("managed_accounts {accounts}"),
788 Event::MarketDepth {
789 req_id,
790 position,
791 operation,
792 side,
793 price,
794 size,
795 market_maker,
796 is_smart_depth,
797 } => println!(
798 "market_depth req_id={req_id} position={position} operation={operation} side={side} price={price} size={size} market_maker={market_maker} smart_depth={is_smart_depth}"
799 ),
800 Event::MarketDepthExchanges { descriptions } => {
801 println!("market_depth_exchanges count={}", descriptions.len());
802 for description in descriptions {
803 println!(
804 " {} {} {} {} {}",
805 description.exchange,
806 description.security_type,
807 description.listing_exchange,
808 description.service_data_type,
809 description.aggregate_group
810 );
811 }
812 }
813 Event::NewsProviders { providers } => {
814 println!("news_providers count={}", providers.len());
815 for provider in providers {
816 println!(" {} {}", provider.code, provider.name);
817 }
818 }
819 Event::ScannerParameters { xml } => println!("{xml}"),
820 Event::OpenOrderEnd
821 | Event::CompletedOrdersEnd
822 | Event::ContractDetailsEnd { .. }
823 | Event::ExecutionDetailsEnd { .. }
824 | Event::PositionEnd
825 | Event::AccountSummaryEnd { .. }
826 | Event::AccountDownloadEnd { .. }
827 | Event::ConnectionClosed
828 | Event::ConnectAck => println!("{event:?}"),
829 Event::Error {
830 req_id,
831 code,
832 message,
833 advanced_order_reject_json,
834 ..
835 } => eprintln!(
836 "TWS error req_id={req_id} code={code} message={message} advanced={advanced_order_reject_json}"
837 ),
838 other => println!("{other:?}"),
839 }
840}
841
842async fn run_current_time(client: &mut TwsApiClient) -> Result<(), CliError> {
843 client.req_current_time().await?;
844 read_until(client, 5, |event| match event {
845 Event::CurrentTime { .. } | Event::CurrentTimeInMillis { .. } => {
846 print_event(&event);
847 true
848 }
849 Event::Error { .. } => {
850 print_event(&event);
851 true
852 }
853 other => {
854 print_event(&other);
855 false
856 }
857 })
858 .await?;
859 Ok(())
860}
861
862async fn run_current_time_millis(client: &mut TwsApiClient) -> Result<(), CliError> {
863 client.req_current_time_in_millis().await?;
864 read_until(client, 5, |event| match event {
865 Event::CurrentTimeInMillis { .. } => {
866 print_event(&event);
867 true
868 }
869 Event::Error { .. } => {
870 print_event(&event);
871 false
872 }
873 other => {
874 print_event(&other);
875 false
876 }
877 })
878 .await?;
879 Ok(())
880}
881
882async fn run_request_ids(client: &mut TwsApiClient) -> Result<(), CliError> {
883 let num_ids = env_parse("TWS_NUM_IDS", 1i32)?;
884 client.req_ids(num_ids).await?;
885 read_until(client, 5, |event| match event {
886 Event::NextValidId { order_id } => {
887 println!("next_valid_id order_id={order_id}");
888 true
889 }
890 Event::Error { .. } => {
891 print_event(&event);
892 false
893 }
894 other => {
895 print_event(&other);
896 false
897 }
898 })
899 .await?;
900 Ok(())
901}
902
903async fn run_set_server_log_level(client: &mut TwsApiClient) -> Result<(), CliError> {
904 let log_level = env_parse("TWS_LOG_LEVEL", 1i32)?;
905 client.set_server_log_level(log_level).await?;
906 println!("server_log_level={log_level}");
907 Ok(())
908}
909
910async fn run_market_data(
911 client: &mut TwsApiClient,
912 symbol: Option<String>,
913) -> Result<(), CliError> {
914 client
915 .req_market_data_type(env_parse("TWS_MARKET_DATA_TYPE", 1i32)?)
916 .await?;
917 let req_id = req_id(9001)?;
918 let symbol = symbol.unwrap_or_else(|| env_string("TWS_SYMBOL", "AAPL"));
919 let mut contract = contract_from_env()?;
920 contract.symbol = symbol;
921 client
922 .req_mkt_data(MarketDataRequest {
923 req_id,
924 contract,
925 generic_tick_list: env_string("TWS_GENERIC_TICK_LIST", ""),
926 snapshot: env_bool("TWS_SNAPSHOT", false)?,
927 regulatory_snapshot: env_bool("TWS_REGULATORY_SNAPSHOT", false)?,
928 market_data_options: env_tag_values("TWS_MARKET_DATA_OPTION"),
929 })
930 .await?;
931 let done = read_until(
932 client,
933 env_parse("TWS_WAIT_SECS", 30u64)?,
934 |event| match event {
935 Event::TickPrice {
936 req_id: event_req_id,
937 ..
938 }
939 | Event::TickSize {
940 req_id: event_req_id,
941 ..
942 }
943 | Event::TickString {
944 req_id: event_req_id,
945 ..
946 }
947 | Event::TickGeneric {
948 req_id: event_req_id,
949 ..
950 }
951 | Event::TickOptionComputation {
952 req_id: event_req_id,
953 ..
954 } if event_req_id == req_id => {
955 print_event(&event);
956 true
957 }
958 Event::Error {
959 req_id: event_req_id,
960 ..
961 } if event_req_id == req_id => {
962 print_event(&event);
963 true
964 }
965 Event::Error { code, message, .. } => {
966 eprintln!("TWS notice {code}: {message}");
967 false
968 }
969 other => {
970 print_event(&other);
971 false
972 }
973 },
974 )
975 .await?;
976 if !done {
977 let _ = client.cancel_mkt_data(req_id).await;
978 }
979 Ok(())
980}
981
982async fn run_market_depth(client: &mut TwsApiClient) -> Result<(), CliError> {
983 let req_id = req_id(9002)?;
984 client
985 .req_mkt_depth(MarketDepthRequest {
986 req_id,
987 contract: contract_from_env()?,
988 num_rows: env_parse("TWS_DEPTH_ROWS", 5i32)?,
989 is_smart_depth: env_bool("TWS_SMART_DEPTH", false)?,
990 market_depth_options: env_tag_values("TWS_DEPTH_OPTION"),
991 })
992 .await?;
993 read_until(
994 client,
995 env_parse("TWS_WAIT_SECS", 20u64)?,
996 |event| match event {
997 Event::MarketDepth {
998 req_id: event_req_id,
999 ..
1000 } if event_req_id == req_id => {
1001 print_event(&event);
1002 false
1003 }
1004 Event::Error {
1005 req_id: event_req_id,
1006 ..
1007 } if event_req_id == req_id => {
1008 print_event(&event);
1009 true
1010 }
1011 Event::Error { .. } => {
1012 print_event(&event);
1013 false
1014 }
1015 other => {
1016 print_event(&other);
1017 false
1018 }
1019 },
1020 )
1021 .await?;
1022 let _ = client
1023 .cancel_mkt_depth(req_id, env_bool("TWS_SMART_DEPTH", false)?)
1024 .await;
1025 Ok(())
1026}
1027
1028async fn run_place_order(client: &mut TwsApiClient) -> Result<(), CliError> {
1029 let mut order = order_from_env()?;
1030 order_kind_from_env(&mut order)?;
1031 let request = PlaceOrderRequest {
1032 order_id: order.order_id,
1033 contract: contract_from_env()?,
1034 order,
1035 extra_fields: env_string("TWS_ORDER_EXTRA_FIELDS", ""),
1036 };
1037 client.place_order(request).await?;
1038 read_until(
1039 client,
1040 env_parse("TWS_WAIT_SECS", 15u64)?,
1041 |event| match event {
1042 Event::OpenOrder { .. } | Event::OrderStatus { .. } | Event::Error { .. } => {
1043 print_event(&event);
1044 false
1045 }
1046 other => {
1047 print_event(&other);
1048 false
1049 }
1050 },
1051 )
1052 .await?;
1053 Ok(())
1054}
1055
1056async fn run_cancel_order(client: &mut TwsApiClient) -> Result<(), CliError> {
1057 client
1058 .cancel_order(
1059 req_id(5001)?,
1060 truefix_twsapi_client::types::OrderCancel::default(),
1061 )
1062 .await?;
1063 read_until(client, 10, |event| match event {
1064 Event::OrderStatus { .. } | Event::Error { .. } => {
1065 print_event(&event);
1066 false
1067 }
1068 other => {
1069 print_event(&other);
1070 false
1071 }
1072 })
1073 .await?;
1074 Ok(())
1075}
1076
1077async fn run_open_orders(client: &mut TwsApiClient) -> Result<(), CliError> {
1078 client.req_open_orders().await?;
1079 read_until(client, 20, |event| match event {
1080 Event::OpenOrder { .. } | Event::OpenOrderEnd => {
1081 print_event(&event);
1082 matches!(event, Event::OpenOrderEnd)
1083 }
1084 Event::Error { .. } => {
1085 print_event(&event);
1086 false
1087 }
1088 other => {
1089 print_event(&other);
1090 false
1091 }
1092 })
1093 .await?;
1094 Ok(())
1095}
1096
1097async fn run_auto_open_orders(client: &mut TwsApiClient) -> Result<(), CliError> {
1098 let auto_bind = env_bool("TWS_AUTO_BIND", true)?;
1099 client.req_auto_open_orders(auto_bind).await?;
1100 println!("auto_open_orders auto_bind={auto_bind}");
1101 Ok(())
1102}
1103
1104async fn run_all_open_orders(client: &mut TwsApiClient) -> Result<(), CliError> {
1105 client.req_all_open_orders().await?;
1106 read_until(client, 20, |event| match event {
1107 Event::OpenOrder { .. } | Event::OpenOrderEnd => {
1108 print_event(&event);
1109 matches!(event, Event::OpenOrderEnd)
1110 }
1111 Event::Error { .. } => {
1112 print_event(&event);
1113 false
1114 }
1115 other => {
1116 print_event(&other);
1117 false
1118 }
1119 })
1120 .await?;
1121 Ok(())
1122}
1123
1124async fn run_global_cancel(client: &mut TwsApiClient) -> Result<(), CliError> {
1125 client
1126 .req_global_cancel(truefix_twsapi_client::types::OrderCancel::default())
1127 .await?;
1128 println!("global_cancel sent");
1129 Ok(())
1130}
1131
1132async fn run_completed_orders(client: &mut TwsApiClient) -> Result<(), CliError> {
1133 client
1134 .req_completed_orders(env_bool("TWS_COMPLETED_API_ONLY", false)?)
1135 .await?;
1136 read_until(client, 20, |event| match event {
1137 Event::CompletedOrder { .. } | Event::CompletedOrdersEnd => {
1138 print_event(&event);
1139 matches!(event, Event::CompletedOrdersEnd)
1140 }
1141 Event::Error { .. } => {
1142 print_event(&event);
1143 false
1144 }
1145 other => {
1146 print_event(&other);
1147 false
1148 }
1149 })
1150 .await?;
1151 Ok(())
1152}
1153
1154async fn run_account_summary(client: &mut TwsApiClient) -> Result<(), CliError> {
1155 let req_id = req_id(9101)?;
1156 client
1157 .req_account_summary(
1158 req_id,
1159 &env_string("TWS_SUMMARY_GROUP", "All"),
1160 &env_string(
1161 "TWS_SUMMARY_TAGS",
1162 "NetLiquidation,TotalCashValue,BuyingPower,AvailableFunds",
1163 ),
1164 )
1165 .await?;
1166 read_until(client, 20, |event| match event {
1167 Event::AccountSummary {
1168 req_id: event_req_id,
1169 ..
1170 } if event_req_id == req_id => {
1171 print_event(&event);
1172 false
1173 }
1174 Event::AccountSummaryEnd {
1175 req_id: event_req_id,
1176 } if event_req_id == req_id => {
1177 print_event(&event);
1178 true
1179 }
1180 Event::Error { .. } => {
1181 print_event(&event);
1182 false
1183 }
1184 other => {
1185 print_event(&other);
1186 false
1187 }
1188 })
1189 .await?;
1190 let _ = client.cancel_account_summary(req_id).await;
1191 Ok(())
1192}
1193
1194async fn run_positions(client: &mut TwsApiClient) -> Result<(), CliError> {
1195 client.req_positions().await?;
1196 read_until(client, 20, |event| match event {
1197 Event::Position { .. } | Event::PositionEnd => {
1198 print_event(&event);
1199 matches!(event, Event::PositionEnd)
1200 }
1201 Event::Error { .. } => {
1202 print_event(&event);
1203 false
1204 }
1205 other => {
1206 print_event(&other);
1207 false
1208 }
1209 })
1210 .await?;
1211 let _ = client.cancel_positions().await;
1212 Ok(())
1213}
1214
1215async fn run_positions_multi(client: &mut TwsApiClient) -> Result<(), CliError> {
1216 let req_id = req_id(9102)?;
1217 client
1218 .req_positions_multi(
1219 req_id,
1220 &env_string("TWS_ACCOUNT", ""),
1221 &env_string("TWS_MODEL_CODE", ""),
1222 )
1223 .await?;
1224 read_until(client, 20, |event| match event {
1225 Event::PositionMulti {
1226 req_id: event_req_id,
1227 ..
1228 } if event_req_id == req_id => {
1229 print_event(&event);
1230 false
1231 }
1232 Event::PositionMultiEnd {
1233 req_id: event_req_id,
1234 } if event_req_id == req_id => {
1235 print_event(&event);
1236 true
1237 }
1238 Event::Error { .. } => {
1239 print_event(&event);
1240 false
1241 }
1242 other => {
1243 print_event(&other);
1244 false
1245 }
1246 })
1247 .await?;
1248 let _ = client.cancel_positions_multi(req_id).await;
1249 Ok(())
1250}
1251
1252async fn run_account_updates_multi(client: &mut TwsApiClient) -> Result<(), CliError> {
1253 let req_id = req_id(9103)?;
1254 client
1255 .req_account_updates_multi(
1256 req_id,
1257 &env_string("TWS_ACCOUNT", ""),
1258 &env_string("TWS_MODEL_CODE", ""),
1259 env_bool("TWS_LEDGER_AND_NLV", false)?,
1260 )
1261 .await?;
1262 let done = read_until(client, 20, |event| match event {
1263 Event::AccountUpdateMulti {
1264 req_id: event_req_id,
1265 ..
1266 } if event_req_id == req_id => {
1267 print_event(&event);
1268 false
1269 }
1270 Event::AccountUpdateMultiEnd {
1271 req_id: event_req_id,
1272 } if event_req_id == req_id => {
1273 print_event(&event);
1274 true
1275 }
1276 Event::Error { .. } => {
1277 print_event(&event);
1278 false
1279 }
1280 other => {
1281 print_event(&other);
1282 false
1283 }
1284 })
1285 .await?;
1286 if !done {
1287 let _ = client.cancel_account_updates_multi(req_id).await;
1288 }
1289 Ok(())
1290}
1291
1292async fn run_historical_data(client: &mut TwsApiClient) -> Result<(), CliError> {
1293 let req_id = req_id(9201)?;
1294 client
1295 .req_historical_data(HistoricalDataRequest {
1296 req_id,
1297 contract: contract_from_env()?,
1298 end_date_time: env_string("TWS_END_DATE_TIME", ""),
1299 duration_str: env_string("TWS_DURATION_STR", "1 D"),
1300 bar_size_setting: env_string("TWS_BAR_SIZE_SETTING", "1 day"),
1301 what_to_show: env_string("TWS_WHAT_TO_SHOW", "TRADES"),
1302 use_rth: env_parse("TWS_USE_RTH", 1i32)?,
1303 format_date: env_parse("TWS_FORMAT_DATE", 1i32)?,
1304 keep_up_to_date: env_bool("TWS_KEEP_UP_TO_DATE", false)?,
1305 chart_options: env_tag_values("TWS_HIST_OPTION"),
1306 })
1307 .await?;
1308 let done = read_until(
1309 client,
1310 env_parse("TWS_WAIT_SECS", 30u64)?,
1311 |event| match event {
1312 Event::HistoricalData {
1313 req_id: event_req_id,
1314 ..
1315 }
1316 | Event::HistoricalDataUpdate {
1317 req_id: event_req_id,
1318 ..
1319 }
1320 | Event::HistoricalDataEnd {
1321 req_id: event_req_id,
1322 ..
1323 } if event_req_id == req_id => {
1324 print_event(&event);
1325 matches!(event, Event::HistoricalDataEnd { .. })
1326 }
1327 Event::Error { .. } => {
1328 print_event(&event);
1329 false
1330 }
1331 other => {
1332 print_event(&other);
1333 false
1334 }
1335 },
1336 )
1337 .await?;
1338 if !done {
1339 let _ = client.cancel_historical_data(req_id).await;
1340 }
1341 Ok(())
1342}
1343
1344async fn run_historical_ticks(client: &mut TwsApiClient) -> Result<(), CliError> {
1345 let req_id = req_id(9202)?;
1346 client
1347 .req_historical_ticks(HistoricalTicksRequest {
1348 req_id,
1349 contract: contract_from_env()?,
1350 start_date_time: env_string("TWS_START_DATE_TIME", ""),
1351 end_date_time: env_string("TWS_END_DATE_TIME", ""),
1352 number_of_ticks: env_parse("TWS_NUMBER_OF_TICKS", 1000i32)?,
1353 what_to_show: env_string("TWS_WHAT_TO_SHOW", "TRADES"),
1354 use_rth: env_bool("TWS_USE_RTH", true)?,
1355 ignore_size: env_bool("TWS_IGNORE_SIZE", false)?,
1356 misc_options: env_tag_values("TWS_HIST_TICKS_OPTION"),
1357 })
1358 .await?;
1359 let done = read_until(
1360 client,
1361 env_parse("TWS_WAIT_SECS", 30u64)?,
1362 |event| match event {
1363 Event::HistoricalTicks {
1364 req_id: event_req_id,
1365 done,
1366 ..
1367 }
1368 | Event::HistoricalTicksBidAsk {
1369 req_id: event_req_id,
1370 done,
1371 ..
1372 }
1373 | Event::HistoricalTicksLast {
1374 req_id: event_req_id,
1375 done,
1376 ..
1377 } if event_req_id == req_id => {
1378 print_event(&event);
1379 done
1380 }
1381 Event::Error { .. } => {
1382 print_event(&event);
1383 false
1384 }
1385 other => {
1386 print_event(&other);
1387 false
1388 }
1389 },
1390 )
1391 .await?;
1392 if !done {
1393 let _ = client.cancel_historical_ticks(req_id).await;
1394 }
1395 Ok(())
1396}
1397
1398async fn run_head_timestamp(client: &mut TwsApiClient) -> Result<(), CliError> {
1399 let req_id = req_id(9301)?;
1400 client
1401 .req_head_timestamp(HeadTimestampRequest {
1402 req_id,
1403 contract: contract_from_env()?,
1404 use_rth: env_bool("TWS_USE_RTH", true)?,
1405 what_to_show: env_string("TWS_WHAT_TO_SHOW", "TRADES"),
1406 format_date: env_parse("TWS_FORMAT_DATE", 1i32)?,
1407 })
1408 .await?;
1409 read_until(client, 20, |event| match event {
1410 Event::HeadTimestamp {
1411 req_id: event_req_id,
1412 ..
1413 } if event_req_id == req_id => {
1414 print_event(&event);
1415 true
1416 }
1417 Event::Error { .. } => {
1418 print_event(&event);
1419 false
1420 }
1421 other => {
1422 print_event(&other);
1423 false
1424 }
1425 })
1426 .await?;
1427 let _ = client.cancel_head_timestamp(req_id).await;
1428 Ok(())
1429}
1430
1431async fn run_histogram_data(client: &mut TwsApiClient) -> Result<(), CliError> {
1432 let req_id = req_id(9302)?;
1433 client
1434 .req_histogram_data(HistogramDataRequest {
1435 req_id,
1436 contract: contract_from_env()?,
1437 use_rth: env_bool("TWS_USE_RTH", true)?,
1438 time_period: env_string("TWS_TIME_PERIOD", "1 D"),
1439 })
1440 .await?;
1441 read_until(client, 20, |event| match event {
1442 Event::HistogramData {
1443 req_id: event_req_id,
1444 ..
1445 } if event_req_id == req_id => {
1446 print_event(&event);
1447 true
1448 }
1449 Event::Error { .. } => {
1450 print_event(&event);
1451 false
1452 }
1453 other => {
1454 print_event(&other);
1455 false
1456 }
1457 })
1458 .await?;
1459 let _ = client.cancel_histogram_data(req_id).await;
1460 Ok(())
1461}
1462
1463async fn run_contract_details(client: &mut TwsApiClient) -> Result<(), CliError> {
1464 let req_id = req_id(9401)?;
1465 client
1466 .req_contract_details(ContractDetailsRequest {
1467 req_id,
1468 contract: contract_from_env()?,
1469 })
1470 .await?;
1471 read_until(client, 20, |event| match event {
1472 Event::ContractDetails {
1473 req_id: event_req_id,
1474 ..
1475 }
1476 | Event::ContractDetailsEnd {
1477 req_id: event_req_id,
1478 } if event_req_id == req_id => {
1479 print_event(&event);
1480 matches!(event, Event::ContractDetailsEnd { .. })
1481 }
1482 Event::Error { .. } => {
1483 print_event(&event);
1484 false
1485 }
1486 other => {
1487 print_event(&other);
1488 false
1489 }
1490 })
1491 .await?;
1492 let _ = client.cancel_contract_data(req_id).await;
1493 Ok(())
1494}
1495
1496async fn run_bond_contract_details(client: &mut TwsApiClient) -> Result<(), CliError> {
1497 let req_id = req_id(9401)?;
1498 let mut contract = contract_from_env()?;
1499 contract.sec_type = "BOND".to_owned();
1500 client
1501 .req_contract_details(ContractDetailsRequest { req_id, contract })
1502 .await?;
1503 read_until(client, 20, |event| match event {
1504 Event::BondContractDetails {
1505 req_id: event_req_id,
1506 ..
1507 }
1508 | Event::ContractDetailsEnd {
1509 req_id: event_req_id,
1510 } if event_req_id == req_id => {
1511 print_event(&event);
1512 matches!(event, Event::ContractDetailsEnd { .. })
1513 }
1514 Event::Error { .. } => {
1515 print_event(&event);
1516 false
1517 }
1518 other => {
1519 print_event(&other);
1520 false
1521 }
1522 })
1523 .await?;
1524 let _ = client.cancel_contract_data(req_id).await;
1525 Ok(())
1526}
1527
1528async fn run_smart_components(client: &mut TwsApiClient) -> Result<(), CliError> {
1529 let req_id = req_id(9402)?;
1530 client
1531 .req_smart_components(req_id, &env_string("TWS_BBO_EXCHANGE", "a6"))
1532 .await?;
1533 read_until(client, 10, |event| match event {
1534 Event::SmartComponents {
1535 req_id: event_req_id,
1536 ..
1537 } if event_req_id == req_id => {
1538 print_event(&event);
1539 true
1540 }
1541 Event::Error { .. } => {
1542 print_event(&event);
1543 false
1544 }
1545 other => {
1546 print_event(&other);
1547 false
1548 }
1549 })
1550 .await?;
1551 Ok(())
1552}
1553
1554async fn run_market_rule(client: &mut TwsApiClient) -> Result<(), CliError> {
1555 let market_rule_id = env_parse("TWS_MARKET_RULE_ID", 26i32)?;
1556 client.req_market_rule(market_rule_id).await?;
1557 read_until(client, 10, |event| match event {
1558 Event::MarketRule {
1559 market_rule_id: event_market_rule_id,
1560 ..
1561 } if event_market_rule_id == market_rule_id => {
1562 print_event(&event);
1563 true
1564 }
1565 Event::Error { .. } => {
1566 print_event(&event);
1567 false
1568 }
1569 other => {
1570 print_event(&other);
1571 false
1572 }
1573 })
1574 .await?;
1575 Ok(())
1576}
1577
1578async fn run_calculate_implied_volatility(client: &mut TwsApiClient) -> Result<(), CliError> {
1579 let req_id = req_id(9502)?;
1580 client
1581 .calculate_implied_volatility(CalculateImpliedVolatilityRequest {
1582 req_id,
1583 contract: contract_from_env()?,
1584 option_price: env_parse("TWS_OPTION_PRICE", 1.0f64)?,
1585 under_price: env_parse("TWS_UNDER_PRICE", 1.0f64)?,
1586 options: env_tag_values("TWS_IV_OPTION"),
1587 })
1588 .await?;
1589 let done = read_until(client, 10, |event| match event {
1590 Event::TickOptionComputation {
1591 req_id: event_req_id,
1592 ..
1593 } if event_req_id == req_id => {
1594 print_event(&event);
1595 true
1596 }
1597 Event::Error { .. } => {
1598 print_event(&event);
1599 false
1600 }
1601 other => {
1602 print_event(&other);
1603 false
1604 }
1605 })
1606 .await?;
1607 if !done {
1608 let _ = client.cancel_calculate_implied_volatility(req_id).await;
1609 }
1610 Ok(())
1611}
1612
1613async fn run_calculate_option_price(client: &mut TwsApiClient) -> Result<(), CliError> {
1614 let req_id = req_id(9503)?;
1615 client
1616 .calculate_option_price(CalculateOptionPriceRequest {
1617 req_id,
1618 contract: contract_from_env()?,
1619 volatility: env_parse("TWS_VOLATILITY", 0.2f64)?,
1620 under_price: env_parse("TWS_UNDER_PRICE", 1.0f64)?,
1621 options: env_tag_values("TWS_OPT_PRICE_OPTION"),
1622 })
1623 .await?;
1624 let done = read_until(client, 10, |event| match event {
1625 Event::TickOptionComputation {
1626 req_id: event_req_id,
1627 ..
1628 } if event_req_id == req_id => {
1629 print_event(&event);
1630 true
1631 }
1632 Event::Error { .. } => {
1633 print_event(&event);
1634 false
1635 }
1636 other => {
1637 print_event(&other);
1638 false
1639 }
1640 })
1641 .await?;
1642 if !done {
1643 let _ = client.cancel_calculate_option_price(req_id).await;
1644 }
1645 Ok(())
1646}
1647
1648async fn run_exercise_options(client: &mut TwsApiClient) -> Result<(), CliError> {
1649 let order_id = req_id(9602)?;
1650 client
1651 .exercise_options(ExerciseOptionsRequest {
1652 order_id,
1653 contract: contract_from_env()?,
1654 exercise_action: env_parse("TWS_EXERCISE_ACTION", 1i32)?,
1655 exercise_quantity: env_parse("TWS_EXERCISE_QTY", 1i32)?,
1656 account: env_string("TWS_ACCOUNT", ""),
1657 override_system_action: env_bool("TWS_EXERCISE_OVERRIDE", false)?,
1658 manual_order_time: env_string("TWS_MANUAL_ORDER_TIME", ""),
1659 customer_account: env_string("TWS_CUSTOMER_ACCOUNT", ""),
1660 professional_customer: env_bool("TWS_PROFESSIONAL_CUSTOMER", false)?,
1661 })
1662 .await?;
1663 read_until(client, 10, |event| match event {
1664 Event::OpenOrder { .. } | Event::OrderStatus { .. } | Event::Error { .. } => {
1665 print_event(&event);
1666 false
1667 }
1668 other => {
1669 print_event(&other);
1670 false
1671 }
1672 })
1673 .await?;
1674 Ok(())
1675}
1676
1677async fn run_executions(client: &mut TwsApiClient) -> Result<(), CliError> {
1678 let req_id = req_id(9501)?;
1679 client
1680 .req_executions(ExecutionRequest {
1681 req_id,
1682 filter: ExecutionFilter {
1683 client_id: env_parse("TWS_FILTER_CLIENT_ID", 0i32)?,
1684 acct_code: env_string("TWS_ACCOUNT", ""),
1685 time: env_string("TWS_EXEC_TIME", ""),
1686 symbol: env_string("TWS_SYMBOL", ""),
1687 sec_type: env_string("TWS_SEC_TYPE", ""),
1688 exchange: env_string("TWS_EXCHANGE", ""),
1689 side: env_string("TWS_EXEC_SIDE", ""),
1690 ..ExecutionFilter::default()
1691 },
1692 })
1693 .await?;
1694 read_until(client, 20, |event| match event {
1695 Event::ExecutionDetails {
1696 req_id: event_req_id,
1697 ..
1698 }
1699 | Event::ExecutionDetailsEnd {
1700 req_id: event_req_id,
1701 } if event_req_id == req_id => {
1702 print_event(&event);
1703 matches!(event, Event::ExecutionDetailsEnd { .. })
1704 }
1705 Event::Error { .. } => {
1706 print_event(&event);
1707 false
1708 }
1709 other => {
1710 print_event(&other);
1711 false
1712 }
1713 })
1714 .await?;
1715 Ok(())
1716}
1717
1718async fn run_news_bulletins(client: &mut TwsApiClient) -> Result<(), CliError> {
1719 let all_messages = env_bool("TWS_NEWS_ALL_MESSAGES", true)?;
1720 client.req_news_bulletins(all_messages).await?;
1721 let done = read_until(client, 15, |event| match event {
1722 Event::NewsBulletin { .. } => {
1723 print_event(&event);
1724 false
1725 }
1726 Event::Error { .. } => {
1727 print_event(&event);
1728 false
1729 }
1730 other => {
1731 print_event(&other);
1732 false
1733 }
1734 })
1735 .await?;
1736 if !done {
1737 let _ = client.cancel_news_bulletins().await;
1738 }
1739 Ok(())
1740}
1741
1742async fn run_news_article(client: &mut TwsApiClient) -> Result<(), CliError> {
1743 let req_id = req_id(9702)?;
1744 client
1745 .req_news_article(
1746 req_id,
1747 &env_string("TWS_NEWS_PROVIDER_CODE", "BZ"),
1748 &env_string("TWS_NEWS_ARTICLE_ID", ""),
1749 &env_tag_values("TWS_NEWS_OPTION"),
1750 )
1751 .await?;
1752 read_until(client, 10, |event| match event {
1753 Event::NewsArticle {
1754 req_id: event_req_id,
1755 ..
1756 } if event_req_id == req_id => {
1757 print_event(&event);
1758 true
1759 }
1760 Event::Error { .. } => {
1761 print_event(&event);
1762 false
1763 }
1764 other => {
1765 print_event(&other);
1766 false
1767 }
1768 })
1769 .await?;
1770 Ok(())
1771}
1772
1773async fn run_historical_news(client: &mut TwsApiClient) -> Result<(), CliError> {
1774 let req_id = req_id(9703)?;
1775 client
1776 .req_historical_news(HistoricalNewsRequest {
1777 req_id,
1778 con_id: env_parse("TWS_CONID", 0i32)?,
1779 provider_codes: env_string("TWS_NEWS_PROVIDER_CODES", ""),
1780 start_date_time: env_string("TWS_START_DATE_TIME", ""),
1781 end_date_time: env_string("TWS_END_DATE_TIME", ""),
1782 total_results: env_parse("TWS_TOTAL_RESULTS", 10i32)?,
1783 options: env_tag_values("TWS_HNEWS_OPTION"),
1784 })
1785 .await?;
1786 read_until(client, 15, |event| match event {
1787 Event::HistoricalNews {
1788 req_id: event_req_id,
1789 ..
1790 } if event_req_id == req_id => {
1791 print_event(&event);
1792 false
1793 }
1794 Event::HistoricalNewsEnd {
1795 req_id: event_req_id,
1796 ..
1797 } if event_req_id == req_id => {
1798 print_event(&event);
1799 true
1800 }
1801 Event::Error { .. } => {
1802 print_event(&event);
1803 false
1804 }
1805 other => {
1806 print_event(&other);
1807 false
1808 }
1809 })
1810 .await?;
1811 Ok(())
1812}
1813
1814async fn run_scanner(client: &mut TwsApiClient) -> Result<(), CliError> {
1815 let req_id = req_id(9601)?;
1816 client
1817 .req_scanner_subscription(ScannerSubscriptionRequest {
1818 req_id,
1819 subscription: ScannerSubscription {
1820 number_of_rows: env_parse("TWS_SCAN_ROWS", 10i32)?,
1821 instrument: env_string("TWS_SCAN_INSTRUMENT", "STK"),
1822 location_code: env_string("TWS_SCAN_LOCATION", "STK.US.MAJOR"),
1823 scan_code: env_string("TWS_SCAN_CODE", "TOP_PERC_GAIN"),
1824 above_price: env_parse("TWS_SCAN_ABOVE_PRICE", 0.0f64)?,
1825 below_price: env_parse("TWS_SCAN_BELOW_PRICE", 0.0f64)?,
1826 above_volume: env_parse("TWS_SCAN_ABOVE_VOLUME", 0i32)?,
1827 market_cap_above: env_parse("TWS_SCAN_MARKET_CAP_ABOVE", 0.0f64)?,
1828 market_cap_below: env_parse("TWS_SCAN_MARKET_CAP_BELOW", 0.0f64)?,
1829 moody_rating_above: env_string("TWS_SCAN_MOODY_ABOVE", ""),
1830 moody_rating_below: env_string("TWS_SCAN_MOODY_BELOW", ""),
1831 sp_rating_above: env_string("TWS_SCAN_SP_ABOVE", ""),
1832 sp_rating_below: env_string("TWS_SCAN_SP_BELOW", ""),
1833 maturity_date_above: env_string("TWS_SCAN_MATURITY_ABOVE", ""),
1834 maturity_date_below: env_string("TWS_SCAN_MATURITY_BELOW", ""),
1835 coupon_rate_above: env_parse("TWS_SCAN_COUPON_ABOVE", 0.0f64)?,
1836 coupon_rate_below: env_parse("TWS_SCAN_COUPON_BELOW", 0.0f64)?,
1837 exclude_convertible: env_bool("TWS_SCAN_EXCLUDE_CONVERTIBLE", false)?,
1838 average_option_volume_above: env_parse("TWS_SCAN_AVG_OPTION_VOLUME_ABOVE", 0i32)?,
1839 scanner_setting_pairs: env_string("TWS_SCAN_SETTING_PAIRS", ""),
1840 stock_type_filter: env_string("TWS_SCAN_STOCK_TYPE_FILTER", ""),
1841 },
1842 scanner_subscription_options: env_tag_values("TWS_SCAN_OPTION"),
1843 scanner_subscription_filter_options: env_tag_values("TWS_SCAN_FILTER"),
1844 })
1845 .await?;
1846 let done = read_until(
1847 client,
1848 env_parse("TWS_WAIT_SECS", 20u64)?,
1849 |event| match event {
1850 Event::ScannerData {
1851 req_id: event_req_id,
1852 ..
1853 }
1854 | Event::ScannerDataEnd {
1855 req_id: event_req_id,
1856 } if event_req_id == req_id => {
1857 print_event(&event);
1858 matches!(event, Event::ScannerDataEnd { .. })
1859 }
1860 Event::Error { .. } => {
1861 print_event(&event);
1862 false
1863 }
1864 other => {
1865 print_event(&other);
1866 false
1867 }
1868 },
1869 )
1870 .await?;
1871 if !done {
1872 let _ = client.cancel_scanner_subscription(req_id).await;
1873 }
1874 Ok(())
1875}
1876
1877async fn run_scanner_parameters(client: &mut TwsApiClient) -> Result<(), CliError> {
1878 client.req_scanner_parameters().await?;
1879 read_until(client, 10, |event| match event {
1880 Event::ScannerParameters { .. } | Event::Error { .. } => {
1881 print_event(&event);
1882 false
1883 }
1884 other => {
1885 print_event(&other);
1886 false
1887 }
1888 })
1889 .await?;
1890 Ok(())
1891}
1892
1893async fn run_sec_def_opt_params(client: &mut TwsApiClient) -> Result<(), CliError> {
1894 let req_id = req_id(9802)?;
1895 client
1896 .req_sec_def_opt_params(
1897 req_id,
1898 &env_string("TWS_UNDERLYING_SYMBOL", &env_string("TWS_SYMBOL", "AAPL")),
1899 &env_string("TWS_FUT_FOP_EXCHANGE", "SMART"),
1900 &env_string("TWS_UNDERLYING_SEC_TYPE", "STK"),
1901 env_parse("TWS_UNDERLYING_CON_ID", env_parse("TWS_CONID", 0i32)?)?,
1902 )
1903 .await?;
1904 read_until(client, 20, |event| match event {
1905 Event::SecurityDefinitionOptionParameter {
1906 req_id: event_req_id,
1907 ..
1908 }
1909 | Event::SecurityDefinitionOptionParameterEnd {
1910 req_id: event_req_id,
1911 } if event_req_id == req_id => {
1912 print_event(&event);
1913 matches!(event, Event::SecurityDefinitionOptionParameterEnd { .. })
1914 }
1915 Event::Error { .. } => {
1916 print_event(&event);
1917 false
1918 }
1919 other => {
1920 print_event(&other);
1921 false
1922 }
1923 })
1924 .await?;
1925 Ok(())
1926}
1927
1928async fn run_soft_dollar_tiers(client: &mut TwsApiClient) -> Result<(), CliError> {
1929 let req_id = req_id(9803)?;
1930 client.req_soft_dollar_tiers(req_id).await?;
1931 read_until(client, 10, |event| match event {
1932 Event::SoftDollarTiers {
1933 req_id: event_req_id,
1934 ..
1935 } if event_req_id == req_id => {
1936 print_event(&event);
1937 true
1938 }
1939 Event::Error { .. } => {
1940 print_event(&event);
1941 false
1942 }
1943 other => {
1944 print_event(&other);
1945 false
1946 }
1947 })
1948 .await?;
1949 Ok(())
1950}
1951
1952async fn run_family_codes(client: &mut TwsApiClient) -> Result<(), CliError> {
1953 client.req_family_codes().await?;
1954 read_until(client, 10, |event| match event {
1955 Event::FamilyCodes { .. } | Event::Error { .. } => {
1956 print_event(&event);
1957 true
1958 }
1959 other => {
1960 print_event(&other);
1961 false
1962 }
1963 })
1964 .await?;
1965 Ok(())
1966}
1967
1968async fn run_matching_symbols(client: &mut TwsApiClient) -> Result<(), CliError> {
1969 let req_id = req_id(9804)?;
1970 client
1971 .req_matching_symbols(req_id, &env_string("TWS_MATCH_PATTERN", "AAPL"))
1972 .await?;
1973 read_until(client, 10, |event| match event {
1974 Event::SymbolSamples {
1975 req_id: event_req_id,
1976 ..
1977 } if event_req_id == req_id => {
1978 print_event(&event);
1979 true
1980 }
1981 Event::Error { .. } => {
1982 print_event(&event);
1983 false
1984 }
1985 other => {
1986 print_event(&other);
1987 false
1988 }
1989 })
1990 .await?;
1991 Ok(())
1992}
1993
1994async fn run_query_display_groups(client: &mut TwsApiClient) -> Result<(), CliError> {
1995 let req_id = req_id(9805)?;
1996 client.query_display_groups(req_id).await?;
1997 read_until(client, 10, |event| match event {
1998 Event::DisplayGroupList {
1999 req_id: event_req_id,
2000 ..
2001 } if event_req_id == req_id => {
2002 print_event(&event);
2003 true
2004 }
2005 Event::Error { .. } => {
2006 print_event(&event);
2007 false
2008 }
2009 other => {
2010 print_event(&other);
2011 false
2012 }
2013 })
2014 .await?;
2015 Ok(())
2016}
2017
2018async fn run_subscribe_to_group_events(client: &mut TwsApiClient) -> Result<(), CliError> {
2019 let req_id = req_id(9806)?;
2020 let group_id = env_parse("TWS_GROUP_ID", 1i32)?;
2021 client.subscribe_to_group_events(req_id, group_id).await?;
2022 let done = read_until(client, 15, |event| match event {
2023 Event::DisplayGroupList {
2024 req_id: event_req_id,
2025 ..
2026 } if event_req_id == req_id => {
2027 print_event(&event);
2028 false
2029 }
2030 Event::DisplayGroupUpdated {
2031 req_id: event_req_id,
2032 ..
2033 } if event_req_id == req_id => {
2034 print_event(&event);
2035 false
2036 }
2037 Event::Error { .. } => {
2038 print_event(&event);
2039 false
2040 }
2041 other => {
2042 print_event(&other);
2043 false
2044 }
2045 })
2046 .await?;
2047 if !done {
2048 let _ = client.unsubscribe_from_group_events(req_id).await;
2049 }
2050 Ok(())
2051}
2052
2053async fn run_update_display_group(client: &mut TwsApiClient) -> Result<(), CliError> {
2054 let req_id = req_id(9807)?;
2055 client
2056 .update_display_group(req_id, &env_string("TWS_CONTRACT_INFO", ""))
2057 .await?;
2058 read_until(client, 10, |event| match event {
2059 Event::DisplayGroupUpdated {
2060 req_id: event_req_id,
2061 ..
2062 } if event_req_id == req_id => {
2063 print_event(&event);
2064 true
2065 }
2066 Event::Error { .. } => {
2067 print_event(&event);
2068 false
2069 }
2070 other => {
2071 print_event(&other);
2072 false
2073 }
2074 })
2075 .await?;
2076 Ok(())
2077}
2078
2079async fn run_unsubscribe_from_group_events(client: &mut TwsApiClient) -> Result<(), CliError> {
2080 let req_id = req_id(9806)?;
2081 client.unsubscribe_from_group_events(req_id).await?;
2082 println!("unsubscribe_from_group_events req_id={req_id}");
2083 Ok(())
2084}
2085
2086async fn run_request_fa(client: &mut TwsApiClient) -> Result<(), CliError> {
2087 let fa_data_type = env_parse("TWS_FA_DATA_TYPE", 1i32)?;
2088 client.request_fa(fa_data_type).await?;
2089 read_until(client, 10, |event| match event {
2090 Event::ReceiveFa {
2091 fa_data_type: event_type,
2092 ..
2093 } if event_type == fa_data_type => {
2094 print_event(&event);
2095 true
2096 }
2097 Event::Error { .. } => {
2098 print_event(&event);
2099 false
2100 }
2101 other => {
2102 print_event(&other);
2103 false
2104 }
2105 })
2106 .await?;
2107 Ok(())
2108}
2109
2110async fn run_replace_fa(client: &mut TwsApiClient) -> Result<(), CliError> {
2111 let req_id = req_id(9808)?;
2112 let fa_data_type = env_parse("TWS_FA_DATA_TYPE", 1i32)?;
2113 client
2114 .replace_fa(req_id, fa_data_type, &env_string("TWS_FA_XML", ""))
2115 .await?;
2116 read_until(client, 10, |event| match event {
2117 Event::ReplaceFaEnd {
2118 req_id: event_req_id,
2119 ..
2120 } if event_req_id == req_id => {
2121 print_event(&event);
2122 true
2123 }
2124 Event::Error { .. } => {
2125 print_event(&event);
2126 false
2127 }
2128 other => {
2129 print_event(&other);
2130 false
2131 }
2132 })
2133 .await?;
2134 Ok(())
2135}
2136
2137async fn run_wsh_meta_data(client: &mut TwsApiClient) -> Result<(), CliError> {
2138 let req_id = req_id(9809)?;
2139 client.req_wsh_meta_data(req_id).await?;
2140 read_until(client, 10, |event| match event {
2141 Event::WshMetaData {
2142 req_id: event_req_id,
2143 ..
2144 } if event_req_id == req_id => {
2145 print_event(&event);
2146 true
2147 }
2148 Event::Error { .. } => {
2149 print_event(&event);
2150 false
2151 }
2152 other => {
2153 print_event(&other);
2154 false
2155 }
2156 })
2157 .await?;
2158 let _ = client.cancel_wsh_meta_data(req_id).await;
2159 Ok(())
2160}
2161
2162async fn run_wsh_event_data(client: &mut TwsApiClient) -> Result<(), CliError> {
2163 let req_id = req_id(9810)?;
2164 client
2165 .req_wsh_event_data(WshEventDataRequest {
2166 req_id,
2167 con_id: env_parse("TWS_CONID", 0i32)?,
2168 filter: env_string("TWS_WSH_FILTER", ""),
2169 fill_watchlist: env_bool("TWS_WSH_FILL_WATCHLIST", false)?,
2170 fill_portfolio: env_bool("TWS_WSH_FILL_PORTFOLIO", false)?,
2171 fill_competitors: env_bool("TWS_WSH_FILL_COMPETITORS", false)?,
2172 start_date: env_string("TWS_START_DATE", ""),
2173 end_date: env_string("TWS_END_DATE", ""),
2174 total_limit: env_parse("TWS_TOTAL_LIMIT", 100i32)?,
2175 })
2176 .await?;
2177 let done = read_until(client, 10, |event| match event {
2178 Event::WshEventData {
2179 req_id: event_req_id,
2180 ..
2181 } if event_req_id == req_id => {
2182 print_event(&event);
2183 false
2184 }
2185 Event::Error { .. } => {
2186 print_event(&event);
2187 false
2188 }
2189 other => {
2190 print_event(&other);
2191 false
2192 }
2193 })
2194 .await?;
2195 if !done {
2196 let _ = client.cancel_wsh_event_data(req_id).await;
2197 }
2198 Ok(())
2199}
2200
2201async fn run_user_info(client: &mut TwsApiClient) -> Result<(), CliError> {
2202 let req_id = req_id(9811)?;
2203 client.req_user_info(req_id).await?;
2204 read_until(client, 10, |event| match event {
2205 Event::UserInfo {
2206 req_id: event_req_id,
2207 ..
2208 } if event_req_id == req_id => {
2209 print_event(&event);
2210 true
2211 }
2212 Event::Error { .. } => {
2213 print_event(&event);
2214 false
2215 }
2216 other => {
2217 print_event(&other);
2218 false
2219 }
2220 })
2221 .await?;
2222 Ok(())
2223}
2224async fn run_real_time_bars(client: &mut TwsApiClient) -> Result<(), CliError> {
2225 let req_id = req_id(9701)?;
2226 client
2227 .req_real_time_bars(RealTimeBarsRequest {
2228 req_id,
2229 contract: contract_from_env()?,
2230 bar_size: env_parse("TWS_BAR_SIZE", 5i32)?,
2231 what_to_show: env_string("TWS_WHAT_TO_SHOW", "TRADES"),
2232 use_rth: env_bool("TWS_USE_RTH", true)?,
2233 options: env_tag_values("TWS_RTBAR_OPTION"),
2234 })
2235 .await?;
2236 let done = read_until(
2237 client,
2238 env_parse("TWS_WAIT_SECS", 20u64)?,
2239 |event| match event {
2240 Event::RealTimeBar {
2241 req_id: event_req_id,
2242 ..
2243 } if event_req_id == req_id => {
2244 print_event(&event);
2245 false
2246 }
2247 Event::Error { .. } => {
2248 print_event(&event);
2249 false
2250 }
2251 other => {
2252 print_event(&other);
2253 false
2254 }
2255 },
2256 )
2257 .await?;
2258 if !done {
2259 let _ = client.cancel_real_time_bars(req_id).await;
2260 }
2261 Ok(())
2262}
2263
2264async fn run_tick_by_tick(client: &mut TwsApiClient) -> Result<(), CliError> {
2265 let req_id = req_id(9801)?;
2266 client
2267 .req_tick_by_tick_data(TickByTickRequest {
2268 req_id,
2269 contract: contract_from_env()?,
2270 tick_type: env_string("TWS_TICK_TYPE", "Last"),
2271 number_of_ticks: env_parse("TWS_NUMBER_OF_TICKS", 0i32)?,
2272 ignore_size: env_bool("TWS_IGNORE_SIZE", false)?,
2273 })
2274 .await?;
2275 let done = read_until(
2276 client,
2277 env_parse("TWS_WAIT_SECS", 20u64)?,
2278 |event| match event {
2279 Event::TickByTick {
2280 req_id: event_req_id,
2281 ..
2282 } if event_req_id == req_id => {
2283 print_event(&event);
2284 false
2285 }
2286 Event::Error { .. } => {
2287 print_event(&event);
2288 false
2289 }
2290 other => {
2291 print_event(&other);
2292 false
2293 }
2294 },
2295 )
2296 .await?;
2297 if !done {
2298 let _ = client.cancel_tick_by_tick_data(req_id).await;
2299 }
2300 Ok(())
2301}
2302
2303async fn run_pnl(client: &mut TwsApiClient) -> Result<(), CliError> {
2304 let req_id = req_id(9901)?;
2305 client
2306 .req_pnl(
2307 req_id,
2308 &env_string("TWS_ACCOUNT", ""),
2309 &env_string("TWS_MODEL_CODE", ""),
2310 )
2311 .await?;
2312 read_until(client, 10, |event| match event {
2313 Event::Pnl {
2314 req_id: event_req_id,
2315 ..
2316 } if event_req_id == req_id => {
2317 print_event(&event);
2318 false
2319 }
2320 Event::Error { .. } => {
2321 print_event(&event);
2322 false
2323 }
2324 other => {
2325 print_event(&other);
2326 false
2327 }
2328 })
2329 .await?;
2330 let _ = client.cancel_pnl(req_id).await;
2331 Ok(())
2332}
2333
2334async fn run_pnl_single(client: &mut TwsApiClient) -> Result<(), CliError> {
2335 let req_id = req_id(9902)?;
2336 client
2337 .req_pnl_single(
2338 req_id,
2339 &env_string("TWS_ACCOUNT", ""),
2340 &env_string("TWS_MODEL_CODE", ""),
2341 env_parse("TWS_CONID", 0i32)?,
2342 )
2343 .await?;
2344 read_until(client, 10, |event| match event {
2345 Event::PnlSingle {
2346 req_id: event_req_id,
2347 ..
2348 } if event_req_id == req_id => {
2349 print_event(&event);
2350 false
2351 }
2352 Event::Error { .. } => {
2353 print_event(&event);
2354 false
2355 }
2356 other => {
2357 print_event(&other);
2358 false
2359 }
2360 })
2361 .await?;
2362 let _ = client.cancel_pnl_single(req_id).await;
2363 Ok(())
2364}
2365
2366async fn run_managed_accounts(client: &mut TwsApiClient) -> Result<(), CliError> {
2367 client.req_managed_accounts().await?;
2368 read_until(client, 5, |event| match event {
2369 Event::ManagedAccounts { .. } | Event::Error { .. } => {
2370 print_event(&event);
2371 false
2372 }
2373 other => {
2374 print_event(&other);
2375 false
2376 }
2377 })
2378 .await?;
2379 Ok(())
2380}
2381
2382async fn run_disconnect(client: &mut TwsApiClient) -> Result<(), CliError> {
2383 client.disconnect().await?;
2384 println!("disconnected");
2385 Ok(())
2386}
2387
2388async fn run_account_updates(client: &mut TwsApiClient) -> Result<(), CliError> {
2389 client
2390 .req_account_updates(
2391 env_bool("TWS_ACCOUNT_SUBSCRIBE", true)?,
2392 &env_string("TWS_ACCOUNT", ""),
2393 )
2394 .await?;
2395 read_until(client, 10, |event| match event {
2396 Event::AccountValue { .. }
2397 | Event::PortfolioValue { .. }
2398 | Event::AccountUpdateTime { .. }
2399 | Event::AccountDownloadEnd { .. }
2400 | Event::Error { .. } => {
2401 print_event(&event);
2402 false
2403 }
2404 other => {
2405 print_event(&other);
2406 false
2407 }
2408 })
2409 .await?;
2410 let _ = client
2411 .req_account_updates(false, &env_string("TWS_ACCOUNT", ""))
2412 .await;
2413 Ok(())
2414}
2415
2416async fn run_market_depth_exchanges(client: &mut TwsApiClient) -> Result<(), CliError> {
2417 client.req_mkt_depth_exchanges().await?;
2418 read_until(client, 5, |event| match event {
2419 Event::MarketDepthExchanges { .. } | Event::Error { .. } => {
2420 print_event(&event);
2421 false
2422 }
2423 other => {
2424 print_event(&other);
2425 false
2426 }
2427 })
2428 .await?;
2429 Ok(())
2430}
2431
2432async fn run_news_providers(client: &mut TwsApiClient) -> Result<(), CliError> {
2433 client.req_news_providers().await?;
2434 read_until(client, 5, |event| match event {
2435 Event::NewsProviders { .. } | Event::Error { .. } => {
2436 print_event(&event);
2437 false
2438 }
2439 other => {
2440 print_event(&other);
2441 false
2442 }
2443 })
2444 .await?;
2445 Ok(())
2446}