请求持仓
reqPositions() 用来请求这个 TWS 会话可见账户的持仓数据。
app.reqPositions()这个方法没有参数,也不传账户号。TWS 会把可见账户的持仓通过 position() 回调返回,并在初始批次结束时调用 positionEnd()。
| 检查项 | 原因 |
|---|---|
| TWS 或 IB Gateway 已登录 | 持仓来自登录账户 |
| API Socket 已开启 | 程序需要连接 API 端口 |
已收到 nextValidId() | 确认基础连接已建立 |
| 知道持仓数据需要脱敏 | 回调可能包含账户号、合约、数量和成本 |
reqPositions() 本身不需要市场数据订阅。即使没有行情权限,仍可能返回持仓合约、数量和成本;但它不会替代行情接口。
最小请求流程
Section titled “最小请求流程”连接 TWS / IB Gateway -> 等待 nextValidId() -> reqPositions() -> position(...) -> positionEnd() -> cancelPositions()如果只需要初始持仓快照,收到 positionEnd() 后可以调用 cancelPositions()。如果要持续监听持仓变化,可以保持订阅,但退出前仍建议取消。
Python 请求示例
Section titled “Python 请求示例”import threadingfrom ibapi.client import EClientfrom ibapi.wrapper import EWrapper
INFO_CODES = {2100, 2104, 2106, 2158}
class RequestPositionsApp(EWrapper, EClient): def __init__(self): EClient.__init__(self, self) self.ready = threading.Event() self.positions_end = threading.Event() self.position_count = 0 self.info_codes = [] self.errors_seen = []
def nextValidId(self, orderId): self.ready.set()
def position(self, account, contract, position, avgCost): self.position_count += 1
def positionEnd(self): 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 = RequestPositionsApp()
try: app.connect("127.0.0.1", 7497, clientId=985)
thread = threading.Thread(target=app.run, daemon=True) thread.start()
connected = app.ready.wait(8)
if not connected: raise RuntimeError("等待 nextValidId 超时")
app.reqPositions()
end_received = app.positions_end.wait(8)
finally: if app.isConnected(): app.cancelPositions() app.disconnect()
print(f"CONNECTED={connected}")print("REQUEST_SENT=True")print(f"POSITION_END_RECEIVED={end_received}")print(f"POSITION_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_SENT=TruePOSITION_END_RECEIVED=TruePOSITION_ROW_COUNT=0INFO_CODES=2104,2106,2158NON_INFO_ERROR_COUNT=0IS_CONNECTED_AFTER_DISCONNECT=False这个环境返回 0 行持仓,但收到了 positionEnd(),说明请求链路是通的。
没有 position() 行就是失败吗?
Section titled “没有 position() 行就是失败吗?”不是。没有持仓时,常见结果就是 0 行 position(),但仍然收到 positionEnd()。这表示初始批次已经结束,只是没有可返回的持仓。
为什么不传账户号?
Section titled “为什么不传账户号?”普通 reqPositions() 请求这个会话可见账户的持仓。如果需要明确账户或模型组合过滤,使用下一组的 reqPositionsMulti()。
是否需要取消?
Section titled “是否需要取消?”建议取消。reqPositions() 是实时持仓更新请求,如果程序只需要一次快照,收到 positionEnd() 后调用 cancelPositions()。