首页

--
LTtx(库通信)
通用查询通道
通用交易通道
通用模式
QMT 能力转接控制台

用 cfquant 替代 miniqmt,把大 QMT 的交易、行情和多账号能力统一接出来。

项目在本机启动 Web 服务,外部 Python 默认先通过 LTtx 发现 Web 路由,网页请求直接进入 Web;两者最终都由账号配置自动路由到通用端或高级端。新用户优先使用通用模式,一个 QMT 加载一个 ctypes 文件即可完成资金、持仓、委托、下单、撤单和回调验证。

通用模式单文件部署 高级模式低延迟交易 LTtx 自动发现 PipeHub 通用后端 多账号自动路由 WebSocket 回调

资产

总资产--
可用资金--
总市值--
持仓盈亏--

操作

LTtx 地址--
LTtx PID--
cfquant 库--
重启策略保留
账号配置
绑定实时状态
账号 首选模式 实际模式 QMT 目录 数据源

持仓

代码 名称 持仓 可用 成本价 市值 盈亏

实时委托

序号 最后回调

提交委托

批量买入

交易数据

代码 名称 持仓 可用 成本价 市值 盈亏
序号 最后回调
时间 代码 名称 价格 数量 金额

系统状态

LTtx / normal / trade

          

绑定操作

账号绑定 --
列表维护账号绑定 绑定信息、运行状态和验证入口集中显示。

绑定列表

账号 / 通道 / QMT 目录
操作 账号名称 资金账号 连接状态 首选模式 实际模式 内部通道 QMT 目录 数据源

绑定验证

资金

总资产--
可用资金--
总市值--
持仓盈亏--

持仓

代码 名称 持仓 可用 市值

交易回调

连接状态未连接
订阅过滤--
最后事件尚未收到真实回调
服务端序号seq 0
接收时间 事件 账号 代码 委托 / 成交编号 价格 数量 成交量 状态 / 摘要 来源 完整信息

接口文档

HTTP / WebSocket / Python SDK
API Key 和 IP 访问策略在设置页配置,文档调试会自动使用已保存的配置。
--
--

请求


                  

响应


                  

测试

从 cfquant/tests 读取测试脚本
请选择测试脚本 左侧列表会逐个罗列 tests 目录下的脚本。
正在读取测试脚本...

设置

访问 / 日志 / 更新

个人资料

管理员 内置头像
内置头像 选择后点击保存资料生效

API Key

Web 访问

当前监听 --
配置端口 --
访问范围 --
网页登录 --

通信模式

监听与访问

网页登录

新密码只写入项目目录文本文件,不会显示在网页中。
密码文件位置(重置后生成) --

外部 API 地址

日志清理

本地服务日志统一写入项目 log/ 目录,默认自动保留最近 30 天;根目录旧日志也会纳入过期清理。

QMT 日志

Python 环境

选择后,PipeHub、LTtx、更新安装和重启流程都会使用该解释器。

通信模式

PipeHub 状态 --

系统信息

查看当前 Web 服务加载的版本、安装目录和启动脚本位置。

当前版本 -- 正在读取版本信息
版本更新日期 -- --
Python SDK 版本 -- 正在读取安装版本 --
项目安装目录 --
启动脚本 -- --
Python 解释器 --
运行数据目录 --
日志目录 --
静态资源目录 --

系统更新

一次更新完整版本,并把最新 cfquant 核心同步到所有已绑定 QMT 目录。

版本信息 完整版本更新 更新完成后,请完全退出并重启 QMT 加载新版本。
未加载项目更新状态
更新保护 先备份,再更新 每次更新前自动创建完整回退点。更新期间网页新委托会暂时锁定;请先停止 QMT 入口脚本,并在非交易时段操作。
暂无可回退版本
回退版本 恢复到更新前的项目版本 回退前会再次备份当前版本;完成后 Web 服务会重启,QMT 入口脚本需要重新启动。
高级选项
执行结果

                    

教程中心

部署 / 网页 / 接口

先把 cfquant 跑起来

填写账号、选择模式并保存,系统自动同步项目代码和 QMT 内部策略代码,然后按提示启动并登录 QMT。

推荐 第一次用通用模式,QMT 目录填 bin.x64;无需手工复制代码,只需先完成 QMT Python 库下载。

跑通后再看

多账号 在“绑定”继续添加账号,QMT 目录填实际登录该账号的 bin.x64 高级模式 需要两个 QMT,普通端和极速交易端都在线后再使用。 排查 先看绑定状态和 QMT 日志,再看 PipeHub 是否在线。

项目架构

cfquant 是本机 QMT 桥接控制台。它把 Web 页面、外部 Python 和大 QMT 策略脚本连在一起,由 Web 统一管理账号、运行模式、回调和更新。

用户侧 网页操作,或在外部 Python 中按 xtquant 习惯调用。 本地服务 Web Server 管账号路由;PipeHub 管通用/极致模式;LTtx 管高级模式和自动发现。 QMT 侧 根据模式加载一个或两个入口脚本,再调用 QMT 原生函数。

三种运行链路

模式 QMT 入口 适合场景
通用模式 CFQUANT_CTYPE_ALL_LOWLAT.py 默认推荐。一个 QMT 即可跑通行情、查询、交易和回调。
极致模式 CFQUANT_LITE.py 入口自包含,适合国泰君安、国泰海通等导入受限环境。
高级模式 CFQUANT.py + CFQUANT_TRADE_LOWLAT.py 两个 QMT,普通端做查询和回调,极速交易端做低延迟交易请求。

关键文件

位置 作用
runtime/config/cfquant_web_config.json 保存账号、QMT 目录、运行模式、共享行情源和市场路由。
qmt_scripts/ 放给 QMT 加载的入口脚本。
bin.x64/cfquant_bridge_config.json Web 为 QMT 写入的身份文件,用于区分不同 QMT 终端。
log/ Web、PipeHub、LTtx 和 QMT 桥接日志。

快速开始

  1. 启动服务。运行 start_cfquant.bat,或执行 cfquant --open-browser
  2. 完成初始化。填写资金账号、账户类型、QMT 安装目录或 bin.x64 目录和运行模式。
  3. 启动 QMT。保存绑定后系统自动同步项目代码和 QMT 内部策略代码,并按选择配置启动;随后按登录前提完成 QMT 登录。
  4. 启动脚本。通用模式启动一个入口;高级模式分别启动普通端和极速交易端。
  5. 验证账号。回到“绑定”页刷新状态,再查资金、持仓。
  6. 接入外部程序。先在“接口”页调通参数,再改外部 Python 代码。

