Computing Dividends

Theory

Note that yfinance provides a built-in way to retrieve dividend data directly via yf.Ticker('AAPL').dividends. The goal of this notebook is not to replicate that, but to verify that the dividend payments implied by the Close and Adj Close price series are consistent with economic theory. This is a useful check of our understanding of how adjusted prices are constructed.

The total daily return \(R_t\) should be the same whether it is obtained from actual or adjusted prices. Let \(P_{t}\) denote the closing price and \(P^{\mathit{adj}}_t\) the adjusted price. Then it must be the case that: \[ R_{t+1} = \frac{P_{t+1} + D_{t+1}}{P_{t}} -1 = \frac{P^{\mathit{adj}}_{t+1}}{P^{\mathit{adj}}_{t}} - 1. \]

Therefore, \[ \begin{align*} D_{t+1} & = P_t \left( \frac{P^{\mathit{adj}}_{t+1}}{P^{\mathit{adj}}_{t}} - \frac{P_{t+1}}{P_{t}} \right) \\ & = P_t \left( \frac{P^{\mathit{adj}}_{t+1} - P^{\mathit{adj}}_{t}}{P^{\mathit{adj}}_{t}} - \frac{P_{t+1} - P_{t}}{P_{t}} \right). \\ \end{align*} \] In other words, the dividends can be computed by looking at the difference in percentage changes of the two series, times the previous closing price. We can compute the percentage changes of the two series using the function pct_change() applied to the dataframe df. The dividends are then computed according to the formula above.

Note that in order to compute \(D_{t+1}\) we need to use the previous closing price \(P_{t}.\)

Practice

We start by loading the library yfinance.

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import yfinance as yf

sns.set_theme()

We then download data for AAPL stock from Jan 01, 2012 until the most recent date, and store the columns Close and Adj Close in a dataframe.

df = yf.download(tickers='AAPL', start='2012-01-01', auto_adjust=False, progress=False, multi_level_index=False).loc[:, ['Close', 'Adj Close']]

We define three new columns in our dataframe:

  • Close Pct Change: Daily percentage change in the Close price.
  • Adj Close Pct Change: Daily percentage change in the Adj Close price.
  • Lag Close: The previous day Close price.
df['Close Pct Change'] = df['Close'].pct_change()
df['Adj Close Pct Change'] = df['Adj Close'].pct_change()
df['Lag Close'] = df['Close'].shift(1)

Finally, we compute the dividends using the formula below.

df['Dividends'] = (df['Adj Close Pct Change'] - df['Close Pct Change']) * df['Lag Close']

The figure below shows the time-series of the quarterly dividends of AAPL stock.

df['Dividends'].plot()
plt.title('Quarterly Dividends of AAPL')
plt.ylabel('Dividend ($)')
plt.show()

Verification

We can verify our result against the dividend data reported directly by yfinance using yf.Ticker('AAPL').dividends. This returns the official dividend amount on each ex-dividend date. We filter our computed series to only days where the dividend exceeds a small threshold (to exclude floating-point noise on non-dividend days), then compute the mean absolute difference between the two series.

div_yf = yf.Ticker('AAPL').dividends.tz_localize(None)
div_yf = div_yf[div_yf.index >= '2012-01-01']

div_computed = df.loc[df['Dividends'] > 0.001, 'Dividends']

comparison = pd.DataFrame({'yfinance': div_yf, 'computed': div_computed}).dropna()
mad = (comparison['yfinance'] - comparison['computed']).abs().mean()
print(f'Mean absolute difference: ${mad:.6f}')
Mean absolute difference: $0.001605

The match is close but not exact. This is because Yahoo Finance stores adjusted prices with limited decimal precision — when we back-calculate dividends from the ratio of adjusted to unadjusted price changes, small rounding errors accumulate. The differences are economically negligible.

Practice Problems

Problem 1 Using Adj Close prices, compute the quarterly dividends paid by Boeing Corporation from January 2015 until May 2023.

Solution
df = yf.download(tickers='BA', start='2015-01-01', end='2023-05-01', auto_adjust=False, progress=False, multi_level_index=False).loc[:, ['Close', 'Adj Close']]
df['Close Pct Change'] = df['Close'].pct_change()
df['Adj Close Pct Change'] = df['Adj Close'].pct_change()
df['Lag Close'] = df['Close'].shift(1)
df['Dividends'] = (df['Adj Close Pct Change'] - df['Close Pct Change']) * df['Lag Close']
df['Dividends'].plot()
plt.title('Quarterly Dividends of BA')
plt.ylabel('Dividend ($)')
plt.show()