use clap::{ArgAction, Parser};
use ibapi::{
Client,
contracts::Contract,
market_data::MarketDataType,
orders::Orders,
prelude::{StreamExt, SubscriptionItemStreamExt},
};
use std::error::Error;
use tokio::time::{self, Duration};
const SYMBOL: &str = "SOXL";
const RUN_DELAY: Duration = Duration::from_secs(1);
const RETRY_DELAY: Duration = Duration::from_secs(10);
#[derive(Parser)]
#[command(
about = concat!(
env!("CARGO_PKG_DESCRIPTION"),
"\n\n",
"More information can be found at: ",
env!("CARGO_PKG_HOMEPAGE"),
),
version,
disable_version_flag = true
)]
struct Cli {
#[arg(short, long, help = "Print version", action = ArgAction::Version)]
_version: Option<bool>,
#[arg(long, default_value = "127.0.0.1:4001")]
address: String,
#[arg(long, default_value_t = 100)]
client_id: i32,
}
#[tokio::main]
async fn main() {
let cli = Cli::parse();
loop {
match Client::connect(&cli.address, cli.client_id).await {
Ok(client) => {
if let Err(error) = run_connection(&client).await {
eprintln!("Error: {error}");
}
}
Err(error) => eprintln!("Connection failed: {error}"),
}
time::sleep(RETRY_DELAY).await;
}
}
async fn run_connection(client: &Client) -> Result<(), Box<dyn Error>> {
tokio::try_join!(run_steps(client), stream_live_data(client))?;
Ok(())
}
async fn run_steps(client: &Client) -> Result<(), Box<dyn Error>> {
loop {
run_step(client).await?;
time::sleep(RUN_DELAY).await;
}
}
async fn stream_live_data(client: &Client) -> Result<(), Box<dyn Error>> {
client
.switch_market_data_type(MarketDataType::Realtime)
.await?;
let contract = Contract::stock(SYMBOL).build();
let mut subscription = client
.market_data(&contract)
.streaming()
.subscribe()
.await?;
println!("Streaming {SYMBOL} market data…");
while let Some(tick) = subscription.next().await {
println!("{SYMBOL} market data: {:?}", tick?);
}
Err(ibapi::Error::UnexpectedEndOfStream.into())
}
async fn run_step(client: &Client) -> Result<(), Box<dyn Error>> {
list_orders(client).await?;
Ok(())
}
async fn list_orders(client: &Client) -> Result<(), Box<dyn Error>> {
println!("Requesting all open orders…");
let subscription = client.all_open_orders().await?;
let mut orders = subscription.filter_data();
let mut order_count: usize = 0;
while let Some(order) = orders.next().await {
match order? {
Orders::OrderData(data) => {
order_count += 1;
println!("- {data:?}");
}
Orders::OrderStatus(status) => println!("{status:?}"),
}
}
if order_count == 0 {
println!("No open orders found.");
} else if order_count == 1 {
println!("Finished listing 1 open order.");
} else {
println!("Finished listing {order_count} open orders.");
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::Cli;
use clap::CommandFactory;
#[test]
fn verify_cli() {
Cli::command().debug_assert();
}
}