启动后看哪里

首页 看当前账号资金、持仓和实时委托。 绑定 维护账号、QMT 目录、模式和共享行情源。 状态 确认 Web、PipeHub、LTtx 和 QMT 入口是否在线。 接口 调试 HTTP、WebSocket、行情下载和回调。

网页端

网页端负责配置、验证和排查。外部策略接入前,先用网页确认账号和 QMT 链路是通的。

首页当前账号的资产、持仓、委托和基础状态。
绑定账号、账户类型、QMT 目录、运行模式、共享行情源。
交易单笔下单、批量下单、撤单和委托刷新。
状态查看普通端、交易端、PipeHub、LTtx 是否在线。
回调查看委托、成交、下单错误、撤单错误事件。
接口在线调试行情、交易、下载和 WebSocket。
设置API Key、远程访问、日志、版本检查和更新。

推荐顺序

  1. 先在“绑定”保存账号配置。
  2. 再在 QMT 启动对应入口脚本。
  3. 回网页刷新绑定状态,确认实际模式在线。
  4. 查资金和持仓,确认账号没有串。
  5. 最后再测试下单、撤单和外部接口。

1. 安装与运行前提

这里的“原来”指 MiniQMT / 原版 xtquant,“现在”指外部 Python 通过 cfquant 接入大 QMT。示例运行在 PyCharm、VS Code 或命令行的外部 Python 环境中;QMT 内部运行的是“部署教程”中的桥接入口脚本。

  1. 在运行策略的同一个 Python 环境安装 cfquant;源码开发时,在项目根目录执行可编辑安装。
  2. 启动本地 Web,在“绑定”页配置资金账号、账户类型和 QMT 目录,并按模式部署、运行 QMT 入口脚本。
  3. 在网页先查到资金和持仓,再运行下面的脚本;把 YOUR_ACCOUNT_ID 换成绑定的资金账号,保留字符串引号。
python -m pip install --upgrade cfquant
# 需要 ZMQ 能力时安装:python -m pip install "cfquant[zmq]"
# 使用本地源码时,在项目根目录执行:python -m pip install -e .
python -c "import cfquant; print(cfquant.__version__, cfquant.__file__)"
cfquant --open-browser
cfquant qmt-scripts --output D:\QMT\cfquant

以上命令用于首次准备;Web 和 QMT 入口已运行时,直接执行策略即可。安装包和导出脚本不会自动完成 QMT 侧的部署。

2. 原来怎么调用,现在怎么改

常用接口保留了接近 xtquant 的名称与参数。迁移先调整导入和连接初始化,再逐项核对策略实际用到的接口;同名入口不代表所有返回字段、异步时序和券商能力都完全一致。

位置原来:xtquant / MiniQMT现在:cfquant / 大 QMT
导入模块from xtquant import xtdata, xtconstantfrom cfquant import xtdata, xtconstant;交易类、账号类和回调类的导入前缀一起替换。
交易连接XtQuantTrader(path, session_id),path 指向 MiniQMT 的 userdata_miniXtQuantTrader("", session_id);QMT 目录由 Web 账号绑定管理,构造函数中的 path 只作兼容保留。
会话标识传入用于区分连接的 session_id。可继续传正整数;省略或传 0 时自动生成正整数,可读取 trader.session_id
账号对象StockAccount("YOUR_ACCOUNT_ID", "STOCK")相同写法;资金账号和账户类型必须与 Web 绑定一致,账号不能写成整数。
启动与订阅start → connect → subscribe可保留这个顺序。若构造时传 account=accountstart() 会尝试自动订阅;下文用显式订阅检查结果。
行情与查询get_full_tickquery_stock_assetquery_stock_positions 等。常用调用参数可沿用;数据来自当前大 QMT 桥接链路。
下单与回调order_stockcancel_order_stockXtQuantTraderCallback沿用常用签名和回调名;返回请求结果后仍需核对委托、成交或错误回报。

原来的导入

from xtquant import xtdata, xtconstant
from xtquant.xttrader import XtQuantTrader, XtQuantTraderCallback
from xtquant.xttype import StockAccount

现在的导入

from cfquant import xtdata, xtconstant
from cfquant.xttrader import XtQuantTrader, XtQuantTraderCallback
from cfquant.xttype import StockAccount

同一份策略里的交易类、常量、账号和行情模块应一起迁移,避免一部分请求仍然发往原版 MiniQMT。

3. 连接、资金、持仓、委托和成交

这两个示例展示同一条查询流程。当前示例可单独保存为 query_account.py 后运行,只查询数据,不提交订单。

原来:连接 MiniQMT 的 userdata_mini

import time
from xtquant.xttrader import XtQuantTrader
from xtquant.xttype import StockAccount

account = StockAccount("YOUR_ACCOUNT_ID", "STOCK")
trader = XtQuantTrader(r"D:\QMT\userdata_mini", int(time.time()))
try:
    trader.start()
    if trader.connect() != 0:
        raise RuntimeError("MiniQMT connection failed")
    if trader.subscribe(account) != 0:
        raise RuntimeError("Account subscription failed")
    print(trader.query_stock_asset(account))
    print(trader.query_stock_positions(account))
    print(trader.query_stock_orders(account))
    print(trader.query_stock_trades(account))
finally:
    trader.stop()

现在:通过 cfquant 自动路由到大 QMT

from cfquant.xttrader import XtQuantTrader
from cfquant.xttype import StockAccount

account = StockAccount("YOUR_ACCOUNT_ID", "STOCK")
trader = XtQuantTrader("", 0)
try:
    trader.start()
    if trader.connect() != 0:
        raise RuntimeError("CFQuant connection failed; check Web and QMT")
    if trader.subscribe(account) != 0:
        raise RuntimeError("Account subscription failed; check binding")

    print("session_id:", trader.session_id)
    asset = trader.query_stock_asset(account)
    if asset is None:
        print("No asset data returned")
    else:
        print("account:", asset.account_id)
        print("cash:", asset.cash, "total_asset:", asset.total_asset)

    positions = trader.query_stock_positions(account)
    for position in positions:
        print(position.stock_code, position.volume, position.can_use_volume)
    print("position_count:", len(positions))

    for order in trader.query_stock_orders(account):
        print("order:", order.order_id, order.stock_code,
              order.order_status, order.traded_volume, order.order_remark)
    for trade in trader.query_stock_trades(account):
        print("trade:", trade.order_id, trade.stock_code,
              trade.traded_volume, trade.traded_price)
