平均收益率是真实的吗?
回测显示平均日收益为 +0.14%。策略是真的好,还是运气好?收益率有噪声,所以任何样本都有均值,连抛硬币策略也有一半的时候均值为正。假设检验提出一个精确的问题:如果真实均值是 mu0(通常为 0),那么样本均值偏离 mu0 这么远有多令人意外?答案就是 p 值。p 值小,说明在「什么都没发生」的假设下,这样的数据极不可能出现。
t = (mean - mu0) / (s / sqrt(n)) p = P(|Z| > |t|)
t 统计量以均值自身的不确定性为单位来衡量均值。均值的不确定性是标准误 s / sqrt(n):样本越大它越小,所以数据越多,同样的均值越有说服力。把所有收益率按比例缩放,均值和标准误同比例变化,因此 t 不变。经验上,|t| 大于 2 就不太可能是运气。
import math
import numpy as np
returns = np.array([0.004, -0.002, 0.001, 0.003, -0.004, 0.002, 0.006, -0.001, 0.005, 0.002, 0.003, -0.002])
n = len(returns)
mean = returns.mean()
std_error = returns.std(ddof=1) / math.sqrt(n) # the uncertainty of the mean
t = mean / std_error # testing against mu0 = 0
print(round(mean, 5), round(std_error, 6)) # 0.00142 0.000892 the mean is only 1.6 standard errors from 0
print(round(t, 3)) # 1.589从 t 到 p 值
在原假设下,样本较大时 t 近似标准正态,所以双侧 p 值是落在离 0 超过 |t|(任一方向)的概率:erfc(|t| / sqrt(2))。当 t = 1.59 时是 11%:这样好的结果纯靠运气每九次就会出现一次,因此不能宣称有真实优势。常用阈值是 5%(|t| = 1.96)。对小样本,正确的参照是尾部更厚的学生 t 分布,所以正态近似的 p 值略小;少于 30 个点时应使用真正的 t 表。
p_value = math.erfc(abs(t) / math.sqrt(2))
print(round(p_value, 4)) # 0.1121 an outcome this good happens by chance about 1 time in 9
print(round(math.erfc(1.96 / math.sqrt(2)), 4)) # 0.05 |t| = 1.96 is the usual 5% limit
# Testing against another value: is the mean different from 0.2% a day?
t_other = (mean - 0.002) / std_error
print(round(t_other, 3), round(math.erfc(abs(t_other) / math.sqrt(2)), 3)) # -0.654 0.513置信区间与配对样本
置信区间比是/否的检验说得更多:均值 -/+ 1.96 * 标准误 是约有 95% 的概率包含真实均值的范围。若区间包含 0,则均值在 5% 水平上不显著。我们的区间是日均 -0.03% 到 +0.32%,跨过零,而且相对均值很宽。要比较在同样日期上测得的两个策略,就把它们每天的差值与 0 比较。配对消除了二者共有的噪声(市场的好日子和坏日子),因此比两次独立检验更敏锐地发现优势。
low, high = mean - 1.96 * std_error, mean + 1.96 * std_error
print(round(low, 5), round(high, 5)) # -0.00033 0.00316 the interval contains 0: not significant at 5%
# Paired: the same six days for two strategies
a = np.array([0.004, -0.002, 0.001, 0.003, -0.004, 0.002])
b = np.array([0.002, -0.003, 0.000, 0.001, -0.002, 0.0])
d = a - b
t_paired = d.mean() / (d.std(ddof=1) / math.sqrt(len(d)))
print(round(d.mean(), 5), round(t_paired, 3)) # 0.001 1.581 a daily edge of 0.1%, but only 6 days# Same mean and spread, four times the data: t doubles and the p-value collapses
spread = returns.std(ddof=1)
for n in (12, 48):
t_n = mean / (spread / math.sqrt(n))
print(n, round(t_n, 3), round(math.erfc(abs(t_n) / math.sqrt(2)), 4))
# 12 1.589 0.1121
# 48 3.178 0.0015显著不等于重要,而且很多检验会作弊足够多的数据下,很小的优势也可能非常显著;数据少时,很大的优势也可能不显著。请始终把效应大小与 p 值一起看。而且如果你检验 100 个策略,仅凭运气就有约 5 个在 5% 水平上通过:在同一批数据上尝试的变体越多,单个 p 值的意义就越小。
t = (mean - mu0) / (s / sqrt(n)):以标准误s / sqrt(n)为单位的均值;数据越多,|t| 越大。- 大样本下双侧 p 值是
erfc(|t| / sqrt(2));|t| > 1.96 即 p < 5%;区间mean -/+ 1.96 * se含 0 则不显著。 - 用每日差值的 t 统计量在同样日期上比较策略;当心检验过多变体。
下一节:线性回归(OLS),拟合一条用一个收益率解释另一个收益率的直线。
实验:评分内容
- 用
t_statistic以噪声为单位衡量均值25 分 - 用
two_sided_p把 t 转换为 p 值25 分 - 用
confidence_interval框出真实均值的范围25 分 - 用
paired_t_statistic逐日比较两个策略25 分