【三阶 Day-3 陈芊璇】量化知识补充

https://guorn.com/

果仁网~   大家不妨打开看看

这次主要想看看计算机在量化里面的应用,然后白天自己补充补充

如下:

量化选股-多因子模型

总体分为基本面选股、市场行为选股。基本面选股包括:多因子模型,风格轮动模型,行业轮动模型。市场行为选股包括:资金流选股,动量反转模型,一致预期模型,趋势追踪模型和筹码选股。

今天要讲的是多因子模型

多因子选股模型是广泛应用的一种方法。采用一系列的因子作为选股标准,满足则买入,不满足则卖出。不同的市场时期总有一些因子在发挥作用,该模型相对来说比较稳定。

模型的优点是可以综合很多信息后给出一个选股结果。选取的因子不同以及如何综合各个因子得到最终判断的方法不同会产生不同的模型。一般来说,综合因子的方法有打分法和回归法两种,打分法较为常见。

模型构建实例

选取09-15年做样本期,进行因子检验。

benchmark = 000001.XSHG

一.备选因子选取

根据市场经验和经济逻辑选取。选择更多和更有效的因子能增强模型信息捕获能力。 如一些基本面指标(PB、PE、EPS、增长率),技术面指标(动量、换手率、波动),或其他指标(预期收益增长、分析师一致预期变化、宏观经济变量)。

结合JQ能提供的数据,具体选取以下三个方面的因子:

(1)估值:账面市值比(B/M)、盈利收益率(EPS)、动态市盈(PEG)

(2)成长性:ROE、ROA、主营毛利率(GP/R)、净利率(P/R)

(3)资本结构:资产负债(L/A)、固定资产比例(FAP)、流通市值(CMV)

下面就上述10个因子的有效性进行验证。

二.因子有效性检验

采用排序的方法检验备选因子的有效性。

对任一个因子,从第一个月月初计算市场每只股票该因子的大小,从小到大对样本股票池排序,平均分为n个组合,一直持有到月末。每月初用同样的方法调整股票池。运用一定样本时期的数据来建立模型。

0.导入所需库

In [1]:

import pandas as pdfrom pandas import Series, DataFrameimport numpy as npimport statsmodels.api as smimport scipy.stats as scsimport matplotlib.pyplot as plt

1.每月初取所有因子数值(以2015-01-01为例)

(1)估值:账面市值比(B/M)、盈利收益率(EPS)、动态市盈(PEG)

(2)成长性:ROE、ROA、主营毛利率(GP/R)、净利率(P/R)

(3)资本结构:资产负债(L/A)、固定资产比例(FAP)、流通市值(CMV)

In [2]:

factors = ['B/M','EPS','PEG','ROE','ROA','GP/R','P/R','L/A','FAP','CMV']#月初取出因子数值def get_factors(fdate,factors):    stock_set = get_index_stocks('000001.XSHG',fdate)    q = query(        valuation.code,        balance.total_owner_equities/valuation.market_cap/100000000,        income.basic_eps,        valuation.pe_ratio,        income.net_profit/balance.total_owner_equities,        income.net_profit/balance.total_assets,        income.total_profit/income.operating_revenue,        income.net_profit/income.operating_revenue,        balance.total_liability/balance.total_assets,        balance.fixed_assets/balance.total_assets,        valuation.circulating_market_cap        ).filter(        valuation.code.in_(stock_set),        valuation.circulating_market_cap    )    fdf = get_fundamentals(q, date=fdate)    fdf.index = fdf['code']    fdf.columns = ['code'] + factors    return fdf.iloc[:,-10:]fdf = get_factors('2015-01-01',factors)fdf.head()

Out[2]:

2.对每个因子按大小排序(以'B/M'为例)

In [3]:

score = fdf['B/M'].order()score.head()

Out[3]:

code600301.XSHG  -0.045989600444.XSHG  -0.029723600228.XSHG  -0.026231600217.XSHG  -0.026090600876.XSHG  -0.010862Name: B/M, dtype: float64

股票池中股票数目

