Overview
This guide shows how to fetch stock dividend data in Python using yfinance and pandas, batch tickers efficiently, and compute simple metrics like trailing 12-month (TTM) yield. It fits algorithmic trading workflows that need reproducible, scriptable data pulls.
Data source note: yfinance retrieves data from Yahoo Finance. Dates returned for dividends correspond to the ex-dividend date. Validate critical decisions with an official data vendor when necessary.
Quickstart
- Install dependencies.
- Fetch dividend history for a ticker.
- Compute TTM dividend yield.
- Batch multiple tickers.
- Save results for reuse.
Install
pip install yfinance pandas
Minimal working example
Fetch the dividend history for a single ticker and compute TTM yield.
import yfinance as yf
import pandas as pd
# 1) Fetch per-share cash dividends (indexed by ex-dividend date)
ticker = "AAPL"
div = yf.Ticker(ticker).dividends # pandas Series: index=dates, values=cash per share
print("Last 5 dividends:\n", div.tail())
# 2) Compute TTM dividend sum and yield
now = pd.Timestamp.utcnow().tz_localize(None)
one_year_ago = now - pd.DateOffset(years=1)
ttm_div = div[div.index >= one_year_ago].sum()
# Latest close price (use Close; Adj Close already reflects dividends)
last_close = yf.Ticker(ticker).history(period="5d")["Close"].iloc[-1]
yield_pct = 100.0 * ttm_div / last_close if last_close > 0 else float("nan")
print(f"TTM dividend: ${ttm_div:.2f}")
print(f"Price: ${last_close:.2f}")
print(f"TTM dividend yield: {yield_pct:.2f}%")
Fetch dividends for multiple tickers (batched)
Use yf.download to reduce request overhead and obtain prices and corporate actions in one call.
import yfinance as yf
import pandas as pd
# Batch tickers
tickers = ["AAPL", "MSFT", "KO"]
# Download OHLCV plus corporate actions (dividends, splits)
# actions=True adds columns: Dividends, Stock Splits; auto_adjust=False keeps raw Close
raw = yf.download(
tickers,
period="5y",
interval="1d",
actions=True,
auto_adjust=False,
progress=False,
)
# raw has MultiIndex columns when multiple tickers are used
# Extract dividends and close, stack to long format: index=(Date, Ticker)
div = raw["Dividends"].stack().rename("dividend").to_frame()
close = raw["Close"].stack().rename("close").to_frame()
# Join to a tidy DataFrame with dividend amounts and prices
panel = close.join(div, how="left").fillna({"dividend": 0.0})
print(panel.head())
Compute simple total return including dividends
If you model returns from raw Close, add dividend yield on ex-dividend days. Do not add dividends if you use Adjusted Close (that would double-count).
# prices and dividends from prior section
prices = panel["close"] # Series with MultiIndex (date, ticker)
# Daily close-to-close returns per ticker
ret = prices.groupby(level=1).pct_change().rename("ret").to_frame()
# Dividend yield applied on ex-dividend dates
div_yield = (panel["dividend"] / prices).rename("div_yield").to_frame().fillna(0.0)
# Total return approximation (no reinvestment slippage modeled)
ret["div_yield"] = div_yield["div_yield"]
ret["total_ret"] = ret["ret"].fillna(0.0) + ret["div_yield"]
print(ret.dropna().head())
Save to disk
Persist for backtests and to avoid re-downloading.
# Save long-form dividends
div.reset_index().rename(columns={"level_1": "Ticker", 0: "dividend"}).to_csv(
"dividends_long.csv", index=False
)
# Save price + dividend panel to Parquet for compact storage
panel.to_parquet("panel_dividends.parquet")
Common fields and expectations
- Dividend value: cash per share on the ex-dividend date.
- Index (date): ex-dividend date (timezone-naive in UTC by default after conversion).
- Currency: in the listing currency of the security.
- Splits: available separately via the Stock Splits column when actions=True.
| Component | Source | Notes |
|---|---|---|
| Dividends | Ticker.dividends or download(..., actions=True) | Ex-dividend dates; per-share cash |
| Prices | Close or Adj Close | Use Close when adding dividends; Adj Close already accounts for dividends |
| Splits | download(..., actions=True) | Split ratio per date |
Pitfalls
- Ex-dividend vs payment date: Series is keyed by ex-dividend date, not payment date. Payment date may lag by weeks.
- Adjusted prices: Adj Close includes dividend adjustments. Do not add dividends to Adj Close returns.
- Currency: Dividends are in the instrument’s currency; converting requires FX data for accurate portfolio aggregation.
- Delistings and symbol changes: History for delisted tickers may be incomplete; map identifiers carefully.
- Survivorship bias: Building universes from current constituents can drop historical payers that later delisted or moved indices.
- Timezones: Convert to timezone-naive UTC for consistent joins; yfinance may return tz-aware indexes in some methods.
- Sparse events: Many tickers pay quarterly or irregularly; resample or forward-fill with care when aligning to daily bars.
Performance notes
- Batch downloads: Prefer yf.download with a list of tickers over per-ticker loops.
- Minimize periods: Request the shortest period/granularity needed (e.g., period="2y" vs "max").
- Caching: Save results (CSV/Parquet) and only update incrementally in subsequent runs.
- Parallelism: If you must loop per ticker (e.g., Ticker().dividends), use ThreadPoolExecutor with small pools (e.g., 4–8) to avoid hammering the source.
- Retry/backoff: Add light retries and backoff to handle transient network hiccups.
- Memory: When stacking MultiIndex frames for thousands of tickers, process in batches and write to disk incrementally.
Tiny FAQ
- Q: Are dividends split-adjusted?
- A: The cash amount is reported per share as of the event; stock splits are separate events.
- Q: What date am I getting?
- A: Ex-dividend date per Yahoo Finance.
- Q: Can I get ETFs and ADRs?
- A: Yes; behavior and currency follow the listing. Check for withholding or irregular schedules.
- Q: Why doesn’t my total return match Adj Close returns?
- A: Adj Close already embeds dividend effects. Use Close+dividends or use Adj Close alone.
- Q: How do I handle missing events?
- A: Re-try fetch, cross-check with another source, and log discrepancies for manual review.