Skip to content

Latest commit

 

History

History
535 lines (415 loc) · 19.5 KB

File metadata and controls

535 lines (415 loc) · 19.5 KB

qmtcli

tests Python License

中文 | English

A stable JSON interface in front of QMT / XtQuant, so any language and any agent can call it safely.

Current state: 60+ CLI subcommands; checked function-by-function against the official xtdata doc, covering 43 functions (see docs/xtdata-alignment.md); checked against the official xttrader doc, covering 25 query/trading method groups (see docs/xttrader-alignment.md); 267 automated tests, all green, with xtquant fully faked so CI never needs a real QMT install; a weekly doc-drift CI job re-checks both matrices against the live doc pages for drift.

Anyone who has automated QMT knows the friction: xtquant only lives inside the broker client's install directory, so import-ing it directly ties your scripts, notebooks, and agents to that one Python environment. Want to swap in a market-data engine written in Rust or Go? Want to wire up an LLM agent to watch positions, flag anomalies, and write trade logs? Suddenly serializing DataFrames, 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 official QMT beginner guide) — is reachable through this one stable stdin/stdout JSON contract.

qmtcli local JSON bridge

qmtcli does not install or launch the QMT trading client itself, store credentials, bypass broker risk checks, or run a network service — but it does auto-discover and wire up the local XtQuant SDK environment (preferring an already-installed venv/pip xtquant over the QMT-bundled copy, see Install). QMT must already be installed and logged in locally.

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 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.
  • Anyone who wants a stable JSON contract instead of re-guessing xtquant parameter and field changes every time the broker ships a QMT update.
  • CI pipelines that need to run tests on a machine without QMT installed at all — the test suite fakes xtquant, so no broker install is required.

Quick Demo

Discover the command surface:

qmtcli capabilities
qmtcli schema
qmtcli examples

Run one JSON request:

Get-Content examples\status.json | qmtcli rpc

Run a JSONL loop for an agent or script:

qmtcli server

Response shape:

{"ok":true,"data":{}}

Error shape:

{"ok":false,"error":"message"}

If a request includes id, the response echoes it.

Why

QMT bundles xtquant inside the broker client install. That is fine for direct Python scripts on a trading machine, but awkward for agents and process-based automation — you inherit whatever numpy/pandas version QMT ships, and a silent broker-side upgrade can quietly change how a DataFrame serializes or drop a field you were relying on. qmtcli keeps that instability local, in a subprocess you can restart at will, and gives callers a simple, testable, version-stable JSON 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;
  • 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, Node.js, shell scripts, or an LLM agent, no language-specific SDK or binding required.

Features

  • QMT SDK discovery from common Windows install paths, preferring an already-installed venv/pip xtquant over the QMT-bundled copy (see Install).
  • status and doctor diagnostics, including SDK source (environment / qmt_bundled / missing), xtquant version, and xtdc availability.
  • Market data commands: calendar, trading dates, sectors, ticks, bars, local cached bars (local-data), full-push K-line (full-kline), L2 data, instrument/ETF/CB/IPO/index-weight metadata, and financials.
  • sector command family for creating, populating, and deleting local custom sectors (create-folder, create, add, remove-stocks, remove, reset).
  • watch: CLI-only live quote streaming (JSONL to stdout) via subscribe_quote/ subscribe_whole_quote, blocking until Ctrl+C — see CLI Streaming.
  • mcp: stdio MCP server exposing qmtcli commands as MCP tools, generated from AGENT_CAPABILITIES — see MCP Server.
  • fields: static xtdata field-name reference dictionaries (tick, kline, balance, ...) extracted from the doc appendix. Fully offline, no QMT install needed.
  • pandas-aware JSON output: DataFrames/Series returned by xtdata are serialized as records (a time index is kept as a plain column), and NaN/NaT/pd.NA/numpy scalars all become plain JSON values instead of invalid NaN tokens or unreadable object dumps.
  • download command for populating the local QMT cache (history, financials, sectors, index weight, convertible bonds, ETF info, holidays, contracts).
  • Account queries: asset, positions, orders (with --cancelable-only), trades.
  • 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.
  • Fixed-price A-share buy, sell, and cancel.
  • Generic data-call for public xtquant.xtdata methods.
  • Generic trade-call for public XtQuantTrader methods.
  • Optional standalone xtdatacenter (xtdc) data mode via --xtdc-token (experimental) — see Standalone Data Mode.
  • Agent/script protocol commands: capabilities, schema, examples, rpc, server.
  • A scheduled doc-drift GitHub Action (weekly, plus manual dispatch) re-checks the alignment matrices below against the live doc pages and files an issue if a new doc function is not yet reflected in either matrix; see scripts/check_doc_drift.py.

See docs/xtdata-alignment.md and docs/xttrader-alignment.md for the full function-to-command coverage matrices against the official xtdata and xttrader docs.

Install

