3. 技术指标计算与特征工程

做量化交易,说白了就是跟数据打交道。原始的价格序列就像一块璞玉,你得把它打磨成能用的特征。我个人习惯把这一步叫做「给市场做CT」——把K线图一层层扫描,找出那些肉眼看不到的规律。

今天咱们就聊聊技术指标的计算和特征工程。嗯,这部分我踩过的坑可不少,待会慢慢说。

3.1 常见技术指标:从原理到代码

先说说几个最常用的指标。你想想看,市场上成千上万的策略,80%以上都离不开这几个老朋友。

3.1.1 SMA(简单移动平均线)

SMA 是最基础的指标。就是把过去 N 天的收盘价加起来除以 N。公式很简单:

SMA = (P₁ + P₂ + ... + Pₙ) / N

但别小看它。我在做趋势跟踪策略时,SMA 的交叉信号虽然简单,配合好止损,年化收益能做到 15% 以上。关键在于参数的选择——我个人习惯用 20 日和 60 日两条均线。

核心要点:SMA 对价格反应滞后,但能有效过滤噪音。适合识别中长期趋势。

3.1.2 EMA(指数移动平均线)

EMA 跟 SMA 不一样。它给近期的价格更高的权重。公式稍微复杂点:

EMA = (P × α) + (前一日EMA × (1 - α))
其中 α = 2 / (N + 1)

为什么要用 EMA?我举个例子。2020年3月美股暴跌时,SMA 还在高位,EMA 已经提前掉头了。说白了,EMA 反应更快,适合短线交易。

我的经验:做日内交易用 5/10/20 EMA,做波段用 12/26 EMA。别贪多,两三条就够了。

3.1.3 RSI(相对强弱指标)

RSI 衡量价格变动的速度和幅度。取值范围 0-100。一般认为:

  • RSI > 70:超买,可能回调
  • RSI < 30:超卖,可能反弹

公式:

RSI = 100 - [100 / (1 + RS)]
RS = 平均上涨幅度 / 平均下跌幅度

我曾经犯过一个错:看到 RSI 到 70 就做空,结果被连续逼空。后来才明白,强趋势行情里 RSI 可以在超买区待很久。嗯,这里要注意:RSI 背离信号比超买超卖更可靠。

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

MACD 由三部分组成:

  • DIF 线:快线(12EMA) - 慢线(26EMA)
  • DEA 线:DIF 的 9 日 EMA
  • 柱状图:2 × (DIF - DEA)

MACD 的核心逻辑是「趋势的加速度」。当柱状图由负转正,说明多头力量在增强。我一般用 MACD 金叉死叉作为入场信号,配合成交量确认。

避坑指南:我曾经在震荡市里用 MACD 金叉做多,连续止损 5 次。后来加了 ADX 指标过滤,才解决了这个问题。记住:任何指标在震荡市都会失效。

3.2 使用 ta-lib 库:一行代码搞定计算

手动算指标太慢了。ta-lib 这个库,你值得拥有。它用 C 语言写的,速度飞快。安装方式:

pip install TA-Lib

如果安装报错,去 这里 下载对应的 whl 文件。

来看看怎么用:

import talib
import pandas as pd
import numpy as np

# 假设 df 是你的数据,包含 'close' 列
close = df['close'].values

# 计算 SMA
sma_20 = talib.SMA(close, timeperiod=20)

# 计算 EMA
ema_12 = talib.EMA(close, timeperiod=12)

# 计算 RSI
rsi_14 = talib.RSI(close, timeperiod=14)

# 计算 MACD
macd, macd_signal, macd_hist = talib.MACD(
    close, 
    fastperiod=12, 
    slowperiod=26, 
    signalperiod=9
)

# 全部塞回 DataFrame
df['sma_20'] = sma_20
df['ema_12'] = ema_12
df['rsi_14'] = rsi_14
df['macd'] = macd
df['macd_signal'] = macd_signal
df['macd_hist'] = macd_hist

注意:ta-lib 返回的数组前 N 个值是 NaN。比如 SMA(20) 前 19 个是空值。记得用 dropna() 处理。

ta-lib 支持的指标超过 150 种。除了上面这些,还有:

  • 布林带:talib.BBANDS
  • ATR(平均真实波幅):talib.ATR
  • ADX(平均趋向指数):talib.ADX
  • CCI(商品通道指数):talib.CCI

我个人习惯把常用的指标封装成一个函数:

def add_technical_indicators(df):
    """给 DataFrame 添加技术指标"""
    close = df['close'].values
    high = df['high'].values
    low = df['low'].values
    volume = df['volume'].values
    
    # 趋势指标
    df['sma_20'] = talib.SMA(close, 20)
    df['sma_60'] = talib.SMA(close, 60)
    df['ema_12'] = talib.EMA(close, 12)
    df['ema_26'] = talib.EMA(close, 26)
    
    # 动量指标
    df['rsi_14'] = talib.RSI(close, 14)
    df['macd'], df['macd_signal'], df['macd_hist'] = talib.MACD(close)
    
    # 波动率指标
    df['atr_14'] = talib.ATR(high, low, close, 14)
    df['upper'], df['middle'], df['lower'] = talib.BBANDS(close, 20, 2)
    
    # 成交量指标
    df['obv'] = talib.OBV(close, volume)
    
    return df

3.3 特征标准化与归一化:让数据站在同一起跑线

你想想看,RSI 取值范围 0-100,而成交量可能是几百万。如果不做处理,模型会天然认为成交量更重要——这显然不对。

标准化和归一化就是解决这个问题的。

3.3.1 归一化(Min-Max Scaling)

把数据缩放到 [0, 1] 区间:

X_scaled = (X - X_min) / (X_max - X_min)

用 sklearn 实现:

from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler()
df[['rsi_14', 'macd', 'volume']] = scaler.fit_transform(
    df[['rsi_14', 'macd', 'volume']]
)

我的建议:归一化适合数据分布比较均匀的情况。如果数据有极端值,效果会变差。

3.3.2 标准化(Z-score Standardization)

把数据变成均值为 0,标准差为 1 的分布:

X_scaled = (X - μ) / σ

代码实现:

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
df[['close', 'volume', 'atr_14']] = scaler.fit_transform(
    df[['close', 'volume', 'atr_14']]
)

标准化对异常值不那么敏感。我个人在大多数场景下都用标准化,除非模型要求输入在 [0,1] 区间(比如某些神经网络)。

3.3.3 什么时候用什么?

场景 推荐方法 原因
数据无明显异常值 归一化 保留原始分布形状
数据有异常值 标准化 对异常值鲁棒
神经网络输入 归一化 加速收敛
SVM、PCA 等算法 标准化 假设数据服从正态分布

避坑指南:我曾经在回测时对整个数据集做了标准化,结果发现未来数据泄露了。正确的做法是:只用训练集的统计量来标准化测试集。用 fit_transform 处理训练集,用 transform 处理测试集。

3.4 本章知识体系

下面这张图总结了本章的核心逻辑:

技术指标计算与特征工程知识体系 原始价格序列 技术指标计算(ta-lib) SMA / EMA RSI MACD 布林带/ATR 特征工程 归一化(Min-Max) 标准化(Z-score) 标准化特征矩阵 → 模型输入

从原始数据出发,经过 ta-lib 计算技术指标,再通过标准化/归一化处理,最终得到可供模型使用的特征矩阵。每一步都有坑,但每一步也都有规律可循。

总结一下:技术指标是工具,特征工程是手艺。工具人人都有,但手艺高低决定了你能不能赚钱。多试、多错、多总结——这是我在这个行业摸爬滚打多年的心得。

专注资料整理