finally:
    trader.stop()

query_stock_asset() 返回资产对象;持仓、委托和成交查询返回对象列表,可用点号读取字段。空持仓或当日没有委托时列表可以为空。connect() == 0 说明连接检查通过,仍需用实际账号查询确认绑定和 QMT 数据可用。脚本结束时调用 stop() 释放本进程的交易订阅和连接。

4. 实时行情与历史 K 线

仅查询行情时无需创建 XtQuantTrader。证券代码仍带市场后缀,例如 000001.SZ600000.SH;先确认网页中的行情源已在线。

原来:xtquant 行情快照

from xtquant import xtdata

ticks = xtdata.get_full_tick(["000001.SZ", "600000.SH"])
print(ticks)

现在:同样的参数,改用 cfquant

from cfquant import xtdata

ticks = xtdata.get_full_tick(["000001.SZ", "600000.SH"])
for code, tick in ticks.items():
    print(code, tick.get("lastPrice"), tick.get("volume"))

历史数据:先补数据,再读取指定区间

原来的 download_history_data()get_market_data_ex() 常用参数可保留,导入前缀改为 cfquant。下面读取固定日期范围的日线;日期用于演示历史查询,可按需要调整。

from cfquant import xtdata

code = "000001.SZ"
xtdata.download_history_data(
    code, period="1d", start_time="20260803", end_time="20260831"
)
bars = xtdata.get_market_data_ex(
    field_list=["open", "high", "low", "close", "volume"],
    stock_list=[code],
    period="1d",
    start_time="20260803",
    end_time="20260831",
    count=-1,
    dividend_type="none",
    fill_data=True,
)
print(type(bars), bars)
if isinstance(bars, dict):
    data = bars.get(code)
    print("last five rows:", data.tail(5) if hasattr(data, "tail") else data)

period="1d" 表示日线,count=-1 表示读取区间内的数据,dividend_type="none" 表示不复权。常见返回为“证券代码 → DataFrame”;具体结构取决于大 QMT 返回值,迁移时先打印类型和字段。桥接传输后的索引、类型也需核对,再接入原策略计算。

下载操作发生在 QMT 侧。首次下载可能较慢,空结果时先检查时间区间、下载状态、行情权限和数据源;批量回测读取速度与 MiniQMT 本地数据读取不同。get_local_data(data_dir=...) 的同名参数不代表接管原来的 MiniQMT 本地数据目录。

5. 行情订阅、回调与取消订阅

原来用 from xtquant import xtdata,现在改为下面的导入。保留 subscribe_quote(..., callback=...)unsubscribe_quote(seq);订阅返回的序号用于取消对应订阅。

from cfquant import xtdata

def on_quote(data):
    print("quote:", data)

seq = xtdata.subscribe_quote("000001.SZ", period="tick", callback=on_quote)
if seq is None or int(seq) <= 0:
    raise RuntimeError("Quote subscription failed")
print("subscribe_id:", seq)
try:
    xtdata.run()  # 保持进程运行,Ctrl+C 结束
except KeyboardInterrupt:
    pass
finally:
    xtdata.unsubscribe_quote(seq)

订阅成功不等于立即有行情变化,非交易时段可能没有推送。回调里先打印实际数据结构,再接入策略;耗时计算和网络请求应交给独立队列处理,避免阻塞回调线程。

6. 交易回调:委托、成交和错误

原来的 XtQuantTraderCallback 子类可保留常用方法名,把导入前缀改成 cfquant。下面是独立的监听脚本,不主动下单;账号有对应事件时才会输出。

from cfquant.xttrader import XtQuantTrader, XtQuantTraderCallback
from cfquant.xttype import StockAccount

class TradeCallback(XtQuantTraderCallback):
    def on_stock_order(self, order):
        print("order:", order.order_id, order.order_status,
              order.traded_volume, order.order_remark)

    def on_stock_trade(self, trade):
        print("trade:", trade.order_id, trade.stock_code,
              trade.traded_volume, trade.traded_price)

    def on_order_error(self, error):
        print("order_error:", error.error_id, error.error_msg)

    def on_cancel_error(self, error):
        print("cancel_error:", error.error_id, error.error_msg)

    def on_order_stock_async_response(self, response):
        print("async_order:", response.seq, response.order_id,
              response.order_remark)

    def on_disconnected(self):
        print("Trading connection closed")

account = StockAccount("YOUR_ACCOUNT_ID", "STOCK")
trader = XtQuantTrader("", 0)
try:
    trader.register_callback(TradeCallback())
    trader.start()
    if trader.connect() != 0:
        raise RuntimeError("Connection failed")
    if trader.subscribe(account) != 0:
        raise RuntimeError("Account subscription failed")
    trader.run_forever()  # 保持进程运行,Ctrl+C 结束
except KeyboardInterrupt:
    pass
finally:
    trader.stop()

行情订阅和账号交易订阅是两件事:前者用 xtdata.subscribe_quote(),后者用 trader.subscribe(account)。监听不到交易事件时,检查账号订阅、QMT 入口状态和网页“回调”页;不要把 run_forever() 放在后续还要执行的下单代码之前。

7. 下单、撤单与异步返回值

原版和当前的常用下单签名都是 order_stock(account, stock_code, order_type, order_volume, price_type, price, strategy_name, order_remark)。股票买卖用 xtconstant.STOCK_BUY / STOCK_SELL,限价用 xtconstant.FIX_PRICE;迁移时同时替换常量模块的导入。

下面是完整的当前写法,默认只打印参数。确认账号、代码、数量和价格后,显式开启 ENABLE_ORDER 才会提交真实委托;示例价格仅用于展示参数。

from uuid import uuid4
from cfquant import xtconstant
from cfquant.xttrader import XtQuantTrader
from cfquant.xttype import StockAccount

