【期货量化实战】跨期套利策略:价差交易完整指南(TqSdk源码详解)

一、前言

跨期套利是期货市场中最经典的套利策略之一,通过同时买卖同一品种不同月份的合约,利用价差回归获利。相比单边趋势交易,套利策略风险更低、收益更稳定。

本文将介绍:

  • 跨期套利的基本原理
  • 价差计算与监控
  • 套利信号判断
  • TqSdk完整实现代码

二、为什么选择天勤量化(TqSdk)

在众多期货量化工具中,**天勤量化(TqSdk)**是目前国内最受欢迎的开源期货量化框架之一:

特点 说明
完全免费 开源免费,无需付费即可获取实时行情
数据全面 支持国内所有期货交易所的实时行情和历史数据
上手简单 几行Python代码即可获取数据
文档完善 官方文档详细,示例代码丰富

安装方法

bash 复制代码
pip install tqsdk

三、跨期套利基础

3.1 什么是跨期套利

跨期套利(Calendar Spread)是指在同一期货品种的不同交割月份合约上建立数量相等、方向相反的头寸。

类型 操作 预期
正套 买近月卖远月 价差扩大
反套 卖近月买远月 价差缩小

3.2 价差类型

价差形态 说明 常见品种
正价差(Contango) 远月价格 > 近月价格 仓储成本品种
负价差(Backwardation) 近月价格 > 远月价格 供需紧张品种

3.3 套利优势

优势 说明
风险较低 对冲了大部分方向性风险
保证金优惠 交易所通常有套利保证金优惠
收益稳定 价差波动相对可预测
适合大资金 容量大、冲击成本低

四、完整代码实现

4.1 价差监控工具

python 复制代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
功能:跨期价差实时监控
说明:本代码仅供学习参考
"""

from tqsdk import TqApi, TqAuth

# ============ 合约配置 ============
SYMBOL_NEAR = "SHFE.rb2505"   # 近月合约
SYMBOL_FAR = "SHFE.rb2510"    # 远月合约

# ============ 初始化 ============
api = TqApi(auth=TqAuth("快期账户", "快期密码"))

quote_near = api.get_quote(SYMBOL_NEAR)
quote_far = api.get_quote(SYMBOL_FAR)

print("=" * 60)
print("跨期价差监控")
print("=" * 60)
print(f"近月合约: {SYMBOL_NEAR}")
print(f"远月合约: {SYMBOL_FAR}")
print("-" * 60)

# 价差历史
spread_history = []

while True:
    api.wait_update()
    
    # 获取最新价格
    price_near = quote_near.last_price
    price_far = quote_far.last_price
    
    # 计算价差(远月 - 近月)
    spread = price_far - price_near
    spread_history.append(spread)
    
    # 保留最近100个数据计算统计值
    if len(spread_history) > 100:
        spread_history = spread_history[-100:]
    
    # 计算统计指标
    avg_spread = sum(spread_history) / len(spread_history)
    max_spread = max(spread_history)
    min_spread = min(spread_history)
    
    print(f"\r近月={price_near:.0f} 远月={price_far:.0f} "
          f"价差={spread:.0f} 均值={avg_spread:.0f} "
          f"区间=[{min_spread:.0f}, {max_spread:.0f}]", end="")

4.2 完整套利策略

python 复制代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
功能:跨期套利策略
说明:本代码仅供学习参考
"""

from tqsdk import TqApi, TqAuth, TqSim
from tqsdk.lib import TargetPosTask
import numpy as np

# ============ 合约配置 ============
SYMBOL_NEAR = "SHFE.rb2505"   # 近月合约
SYMBOL_FAR = "SHFE.rb2510"    # 远月合约
LOTS = 1                      # 每腿手数

# ============ 策略参数 ============
LOOKBACK = 60                 # 回看周期
ENTRY_THRESHOLD = 2.0        # 开仓阈值(标准差倍数)
EXIT_THRESHOLD = 0.5         # 平仓阈值
MAX_HOLD_BARS = 100          # 最大持仓K线数

# ============ 初始化 ============
api = TqApi(TqSim(init_balance=500000), auth=TqAuth("快期账户", "快期密码"))

