最近研究了一下这个利用财报基本面数据寻找alpha的策略,发现还是有比较高的信息率的,在没有任何对冲的情况下,也能在不同的时期有所表现,且整体强于基准,不过在查看代码实现的时候,发现了以下几个问题,对代码进行了相应的修改,算是bug fix吧,希望对大家有参考价值。
潜在问题如下:
1)基本面数据不同lag没变化的问题,用LD来动态往前找基本面数据。每次循环得到一个新的LD,因为他是本次pubdata的前一天,用这个日期去query会查到前一次的财务数据,以此往前推。
for i in stocks: # 获得前一个交易日的日期(因为当前交易日你是不知道当前收盘价的)D=getDay(dateStr,-1)LD=D #LD用来拿上一次的财报alpha = [] # 一个循环,对每一个财务季度进行获取因子for j in range(0,lags): # 查询财务因子的语句q = query(indicator,valuation).filter(indicator.code.in_([i])) # f_temp=get_fundamentals(q,D)f_temp=get_fundamentals(q,LD) #这里原来的code有个问题,D在循环里没变化,所以改用LDrow_name=i '_lag' str(j) #先判定是否上市,如果上市,才将其装入if len(f_temp)>0: #如果能取到数据,说明已经上市f_temp=f_temp[cols2]f_temp.index=[row_name]table_factors.ix[[row_name],cols2]=f_temp #得到本期财报的披露日期向前推一个日期:LD=getDay(table_factors['pubDate'][row_name],-1)r=getAlpha(i,LD,D,g.rf)[0]table_factors.ix[[row_name],['alpha']]=rtable_factors=table_factors.dropna() #把没有alpha的值都drop了# print(table_factors)return table_factors
2)基本面数据标准化处理。修改方法是使用sklearn的标准化函数来对factor进行标准化处理。原因是线性回归这种函数是通过计算和优化残差距离来拟合的,如果factor不在一个scale范围内,可能会使有些factor对结果的影响大,有些小,另外标准化也会加快算法的收敛速度,有利于数值计算。
# 判断是否调仓日,如果是调仓日,则进行调仓操作if g.if_trade: # 获得今天的日期的字符串todayStr=str(context.current_dt)[0:10]print(todayStr) # 获得当前股票对应季度的对应财务因子数据,数据类型为dataframefactors_table=getDS(g.all_stocks,todayStr,g.lag) # 计算回归系数 # factor没有归一化可能会有点问题。。)scaler = StandardScaler()factors_table_new = scaler.fit_transform(factors_table[g.factors]) # print(factors_table)weights=linreg(factors_table_new,factors_table[['alpha']])# 获得当前的因子current_factors=getOriginalFactors(g.factors,todayStr) # print(type(current_factors)) #这里也要对因子做归一化处理 # current_factors_new = scaler.transform(current_factors)current_factors_new = pd.DataFrame(scaler.transform(current_factors),index=current_factors.index, columns=current_factors.columns)print((current_factors_new)) # 计算调仓信号toBuy = get_signal(context,current_factors_new, weights)print(toBuy) # 执行调仓buy_and_sell(context, toBuy) # 调仓指令归于“否”g.if_trade = False
3)调整factor,选择相对比率,去除绝对数值的factor,比如eps和adjusted_profit。因为绝对数值的scale太大,和公司体量有关,不能说大的就一定好,比较并不公平。相对比率则比较客观,比如roe高的企业,优秀的概率则高一些。
修改如下:
g.factors=[#'eps', #'adjusted_profit', #数字最大,也不是相对数值去掉,试试看! 'inc_return', 'roa', 'roe', 'net_profit_margin', 'gross_profit_margin', 'operation_profit_to_total_revenue', 'ga_expense_to_total_revenue', 'goods_sale_and_service_to_revenue', 'inc_total_revenue_year_on_year', 'inc_total_revenue_annual', 'inc_operation_profit_year_on_year', 'inc_operation_profit_annual']
4)股票购买数量由10调整到g.N,原来默认的20其实没有作用。原文代码里用10控制最大的选股数,但是本意是20个最多,所以改了下控制参数,就可以了。资金量大的时候可以更加分散化。
NoB=0for i in range(0,g.N):if points[i]>0:
NoB =1#if NoB>10 #为什么只买10只股票,还是只考虑10个point为正的股票,改成用g.N控制个数if NoB>g.N:
NoB = g.N
toBuy=array(points.index[0:NoB])return toBuy
改进空间:
如果要做成真正的量化策略,还可以进一步调整和加入以下部分:
1)解决财报日期和交易日期发布不匹配,LD和D之间时间长度不稳定的问题。我把调仓时间改成21天了,所以给策略一个自己纠正的机会,一定程度缓解了这个问题。不过应该有更好的改进方法。
2)股指期货对冲。可变成纯alpha策略,毕竟策略考虑的是alpha。
3)风险控制。策略波动率比基准还大,明显有改进空间。
4)仓位控制。牛市涨的还行,熊市跌的也快,如果加入仓位控制,应该会更好一些。