#![cfg(feature = "dataframe")]
use finance_query::{Interval, TimeRange};
#[tokio::test]
#[ignore = "requires network access"]
async fn test_chart_to_dataframe() {
use finance_query::Ticker;
let ticker = Ticker::new("AAPL").await.unwrap();
let chart = ticker
.chart(Interval::OneDay, TimeRange::OneMonth)
.await
.unwrap();
let df = chart.to_dataframe().unwrap();
println!("{}", df);
assert!(df.column("timestamp").is_ok());
assert!(df.column("open").is_ok());
assert!(df.column("high").is_ok());
assert!(df.column("low").is_ok());
assert!(df.column("close").is_ok());
assert!(df.column("volume").is_ok());
assert!(df.height() > 0);
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_quote_to_dataframe() {
use finance_query::Ticker;
let ticker = Ticker::new("NVDA").await.unwrap();
let quote = ticker.quote().await.unwrap();
let df = quote.to_dataframe().unwrap();
println!("{}", df);
assert_eq!(df.height(), 1);
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_corporate_events_to_dataframe() {
use finance_query::{CapitalGain, Dividend, Split, Ticker, TimeRange};
let ticker = Ticker::new("AAPL").await.unwrap();
let dividends = ticker.dividends(TimeRange::OneYear).await.unwrap();
let div_df = Dividend::vec_to_dataframe(÷nds).unwrap();
assert!(div_df.column("timestamp").is_ok());
assert!(div_df.column("amount").is_ok());
let splits = ticker.splits(TimeRange::Max).await.unwrap();
let split_df = Split::vec_to_dataframe(&splits).unwrap();
assert!(split_df.column("timestamp").is_ok());
assert!(split_df.column("ratio").is_ok());
let gains = ticker.capital_gains(TimeRange::FiveYears).await.unwrap();
let gains_df = CapitalGain::vec_to_dataframe(&gains).unwrap();
assert!(gains_df.column("timestamp").is_ok());
assert!(gains_df.column("amount").is_ok());
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_screener_to_dataframe() {
use finance_query::{Screener, finance};
let gainers = finance::screener(Screener::DayGainers, 50).await.unwrap();
let df = gainers.to_dataframe().unwrap();
println!("{}", df);
assert!(df.height() > 0);
}
#[cfg(feature = "indicators")]
#[tokio::test]
#[ignore = "requires network access"]
async fn test_indicators_to_dataframe() {
use finance_query::Ticker;
let ticker = Ticker::new("TSLA").await.unwrap();
let indicators = ticker
.indicators(Interval::OneDay, TimeRange::ThreeMonths)
.await
.unwrap();
let df = indicators.to_dataframe().unwrap();
println!("RSI(14): {:?}", df.column("rsi_14").unwrap());
println!("ADX(14): {:?}", df.column("adx_14").unwrap());
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_polars_filtering() {
use finance_query::Ticker;
let ticker = Ticker::new("AAPL").await.unwrap();
let chart = ticker
.chart(Interval::OneDay, TimeRange::SixMonths)
.await
.unwrap();
let df = chart.to_dataframe().unwrap();
println!("Total days: {}", df.height());
assert!(df.height() > 0);
}
#[tokio::test(flavor = "multi_thread")]
#[ignore = "requires network access"]
async fn test_polars_statistics() {
use finance_query::Ticker;
use polars::prelude::*;
let ticker = Ticker::new("AAPL").await.unwrap();
let chart = ticker
.chart(Interval::OneDay, TimeRange::SixMonths)
.await
.unwrap();
let df = chart.to_dataframe().unwrap();
let stats = df
.clone()
.lazy()
.select([
col("close").mean().alias("avg_close"),
col("high").max().alias("max_high"),
col("low").min().alias("min_low"),
])
.collect()
.unwrap();
let avg_close: f64 = stats
.column("avg_close")
.unwrap()
.f64()
.unwrap()
.get(0)
.unwrap();
let max_high: f64 = stats
.column("max_high")
.unwrap()
.f64()
.unwrap()
.get(0)
.unwrap();
let min_low: f64 = stats
.column("min_low")
.unwrap()
.f64()
.unwrap()
.get(0)
.unwrap();
println!("Average close: ${:.2}", avg_close);
println!("Range: ${:.2} - ${:.2}", min_low, max_high);
assert!(avg_close > 0.0);
assert!(max_high >= min_low);
}
#[tokio::test(flavor = "multi_thread")]
#[ignore = "requires network access"]
async fn test_polars_calculated_columns() {
use finance_query::Ticker;
use polars::prelude::*;
let ticker = Ticker::new("AAPL").await.unwrap();
let chart = ticker
.chart(Interval::OneDay, TimeRange::OneMonth)
.await
.unwrap();
let df = chart.to_dataframe().unwrap();
let df = df
.lazy()
.with_column(
((col("close") - col("close").shift(lit(1))) / col("close").shift(lit(1)) * lit(100.0))
.alias("daily_return_pct"),
)
.collect()
.unwrap();
assert!(df.column("daily_return_pct").is_ok());
}
#[tokio::test(flavor = "multi_thread")]
#[ignore = "requires network access"]
async fn test_polars_time_based_operations() {
use chrono::DateTime;
use finance_query::Ticker;
use polars::prelude::*;
let ticker = Ticker::new("AAPL").await.unwrap();
let chart = ticker
.chart(Interval::OneDay, TimeRange::OneYear)
.await
.unwrap();
let df = chart.to_dataframe().unwrap();
let dates: Vec<_> = df
.column("timestamp")
.unwrap()
.i64()
.unwrap()
.into_iter()
.map(|ts| ts.map(|t| DateTime::from_timestamp(t, 0).unwrap()))
.collect();
assert!(!dates.is_empty());
let start_ts = 1704067200i64; let df_filtered = df
.lazy()
.filter(col("timestamp").gt_eq(lit(start_ts)))
.collect()
.unwrap();
println!("Filtered rows: {}", df_filtered.height());
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_polars_sorting_and_ranking() {
use finance_query::{Screener, finance};
use polars::prelude::*;
let gainers = finance::screener(Screener::DayGainers, 100).await.unwrap();
let mut df = gainers.to_dataframe().unwrap();
df = df
.sort(
["market_cap"],
SortMultipleOptions::default().with_order_descending(true),
)
.unwrap();
let top_10 = df.head(Some(10));
println!("{}", top_10);
assert!(top_10.height() <= 10);
}
#[tokio::test(flavor = "multi_thread")]
#[ignore = "requires network access"]
async fn test_polars_aggregations() {
use finance_query::Ticker;
use polars::prelude::*;
let ticker = Ticker::new("AAPL").await.unwrap();
let chart = ticker
.chart(Interval::OneDay, TimeRange::OneYear)
.await
.unwrap();
let df = chart.to_dataframe().unwrap();
let monthly = df
.lazy()
.with_column((col("timestamp") / lit(86400i64 * 30i64)).alias("month"))
.group_by([col("month")])
.agg([
col("close").mean().alias("avg_close"),
col("volume").sum().alias("total_volume"),
col("high").max().alias("max_high"),
col("low").min().alias("min_low"),
])
.collect()
.unwrap();
println!("{}", monthly);
assert!(monthly.height() > 0);
}
#[tokio::test(flavor = "multi_thread")]
#[ignore = "requires network access"]
async fn test_multiple_symbols_concat() {
use finance_query::Ticker;
use polars::prelude::*;
let aapl = Ticker::new("AAPL").await.unwrap();
let msft = Ticker::new("MSFT").await.unwrap();
let nvda = Ticker::new("NVDA").await.unwrap();
let aapl_chart = aapl
.chart(Interval::OneDay, TimeRange::OneMonth)
.await
.unwrap();
let msft_chart = msft
.chart(Interval::OneDay, TimeRange::OneMonth)
.await
.unwrap();
let nvda_chart = nvda
.chart(Interval::OneDay, TimeRange::OneMonth)
.await
.unwrap();
let mut aapl_df = aapl_chart.to_dataframe().unwrap();
let mut msft_df = msft_chart.to_dataframe().unwrap();
let mut nvda_df = nvda_chart.to_dataframe().unwrap();
aapl_df
.with_column(Series::new("symbol".into(), vec!["AAPL"; aapl_df.height()]).into())
.unwrap();
msft_df
.with_column(Series::new("symbol".into(), vec!["MSFT"; msft_df.height()]).into())
.unwrap();
nvda_df
.with_column(Series::new("symbol".into(), vec!["NVDA"; nvda_df.height()]).into())
.unwrap();
let combined = concat(
[aapl_df.lazy(), msft_df.lazy(), nvda_df.lazy()],
UnionArgs::default(),
)
.unwrap()
.collect()
.unwrap();
println!("Combined data: {} rows", combined.height());
assert!(combined.height() > 0);
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_polars_joining_dataframes() {
use finance_query::{Dividend, Ticker, TimeRange};
let ticker = Ticker::new("AAPL").await.unwrap();
let aapl_chart = ticker
.chart(Interval::OneDay, TimeRange::OneMonth)
.await
.unwrap();
let aapl_divs = ticker.dividends(TimeRange::OneMonth).await.unwrap();
let price_df = aapl_chart.to_dataframe().unwrap();
let div_df = Dividend::vec_to_dataframe(&aapl_divs).unwrap();
assert!(price_df.column("timestamp").is_ok());
assert!(div_df.column("timestamp").is_ok());
}
#[tokio::test(flavor = "multi_thread")]
#[ignore = "requires network access"]
async fn test_polars_custom_analysis() {
use finance_query::{Screener, Ticker, finance};
use polars::prelude::*;
let ticker = Ticker::new("AAPL").await.unwrap();
let chart = ticker
.chart(Interval::OneDay, TimeRange::OneMonth)
.await
.unwrap();
let df = chart.to_dataframe().unwrap();
let range_pct = df
.clone()
.lazy()
.select([
col("timestamp"),
((col("high") - col("low")) / col("close") * lit(100.0)).alias("range_pct"),
])
.collect()
.unwrap();
let volatile_days = range_pct
.sort(
["range_pct"],
SortMultipleOptions::default().with_order_descending(true),
)
.unwrap()
.head(Some(10));
println!("Most volatile days:\n{}", volatile_days);
assert!(volatile_days.height() <= 10);
let _ = finance::screener(Screener::DayGainers, 100).await.unwrap();
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_vec_to_dataframe() {
use finance_query::{Dividend, SearchOptions, Ticker, TimeRange, finance};
let ticker = Ticker::new("AAPL").await.unwrap();
let dividends = ticker.dividends(TimeRange::FiveYears).await.unwrap();
let df = Dividend::vec_to_dataframe(÷nds).unwrap();
assert!(df.column("timestamp").is_ok());
let results = finance::search("tech", &SearchOptions::default())
.await
.unwrap();
let df = results.quotes.to_dataframe().unwrap();
assert!(df.height() > 0);
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_single_item_to_dataframe() {
use finance_query::Ticker;
let ticker = Ticker::new("AAPL").await.unwrap();
let quote = ticker.quote().await.unwrap();
let df = quote.to_dataframe().unwrap();
assert_eq!(df.height(), 1);
assert!(df.width() >= 30);
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_error_handling_match() {
use finance_query::Ticker;
let ticker = Ticker::new("AAPL").await.unwrap();
let chart = ticker
.chart(Interval::OneDay, TimeRange::OneMonth)
.await
.unwrap();
match chart.to_dataframe() {
Ok(df) => {
println!("DataFrame created: {} rows", df.height());
assert!(df.height() > 0);
}
Err(e) => {
eprintln!("DataFrame conversion error: {}", e);
panic!("Expected successful conversion");
}
}
}
#[tokio::test(flavor = "multi_thread")]
#[ignore = "requires network access"]
async fn test_best_practices_cache_and_tail() {
use finance_query::Ticker;
use polars::prelude::*;
let ticker = Ticker::new("AAPL").await.unwrap();
let chart = ticker
.chart(Interval::OneDay, TimeRange::OneMonth)
.await
.unwrap();
let df = chart.to_dataframe().unwrap();
let high_volume = df
.clone()
.lazy()
.filter(col("volume").gt(lit(50_000_000i64)))
.collect()
.unwrap();
let recent = df.tail(Some(5));
println!("High volume days: {}", high_volume.height());
println!("Recent 5 days:\n{}", recent);
assert!(recent.height() <= 5);
}