In [4]:

len(score)

Out[4]:

966

3.按分值将股票池五等分构造组合port1-5

In [5]:

startdate = '2015-01-01'enddate = '2015-02-01'nextdate = '2015-03-01'df = {}CMV = fdf['CMV']port1 = list(score.index)[: len(score)/5]port2 = list(score.index)[ len(score)/5: 2*len(score)/5]port3 = list(score.index)[ 2*len(score)/5: -2*len(score)/5]port4 = list(score.index)[ -2*len(score)/5: -len(score)/5]port5 = list(score.index)[ -len(score)/5: ]

Out[5]:

15066.599999999999

4.函数-计算组合月收益(按流通市值加权)

In [6]:

defcaculate_port_monthly_return(port,startdate,enddate,nextdate,CMV):close1=get_price(port,startdate,enddate,'daily',['close'])close2=get_price(port,enddate,nextdate,'daily',['close'])weighted_m_return=((close2['close'].ix[0,:]/close1['close'].ix[0,:]-1)*CMV).sum()/(CMV.ix[port].sum())returnweighted_m_returncaculate_port_monthly_return(port1,'2015-01-01','2015-02-01','2015-03-01',fdf['CMV'])

Out[6]:

0.042660461430416276

5.函数-计算benchmark月收益

In [7]:

def caculate_benchmark_monthly_return(startdate,enddate,nextdate):    close1 = get_price(['000001.XSHG'],startdate,enddate,'daily',['close'])['close']    close2 = get_price(['000001.XSHG'],enddate, nextdate, 'daily',['close'])['close']    benchmark_return = (close2.ix[0,:]/close1.ix[0,:]-1).sum()    return benchmark_returncaculate_benchmark_monthly_return('2015-01-01','2015-02-01','2015-03-01')

Out[7]:

-0.06632375461831419

6.观察5个组合在2015-01-01日构建起一个月内的收益情况

In [8]:

benchmark_return = caculate_benchmark_monthly_return(startdate,enddate,nextdate)df['port1'] =  caculate_port_monthly_return(port1,startdate,enddate,nextdate,CMV)df['port2'] = caculate_port_monthly_return(port2,startdate,enddate,nextdate,CMV)df['port3'] = caculate_port_monthly_return(port3,startdate,enddate,nextdate,CMV)df['port4'] = caculate_port_monthly_return(port4,startdate,enddate,nextdate,CMV)df['port5'] = caculate_port_monthly_return(port5,startdate,enddate,nextdate,CMV)print Series(df)print 'benchmark_return %s'%benchmark_return

Out[8]:

port1    0.042660port2  -0.047200port3    0.012783port4  -0.063027port5  -0.117817dtype: float64benchmark_return -0.0663237546183

7.构建因子组合并计算每月换仓时不同组合的月收益率

数据范围:2009-2015共7年

得到结果monthly_return为panel数据,储存所有因子,在7×12个月内5个组合及benchmark的月收益率

In [9]:

