use dotenv::dotenv;
use std::env;
use webull_unofficial::{
error::{Result, WebullError},
models::*,
BarsRequestBuilder, NewsRequestBuilder, PlaceOrderRequest, WebullClient,
};
#[tokio::main]
async fn main() -> Result<()> {
dotenv().ok();
env_logger::init();
let username =
env::var("WEBULL_USERNAME").expect("Please set WEBULL_USERNAME environment variable");
let password =
env::var("WEBULL_PASSWORD").expect("Please set WEBULL_PASSWORD environment variable");
let mut client = WebullClient::new_paper(Some(6))?;
println!("Logging in...");
client
.login(&username, &password, None, None, None, None)
.await?;
println!("\nFinding ticker AAPL...");
let tickers = client.find_ticker("AAPL").await?;
if let Some(ticker) = tickers.first() {
let ticker_id = ticker.ticker_id;
let ticker_id_str = ticker_id.to_string();
println!("\n=== Order Builder Examples ===");
let quote = client.get_quotes(&ticker_id_str).await?;
println!("Current AAPL price: ${:.2}", quote.close);
let limit_order = PlaceOrderRequest::limit(quote.close - 1.0)
.ticker_id(ticker_id)
.buy()
.quantity(10.0)
.time_in_force(TimeInForce::GoodTillCancel)
.build()
.map_err(|e| WebullError::InvalidRequest(e))?;
println!("Built limit order: {:?}", limit_order);
println!("\n=== Bars Builder Examples ===");
let _bars_request = BarsRequestBuilder::new()
.ticker_id(&ticker_id_str)
.interval("5m")
.count(50)
.build()
.map_err(|e| WebullError::InvalidRequest(e))?;
let bars = client
.get_bars_with()
.ticker_id(&ticker_id_str)
.interval("5m")
.count(50)
.await?;
println!("Fetched {} 5-minute bars", bars.len());
if let Some(latest_bar) = bars.first() {
println!(
"Latest bar - Open: ${:.2}, Close: ${:.2}, Volume: {:.0}",
latest_bar.open, latest_bar.close, latest_bar.volume
);
}
let daily_bars = client
.get_bars_with()
.ticker_id(&ticker_id_str)
.interval("1d")
.count(30)
.from_date(chrono::Utc::now() - chrono::Duration::days(30))
.await?;
println!("Fetched {} daily bars from last 30 days", daily_bars.len());
let custom_bars_request = BarsRequestBuilder::new()
.ticker_id(&ticker_id_str)
.interval("1h")
.count(100)
.timestamp(1609459200) .build()
.map_err(|e| WebullError::InvalidRequest(e))?;
println!("Custom bars request built: {:?}", custom_bars_request);
println!("\n=== News Builder Examples ===");
let latest_news = client.get_news_with().ticker("AAPL").latest(10).await?;
println!("Fetched {} latest news items", latest_news.len());
for (i, news) in latest_news.iter().take(3).enumerate() {
println!("{}. {}", i + 1, news.title);
if let Some(source) = &news.source_name {
println!(" Source: {}", source);
}
}
if let Some(last_news) = latest_news.last() {
let more_news = client
.get_news_with()
.ticker("AAPL")
.after(last_news.id)
.count(5)
.await?;
println!(
"\nFetched {} more news items after ID {}",
more_news.len(),
last_news.id
);
}
let custom_news_request = NewsRequestBuilder::new()
.ticker("TSLA")
.last_id(1000000)
.count(25)
.build()
.map_err(|e| WebullError::InvalidRequest(e))?;
println!("Custom news request built: {:?}", custom_news_request);
println!("\n=== Combining Builders ===");
let recent_bars = client
.get_bars_with()
.ticker_id(&ticker_id_str)
.interval("15m")
.count(10)
.await?;
if let Some(last_bar) = recent_bars.first() {
let analysis_order = PlaceOrderRequest::limit(last_bar.low - 0.50)
.ticker_id(ticker_id)
.buy()
.quantity(5.0)
.time_in_force(TimeInForce::Day)
.build()
.map_err(|e| WebullError::InvalidRequest(e))?;
println!("Created order based on recent bars analysis:");
println!(" Last bar low: ${:.2}", last_bar.low);
println!(
" Order limit price: ${:.2}",
analysis_order.limit_price.unwrap_or(0.0)
);
}
} else {
println!("Ticker AAPL not found");
}
Ok(())
}