15.9 风险控制建模-Fama-French三因子模型应用

用户头像Reflecti0N
2023-08-07 发布

在CAPM模型的基础上,再向大家讲述Fama-French的三因子模型,并构建策略,实际应用于A股市场。

import pandas as pd 
import numpy as np 
from sklearn import linear_model
# 初始化函数,全局只运行一次
def init(context):
    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

#每日开盘前9:00被调用一次,用于储存自定义参数、全局变量,执行盘前选股等
def before_trading(context):
    #判断是否调仓
    if g.day%g.tradeday==0:
        g.trade=True
    else:
        g.trade=False
    g.day=g.day+1

## 开盘时运行函数
def handle_bar(context, bar_dict):
    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=context.portfolio.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个交易日的日期
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_(stock_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]
    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
收益&风险
源码

评论