接收单个持仓盈亏
reqPnLSingle() 的返回结果通过 pnlSingle() 接收。
def pnlSingle(self, reqId, pos, dailyPnL, unrealizedPnL, realizedPnL, value): ...它返回的是单个持仓维度的盈亏和市值,不包含完整合约对象。因此程序通常要把 reqId 与请求时的 conId、account 保存起来,才能知道回调属于哪个合约。
| 字段 | 中文含义 | 说明 |
|---|---|---|
reqId | 请求编号 | 对应 reqPnLSingle() 的请求编号 |
pos | 持仓数量 | 可能是 Decimal,建议保存为字符串 |
dailyPnL | 当日盈亏 | 按 TWS 的 PnL 重置规则计算 |
unrealizedPnL | 未实现盈亏 | 未平仓部分的盈亏 |
realizedPnL | 已实现盈亏 | 已平仓或已实现部分的盈亏 |
value | 市值 | 持仓的市场价值 |
这些都是敏感交易数据。公开日志中不建议直接展示真实金额。
处理结构建议
Section titled “处理结构建议”pending_single_pnl = { 9302: { "account": "ACCOUNT_1", "conid": 265598, "modelCode": "", }}
def pnlSingle(self, reqId, pos, dailyPnL, unrealizedPnL, realizedPnL, value): request = pending_single_pnl.get(reqId) row = { "reqId": reqId, "account": request["account"], "conid": request["conid"], "position": str(pos), "dailyPnL": dailyPnL, "unrealizedPnL": unrealizedPnL, "realizedPnL": realizedPnL, "value": value, }如果要把结果展示到页面上,建议在后端完成账户号脱敏和字段筛选,再返回给前端。
TWS 模拟账户请求 AAPL conId=265598 的单持仓 PnL,结果为:
SINGLE_PNL_CALLBACK_RECEIVED=FalseSINGLE_PNL_ROW_COUNT=0SINGLE_PNL_REQID_COUNTS=NONESINGLE_PNL_VALUE_TYPE_COUNTS=NONEINFO_CODES=2104,2106,2158NON_INFO_ERROR_COUNT=0没有该合约持仓时,pnlSingle() 可能不会被调用。程序应设置超时,不要无限等待。
为什么 pnlSingle() 没有 account 和 conId?
Section titled “为什么 pnlSingle() 没有 account 和 conId?”因为回调只返回 reqId 和 PnL 数值。账户、模型和合约 ID 需要由程序在请求时自己保存映射关系。
dailyPnL 和 unrealizedPnL 一定一样吗?
Section titled “dailyPnL 和 unrealizedPnL 一定一样吗?”不一定。dailyPnL 受当日重置规则影响,unrealizedPnL 表示未平仓部分的盈亏。
value 是成本还是市值?
Section titled “value 是成本还是市值?”value 是持仓的市场价值,不是平均成本。