Skip to content

[FEATURE REQUEST] Financial Event Calendar #137

@Verdenroz

Description

@Verdenroz

Feature Request: Financial Event Calendar

Summary

Add a Ticker::calendar(range) and Tickers::calendar(range) method that aggregates upcoming financial events — earnings dates, ex-dividend and dividend payment dates, options expiration dates, and (when the fred feature is enabled) economic data releases — into a single time-sorted list. No new API endpoints are required; all data is sourced from existing fetches already in the library.

Problem Statement

The data needed to build a financial calendar already exists across several FinanceQuery endpoints, but there is no unified way to retrieve it:

  • Earnings dates live in CalendarEvents (from the quoteSummary module), but require a full quote fetch to access.
  • Dividend dates (ex-dividend and payment) are in the same CalendarEvents struct, buried alongside earnings data.
  • Options expiration dates come from the options chain endpoint — a separate call with a different response shape.
  • Economic releases (CPI, NFP, FOMC decisions) are available via FRED when the fred feature is enabled, but there is no connection between FRED data and the ticker-level calendar.

A user monitoring a portfolio of 10 tickers today must make ~30+ separate calls, manually parse four different date formats, and stitch the results into a sorted view themselves. This is tedious in library code, impractical in the CLI, and nearly impossible for an AI agent to do efficiently via MCP tool calls.

Proposed Solution

New CalendarEvent Type

// src/models/calendar/mod.rs (new)

/// A single upcoming financial event.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CalendarEvent {
    /// Unix timestamp (seconds) when the event occurs.
    pub timestamp: i64,
    /// ISO 8601 date string for display (e.g. "2026-01-23").
    pub date: String,
    /// Ticker symbol this event belongs to. None for market-wide events.
    pub symbol: Option<String>,
    /// The specific event.
    pub event: EventKind,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum EventKind {
    /// Upcoming earnings report with analyst estimate data.
    Earnings {
        eps_estimate_low: Option<f64>,
        eps_estimate_avg: Option<f64>,
        eps_estimate_high: Option<f64>,
        revenue_estimate_avg: Option<i64>,
        is_estimate: bool,
    },
    /// Ex-dividend date — must hold shares before this date to receive dividend.
    ExDividend {
        amount: Option<f64>,
    },
    /// Dividend payment date — cash arrives in account.
    DividendPayment {
        amount: Option<f64>,
    },
    /// Options contract expiration for this ticker.
    OptionsExpiration {
        contract_count: usize,
    },
    /// Economic data release (requires `fred` feature).
    #[cfg(feature = "fred")]
    EconomicRelease {
        name: String,
        series_id: String,
    },
}

Library API

// Single ticker
let ticker = Ticker::builder("AAPL").build().await?;
let events = ticker.calendar(TimeRange::OneMonth).await?;
// Returns Vec<CalendarEvent> sorted by timestamp ascending

// Watchlist — fetches all tickers concurrently, merges and sorts
let tickers = Tickers::builder(vec!["AAPL", "MSFT", "TSLA", "NVDA"])
    .build()
    .await?;
let events = tickers.calendar(TimeRange::OneMonth).await?;

Data sources and zero new API calls:

Event type Source Already fetched by
Earnings date + estimates CalendarEventsEarningsCalendar quoteSummary (calendarEvents module)
Ex-dividend date CalendarEventsex_dividend_date same quoteSummary call
Dividend payment date CalendarEventsdividend_date same quoteSummary call
Options expirations OptionsChain::expiration_date options endpoint
Economic releases FRED releases/dates API fred feature — new sub-call

The Tickers::calendar() implementation fetches all event sources concurrently (one quoteSummary batch + one options call per ticker) and merges the results in a single sort pass — comparable overhead to Tickers::quotes().

Server

GET /v2/calendar?symbols=AAPL,MSFT,TSLA&range=1mo