factors = ['B/M','EPS','PEG','ROE','ROA','GP/R','P/R','L/A','FAP','CMV']#因为研究模块取fundmental数据默认date为研究日期的前一天。所以要自备时间序列。按月取year = ['2009','2010','2011','2012','2013','2014','2015']month = ['01','02','03','04','05','06','07','08','09','10','11','12']result = {}for i in range(7*12):    startdate = year[i/12] + '-' + month[i%12] + '-01'    try:        enddate = year[(i+1)/12] + '-' + month[(i+1)%12] + '-01'    except IndexError:        enddate = '2016-01-01'    try:        nextdate = year[(i+2)/12] + '-' + month[(i+2)%12] + '-01'    except IndexError:        if enddate == '2016-01-01':            nextdate = '2016-02-01'        else:            nextdate = '2016-01-01'    print 'time %s'%startdate    fdf = get_factors(startdate,factors)    CMV = fdf['CMV']    #5个组合,10个因子    df = DataFrame(np.zeros(6*10).reshape(6,10),index = ['port1','port2','port3','port4','port5','benchmark'],columns = factors)    for fac in factors:        score = fdf[fac].order()        port1 = list(score.index)[: len(score)/5]        port2 = list(score.index)[ len(score)/5+1: 2*len(score)/5]        port3 = list(score.index)[ 2*len(score)/5+1: -2*len(score)/5]        port4 = list(score.index)[ -2*len(score)/5+1: -len(score)/5]        port5 = list(score.index)[ -len(score)/5+1: ]        df.ix['port1',fac] = caculate_port_monthly_return(port1,startdate,enddate,nextdate,CMV)        df.ix['port2',fac] = caculate_port_monthly_return(port2,startdate,enddate,nextdate,CMV)        df.ix['port3',fac] = caculate_port_monthly_return(port3,startdate,enddate,nextdate,CMV)        df.ix['port4',fac] = caculate_port_monthly_return(port4,startdate,enddate,nextdate,CMV)        df.ix['port5',fac] = caculate_port_monthly_return(port5,startdate,enddate,nextdate,CMV)        df.ix['benchmark',fac] = caculate_benchmark_monthly_return(startdate,enddate,nextdate)        print 'factor %s'%fac    result[i+1]=dfmonthly_return = pd.Panel(result)

8.取某个因子的5个组合收益情况('L/A'为例)

In [10]:

monthly_return[:,:,'L/A']

Out [10]:

In [11]:

(monthly_return[:,:,'L/A'].T+1).cumprod().tail()

Out [11]:

9.因子检验量化指标

模型建立后,计算n个组合的年化复合收益、超额收益、不同市场情况下高收益组合跑赢benchmark和低收益组合跑输benchmark的概率。

检验有效性的量化标准:

(1)序列1-n的组合,年化复合收益应满足一定排序关系,即组合因子大小与收益具有较大相关关系。假定序列i的组合年化收益为Xi,则Xi与i的相关性绝对值Abs(Corr(Xi,i))>MinCorr。此处MinCorr为给定的最小相关阀值。

(2)序列1和n表示的两个极端组合超额收益分别为AR1、ARn。MinARtop、MinARbottom表示最小超额收益阀值。

if AR1 > ARn #因子越小,收益越大

则应满足AR1 > MinARtop >0 and ARn < MinARbottom < 0

if AR1 < ARn #因子越小,收益越大

则应满足ARn > MinARtop >0 and AR1 < MinARbottom < 0

以上条件保证因子最大和最小的两个组合,一个明显跑赢市场,一个明显跑输市场。

(3) 在任何市场行情下,1和n两个极端组合,都以较高概率跑赢or跑输市场。

以上三个条件,可以选出过去一段时间有较好选股能力的因子。

In [12]:

total_return = {}annual_return = {}excess_return = {}win_prob = {}loss_prob = {}effect_test = {}MinCorr = 0.3Minbottom = -0.05Mintop = 0.05for fac in factors:    effect_test[fac] = {}    monthly = monthly_return[:,:,fac]    total_return[fac] = (monthly+1).T.cumprod().iloc[-1,:]-1    annual_return[fac] = (total_return[fac]+1)**(1./6)-1    excess_return[fac] = annual_return[fac]- annual_return[fac][-1]    #判断因子有效性    #1.年化收益与组合序列的相关性 大于 阀值    effect_test[fac][1] = annual_return[fac][0:5].corr(Series([1,2,3,4,5],index = annual_return[fac][0:5].index))    #2.高收益组合跑赢概率    #因子小,收益小,port1是输家组合,port5是赢家组合    if total_return[fac][0] < total_return[fac][-2]:        loss_excess = monthly.iloc[0,:]-monthly.iloc[-1,:]        loss_prob[fac] = loss_excess[loss_excess<0].count()/float(len(loss_excess))        win_excess = monthly.iloc[-2,:]-monthly.iloc[-1,:]        win_prob[fac] = win_excess[win_excess>0].count()/float(len(win_excess))                effect_test[fac][3] = [win_prob[fac],loss_prob[fac]]                #超额收益        effect_test[fac][2] = [excess_return[fac][-2]*100,excess_return[fac][0]*100]                #因子小,收益大,port1是赢家组合,port5是输家组合    else:        loss_excess = monthly.iloc[-2,:]-monthly.iloc[-1,:]        loss_prob[fac] = loss_excess[loss_excess<0].count()/float(len(loss_excess))        win_excess = monthly.iloc[0,:]-monthly.iloc[-1,:]        win_prob[fac] = win_excess[win_excess>0].count()/float(len(win_excess))                effect_test[fac][3] = [win_prob[fac],loss_prob[fac]]                #超额收益        effect_test[fac][2] = [excess_return[fac][0]*100,excess_return[fac][-2]*100]#effect_test[1]记录因子相关性,>0.5或<-0.5合格#effect_test[2]记录【赢家组合超额收益,输家组合超额收益】#effect_test[3]记录赢家组合跑赢概率和输家组合跑输概率。【>0.5,>0.4】合格(因实际情况,跑输概率暂时不考虑)DataFrame(effect_test)

