4. 特征工程

任务目标

本案例基于股票历史行情数据,构建价格衍生特征集,为后续机器学习选股模型提供输入。核心任务是:从原始OHLCV数据中生成动量类、波动类、均线类共三类特征,并输出可直接用于模型训练的特征矩阵。

环境与依赖

需要 pandas、numpy。数据使用本地CSV文件,包含至少date, open, high, low, close, volume六列,按日期升序排列。

核心实现

import pandas as pd
import numpy as np

def generate_price_features(df, window=5):
    """
    生成价格衍生特征
    参数:
        df: 含 open/high/low/close/volume 的DataFrame,按日期升序
        window: 动量与波动窗口期
    返回:
        含原始列 + 衍生特征的新DataFrame
    """
    df = df.copy()
    close = df['close']
    high = df['high']
    low = df['low']

    # --- 动量类 ---
    df['ret_1d'] = close.pct_change()                    # 1日收益率
    df['ret_window'] = close.pct_change(window)          # N日收益率
    df['mom_ratio'] = close / close.shift(window) - 1    # 动量比率

    # --- 波动类 ---
    df['high_low_range'] = (high - low) / close          # 日内振幅
    df['volatility'] = df['ret_1d'].rolling(window).std()  # 滚动波动率

    # --- 均线类 ---
    df['ma_short'] = close.rolling(window).mean()        # 短期均线
    df['ma_long'] = close.rolling(window * 2).mean()     # 长期均线
    df['price_vs_ma'] = close / df['ma_short'] - 1       # 价格偏离短期均线

    # 删除前window行(滚动窗口导致的NaN)
    df = df.iloc[window:]
    return df.reset_index(drop=True)

# 最小调用入口
if __name__ == "__main__":
    # 读取数据(示例路径,请替换为实际文件)
    raw = pd.read_csv('stock_data.csv', parse_dates=['date'])
    raw = raw.sort_values('date')

    # 生成特征
    featured = generate_price_features(raw, window=5)

    # 保存结果
    featured.to_csv('featured_data.csv', index=False)
    print(f"特征矩阵形状: {featured.shape}")
    print(f"衍生特征列: {[c for c in featured.columns if c not in raw.columns]}")

运行与验证

运行方式:将上述代码保存为 feature_engineering.py,与 stock_data.csv 置于同一目录,执行 python feature_engineering.py。

验证方法:

  1. 程序正常结束,输出特征矩阵形状为 (总行数-5, 总列数+9),新增9个特征列。
  2. 打开 featured_data.csv,检查ret_1d列:第1行应为NaN(因pct_change首值为空),后续行数值约在-0.1~0.1之间(正常股票日收益率范围)。
  3. 检查volatility列:所有值均为非负数,且前几行为NaN(滚动窗口未满)。
  4. 检查price_vs_ma列:数值有正有负,表示价格相对均线的偏离方向。

通过条件:上述检查全部通过,且无报错,即特征工程闭环完成。

常见问题

  • 数据不足:若原始数据少于10行,ma_long将全为NaN,需保证数据量至少为 2*window+1 行。
  • 日期排序:若原始数据未按日期升序,特征计算将失去时序意义,务必先执行 sort_values('date')。
  • 窗口选择:本案例默认window=5,实际使用中可根据交易频率调整(如日线数据常用5/10/20)。