quote_near = api.get_quote(SYMBOL_NEAR)
quote_far = api.get_quote(SYMBOL_FAR)
kline_near = api.get_kline_serial(SYMBOL_NEAR, 300, LOOKBACK + 20)
kline_far = api.get_kline_serial(SYMBOL_FAR, 300, LOOKBACK + 20)

# 持仓管理
target_pos_near = TargetPosTask(api, SYMBOL_NEAR)
target_pos_far = TargetPosTask(api, SYMBOL_FAR)

account = api.get_account()

print("=" * 60)
print("跨期套利策略")
print("=" * 60)
print(f"近月: {SYMBOL_NEAR}")
print(f"远月: {SYMBOL_FAR}")
print(f"开仓阈值: {ENTRY_THRESHOLD}倍标准差")
print(f"平仓阈值: {EXIT_THRESHOLD}倍标准差")
print("-" * 60)

# 状态变量
position = 0       # 1=正套(买近卖远), -1=反套(卖近买远), 0=空仓
hold_bars = 0      # 持仓K线数

while True:
    api.wait_update()
    
    if api.is_changing(kline_near.iloc[-1], "datetime"):
        # 获取收盘价序列
        close_near = kline_near["close"].values[-LOOKBACK:]
        close_far = kline_far["close"].values[-LOOKBACK:]
        
        # 计算价差序列
        spread = close_far - close_near
        
        # 统计指标
        spread_mean = np.mean(spread)
        spread_std = np.std(spread)
        
        # 当前价差
        current_spread = close_far[-1] - close_near[-1]
        
        # 计算z-score
        z_score = (current_spread - spread_mean) / spread_std if spread_std > 0 else 0
        
        print(f"\n价差={current_spread:.0f} 均值={spread_mean:.0f} "
              f"标准差={spread_std:.0f} Z值={z_score:.2f}")
        
        # ============ 交易逻辑 ============
        if position == 0:
            # 无持仓,判断开仓
            if z_score > ENTRY_THRESHOLD:
                # 价差过大,做反套(卖近买远)
                target_pos_near.set_target_volume(-LOTS)
                target_pos_far.set_target_volume(LOTS)
                position = -1
                hold_bars = 0
                print(f"[开仓] 反套:卖{SYMBOL_NEAR} 买{SYMBOL_FAR}")
                
            elif z_score < -ENTRY_THRESHOLD:
                # 价差过小,做正套(买近卖远)
                target_pos_near.set_target_volume(LOTS)
                target_pos_far.set_target_volume(-LOTS)
                position = 1
                hold_bars = 0
                print(f"[开仓] 正套:买{SYMBOL_NEAR} 卖{SYMBOL_FAR}")
        
        else:
            # 有持仓,判断平仓
            hold_bars += 1
            
            should_close = False
            close_reason = ""
            
            # 条件1:价差回归
            if position == 1 and z_score > -EXIT_THRESHOLD:
                should_close = True
                close_reason = "价差回归"
            elif position == -1 and z_score < EXIT_THRESHOLD:
                should_close = True
                close_reason = "价差回归"
            
            # 条件2:超时平仓
            if hold_bars >= MAX_HOLD_BARS:
                should_close = True
                close_reason = "持仓超时"
            
            if should_close:
                target_pos_near.set_target_volume(0)
                target_pos_far.set_target_volume(0)
                print(f"[平仓] {close_reason},持仓{hold_bars}根K线")
                position = 0
                hold_bars = 0
        
        # 显示账户状态
        print(f"  账户权益: {account.balance:.0f} 浮盈: {account.float_profit:.0f}")

4.3 代码解析

代码 说明
spread = far - near 计算价差
z_score = (spread - mean) / std 标准化价差
z_score > 2.0 价差偏大,做反套
z_score < -2.0 价差偏小,做正套
abs(z_score) < 0.5 价差回归,平仓

五、价差分析工具

