[+25, +50) 这个评分区间,4 次全错,平均亏 6.58%。
这是我在观潮的 82 条信号回填数据里发现的——不是随便翻到的,是跑完 9 维分析后,唯一一个胜率为零的区间。它不大不小,刚好卡在"看多"的门槛上,每次都信心不够但硬给方向,每次都错。
今天把它砍了。14 行代码。
这是观潮系列的第九篇。如果说前八篇是"造引擎",这一篇是引擎第一次用自己的数据改了自己。
管道通了
先说前置条件。上一篇《托管记》里,我在 Azure VM 上配了 cron 定时任务——每天北京时间 15:30(收盘后),自动扫描持仓 + 回填历史信号。
昨天第一次跑,信号回填超时了(积压量太大)。今天第二次:
[2026-03-30T07:30:01Z] Daily scan started ← UTC 07:30 = 北京 15:30
scan_market (holdings) ✅ OK
scan_market (watchlist) ✅ OK
update_signal_outcomes ✅ OK ← 21 秒
[2026-03-30T07:30:22Z] Daily scan completed
21 秒。全绿。管道通了。
这意味着观潮现在有了一个完整的数据管道:分析 → 记录信号 → 每天自动回填 5/10/20 日走势 → 计算胜率。不需要我手动操作,数据自己会长出来。
跑通后顺手修了一个 bug——两条信号的 20 日数据和 10 日完全一样,根因是 baostock 偶发返回重复日期的 K 线行。三层修复(数据源去重 + 回填层去重 + 日历天安全阀),清掉脏数据,干净了再分析。
52 条信号全景
82 条信号中,52 条已经有 5 日回填,27 条有 10 日回填。我从 9 个维度做了分析:
看空远比看多准:
| 方向 | 5d 胜率 | 样本 | 平均涨幅 |
|---|---|---|---|
| 看空 | 81.6% | 38 | -6.01% |
| 看多 | 50.0% | 10 | +2.42% |
看空信号接近八成准确。看多?抛硬币。
评分区间是关键:
| 区间 | 胜率 | 样本 | 平均涨幅 |
|---|---|---|---|
| [+75,+100) 强看多 | 66.7% | 3 | +9.96% |
| [+50,+75) 看多 | 100% | 3 | +6.89% |
| [+25,+50) 偏多 | 0% | 4 | -6.58% |
| [-50,-25) 偏空 | 88.9% | 9 | -4.43% |
| [-100,-75) 强看空 | 78.3% | 23 | -7.62% |
+50 以上,100% 正确。+25 到 +50,0% 正确。这不是样本不足——是这个区间本身有结构性问题。
死亡地带
[+25, +50) 这个区间,我称之为"死亡地带"。
翻车案例集中在这里:
- 威海广泰 +40 看多 → 5 日 -9.33%(mild_bear 体制,有冲突)
- 威海广泰 +45 看多 → 5 日 -9.33%(同上)
- 赛力斯 +32 看多 → 5 日 -5.67%(mild_bear 体制,有冲突)
- 赛力斯 +12 偏多 → 5 日 -4.85%(mild_bear 体制,有冲突)
全是熊市里的弱多信号。信心不够但硬给方向,每次都被打脸。
小龙虾看完数据后说了一句话,直接定了方案:
"不是扣分,是直接不给方向——熊市里的弱多就是陷阱。"
14 行手术
改动极小,逻辑极清晰:
规则 1:看多门槛从 +25 提高到 +50。
# 之前
if adjusted_score >= 25:
final_signal = "看多"
# 现在
if adjusted_score >= 50:
final_signal = "看多"
elif adjusted_score >= 25:
if regime in ("bear", "mild_bear"):
final_signal = "中性观望" # 熊市弱多 → 不给方向
else:
final_signal = "偏多"
规则 2:bear/mild_bear 体制下,+25~+50 强制归为中性观望。
不扣分,不调权——直接不给方向。三处信号判断(analyze_stock、scan_market、compare_stocks)同步修改,14 行代码,commit d34274b。
数据支撑写进了 docs/strategy-evolution.md——每次策略调整的完整证据链:数据 → 分析 → 结论 → 代码改动,不拍脑袋调参。
冲突可见化
分析数据的时候,还发现了一个"差异化被藏起来"的问题。
冲突检测是观潮的核心差异化——"技术面看多但主力在出货"这种矛盾信号,是散户最容易忽略的。但在 Dashboard 上,冲突只有一个金色呼吸框暗示"有矛盾",具体什么矛盾要点进详情才看得到。
我在 Dashboard 上只看到威海广泰一只股亮了金色框,其他有冲突的都没亮。
一查,原来 scan_market 只做技术分析,根本不调冲突检测函数。威海广泰亮金色框是因为之前点过它的详情面板,前端缓存了冲突数据。
两步修复:
后端:
scan_market的_score_one()加入轻量冲突检测——用 OBV 斜率代替 AKShare 资金流向,零额外网络请求,扫描耗时不增加前端:卡片底部新增一行琥珀色小字,直接显示冲突描述
差异化藏在详情面板里,等于没有差异化。一行琥珀色小字,让“技术面偏空但主力在吸筹”这种关键信息抬头可见。
闭环
回头看今天做的事:
部署 (cron) → 自动采集 (scan_market)
→ 自动回填 (update_signal_outcomes)
→ 数据分析 (9 维拆解)
→ 策略调优 (14 行代码)
→ 重新部署 (git push + systemctl restart)
这是观潮的第一次闭环进化。不是我凭经验调参,是数据告诉我 [+25, +50) 全错,然后小龙虾确认"该砍",然后代码改了,然后部署了。
跟 Skill Evolution 的理念一样:执行 → 产出 → 反馈 → 优化。只不过这次的"反馈"不是 Code Review,而是真金白银的 5 日涨跌幅。
忍住不动的 81.6%
砍掉死亡地带是容易的决定。更难的是忍住不动那些“看起来很好”的数据。
看空信号 81.6% 胜率,多漂亮的数字。直觉会说:既然看空这么准,为什么不加大看空权重?为什么不降低看空门槛?
小龙虾一句话打回来了:
“看空 81.6% 胜率要小心过拟合这段行情——这两周大盘本身偏弱,看空天然顺风。等牛市来了这个数字会回落。不要因为看空准就加大看空权重——策略要穿越周期。”
这段话比“砍掉死亡地带”的认知价值更高。砍掉 0% 胜率的区间是札实的、显而易见的策略改进;而忍住不动 81.6%,是对“过拟合 vs 穿越周期”的清醒判断。
同样的克制还有:“冲突+高分更准”这个发现(88.2% vs 72.2%)——直觉说得通,穿越矛盾的信号本身就经过了“压力测试”。但 17 个样本不够做决策,先标记追踪,攻到 50+ 再回顾。
做了的事和没做的事,同样重要。 52 条样本够砍掉 [+25,+50) 吗?够了——这是结构性问题,4/4 全错且全在熊市弱多场景。够开始加大看空权重吗?不够——还没经过牛市检验。
观潮系列
| 日期 | 标题 |
|---|---|
| 03-11 | 观潮诞生记 |
| 03-12 | 观潮进化记 |
| 03-13 | 观潮实战记 |
| 03-19 | 观潮验证记 |
| 03-20 | 观潮出海记 |
| 03-21 | 观潮锐评记 |
| 03-23 | 观潮自省记 |
| 03-29 | 观潮托管记 |
| 03-30 | 观潮淬火记 ← 本篇 |

