原创文章第107篇,专注“个人成长与财富自由、世界运作的逻辑, AI量化投资”。
前面的文章我们把数据,因子定制,自动标注的功能都准备好了,今天继续因子分析,分析的框架当然还是alphalens。
星球有一期研报复现讲的alphalens的使用:
【每周研报复现】AI量化特征工程之alphalens:一套用于分析 alpha 因子的通用工具
qlib因子分析之alphalens源码解读
基于alphalens对qlib的alpha158做单因子分析
alphalens的使用很简单,就是整理两个数据结构,主要是对pandas dataframe的使用要熟悉。
因子数据是以date,code为双索引的dataframe格式:

而收盘价是以日期为索引,而每个资产为单独一列:
准备好的数据之后,调用utils里的get_clear_factor_and_forward_returns:

得到未来N期(默认是1天,5天和10天的收益率),因子值,以及因子分位(factor_quantile,默认从小到大分成5份)

通过调用tears里的create_information_tear_sheet,得到信息分析的结果:

如上就是因子分析里的重要概念IC分析。
什么是IC呢?我们具体可以看alphalens的代码:
iC值等于同一横截面上收益率与因子的spearman相关性(就是我们熟悉的统计学中的相关系数):
stats.spearmanr( factor_data['5D'], factor_data['factor'])
生成IC表格的代码:
def plot_information_table(ic_data):
ic_summary_table = pd.DataFrame()
ic_summary_table["IC Mean"] = ic_data.mean()
ic_summary_table["IC Std."] = ic_data.std()
ic_summary_table["Risk-Adjusted IC"] = \
ic_data.mean() / ic_data.std()
t_stat, p_value = stats.ttest_1samp(ic_data, 0)
ic_summary_table["t-stat(IC)"] = t_stat
ic_summary_table["p-value(IC)"] = p_value
ic_summary_table["IC Skew"] = stats.skew(ic_data)
ic_summary_table["IC Kurtosis"] = stats.kurtosis(ic_data)
print("Information Analysis")
utils.print_table(ic_summary_table.apply(lambda x: x.round(3)).T)
可以看出来,IC值是每个截面的因子值与未来某期收益率的相关系数,然后对这个相关系数序列均值,标准差,偏度,峰度等等。风险调整后的IC就是IC均值/IC标准差。
一般而言,看RIC达到0.05以上认为相关性显著。
IC值越大越好。上面的例子中,20日动量与未来1天,5天及10天都有较显著的相关性,说明这是一个比较有效的因子。
收益分析:

先说后面三列,Top Quantile就是因子值最高的与最低的,两个构建组合。Spread就是两者之间的差。
Alpha与Beta:
这里的y是因子收益率,x是因子值,与IC不同,IC是两者之间的相关系数,而Alpha,Beta是二者之间的线性拟合。
alpha_beta = pd.DataFrame()
for period in returns.columns.values:
x = universe_ret[period].values
y = returns[period].values
x = add_constant(x)
@plotting.customize
def create_returns_tear_sheet(
factor_data, long_short=True, group_neutral=False, by_group=False
):
factor_returns = perf.factor_returns(
factor_data, long_short, group_neutral
)
mean_quant_ret, std_quantile = perf.mean_return_by_quantile(
factor_data,
by_group=False,
demeaned=long_short,
group_adjust=group_neutral,
)
mean_quant_rateret = mean_quant_ret.apply(
utils.rate_of_return, axis=0, base_period=mean_quant_ret.columns[0]
)
mean_quant_ret_bydate, std_quant_daily = perf.mean_return_by_quantile(
factor_data,
by_date=True,
by_group=False,
demeaned=long_short,
group_adjust=group_neutral,
)
mean_quant_rateret_bydate = mean_quant_ret_bydate.apply(
utils.rate_of_return,
axis=0,
base_period=mean_quant_ret_bydate.columns[0],
)
compstd_quant_daily = std_quant_daily.apply(
utils.std_conversion, axis=0, base_period=std_quant_daily.columns[0]
)
alpha_beta = perf.factor_alpha_beta(
factor_data, factor_returns, long_short, group_neutral
)
mean_ret_spread_quant, std_spread_quant = perf.compute_mean_returns_spread(
mean_quant_rateret_bydate,
factor_data["factor_quantile"].max(),
factor_data["factor_quantile"].min(),
std_err=compstd_quant_daily,
)
fr_cols = len(factor_returns.columns)
vertical_sections = 2 + fr_cols * 3
gf = GridFigure(rows=vertical_sections, cols=1)
plotting.plot_returns_table(
alpha_beta, mean_quant_rateret, mean_ret_spread_quant
)
plotting.plot_quantile_returns_bar(
mean_quant_rateret,
by_group=False,
ylim_percentiles=None,
ax=gf.next_row(),
)
plotting.plot_quantile_returns_violin(
mean_quant_rateret_bydate, ylim_percentiles=(1, 99), ax=gf.next_row()
)
trading_calendar = factor_data.index.levels[0].freq
if trading_calendar is None:
trading_calendar = pd.tseries.offsets.BDay()
warnings.warn(
"'freq' not set in factor_data index: assuming business day",
UserWarning,
)
# Compute cumulative returns from daily simple returns, if '1D'
# returns are provided.
if "1D" in factor_returns:
title = (
"Factor Weighted "
+ ("Group Neutral " if group_neutral else "")
+ ("Long/Short " if long_short else "")
+ "Portfolio Cumulative Return (1D Period)"
)
plotting.plot_cumulative_returns(
factor_returns["1D"], period="1D", title=title, ax=gf.next_row()
)
plotting.plot_cumulative_returns_by_quantile(
mean_quant_ret_bydate["1D"], period="1D", ax=gf.next_row()
)
ax_mean_quantile_returns_spread_ts = [
gf.next_row() for x in range(fr_cols)
]
plotting.plot_mean_quantile_returns_spread_time_series(
mean_ret_spread_quant,
std_err=std_spread_quant,
bandwidth=0.5,
ax=ax_mean_quantile_returns_spread_ts,
)
plt.show()
gf.close()
if by_group:
(
mean_return_quantile_group,
mean_return_quantile_group_std_err,
) = perf.mean_return_by_quantile(
factor_data,
by_date=False,
by_group=True,
demeaned=long_short,
group_adjust=group_neutral,
)
mean_quant_rateret_group = mean_return_quantile_group.apply(
utils.rate_of_return,
axis=0,
base_period=mean_return_quantile_group.columns[0],
)
num_groups = len(
mean_quant_rateret_group.index.get_level_values("group").unique()
)
vertical_sections = 1 + (((num_groups - 1) // 2) + 1)
gf = GridFigure(rows=vertical_sections, cols=2)
ax_quantile_returns_bar_by_group = [
gf.next_cell() for _ in range(num_groups)
]
plotting.plot_quantile_returns_bar(
mean_quant_rateret_group,
by_group=True,
ylim_percentiles=(5, 95),
ax=ax_quantile_returns_bar_by_group,
)
plt.show()
gf.close()
不同分位计算收益率;

按分位数做投资组合,比如quantile=1的加权平均做一个组合,quanttile=5的做另一个组合。
我们只有3支,只形成三个分位段,这样形成按分位的组合序列:


小结:
今天重点分析了alphalens的IC分析与收益分析。IC分析是其中最重要的,就是单因子与预期收益之间的相关关系。
而收益分析类似“回测”,即买入因子最高分位的收益率是多少,最低分位的收益率是多少,两者多空的利差是多少。另外收益率与因子值做线性拟合,得到alpha和beta。
最新代码与数据,请前往星球量化专栏下载。



















