Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ Use `uv run ...` unless the package is installed into the active global Python.
- Do not store credentials or account secrets.
- Do not auto-download, vendor, or install broker QMT.
- Do not place real orders in tests.
- Keep `data-call` and `trade-call` blocking `_`-prefixed methods.
- Keep order validation: positive price and positive A-share volume multiple of 100.
- qmtcli is read-only: do not add order placement, cancellation, or fund transfer (no `buy`/`sell`/
`cancel`). Orders go through a gateway with per-order human confirmation outside this tool.
- Keep `data-call` blocking `_`-prefixed methods and `trade-call` limited to `query_*` /
`smt_query_*` (`READ_ONLY_TRADER_PREFIXES` in `gateway.py`).
- Keep `server` as stdin/stdout JSONL, not TCP/HTTP.

## Common Agent Tasks
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ begin.

## Unreleased

- **Breaking:** qmtcli is now read-only. Removed `buy`, `sell`, and `cancel` from the CLI, `rpc`,
`server`, and `capabilities`, and `trade-call` now only accepts `query_*` / `smt_query_*`
methods. An agent could place an order through these without per-order approval; orders now go
through a gateway with per-order human confirmation outside this tool.
- Removed a host-dependent offline test that could discover a real QMT SDK and
initialize its trader; deterministic fake-SDK and error-envelope coverage remains.
- Updated locked urllib3 to 2.8.0 and certifi to 2026.10.19 for transport security
Expand Down
40 changes: 19 additions & 21 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ handling NaN, and guessing which field encoding to trust are your problem too.

