Skip to content

Ticker API Reference

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

use finance_query::Ticker;

let ticker = Ticker::new("AAPL").await?;

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, SixMonths
  • OneYear, TwoYears, FiveYears, TenYears
  • YearToDate, 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 &dividends {
    // 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

Feature flag required

finance-query = { version = "...", features = ["risk"] }

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 statements
  • Frequency::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