ENABLE_ORDER = False
account = StockAccount("YOUR_ACCOUNT_ID", "STOCK")
order_params = dict(
    stock_code="000001.SZ",
    order_type=xtconstant.STOCK_BUY,
    order_volume=100,
    price_type=xtconstant.FIX_PRICE,
    price=10.00,
    strategy_name="python_demo",
    order_remark="demo_" + uuid4().hex[:12],
)

if not ENABLE_ORDER:
    print("Preview only:", account.account_id, order_params)
else:
    trader = XtQuantTrader("", 0)
    try:
        trader.start()
        if trader.connect() != 0 or trader.subscribe(account) != 0:
            raise RuntimeError("Connection or subscription failed")
        order_id = trader.order_stock(account, **order_params)
        print("order_id:", order_id)
        print("order:", trader.query_stock_order(account, order_id))
    finally:
        trader.stop()

与原来相比,这个下单调用本身可沿用,连接改用 XtQuantTrader("", 0)order_volume 在此股票例子中以股为单位,其他品种按各自交易单位填写。每次请求使用可追踪的备注;备注有助于关联查询和回调,并不提供自动去重保证。

撤单:明确指定要撤的委托

原来的 cancel_order_stock(account, order_id) 调用可保留。下面先查询可撤委托,只有开启撤单且填入对应编号才提交;不会自动撤掉账号下的全部订单。

from cfquant.xttrader import XtQuantTrader
from cfquant.xttype import StockAccount

ENABLE_CANCEL = False
TARGET_ORDER_ID = ""  # 填写查询或回调中得到的 order_id
account = StockAccount("YOUR_ACCOUNT_ID", "STOCK")
trader = XtQuantTrader("", 0)
try:
    trader.start()
    if trader.connect() != 0 or trader.subscribe(account) != 0:
        raise RuntimeError("Connection or subscription failed")
    orders = trader.query_stock_orders(account, cancelable_only=True)
    for order in orders:
        print(order.order_id, order.stock_code, order.order_remark)
    if ENABLE_CANCEL:
        target = next(
            (order for order in orders
             if str(order.order_id) == TARGET_ORDER_ID), None
        )
        if target is None:
            raise RuntimeError("Target order is not in the cancelable list")
        result = trader.cancel_order_stock(account, target.order_id)
        print("cancel_result:", result)
    else:
        print("Query only; no cancellation submitted")
finally:
    trader.stop()
接口 / 结果如何确认
order_stock()返回委托编号;失败可能返回 -1 或抛出异常。拿到编号不代表已成交,应查委托状态或看成交回调;刚提交时单笔查询也可能暂时为空。
order_stock_async()与同步下单的参数相同,但返回的是请求序号 seq,不是委托编号。通过本交易实例的 on_order_stock_async_response 关联 response.seqresponse.order_id,不能用 seq 撤单。
cancel_order_stock()0 表示撤单请求调用成功,-1 表示失败;最终是否撤成,以后续委托状态或撤单错误回调为准。查询之后订单也可能已经成交。
超时 / 连接异常结果可能尚未确认。先按账号、代码、备注核对委托和回调,再决定是否重试,避免重复提交。

使用异步下单时,把调用放在第 6 节注册回调和订阅账号之后、run_forever() 之前,并保留进程等待回报。当前只读 query_*_async() 使用后台查询并立即返回请求序号,查询结果由独立回调线程派发;查询或回调异常会记录到 cfquant.xttrader 日志。这个序号不是委托编号,也不代表原版专用响应线程开关已实现。

8. 多账号与账户类型

每个请求携带明确的账号对象,Web 根据绑定路由到相应 QMT。普通账户用 STOCK,信用账户用 CREDIT;同一个账号数字配上不同账户类型,含义也不同。

from cfquant.xttrader import XtQuantTrader
from cfquant.xttype import StockAccount

accounts = [
    StockAccount("YOUR_STOCK_ACCOUNT_ID", "STOCK"),
    StockAccount("YOUR_CREDIT_ACCOUNT_ID", "CREDIT"),
]
trader = XtQuantTrader("", 0)
try:
    trader.start()
    if trader.connect() != 0:
        raise RuntimeError("Connection failed")
    for account in accounts:
        if trader.subscribe(account) != 0:
            raise RuntimeError("Subscription failed: " + account.account_id)
        asset = trader.query_stock_asset(account)
        print(account.account_id, asset)
finally:
    trader.stop()

其他账户类型还有 FUTUREFUTURE_OPTIONSTOCK_OPTION。对应业务要使用匹配的买卖 / 开平仓常量和合约代码,不能只换账户类型就照搬股票下单参数。xtdata 的行情调用不接收这个交易账号参数,多账号共享行情源需在 Web“绑定”页配置。

9. 旧版手动通道与当前 auto 路由

如果原来用的已经是 cfquant,并手工指定 Pipe 或 LTtx 通道,当前常规部署可改用默认 auto。Web 在线且自动发现成功时,请求进入 Web 统一路由,再按账号绑定选择通用、极致或高级模式;发现不到 Web 注册信息时,会回退到通用 PipeHub。

原来:显式指定连接链路

from cfquant import configure

configure(transport="ctypes", pipe_name=r"\\.\pipe\cfquant_pipe_hub")
# 然后再创建 trader 或调用 xtdata

现在:使用自动发现和账号路由

from cfquant import configure, xtdata

# 无旧配置时可省略;迁移旧脚本时可显式选择 auto
configure(transport="auto")
print(xtdata.get_full_tick(["000001.SZ"]))

检查旧脚本及启动环境中的 CFQUANT_TRANSPORT,避免仍被固定为 ctypeslttx。自定义 LTtx 地址、端口、认证或 Pipe 名称仍需保持一致;auto 不会自动修复这些连接参数。

排查时:显式指定 Web 路由

from cfquant import configure

configure(
    transport="web_lttx",
    host="127.0.0.1",
    port=2049,
    token="LTtx",
    web_request_channel="cfquant.web.request",
    timeout=15,
)
# 然后再创建 trader 或调用 xtdata

这里的 2049 是默认 LTtx 端口,LTtx 是默认通信 token;都要与实际服务配置一致。它们不同于网页 HTTP 地址(默认 8765)及 HTTP API Key。configure() 应在创建交易实例、查询和订阅之前调用;切换通道后重启外部策略进程更便于确认配置生效。

10. 常见迁移问题

