Fama-French三因子火锅策略

Fama-French三因子火锅策略

import statsmodels.api as sm
from statsmodels import regression
import numpy as np
import pandas as pd
import time 
from datetime import date
from jqdata import *


'''
================================================================================
总体回测前
================================================================================
'''
#总体回测前要做的事情
def initialize(context):
    set_params()        #1设置策参数
    set_variables()     #2设置中间变量
    set_backtest()      #3设置回测条件
    
#1
#设置策参数
def set_params():
    g.tc=15  # 调仓频率
    g.yb=63  # 样本长度
    g.N=15   # 持仓数目
    g.NoF=3  # 三因子模型还是五因子模型
    
#2
#设置中间变量
def set_variables():
    g.t=0               #记录连续回测天数
    g.rf=0.04           #无风险利率
    g.if_trade=False    #当天是否交易
    
    #将2005-01-04至今所有交易日弄成列表输出
    today=date.today()     #取当日时间xxxx-xx-xx
    a=get_all_trade_days() #取所有交易日:[datetime.date(2005, 1, 4)到datetime.date(2016, 12, 30)]
    g.ATD=['']*len(a)      #获得len(a)维的单位向量
    for i in range(0,len(a)):
        g.ATD[i]=a[i].isoformat() #转换所有交易日为iso格式:2005-01-04到2016-12-30
        #列表会取到2016-12-30,现在需要将大于今天的列表全部砍掉
        if today<=a[i]:
            break
    g.ATD=g.ATD[:i]        #iso格式的交易日:2005-01-04至今
    
#3
#设置回测条件
def set_backtest():
    set_option('use_real_price', True) #用真实价格交易
    log.set_level('order', 'error')
    set_slippage(FixedSlippage(0))     #将滑点设置为0




'''
================================================================================
每天开盘前
================================================================================
'''
#每天开盘前要做的事情
def before_trading_start(context):
    if g.t%g.tc==0:
        #每g.tc天,交易一次行
        g.if_trade=True 
        # 设置手续费与手续费
        set_slip_fee(context) 
        # 设置可行股票池:获得当前开盘的沪深300股票池并剔除当前或者计算样本期间停牌的股票
        g.all_stocks = set_feasible_stocks(get_index_stocks('000300.XSHG'),g.yb,context)
    g.t+=1

#4 根据不同的时间段设置滑点与手续费
def set_slip_fee(context):
    # 将滑点设置为0
    set_slippage(FixedSlippage(0)) 
    # 根据不同的时间段设置手续费
    dt=context.current_dt
    log.info(type(context.current_dt))
    
    if dt>datetime.datetime(2013,1, 1):
        set_commission(PerTrade(buy_cost=0.0003, sell_cost=0.0013, min_cost=5)) 
        
    elif dt>datetime.datetime(2011,1, 1):
        set_commission(PerTrade(buy_cost=0.001, sell_cost=0.002, min_cost=5))
            
    elif dt>datetime.datetime(2009,1, 1):
        set_commission(PerTrade(buy_cost=0.002, sell_cost=0.003, min_cost=5))
                
    else:
        set_commission(PerTrade(buy_cost=0.003, sell_cost=0.004, min_cost=5))


#5
# 设置可行股票池:
# 过滤掉当日停牌的股票,且筛选出前days天未停牌股票
# 输入:stock_list-list类型,样本天数days-int类型,context(见API)
# 输出:颗星股票池-list类型
def set_feasible_stocks(stock_list,days,context):
    # 得到是否停牌信息的dataframe,停牌的1,未停牌得0
    suspened_info_df = get_price(list(stock_list), start_date=context.current_dt, end_date=context.current_dt, frequency='daily', fields='paused')['paused'].T
    # 过滤停牌股票 返回dataframe
    unsuspened_index = suspened_info_df.iloc[:,0]<1
    # 得到当日未停牌股票的代码list:
    unsuspened_stocks = suspened_info_df[unsuspened_index].index
    # 进一步,筛选出前days天未曾停牌的股票list:
    feasible_stocks=[]
    current_data=get_current_data()
    for stock in unsuspened_stocks:
        if sum(attribute_history(stock, days, unit='1d',fields=('paused'),skip_paused=False))[0]==0:
            feasible_stocks.append(stock)
    return feasible_stocks




