
调用insert_order只表示程序创建了一份报单请求并立即拿到对应的委托引用真正发送发生在下一次wait_update。此后订单可能存活、部分成交、全部成交、撤销或被拒绝字段会随交易回报持续变化。因此“函数没有报错”绝不能等同于“已经成交”可靠的程序必须完整观察委托、成交和持仓三条状态链。用模拟账户观察第一笔委托下面示例使用本地模拟账户合约和价格只用于展示生命周期。运行前应确认合约仍可交易并先在模拟环境核对方向、开平和手数。import math import os from tqsdk import TqApi, TqAuth, TqSim symbol SHFE.rb2610 sim TqSim(init_balance100000) api TqApi( sim, authTqAuth(os.environ[TQ_USER], os.environ[TQ_PASSWORD]), ) quote api.get_quote(symbol) try: while not math.isfinite(quote.ask_price1): api.wait_update() order api.insert_order( symbolsymbol, directionBUY, offsetOPEN, volume1, limit_pricequote.ask_price1, ) while order.status ! FINISHED: api.wait_update() if api.is_changing(order): print(order.status, order.volume_left, order.last_msg) finally: api.close()代码先等待卖一价有效再创建限价买入开仓委托。insert_order返回后订单尚未因为这一行自动成交循环继续调用wait_update请求才会发送后续回报也才能更新对象。示例只说明观察方式不代表对价委托在真实市场一定成交。ALIVE 与 FINISHED 不是成交与未成交的简单二分status为ALIVE表示委托仍有效FINISHED表示委托生命周期已经结束。结束原因可能是全部成交也可能是撤销、拒绝或其他终止情况所以还要结合volume_orign、volume_left、last_msg和成交记录判断结果。volume_orign是原始报单手数字段名按接口保留了这个拼写volume_left是未成交手数。两者之差可以辅助理解已成交数量但正式回查还应读取该委托对应的trade_records确认每笔成交的价格、手数和时间。部分成交时订单仍可能处于ALIVE未成交手数继续留在市场。程序不能看到一次成交就认为整个任务完成也不能因为最终状态是FINISHED就默认全部成交。每个动作都应有明确完成条件例如“订单结束且未成交手数为零”或者“撤单完成后重新评估剩余目标”。成交记录与持仓变化要分别确认成交记录回答订单实际成交了什么持仓对象回答账户当前持有什么。两者通常相关但更新时间和业务含义不同。程序可以在订单变化时输出该订单的成交记录再在持仓变化时核对最终仓位。position sim.get_position(symbol) while True: api.wait_update() if api.is_changing(order): for trade in order.trade_records.values(): print(成交:, trade.trade_id, trade.price, trade.volume) if api.is_changing(position): print(当前净持仓:, position.pos) if order.status FINISHED: break成交集合在循环中可能被多次读取日志层应按成交标识去重避免同一笔成交重复写入。持仓达到预期也不能替代订单核对因为可能仍有活动委托稍后成交会再次改变仓位。一份实用的订单状态记录至少包含订单标识、合约、方向、开平、原始手数、剩余手数、状态和最后状态信息。每次只在对象变化时追加记录就能重建订单从创建到结束的路径。若还记录触发它的业务意图标识后续可以区分“同一个信号产生的重试”和“两个真正独立的信号”。def order_snapshot(order): return { order_id: order.order_id, symbol: f{order.exchange_id}.{order.instrument_id}, direction: order.direction, offset: order.offset, volume: order.volume_orign, volume_left: order.volume_left, status: order.status, message: order.last_msg, }这只是普通字典快照后续订单更新不会修改已经保存的记录。日志中不要写认证信息也不需要把整个账户对象倾倒出来。保持字段最小且足以定位反而更容易比较不同订单。订单结束后还可以做一次机械对账原始手数减去剩余手数是否与该订单成交记录的手数总和一致成交方向和开平是否与原始请求一致最终持仓变化能否由这些成交解释。任何一项不一致都不应靠再次下单“修正”而应先保留现场并排查。撤单同样需要事件循环完成调用cancel_order是提交撤销请求不是同步删除订单。撤销后仍要继续wait_update等待订单状态更新。若撤单与成交在时间上接近仍可能出现成交因此撤单完成后应重新读取成交和持仓而不是直接恢复为“未交易”状态。if order.status ALIVE and should_cancel(): api.cancel_order(order) while order.status ALIVE: api.wait_update()should_cancel是策略自己的条件占位。它需要说明超时、价格偏离或信号失效中的哪一种情况触发撤单。只写“没成交就撤”会因为更新频率不同产生完全不同的行为。撤单之后若要重新下单应先计算剩余目标不能照抄原始手数。已经部分成交的订单若按原手数重发最终持仓就会超过预期。最容易导致重复下单的几个误区第一个误区是信号每次行情变化都为真于是每轮都调用insert_order。应记录当前活动委托和目标状态同一业务意图尚未结束时不创建第二份订单。第二个误区是程序重启后只看内存变量。内存中的订单引用已经丢失但账户里可能仍有活动委托。启动时应查询账户现有委托、成交和持仓重建业务状态后再允许新动作。第三个误区是只打印异常文字不记录订单标识、合约、方向、开平、手数和状态。没有这些定位信息看到“报单失败”也无法知道是哪一次请求、失败后是否产生了持仓。第四个误区是把行情价格当作成交价格写入统计。成交必须来自 Trade 回报行情只能解释当时市场背景。否则滑点和未成交会从结果中消失。第五个误区是忽略交易所与品种的开平规则只根据净仓拼接OPEN或CLOSE。今昨仓、方向和账户实际持仓都会影响合法动作。规则尚未处理清楚时应先使用模拟环境观察不要让一条看似通用的下单函数自动猜测全部业务含义。第六个误区是捕获所有异常后继续循环。若订单创建、撤销或状态解析出现无法解释的错误程序应停止新增动作并输出定位信息。无条件继续可能让内部状态与账户事实逐渐分离最后只能看到持仓错误却找不到最早的失败点。委托生命周期清单下单前确认具体合约、方向、开平、手数、价格和账户环境。insert_order后持续wait_update不把返回引用当成成交证明。同时检查状态、原始手数、剩余手数、状态信息和成交记录。撤单后等待回报并按实际成交重新计算剩余目标。程序重启先恢复活动委托与持仓所有日志保留订单定位信息。把委托当作一个跨越多次事件更新的生命周期交易代码才会从“一行下单”变成可解释的状态管理。每一次仓位变化都能追溯到具体订单和成交才是进入更复杂策略前真正需要的基础。