现象检查与处理
ModuleNotFoundError: cfquant在策略使用的解释器执行 python -m pip install cfquant。核对 IDE 解释器和 cfquant.__file__,确认没有装到另一个环境或导入旧副本。
连接返回 -1,或出现 CfquantTimeout检查 Web、LTtx / PipeHub 和对应 QMT 入口是否在线,再核对旧环境变量和自定义端口。交易请求超时后先查委托,不要立即重复下单。
连接成功,但账号查询为空或报错核对资金账号字符串、STOCK / CREDIT 等账户类型、绑定目录和 QMT 登录账号;先在网页执行同一查询,区分绑定问题与脚本问题。
快照 / 历史数据为空核对证券代码后缀、日期范围、周期、行情源、下载完成情况及券商权限。先查单只证券的小区间,再扩大批量。
没有回调检查回调注册、对应的行情或账号订阅、进程是否仍在运行,以及 QMT 是否实际产生了该事件;stop() 后本进程不再监听。
同名接口不可用 / 字段不一致对照项目文档 docs/xtquant原版接口适配清单.mddocs/xtdata平替追踪.mddocs/xttrader平替追踪.md。条件适配接口仍依赖券商大 QMT 暴露对应 callable;客户端连接管理、本地目录语义及部分异步接口不能只替换导入就假定完全兼容。

建议迁移顺序:确认导入版本 → 只读行情 → 资金和持仓 → 回调监听 → 单笔委托和撤单 → 多账号与策略自动运行。每一步都核对实际返回值,再接入下一段策略逻辑。

cftrader 批量交易与撤单

cfquant.cftrader 是项目扩展的交易模块。通过 CfQuantTrader(trader) 复用已有 XtQuantTrader 实例;连接、订阅、查询、单笔撤单和回调继续交给原实例。

整批请求通过一次 RPC 发送到大 QMT,再由 QMT 内部连续调用 passorder 或 cancel。 100 笔操作对应一次批量通信、100 次本地调用,减少逐笔通信往返。批量接口适合组合调仓、批量止盈止损,以及撤掉一组未成委托;最终成交或撤成仍以委托查询和回调为准。

需要更新并重启 Web 服务和 QMT 核心;极致模式需重新导入更新后的内置脚本。启用同账号 SH/SZ 独立市场路由时,批量撤单建议每笔带 stock_codemarket,便于路由到正确交易端。

本机假 QMT 桥基准(2026-09-11,100 单,5 次预热、30 次采样)显示:批量同步中位 4.802 ms,100 次单笔同步中位 9.169 ms;批量异步中位 7.093 ms,100 次单笔异步中位 9.053 ms。该结果只衡量 SDK 到桥接分发开销,不代表真实券商柜台耗时。

100 单路径RPC 次数中位耗时平均耗时
批量同步 order_stock_batch14.802 ms4.901 ms
单笔同步循环 order_stock1009.169 ms8.930 ms
批量异步 order_stock_batch_async17.093 ms7.122 ms
单笔异步循环 order_stock_async1009.053 ms9.186 ms

模拟账号实测(2026-09-11 02:45):信用账号 900010001595 通过 Web LTtx 统一路由连接 acct_4b2b38c167,对 600000.SH 以 8.88 元、每笔 100 股测试;四条路径各 100 单均返回 submitted=100,复核 400 单均已撤。

模拟账号 100 单路径RPC 次数提交耗时单笔均摊
批量同步 order_stock_batch1963.252 ms9.6325 ms
单笔同步循环 order_stock10023250.174 ms232.5017 ms
批量异步 order_stock_batch_async152.578 ms0.5258 ms
单笔异步循环 order_stock_async1002952.912 ms29.5291 ms
接口参数与结果
order_stock保留原单笔参数顺序,返回订单号。
order_stock_async保留原单笔参数顺序,返回请求序号 seq
order_stock_batchaccount, orders, strategy_name="", order_remark="", stop_on_error=False;返回逐笔订单号和批量统计。
order_stock_batch_async与同步批量参数相同;返回逐笔请求序号和批量统计。
cancel_order_stock_batchaccount, order_ids, stop_on_error=False;返回逐笔撤单调用结果。
cancel_order_stock_batch_async与同步批量撤单参数相同;返回逐笔请求序号和撤单调用结果。

orders 是订单字典列表。每笔必填 stock_codeorder_typeorder_volumeprice_typeprice,沿用原常量和字段名。可单独指定 strategy_nameorder_remark;未填备注时自动生成逐笔备注。

每批对应一个账号,参数会在发送前整批校验。下单数量必须为正整数,价格必须为有限数值,字段拼写错误会直接报错;撤单列表每项至少包含 order_id。完整文档位于 docs/cftrader批量交易.md

批量同步下单

QMT 先连续提交整批,再集中解析订单号,共用一个等待窗口,不在每笔下单之间等待编号。示例默认预览参数;替换账号、代码和价格,开启 ENABLE_TRADING 后提交。

from cfquant import cftrader, xtconstant
from cfquant.xttrader import XtQuantTrader
from cfquant.xttype import StockAccount

ENABLE_TRADING = False
account = StockAccount("YOUR_ACCOUNT_ID", "STOCK")
orders = [
    dict(stock_code="600000.SH", order_type=xtconstant.STOCK_BUY,
         order_volume=100, price_type=xtconstant.FIX_PRICE, price=10.0),
    dict(stock_code="000001.SZ", order_type=xtconstant.STOCK_BUY,
         order_volume=100, price_type=xtconstant.FIX_PRICE, price=9.0),
]
trader = XtQuantTrader("", 0)
orders_api = cftrader.CfQuantTrader(trader)
if ENABLE_TRADING:
    try:
        trader.start()
        if trader.connect() != 0 or trader.subscribe(account) != 0:
            raise RuntimeError("Connection or subscription failed")
        result = orders_api.order_stock_batch(
            account, orders, strategy_name="rebalance", stop_on_error=True,
        )
        for row in result["results"]:
            print(row["index"], row["status"], row["order_id"], row["error"])
    finally:
        trader.stop()
else:
    print("Preview only:", orders)

results 与输入等长且顺序一致,index 从 0 开始。获得订单号不代表成交;QMT 明确拒绝记录为 failed,已提交但编号未确认记录为 unknown,此时后续订单可能已经提交。

