导语:在CAPM 模型的基础上,再向大家讲述Fama-French 的三因子模型,并构建策略,实

际应用于 A 股市场。

一、策略阐述

Fama-French 三因子模型由来

Fama 和 French 在研究股票超额收益率时,提出了一个观点:小公司股票、以及具有较

高股权账面-市值比的股票,其历史平均收益率一般会高于 CAPM 模型所预测的收益率。

Fama 和 French 认为:1.市值较小的公司通常规模比较小,公司整体风险更大,需要获

得更高的收益来补偿投资者;2.账面市值比是账面的所有者权益除以市值,简称BM 。BM

效应是指账面市值比效应,指 BM 值较高的公司平均月收益率高于BM 值较低的公司。

关于 BM 效应,国内外学者已做了许多研究。Fama 和 French    (1992)研究了 1963 到

1990 年所有在NYSE 、AMEX 、NASDAQ 上市的股票,发现BM 值最高的组合月均收益率

超过 BM 值最低的组合达1.53%。肖军,徐信忠 (2004 )以1993 年6 月至2001 年6 月沪深

股市A 股股票为样本,计算持有一年、两年、三年的收益率数据,认为BM 效应存在。

模型介绍

顾名思义,Fama-French 三因子模型中包含三个因子:市值因子(SMB)、账面市值比

因子(HML)、市场风险因子(RM) 。三因子模型的本质就是把CAPM 中的未被解释的超额收

益分解掉,将其分解成SMB、BM 、RM 和其他未能解释的因素(a),公式表达即为

Ri=ai+biRM+siE(SMB)+hiE(HML)+εi

其中:

Ri=E(ri−rf) ,指股票i 比起无风险投资的期望超额收益率。

RM=E(rM−rf) ,为市场相对无风险投资的期望超额收益率。

E(SMB)是小市值公司相对大市值公司股票的期望超额收益率。

E(HML)则是高B/M 公司股票比起低B/M 的公司股票的期望超额收益率。

a 即观测值,线性回归的常数项。

εi 是回归残差项。

E(SMB) 的计算方式如下:把市场里面的所有股票按市值排序,随后分成两份:第一份

是大市值股票 (市值前20% 的股票),第二份是小市值股票 (市值后 20% 的股票)。记大市

值股票的平均期望收益率为 E(rS) ,小市值股票的期望收益率为 E(rB) 。那么E(SMB)=E(rS)

−E(rB) 。E(HML) 的定义也类似。

假设三因子模型是正确的,市场风险、市值风险、账面市值比这三类风险能很好地解释

个股的超额收益,那么a 的长期均值应该是 0 。如果短期内对于某个时期的股票,回归得到

a<0,说明这段时间个股收益率偏低。根据有效市场假设,任何非理性的价格最终都会回归

----------------------- Page 116-----------------------

理性,这些短期内收益率偏低的个股,最终都会涨回去。

Fama-French 三因子模型实际应用

考虑到三因子模型主要偏向小市值个股,因此本篇内容创业板指数为基准指数,来向大

家展示三因子模型。

策略交易规则:

A.设置调仓频率,每20 个交易 日进行调仓。

B.设置样本长度,用于线性回归,考虑到模型观测值 a 长期为 0,因此选取了过去 252

个交易 日的样本数据。

C.持仓数量为 a 值最小的20 只股票。

策略选股:

第一步:调仓日对于过去 252 个交易 日的数据进行回归分析,自变量为:

RM 、E(SMB) 、E(HML) ,应变量为 Ri ,每个股票的回归结果取常数项,即为 a 值。

第二步:选出α 最小的20 支股票。

以下为策略实现的基本信息:

策略实现难度:4

实现过程中所需要用到的API 函数,ps:通过 MindGo 量化交易平台 API 文档快速掌握:

需要用到的API 函数     功能

set_benchmark()  设置基准指数

order_value()    按金额下单

get_index_stocks() 获取指数成分股

get_all_trade_days() 获取所有交易日

get_price()      获取历史行情数据

get_candle_stick() 获取股票、指数、基金的历史行情数据,包括分钟级、 日级、

周级、月级、年级数据

get_last_datetime() 获取前一个交易 日或者前一分钟的时间

get_fundamentals() 查询财务数据

account.available_cash 账户当前可用资金

g.               全局变量 g

----------------------- Page 117-----------------------

二、代码示意图

三、编写释义

三因子模型的核心部分是计算因子值和因子线性回归,建议初学者前往研究环境操作。

因子值的计算完毕后需整理成一个DataFrame 格式,以便后续进行因子回归,该部分操

作需要同学们熟悉DataFrame 的常用操作,以下是作者实现三因子模型中,研究环境的代码

