WorldQuant 101 Alphas及一些思考


(yishui) #1

最近做了一个小活儿,实现并分析WorldQuant 101 Alphas因子。做之前在群里问了一下,QIML公众号的大佬说 WorldQuant 101没什么用,做完之后觉得他说得也有一番道理的,一些公式吊诡而冗杂甚至有些无理取闹,集大成者是#029:

min(product(rank(rank(scale(log(sum(ts_min(rank(rank((-1 * rank(delta((close - 1),5))))), 2), 1))))), 1), 5) + ts_rank(delay((-1 * returns), 6), 5)

还有后60个公式参数基本为保留了6位的小数让人意义不明,比如3.67819天是啥意思呢?很多人处理的时候把那个小数取整,那些小数本来就是做密文用的,取整没意义吧。公布这些因子的人说,其中有80%仍在使用,我认为参数肯定不会是这样原封不动参数不改得用,发出来只是为了表明“这些因子很厉害,你过来商量一下价格吧”。101 Alphas更值得借鉴的是设计因子的方法,有一些公式内部的逻辑确实有点东西的,在这里简单总结一下。

先列举一点或许有点参考价值的因子写法:

**一、价量关系。**就算是新股民也会听说价量的段子,什么最近大盘缩量啊,量在价先啊,高手只看K线和成交量啊啥的。确实,价量是交易最基本的组成要素,无论提到多高的高度不为过。通常把价涨量缩和价跌量升称为背离,剩下两个有人喜欢叫同向,我喜欢叫共振。最简单描述价量关系的指标就是相关系数,101 Alphas中有很多种写法。

  • 背离

  • -1 * correlation(volume, close, N)—成交量与收盘价在N日内的背离程度

  • -1 * correlation(delta(volume, 1), delta(close,1), N)—成交量变动与收盘价日内变动在N日内的背离程度

  • -1 * correlation(rank(delta(volume), 1)), rank(delta(close,1)), N)—成交量变动全市场强度与收盘价日内变动全市场强度在N日内的背离程度

  • -1 * correlation(ts_rank(delta(volume), 1),M), ts_rank(delta(close,1),K), N)—当日成交量变动在M日内的相对强度与当日收盘价日内变动在K日内的相对强度 在N日内的背离程度

  • 共振

  • 上面所有公式去掉-1

  • 契合程度

  • abs(correlation(·))这种写法,我称其为契合程度,在#036和#087中出现

海通证券金工研报《选股因子系列研究(十二)—”量“与”价“的结合》对_correlation(close,turnover,10)_做了一番研究,得出了”…量价相关性在半个月的换仓周期下具有十分好的选股效果并具有显著的Alpha。”这一结论。而且这篇研报也证实了我的一个想法:熊市期间,“…量价相关性选股因子多头收益来源于放量下跌的标的,而空头收益来源于放量上涨的标的…“。当然对于这一结论,101中也有类似因子。