python 复制代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
功能:价差统计分析
说明:本代码仅供学习参考
"""

from tqsdk import TqApi, TqAuth
import numpy as np

# ============ 合约配置 ============
SYMBOL_NEAR = "SHFE.rb2505"
SYMBOL_FAR = "SHFE.rb2510"

api = TqApi(auth=TqAuth("快期账户", "快期密码"))

# 获取日K线
kline_near = api.get_kline_serial(SYMBOL_NEAR, 86400, 60)
kline_far = api.get_kline_serial(SYMBOL_FAR, 86400, 60)

api.wait_update()

# 计算价差
close_near = kline_near["close"].values
close_far = kline_far["close"].values
spread = close_far - close_near

# 统计分析
print("=" * 50)
print("价差统计分析")
print("=" * 50)
print(f"数据天数: {len(spread)}")
print("-" * 50)

# 基础统计
print(f"均值: {np.mean(spread):.2f}")
print(f"标准差: {np.std(spread):.2f}")
print(f"最大值: {np.max(spread):.2f}")
print(f"最小值: {np.min(spread):.2f}")
print(f"当前值: {spread[-1]:.2f}")

# 分位数
print(f"\n分位数:")
for p in [10, 25, 50, 75, 90]:
    print(f"  {p}%分位: {np.percentile(spread, p):.2f}")

# Z-Score
current_z = (spread[-1] - np.mean(spread)) / np.std(spread)
print(f"\n当前Z-Score: {current_z:.2f}")

if current_z > 2:
    print("建议: 价差偏大,可考虑反套")
elif current_z < -2:
    print("建议: 价差偏小,可考虑正套")
else:
    print("建议: 价差正常,观望")

api.close()

六、套利注意事项

6.1 合约选择

要点 说明
流动性 选择成交量大的合约
月份间隔 通常选3-6个月间隔
避免临近交割 近月合约避免进入交割月

6.2 风险提示

风险 说明 应对
价差不回归 价差持续扩大或缩小 设置止损
流动性风险 无法同时成交 选流动性好的合约
保证金风险 极端行情追保 控制仓位
交割风险 近月合约进入交割 提前移仓

七、进阶技巧

7.1 动态阈值

python 复制代码
# 根据波动率动态调整开仓阈值
def get_dynamic_threshold(spread_std, base_threshold=2.0):
    """
    波动率大时提高阈值,波动率小时降低阈值
    """
    # 假设历史标准差为50
    NORMAL_STD = 50
    ratio = spread_std / NORMAL_STD
    
    # 限制调整范围
    ratio = max(0.5, min(ratio, 2.0))
    
    return base_threshold * ratio

7.2 价差比率

python 复制代码
# 使用价差比率而非绝对价差
spread_ratio = close_far / close_near - 1

# 适用于不同价位品种的横向比较

八、总结

要点 内容
策略原理 利用价差回归获利
核心指标 Z-Score标准化价差
开仓条件 Z值超过±2倍标准差
平仓条件 Z值回归到±0.5以内
风险控制 设置止损和持仓时间限制

免责声明:本文仅供学习交流使用,不构成任何投资建议。期货交易有风险,入市需谨慎。

更多资源

相关推荐
科技小花1 天前
全球化深水区,数据治理成为企业出海 “核心竞争力”
大数据·数据库·人工智能·数据治理·数据中台·全球化
X56611 天前
如何在 Laravel 中正确保存嵌套动态表单数据(主服务与子服务)
jvm·数据库·python
超级码力6661 天前
【Latex文件架构】Latex文件架构模板
算法·数学建模·信息可视化
ZhengEnCi1 天前
03ab-PyTorch安装教程 📚
python
穿条秋裤到处跑1 天前
每日一道leetcode(2026.04.29):二维网格图中探测环
算法·leetcode·职场和发展
Merlos_wind1 天前
HashMap详解
算法·哈希算法·散列表
狐狐生风1 天前
LangChain 向量存储:Chroma、FAISS
人工智能·python·学习·langchain·faiss·agentai
虹科网络安全1 天前
艾体宝干货|数据复制详解:类型、原理与适用场景
java·开发语言·数据库
狐狐生风1 天前
LangChain RAG 基础
人工智能·python·学习·langchain·rag·agentai
2301_771717211 天前
解决mysql报错:1406, Data too long for column
android·数据库·mysql