草稿,分享给同学们。

以下代码可在MINDGO 研究环境中模仿学习:

import pandas as pd

import numpy as np

from sklearn import linear_model

def stock(date):  # 以沪深300 为股票池,除去 ST 和停牌

stk=list(get_index_stocks('000300.SH',date))

price=get_price(stk, None, date, '1d', ['is_paused', 'is_st'], False, None, 1, is_panel=1)

stopstk=price['is_paused'].iloc[-1]

ststk=price['is_st'].iloc[-1]

startstk=(stopstk[stopstk==0].index)

okstk=(ststk[ststk==0].index)

tradestk=list(set(startstk)&set(okstk))

return tradestk

#==========================获取当天交易日的前N 个交易 日的日期

def tradeday(today,n):

daylist=list(get_all_trade_days().strftime('%Y%m%d'))

----------------------- Page 118-----------------------

calnum = daylist.index(today)#获取今日日期在整个月历中的序

lasttrade=daylist[calnum-n]#去前N 个序的交易 日

return lasttrade

today='20180131'

ldate=tradeday(today,60)

stock_list = stock(ldate)  #获取交易股票列表

stock_num = int(len(stock_list)*0.2)

#查询市值、所有者权益数据

q =

query(valuation.symbol,valuation.market_cap,balance.total_equity).filter(valuation.symbol.in_(stoc

k_list),)

basic = get_fundamentals(q, date = ldate)

basic['B/M']= basic['balance_total_equity']/basic['valuation_market_cap']

ret=get_price(stock_list, None, today, '1d', ['quote_rate'], False,  'pre', 60, is_panel=1)['quote_rate']

ret=ret/100

ret_jz=get_price('000300.SH', None, today, '1d', ['quote_rate'], False,  'pre', 60, is_panel=1)

['quote_rate']

ret_jz=ret_jz/100-0.04/252

df=pd.DataFrame()

df['Rm']=ret_jz

basic.index=basic['valuation_symbol']

del basic['balance_total_equity']

for i in ['valuation_market_cap','B/M']:

basic = pd.DataFrame(basic).sort_values(by =i, ascending=False)

stockmax=list(basic.iloc[:stock_num]['valuation_symbol'])

stockmin=list(basic.iloc[-stock_num:]['valuation_symbol'])

df[i]=np.mean(ret[stockmin].T)-np.mean(ret[stockmax].T)

ret=ret-0.04/252

df.columns=['RM','SMB','HML']

clf = linear_model.LinearRegression()

#数据无法获取导致的,处理后的nan 使其为 0

ret.iloc[:] = ret.iloc[:].fillna(0)

df.iloc[:] = df.iloc[:].fillna(0)

#对三因子进行线性回归

x_list=['RM','SMB','HML']

df2=pd.DataFrame(index=['alpha'])

for i in ret.columns:

y = ret[i].values

x = df[x_list].values

clf.fit(x,y)

df2[i]=clf.coef_[0]

df2=df2.T

df2 = pd.DataFrame(df2).sort_values(by ='alpha', ascending=True)

df2.iloc[:10]

----------------------- Page 119-----------------------

因子表格:(PS :通过可以对照表格格式,运用 DataFrame 对象基本操作来完成它!)

A 、三因子数据

B 、个股Ri 数据

最终结果 a :

----------------------- Page 120-----------------------

四、最终结果

策略回测区间:2014.01.01-2018.01.31

回测资金:1000000

回测频率:日级

回测结果:红色曲线为策略收益率曲线,蓝色曲线为对应的基准指数收益率曲线

策略源代码:

import pandas as pd

import numpy as np

from sklearn import linear_model

#==========================初始化函数================================

def initialize(account):

g.bstk = '399006.SZ' #设置指数

set_benchmark(g.bstk)#设置基准指数

g.day = 0 #记录运行天数

g.tradeday = 20 #调仓频率

g.stock = [] #储存上期的股票池

g.trade = False #是否调仓的开关

g.longday = 252 #样本长度

g.stocknum = 20 #持仓数量

pass

#==========================盘前运行================================

def before_trading_start(account,data):

#判断是否调仓

if g.day%g.tradeday==0:

g.trade=True

else:

g.trade=False

----------------------- Page 121-----------------------

g.day=g.day+1

#===========================盘中交易==============================

def handle_data(account, data):

if g.trade==True:

#获取选股结果

needstock_list = alpha_FF()

#获取上期持仓个股

holdstock_list = list(g.stock)

#确定本期需要卖出的个股

sell_list = list(set(holdstock_list)-set(needstock_list))

#执行卖出操作,运用for 循环,逐个操作。

for s in sell_list:

