第三节:基础统计特征提取
好,咱们今天聊聊基础统计特征提取。说白了,就是把原始的价格数据,变成我们量化模型能用的“食材”。
我个人习惯,拿到数据第一件事不是跑模型,而是先算这些基础指标。为什么?因为它们是后续所有策略的基石。你想想看,连波动率都算不准,谈什么风险控制?
3.1 日收益率计算
日收益率,最简单的定义就是今天的收盘价减去昨天的收盘价,再除以昨天的收盘价。公式长这样:
R_t = (P_t - P_{t-1}) / P_{t-1}
但在实际项目中,我建议用对数收益率。为什么?因为对数收益率在时间序列上具有可加性,而且更符合正态分布的假设。我在处理玉米期货数据时,就发现对数收益率比简单收益率更稳定。
代码实现也很简单:
import pandas as pd
import numpy as np
# 假设df是包含收盘价的数据框
df['return'] = np.log(df['close'] / df['close'].shift(1))
# 或者用pct_change
df['return_simple'] = df['close'].pct_change()
3.2 波动率计算
波动率,说白了就是收益率的“震荡幅度”。我们通常用标准差来衡量。但这里有个坑——你选多少天的窗口?
我个人习惯用20个交易日(差不多一个自然月)作为滚动窗口。这样既能捕捉到短期波动,又不会太敏感。
df['volatility'] = df['return'].rolling(window=20).std() * np.sqrt(252)
# 乘以sqrt(252)是为了年化,252是一年大概的交易天数
3.3 移动平均线(MA5, MA10, MA20)
移动平均线,最基础的趋势指标。MA5代表5日均线,MA10是10日,MA20是20日。它们的作用就是平滑价格,让我们看清趋势方向。
代码实现:
df['MA5'] = df['close'].rolling(window=5).mean()
df['MA10'] = df['close'].rolling(window=10).mean()
df['MA20'] = df['close'].rolling(window=20).mean()
嗯,这里要注意一点:移动平均线是滞后指标。价格已经涨上去了,均线才慢慢跟上。所以它更适合判断趋势,而不是预测拐点。
我在做豆粕的季节性策略时,发现MA20和MA5的金叉死叉信号,在每年3-4月和9-10月特别有效。为什么?因为这两个时间段是豆粕的消费旺季和淡季转换期。
3.4 相对强弱指标(RSI)
RSI,全称Relative Strength Index,用来衡量价格涨跌的力度。公式稍微复杂一点:
RSI = 100 - (100 / (1 + RS))
其中 RS = 平均上涨幅度 / 平均下跌幅度
通常我们用14天作为周期。RSI超过70算超买,低于30算超卖。但说实话,这个阈值在农产品上要调整。我记得做棉花期货时,RSI经常在60-80之间震荡,70根本不算超买。
代码实现:
def compute_rsi(data, window=14):
delta = data.diff()
gain = (delta.where(delta > 0, 0)).rolling(window=window).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=window).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
return rsi
df['RSI'] = compute_rsi(df['close'], window=14)
3.5 知识体系总览
下面这张图,把咱们今天讲的内容串起来了:
3.6 实战中的避坑指南
我曾经在计算波动率时,直接用默认的252天年化,结果发现豆粕的波动率被低估了。后来一查,豆粕期货一年实际交易天数只有240天左右。所以,一定要根据品种调整年化因子。
还有一次,我在做RSI时,发现棉花期货的RSI经常在70以上震荡,但价格还在涨。后来我调整了阈值,把超买线改到80,效果就好多了。说白了,农产品有它自己的脾气,不能生搬硬套股票的那套参数。
3.7 总结
今天咱们讲了四个基础指标:日收益率、波动率、移动平均线和RSI。它们看似简单,但用好了就是利器。我个人建议,拿到数据后先跑一遍这些指标,看看数据质量,感受一下品种的“性格”。
下一节,我们会把这些指标组合起来,构建真正的季节性因子。嗯,到时候你会发现,原来这些基础特征可以玩出这么多花样。