'''
================================================================================
每天交易时
================================================================================
'''

#每天交易时要做的事情
def handle_data(context, data):
    if g.if_trade==True:
        # 获得调仓日的日期字符串
        todayStr=str(context.current_dt)[0:10]#去掉时分秒,保留年月日
        # 计算每个股票的ai
        ais=FF(g.all_stocks,getDay(todayStr,-g.yb),getDay(todayStr,-1),g.rf)
        # 为每个持仓股票分配资金
        g.everyStock=context.portfolio.portfolio_value/g.N
        # 依打分排序,当前需要持仓的股票
        try:
            stock_sort=ais.sort('score')['code']
        except AttributeError:
            stock_sort=ais.sort_values('score')['code']
        order_stock_sell(context,data,stock_sort)
                
        order_stock_buy(context,data,stock_sort)       
                
    g.if_trade=False


#6
#获得卖出信号,并执行卖出操作
#输入:context, data,已排序股票列表stock_sort-list类型
#输出:none
def order_stock_sell(context,data,stock_sort):
    # 对于不需要持仓的股票,全仓卖出
    for stock in context.portfolio.positions:
        #除去排名前g.N个股票(选股!)
        if stock not in stock_sort[:g.N]:
            stock_sell = stock
            order_target_value(stock_sell, 0)


#7
#获得买入信号,并执行买入操作
#输入:context, data,已排序股票列表stock_sort-list类型
#输出:none
def order_stock_buy(context,data,stock_sort):
    # 对于需要持仓的股票,按分配到的份额买入
    for stock in stock_sort:
        stock_buy = stock
        order_target_value(stock_buy, g.everyStock)


#8
#按照Fama-French规则计算k个参数并且回归,计算出股票的alpha并且输出
#输入:stocks-list类型; begin,end为“yyyy-mm-dd”类型字符串,rf为无风险收益率-double类型
#输出:最后的打分-dataframe类型
def FF (stocks,begin,end,rf):
    LoS=len(stocks)
    #查询三因子/五因子的语句
    q = query(
        valuation.code,
        valuation.market_cap,
        (balance.total_owner_equities/valuation.market_cap/100000000.0).label("BTM"),
        indicator.roe,
        balance.total_assets.label("Inv")
    ).filter(
        valuation.code.in_(stocks)
    )
    
    df = get_fundamentals(q,begin)
    
    #计算5因子再投资率的时候需要跟一年前的数据比较,所以单独取出计算
    ldf=get_fundamentals(q,getDay(begin,-252))
    # 若前一年的数据不存在,则暂且认为Inv=0
    if len(ldf)==0:
        ldf=df
    df["Inv"]=np.log(df["Inv"]/ldf["Inv"])
    
    
    # 选出特征股票组合
    try:
        S=df.sort('market_cap')['code'][:LoS/3]
        B=df.sort('market_cap')['code'][LoS-LoS/3:]
        L=df.sort('BTM')['code'][:LoS/3]
        H=df.sort('BTM')['code'][LoS-LoS/3:]
        W=df.sort('roe')['code'][:LoS/3]
        R=df.sort('roe')['code'][LoS-LoS/3:]
        C=df.sort('Inv')['code'][:LoS/3]
        A=df.sort('Inv')['code'][LoS-LoS/3:]
    except AttributeError:
        S=df.sort_values('market_cap')['code'][:int(LoS/3)]
        B=df.sort_values('market_cap')['code'][LoS-int(LoS/3):]
        L=df.sort_values('BTM')['code'][:int(LoS/3)]
        H=df.sort_values('BTM')['code'][LoS-int(LoS/3):]
        W=df.sort_values('roe')['code'][:int(LoS/3)]
        R=df.sort_values('roe')['code'][LoS-int(LoS/3):]
        C=df.sort_values('Inv')['code'][:int(LoS/3)]
        A=df.sort_values('Inv')['code'][LoS-int(LoS/3):]
    
    # 获得样本期间的股票价格并计算日收益率
    df2 = get_price(stocks,begin,end,'1d')
    df3=df2['close'][:]
    df4=np.diff(np.log(df3),axis=0)+0*df3[1:]
    #求因子的值
    SMB=sum(df4[S].T)/len(S)-sum(df4[B].T)/len(B)
    HMI=sum(df4[H].T)/len(H)-sum(df4[L].T)/len(L)
    RMW=sum(df4[R].T)/len(R)-sum(df4[W].T)/len(W)
    CMA=sum(df4[C].T)/len(C)-sum(df4[A].T)/len(A)
    
    #用沪深300作为大盘基准
    dp=get_price('000300.XSHG',begin,end,'1d')['close']
    RM=diff(np.log(dp))-rf/252
    
    #将因子们计算好并且放好
    X=pd.DataFrame({"RM":RM,"SMB":SMB,"HMI":HMI,"RMW":RMW,"CMA":CMA})
    #取前g.NoF个因子为策略因子
    factor_flag=["RM","SMB","HMI","RMW","CMA"][:g.NoF]
    print (factor_flag)
    X=X[factor_flag]
    
    # 对样本数据进行线性回归并计算ai
    t_scores=[0.0]*LoS
    for i in range(LoS):
        t_stock=stocks[i]
        sample=pd.DataFrame()
        t_r=linreg(X,df4[t_stock]-rf/252,len(factor_flag))
        t_scores[i]=t_r[0]
    
    #这个scores就是alpha 
    scores=pd.DataFrame({'code':stocks,'score':t_scores})
    return scores


