Skip to content

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):编译级别的逐元素运算
PYTHON9 行 · 388 B
📄此处有展示代码9 行 · 388 B展开 ▼
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(0.7106465556779615), np.float64(-1.1572845558635751), np.float64(0.2772718305598975), np.float64(2.6488618002415865), np.float64(1.0230072607302223), np.float64(0.55163120135742), np.float64(0.5658169479727335), np.float64(0.48824671916520496), np.float64(-0.6351985982378608), np.float64(-0.7396050868268184)]

--- high_vol_days (ndarray) ---
ndarray shape=(651, 500) dtype=float64 head10=[np.float64(0.7050466696358674), np.float64(-1.2225831420419941), np.float64(0.22255680837900627), np.float64(2.7046363497064743), np.float64(1.0564688082459877), np.float64(0.529008584313162), np.float64(0.5550660934399854), np.float64(0.4922784205649216), np.float64(-0.6312627539710521), np.float64(-0.7730151333363183)]

--- returns (ndarray) ---
ndarray shape=(1000, 500) dtype=float64 head10=[np.float64(0.7050466696358674), np.float64(-1.2225831420419941), np.float64(0.22255680837900627), np.float64(2.7046363497064743), np.float64(1.0564688082459877), np.float64(0.529008584313162), np.float64(0.5550660934399854), np.float64(0.4922784205649216), np.float64(-0.6312627539710521), np.float64(-0.7730151333363183)]

--- stock_means (ndarray) ---
ndarray shape=(500,) dtype=float64 head10=[np.float64(-0.0055998860420941295), np.float64(-0.06529858617841895), np.float64(-0.054715022180891235), np.float64(0.055774549464887815), np.float64(0.033461547515765265), np.float64(-0.02262261704425807), np.float64(-0.01075085453274815), np.float64(0.004031701399716626), np.float64(0.0039358442668087044), np.float64(-0.03341004650949998)]

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  # PyTorch

Python实战 ​

案例1:移动平均 ​

PYTHON2 行 · 86 B
📄此处有展示代码2 行 · 86 B展开 ▼
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面板数据操作——因子计算流水线 ​

PYTHON66 行 · 2.6 KB
📄此处有展示代码66 行 · 2.6 KB展开 ▼
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)
▶ 点击展开可浏览运行结果
--- 结果 ---
========================================================
自动调用 factor_computation_pipeline(...) — 运行结果:
========================================================
[momentum_1m] DataFrame
DataFrame shape=(100, 5)
            c0  c1  c2  c3  c4
2024-01-01 NaN NaN NaN NaN NaN
2024-01-02 NaN NaN NaN NaN NaN
2024-01-03 NaN NaN NaN NaN NaN
2024-01-04 NaN NaN NaN NaN NaN
2024-01-05 NaN NaN NaN NaN NaN
2024-01-06 NaN NaN NaN NaN NaN
...(共 100 行,仅显示前 6 行)
[momentum_12_1m] DataFrame
DataFrame shape=(100, 5)
            c0  c1  c2  c3  c4
2024-01-01 NaN NaN NaN NaN NaN
2024-01-02 NaN NaN NaN NaN NaN
2024-01-03 NaN NaN NaN NaN NaN
2024-01-04 NaN NaN NaN NaN NaN
2024-01-05 NaN NaN NaN NaN NaN
2024-01-06 NaN NaN NaN NaN NaN
...(共 100 行,仅显示前 6 行)
[volatility_1m] DataFrame
DataFrame shape=(100, 5)
            c0  c1  c2  c3  c4
2024-01-01 NaN NaN NaN NaN NaN
2024-01-02 NaN NaN NaN NaN NaN
2024-01-03 NaN NaN NaN NaN NaN
2024-01-04 NaN NaN NaN NaN NaN
2024-01-05 NaN NaN NaN NaN NaN
2024-01-06 NaN NaN NaN NaN NaN
...(共 100 行,仅显示前 6 行)
[composite] DataFrame
DataFrame shape=(100, 5)
            c0  c1  c2  c3  c4
2024-01-01 NaN NaN NaN NaN NaN
2024-01-02 NaN NaN NaN NaN NaN
2024-01-03 NaN NaN NaN NaN NaN
2024-01-04 NaN NaN NaN NaN NaN
2024-01-05 NaN NaN NaN NaN NaN
2024-01-06 NaN NaN NaN NaN NaN
...(共 100 行,仅显示前 6 行)
[daily_returns] DataFrame
DataFrame shape=(100, 5)
                  c0         c1         c2        c3         c4
2024-01-01       NaN        NaN        NaN       NaN        NaN
2024-01-02  3.398274  14.347028  -1.344852  1.017475   1.830285
2024-01-03 -0.746377  -0.450227   1.073760 -0.100451  -1.852795
2024-01-04 -1.351185  -0.828614  -0.891140 -0.463389  -1.093603
2024-01-05  3.573839 -13.042772 -10.096216 -0.095644  10.234815
2024-01-06 -1.760019  -1.257713  -1.506929  0.282821  -0.358492
...(共 100 行,仅显示前 6 行)

案例3:高效数据管道——Pandas链式操作 ​

PYTHON56 行 · 1.9 KB
📄此处有展示代码56 行 · 1.9 KB展开 ▼
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),混合在一起很快就会陷入"依赖地狱"。

实战练习 ​

  1. 面板数据操作:使用AkShare或Tushare获取沪深300成分股的日线数据(至少10只股票、1年数据),构建一个价格面板DataFrame(index=日期,columns=股票代码)。计算每只股票的累计收益率,找出表现最好和最差的3只。

  2. 因子计算:在上一题的面板数据基础上,计算以下因子:(a) 20日动量,(b) 20日波动率,(c) 20日日均换手率(如果有成交量数据)。对每个因子在每月末做横截面排名,将排名转换为因子分位数(0到1),然后计算下个月的收益与本月因子分位数的相关性。

  3. 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或分布式方案