特征工程基础:技术指标与基本面因子的实战处理
各位同学,今天我们来聊聊特征工程。说实话,这是整个量化模型中最容易被低估的一环。我见过太多人把精力全花在模型调参上,结果特征没处理好,模型效果惨不忍睹。嗯,咱们今天就把这块硬骨头啃下来。
一、技术指标计算:从K线到信号的转化
技术指标,说白了就是把价格和成交量数据,通过数学公式转化成有交易含义的信号。我个人习惯把技术指标分成两类:趋势类和震荡类。
1. 移动平均线(MA)
MA是最基础的指标。它的核心思想很简单:平滑价格波动,看清趋势方向。
核心公式:
MA(N) = (P₁ + P₂ + ... + Pₙ) / N
其中P为收盘价,N为周期数
import pandas as pd
import numpy as np
def calculate_ma(df, periods=[5, 10, 20, 60]):
"""
计算多周期移动平均线
"""
for period in periods:
df[f'MA_{period}'] = df['close'].rolling(window=period).mean()
return df
# 实战用法
df = calculate_ma(df, periods=[5, 20, 60])
# 金叉信号:MA5上穿MA20
df['golden_cross'] = (df['MA_5'] > df['MA_20']) & (df['MA_5'].shift(1) <= df['MA_20'].shift(1))
我的经验:别只用收盘价算MA。我在做期货策略时,发现用典型价((高+低+收)/3)算出来的MA,对日内波动的过滤效果更好。你想想看,收盘价容易被大单操纵,典型价反而更稳定。
2. MACD指标
MACD是MA的升级版。它用两条不同周期的EMA,来捕捉趋势的强弱变化。
def calculate_macd(df, fast=12, slow=26, signal=9):
"""
计算MACD指标
"""
# 计算快慢EMA
df['EMA_fast'] = df['close'].ewm(span=fast, adjust=False).mean()
df['EMA_slow'] = df['close'].ewm(span=slow, adjust=False).mean()
# DIF线 = 快EMA - 慢EMA
df['DIF'] = df['EMA_fast'] - df['EMA_slow']
# DEA线 = DIF的EMA
df['DEA'] = df['DIF'].ewm(span=signal, adjust=False).mean()
# MACD柱 = 2 * (DIF - DEA)
df['MACD_bar'] = 2 * (df['DIF'] - df['DEA'])
return df
避坑指南:我曾经在回测中发现MACD信号频繁失效。后来排查发现,是EMA的初始值设置问题。默认的ewm函数会用第一个值作为初始值,这在数据量少的时候偏差很大。建议至少用100根K线预热后再计算。
3. RSI与KDJ:震荡类指标
RSI衡量的是涨跌力度,KDJ则加入了随机思想。这两个指标在震荡行情中特别好用。
def calculate_rsi(df, period=14):
"""
计算RSI指标
"""
delta = df['close'].diff()
gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
rs = gain / loss
df['RSI'] = 100 - (100 / (1 + rs))
return df
def calculate_kdj(df, n=9, m1=3, m2=3):
"""
计算KDJ指标
"""
# 计算RSV
low_n = df['low'].rolling(window=n).min()
high_n = df['high'].rolling(window=n).max()
rsv = (df['close'] - low_n) / (high_n - low_n) * 100
# K、D、J值
df['K'] = rsv.ewm(com=m1-1, adjust=False).mean()
df['D'] = df['K'].ewm(com=m2-1, adjust=False).mean()
df['J'] = 3 * df['K'] - 2 * df['D']
return df
二、基本面因子提取:从财报到量化特征
技术指标看的是价格行为,基本面因子看的是公司质地。我个人觉得,两者结合才是王道。
1. PE(市盈率)
PE = 股价 / 每股收益。它反映的是市场愿意为每1元利润支付多少价格。
实战处理要点:
- PE为负值时,说明公司亏损,需要特殊处理
- PE过高(>100)或过低(<0)的股票,建议剔除或做截尾处理
- 不同行业的PE差异很大,建议做行业中性化
2. PB(市净率)
PB = 股价 / 每股净资产。这个指标对金融、地产等重资产行业特别有效。
def extract_fundamental_factors(df_fund):
"""
提取并清洗基本面因子
"""
# 处理极端值:用中位数填充
for col in ['PE', 'PB', 'ROE']:
median_val = df_fund[col].median()
df_fund[col] = df_fund[col].clip(lower=df_fund[col].quantile(0.01),
upper=df_fund[col].quantile(0.99))
df_fund[col].fillna(median_val, inplace=True)
# 行业中性化处理
df_fund['PE_neutral'] = df_fund.groupby('industry')['PE'].transform(
lambda x: (x - x.mean()) / x.std()
)
return df_fund
3. ROE(净资产收益率)
ROE = 净利润 / 净资产。这是巴菲特最看重的指标之一。ROE持续高于15%的公司,通常有较强的护城河。
我的经验:别只看单期ROE。我习惯用过去3年ROE的均值和标准差,均值代表盈利能力,标准差代表稳定性。你会发现,那些ROE稳定在20%左右的消费股,比ROE忽高忽低的周期股更适合做长期策略。
三、特征标准化与归一化:让数据站在同一起跑线
为什么需要标准化?你想想看,PE可能是几十倍,ROE只有百分之十几,MACD柱可能是小数。如果不做处理,模型会天然偏向数值大的特征。这显然不合理。
1. Z-score标准化
把数据变成均值为0、标准差为1的分布。适合数据本身近似正态分布的情况。
from sklearn.preprocessing import StandardScaler
def zscore_normalize(df, columns):
"""
Z-score标准化
"""
scaler = StandardScaler()
df[columns] = scaler.fit_transform(df[columns])
return df, scaler
# 使用示例
features = ['MA_5', 'RSI', 'PE', 'ROE']
df, scaler = zscore_normalize(df, features)
2. Min-Max归一化
把数据压缩到[0,1]区间。适合有明确上下界的特征,比如RSI天然在0-100之间。
from sklearn.preprocessing import MinMaxScaler
def minmax_normalize(df, columns):
"""
Min-Max归一化
"""
scaler = MinMaxScaler()
df[columns] = scaler.fit_transform(df[columns])
return df, scaler
避坑指南:我曾经犯过一个低级错误——在回测时对整个数据集做标准化。这会导致未来数据泄露!正确的做法是:用训练集的统计量去标准化测试集。记住,测试集的数据对模型来说应该是"未来"。
3. 如何选择?
| 方法 | 适用场景 | 注意事项 |
|---|---|---|
| Z-score | 数据近似正态分布,有异常值 | 对异常值敏感度较低 |
| Min-Max | 数据有明确边界,无极端值 | 对异常值敏感 |
| RobustScaler | 数据有较多异常值 | 基于中位数和四分位数 |
四、知识体系总览
下面这张图,是我整理的特征工程核心流程。你可以把它当作一个检查清单,每次做特征工程时对照着来。
这张图把整个流程串起来了。从原始数据出发,分别计算技术指标和基本面因子,然后统一做标准化处理,最终形成特征矩阵。每一步都有坑,每一步都需要细心处理。
核心要点总结:
- 技术指标要选对参数,不同周期、不同品种参数差异很大
- 基本面因子要做行业中性化和极端值处理
- 标准化时务必注意数据泄露问题
- 特征之间相关性过高时,考虑做PCA降维
好了,特征工程的基础内容就讲到这里。这些方法我在实盘策略中反复验证过,效果稳定。下一章我们会把这些特征组合起来,构建完整的选股模型。记住,好的特征胜过复杂的模型——这是我在量化圈摸爬滚打多年最深的体会。