🚀 AI 一键生成 joinquant 策略代码

如何在 JoinQuant 中实现股指期货的期现套利策略?

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

问题描述

编写一个股指期货和现货 ETF 之间的套利策略。

解决方案

这是一个基于 JoinQuant(聚宽)平台的**股指期货(IF - 沪深300)与现货 ETF(510300 - 华泰柏瑞沪深300ETF)**的期现套利策略。

策略原理

该策略主要捕捉正向套利机会(Cash-and-Carry Arbitrage):

  1. 开仓条件:当期货价格显著高于现货价格(基差率 > 设定阈值,例如 1%)时,认为期货被高估。此时买入现货 ETF,同时卖空股指期货
  2. 平仓条件:当期货价格与现货价格收敛(基差率 < 设定阈值,例如 0% 或负值)时,卖出现货 ETF,同时平仓买入股指期货,赚取基差缩小的利润。
  3. 对冲比例:1手 IF 合约价值 = 期货价格 × 300。买入等市值的 ETF 进行完全对冲。

策略代码

# -*- coding: utf-8 -*-
from jqdata import *

def initialize(context):
    """
    初始化函数
    """
    # 1. 设定基准:沪深300
    set_benchmark('000300.XSHG')
    
    # 2. 开启真实价格模式(动态复权)
    set_option('use_real_price', True)
    
    # 3. 设置手续费
    # 股票/ETF:买入万3,卖出万3(ETF免印花税)
    set_order_cost(OrderCost(open_tax=0, close_tax=0, open_commission=0.0003, close_commission=0.0003, close_today_commission=0, min_commission=5), type='stock')
    # 期货:万0.23
    set_order_cost(OrderCost(open_tax=0, close_tax=0, open_commission=0.000023, close_commission=0.000023, close_today_commission=0.0023, min_commission=0), type='index_futures')
    
    # 4. 设定账户资金分配 (股票账户和期货账户)
    # 注意:实盘或回测中需要确保账户有足够的资金支持期货保证金和ETF全额购买
    init_cash = context.portfolio.starting_cash
    set_subportfolios([SubPortfolioConfig(cash=init_cash, type='stock'),
                       SubPortfolioConfig(cash=init_cash, type='index_futures')])
    
    # 5. 定义全局变量
    g.etf_code = '510300.XSHG'      # 现货标的:沪深300ETF
    g.future_underlying = 'IF'      # 期货标的:沪深300股指期货
    g.contract_multiplier = 300     # IF合约乘数
    
    # 6. 套利阈值设置
    g.open_threshold = 0.015        # 开仓阈值:期货溢价 1.5%
    g.close_threshold = 0.002       # 平仓阈值:溢价收敛至 0.2%
    
    # 记录当前持有的期货合约代码
    g.current_future_code = None

    # 设置运行频率,这里使用每分钟运行一次
    run_daily(arbitrage_logic, time='every_bar')

