第4章:基础篇:pandas在量化中的应用
做量化交易,说白了就是跟数据打交道。而pandas,就是咱们手里最顺手的工具。我刚开始做期货策略那会儿,还傻乎乎地用Excel手工处理数据,后来发现根本跑不动——几千根K线,手动算个均线都得半天。直到用了pandas,才真正体会到什么叫「效率」。这一章,我就把pandas在量化里最常用的三板斧教给你。
4.1 DataFrame操作:你的数据工作台
DataFrame是什么?你可以把它想象成一张Excel表格,但比Excel灵活得多。每一列是一个字段,每一行是一根K线。我习惯把DataFrame叫做「数据工作台」——你所有的清洗、计算、筛选,都在这个台面上完成。
4.1.1 创建与查看数据
先从最基础的开始。假设我们有一份螺纹钢的日线数据:
import pandas as pd
import numpy as np
# 模拟数据
dates = pd.date_range('2024-01-01', periods=10, freq='D')
data = {
'open': [3800, 3810, 3795, 3820, 3835, 3825, 3810, 3805, 3840, 3850],
'high': [3820, 3830, 3810, 3840, 3850, 3840, 3825, 3820, 3855, 3865],
'low': [3790, 3800, 3785, 3810, 3825, 3815, 3800, 3795, 3830, 3840],
'close':[3815, 3820, 3805, 3835, 3845, 3830, 3815, 3810, 3845, 3855],
'volume':[12000, 13500, 11000, 15000, 16000, 14000, 12500, 11800, 15500, 17000]
}
df = pd.DataFrame(data, index=dates)
print(df.head()) # 看前5行
嗯,这里要注意:index参数我习惯用时间序列,这样后面做时间切片会非常方便。你想想看,如果索引是数字,你要取某一天的数据还得先查行号,多麻烦。
4.1.2 列操作:加减乘除随心所欲
量化里最常用的,就是根据已有列计算新列。比如计算涨跌幅:
# 计算涨跌幅
df['return'] = df['close'].pct_change()
# 计算振幅
df['range'] = (df['high'] - df['low']) / df['open']
# 计算典型价格
df['typical'] = (df['high'] + df['low'] + df['close']) / 3
我在项目中遇到过一个问题:新手喜欢用for循环逐行计算,数据量一上来就卡死。记住,pandas的向量化操作比循环快几十倍。能用一行代码解决的,绝不用循环。
4.1.3 筛选与切片:快速定位
做策略回测时,经常需要筛选特定条件的数据。比如找出涨幅超过2%的交易日:
# 条件筛选
big_up = df[df['return'] > 0.02]
print(big_up)
# 多条件筛选
condition = (df['volume'] > 15000) & (df['return'] > 0.01)
selected = df[condition]
这里有个坑:条件筛选要用&而不是and,用|而不是or。我曾经因为这个bug排查了半小时,最后发现是逻辑运算符用错了。嗯,这种低级错误,犯过一次就记住了。
4.2 时间序列处理:量化交易的时间轴
期货数据天然就是时间序列。开盘、收盘、交割、换月……每一个动作都跟时间绑定。pandas的时间序列处理能力,说实话,是我见过最顺手的。
4.2.1 时间索引的威力
把时间设为索引后,切片变得极其优雅:
# 取某一天
print(df.loc['2024-01-05'])
# 取某段时间
print(df.loc['2024-01-03':'2024-01-07'])
# 取某个月
print(df.loc['2024-01'])
你想想看,如果用数字索引,你要取1月3日到1月7日的数据,还得先知道这两天的行号。但用时间索引,直接写日期就行,是不是很爽?
4.2.2 重采样:切换时间周期
做量化经常需要把日线转成周线、月线。pandas的resample就是干这个的:
# 日线转周线
weekly = df.resample('W').agg({
'open': 'first',
'high': 'max',
'low': 'min',
'close': 'last',
'volume': 'sum'
})
print(weekly)
我个人习惯用W表示周,M表示月,Q表示季度。注意:resample默认以结束日期为标签,如果你想要开始日期,可以加参数label='left'。
4.2.3 移位操作:获取历史数据
策略里经常要用到前一天的收盘价。用shift就能搞定:
# 获取前一天收盘价
df['prev_close'] = df['close'].shift(1)
# 获取前5日最高价
df['high_5'] = df['high'].shift(5)
# 计算当日涨跌
df['change'] = df['close'] - df['prev_close']
这里要注意:shift(1)会把第一行变成NaN。我建议在计算前先dropna(),不然NaN会像病毒一样传染到后面的计算中。
4.3 滚动计算:移动平均线的秘密
滚动计算,说白了就是「滑动窗口」。均线、布林带、RSI……这些技术指标的核心都是滚动计算。pandas的rolling函数,就是专门干这个的。
4.3.1 简单移动平均线
最经典的例子:计算5日均线:
# 5日均线
df['ma5'] = df['close'].rolling(window=5).mean()
# 20日均线
df['ma20'] = df['close'].rolling(window=20).mean()
# 查看结果
print(df[['close', 'ma5', 'ma20']].head(10))
你可能会问:为什么前4行的ma5是NaN?因为窗口需要5个数据才能计算。嗯,这是滚动计算的特性,记得处理一下。
4.3.2 滚动标准差:布林带的基础
布林带需要均线和标准差。用rolling一步到位:
# 计算滚动标准差
df['std20'] = df['close'].rolling(window=20).std()
# 布林带上下轨
df['upper'] = df['ma20'] + 2 * df['std20']
df['lower'] = df['ma20'] - 2 * df['std20']
我在项目中遇到过一个问题:直接用rolling计算标准差,默认是样本标准差(分母n-1)。但有些策略需要总体标准差(分母n)。这时候可以加参数ddof=0:
# 总体标准差
df['std20_pop'] = df['close'].rolling(window=20).std(ddof=0)
4.3.3 自定义滚动函数
有时候内置函数不够用,比如你想计算滚动窗口内的最大值出现位置。这时候可以用apply:
# 自定义:滚动窗口内最高价的位置
def argmax_in_window(series):
return series.argmax()
df['high_pos'] = df['high'].rolling(window=10).apply(argmax_in_window)
不过要提醒你:apply比内置函数慢很多。数据量大的时候,尽量用mean、std这些原生方法。我一般只在万不得已时才用apply。
4.4 实战:一个完整的均线策略数据准备
光说不练假把式。咱们把上面学的串起来,做一个完整的均线策略数据准备流程:
# 1. 读取数据(假设已有CSV)
df = pd.read_csv('rb2401.csv', index_col=0, parse_dates=True)
# 2. 计算技术指标
df['ma5'] = df['close'].rolling(5).mean()
df['ma20'] = df['close'].rolling(20).mean()
df['std20'] = df['close'].rolling(20).std()
# 3. 生成交易信号
df['signal'] = 0
df.loc[df['ma5'] > df['ma20'], 'signal'] = 1 # 金叉做多
df.loc[df['ma5'] < df['ma20'], 'signal'] = -1 # 死叉做空
# 4. 计算每日收益
df['daily_return'] = df['close'].pct_change()
df['strategy_return'] = df['signal'].shift(1) * df['daily_return']
# 5. 清理NaN
df = df.dropna()
print(df.tail())
这个流程,说白了就是量化策略的标准模板。你以后写任何策略,基本都逃不出这个框架:数据加载 → 指标计算 → 信号生成 → 收益计算。
核心要点:
- DataFrame是数据工作台,所有操作都在上面完成
- 时间索引让切片变得优雅,一定要养成习惯
- 滚动计算是技术指标的基础,掌握rolling就掌握了半壁江山
- 向量化操作比循环快几十倍,能用一行别用十行
我的小技巧:
写pandas代码时,我习惯每写一步就print(df.head())看一眼。数据量大的时候,用.shape检查行数列数。这样能及时发现数据异常,避免后面全盘出错。
避坑指南:
我曾经在回测时发现策略收益异常高,排查了半天,原来是shift用错了方向——把未来的数据当成了历史数据。记住:shift(1)是取前一天的数据,不是后一天。这个方向搞反了,你的策略就是「未来函数」,实盘必亏。
4.5 本章知识体系
下面这张图,帮你理清本章的核心脉络:
这张图展示了本章的三大核心模块。它们不是孤立的,而是层层递进的关系:先用DataFrame操作整理数据,再用时间序列处理对齐时间轴,最后用滚动计算生成技术指标。三者结合,你就能搭建出策略的数据处理流水线。
公众号:蓝海资料掘金营,微信deep3321