请求按模型获取账户更新
reqAccountUpdatesMulti() 是按账户和模型组合请求账户字段更新的入口。请求发出后,TWS 会通过 accountUpdateMulti() 返回字段行,并通过 accountUpdateMultiEnd() 表示初始批次结束。
请求方法如下:
app.reqAccountUpdatesMulti(reqId, account, modelCode, ledgerAndNLV)本页重点说明如何发起请求。字段接收和取消订阅分别对应 accountUpdateMulti()、accountUpdateMultiEnd() 和 cancelAccountUpdatesMulti()。
调用前建议先确认这些条件:
| 检查项 | 为什么重要 |
|---|---|
| 已连接 TWS 或 IB Gateway | Socket API 必须依赖已登录的客户端或网关 |
已收到 nextValidId() | 说明基础 API 握手完成 |
已通过 reqManagedAccts() 拿到账户号 | 避免手写账户号出错,也便于脱敏日志 |
reqId 没有被其他请求复用 | 对应回调和取消都要靠这个编号 |
| 明确是否有模型组合 | 没有模型组合传空字符串,有模型组合传真实 modelCode |
如果只是单账户入门测试,modelCode="" 就可以验证 multi 账户更新链路。不要随便编一个模型代码;模型代码必须来自 IBKR 账户里的模型组合配置。
| 参数 | 类型 | 中文含义 | 示例 | 说明 |
|---|---|---|---|---|
reqId | int | 请求编号 | 9101 | 程序自定义。建议每类请求使用不同编号段,方便排查。 |
account | str | 账户代码 | DU1234567 | 登录会话可见的账户号。公开输出时要脱敏。 |
modelCode | str | 模型组合代码 | "" 或模型代码 | 空字符串表示不按模型组合过滤。 |
ledgerAndNLV | bool | 是否返回账本和净值相关字段 | True | 需要 Ledger、现金和净清算值字段时设为 True。 |
ledgerAndNLV=True 通常会返回更多 $LEDGER- 字段。它适合账户资金核对、多币种账本展示、风控面板等场景;如果只需要少数摘要字段,账户摘要接口会更轻。
最小请求流程
Section titled “最小请求流程”连接 127.0.0.1:7497 -> 等待 nextValidId() -> reqManagedAccts() -> managedAccounts(accountsList) -> 选择一个可见账户 -> reqAccountUpdatesMulti(reqId, account, "", True)请求发出后不要马上断开连接。程序需要继续运行事件循环,等待 accountUpdateMulti() 和 accountUpdateMultiEnd() 回调到达。
Python 请求示例
Section titled “Python 请求示例”下面示例只突出“如何发起请求”。它会连接 TWS 模拟交易端口,自动获取可见账户,然后使用空 modelCode 发起请求。
import threadingfrom ibapi.client import EClientfrom ibapi.wrapper import EWrapper
REQ_ID = 9101INFO_CODES = {2100, 2104, 2106, 2158}
class RequestAccountUpdatesMultiApp(EWrapper, EClient): def __init__(self): EClient.__init__(self, self) self.ready = threading.Event() self.managed_ready = threading.Event() self.multi_end = threading.Event() self.managed_accounts = [] self.row_count = 0 self.end_req_id = None self.info_codes = [] self.errors_seen = []
def nextValidId(self, orderId): # 收到 nextValidId 后再发请求,避免连接刚建立就调用业务接口。 self.ready.set()
def managedAccounts(self, accountsList): # accountsList 是逗号分隔账户列表,真实账户号不要直接打印到公开日志。 self.managed_accounts = [item for item in accountsList.split(",") if item] self.managed_ready.set()
def accountUpdateMulti(self, reqId, account, modelCode, key, value, currency): # 请求页只统计是否收到数据;字段解释放在接收页面。 if reqId == REQ_ID: self.row_count += 1
def accountUpdateMultiEnd(self, reqId): self.end_req_id = reqId self.multi_end.set()
def error(self, reqId, errorTime, errorCode, errorString, advancedOrderRejectJson=""): if errorCode in INFO_CODES: self.info_codes.append(errorCode) else: self.errors_seen.append((reqId, errorCode, errorString))
app = RequestAccountUpdatesMultiApp()
try: app.connect("127.0.0.1", 7497, clientId=975)
thread = threading.Thread(target=app.run, daemon=True) thread.start()
if not app.ready.wait(8): raise RuntimeError("等待 nextValidId 超时,请检查 TWS API 设置和端口")
app.reqManagedAccts()
if not app.managed_ready.wait(8): raise RuntimeError("等待 managedAccounts 超时")
if not app.managed_accounts: raise RuntimeError("这个 TWS 会话没有返回可见账户")
account = app.managed_accounts[0] model_code = "" ledger_and_nlv = True
app.reqAccountUpdatesMulti( REQ_ID, account, model_code, ledger_and_nlv, )
if not app.multi_end.wait(8): raise RuntimeError("等待 accountUpdateMultiEnd 超时")
finally: if app.isConnected(): app.cancelAccountUpdatesMulti(REQ_ID) app.disconnect()
print(f"REQUEST_ID={REQ_ID}")print(f"ACCOUNT_ALIAS={'ACCOUNT_1' if app.managed_accounts else 'NONE'}")print("MODEL_CODE=EMPTY")print("LEDGER_AND_NLV=True")print(f"END_RECEIVED={app.multi_end.is_set()}")print(f"END_REQID_MATCHES={app.end_req_id == REQ_ID}")print(f"CALLBACK_ROW_COUNT={app.row_count}")print("INFO_CODES=" + (",".join(map(str, sorted(set(app.info_codes)))) if app.info_codes else "NONE"))print(f"NON_INFO_ERROR_COUNT={len(app.errors_seen)}")同一台机器上的 TWS 模拟账户返回结果如下,账户号已经脱敏:
REQUEST_ID=9101ACCOUNT_ALIAS=ACCOUNT_1MODEL_CODE=EMPTYLEDGER_AND_NLV=TrueEND_RECEIVED=TrueEND_REQID_MATCHES=TrueCALLBACK_ROW_COUNT=50INFO_CODES=2104,2106,2158NON_INFO_ERROR_COUNT=0这说明请求成功发出,并且收到了和 REQ_ID=9101 对应的结束回调。MODEL_CODE=EMPTY 表示这次请求没有指定模型组合。
有模型组合时怎么改
Section titled “有模型组合时怎么改”如果你的账户确实配置了模型组合,只需要把 model_code 改成真实模型代码:
model_code = "真实模型组合代码"app.reqAccountUpdatesMulti(REQ_ID, account, model_code, True)接收回调时要同时检查 reqId 和 modelCode:
def accountUpdateMulti(self, reqId, account, modelCode, key, value, currency): if reqId != REQ_ID: return if modelCode != "真实模型组合代码": return # 在这里处理这个模型组合的账户字段。如果填入的 modelCode 不属于账户,通常收不到你期望的数据。排查时先用空 modelCode 验证账户层面请求能否成功,再切换到真实模型组合代码。
请求编号怎么设计
Section titled “请求编号怎么设计”reqId 是程序管理请求状态的重要编号。建议:
- 不同接口使用不同编号段,例如账户摘要用
8000段,按模型账户更新用9100段。 - 日志里打印
reqId,但不要打印真实账户号和金额。 - 取消请求时使用同一个
reqId,不要重新生成。
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
等不到 nextValidId() | TWS API Socket 未开启、端口错误、连接被拒绝 | 检查 TWS API 设置和端口 |
等不到 managedAccounts() | 连接未完成、账户会话异常 | 先确认 TWS 已登录模拟账户 |
等不到 accountUpdateMultiEnd() | 账户号错误、模型代码错误、事件循环未运行 | 先用空 modelCode 和可见账户号测试 |
收到信息码 2104、2106、2158 | 数据服务连接状态提示 | 一般不是失败,结合实际回调判断 |