跳转到内容

按模型获取账户更新总览

reqAccountUpdatesMulti() 用来按账户和模型组合请求账户值更新。它和前面的 reqAccountUpdates() 都属于账户数据接口,但 multi 版本多了 reqIdmodelCode,更适合多账户、顾问账户、模型组合或需要同时区分多条请求来源的程序。

典型调用链路如下:

reqAccountUpdatesMulti(reqId, account, modelCode, ledgerAndNLV)
-> accountUpdateMulti(reqId, account, modelCode, key, value, currency)
-> accountUpdateMultiEnd(reqId)
cancelAccountUpdatesMulti(reqId)

如果你的程序只是连接一个普通模拟账户,并且只想看账户资金、保证金和购买力,前面的 reqAccountUpdates() 更容易上手。等你需要用 reqId 管理多条账户数据请求,或要按模型组合拆分数据时,再使用本组接口。

对比项reqAccountUpdates()reqAccountUpdatesMulti()
请求标识没有独立 reqId需要传入 reqId
账户参数acctCodeaccount
模型组合参数不支持支持 modelCode
返回账户值回调updateAccountValue()accountUpdateMulti()
返回结束回调accountDownloadEnd()accountUpdateMultiEnd()
取消方式reqAccountUpdates(False, account)cancelAccountUpdatesMulti(reqId)

multi 版本的核心优势是“可识别”。每条账户更新数据都会带回 reqIdaccountmodelCode,程序可以更清楚地判断这行数据属于哪一次请求、哪个账户、哪个模型组合。

Python API 中常用的三个方法或回调如下:

名称类型中文含义说明
reqAccountUpdatesMulti()请求方法请求按账户和模型返回账户更新发起订阅或初始批次请求
accountUpdateMulti()回调方法接收按模型返回的账户字段每个字段一行,包含 key、value 和 currency
accountUpdateMultiEnd()回调方法接收本次初始批次结束信号参数只有 reqId
cancelAccountUpdatesMulti()请求方法取消按模型账户更新通过 reqId 取消对应请求
app.reqAccountUpdatesMulti(reqId, account, modelCode, ledgerAndNLV)
参数中文含义常用取值说明
reqId请求编号例如 9101由你的程序自己分配。回调里会带回同一个编号。
account账户代码例如 DU1234567这个 TWS 会话可见的账户号。公开日志应脱敏。
modelCode模型组合代码空字符串或模型代码没有模型组合时可以传空字符串;有模型组合时填对应代码。
ledgerAndNLV是否包含 Ledger 和净清算值相关字段True / FalseTrue 时会请求更多账户账本和净值相关字段。

modelCode 不是证券代码,也不是策略名称。它指的是 IBKR 账户体系里的模型组合代码。如果账户没有模型组合,传空字符串可以请求账户层面的 multi 更新。

def accountUpdateMulti(self, reqId, account, modelCode, key, value, currency):
...
字段中文含义说明
reqId请求编号对应你发起请求时传入的编号
account账户代码真实账户号,公开输出时要脱敏
modelCode模型组合代码没有模型组合时通常为空字符串
key账户字段名例如 $LEDGER-CashBalanceNetLiquidation
value字段值字符串形式返回,可能是数字,也可能是文本状态
currency币种例如 USDBASE,也可能为空

updateAccountValue() 类似,accountUpdateMulti()value 也是字符串。写程序时不要直接假设所有字段都能转成浮点数,应该按 key 或字段用途分别处理。

下面示例会先获取可见账户,再用空 modelCode 请求 multi 账户更新,等待 accountUpdateMultiEnd(),最后用 cancelAccountUpdatesMulti(reqId) 取消请求。示例输出只保留字段数量、币种、样例 key 和脱敏账户别名。

