前言
国内期货策略在 Linux 服务器上 7×24 跑,难免遇到:机器重启、发版替换、内存 OOM 被系统杀掉、网络闪断后进程退出。进程一死,内存里的变量全没:你记的target_vol=3、网格档位、上一根 K 线是否已处理、emergency标志,都会消失。若新进程启动后默认“从空仓开始”,再次set_target_volume(3),而期货公司柜台里其实已有 2 手,就会变成超仓、重复开仓,或与人工作出的仓位冲突。
可靠原则只有一句:柜台是真相同期,本地文件是辅助。天勤在连接后通过wait_update()更新get_position、get_order、get_account;业务状态(上次处理到哪根 bar)需自己用 JSON/SQLite 持久化。下面说明崩溃重启后恢复顺序、各英文字段指什么、如何避免重复开仓。
一、什么必须从柜台读、什么可以写文件
| 数据 | 天勤接口 | 重启后怎么用 |
|---|---|---|
| 真实净持仓 | get_position(symbol).pos | 真相,优先于文件 |
| 在途委托 | get_order(),status 为 ALIVE | 决定要不要撤单或同步 task |
| 资金 | get_account() | 风控与能否加仓 |
| 上次处理的 bar 时间 | 自建 state JSON | 防同一根 K 线重复下单 |
| 策略阶段机、网格 level | 文件 | 辅助,与 pos 冲突以 pos 为准 |
| emergency 标志 | 文件(自定字段) | true 时应先平仓再交易 |
ALIVE是委托 status 的一种,表示单还在交易所有效、未完全成交也未撤完。FINISHED表示该委托生命周期结束。
二、推荐启动恢复流程(按顺序,勿跳步)
api=make_api()state=load_state()# 本地 JSON,可能过时for_inrange(20):api.wait_update()# 先收几帧,让 position/order 有数forsinsymbols:pos=api.get_position(s)actual=pos.pos state.setdefault("targets",{})[s]=actual tasks[s].set_target_volume(actual)# 让 TargetPosTask 与柜台一致# 打印 ALIVE 单,决定是否人工处理foroid,oinapi.get_order().items():ifo.status=="ALIVE":log("alive order",oid,o.volume_left,o.last_msg)# 再进入主循环,按 datetime 产生新信号含义:
- 不要一启动就按文件里的
target=3去 set,要先wait_update读pos.pos。 set_target_volume(actual)是让天勤内部 task 认为“目标就是当前仓”,避免它立刻再报一单去追文件里的旧目标。- 若文件写
target=3但actual=1,必须打告警,以 1 为起点,由新信号决定是否加到 3,勿直接 set 3。
三、持久化写什么、何时写
建议在 K 线datetime变(新 bar)且信号逻辑跑完后写文件:
state["last_bar_dt"]=str(kl.iloc[-2].datetime)state["targets"][symbol]=intended_target# 策略意图,非柜台真相state["version"]=CONFIG_VERSION save_state_atomic(state)# 先写 .tmp 再 rename,防写到一半崩溃datetime是 K 线表上的业务时间,由行情服务写入。intended_target是策略想达到的目标;柜台pos可能因部分成交滞后,两者不一致时要走对账逻辑(见部分成交专题)。
四、挂单与重复 insert_order
重启后get_order()可能仍有ALIVE单。若策略不应保留挂单(例如只用 task 调仓),不要盲再insert_order,应让set_target_volume驱动 task 撤旧挂新,或按团队规范显式撤单。同一合约勿 task 与insert_order混用。
五、emergency 与崩溃交叉
若崩溃前已进入 emergency(当日回撤超限),文件应持久化emergency=true。重启后应先读 position:有仓则继续平到 0,且禁止信号层新开仓,直到人工复位emergency=false。否则会出现“风控已触发却又开仓”的严重事故。
六、多策略、多环境
- 多策略:每策略独立 state 文件目录,勿共用一个
targets字典。 TqKq与TqSim持仓体系不同,重启后make_api()的 MODE 必须与崩溃前一致,勿 sim 状态文件配 live API。- 指标缓存可持久化,或启动后 warm-up 重算(见冷启动专题),勿用旧文件里的指标值当真相。
七、演练纳入发布流程
模拟盘对运行中进程kill -9,再用 systemd 拉起,检查:是否重复开仓、ALIVE 单是否异常、日志里 pos 与 APP 是否一致。通过后再上实盘。这比真事故时改代码成本低得多。
总结
崩溃重启之后最重要的不是尽快恢复下单,而是先恢复对‘当前真实状态’的认识:柜台里到底有多少 pos、有哪些 ALIVE 单、资金是否可继续承受。可靠的顺序是 wait_update 先把真相读回来,再把任务目标同步到实际 pos,把文件中的 last_bar_dt 或策略阶段机作为辅助证据,而不是用旧记忆直接开新仓。还要处理挂单:不能在不清楚交易所状态时盲目重复 insert_order,而是让 task/对账逻辑接管。若崩溃前已进入 emergency,也要确保紧急状态能被持久化并在重启后继续执行“先平后启”。当这些边界写清楚,系统才能从异常中平稳接续,不会因为重启把正常风险再重跑一遍。
FAQ
1)文件写 3、position 是 0?
以 position 为准,改文件并查是否人工平仓或拒单。
2)重启后指标全是 nan?
新 session 要 warm-up,或从文件恢复足够长的 last_bar_dt 后重算。
3)underlying_symbol 换月变了?
重启后重新读主连 quote 的 underlying_symbol,更新 trade_sym 与 task 订阅。
4)容器自动重启太频繁?
先修 OOM 或断线,勿让恢复逻辑在仓位未查清时高频 set。
风险提示
以上内容用于恢复流程参考,不构成投资建议。