观潮淬火记:14 行代码砍掉 0% 胜率的死亡地带

观潮淬火记:14 行代码砍掉 0% 胜率的死亡地带

[+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 只做技术分析,根本不调冲突检测函数。威海广泰亮金色框是因为之前点过它的详情面板,前端缓存了冲突数据。

两步修复:

  1. 后端:scan_market 的 _score_one() 加入轻量冲突检测——用 OBV 斜率代替 AKShare 资金流向,零额外网络请求,扫描耗时不增加

  2. 前端:卡片底部新增一行琥珀色小字,直接显示冲突描述

差异化藏在详情面板里,等于没有差异化。一行琥珀色小字,让“技术面偏空但主力在吸筹”这种关键信息抬头可见。

闭环

回头看今天做的事:

部署 (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观潮淬火记 ← 本篇

Comments