order_target(s,0)

#确定本期需要买入的个股,其余即为继续持仓的个股

buy_list=[]

for i in needstock_list:

if i in holdstock_list:

pass

else:

buy_list.append(i)

#确定可用资金,平分分配至需买入的个股

n=len(buy_list)

cash=account.available_cash/n

#执行买入操作

for s in range(0,n,1):

stock=list(buy_list)[s]

order_value(stock,cash)

#操作完毕,将选股结果放到上期股票池储存变量中,以备下次使用。

g.stock = frozenset(needstock_list)

else:

pass

#=================获取股票池==================================

def stock(yestoday,today):  #要的是过去交易 日的股票池,但换仓 日不应该停牌

stk2=list(get_index_stocks(g.bstk,yestoday))#过去的

stk=list(get_index_stocks(g.bstk,today))#换仓 日的

#获取换仓 日的股票再换仓 日的ST 和停牌情况

price=get_price(stk, None, today, '1d', ['is_paused', 'is_st'], False, None, 1, is_panel=1)

stopstk=price['is_paused'].iloc[-1]

ststk=price['is_st'].iloc[-1]

startstk=(stopstk[stopstk==0].index)

okstk=(ststk[ststk==0].index)

tradestk=list(set(startstk)&set(okstk)&set(stk2))#符合不停牌非ST 和过去交易 日的沪深300

成分股

return tradestk

#==========================获取当天交易日的前N 个交易 日的日期

----------------------- Page 122-----------------------

def tradeday(today,n):

daylist=list(get_all_trade_days().strftime('%Y%m%d'))

calnum = daylist.index(today)#获取今日日期在整个月历中的序

lasttrade=daylist[calnum-n]#去前N 个序的交易 日

return lasttrade

#==========================alpha 计算函数=========================

def alpha_FF():

today=get_last_datetime().strftime('%Y%m%d')#获取当天日期 格式 YMD

#获取前N 个交易 日的日期

ldate=tradeday(today,g.longday)

stock_list = stock(ldate,today)  #获取交易股票列表

stock_num = int(len(stock_list)*0.2)

#查询市值、所有者权益数据

q =

query(valuation.symbol,valuation.market_cap,balance.total_equity).filter(valuation.symbol.in_(stoc

k_list),)

basic = get_fundamentals(q, date = ldate)

basic['B/M']= basic['balance_total_equity']/basic['valuation_market_cap']

ret=get_price(stock_list, None, today, '1d', ['quote_rate'], False,  'pre', g.longday, is_panel=1)

['quote_rate']

ret=ret/100

ret_jz=get_price(g.bstk, None, today, '1d', ['quote_rate'], False,  'pre', g.longday, is_panel=1)

['quote_rate']

ret_jz=ret_jz/100-0.04/252

df=pd.DataFrame()

df['Rm']=ret_jz

basic.index=basic['valuation_symbol']

del basic['balance_total_equity']

for i in ['valuation_market_cap','B/M']:

basic = pd.DataFrame(basic).sort_values(by =i, ascending=False)

stockmax=list(basic.iloc[:stock_num]['valuation_symbol'])

stockmin=list(basic.iloc[-stock_num:]['valuation_symbol'])

df[i]=np.mean(ret[stockmin].T)-np.mean(ret[stockmax].T)

ret=ret-0.04/252

df.columns=['RM','SMB','HML']

clf = linear_model.LinearRegression()

#数据无法获取导致的,处理后的nan 使其为 0

ret.iloc[:] = ret.iloc[:].fillna(0)

df.iloc[:] = df.iloc[:].fillna(0)

#对三因子进行线性回归

x_list=['RM','SMB','HML']

df2=pd.DataFrame(index=['alpha'])

for i in ret.columns:

y = ret[i].values

x = df[x_list].values

clf.fit(x,y)

df2[i]=clf.coef_[0]

----------------------- Page 123-----------------------

df2=df2.T

df2 = pd.DataFrame(df2).sort_values(by ='alpha', ascending=True)

#获取股票的代码

needstock_list=[]

for s in range(0,g.stocknum,1):

needstock_list.append(list(df2.index)[s])

return needstock_list

----------------------- Page 124-----------------------