Out[12]:

检验结果,同时满足上述三个条件的5个有效因子(粗体)

(1)估值:账面市值比(B/M)、盈利收益率(EPS)、动态市盈(PEG)

(2)成长性:ROE、ROA、主营毛利率(GP/R)、净利率(P/R)

(3)资本结构:资产负债(L/A)、固定资产比例(FAP)流通市值(CMV)

其中:CMV,FAP,PEG三个因子越小收益越大;B/M,P/R越大收益越大

(1)有效因子的总收益和年化收益

小市值妖孽!!按CMV因子排序时,CMV小的组合总收益14.6倍,年化58%!总收益第二名是FAP的port2,达到2.71倍。(这也是造成FAP组合收益相关性稍低的原因)

In [13]:

effective_factors = ['B/M','PEG','P/R','FAP','CMV']DataFrame(total_return).ix[:,effective_factors]

Out[13]:

In [14]:

DataFrame(annual_return).ix[:,effective_factors]

Out[14]:

(2)有效因子组合和benchmark收益率展示

In [15]:

def draw_return_picture(df):    plt.figure(figsize =(10,4))    plt.plot((df.T+1).cumprod().ix[:,0], label = 'port1')    plt.plot((df.T+1).cumprod().ix[:,1], label = 'port2')    plt.plot((df.T+1).cumprod().ix[:,2], label = 'port3')    plt.plot((df.T+1).cumprod().ix[:,3], label = 'port4')    plt.plot((df.T+1).cumprod().ix[:,4], label = 'port5')    plt.plot((df.T+1).cumprod().ix[:,5], label = 'benchmark')    plt.xlabel('return of factor %s'%fac)    plt.legend(loc=0)for fac in effective_factors:    draw_return_picture(monthly_return[:,:,fac])

Out [15]:

3.冗余因子的剔除

(仅给出思路,此处因子较少不做这一步)

有些因子,因为内在的逻辑比较相近等原因,选出来的组合在个股构成和收益等方面相关性较高。所以要对这些因子做冗余剔除,保留同类因子中收益最好、区分度最高的因子。具体步骤:

(1)对不同因子的n个组合打分。收益越大分值越大。分值达到好将分值赋给每月该组合内的所有个股。

if AR1 > ARn #因子越小,收益越大

则组合i的分值为(n-i+1)

if AR1 < ARn #因子越小,收益越小

则组合i的分值为i

(2)按月计算个股不同因子得分的相关性矩阵。得到第t月个股的因子得分相关性矩阵Score_Corrt,u,v。u,v为因子序号。

(3)计算样本期内相关性矩阵的平均值。即样本期共m个月,加总矩阵后取1/m。

(4)设定得分相关性阀值MinScoreCorr。只保留与其他因子相关性较小的因子。

4.模型建立和选股

根据选好的有效因子,每月初对市场个股计算因子得分,按一定权重求得所有因子的平均分。如遇因子当月无取值时,按剩下的因子分值求加权平均。通过对个股的加权平均得分进行排序,选择排名靠前的股票交易。

