今天要聊的是 Alpha 101 系列中的第 3 个因子,公式是:

Python
Alpha#3 = -1 × correlation(rank(open), rank(volume), 10)

这玩意儿看起来又是个典型的Alpha,各种rank、各种correlation、一堆负号,但我真心觉得它有点东西。虽然你可能第一眼觉得它像是某种工业垃圾因子(其实Alpha 101里不少长这样),但别急,细嚼慢咽一下,它比你想象得聪明。

因子拆解

先拆一拆结构,没那么复杂:

  • rank(open):开盘价做了个横向排名,标准化处理,防止极端值影响。
  • rank(volume):同理,对成交量进行横向排名。
  • correlation(…, 10):过去10天里两个序列之间的皮尔逊相关性。
  • 最后乘个负号:反向思维,低相关越好,或者说负相关越强,分数越高。

所以这个因子的核心想法是:

在过去10天里,开盘价的排名和成交量的排名之间的相关性越低(最好是负相关),这个因子的值就越大。

注意,这里不是说开盘价和成交量的值,而是它们的“相对位置”是否联动。这其实挺有意思的,它在追踪一种“非共振”的状态。

Alpha #3因子在捕捉量价不同步信号

我个人是这么理解这个因子的:它试图去抓住市场中那些价格和量不同步的时刻

当一只股票开盘价很高,但量却排在低位,或者反过来,说明有点“意外”——可能是资金还没反应过来、也可能是有些投资者在悄悄布局。这种状态下,主力还没发力,但价格已经先动了,或者量突然异常,但价格没跟上。

这种错位感,我特别喜欢。它不追求“高量高价”这种大家都看到的机会,而是去找那些还没被定价机制完全消化的信息,这在短线交易中是很宝贵的。

说得再直白一点:

  • 相关性高:说明市场在一个“共识环境”下运作,该涨的涨,该量的量,没啥特别。
  • 相关性低甚至为负:可能是预示某种反转、突发事件、结构性分歧,市场还没定好方向。

这个因子在小市值 + 波动股里比较有戏

坦白说,如果你把这个因子丢到全市场里跑,表现未必惊艳。但当我把它筛选在一些波动大的小票里(特别是那些隔夜容易受消息冲击的品种),它的信号就相当敏感。

比如某只小票在开盘前出个模糊利好,结果开盘价格被抬高了,但市场大部分人还没搞清楚消息真假,成交量没怎么放出来。这时候rank(open)拉高但rank(volume)不跟,correlation就会拉低,Alpha#3 给出一个不错的提示信号。

不过实话实说,这个因子不太适合中长周期,它的“有效信息”窗口很短,甚至1~3天后就没啥用了。你要真拿它去做30日持有期,那是用手榴弹钓鱼——浪费。

一点吐槽

我得说实话,这个因子是典型的“工具性”因子,不太会成为明星选手,但它作为信号融合的一环非常有价值。它没啥想象空间,不像动量类因子或者盈利修复类因子能讲大故事。

它适合混合因子池里补盲区,比如你组合里动量、反转、估值都有了,但就是缺一个状态检测器,它就能提醒你某只票最近的量价关系有点古怪,值得看看。

我会把这个因子定义为一种情绪混乱探测器。它不预测未来,也不评估基本面,它只是说:

过去10天,这票的开盘价和成交量有点不同步,可能有事。

你拿它做主力因子可能不够力,但如果你是一个多因子模型构建者,或者你和我一样喜欢捕捉市场异象,这个因子值得拥有姓名。

Python
import pandas as pd
import numpy as np
from scipy.stats import rankdata

def rank_series(series):
    """对 Series 做横向排名,按时间点逐个 rank"""
    return series.rolling(window=1).apply(lambda x: rankdata(x)[-1], raw=True)

def rolling_corr(series1, series2, window):
    """计算两个序列的滚动相关性"""
    return series1.rolling(window).corr(series2)

def alpha_3(df, window=10):
    """
    Alpha#3 = -1 * correlation(rank(open), rank(volume), 10)
    输入: df 包含 open 和 volume 两列
    输出: 一个新的 Series,表示该因子的值
    """
    # 横向排名
    rank_open = df['open'].rank(axis=0, method='average', pct=True)
    rank_volume = df['volume'].rank(axis=0, method='average', pct=True)
    
    # 滚动相关性
    corr = rolling_corr(rank_open, rank_volume, window)
    
    return -1 * corr

# 示例:构造数据
# 假设你有一个 DataFrame,列名为 open, volume,索引是日期
data = {
    'open': [10, 10.5, 10.7, 10.1, 10.3, 10.6, 10.4, 10.9, 11.0, 11.1, 11.3],
    'volume': [1000, 1100, 900, 950, 980, 1030, 990, 1020, 1005, 1010, 1070],
}
df = pd.DataFrame(data)
df['alpha_3'] = alpha_3(df)

print(df)