4、Python科学计算环境:Anaconda环境管理、NumPy基础、Pandas数据处理、Matplotlib可视化
做量化回测,说白了就是跟数据打交道。你想想看,行情数据、因子数据、交易记录,哪个不是成百上千万条?我刚开始做回测时,直接用原生Python写循环,结果一个简单的均线策略跑了半小时还没出结果。嗯,从那以后我就明白了——科学计算环境,是回测系统的地基。
这一章,咱们就把地基打牢。我会带你过一遍Anaconda、NumPy、Pandas、Matplotlib这四个核心工具。都是实战中天天用的东西,没什么花架子。
4.1 Anaconda环境管理:别让依赖搞崩你的回测
我个人习惯,每个项目都建一个独立的虚拟环境。为什么?因为Python的包依赖太容易打架了。我曾经在同一个环境里装了ta-lib和backtrader,结果升级numpy时把ta-lib搞崩了,回测数据全变成NaN。那叫一个酸爽。
Anaconda就是来解决这个问题的。它自带conda包管理器,比pip更擅长处理二进制依赖。比如安装ta-lib这种C扩展库,用pip经常报错,用conda一行搞定。
核心操作:
- 创建环境:
conda create -n backtest python=3.9 - 激活环境:
conda activate backtest - 安装包:
conda install numpy pandas matplotlib - 导出环境:
conda env export > environment.yml
我的小技巧:每次开始新项目,先跑一遍conda create -n 项目名 python=3.9。这样就算后面装崩了,删掉环境重来就行,不影响其他项目。
4.2 NumPy基础:回测的数学引擎
NumPy是Python科学计算的基石。量化回测里,你处理的所有数据——价格、收益率、波动率——底层都是NumPy数组。为什么不用Python列表?因为慢。我测试过,计算100万条数据的移动平均,NumPy比纯Python快100倍以上。
核心就三个东西:数组、向量化运算、广播机制。
4.2.1 创建数组
import numpy as np
# 从列表创建
prices = np.array([100.0, 101.5, 102.3, 101.8, 103.2])
# 全零数组,常用于初始化持仓
positions = np.zeros(100)
# 等差数列,常用于生成时间轴
time_axis = np.linspace(0, 1, 1000)
# 随机数,蒙特卡洛模拟常用
returns = np.random.randn(1000) * 0.02 + 0.001
4.2.2 向量化运算
这是NumPy最爽的地方。不用写循环,直接对整个数组做运算。我刚开始做回测时,计算收益率还傻傻地写for循环,后来发现一行代码搞定:
# 计算日收益率(向量化,无循环)
daily_returns = (prices[1:] - prices[:-1]) / prices[:-1]
# 计算累计收益率
cumulative_returns = np.cumprod(1 + daily_returns)
# 计算滚动标准差(波动率)
rolling_std = np.std(daily_returns) * np.sqrt(252) # 年化
避坑指南:我曾经在回测中直接用np.std()计算样本标准差,忘了设置ddof=1。结果夏普比率算出来偏高,差点用错误参数去实盘。记住:金融数据通常用样本标准差,即np.std(arr, ddof=1)。
4.2.3 广播机制
广播是NumPy的隐藏技能。比如你有一个1000只股票的日收益率矩阵(1000行×252列),想给每只股票加上一个基准收益,不用写循环:
# 假设 returns 是 (1000, 252) 的矩阵
# benchmark_returns 是 (252,) 的向量
# 广播会自动对齐维度
excess_returns = returns - benchmark_returns # 结果还是 (1000, 252)
4.3 Pandas数据处理:回测的瑞士军刀
NumPy处理的是数值,Pandas处理的是带标签的数据。量化回测里,时间序列、因子数据、交易记录,全是Pandas的DataFrame。我个人觉得,Pandas是回测系统里最重要的库,没有之一。
4.3.1 时间序列索引
金融数据天然带时间戳。Pandas的DatetimeIndex就是为这个设计的:
import pandas as pd
# 创建时间序列
dates = pd.date_range('2020-01-01', periods=1000, freq='D')
price_series = pd.Series(np.random.randn(1000).cumsum() + 100, index=dates)
# 按时间切片
price_2021 = price_series['2021']
# 重采样到周频
weekly_prices = price_series.resample('W').last()
# 滚动计算
ma_20 = price_series.rolling(window=20).mean()
实战经验:我处理过最头疼的数据是期货的连续合约。不同合约有不同到期日,直接用日期索引会乱。我的做法是:先用pd.to_datetime()统一格式,再用set_index()设为索引。这样后面所有操作都基于时间轴,不会错位。
4.3.2 数据清洗
真实行情数据从来不是干净的。停牌、涨跌停、数据缺失,都是家常便饭。Pandas提供了全套清洗工具:
# 读取CSV行情数据
df = pd.read_csv('stock_data.csv', parse_dates=['date'], index_col='date')
# 检查缺失值
print(df.isnull().sum())
# 向前填充(用上一个交易日数据填充停牌日)
df.fillna(method='ffill', inplace=True)
# 删除全为NaN的行
df.dropna(how='all', inplace=True)
# 去重(同一个交易日可能有重复数据)
df = df[~df.index.duplicated(keep='first')]
4.3.3 分组与聚合
多因子回测时,经常需要按股票分组计算。Pandas的groupby就是干这个的:
# 假设有因子数据:股票代码、日期、因子值、下期收益率
# 按股票分组,计算每个股票的因子与收益率的相关系数
ic_series = df.groupby('date').apply(
lambda g: g['factor'].corr(g['next_return'])
)
# 按行业分组,计算行业平均因子值
industry_factor = df.groupby(['date', 'industry'])['factor'].mean()
4.4 Matplotlib可视化:让回测结果说话
回测跑完了,一堆数字摆在那,谁看得懂?可视化就是让数据说话的工具。我个人习惯,每个回测报告至少包含三张图:净值曲线、回撤曲线、因子收益分布。
4.4.1 净值曲线
import matplotlib.pyplot as plt
# 假设 cumulative_returns 是累计收益率序列
plt.figure(figsize=(12, 6))
plt.plot(cumulative_returns.index, cumulative_returns.values,
label='策略净值', linewidth=2)
plt.plot(benchmark.index, benchmark.values,
label='基准净值', linewidth=1, alpha=0.7)
plt.title('策略净值曲线')
plt.xlabel('日期')
plt.ylabel('净值')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
4.4.2 回撤曲线
回撤是衡量风险的核心指标。我习惯用填充色标出最大回撤区间:
# 计算回撤
cum_max = cumulative_returns.cummax()
drawdown = (cumulative_returns - cum_max) / cum_max
plt.figure(figsize=(12, 4))
plt.fill_between(drawdown.index, 0, drawdown.values,
color='red', alpha=0.3, label='回撤')
plt.title('策略回撤曲线')
plt.xlabel('日期')
plt.ylabel('回撤幅度')
plt.axhline(y=-0.1, color='gray', linestyle='--', label='-10%警戒线')
plt.legend()
plt.show()
我的习惯:每次回测完,先把净值曲线和回撤曲线并排显示。如果回撤曲线有超过20%的深坑,我会仔细检查那段时间的交易记录。很多时候,是数据错误导致的假回撤。
4.4.3 多图布局
回测报告通常需要多张图一起看。Matplotlib的subplots可以搞定:
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# 左上:净值曲线
axes[0, 0].plot(cumulative_returns)
axes[0, 0].set_title('净值曲线')
# 右上:回撤曲线
axes[0, 1].fill_between(drawdown.index, 0, drawdown.values, color='red', alpha=0.3)
axes[0, 1].set_title('回撤曲线')
# 左下:月度收益热力图
monthly_returns = daily_returns.resample('M').apply(lambda x: (1+x).prod() - 1)
# 这里省略热力图绘制细节
# 右下:因子IC序列
axes[1, 1].plot(ic_series)
axes[1, 1].axhline(y=0, color='gray', linestyle='--')
axes[1, 1].set_title('因子IC序列')
plt.tight_layout()
plt.show()
4.5 本章知识体系
下面这张图,是我自己整理的科学计算环境知识结构。你可以把它当作学习地图:
这四个工具,说白了就是一套流水线:Anaconda管环境,Pandas管清洗,NumPy管计算,Matplotlib管展示。我做了这么多年回测,这套组合拳从来没换过。你只要把这四个工具练熟了,后面写策略、搭系统,都会顺手很多。
一句话总结:环境不乱、数据干净、计算高效、图表清晰——这就是高性能回测系统的数据基础。
公众号:蓝海资料掘金营,微信deep3321