🚀 AI 一键生成 ptrade 策略代码

如何在 PTrade 平台实现期货跨期套利策略?(附源码)

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

问题描述

请用 Python 给我写一个股指期货的跨期套利策略范例。

解决方案

这是一个基于 PTrade 平台的股指期货跨期套利(Calendar Spread Arbitrage)策略范例。

策略原理

该策略基于价差的均值回归原理(统计套利):

  1. 选取标的:选取同一品种的两个不同到期日的合约(例如:近月合约 IF2309 和远月合约 IF2312)。
  2. 计算价差价差 = 近月合约价格 - 远月合约价格
  3. 统计指标:利用历史数据计算价差的均值(Mean)标准差(Std)
  4. 交易信号
    • 正向套利(做多价差):当 价差 < 均值 - k * 标准差 时,认为价差过小,买入近月、卖出远月。
    • 反向套利(做空价差):当 价差 > 均值 + k * 标准差 时,认为价差过大,卖出近月、买入远月。
    • 平仓:当价差回归到均值附近时,平掉双边仓位。

PTrade 策略代码

import numpy as np
import pandas as pd

def initialize(context):
    """
    策略初始化函数
    """
    # 1. 设定套利合约 (请根据实际时间修改合约代码)
    # 近月合约 (Near Leg)
    g.near_contract = 'IF2309.CCFX'
    # 远月合约 (Far Leg)
    g.far_contract = 'IF2312.CCFX'
    
    # 2. 设置股票池
    g.security = [g.near_contract, g.far_contract]
    set_universe(g.security)
    
    # 3. 策略参数设置
    g.window_size = 60   # 计算均值和标准差的历史窗口长度 (如60个周期)
    g.k_std = 2.0        # 开仓阈值:标准差的倍数
    g.lot_size = 1       # 单次交易手数
    
    # 4. 设置手续费和保证金 (可选,根据实际情况调整)
    # 设置期货保证金比例,例如 12%
    set_margin_rate('IF', 0.12)
    # 设置手续费,例如万分之0.23
    set_future_commission('IF', 0.000023)
    
    # 5. 初始化信号状态
    # 0: 空仓, 1: 持有正向套利仓位 (多近空远), -1: 持有反向套利仓位 (空近多远)
    g.status = 0 

def handle_data(context, data):
    """
    盘中运行函数,假设运行频率为分钟级
    """
    # 检查数据是否完整,如果任一合约停牌或无数据,则跳过
    if g.near_contract not in data or g.far_contract not in data:
        return
    
    # 1. 获取历史数据用于计算统计指标
    # 获取过去 g.window_size 个周期的收盘价
    hist_near = get_history(g.window_size, '1m', 'close', g.near_contract, fq=None, include=False)
    hist_far = get_history(g.window_size, '1m', 'close', g.far_contract, fq=None, include=False)
    
    # 确保数据长度足够
    if len(hist_near) < g.window_size or len(hist_far) < g.window_size:
        log.info("历史数据不足,跳过计算")
        return
    
    # 提取收盘价序列 (注意:get_history返回DataFrame,需提取values)
    # 这里假设返回的是单只股票的DF,列名为close
    close_near_arr = hist_near['close'].values
    close_far_arr = hist_far['close'].values
    
    # 2. 计算价差序列 (Spread = Near - Far)
    spread_history = close_near_arr - close_far_arr
    
    # 计算均值和标准差
    spread_mean = np.mean(spread_history)
    spread_std = np.std(spread_history)
    
    # 3. 获取当前实时价格
    current_near_price = data[g.near_contract]['close']
    current_far_price = data[g.far_contract]['close']
    current_spread = current_near_price - current_far_price
    
    # 定义上下轨
    upper_band = spread_mean + g.k_std * spread_std
    lower_band = spread_mean - g.k_std * spread_std
    
    # 4. 交易逻辑
    
    # --- 开仓逻辑 ---
    if g.status == 0:
        # 情况A: 价差过大 -> 卖近买远 (做空价差)
        if current_spread > upper_band:
            log.info("触发反向套利开仓: 价差 %.2f > 上轨 %.2f" % (current_spread, upper_band))
            # 卖出开仓近月
            sell_open(g.near_contract, g.lot_size)
            # 买入开仓远月
            buy_open(g.far_contract, g.lot_size)
            g.status = -1
            
        # 情况B: 价差过小 -> 买近卖远 (做多价差)
        elif current_spread < lower_band:
            log.info("触发正向套利开仓: 价差 %.2f < 下轨 %.2f" % (current_spread, lower_band))
            # 买入开仓近月
            buy_open(g.near_contract, g.lot_size)
            # 卖出开仓远月
            sell_open(g.far_contract, g.lot_size)
            g.status = 1
            
    # --- 平仓逻辑 ---
    elif g.status == -1: # 当前持有反向套利仓位 (空近多远)
        # 当价差回归到均值以下,平仓获利
        if current_spread <= spread_mean:
            log.info("触发反向套利平仓: 价差 %.2f 回归均值 %.2f" % (current_spread, spread_mean))
            # 买入平仓近月
            buy_close(g.near_contract, g.lot_size)
            # 卖出平仓远月
            sell_close(g.far_contract, g.lot_size)
            g.status = 0
            
    elif g.status == 1: # 当前持有正向套利仓位 (多近空远)
        # 当价差回归到均值以上,平仓获利
        if current_spread >= spread_mean:
            log.info("触发正向套利平仓: 价差 %.2f 回归均值 %.2f" % (current_spread, spread_mean))
            # 卖出平仓近月
            sell_close(g.near_contract, g.lot_size)
            # 买入平仓远月
            buy_close(g.far_contract, g.lot_size)
            g.status = 0

