请求账户摘要
reqAccountSummary() 用来向 TWS 或 IB Gateway 请求账户摘要数据。它读取的是 TWS Account Window 的 Summary 类字段,例如净清算值、购买力、可用资金等。
这个方法不是“调用后立刻返回结果”的普通函数。它会发送一个请求,然后由 accountSummary() 回调一行一行返回数据,最后用 accountSummaryEnd() 表示本轮初始数据已经发完。
reqAccountSummary(reqId, groupName, tags) -> accountSummary(reqId, account, tag, value, currency) -> accountSummaryEnd(reqId)Python API 中的调用形式如下:
self.reqAccountSummary(reqId, groupName, tags)| 参数 | 中文含义 | 常见写法 |
|---|---|---|
reqId | 请求 ID,用来把后面的回调和本次请求对应起来 | 使用一个尚未被占用的整数 |
groupName | 账户组名称 | 普通账户通常填 "All" |
tags | 需要返回的字段名,用英文逗号分隔 | "NetLiquidation,BuyingPower,AvailableFunds" |
reqId 只需要在这次连接中便于区分即可。实际项目里通常会维护一个自增请求 ID,避免两个还没结束的请求使用同一个 reqId。
groupName 怎么填
Section titled “groupName 怎么填”大多数个人账户或单账户开发环境,直接使用:
app.reqAccountSummary(reqId=9001, groupName="All", tags="NetLiquidation")"All" 表示请求登录会话中可见账户的摘要数据。
如果是 Financial Advisor 或多账户结构,并且已经在 TWS Global Configuration 里创建了 Advisor Account Group,可以把 groupName 改成对应的账户组名称。没有账户组时不要随便填写自定义字符串,否则可能收不到数据。
tags 怎么填
Section titled “tags 怎么填”tags 是一个逗号分隔字符串,不是 Python 列表。建议先从少量字段开始:
tags = "NetLiquidation,BuyingPower,AvailableFunds"常见字段含义:
| 字段 | 中文含义 | 适合用途 |
|---|---|---|
NetLiquidation | 净清算值 | 估算账户总权益 |
BuyingPower | 购买力 | 判断大致还能买入多少保证金证券 |
AvailableFunds | 可用资金 | 下单前做资金余量检查 |
TotalCashValue | 总现金价值 | 查看现金类余额 |
ExcessLiquidity | 超额流动性 | 观察保证金压力 |
Cushion | 安全垫比例 | 粗略判断账户离保证金压力有多远 |
“账户摘要标签”页面会整理更多字段。这里先记住一个原则:调试阶段不要一次请求太多字段,先把连接、回调和字段解析跑通。
官方说明里 reqAccountSummary() 的语义是 request and keep up to date,也就是请求后会保持更新。实际编程时有两种用法:
| 用法 | 做法 |
|---|---|
| 只想取一次账户摘要 | 发送请求,等到 accountSummaryEnd(),整理结果后调用 cancelAccountSummary(reqId) |
| 想持续监听摘要变化 | 发送请求后保持连接,持续处理 accountSummary(),不用立刻取消 |
新手示例一般采用第一种:拿到初始摘要后就取消订阅。这样程序生命周期清楚,也不容易把旧请求留在后台。
Python 原生示例
Section titled “Python 原生示例”下面示例直接使用官方 EWrapper + EClient 模型,不依赖同步封装。它会请求三个账户摘要字段,等待 accountSummaryEnd(),然后取消订阅。
import threadingfrom ibapi.client import EClientfrom ibapi.wrapper import EWrapper
REQ_ID = 9002TAGS = "NetLiquidation,BuyingPower,AvailableFunds"
class AccountSummaryApp(EWrapper, EClient): def __init__(self): EClient.__init__(self, self) self.ready = threading.Event() self.done = threading.Event() self.rows = [] self.errors_seen = []
def nextValidId(self, orderId): # 收到这个回调,说明 API 握手完成,可以发送请求。 self.ready.set()
def accountSummary(self, reqId, account, tag, value, currency): # account 和 value 都是敏感数据,公开日志里应当脱敏。 self.rows.append((reqId, account, tag, currency or "EMPTY"))
def accountSummaryEnd(self, reqId): # 初始账户摘要已经返回完毕。 self.done.set()
def error(self, reqId, errorTime, errorCode, errorString, advancedOrderRejectJson=""): # 2104、2106、2158 常见于行情/数据服务连接状态提示,不作为本例错误输出。 if errorCode not in (2104, 2106, 2158): self.errors_seen.append((reqId, errorCode, errorString))
app = AccountSummaryApp()
try: app.connect("127.0.0.1", 7497, clientId=961)
thread = threading.Thread(target=app.run, daemon=True) thread.start()
if not app.ready.wait(8): raise RuntimeError("等待 nextValidId 超时")
app.reqAccountSummary(REQ_ID, "All", TAGS)
if not app.done.wait(8): raise RuntimeError("等待 accountSummaryEnd 超时")
app.cancelAccountSummary(REQ_ID)
latest = {} for req_id, account, tag, currency in app.rows: latest[(account, tag, currency)] = req_id
print(f"ACCOUNT_COUNT={len({account for account, _, _ in latest.keys()})}") print(f"RAW_CALLBACK_ROWS={len(app.rows)}") print(f"UNIQUE_FIELD_ROWS={len(latest)}")
finally: if app.isConnected(): app.disconnect()这里用 latest[(account, tag, currency)] = req_id 保存最新字段,是因为 reqAccountSummary() 可能先返回初始快照,随后又推送更新。同一个账户、同一个字段收到多次回调并不一定是错误。
使用TWS 模拟账户、127.0.0.1:7497 和独立 clientId 检查时,脱敏后的输出如下:
CONNECTED=TrueREQUEST_ID=9002GROUP=AllTAGS_REQUESTED=NetLiquidation,BuyingPower,AvailableFundsACCOUNT_COUNT=1RAW_CALLBACK_ROWS=6UNIQUE_FIELD_ROWS=3TAGS_RECEIVED=AvailableFunds,BuyingPower,NetLiquidationCURRENCIES=USDACCOUNT_ALIAS=ACCOUNT_1;FIELD=AvailableFunds;CURRENCY=USD;VALUE=<redacted>ACCOUNT_ALIAS=ACCOUNT_1;FIELD=BuyingPower;CURRENCY=USD;VALUE=<redacted>ACCOUNT_ALIAS=ACCOUNT_1;FIELD=NetLiquidation;CURRENCY=USD;VALUE=<redacted>NON_INFO_ERROR_COUNT=0IS_CONNECTED_AFTER_DISCONNECT=FalseUNIQUE_FIELD_ROWS=3 说明本次请求按预期收到了 3 个去重字段。RAW_CALLBACK_ROWS=6 说明同一字段可能收到初始值和更新值各一次;程序应当按账户、字段和币种做归并,而不是简单把每一行都当成新字段。
| 现象 | 常见原因 | 处理方式 |
|---|---|---|
收不到 accountSummaryEnd() | TWS 未允许 API、端口不对、请求参数不正确 | 先用 groupName="All" 和少量官方字段测试 |
| 返回字段比预期多或重复 | 账户摘要是订阅式数据,可能有初始值和更新值 | 按 (account, tag, currency) 保存最新值 |
| 某些字段没有返回 | 字段名拼写错误,或账户不支持该字段 | 先使用常见字段检查时,再扩大标签范围 |
currency 为空 | 该字段不是金额字段,或官方回调本身没有币种 | 程序里允许空币种,显示为 EMPTY 或空字符串 |
| 日志里出现真实账户号或金额 | 直接打印了 account 或 value | 公开日志和文档中必须脱敏 |