Sequoia-X 项目研究站 · 反转信号
Sequoia-X 反转信号:洗盘、跌停反包与定增事件
另外三个策略走的是与突破相反的方向:涨停洗盘要求昨天刚涨停、今天放量收阴却守住昨收;上升趋势跌停要求在 MA20 高于 MA60 的多头结构里出现放量跌停;定增公告监控则完全离开 K 线,去拉东方财富的增发数据筛最近七天的定向增发。三者的数据来源不同,能不能离线复现也不一样,这决定了出问题时要查哪一环。
Sequoia-X 三套信号的横向对照
先看清哪些读本地库、哪些要走网络,再逐条看阈值。
| 策略类 | 核心判定 | 数据来源 | 最少 K 线 | webhook_key |
|---|---|---|---|---|
| LimitUpShakeout | 昨日涨停 + 今日收阴 + 今日放量 2 倍 + 不破昨收 | 本地 SQLite 日 K | 3 根 | shakeout |
| UptrendLimitDown | 昨日 MA20 > MA60 + 今日跌停 + 今日放量 2 倍 | 本地 SQLite 日 K | 60 根 | limit_down |
| PrivatePlacement | 最近 7 天内的定向增发公告 | akshare 东财增发接口(在线) | 不依赖 K 线 | private_placement |
| 能否离线复现 | 前两个只要本地库里有数据就能重算 | — | — | 第三个每次都要联网取公告 |
| 命中波动 | 前两个随行情与阈值变化,通常命中极少 | — | — | 第三个随公告发布节奏波动 |
| 异常处理 | 前两个单只失败写 warning 跳过 | — | — | 第三个接口报错时记 error 并返回空列表 |
Sequoia-X 涨停洗盘:四条件锁定「洗而不破」
sequoia_x/strategy/limit_up_shakeout.py,只看最近三根 K 线。
| 条件 | 源码判定 | 适用场景 | 注意点 |
|---|---|---|---|
| 昨日涨停 | prev1["close"] >= prev2["close"] × 1.095 | 识别前一日的涨停 | 用 9.5% 作为近似阈值,不是按板块分别取 10%/20%/30% 的上限 |
| 今日收阴 | today["close"] < today["open"] | 确认出现回调 | 只看实体方向,不限制跌幅远近 |
| 今日放量 | today["volume"] > prev1["volume"] × 2.0 | 确认换手充分 | 基准是「昨日成交量」而非均量,与另一个跌停策略的口径不同 |
| 支撑不破 | today["low"] >= prev1["close"] | 要求不跌穿昨日收盘价 | 这是「洗盘」与「破位」的分界条件,也是四个条件里最严的一条 |
| 最小样本 | 少于 3 根 K 线跳过 | 理论上门槛最低的策略 | 虽然只要 3 根,但四条件同时成立的标的通常很少 |
| 读取方式 | df.iloc[-3] / [-2] / [-1] 取三行 | 定位「前日 / 昨日 / 今日」 | 前提是数据已更新到「今日」,否则这里的三根是历史的前三根 |
Sequoia-X 上升趋势跌停:先确认多头结构
sequoia_x/strategy/uptrend_limit_down.py,需要 60 日均线,所以最小样本是 60 根。
| 条件 | 源码判定 | 适用场景 | 注意点 |
|---|---|---|---|
| 趋势前提 | 昨日 ma20 > ma60 | 只在这种结构里找跌停 | 用昨日均线而非今日,避免当日暴跌污染均线判断 |
| 跌停 | today["close"] <= prev["close"] × 0.905 | 捕捉当日大幅下挫 | 同样是 9.5% 的近似口径,与涨停洗盘对称 |
| 放量 | today["volume"] > today["vol_ma20"] × 2.0 | 要求放量而非缩量下跌 | 这里用 20 日均量做基准,与涨停洗盘用昨日量不同,改阈值时别串了 |
| NaN 处理 | 均线为 NaN 时 continue 跳过该股 | 数据不足 60 根时 | 不会报错,只是静默跳过;命中为零时先确认库里的历史长度 |
| 最小样本 | 少于 60 根直接跳过 | 四个策略里门槛最高的之一 | 默认起始日期为 2024-01-01,新上市标的可能长期不满足 |
| 输出 | 返回命中代码列表并记日志 | → 飞书卡片推送 | 源码未做额外排序,清单顺序来自本地库遍历 |
Sequoia-X 定增监控:不走本地 K 线的策略
sequoia_x/strategy/private_placement.py,逻辑是「取公告 → 过滤 → 取最近 7 天 → 去重」。
| 步骤 | 源码实现 | 适用场景 | 注意点 |
|---|---|---|---|
| 取数 | akshare.stock_qbzf_em()(东方财富-全部增发) | 需要全量增发公告清单 | 策略里只有它依赖外部在线接口,接口不可用时记 error 并返回空列表 |
| 只看定向增发 | 筛 发行方式 == "定向增发" | 排除公开增发 | 字段名与取值依赖接口返回结构,akshare 版本变化时需复核 |
| 时间窗口 | _LOOKBACK_DAYS = 7,取发行日期在最近 7 天内的记录 | 覆盖一周内新公告 | 按「发行日期」而非「公告日期」过滤,两者口径可能不同 |
| 排序 | 按发行日期降序(最新的在前) | 推送时先看最新公告 | 异常日期会被 dropna 剔除 |
| 代码提取 | 正则 (\d{6}) 从「股票代码」列抓 6 位数字 | 去掉可能的市场前缀 | 抓不到 6 位数字的记录被丢弃 |
| 去重 | 同一只股票多次定增只保留首次出现 | 避免同一标的重复推送 | 去重后的顺序仍按发行日期降序 |
stock_daily,也不要求任何 K 线长度。所以即使你还没回填数据,它也可能有输出;反过来,本地库很新也不会让它命中更多。排查时先分清这条差异。Sequoia-X 的 README 写 6 个、代码里有 7 个
这是仓库当前真实存在的文档与代码差异,买账前先知道。
| 位置 | 写的是什么 | 适用场景 | 注意点 |
|---|---|---|---|
| README 内置策略表 | 列出 6 个策略(海龟、均线放量、高窄旗形、涨停洗盘、上升趋势跌停、RPS) | 快速了解项目能力 | 表格里没有定增公告策略 |
main.py 策略列表 | 实例化并注册 7 个策略,含 PrivatePlacementStrategy | 看实际会跑哪些 | 导入区与列表区两处都能看到它 |
| 策略目录 | sequoia_x/strategy/ 下有 7 个策略文件 + base.py | 核对策略总数 | 以文件与 main.py 为准,README 表可能是遗留内容 |
| 推送配置示例 | .env.example 里给出 7 个 STRATEGY_WEBHOOK_* 键 | 配置分群路由 | 含 PRIVATE_PLACEMENT,与代码的 7 个策略一致 |
| 影响 | 只读 README 的人会以为只有 6 个策略 | 评估能力或做二次开发时 | 本站按代码口径为准,并在页面上明示差异 |
Sequoia-X 策略的数据依赖与复现难度
决定「今天没结果」到底是行情原因还是数据原因。
| 策略 | 依赖本地 SQLite | 依赖外部接口 | 离线可复现 | 排查方向 |
|---|---|---|---|---|
| 涨停洗盘 | 是(最近 3 根) | 否 | 可以 | 先看数据是否更新到当日 |
| 上升趋势跌停 | 是(≥60 根) | 否 | 可以 | 看历史长度是否满足 60 根 |
| 定增监控 | 否 | 是(akshare 增发接口) | 不可以 | 看日志里是否有取数失败 |
| 突破类(对照) | 是 | 海龟策略会额外连 baostock 查市值 | 基本可以 | 市值查询失败会影响排序 |
| 推送环节 | 否 | 是(飞书 Webhook) | 不可以 | 看 code != 0 与 ERROR 日志 |
| 共同前提 | 库中存在该标的的行记录 | — | — | 未上市或长期停牌标的可能不在清单里 |
Sequoia-X 源码逐条核对:三套条件的真实判定
以下为 Sequoia-X 在 commit 444c0db 的原样摘录(只删掉日志与异常处理)。反转类信号的判定比突破类更依赖「相邻几根 K 线的关系」,逐行看比看结论更有用。
1. 涨停洗盘:用昨日涨停、今日放量收阴但不破昨收来定义「洗而不破」
# sequoia_x/strategy/limit_up_shakeout.py (_MIN_BARS = 3)
prev2, prev1, today = df.iloc[-3], df.iloc[-2], df.iloc[-1] # 前日 / 昨日 / 今日
limit_up_yesterday = prev1["close"] >= prev2["close"] * 1.095 # 条件1 昨日涨停(固定 9.5% 系数)
bearish_today = today["close"] < today["open"] # 条件2 今日收阴
volume_surge = today["volume"] > prev1["volume"] * 2.0 # 条件3 今日量 > 昨日量的 2 倍
support_hold = today["low"] >= prev1["close"] # 条件4 今日最低价不破昨日收盘价
两个容易被忽略的点:①1.095 是固定系数,ST 股 5% 的涨停永远达不到,主板 10%、创业板与科创板 20%、北交所 30% 的涨停都能满足;②条件 4 要求今日最低价不低于昨日涨停后的收盘价,也就是今日基本不能跌破涨停价,再叠加「今日阴线」,等于要求高开回落但守住关键价位——这解释了它为什么长期低频。
2. 上升趋势跌停:趋势判断用的是昨日均线
# sequoia_x/strategy/uptrend_limit_down.py (_MIN_BARS = 60)
df["ma20"] = df["close"].rolling(20).mean()
df["ma60"] = df["close"].rolling(60).mean()
df["vol_ma20"] = df["volume"].rolling(20).mean()
prev, today = df.iloc[-2], df.iloc[-1]
uptrend = prev["ma20"] > prev["ma60"] # 条件1 昨日 MA20 > MA60
limit_down = today["close"] <= prev["close"] * 0.905 # 条件2 今日跌幅 ≥ 9.5%
volume_surge = today["volume"] > today["vol_ma20"] * 2.0 # 条件3 今日量 > 20 日均量 2 倍
同样是 0.905 固定系数:ST 股 5% 跌停不满足,一字跌停因成交极小也几乎不可能满足 2 倍放量——所以它抓的其实是「放量下跌 / 打开跌停的错杀」,而不是「跌停」这个字面意思。
3. 定增监控:不读本地库的那一个策略
# sequoia_x/strategy/private_placement.py
df = ak.stock_qbzf_em() # akshare 东方财富「全部增发」接口
df = df[df["发行方式"] == "定向增发"] # 剔除公开增发
df["发行日期"] = pd.to_datetime(df["发行日期"], errors="coerce")
df = df[df["发行日期"].dt.date >= today - timedelta(days=7)] # 只留最近 7 天
df = df.sort_values("发行日期", ascending=False) # 新的在前
symbols = df["股票代码"].astype(str).str.extract(r"(\d{6})")[0].dropna().tolist()
它不查行情、不做技术判定,只按公告日期过滤后去重;接口报错时源码会记录 ERROR 并返回空清单——空清单与「今天没有新公告」在推送层看不出区别,排查时要看日志而不是看群里有没有消息。
Sequoia-X 这三类信号为什么经常整天没有结果
零命中往往不是程序坏了,而是条件本身很苛刻。下面把「苛刻在哪」拆开,方便你判断该等、该调、还是该换策略。
| 策略 | 苛刻点(源码依据) | 典型命中量级 | 看到零命中该怎么办 |
|---|---|---|---|
| 涨停洗盘 | 昨日必须涨停(≥9.5%),今日必须收阴、放量到昨日 2 倍、且最低价不破昨收。四条同时成立的日子本来就少 | 多数交易日为 0,偶发 1–3 只 | 先确认库内日期正确;再确认当日是否有涨停股(可用 SQL 数一遍),有涨停股而没有结果属正常 |
| 上升趋势跌停 | 要求昨日 MA20 > MA60(中期多头),今日跌幅 ≥9.5% 且成交量放大到 20 日均量 2 倍 | 0–2 只,极端行情日可能集中出现 | 单日零命中正常;若连续多日有跌停股但零命中,检查是否为一字跌停(成交太小) |
| 定增监控 | 只保留发行日期在最近 7 天内的定向增发公告,并剔除公开增发;不读行情 | 取决于公告节奏,常见 0–5 只 | 无公告时源码直接返回空清单;接口失败同样返回空清单但会写 ERROR 日志 |
| 跨策略共性 | 命中为零时 main.py 只记一行日志、不推送,所以飞书安静不等于程序没跑 | — | 判断「是否正常」看日志里每个策略的结果行与数据日期,不看群里有没有卡片 |
把「今天该不该有信号」变成一次可执行的检查
-- 1) 数据是否到目标交易日
SELECT MAX(date) FROM stock_daily;
-- 2) 当天到底有没有涨停股(后复权价按 9.5% 近似判定,与源码口径一致)
SELECT COUNT(*) FROM (
SELECT symbol, date, close,
LAG(close) OVER (PARTITION BY symbol ORDER BY date) AS prev_close
FROM stock_daily
) t JOIN (SELECT MAX(date) d FROM stock_daily) m ON t.date = m.d
WHERE t.prev_close IS NOT NULL AND t.close >= t.prev_close * 1.095;
第 2 条查询返回 0,说明当天市场本来就没有涨停股,涨停洗盘零命中完全合理;返回几十只而策略零命中,才需要回头看条件 3(昨日量 2 倍)与条件 4(不破昨收)。这套查询是本站按源码口径写的教程扩展,不是官方提供。
反转类信号的失效场景
「洗盘」和「出货」在 K 线上可能长得一样。下表把容易误读的情形与检查方法列出来。
| 场景 | 机制(对应代码) | 怎么发现 | 怎么处理 |
|---|---|---|---|
| 涨停洗盘其实是高位出货 | 四条条件不判断位置:连板后的放量收阴同样满足「不破昨收」 | 把命中票按此前区间涨幅分组,看后续 5 日表现差异 | 先确认能不能拿到换手率与涨跌停价(库内没有),再决定是否加位置过滤 |
| 跌停反包失败、趋势已坏 | 趋势条件用的是昨日 MA20 > MA60,今日跌停本身就可能把结构打坏 | 对命中票统计后续 20 日是否跌破前低 | 把它当「异常下跌观察名单」而非抄底信号,单独设止损规则(项目不含止损) |
| ST 股的 5% 涨跌停被系统性漏掉 | 涨停与跌停判定都用固定系数 1.095 / 0.905,ST 的 5% 永远达不到 | 列一只近期 5% 涨停的 ST 股,确认它从未出现在结果里 | 这是硬编码阈值的结果,要覆盖 ST 必须改系数并自行承担风险(ST 本身有额外交易限制) |
| 一字跌停不会被命中 | 需要成交量放大到 20 日均量 2 倍,一字跌停成交极小 | 对跌停股当日成交量与均量做比值分布 | 理解它抓的是「放量下跌/开板」,而不是所有跌停 |
| 把接口失败读成「今天没公告」 | akshare 接口异常时 run() 记录 ERROR 并返回空清单,推送层无法区分 | 在日志里搜该策略的 ERROR 行 | 把日志当成第一事实来源;必要时单独写脚本定期检查公告接口可用性 |
| 公告 ≠ 利好,定增不等于股价支撑 | 该策略只做「最近 7 天有定向增发公告」的信息过滤,不含任何基本面判断 | 把同期命中票的后续表现与基准对比 | 把它定位成信息提醒而不是选股结论;公告解读需要另做功课 |
| 后复权价与实时价错配 | 库内是后复权序列,涨跌幅在除权日附近与行情软件显示的口径不同 | 抽一只近期除权的股票对照两处价格 | 库内只用于相对判定;盘口与委托必须以实时行情为准 |
反转与事件类常见问题
为什么涨停阈值写 9.5%?
源码用的是 1.095 的近似系数来判断涨停与跌停,并没有按主板 10%、创业板与科创板 20%、北交所 30% 分别取上限。这是代码的实际实现,具体阈值以仓库源码为准。
定增监控会依赖本地数据吗?
不会。它调用 akshare 的增发接口拿公告数据,再按发行方式与发行日期过滤,不读本地 SQLite。所以未回填数据时它也可能有输出,反之本地库再新也不会让它多命中。
两个策略的「放量 2 倍」基准一样吗?
不一样。涨停洗盘用的是「昨日成交量 × 2」,上升趋势跌停用的是「20 日均量 × 2」。口径不同意味着同一只股票在两个策略里的放量判定结果可能相反,调整参数时要分别确认。
Sequoia-X 跳过的股票会在日志里体现吗?
会。逐只计算时若抛异常会写一条带股票代码的 WARNING 日志;均线为 NaN 或 K 线不足的情况则直接跳过、不出现在结果里。命中为零时建议同时检查 warning 行与本地数据长度。
Sequoia-X 这三个策略适合什么场景?
前两个偏「趋势中的异常下跌」识别,适合把候选清单交给人工判断;第三个是公告事件监控,更接近信息提醒。三者都只是候选清单,项目不提供任何买点或价格预测,也不对策略有效性作承诺。
以后会新增策略吗?
源码结构允许:新增一个继承 BaseStrategy 的类并注册到 main.py 的策略列表即可,.env.example 的 webhook 键也按同样数量给出。具体接法见新增策略页。
固定系数 1.095 会让哪些股票被漏掉?
主要是不受 10% 限制的品种:ST/*ST 股涨停幅度为 5%,涨幅永远达不到 9.5%,因此不会被识别为「昨日涨停」;一字跌停与成交极小的跌停也不会满足 2 倍放量。这是源码的硬编码行为,若要按板块区分涨跌幅上限(主板 10%、创业板与科创板 20%、北交所 30%),必须自行改判定并重新验证,具体口径以官方源码为准。
怎么判断「今天零命中」是行情原因还是程序问题?
按本页的三步走:先查 SELECT MAX(date) FROM stock_daily 确认数据到目标交易日;再用涨停股计数查询确认当天市场有没有符合条件的标的;最后看日志里该策略是否有结果行与 ERROR 行。三步都正常而命中为零,就是行情匹配不上,源码在这种情况下只记日志、不推送,属于预期行为。