def after_trading_end(context, data):
    """
    盘后处理
    """
    log.info("今日交易结束,当前状态: %s" % g.status)

代码关键点解析

  1. 合约代码 (g.near_contract, g.far_contract):

    • 期货合约有到期日,代码会随时间变化(如 IF2309 代表2023年9月交割)。在实盘中,您可能需要编写逻辑自动获取主力合约和次主力合约,但在本范例中为了清晰展示逻辑,采用了硬编码。
    • 后缀 .CCFX 代表中金所。
  2. 数据获取 (get_history):

    • 我们获取了过去 g.window_size 个周期的分钟线数据来计算布林带(均值和标准差)。
    • include=False 表示不包含当前这根未走完的K线,防止信号闪烁。
  3. 下单指令:

    • buy_open: 买入开仓(做多)。
    • sell_open: 卖出开仓(做空)。
    • buy_close: 买入平仓(平空头)。
    • sell_close: 卖出平仓(平多头)。
    • 注意:PTrade 中没有原生的“组合下单”指令,因此我们是连续发送两个单子。在极端行情下,可能会出现单边成交的风险(Legging Risk),实盘中通常需要更复杂的算法来处理滑点和未成交部分。
  4. 状态管理 (g.status):

    • 使用一个简单的状态变量来防止重复开仓,并确保平仓逻辑与持仓方向匹配。

常见问题 (Q&A)

Q: 如何在回测中设置分钟级别?
A: 在 PTrade 回测界面的“基本设置”中,将“频率”设置为“分钟”,这样 handle_data 就会每分钟执行一次。

Q: 为什么使用 get_history 而不是直接用 data 计算均值?
A: data 对象只包含当前时刻(本分钟)的数据快照。要计算均值和标准差,必须使用 get_history 获取一段时间的历史序列。

Q: 实盘中如何自动换月?
A: 您可以在 before_trading_start 中调用 get_future_contracts (如果券商支持) 或通过 get_instruments 结合成交量判断主力合约,动态更新 g.near_contractg.far_contract

Q: 这个策略的风险在哪里?
A:

  1. 单边风险:如果价差持续扩大不回归(例如出现逼空行情),策略会产生亏损。通常需要设置止损线。
  2. 滑点风险:两条腿下单有时间差,可能导致成交价格偏离预期。
  3. 流动性风险:远月合约可能流动性较差,导致买卖价差过大。