3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
编写牛市价差(Bull Call Spread)期权策略需要同时操作两个方向的期权合约:买入一个较低行权价的认购期权(Call),同时卖出一个较高行权价的认购期权(Call)。两者标的资产相同,到期日相同。
这种策略通常用于预期标的资产价格会温和上涨,通过卖出高行权价期权来降低权利金成本。
以下是基于 QMT Python API 实现的策略代码。
# -*- coding: gbk -*-
import pandas as pd
import time
import datetime
def init(ContextInfo):
# 1. 设置账户信息 (请替换为您的真实资金账号)
ContextInfo.accID = '6000000000'
ContextInfo.set_account(ContextInfo.accID)
# 2. 策略参数设置
ContextInfo.underlying = '510050.SH' # 标的资产:50ETF
ContextInfo.trade_period = '1d' # 运行周期:日线
ContextInfo.holding = False # 持仓状态标记
ContextInfo.long_leg = '' # 买入持仓合约代码
ContextInfo.short_leg = '' # 卖出持仓合约代码
# 3. 资金分配
ContextInfo.lots = 1 # 每次交易手数
print("牛市价差策略初始化完成")
def handlebar(ContextInfo):
# 获取当前K线的时间
bar_index = ContextInfo.barpos
current_time = ContextInfo.get_bar_timetag(bar_index)
current_date_str = timetag_to_datetime(current_time, '%Y%m%d')
# 1. 获取标的资产行情 (50ETF)
market_data = ContextInfo.get_market_data_ex(
['close'],
[ContextInfo.underlying],
period=ContextInfo.trade_period,
count=21, # 取过去21根K线计算均线
subscribe=True
)
if ContextInfo.underlying not in market_data:
return
close_prices = market_data[ContextInfo.underlying]['close']
current_price = close_prices.iloc[-1]
# 计算20日均线作为趋势判断
if len(close_prices) < 20:
return
ma20 = close_prices.iloc[-20:].mean()
# 2. 交易逻辑
# --- 平仓逻辑 (如果持有持仓) ---
if ContextInfo.holding:
# 检查是否临近到期 (这里简化逻辑:如果是合约到期月份的最后几天则平仓)
# 获取持仓合约的详细信息
detail = ContextInfo.get_instrumentdetail(ContextInfo.long_leg)
expire_date = str(detail['ExpireDate']) # 格式如 20231227
# 如果当前日期 >= 到期日前2天,或者 价格跌破均线,则平仓
is_near_expire = int(current_date_str) >= int(expire_date) - 2
is_trend_broken = current_price < ma20
if is_near_expire or is_trend_broken:
print(f"触发平仓: 临近到期={is_near_expire}, 趋势破坏={is_trend_broken}")
# 平掉买入腿 (卖出平仓)
buy_close_tdayfirst(ContextInfo.long_leg, ContextInfo.lots, 'LATEST', 0, ContextInfo, ContextInfo.accID)
# 平掉卖出腿 (买入平仓)
sell_close_tdayfirst(ContextInfo.short_leg, ContextInfo.lots, 'LATEST', 0, ContextInfo, ContextInfo.accID)
ContextInfo.holding = False
ContextInfo.long_leg = ''
ContextInfo.short_leg = ''
# --- 开仓逻辑 (如果没有持仓) ---
else:
# 如果价格站上20日均线,看多,构建牛市价差
if current_price > ma20:
print(f"趋势看多 (现价 {current_price:.3f} > MA20 {ma20:.3f}),构建牛市价差组合")
# A. 确定期权到期月份 (选择当月或次月)
# 获取当前年月,例如 202312
target_month = current_date_str[:6]
# B. 获取期权链
# 获取指定标的、指定月份的认购期权(CALL)列表
# 注意:get_option_list 第二个参数如果是YYYYMM格式,返回该月到期的合约
option_list = ContextInfo.get_option_list(ContextInfo.underlying, target_month, "CALL")
if not option_list:
print(f"{target_month} 无可用期权合约")
return
# C. 筛选行权价
# 我们需要找到行权价及对应的合约代码
strikes = {} # {行权价: 合约代码}
for opt_code in option_list:
detail = ContextInfo.get_instrumentdetail(opt_code)
strike_price = detail['OptExercisePrice']
strikes[strike_price] = opt_code
# 对行权价进行排序
sorted_strikes = sorted(strikes.keys())
# D. 寻找平值合约 (ATM) 和 虚值合约 (OTM)
# 找到离当前标的价格最近的行权价作为 ATM
atm_strike = min(sorted_strikes, key=lambda x: abs(x - current_price))
atm_index = sorted_strikes.index(atm_strike)
# 确保有更高行权价的合约存在
if atm_index + 1 < len(sorted_strikes):
low_strike = atm_strike # 较低行权价 (买入)
high_strike = sorted_strikes[atm_index + 1] # 较高行权价 (卖出),这里选择高一档
long_code = strikes[low_strike]
short_code = strikes[high_strike]
print(f"构建组合: 买入 {long_code}(行权价{low_strike}),卖出 {short_code}(行权价{high_strike})")
# E. 下单执行
# 1. 买入低行权价认购期权 (买入开仓)
buy_open(long_code, ContextInfo.lots, 'LATEST', 0, ContextInfo, ContextInfo.accID)
# 2. 卖出高行权价认购期权 (卖出开仓)
sell_open(short_code, ContextInfo.lots, 'LATEST', 0, ContextInfo, ContextInfo.accID)
# 更新状态
ContextInfo.holding = True
ContextInfo.long_leg = long_code
ContextInfo.short_leg = short_code
else:
print("无法找到合适的虚值合约构建价差")
# 辅助函数:时间戳转字符串
def timetag_to_datetime(timetag, format_str):
import time
return time.strftime(format_str, time.localtime(timetag / 1000))
期权合约获取 (get_option_list):
ContextInfo.get_option_list(标的代码, 月份, 类型) 获取期权列表。target_month 需要根据当前回测或实盘的时间动态生成(格式为 YYYYMM)。行权价筛选:
ContextInfo.get_instrumentdetail(合约代码) 获取合约详情,其中 OptExercisePrice 是行权价。交易指令:
buy_open (买入开仓)。sell_open (卖出开仓)。buy_close_tdayfirst (买入平仓,优先平今) 和 sell_close_tdayfirst (卖出平仓,优先平今)。到期日管理:
get_instrumentdetail 获取 ExpireDate,并在临近到期前强制平仓,避免进入行权交割流程(大部分投机策略不希望进行实物交割)。FIX)而非市价单,或者使用算法交易接口。Q: 如何修改策略为熊市价差(Bear Put Spread)?
A: 逻辑类似,但方向相反:
get_option_list 的类型从 "CALL" 改为 "PUT"。Q: 为什么使用 get_market_data_ex 而不是 get_market_data?
A: get_market_data_ex 是 QMT 推荐的新版接口,返回的数据结构更统一(DataFrame),且支持订阅模式,处理效率更高。
Q: 实盘中如何保证两腿同时成交?
A: 在普通账户中很难保证完全同时。通常建议先成交买入腿(权利方),再成交卖出腿(义务方),以防止裸卖空的风险暴露。如果拥有组合交易权限,可以使用 passorder 的组合下单类型(如 opType=40 期货组合开多等,具体需参考券商提供的组合策略类型ID)。