def arbitrage_logic(context):
    """
    套利核心逻辑,每分钟执行
    """
    # 1. 获取当前主力合约
    dominant_future = get_dominant_future(g.future_underlying)
    if not dominant_future:
        return
    
    # 如果主力合约发生切换,且持有旧合约,建议先平仓(简化逻辑)
    if g.current_future_code and g.current_future_code != dominant_future:
        close_all_positions(context)
        g.current_future_code = dominant_future
        return
    
    g.current_future_code = dominant_future
    
    # 2. 获取价格数据
    # 获取ETF最新价
    etf_data = get_price(g.etf_code, count=1, end_date=context.current_dt, frequency='1m', fields=['close'])
    if etf_data.empty:
        return
    etf_price = etf_data['close'][-1]
    
    # 获取期货最新价
    future_data = get_price(g.current_future_code, count=1, end_date=context.current_dt, frequency='1m', fields=['close'])
    if future_data.empty:
        return
    future_price = future_data['close'][-1]
    
    # 3. 计算基差率 (Spread Rate)
    # 基差率 = (期货价格 - 现货价格) / 现货价格
    # 注意:这里简单用ETF价格代替现货指数价格,实际操作中ETF可能有折溢价,但ETF是可以直接交易的资产
    spread_rate = (future_price - etf_price) / etf_price
    
    # 4. 获取当前持仓状态
    # ETF持仓
    long_position = context.portfolio.positions[g.etf_code].total_amount
    # 期货空单持仓
    short_position = context.portfolio.short_positions[g.current_future_code].total_amount
    
    # 5. 交易信号判断
    
    # --- 开仓逻辑 (正向套利:买现货,卖期货) ---
    if spread_rate > g.open_threshold and long_position == 0 and short_position == 0:
        log.info("触发开仓信号: 基差率 {:.2%}, 期货 {}, 现货 {}".format(spread_rate, future_price, etf_price))
        
        # 计算可买手数
        # 1手期货价值
        future_value_per_hand = future_price * g.contract_multiplier
        # 账户可用资金
        cash = context.portfolio.available_cash
        
        # 简易资金管理:预留20%资金作为期货保证金缓冲,剩余资金用于买ETF
        # 假设期货保证金15%,ETF全额。
        # 1组套利组合需要的资金约 = future_value_per_hand * (1 + 0.15)
        cost_per_unit = future_value_per_hand * 1.2 
        
        hands = int(cash // cost_per_unit)
        
        if hands > 0:
            # 1. 卖空期货
            order_target(g.current_future_code, 0, side='long') # 确保无多单
            op_future = order(g.current_future_code, hands, side='short')
            
            if op_future:
                # 2. 买入对应价值的ETF
                # 需买入ETF金额 = 手数 * 期货价格 * 300
                target_etf_value = hands * future_value_per_hand
                op_etf = order_value(g.etf_code, target_etf_value)
                
                if op_etf:
                    log.info("开仓成功: 卖空 {} 手期货, 买入 {:.2f} 元 ETF".format(hands, target_etf_value))
                else:
                    log.warn("ETF买入失败,尝试平仓期货以防裸空")
                    order_target(g.current_future_code, 0, side='short')
    
    # --- 平仓逻辑 (基差收敛:卖现货,平期货) ---
    elif spread_rate < g.close_threshold and long_position > 0 and short_position > 0:
        log.info("触发平仓信号: 基差率 {:.2%}".format(spread_rate))
        
        # 1. 平仓期货空单
        order_target(g.current_future_code, 0, side='short')
        
        # 2. 卖出ETF
        order_target(g.etf_code, 0)
        
        log.info("平仓完成")

    # 记录数据用于画图
    record(Spread_Rate=spread_rate)

def close_all_positions(context):
    """
    清空所有仓位
    """
    # 清空股票/ETF
    for stock in context.portfolio.positions:
        order_target(stock, 0)
    
    # 清空期货空单
    for future in context.portfolio.short_positions:
        order_target(future, 0, side='short')
        
    # 清空期货多单(如果有)
    for future in context.portfolio.long_positions:
        order_target(future, 0, side='long')

策略关键点说明

  1. 标的选择

    • 期货:使用 get_dominant_future('IF') 获取沪深300股指期货的主力合约。主力合约流动性最好,适合交易。
    • 现货:使用 510300.XSHG(华泰柏瑞沪深300ETF)。这是市场上流动性最好的300ETF之一,与指数拟合度高。
  2. 基差率计算

    • 公式:(期货价格 - ETF价格) / ETF价格
    • 当比率大于 g.open_threshold(如1.5%)时,说明期货相对于现货太贵了,存在无风险(或低风险)套利空间。
  3. 资金配比与对冲

    • 股指期货合约乘数为 300。
    • 为了实现市值中性(Delta Neutral),买入 ETF 的总市值必须等于 期货手数 * 期货价格 * 300
    • 代码中简单的资金管理逻辑是预留部分现金作为期货保证金(防止爆仓),其余资金用于购买 ETF。
  4. 交易执行

    • 先开空单期货,成功后再买入 ETF。这是为了防止 ETF 买入后期货无法开仓导致单边风险暴露。
    • 平仓时同时平掉双边头寸。
  5. 注意事项

    • T+1 限制:A股市场的 ETF 通常是 T+1 交易(部分跨境或债券ETF除外,但510300是T+1)。这意味着当天买入的 ETF 当天不能卖出。如果基差在当天迅速收敛,策略可能无法立即平仓。
    • 合约换月:代码中包含简单的换月逻辑(当主力合约变更时平仓)。在实际生产中,通常会进行“移仓换月”(Roll Over),即平掉旧合约同时开新合约。
    • 滑点与冲击成本:实际交易中需要考虑买卖价差和市场深度。

Q&A

Q: 为什么选择 ETF 而不是一篮子股票?
A: 购买沪深300的一篮子股票(300只股票)资金门槛极高且交易成本(佣金、印花税)复杂,容易产生跟踪误差。ETF 是现成的组合,交易便捷,且免收印花税,是期现套利最常用的现货工具。

Q: 什么是“负基差”套利?代码里为什么没有写?
A: 负基差套利(反向套利)需要“买入期货,卖空现货”。在A股市场,融券卖空 ETF 难度较大(券源少、成本高),因此个人投资者或小型机构通常只做正向套利(买现货卖期货)。

Q: 如何优化这个策略?
A: 1. 优化阈值:根据历史波动率动态调整开平仓阈值(如 Bollinger Bands)。2. 换月优化:在合约交割周自动移仓,而不是简单平仓。3. 资金利用率:利用 ETF 作为融资融券的担保品,进一步提高资金杠杆(需谨慎风控)。