#[tokio::test]
#[ignore = "requires network access"]
async fn test_tickers_simple_construction() {
use finance_query::Tickers;
let tickers = Tickers::new(vec!["AAPL", "MSFT", "GOOGL"]).await.unwrap();
let response = tickers.quotes().await.unwrap();
assert!(response.success_count() > 0);
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_tickers_builder_pattern() {
use finance_query::{Region, Tickers};
use std::time::Duration;
let tickers = Tickers::builder(vec!["AAPL", "MSFT"])
.region(Region::UnitedStates)
.timeout(Duration::from_secs(30))
.build()
.await
.unwrap();
let response = tickers.quotes().await.unwrap();
assert!(response.success_count() > 0);
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_tickers_max_concurrency() {
use finance_query::Tickers;
let tickers = Tickers::builder(vec!["AAPL", "MSFT", "GOOGL", "TSLA"])
.max_concurrency(3)
.build()
.await
.unwrap();
let response = tickers.quotes().await.unwrap();
assert!(response.success_count() > 0);
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_tickers_shared_session() {
use finance_query::{Ticker, Tickers};
let aapl = Ticker::new("AAPL").await.unwrap();
let handle = aapl.client_handle();
let tickers = Tickers::builder(["MSFT", "GOOGL"])
.client(handle)
.build()
.await
.unwrap();
let response = tickers.quotes().await.unwrap();
assert!(response.success_count() > 0);
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_batch_quotes_with_logo() {
use finance_query::Tickers;
let tickers = Tickers::builder(vec!["AAPL", "MSFT"])
.logo()
.build()
.await
.unwrap();
let response = tickers.quotes().await.unwrap();
for (symbol, quote) in &response.quotes {
let price = quote
.regular_market_price
.as_ref()
.and_then(|v| v.raw)
.unwrap_or(0.0);
println!("{} Price: ${:.2}", symbol, price);
if let Some(logo) = "e.logo_url {
println!(" Logo: {}", logo);
}
}
for (symbol, error) in &response.errors {
eprintln!("Failed to fetch {}: {}", symbol, error);
}
assert!(response.success_count() > 0);
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_batch_quotes_utility_methods() {
use finance_query::Tickers;
let tickers = Tickers::builder(vec!["AAPL", "GOOGL"])
.logo()
.build()
.await
.unwrap();
let response = tickers.quotes().await.unwrap();
println!("Successful: {}", response.success_count());
println!("Failed: {}", response.error_count());
if !response.all_successful() {
for (symbol, error) in &response.errors {
eprintln!("Failed to fetch {}: {}", symbol, error);
}
}
assert!(response.success_count() > 0);
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_batch_charts() {
use finance_query::{Interval, Tickers, TimeRange};
let tickers = Tickers::new(vec!["AAPL", "MSFT"]).await.unwrap();
let response = tickers
.charts(Interval::OneDay, TimeRange::OneMonth)
.await
.unwrap();
for (symbol, chart) in &response.charts {
println!("{}: {} candles", symbol, chart.candles.len());
if let Some(last) = chart.candles.last() {
println!(" Last Close: ${:.2}", last.close);
}
}
for (symbol, error) in &response.errors {
eprintln!("Failed to fetch chart for {}: {}", symbol, error);
}
assert!(response.success_count() > 0);
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_batch_spark() {
use finance_query::{Interval, Tickers, TimeRange};
let tickers = Tickers::new(vec!["AAPL", "MSFT"]).await.unwrap();
let response = tickers
.spark(Interval::OneDay, TimeRange::FiveDays)
.await
.unwrap();
for (symbol, spark) in &response.sparks {
println!("{}: {} data points", symbol, spark.len());
if let Some(change) = spark.percent_change() {
println!(" Change: {:+.2}%", change);
}
if let Some(min) = spark.min_close() {
println!(" Low: ${:.2}", min);
}
if let Some(max) = spark.max_close() {
println!(" High: ${:.2}", max);
}
}
for (symbol, error) in &response.errors {
eprintln!("Failed to fetch spark for {}: {}", symbol, error);
}
assert!(response.success_count() > 0);
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_batch_dividends() {
use finance_query::{Tickers, TimeRange};
let tickers = Tickers::new(vec!["AAPL", "MSFT"]).await.unwrap();
let response = tickers.dividends(TimeRange::OneYear).await.unwrap();
for (symbol, dividends) in &response.dividends {
println!("{}: {} dividends", symbol, dividends.len());
for div in dividends {
println!(" Timestamp: {}, Amount: ${:.2}", div.timestamp, div.amount);
}
}
for (symbol, error) in &response.errors {
eprintln!("Failed to fetch dividends for {}: {}", symbol, error);
}
assert!(response.success_count() > 0);
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_batch_splits() {
use finance_query::{Tickers, TimeRange};
let tickers = Tickers::new(vec!["NVDA", "TSLA", "AAPL"]).await.unwrap();
let response = tickers.splits(TimeRange::FiveYears).await.unwrap();
for (symbol, splits) in &response.splits {
if !splits.is_empty() {
println!("{}: {} splits", symbol, splits.len());
for split in splits {
println!(" Timestamp: {}, Ratio: {}", split.timestamp, split.ratio);
}
}
}
assert!(response.success_count() > 0);
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_batch_capital_gains() {
use finance_query::{Tickers, TimeRange};
let etfs = Tickers::new(vec!["SPY", "VOO", "VTI"]).await.unwrap();
let response = etfs.capital_gains(TimeRange::TwoYears).await.unwrap();
for (symbol, gains) in &response.capital_gains {
if !gains.is_empty() {
println!("{}: {} capital gains distributions", symbol, gains.len());
for gain in gains {
println!(
" Timestamp: {}, Amount: ${:.2}",
gain.timestamp, gain.amount
);
}
}
}
assert!(response.success_count() > 0);
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_batch_financials() {
use finance_query::{Frequency, StatementType, Tickers};
let tickers = Tickers::new(vec!["AAPL", "MSFT"]).await.unwrap();
let response = tickers
.financials(StatementType::Income, Frequency::Quarterly)
.await
.unwrap();
for (symbol, statement) in &response.financials {
println!("{}: {} metrics", symbol, statement.statement.len());
if let Some(revenue_data) = statement.statement.get("TotalRevenue") {
println!(" Revenue data points: {}", revenue_data.len());
if let Some((date, value)) = revenue_data.iter().next() {
println!(" Latest Revenue ({}): ${}", date, value);
}
}
if let Some(income_data) = statement.statement.get("NetIncome")
&& let Some((date, value)) = income_data.iter().next()
{
println!(" Latest Net Income ({}): ${}", date, value);
}
}
assert!(response.success_count() > 0);
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_batch_news() {
use finance_query::Tickers;
let tickers = Tickers::new(vec!["AAPL", "MSFT"]).await.unwrap();
let response = tickers.news().await.unwrap();
for (symbol, articles) in &response.news {
println!("{}: {} news articles", symbol, articles.len());
for article in articles.iter().take(3) {
println!(" Title: {}", article.title);
println!(" Source: {}", article.source);
println!(" Link: {}", article.link);
}
}
assert!(response.success_count() > 0);
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_batch_recommendations() {
use finance_query::Tickers;
let tickers = Tickers::new(vec!["AAPL", "MSFT"]).await.unwrap();
let response = tickers.recommendations(5).await.unwrap();
for (symbol, rec) in &response.recommendations {
println!("{}: {} recommendations", symbol, rec.recommendations.len());
for r in &rec.recommendations {
println!(" {} ({})", r.symbol, r.score);
}
}
assert!(response.success_count() > 0);
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_batch_options() {
use finance_query::Tickers;
let tickers = Tickers::new(vec!["AAPL", "MSFT"]).await.unwrap();
let response = tickers.options(None).await.unwrap();
for (symbol, options) in &response.options {
let exp_dates = options.expiration_dates();
println!("{}: {} expirations", symbol, exp_dates.len());
let calls = options.calls();
let puts = options.puts();
println!(" Calls: {} contracts", calls.len());
println!(" Puts: {} contracts", puts.len());
}
let first_exp = response
.options
.values()
.next()
.and_then(|opts| opts.expiration_dates().into_iter().nth(1));
if let Some(specific_date) = first_exp {
let _dated = tickers.options(Some(specific_date)).await.unwrap();
}
assert!(response.success_count() > 0);
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_dynamic_symbol_management() {
use finance_query::Tickers;
let mut tickers = Tickers::new(vec!["AAPL", "MSFT"]).await.unwrap();
println!("Initial symbols: {:?}", tickers.symbols());
tickers.add_symbols(&["GOOGL", "TSLA", "NVDA"]);
println!("After adding: {:?}", tickers.symbols());
tickers.remove_symbols(&["MSFT", "TSLA"]).await;
println!("After removing: {:?}", tickers.symbols());
let response = tickers.quotes().await.unwrap();
assert!(response.success_count() > 0);
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_tickers_caching() {
use finance_query::Tickers;
use std::time::Duration;
let tickers = Tickers::builder(vec!["AAPL", "MSFT"])
.cache(Duration::from_secs(30))
.build()
.await
.unwrap();
let response1 = tickers.quotes().await.unwrap();
let response2 = tickers.quotes().await.unwrap();
tickers.clear_cache().await;
let response3 = tickers.quotes().await.unwrap();
tickers.clear_quote_cache().await; tickers.clear_chart_cache().await;
assert_eq!(response1.success_count(), response2.success_count());
assert_eq!(response2.success_count(), response3.success_count());
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_individual_access() {
use finance_query::{Interval, Tickers, TimeRange};
let tickers = Tickers::new(vec!["AAPL", "MSFT"]).await.unwrap();
let aapl = tickers.quote("AAPL").await.unwrap();
println!(
"AAPL price: {:?}",
aapl.regular_market_price.as_ref().and_then(|v| v.raw)
);
let msft_chart = tickers
.chart("MSFT", Interval::OneDay, TimeRange::OneMonth)
.await
.unwrap();
println!("MSFT candles: {}", msft_chart.candles.len());
assert!(!aapl.symbol.is_empty());
assert!(!msft_chart.candles.is_empty());
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_tickers_best_practices() {
use finance_query::{Interval, Tickers, TimeRange};
let tickers = Tickers::builder(vec!["AAPL", "GOOGL", "INVALID", "MSFT"])
.logo()
.build()
.await
.unwrap();
let quotes_response = tickers.quotes().await.unwrap();
for (symbol, error) in "es_response.errors {
println!("Failed to fetch {}: {}", symbol, error);
}
for (symbol, quote) in "es_response.quotes {
let price = quote
.regular_market_price
.as_ref()
.and_then(|v| v.raw)
.unwrap_or(0.0);
println!("{}: ${:.2}", symbol, price);
}
let charts_response = tickers
.charts(Interval::OneDay, TimeRange::OneMonth)
.await
.unwrap();
assert!(quotes_response.success_count() > 0);
let _ = charts_response;
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_batch_indicators() {
#[cfg(feature = "indicators")]
{
use finance_query::{Interval, Tickers, TimeRange};
let tickers = Tickers::new(vec!["AAPL", "MSFT"]).await.unwrap();
let response = tickers
.indicators(Interval::OneDay, TimeRange::OneMonth)
.await
.unwrap();
for (symbol, indicators) in &response.indicators {
println!("{} Indicators:", symbol);
if let Some(rsi) = indicators.rsi_14 {
println!(" RSI(14): {:.2}", rsi);
}
if let Some(sma) = indicators.sma_20 {
println!(" SMA(20): {:.2}", sma);
}
if let Some(macd) = &indicators.macd
&& let Some(line) = macd.macd
{
println!(" MACD: {:.2}", line);
}
}
assert!(response.success_count() > 0);
}
}