Recommended: install with the sdk extra so xtquant/pandas/numpy come from this environment instead of QMT's bundled site-packages; the broker QMT client then only needs to provide the local trading/data service, not the Python SDK itself:

uv sync --extra dev --extra sdk
pip install -e ".[dev,sdk]"

Bundled-SDK fallback (no sdk extra) still works — qmtcli falls back to the QMT install's own xtquant/numpy/pandas when nothing is already importable — but the bundled copy can be older than the current docs (for example, some bundled builds lack get_period_list) and its bundled numpy should never be allowed to shadow a venv's own numpy. qmtcli doctor reports which source won as sdk_source. See Runtime caveats for details.

For local development:

uv sync --extra dev
uv run qmtcli status

Editable pip install:

pip install -e .[dev]
qmtcli status

Agent And Script Usage

Discovery:

qmtcli capabilities
qmtcli schema
qmtcli examples

One-shot RPC:

Get-Content examples\status.json | qmtcli rpc
Get-Content examples\data_call.json | qmtcli rpc

JSONL loop:

.\examples\jsonl_server.ps1

Generic integration notes are in examples/agent_tool.md. A short demo recording script is in docs/demo_storyboard.md.

All named data commands (calendar, bars, sector-stocks, download, ...) are available over rpc/server too, not just the CLI; send their CLI parameter names as JSON fields, for example {"command":"bars","symbols":["600519.SH"],"period":"1d"}. The command name accepts either dashes or underscores (sector-stocks or sector_stocks).

Server Streaming

In server mode only, subscribe, subscribe_whole, and unsubscribe wrap the callback-based xtdata.subscribe_quote, subscribe_whole_quote, and unsubscribe_quote:

{"command":"subscribe","symbol":"600519.SH","period":"1d"}
{"command":"subscribe_whole","symbols":["600519.SH","000001.SZ"]}
{"command":"unsubscribe","seq":1}

Subscribing responds with {"ok":true,"data":{"seq":1}}, and every later quote push from xtquant is printed as its own JSONL line carrying an event field instead of ok, interleaved with normal responses on the same stdout stream:

{"ok":true,"data":{"seq":1}}
{"event":"quote","seq":1,"symbol":"600519.SH","data":{"600519.SH":{"lastPrice":1500.0}}}

The one-shot rpc command and the CLI reject subscribe commands with {"ok":false,"error": "subscribe commands require server mode"}, since a subscription only makes sense while server keeps the process alive.

CLI Streaming (watch)

watch is a CLI-only alternative to server-mode subscribe/subscribe_whole: it subscribes directly, then blocks in xtdata.run(), printing one JSONL line per quote push until interrupted (Ctrl+C exits cleanly with code 0). It is not available over rpc/server.

qmtcli watch 600519.SH
qmtcli watch 600519.SH 000001.SZ --period 1m
qmtcli watch 600519.SH 000001.SZ --whole
{"event":"quote","symbol":"600519.SH","data":{"600519.SH":{"lastPrice":1500.0}}}

MCP Server

qmtcli mcp runs a stdio MCP (Model Context Protocol) server exposing qmtcli commands as MCP tools. Capabilities-driven, single source of truth: tools are generated from AGENT_CAPABILITIES, and every tool call is dispatched through the same rpc machinery as rpc/server — there is no second command registry.

Install the mcp extra:

uv sync --extra mcp

Run it directly (talks JSON-RPC over stdio; nothing else prints to stdout):

qmtcli mcp

Register it with Claude Code:

claude mcp add qmt -- uv run --directory D:/projects/qmtcli --extra mcp qmtcli mcp

Tool names are qmt_<name> with dashes replaced by underscores, for example qmt_full_tick, qmt_sector_stocks, qmt_account_infos. There is no persistent --account/--path like the 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- 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.

Without the mcp extra installed, qmtcli mcp prints {"ok":false,"error":"mcp extra is not installed; pip install 'qmtcli[mcp]'"} and exits 1.

QMT Paths

Pass either a QMT install root or its userdata_mini directory:

qmtcli --path D:\DFZQxtqmt_client_real_win64 doctor
qmtcli --path D:\DFZQxtqmt_client_real_win64\userdata_mini --account ACCOUNT_ID asset

When --path is omitted, these roots are checked:

  • D:\DFZQxtqmt_client_real_win64
  • D:\DFZQxtqmt_client_test_win64
  • C:\DFZQxtqmt_client_real_win64
  • C:\DFZQxtqmt_client_test_win64

Expected SDK location:

<QMT root>\bin.x64\Lib\site-packages\xtquant

Common Commands

Diagnostics:

qmtcli status
qmtcli doctor
python -m qmtcli status

Market data:

