使用说明
建议先点击右上角“下载 AI Agent 文档”。下载后的 Markdown 可以直接喂给 AI agent 阅读。局域网内 OMS 静态页面通常无法被外部 AI agent 访问,下载文档更稳定。
xs_opend 是先胜内部逻辑盘交易 SDK。它封装的是交易接口与逻辑盘账本,不封装富途行情策略接口。
如果策略需要行情、K 线、盘口、实时快照等能力,请继续使用富途官方 OpenD 行情 API。生产上建议:行情由富途 OpenD 提供,交易提交与逻辑账本由 xs_opend 提供,账本状态由常驻 worker 更新。
重要 1.1.1 版本开始,推荐架构是:策略下单,worker 接收富途订单推送并维护账本,OMS 只负责展示与人工操作入口。
一、业务概念
逻辑盘是在一个富途 A 股模拟盘之上拆分出来的内部账本。多个策略可以共用同一个富途模拟账户,但在先胜数据库里分别拥有独立的现金、持仓、订单、成交、盈亏与资金流水。
| 对象 | 含义 | 说明 |
|---|---|---|
| 真实 OpenD 模拟账户 | 富途侧 A 股模拟盘 | 由 OpenD host、port、可选 acc_id 表示。最终订单提交到这里。 |
| 逻辑盘 | 先胜内部子账户 | 每个逻辑盘有唯一 logic_account_id。策略侧必须绑定该 ID 才会进入逻辑账本。 |
| 逻辑订单 | 内部订单记录 | 记录 logic_order_id、futu_order_id、状态、成交数量、冻结金额或冻结股数。 |
| 逻辑持仓 | 内部持仓记录 | 按 logic_account_id + code 独立记账。不同逻辑盘买同一只股票不会混账。 |
| 账本服务 worker | 常驻同步进程 | 接收富途订单推送、刷新持仓价格、同步真实盘资产,并做低频兜底同步。 |
二、新版架构
1. worker 负责账本更新
生产推荐只让 worker 持续维护账本。worker 运行后会做几件事:
- 监听富途
TradeOrderHandlerBase订单推送。 - 把富途原始状态归一为先胜内部状态,例如
SUBMITTED、PART_FILLED、FILLED、CANCELLED、REJECTED。 - 按推送里的累计成交数量计算增量成交,更新现金、持仓、冻结和成交流水。
- 定时刷新持仓现价、昨收、个股今日涨跌。
- 定时同步真实盘资产,例如当前资产、基准资产、可分配额度。
- 低频全量同步活动订单,防止偶发漏推送。
2. 策略程序负责下单和读取本地状态
策略程序通过 xs_place_order() 提交订单,然后通过 xs_get_order_list()、xs_get_position_list()、xs_get_funds() 读取数据库中的本地账本。
策略程序不应该高频调用 xs_sync() 轮询富途订单。这个接口会触发富途 order_list_query(refresh_cache=True),容易打满富途限频,尤其是多个策略同时运行时。
三、OMS 页面使用说明
1. 线上/测试环境
- 线上环境连接生产库
xianshengweb_product,逻辑盘 ID 通常以LSIM开头。 - 测试环境连接测试库
xianshengweb_copy_dev,逻辑盘 ID 通常以TEST开头。 - 本机地址
localhost、127.0.0.1、192.168.*默认识别为测试环境。 - 线上域名
oms.*默认识别为线上环境。
2. !!!测试库与生产库完全隔离
测试库和生产库不是同一套账本。逻辑盘、订单、持仓、成交、资金流水、worker 状态、收益快照都按数据库隔离,互不共享、互不同步。
TEST-...逻辑盘只应该存在于测试库xianshengweb_copy_dev。如果用 SDK 默认配置连接生产库,会报logic account not found。LSIM-...逻辑盘只应该在生产库xianshengweb_product运行。不要把生产逻辑盘 ID 拿到测试策略里误跑。- SDK 缺省 DSN 指向生产库,所以本机测试逻辑盘时必须显式传入测试库 DSN。
- worker 也必须连接对应环境的 DSN。测试 worker 连接测试库,生产 worker 连接生产库;否则会出现 worker 在线但 OMS 看不到数据,或者更危险地写错库。
- OMS 根据 URL 自动选择环境:本机和
192.168.*是测试库,oms.*是生产库。页面隐藏环境切换按钮,避免误切库。
排查环境问题时,先确认三件事:OMS 当前 URL、Python SDK 的 dsn、worker 启动命令里的 --dsn 是否指向同一个环境。
3. OpenD 账户
OpenD 账户代表真实富途模拟盘。创建时需要维护账户名称、OpenD host、OpenD port、市场、可选 futu_acc_id。一个 worker 通常只负责同步一个真实盘及其下属逻辑盘。
4. 逻辑盘
逻辑盘是策略运行的最小账本单位。创建时填写名称、初始资金、运行时间 RUN AT、策略注释。策略注释用于复盘,不参与交易逻辑。
5. 账本服务
账本服务页面用于查看 worker 是否在线、最近心跳、订单推送时间、全量同步时间、价格刷新时间、队列深度和处理日志。测试环境下会按浏览器本地用户标识区分不同电脑上的 worker。
四、资金与账本规则
1. 真实盘额度
真实盘已分配 = sum(ACTIVE 逻辑盘 total_deposit)
真实盘可分配 = base_assets
- sum(所有逻辑盘 total_deposit)
+ sum(DELETED 逻辑盘 released_on_deleted)
逻辑盘运行期间的持仓涨跌不影响真实盘可分配额度。只有删除逻辑盘时,最终释放现金会通过 released_on_deleted 回到真实盘可分配额度。
2. 冻结规则
| 场景 | 账本动作 | 说明 |
|---|---|---|
| 买入下单 | 冻结现金 | 按限价和保护系数冻结。订单成交、撤单、拒单后释放未使用部分。 |
| 卖出下单 | 冻结股票 | 减少 available_qty,增加 frozen_qty。成交或撤单后释放剩余冻结。 |
| 全部成交 | 释放订单剩余冻结 | 买入多冻结现金回到可用现金;卖出剩余冻结股数回到可用数量。 |
| 撤单/拒单 | 释放未成交部分 | 状态归一为 CANCELLED 或 REJECTED 后释放。 |
PENDING_SUBMIT 且没有 futu_order_id 的订单,可能会造成可用股数或冻结现金异常。正常路径下 worker 和全量同步会清理终态订单剩余冻结。3. 持仓字段
| 字段 | 语义 |
|---|---|
qty | 逻辑盘持仓总数量。 |
available_qty | 可卖数量,通常等于 qty - frozen_qty。卖单未完成时会降低。 |
frozen_qty | 活动卖单冻结数量。 |
cost_price | 逻辑账本加权平均成本。 |
last_price | worker 从富途快照或持仓同步得到的当前价格。 |
pre_close | 昨收价,用于计算个股今日涨跌。 |
market_value | qty * last_price。 |
pl_ratio | 持仓盈亏,按成本价与现价计算。 |
day_change_ratio | 个股今日涨跌,按昨收与现价计算。 |
五、Python 接入准备
1. 安装 SDK
SDK 以 wheel 包发布。当前建议安装最新 xs_opend-1.1.1-py3-none-any.whl。公司共享目录示例:
python3 -m venv ~/.xs-opend-worker
~/.xs-opend-worker/bin/python -m pip install -U '/Volumes/990PRO_RAID1/pyShares/先胜api/xs_opend-1.1.1-py3-none-any.whl'
不要直接用系统 python3 -m pip install 安装到系统 Python。macOS 新版 Python 可能出现 externally-managed-environment,固定虚拟环境可以避免不同电脑上 python3 与 pip 指向不同解释器。
2. !!!运行环境
| 场景 | 推荐写法 | 说明 |
|---|---|---|
| 本机直连测试 | 不传 logic_account_id,传 host="127.0.0.1" | 直接操作当前电脑上的富途模拟盘,不写逻辑盘表。 |
| 本机测试逻辑盘 | 传测试库 dsn、测试逻辑盘 ID、host="127.0.0.1" | 用于本地策略联调,写入测试库。 |
| macmini PyCharm 测试 | 可以传逻辑盘 ID,显式传 host="127.0.0.1" | Python 进程在服务器宿主机上,连接宿主机 OpenD。 |
| 生产 Docker 策略 | 传 logic_account_id,通常不传 host | SDK 缺省 host.docker.internal,用于容器访问服务器宿主机 OpenD。 |
正式策略必须传入 logic_account_id。不传逻辑盘 ID 会进入直连模式,绕过先胜逻辑账本,直接操作富途模拟盘。
SDK 缺省 DSN 指向生产库 xianshengweb_product,缺省 host 为 host.docker.internal。测试库请显式传入测试 DSN。
3. 导入方式
from xs_opend import XsOpenDClient, XsRejectError, XsOpenDError
旧源码路径导入如 from xs_OpenD.xs_opend import ... 只适合直接运行源码目录,不适合作为生产策略的标准写法。生产以 wheel 安装后的 from xs_opend import ... 为准。
六、账本服务 worker
1. 启动命令
OMS 的“账本服务”页面可以按当前环境、用户标识、真实盘、同步间隔生成启动命令。命令本质类似:
~/.xs-opend-worker/bin/python -m xs_opend.worker \
--dsn 'postgresql://xsquant_user:***@192.168.2.1:5432/xianshengweb_product?sslmode=disable&gssencmode=disable&connect_timeout=10&options=-c%20TimeZone%3DAsia%2FShanghai' \
--env-tag production \
--worker-id p000-broker-3-worker \
--client-id p000 \
--client-label p000 \
--broker-account-id 3 \
--full-sync-interval 300 \
--price-sync-interval 10 \
--heartbeat-interval 5 \
--push-queue-maxsize 10000 \
--snapshot-batch-size 200 \
--snapshot-batch-sleep 0.35
Docker 部署时请使用 python -m xs_opend.worker。注意不要写成 python -m -m xs_opend.worker。
2. 日志事件
| 事件 | 含义 |
|---|---|
PUSH_CONNECT | 已连接 OpenD 订单推送。 |
ORDER_PUSH | 收到订单推送。日志展示富途原始状态 raw 和先胜归一状态 normalized。 |
FULL_SYNC_ACCOUNT | 单个逻辑盘兜底同步完成。 |
FULL_SYNC | 全量同步汇总。若没有活动订单,真实盘订单查询次数可以是 0,这是正常的。 |
PRICE_SYNC | 持仓快照刷新完成,更新现价、昨收、个股今日涨跌。 |
BROKER_ASSET_SYNC | 真实盘当前资产、基准资产、已分配、可分配刷新完成。 |
3. 限频与缓存
- worker 处理订单推送不调用富途查询接口,只按推送快照更新账本。
- 全量同步按真实盘合并查询。一个真实盘下多个逻辑盘,不会每个逻辑盘各查一次富途订单。
- 没有活动订单时,全量同步不会查富途订单,所以日志出现
真实盘订单查询 0 次是合理的。 - 资产、持仓、订单、成交查询在 Broker 层使用
refresh_cache=True,避免读到富途端缓存旧值。 - 快照接口
get_market_snapshot没有refresh_cache参数,worker 通过snapshot_batch_size和snapshot_batch_sleep控制批量与节奏。 - 不要让策略用
xs_sync()高频轮询订单状态,否则会和 worker 的兜底同步叠加,快速触发富途限频。
七、Client 初始化
client = XsOpenDClient(
dsn=None,
logic_account_id=None,
host="host.docker.internal",
port=11111,
futu_acc_id=None,
dry_run=False,
broker=None,
config_path=None,
auto_sync=False,
)
1. 参数说明
| 参数 | 默认值 | 说明 |
|---|---|---|
dsn | config 缺省 | 数据库连接串。测试环境必须显式传测试库 DSN,生产通常使用缺省配置。 |
logic_account_id | None | 传入后进入逻辑盘模式;不传则直连富途模拟盘。 |
host | host.docker.internal | OpenD host。生产 Docker 通常不传,本机测试传 127.0.0.1。 |
port | 11111 | OpenD port。 |
futu_acc_id | None | 富途交易账户 ID。多数 A 股模拟盘可为空。 |
dry_run | False | 仅测试账本流程,不真实调用富途。 |
auto_sync | False | 是否在每次 API 前主动调用 xs_sync()。新版架构不建议开启,常规同步交给 worker。 |
2. 运行模式
| 模式 | 触发条件 | 行为 |
|---|---|---|
| 逻辑盘模式 | 传入 logic_account_id | 写入先胜逻辑盘订单、持仓、资金表,并提交富途模拟盘订单。 |
| 直连模式 | 不传 logic_account_id | 直接调用富途模拟盘,不写先胜逻辑盘表。 |
八、Python API 详解
1. xs_place_order(...) 下单
业务含义:提交逻辑盘订单。逻辑盘模式下,SDK 会先做内部校验和冻结,再调用富途 OpenD 下限价单。
order = client.xs_place_order(
code="SZ.000001",
qty=300,
price=11.00,
trd_side="BUY",
order_type="LIMIT",
remark="strategy_alpha_buy_001",
)
| 参数 | 说明 |
|---|---|
code | 股票代码,例如 SH.600000、SZ.000001。 |
qty | 下单数量。A 股通常应为 100 股整数倍,策略侧自行保证。 |
price | 限价价格。必填。 |
trd_side | BUY 或 SELL。 |
order_type | 当前只支持 LIMIT。传 MARKET 会被拒绝。 |
remark | 策略备注。富途订单 remark 会写入 logic_order_id,便于推送回查。 |
市价效果:不要使用市价单。买入可用接近涨停价的限价单模拟,卖出可用接近跌停价的限价单模拟。这样更安全,也更容易复盘。
返回字段:logic_order_id、futu_order_id、status、code、qty、price、trd_side、frozen_cash、frozen_qty、created_at 等。
异常:现金不足、持仓不足、逻辑盘不存在、非 LIMIT 单等会抛出 XsRejectError。
2. xs_cancel_order(order_id) 撤单
逻辑盘模式下传 logic_order_id,直连模式下传富途订单号。
client.xs_cancel_order("XSO-7DF1C6ACF1664095A765")
撤单成功或远端已撤单后,订单状态归一为 CANCELLED,并释放剩余冻结现金或冻结股数。终态订单重复撤单会返回当前状态,不应造成二次释放。
3. xs_get_funds() 查询资金
funds = client.xs_get_funds()
| 字段 | 含义 |
|---|---|
cash_balance | 可用现金。 |
frozen_cash | 买单冻结现金。 |
position_value | 逻辑持仓市值。 |
total_assets | cash_balance + frozen_cash + position_value。 |
total_deposit | 累计入金。 |
realized_pnl | 已实现盈亏。 |
逻辑盘模式下该接口读取先胜数据库,不主动访问富途,除非创建 client 时设置了 auto_sync=True。
4. xs_get_position_list() 查询持仓
positions = client.xs_get_position_list()
| 字段 | 含义 |
|---|---|
code | 股票代码。 |
stock_name | 股票名称。 |
qty | 持仓总数量。 |
available_qty | 可卖数量,活动卖单会降低这个值。 |
frozen_qty | 卖单冻结数量。 |
cost_price | 加权平均成本。 |
last_price | 现价。 |
pre_close | 昨收。 |
market_value | 市值。 |
pl_ratio | 账户持仓盈亏比例。 |
day_change_ratio | 个股今日涨跌。 |
返回结果会过滤 qty=0 且 frozen_qty=0 的空持仓。
5. xs_get_order_list() 查询订单
orders = client.xs_get_order_list()
这是策略读取订单状态的首选接口。逻辑盘模式下它读取本地数据库,不访问富途。worker 会通过订单推送持续更新数据库。
| 状态 | 说明 |
|---|---|
PENDING_SUBMIT | 本地已创建,正在或等待提交富途。 |
SUBMITTED | 富途已接收,未成交或未完全成交。 |
PART_FILLED | 部分成交。 |
FILLED | 全部成交。 |
CANCELLED | 已撤单。 |
REJECTED | 下单失败、富途拒绝、异常失败。 |
6. xs_get_deal_list() 查询成交
deals = client.xs_get_deal_list()
逻辑盘模式下读取先胜数据库中的成交流水。worker 根据富途订单推送中的累计成交数量和成交均价生成增量成交,避免重复入账。
7. xs_sync() 主动同步
result = client.xs_sync()
定位:低频兜底或人工排查接口,不是策略轮询接口。
它会读取本地活动订单。只有存在活动订单时,才按真实盘调用富途 order_list_query(refresh_cache=True) 并同步状态。无活动订单时,只做本地终态订单冻结清理和空持仓清理。
xs_sync() 等订单结果。这会快速触发富途订单查询限频,严重时会让策略重复下单。8. xs_sync_position_prices_from_broker() 同步持仓现价
client.xs_sync_position_prices_from_broker()
该接口从富途持仓列表读取价格并更新逻辑持仓。新版生产架构中,常规价格刷新由 worker 的 PRICE_SYNC 完成,策略通常不需要主动调用。
九、推荐代码
1. 本机测试逻辑盘
from xs_opend import XsOpenDClient
TEST_DSN = (
"postgresql://xsquant_user:xianshengQuant2025@192.168.2.1:5432/"
"xianshengweb_copy_dev?sslmode=disable&gssencmode=disable&connect_timeout=10"
)
with XsOpenDClient(
dsn=TEST_DSN,
logic_account_id="TEST-20260609-0017",
host="127.0.0.1",
port=11111,
) as client:
print(client.xs_get_funds())
print(client.xs_get_position_list())
2. 生产策略下单
from xs_opend import XsOpenDClient, XsRejectError
LOGIC_ID = "LSIM-20260610-0018"
try:
with XsOpenDClient(logic_account_id=LOGIC_ID) as client:
order = client.xs_place_order(
code="SZ.002910",
qty=500,
price=9.20,
trd_side="BUY",
order_type="LIMIT",
remark="morning_signal",
)
print("下单成功:", order["logic_order_id"], order.get("futu_order_id"))
except XsRejectError as exc:
print("逻辑盘拒单:", exc)
3. 等待订单结果的正确方式
下单后用本地订单表判断状态,等待 worker 推送更新。不要在循环里调用 xs_sync()。
import time
from xs_opend import XsOpenDClient
TERMINAL = {"FILLED", "CANCELLED", "REJECTED"}
with XsOpenDClient(logic_account_id="LSIM-20260610-0018") as client:
order = client.xs_place_order(
code="SZ.002910",
qty=500,
price=9.20,
trd_side="BUY",
order_type="LIMIT",
remark="wait_local_order_status",
)
logic_order_id = order["logic_order_id"]
for _ in range(120):
orders = client.xs_get_order_list()
current = next((o for o in orders if o["logic_order_id"] == logic_order_id), None)
if current:
print(current["status"], current.get("dealt_qty"), current.get("dealt_avg_price"))
if current["status"] in TERMINAL:
break
time.sleep(0.5)
4. 反例:高频 xs_sync
# 不推荐:这会不断调用富途 order_list_query,容易限频
while True:
client.xs_sync()
time.sleep(0.4)
十、常见问题
1. 不传 logic_account_id 会怎样?
进入直连模式,直接操作富途模拟盘,不写先胜逻辑盘表。正式策略不要这样运行。
2. 为什么 logic account not found?
常见原因是环境库不一致。例如策略默认连接生产库,但传入的是测试库里的 TEST-... 逻辑盘。测试逻辑盘请显式传测试 DSN。
3. 为什么 worker 日志里“真实盘订单查询 0 次”?
如果当前没有活动订单,worker 全量同步不会查富途订单。这是为了减少限频消耗,是正常行为。
4. 为什么成交后可用数量比持仓数量少?
通常是存在活动卖单冻结了股票。查看订单列表中是否有 SUBMITTED 或 PART_FILLED 的卖单。若存在异常长期 PENDING_SUBMIT 且没有富途订单号,需要人工排查或修复。
5. 富途模拟盘手续费怎么处理?
富途模拟盘 API 不提供稳定的订单费用明细。逻辑盘当前主要基于成交价和成交数量维护账本;如需更真实成本,应在先胜内部增加可配置费率模型。
6. 可以用市价单吗?
不可以。xs_place_order() 当前只允许 LIMIT。需要快速成交时,请用激进限价模拟。
7. OMS 会自己更新账本吗?
新版架构中,OMS 主要展示数据库状态。账本更新主要由 worker 完成。OMS 的刷新按钮用于重新读取展示数据,不应该承担核心账本维护职责。