统计特征(上):均值、方差、偏度、峰度、最大值、最小值、极差
各位同学,咱们今天聊聊时间序列里最基础、也最容易被忽视的东西——统计特征。
说实话,我刚开始做量化那会儿,觉得这些统计量太简单了。均值方差嘛,大学就学过。结果呢?第一次实盘回测,策略在震荡市里表现挺好,一遇到极端行情直接崩了。后来复盘才发现,我根本没关注序列的偏度和峰度。嗯,这就是典型的「基础不牢,地动山摇」。
所以今天咱们把这几个特征掰开揉碎了讲。你想想看,一个时间序列,你拿到手第一件事是什么?肯定是看它长什么样。均值告诉你中心在哪,方差告诉你波动多大,偏度和峰度告诉你分布的形状。这些信息,是后续所有因子合成的基石。
1. 均值与方差:最熟悉的陌生人
均值,说白了就是「平均价格」。但在时间序列里,均值有个坑——它可能随时间变化。我习惯用滚动均值来捕捉这种变化,而不是算一个全局平均。
方差呢,衡量的是离散程度。金融里我们更常用标准差(方差的平方根),因为它和原始数据量纲一致。举个例子,某股票日收益率的标准差是0.02,意味着每天涨跌1-2个百分点是常态。
均值 μ = (1/N) * Σx_i
方差 σ² = (1/N) * Σ(x_i - μ)²
import numpy as np
import pandas as pd
# 生成模拟数据
np.random.seed(42)
returns = np.random.normal(0.001, 0.02, 1000)
# 计算统计量
mean_val = np.mean(returns)
var_val = np.var(returns, ddof=1) # 样本方差,ddof=1
std_val = np.std(returns, ddof=1)
print(f"均值: {mean_val:.6f}")
print(f"方差: {var_val:.6f}")
print(f"标准差: {std_val:.6f}")
3. 最大值、最小值与极差:边界的力量
这三个特征,我经常用在风控因子里。最大值告诉你历史上最疯狂的时刻,最小值告诉你最绝望的时刻,极差就是两者之差,衡量了整个波动范围。
我曾经做过一个CTA策略,专门捕捉突破行情。核心逻辑就是:当价格突破过去20天的最大值时开多,跌破最小值时开空。这个策略在趋势行情里赚得盆满钵满,但在震荡市里被反复打脸。后来我加了极差条件——极差太小说明市场在窄幅震荡,这时候就不交易。
# 滚动窗口计算
window = 20
df = pd.DataFrame({'price': np.cumprod(1 + returns) * 100})
df['max_20'] = df['price'].rolling(window).max()
df['min_20'] = df['price'].rolling(window).min()
df['range_20'] = df['max_20'] - df['min_20']
print(df.head(25))
4. 偏度与峰度:分布的「形状」
这两个特征,很多初学者会跳过。但我告诉你,在因子合成里,它们往往是最有信息量的。
偏度衡量分布的不对称性。正偏度意味着右尾更长,也就是有更多极端正收益。负偏度则相反。在股票里,个股收益率的偏度通常为正,因为上涨的幅度可以很大(比如涨停),但下跌有跌停限制。
峰度衡量分布的「尖峭」程度。高峰度意味着分布有厚尾——极端值出现的概率比正态分布高。金融数据几乎都是高峰度的,这就是为什么「黑天鹅」事件频发。
偏度 = (1/N) * Σ((x_i - μ)/σ)³
峰度 = (1/N) * Σ((x_i - μ)/σ)⁴ - 3
(减3是为了让正态分布的峰度为0)
from scipy.stats import skew, kurtosis
skew_val = skew(returns)
kurt_val = kurtosis(returns, fisher=True) # Fisher=True 表示减3
print(f"偏度: {skew_val:.4f}")
print(f"峰度: {kurt_val:.4f}")
# 判断分布形态
if skew_val > 0.5:
print("右偏分布,正极端值较多")
elif skew_val < -0.5:
print("左偏分布,负极端值较多")
else:
print("近似对称分布")
if kurt_val > 1:
print("高峰度,厚尾风险大")
elif kurt_val < -1:
print("低峰度,分布较平坦")
else:
print("峰度接近正态")
5. 知识体系总览
下面这张图,是我自己整理的知识框架。你可以把它当作一个检查清单——每次拿到新数据,按这个顺序过一遍,基本不会漏掉重要信息。
6. 实战:把这些特征合成一个因子
最后,咱们来个实战。假设我们要构造一个「极端波动因子」——当价格出现极端波动时,这个因子值会变大。
def extreme_volatility_factor(price_series, window=20):
"""
构造极端波动因子:
结合极差、偏度、峰度的信息
"""
# 计算收益率
returns = price_series.pct_change().dropna()
# 滚动窗口计算
rolling_range = price_series.rolling(window).max() - price_series.rolling(window).min()
rolling_skew = returns.rolling(window).skew()
rolling_kurt = returns.rolling(window).kurt()
# 标准化
range_norm = (rolling_range - rolling_range.mean()) / rolling_range.std()
skew_norm = (rolling_skew - rolling_skew.mean()) / rolling_skew.std()
kurt_norm = (rolling_kurt - rolling_kurt.mean()) / rolling_kurt.std()
# 合成因子:极差大 + 偏度极端 + 峰度高 = 极端波动
factor = (range_norm.abs() + skew_norm.abs() + kurt_norm.abs()) / 3
return factor
# 测试
price = pd.Series(np.cumprod(1 + returns) * 100)
factor = extreme_volatility_factor(price)
print(f"因子均值: {factor.mean():.4f}")
print(f"因子标准差: {factor.std():.4f}")
好了,今天的内容就到这。这些统计特征,看似简单,但用好了能解决很多实际问题。下次你拿到一个时间序列,不妨先算算这些量,心里就有底了。
公众号:蓝海资料掘金营,微信deep3321