期货量化目标仓和净持仓对不齐:天勤 TargetPosTask 与 pos 偏差排查

前言

国内期货趋势量化程序的典型链路是:天勤 TqApi 订阅螺纹钢 5 分钟 K 线,双均线算出「目标净持仓 5 手」这一交易信号,执行层调用 TargetPosTask.set_target_volume(5),task 在后续每次 api.wait_update() 里向期货公司发单、撤单、对价改价,柜台回报刷新到 get_position("SHFE.rb2510").pos(净持仓手数,正为多、负为空)。

排查实盘时,日志连续三天写着 target=5,但 pos 一直是 4 或 6,差一两手对不齐。第一反应往往是「均线算错」,更常见却是:部分成交、场上还有 status=="ALIVE" 的在途单、同一合约重复建了 task、或手写 insert_order 与 task 抢仓位。set_target_volume 当下并不成交,target 与 pos 短期不一致可以是正常现象。下面按天勤单例约束、调仓时序和排查顺序,说明怎么区分正常过渡与真故障。

一、先弄清几个名词

名称 是什么 偏差排查里怎么用
set_target_volume(n) 告诉 task 目标净持仓手数 只改意图,不立刻成交
get_position(symbol).pos 柜台确认的净持仓 真相,正负表示多空
TargetPosTask 天勤自动调仓类 在 wait_update 里执行
wait_update() 收行情与交易回报 不调就不会推进 task
get_order() 当前委托字典 看在途 ALIVE 单
volume_left 委托剩余未成交量 部分成交时大于 0
status 委托状态 ALIVE/FINISHED 是否在途
min_volume / max_volume 大单拆分参数 分批导致短期对不齐
offset_priority 开平顺序 平今失败时 pos 卡住

二、为什么 target 和 pos 可以短期不一致

TargetPosTask 文档写得很清楚:set_target_volume 当下不下单,下单撤单发生在之后的 wait_update()。因此下列情况属于正常现象:

  • 刚调用 set_target_volume(5),下一行就读 pos,仍是旧值。
  • 单子已报入交易所,部分成交,pos 在 4 与 5 之间。
  • 启用了 min_volume/max_volume 拆分,多帧 wait_update 才凑满目标。
  • 对价 price="ACTIVE" 追价时,旧单撤完新单未成交,中间有空窗。

需要告警的是:连续多个交易时段、无 ALIVE 单、无拒单,而 pos 仍与 target 差值不变。

三、天勤单例约束:重复建 task 会埋雷

target_pos_task.py 里 TargetPosTask 用单例元类:同一账户同一 symbol 只能有一个 task 实例。若用不同 price、offset_priority、min_volume 再建一次,会直接抛异常。

更隐蔽的是:同一合约既用 TargetPosTask,又手写 insert_order。文档明确禁止混用,否则 task 内部状态与柜台持仓错位,表现为 target 已改但 pos 长期不对,或莫名多出一笔委托。

排查时全局搜索该 symbol 是否只有一处 TargetPosTask,以及是否还有 insert_order 调用。

四、推荐排查顺序

  1. api.wait_update() 至少跑几帧,确认不是读太早。
  2. 读 pos = api.get_position(symbol).pos,记 target(自己维护或 task 日志)。
  3. 遍历 get_order(),筛 symbol 相同且 status == "ALIVE" 的项,看 volume_left 与 direction。
  4. 若有拒单,读 last_msg;开平、资金类错误会导致 task 停在某一手数。
  5. 查是否拆分模式:看创建 task 时的 max_volume。
  6. 多账户模式是否漏传 account 参数,导致读到别的账户持仓。

可落地的日志字段建议每次调仓记:bar_datetime、target、pos、alive_volume(在途剩余量之和)、delta=target-pos。

python 复制代码
def log_pos_gap(api, symbol, target, logger):
    api.wait_update()
    pos = api.get_position(symbol).pos
    alive = sum(
        o.volume_left for o in api.get_order().values()
        if o.instrument_id in symbol and o.status == "ALIVE"
    )
    logger.info(
        "pos_gap symbol=%s target=%s pos=%s alive_left=%s",
        symbol, target, pos, alive,
    )

五、纠偏与恢复

确认无在途单且 pos 仍不对时:

  • 以 get_position 为准重置本地 state,再 set_target_volume 到真实目标。
  • 若怀疑 task 内部错乱,进程级重启:停机、对账、重建 TargetPosTask(改创建参数必须重启,见发版边界)。
  • 不要连续狂调 set_target_volume 试图「催成交」,容易触发报单频率规则。

六、典型 last_msg 与偏差共存

last_msg 关键词 常见后果
资金不足 target 不变,pos 落后
非交易时段 夜盘衔接误发单
平今不足 空头减仓卡住
超过最大委托量 部分成交

拒单后若不读 last_msg,会误以为 task 还在正常追价。

七、模拟与实盘偏差对比

TqSim 往往更快对齐;实盘 ACTIVE 追价在波动大时可能反复撤单重报,gap 持续更久。应用同一套 log_pos_gap 在两种环境各跑一天,建立「正常 gap 持续时间」基线。

总结

期货量化里目标仓与净持仓短期不一致,多数是天勤 TargetPosTask 的正常过渡:调仓在 wait_update 里分步完成,部分成交和拆单都会拉长对齐时间。持续对不齐时,按 pos、在途 ALIVE 单、last_msg、是否混用 insert_order、是否重复建 task 这条链排查;日志里同时记 target 与 pos,比单看信号可靠得多。在 TqSim 或 TqKq 上把偏差场景跑一遍,再挂实盘,能少踩很多「以为程序坏了其实是成交慢」的坑。

FAQ

1)差 1 手要不要强行补单?

先看在途单和部分成交;无在途且拒单已排除,再 set_target_volume 一次即可,避免与 task 内部循环打架。

2)反手时偏差更大正常吗?

反手要先平后开,中间帧 pos 可能经过 0 或反向,属于正常,用日志看全过程。

3)模拟盘会对得更齐吗?

TqSim 成交模型更理想,实盘偏差更大;模拟对齐不能代表实盘一定对齐。

4)能否不用 TargetPosTask?

可以全程 insert_order,但要自己管开平和在途单,工作量更大。


本文基于天勤 TqSdk 公开 API 与源码整理,不构成投资建议。实盘前请在仿真环境验证。

相关推荐
进击的明明4 小时前
当AI开始“边想边干”:深入聊聊ReAct,以及如何用Python手搓一个简单Agent
python·面试·agent
全栈弄潮儿4 小时前
Python实战第8期:函数
后端·python·编程语言
卷无止境6 小时前
暗流与破局:AI时代的代码安全审查工具全景指南
后端·python
卷无止境9 小时前
一人公司的全栈选型指南:让 AI Agent 写得快,也让生意跑得稳
后端·python
滴啦嘟啦哒10 小时前
【具身笔记】【π0.5 微调与仿真】五、在MuJoCo中实现可视化仿真
笔记·python·vla·具身
全栈练习生11 小时前
AI安全与红队测试
python·ai
费曼学习法11 小时前
把「发送成功」当「送达」?我排查 IM 长消息被静默吞掉的完整过程
python·ai编程·自动化运维
鸽鸽程序猿11 小时前
【项目】【在线判题系统】b端题目增删改
开发语言·python
NetX行者11 小时前
Python 工程化入门:从脚本到规范的 Python 项目结构
网络·python
MasonYyp11 小时前
asyncio并发协程
python