# Response
[
  {
    "timestamp": 1753228800,
    "date": "2026-07-22",
    "symbol": "TSLA",
    "event": {
      "type": "earnings",
      "eps_estimate_low": 0.61,
      "eps_estimate_avg": 0.74,
      "eps_estimate_high": 0.89,
      "revenue_estimate_avg": 26800000000,
      "is_estimate": true
    }
  },
  {
    "timestamp": 1753574400,
    "date": "2026-07-26",
    "symbol": "MSFT",
    "event": {
      "type": "options_expiration",
      "contract_count": 312
    }
  },
  {
    "timestamp": 1754352000,
    "date": "2026-08-04",
    "symbol": "AAPL",
    "event": {
      "type": "ex_dividend",
      "amount": 0.25
    }
  }
]

CLI

$ fq calendar AAPL MSFT TSLA NVDA --range 1mo

DATE        SYMBOL  EVENT                    DETAILS
──────────────────────────────────────────────────────────────────
2026-07-18  NVDA    Options Expiration       847 contracts
2026-07-22  TSLA    Earnings (est.)          EPS: $0.61–$0.89 avg $0.74
2026-07-26  MSFT    Options Expiration       312 contracts
2026-07-29  AAPL    Earnings (est.)          EPS: $1.41–$1.65 avg $1.57
2026-08-04  AAPL    Ex-Dividend              $0.25/share
2026-08-14  MSFT    Ex-Dividend              $0.83/share
2026-08-20  NVDA    Options Expiration       1,204 contracts
2026-08-21  TSLA    Dividend Payment         $0.10/share

8 events across 4 symbols over the next 30 days.

Output formats: --output table (default), --output json, --output ical (iCalendar .ics file for import into Google Calendar / Apple Calendar).

MCP

#[derive(Deserialize, JsonSchema)]
pub struct GetCalendarParams {
    /// Comma-separated ticker symbols (e.g. "AAPL,MSFT,TSLA")
    pub symbols: String,
    /// Time range: "1wk", "1mo", "3mo" (default "1mo")
    pub range: Option<String>,
}

The get_calendar tool returns the merged sorted event list, enabling AI agents to answer questions like "what earnings are coming up for my portfolio this month?" in a single tool call instead of calling get_quote per ticker and parsing dates manually.

Use Cases

  1. Portfolio monitoring — a user running fq calendar on their watchlist sees at a glance that three earnings reports, two ex-dividend dates, and a major options expiration all cluster in the same week, informing position sizing decisions.
  2. Earnings season preparation — an AI agent using the MCP server calls get_calendar once to surface all upcoming earnings in a user's portfolio, then calls get_quote and get_financials for each to build a pre-earnings briefing.
  3. Options trader — tracking options expiration clusters across a sector to anticipate volatility events; fq calendar XLE XOM CVX SLB --range 3mo shows all expirations in a single sorted view.
  4. iCalendar exportfq calendar AAPL MSFT --output ical > finance.ics imports earnings and dividend dates directly into Google Calendar for reminder notifications.

Alternatives Considered

  • Manual stitching (current workaround) — requires calling quote, options, and dividends separately per ticker, parsing three different date formats, and sorting manually. Impractical for more than 2–3 tickers.
  • External financial calendar APIs — services like Earnings Whispers or Nasdaq's earnings calendar provide similar data but add external API dependencies and potential rate limits. FinanceQuery already has all the underlying data.

Additional Context

  • No new Yahoo Finance endpoints: earnings, dividend, and options expiration data are already fetched by existing endpoints. The calendar method is purely an aggregation layer over data the library already retrieves.
  • FRED economic releases: when the fred feature is enabled, the FRED releases/dates API provides upcoming economic data release dates (CPI, PPI, NFP, GDP advance estimate, etc.) and is merged into the event stream as market-wide events with symbol: None.
  • iCalendar output: the --output ical CLI option requires the icalendar crate (optional dependency, gated on a ical feature flag). Not required for the core calendar feature.
  • Timezone handling: all timestamps are returned as UTC Unix timestamps; display formatting uses the exchange's timezone (already available from ChartMeta::timezone and ChartMeta::exchange_timezone_name).
  • Non-breaking: calendar() is a new method on Ticker and Tickers. No existing methods or types change.

Priority

  • Critical - Blocking my use of the project
  • High - Would significantly improve my workflow
  • Medium - Would be nice to have
  • Low - Minor improvement

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions