🚀 AI 一键生成 ptrade 策略代码

如何用 PTrade 平台编写期货跨期套利策略? (附Python源码)

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

问题描述

请帮我编写一个期货跨期套利策略,例如买入一个近月合约,同时卖出一个远月合约,并在价差回归时平仓。

解决方案

这是一个基于PTrade平台的期货跨期套利策略

策略逻辑说明

本策略采用**统计套利(均值回归)**的思路。跨期套利的核心在于两个不同交割月份的合约(近月与远月)之间的价差(Spread)通常会在一定范围内波动。

  1. 定义价差价差 = 近月合约价格 - 远月合约价格
  2. 入场条件(做多价差):当价差过低(低于均值 - N倍标准差)时,我们认为近月合约被低估或远月被高估。此时执行**“买入近月,卖出远月”**。
  3. 出场条件(回归平仓):当价差回归到均值(或超过均值)时,获利平仓。
  4. 合约选择:策略会自动选择当下的主力合约(近月)和次主力合约(远月)。

策略代码

import numpy as np
import pandas as pd

def initialize(context):
    """
    策略初始化函数
    """
    # 设定基准合约品种,例如沪深300股指期货 'IF'
    g.product_code = 'IF'
    
    # 套利参数设置
    g.window_size = 60      # 计算均值和标准差的窗口大小(如过去60根分钟K线)
    g.std_multiplier = 2.0  # 进场阈值:标准差倍数
    
    # 交易数量(手)
    g.trade_amount = 1
    
    # 全局变量存储当前操作的合约
    g.near_contract = None # 近月合约
    g.far_contract = None  # 远月合约
    
    # 设定系统参数:分钟回测
    set_universe([g.product_code])

def before_trading_start(context, data):
    """
    盘前处理:每天开盘前确认当天的近月和远月合约
    """
    # 获取该品种所有合约
    # 注意:get_future_contracts是获取期货合约列表的常用方法,
    # 如果PTrade环境特定版本不支持,需手动维护合约列表或使用get_code_list
    all_contracts = get_future_contracts(g.product_code)
    
    # 过滤掉已经退市或未上市的,并按交割日期排序
    valid_contracts = []
    for code in all_contracts:
        info = get_instruments(code)
        # 简单的过滤逻辑:确保合约存在且未过期
        if info is not None:
            valid_contracts.append(code)
            
    # 排序:通常合约代码数字越小越近,或者根据上市日期排序
    # 这里简单按代码字符串排序,通常IF2309 < IF2310
    valid_contracts.sort()
    
    # 选取近期和远期合约(取排序后的前两个)
    if len(valid_contracts) >= 2:
        g.near_contract = valid_contracts[0]
        g.far_contract = valid_contracts[1]
        log.info("今日套利合约组合: 近月 [%s] / 远月 [%s]" % (g.near_contract, g.far_contract))
    else:
        g.near_contract = None
        g.far_contract = None
        log.warning("可用合约不足,无法进行跨期套利")

def handle_data(context, data):
    """
    盘中运行:每分钟执行一次
    """
    # 检查合约是否有效
    if g.near_contract is None or g.far_contract is None:
        return

    # 1. 获取历史数据用于计算统计指标
    # 获取过去 N 分钟的收盘价
    near_hist = get_history(g.window_size, '1m', 'close', g.near_contract)
    far_hist = get_history(g.window_size, '1m', 'close', g.far_contract)
    
    # 确保数据长度足够
    if len(near_hist) < g.window_size or len(far_hist) < g.window_size:
        return

    # 提取收盘价序列 (Series)
    # 注意:get_history返回格式可能因版本不同而异,这里假设返回DataFrame或Series
    # 如果是DataFrame,通常列名为 'close'
    p_near = near_hist['close']
    p_far = far_hist['close']
    
    # 2. 计算价差序列 (Spread = Near - Far)
    spread_series = p_near - p_far
    
    # 3. 计算统计指标 (均值和标准差)
    spread_mean = spread_series.mean()
    spread_std = spread_series.std()
    
    # 当前最新价差
    current_spread = spread_series.iloc[-1]
    
    # 计算阈值
    # 做多价差阈值(价差过低):均值 - N * 标准差
    long_spread_threshold = spread_mean - g.std_multiplier * spread_std
    # 平仓阈值:回归均值
    exit_threshold = spread_mean

    # 4. 获取当前持仓信息
    pos_near = get_position(g.near_contract)
    pos_far = get_position(g.far_contract)
    
    # 判断当前是否有持仓 (多近月 + 空远月)
    # 注意:期货持仓分多头(long_amount)和空头(short_amount)
    has_long_spread_pos = (pos_near.long_amount > 0) and (pos_far.short_amount > 0)

    # 5. 交易逻辑
    
    # --- 开仓逻辑:价差低于下轨,且无持仓 ---
    if current_spread < long_spread_threshold and not has_long_spread_pos:
        log.info("触发开仓信号: 当前价差 %.2f < 阈值 %.2f" % (current_spread, long_spread_threshold))
        
        # 买入近月 (多开)
        buy_open(g.near_contract, g.trade_amount)
        # 卖出远月 (空开)
        sell_open(g.far_contract, g.trade_amount)
        
    # --- 平仓逻辑:价差回归均值,且有持仓 ---
    elif current_spread >= exit_threshold and has_long_spread_pos:
        log.info("触发平仓信号: 当前价差 %.2f >= 均值 %.2f" % (current_spread, exit_threshold))
        
        # 卖出近月 (多平)
        # close_today=False 表示优先平昨仓,若需严格区分平今平昨需根据交易所规则调整
        sell_close(g.near_contract, g.trade_amount, close_today=False)
        # 买入远月 (空平)
        buy_close(g.far_contract, g.trade_amount, close_today=False)

