4、Python基础回顾:NumPy与Pandas在量化中的应用
说实话,很多刚入行的朋友问我:「学量化交易,Python要学到什么程度?」我的回答很简单——你不需要成为Python专家,但NumPy和Pandas这两把刀,你必须磨利了。
为什么?因为量化交易的本质就是处理数据、计算指标、回测策略。而这两件事,恰好是NumPy和Pandas的看家本领。我自己做策略开发这些年,90%的代码时间都在跟这两个库打交道。
核心认知:NumPy负责「算得快」,Pandas负责「管得好」。两者配合,你就能高效处理金融时间序列数据。
4.1 NumPy:量化计算的「发动机」
NumPy的核心是ndarray,也就是N维数组。你想想看,股票数据是什么?本质上就是一堆数字排成行和列。用Python原生的列表去算,慢得你想哭。用NumPy,同样的运算快几十倍。
我的经验:刚开始做量化时,我用纯Python写了一个移动平均线计算,回测10年的数据跑了快3分钟。换成NumPy后,0.3秒搞定。嗯,这就是差距。
4.1.1 创建数组
import numpy as np
# 从列表创建
prices = np.array([100.5, 101.2, 102.8, 103.1, 102.5])
# 创建全0数组(比如初始化仓位记录)
zeros = np.zeros((5, 3)) # 5行3列
# 创建等差数列(比如模拟时间轴)
time_axis = np.linspace(0, 1, 100) # 从0到1,均匀取100个点
# 随机数(蒙特卡洛模拟常用)
np.random.seed(42) # 固定随机种子,保证可复现
random_returns = np.random.randn(1000) # 生成1000个正态分布随机数
4.1.2 向量化运算
这是NumPy最爽的地方。不用写循环,直接对整个数组做运算。
# 假设这是某股票5天的收盘价
close = np.array([100, 102, 101, 105, 107])
# 计算每日收益率(向量化,一行搞定)
returns = np.diff(close) / close[:-1]
# 结果:array([0.02, -0.0098, 0.0396, 0.019])
# 计算累计收益率
cum_return = np.cumprod(1 + returns) - 1
# 统计指标
mean_return = np.mean(returns)
std_return = np.std(returns)
max_drawdown = np.min(returns) # 简化版最大回撤
避坑指南:我曾经在计算夏普比率时,直接用
np.std()算收益率标准差,结果怎么算都不对。后来才发现——np.std()默认是总体标准差,而金融数据通常用样本标准差。记得加参数ddof=1。
4.2 Pandas:量化数据的「瑞士军刀」
如果说NumPy是发动机,那Pandas就是整辆车。它让你能轻松地加载、清洗、分析、可视化金融数据。
4.2.1 DataFrame基础操作
import pandas as pd
# 创建DataFrame(模拟股票数据)
data = {
'日期': ['2024-01-02', '2024-01-03', '2024-01-04'],
'开盘': [100.5, 101.2, 102.0],
'收盘': [101.8, 102.5, 101.3],
'成交量': [10000, 12000, 9500]
}
df = pd.DataFrame(data)
# 查看数据
print(df.head()) # 前5行
print(df.info()) # 数据类型、缺失值
print(df.describe()) # 统计摘要
4.2.2 时间序列处理
量化交易里,时间就是一切。Pandas的时间序列功能,我用得最多。
# 把日期列设为索引
df['日期'] = pd.to_datetime(df['日期'])
df.set_index('日期', inplace=True)
# 重采样(比如把日线转周线)
weekly = df.resample('W').agg({
'开盘': 'first',
'收盘': 'last',
'成交量': 'sum'
})
# 滚动窗口计算(移动平均线)
df['MA5'] = df['收盘'].rolling(window=5).mean()
df['MA20'] = df['收盘'].rolling(window=20).mean()
# 移位操作(计算前一天的收盘价)
df['前日收盘'] = df['收盘'].shift(1)
个人习惯:我每次拿到新数据,第一件事就是
df.isnull().sum()检查缺失值。金融数据经常有停牌、节假日导致的数据缺失,不处理直接跑策略,结果会骗人的。
4.2.3 数据合并与连接
做多因子策略时,经常需要合并不同来源的数据。
# 假设有两个DataFrame
df_stock_a = pd.DataFrame({'收盘': [100, 102]}, index=['2024-01-02', '2024-01-03'])
df_stock_b = pd.DataFrame({'收盘': [50, 51]}, index=['2024-01-02', '2024-01-03'])
# 横向合并(类似Excel的VLOOKUP)
portfolio = pd.concat([df_stock_a, df_stock_b], axis=1, keys=['股票A', '股票B'])
# 按日期合并(处理不同频率的数据)
df_merged = pd.merge(df_daily, df_monthly, left_index=True, right_index=True, how='left')
4.3 实战:计算一个简单的均线策略信号
光说不练假把式。咱们用真实数据走一遍。
import numpy as np
import pandas as pd
# 模拟100天的股价数据(随机游走)
np.random.seed(42)
days = 100
price = 100 * np.cumprod(1 + np.random.randn(days) * 0.01)
# 构建DataFrame
df = pd.DataFrame({
'close': price,
'date': pd.date_range('2024-01-01', periods=days)
})
df.set_index('date', inplace=True)
# 计算均线
df['ma5'] = df['close'].rolling(5).mean()
df['ma20'] = df['close'].rolling(20).mean()
# 生成交易信号
df['signal'] = 0
df.loc[df['ma5'] > df['ma20'], 'signal'] = 1 # 金叉,买入
df.loc[df['ma5'] < df['ma20'], 'signal'] = -1 # 死叉,卖出
# 计算策略收益率
df['return'] = df['close'].pct_change()
df['strategy_return'] = df['signal'].shift(1) * df['return']
# 看看结果
print(df[['close', 'ma5', 'ma20', 'signal', 'strategy_return']].head(10))
关键点:注意
shift(1)的使用。信号是在当天收盘后生成的,所以实际交易要等到第二天开盘。这个细节,很多新手会忽略,导致回测结果虚高。
4.4 常用量化函数速查表
| 功能 | NumPy实现 | Pandas实现 |
|---|---|---|
| 收益率计算 | np.diff(prices)/prices[:-1] |
df['close'].pct_change() |
| 移动平均 | 需手动实现 | df['close'].rolling(20).mean() |
| 标准差 | np.std(returns, ddof=1) |
df['return'].std() |
| 协方差矩阵 | np.cov(returns_matrix) |
df.cov() |
| 相关系数 | np.corrcoef(x, y) |
df.corr() |
| 缺失值处理 | np.isnan(arr) |
df.dropna() / df.fillna() |
这张表我建议你收藏。刚开始做策略时,经常要查某个函数怎么写。用多了自然就记住了。
我曾经踩过的坑:用
pct_change()计算收益率时,如果数据里有0值,会出现inf(无穷大)。比如某天股价为0(除权除息处理不当),收益率就变成无穷大。所以,数据清洗永远要放在第一步。
好了,NumPy和Pandas的基础就回顾到这里。说白了,这两个库就是量化交易的「左膀右臂」。你不需要背下所有函数,但一定要理解它们的核心思想:向量化运算和数据框操作。后面的章节里,我们会反复用到这些知识。