ths 授权版测试说明
这份说明写给拿到压缩包的测试者,按顺序做即可。
- 文中账号、金额、编号都是编造的示例:同花顺账号
13800000000,资金账号00001234567890。JSON 键名和结构与程序一致,//后是说明,实际输出里没有。 - 命令输出含你的账号、资金和持仓,不要整段贴到公开场合。
交易警告:
ths daemon buy / sell / cancel不加--live只做本地演练;加了--live就会真实下单或撤单,不再二次确认。 带--live的交易示例都单独标了 ⚠️。
1. 快速开始
开始前:请先在同花顺 App 里把要测试的券商账户绑定到你的同花顺账号上。
1.1 安装
curl -fsSL https://ast.anepgrto.online/install.sh | shWindows 版正在开发中。
ths login 输入 Key 并登录。手动安装下载指定平台的压缩包(内含 ths、Python 包和说明),解压后把 ths 放入 PATH。
1.2 启动
运行 ths login,后台进程会自动启动,不需要先 ths daemon start。下面的 Key、短信、交易密码都在这一条命令里依次出现。
ths login
1.3 输入 Key
推荐用短信登录:选 2,输入手机号;提示未授权时粘贴授权 Key。同一个账号请始终用同一种方式登录。
请选择 [1]:2
手机号(可见输入):13800000000
未授权(138****0000)
输入授权 Key 继续,直接回车退出:XXXX-XXXX-XXXX
1.4 登录
输入 y 发一次短信验证码,输入验证码,再输入券商交易密码。看到"已认证"即完成。
向 138****0000 发送一次短信验证码?输入 y 确认,回车取消 [y/N]:y
短信验证码(隐藏输入):
交易密码(隐藏输入;直接回车跳过该券商):
券商 broker_id=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:已认证,连接驻留daemon
1.5 查询
查持仓(资金账号换成你自己的)。把 positions 换成 balance 查资金、换成 orders 查委托,详见第 2 章。
ths daemon query --broker-account 00001234567890 --kind positions --live
--live 表示向券商实时查询;换成 --cache 则只读上次查询的缓存,不联网。
停止后台用 ths daemon stop。
2. 查询与交易(重点)
| 操作 | 命令 | 是否发往券商 | 是否需要 --live |
|---|---|---|---|
| 查资金 | ths daemon query --kind balance |
--live 发;--cache 不发 |
二选一:--live 实时,--cache 读缓存 |
| 查持仓 | ths daemon query --kind positions |
同上 | 同上 |
| 查委托 | ths daemon query --kind orders |
同上 | 同上 |
| 买入 | ths daemon buy |
只有带 --live 才发 |
不带=演练;带=真实下单 |
| 卖出 | ths daemon sell |
只有带 --live 才发 |
不带=演练;带=真实下单 |
| 撤单 | ths daemon cancel |
只有带 --live 才发 |
不带=演练;带=真实撤单 |
- 先
ths login。查询和交易不会自动重新登录。 - 用
--broker-account 完整资金账号选券商,原样输入、保留前导零;也可用ths daemon status里的--broker-id。下文命令先设好变量:BROKER_ACCOUNT=00001234567890(换成你的)。 - 默认输出 JSON(stdout),提示在 stderr。保存结果:
... > result.json。 - 外层固定 5 个字段:
{
"version": 2, // 本机通信协议版本
"id": 47, // 本次请求的随机编号,排查时原样提供
"ok": true, // 只表示本机请求处理成功,不表示成交;交易要看 data.status
"data": {}, // 结果;失败时为 null
"error": null // 失败时为 {"code": ..., "message": ...}
}
server_message是券商/服务端返回的原文,原样保留;error_codes/server_codes是原文里的数字码,保留原顺序和重复。error.code、error.message是程序自己的分类和说明。
2.1 资金
查可用资金、总资产、股票市值。
ths daemon query --broker-account "$BROKER_ACCOUNT" --kind balance --live
{
"version": 2,
"id": 47,
"ok": true, // false 是失败,不等于余额为零
"data": {
"account_id": "11111111111111111111111111111111",
"broker_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"kind": "balance",
"result": {
"available_funds": 1092.18, // 可用资金
"total_assets": "2092.1800", // 总资产,保留券商原文小数位;没返回时为 null
"market_value": "1000.00" // 股票总市值;没返回时为 null
// 两处可用资金不一致时另有 "warning": "资金返回值存在差异,请核对券商客户端。"
}
},
"error": null
}
目前没有"今日盈亏"字段。
2.2 持仓
查每只股票的数量、成本、市值、盈亏。
ths daemon query --broker-account "$BROKER_ACCOUNT" --kind positions --live
{
"version": 2,
"id": 48,
"ok": true,
"data": {
"account_id": "11111111111111111111111111111111",
"broker_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"kind": "positions",
"result": {
"row_count": 1, // 持仓条数;0 加空数组=确实没有持仓
"positions": [
{
"code": "000001", // 证券代码,保留前导零
"name": "合成股票",
"quantity": 1000, // 持仓数量,按券商原样,不换算成"手"
"available": 800, // 可卖数量
"cost_price": "10.1200", // 成本价,字符串,保留券商原文小数位
"current_price": 10.4, // 现价
"market_value": 10400.125, // 市值
"profit_loss": -12.375, // 浮动盈亏
"profit_loss_percent": "-0.1188" // 盈亏比例,已是百分数(-0.1188 表示 -0.1188%),不要再乘 100
}
]
}
},
"error": null
}
缺的字段为 null,不会补 0。
2.3 委托
查当日委托:状态、成交数量、委托编号(撤单要用)。
ths daemon query --broker-account "$BROKER_ACCOUNT" --kind orders --live
{
"version": 2,
"id": 49,
"ok": true,
"data": {
"account_id": "11111111111111111111111111111111",
"broker_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"kind": "orders",
"result": {
"row_count": 1,
"orders": [
{
"code": "000001",
"name": "合成股票",
"order_id": "12345", // 委托编号,撤单时用
"order_date": "20261019",
"side": "买入", // 券商原文
"price": "10.1200", // 委托价
"quantity": 100, // 委托数量
"filled_quantity": 0, // 已成交数量
"average_fill_price": 0.0, // 成交价
"status": "已报", // 委托状态,券商原文
"reason": null, // 券商说明原文(如废单原因)
"cancelled_quantity": 0, // 已撤数量
"market": "1"
}
]
},
// 同一批委托按状态分组的副本,不要和 orders 重复累加,统计以 orders 为准。
// 分组:unfilled 未成交;partially_filled 部分成交;cancellation_pending 撤单中;cancelled 已撤;
// partially_cancelled 部撤;partially_filled_then_cancelled 部成后撤;filled 已成交;waste 废单;rejected 已拒绝;unknown 未识别
"groups": {
"unfilled": [
{ "code": "000001", "name": "合成股票", "order_id": "12345", "order_date": "20261019", "side": "买入",
"price": "10.1200", "quantity": 100, "filled_quantity": 0, "average_fill_price": 0.0,
"status": "已报", "reason": null, "cancelled_quantity": 0, "market": "1" }
]
}
},
"error": null
}
2.4 缓存与查询失败
--cache 读上次查询留在后台的结果,不联网:
ths daemon query --broker-account "$BROKER_ACCOUNT" --kind orders --cache
结果同 --live,data 里多出:
{
"source": "cache", // 缓存,不是实时数据
"upstream_requests": 0, // 没有联网
"cache": {
"source": "explicit_query", // explicit_query 手动查过;post_acceptance_refresh 下单受理后自动刷新
"cached_at": "2026-10-19T02:00:00Z", // 缓存时间(UTC)
"age_ms": 1250.0, // 距今毫秒数;可能已过时
"connection_ready": true
}
}
查询失败(测试中实际取得):
{
"version": 2,
"id": 3089527198357359987,
"ok": false, // 失败;不等于余额为零或没有持仓
"data": null,
"error": {
"code": "broker_account_not_found",
"message": "Broker account is not in the selected current daemon roster"
}
}
error.code |
意思 |
|---|---|
broker_account_not_found |
后台没有这个资金账号:检查输入,或先 ths login |
ambiguous_broker_account |
同一资金账号对应多家,加 --broker-id |
broker_not_ready |
券商未就绪或已断开,看 ths daemon status,必要时重新 ths login |
broker_busy |
这家券商正在处理别的请求,稍后再试 |
cache_missing |
--cache 但没有缓存(不会自动改成联网) |
server_rejected |
券商拒绝,原文在 error.server_message |
2.5 买入
不带 --live:本地演练(不下单)
ths daemon buy --broker-account "$BROKER_ACCOUNT" --code 000001 --price 10.12 --qty 100
参数:--code 代码;--price 价格;--qty 数量(整数);--market shenzhen|shanghai|beijing 一般不填,按代码识别。
{
"mode": "dry_run", // 演练
"transaction_sent": false, // 没有发送交易
"upstream_requests": 0, // 没有联网
"validation": "local_only",
"account_id": "11111111111111111111111111111111",
"broker_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"broker_account": "00001234567890",
"preview": { // 程序理解的下单内容,请核对
"operation": "buy",
"exchange": "shenzhen", // 按代码识别:shenzhen/shanghai/beijing
"market": "1",
"code": "000001",
"price": "10.12",
"quantity": 100
}
}
演练成功不代表资金够或券商会接受。
⚠️ 带 --live:真实买入
⚠️ 会真实下单,没有第二次确认。确认账户、代码、价格、数量无误并决定交易时才执行。
# ⚠️ 真实买入
ths daemon buy --broker-account "$BROKER_ACCOUNT" --code "$CODE" --price "$PRICE" --qty "$QTY" --live
{
"version": 2,
"id": 63,
"ok": true, // 被拒时也是 true,一定看 data.status
"data": {
"account_id": "11111111111111111111111111111111",
"broker_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"preview": { "operation": "buy", "exchange": "shenzhen", "market": "1", "code": "000001", "price": "10.12", "quantity": 100 },
"status": "accepted", // 交易结果:accepted/rejected/confirmation_required/unknown,含义见 2.6 的表
"order_id": "12345", // 委托编号;可能为 null,不代表失败
"order_cache_refresh": {"status": "updated"}, // 受理后自动查一次委托;order_not_found/failed 不影响已受理
"message": "请求已受理,不代表成交或最终委托有效;请查询委托状态。",
"notice_confirmation_attempted": false, // true=券商弹出风险告知、按授权自动确认过一次,原文在 risk_notice
"automatic_retries": 0 // 始终 0:从不自动重发
// 被拒时另有 server_message(券商原文)和 error_codes(数字码),示例见 2.6
},
"error": null
}
受理不等于成交。
accepted只表示券商收下了委托,用ths daemon query --kind orders --live看后续状态。
order_id从哪里来:受理时券商回给的编号在data.order_id。它是null时,去委托列表(2.3)按代码、价格、数量、时间核对,不要重下。
2.6 卖出
用法同买入,把 buy 换成 sell。
ths daemon sell --broker-account "$BROKER_ACCOUNT" --code 000001 --price 10.12 --qty 100 # 演练
⚠️ 会真实卖出,没有第二次确认。
# ⚠️ 真实卖出
ths daemon sell --broker-account "$BROKER_ACCOUNT" --code "$CODE" --price "$PRICE" --qty "$QTY" --live
被拒时的返回:
{
"version": 2,
"id": 64,
"ok": true, // 被拒也是 true
"data": {
"account_id": "11111111111111111111111111111111",
"broker_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"preview": { "operation": "sell", "exchange": "shenzhen", "market": "1", "code": "000001", "price": "10.12", "quantity": 100 },
"status": "rejected", // 券商拒绝;不会自动重发
"order_id": null,
"server_message": "(券商返回的原文)", // 不翻译不删减
"error_codes": [-150906135], // 原文里的数字码,原顺序
"notice_confirmation_attempted": false,
"automatic_retries": 0
},
"error": null
}
data.status |
意思 | 退出码 | 怎么办 |
|---|---|---|---|
accepted |
已受理 | 0 | 受理不等于成交,查委托 |
rejected |
被拒 | 4 | 看 server_message |
confirmation_required |
券商要求额外确认 | 3 | 程序已停止,原文在 server_message |
unknown |
已发出但结果不明 | 6 | 先查委托,不要重下 |
结果未知不要重试。 看到
unknown、超时或连接中断时,委托可能已到券商。先ths daemon query --kind orders --live核对,确认没有这笔再决定。程序自己永远不会重发。
准备阶段就失败(如券商没就绪)时,输出外层 ok:false 的失败 JSON,不会下单。
2.7 撤单
撤单要先查委托。 用
ths daemon query --kind orders --live找到order_id(本次连接刚下的单可直接撤)。后台没"见过"的委托返回invalid_input,不会撤单。
ths daemon cancel --broker-account "$BROKER_ACCOUNT" --order-id 12345 # 演练
{
"mode": "dry_run",
"transaction_sent": false, // 没有发送撤单
"upstream_requests": 0,
"validation": "local_only", // 只检查编号格式(1–15 位数字),不查委托是否存在
"account_id": "11111111111111111111111111111111",
"broker_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"broker_account": "00001234567890",
"preview": { "operation": "cancel", "order_id": "12345" }
}
⚠️ 会真实撤单,没有第二次确认。
ths daemon query --broker-account "$BROKER_ACCOUNT" --kind orders --live # 先查委托
# ⚠️ 真实撤单
ths daemon cancel --broker-account "$BROKER_ACCOUNT" --order-id "$ORDER_ID" --live
{
"version": 2,
"id": 65,
"ok": true,
"data": {
"account_id": "11111111111111111111111111111111",
"broker_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"preview": {
"operation": "cancel", "exchange": null, "market": "1", "code": "000001",
"price": "10.1200", "quantity": 100,
"order_id": "12345", // 要撤的委托
"order_date": "20261019" // 委托日期,来自之前查到的委托
},
"status": "accepted", // 撤单请求已受理,不代表已撤成;取值和退出码同 2.6
"order_id": "12345", // 回显原委托编号
"message": "请求已受理,不代表成交或最终委托有效;请查询委托状态。",
"notice_confirmation_attempted": false,
"automatic_retries": 0
// 被拒时另有 server_message(券商原文,如"不在允许撤单时间")和 error_codes
},
"error": null
}
撤单受理不等于撤成。 再查一次委托确认。能否撤由券商决定。
3. 授权说明
- 按账号授权:授权按你登录时输入的同花顺账号(短信登录即手机号)计算,每个账号各自需要授权。登录时提示输入授权 Key 即可兑换,Key 一次性使用。
- 未授权/到期:登录会显示
未授权(138****0000)或授权已到期(…)并提示输入 Key;直接回车则退出。授权已停用请联系管理员。 - 券商需后台启用:未启用的券商不会登录,并提示
合成证券 0000****890:券商账户未启用,未登录(请联系管理员)。全部未启用时提示券商账户均未启用,未登录券商(请联系管理员)。 - 断网:连不上授权服务器时,最近一次联网登录后 24 小时内(且授权未到期)仍可登录,之后需要联网。
- 操作记录上传:
--live查询和带--live的买卖撤单结果摘要(含持仓明细、券商原文)会上传到授权服务器;--cache和演练不上传,上传失败不影响操作,离线时先加密排队稍后补发。升级到此版本后需要重新ths login一次才开始上传。上传状态见ths daemon status的upload(4.3)。 - 常见授权错误:
授权 Key 兑换失败:…(key_invalid)Key 输错;(key_used)Key 已被用过;无法连接授权服务器…检查网络。
查看授权:
ths license status # 只看本机;加 --live 向服务器刷新
{
"device": {"enrolled": true, "device_id": "dev_0123abcd"},
"accounts": [
{
"login_name": "138****0000", // 已打码
"status": "active", // active 有效;unlicensed 未授权;expired 到期;disabled 停用
"expires_at": "2026-11-17T03:40:55Z", // 授权到期(UTC)
"ticket": {"exp": "2026-10-18T03:40:56Z", "offline_release": true} // 可离线登录的截止时间;现在能否离线登录
}
],
"source": "local" // local 本机;license_server 已联网刷新
}
单独兑换 Key(先输入账号,再输入 Key):
ths license redeem
{
"redeemed": true,
"login_name": "138****0000",
"account": {"status": "active", "expires_at": "2026-11-17T03:40:56Z"}, // 兑换后的状态和到期时间
"redemption": {"duration": "1m", "before_expires_at": null, "after_expires_at": "2026-11-17T03:40:56Z"} // 本次加的时长(1m=1 个月)
}
4. 命令参考
4.1 ths login:登录
ths login # 菜单
ths login sms # 短信登录(推荐)
ths login pwd # 密码登录
ths login sms --new-pwd # 券商交易密码改了,逐家重新输入
- 同花顺登录仍有效时不再要密码或短信。
- 密码登录若服务端要求安全校验,会提示发一次短信,输入
y才发。 - 没有
--password参数,密码和验证码只在提示时输入。 - 再登录另一个账号直接再
ths login,不要先 stop。
返回示例(结构来自测试输出,券商部分按代码补全):
{
"account_id": "11111111111111111111111111111111", // 后台会话编号;daemon 重启后会变
"account": "13800000000",
"results": [ // 每家券商一条,逐家看是否成功
{
"broker_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"response": {
"version": 2,
"id": 101,
"ok": true, // 这家券商是否登录成功
"data": {
"broker_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"broker_name": "合成证券",
"branch_name": "合成营业部",
"broker_account": "00001234567890", // 完整资金账号,查询交易用它选券商
"state": "ready", // ready 可用,其他值见 4.3
"authenticated": true,
"credentials_stored": true,
"reused": false // true=连接本来就在,直接复用
},
"error": null // 失败时原因在这里,券商原文在 error.server_message
}
}
],
"automatic_retries": 0,
"reopened": false, // true=因未启用券商自动断开并重新打开过该账号
"authenticated": true, // 同花顺部分已登录;券商看 results
"method": "sms", // sms 或 pwd
"ths_session": "authenticated", // reused 复用已有登录;authenticated 本次重新登录
"license": {
"source": "server", // server 已联网核对;offline_ticket 离线放行
"status": "active",
"expires_at": "2026-11-17T03:40:55Z" // 授权到期(UTC)
}
}
未启用的券商在 results 里是 "status": "broker_disabled",response.error.message 为 券商账户未启用,未登录(请联系管理员)。
登录失败时 stderr 显示原因;Python 或 --json 模式下最后一行是失败对象(测试中实际取得):
{
"authenticated": false,
"stage": "license", // 停在哪一步:license 授权、sms 短信、password-auth 密码、broker 券商等
"code": "license_key_rejected", // 失败代码
"message": "授权 Key 兑换失败:授权 Key 无效(key_invalid)",
"server_message": "授权 Key 无效", // 服务端原文;本机或网络问题时为 null
"error_codes": [] // 服务端数字码,原顺序
}
4.2 ths daemon start:手动启动后台
一般不用:ths login 和带 --live 的查询、交易会自动启动后台。手动启动时 stderr 显示进度,最多等 120 秒恢复上次的登录。
ths daemon start
{
"started": true, // false=已在运行(already_running 为 true)
"already_running": false,
"status": { }, // 与 daemon status 的 data 相同,见 4.3
"synchronization": "empty" // ready 全部恢复;partial 部分;failed 失败;empty 无可恢复;pending 超时仍在后台恢复
}
4.3 ths daemon status:后台状态
只读本机状态,不联网,不自动启动后台。
ths daemon status
{
"version": 2,
"id": 50,
"ok": true,
"data": {
"account_count": 1, // 后台持有的账号数;0 可能是还没登录或还在恢复
"accounts": [
{
"account_id": "11111111111111111111111111111111", // 可用于 daemon close --account-id
"account": "13800000000",
"ths_state": "ready",
"brokers": [
{
"broker_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 本次会话的券商编号;重启后会变
"broker_name": "合成证券",
"broker_account": "00001234567890",
// ready 可用;credentials_required 缺交易密码;failed_no_retry 上次失败不自动重试;disconnected 已断开
"state": "ready",
"authenticated": true, // 本机最后一次观察的结果,不是实时探测
"credentials_stored": true
// 正在处理请求时另有 "busy": true
}
]
}
],
"restoration": {
"state": "complete", // 启动恢复:empty 无记录;restoring 进行中;complete 完成(仍要逐家看);failed 失败
"pending_accounts": 0,
"results": []
},
"schedule": {
"mode": "manual", // 运行模式,见 4.7
"exchange_date": "2026-10-19",
"phase": "trading", // trading 交易时段;non_trading_day 非交易日 等
"trading_day": true,
"open": "09:00",
"close": "15:05" // 收盘自动退出时间(北京时间)
},
"upload": { // 操作记录上传
"state": "active", // active 正常;stopped 已停止;unavailable 授权配置不可用
"pending": 0, // 待上传条数(每次操作算请求、结果 2 条)
"outbox_bytes": 0, // 本机排队文件大小(字节)
"dropped": 0, // 队列满被丢弃的条数
"rejected": 0, // 被服务器拒收的条数
"skipped": 0, // 未记录的条数(如升级后还没重新 ths login)
"last_success_at": "2026-10-19T02:00:05Z", // 最近一次上传成功(UTC);从未成功为 null
"last_error": null // 最近问题,正常为 null;如 server_unreachable 连不上、server_busy 繁忙、outbox_full 排队已满
},
"pid": 84976,
"protocol_version": 2
},
"error": null
}
后台没运行时没有 JSON,stderr 显示 ths: operation failed (daemon is unavailable)。
4.4 ths daemon query / buy / sell / cancel
见第 2 章。
4.5 ths daemon close:关闭一个账号
ths daemon close # 按提示输入同花顺账号
ths daemon close --account-id "$ACCOUNT_ID" # 用 daemon status 里的 account_id
只断开这个账号及其券商,其他账号不受影响;登录信息保留,但下次后台重启不再自动恢复它。
{
"version": 2,
"id": 51,
"ok": true,
"data": {
"closed": true, // false=这个账号本来就没打开
"account_id": "11111111111111111111111111111111"
},
"error": null
}
--account-id 写错时返回 "ok": false、error.code 为 account_id_not_found,退出码仍是 0,请看 JSON 的 ok 字段。
4.6 ths daemon stop:停止后台
ths daemon stop
{
"version": 2,
"id": 53,
"ok": true,
"data": {"stopping": true}, // 断开全部连接并退出;登录信息和恢复清单保留
"error": null
}
后台也会在收盘(默认 15:05)自动退出;收盘后或非交易日空闲 30 分钟也会退出。
4.7 ths daemon mode:运行模式
ths daemon mode # 查看
ths daemon mode auto # 开机自启 + 交易日开盘自动登录
ths daemon mode manual # 默认:用到时才启动,不自动登录
{
"mode": "manual", // 下次启动使用的模式
"source": "inferred", // configured 设置过;inferred 推断
"service_installed": false, // 是否装了开机自启服务
"daemon_running": true,
"running_mode": "manual", // 运行中后台的模式
"applies_after_restart": false // true=需 ths daemon stop 后再启动才生效
}
auto会把当前ths的路径写进系统服务,请先把ths放到固定位置。- 开盘自动登录只复用已保存的登录;同花顺登录过期时,需要 Python 里保存过同花顺密码才能自动续,否则会通知你手动登录。
4.8 其他命令
ths status 是旧版本地凭据入口,与 ths login 无关;看登录状态请用 ths daemon status。device、broker、service、password-store 等是维护用的,测试时不需要。
5. Python
5.1 安装
python3 -m pip install ~/.local/share/ths/python
python3 -m pip install "$HOME/.local/share/ths/python[file-store]" # 没有钥匙串的机器(如无桌面 Linux)
需要 Python 3.9+,且 ths 在 PATH 里(或 export THS_BIN=/绝对路径/ths)。新账号请先在终端 ths login 一次,否则 Python 以 device_required 停止。
5.2 登录(短信)
import getpass
from ths_client import ThsClient, CliError, DaemonError
client = ThsClient() # 或 ThsClient(executable="/绝对路径/ths")
result = client.login_with_sms(
"13800000000",
confirm_sms=lambda info: input(f"向 {info['mobile']} 发一次验证码?输入 y:") == "y",
verification_code=lambda: input("短信验证码:"),
broker_password=lambda broker: getpass.getpass(
f"{broker['broker_name']} {broker['broker_account']} 交易密码(回车跳过):") or None,
license_key=lambda info: input(f"授权状态 {info['status']},输入授权 Key(回车退出):") or None,
)
for item in result["results"]:
print(item["broker_id"], item["response"]["ok"])
| 回调 | 何时调用 | 返回 |
|---|---|---|
license_key |
未授权/到期/停用,收到 {"status", "expires_at"} |
Key;None 停止 |
confirm_sms |
需要发短信,收到 {"mobile"} |
True 才发一次 |
verification_code |
发码受理后 | 验证码 |
broker_password |
某家券商要交易密码 | 密码;None 跳过 |
没传的回调按"拒绝/跳过"处理。返回值即 4.1 的登录结果。失败抛 CliError:error.exit_code 退出码,error.data 是 4.1 的失败对象。
密码登录:client.login_with_password("账号", lambda: getpass.getpass("同花顺密码:"), ...),回调同上;服务端要求短信校验时还需传 confirm_verification 和 verification_code。
同花顺登录仍有效、只登录券商:client.login_brokers("13800000000", broker_password_callback)。
5.3 查询
broker_account = "00001234567890" # 原样,保留前导零
client.daemon_status() # 4.3 的 data;后台没运行时抛 DaemonError
client.query("balance", broker_account=broker_account) # 2.1 的 data
client.query("positions", broker_account=broker_account) # 2.2 的 data
client.query("orders", broker_account=broker_account) # 2.3 的 data
client.query("orders", broker_account=broker_account, cache=True) # 只读缓存
Python 只返回 data 部分,没有 version/id/ok/error 外层:
{
"account_id": "11111111111111111111111111111111",
"broker_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"kind": "balance",
"result": {"available_funds": 1092.18, "total_assets": "2092.1800", "market_value": "1000.00"}
}
失败抛 DaemonError:
try:
client.query("positions", broker_account=broker_account)
except DaemonError as error:
print(error.code, error.message) # 程序分类和说明,同 2.4
print(error.server_message) # 券商原文;没有时为 None
print(error.server_codes) # 数字码,原顺序
5.4 下单/撤单(两道门禁)
⚠️ 以下代码会真实下单或撤单。
先准备(不下单),再执行。执行要求 allow_transactions=True 且确认回调对完整预览返回 True。
prepared = client.daemon.prepare_trade(
side="buy", code="000001", price="10.12", quantity=100,
broker_account="00001234567890",
)
def confirm(preview):
print(preview)
return input("确认真实下单?输入 YES:") == "YES"
# ⚠️ 真实下单
result = client.daemon.execute_trade(prepared, allow_transactions=True, confirm=confirm)
print(result["status"], result["order_id"])
prepare_trade 返回:
{
"account_id": "11111111111111111111111111111111",
"broker_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"broker_name": "合成证券",
"broker_account": "00001234567890",
"preview_id": "cccccccccccccccccccccccccccccccc", // 一次性,只能执行一次;再准备会替换旧的
"preview": {"operation": "buy", "exchange": "shenzhen", "market": "1", "code": "000001", "price": "10.12", "quantity": 100},
"transaction_sent": false // 还没下单
}
execute_trade 返回 2.5 的 data,看 status;被拒不抛异常,而是 status: "rejected"。抛 DaemonError("outcome_unknown") 时先查委托,不要重下。
撤单(先查委托):
client.query("orders", broker_account="00001234567890")
prepared = client.daemon.prepare_cancel(order_id="12345", broker_account="00001234567890")
# ⚠️ 真实撤单
result = client.daemon.execute_trade(prepared, allow_transactions=True, confirm=confirm)
5.5 关闭和停止
client.daemon.close("13800000000") # {"closed": True, "account_id": "..."}
client.daemon.shutdown() # {"stopping": True}
6. 常见问题
| 现象 | 处理 |
|---|---|
第一次运行 ths daemon status 显示 daemon is unavailable |
正常,后台还没启动。ths login 后再看 |
未授权 / 授权已到期 |
登录时输入 Key,或先 ths license redeem |
授权已停用,请联系管理员 |
联系管理员 |
授权 Key 兑换结果未知 |
先 ths license status --live 看是否已生效,不要马上再兑换 |
无法连接授权服务器… |
检查网络;离线规则见第 3 章 |
授权与同花顺账户不一致… |
常见于换了登录方式(手机号↔用户名)。始终用同一种方式,或联系管理员 |
登录后看不到某个券商,或 brokers 为空 |
该券商账户没绑定在这个同花顺账号上。先在同花顺 App「交易」里用同一个同花顺账号添加该券商,再重新 ths login |
… 券商账户未启用,未登录(请联系管理员) |
联系管理员在后台启用 |
该同花顺账号有操作进行中,未断开、未登录券商 |
等查询或交易结束后重新 ths login |
| 密码登录要求安全校验短信 | 正常,输入 y,或改用 ths login sms |
sms_send_outcome_unknown |
发码可能已发出,先等短信,不要马上重来 |
device_required |
先在终端 ths login 一次 |
broker_not_ready |
看 ths daemon status,必要时重新 ths login |
restoration_in_progress |
后台在恢复上次登录,稍等 |
下单 order_id 为 null 或返回 unknown |
去委托列表核对,不要重下 |
upload.skipped 一直增加 |
升级后还没重新登录,运行一次 ths login |
| macOS 提示"无法验证开发者" | xattr -d com.apple.quarantine ~/.local/bin/ths |
| Windows 能用吗 | Windows 版正在开发中 |
停止和卸载:
ths daemon stop
ths daemon mode manual # 切过 auto 的,先卸载开机自启服务
rm ~/.local/bin/ths
python3 -m pip uninstall ths-client # 用过 Python 包时
本机数据都在 ~/.ths/(加密的登录信息、交易密码、授权票据、待上传记录),不再使用时可 rm -rf ~/.ths;授权在服务端,不会丢。Python 保存过同花顺密码的,可用 PasswordStore().delete("账号") 删除钥匙串条目。
7. 反馈问题
请提供:
- 时间(精确到分钟)、操作系统和芯片、
ths --version。 - 执行的命令(原样复制)。
- 完整输出(stdout 的 JSON 和 stderr)。发出前把资金账号、金额、持仓数量换成
***,但保留code、message、server_message、error_codes/server_codes、stage、status。 - 授权问题附
ths license status输出(账号已打码)。
不要发送:同花顺密码、交易密码、短信验证码、授权 Key,以及 ~/.ths/ 里的任何文件。