> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cfosilvia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# EARNINGS CALENDAR SYSTEM

# Earnings Calendar System

## Overview

The earnings calendar system automatically fetches and displays upcoming earnings reports for all securities in the user's portfolio, including manually tracked holdings, connected brokerage accounts, and SnapTrade positions.

## Architecture

### Data Flow

1. **Cron Job** (`/api/cron/fetch-earnings`) runs weekly on Sundays at 3 AM UTC
2. Fetches all unique tickers from THREE sources:
   * `securities` table (Plaid-connected holdings)
   * `manual_public_holdings` table (manually tracked stocks)
   * `snaptrade_positions` table (SnapTrade brokerage positions)
3. Deduplicates tickers using a Set to create a unique list
4. Calls Finnhub API for each unique ticker to get earnings data for the next 365 days
5. Upserts earnings data into the `earnings` table
6. Frontend queries earnings based on user's holdings

### Components

#### Backend

* **`/app/api/cron/fetch-earnings/route.ts`**: Cron job that fetches earnings data
  * Runs weekly: `0 3 * * 0` (Sunday 3 AM UTC)
  * Max duration: 800 seconds
  * Rate limited: 1 request per second (Finnhub free tier: 60 calls/min)

#### Frontend

* **`/hooks/queries/use-earnings.ts`**: React hook that fetches earnings for user's holdings
  * Extracts ticker symbols from all holding types (manual, SnapTrade, connected)
  * Queries earnings table for matching tickers
  * Filters to show only upcoming/future earnings

* **`/components/dashboard-earnings-card.tsx`**: Dashboard card showing next 4 upcoming earnings

* **`/app/earnings-calendar/page.tsx`**: Full earnings calendar page

### Database Schema

```sql theme={null}
CREATE TABLE earnings (
  id SERIAL PRIMARY KEY,
  ticker_symbol TEXT NOT NULL,
  date DATE NOT NULL,
  quarter TEXT,
  year TEXT,
  eps_estimate NUMERIC,
  eps_actual NUMERIC,
  revenue_estimate NUMERIC,
  revenue_actual NUMERIC,
  time TEXT, -- 'bmo' (before market open) or 'amc' (after market close)
  created_at TIMESTAMP DEFAULT NOW(),
  UNIQUE(ticker_symbol, date)
);
```

## API Integration

### Finnhub API

* **Endpoint**: `https://finnhub.io/api/v1/calendar/earnings`
* **Rate Limit**: 60 calls/minute (free tier)
* **Authentication**: API key via `X-Finnhub-Token` header
* **Response**: Array of earnings events with estimates and actuals

### Environment Variables

* `FINNHUB_API_KEY`: Required for API authentication
* `SUPABASE_URL`: Supabase project URL
* `SUPABASE_SERVICE_ROLE_KEY`: Service role key for admin access
* `CRON_SECRET`: Secret for authenticating cron requests

## Holdings Support

The system supports earnings for all holding types, with both backend (data fetching) and frontend (display) integration:

### Manual Holdings

* **Backend source**: `manual_public_holdings` table (column: `symbol`)
* **Frontend structure**: `securities` is an object with `ticker_symbol`
* Includes only active holdings (`is_active = true`)

### SnapTrade Holdings

* **Backend source**: `snaptrade_positions` table (column: `symbol`)
* **Frontend structure**: `securities` is an array with first element containing `ticker_symbol`
* Positions from brokerage accounts (Schwab, Fidelity, etc.)

### Connected Holdings (Plaid)

* **Backend source**: `securities` table (column: `ticker_symbol`)
* **Frontend structure**: `securities` is an object with `ticker_symbol`
* Holdings from Plaid-connected bank/brokerage accounts

### Deduplication

The backend cron job uses a JavaScript `Set` to automatically deduplicate tickers across all three sources, ensuring each ticker is only fetched once from the Finnhub API even if a user has the same stock in multiple accounts.

## Rate Limiting

The cron job implements rate limiting to respect Finnhub's API limits:

* 1 second delay between requests
* Processes securities sequentially (not parallel)
* Max 60 securities per minute
* Weekly schedule prevents excessive API usage

## Error Handling

* Failed API calls are logged and counted
* Errors don't stop the entire batch
* Response includes success/failure counts and error details
* Upsert logic prevents duplicate entries

## Manual Trigger

To manually trigger the earnings fetch (for testing):

```bash theme={null}
curl -X GET https://your-domain.com/api/cron/fetch-earnings \
  -H "Authorization: Bearer YOUR_CRON_SECRET"
```

## Monitoring

Check cron job logs in Vercel dashboard:

* Look for "Starting earnings data fetch..."
* Monitor success/error counts
* Check for Finnhub API rate limit errors
* Verify earnings data is being inserted

## Future Enhancements

* [ ] Add earnings surprises (actual vs estimate)
* [ ] Send notifications for earnings reports
* [ ] Filter earnings by portfolio value/importance
* [ ] Add earnings call transcripts integration
* [ ] Historical earnings performance tracking
* [ ] Earnings impact on stock price analysis


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.