导语:提起资本资产定价模型,大家可能都有学习过,那么本篇内容主要向大家讲述资本资
产定价模型的实际应用,我们将其构建成量化策略,并在历史行情中回测。
一、策略阐述
CAPM 模型公式
夏普发现单个股票或者股票组合的预期回报率的公式如下:E[ri]=rf+β(E[rM]−rf) 。
其中,rf 是无风险回报率,β 是证券的Beta 系数,E[rM]是市场期望回报率,E[rM]−rf
是市场溢价。
超额收益
当一只股票的价格被正确定价的时候,它的风险溢价与市场组合的风险溢价是呈现完全
的线性关系,也就是线性回归后应该得到一条斜率为 β,截距为 rf 的拟合直线。
实际上,每只股票的收益率与市场组合的收益率进行回归的时候截距都不会等于rf ,而
这个偏差值 α 的计算公式为:α=(E[ri]−rf)−β(E[rM]−rf)
按照CAPM 模型,当正确定价时,股票的α 值应该为零,所有α 值不为零的股票我们都
可以认为被错误的定价了。被错误定价的股票可以分为两类
1.α<0,此时股票的价格被低估了
2.α>0 ,此时股票的价格被高估了
假设CAPM 理论是成立的,这些错误定价的股票都会最终回到得到正确的定价,那么
选股时应该买入α 较低的股票。
CAPM 模型实际应用
第一步:我们选取创业板指数成分股为股票池,由于创业板为风险较大板块,因此错误
定价的发生概率和偏离度会更大。
第二步:获取股票池内个股与创业板指数的过去 60 个交易 日的涨跌幅,进行线性回归,
计算出每一个股票的 alpha 值。
第三步:按alpha 从小到大排序,选取前10 只股票作为持仓,持股一个月。
以下为策略实现的基本信息:
策略实现难度:3
实现过程中所需要用到的API 函数,ps:通过 MindGo 量化交易平台 API 文档快速掌握:
需要用到的API 函数 功能
handle_data() 定时运行函数,按交易 日或按分钟运行
set_benchmark() 设置基准指数
----------------------- Page 111-----------------------
二、代码示意图
三、编写释义
本篇内容的关键点是将个股的 alpha 通过线性回归计算获取,以下是作者实现过程中的
笔记,建议初学者到研究环境中使用,尤其是线性回归模板。
from scipy import stats
#=============== 以创业板为股票池,除去 ST 和停牌=====
def stock(date):
stk=list(get_index_stocks('399006.SZ',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
#=========选股函数===========
date='20180125'
stock_list=stock(date)
ret=get_price(stock_list, None, date, '1d', ['quote_rate'], False, 'pre', 60, is_panel=1)['quote_rate']
ret=(ret/100-0.04/252)*100
ret_jz=get_price('000300.SH', None, date, '1d', ['quote_rate'], False, 'pre', 60, is_panel=1)
['quote_rate']
ret['ret_jz']=(ret_jz/100-0.04/252)*100
#进行线性回归
y=list(ret['ret_jz'])
df=pd.DataFrame(columns=['alpha'])
for i in ret.columns:
x = list(ret[i])
beta, stockalpha, r_value, p_value, slope_std_error=stats.linregress(y, x)
df.loc[i]=stockalpha
----------------------- Page 112-----------------------
df=pd.DataFrame(df).sort_values(by='alpha',ascending=True)
df['syboml']=df.index
df[df['syboml']!='ret_jz']
df
运行结果:
----------------------- Page 113-----------------------
四、最终结果
策略回测区间:2014.01.01-2018.01.31
回测资金:1000000
回测频率:日级
回测结果:红色曲线为策略收益率曲线,蓝色曲线为对应的基准指数收益率曲线
策略源代码:
import pandas as pd
import numpy as np
from scipy import stats
#================================初始化函数=============================
def initialize(account):
g.n = 10 #设置最大持仓数量为 20 只股票
g.longday = 60 #设置数据长度
run_monthly(trade,date_rule=1) #将trade 交易函数设置为定时运行:每个月第一个交易 日
account.stock = []#储存上期的股票池
g.settock='399006.SZ'
set_benchmark(g.settock)
#================================主函数 (选股+交易)=====================
def trade(account, data):
#执行选股函数:CAPM(),并将结果导入,该股票列表是需要买入的个股。
needstock_list = CAPM()
#获取上期持仓个股
holdstock_list = list(account.stock)
#确定本期需要卖出的个股
sell_list = list(set(holdstock_list)-set(needstock_list))
#执行卖出操作,运用for 循环,逐个操作。
for s in sell_list:
order_target(s,0)
----------------------- Page 114-----------------------
#确定本期需要买入的个股,其余即为继续持仓的个股
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_target_percent(stock,0.1)
#操作完毕,将选股结果放到上期股票池储存变量中,以备下次使用。
account.stock = frozenset(needstock_list)
#=========选股函数===========
def CAPM():
date=get_last_datetime().strftime('%Y%m%d')
stock_list=stock(date)
ret=get_price(stock_list, None, date, '1d', ['quote_rate'], False, 'pre', g.longday, is_panel=1)
['quote_rate']
ret=(ret/100-0.04/252)*100
ret_jz=get_price('000300.SH', None, date, '1d', ['quote_rate'], False, 'pre', g.longday, is_panel=1)
['quote_rate']
ret['ret_jz']=(ret_jz/100-0.04/252)*100
#进行线性回归
y=list(ret['ret_jz'])
df=pd.DataFrame(columns=['alpha'])
for i in ret.columns:
x = list(ret[i])
beta, stockalpha, r_value, p_value, slope_std_error=stats.linregress(y, x)
df.loc[i]=stockalpha
df=pd.DataFrame(df).sort_values(by='alpha',ascending=True)
df['syboml']=df.index
df=df[df['syboml']!='ret_jz']
#获取股票的代码
needstock_list=[]
for s in range(0,g.n,1):
needstock_list.append(df.iloc[s]['syboml'])
return needstock_list
#=============== 以沪深300 为股票池,除去 ST 和停牌=====
def stock(date):
stk=list(get_index_stocks(g.settock,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
----------------------- Page 115-----------------------