主题切换
3.1 Python数据栈
概念详解
为什么Python是量化的首选语言
十年前,量化交易的世界被C++、MATLAB和R分割。Python在金融领域的崛起绝非偶然,它提供了量化工作流所需的三项核心能力:数据处理(NumPy/Pandas)、科学计算(SciPy/Statsmodels)和机器学习(scikit-learn/TensorFlow)的无缝集成。
更关键的是,Python拥有一个庞大的开源社区,几乎每个量化需求——从获取行情数据到执行算法交易——都有现成的库和工具。这使得量化研究者可以专注于alpha的发现,而非基础设施工匠。
名词解释:NumPy
Python科学计算基础库,提供高性能多维数组对象及丰富的数学函数,通过向量化运算加速计算。
名词解释:Pandas
基于NumPy构建的数据分析库,核心数据结构DataFrame和Series,擅长处理时间序列和表格数据。
名词解释:向量化运算 (Vectorization)
使用数组操作替代显式循环,利用底层C实现并行化,大幅提升执行速度。见3.3章详细讨论。
Python量化技术栈全景图
┌─────────────────────────────────────────────────────┐
│ 策略层 │
│ Zipline / Backtrader / VNPY / 自研回测框架 │
├─────────────────────────────────────────────────────┤
│ 模型层 │
│ scikit-learn / LightGBM / PyTorch / statsmodels │
├─────────────────────────────────────────────────────┤
│ 数据层 │
│ Pandas / NumPy / Dask / PySpark │
├─────────────────────────────────────────────────────┤
│ 获取层 │
│ Tushare / AKShare / yfinance / WindPy / 自定义API │
├─────────────────────────────────────────────────────┤
│ 执行层 │
│ QMT/XT API / CTP / 交易所API / 自定义网关 │
└─────────────────────────────────────────────────────┘核心库深度介绍
NumPy:一切计算的基础
NumPy的ndarray是Python数据科学的基石。对于量化交易来说,理解以下NumPy特性至关重要:
- 广播 (Broadcasting):不同形状数组间的自动对齐运算
- 花式索引 (Fancy Indexing):基于条件的快速数据筛选
- 通用函数 (ufunc):编译级别的逐元素运算
此处有展示代码展开 ▼
python
import numpy as np
# 广播:计算所有股票相对于各自均值的偏离
returns = np.random.randn(1000, 500) # 1000天, 500只股票
stock_means = returns.mean(axis=0) # shape (500,)
demeaned = returns - stock_means # (1000, 500) - (500,) -> (1000, 500) 自动广播
# 花式索引:筛选满足条件的行
high_vol_days = returns[np.abs(returns.mean(axis=1)) > 0.02]点击展开可浏览运行结果
--- demeaned (ndarray) --- ndarray shape=(1000, 500) dtype=float64 head10=[np.float64(1.1893288339609283), np.float64(0.0728123616173192), np.float64(0.4331171380710701), np.float64(-0.1967714052375393), np.float64(0.8922976167523496), np.float64(1.536297783486719), np.float64(0.11226771247117814), np.float64(-0.7151156343396026), np.float64(1.1772743120651197), np.float64(-0.19461338912431336)] --- high_vol_days (ndarray) --- ndarray shape=(664, 500) dtype=float64 head10=[np.float64(1.739583411401137), np.float64(-0.034120952875441646), np.float64(-0.9176010243475826), np.float64(0.766209485300697), np.float64(1.0686862169878393), np.float64(-1.0773722468195601), np.float64(-0.5700690593425428), np.float64(-0.2938560156956932), np.float64(1.8901395528574954), np.float64(-1.3099474237899122)] --- returns (ndarray) --- ndarray shape=(1000, 500) dtype=float64 head10=[np.float64(1.2512111309549154), np.float64(0.0794967639423766), np.float64(0.447483693662278), np.float64(-0.16526479832528823), np.float64(0.8893388532708223), np.float64(1.4692127154570833), np.float64(0.11608329609583846), np.float64(-0.6882166397106123), np.float64(1.178022332545264), np.float64(-0.18484998793086407)] --- stock_means (ndarray) --- ndarray shape=(500,) dtype=float64 head10=[np.float64(0.06188229699398715), np.float64(0.006684402325057403), np.float64(0.014366555591207893), np.float64(0.031506606912251066), np.float64(-0.0029587634815273875), np.float64(-0.06708506802963586), np.float64(0.0038155836246603204), np.float64(0.026898994628990217), np.float64(0.0007480204801442208), np.float64(0.009763401193449288)]
Pandas:量化的数据处理引擎
Pandas的两大数据结构各有专长:
- Series:一维标签数组,适合存储单只股票的数据(价格、收益率等)
- DataFrame:二维表格,适合存储多只股票的面板数据
Pandas在量化中的核心功能包括:
- 时间序列对齐和重采样
- 滚动窗口计算(移动平均、滚动标准差等)
- GroupBy操作(按行业、市值分组统计)
- 数据合并(merge/join/concat)
SciPy与Statsmodels:统计建模
- SciPy:优化(scipy.optimize)、插值(scipy.interpolate)、统计分布(scipy.stats)
- Statsmodels:OLS回归、ARIMA/GARCH、协整检验、VAR模型等经典计量方法
scikit-learn:机器学习的瑞士军刀
对于量化因子建模,scikit-learn提供了:
- 线性模型(含正则化)
- 树模型(随机森林、GBDT)
- 交叉验证和网格搜索
- 特征工程(标准化、PCA、特征选择)
- 模型评估指标
环境搭建建议
推荐使用 Miniconda 管理Python环境,避免依赖冲突:
bash
# 创建专用量化环境
conda create -n quant python=3.11
conda activate quant
# 核心科学计算
conda install numpy pandas scipy matplotlib jupyter
# 统计与计量
conda install -c conda-forge statsmodels
# 机器学习
conda install -c conda-forge scikit-learn
# 金融数据获取
pip install tushare akshare yfinance
# 高性能
conda install numba cython
pip install polars # 新一代高性能DataFrame库
# 可选:深度学习
pip install torch torchvision # PyTorchPython实战
案例1:移动平均
此处有展示代码展开 ▼
python
df['MA5'] = df['Close'].rolling(5).mean()
df['MA20'] = df['Close'].rolling(20).mean()点击展开可浏览运行结果
Close MA5 MA20 2024-02-20 83.58 84.25 87.97 2024-02-21 83.00 84.12 87.45 2024-02-22 81.98 83.48 86.87 2024-02-23 82.90 82.91 86.43 2024-02-24 84.45 83.18 86.00 2024-02-25 85.84 83.63 85.73 2024-02-26 84.58 83.95 85.39 2024-02-27 84.12 84.38 85.16 2024-02-28 84.62 84.72 85.06 2024-02-29 86.08 85.05 85.02 最新收盘: 86.08 MA5: 85.05 MA20: 85.02
案例2:Pandas面板数据操作——因子计算流水线
此处有展示代码展开 ▼
python
import pandas as pd
import numpy as np
def factor_computation_pipeline(prices_df, volumes_df=None, market_cap_df=None):
"""
一个典型的因子计算流水线,展示Pandas在量化中的核心用法
prices_df: DataFrame, index=日期, columns=股票代码, values=收盘价
volumes_df: DataFrame, 同上, values=成交量
market_cap_df: DataFrame, 同上, values=总市值
"""
# === 收益率计算 ===
daily_returns = prices_df.pct_change() # 日收益率
# === 动量因子 ===
momentum_1m = prices_df / prices_df.shift(21) - 1 # 1个月动量(~21个交易日)
momentum_3m = prices_df / prices_df.shift(63) - 1 # 3个月动量
momentum_12m = prices_df / prices_df.shift(252) - 1 # 12个月动量
momentum_12_1m = momentum_12m - momentum_1m # 12-1月动量(剔除短期反转)
# === 波动率因子 ===
volatility_1m = daily_returns.rolling(21).std() * np.sqrt(252) # 年化波动率
volatility_3m = daily_returns.rolling(63).std() * np.sqrt(252)
# === 换手率因子 ===
if volumes_df is not None:
turnover_1m = volumes_df.rolling(21).mean() # 日均成交量
turnover_ratio = volumes_df / volumes_df.rolling(252).mean() # 相对换手率
# === 流动性因子 (Amihud) ===
if volumes_df is not None:
amihud = (daily_returns.abs() / (volumes_df * prices_df)).rolling(21).mean()
# === 横截面标准化 ===
# 将每个日期的所有股票因子值标准化为均值0标准差1
def cross_sectional_zscore(factor_df):
return factor_df.sub(factor_df.mean(axis=1), axis=0).div(factor_df.std(axis=1), axis=0)
# 对动量因子做横截面标准化
mom_1m_z = cross_sectional_zscore(momentum_1m)
vol_1m_z = cross_sectional_zscore(volatility_1m)
# === 合并多个因子(等权合成) ===
composite = (mom_1m_z + (-vol_1m_z)) / 2 # 高动量 + 低波动
# === 处理缺失值 ===
composite = composite.replace([np.inf, -np.inf], np.nan)
# 前向填充缺失值(停牌日沿用前值)
composite = composite.ffill(limit=5) # 最多填充5天
# 极端值处理 (Winsorize at 1% and 99%)
lower = composite.quantile(0.01, axis=1)
upper = composite.quantile(0.99, axis=1)
composite = composite.clip(lower, upper, axis=0)
return {
'momentum_1m': momentum_1m,
'momentum_12_1m': momentum_12_1m,
'volatility_1m': volatility_1m,
'composite': composite,
'daily_returns': daily_returns
}
# 使用示例
# factors = factor_computation_pipeline(prices_df, volumes_df)点击展开可浏览运行结果
📘 本段代码定义了 1 个函数/类:函数 `factor_computation_pipeline`(一个典型的因子计算流水线,展示Pandas在量化中的核心用法)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。
案例3:高效数据管道——Pandas链式操作
此处有展示代码展开 ▼
python
import pandas as pd
import numpy as np
def efficient_data_pipeline(raw_prices):
"""
展示使用Pandas链式操作构建高效数据处理管道
"""
# 方法1: 传统的逐步操作(可读性好但效率低)
def traditional_approach(df):
df = df.copy()
df['return'] = df['Close'].pct_change()
df['MA20'] = df['Close'].rolling(20).mean()
df['MA60'] = df['Close'].rolling(60).mean()
df['signal'] = (df['MA20'] > df['MA60']).astype(int)
df['signal'] = df['signal'].shift(1)
df['strategy_return'] = df['signal'] * df['return']
return df
# 方法2: 链式操作(更优雅,Pandas推荐风格)
def chained_approach(df):
return (df
.assign(
return_=lambda d: d['Close'].pct_change(),
MA20=lambda d: d['Close'].rolling(20).mean(),
MA60=lambda d: d['Close'].rolling(60).mean()
)
.assign(
signal=lambda d: (d['MA20'] > d['MA60']).astype(int).shift(1)
)
.assign(
strategy_return=lambda d: d['signal'] * d['return_']
)
.dropna()
)
# 方法3: 使用Polars的惰性计算(LazyFrame)
def polars_approach(filepath):
import polars as pl
return (pl.scan_csv(filepath)
.with_columns([
pl.col('Close').pct_change().alias('return_'),
pl.col('Close').rolling_mean(20).alias('MA20'),
pl.col('Close').rolling_mean(60).alias('MA60'),
])
.with_columns([
(pl.col('MA20') > pl.col('MA60')).cast(pl.Int64).shift(1).alias('signal')
])
.with_columns([
(pl.col('signal') * pl.col('return_')).alias('strategy_return')
])
.collect() # 在此刻执行所有惰性计算
)
return chained_approach(raw_prices)点击展开可浏览运行结果
📘 本段代码定义了 1 个函数/类:函数 `efficient_data_pipeline`(展示使用Pandas链式操作构建高效数据处理管道)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。
常见误区
误区一:用SQL的思维写Pandas
事实:虽然Pandas提供了类似SQL的操作(merge、groupby),但这种思维模式会导致效率低下。Pandas的向量化操作、transform和rolling窗口函数提供了远超SQL的表达能力。用Pythonic的方式思考——先做列级变换,再做聚合。
误区二:忽视索引的对齐行为
事实:Pandas的自动索引对齐是其最强大的特性之一,也是最容易引发隐蔽bug的地方。两个DataFrame相加时,Pandas会按行索引和列名对齐,不对应的位置会产生NaN。在做多股票面板数据操作时,时刻注意索引结构。
误区三:所有数据都用Pandas
事实:当数据量超过内存容量时,应考虑替代方案:
- Dask:类Pandas API的分布式计算
- Polars:新一代高性能DataFrame,惰性计算+多线程
- PySpark:大规模集群计算
- SQL + Parquet:列式存储,适合TB级数据
误区四:一个环境包打天下
事实:建议为不同项目维护独立的conda环境。量化项目的依赖往往很特定(特定版本的numpy、特定的数据库驱动、特定的交易API),混合在一起很快就会陷入"依赖地狱"。
实战练习
面板数据操作:使用AkShare或Tushare获取沪深300成分股的日线数据(至少10只股票、1年数据),构建一个价格面板DataFrame(index=日期,columns=股票代码)。计算每只股票的累计收益率,找出表现最好和最差的3只。
因子计算:在上一题的面板数据基础上,计算以下因子:(a) 20日动量,(b) 20日波动率,(c) 20日日均换手率(如果有成交量数据)。对每个因子在每月末做横截面排名,将排名转换为因子分位数(0到1),然后计算下个月的收益与本月因子分位数的相关性。
Pandas vs Polars:用Pandas和Polars分别实现相同的因子计算流水线(至少包含移动平均、滚动相关系数和横截面标准化),用较大的数据集(如500只股票x1000天)测试两者的运行速度差异。
延伸阅读
- 《Python for Data Analysis》—— Wes McKinney (Pandas作者),Python数据分析的必读书
- 《Effective Pandas》—— Matt Harrison,Pandas最佳实践
- Polars官方文档 (pola.rs) —— 新一代高性能DataFrame
- 《High Performance Python》—— Gorelick and Ozsvald,Python性能优化指南
- NumPy官方教程 —— 向量化计算的深入讲解
本章要点
- Python的量化优势在于NumPy/Pandas/SciPy/scikit-learn的无缝集成
- NumPy的广播和向量化运算是高效数值计算的核心
- Pandas是量化数据处理的中枢,掌握索引对齐、滚动窗口和链式操作是必备技能
- 为不同项目维护独立的conda环境,避免依赖冲突
- 数据量超内存时考虑Polars、Dask或分布式方案