第四章:经典量化策略集锦(第九篇:Fama-French 三因子模型应用 )相关推荐

  1. 第四章:经典量化策略集锦(第三篇:网格交易—动态调仓策略)

    导语:市场上股票的价格总是在不断的上下波动,投资者通过低买高卖获利.第三篇将向大 家介绍网格交易,一个动态的调仓策略,有效解决仓位和价位控制. 一.策略阐述 窥探网格交易 "追涨杀跌&quo ...

  2. 第四章:经典量化策略集锦(第五篇:布林强盗,一个霸道的交易系统)

    导语:作为策略锦集第五篇,再向大家介绍一个霸道的交易系统-布林强盗交易系统. 一.布林强盗交易系统阐述 布林强盗交易系统的由来 布林线 (BOLL )是非常著名的一个技术分析指标,其由John Bol ...

  3. 第四章:经典量化策略集锦(第四篇:进军交易系统,从 Dual Thrust 中学“趋势”)

    导语: "趋势"这个字眼,大家肯定不陌生,炒股票的老股民天天口里说着 "趋势为王", 那么今天我们向大家介绍一个趋势跟踪交易系统:Dual Thrust ,其简 ...

  4. python量化:如何利用tushare构造FF三因子模型?

    Python量化:如何利用tushare构造FF三因子模型? FF三因子模型介绍 代码实现 从tushare调取数据 利用数据构建因子 总结 笔者是一枚大二菜狗,最近刚上完学院开的python金融量化 ...

  5. The Little Book of Semaphores 信号量小书 第四章 经典同步问题 4.3 无饿死互斥

    第四章 经典同步问题 4.3 无饿死的互斥 在上一节中,我们讨论了被称之为绝对饥饿的问题,其中一类线程(读者)允许另一类别(写者)挨饿. 在更基本的层面上,我们必须解决线程饿死的问题,即一个线程可能无 ...

  6. 中国式危机公关9加1策略(第四章 情感公关策略)

    第四章 情感公关策略 第一节 情感公关的应用原则 现代心理学研究表明,人的行为是由动机支配的,动机是由需要引起的,需要是以个性心理现象为基础的. 需要,就是人们对某种目标的渴求或欲望,也就是一种心理的 ...

  7. 分享量化交易券商接口的几个经典量化策略

    今天我们来一起看一下量化交易券商接口的几个经典量化策略. 量化交易是指从大量的历史数据中筛选出"大概率事件",然后利用大概率之间制定策略,建立模型. 主要采取数学模型替代主观判断的 ...

  8. 经典量化策略——做市商交易(期货)

    阅读原文

  9. 【量化策略系列基本篇之一】股票动量策略(汇总篇)

    本文持续更新中.最后更新时间:11/3/2019 更新日志(11/3/2019): (1)优化部分表述和排版,修改错误 (2)添加移动平均动量部分内容 (3)修改部分2.1中部分代码错误 (4)增加免 ...

最新文章

  1. 源码资本张宏江:只有算法和技术,那你一定挣不到钱
  2. eBPF内核探测中将任意系统调用转换成事件
  3. 解决@vue/cli 创建项目是安装chromedriver时失败的问题
  4. js 随机1-10随机数_寻找随机的错误-一个真实的故事
  5. 登录mysql一闪而过_解决MySQL 一闪而过的情况
  6. python车牌识别系统开源代码_天津谁做车牌识别系统供应商,伸缩栅栏门_郑州荣锋科技有限公司...
  7. 02 ARM11 时钟初始化后的跑马灯程序
  8. Tcl Tutorial 笔记 ·ubuntu命令行运行tcl 命令
  9. 格式要求_论著的格式要求
  10. “好工作”的最佳标准:坚持理想,顺便赚钱——一个媒体人的心路历程
  11. 作用域和作用域链的理解
  12. 禁止html5手机端双击页面放大的问题
  13. 取MySQL结果集的第一条记录
  14. cc1101初始化c语言程序,STC89C52单片机驱动CC1101无线模块的接收C语言程序
  15. 调教ChatGpt看这一篇文章就够了
  16. Python编程基础之Python语言的基础知识(上)
  17. Linux 内核构建
  18. airpodspro窃听模式_AirPods怎么开启助听模式 实时收听设置方法介绍
  19. 开维控制精灵 Ctrl js 华为手机群发短信
  20. 计算机专业买什么商务本好,单位购买什么笔记本的比较多?各位能不能说来听听?华硕笔记本电脑哪个系列性价好,商务办公用?...

热门文章

  1. 云服务器创建FTP站点
  2. coreldraw x8里线段显示尺寸_CorelDRAW X8制作精准尺寸辅助线的方法
  3. SEO工具:百度收录批量查询工具
  4. Python模块和包-你还在全部写成一坨吗?
  5. Unity中简单粗暴的消息机制(可限制消息调用次数)
  6. macbook466加了两条1333金士顿正常
  7. 0ctf freenote
  8. Win11限制第三方浏览器工具:默认打开需用微软的edge浏览器
  9. Btrfs 与 Ext4 - 功能、优势和劣势
  10. 视觉 SLAM 十四讲 —— 第十三讲 建图