管理账户总览
管理账户接口用来获取 TWS 或 IB Gateway 会话可见的账户列表。很多账户类接口都需要账户号,例如账户摘要、账户更新、持仓和盈亏;新手最稳妥的做法是先调用 reqManagedAccts(),再把返回的账户号传给账户类接口。
reqManagedAccts() -> managedAccounts(accountsList)这个接口没有请求参数,也没有取消方法。回调返回的是逗号分隔的账户字符串。
它解决什么问题
Section titled “它解决什么问题”| 问题 | 说明 |
|---|---|
| 不知道登录会话有哪些账户 | 用 reqManagedAccts() 获取可见账户列表 |
| 不想在代码里写死账户号 | 先请求账户列表,再选择目标账户 |
| 多账户或顾问账户开发 | 用返回列表做账户选择和权限检查 |
| 公开日志需要脱敏 | 把真实账户号映射成 ACCOUNT_1 这类别名 |
虽然官方源码注释里强调 FA managed account 场景,但实际 TWS API 开发里,普通账户也常用这个回调获取可见账户号。TWS 模拟账户验证也能正常返回 1 个可见账户。
| 名称 | 类型 | 中文含义 | 说明 |
|---|---|---|---|
reqManagedAccts() | 请求方法 | 请求可见账户列表 | 没有参数 |
managedAccounts(accountsList) | 回调方法 | 接收账户列表 | accountsList 是逗号分隔字符串 |
accountsList 示例:
DU1234567,DU2345678公开文档和日志里不要直接展示真实账户号,应改成:
ACCOUNT_1,ACCOUNT_2Python 总览示例
Section titled “Python 总览示例”import threadingfrom ibapi.client import EClientfrom ibapi.wrapper import EWrapper
INFO_CODES = {2100, 2104, 2106, 2158}
class ManagedAccountsOverviewApp(EWrapper, EClient): def __init__(self): EClient.__init__(self, self) self.ready = threading.Event() self.managed_ready = threading.Event() self.accounts = [] self.info_codes = [] self.errors_seen = []
def nextValidId(self, orderId): self.ready.set()
def managedAccounts(self, accountsList): # accountsList 是逗号分隔的真实账户号。公开输出前要脱敏。 self.accounts = [item for item in (accountsList or "").split(",") if item] self.managed_ready.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 = ManagedAccountsOverviewApp()
try: app.connect("127.0.0.1", 7497, clientId=981)
thread = threading.Thread(target=app.run, daemon=True) thread.start()
if not app.ready.wait(8): raise RuntimeError("等待 nextValidId 超时")
app.reqManagedAccts()
if not app.managed_ready.wait(8): raise RuntimeError("等待 managedAccounts 回调超时")
finally: if app.isConnected(): app.disconnect()
aliases = [f"ACCOUNT_{index}" for index, _ in enumerate(app.accounts, 1)]
print("REQUEST_SENT=True")print(f"MANAGED_CALLBACK_RECEIVED={app.managed_ready.is_set()}")print(f"ACCOUNT_COUNT={len(app.accounts)}")print("ACCOUNT_ALIASES=" + (",".join(aliases) if aliases else "NONE"))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 模拟账户、127.0.0.1:7497 和独立 clientId 检查时,脱敏输出如下:
CONNECTED=TrueREQUEST_SENT=TrueMANAGED_CALLBACK_RECEIVED=TrueACCOUNT_COUNT=1ACCOUNT_ALIASES=ACCOUNT_1RAW_LIST_HAS_TRAILING_COMMA=FalseINFO_CODES=2104,2106,2158NON_INFO_ERROR_COUNT=0IS_CONNECTED_AFTER_DISCONNECT=False这说明这个会话可见 1 个账户,回调正常到达,没有非信息类错误。
账户号怎么继续使用
Section titled “账户号怎么继续使用”拿到账户号后,常见用法如下:
account = app.accounts[0]
app.reqAccountSummary(8001, "All", "NetLiquidation,BuyingPower")app.reqAccountUpdates(True, account)app.reqPositions()app.reqPnL(9001, account, "")不同接口对账户参数的要求不完全一样。不要把 accountsList 原始字符串直接传给所有接口,通常应先 split 成列表,再取目标账户。
可以把账户号写死吗?
Section titled “可以把账户号写死吗?”测试脚本可以临时写死,但正式程序不建议。账户号可能因模拟账户、真实账户、多账户权限或登录用户不同而变化。先请求 managedAccounts() 更稳。
返回 1 个账户是不是异常?
Section titled “返回 1 个账户是不是异常?”不是。普通个人模拟账户通常只返回 1 个可见账户。多账户、顾问账户或 IBroker 场景才更常见多个账户。
信息码 2104、2106、2158 是失败吗?
Section titled “信息码 2104、2106、2158 是失败吗?”不是。它们通常是数据服务连接状态提示。判断请求是否成功,应看是否收到 managedAccounts() 回调,以及是否有非信息类错误。