K1策略升级:新增市场状态感知+动态过滤门

背景

K1斜度轮动策略跑了有一阵子了——39只ETF候选池,每天算斜度排名,持Top1,满仓30万。信号灯(绿灯/黄灯满仓,红灯禁开)、大盘单日跌超3%禁买、5%回撤止损,这些规则稳了好些天。

但有个问题从一开始就有:策略对市场整体冷暖没有感知。红灯只是沪深300跌破MA120这一个条件,但如果市场整体在阴跌通道里,个别ETF斜度好看,策略还是会开仓——买了以后大盘继续跌,持仓跟着吃面。这轮行情下来尤其明显。

方案

这次给K1加了三个新东西,核心思路就一句:让策略先抬头看天,再低头挑标的

1. 市场状态判断(每天算一次,缓存复用)

取39只候选池每只过去60日的收盘收益(含当日),算中位数。同时取沪深300同期60日收益做参照。判定逻辑:

本质上就是两问:池子整体赚不赚钱?有没有大幅跑输大盘?两个条件都满足才算好市场。这个计算一天只做一次,结果缓存进state,当天后续调用直接复用,不重复算。

2. 状态切换过滤(Top1买入前拦截)

市场状态决定过滤门的松紧:

其中 accel = 今日25日ln斜率 − 前25日ln斜率(二阶导,判断趋势是否还在加速而不是减速)。ret5 = 今收/5日前收 − 1,ret10 同理。

3. 拦截后不补位

这是跟冷却顺延的关键区别,也是最容易搞混的地方:

逻辑很简单:熊市里强行买第二名,多半是换个姿势亏钱。冷却顺延是"这个标的暂时不能买,换一个同类";状态拦截是"这个市场不该买,管你第几名都不买"。

实测

3次完整运行,真实行情数据,39/39拉取全成功:

市场状态: 🔴 坏市场 | 池中位60日=-6.81% | 沪深300同期=-4.48% | 严格档

池子里60日跌了6.81%,沪深300跌了4.48%,判定坏市场。第2、3次运行命中缓存,当天只算一次——这个缓存逻辑也跑通了。今天本身就是红灯禁开仓+坏市场两道锁,策略不开仓符合预期。

顺便修了个老bug

第1次跑的时候发现一个问题:"当前持仓"显示空白,但"今日操作"却说持有。排查下来定位到原代码的坑:持仓表格只在排名前10里找持仓。港股通创新药(sz159570)当天排名第20,直接找不到了。

这个bug应该从策略第一天就存在,只不过之前持仓一直在Top10里没触发。修复很简单——持仓表格改为直接遍历实际持仓,不再依赖排名。修完以后再跑:

当前持仓(Top1):
排名  代码      名称        得分          股数    成本    现价    浮盈    回撤
19    159570  港股通创新药  -0.000025  17,000  1.4695 1.4620  -0.51%    0.51%

今日操作:
📌 持有:港股通创新药(sz159570) → 不变

部署

新逻辑已生效,state完整性验证通过——持仓完好、冷却天数正确、market_state缓存保留。明早开盘cron(11:20/14:40)自动带新规则跑。

核心改动就加在开仓前的拦截逻辑里——k1_strategy.py 的排名循环找到Top1后、下单前,多一层判断:先看市场状态是松是严,再看accel和涨幅过不过档位。所有原有逻辑(信号灯、MA30/MA20筛选、5%止损、冷却顺延)完全不动。


这事值得记一笔,因为它是K1第一次有了"整体市场观"——不再闷头看排名,会先判断环境再决定要不要伸手。说起来简单,做起来也就几十行代码,但这个决策框架的加入,让策略从"纯动量"往"动量+风控"靠了一步。