跳转至


课程  因子投资  机器学习  Python  Poetry  ppw  tools  programming  Numpy  Pandas  pandas  算法  hdbscan  聚类  选股  Algo  minimum  numpy  algo  FFT  模式识别  配对交易  GBDT  LightGBM  XGBoost  statistics  CDF  KS-Test  monte-carlo  VaR  回测  过拟合  algorithms  machine learning  strategy  python  sklearn  pdf  概率  数学  面试题  量化交易  策略分类  风险管理  Info  interview  career  xgboost  PCA  wavelet  时序事件归因  SHAP  Figures  Behavioral Economics  graduate  arma  garch  人物  职场  Quantopian  figure  Banz  金融行业  买方  卖方  story  量化传奇  rsi  zigzag  穹顶压力  因子  ESG  因子策略  投资  策略  pe  ORB  Xgboost  Alligator  Indicator  factor  alpha101  alpha  技术指标  wave  quant  algorithm  pearson  spearman  tushare  因子分析  Alphalens  涨停板  herd-behaviour  momentum  因子评估  review  SMC  聪明钱  trade  history  indicators  zscore  波动率  强化学习  顶背离  freshman  resources  others  AI  DeepSeek  network  量子计算  金融交易  IBM  weekly  LLT  backtest  backtrader  研报  papers  UBL  quantlib  jupyter-notebook  scikit-learn  pypinyin  qmt  xtquant  blog  static-site  duckdb  工具  colors  free resources  barra  world quant  Alpha  openbb  数据  risk-management  llm  prompt  CANSLIM  Augment  arsenal  copilot  vscode  code  量化数据存储  hdf5  h5py  cursor  augment  trae  Jupyter  jupysql  pyarrow  parquet  数据源  quantstats  实盘  clickhouse  notebook  redis  remote-agent  AI-tools  Moonshot  回测,研报,tushare 

['strategy'] »

Alphalens 因子分析 - 以低换手率因子为例(1)


Table of Content

R50 因子分析是量化研究的基本技能之一。通过因子分析,找出有效的因子,通过相关性去重后,就可以通过机器学习、线性回归等方法把因子组合起来,构成交易策略。

这一篇笔记我们就介绍如何使用 Alphalens 来进行单因子分析。我们使用的因子是低换手率因子。

股谚有一种说法,天量见天价,地量见地价。其背后的原理是,当成交量达到天量时,击鼓传花的游戏将后继乏人,难以持续,因而股价大概率回落。而成交量出现地量时,显示成交极为冷清,被套者无人愿割,因此由于失去做空力量,从而有可能向上反转。


衡量成交量的大小,我们可以有两种方法。

一种是在时间维度上,我们使用成交量是多少天(n)以来的最小值、最大值来表示。n 越大,包含的信息量就越大;另一种是在横截面维度上进行排名,此时需要先对成交量进行对齐,对齐的方式,就是将成交量转换成换手率。

Info

换手率是一定时间内,市场中标的转手买卖的频率,是反映标的交投活跃程度的指标之一。它的计算公式是 \(\(换手率 = \frac{成交额}{流通股数}\)\)

手工计算需要两方面的数据,一是每日的成交额,另一组是流通股数。流通股数是一个低频变化的数据,也需要进行复权,计算比较复杂。所以,我们一般直接从数据源处获得此数据。

这篇笔记我们仍然使用免费的 akshare 来获取相关数据。不过,考虑到 QMT 量化权限的开通门槛现在已经很低了,只要开通了 QMT,也就相当于免费获得了这些数据,因此后面的笔记就会主要使用 QMT 来获取数据了,只有在某些品种 QMT 还没有提供时,我们才会使用 akshare。

我们的低换手率因子将构建在沪深 300 成份股上。整个构建过程是:


💡 1. 获取沪深 300 成份股代码

💡 2. 获取一段时间内 300 成份股的价格和换手率数据

💡 3. 生成 alphalens 需要的 factor 和 forward returns 数据

💡 4. 进行因子检验分析

Alphalens 能生成的报告非常多(下图为其中之一),所以,第 4 步我们将分几篇笔记详细进行介绍。

一个完整的因子分析流程包括原始数据获取、生成因子、数据(及因子)预处理、因子检验。

预处理包括缺失值统计、中性化、去极值、标准化等操作;因子检验则有 IC 法、分层回测等方法。

因子生成因为涉及到核心算法,需要由研究员自己完成,其它的部分,我们掌握原理之后,就可以借助框架来完成。不过,在本例中,因子生成无须另外构造,我们直接使用换手率就好。


获取数据

我们通过下面的代码来获取沪深 300 成份股代码:

1
2
3
4
5
import akshare as ak

df = ak.index_stock_cons_csindex(symbol="000300")
secs = df["成分券代码"]
secs

我们通过 index_stock_cons_csindex 这个 API 来获取指数成份股,沪深 300 的代码是 000300。

你将会看到 000001, 000002 等输出。akshare 输出的证券代码很多时候是不带交易所(前)后缀的。

Warning

这里实际上已经引入了一个错误。通过 index_stock_cons_csindex 获取的指数成份股,都是最新的数据。而成份股列表实际上是在持续更新的。如果我们在 2023 年的元月获取沪深 300 成份股,结果很可能会与今天获取的不一致。

考虑到某所经常把创新高的标的纳入指数,下跌后的标的剔除指数,所以,因未能使用 PIT 数据,我们实际上上修了该因子的收益率。

