请求单个持仓盈亏
reqPnLSingle() 用来订阅单个持仓的实时盈亏。
app.reqPnLSingle(reqId, account, modelCode, conid)它只适合请求账户中已经存在的持仓。如果 conid 无效,或者账户里没有这个合约持仓,可能不会返回 pnlSingle() 回调。
def reqPnLSingle(self, reqId: int, account: str, modelCode: str, conid: int): ...| 参数 | 中文含义 | 说明 |
|---|---|---|
reqId | 请求编号 | 自己分配,用于接收和取消这次订阅 |
account | 账户代码 | 建议从 reqManagedAccts() 获取,不写死 |
modelCode | 模型代码 | 没有模型组合时传空字符串 "" |
conid | 合约 ID | 必须是合约的 IBKR conId,不是股票代码 |
conid 可以通过合约详情接口查到。比如 AAPL 股票常见 conId 为 265598,但只有账户确实持有该合约时,单持仓 PnL 才有意义。
nextValidId() -> reqManagedAccts() -> reqPnLSingle(reqId, account, "", conid) -> pnlSingle(...) -> cancelPnLSingle(reqId)没有收到 pnlSingle() 不一定是程序崩了。对于单持仓 PnL,需要同时检查账户是否有该持仓、conId 是否正确、TWS 是否已经加载组合数据。
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 RequestPnLSingleApp(EWrapper, EClient): def __init__(self): EClient.__init__(self, self) self.ready = threading.Event() self.accounts_ready = threading.Event() self.pnl_single_ready = threading.Event() self.accounts = [] self.row_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 pnlSingle(self, reqId, pos, dailyPnL, unrealizedPnL, realizedPnL, value): self.row_count += 1 self.pnl_single_ready.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 = RequestPnLSingleApp()req_id = 9302conid = 265598 # AAPL 示例 conId;真实使用时应替换为账户中实际持仓的 conId
try: app.connect("127.0.0.1", 7497, clientId=122) 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("等待账户列表超时")
app.reqPnLSingle(req_id, app.accounts[0], "", conid) callback_received = app.pnl_single_ready.wait(5)
finally: if app.isConnected(): app.cancelPnLSingle(req_id) app.disconnect()
print(f"CONNECTED={connected}")print(f"SINGLE_PNL_CALLBACK_RECEIVED={callback_received}")print(f"SINGLE_PNL_ROW_COUNT={app.row_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=TrueSINGLE_REQUEST_ID=9302SINGLE_CONID=265598SINGLE_PNL_CALLBACK_RECEIVED=FalseSINGLE_PNL_ROW_COUNT=0SINGLE_PNL_REQID_COUNTS=NONESINGLE_PNL_VALUE_TYPE_COUNTS=NONESINGLE_CANCEL_SENT=TrueSINGLE_ROWS_BEFORE_CANCEL=0SINGLE_ROWS_AFTER_CANCEL_WAIT=0INFO_CODES=2104,2106,2158NON_INFO_ERROR_COUNT=0IS_CONNECTED_AFTER_DISCONNECT=FalseTWS 模拟账户没有 AAPL 持仓,因此没有收到 pnlSingle()。这和官方边界一致:合约不在账户持仓中时,单持仓 PnL 可能没有响应。
conid 是股票代码吗?
Section titled “conid 是股票代码吗?”不是。conid 是 IBKR 合约 ID。股票代码是 symbol,两者不是同一个字段。
没有回调是不是一定失败?
Section titled “没有回调是不是一定失败?”不是。单持仓 PnL 对“账户中是否存在该持仓”很敏感。没有持仓时可能没有回调,也没有非信息类错误。
如何让示例返回数据?
Section titled “如何让示例返回数据?”需要账户里真实持有这个合约。模拟账户中可以先通过持仓接口确认 conId,再请求对应合约的 PnL。