use dotenv::dotenv;
use std::env;
use webull_unofficial::{error::Result, models::*, 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 with fluent API...");
client
.login_with()
.username(&username)
.password(&password)
.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();
let quote = client.get_quotes(&ticker_id_str).await?;
println!("Current AAPL price: ${:.2}", quote.close);
println!("\n=== Fluent API Examples ===");
println!("\n1. Getting 5-minute bars with fluent API:");
let bars = client
.get_bars_with()
.ticker_id(&ticker_id_str)
.interval("5m")
.count(50)
.await?;
println!(" Fetched {} bars", bars.len());
if let Some(latest) = bars.first() {
println!(
" Latest bar - Open: ${:.2}, Close: ${:.2}",
latest.open, latest.close
);
}
println!("\n2. Getting daily bars from 30 days ago:");
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", daily_bars.len());
println!("\n3. Getting latest news:");
let news = client.get_news_with().ticker("AAPL").latest(5).await?;
println!(" Found {} news items:", news.len());
for (i, item) in news.iter().enumerate() {
println!(" {}. {}", i + 1, item.title);
}
if let Some(last_news) = news.last() {
println!("\n4. Getting more news (pagination):");
let more_news = client
.get_news_with()
.ticker("AAPL")
.after(last_news.id)
.count(3)
.await?;
println!(
" Found {} more news items after ID {}",
more_news.len(),
last_news.id
);
}
println!("\n5. Getting options:");
let options = client
.get_options_with()
.ticker("AAPL")
.calls_only()
.near_the_money(quote.close, 5.0) .await?;
println!(" Found {} call options near the money", options.len());
println!("\n6. Building orders with fluent API:");
println!(" Market order (demo - not placing):");
let _market_order_id = client.place_market_order_with()
.ticker_id(ticker_id)
.buy()
.quantity(1.0)
;
println!(" ✓ Market order builder created");
println!(" Limit order (demo - not placing):");
let _limit_order_id = client.place_limit_order_with(quote.close - 10.0)
.ticker_id(ticker_id)
.sell()
.quantity(2.0)
.extended_hours()
.time_in_force(TimeInForce::GoodTillCancel)
;
println!(" ✓ Limit order builder created");
println!(" Stop-limit order (demo - not placing):");
let _stop_limit_order_id = client.place_stop_limit_order_with(quote.close - 5.0, quote.close - 5.5)
.ticker_id(ticker_id)
.sell()
.quantity(1.0)
;
println!(" ✓ Stop-limit order builder created");
println!("\n7. Chaining operations:");
let recent_bars = client
.get_bars_with()
.ticker_id(&ticker_id_str)
.interval("15m")
.count(10)
.await?;
if let (Some(first), Some(last)) = (recent_bars.last(), recent_bars.first()) {
let price_change = ((last.close - first.close) / first.close * 100.0).abs();
println!(" Price change in last 10 bars: {:.2}%", price_change);
if price_change > 1.0 {
println!(" Significant movement detected, fetching news...");
let related_news = client.get_news_with().ticker("AAPL").latest(3).await?;
println!(
" Found {} potentially related news items",
related_news.len()
);
}
}
} else {
println!("Ticker AAPL not found");
}
println!("\n✅ All fluent API examples completed successfully!");
Ok(())
}