接下来我们获取这些标的的收盘价和换手率。在 akshare 中,stock_zh_a_hist 这个 API 的返回值中,同时包含了收盘价和换手率:


1
2
bars = ak.stock_zh_a_hist("000001", adjust="hfq", start_date="20150104")
bars.tail()

返回值有很多列,我们只关心日期、收盘和换手率这三项。这个 API 有一个 adjust 参数,即要求我们选择复权方式。在本例中我们使用 qfq 和 hfq 都没有任何区别,但是不能使用不复权数据。

熟悉了 akshare 的基本 API 之后,我们就正式获取数据,并将它转换成 alphalens 所需要的格式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from typing import List

def prepare_data(secs: List[str], start: str, end: str):
    factors = []
    prices = []

    for sec in secs:
        bars = ak.stock_zh_a_hist(sec, adjust="qfq", start_date=start, end_date=end)
        bars["asset"] = [sec] * len(bars)
        prices.append(bars[["日期", "asset", "收盘"]])

        factors.append(bars[["日期", "asset", "换手率"]])

    # 处理因子表
    factor = pd.concat(factors)
    factor.rename(columns = {"换手率":"factor", "日期":"date"}, inplace=True)
    factor["date"] = pd.to_datetime(factor["date"], utc=True)
    factor.set_index(["date", "asset"], inplace=True)

alphalens 进行分析时,需要的 factor 数据表的格式如下:

75%

关键之处是,它是必须是一个由 date + asset 的双重索引的 DataFrame。而这个 dataframe 应该只有一列,列名字没有规定,但建议使用 factor 这样的列名字,这样更容易理解(bad example here!)。

stock_zh_a_hist 只能返回个股的行情数据。我们要先给它增加 asset 列(其值为个股代码),然后简单地将每支个股的换手率数据拼接成一个大的 dataframe,再进行重命名、设置多重索引就可以了。

这里要注意, alphalens 要注日期时间字段为 timezone-aware 数据,即要设置时区,并且两个表的时间要一致。


akshare 返回的日期字段类型为字符串,所以我们要进行一次转换。但像换手率、收盘价数据则已经是 float64 格式了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
def prepare_data(secs: List[str], start: str, end: str):
    ...
    # 接上一段代码, 处理 PRICES 表格
    prices = pd.concat(prices).pivot(index="日期", columns="asset", values="收盘")

    # 价格表 INDEX 类型转换: STR -> DATE
    prices.index = pd.to_datetime(prices.index, utc=True)

    # 价格表 INDEX 名字必须转换为'DATE'
    prices.rename_axis('date', inplace=True)

    return factor, prices

prices 表格必须转换成以下格式:

格式的要点是,它是以日期为索引,每个 asset 的代码为列的一个 dataframe,每一个 cell 的值则是当天某个 asset 对应的收盘价。

先将各个子 price frame 拼接起来,然后通过 pivot 函数,对表格进行转换,最终就得到上面的格式。这个转换如下图所示:


现在,我们就得到了符合 alphalens 要求的数据。数据准备工作已经完成。

数据预处理

处理缺失值、中性化、标准化等工作,如果有必要,alphalens 将会为我们处理。alphalens 提供了 get_clean_factor_and_forward_returns 这个 API:

1
2
3
4
5
6
7
8
9
from alphalens.utils import get_clean_factor_and_forward_returns

factor_data = get_clean_factor_and_forward_returns(
                                        factor, 
                                        prices, 
                                        bins=None, 
                                        quantiles=10
                                    )
factor_data.tail()

经过预处理后的结果如下:

75%


因子分析有绝对收益法、IC 法和分层法,这在 Alphalens 中是同步处理的 -- 你可以选择只看某个分析结果,但在预处理阶段,我们必须把相应的参数传递进去。这里的参数 bins/quantiles 就是用来分层的。

bins/quantiles 的用法类似于 dataframe 的 cut 或者 hist() 函数中的同名参数。这里我们简单介绍一下。

上例中我们指定了 quantiles=10,它是把每一天的因子数据从小到大排列后,按 len(df)/10 进行切分,即每个分片中,大致落入相等的因子记录(由于缺失值处理等原因,所以不会完全相等)。

我们可以 group 之后,看下结果:

1
factor_data.groupby("factor_quantile").count()

50%

如果我们不使用 quantiles 参数,而是指定 bin 为 10,则它会把区间 [min(factor), max(factor)] 进行均匀划分,即每个 bins 的宽度一样,但落在 bin 中的个数不一样, groupby 之后的结果如下:


50%

两相对照,两个参数的异同不言自明。

!!! faq 本例中,我们将使用 quantiles,为什么? 我们要验证的是地量见地价这句股谚。因此,我们应该选择换手率最低的个股买入。为了平衡风险,我们可能选择 30 支,也就是 10%左右的标的买入。因此,我们按 quantiles 划分才是合理的。

现在,我们就完成了因子分析的前三步,接下来,我们将在下一篇笔记中介绍低换手率因子的结果。

Tip

尽管有无数人使用,但 Alphalens 已经无人维护了。它依赖的 python 库却在不断前行。现在你使用 alphalens 的话,会遇到以下错误:

1
    AttributeError: 'Index' object has no attribute 'get_values'
这个错误是因为pandas更新造成的。

好在 ml4trading 已经接手了这一工作,通过 alphalens-reloaded 来继续维护。如果你要使用这个库的话,也请在 Github 上为这个项目 star 吧。