第4章:特征工程入门:基础技术指标与截面标准化

各位同学,欢迎来到特征工程的第一课。

说实话,在量化投资里,模型选得再好,如果特征没做好,那基本就是白搭。我见过太多人一上来就堆几百个因子,结果过拟合得一塌糊涂。今天咱们就从最基础、最实用的技术指标讲起,一步步搭建你的Alpha特征库。

4.1 基础技术指标:均线、RSI、MACD

这些指标你可能早就听说过,但用在指数增强里,怎么用才有效?我个人的习惯是:不要直接用原始值,而是用衍生值

4.1.1 移动平均线(MA)

均线是最简单的趋势指标。但直接拿收盘价减均线,其实噪音很大。

我一般这样处理:

  • 价格位置:当前价格 / 均线 - 1,表示价格偏离均线的程度
  • 均线斜率:当前均线值 / N天前均线值 - 1,表示趋势的加速度
  • 多均线排列:短期均线在长期均线上方还是下方

核心经验:在A股市场,5日均线和20日均线的交叉信号,比单纯用一根均线有效得多。我曾在回测中发现,用价格偏离20日均线超过5%作为反转信号,胜率能提升近10%。

# 计算均线偏离度
import pandas as pd
import numpy as np

def ma_features(df, windows=[5, 10, 20, 60]):
    """计算均线相关特征"""
    for w in windows:
        ma = df['close'].rolling(w).mean()
        df[f'ma_{w}'] = ma
        df[f'price_to_ma_{w}'] = df['close'] / ma - 1
        df[f'ma_slope_{w}'] = ma.pct_change(w)
    return df

4.1.2 RSI(相对强弱指标)

RSI这个指标,说白了就是衡量最近涨跌的力度。默认用14天,但我建议你试试不同周期。

为什么?因为不同股票、不同市场环境下,RSI的有效周期是不一样的。我曾经在回测中发现,对于小盘股,6日RSI比14日RSI更灵敏,信号也更早。

避坑指南:我曾经直接用RSI的原始值(0-100)作为特征,结果模型学到的全是「超买超卖」的线性关系。后来改成RSI的排名分位数,效果好了很多。你想想看,在牛市中RSI长期在70以上,这时候70还能叫超买吗?

def rsi_features(df, periods=[6, 14, 21]):
    """计算RSI相关特征"""
    for p in periods:
        delta = df['close'].diff()
        gain = delta.clip(lower=0)
        loss = -delta.clip(upper=0)
        avg_gain = gain.rolling(p).mean()
        avg_loss = loss.rolling(p).mean()
        rs = avg_gain / avg_loss
        df[f'rsi_{p}'] = 100 - (100 / (1 + rs))
        # 排名分位数
        df[f'rsi_rank_{p}'] = df[f'rsi_{p}'].rank(pct=True)
    return df

4.1.3 MACD(指数平滑异同移动平均线)

MACD其实是个趋势跟随指标,但很多人把它当震荡指标用,这就错了。

我个人习惯用MACD的柱状图变化率,而不是柱状图本身。因为柱状图的变化方向,往往比绝对值更有预测意义。

MACD成分 计算公式 我常用的衍生特征
DIF线 EMA(12) - EMA(26) DIF的5日变化率
DEA线 DIF的9日EMA DIF与DEA的差值
MACD柱 (DIF - DEA) × 2 柱状图的斜率

4.2 滚动窗口统计量

技术指标说到底也是窗口统计量的一种。但除了均线、RSI这些,还有很多更基础的统计量值得挖掘。

我一般会计算以下几类:

  • 波动类:滚动标准差、平均真实波幅(ATR)
  • 动量类:N日收益率、N日最大回撤
  • 分布类:滚动偏度、滚动峰度
  • 相关性类:与大盘的滚动相关系数

注意:滚动窗口的大小选择很关键。窗口太小(比如3天),噪音太大;窗口太大(比如120天),信号太滞后。我一般会同时用多个窗口,然后让模型自己去选。比如同时用5、10、20、60天。

def rolling_stats(df, windows=[5, 10, 20]):
    """滚动窗口统计量"""
    for w in windows:
        df[f'ret_{w}d'] = df['close'].pct_change(w)
        df[f'vol_{w}d'] = df['close'].pct_change().rolling(w).std()
        df[f'max_drawdown_{w}d'] = (
            df['close'].rolling(w).max() / df['close'] - 1
        )
        df[f'skew_{w}d'] = df['close'].pct_change().rolling(w).skew()
        df[f'kurt_{w}d'] = df['close'].pct_change().rolling(w).kurt()
    return df

4.3 截面标准化

嗯,这里要注意。前面讲的所有特征,都是针对单只股票的时间序列。但指数增强模型,需要的是横截面比较——也就是在同一时刻,哪只股票更强。

所以,截面标准化是必须的。我常用的方法有三种:

  1. Z-score标准化:减去截面均值,除以截面标准差。适合分布比较对称的特征。
  2. 排名标准化:把特征值转为排名,再映射到[-1, 1]区间。对异常值不敏感。
  3. 分位数标准化:按分位数分组,比如分成10组。适合非线性关系。

我的经验:对于技术指标这类特征,我强烈建议用排名标准化。因为技术指标本身就有很多异常值(比如某只股票突然涨停,RSI直接跳到100),如果用Z-score,整个截面都会被带偏。排名标准化就稳多了。

def cross_sectional_normalize(df, features, method='rank'):
    """截面标准化"""
    for f in features:
        if method == 'zscore':
            mean = df.groupby('date')[f].transform('mean')
            std = df.groupby('date')[f].transform('std')
            df[f'{f}_zscore'] = (df[f] - mean) / std
        elif method == 'rank':
            df[f'{f}_rank'] = df.groupby('date')[f].rank(pct=True) * 2 - 1
    return df

4.4 本章知识体系

说了这么多,咱们用一张图来总结一下本章的核心逻辑:

特征工程入门:知识体系 基础技术指标 均线(MA) RSI(相对强弱) MACD(趋势跟随) 滚动窗口统计量 波动类(标准差、ATR) 动量类(收益率、回撤) 分布类(偏度、峰度) 截面标准化 Z-score标准化 排名标准化 分位数标准化 核心原则 用衍生值代替原始值 · 多窗口并行 · 截面标准化优先

这张图把咱们今天讲的内容串起来了。从基础技术指标到滚动统计量,再到截面标准化,每一步都是为了让特征更干净、更有效。

最后说一句:特征工程没有银弹。我做了这么多年,最大的体会就是——多试、多对比、多思考。同样的指标,换一个窗口、换一种标准化方式,结果可能天差地别。别怕麻烦,这是量化投资中最值得花时间的地方。


专注资料整理