批量异步下单与原回调

SDK 预分配逐笔请求序号,将整批一次发送到 QMT;QMT 连续下单后返回逐笔受理状态,不等待订单号或成交。回报仍进入原来的 XtQuantTraderCallback,通过 response.seq 关联请求序号与订单号。

from cfquant import cftrader, xtconstant
from cfquant.xttrader import XtQuantTrader, XtQuantTraderCallback
from cfquant.xttype import StockAccount

class Callback(XtQuantTraderCallback):
    def on_order_stock_async_response(self, response):
        print("response:", response.seq, response.order_id, response.order_remark)

    def on_stock_order(self, order):
        print("order:", order.order_id, order.order_status)

    def on_stock_trade(self, trade):
        print("trade:", trade.order_id, trade.traded_volume)

    def on_order_error(self, error):
        print("error:", error)

ENABLE_TRADING = False
account = StockAccount("YOUR_ACCOUNT_ID", "STOCK")
orders = [
    dict(stock_code="600000.SH", order_type=xtconstant.STOCK_BUY,
         order_volume=100, price_type=xtconstant.FIX_PRICE, price=10.0),
    dict(stock_code="000001.SZ", order_type=xtconstant.STOCK_BUY,
         order_volume=100, price_type=xtconstant.FIX_PRICE, price=9.0),
]
trader = XtQuantTrader("", 0)
orders_api = cftrader.CfQuantTrader(trader)
if ENABLE_TRADING:
    try:
        trader.register_callback(Callback())
        trader.start()
        if trader.connect() != 0 or trader.subscribe(account) != 0:
            raise RuntimeError("Connection or subscription failed")
        result = orders_api.order_stock_batch_async(
            account, orders, strategy_name="rebalance", stop_on_error=False,
        )
        for row in result["results"]:
            print(row["index"], row["status"], row["seq"], row["order_remark"])
        trader.run_forever()
    except KeyboardInterrupt:
        pass
    finally:
        trader.stop()
else:
    print("Preview only:", orders)

回调可能早于批量方法返回;可先用每笔 order_remark 关联。批量返回后需保持进程运行,seq 不能用于撤单。原单笔异步与批量异步共用请求序号和回调去重。

信用与其他账号类型

信用账户使用 StockAccount("YOUR_CREDIT_ACCOUNT_ID", "CREDIT")。融资买入使用 xtconstant.CREDIT_FIN_BUY,担保品卖出使用 xtconstant.CREDIT_SELL,同步和异步批量字段一致。期货、期货期权和股票期权沿用原账号类型、合约代码和对应常量。

批量同步撤单

批量撤单只提交撤单请求,不判断原委托最终是否已经撤成。返回 submitted 表示 QMT 的 cancel 调用已被接受;最终状态仍要查询委托表或等待撤单错误回调。

from cfquant import cftrader
from cfquant.xttrader import XtQuantTrader
from cfquant.xttype import StockAccount

ENABLE_TRADING = False
account = StockAccount("YOUR_ACCOUNT_ID", "STOCK")
order_ids = [
    dict(order_id="1001", stock_code="600000.SH"),
    dict(order_id="1002", market="SZ"),
]
trader = XtQuantTrader("", 0)
orders_api = cftrader.CfQuantTrader(trader)
if ENABLE_TRADING:
    try:
        trader.start()
        if trader.connect() != 0 or trader.subscribe(account) != 0:
            raise RuntimeError("Connection or subscription failed")
        result = orders_api.cancel_order_stock_batch(
            account, order_ids, stop_on_error=False,
        )
        for row in result["results"]:
            print(row["index"], row["status"], row["order_id"], row["cancel_result"], row["error"])
    finally:
        trader.stop()
else:
    print("Preview only:", order_ids)

批量异步撤单与原回调

异步批量撤单使用 cancel_order_stock_batch_async。SDK 为每笔撤单分配 seq,QMT 本地连续调用 cancel 后返回逐笔受理状态,并继续触发原 on_cancel_order_stock_async_responseon_cancel_error 回调。

from cfquant import cftrader
from cfquant.xttrader import XtQuantTrader, XtQuantTraderCallback
from cfquant.xttype import StockAccount

class Callback(XtQuantTraderCallback):
    def on_cancel_order_stock_async_response(self, response):
        print("cancel response:", response.seq, response.order_id, response.cancel_result)

    def on_cancel_error(self, error):
        print("cancel error:", error)

ENABLE_TRADING = False
account = StockAccount("YOUR_ACCOUNT_ID", "STOCK")
order_ids = [
    dict(order_id="1001", stock_code="600000.SH"),
    dict(order_id="1002", market="SZ"),
]
trader = XtQuantTrader("", 0)
orders_api = cftrader.CfQuantTrader(trader)
if ENABLE_TRADING:
    try:
        trader.register_callback(Callback())
        trader.start()
        if trader.connect() != 0 or trader.subscribe(account) != 0:
            raise RuntimeError("Connection or subscription failed")
        result = orders_api.cancel_order_stock_batch_async(
            account, order_ids, stop_on_error=False,
        )
        for row in result["results"]:
            print(row["index"], row["status"], row["seq"], row["cancel_result"], row["error"])
        trader.run_forever()
    except KeyboardInterrupt:
        pass
    finally:
        trader.stop()
else:
    print("Preview only:", order_ids)

返回值与失败处理

顶层返回 batch_idaccountasynchronousoktotalattemptedsubmittedfailedunknownskippedresults。每行包含状态、原始索引、代码、策略名、备注、订单号或请求序号及错误信息。

状态含义后续订单
submitted已获得订单号或请求序号,行内 ok=True;最终成交以原回报为准。继续提交。
failedQMT 下单明确拒绝,同步、异步均适用,行内 ok=Falsestop_on_error=True 时停止。
unknown同步编号未确认、整批回包异常或本地下单异常,行内 ok=None先查委托与回调;整批通信超时不代表 QMT 停止执行。
skipped本次未调用该行下单,行内 ok=None不会自动补发。

execution="qmt" 标明采用 QMT 内批量执行协议,qmt_submit_ms 是 QMT 提交循环耗时,不包含通信与编号解析。异步所有行保留预分配 seq,只有 submitted 代表确认受理。

