本篇笔记参考中金22年七月最新的研报《量化多因子系列(7):价量因子手册》,将单纯从因子的基本定义以及构造方法和测试结果结论出发以得到可供近期直接参考的投资洞察,以及在最后展示了因子的自定义实现以及自己在聚宽上研究时无意观察到的一个波动率因子的风格切换现象。所有价量因子的测试都遵循十组月频全市场的IC和分组回测检验。
动量和反转因子
动量&反转因子通常由过去一段时间的特定类型的涨跌幅构造,其因子收益一方面可能来源于非理性投资者的行为偏差造成的错误定价,另一方面也可能来源于承担特定风险获得的风险补偿。构造如下:
动量和反转因子
结果显示,在全市场内,业绩公告日前隔夜动量因子的表现较好,mmp_report_overnight 的平均 IC为2.9%,平均 ICIR 为 0.73。报告期动量因子 mmt_report_period 表现较好,年化超额收益率可达 9.1%。而月度反转因子中,mmt_intraday_M 因子的 IC 均值为-6.8%,ICIR 为-0.77。效果最好的是 mmt_range_M 即振幅调整动量(注:振幅的定义为当日最高价-最低价的绝对值/前日收盘价),全市场的 IC 均值为-6.8%,ICIR 为-1.03。
波动率因子
波动率因子刻画了股票价格或者股票收益在过去一段时间的不确定性程度,一般高波动预示着未来更弱的收益,即存在反转效应。基于传统的收益波动和振幅改进得到的 39 个波动率因子:
波动率因子
结果显示,上行波动率因子的有效性优于传统波动率因子,且短周期波动率因子效果普遍好于长周期。此外,振幅的标准差因子 vol_highlow_std_3M 在全市场的有效性较好。
流动性因子
流动性刻画股票交易所需要的时间和成本,一般流动性差的个股有更高的预期收益,这可以解释为对流动性风险的流动性补偿。高频的流动性因子可以通过价差宽度,价格深度来刻画,而中低频的流动性因子则主要为价格弹性或者换手率。基于换手率和价格弹性构造 21 个因子如下:
波动率因子
实证结果显示,换手率标准差因子 liq_turn_std_1M 在全市场范围内的表现最好,IC 均值达到-9.3%,ICIR达到-1.06。此外,六个月的换手率标准差因子在中证 1000 的股票池内表现也很好。
量价相关因子
量价相关性刻画股票换手率和价格或者收益率背离,同向程度。该类因子的有效性可以从信息扩散角度解释:信息扩散初期应该是量能领先于价格,此时量的变化主要由小部分理性交易者主导;信息扩散中期,量价基本同步;而到信息扩散末期,更多成交可能来源于“羊群效应”而非理性行为。这里所构造的量价相关性因子主要分为:价格相关因子,量能领先因子,量价同步因子。
量价因子
实证分析显示,corr_ret_turned_1M(即量价同步因子)在全市场的表现最好,IC 均值达到-6.1%,ICIR达到 1.07,此外换手率与价格相关性因子(量价同步)也有不错的选股效果。
筹码分布因子
目前主流的金融终端都会提供查看筹码价格分布的功能,其主要思路均是选取过去一段时间的量价信息,从最开始的成交量、成交价,结合其中的换手率考虑衰减函数,得出当前时间点的一个筹码价格分布。假定 T-10 日的股票 VWAP 为 P(T-10),成交量为 V(T-10),根据每日的换手率设定一个衰减函数p(T)。选取过去一段时间,计算过去每一天的量衰减到当前的情况即可以得到完整的筹码价格分布。据此构建了筹码分布的均值、标准差、偏度、峰度等因子,以及不同盈亏水平的筹码占比因子如下:
筹码分布因子
值得注意的是筹码占比因子具有更多的反转意味:该日的盈利筹码占比越高,未来收益表现越弱。且筹码分布刻画相关(均值方差偏度峰度)因子表现显著弱于筹码收益占比因子。Distribution_ret_avg 全市场的 IC 值达到了-4.1%。
资金流向因子
资金流因子采用的核心思想是捕捉“聪明资金”的流向,采取跟随策略,期望获得基本面改善带来的超额收益。可以基于 Wind 的日度资金流向构建如下因子:
资金流向因子
有意思的结论是,大单的买入因子具有显著的正向预测能力,而小单的主动买入因子具有负向的预测效果。这可以解释为大单更多代表理性的机构观点而小单则代表相对非理性的观点。而开盘的资金流向因子有效性又强于尾盘资金流向因子,因此尤其可以关注开盘大单买入因子。
北向资金流因子
北向资金流因子主要刻画陆股通的持仓偏好情况,该类因子的有效性必须建立在陆股通通道的投资者多为理性投资者的基础上。构造的九个“北向资金流因子”如下:
北向资金流因子
可以发现北向资金因子大致分为持仓占比因子和持仓变化因子。在全市场中,构造逻辑最为简单的北向持仓占比因子 north_hold_prop 的选股性能最好,IC 均值为 3.5%,ICIR 为 0.6。
融资融券因子
关于融资融券:融资买空业务即金融机构给证券公司或者投资者给予的资金融出供其进行股票交易,一般出现在投资者看多某只股票;而融券业务即保证金卖空交易,即投资者在看空证券行情背景下,持有少量可融券公司的少量股票或没有改公司股票的情况下,同时向证券公司或者相关金融机构借入该看沽公司的证券并在证券市场上抛售的信用交易业务。由此构造的因子如下:
融资融券因子
结果显示,融资融券在全市场范围内单调性欠佳,且有效性一般。从预测的方向性来看,不论是融资因子还是融券因子,都表现为负向的预测效果。
总结
综合前文的实证结果,选取选股效果较好的因子,通过等权的方式合成了 12 个复合因子:
价量复合因子
研报中所给出的结论如下:
- 价量复合因子大多分组单调性较好,但部分因子的预测能力主要由空头部分贡献,多头组合超额收益相对较低。
- 沪深 300 范围内推荐关注量价相关性因子、北向持仓变化因子。
- 中小市值范围内则推荐关注换手率因子、反转因子。
我选取了构造逻辑相对比较简单的liq_turn_std_6M 因子在聚宽上实现并进行单因子回测,其他因子可以通过类似的方法自定义因子类,测试策略代码如下:
from jqdata import *
from jqfactor import Factor, calc_factors
import datetime
import numpy as np
import pandas as pd
def initialize(context):
set_benchmark('000905.XSHG')
set_params() #strategy parameters
set_variables() #trading variable
set_backtest() #setting commisions,filter feasible stocks
def set_params():
g.num_stock = 50 #stock selected by multifactor ranking
g.frequency = 20 #back adjusted period
g.Factor = [liq_turn_std_6M()] #factors we use
def set_variables():
g.t = 0
g.if_trade = False
def set_backtest():
set_option('use_real_price', True)
log.set_level('order', 'error') #recording the error
def before_trading_start(context):
if g.t%g.frequency==0: #judge if it is
g.if_trade=True
g.all_stocks = get_index_stocks('000905.XSHG')
g.t+=1
def handle_data(context, data):
if g.if_trade==True:
universe = g.all_stocks #原始的股票池
factor_values = get_factor_values(context, g.Factor, universe)
momentum = factor_values['lts6m'].rank(ascending = False)#big->small
final_factor = 1.* momentum #rank-based aggregation
try:
stock_list = list(final_factor.sort_values(ascending=False)[:g.num_stock].index)
except:
stock_list = list(final_factor.order(ascending=False)[:g.num_stock].index)
rebalance(context,data,stock_list)
g.if_trade=False
def get_factor_values(context,factor_list, universe):
factor_name = list(factor.name for factor in factor_list)
values = calc_factors(universe,
factor_list,
context.previous_date,
context.previous_date) #起始日和结束日都取调仓一天
factor_dict = {i:values.iloc[0] for i in factor_name}
return factor_dict
def rebalance(context,data,toBuy):
current_holding=context.portfolio.positions.keys() #现有的持仓列表
stocks_to_sell = list(set(current_holding) - set(toBuy))
bulk_orders(stocks_to_sell, 0) #全部卖出
total_value = context.portfolio.total_value
bulk_orders(toBuy, total_value/g.num_stock) #买入剩下的股票
def bulk_orders(stock_list,target_value): #批量调仓函数
for i in stock_list:
order_target_value(i, target_value)
class liq_turn_std_6M(Factor):
name = 'lts6m'
max_window = 120
dependencies = ['close','circulating_market_cap','volume'] #每日收盘价,每日流通市值,每日成交量
def calc(self,data):
volume = data['volume']
num_shares = data['circulating_market_cap']/data['close']*10000*10000
turn_rate = volume/num_shares #换手率序列
return turn_rate.std()*100因子类定义见代码最后一段,通过当日流通市值和收盘价计算当日流通股本从而计算当日换手率。股票池取中证 500 成分股,换仓日为 20 日,取因子值排名最后 50 名的股票(因为存在反转效应)构建等权组合,从 2010年到 2022-07-01 回测,结果如下:
可见该因子的年化收益为 10.05%,年化超额收益在 7%左右。值得注意的是超额收益曲线前八年平稳上升,随后却出现了较大回撤,近几年波动较大,应注意因子有效性衰减的可能性。
番外:传统波动率因子的风格切换
我在此前测试一些传统因子的时候所萌生的想法,传统的波动率因子是否在不同的市场牛熊持续风格下有不同的表现?于是我做了如下的测试:
首先定义传统波动率因子:个股过去二十个交易日的收盘价标准差
观点:在中长期牛市状态做多波动率,中长期熊市状态做空波动率。
实证:从过去四年 2018-2021 内取一系列持续时间大于等于一个月的上行/下行市场区间,对上行区间做多波动率,下行区间做空波动率,统计区间内策略的超额收益情况。选股域为中证 500 成分股,取四分位的头部等权组合作为策略组合,基准取中证 500,换仓频率为十天或者二十天。回测结果如下:
令我惊喜的是,这个简单的策略在所有的市场上行/下行区间都取得了超额收益,且近两年保持了很高的有效性,暗示着波动率因子在不同的市场状态也或许存在反转效应。此外,在市场下行状态,波动率因子带来的超额收益较弱,而上行区间则都得到了较为不错的超额收益,因此如果对未来数月内市场的反弹或者趋势性复苏有充分把握,可以考虑做多波动率因子。当然这个只是初步的现象和猜想,对于为何会存在该反转现象我并未做深入研究。 |
|