未完成订单
get_open_orders() 用来查询还没有结束的订单,也就是仍然处于活动状态、还能被成交、修改或取消的订单。
它对应原始 TWS API 的这一组调用和回调:
reqOpenOrders() -> openOrder(orderId, contract, order, orderState) -> openOrderEnd()注意:未完成订单不是历史订单。已经完全成交、已经取消、已经失效的订单,不会通过 reqOpenOrders() 当作开放订单返回。要查成交记录,需要看后面的“成交”章节。
本地官方源码中的同步封装如下:
def get_open_orders(self, timeout=3): # 清空上一次缓存的开放订单,避免新旧结果混在一起。 self.open_orders = {}
self.reqOpenOrders() return self._wait_for_response(0, "open_orders", timeout)源码里的 openOrder() 回调会把每一条订单整理成字典:
def openOrder(self, orderId, contract, order, orderState): open_order_data = { "orderId": orderId, "contract": contract, "order": order, "orderState": orderState, }
self.open_orders[orderId] = open_order_data当 TWS 把开放订单都发完后,会触发 openOrderEnd()。同步封装就是等这个结束信号,再把 self.open_orders 返回给调用方。
返回结构可以理解成:
| 字段 | 中文含义 |
|---|---|
orderId | API 客户端可识别的订单 ID |
contract | 合约对象,例如股票代码、证券类型、交易所、币种 |
order | 订单对象,例如买卖方向、订单类型、数量、限价、有效期 |
orderState | 订单状态对象,例如 PreSubmitted、Submitted、PendingCancel |
最小查询示例
Section titled “最小查询示例”下面的示例会在模拟账户中提交一笔极低价格的 AAPL 限价买单,使它保持未成交;随后调用 get_open_orders() 查询未完成订单,最后立刻撤单并确认列表清空。
这个价格只是为了演示开放订单查询流程,不是交易建议。
from decimal import Decimalimport time
from ibapi.contract import Contractfrom ibapi.order import Orderfrom ibapi.sync_wrapper import TWSSyncWrapper, ResponseTimeout
def stock_contract(symbol: str, currency: str = "USD") -> Contract: contract = Contract() contract.symbol = symbol contract.secType = "STK" contract.exchange = "SMART" contract.currency = currency return contract
def low_price_limit_buy() -> Order: order = Order() order.action = "BUY" order.orderType = "LMT" order.totalQuantity = Decimal("1") order.lmtPrice = 1.00 order.tif = "DAY" order.transmit = True order.whatIf = False order.orderRef = "DOC_OPEN_ORDERS_TEST" return order
app = TWSSyncWrapper(timeout=12)
try: if not app.connect_and_start("127.0.0.1", 7497, 955): raise RuntimeError("连接 TWS API 失败")
place_status = app.place_order_sync( stock_contract("AAPL"), low_price_limit_buy(), timeout=12, )
order_id = place_status["orderId"] print(f"PLACE_STATUS={place_status['status']}")
open_orders = app.get_open_orders(timeout=8) print(f"OPEN_ORDER_COUNT={len(open_orders)}")
for order_id, item in open_orders.items(): contract = item["contract"] order = item["order"] order_state = item["orderState"]
print(f"SYMBOL={contract.symbol}") print(f"SEC_TYPE={contract.secType}") print(f"EXCHANGE={contract.exchange}") print(f"ACTION={order.action}") print(f"ORDER_TYPE={order.orderType}") print(f"TOTAL_QUANTITY={order.totalQuantity}") print(f"LMT_PRICE={order.lmtPrice}") print(f"TIF={order.tif}") print(f"ORDER_REF={order.orderRef}") print(f"STATE_STATUS={order_state.status}")
cancel_status = app.cancel_order_sync(order_id, timeout=12) print(f"CANCEL_STATUS={cancel_status['status']}")
time.sleep(2) open_orders_after_cancel = app.get_open_orders(timeout=8) print(f"OPEN_ORDER_COUNT_AFTER_CANCEL={len(open_orders_after_cancel)}")
except ResponseTimeout: print("等待 TWS 返回开放订单或订单状态时超时,请到 TWS 订单窗口确认订单状态。")
finally: app.disconnect_and_stop() print(f"IS_CONNECTED_AFTER_STOP={app.isConnected()}")使用 TWS 模拟账户、127.0.0.1:7497 和独立 clientId 检查时,输出类似:
CONNECTED=TruePLACE_STATUS=PreSubmittedPLACE_FILLED=0PLACE_REMAINING=1OPEN_ORDER_COUNT=1ORDER_ID=1SYMBOL=AAPLSEC_TYPE=STKEXCHANGE=SMARTACTION=BUYORDER_TYPE=LMTTOTAL_QUANTITY=1LMT_PRICE=1.0TIF=DAYORDER_REF=DOC_OPEN_ORDERS_TESTSTATE_STATUS=PreSubmittedCANCEL_STATUS=PendingCancelOPEN_ORDER_COUNT_AFTER_CANCEL=0IS_CONNECTED_AFTER_STOP=False这说明 get_open_orders() 读取到了刚提交的未完成订单;撤单后再次查询,开放订单数量变为 0。
连接日志里可能还会看到 2104、2106、2158 这类市场数据场连接正常的提示,或者撤单后的 202 提示。这些是 TWS 的状态和撤单通知,不代表开放订单查询失败。
reqOpenOrders 的边界
Section titled “reqOpenOrders 的边界”官方文档把开放订单查询分成三类:
| 方法 | 中文含义 | 适合场景 |
|---|---|---|
reqOpenOrders() | 查询这个客户端可以看到的开放订单 | 普通策略程序最常用 |
reqAllOpenOrders() | 查询所有 API 客户端提交的开放订单 | 多个 API 程序同时连接同一个 TWS 时使用 |
reqAutoOpenOrders(True) | 让 clientId=0 绑定之后的 TWS 手工订单 | 需要 API 接管手工订单时使用 |
get_open_orders() 封装的是 reqOpenOrders(),它最适合新手和普通策略系统:谁提交订单,谁查询和管理订单,边界清楚。
clientId 和订单可见性
Section titled “clientId 和订单可见性”TWS API 里,订单不是只按账户归属,还和 API 客户端有关。
| 情况 | 结果 |
|---|---|
用 clientId=955 提交订单,再用同一个连接查询 | 通常可以看到这笔订单 |
用另一个普通 clientId 查询 | 不一定能看到或管理这笔订单 |
用 reqAllOpenOrders() 查询 | 可以查看所有 API 客户端提交的开放订单 |
用 clientId=0 并启用自动绑定 | 可以绑定部分 TWS 手工订单,随后 API 才能管理 |
如果你的系统只做自动策略,建议每个策略使用固定的 clientId,并把 orderId、permId、orderRef 记录到自己的日志或数据库。这样程序重启后,才能把 TWS 返回的订单和本地策略订单对应起来。
openOrder 和 orderStatus 的区别
Section titled “openOrder 和 orderStatus 的区别”开放订单查询常常会同时涉及 openOrder() 和 orderStatus(),两者含义不同:
| 回调 | 主要内容 | 适合用来做什么 |
|---|---|---|
openOrder() | 合约、订单参数、订单状态对象 | 恢复订单列表、展示订单详情 |
orderStatus() | 成交数量、剩余数量、平均成交价、状态变化 | 监听订单生命周期 |
openOrderEnd() | 本轮开放订单发送结束 | 判断这次查询已经收齐 |
只看 openOrder() 不够,因为它更像“订单详情快照”;真正判断是否成交、剩余多少、是否取消,仍然要结合 orderStatus()。
常用字段解释
Section titled “常用字段解释”| 字段 | 示例 | 中文解释 |
|---|---|---|
contract.symbol | AAPL | 合约代码 |
contract.secType | STK | 证券类型,STK 表示股票 |
contract.exchange | SMART | 路由或交易所 |
order.action | BUY | 买入或卖出 |
order.orderType | LMT | 订单类型,LMT 表示限价单 |
order.totalQuantity | 1 | 委托数量 |
order.lmtPrice | 1.0 | 限价单价格 |
order.tif | DAY | 有效期,DAY 表示当日有效 |
order.orderRef | DOC_OPEN_ORDERS_TEST | 自定义订单标记,方便日志追踪 |
orderState.status | PreSubmitted | TWS 看到的订单状态 |
| 现象 | 常见原因 | 处理方式 |
|---|---|---|
| 返回 0 条开放订单 | 没有活动订单,或订单已经成交/取消 | 到 TWS 订单窗口确认;需要历史成交时用成交查询 |
| 刚撤单后还能看到订单 | 状态可能还在 PendingCancel | 等待几秒后重新查询,或继续监听 orderStatus() |
| 看不到手工订单 | 普通 clientId 不会自动接管 TWS 手工订单 | 了解 clientId=0、reqAutoOpenOrders(True) 和订单绑定后再使用 |
| 多个程序看到的订单不一致 | clientId 不同,订单可见范围不同 | 为每个策略固定 clientId,必要时使用 reqAllOpenOrders() |
| 查询超时 | TWS 没有在超时时间内返回 openOrderEnd() | 检查 TWS 是否在线、API 连接是否正常、是否有弹窗阻塞 |