qstrade
面向中国股票与期货的 Python 行情、交易及事件驱动策略库。当前版本 0.0.2,提供 Insight、天勤行情适配、CTP 期货交易及 MA5/MA10 策略示例。
公共接口统一使用 Wind 证券代码;Tick、Bar 以统一字典返回。导入库不会登录账户或加载原生 SDK,由调用方管理连接与退出。
环境与依赖
项目支持 Python 3.9 及以上版本,原生 SDK 还需匹配操作系统、Python 版本和系统位数;已验证环境为 Windows、Python 3.9。
默认依赖包括 tqsdk==3.8.3、gdp-insight-py-win==1.0.0 和 insight-py-win==6.1.1。其中 gdp-insight-py-win 提供 gdp_python,当前 Insight 适配器使用的 insight_python 由 insight-py-win 提供。CTP Python 绑定仍需由调用方提供,并通过配置指定 SDK 路径。
配置与初始化
create_market() 根据默认配置创建未登录实例,login() 成功后返回该实例自身,支持链式调用;登录失败会抛出异常并尝试清理会话。
from qstrade.market import create_market
market = create_market().login()
try:
market.subscribe_tick(["C2701.DCE"]) # 默认打印标准 Tick 数据
input("按回车结束订阅\n")
finally:
market.logout()
系统只从 JSON 配置文件读取配置,不读取环境变量,也不会被环境变量覆盖。默认读取当前工作目录的 config.local.json;显式调用 load_config(path) 时只读取指定文件。指定文件不存在会报错,默认文件不存在时使用内置默认值及空凭据,连接前仍需填写实际账户配置。相对 SDK 和运行目录路径以当前工作目录为基准。
需要指定配置文件或行情源时:
from qstrade.config import load_config
from qstrade.market import create_market
settings = load_config("config.local.json")
market = create_market(provider="tqsdk", settings=settings).login()
try:
print("行情已登录:", market.connected)
finally:
market.logout()
provider 可选 insight 或 tqsdk,省略时使用 market.default_provider。策略引擎按交易类型选择 market.futures_provider 或 market.stock_provider;天勤适配目前只支持期货。凭据统一放在配置文件的 insight、tqsdk、ctp 段中,CTP SDK 路径使用 ctp.sdk_path。
独立使用天勤行情的最小配置文件示例:
{
"market": {
"default_provider": "tqsdk",
"futures_provider": "tqsdk",
"stock_provider": "insight"
},
"tqsdk": {
"username": "填写天勤账号",
"password": "填写天勤密码"
}
}
使用策略和 CTP 交易时,需在同一文件中补齐 trader、ctp 配置;完整模板包含各行情源和交易通道的配置字段。
需要创建空白配置文件时,可在 Python 中读取包内模板,已有文件不会被覆盖:
from importlib.resources import files
from pathlib import Path
content = files("qstrade").joinpath("config.example.json").read_text(encoding="utf-8")
with Path("config.local.json").open("x", encoding="utf-8") as target:
target.write(content)
配置字段及生命周期详见 配置说明。
Wind 统一证券代码
策略、行情订阅、历史查询、交易报单和合约查询均使用同一套 Wind 格式,回调与查询结果中的 symbol 也遵循该格式。调用方无需自行转换供应商代码,转换集中在 qstrade.utils.code_utils,由行情和交易适配器内部调用。
股票
格式为 六位数字 + 点 + 两位大写交易所代码,代码中的前导零必须保留。
| 交易所 | 后缀 | 示例 |
|---|---|---|
| 上海证券交易所 | SH |
600397.SH |
| 深圳证券交易所 | SZ |
300750.SZ、000001.SZ |
| 北京证券交易所 | BJ |
920590.BJ |
期货
格式为 大写品种代码 + 四位年月 YYMM + 点 + 三位大写交易所代码。月份取 01 至 12;郑商所公共代码同样使用四位年月。月均等合约的特殊大写字母保留在年月之后,例如 PP2701F.DCE。
| 交易所 | 后缀 | 示例 |
|---|---|---|
| 上海期货交易所 | SHF |
SP2704.SHF |
| 上海国际能源交易中心 | INE |
SC2709.INE |
| 广州期货交易所 | GFE |
SI2703.GFE |
| 中国金融期货交易所 | CFE |
IH2612.CFE |
| 大连商品交易所 | DCE |
JM2706.DCE、PP2701F.DCE |
| 郑州商品交易所 | CZC |
SA2708.CZC |
例如 SA2708.CZC 表示纯碱 2027 年 8 月合约;调用方始终使用这个代码,CTP 所需的 SA708、天勤所需的 CZCE.SA708 由适配器转换。
以下是连接已建立后的调用方式,报单示例仅定义函数,不会自动执行:
# market 已登录,trader 已连接。
market.subscribe_tick(["SA2708.CZC"])
market.subscribe_kline(["SA2708.CZC"], "1min")
bars = market.get_kline(
["SA2708.CZC"], "2026-10-21", "2026-10-24", frequency="1min"
)
contracts = trader.query_instruments("SA2708.CZC")
def buy_one_lot(trader, limit_price):
return trader.place_order("SA2708.CZC", "BUY", "OPEN", limit_price, 1)
公共接口不接受 rb2701、SA708 或 DCE.c2701 等供应商写法。格式正确不代表合约已挂牌或账户具备权限,示例代码需替换为实际可用合约。更多转换规则见 Wind 代码说明。
行情模块
Insight 和天勤的 Tick 订阅、K 线订阅及历史 K 线查询使用统一结构。TickData、BarData、QuoteLevel 定义在 qstrade.market.data,使用 TypedDict 描述,运行时均为普通 Python 字典。
订阅与历史查询
订阅时通过 callback 传入处理函数,省略或传入 None 时默认打印数据。get_kline() 返回 list[BarData],与 K 线回调中的单条记录结构一致。
import time
from datetime import datetime, timedelta
from qstrade.market import create_market
from qstrade.market.data import TickData, BarData
def on_tick(tick: TickData):
print("Tick:", tick["symbol"], tick["last_price"], tick["volume"])
def on_bar(bar: BarData):
print("Bar:", bar["symbol"], bar["timestamp"], bar["close"], bar["is_closed"])
market = create_market(provider="tqsdk").login()
try:
symbol = "C2701.DCE"
market.subscribe_tick([symbol], callback=on_tick)
market.subscribe_kline([symbol], "1min", callback=on_bar)
# 查询参数采用上海本地、无时区的 datetime。
end = datetime(2026, 10, 23, 15, 0)
bars = market.get_kline(
[symbol], end - timedelta(days=1), end, frequency="1min", fq="none"
)
for bar in bars[-3:]:
on_bar(bar)
time.sleep(30) # 保持进程存活,接收订阅回调
finally:
market.logout()
查询日期及合约按实际需求调整;历史范围受行情源和账户权限限制。回调应快速返回,不要在 SDK 回调内同步查询、订阅、登录或释放会话。各行情源支持的频率不同,类型中存在某个频率不代表所有行情源都支持。
共用字段与数据约定
Tick 和 Bar 都包含以下身份字段。下表及后续表格中的 ? 表示字段值可为 None,所有字段键仍会出现。缺失值不填成零,真实零成交量、零盘口量保留为 0。
| 字段 | 类型 | 含义 |
|---|---|---|
symbol |
str |
Wind 证券代码 |
exchange |
str |
Wind 交易所后缀,与 symbol 一致 |
asset_type |
str |
stock(股票)或 futures(期货) |
source |
str |
数据来源,如 insight、tqsdk、replay |
trading_day |
str? |
交易所交易日,格式 YYYY-MM-DD;未知时为空 |
时间统一为上海本地字符串 YYYY-MM-DD HH:MM:SS[.小数秒],不带时区后缀,保留来源精度。夜盘事件日期可能与交易日不同,不应从 timestamp 的日期直接推算 trading_day。
成交量单位为股票“股”、期货“手”,成交额单位为人民币元。期货价格保留合约原生报价单位,合约乘数不会乘入价格。
TickData:行情快照
Tick 是某一时点的行情快照,盘口更新也可能触发回调,不等同于一笔成交。除共用字段外,包含以下字段:
| 字段 | 类型 | 含义 |
|---|---|---|
timestamp |
str |
交易所事件时间,不用本地接收时间替代 |
last_price |
float? |
最新成交价 |
open / high / low |
float? |
本交易日开盘价、截至当前最高价和最低价 |
close |
float? |
本交易日官方收盘价,尚未提供时为空 |
pre_close |
float? |
前一交易日可比收盘参考价 |
volume |
int? |
当日累计成交量 |
amount |
float? |
当日累计成交额 |
quotes |
list[QuoteLevel]? |
按买卖一档起依次排列的盘口列表 |
upper_limit / lower_limit |
float? |
本交易日涨停价、跌停价 |
open_interest |
int? |
当前期货持仓量,单位手 |
pre_open_interest |
int? |
上一交易日期货持仓量,单位手 |
settlement |
float? |
本交易日期货结算价,发布后才有值 |
pre_settlement |
float? |
上一交易日期货结算价 |
股票的持仓量和结算价相关字段为 None。Tick 仅保留 volume、amount 表示当日累计量额,不提供 cum_*、delta_* 字段;最新价字段为 last_price,没有 price 别名。
QuoteLevel 的每个档位包含:
| 字段 | 类型 | 含义 |
|---|---|---|
bid_price |
float? |
买价 |
bid_volume |
int? |
该买价上的委托量 |
ask_price |
float? |
卖价 |
ask_volume |
int? |
该卖价上的委托量 |
盘口量单位同成交量,索引 0 是买卖一档。档位按实际来源保留,不用零补齐至固定深度;quotes=None 表示未提供盘口,中间缺失档位不会导致后续档位前移。
以下为字段完整的虚构期货 Tick 示例:
from qstrade.market.data import TickData
tick: TickData = {
"symbol": "C2701.DCE",
"exchange": "DCE",
"asset_type": "futures",
"source": "tqsdk",
"trading_day": None,
"timestamp": "2026-10-23 09:30:00.123456",
"last_price": 2300.0,
"open": 2290.0,
"high": 2305.0,
"low": 2288.0,
"close": None,
"pre_close": 2295.0,
"volume": 12000,
"amount": None,
"quotes": [
{"bid_price": 2299.0, "bid_volume": 12, "ask_price": 2300.0, "ask_volume": 8}
],
"upper_limit": 2400.0,
"lower_limit": 2200.0,
"open_interest": 300000,
"pre_open_interest": 298000,
"settlement": None,
"pre_settlement": 2294.0,
}
BarData:K 线
实时和历史 K 线采用相同结构。除共用字段外,包含以下字段:
| 字段 | 类型 | 含义 |
|---|---|---|
frequency |
str |
周期,如 15s、1min、daily、weekly、monthly |
timestamp |
str? |
区间起点;无法确认起点时为空 |
open / high / low / close |
float? |
区间开盘、最高、最低、收盘价;形成中 K 线的 close 为区间内最新价 |
volume |
int? |
该区间成交量,不是当日累计量 |
amount |
float? |
该区间成交额 |
open_interest |
int? |
区间终点期货持仓量;形成中 K 线为当前值 |
open_interest_start |
int? |
区间起点期货持仓量 |
pre_close |
float? |
仅日线:前一交易日可比收盘参考价 |
settlement / pre_settlement |
float? |
仅期货日线:当日和前一交易日结算价 |
fq |
str |
none 不复权、pre 前复权、post 后复权;期货固定为 none |
is_closed |
bool? |
True 已确认完成、False 正在形成、None 无法确认 |
Bar 的时间字段只有 timestamp,不另存 start_time、end_time;价格使用 OHLC,不提供 price=close 别名。股票的持仓量和结算字段为空,分钟、周、月线的日线专用参考字段为空。
以下为字段完整的虚构分钟 K 线示例:
from qstrade.market.data import BarData
bar: BarData = {
"symbol": "C2701.DCE",
"exchange": "DCE",
"asset_type": "futures",
"source": "tqsdk",
"trading_day": None,
"frequency": "1min",
"timestamp": "2026-10-23 09:30:00.000000000",
"open": 2300.0,
"high": 2302.0,
"low": 2298.0,
"close": 2301.0,
"volume": 120,
"amount": None,
"open_interest": 300010,
"open_interest_start": 300000,
"pre_close": None,
"settlement": None,
"pre_settlement": None,
"fq": "none",
"is_closed": True,
}
目前天勤未提供的 K 线成交额及交易日保持 None。Insight K 线原生时间的起止语义尚未确认,默认 timestamp=None,均线策略会跳过;独立确认所用接口时间标签确为周期起点后,才可配置 insight.bar_time_label="start"。详细口径见 行情数据结构。
交易模块
create_trader() 创建未连接的交易实例,由调用方执行 connect() 和 disconnect()。以下示例仅查询账户和持仓:
from qstrade.trader import create_trader
trader = create_trader()
try:
trader.connect()
account = trader.query_account()
positions = trader.query_positions()
print(account)
print(positions)
finally:
trader.disconnect()
报单使用 place_order(symbol, direction, offset, price, volume),撤单使用 cancel_order(order_id)。返回订单 ID 只表示本地已提交请求,受理和成交状态以委托、成交回报为准;查询与回报中的证券代码仍为 Wind 格式。
策略示例
StrategyEngine 负责行情和交易通道的生命周期,策略实现 on_tick() / on_kline() 等回调。下例使用默认配置启动 MA5/MA10 观察模式,不提交策略订单:
import time
from qstrade.strategy import StrategyEngine
from qstrade.strategy.examples import MaCrossStrategy
engine = StrategyEngine(trading_enabled=False)
strategy = MaCrossStrategy(
"ma5_ma10", engine, "C2701.DCE", volume=1, frequency="1min", warmup_days=0
)
engine.add_strategy(strategy)
try:
engine.start()
time.sleep(30)
finally:
engine.stop()
显式设置 trading_enabled=True 后允许策略报撤单,库中该参数默认值也是 True,调用方应明确指定。MA 示例在 MA5 高于 MA10 且空仓时开多,低于时平多;不开空、不加仓,不要求前后两组均线发生交叉。持仓按实际成交维护,停止时尝试撤销本次引擎的在途订单,不主动平仓。完整生命周期和执行规则见 策略说明。
无需账户的离线回放也可以直接通过 Python 调用:
from qstrade import __version__
from qstrade.strategy.examples.replay import run_replay
print(__version__)
print(run_replay())
# {'orders': 2, 'trades': 2, 'position': 0}
回放仅验证事件与成交处理流程,不模拟真实交易成本或收益。
项目结构
| 路径 | 内容 |
|---|---|
src/qstrade/market/ |
行情接口、统一数据结构及供应商适配 |
src/qstrade/trader/ |
交易接口及 CTP 适配 |
src/qstrade/strategy/ |
策略基类、引擎、均线与回放示例 |
src/qstrade/utils/ |
代码转换、时间、回调及异常工具 |
src/qstrade/config.py |
配置模型与加载函数 |
examples/ |
Python 调用示例 |
tests/ |
离线回归测试 |
docs/ |
接口与策略详细说明 |
从旧项目迁移时,将 from strategy_system... 改为 from qstrade...,行情统一使用 create_market().login() 初始化。
Metadata
Release files for qstrade 0.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| qstrade-0.0.2.tar.gz | 118.6 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| qstrade-0.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 188.1 kB
Release files / qstrade-0.0.2.tar.gz
| Download URL | qstrade-0.0.2.tar.gz |
|---|---|
| Size | 118.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a219ae366bb4ae3953db92e07f25ae1401ac7e34f915a6922c600a01edf536e1
|
|
BLAKE2b-256 checksum How to use checksums |
f58a06f75311fba04c403ca79a679eec30c21670448627e87c43f43f1311ef39
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.13
|
Release files / qstrade-0.0.2-py3-none-any.whl
| Download URL | qstrade-0.0.2-py3-none-any.whl |
|---|---|
| Size | 69.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6b4f714e5ce18e5f354b2bd09845643facc2724cc5b3e34a09a821ec7465d84b
|
|
BLAKE2b-256 checksum How to use checksums |
2a4c69d178839ae0c2e8b71c3bbc4c4ade721b0cf3556a6938f08fc58396c6ca
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.13
|
Security for qstrade 0.0.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.