问题描述
解决方案
这是一个基于PTrade平台的期货跨期套利策略(Calendar Spread Arbitrage)示例。
策略原理
跨期套利是指在同一交易所进行同一品种、不同交割月份的期货合约交易。
- 操作方向:买入近月合约(做多),同时卖出远月合约(做空)。这通常被称为“多头套利”或“买入套利”。
- 获利逻辑:预期价差(近月价格 - 远月价格)会扩大。或者当前价差过低(被低估),预期会回归均值。
- 交易逻辑:
- 计算价差:
Spread = Price_Near - Price_Far - 开仓:当
Spread < 开仓阈值时,买入近月,卖出远月。 - 平仓:当
Spread > 平仓阈值时,卖出近月,买入远月(平掉双边仓位)。
- 计算价差:
PTrade 策略代码
def initialize(context):
"""
策略初始化函数
"""
# 1. 设置套利合约对 (请根据实际时间修改合约代码)
# 近月合约 (做多的一端)
g.near_contract = 'IF2306.CCFX'
# 远月合约 (做空的一端)
g.far_contract = 'IF2309.CCFX'
# 2. 设置股票池/期货池
set_universe([g.near_contract, g.far_contract])
# 3. 策略参数设置
# 开仓阈值:当价差小于此值时开仓 (例如价差为负且很深,预期回归)
g.entry_threshold = -20.0
# 平仓阈值:当价差大于此值时止盈/平仓
g.exit_threshold = 10.0
# 单次交易手数
g.trade_unit = 1
# 4. 设置手续费 (可选,根据实际品种设置,此处以万分之0.23为例)
# set_future_commission(g.near_contract, 0.000023)
# set_future_commission(g.far_contract, 0.000023)
# 5. 设置保证金比例 (可选,此处设为15%)
# set_margin_rate(g.near_contract, 0.15)
# set_margin_rate(g.far_contract, 0.15)
log.info("策略初始化完成,近月: %s, 远月: %s" % (g.near_contract, g.far_contract))
def handle_data(context, data):
"""
盘中运行函数,每个tick或分钟执行一次
"""
# 检查数据是否包含我们的合约
if g.near_contract not in data or g.far_contract not in data:
return
# 1. 获取最新价格
price_near = data[g.near_contract]['close']
price_far = data[g.far_contract]['close']
# 2. 计算当前价差 (近月 - 远月)
current_spread = price_near - price_far
# 3. 获取当前持仓信息
# 获取近月合约的多头持仓
pos_near = get_position(g.near_contract)
long_pos_near = pos_near.long_amount if pos_near else 0
# 获取远月合约的空头持仓
pos_far = get_position(g.far_contract)
short_pos_far = pos_far.short_amount if pos_far else 0
# 记录日志方便调试
# log.info("当前价差: %.2f, 持仓状态(近多/远空): %d/%d" % (current_spread, long_pos_near, short_pos_far))
# 4. 交易逻辑
# --- 开仓逻辑 ---
# 如果当前没有持仓,且价差低于开仓阈值 -> 买近卖远
if long_pos_near == 0 and short_pos_far == 0:
if current_spread < g.entry_threshold:
log.info("触发开仓信号: 价差 %.2f < 阈值 %.2f" % (current_spread, g.entry_threshold))
# 买入开仓近月
buy_open(g.near_contract, g.trade_unit)
# 卖出开仓远月
sell_open(g.far_contract, g.trade_unit)
# --- 平仓逻辑 ---
# 如果持有套利仓位,且价差回归到平仓阈值之上 -> 平仓获利
elif long_pos_near > 0 and short_pos_far > 0:
if current_spread > g.exit_threshold:
log.info("触发平仓信号: 价差 %.2f > 阈值 %.2f" % (current_spread, g.exit_threshold))
# 卖出平仓近月 (多平)
# 注意:上期所品种(如CU, RB)需区分平今平昨,中金所(IF, IC)通常不需要
# 这里使用 close_today=False 优先平昨,通用性较好
sell_close(g.near_contract, g.trade_unit, close_today=False)
# 买入平仓远月 (空平)
buy_close(g.far_contract, g.trade_unit, close_today=False)
def before_trading_start(context, data):
"""
盘前处理
"""
pass
def after_trading_end(context, data):
"""
盘后处理
"""
pass
代码关键点解析
-
合约代码 (
g.near_contract,g.far_contract):- 期货合约代码包含后缀,例如中金所是
.CCFX(如IF2306.CCFX),上期所是.XSGE(如RB2310.XSGE)。 - 注意:期货合约会到期,实盘中需要定期手动修改代码,或者编写更复杂的逻辑来自动识别主力合约。
- 期货合约代码包含后缀,例如中金所是
-
价差计算 (
current_spread):- 逻辑核心是
近月价格 - 远月价格。 - 如果
current_spread是负数(例如 -30),说明远月比近月贵(升水结构)。 - 如果预期价差会缩小(即变得不那么负,或者变正),则买入近月、卖出远月。
- 逻辑核心是
-
持仓判断 (
get_position):- PTrade中,期货的
Position对象区分多头和空头。 long_amount: 多头持仓量。short_amount: 空头持仓量。- 策略中我们检查
long_pos_near(近月是否有多单) 和short_pos_far(远月是否有空单) 来确认是否持有套利组合。
- PTrade中,期货的
-
下单函数:
buy_open: 买入开仓(做多)。sell_open: 卖出开仓(做空)。sell_close: 卖出平仓(平多单)。buy_close: 买入平仓(平空单)。
-
平今仓与平昨仓 (
close_today):- 代码中
close_today=False表示优先平昨仓(历史持仓)。 - 如果是上海期货交易所(如螺纹钢 RB、铜 CU),平今仓手续费可能极高,通常建议优先平昨。如果是中金所(IF, IC, IM),该参数影响较小,但设为
False是比较安全的默认做法。
- 代码中
如何在 PTrade 中运行
- 新建一个策略,将上述代码复制进去。
- 修改合约代码:将
IF2306.CCFX和IF2309.CCFX替换为当前市场上真实存在的、有流动性的合约代码。 - 设置回测参数:选择期货账户,设置初始资金(如 1,000,000),频率建议选择“分钟线”以获得更精确的价差捕捉。
- 运行回测查看效果。
Q&A
Q: 如何让策略自动选择主力合约,而不是手动硬编码?
A: 可以使用 get_instruments 或 get_future_contracts (如果券商支持) 获取某品种的所有合约,然后根据持仓量 (amount) 或 成交量 (volume) 进行排序,选出最大的两个作为近月和远月主力。这需要编写额外的逻辑函数放在 before_trading_start 中。
Q: 为什么我的单子没有成交?
A: 1. 检查资金是否足够支付保证金。2. 检查合约代码是否已过期或错误。3. 在回测中,如果使用限价单 (limit_price) 可能会因为价格未触及而不成交,本示例默认使用市价(回测中通常以收盘价/最新价成交)。
Q: 什么是“移仓换月”?
A: 当近月合约临近交割时,必须平掉近月合约,并重新在新的合约上开仓,以保持套利组合的存在。本简单示例未包含自动移仓逻辑,实盘中需注意合约到期日。