#9
# 辅助线性回归的函数
# 输入:X:回归自变量 Y:回归因变量 完美支持list,array,DataFrame等三种数据类型
#      columns:X有多少列,整数输入,不输入默认是3()
# 输出:参数估计结果-list类型
def linreg(X,Y,columns=3):
    X=sm.add_constant(array(X))
    Y=array(Y)
    if len(Y)>1:
        results = regression.linear_model.OLS(Y, X).fit()
        return results.params
    else:
        return [float("nan")]*(columns+1)


#10
# 日期计算之获得某个日期之前或者之后dt个交易日的日期
# 输入:precent-当前日期-字符串(如2016-01-01)
#      dt-整数,如果要获得之前的日期,写负数,获得之后的日期,写正数
# 输出:字符串(如2016-01-01)
def getDay(precent,dt):
    for i in range(0,len(g.ATD)):
        if precent<=g.ATD[i]:
            t_temp = i
            if t_temp+dt>=0:
                return g.ATD[t_temp+dt]#present偏移dt天后的日期
            else:
                t= datetime.datetime.strptime(g.ATD[0],'%Y-%m-%d')+datetime.timedelta(days = dt)
                t_str=datetime.datetime.strftime(t,'%Y-%m-%d')
                return t_str




'''
================================================================================
每天收盘后
================================================================================
'''
#每天收盘后要做的事情
def after_trading_end(context):
    return
# 进行长运算(本模型中不需要)
  • 11
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
PythonFama-French因子模型是一种用于资产定价的统计模型,通过分析影响资产收益率变动的因素,帮助投资者解释股票回报的来源。 Fama-French因子模型的主要因子包括市场因子、规模因子和价值因子。市场因子表示市场收益率的变动对股票回报的影响。规模因子是指公司市值的大小对股票回报的影响,较小规模的公司通常具有较高的回报。价值因子表明公司股票的价值相对于其基本面指标的水平,价格较低的公司往往有更高的回报。 在Python中,可以使用pandas和numpy等库来处理数据和进行统计分析。首先,需要从数据库或财经网站上获取相关股票的历史收益率数据和市值数据。然后,使用pandas将数据导入Python,并进行数据清理和预处理。 接下来,利用线性回归模型,使用statsmodels库来拟合因子模型。将股票的收益率作为因变量,市场因子、规模因子和价值因子作为自变量。运用OLS方法拟合模型,得出各因子的回归系数和截距项。 最后,通过分析回归系数和截距项的显著性,可以判断各因子对股票回报的影响程度。如果回归系数显著不为零,说明该因子对股票回报有显著影响。通过调整各因子的权重,可以对股票组合进行定价和优化,帮助投资者选择合适的投资策略。 总之,Python提供了丰富的数据处理和统计分析工具,可以方便地应用Fama-French因子模型进行资产定价和投资决策。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI量化小木屋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值