qmtcli calendar SH
qmtcli trading-dates SH --count 5
qmtcli sector-list
qmtcli sector-stocks 沪深A股
qmtcli full-tick 600519.SH 000001.SZ
qmtcli bars 600519.SH --period 1d --count 100
qmtcli local-data 600519.SH --period 1d --count 100
qmtcli full-kline 600519.SH --period 1m
qmtcli instrument-detail 600519.SH
qmtcli instrument-type 600519.SH
qmtcli divid-factors 600519.SH
qmtcli holidays
qmtcli period-list
qmtcli ipo-info
qmtcli cb-info 123001.SZ
qmtcli etf-info
qmtcli index-weight 000300.SH
qmtcli financials 600519.SH --tables Balance

Static field-name reference dictionaries (fully offline, no QMT install needed):

qmtcli fields
qmtcli fields tick
qmtcli fields balance

L2 commands require Level-2 market data permission from the broker and are not covered by the public xtdata doc page:

qmtcli l2-quote 600519.SH
qmtcli l2-order 600519.SH
qmtcli l2-transaction 600519.SH

Download data into the local QMT cache; these calls return once the SDK download finishes and do not print market data themselves — use the read commands above for that:

qmtcli download history 600519.SH --period 1d
qmtcli download financials 600519.SH --tables Balance
qmtcli download sectors
qmtcli download index-weight
qmtcli download cb
qmtcli download etf
qmtcli download holidays
qmtcli download history-contracts

Manage local custom sectors (mutates local sector definitions only; danger: mutates_local_data):

qmtcli sector create-folder MyGroup --parent 我的自定义板块
qmtcli sector create MySector --parent 我的自定义板块
qmtcli sector add MySector 600519.SH 000001.SZ
qmtcli sector remove-stocks MySector 600519.SH
qmtcli sector reset MySector 600519.SH
qmtcli sector remove MySector

Account and order commands require --account:

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

These wrap named XtQuantTrader query methods; see docs/xttrader-alignment.md for the full mapping. Most require --account:

qmtcli --account ACCOUNT_ID position-statistics
qmtcli --account ACCOUNT_ID credit-detail
qmtcli --account ACCOUNT_ID stk-compacts
qmtcli --account ACCOUNT_ID credit-subjects
qmtcli --account ACCOUNT_ID credit-slo-code
qmtcli --account ACCOUNT_ID credit-assure
qmtcli --account ACCOUNT_ID ipo-limit
qmtcli --account ACCOUNT_ID com-fund
qmtcli --account ACCOUNT_ID com-position

ipo-data, account-infos, and account-status wrap XtQuantTrader methods that take no account argument at all, so --account is optional for these three — qmtcli still connects a QMT session, it just skips subscribing an account:

qmtcli ipo-data
qmtcli account-infos
qmtcli account-status

Escape Hatches

data-call calls any public method on xtquant.xtdata:

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, the StockAccount object is prepended to positional arguments:

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

Private method names beginning with _ are blocked.

Standalone Data Mode (xtdc, experimental)

--xtdc-token (or the QMTCLI_XTDC_TOKEN environment variable) enables standalone xtdatacenter data mode: before running any command, qmtcli calls xtquant.xtdatacenter.set_token() and .init(), so xtdata calls can route through the 迅投投研 standalone data service instead of a local QMT client:

$env:QMTCLI_XTDC_TOKEN = "YOUR_TOKEN"
qmtcli calendar SH

qmtcli --xtdc-token YOUR_TOKEN --xtdc-port 58620 calendar SH

This requires a valid 迅投投研 data token and is experimental — verified only as far as init() succeeding, not end-to-end against real data. --xtdc-port is accepted (default 58620) but not yet wired to a specific xtdatacenter call. qmtcli doctor reports whether the xtquant.xtdatacenter module itself is importable as xtdc_available. See Runtime caveats for more.

JSON Request Examples

{"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}

For JSONL server, one input line produces one output line. Request id is echoed.

Safety Boundaries

  • 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 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.

Command Help

qmtcli --help
qmtcli capabilities --help
qmtcli schema --help
qmtcli examples --help
qmtcli data-call --help
qmtcli download --help
qmtcli sector --help
qmtcli fields --help
qmtcli trade-call --help
qmtcli watch --help
qmtcli rpc --help
qmtcli server --help
qmtcli mcp --help

Development

uv run --extra dev pytest -q
uv run --extra dev ruff check .
uv build

Regenerate the static fields catalog from the doc appendix, and check the alignment matrices for drift against the live doc pages (also runs on a weekly schedule in CI, see .github/workflows/doc-drift.yml):

python scripts/extract_doc_fields.py
python scripts/check_doc_drift.py

For coding agents, see AGENTS.md.

PyPI packaging metadata is present so the name/build shape is reserved for future publishing, but this project is not published by this repository workflow.

Feedback

If qmtcli saved you the trouble of integrating QMT yourself, a star is the most direct way to help others find it. Hit a rough edge, missing command, or an agent framework it doesn't fit smoothly — open an issue or send a PR.