🚀 AI 一键生成 ptrade 策略代码

PTrade 期权教程:买入跨式(Straddle)套利策略实现

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

问题描述

如何编写期权跨式套利策略?

解决方案

这是一个关于如何在PTrade中编写**期权跨式套利(Long Straddle)**策略的详细指南。

策略原理:买入跨式套利 (Long Straddle)

跨式套利是一种波动率策略,适用于你预期标的资产(如50ETF)会有大幅波动,但无法确定方向(大涨或大跌)的场景。

  • 操作方式:同时买入一张平值(ATM)看涨期权(Call)和一张平值(ATM)看跌期权(Put)。两者的标的资产、行权价、到期日均相同。
  • 获利条件:标的资产价格大幅偏离行权价(无论上涨还是下跌),且涨跌幅超过权利金成本。
  • 风险:如果标的资产价格横盘不动,时间价值(Theta)流逝会导致亏损。

策略实现步骤

在PTrade中实现该策略,主要包含以下逻辑:

  1. 获取标的价格:获取标的ETF(如510050.SS)的当前价格。
  2. 筛选合约
    • 获取该标的的所有期权合约。
    • 筛选出当月或次月到期的合约。
    • 找到行权价最接近当前标的价格的合约(平值合约)。
  3. 下单交易:同时买入Call和Put。
  4. 止盈止损:设定获利目标或最大亏损(时间止损)。

PTrade 策略代码

以下是一个完整的动态跨式套利策略代码。代码会自动寻找平值期权进行开仓。

import numpy as np

def initialize(context):
    """
    策略初始化函数
    """
    # 1. 设置标的资产:上证50ETF
    g.underlying = '510050.SS'
    
    # 2. 设置策略参数
    g.target_month_index = 0  # 0表示当月,1表示次月
    g.open_position = False   # 开仓标记
    g.call_contract = None    # 记录持仓的Call代码
    g.put_contract = None     # 记录持仓的Put代码
    
    # 3. 设定基准
    set_universe([g.underlying])
    set_benchmark(g.underlying)
    
    # 4. 设置手续费(根据券商实际情况调整,期权通常按张收费)
    # 注意:PTrade回测中期权手续费设置需参考具体文档,此处仅为示例
    # set_commission(...) 
    
    # 5. 每天开盘后运行策略
    run_daily(context, trade_logic, time='10:00')

def get_atm_options(context, underlying):
    """
    获取平值(ATM)的Call和Put合约
    """
    # 获取标的当前价格
    snapshot = get_snapshot(underlying)
    if not snapshot:
        return None, None
    current_price = snapshot[underlying]['last_px']
    
    # 获取该标的的所有期权合约
    # 注意:get_option_contracts 是PTrade常用获取期权链的API
    # 如果您的环境不支持,需确认是否有权限或使用 get_instruments 配合筛选
    try:
        options = get_option_contracts(underlying)
    except:
        log.error("无法获取期权合约列表,请检查权限或API版本")
        return None, None

    if not options:
        return None, None

    # 获取所有合约的详细信息
    instruments = [get_instruments(x) for x in options]
    
    # 1. 筛选出认购(Call)和认沽(Put)
    # 2. 筛选出目标到期月份的合约
    # 这里简化逻辑:先找到所有到期日,排序,取最近的一个到期日(当月)
    all_dates = sorted(list(set([i.delivery_date for i in instruments])))
    
    # 过滤掉已经过期的(如果回测时间在交割日当天,需小心处理)
    valid_dates = [d for d in all_dates if d > context.blotter.current_dt.strftime('%Y%m%d')]
    
    if len(valid_dates) <= g.target_month_index:
        return None, None
        
    target_date = valid_dates[g.target_month_index]
    
    # 筛选出目标到期日的合约
    target_options = [i for i in instruments if i.delivery_date == target_date]
    
    # 寻找平值合约:行权价与当前价格差值最小的
    # 创建一个列表:(合约代码, 行权价, 类型)
    # option_type: 'C' for Call, 'P' for Put
    candidates = []
    for opt in target_options:
        # PTrade中 get_instruments 返回的对象通常包含 option_type 或类似字段
        # 假设 contract_name 包含 "购" 或 "沽" 或者通过 option_type 判断
        # 这里使用 PTrade 标准字段判断
        otype = ''
        if hasattr(opt, 'option_type'):
            otype = opt.option_type
        
        # 如果没有option_type,尝试从名称判断 (备用方案)
        if not otype:
            if '购' in opt.contract_name: otype = 'C'
            elif '沽' in opt.contract_name: otype = 'P'
            
        # 获取行权价 (exercise_price 或 strike_price)
        strike = 0.0
        if hasattr(opt, 'exercise_price'):
            strike = opt.exercise_price
        elif hasattr(opt, 'strike_price'): # 部分版本字段名可能不同
            strike = opt.strike_price
            
        candidates.append({
            'code': opt.contract_code, # 或 trade_code / symbol
            'strike': strike,
            'type': otype
        })
    
    if not candidates:
        return None, None

    # 找到最接近当前价格的行权价
    strikes = sorted(list(set([x['strike'] for x in candidates])))
    atm_strike = min(strikes, key=lambda x: abs(x - current_price))
    
    # 获取该行权价对应的Call和Put
    call_code = None
    put_code = None
    
    for c in candidates:
        if c['strike'] == atm_strike:
            if c['type'] == 'C':
                call_code = c['code']
            elif c['type'] == 'P':
                put_code = c['code']
                
    return call_code, put_code

