问题描述
解决方案
这是一个基于 QMT 平台的国债逆回购自动化策略。
策略逻辑说明
- 运行时间:策略设定在每日收盘前(例如 15:10)自动运行,利用账户闲置资金进行逆回购,不影响日内股票交易。
- 标的选择:自动比较 上交所 GC001 (204001.SH) 和 深交所 R-001 (131810.SZ) 的实时收益率(买一价),选择收益率较高的品种下单。
- 资金利用:自动读取账户可用资金,预留少量资金后,全仓参与。
- 交易方向:国债逆回购在交易软件中表现为 “卖出”(即借出资金,获得债券质押)。
- 防重单:增加日期标记,防止同一天重复下单。
策略代码
# -*- coding: gbk -*-
import time
def init(ContextInfo):
"""
初始化函数
"""
# ================= 策略参数设置 =================
# 请在此处填写您的资金账号
ContextInfo.account_id = 'YOUR_ACCOUNT_ID'
# 账号类型:'STOCK' (股票), 'CREDIT' (信用)
ContextInfo.account_type = 'STOCK'
# 逆回购代码:GC001(沪), R-001(深)
ContextInfo.repo_codes = ['204001.SH', '131810.SZ']
# 触发时间:建议在 15:00 之后,15:30 之前
# 股票交易结束后利用闲置资金,避免影响正常交易
ContextInfo.trigger_time = "15:10"
# 预留资金(元),防止资金完全占满导致扣费失败等情况
ContextInfo.reserve_cash = 1000
# 用于记录当天是否已经交易过的标记
ContextInfo.traded_date = None
# 绑定账号,接收回报
ContextInfo.set_account(ContextInfo.account_id)
print("国债逆回购策略初始化完成,将在 {} 触发".format(ContextInfo.trigger_time))
def handlebar(ContextInfo):
"""
K线/行情驱动函数
"""
# 只在实时行情的最末端运行,回测模式或历史K线不运行
if not ContextInfo.is_last_bar():
return
# 获取当前日期和时间
current_date = time.strftime('%Y%m%d', time.localtime(time.time()))
current_time = time.strftime('%H:%M', time.localtime(time.time()))
# 1. 检查是否到达触发时间
if current_time != ContextInfo.trigger_time:
return
# 2. 检查当天是否已经交易过,防止重复下单
if ContextInfo.traded_date == current_date:
return
print("到达触发时间: {}, 开始执行逆回购逻辑...".format(current_time))
# 3. 获取账户资金情况
# get_trade_detail_data 获取 'ACCOUNT' 类型数据
acct_info_list = get_trade_detail_data(ContextInfo.account_id, ContextInfo.account_type, 'ACCOUNT')
if not acct_info_list:
print("未获取到账户信息,请检查账号配置或连接状态")
return
# 获取可用资金
available_cash = acct_info_list[0].m_dAvailable
print("当前账户可用资金: {:.2f}".format(available_cash))
# 计算可用于逆回购的资金
trade_cash = available_cash - ContextInfo.reserve_cash
# 逆回购门槛通常为 1000 元
if trade_cash < 1000:
print("可用资金不足 1000 元,不执行逆回购")
ContextInfo.traded_date = current_date # 标记今日已检查
return
# 4. 获取逆回购品种的实时行情
# 获取全推行情数据
quotes = ContextInfo.get_full_tick(ContextInfo.repo_codes)
best_code = None
best_rate = -1.0
# 比较各品种收益率
for code in ContextInfo.repo_codes:
if code in quotes:
tick = quotes[code]
# 逆回购操作是“卖出”,所以我们看对手的“买一价”(bidPrice)
# 注意:get_full_tick 返回的 bidPrice 是一个列表,bidPrice[0] 即买一价
# 逆回购的价格即为年化收益率
try:
# 防御性编程,确保有买一价
if 'bidPrice' in tick and len(tick['bidPrice']) > 0:
rate = tick['bidPrice'][0]
# 过滤掉无效数据(如0或极小值)
if rate > 0.01:
print("品种: {} 当前年化收益率(买一价): {:.3f}%".format(code, rate))
if rate > best_rate:
best_rate = rate
best_code = code
except Exception as e:
print("获取 {} 行情异常: {}".format(code, e))
if best_code is None:
print("未获取到有效的逆回购行情,取消交易")
return
print("最优品种: {}, 收益率: {:.3f}%".format(best_code, best_rate))
# 5. 计算下单数量
# 国债逆回购 1 张 = 100 元面值
# 交易单位:手(1手=10张=1000元面值)
# 交易所下单数量通常指“张”数(100元为单位),或者是“股”数。
# 在 QMT/券商柜台中,通常下单数量填 100 的整数倍(即 100 张 = 10000 元)。
# 实际上:1000元 = 10张。
# 计算公式:资金 / 100 * 1 (因为单位是张,每张100元)
# 为了稳妥,向下取整到 10 张的整数倍(即 1000 元的整数倍)
# 计算有多少个 1000 元
units_of_1000 = int(trade_cash / 1000)
# 转换为张数 (1000元 = 10张)
order_volume = units_of_1000 * 10
if order_volume <= 0:
print("计算下单数量为 0,不执行")
return
# 6. 下单
# opType: 24 (股票卖出/逆回购卖出)
# orderType: 1101 (单股单账号普通股/手方式下单)
# priceType: 11 (指定价/模型价) - 这里我们用对手价或者买一价下单,为保证成交,直接用刚才获取的 best_rate
# 逆回购本质是把钱借出去,操作上是“卖出”
print("正在下单: 代码={}, 价格={:.3f}, 数量={}".format(best_code, best_rate, order_volume))
passorder(
24, # 操作类型:24-卖出
1101, # 下单方式:单账号、股数
ContextInfo.account_id,
best_code,
11, # 报价类型:11-指定价 (使用 best_rate)
best_rate, # 价格
order_volume, # 数量
ContextInfo
)
# 标记今日已交易
ContextInfo.traded_date = current_date
print("逆回购下单指令已发送")
使用说明
- 修改账号:
- 找到代码中的
ContextInfo.account_id = 'YOUR_ACCOUNT_ID',将'YOUR_ACCOUNT_ID'替换为您真实的 QMT 资金账号。
- 找到代码中的
- 运行模式:
- 请在 “实盘” 模式下运行。
- 请确保 QMT 客户端已登录交易账号。
- 触发时间:
- 代码默认设置为
15:10。此时股票交易已结束,不会占用股票交易资金。国债逆回购的交易时间延长至 15:30,因此这个时间点是安全的。
- 代码默认设置为
- 下单逻辑细节:
- 方向:代码中使用
opType=24(卖出)。这是因为逆回购在交易系统中定义为“卖出”操作(卖出资金使用权)。 - 价格:使用实时的“买一价”(即借款人愿意支付的利息)。
- 数量:代码自动计算可用资金,按照 1000 元(10张)为最小单位向下取整,确保下单合规。
- 方向:代码中使用
常见问题 (Q&A)
Q: 为什么逆回购是“卖出”而不是“买入”?
A: 在交易所规则中,逆回购方(借出资金方)是融出资金,获得债券质押权。在交易终端的操作习惯上,逆回购对应的是“卖出”指令(代码 GCxxx 或 R-xxx)。
Q: 为什么数量是 trade_cash / 1000 * 10?
A: 沪深两市逆回购的门槛都是 1000 元。在 QMT 和大多数柜台系统中,委托数量的单位通常是“张”(面值 100 元)。1000 元对应 10 张。因此,我们先算有多少个 1000 元,再乘以 10 得到张数。
Q: 策略会重复下单吗?
A: 代码中加入了 ContextInfo.traded_date 变量。一旦当天下单成功或检查完毕,会记录当前日期,直到第二天日期变更前不会再次触发。
Q: 为什么选择 15:10 之后?
A: 15:00 股票收盘,此时账户内的剩余资金确定闲置。逆回购交易时间持续到 15:30,利用这段时间差进行现金管理是最优选择。