跳转到内容

请求按模型持仓

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 参数需要传有效账户
明确是否需要模型过滤没有模型时传 "",不要随便填股票代码或策略名称
nextValidId()
-> reqManagedAccts()
-> reqPositionsMulti(reqId, account, modelCode)
-> positionMulti(...)
-> positionMultiEnd(reqId)

如果只需要一次持仓快照,收到 positionMultiEnd(reqId) 后再调用 cancelPositionsMulti(reqId)

import threading
from ibapi.client import EClient
from 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=True
REQUEST_ID=9201
ACCOUNT_ALIAS=ACCOUNT_1
MODEL_CODE=EMPTY
REQUEST_SENT=True
POSITION_MULTI_END_RECEIVED=True
POSITION_MULTI_ROW_COUNT=0
INFO_CODES=2104,2106,2158
NON_INFO_ERROR_COUNT=0
IS_CONNECTED_AFTER_DISCONNECT=False

TWS 模拟账户没有持仓,因此持仓行数为 0。这个结果仍然有效,因为结束回调已经返回。

可以由程序自己分配,但同一时间不要和其他活跃请求重复。建议在程序里用统一的请求编号生成器,而不是手动散落写数字。

在一些多账户接口中空字符串有特殊含义,但新手示例不建议依赖这种写法。更稳妥的方式是先用 reqManagedAccts() 获取账户,再传明确账户代码。

可能返回空结果,或者无法得到你预期的模型持仓。普通个人账户没有模型组合时,传空字符串即可。