use clap::{ArgAction, Parser};
use ibapi::{
client::blocking::Client,
contracts::{Contract, tick_types::TickType},
market_data::{MarketDataType, realtime::TickTypes},
};
use std::{error::Error, io, time::Duration};
const SYMBOL: &str = "AAPL";
const SNAPSHOT_TIMEOUT: 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,
}
fn main() -> Result<(), Box<dyn Error>> {
let cli = Cli::parse();
let client = Client::connect(&cli.address, cli.client_id)?;
let contract = Contract::stock(SYMBOL).build();
client.switch_market_data_type(MarketDataType::Delayed)?;
let ticks = client
.market_data(&contract)
.snapshot_once(SNAPSHOT_TIMEOUT)?;
let price = latest_price(&ticks).ok_or_else(|| {
io::Error::other(format!(
"no latest trade price for {SYMBOL} arrived within {} seconds",
SNAPSHOT_TIMEOUT.as_secs(),
))
})?;
println!("{SYMBOL}: ${price:.2}");
Ok(())
}
fn latest_price(ticks: &[TickTypes]) -> Option<f64> {
price_for_tick_type(ticks, &TickType::Last)
.or_else(|| price_for_tick_type(ticks, &TickType::DelayedLast))
}
fn price_for_tick_type(ticks: &[TickTypes], wanted: &TickType) -> Option<f64> {
ticks.iter().find_map(|tick| match tick {
TickTypes::Price(price) if &price.tick_type == wanted => Some(price.price),
TickTypes::PriceSize(price) if &price.price_tick_type == wanted => Some(price.price),
_ => None,
})
}
#[cfg(test)]
mod tests {
use super::{Cli, latest_price};
use clap::CommandFactory;
use ibapi::{
contracts::tick_types::TickType,
market_data::realtime::{TickPrice, TickTypes},
};
#[test]
fn verify_cli() {
Cli::command().debug_assert();
}
#[test]
fn extracts_latest_price() {
let ticks = [
TickTypes::Price(TickPrice {
tick_type: TickType::DelayedLast,
price: 198.50,
..TickPrice::default()
}),
TickTypes::Price(TickPrice {
tick_type: TickType::Last,
price: 200.25,
..TickPrice::default()
}),
];
assert_eq!(latest_price(&ticks), Some(200.25_f64));
}
}