use yfinance 0.2.54 to get the longest possible history of monthly appl closing prices. compute returns as percent changes
I'll help you get Apple's stock data and calculate the returns. Let me first install the specified version of yfinance and then get the data.
Here's the analysis of AAPL's monthly closing prices and returns since 1985:
First few rows of monthly data:
0.0995843932 | |
0.0849902481 | -14.6550525135 |
0.0759759098 | -10.6063206758 |
0.0729713887 | -3.9545707821 |
0.0596649013 | -18.2352118949 |
Summary statistics of monthly returns:
482 |
2.3628205675 |
12.0985866803 |
-57.7436606497 |
-4.6209760664 |
2.5173137004 |
9.6432489898 |
45.3783447232 |
The data shows that AAPL stock has:
use pandas datareader to get the longest possible history of the monthly Fama-French 5 factors
get the full data set. do not start in 2020
I'll get the complete Fama-French 5-factor dataset from its inception.
compute the means of the Fama-French factors
I'll compute the means of the Fama-French factors and convert them to annualized percentages for better interpretation.
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[19], line 27
25 # Calculate t-statistics
26 t_stats = monthly_means / monthly_se
---> 27 p_values = 2 * (1 - stats.t.cdf(abs(t_stats), df=n-1))
29 print("\nt-statistics and p-values:")
30 stats_df = pd.DataFrame({
31 't-stat': t_stats.round(3),
32 'p-value': p_values.round(3)
33 })
NameError: name 'stats' is not defined
Also, evaluate what went wrong and consider options for how to fix it. Break the execution up into cell blocks.merge the appl return data with the Fama-French. drop all rows with missing data.
compute aapl return - RF. call it the excess aapl return. regress the excess aapl return on the five fama-french factors.
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[3], line 5
2 import statsmodels.api as sm
4 # Calculate excess return
----> 5 merged_df['AAPL_Excess'] = merged_df['AAPL_Return'] - merged_df['RF']
7 # Prepare X (factors) and y (excess returns) for regression
8 X = merged_df[['Mkt-RF', 'SMB', 'HML', 'RMW', 'CMA']]
NameError: name 'merged_df' is not defined
Also, evaluate what went wrong and consider options for how to fix it. Break the execution up into cell blocks.multiply the factor betas times the historical factor means and add them up
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[9], line 11
8 ff5 = web.DataReader('F-F_Research_Data_5_Factors_2x3', 'famafrench')[0]
10 # Convert index to datetime
---> 11 ff5.index = pd.to_datetime(ff5.index.astype(str) + '01', format='%Y%m%d')
13 # Display first few rows and basic statistics
14 print("First few rows of Fama-French 5 Factors:")
File /opt/conda/lib/python3.11/site-packages/pandas/core/tools/datetimes.py:1076, in to_datetime(arg, errors, dayfirst, yearfirst, utc, format, exact, unit, infer_datetime_format, origin, cache)
1074 result = _convert_and_box_cache(arg, cache_array, name=arg.name)
1075 else:
-> 1076 result = convert_listlike(arg, format, name=arg.name)
1077 elif is_list_like(arg):
1078 try:
1079 # error: Argument 1 to "_maybe_cache" has incompatible type
1080 # "Union[float, str, datetime, List[Any], Tuple[Any, ...], ExtensionArray,
1081 # ndarray[Any, Any], Series]"; expected "Union[List[Any], Tuple[Any, ...],
1082 # Union[Union[ExtensionArray, ndarray[Any, Any]], Index, Series], Series]"
File /opt/conda/lib/python3.11/site-packages/pandas/core/tools/datetimes.py:433, in _convert_listlike_datetimes(arg, format, name, utc, unit, errors, dayfirst, yearfirst, exact)
431 # `format` could be inferred, or user didn't ask for mixed-format parsing.
432 if format is not None and format != "mixed":
--> 433 return _array_strptime_with_fallback(arg, name, utc, format, exact, errors)
435 result, tz_parsed = objects_to_datetime64(
436 arg,
437 dayfirst=dayfirst,
(...)
441 allow_object=True,
442 )
444 if tz_parsed is not None:
445 # We can take a shortcut since the datetime64 numpy array
446 # is in UTC
File /opt/conda/lib/python3.11/site-packages/pandas/core/tools/datetimes.py:467, in _array_strptime_with_fallback(arg, name, utc, fmt, exact, errors)
456 def _array_strptime_with_fallback(
457 arg,
458 name,
(...)
462 errors: str,
463 ) -> Index:
464 """
465 Call array_strptime, with fallback behavior depending on 'errors'.
466 """
--> 467 result, tz_out = array_strptime(arg, fmt, exact=exact, errors=errors, utc=utc)
468 if tz_out is not None:
469 unit = np.datetime_data(result.dtype)[0]
File strptime.pyx:501, in pandas._libs.tslibs.strptime.array_strptime()
File strptime.pyx:451, in pandas._libs.tslibs.strptime.array_strptime()
File strptime.pyx:583, in pandas._libs.tslibs.strptime._parse_with_format()
ValueError: time data "2020-0301" doesn't match format "%Y%m%d", at position 0. You might want to try:
- passing `format` if your strings have a consistent format;
- passing `format='ISO8601'` if your strings are all ISO8601 but not necessarily in exactly the same format;
- passing `format='mixed'`, and the format will be inferred for each element individually. You might want to use `dayfirst` alongside this.
HINT: Explain options for cleaning the dataset to the user.
Also, evaluate what went wrong and consider options for how to fix it. Break the execution up into cell blocks.Let me fix the date formatting issue and try again:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[13], line 5
3 cumulative_returns = (1 + ff5[['Mkt-RF', 'SMB', 'HML', 'RMW', 'CMA']]/100).cumprod()
4 for column in cumulative_returns.columns:
----> 5 plt.plot(cumulative_returns.index, cumulative_returns[column], label=column)
6 plt.title('Cumulative Returns of Fama-French 5 Factors')
7 plt.xlabel('Date')
File /opt/conda/lib/python3.11/site-packages/matplotlib/pyplot.py:3578, in plot(scalex, scaley, data, *args, **kwargs)
3570 @_copy_docstring_and_deprecators(Axes.plot)
3571 def plot(
3572 *args: float | ArrayLike | str,
(...)
3576 **kwargs,
3577 ) -> list[Line2D]:
-> 3578 return gca().plot(
3579 *args,
3580 scalex=scalex,
3581 scaley=scaley,
3582 **({"data": data} if data is not None else {}),
3583 **kwargs,
3584 )
File /opt/conda/lib/python3.11/site-packages/matplotlib/axes/_axes.py:1723, in Axes.plot(self, scalex, scaley, data, *args, **kwargs)
1721 lines = [*self._get_lines(self, *args, data=data, **kwargs)]
1722 for line in lines:
-> 1723 self.add_line(line)
1724 if scalex:
1725 self._request_autoscale_view("x")
File /opt/conda/lib/python3.11/site-packages/matplotlib/axes/_base.py:2309, in _AxesBase.add_line(self, line)
2306 if line.get_clip_path() is None:
2307 line.set_clip_path(self.patch)
-> 2309 self._update_line_limits(line)
2310 if not line.get_label():
2311 line.set_label(f'_child{len(self._children)}')
File /opt/conda/lib/python3.11/site-packages/matplotlib/axes/_base.py:2332, in _AxesBase._update_line_limits(self, line)
2328 def _update_line_limits(self, line):
2329 """
2330 Figures out the data limit of the given line, updating self.dataLim.
2331 """
-> 2332 path = line.get_path()
2333 if path.vertices.size == 0:
2334 return
File /opt/conda/lib/python3.11/site-packages/matplotlib/lines.py:1032, in Line2D.get_path(self)
1030 """Return the `~matplotlib.path.Path` associated with this line."""
1031 if self._invalidy or self._invalidx:
-> 1032 self.recache()
1033 return self._path
File /opt/conda/lib/python3.11/site-packages/matplotlib/lines.py:669, in Line2D.recache(self, always)
667 if always or self._invalidx:
668 xconv = self.convert_xunits(self._xorig)
--> 669 x = _to_unmasked_float_array(xconv).ravel()
670 else:
671 x = self._x
File /opt/conda/lib/python3.11/site-packages/matplotlib/cbook.py:1345, in _to_unmasked_float_array(x)
1343 return np.ma.asarray(x, float).filled(np.nan)
1344 else:
-> 1345 return np.asarray(x, float)
TypeError: float() argument must be a string or a real number, not 'Period'
Also, evaluate what went wrong and consider options for how to fix it. Break the execution up into cell blocks.The Fama-French 5 factors data shows:
First few rows of Fama-French 5 Factors:
| Mkt-RF | SMB | HML | RMW | CMA | RF | |
|---|---|---|---|---|---|---|
| Date | ||||||
| 2020-03 | -13.39 | -8.24 | -13.88 | -1.56 | 1.26 | 0.13 |
| 2020-04 | 13.65 | 2.56 | -1.34 | 2.73 | -1.03 | 0.0 |
| 2020-05 | 5.58 | 1.99 | -4.85 | 0.93 | -3.24 | 0.01 |
| 2020-06 | 2.46 | 1.97 | -2.23 | 0.13 | 0.53 | 0.01 |
| 2020-07 | 5.77 | -3.18 | -1.44 | 0.4 | 1.03 | 0.01 |
Data range:
Start date: 2020-03
End date: 2024-12
Summary statistics:
58 | 58 | 58 | 58 | 58 | 58 |
1.2367241379 | -0.0846551724 | 0.1575862069 | 0.6967241379 | 0.1289655172 | 0.2001724138 |
5.4028275209 | 3.5189213935 | 4.6660762698 | 2.6545739328 | 3.0050984102 | 0.1964106991 |
-13.39 | -8.24 | -13.88 | -4.79 | -7.2 | 0 |
-2.365 | -2.565 | -2.41 | -1.49 | -1.74 | 0.01 |
2.1 | -0.405 | -0.305 | 0.75 | -0.41 | 0.16 |
4.81 | 1.8675 | 3.0375 | 2.3825 | 1.6375 | 0.4 |
13.65 | 8.28 | 12.8 | 7.27 | 7.74 | 0.48 |
The cumulative returns for each factor are shown here:
The data includes the five Fama-French factors:
The data covers the period from March 2020 to December 2024, with monthly observations.
Here's the complete Fama-French 5 factors dataset:
Data range:
Start date: 1963-07
End date: 2024-12
First few rows:
-0.39 | -0.41 | -0.97 | 0.68 | -1.18 | 0.27 |
5.07 | -0.8 | 1.8 | 0.36 | -0.35 | 0.25 |
-1.57 | -0.52 | 0.13 | -0.71 | 0.29 | 0.27 |
2.53 | -1.39 | -0.1 | 2.8 | -2.01 | 0.29 |
-0.85 | -0.88 | 1.75 | -0.51 | 2.24 | 0.27 |
Summary statistics:
738 | 738 | 738 | 738 | 738 | 738 |
0.5832926829 | 0.1971273713 | 0.2774796748 | 0.2849457995 | 0.2566260163 | 0.3637804878 |
4.4797868206 | 3.0498955451 | 2.9948184362 | 2.2162787543 | 2.0718758007 | 0.2634958398 |
-23.24 | -15.32 | -13.88 | -18.65 | -7.2 | 0 |
-1.97 | -1.5675 | -1.415 | -0.8025 | -1.02 | 0.15 |
0.97 | 0.07 | 0.195 | 0.255 | 0.09 | 0.38 |
3.4375 | 2.005 | 1.73 | 1.3175 | 1.47 | 0.4975 |
16.1 | 18.28 | 12.8 | 13.07 | 9.07 | 1.35 |
Here's the cumulative performance of each factor:
The complete dataset starts from July 1963 and shows that:
The cumulative return plot shows the market factor has had the strongest long-term performance, while the other factors have provided more modest but generally positive returns over the full period.
Here are the Fama-French factor means and their statistical significance:
Fama-French 5 Factor Means (July 1963 - Present):
0.583 | 0.165 | 7 | 1.979 |
0.197 | 0.112 | 2.366 | 1.347 |
0.277 | 0.11 | 3.33 | 1.323 |
0.285 | 0.082 | 3.419 | 0.979 |
0.257 | 0.076 | 3.08 | 0.915 |
0.364 | 0.01 | 4.365 | 0.116 |
Statistical Significance Tests:
3.537 | 0 |
1.756 | 0.08 |
2.517 | 0.012 |
3.493 | 0.001 |
3.365 | 0.001 |
37.505 | 0 |
Key findings from July 1963 to present:
The risk-free rate (RF) averaged 4.37% annually over this period. All factors except SMB show strong statistical significance at conventional levels (p < 0.05).
The merged dataset contains 479 monthly observations from February 1985 to December 2024. Here's the analysis:
Merged dataset shape: (479, 7)
Date range:
Start: 1985-02
End: 2024-12
First few rows:
-14.6550386466 | 1.22 | 1.04 | -0.1 | 1.29 | 1.06 | 0.58 |
-10.6062934468 | -0.84 | -1.39 | 4.07 | 1.23 | 2.88 | 0.62 |
-3.9545692309 | -0.96 | -0.1 | 3.72 | 1.55 | 0.71 | 0.72 |
-18.2352606039 | 5.09 | -2.31 | -0.96 | 1.33 | -1.49 | 0.66 |
3.5969581246 | 1.27 | 0.35 | 0.38 | 1.76 | -0.77 | 0.55 |
Summary statistics:
479 | 479 | 479 | 479 | 479 | 479 | 479 |
2.3992817744 | 0.7491649269 | 0.0246137787 | 0.1452400835 | 0.3911064718 | 0.2142797495 | 0.260480167 |
12.1230379572 | 4.5011469635 | 3.002278808 | 3.1543134269 | 2.4721799003 | 2.1188308563 | 0.2104895149 |
-57.7436456785 | -23.24 | -15.32 | -13.88 | -18.65 | -7.2 | 0 |
-4.4745221598 | -1.93 | -1.825 | -1.625 | -0.76 | -1.035 | 0.02 |
2.605825297 | 1.19 | -0.06 | -0.03 | 0.39 | 0.01 | 0.26 |
9.662849074 | 3.545 | 1.82 | 1.66 | 1.45 | 1.395 | 0.43 |
45.3782503416 | 13.65 | 18.28 | 12.8 | 13.07 | 9.07 | 0.79 |
Here's the correlation matrix between AAPL returns and the Fama-French factors:
AAPL's excess returns (return minus risk-free rate):
APPL Excess Return Summary:
479 |
2.1388016074 |
12.1334073034 |
-58.2536456785 |
-4.7860787128 |
2.3847923629 |
9.4852132255 |
44.8382503416 |
Key observations:
We've now merged and analyzed the AAPL returns with the Fama-French factors, showing both the raw correlations and excess returns since 1985.
What would you like to explore next? Some options:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[5], line 20
18 import pandas_datareader.data as web
19 ff5 = web.DataReader('F-F_Research_Data_5_Factors_2x3', 'famafrench', start='1900-01-01')[0]
---> 20 ff5.index = ff5.index.to_period('M').astype(str)
22 # Merge the datasets
23 merged_df = pd.merge(aapl_df[['AAPL_Return']], ff5,
24 left_index=True, right_index=True,
25 how='inner')
AttributeError: 'PeriodIndex' object has no attribute 'to_period'
Also, evaluate what went wrong and consider options for how to fix it. Break the execution up into cell blocks.Here are the results of the Fama-French 5-factor regression for Apple's excess returns:
Fama-French 5-Factor Regression Results for AAPL Excess Returns
=============================================================
Sample Period: 1985-02 to 2024-12
Number of monthly observations: 479
Coefficients and Statistical Significance:
1.4418 | 0.4968 | 2.9022 | 0.0037 |
1.1225 | 0.1258 | 8.9234 | 0 |
0.3248 | 0.1637 | 1.9842 | 0.0472 |
-0.6358 | 0.1952 | -3.2573 | 0.0011 |
0.2463 | 0.1728 | 1.4257 | 0.1539 |
-0.7277 | 0.3429 | -2.1224 | 0.0338 |
Regression Statistics:
R-squared: 0.3037
Adjusted R-squared: 0.2964
F-statistic: 38.7724
Prob (F-statistic): 0.0000
Actual vs. Fitted Values Plot:
Key findings from the regression:
The model explains about 30% of the variation in AAPL's excess returns (R-squared = 0.30). The significant F-statistic (p < 0.001) indicates that the factors jointly explain AAPL's returns. The positive alpha (constant) of 1.44% monthly is statistically significant, suggesting AAPL has delivered excess returns beyond what the five factors explain.
Here's the breakdown of AAPL's expected excess returns based on the Fama-French factors:
Factor Contributions to Expected Excess Returns:
==============================================
1.1225 | 0.5833 | 0.6548 |
0.3248 | 0.1971 | 0.064 |
-0.6358 | 0.2775 | -0.1764 |
0.2463 | 0.2849 | 0.0702 |
-0.7277 | 0.2566 | -0.1867 |
Total factor contribution (monthly %): 0.4258
Alpha (monthly %): 1.4418
Total expected excess return (monthly %): 1.8676
Annualized Returns:
==================
Factor contribution: 5.11%
Alpha: 17.30%
Total expected excess return: 22.41%
Key observations:
Market factor contributes the most to expected returns (0.65% monthly) due to both:
Size and Profitability factors add small positive contributions:
Value and Investment factors contribute negatively:
The total factor contribution is about 0.43% monthly (5.11% annually)
Alpha is substantial at 1.44% monthly (17.30% annually)
The total expected excess return is 1.87% monthly or 22.41% annually
This suggests that while AAPL's exposure to systematic factors explains some of its performance, a significant portion (the alpha) comes from company-specific factors not captured by the Fama-French model.