取消持仓请求
cancelPositions() 用来取消由 reqPositions() 发起的实时持仓更新。
app.cancelPositions()这个方法没有参数。因为普通 reqPositions() 没有 reqId,取消时也不需要传请求编号。
和 positionEnd() 的区别
Section titled “和 positionEnd() 的区别”| 名称 | 含义 |
|---|---|
positionEnd() | 初始持仓批次已经返回完 |
cancelPositions() | 主动停止继续接收持仓更新 |
收到 positionEnd() 不等于自动取消。如果程序只需要一次持仓快照,建议收到 positionEnd() 后显式调用 cancelPositions()。
reqPositions() -> position(...) -> positionEnd() -> cancelPositions()取消后,短时间内可能仍有已排队回调。判断是否正常时,看取消后回调数量是否继续增长,以及是否出现非信息类错误。
Python 取消示例
Section titled “Python 取消示例”import threadingimport timefrom ibapi.client import EClientfrom ibapi.wrapper import EWrapper
INFO_CODES = {2100, 2104, 2106, 2158}
class CancelPositionsApp(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 = CancelPositionsApp()cancel_sent = False
try: app.connect("127.0.0.1", 7497, clientId=987)
thread = threading.Thread(target=app.run, daemon=True) thread.start()
if not app.ready.wait(8): raise RuntimeError("等待 nextValidId 超时")
app.reqPositions()
if not app.positions_end.wait(8): raise RuntimeError("等待 positionEnd 超时")
rows_before_cancel = app.position_count
app.cancelPositions() cancel_sent = True time.sleep(0.5)
rows_after_cancel = app.position_count
finally: if app.isConnected(): app.disconnect()
print(f"POSITION_END_RECEIVED={app.positions_end.is_set()}")print(f"CANCEL_SENT={cancel_sent}")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)}")print(f"IS_CONNECTED_AFTER_DISCONNECT={app.isConnected()}")POSITION_END_RECEIVED=TrueCANCEL_SENT=TrueROWS_BEFORE_CANCEL=0ROWS_AFTER_CANCEL_WAIT=0INFO_CODES=2104,2106,2158NON_INFO_ERROR_COUNT=0IS_CONNECTED_AFTER_DISCONNECT=False账户没有持仓,取消前后持仓行数都为 0。接口成功的依据是收到了 positionEnd(),并且没有非信息类错误。
没有持仓还需要取消吗?
Section titled “没有持仓还需要取消吗?”建议取消。即使返回 0 行,reqPositions() 仍是实时持仓更新请求。取消可以让程序状态更清楚。
取消需要传账户号吗?
Section titled “取消需要传账户号吗?”不需要。普通 cancelPositions() 没有参数。按账户或模型组合取消是下一组 cancelPositionsMulti(reqId) 的内容。
取消后还能重新请求吗?
Section titled “取消后还能重新请求吗?”可以。再次调用 reqPositions() 会重新发起持仓请求。程序里要避免重复请求却忘记取消,导致状态难以排查。