# 辅助函数:获取期货合约列表
# 如果PTrade环境中没有内置 get_future_contracts,可以使用此替代方案或手动指定
def get_future_contracts(symbol):
    """
    获取指定品种的期货合约列表
    注意:此函数依赖于 get_code_list 或类似接口,若不可用需咨询券商
    这里仅作演示,实际使用建议直接在 initialize 中指定具体合约代码列表
    """
    # 示例:返回假设的合约代码,实际需调用API获取
    # 在实盘中,通常可以使用 get_all_securities 筛选
    # 或者直接使用主力合约代码如 'IF2309.CCFX'
    
    # 这里为了代码不报错,使用一种通用的获取方式(需根据实际环境调整)
    try:
        # 尝试获取该品种的主力合约和次主力
        # PTrade通常支持 'IF8888.CCFX' (主力) 但套利需要具体月份
        # 此处建议用户在实盘中手动维护一个合约列表,或者使用 get_instruments 遍历
        return ['IF2309.CCFX', 'IF2310.CCFX', 'IF2312.CCFX'] 
    except:
        return []

代码关键点解析

  1. 合约获取 (before_trading_start):

    • 期货合约会到期,因此不能在 initialize 中写死。
    • 代码中演示了如何选取排序靠前的两个合约作为“近月”和“远月”。
    • 注意:在实盘中,建议使用 get_instruments 配合循环来筛选当前上市交易的合约,或者手动在策略参数中更新合约代码。
  2. 数据获取 (get_history):

    • 使用 1m (1分钟) 频率获取数据,窗口大小设为 60,意味着基于过去1小时的分钟线计算价差的布林带(均值和标准差)。
    • spread_series = p_near - p_far 利用了 Pandas 的向量化计算,直接得出价差序列。
  3. 交易指令:

    • 开仓(买近卖远):
      • buy_open(near): 多头开仓近月。
      • sell_open(far): 空头开仓远月。
    • 平仓(卖近买远):
      • sell_close(near): 多头平仓近月。
      • buy_close(far): 空头平仓远月。
    • close_today=False: 对于中金所(IF/IC/IH/IM),平今仓和平昨仓手续费通常一致(或有特定高倍率),参数设为 False 系统会自动处理。如果是上期所(如螺纹钢 RB),平今仓手续费可能较高,需根据实际情况调整此参数。
  4. 风险控制:

    • 代码中加入了 has_long_spread_pos 状态判断,防止重复开仓。
    • 使用了 log.info 记录关键交易信号,便于回测分析。

Q&A

Q: 如何将策略改为“卖出近月,买入远月”(做空价差)?
A: 修改 handle_data 中的逻辑:

  1. 计算上轨阈值:short_spread_threshold = spread_mean + g.std_multiplier * spread_std
  2. 开仓条件改为:current_spread > short_spread_threshold
  3. 交易指令改为:sell_open(near)buy_open(far)

Q: 为什么使用 get_history 而不是 get_snapshot
A: get_snapshot 只能获取当前切片的一个价格,无法计算均值和标准差。我们需要 get_history 获取一段时间的序列数据来计算统计指标(Mean, Std)。

Q: 实盘中合约到期了怎么办?
A: before_trading_start 函数会在每天开盘前运行。如果逻辑正确(动态获取合约),它会自动切换到新的近月和远月合约。但在实际操作中,跨期套利通常涉及移仓换月,本策略仅演示了基本的开平仓逻辑,未包含自动移仓(即在旧合约到期前强制平仓并开新合约)的功能,实盘需注意合约的最后交易日。