跳转到内容

持仓

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 positions

position() 回调会按账户分组保存持仓:

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,
}
]
}

公开日志和文档中不要展示真实账户号、持仓数量和成本价。

下面示例只打印账户数量、持仓行数和字段名。即使账户里有持仓,也不直接输出具体数量和成本价。

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=True
ACCOUNT_COUNT=0
POSITION_ROW_COUNT=0
IS_CONNECTED_AFTER_STOP=False

这说明请求已经完成,只是账户没有可返回的持仓行。不要把 0 行持仓误判成连接失败。

字段中文含义
account账户代码,公开展示前必须脱敏
contract.symbol合约代码
contract.secType证券类型,例如 STKOPTFUT
contract.currency计价币种
contract.exchange交易所或路由;部分期货持仓回调中可能为空
position持仓数量
avgCost平均成本

position 可以是正数、负数或 0。正数通常表示多头,负数通常表示空头。实际解释还要结合产品类型、账户权限和交易所规则。

官方的 reqPositions() 本质上是一个持仓更新订阅:

  1. 调用后,TWS 会先返回所有可见持仓。
  2. 持仓快照发送完,会触发 positionEnd()
  3. 如果持仓变化,还可能继续收到新的 position()
  4. 不再需要持仓更新时,应调用 cancelPositions()

get_positions() 是同步封装,所以它在收到 positionEnd() 后会自动调用 cancelPositions()。这适合一次性读取持仓。如果你要做实时持仓监控,则要使用原始异步模型持续监听,而不是每秒反复调用同步方法。

持仓和组合数据很像,但并不完全相同:

数据主要接口重点
持仓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 做唯一匹配,应结合 conIdsymbolsecType
成本价和预期不同平均成本计算口径可能和策略自己的算法不同程序要保留自己的成交流水和成本计算

IBKR Campus: TWS API Documentation