信号能预测什么吗?
在围绕一个信号建立策略之前,先问它指的方向对不对。每天信号给每只股票一个分数,一天后每只股票有了收益。信息系数(IC)就是各股票的分数与之后收益之间的相关系数。取值在 -1 到 1 之间,零表示没有预测力。真实的信号很弱:0.02 到 0.05 的 IC 就已经有用,因为它会在许多股票和许多天里重复出现。
import numpy as np
signal = np.array([0.3, -1.2, 0.8, 0.1, 1.5]) # today's score for five stocks
returns = np.array([0.01, -0.02, 0.005, -0.004, 0.03]) # their returns over the next day
s = signal - signal.mean()
r = returns - returns.mean()
ic = np.sum(s * r) / np.sqrt(np.sum(s ** 2) * np.sum(r ** 2)) # the Pearson correlation
print(round(float(ic), 4))
print(round(float(np.corrcoef(signal, returns)[0, 1]), 4)) # the same number秩 IC:对极端值稳健
皮尔逊相关系数容易被极端值主导:一只分数极大的股票,或一天暴涨 40%,都可能让它翻转。秩 IC 把每个数组换成其数值的名次(最小为 1),再对名次求相关。它只问信号排在前面的股票最后是否也排在前面,不管数字多大,所以对信号的任何递增变换都保持不变。大多数量化团队汇报的是这个版本,它就是斯皮尔曼相关。
# The rank of each value: argsort gives the order, assigning 1..n through it gives the ranks
import numpy as np
values = np.array([30.0, 10.0, 20.0, 5.0])
order = np.argsort(values) # positions from smallest to largest: [3 1 2 0]
ranks = np.empty(len(values))
ranks[order] = np.arange(1, len(values) + 1)
print(order, ranks) # [3 1 2 0] [4. 2. 3. 1.]# Ranks via argsort, and a rank IC that shrugs off an extreme value
import numpy as np
def ranks(values):
order = np.argsort(values)
out = np.empty(len(values))
out[order] = np.arange(1, len(values) + 1)
return out
def ic(a, b):
a, b = a - a.mean(), b - b.mean()
return float(np.sum(a * b) / np.sqrt(np.sum(a ** 2) * np.sum(b ** 2)))
signal = np.array([0.3, -1.2, 0.8, 0.1, 1.5])
returns = np.array([0.01, -0.02, 0.005, -0.004, 0.03])
print(ranks(signal)) # [3. 1. 4. 2. 5.]
print(round(ic(signal, returns), 4), round(ic(ranks(signal), ranks(returns)), 4))
signal_with_outlier = signal.copy()
signal_with_outlier[4] = 1000.0 # one absurd score
print(round(ic(signal_with_outlier, returns), 4), round(ic(ranks(signal_with_outlier), ranks(returns)), 4))时间上的稳定性:IC 的信息比率
单日的 IC 噪声很大。每天都算一遍得到一个序列,然后看它的均值和稳定性。ICIR 是 IC 均值除以其标准差,它奖励每天都对一点的信号,而不是只在少数几天大对的信号。它相当于信号本身的夏普比率。一个日度 IC 序列若均值 0.03、标准差 0.06,则 ICIR 为 0.5。
# A daily rank-IC series for a weak signal, and its ICIR
import numpy as np
def ranks(values):
out = np.empty(len(values))
out[np.argsort(values)] = np.arange(1, len(values) + 1)
return out
def ic(a, b):
a, b = a - a.mean(), b - b.mean()
return float(np.sum(a * b) / np.sqrt(np.sum(a ** 2) * np.sum(b ** 2)))
rng = np.random.default_rng(8)
daily_ics = []
for day in range(250):
signal = rng.normal(size=300) # 300 stocks
returns = 0.01 * (0.05 * signal + rng.normal(size=300)) # the signal explains a sliver of the return
daily_ics.append(ic(ranks(signal), ranks(returns)))
daily_ics = np.array(daily_ics)
print(round(float(daily_ics.mean()), 4), round(float(daily_ics.std(ddof=1)), 4))
print(round(float(daily_ics.mean() / daily_ics.std(ddof=1)), 3))好的 IC 很小,过大反而是警告市场接近有效,所以 IC 超过 0.1 更可能是代码错误而不是发现:要检查前视偏差、用被预测的收益构造出来的信号,或数据错误。真实的 IC 还会随预测期限衰减,扣除成本后的价值比扣除前更重要。计算时一定要用信号没有拟合过的数据。
- IC 是今日各股票信号与其之后收益的相关系数;0.02 到 0.05 有用,超过 0.1 可疑。
- 秩 IC 对名次求相关(两次
argsort):对极端值稳健,对信号的任何递增变换保持不变。 - ICIR = 多日 IC 均值 / IC 标准差:奖励稳定的能力;务必用样本外数据评估。
下一节:历史 VaR,投资组合除最糟糕的几天之外不应超过的损失。
实验:评分内容
- 用
ranks给数值排序25 分 - 用
ic计算信号与结果的相关25 分 - 用
rank_ic忽略极端值25 分 - 用
icir评判稳定性25 分