Skip to main content

Section A.1 The Dow Jones

The MeasuringWorth Foundation has compiled the value of the Dow Jones Industrial Average at the end of each day from February 16, 1885 to the present, with adjustments at several points to make the values comparable. The following cells download and read this data.
Listing A.1.1. Python Code
DATA_PATH = "https://github.com/AllenDowney/ThinkStats/raw/v3/data/"
filename = "DJA.csv"
download(DATA_PATH + filename)
Listing A.1.2. Python Code
$ djia = pd.read_csv(filename, skiprows=4, parse_dates=[0], index_col=0)
djia.head()
               DJIA
Date               
1885-02-16  30.9226
1885-02-17  31.3365
1885-02-18  31.4744
1885-02-19  31.6765
1885-02-20  31.4252
To compute annual returns, weโ€™ll start by selecting the closing price on the last trading day of each year (dropping 2024 because we donโ€™t have a complete year).
Listing A.1.3. Python Code
$ annual = djia.groupby(djia.index.year).last().drop(2024)
annual
            DJIA
Date            
1885     39.4859
1886     41.2391
1887     37.7693
1888     39.5866
1889     42.0394
...          ...
2019  28538.4400
2020  30606.4800
2021  36338.3000
2022  33147.2500
2023  37689.5400

[139 rows x 1 columns]
Next weโ€™ll compute the annual price return, which is the ratio of successive year-end closing prices.
Listing A.1.4. Python Code
$ annual['Ratio'] = annual['DJIA'] / annual['DJIA'].shift(1)
annual
            DJIA     Ratio
Date                      
1885     39.4859       NaN
1886     41.2391  1.044401
1887     37.7693  0.915861
1888     39.5866  1.048116
1889     42.0394  1.061960
...          ...       ...
2019  28538.4400  1.223384
2020  30606.4800  1.072465
2021  36338.3000  1.187275
2022  33147.2500  0.912185
2023  37689.5400  1.137034

[139 rows x 2 columns]
And the relative return as a percentage.
Listing A.1.5. Python Code
annual['Return'] = (annual['Ratio'] - 1) * 100
Looking at the years with the biggest losses and gains, we can see that most of the extremes were before the 1960sโ€”with the exception of the 2008 financial crisis.
Listing A.1.6. Python Code
$ annual.dropna().sort_values(by='Return')
           DJIA     Ratio     Return
Date                                
1931    77.9000  0.473326 -52.667396
1907    43.0382  0.622683 -37.731743
2008  8776.3900  0.661629 -33.837097
1930   164.5800  0.662347 -33.765293
1920    71.9500  0.670988 -32.901240
...         ...       ...        ...
1954   404.3900  1.439623  43.962264
1908    63.1104  1.466381  46.638103
1928   300.0000  1.482213  48.221344
1933    99.9000  1.666945  66.694477
1915    99.1500  1.816599  81.659949

[138 rows x 3 columns]
Hereโ€™s what the distribution of annual returns looks like.
Listing A.1.7. Python Code
from empiricaldist import Cdf

cdf_return = Cdf.from_seq(annual['Return'])
cdf_return.plot()

decorate(xlabel='Annual return (percent)', ylabel='CDF')
Immediately we see why capping returns at 10% might be a bad ideaโ€”this cap is exceeded almost 45% of the time, and sometimes by a lot!
Listing A.1.8. Python Code
$ 1 - cdf_return(10)
0.4492753623188406