Ticker API Reference¶
Cargo Docs
The Ticker struct is the primary interface for fetching symbol-specific data from Yahoo Finance. It provides lazy-loaded, cached access to quotes, charts, financials, and more.
Multiple Symbols
Need to fetch data for multiple symbols? Use the Tickers struct for efficient batch operations.
Creation¶
Simple Construction¶
Builder Pattern¶
For advanced configuration, use the builder:
use finance_query::{Ticker, Region};
use std::time::Duration;
// Using region enum (recommended - sets lang and region code correctly)
let ticker = Ticker::builder("2330.TW")
.region(Region::Taiwan)
.timeout(Duration::from_secs(30))
.build()
.await?;
// With logo fetching and in-memory cache (TTL: 5 minutes)
let ticker = Ticker::builder("AAPL")
.logo()
.cache(Duration::from_secs(300))
.build()
.await?;
// Manual language/region configuration
let ticker = Ticker::builder("AAPL")
.lang("en-US")
.region_code("US")
.timeout(Duration::from_secs(20))
.proxy("http://proxy.example.com:8080")
.build()
.await?;
Builder Methods:
.region(Region)- Set region (automatically configures lang and region_code).lang(String)- Set language code (e.g., "en-US", "de-DE", "zh-TW").region_code(String)- Set region code (e.g., "US", "JP").timeout(Duration)- Set HTTP request timeout.proxy(String)- Set proxy URL.logo()- Fetch company logo URLs alongside quote data.cache(Duration)- Enable in-memory caching with the given TTL (time-to-live)
See Configuration for details on available regions and settings.
Quote Data¶
Aggregated Quote¶
Get a comprehensive quote with all key metrics:
// Enable logo fetching via builder
let ticker = Ticker::builder("AAPL").logo().build().await?;
let quote = ticker.quote().await?;
println!("Symbol: {}", quote.symbol);
println!("Name: {}", quote.short_name.as_deref().unwrap_or("N/A"));
let price = quote.regular_market_price.as_ref().and_then(|v| v.raw).unwrap_or(0.0);
println!("Price: ${:.2}", price);
let change = quote.regular_market_change.as_ref().and_then(|v| v.raw).unwrap_or(0.0);
let change_pct = quote.regular_market_change_percent.as_ref().and_then(|v| v.raw).unwrap_or(0.0);
println!("Change: {:+.2} ({:+.2}%)", change, change_pct);
let market_cap = quote.market_cap.as_ref().and_then(|v| v.raw).unwrap_or(0);
println!("Market Cap: ${}", market_cap);
// Logo URLs (only populated when .logo() is used on the builder)
println!("Logo: {:?}", quote.logo_url);
println!("Company Logo: {:?}", quote.company_logo_url);
The Quote struct aggregates data from multiple quote modules into a single structure.
Quote Modules¶
Access specific quote modules directly. All modules are fetched together on first access and cached:
// First access triggers ONE API call for ALL modules
let price = ticker.price().await?;
if let Some(p) = price {
println!("Market State: {}", p.market_state.as_deref().unwrap_or("N/A"));
println!("Currency: {}", p.currency.as_deref().unwrap_or("N/A"));
}
// Subsequent calls use cached data (no network request)
let financial_data = ticker.financial_data().await?;
if let Some(fd) = financial_data {
let revenue = fd.total_revenue.as_ref().and_then(|v| v.raw).unwrap_or(0);
println!("Revenue: ${}", revenue);
let profit_margins = fd.profit_margins.as_ref().and_then(|v| v.raw).unwrap_or(0.0);
println!("Profit Margin: {:.2}%", profit_margins * 100.0);
}
// Get EPS from DefaultKeyStatistics (not FinancialData)
if let Some(stats) = ticker.key_stats().await? {
let eps = stats.trailing_eps.as_ref().and_then(|v| v.raw).unwrap_or(0.0);
println!("EPS: ${:.2}", eps);
}
let profile = ticker.asset_profile().await?;
if let Some(prof) = profile {
println!("Sector: {}", prof.sector.as_deref().unwrap_or("N/A"));
println!("Industry: {}", prof.industry.as_deref().unwrap_or("N/A"));
println!("Website: {}", prof.website.as_deref().unwrap_or("N/A"));
println!("Description: {}", prof.long_business_summary.as_deref().unwrap_or("N/A"));
}
Available Quote Modules:
| Method | Returns | Description |
|---|---|---|
.price() |
Price |
Current price, market state, currency |
.summary_detail() |
SummaryDetail |
Market cap, P/E, dividend, 52-week range |
.financial_data() |
FinancialData |
Revenue, margins, EPS, cash flow |
.key_stats() |
DefaultKeyStatistics |
Extended statistics (beta, shares outstanding, etc.) |
.asset_profile() |
AssetProfile |
Company info (sector, industry, description, officers) |
.calendar_events() |
CalendarEvents |
Upcoming earnings, dividends, splits |
.earnings() |
Earnings |
Historical and forecasted earnings |
.earnings_trend() |
EarningsTrend |
Analyst earnings estimates and trends |
.earnings_history() |
EarningsHistory |
Past earnings surprises |
.recommendation_trend() |
RecommendationTrend |
Analyst buy/sell/hold recommendations |
.insider_holders() |
InsiderHolders |
Insider ownership |
.insider_transactions() |
InsiderTransactions |
Recent insider trading activity |
.institution_ownership() |
InstitutionOwnership |
Institutional holders |
.fund_ownership() |
FundOwnership |
Mutual fund holders |
.major_holders() |
MajorHoldersBreakdown |
Ownership percentages |
.share_purchase_activity() |
NetSharePurchaseActivity |
Insider net purchase activity |
.quote_type() |
QuoteTypeData |
Asset type, exchange, timezone |
.summary_profile() |
SummaryProfile |
Company summary (address, employees, etc.) |
.sec_filings() |
SecFilings |
Recent SEC filings |
.grading_history() |
UpgradeDowngradeHistory |
Analyst upgrade/downgrade history |
All methods return Result<Option<T>> - the Option is None if the module is not available for this symbol (e.g., crypto doesn't have SEC filings).
Example: Company Analysis¶
let ticker = Ticker::new("MSFT").await?;
// Get financial health
if let Some(fd) = ticker.financial_data().await? {
println!("Financials:");
let revenue = fd.total_revenue.as_ref().and_then(|v| v.raw).unwrap_or(0) as f64;
println!(" Revenue: ${:.2}B", revenue / 1e9);
let profit_margins = fd.profit_margins.as_ref().and_then(|v| v.raw).unwrap_or(0.0);
println!(" Profit Margin: {:.2}%", profit_margins * 100.0);
let roe = fd.return_on_equity.as_ref().and_then(|v| v.raw).unwrap_or(0.0);
println!(" ROE: {:.2}%", roe * 100.0);
let dte = fd.debt_to_equity.as_ref().and_then(|v| v.raw).unwrap_or(0.0);
println!(" Debt to Equity: {:.2}", dte);
}
// Get valuation
if let Some(sd) = ticker.summary_detail().await? {
println!("\nValuation:");
let trailing_pe = sd.trailing_pe.as_ref().and_then(|v| v.raw).unwrap_or(0.0);
println!(" P/E Ratio: {:.2}", trailing_pe);
let forward_pe = sd.forward_pe.as_ref().and_then(|v| v.raw).unwrap_or(0.0);
println!(" Forward P/E: {:.2}", forward_pe);
}
// Get analyst sentiment
if let Some(rt) = ticker.recommendation_trend().await? {
if let Some(latest) = rt.trend.first() {
println!("\nAnalyst Recommendations:");
println!(" Strong Buy: {}", latest.strong_buy);
println!(" Buy: {}", latest.buy);
println!(" Hold: {}", latest.hold);
println!(" Sell: {}", latest.sell);
println!(" Strong Sell: {}", latest.strong_sell);
}
}
Historical Data¶
Chart (OHLCV) Data¶
Get historical candlestick data:
use finance_query::{Interval, TimeRange};
// Daily candles for the past month
let chart = ticker.chart(Interval::OneDay, TimeRange::OneMonth).await?;
println!("Symbol: {}", chart.symbol);
println!("Currency: {}", chart.meta.currency.as_deref().unwrap_or("N/A"));
println!("Exchange: {}", chart.meta.exchange_name.as_deref().unwrap_or("N/A"));
println!("Timezone: {}", chart.meta.timezone.as_deref().unwrap_or("N/A"));
for candle in &chart.candles {
println!(
"{}: O=${:.2}, H=${:.2}, L=${:.2}, C=${:.2}, V={}",
candle.timestamp, candle.open, candle.high, candle.low, candle.close, candle.volume
);
}
Available Intervals:
- Intraday:
OneMinute,FiveMinutes,FifteenMinutes,ThirtyMinutes,OneHour - Daily and above:
OneDay,OneWeek,OneMonth,ThreeMonths
Available Time Ranges:
OneDay,FiveDays,OneMonth,ThreeMonths,SixMonthsOneYear,TwoYears,FiveYears,TenYearsYearToDate,Max
Chart Structure:
pub struct Chart {
pub symbol: String, // Stock symbol
pub meta: ChartMeta, // Metadata (exchange, currency, timezone, etc.)
pub candles: Vec<Candle>, // OHLCV candles
pub interval: Option<Interval>,
pub range: Option<TimeRange>,
}
pub struct Candle {
pub timestamp: i64, // Unix timestamp (seconds)
pub open: f64,
pub high: f64,
pub low: f64,
pub close: f64,
pub volume: i64, // Signed integer
pub adj_close: Option<f64>, // Adjusted close (accounts for splits/dividends)
}
Corporate Events¶
Dividends¶
let dividends = ticker.dividends(TimeRange::TwoYears).await?;
for div in ÷nds {
// div.timestamp is a Unix timestamp (i64, seconds since epoch)
println!("timestamp={}, amount=${:.4}", div.timestamp, div.amount);
}
Stock Splits¶
let splits = ticker.splits(TimeRange::Max).await?;
for split in &splits {
// ratio is a human-readable string like "4:1"
println!("timestamp={}, ratio={} ({}/{})",
split.timestamp, split.ratio, split.numerator, split.denominator
);
}
Capital Gains¶
Distributions of capital gains (common for ETFs and mutual funds):
let gains = ticker.capital_gains(TimeRange::FiveYears).await?;
for gain in &gains {
println!("timestamp={}, amount=${:.4} per share", gain.timestamp, gain.amount);
}
Dividend Analytics¶
Compute analytics from the dividend history (pure calculation, no extra network request):
let analytics = ticker.dividend_analytics(TimeRange::FiveYears).await?;
println!("Total paid: ${:.2}", analytics.total_paid);
println!("Payments: {}", analytics.payment_count);
println!("Average payment: ${:.4}", analytics.average_payment);
if let Some(cagr) = analytics.cagr {
println!("CAGR: {:.1}%", cagr * 100.0);
}
if let Some(last) = &analytics.last_payment {
println!("Most recent: ${:.4} at timestamp {}", last.amount, last.timestamp);
}
DividendAnalytics fields:
| Field | Type | Description |
|---|---|---|
total_paid |
f64 |
Total dividends paid in the requested range |
payment_count |
usize |
Number of dividend payments |
average_payment |
f64 |
Average dividend per payment |
cagr |
Option<f64> |
Compound Annual Growth Rate of the dividend; None if fewer than 2 payments spanning a full year |
last_payment |
Option<Dividend> |
Most recent dividend |
first_payment |
Option<Dividend> |
Earliest dividend in the range |
Technical Indicators¶
Calculate technical indicators with three approaches:
1. Summary API¶
Get all pre-calculated indicators at once:
use finance_query::{Interval, TimeRange};
let indicators = ticker.indicators(Interval::OneDay, TimeRange::ThreeMonths).await?;
// Simple indicators (Option<f64>)
if let Some(rsi) = indicators.rsi_14 {
println!("RSI(14): {:.2}", rsi);
if rsi < 30.0 { println!(" -> Oversold"); }
else if rsi > 70.0 { println!(" -> Overbought"); }
}
if let Some(sma) = indicators.sma_200 {
println!("SMA(200): {:.2}", sma);
}
// Compound indicators (Option<Struct>)
if let Some(macd) = &indicators.macd {
if let (Some(line), Some(signal)) = (macd.macd, macd.signal) {
println!("MACD: {:.4} | Signal: {:.4}", line, signal);
if line > signal { println!(" -> Bullish"); }
}
}
if let Some(bb) = &indicators.bollinger_bands {
if let (Some(upper), Some(lower)) = (bb.upper, bb.lower) {
println!("Bollinger: Upper={:.2}, Lower={:.2}", upper, lower);
}
}
2. Chart Extension Methods¶
Calculate specific indicators with any period:
let chart = ticker.chart(Interval::OneDay, TimeRange::ThreeMonths).await?;
// Custom periods
let sma_15 = chart.sma(15);
let rsi_21 = chart.rsi(21)?;
let macd = chart.macd(8, 21, 5)?; // Fast, slow, signal
// Access latest value
if let Some(&latest_rsi) = rsi_21.last().and_then(|v| v.as_ref()) {
println!("RSI(21): {:.2}", latest_rsi);
}
3. Direct Functions¶
Use indicator functions directly with custom data:
use finance_query::indicators::{sma, rsi};
let chart = ticker.chart(Interval::OneDay, TimeRange::OneMonth).await?;
let closes: Vec<f64> = chart.candles.iter().map(|c| c.close).collect();
let sma_25 = sma(&closes, 25);
let rsi_10 = rsi(&closes, 10)?;
if let Some(&latest) = rsi_10.last().and_then(|v| v.as_ref()) {
println!("RSI(10): {:.2}", latest);
}
See Also
For complete indicator documentation including all available indicators, see Indicators.
Candlestick Patterns¶
Detect candlestick patterns from chart data (requires indicators feature):
use finance_query::indicators::{CandlePattern, PatternSentiment};
let chart = ticker.chart(Interval::OneDay, TimeRange::ThreeMonths).await?;
// Returns Vec<Option<CandlePattern>>, 1:1 aligned with chart.candles
let signals = chart.patterns();
// Zip patterns with candles for context
for (candle, pattern) in chart.candles.iter().zip(signals.iter()) {
if let Some(p) = pattern {
println!(
"timestamp={}: {:?} ({:?})",
candle.timestamp, p, p.sentiment()
);
}
}
// Count bullish signals in the period
let bullish_count = signals
.iter()
.filter(|s| s.map(|p| p.sentiment() == PatternSentiment::Bullish).unwrap_or(false))
.count();
println!("{bullish_count} bullish patterns detected");
Available Patterns (20 total):
| Category | Pattern | Signal |
|---|---|---|
| Three-bar | MorningStar |
Bullish reversal |
| Three-bar | EveningStar |
Bearish reversal |
| Three-bar | ThreeWhiteSoldiers |
Bullish continuation |
| Three-bar | ThreeBlackCrows |
Bearish continuation |
| Two-bar | BullishEngulfing |
Bullish reversal |
| Two-bar | BearishEngulfing |
Bearish reversal |
| Two-bar | BullishHarami |
Bullish reversal |
| Two-bar | BearishHarami |
Bearish reversal |
| Two-bar | PiercingLine |
Bullish reversal |
| Two-bar | DarkCloudCover |
Bearish reversal |
| Two-bar | TweezerBottom |
Bullish reversal at support |
| Two-bar | TweezerTop |
Bearish reversal at resistance |
| One-bar | Hammer |
Bullish reversal (downtrend) |
| One-bar | InvertedHammer |
Bullish reversal (downtrend) |
| One-bar | HangingMan |
Bearish reversal (uptrend) |
| One-bar | ShootingStar |
Bearish reversal (uptrend) |
| One-bar | BullishMarubozu |
Bullish strength |
| One-bar | BearishMarubozu |
Bearish strength |
| One-bar | Doji |
Indecision |
| One-bar | SpinningTop |
Indecision |
Pattern priority: Three-bar → two-bar → one-bar. Each candle slot holds at most one pattern. Output is always the same length as chart.candles.
Combine with indicators
Patterns are most useful as filters on top of indicators. For example, RSI < 30 combined with a Hammer or BullishEngulfing gives a stronger entry signal than either alone.
Risk Analytics¶
Compute a comprehensive risk summary from historical price data:
use finance_query::{Ticker, Interval, TimeRange};
let ticker = Ticker::new("AAPL").await?;
// With S&P 500 as benchmark (enables beta calculation)
let risk = ticker.risk(Interval::OneDay, TimeRange::OneYear, Some("^GSPC")).await?;
// Without a benchmark
let risk = ticker.risk(Interval::OneDay, TimeRange::OneYear, None).await?;
println!("VaR 95%: {:.2}%", risk.var_95 * 100.0);
println!("Max Drawdown: {:.2}%", risk.max_drawdown * 100.0);
if let Some(sharpe) = risk.sharpe {
println!("Sharpe: {:.2}", sharpe);
}
if let Some(beta) = risk.beta {
println!("Beta: {:.2}", beta);
}
See Risk Analytics for the full RiskSummary field reference and standalone metric functions.
Recommendations¶
Get similar stocks and analyst recommendations:
let rec = ticker.recommendations(5).await?;
println!("Similar stocks to {}:", ticker.symbol());
for similar in &rec.recommendations {
println!(" {} - {}", similar.symbol, similar.score);
}
Financial Statements¶
Get income statement, balance sheet, or cash flow statement:
use finance_query::{StatementType, Frequency};
// Annual income statement
let income = ticker.financials(
StatementType::Income,
Frequency::Annual
).await?;
// Access data by metric name
if let Some(revenue_map) = income.statement.get("TotalRevenue") {
for (date, value) in revenue_map {
println!("{}: Revenue ${:.2}B", date, value / 1e9);
}
}
if let Some(net_income_map) = income.statement.get("NetIncome") {
for (date, value) in net_income_map {
println!("{}: Net Income ${:.2}B", date, value / 1e9);
}
}
// Quarterly balance sheet
let balance = ticker.financials(
StatementType::Balance,
Frequency::Quarterly
).await?;
// Cash flow statement
let cashflow = ticker.financials(
StatementType::CashFlow,
Frequency::Annual
).await?;
Statement Types:
StatementType::Income- Income statement (revenue, expenses, profit)StatementType::Balance- Balance sheet (assets, liabilities, equity)StatementType::CashFlow- Cash flow statement (operating, investing, financing)
Frequencies:
Frequency::Annual- Yearly statementsFrequency::Quarterly- Quarterly statements
Options Data¶
Get options chains:
// Get all available expiration dates and options
let options = ticker.options(None).await?;
// expiration_dates() and strikes() are methods
println!("Available expiration dates:");
for exp in options.expiration_dates() {
println!(" {}", exp); // Unix timestamp (i64)
}
// calls() and puts() return a Contracts collection
println!("\nCalls:");
for call in &*options.calls() {
println!(
" Strike ${:.2}: last=${:.2}, volume={}",
call.strike,
call.last_price.unwrap_or(0.0),
call.volume.unwrap_or(0),
);
}
println!("\nPuts:");
for put in &*options.puts() {
println!(
" Strike ${:.2}: last=${:.2}, IV={:.4}",
put.strike,
put.last_price.unwrap_or(0.0),
put.implied_volatility.unwrap_or(0.0),
);
}
// Get options for a specific expiration date
let exp_dates = options.expiration_dates();
if exp_dates.len() > 1 {
let options_dated = ticker.options(Some(exp_dates[1])).await?;
}
News¶
Get recent news for the symbol:
let news = ticker.news().await?;
for article in &news {
println!("{}", article.title);
println!(" Source: {}", article.source);
println!(" Published: {}", article.time);
println!(" URL: {}", article.link);
println!();
}
Earnings Transcripts¶
Get earnings call transcripts:
use finance_query::finance;
// Get latest transcript
let transcript = finance::earnings_transcript(&ticker.symbol(), None, None).await?;
println!("Transcript for {} - Q{} {}",
ticker.symbol(),
transcript.quarter(),
transcript.year()
);
// Access paragraphs with speaker names resolved
for (paragraph, speaker) in transcript.paragraphs_with_speakers() {
if let Some(name) = speaker {
println!("[{:.1}s] {}: {}", paragraph.start, name, paragraph.text);
}
}
// Get a specific quarter transcript
let _q1 = finance::earnings_transcript(&ticker.symbol(), Some("Q1"), Some(2024)).await?;
// Get all available transcripts (metadata only)
let all_transcripts = finance::earnings_transcripts(&ticker.symbol(), None).await?;
for meta in &all_transcripts {
println!("{} {} - {}",
meta.year.unwrap_or(0),
meta.quarter.as_deref().unwrap_or("?"),
meta.title
);
}
Caching Behavior¶
Understanding how Ticker caches data is important for efficient usage.
In-Memory Cache (Optional)¶
Enable with .cache(Duration) on the builder. Disabled by default.
use std::time::Duration;
let ticker = Ticker::builder("AAPL")
.cache(Duration::from_secs(300)) // 5-minute TTL
.build()
.await?;
When enabled, all fetched data is stored in an Arc<RwLock<...>> cache inside the Ticker and automatically invalidated after the TTL.
Quote Summary Modules¶
let ticker = Ticker::new("AAPL").await?;
// First access to ANY quote module -> 1 API call fetching ALL ~30 modules
let price = ticker.price().await?;
// All subsequent module accesses -> 0 API calls (same Ticker instance)
let financial_data = ticker.financial_data().await?; // no network
let profile = ticker.asset_profile().await?; // no network
let stats = ticker.key_stats().await?; // no network
Chart Data¶
Charts are cached separately per (interval, range) combination:
// First call -> 1 API call
let daily_1mo = ticker.chart(Interval::OneDay, TimeRange::OneMonth).await?;
// Same interval+range -> cached (0 API calls)
let daily_1mo_again = ticker.chart(Interval::OneDay, TimeRange::OneMonth).await?;
// Different interval or range -> new API call
let hourly_1mo = ticker.chart(Interval::OneHour, TimeRange::OneMonth).await?;
let daily_3mo = ticker.chart(Interval::OneDay, TimeRange::ThreeMonths).await?;
Financials and Options¶
Financials are cached per (statement_type, frequency) combination:
use finance_query::{StatementType, Frequency};
// First call -> 1 API call
let income_annual = ticker.financials(StatementType::Income, Frequency::Annual).await?;
// Same parameters -> cached
let income_annual_again = ticker.financials(StatementType::Income, Frequency::Annual).await?;
// Different parameters -> new API call
let income_quarterly = ticker.financials(StatementType::Income, Frequency::Quarterly).await?;
let balance_annual = ticker.financials(StatementType::Balance, Frequency::Annual).await?;
Options are cached per expiration date:
// First call -> 1 API call
let current_options = ticker.options(None).await?;
// Same date -> cached
let current_again = ticker.options(None).await?;
// Different date -> new API call
let future_options = ticker.options(Some(1735689600)).await?;
News and Recommendations¶
These are fetched once per Ticker instance and cached:
let news = ticker.news().await?;
let news_again = ticker.news().await?; // cached
let recs = ticker.recommendations(10).await?;
let recs_again = ticker.recommendations(10).await?; // cached
Best Practices¶
Optimize Performance with Caching
- Reuse Ticker instances across multiple queries to benefit from caching
- Request the data you need upfront - accessing one quote module fetches them all anyway
- Be strategic with chart requests - each new
(interval, range)pair triggers a new request
// Good: Reuse ticker for multiple operations
let ticker = Ticker::builder("AAPL").logo().build().await?;
let quote = ticker.quote().await?;
let chart = ticker.chart(Interval::OneDay, TimeRange::OneMonth).await?;
let profile = ticker.asset_profile().await?;
// Less efficient: Creating new tickers each time
// (loses caching benefits, re-authenticates with Yahoo each time)
let ticker1 = Ticker::builder("AAPL").logo().build().await?;
let quote = ticker1.quote().await?;
let ticker2 = Ticker::new("AAPL").await?;
let chart = ticker2.chart(Interval::OneDay, TimeRange::OneMonth).await?;
Next Steps¶
- Technical Indicators - Access 42 indicators + candlestick patterns for analysis
- Backtesting - Test trading strategies against historical data
- Risk Analytics - VaR, Sharpe/Sortino/Calmar, beta, and drawdown (requires
riskfeature) - Batch Tickers - Efficient operations for multiple symbols
- DataFrame Support - Convert responses to Polars DataFrames for analysis
- Configuration - Customize language, region, and network settings