🚀 AI 一键生成 ptrade 策略代码

PTrade 策略实战:如何使用股指期货对冲股票组合风险?

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

问题描述

我持有一篮子股票,如何用股指期货来对冲大盘下跌的风险?请提供一个策略框架。

解决方案

这是一个使用股指期货(如沪深300股指期货 IF)对冲股票现货持仓风险的策略框架。

策略核心逻辑

该策略属于Beta对冲策略(或是市场中性策略的一种简化版)。

  1. 多头部位:持有股票现货(一篮子股票)。
  2. 空头部位:卖空股指期货合约。
  3. 对冲目标:当大盘下跌时,期货空头的盈利抵消股票多头的亏损,从而锁定组合价值。
  4. 对冲比例:本框架采用市值对冲(假设Beta=1),即 期货空头总价值 ≈ 股票多头总市值

PTrade 策略代码框架

import numpy as np

def initialize(context):
    """
    初始化函数
    """
    # 1. 设定基准
    set_benchmark('000300.SS')
    
    # 2. 设定要持有的股票篮子 (示例:持有几只银行和科技股)
    # 实际使用时,这里应该是您实际持仓的股票列表
    g.stock_basket = ['600036.SS', '600519.SS', '600570.SS', '000001.SZ']
    
    # 3. 设定用于对冲的股指期货合约
    # 注意:实盘中需要处理合约换月逻辑,这里为了演示简化为固定合约
    # IF: 沪深300, IC: 中证500, IH: 上证50, IM: 中证1000
    g.future_contract = 'IF2312.CCFX' 
    
    # 4. 设定合约乘数 (IF为300, IC/IM/IH为200,具体需查询交易所标准)
    g.contract_multiplier = 300 
    
    # 5. 设定保证金比例 (假设为15%,具体依券商而定)
    set_margin_rate(g.future_contract[:2], 0.15)
    
    # 6. 设定交易费率 (期货费率通常较低)
    set_future_commission(g.future_contract[:2], 0.00005)
    
    # 7. 设定定时任务:每天尾盘14:50进行对冲仓位调整
    run_daily(context, adjust_hedge_position, time='14:50')

def handle_data(context, data):
    """
    盘中运行函数
    在此处模拟买入持有股票,构建底仓
    """
    # 示例:如果当前没有股票持仓,则买入篮子里的股票
    # 实际场景中,这里可能是您的选股策略逻辑
    if len(context.portfolio.positions) == 0:
        cash_per_stock = context.portfolio.cash / len(g.stock_basket) * 0.8
        for stock in g.stock_basket:
            order_value(stock, cash_per_stock)
            log.info("建仓股票: %s" % stock)

def adjust_hedge_position(context):
    """
    对冲调整逻辑:计算股票市值,调整期货空单数量
    """
    # 1. 计算当前股票持仓的总市值
    total_stock_value = 0.0
    
    # 遍历所有持仓
    positions = context.portfolio.positions
    for code, pos in positions.items():
        # 过滤掉期货持仓,只计算股票市值 (标的代码后缀判断或business_type判断)
        if '.CCFX' not in code and '.XSGE' not in code and '.XZCE' not in code and '.XDCE' not in code:
            # 获取最新价格
            snap = get_snapshot(code)
            if snap and code in snap:
                current_price = snap[code]['last_px']
                # 市值 = 持仓数量 * 最新价
                total_stock_value += pos.amount * current_price
    
    log.info("当前股票持仓总市值: %.2f" % total_stock_value)
    
    if total_stock_value <= 0:
        return

    # 2. 获取期货合约最新价格
    future_snap = get_snapshot(g.future_contract)
    if not future_snap or g.future_contract not in future_snap:
        log.warning("无法获取期货合约行情,跳过对冲调整")
        return
        
    future_price = future_snap[g.future_contract]['last_px']
    
    # 3. 计算一张期货合约的价值
    # 合约价值 = 指数点位 * 合约乘数
    contract_value = future_price * g.contract_multiplier
    
    # 4. 计算理论需要做空的合约张数
    # 理论张数 = 股票总市值 / 单张合约价值
    # 这里假设 Beta = 1,即完全等市值对冲
    target_short_count = int(round(total_stock_value / contract_value))
    
    log.info("期货价格: %.2f, 单张合约价值: %.2f, 目标空单张数: %d" % (future_price, contract_value, target_short_count))
    
    # 5. 获取当前已持有的期货空单数量
    current_short_amount = 0
    future_position = get_position(g.future_contract)
    if future_position:
        # 注意:PTrade中 sell_open 产生的持仓通常记录在 short_amount
        current_short_amount = future_position.short_amount
        
    # 6. 执行交易:调整仓位至目标张数
    diff = target_short_count - current_short_amount
    
    if diff > 0:
        # 需要增加空单 (卖出开仓)
        log.info("执行对冲:卖出开仓 %d 张 %s" % (diff, g.future_contract))
        sell_open(g.future_contract, diff)
        
    elif diff < 0:
        # 需要减少空单 (买入平仓)
        # diff是负数,取绝对值
        close_amount = abs(diff)
        log.info("减少对冲:买入平仓 %d 张 %s" % (close_amount, g.future_contract))
        # 平仓优先平今还是平昨取决于交易所规则,这里简化处理
        buy_close(g.future_contract, close_amount)
        
    else:
        log.info("当前对冲比例合适,无需调整")