批量撤单结果额外有 operation="cancel";每行包含 order_id、可选 stock_code/marketseqcancel_resulterrorcancel_result=0 表示 QMT 撤单调用成功,不代表原委托已经处于已撤状态。

QMT 本地下单异常会停止尚未执行的行。trader.set_timeout() 对整批 RPC 生效,整批超时或回包丢失时所有未确认行标为 unknown,保留原异步关联;SDK 和 Web 均不自动重试或切换通道重发。已提交订单不回滚,不保证原子提交或同时成交。

启用 SH/SZ 独立市场路由时,连续同目标市场的订单组成一段,每段一个批量请求,由对应 QMT 本地执行;返回结果仍保持输入顺序。

【cfquant】QMT 部署教程

本教程介绍如何在大 QMT 中部署 cfquant,并通过本地 Web 控制台或外部 Python 调用行情、查询、交易和回调能力。

建议先部署通用模式 通用模式只需要一个 QMT 和一个入口脚本,最适合首次验证。国泰君安和国泰海通的 QMT 建议直接使用极致模式;国泰海通无法部署高级模式。
1. 先启动本地服务 通过 start_cfquant.batcfquant --open-browser 打开 Web 控制台,默认地址是 http://127.0.0.1:8765/
2. 再准备 QMT 环境 登录 QMT,并确认“模型研究”或“模型交易”里的 Python 环境已经安装可用。
3. 最后绑定账号验证 在网页“绑定”页填写资金账号、QMT 安装目录或 bin.x64 和运行模式,能查到资产、持仓或行情即部署成功。
模式 入口脚本 QMT 数量 说明
通用模式 CFQUANT_CTYPE_ALL_LOWLAT.py 1 个 默认推荐,适合大多数行情、查询、下单、撤单和回调场景。
极致模式 CFQUANT_LITE.py 1 个 适合 QMT 对 Python 包导入有限制,或国泰君安、国泰海通等环境。
高级模式 CFQUANT.py + CFQUANT_TRADE_LOWLAT.py 2 个 部署复杂,但外部程序到 QMT 内部的下单链路延迟更低。

通用模式自动部署

  1. 配置账号。选择通用模式,填写对应 QMT 安装目录或 bin.x64。国泰君安和国泰海通 QMT 请使用极致模式。
  2. 保存绑定。勾选“自动导入并管理 QMT 策略”,确认账号、模拟/实盘和策略自动运行设置后保存。初始化与后续绑定均由系统自动导入策略。
  3. 启动并登录 QMT。按保存后的提示重启 QMT;勾选“自动启动 QMT”后,在启动的 QMT 中登录即可。QMT 已设置自动登录时,请等待自动登录完成。
  4. 检查连接。登录后查看绑定列表中的通道状态,或点击“检测连接”。未勾选“QMT 启动后自动运行”时,在“模型交易”运行已导入的托管策略。
QMT 登录前提 国金 QMT:请手动输入密码登录,登录后在 QMT 内完成相应初始化设置。非国金 QMT:请在登录界面勾选自动登录和记住密码。所有 QMT 都要先完成 Python 库下载。
等待退出或部署失败 提示等待退出时,请正常退出 QMT,保持 cfquant 运行,等待模型配置完成后再启动。部署失败时,按错误提示修正目录、权限或模型账号 Key 后重新保存。
通用模式账号绑定示意
通用模式账号与 QMT 目录配置示意,以当前绑定表单为准。

极致模式自动部署

  1. 配置账号。选择极致模式,填写对应 QMT 安装目录或 bin.x64。系统会自动部署自包含策略。
  2. 保存绑定。勾选“自动导入并管理 QMT 策略”,确认账号、模拟/实盘和策略自动运行设置后保存。初始化与后续绑定均由系统自动导入策略。
  3. 启动并登录 QMT。按保存后的提示重启 QMT;勾选“自动启动 QMT”后,在启动的 QMT 中登录即可。QMT 已设置自动登录时,请等待自动登录完成。
  4. 检查连接。登录后查看绑定列表中的通道状态,或点击“检测连接”。未勾选“QMT 启动后自动运行”时,在“模型交易”运行已导入的托管策略。
QMT 登录前提 国金 QMT:请手动输入密码登录,登录后在 QMT 内完成相应初始化设置。非国金 QMT:请在登录界面勾选自动登录和记住密码。所有 QMT 都要先完成 Python 库下载。
等待退出或部署失败 提示等待退出时,请正常退出 QMT,保持 cfquant 运行,等待模型配置完成后再启动。部署失败时,按错误提示修正目录、权限或模型账号 Key 后重新保存。
极致模式账号绑定示意
极致模式账号与 QMT 目录配置示意,以当前绑定表单为准。

高级模式自动部署

  1. 配置账号。选择高级模式,分别填写普通端和极速交易端两个不同的 QMT 目录,系统分别部署对应策略。
  2. 保存绑定。勾选“自动导入并管理 QMT 策略”,确认账号、模拟/实盘和策略自动运行设置后保存。初始化与后续绑定均由系统自动导入策略。
  3. 启动并登录 QMT。按保存后的提示重启 QMT;勾选“自动启动 QMT”后,在启动的 QMT 中登录即可。QMT 已设置自动登录时,请等待自动登录完成。
  4. 检查连接。登录后查看绑定列表中的通道状态,或点击“检测连接”。未勾选“QMT 启动后自动运行”时,在“模型交易”运行已导入的托管策略。
QMT 登录前提 国金 QMT:请手动输入密码登录,登录后在 QMT 内完成相应初始化设置。非国金 QMT:请在登录界面勾选自动登录和记住密码。所有 QMT 都要先完成 Python 库下载。
等待退出或部署失败 提示等待退出时,请正常退出 QMT,保持 cfquant 运行,等待模型配置完成后再启动。部署失败时,按错误提示修正目录、权限或模型账号 Key 后重新保存。

两个 QMT 都需要完成登录。自动启动选项仅启动绑定的主 QMT 目录,极速交易端需单独启动并登录。

高级模式账号绑定示意
高级模式账号与 QMT 目录配置示意,以当前绑定表单为准。

四、写在最后

  • 部署相对复杂一些,但延迟确实是做得比较低。
  • 有不清楚的可以通过首页加入项目交流群。
  • 如有远程帮忙部署指导需求,可以添加微信 13696119612,服务费 100 元/次。
  • 如有其他二次开发需求、整体系统架构方案、多账号交易矩阵、低延迟链路需求,敬请联系。

