🚀 AI 一键生成 qmt 策略代码

QMT 现金管理:如何利用闲置资金自动参与国债逆回购交易?

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

问题描述

请写一个自动做国债逆回购的 QMT 策略

解决方案

这是一个基于 QMT 平台的国债逆回购自动化策略。

策略逻辑说明

  1. 运行时间:策略设定在每日收盘前(例如 15:10)自动运行,利用账户闲置资金进行逆回购,不影响日内股票交易。
  2. 标的选择:自动比较 上交所 GC001 (204001.SH)深交所 R-001 (131810.SZ) 的实时收益率(买一价),选择收益率较高的品种下单。
  3. 资金利用:自动读取账户可用资金,预留少量资金后,全仓参与。
  4. 交易方向:国债逆回购在交易软件中表现为 “卖出”(即借出资金,获得债券质押)。
  5. 防重单:增加日期标记,防止同一天重复下单。

策略代码

# -*- 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("逆回购下单指令已发送")

使用说明

  1. 修改账号
    • 找到代码中的 ContextInfo.account_id = 'YOUR_ACCOUNT_ID',将 'YOUR_ACCOUNT_ID' 替换为您真实的 QMT 资金账号。
  2. 运行模式
    • 请在 “实盘” 模式下运行。
    • 请确保 QMT 客户端已登录交易账号。
  3. 触发时间
    • 代码默认设置为 15:10。此时股票交易已结束,不会占用股票交易资金。国债逆回购的交易时间延长至 15:30,因此这个时间点是安全的。
  4. 下单逻辑细节
    • 方向:代码中使用 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,利用这段时间差进行现金管理是最优选择。