🚀 AI 一键生成 ptrade 策略代码

PTrade 统计分析:使用 statsmodels 进行时间序列检验

ptrade | 发布时间: 2025/12/23
以下内容由 EasyQuant 生成。

问题描述

如何对价格序列进行 ADF 平稳性检验?

解决方案

在量化交易中,ADF(Augmented Dickey-Fuller)检验是判断时间序列(如股票价格、价差、残差等)是否具有**平稳性(Stationarity)**的常用统计方法。

平稳性是许多统计套利策略(如配对交易、均值回归)的前提。如果一个序列是平稳的,它的均值和方差不随时间变化,价格偏离均值后有很大通过率会回归。

ADF 检验原理简述

  • 原假设 ($H_0$):序列存在单位根(Unit Root),即序列是不平稳的。
  • 备择假设 ($H_1$):序列不存在单位根,即序列是平稳的。
  • 判断标准
    • P-value(P值):如果 P值 < 显著性水平(通常为 0.05 或 0.01),则拒绝原假设,认为序列是平稳的。
    • Test Statistic(t统计量):如果 t统计量小于临界值(1%, 5%, 10% Critical Values),则拒绝原假设,认为序列是平稳的。

PTrade 实现代码

在 PTrade 中,我们可以使用 Python 标准的第三方库 statsmodels 来进行 ADF 检验。

以下是一个完整的策略示例,该策略在每天盘前获取某只股票过去 100 天的收盘价,并对其进行 ADF 检验。

# 导入 statsmodels 库中的 adfuller 函数
from statsmodels.tsa.stattools import adfuller
import numpy as np

def initialize(context):
    """
    初始化函数
    """
    # 设置要检验的标的,这里以 600519.SS (贵州茅台) 为例
    g.security = '600519.SS'
    set_universe(g.security)

def before_trading_start(context, data):
    """
    盘前处理函数,执行 ADF 检验
    """
    # 1. 获取历史数据
    # 获取过去 100 天的收盘价数据
    # 注意:为了统计检验的有效性,建议数据长度至少在 30 个以上
    history_data = get_history(100, frequency='1d', field='close', security_list=g.security)
    
    # 提取收盘价序列 (numpy array)
    close_prices = history_data['close'].values
    
    # 2. 进行 ADF 检验
    # adfuller 返回值是一个元组,包含统计量、P值等信息
    adf_result = adfuller(close_prices)
    
    # 3. 解析结果
    adf_statistic = adf_result[0]  # t统计量
    p_value = adf_result[1]        # P值
    usedlag = adf_result[2]        # 使用的滞后阶数
    nobs = adf_result[3]           # 样本数量
    critical_values = adf_result[4] # 临界值字典 (1%, 5%, 10%)
    
    # 4. 打印详细日志
    log.info("========== ADF 平稳性检验报告 ==========")
    log.info("标的: %s" % g.security)
    log.info("T-Statistic (t统计量): %f" % adf_statistic)
    log.info("P-Value (P值): %f" % p_value)
    log.info("Critical Values (临界值):")
    for key, value in critical_values.items():
        log.info("   %s: %f" % (key, value))
        
    # 5. 判断结论 (使用 5% 作为显著性水平)
    if p_value < 0.05:
        log.info("结论: P值 < 0.05,拒绝原假设,序列是【平稳】的。")
    else:
        log.info("结论: P值 >= 0.05,无法拒绝原假设,序列是【不平稳】的。")
        
        # 额外演示:通常价格本身不平稳,但收益率可能是平稳的
        # 计算一阶差分(即收益率近似值)
        diff_prices = np.diff(close_prices)
        # 剔除 NaN (如果有)
        diff_prices = diff_prices[~np.isnan(diff_prices)]
        
        adf_result_diff = adfuller(diff_prices)
        p_value_diff = adf_result_diff[1]
        log.info("--- 尝试对一阶差分(收益率)进行检验 ---")
        log.info("一阶差分 P-Value: %f" % p_value_diff)
        if p_value_diff < 0.05:
            log.info("结论: 一阶差分序列是【平稳】的。")
            
    log.info("========================================")

def handle_data(context, data):
    """
    盘中运行函数
    """
    pass

代码详解

  1. 数据获取 (get_history):
    • 使用 get_history 获取指定长度(如 100 天)的收盘价数据。ADF 检验对数据量有一定要求,数据太少会导致检验效力不足。
  2. 调用 adfuller:
    • from statsmodels.tsa.stattools import adfuller 是核心导入语句。
    • adfuller(series) 接收一个一维数组或 Series。
  3. 返回值解析:
    • result[0]: Test Statistic。负得越多越好。
    • result[1]: p-value。这是最直观的判断依据。
    • result[4]: Critical Values。这是一个字典,展示了在 1%、5%、10% 置信度下的临界值。如果 Test Statistic 小于这些临界值,也可以判定为平稳。
  4. 差分处理:
    • 在实际金融数据中,原始的价格序列(Price)通常是不平稳的(Random Walk)。
    • 通常我们需要对价格进行一阶差分(即 $P_t - P_{t-1}$,近似于收益率),差分后的序列通常是平稳的。代码中包含了一个简单的差分检验逻辑作为演示。

注意事项

  • Python 版本兼容性: 代码使用了 % 格式化字符串,而不是 f-string,以确保在 PTrade 可能使用的旧版本 Python(如 3.5)环境中也能正常运行。
  • 应用场景: 单独对一只股票的价格做 ADF 检验通常意义不大(因为大概率不平稳)。ADF 检验更多用于**配对交易(Pairs Trading)中,检验两只股票价格的价差(Spread)**或者是回归残差是否平稳。如果价差平稳,则可以进行均值回归交易。

Q&A

Q1: 为什么股票价格通常通不过 ADF 检验?
A1: 股票价格通常遵循“随机游走”模型,意味着当前的变动与过去无关,均值和方差随时间发散,因此是不平稳的。通常收益率(价格的变化量)才是平稳的。

Q2: 如果 P 值很小(例如 0.001),代表什么?
A2: P 值越小,拒绝原假设(不平稳)的证据越强。0.001 意味着有 99.9% 的信心认为该序列是平稳的,非常适合做均值回归策略。

Q3: 在 PTrade 中做配对交易,如何使用 ADF?
A3: 你需要获取两只股票的价格序列 $A$ 和 $B$,计算价差 $Spread = A - \beta \times B$($\beta$ 可通过 OLS 回归计算),然后对 $Spread$ 序列调用 adfuller。如果 $Spread$ 平稳,则两者存在协整关系。