`qmtcli` exists to do that work once: it starts a small local process, takes JSON in, hands JSON
back, and talks to the QMT client you already have logged in. Everything the official QMT/XtQuant
API exposes — diagnostics, market data, account queries, and guarded stock order placement (see the
API exposes — diagnostics, market data, and account queries (see the
official [QMT beginner guide](https://dict.thinktrader.net/freshman/rookie.html)) — is reachable
through this one stable stdin/stdout JSON contract.

Expand All @@ -37,8 +37,8 @@ copy, see [Install](#install)). QMT must already be installed and logged in loca

## Who This Is For

- Anyone who wants an LLM agent (Claude, GPT, a local model) to check quotes, read positions, or
place guarded orders directly — `qmtcli mcp` wires it into MCP in one command, with tools
- Anyone who wants an LLM agent (Claude, GPT, a local model) to check quotes or read positions
directly — `qmtcli mcp` wires it into MCP in one command, with tools
generated from the capability list instead of a hand-maintained second registry.
- Teams building a quant system in Rust, Go, or Node.js who don't want to stand up a separate
Python sidecar just to talk to QMT.
Expand Down Expand Up @@ -95,7 +95,7 @@ contract instead:
- `capabilities`, `schema`, and `examples` for discovery;
- `rpc` for one request over stdin/stdout;
- `server` for newline-delimited JSON requests;
- explicit labels for read-only calls, escape hatches, order placement, and cancellation;
- explicit labels for read-only calls and escape hatches;
- tests that fake `xtquant`, so CI does not need a broker install.

Any runtime that can start a local process and exchange JSON can use it — Python, Rust, Go,
Expand Down Expand Up @@ -127,9 +127,10 @@ Node.js, shell scripts, or an LLM agent, no language-specific SDK or binding req
- Trader query commands: position statistics, credit/margin queries, IPO quota and data, account
infos/status, and ordinary-account fund/position queries — see
[Trade Query Commands](#trade-query-commands).
- Fixed-price A-share `buy`, `sell`, and `cancel`.
- **Read-only**: `qmtcli` never places or cancels orders or moves funds. Orders go through a gateway
with per-order human confirmation, outside this tool.
- Generic `data-call` for public `xtquant.xtdata` methods.
- Generic `trade-call` for public `XtQuantTrader` methods.
- Generic `trade-call` for `XtQuantTrader` query methods (`query_*` / `smt_query_*`).
- Optional standalone xtdatacenter (xtdc) data mode via `--xtdc-token` (experimental) — see
[Standalone Data Mode](#standalone-data-mode-xtdc-experimental).
- Agent/script protocol commands: `capabilities`, `schema`, `examples`, `rpc`, `server`.
Expand Down Expand Up @@ -276,9 +277,8 @@ Tool names are `qmt_<name>` with dashes replaced by underscores, for example `qm
CLI has — every trade-query/account tool takes optional `path`/`account`/`account_type`
arguments per call instead.

Excluded by design: `buy`, `sell`, `cancel` (order placement/cancellation) and `trade_call` (an
unguarded escape hatch onto the full `XtQuantTrader` surface) — use the CLI directly for guarded
trading. `watch` and the `subscribe`/`subscribe_whole`/`unsubscribe` trio are also excluded (CLI-
Excluded by design: `trade_call` (an escape hatch onto caller-selected `XtQuantTrader` query
methods; the named queries cover agent use). `watch` and the `subscribe`/`subscribe_whole`/`unsubscribe` trio are also excluded (CLI-
only / server-only streaming, not a fit for one-shot tool calls). Everything else — `status`,
`doctor`, `data_call`, `fields`, `download`, every named data command, and every trade query
(`asset`, `positions`, `orders`, `trades`, ...) — is available.
Expand Down Expand Up @@ -383,17 +383,14 @@ qmtcli sector reset MySector 600519.SH
qmtcli sector remove MySector
```

Account and order commands require `--account`:
Account commands require `--account`:

```powershell
qmtcli --account ACCOUNT_ID asset
qmtcli --account ACCOUNT_ID positions
qmtcli --account ACCOUNT_ID orders
qmtcli --account ACCOUNT_ID orders --cancelable-only
qmtcli --account ACCOUNT_ID trades
qmtcli --account ACCOUNT_ID buy 600519.SH 100 1500.00
qmtcli --account ACCOUNT_ID sell 600519.SH 100 1500.00
qmtcli --account ACCOUNT_ID cancel ORDER_ID
```

## Trade Query Commands
Expand Down Expand Up @@ -433,13 +430,15 @@ qmtcli data-call get_stock_list_in_sector --args "[\"沪深A股\"]"
qmtcli data-call get_cb_info --args "[\"123001.SZ\"]"
```

`trade-call` calls any public method on `XtQuantTrader` after connecting the account. By default,
`trade-call` calls an `XtQuantTrader` query method after connecting the account; only `query_*` and
`smt_query_*` are allowed, and account-changing methods such as `order_stock`,
`cancel_order_stock`, and `fund_transfer` are refused. By default,
the `StockAccount` object is prepended to positional arguments:

```powershell
qmtcli --account ACCOUNT_ID trade-call query_stock_orders
qmtcli --account ACCOUNT_ID trade-call some_method --args "[1,2]" --kwargs "{\"flag\":true}"
qmtcli --account ACCOUNT_ID trade-call method_without_account --no-account
qmtcli --account ACCOUNT_ID trade-call query_stock_order --args "[12345]"
qmtcli --account ACCOUNT_ID trade-call query_account_infos --no-account
```

Private method names beginning with `_` are blocked.
Expand Down Expand Up @@ -469,7 +468,7 @@ yet wired to a specific xtdatacenter call. `qmtcli doctor` reports whether the
```json
{"command":"status"}
{"command":"data_call","method":"get_stock_list_in_sector","args":["沪深A股"]}
{"command":"buy","account":"ACCOUNT_ID","symbol":"600519.SH","volume":100,"price":1500.0}
{"command":"positions","account":"ACCOUNT_ID"}
```

For JSONL `server`, one input line produces one output line. Request `id` is echoed.
Expand All @@ -479,12 +478,11 @@ For JSONL `server`, one input line produces one output line. Request `id` is ech
- Local only: `server` reads stdin and writes stdout; it does not open a socket.
- No broker software download or auto-install.
- No credential storage.
- No order retry loop.
- `capabilities` marks order placement and cancel actions as dangerous, marks `download` as
- No order placement, cancellation, or fund transfer: there is no `buy`/`sell`/`cancel`, and
`trade-call` only allows query methods.
- `capabilities` marks `download` as
`downloads_data` since it writes into the local QMT cache, and marks `sector` as
`mutates_local_data` since it creates/modifies local custom sector definitions.
- A-share order volume must be a positive multiple of 100.
- Order price must be positive.
- Private `xtdata` / `XtQuantTrader` method names are blocked.
- QMT account permissions, risk checks, and final execution remain controlled by QMT and the broker.

Expand Down
32 changes: 14 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

给 QMT / XtQuant 装一层稳定的接口,让任何语言、任何 Agent 都能安全稳定的调用它。

- **一句话**:让 AI 用 JSON 跟 QMT 聊 A 股——行情账户随便查,下单有护栏。
- **一句话**:让 AI 用 JSON 跟 QMT 聊 A 股——行情账户随便查,不下单。
- **适合谁**:想把行情、账户、交易能力接进自己的脚本或 Agent,又不想再啃 xtquant 的人。
- **不适合谁**:不碰 A 股程序化交易,或者只需要一个现成看盘软件的人。

Expand Down Expand Up @@ -38,7 +38,7 @@ QMT 自带的那份,见[安装](#安装))。使用前需要先在本机安

## 适合谁

- 想让 LLM Agent(Claude、GPT、本地模型)直接查行情、看持仓、下单的人——`qmtcli ` 一条
- 想让 LLM Agent(Claude、GPT、本地模型)直接查行情、看持仓的人——`qmtcli ` 一条
命令接进 ,工具从能力清单自动生成,不用手写第二套注册表。
- 想用 Rust、Go、Node.js 写量化系统,又不想专门为了调 QMT 起一个 Python 服务的人。
- 想要一份稳定的 JSON 链接,而不是每次券商升级 QMT 就得重新猜 `xtquant` 参数和字段变化的人。
Expand Down Expand Up @@ -91,7 +91,7 @@ QMT 把 `xtquant` 放在券商客户端安装目录里。直接在交易机上
- `capabilities`、`schema`、`examples` 用于能力发现;
- `rpc` 用于 stdin/stdout 单请求;
- `server` 用于逐行 JSON 请求;
- 对只读调用、escape hatch、下单、撤单做明确标记;
- 对只读调用、escape hatch 做明确标记;
- 测试里 fake `xtquant`,CI 不需要真实券商环境。

只要一个运行时能启动本地进程并交换 JSON,就能使用它——Python、Rust、Go、Node.js、Shell 脚本,
Expand Down Expand Up @@ -121,9 +121,9 @@ QMT 把 `xtquant` 放在券商客户端安装目录里。直接在交易机上
- 账户查询:资产、持仓、委托(支持 `--cancelable-only`)、成交。
- 交易查询命令:持仓统计、信用/融资融券查询、新股额度与数据、账号信息/状态、普通柜台资金/持仓
查询——见[交易查询命令](#交易查询命令)。
- 固定价 A 股 `buy`、`sell`、`cancel`。
- **只读**:`qmtcli` 不下单、不撤单、不划资金。下单走带逐笔人工确认的网关,不在这个工具里。
- `data-call` 调用公开的 `xtquant.xtdata` 方法。
- `trade-call` 调用公开的 `XtQuantTrader` 方法。
- `trade-call` 调用 `XtQuantTrader` 的查询方法(`query_*` / `smt_query_*`)。
- 可选的独立 xtdatacenter(xtdc)数据模式,通过 `--xtdc-token` 开启(实验性)——见
[独立数据模式](#独立数据模式xtdc实验性)。
- Agent / 脚本协议命令:`capabilities`、`schema`、`examples`、`rpc`、`server`。
Expand Down Expand Up @@ -264,8 +264,8 @@ claude mcp add qmt -- uv run --directory D:/projects/qmtcli --extra mcp qmtcli m
`qmt_account_infos`。这里没有像 CLI 那样常驻的 `--account`/`--path`——每个交易查询/账户类工具
都在每次调用时接受可选的 `path`/`account`/`account_type` 参数。

以下按设计被排除:`buy`、`sell`、`cancel`(下单/撤单)和 `trade_call`(直接暴露完整
`XtQuantTrader` 方法面的非受控 escape hatch)——需要下单交易请直接使用 CLI。`watch` 以及
以下按设计被排除:`trade_call`(由调用方任选 `XtQuantTrader` 查询方法的 escape hatch,
具名查询已覆盖 Agent 的用途)。`watch` 以及
`subscribe`/`subscribe_whole`/`unsubscribe` 三件套也被排除(分别是仅 CLI、仅 server 的流式推送,
不适合一次性的工具调用)。其余的——`status`、`doctor`、`data_call`、`fields`、`download`、所有
行情命令,以及所有交易查询(`asset`、`positions`、`orders`、`trades` 等)——都可以使用。
Expand Down Expand Up @@ -377,9 +377,6 @@ qmtcli --account ACCOUNT_ID positions
qmtcli --account ACCOUNT_ID orders
qmtcli --account ACCOUNT_ID orders --cancelable-only
qmtcli --account ACCOUNT_ID trades
qmtcli --account ACCOUNT_ID buy 600519.SH 100 1500.00
qmtcli --account ACCOUNT_ID sell 600519.SH 100 1500.00
qmtcli --account ACCOUNT_ID cancel ORDER_ID
```

## 交易查询命令
Expand Down Expand Up @@ -417,13 +414,14 @@ qmtcli data-call get_stock_list_in_sector --args "[\"沪深A股\"]"
qmtcli data-call get_cb_info --args "[\"123001.SZ\"]"
```

`trade-call` 在连接账户后调用公开的 `XtQuantTrader` 方法。默认会把 `StockAccount` 对象
`trade-call` 在连接账户后调用 `XtQuantTrader` 的查询方法,只放行 `query_*` 和 `smt_query_*`;
`order_stock`、`cancel_order_stock`、`fund_transfer` 等会改变账户的方法一律拒绝。默认会把 `StockAccount` 对象
放在位置参数最前面:

```powershell
qmtcli --account ACCOUNT_ID trade-call query_stock_orders
qmtcli --account ACCOUNT_ID trade-call some_method --args "[1,2]" --kwargs "{\"flag\":true}"
qmtcli --account ACCOUNT_ID trade-call method_without_account --no-account
qmtcli --account ACCOUNT_ID trade-call query_stock_order --args "[12345]"
qmtcli --account ACCOUNT_ID trade-call query_account_infos --no-account
```

`_` 开头的私有方法名会被阻止。
Expand Down Expand Up @@ -451,7 +449,7 @@ xtdatacenter 调用上。`qmtcli doctor` 会用 `xtdc_available` 字段报告 `x
```json
{"command":"status"}
{"command":"data_call","method":"get_stock_list_in_sector","args":["沪深A股"]}
{"command":"buy","account":"ACCOUNT_ID","symbol":"600519.SH","volume":100,"price":1500.0}
{"command":"positions","account":"ACCOUNT_ID"}
```

`server` 模式下一行输入对应一行输出。请求里的 `id` 会被带回。
Expand All @@ -461,11 +459,9 @@ xtdatacenter 调用上。`qmtcli doctor` 会用 `xtdc_available` 字段报告 `x
- 本地使用:`server` 只读 stdin、写 stdout,不打开 socket。
- 不下载或自动安装券商软件。
- 不保存账号密码或登录凭据。
- 不自动重试下单。
- `capabilities` 会标记下单和撤单能力为危险动作;`download` 因为写入本地 QMT 缓存,被标记为
- 不下单、不撤单、不划资金:没有 `buy`/`sell`/`cancel`,`trade-call` 只放行查询方法。
- `capabilities` 中 `download` 因为写入本地 QMT 缓存,被标记为
`downloads_data`;`sector` 因为创建/修改本地自定义板块,被标记为 `mutates_local_data`。
- A 股委托数量必须是正的 100 倍数。
- 委托价格必须大于 0。
- 阻止调用 `_` 开头的 `xtdata` / `XtQuantTrader` 方法。
- 账户权限、风控检查和最终成交仍由 QMT 和券商控制。

Expand Down
14 changes: 7 additions & 7 deletions docs/xttrader-alignment.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ Checked: 2026-07-07, against `xtquant` 250516.1.1.

This table maps every function documented on the xttrader doc page to its `qmtcli` CLI surface.
Where a function is not exposed as a named command, it can still be called through the generic
`trade-call` (read/query or mutating) escape hatch, at the caller's own risk — no argument
validation beyond blocking private (`_`-prefixed) method names.
`trade-call` escape hatch — but only query methods (`query_*` / `smt_query_*`). qmtcli is read-only:
every order, cancel, and fund-transfer method is refused, listed below as "refused".

| XtQuantTrader function | doc section (Chinese) | CLI command | notes |
| --- | --- | --- | --- |
| `XtQuantTrader`, `register_callback`, `start`, `connect`, `stop`, `run_forever`, `set_relaxed_response_order_enabled` | 创建API实例 / 注册回调类 / 准备API环境 / 创建连接 / 停止运行 / 阻塞当前线程进入等待状态 / 开启主动请求接口的专用线程 | not exposed | internal gateway lifecycle; `QMTGateway.connect()` creates the `XtQuantTrader` instance and calls `start()`/`connect()` itself, once per CLI invocation |
| `subscribe`, `unsubscribe` | 订阅账号信息 / 反订阅账号信息 | internal, auto on connect | `QMTGateway.connect()` calls `trader.subscribe(account)` automatically whenever an account id is present (skipped for the account-less queries below); not a standalone command |
| `order_stock` | 股票同步报单 | `buy` / `sell` | fixed-price A-share orders |
| `order_stock_async`, `cancel_order_stock_async`, `cancel_order_stock_sysid`, `cancel_order_stock_sysid_async` | 股票异步报单 / 股票异步撤单 / 股票同步撤单(按 sysid) / 股票异步撤单(按 sysid) | `trade-call` | escape hatch only; the async variants are unsuitable for a one-shot CLI process, and the sysid-keyed cancel is an alternate identifier scheme not wired to the named `cancel` command |
| `cancel_order_stock` | 股票同步撤单 | `cancel` | |
| `fund_transfer`, `sync_transaction_from_external`, `smt_negotiate_order_async` | 资金划拨 / 外部交易数据录入 / 库存券约券申请 | `trade-call` | mutating; at the caller's own risk, no extra validation beyond blocking `_`-prefixed names |
| `order_stock` | 股票同步报单 | refused | qmtcli never places orders |
| `order_stock_async`, `cancel_order_stock_async`, `cancel_order_stock_sysid`, `cancel_order_stock_sysid_async` | 股票异步报单 / 股票异步撤单 / 股票同步撤单(按 sysid) / 股票异步撤单(按 sysid) | refused | qmtcli never places or cancels orders |
| `cancel_order_stock` | 股票同步撤单 | refused | qmtcli never cancels orders |
| `fund_transfer`, `sync_transaction_from_external`, `smt_negotiate_order_async` | 资金划拨 / 外部交易数据录入 / 库存券约券申请 | refused | mutating; not a query method |
| `query_stock_asset` | 资产查询 | `asset` | |
| `query_stock_orders` | 委托查询 | `orders` (`--cancelable-only`) | `cancelable_only` defaults to false, matching the SDK default |
| `query_stock_trades` | 成交查询 | `trades` | |
Expand All @@ -33,7 +33,7 @@ validation beyond blocking private (`_`-prefixed) method names.
| `query_account_status` | 账号状态查询 | `account-status` | connects to QMT session; no account id needed — the method itself takes no account argument |
| `query_com_fund` | 普通柜台资金查询 | `com-fund` | |
| `query_com_position` | 普通柜台持仓查询 | `com-position` | |
| `export_data`, `query_data` | 通用数据导出 / 通用数据查询 | `trade-call` | generic export/query surface; not individually wired to a named command |
| `export_data`, `query_data` | 通用数据导出 / 通用数据查询 | `trade-call` (`query_data` only) | `export_data` writes files and is refused; `query_data` is reachable as a query method |
| `smt_query_quoter`, `smt_query_compact` | 券源行情查询 / 约券合约查询 | `trade-call` | read-only securities-lending (约券) queries; documented on the doc page but not independently wired to a named command in this wave |
| `on_disconnected`, `on_account_status`, `on_stock_order`, `on_stock_trade`, `on_order_error`, `on_cancel_error`, `on_order_stock_async_response`, `on_smt_appointment_async_response` | 连接状态回调 / 账号状态信息推送 / 委托信息推送 / 成交信息推送 / 下单失败信息推送 / 撤单失败信息推送 / 异步下单回报推送 / 约券相关异步接口的回报推送 | not exposed | trader push callbacks delivered through `register_callback`; server mode's streaming support (`subscribe`/`subscribe_whole`/`unsubscribe`) covers `xtdata` quote subscriptions only, not these `XtQuantTrader` callbacks |

Expand Down
8 changes: 4 additions & 4 deletions examples/agent_tool.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ Send one request per line:

Treat these as requiring explicit user approval in any agent policy:

- `buy` - places an order
- `sell` - places an order
- `cancel` - cancels an order
- `trade_call` - arbitrary public `XtQuantTrader` method
- `trade_call` - caller-selected `XtQuantTrader` query method (`query_*` / `smt_query_*` only)

qmtcli has no order placement or cancellation. Orders go through a gateway with per-order human
confirmation, outside this tool.

`data_call` is an escape hatch for public `xtquant.xtdata` methods. It is usually read-only, but
agents should still inspect the requested method before calling it.
Expand Down
Loading
Loading