以下代码段等权重对因子分值求和,选出分值最高的股票进行交易。

(1)模型构建

In [16]:

def score_stock(fdate):    #CMV,FAP,PEG三个因子越小收益越大,分值越大,应降序排;B/M,P/R越大收益越大应顺序排    effective_factors = {'B/M':True,'PEG':False,'P/R':True,'FAP':False,'CMV':False}    fdf = get_factors(fdate)    score = {}    for fac,value in effective_factors.items():        score[fac] = fdf[fac].rank(ascending = value,method = 'first')    print DataFrame(score).T.sum().order(ascending = False).head(5)    score_stock = list(DataFrame(score).T.sum().order(ascending = False).index)    return score_stock,fdf['CMV']def get_factors(fdate):    factors = ['B/M','PEG','P/R','FAP','CMV']    stock_set = get_index_stocks('000001.XSHG',fdate)    q = query(        valuation.code,        balance.total_owner_equities/valuation.market_cap/100000000,        valuation.pe_ratio,        income.net_profit/income.operating_revenue,        balance.fixed_assets/balance.total_assets,        valuation.circulating_market_cap        ).filter(        valuation.code.in_(stock_set)    )    fdf = get_fundamentals(q,date = fdate)    fdf.index = fdf['code']    fdf.columns = ['code'] + factors    return fdf.iloc[:,-5:][score_result,CMV] = score_stock('2016-01-01')

Out [16]:

code600382.XSHG    4274600638.XSHG    4224600291.XSHG    4092600791.XSHG    4078600284.XSHG    4031dtype: float64

In [17]:

year = ['2009','2010','2011','2012','2013','2014','2015']month = ['01','02','03','04','05','06','07','08','09','10','11','12']factors = ['B/M','PEG','P/R','FAP','CMV']result = {}for i in range(7*12):    startdate = year[i/12] + '-' + month[i%12] + '-01'    try:        enddate = year[(i+1)/12] + '-' + month[(i+1)%12] + '-01'    except IndexError:        enddate = '2016-01-01'    try:        nextdate = year[(i+2)/12] + '-' + month[(i+2)%12] + '-01'    except IndexError:        if enddate == '2016-01-01':            nextdate = '2016-02-01'        else:            nextdate = '2016-01-01'    print 'time %s'%startdate    #综合5个因子打分后,划分几个组合    df = DataFrame(np.zeros(7),index = ['Top20','port1','port2','port3','port4','port5','benchmark'])    [score,CMV] = score_stock(startdate)    port0 = score[:20]    port1 = score[: len(score)/5]    port2 = score[ len(score)/5+1: 2*len(score)/5]    port3 = score[ 2*len(score)/5+1: -2*len(score)/5]    port4 = score[ -2*len(score)/5+1: -len(score)/5]    port5 = score[ -len(score)/5+1: ]    print len(score)    df.ix['Top20'] = caculate_port_monthly_return(port1,startdate,enddate,nextdate,CMV)    df.ix['port1'] = caculate_port_monthly_return(port1,startdate,enddate,nextdate,CMV)    df.ix['port2'] = caculate_port_monthly_return(port2,startdate,enddate,nextdate,CMV)    df.ix['port3'] = caculate_port_monthly_return(port3,startdate,enddate,nextdate,CMV)    df.ix['port4'] = caculate_port_monthly_return(port4,startdate,enddate,nextdate,CMV)    df.ix['port5'] = caculate_port_monthly_return(port5,startdate,enddate,nextdate,CMV)    df.ix['benchmark'] = caculate_benchmark_monthly_return(startdate,enddate,nextdate)    result[i+1]=dfbacktest_results = pd.DataFrame(result)

(哈哈,此处结果下一次再公布~)

5.不足和改进

随着模型使用人数的增加,有的因子会逐渐失效,也可能出现一些新的因素需要加入到因子库中。同时,各因子的权重设计有进一步改进空间。模型本身需要做持续的再评价,并不断改进来适应市场的变化。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容