请求按模型持仓
reqPositionsMulti() 用来请求某个账户和某个模型组合下的持仓。
app.reqPositionsMulti(reqId, account, modelCode)这个接口的核心价值是“可过滤、可追踪”。它带 reqId,所以程序可以同时管理多个订阅,并且在回调和取消时知道数据属于哪一次请求。
def reqPositionsMulti(self, reqId: int, account: str, modelCode: str): ...| 参数 | 中文含义 | 常见写法 | 说明 |
|---|---|---|---|
reqId | 请求编号 | 9201 | 自己分配,不能和正在使用的请求混淆 |
account | 账户代码 | 由 reqManagedAccts() 返回 | 建议不要在公开示例里写死真实账号 |
modelCode | 模型代码 | "" | 没有模型组合过滤时传空字符串 |
| 检查项 | 原因 |
|---|---|
| TWS 或 IB Gateway 已登录 | 持仓来自登录会话 |
| API Socket 已启用 | 程序需要连接本地 Socket 端口 |
已收到 nextValidId() | 说明基础连接已经建立 |
| 已拿到账户代码 | account 参数需要传有效账户 |
| 明确是否需要模型过滤 | 没有模型时传 "",不要随便填股票代码或策略名称 |
最小请求流程
Section titled “最小请求流程”nextValidId() -> reqManagedAccts() -> reqPositionsMulti(reqId, account, modelCode) -> positionMulti(...) -> positionMultiEnd(reqId)如果只需要一次持仓快照,收到 positionMultiEnd(reqId) 后再调用 cancelPositionsMulti(reqId)。
Python 请求示例
Section titled “Python 请求示例”import threading
from ibapi.client import EClientfrom ibapi.wrapper import EWrapper
INFO_CODES = {2100, 2103, 2104, 2105, 2106, 2107, 2108, 2158}
class RequestPositionsMultiApp(EWrapper, EClient): def __init__(self): EClient.__init__(self, self) self.ready = threading.Event() self.accounts_ready = threading.Event() self.positions_end = threading.Event() self.accounts = [] self.position_count = 0 self.info_codes = [] self.errors_seen = []
def nextValidId(self, orderId): self.ready.set()
def managedAccounts(self, accountsList): self.accounts = [item for item in accountsList.split(",") if item] self.accounts_ready.set()
def positionMulti(self, reqId, account, modelCode, contract, pos, avgCost): self.position_count += 1
def positionMultiEnd(self, reqId): self.positions_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 = RequestPositionsMultiApp()req_id = 9201
try: app.connect("127.0.0.1", 7497, clientId=119) thread = threading.Thread(target=app.run, daemon=True) thread.start()
connected = app.ready.wait(8)
if not connected: raise RuntimeError("等待 nextValidId 超时")
app.reqManagedAccts() if not app.accounts_ready.wait(5): raise RuntimeError("等待账户列表超时")
account = app.accounts[0] model_code = ""
app.reqPositionsMulti(req_id, account, model_code) end_received = app.positions_end.wait(8)
finally: if app.isConnected(): app.cancelPositionsMulti(req_id) app.disconnect()
print(f"CONNECTED={connected}")print(f"REQUEST_ID={req_id}")print("ACCOUNT_ALIAS=ACCOUNT_1")print("MODEL_CODE=EMPTY")print("REQUEST_SENT=True")print(f"POSITION_MULTI_END_RECEIVED={end_received}")print(f"POSITION_MULTI_ROW_COUNT={app.position_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)}")print(f"IS_CONNECTED_AFTER_DISCONNECT={app.isConnected()}")CONNECTED=TrueREQUEST_ID=9201ACCOUNT_ALIAS=ACCOUNT_1MODEL_CODE=EMPTYREQUEST_SENT=TruePOSITION_MULTI_END_RECEIVED=TruePOSITION_MULTI_ROW_COUNT=0INFO_CODES=2104,2106,2158NON_INFO_ERROR_COUNT=0IS_CONNECTED_AFTER_DISCONNECT=FalseTWS 模拟账户没有持仓,因此持仓行数为 0。这个结果仍然有效,因为结束回调已经返回。
reqId 可以随便写吗?
Section titled “reqId 可以随便写吗?”可以由程序自己分配,但同一时间不要和其他活跃请求重复。建议在程序里用统一的请求编号生成器,而不是手动散落写数字。
account 可以传空字符串吗?
Section titled “account 可以传空字符串吗?”在一些多账户接口中空字符串有特殊含义,但新手示例不建议依赖这种写法。更稳妥的方式是先用 reqManagedAccts() 获取账户,再传明确账户代码。
modelCode 传错会怎样?
Section titled “modelCode 传错会怎样?”可能返回空结果,或者无法得到你预期的模型持仓。普通个人账户没有模型组合时,传空字符串即可。