import threading
import time
from collections import Counter
from ibapi.client import EClient
from ibapi.wrapper import EWrapper
INFO_CODES = {2100, 2104, 2106, 2158}
REQ_ID = 9101
class AccountUpdatesMultiOverviewApp(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.rows = []
self.end_req_id = None
self.info_codes = []
self.errors_seen = []
def nextValidId(self, orderId):
self.ready.set()
def managedAccounts(self, 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):
# value 和 account 可能包含敏感信息;示例只保存结构化统计所需字段。
self.rows.append({
"reqId": reqId,
"account": account,
"modelCode": modelCode or "EMPTY",
"key": key,
"value": value,
"currency": currency or "EMPTY",
})
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))
def value_type(value):
try:
float(value)
return "decimal"
except (TypeError, ValueError):
return "text"
app = AccountUpdatesMultiOverviewApp()
try:
app.connect("127.0.0.1", 7497, clientId=974)
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 超时")
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 超时")
rows_before_cancel = len(app.rows)
app.cancelAccountUpdatesMulti(REQ_ID)
time.sleep(0.5)
rows_after_cancel = len(app.rows)
finally:
if app.isConnected():
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"RAW_CALLBACK_ROWS={len(app.rows)}")
print(f"UNIQUE_KEY_COUNT={len({row['key'] for row in app.rows})}")
print("CURRENCY_COUNTS=" + ",".join(f"{key}:{value}" for key, value in Counter(row["currency"] for row in app.rows).items()))
print("VALUE_TYPE_COUNTS=" + ",".join(f"{key}:{value}" for key, value in Counter(value_type(row["value"]) for row in app.rows).items()))
print(f"ROWS_BEFORE_CANCEL={rows_before_cancel}")
print(f"ROWS_AFTER_CANCEL_WAIT={rows_after_cancel}")
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=True
REQUEST_ID=9101
ACCOUNT_ALIAS=ACCOUNT_1
MODEL_CODE=EMPTY
LEDGER_AND_NLV=True
END_RECEIVED=True
END_REQID_MATCHES=True
RAW_CALLBACK_ROWS=50
UNIQUE_KEY_COUNT=25
REQID_COUNTS=9101:50
MODEL_CODE_COUNTS=EMPTY:50
CURRENCY_COUNTS=BASE:25,USD:25
VALUE_TYPE_COUNTS=decimal:44,text:6
SAMPLE_KEYS=$LEDGER-AccountOrGroup,$LEDGER-AccruedCash,$LEDGER-CashBalance,$LEDGER-CorporateBondValue,$LEDGER-Cryptocurrency,$LEDGER-Currency,$LEDGER-ExchangeRate,$LEDGER-FundValue,$LEDGER-FutureOptionValue,$LEDGER-FuturesPNL,$LEDGER-FxCashBalance,$LEDGER-IssuerOptionValue,$LEDGER-MoneyMarketFundValue,$LEDGER-MutualFundValue
CANCEL_SENT=True
ROWS_BEFORE_CANCEL=50
ROWS_AFTER_CANCEL_WAIT=50
INFO_CODES=2104,2106,2158
NON_INFO_ERROR_COUNT=0
IS_CONNECTED_AFTER_DISCONNECT=False

这次验证说明:

  • reqAccountUpdatesMulti() 能在普通模拟账户环境下正常返回数据。
  • modelCode 会在回调中显示为空模型组合,本例脱敏为 EMPTY
  • 50 行回调全部带回同一个 reqId=9101,便于程序按请求归类。
  • ledgerAndNLV=True 时,本例返回了 25 个 $LEDGER- 相关 key,每个 key 对应 BASEUSD 两种币种行。
  • cancelAccountUpdatesMulti(9101) 发送后,短时间等待没有新增行。
场景建议
单账户新手学习账户字段优先使用 reqAccountUpdates()
需要用 reqId 区分多条账户请求使用 reqAccountUpdatesMulti()
顾问账户、多个子账户或模型组合使用 reqAccountUpdatesMulti(),并按 accountmodelCode 分组
只需要净清算值、购买力等摘要字段优先使用 reqAccountSummary()
需要组合持仓明细看持仓接口和 position multi 页面

不可以。modelCode 要和 IBKR 账户里的模型组合代码对应。随便填一个字符串,不会自动创建模型组合,也不会变成策略标签。

不一定。它会让返回字段更完整,但数据量也更多。如果你的页面只展示少量摘要字段,先用账户摘要接口更简单。如果你的程序要核对账本币种、现金余额、净值或多币种账户状态,再考虑打开它。

multi 账户更新会返回持仓明细吗?

Section titled “multi 账户更新会返回持仓明细吗?”

它主要返回账户字段 key/value。持仓明细更适合用 reqPositions()reqPositionsMulti() 处理。不要把账户更新字段当作完整持仓列表。

IBKR Campus: TWS API Documentation