一个实例,一条更新循环
ExApi(token, account=None) 提供同步 API。所有调用必须在创建实例的线程中执行,不能在运行中的 asyncio 事件循环里直接调用。SDK 的内部 I/O 由同步调用推进,长时间 CPU 计算或阻塞业务代码会延迟更新和故障检查。
with ExApi(token) as api:
quote = api.watch_quote(symbol)
bars = api.watch_kline(symbol, "1m")
while api.wait_update():
# 在同一次提交上检查多个观察对象。
if quote.is_changing("last_price"):
consume_quote(quote.data())
if bars.is_changing():
consume_bars(bars.data())示例中的 consume_quote、consume_bars 是你的业务函数,SDK 不提供这些方法。需要多策略时自行设计调度,不要跨线程共享同一个实例,也不要给每个句柄各写一层无限循环。
wait_update 的语义
wait_update(deadline=None) -> bool 等待并发布下一批用户可见更新;到达调用方指定的绝对期限且没有更新时返回 False。无期限时可能持续等待。SDK 错误通过异常报告,不用 False 表示致命错误。
它也负责推进连接恢复、CTP 同步及交易回报。创建观察句柄不代表已有可交易的、最新的数据。
data 返回独立快照
Quote、资金、持仓、订单、成交均通过快照读取;保存的快照不随未来wait_update()改变。- K 线返回独立 pandas DataFrame,带有
attrs["symbol"]与attrs["period"]。在快照上增加指标列不会保留到下一次data()。 - 持仓、订单、成交返回只读 Mapping,即使指定了单个合约,也不是单个对象。
- 断线时仍保留最后一次快照。数据存在不等于有效;交易先检查
trading_status(),行情先检查timestamp。
is_changing 检查最近一次提交
is_changing(key=None):不传字段检查对象变化;字符串检查单字段;字符串列表检查其中任意字段变化。K 线使用列名,例如 "timestamp"、"close"。集合句柄可按对象字段检查,成员增减也属于集合变化。
if quote.is_changing(["last_price", "volume"]):
current = quote.data()
print(current.last_price, current.volume)同一合约多次订阅行情返回同一句柄。K 线按 (symbol, period, data_length) 区分句柄;不同长度是不同窗口。仅使用观察句柄的方法,不访问 _quotes、_serials 等私有结构。
清理资源
优先使用 with ExApi(...) as api:。也可以在 try/finally 中调用 api.close()。关闭释放资源,不会撤销已提交委托。如有未结束订单,退出前依据业务规则明确处理,而不是认为关闭连接等于撤单。
创建实例后跨线程、关闭后继续使用,或在 asyncio 事件循环里调用同步 API,属于使用方式错误,可能直接抛出 RuntimeError。使用独立线程运行同步程序时,必须在那个线程里创建、使用并关闭整个实例。