stockholm 0.2.2

An algorithmic trading bot.
use clap::{ArgAction, Parser};
use ibapi::{Client, contracts::Contract, market_data::MarketDataType};
use std::error::Error;
use tokio::time::{self, Duration};

// These constants identify the demonstration instrument and bound the market data request.
const SYMBOL: &str = "AAPL";
const RETRY_DELAY: Duration = Duration::from_secs(10);
const SNAPSHOT_TIMEOUT: Duration = Duration::from_secs(5);

// This struct represents the command-line arguments.
#[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>,

    /// Address of the running TWS or IB Gateway API.
    #[arg(long, default_value = "127.0.0.1:4001")]
    address: String,

    /// Client ID to use for the API connection.
    #[arg(long, default_value_t = 100)]
    client_id: i32,
}

// Parse the configuration and retry the application after top-level failures.
#[tokio::main]
async fn main() {
    // Parse the command-line arguments.
    let cli = Cli::parse();

    // Restart the application after a delay whenever a top-level operation fails.
    loop {
        if let Err(error) = run(&cli).await {
            eprintln!("Application failed: {error}");
        }

        time::sleep(RETRY_DELAY).await;
    }
}

// Connect to Interactive Brokers and print a snapshot of the raw AAPL market data.
async fn run(cli: &Cli) -> Result<(), Box<dyn Error>> {
    // Connect to the configured TWS or IB Gateway instance.
    let client = Client::connect(&cli.address, cli.client_id).await?;

    // Configure subsequent requests to use subscribed real-time market data.
    client
        .switch_market_data_type(MarketDataType::Realtime)
        .await?;

    // Prepare the demonstration instrument.
    let contract = Contract::stock(SYMBOL).build();

    // Mark the start of the request before collecting its snapshot.
    println!("Requesting {SYMBOL} market data snapshot…\n");

    // Collect a complete bounded snapshot and propagate failures to the retry loop.
    let ticks = client
        .market_data(&contract)
        .snapshot_once(SNAPSHOT_TIMEOUT)
        .await?;

    // Print every tick without interpreting or filtering the market data.
    for tick in ticks {
        println!("{tick:?}");
    }
    println!();

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::Cli;
    use clap::CommandFactory;

    #[test]
    fn verify_cli() {
        Cli::command().debug_assert();
    }
}