持仓
get_positions() 用来查询账户持仓。持仓表示账户现在还持有多少某个合约,而不是订单,也不是成交流水。
它对应原始 TWS API 的这一组调用和回调:
reqPositions() -> position(account, contract, position, avgCost) -> positionEnd()cancelPositions()如果账户没有持仓,返回空字典或 0 行持仓是正常结果。
本地官方源码中的同步封装如下:
def get_positions(self, timeout=10): self.positions = {}
self.reqPositions() positions = self._wait_for_response(0, "positions", timeout)
self.cancelPositions()
return positionsposition() 回调会按账户分组保存持仓:
def position(self, account, contract, position, avgCost): if account not in self.positions: self.positions[account] = []
self.positions[account].append({ "contract": contract, "position": position, "avgCost": avgCost, })返回结构可以理解成:
{ "ACCOUNT_CODE": [ { "contract": Contract, "position": Decimal, "avgCost": float, } ]}公开日志和文档中不要展示真实账户号、持仓数量和成本价。
最小查询示例
Section titled “最小查询示例”下面示例只打印账户数量、持仓行数和字段名。即使账户里有持仓,也不直接输出具体数量和成本价。
from ibapi.sync_wrapper import TWSSyncWrapper, ResponseTimeout
app = TWSSyncWrapper(timeout=12)
try: if not app.connect_and_start("127.0.0.1", 7497, 957): raise RuntimeError("连接 TWS API 失败")
positions = app.get_positions(timeout=10)
account_count = len(positions) position_row_count = sum(len(rows) for rows in positions.values())
print(f"ACCOUNT_COUNT={account_count}") print(f"POSITION_ROW_COUNT={position_row_count}")
for index, rows in enumerate(positions.values(), start=1): print(f"ACCOUNT_ALIAS=ACCOUNT_{index}") print(f"ROWS={len(rows)}")
if rows: item = rows[0] contract = item["contract"]
print("SAMPLE_FIELDS=contract.symbol,contract.secType,contract.currency,position,avgCost") print(f"SAMPLE_SYMBOL={contract.symbol}") print(f"SAMPLE_SEC_TYPE={contract.secType}") print(f"SAMPLE_CURRENCY={contract.currency}") print("SAMPLE_POSITION=<redacted>") print("SAMPLE_AVG_COST=<redacted>")
except ResponseTimeout: print("等待持仓回调超时,请确认 TWS API 连接正常。")
finally: app.disconnect_and_stop() print(f"IS_CONNECTED_AFTER_STOP={app.isConnected()}")使用 TWS 模拟账户、127.0.0.1:7497 和独立 clientId 检查时,如果账户没有持仓,输出类似:
CONNECTED=TrueACCOUNT_COUNT=0POSITION_ROW_COUNT=0IS_CONNECTED_AFTER_STOP=False这说明请求已经完成,只是账户没有可返回的持仓行。不要把 0 行持仓误判成连接失败。
| 字段 | 中文含义 |
|---|---|
account | 账户代码,公开展示前必须脱敏 |
contract.symbol | 合约代码 |
contract.secType | 证券类型,例如 STK、OPT、FUT |
contract.currency | 计价币种 |
contract.exchange | 交易所或路由;部分期货持仓回调中可能为空 |
position | 持仓数量 |
avgCost | 平均成本 |
position 可以是正数、负数或 0。正数通常表示多头,负数通常表示空头。实际解释还要结合产品类型、账户权限和交易所规则。
官方的 reqPositions() 本质上是一个持仓更新订阅:
- 调用后,TWS 会先返回所有可见持仓。
- 持仓快照发送完,会触发
positionEnd()。 - 如果持仓变化,还可能继续收到新的
position()。 - 不再需要持仓更新时,应调用
cancelPositions()。
get_positions() 是同步封装,所以它在收到 positionEnd() 后会自动调用 cancelPositions()。这适合一次性读取持仓。如果你要做实时持仓监控,则要使用原始异步模型持续监听,而不是每秒反复调用同步方法。
和组合数据的区别
Section titled “和组合数据的区别”持仓和组合数据很像,但并不完全相同:
| 数据 | 主要接口 | 重点 |
|---|---|---|
| 持仓 | reqPositions() | 合约、数量、平均成本 |
| 组合 | reqAccountUpdates() / updatePortfolio() | 持仓数量、市场价、市值、未实现盈亏、已实现盈亏 |
| 账户摘要 | reqAccountSummary() | 账户净值、可用资金、购买力等账户级字段 |
如果只是判断账户现在持有什么,用持仓接口;如果要显示市值和盈亏,用组合数据;如果要看资金和保证金,用账户摘要。
官方文档说明,reqPositions() 可以用于多账户持仓订阅,但在非常大的 Introducing Broker 或 Financial Advisor 主账户上可能不可用。对于大量子账户,应考虑 reqPositionsMulti(),按账户或模型订阅更小范围的持仓更新。
普通个人模拟账户通常不需要一开始就处理 reqPositionsMulti(),但系统设计里要知道这个边界,避免接入 FA 或多账户时把单账户逻辑写死。
| 现象 | 常见原因 | 处理方式 |
|---|---|---|
POSITION_ROW_COUNT=0 | 没有持仓 | 这是正常结果;如需确认,可在 TWS 组合窗口查看 |
| 持仓查询超时 | 没有收到 positionEnd() | 检查 TWS 是否在线、API 是否已连接、是否有弹窗阻塞 |
| 多账户只看到部分数据 | 账户权限或主账户限制 | 确认账户结构;大型 FA / IBroker 账户考虑 reqPositionsMulti() |
| 期货持仓没有交易所字段 | 官方说明部分期货可能不返回 exchange | 不要只靠 exchange 做唯一匹配,应结合 conId、symbol、secType |
| 成本价和预期不同 | 平均成本计算口径可能和策略自己的算法不同 | 程序要保留自己的成交流水和成本计算 |