ths 授权版测试说明

版本 0.1.1 · 支持 macOS(Apple 芯片/Intel)和 Linux,Windows 版正在开发中。

ths 授权版测试说明

1. 快速开始

开始前:请先在同花顺 App 里把要测试的券商账户绑定到你的同花顺账号上。

1.1 安装

curl -fsSL https://ast.anepgrto.online/install.sh | sh
识别架构自动选择 Apple 芯片、Intel 或 Linux x86_64。
下载并校验从本站下载,SHA-256 校验不一致就停止。
配置 PATH装到 ~/.local/bin,自动写入你的 shell,不需要 sudo。
完成执行 ths login 输入 Key 并登录。
手动安装下载指定平台的压缩包(内含 ths、Python 包和说明),解压后把 ths 放入 PATH。
Mac · Apple 芯片M1/M2/M3/M4
下载 · 4.7 MBths-0.1.1-macos-arm64.tar.gz
SHA-2567b6d9c7f312c5f4cb05e74d897d7b9be38c464ce1a834173a3bee789106e9d0f
Mac · Intel 芯片2020 年及以前的大多数 Mac
下载 · 4.9 MBths-0.1.1-macos-x86_64.tar.gz
SHA-25623bdfa59fa6dd3800aa5926038d527bbe8a74aa34869212f58a340ee23d2a6b6
Linux · x86_6464 位 Linux(静态链接,无依赖)
下载 · 4.5 MBths-0.1.1-linux-x86_64.tar.gz
SHA-2562eeaeec57052f9ecd1c909c89e28d90f821955e132baae411959700f3dd00794
WindowsWindows 10/11
正在开发中

校验:SHA256SUMS,macOS 用 shasum -a 256 文件名,Linux 用 sha256sum 文件名 核对。

1.2 启动

运行 ths login,后台进程会自动启动,不需要先 ths daemon start。下面的登录、Key、短信、交易密码都在这一条命令里依次出现。

ths login

1.3 登录

推荐用短信登录:选 2,输入手机号。账号还没授权时会提示输入授权 Key(已授权则跳过)。然后输入 y 发送验证码、输入验证码,再输入券商交易密码,看到"已认证"即完成。同一个账号请始终用同一种方式登录。

请选择 [1]:2
手机号(可见输入):13800000000
未授权(138****0000)
输入授权 Key 继续,直接回车退出:XXXX-XXXX-XXXX
向 138****0000 发送一次短信验证码?输入 y 确认,回车取消 [y/N]:y
短信验证码(隐藏输入):
交易密码(隐藏输入;直接回车跳过该券商):
券商 broker_id=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:已认证,连接驻留daemon

1.4 查询

查持仓(资金账号换成你自己的)。把 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 读上次查询留在后台的结果,不联网。

每次下单后会自动更新一次缓存:买入/卖出被受理后,程序会自动查一次委托列表,更新委托缓存,之后 --kind orders --cache 就能看到这笔新委托。资金和持仓缓存不会自动更新,需要时用 --live 再查。

示例:

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 买入

⚠️ 高频交易须先咨询券商:程序化或高频下单前,请先向你的券商确认是否有频率、笔数等限制或特别政策,并遵守交易所和监管的程序化交易规则(如报备要求、异常交易认定)。

下单后自动刷新委托:买入/卖出被受理后,程序会自动查询一次委托列表,更新本地缓存(结果见 order_cache_refresh)。之后用 --cache 查委托或撤单,都能直接用到这笔新委托。资金和持仓不会自动刷新,需要时用 --live 再查一次。

不带 --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)和买入/卖出/撤单的结果会自动上传到授权服务器,带券商返回的原文,方便管理员排查问题;--cache 和本地演练不上传。上传在后台进行,失败会在本机排队稍后补传,不影响查询和交易本身。从 0.1.0 升级上来的,ths update 后先 ths daemon stop,再 ths login 一次。
  • 常见授权错误:授权 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": {                          // 操作记录上传(见第 3 章)
      "state": "active",                 // active 正常;stopped 已停止;unavailable 本机未配置
      "pending": 0,                      // 本机排队待传的条数
      "dropped": 0,                      // 队列满被丢弃的条数
      "rejected": 0,                     // 服务器拒收的条数
      "skipped": 0,                      // 跳过的条数(如券商还没在授权服务器登记,重新 ths login 一次即可)
      "last_success_at": "2026-10-19T01:30:05Z",   // 最近一次上传成功(UTC)
      "last_error": null                 // 最近一次错误原因,正常为 null
    },
    "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 update:检查更新

ths update --check   # 只检查,不安装
ths update           # 有新版就下载、校验并替换
{
  "current": "0.1.0",          // 当前版本
  "latest": "0.1.1",           // 下载站最新版本
  "update_available": true
}

升级不会重启后台:之后 ths daemon stop,再 ths login 才用上新版本。用 Python 的再运行一次 python3 -m pip install ~/.local/share/ths/python。ths login 时每天最多自动检查一次,有新版会提示一行。

4.9 其他命令

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 下单/撤单

⚠️ 以下代码会真实下单或撤单。

先准备(不下单),再执行。执行时确认回调对完整预览返回 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, 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, 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 后台在恢复上次登录,稍等
ths daemon status 里 upload.skipped 增加 券商还没在授权服务器登记,ths daemon stop 后重新 ths login 一次
upload.pending 一直不为 0 连不上授权服务器,网络恢复后会自动补传;重启后台也不会丢
下单 order_id 为 null 或返回 unknown 去委托列表核对,不要重下
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. 反馈问题

请提供:

  1. 时间(精确到分钟)、操作系统和芯片、ths --version。
  2. 执行的命令(原样复制)。
  3. 完整输出(stdout 的 JSON 和 stderr)。发出前把资金账号、金额、持仓数量换成 ***,但保留 code、message、server_message、error_codes/server_codes、stage、status。
  4. 授权问题附 ths license status 输出(账号已打码)。

不要发送:同花顺密码、交易密码、短信验证码、授权 Key,以及 ~/.ths/ 里的任何文件。

更新于 2026-10-17