工程与管理交易策略工程教训

STS:一个策略从设计到上线的完整过程

6 月 23 日是单日 commit 最多的一天——24 个。大部分围绕一个策略:STS(Surge Trailing Short,放量追踪做空)。

这个策略的开发过程有一个反直觉的特点:前半段加功能,后半段删功能。


时间线

00:26  DI rank(趋势强度排行)完成
12:35  STS 设计文档定稿
13:27  设计评审:8 个实现前置条件
13:51  拓扑审计:调用图、不变量、死代码
16:13  Phase 1 实现
16:19  修复:最小名义金额 $5 → $30
16:35  删除 STOP_LOSS 退出
16:45  删除 TOTAL_LOSS_LIMIT 退出
16:49  修改 TOTAL_DURATION → 仅在盈利时退出
16:51  删除 TREND_REVERSAL 退出
16:55  删除 MAX_CYCLES 退出
17:04  新增 SUPPORT_LEVEL 退出
17:11  COIN_TREND_GATE 方向性阻止
18:41  STS v2:周期级趋势收割 + 两层支撑 + 重入
20:52  Owner-Safe Mutex:UUID token + Lua 原子释放

一天内:设计 → 实现 → 评审 → 删 5 个退出条件 → v2 重写 → 并发安全。


为什么删的比加的多

Phase 1 实现时,照着设计文档加了 6 个退出条件。实现完一跑,发现 5 个有问题:

  • STOP_LOSS:和网格策略的止损逻辑冲突,两个止损同时触发会重复平仓
  • TOTAL_LOSS_LIMIT:按总亏损限退出会过早平仓——亏损可能是暂时的
  • TREND_REVERSAL:STS 本身就是趋势追踪,趋势反转不应该退出(应该反过来做多)
  • MAX_CYCLES:人为限制网格周期数没有意义——只要还在赚钱就应该继续
  • TOTAL_DURATION 改条件:只在盈利时退出,亏损时不限时

最后只保留了 1 个退出条件(SUPPORT_LEVEL:价格接近 7 日支撑位且盈利时退出)加上修改后的 TOTAL_DURATION。

设计文档 + 实现 + 反思 = 更好的设计。文档的价值不是让你照着写,是让你写完之后发现哪些是多余的。


Owner-Safe Mutex

并发安全的关键设计:多个 STS 实例可能同时操作同一个标的。

-- Redis Lua 脚本
local token = ARGV[1]
local owner = redis.call('GET', KEYS[1])
if owner == false or owner == token then
  redis.call('SET', KEYS[1], token, 'PX', 30000)
  return 1  -- 获取锁成功
end
return 0  -- 被其他实例占用

Token 不是线程 ID,是 UUID——即使进程重启,token 也不会冲突。30 秒 TTL 防止死锁。

这和广告系统的竞价锁一样——同一个广告位的同一个 impression,不能有两个策略同时出价。


一句话

24 个 commit 里最有价值的是那 5 个 delete——删掉不需要的退出条件,比加 10 个新功能更需要判断力。