**二、(Alpha#007)趋势末期的放量通常意味趋势结束。**这句本身就存在未来函数,涨或跌了多少算是末期,这个谁也不知道。在#007中是这么表达的

(adv20 < volume) ? (-1 * ts_rank(abs(delta(close, 7)), 60)) * sign(delta(close, 7)) : (-1)

若短期未放量,分值最小等于-1;

若短期放量,且近期上涨,中期涨幅越大,分值越低;

若短期放量,且近期下跌,中期跌幅越大,分值越高。

短期、近期、中期的叫法是参数20、7、60所致。据上文所述研报,在熊市短线上这个因子还是比较合理的,只是我们分辨不清放多大量叫大量罢了,而且参数肯定要调,我猜(5,4,10)。

**三、(Alpha#008)预期的力量。**将 当前价格*同期的收益率 理解为预期未来价格。

delay(sum(open, 5) * sum(returns, 5),10) - sum(open, 5) * sum(returns, 5)

这个公式的意思是 近期涨跌幅比过去预期的大,更有可能反转

我一直认为预期价格也是一种识别”异常变动“的方法,如果称 以过去价格和同期收益率计算的价格 为预期当前价格,如果当前价格上穿预期当前价格,可以认为其会突破/反转,然后像其他CTA策略那样进行交易。

不过我在之前跑过最简单的”突破“,效果还不如布林线突破。但牛熊线也是一种预期,为什么效果会提升不少呢?

**四、(Alpha#018)实体长度的另一种分析方法。**这个指标加点收益率的约束就能描述很多形态。

correlation(close,open,N)

直观解释:N日内实体长度越一致,颜色越相同,值越大。

五、(Alpha#028)影线的玩法。

(high + low)/2 - close

日内冲高<0,日内冲高回落>0,日内探底>0,日内探底回升<0。这是一种老式的感知系忍术,我一直在好奇这种东西会不会被新式的感知系忍术如NN或CNN替代。

如果把“高开低收量”比作图片的通道,我不认为现有的卷积层和池化层可以很好得感知其中的信息,不只是序列需要感知,通道也需要。现在很多文章喜欢把那些指标啥的也作为通道,讲道理我不知道为啥。在CV领域好用必然有其内在逻辑,就算逻辑是个黑盒;现在CNN在这上面不好用(暂不说LSTM和GRU)必然也有其内在逻辑。是不是通道之间也需要卷积和池化?是不是不能再简单得点乘之后相加?通道点乘之后池化会怎么样?用三值矩阵encode通道会不会更好?量在价格通道里必然是异类,怎么处理更好?这些问题一个头两个大。

说回老式感知系忍术,后来我想了一下,有真实波幅ATR,必然有真实涨幅ATU,便试着写了一个,这个公式取算术平均之后会有一些有趣的现象发生。

(CLOSE>OPEN) ?

(2*CLOSE-LOW-HIGH)/CLOSE) :

(HIGH+LOW-2*OPEN)/OPEN)

类似的东西在#060中也有提及,不过和我的逻辑并不相同。(2*close-low-high)/(high-low),由于有一字板所以改成了

(2 * close - low - high) / ( high - low + 0.0001)

光头阳线为1,光脚阴线为-1;阳线时,上影线越短,下影线越长,值越大;阴线时,下影线越短,上影线越长,值越小。

**六、(Alpha#040)波动的新尝试。**通常说的波动都喜欢用收益率的波动std(return,N),但这里却使用了最高价的波动std(high,N),先不说有没有用,这种尝鲜的方式我就很喜欢。

**七、(Alpha#005 #011 #083 #086)成交量加权价格。**通达信公式为AMOUNT/VOL/100,但是不能使用复权数据计算。

vwap - close

就这简简单单的公式我想了好久,我觉得vwap解释为密集成交价还是比较合理的,不过减去close是否可以简单的解释为缩量回落或拉升的程度。直觉觉得这个应该有用,尤其是加了算术平均的情况下。在#066中还有一些花里胡哨的用法如,(2*low - 2*vwap) / (2*open - high - low)

取其精华之后,再来讲讲糟粕或A股适用性

单单就一个涨停板限制就可以把其中大多数alpha的构造逻辑摧毁,对于波动,在中国市场不太适合rank和ts_rank函数。

  • rank是横截面排序,某一股票的alpha在当前时刻下在其他所有股票的alpha中的位置;ts_rank是序列排序,某一股票的当前alpha在某段时间内的alpha中的位置。一个是与其他股票比较,一个是与自己比较,这两个函数都是在描述强弱。
  • 然而作用于涨跌幅的时候涨停板会从中作梗:首先是四舍五入的问题,涨停板的涨幅可以上达10.05%,下达9.95%,明明都是板却非要比个高低。由于磁吸效应的存在,涨停板强度的描述可以利用封板时间比,报成比,换手率等指标衡量。不过说回来,现在的主力真不喜欢拉板,熊市的磁吸效应没那么强而且从监管的角度来讲太危险了。
  • 其次是一字板本身的价量背离问题。对于单一bar来讲,一字板是最严重的价量背离。我不知道大多数人讲的价量关系是不是在流动性充裕的基础上的,当流动性有问题的时候,价量关系的衡量标准和平常必然不同。这个问题可以通过拉长时间区间来缓解,可以用长周期如周线来抵消涨停板带来的异常,也可以针对特点改一下rank与ts_rank。

行业中性

有些公式中需要进行行业中性化,我用的是申万三级行业,主要是方便。中性化代码放在下面,虽然用了向量乘法但效率还是有点慢,等着再改改。

def neu_industry(stock_data,sector_series):
    """
    Wrapper function to neutralize industry.
    a_transform = a / sector_average(a)
    :param sector_series: a pandas Series of which index of stock codes 
                        and sector/industry/subindustry column contains these codes.
    :param stock_data: a pandas DataFrame of which index of date/datetime 
                        and columns are stock codes as the index of sector_series.
    :return: a pandas DataFrame shape like stock_data with industry neutralization.
    """
    #align sector_series to stock_data in case of stock_data's sparse stocks
    inner = sector_series[stock_data.columns]
    stock_data = stock_data.replace([-np.inf,np.inf],0).fillna(value=0)
    trans_matrix = pd.DataFrame()
    for stock in stock_data.columns:
        df = inner==inner[stock]
        df /= df.sum()
        trans_matrix = pd.concat([trans_matrix,df],axis=1,sort=True)
    
    result = pd.DataFrame(np.dot(stock_data.fillna(value=0),trans_matrix.fillna(value=0)),
                          index=stock_data.index,columns=stock_data.columns)    
    return (stock_data / result)

后记

  • 折磨了这么久,越来越觉得“股价预测”这个东西是不存在的,或者说在宏观上不存在,或者说这个方向就不对。微观上玩高频的那些人确实可以说是预测价格,因为拿到了最本质的逻辑,除此之外,预测不存在的。所以为什么说有时候形态甚至更有点用,因为形态内部确实存在逻辑,你觉得会涨就得花钱买股票, 你觉得会跌就得卖股票。可行性高的还是感知+决策,除此之外都是在做学问,而不是在做交易。
  • 历史会重演指的是逻辑会重演。最近亏得有点多,人都变得玄学了:历史确实会重演,但是人不能两次踏入同一条河流—河床是逻辑,水是波动,是噪音,河床不会变,变得是水流的轻重缓急。不过河床是水冲刷出来的,水可以顺着河床流淌,也可以漫过河床,此之谓恐惧与贪婪。
  • 101 Alphas的IC等着有空跑一下,简单测试的时候发现有的因子确实有点东西。