代码关键点解析

  1. initialize (初始化):

    • 设置了股票池和对应的期货合约(这里使用的是 IF 沪深300期货)。
    • 设置了 run_daily 在每天 14:50 运行。选择尾盘调整是因为此时当天的股票市值波动已趋于稳定,计算出的对冲手数更准确,且避免了盘中频繁交易产生的高额手续费。
  2. adjust_hedge_position (核心对冲逻辑):

    • 计算现货市值:遍历 context.portfolio.positions,累加所有股票的市值。代码中增加了判断逻辑,确保只计算股票,排除了期货本身的持仓价值。
    • 计算合约价值期货价格 * 合约乘数(IF合约乘数为300元/点)。
    • 计算手数股票总市值 / 单张合约价值。这里使用了最简单的等市值对冲(即假设组合的 Beta 值为 1)。
    • 动态调整:对比“目标空单数”和“当前空单数”。
      • 如果股票涨了,市值增加,需要加空sell_open)。
      • 如果股票跌了,市值减少,需要平空buy_close)一部分,释放保证金并兑现期货端的盈利。
  3. API 使用:

    • get_snapshot(code): 获取最新的行情快照,用于计算实时市值。
    • sell_open: 卖出开仓(做空)。
    • buy_close: 买入平仓(平掉空头)。

风险与注意事项

  1. 基差风险 (Basis Risk): 期货价格和现货指数价格并不完全同步。在市场极度悲观时,期货可能会出现深度贴水(期货价格远低于现货),此时做空期货可能会面临基差收敛带来的额外成本。
  2. Beta 偏差: 上述代码假设 Beta=1。如果你的股票篮子波动率远大于沪深300指数(例如全仓持有高波动科技股),等市值对冲可能不够,需要计算 Beta 值来调整手数(目标张数 = (股票市值 * Beta) / 合约价值)。
  3. 合约换月: 期货合约有到期日。实盘策略必须增加逻辑,在合约到期前(通常是交割周的周二或周三)将仓位移仓换月到下个月份的合约,否则会进入交割流程。
  4. 保证金管理: 期货交易带有杠杆。虽然是对冲,但如果股市大涨,期货端会产生浮亏,需要确保账户里有足够的现金(Cash)来缴纳追加保证金,否则会被强平。

Q&A

Q: 如何处理期货合约的换月问题?
A: 您需要编写一个函数来判断当前日期距离合约交割日的天数。通常在当月合约交割日前3-5天,平掉当月合约的空单,同时在下月合约上开立等量的空单。可以使用 get_instruments 获取合约的 delivery_date

Q: 如果我的股票主要由中小盘股组成,应该选哪个期货?
A: 如果持仓主要是中小盘股票,建议使用 IC (中证500)IM (中证1000) 股指期货进行对冲,因为它们的相关性更高,对冲效果更好。IF (沪深300) 主要用于对冲大盘蓝筹股。

Q: 为什么不在盘中实时对冲?
A: 盘中股价波动剧烈,实时对冲会导致频繁开平仓,产生巨大的滑点和手续费损耗。通常对于非高频策略,每日尾盘调整一次对冲比例已经足够覆盖隔夜风险和大部分日内风险。