Skip to content

Commit 3671e59

Browse files
authored
Merge pull request #227 from QilongChan/feature-FamaFrench-Five-Factors
Fama-French Five-Factor Strategy
2 parents c0e8050 + 3675f27 commit 3671e59

File tree

5 files changed

+115
-0
lines changed

5 files changed

+115
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<p>
2+
The relationship between return and risk has long been a popular topic for research. Investors have been seeking financial models that quantify risk and use it to estimate the expected return on equity. The Fama French five-factor model, improved from the Fama French three-factor model, is one of the most classic models (Fama and French, 2015). In this post, we will discuss this model and develop a stock-picking strategy based on it.
3+
</p>
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<p>
2+
The Fama French five-factor model was proposed in 2014 and is adapted from the Fama French three-factor model (Fama and French, 2015). It builds upon the dividend discount model which states that the value of stocks today is dependent upon future dividends.
3+
Fama and French add two factors, investment and profitability, to the dividend discount model to better capture the relationship between risk and return.
4+
The model is as follows
5+
</p>
6+
\[ R = \alpha + \beta_m MKT + \beta_s SMB + \beta_h HML + \beta_r RMW + \beta_c CMA\]
7+
8+
<p>
9+
where
10+
</p>
11+
<ul>
12+
<li>MKT is the excess return of the market. It is the return on the value-weighted market portfolio.</li>
13+
<li>SMB is the return on a diversified portfolio of small-cap stocks minus the return on a diversified portfolio of big-cap stocks.</li>
14+
<li>HML is the difference between the returns on diversified portfolios of stocks with high and low Book-to-Market ratios.</li>
15+
<li>RMW is the difference between the returns on diversified portfolios of stocks with robust (high and steady) and weak (low) profitability.</li>
16+
<li>CMA is the difference between the returns on diversified portfolios of the stocks of low and high investment firms, which we call conservative and aggressive. Here, low/high investment means reinvestment ratio is low/high.</li>
17+
</ul>
18+
19+
<p>
20+
Taking inspiration from the Fama French five-factor model, we can develop a multi-factor stock selection strategy that focuses on five factors: size, value, quality, profitability, and investment pattern.
21+
</p>
22+
23+
<p>
24+
First, we run a Coarse Selection to drop equities which have no fundamental data or have too low prices. Then we select those with the highest dollar volume.
25+
Note that a useful technique is used here: we can use Universe.Unchanged to remain the same universe when there is no necessary change, which greatly speeds up the backtest.
26+
</p>
27+
28+
<div class="section-example-container">
29+
<pre class="python">
30+
def CoarseSelectionFunction(self, coarse):
31+
'''Drop securities which have no fundamental data or have too low prices.
32+
Select those with highest by dollar volume'''
33+
34+
if self.Time < self.nextLiquidate:
35+
return Universe.Unchanged
36+
37+
selected = sorted([x for x in coarse if x.HasFundamentalData and x.Price > 5],
38+
key=lambda x: x.DollarVolume, reverse=True)
39+
40+
return [x.Symbol for x in selected[:self.num_coarse]]
41+
</pre>
42+
</div>
43+
44+
<p>
45+
Secondly, in Fine Selection, we use the terms TotalEquity, BookValuePerShare, OperationProfitMargin, ROE, and TotalAssetsGrowth to account for the five factors, respectively. We then calculate a custom ranking metric for each stock using these five terms. Our algorithm will go long in the five stocks with the highest scores and short the five stocks with the lowest scores.
46+
</p>
47+
<div class="section-example-container">
48+
<pre class="python">
49+
def FineSelectionFunction(self, fine):
50+
'''Select securities with highest score on Fama French 5 factors'''
51+
52+
# Select stocks with these 5 factors:
53+
# MKT -- Book value per share: Value
54+
# SMB -- TotalEquity: Size
55+
# HML -- Operation profit margin: Quality
56+
# RMW -- ROE: Profitability
57+
# CMA -- TotalAssetsGrowth: Investment Pattern
58+
filtered = [x for x in fine if x.ValuationRatios.BookValuePerShare
59+
and x.FinancialStatements.BalanceSheet.TotalEquity
60+
and x.OperationRatios.OperationMargin.Value
61+
and x.OperationRatios.ROE
62+
and x.OperationRatios.TotalAssetsGrowth]
63+
64+
# Sort by factors
65+
sortedByMkt = sorted(filtered, key=lambda x: x.ValuationRatios.BookValuePerShare, reverse=True)
66+
sortedBySmb = sorted(filtered, key=lambda x: x.FinancialStatements.BalanceSheet.TotalEquity.Value, reverse=True)
67+
sortedByHml = sorted(filtered, key=lambda x: x.OperationRatios.OperationMargin.Value, reverse=True)
68+
sortedByRmw = sorted(filtered, key=lambda x: x.OperationRatios.ROE.Value, reverse=True)
69+
sortedByCma = sorted(filtered, key=lambda x: x.OperationRatios.TotalAssetsGrowth.Value, reverse=False)
70+
71+
stockBySymbol = {}
72+
73+
# Get the rank based on 5 factors for every stock
74+
for index, stock in enumerate(sortedByMkt):
75+
mktRank = self.beta_m * index
76+
smbRank = self.beta_s * sortedBySmb.index(stock)
77+
hmlRank = self.beta_h * sortedByHml.index(stock)
78+
rmwRank = self.beta_r * sortedByRmw.index(stock)
79+
cmaRank = self.beta_c * sortedByCma.index(stock)
80+
avgRank = np.mean([mktRank,smbRank,hmlRank,rmwRank,cmaRank])
81+
stockBySymbol[stock.Symbol] = avgRank
82+
83+
sorted_dict = sorted(stockBySymbol.items(), key = lambda x: x[1], reverse = True)
84+
symbols = [x[0] for x in sorted_dict]
85+
86+
# Pick the stocks with the highest scores to long
87+
self.longSymbols= symbols[:self.num_long]
88+
# Pick the stocks with the lowest scores to short
89+
self.shortSymbols = symbols[-self.num_short:]
90+
91+
return self.longSymbols + self.shortSymbols
92+
</pre>
93+
</div>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<p>
2+
In this example, the portfolio is rebalanced every 30 days and the backtest period runs from Jan 2010 to Aug 2019. You can improve upon this strategy by changing the fundamental factors, the weight of each factor and the rebalance frequency.
3+
</p>
4+
5+
<p>
6+
The Fama French five-factor model provides a scientific way to measure asset pricing. For the five aspects that Fama and French mentioned, we used one possible combination in our backtest. We can see from the results that it achieves an annual rate of return around 5% with a max drawdown of 30% over 8 years.
7+
These factors perhaps cannot capture a sufficient amount of information on the assets' pricing, and therefore, there are still many aspects can be improved (e.g. the weights of factors, a different set of factors for different kinds of equities,etc.) We encourage you to explore and create better algorithms upon this tutorial!
8+
</p>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<div class="qc-embed-frame" style="display: inline-block; position: relative; width: 100%; min-height: 100px; min-width: 300px;">
2+
<div class="qc-embed-dummy" style="padding-top: 56.25%;"></div>
3+
<div class="qc-embed-element" style="position: absolute; top: 0; bottom: 0; left: 0; right: 0;">
4+
<iframe class="qc-embed-backtest" height="100%" width="100%" style="border: 1px solid #ccc; padding: 0; margin: 0;" src="https://www.quantconnect.com/terminal/processCache?request=embedded_backtest_78601a5bd785ca32803c13525c688046.html"></iframe>
5+
</div>
6+
</div>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<ul>
2+
<li>
3+
<a href="https://www.sciencedirect.com/science/article/pii/S0304405X14002323" rel="nofollow">A five-factor asset pricing model</a>
4+
</li>
5+
</ul>

0 commit comments

Comments
 (0)