def trade_logic(context):
    """
    交易主逻辑
    """
    # 检查是否已有持仓
    has_position = False
    positions = context.portfolio.positions
    
    # 简单的持仓检查
    if g.call_contract and g.call_contract in positions and positions[g.call_contract].amount > 0:
        has_position = True
    
    # --- 开仓逻辑 ---
    if not has_position:
        log.info("当前无持仓,尝试构建跨式组合...")
        
        # 获取平值合约
        call_code, put_code = get_atm_options(context, g.underlying)
        
        if call_code and put_code:
            log.info("选定合约 - Call: %s, Put: %s" % (call_code, put_code))
            
            # 买入一张 Call
            order(call_code, 1)
            # 买入一张 Put
            order(put_code, 1)
            
            # 记录当前持仓
            g.call_contract = call_code
            g.put_contract = put_code
            g.open_position = True
        else:
            log.info("未找到合适的期权合约")
            
    # --- 平仓逻辑 (示例) ---
    else:
        # 这里演示简单的止盈止损逻辑
        # 实际策略中,可以基于组合的Delta、Gamma或总盈亏比例来平仓
        
        # 获取持仓对象
        pos_call = get_position(g.call_contract)
        pos_put = get_position(g.put_contract)
        
        # 计算总浮动盈亏
        total_pnl = pos_call.pnl + pos_put.pnl
        
        # 假设初始成本 (粗略计算,实际应记录开仓均价 * 合约乘数)
        # 这里仅做逻辑演示:如果总盈利超过一定金额,或者亏损超过一定金额,则平仓
        
        # 止盈:假设赚了500元
        if total_pnl > 500:
            log.info("触发止盈,当前盈利: %s" % total_pnl)
            order_target(g.call_contract, 0)
            order_target(g.put_contract, 0)
            g.call_contract = None
            g.put_contract = None
            
        # 止损:假设亏了300元
        elif total_pnl < -300:
            log.info("触发止损,当前亏损: %s" % total_pnl)
            order_target(g.call_contract, 0)
            order_target(g.put_contract, 0)
            g.call_contract = None
            g.put_contract = None
            
        # 到期日临近强制平仓 (防止进入交割)
        # 获取合约信息判断到期日
        # ... (此处省略具体日期判断代码,建议在get_instruments中获取delivery_date并判断)

def handle_data(context, data):
    """
    盘中运行函数(分钟级回测会用到)
    """
    pass

代码关键点解析

  1. get_option_contracts(underlying):

    • 这是PTrade中获取某标的下所有期权合约列表的关键函数。
    • 注意:如果您的PTrade版本较旧或权限受限,可能无法调用此接口。如果报错,请咨询券商开通期权量化权限。
  2. 寻找平值 (ATM) 合约:

    • 逻辑是:abs(行权价 - 标的现价) 最小的合约。
    • 代码中通过 get_instruments 获取合约详情(包括行权价 exercise_price 和到期日 delivery_date)。
  3. 下单 order:

    • 期权交易单位通常为“张”。order(code, 1) 表示买入1张。
    • 买入跨式策略要求同时持有Call和Put,因此代码中是连续下单。
  4. 风险管理:

    • 期权买方最大的敌人是时间价值衰减。如果标的横盘,Call和Put都会亏损。
    • 代码中加入了一个简单的基于盈亏金额(PnL)的平仓逻辑。在实盘中,建议使用百分比或结合隐含波动率(IV)的变化来做决策。

常见问题 Q&A

Q: 为什么回测时没有成交?

  • A:
    1. 检查是否开通了期权回测权限。
    2. 检查资金是否足够(期权权利金虽然便宜,但要注意保证金账户设置)。
    3. 检查 get_snapshot 是否在回测模式下返回了有效数据(部分回测模式不支持快照,需改用 get_price)。

Q: 如何避免行权风险?

  • A: 在策略中加入日期判断逻辑,在 delivery_date(行权日/到期日)的前1-2天强制平仓。不要持有至到期,除非你打算进行实物交割。

Q: 标的可以是股票吗?

  • A: 目前国内场内期权标的主要为ETF(50ETF, 300ETF, 500ETF等)和部分商品期货。个股期权通常为场外期权(OTC),API调用方式可能不同。