ExecID 行为
execId 是 IBKR 给每一笔成交分配的成交编号。它出现在 Execution.execId 中,也会出现在 commissionAndFeesReport.execId 中。
这意味着你可以用 execId 把成交记录和佣金费用报告关联起来。对交易系统来说,execId 是成交表里最重要的字段之一。
execDetails.execution.execId == commissionAndFeesReport.execId成交先到、费用后到是常见情况。程序应该允许先保存成交,再根据相同 execId 更新费用字段,而不是假设两个回调一定连续到达。
为什么不能只用 orderId
Section titled “为什么不能只用 orderId”一个订单可以被分成多笔成交。例如买入 100 股,可能先成交 40 股,再成交 60 股。此时它们可能有相同的 orderId 和 permId,但会有不同的 execId。
| 字段 | 粒度 | 说明 |
|---|---|---|
orderId | API 客户端订单 | 同一个客户端内递增,重连后仍要小心管理。 |
permId | IBKR 永久订单编号 | 更适合跨客户端、跨会话追踪同一个订单。 |
execId | 单笔成交 | 更适合做成交明细、佣金关联和去重。 |
因此,成交表建议至少把 execId、orderId、permId、clientId 都保存下来。
def execDetails(self, reqId, contract, execution): # execId 是成交流水去重的核心字段。 key = execution.execId
row = { "exec_id": execution.execId, "perm_id": execution.permId, "order_id": execution.orderId, "client_id": execution.clientId, "symbol": contract.symbol, "side": execution.side, "shares": float(execution.shares), "price": execution.price, "time": execution.time, }
save_or_update_execution(key, row)如果你的系统支持多个账户、多个 TWS 会话或多个交易环境,数据库唯一键可以设计成:
account + execId或更保守一些:
account + permId + execId与佣金报告合并
Section titled “与佣金报告合并”def commissionAndFeesReport(self, report): # 用同一个 execId 找到之前保存的成交记录,再补齐费用字段。 update_execution_fee( exec_id=report.execId, commission_and_fees=report.commissionAndFees, currency=report.currency, realized_pnl=report.realizedPNL, )commissionAndFeesReport() 的字段名在新版 API 中是 commissionAndFees。一些旧资料可能还写作 commission,读文档或迁移旧代码时要注意版本差异。
EXEC_ROWS=0COMMISSION_ROWS=0查询范围内没有成交时,不会出现实际 execId。这不是异常;它只表示 TWS 可见范围里没有成交流水。