本文片段放在已创建 CTP 账户并完成同步的 ExApi 生命周期内。symbol 是要交易的合约,limit_price 是自行确定的有效限价,配置步骤见 CTP 账户。
交易操作:下单会向真实期货公司提交委托,请先在适当测试环境充分验证策略。
限价委托#
orders = api.watch_order()
if api.trading_status() != "READY":
raise RuntimeError("Trading account is not READY")
results = api.insert_order(
symbol=symbol,
direction="BUY",
offset="OPEN",
volume=1,
limit_price=limit_price,
)
tracked_ids = set()
for result in results:
print(result.status, result.order_id, result.message)
if result.status in {"SUBMITTED", "UNKNOWN"} and result.order_id:
tracked_ids.add(result.order_id)| 参数 | 要求 |
|---|---|
direction | BUY / SELL |
offset | OPEN / CLOSE |
volume | 正整数,单位为手 |
limit_price | 符合最小变动价位的有效限价 |
SDK 仅支持普通投机限价委托。最小变动价位可通过 合约详情 的 data["price_tick"] 核对,最终以柜台校验为准;不要直接将未经检查的缺失或过期行情作为限价。
即时提交结果#
| TradingResult.status | 含义 | 正确处理 |
|---|---|---|
SUBMITTED | 已交给交易接口 | 等待订单回报;不等于柜台接受或成交 |
REJECTED | 本地拒绝或明确未发送 | 检查 message;下单不会创建订单,order_id 为 None |
UNKNOWN | 提交结果不明 | 保留订单号等待自然回报,不自动重发 |
返回值为 list[TradingResult],立即可读且不会随回报改变。本地拒绝只写入返回结果,不会创建订单供 watch_order() 查询。
平仓输入 CLOSE 后,SDK 按交易所处理:
- 上期所、能源中心:按昨仓优先、今仓补足拆分,实际订单的 offset 为
CLOSE_YESTERDAY/CLOSE_TODAY。 - 其他交易所:发送普通
CLOSE,由柜台和交易所决定冲抵顺序。
拆单不是原子操作:前一子单失败或结果不明时,后续子单不再发送,以拒绝结果说明;各子单独立跟踪,也可能只成交一部分。
跟踪最终状态#
沿用下单前创建的 orders 和本轮结果中的 tracked_ids。下面为本次跟踪设置 60 秒总期限;这是应用示例期限,不是订单失效时间:
import time
deadline = time.time() + 60
while tracked_ids and time.time() < deadline:
if not api.wait_update(deadline=deadline):
break
if api.trading_status() != "READY":
continue
snapshot = orders.data()
for order_id in list(tracked_ids):
order = snapshot.get(order_id)
if order is None:
continue
print(order_id, order.status, order.volume_traded, order.volume_left,
order.status_msg, order.cancel_error)
if order.status in {"FILLED", "CANCELLED", "REJECTED"}:
tracked_ids.remove(order_id)
if tracked_ids:
print("跟踪等待结束,以下订单结果仍需核对:", sorted(tracked_ids))到期只结束本次等待,不代表报单失败、撤单成功或订单被撤销。 对于仍无终态的订单,尤其是 UNKNOWN,保留订单标识,核对账户的订单、成交和持仓;SDK 无法确认时,通过期货公司客户端或人工核实,不能按“没收到回报”重新下单。
| 订单状态 | 含义 |
|---|---|
PENDING | 待回报 |
ALIVE | 尚未确认终态 |
FILLED | 全部成交 |
CANCELLED | 未全成且不再排队,volume_left 仍可大于零 |
REJECTED | 提交失败或被拒 |
撤单#
results = api.cancel_order(order_id)
tracked_ids = {order_id}
for result in results:
print(result.status, result.order_id, result.message)传入本地 order_id,不是 order_sys_id。
- 返回空列表:订单不存在、已结束或已经在撤单处理中;空列表本身不能证明已撤成功。
- 立即拒绝:订单尚未确认或缺少系统订单号,不缓存撤单意图。
- 确认结果:查看订单的
status、cancel_pending、cancel_error;撤单SUBMITTED不等于已撤成功。
沿用下单前创建的 orders,对新的 tracked_ids 运行上面的有限等待循环,核对原订单;立即拒绝时也应查看 message,原委托不会因此撤销。等待期间仍可能成交,最终以订单终态和 volume_traded 判断实际成交量。部分成交后撤单成功,通常表现为 CANCELLED 且 volume_traded > 0、volume_left > 0;若撤单完成前已全部成交,最终可能为 FILLED。撤单不会撤回已发生的成交。
status_msg、cancel_error 提供后续提示;部分异步失败目前只有“报单被拒绝”“撤单被拒绝”等通用文字,不保证包含详细柜台原因。
断线恢复与程序重启#
- 非
READY时暂停新交易决策,继续通过更新循环推进恢复。 - 恢复
READY后重新读取订单、成交和持仓,核对断线前未结束的委托及实际成交,再决定后续操作。 - 结果仍不明确时转人工核实,不自动重发报单或撤单。
订单视图包含同一账户其他客户端的记录。策略应自行保存已提交或结果不明的订单号及业务关联;重启后先与账户快照核对,不能将所有 ALIVE 订单都当作本策略新订单,也不能将本地记录丢失解释为未下单。当前交易日范围和持久化边界见 交易数据。
错误与退出#
下单、撤单的参数或业务拒绝通常通过 TradingResult 报告,不自动重试;账户验证、运行故障和调用方式错误仍可能抛异常。异常应覆盖整个调用与更新循环,见 错误处理。
关闭 ExApi 不会撤销订单。如果策略退出时需要撤单,应在仍能正常交易时自行发起并跟踪结果;认证或连接已失效时,应通过期货公司客户端等渠道处理存量委托。