Skip to main content

twsapi/
main.rs

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}