同账号独立市场

适合同一资金账号拆成上海、深圳两个 QMT 交易端的场景。网页保存一个主账号,交易请求按证券后缀自动分流。

*.SH 走上海 QMT 子桥。 *.SZ 走深圳 QMT 子桥。 批量下单 自动拆成 SH、SZ 两组请求,再合并返回。

配置步骤

  1. 在“绑定”页编辑账号,打开“同账号独立市场路由”。
  2. 分别填写上海、深圳 QMT 的 bin.x64 目录。
  3. 保存后确认两个目录生成市场身份文件。
  4. 上海 QMT 加载 _SH.py,深圳 QMT 加载 _SZ.py
  5. 深圳入口需挂在深市标的运行,例如 SZ399001000001.SZ;不要把 _SZ.py 挂在 SH000300 上。

入口文件

模式 上海 深圳
通用 CFQUANT_CTYPE_ALL_LOWLAT_SH.py CFQUANT_CTYPE_ALL_LOWLAT_SZ.py
极致 CFQUANT_LITE_SH.py CFQUANT_LITE_SZ.py
高级交易端 CFQUANT_TRADE_LOWLAT_SH.py CFQUANT_TRADE_LOWLAT_SZ.py

多账号通道

bridge_id 是内部路由标识。普通用户只需要填写账号和 QMT 目录,Web 会按目录复用或分配通道。

场景 建议
单 QMT 单账号 使用默认通道 default
单 QMT 多账号 多个账号可共用同一个 QMT 目录。
多 QMT 多账号 每个账号填写实际登录它的 QMT bin.x64 目录。

排查要点

  • QMT 日志里的 bridge_id 要和网页绑定状态一致。
  • 目录填错时,Web 可能把身份文件写到另一个 QMT。
  • 外部程序优先传 account_id,不要手动写死 bridge_id

账号绑定

账号绑定是运行配置的核心。每个账号保存自己的账户类型、QMT 目录、首选模式、启用状态和共享行情源标记。

字段 说明
资金账号 外部请求按它路由,普通和信用账户要区分账户类型。
QMT 目录 填写对应 QMT 的安装目录或 bin.x64,用于自动同步项目代码、内部策略代码、身份文件和后续更新。
运行模式 ctypeslitelttx 三选一。
高级模式第二目录 选择高级模式时必须填写另一个 QMT 的安装目录或 bin.x64
共享行情源 多账号时只选一个稳定账号,避免重复订阅全推行情。

保存后怎么验证

  1. 刷新“绑定状态”,看首选模式和实际模式。
  2. 点击“验证”,确认资金和持仓来自正确账号。
  3. 高级模式离线时会自动回退通用模式,状态里会显示回退原因。

交易与回调

交易页适合做实盘前验证:先查资金和持仓,再用小数量测试下单、撤单和回调。

能力 说明
单笔下单 POST /api/order,对应 order_stock
批量下单 POST /api/orders/batch,逐笔提交并合并结果。
撤单 POST /api/cancel,委托状态仍以 QMT 和回调为准。
信用查询 支持信用资产、标的、担保品、合约等只读探测。
回调 GET /api/callbacksWS /ws/callbacks 接收委托、成交和错误事件。

实盘前检查

  • 确认页面当前账号就是要交易的资金账号。
  • 确认下单确认文本、价格、数量和买卖方向。
  • 确认回调页能收到委托或错误事件。
  • 自动化程序不要只看请求返回,还要结合回调或后续查询确认最终状态。

延迟参考

延迟只表示本机程序到 QMT 脚本的链路量级,不代表券商柜台或交易所确认速度。实际结果会受 QMT 版本、券商环境、机器负载和交易时段影响。

推荐读法 普通用户优先看稳定性;低延迟交易再比较极速交易端和 ctypes 交易通道。

当前测试量级

链路 查询量级 下单请求样本 适合场景
通用 ctypes 约 180-260 ms 约 20 ms 部署简单、功能验证、多账号日常使用。
高级普通端 约 250 ms 约 176 ms 查询、行情、账号级回调。
高级极速交易端 约 1-4 ms 约 1 ms 追求更低本机到 QMT 下单链路。

注意事项

  • 撤单请求返回和委托状态变成“已撤”不是同一个时点。
  • 午间、收盘后和首次请求通常比交易时段更不稳定。
  • 需要严肃评估时,用自己的券商、账号、机器和目标时间段重新测。

外部接口

“接口”页就是在线调试台。外部程序接入前,先在这里跑通同一个账号和参数。

curl -H "X-API-Key: your-api-key" ^
  "http://127.0.0.1:8765/api/account?account_id=YOUR_ACCOUNT_ID§ions=asset,positions&force=1"

启用 API Key 后,HTTP 使用 X-API-Key 请求头;WebSocket 使用 apikey 查询参数。

常用接口

接口 用途
GET /api/account资金、持仓、委托、成交。
POST /api/order单笔下单。
POST /api/orders/batch批量下单。
POST /api/cancel撤单。
POST /api/data/full-tick实时 Tick。
POST /api/data/marketK 线和行情数据。
POST /api/data/history/download历史行情下载。
POST /api/data/financial财务数据读取。
GET /api/callbacks读取交易回调。
WS /ws/callbacks实时交易回调。
WS /ws/quotes实时行情事件。

排查问题

按层排查最快:先确认 Web 能访问,再看本地服务,再看 QMT 脚本,最后看账号和权限。

现象 优先检查 处理
网页打不开 Web 端口、启动日志 重启服务,查看 log/cfquant_web_server.stderr.log
QMT 离线 入口脚本、QMT 登录、bridge_id 重启 QMT 入口,确认加载了当前模式对应脚本。
高级模式缺交易端 第二个 QMT 目录、CFQUANT_TRADE_LOWLAT.py 在绑定页补充极速交易端目录并重新保存。
查不到账号数据 账号类型、QMT 登录账号、绑定目录 在绑定页点击验证,确认资金和持仓来自预期账号。
下单失败 确认文本、价格数量、权限、回调错误 先小数量测试,再查看回调页和 QMT 日志。
版本或缓存异常 前端版本、浏览器缓存 重启 Web 后用 Ctrl + F5 强制刷新。

日志

教程中心