agentsclimarketplace

Invest a gap scan

Skill Veblin/invest-skills/skills/invest-a-gap-scan

大A研究助手,仅作为学习不作为决策的依据

Install
npx -y skills add Veblin/invest-skills --skill invest-a-gap-scan

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

跳空缺口扫描 — 向上缺口 + MA60 上方 + 未回补,指数成分股池(沪深300+中证A500+科创50)

SKILL.md

10.7 KB, as published. Nobody here has run it

invest-a-gap-scan 跳空缺口扫描

概述

扫描沪深300 + 中证A500 + 科创50 成分股并集(去重后约 450~500 只),找出同时满足:

  1. 近 60 个交易日内有向上跳空缺口low[i] > high[i-1],幅度 >= 1%)
  2. 自缺口日起至今收盘始终在 MA60 上方close[t] >= MA60[t] for all t >= gap_idx)
  3. 缺口形成后最低价从未回补至上沿及以下min(low[gap_idx+1:]) > gap_high

缺口定义

  gap_high  ─────── low[i]         (缺口上沿 = 跳空日最低价)
                      |
                   gap_pct = low[i] / high[i-1] - 1
                      |
  gap_low   ─────── high[i-1]      (缺口下沿 = 前日最高价)
  • 未回补: 缺口形成后所有交易日的最低价均 > 缺口上沿
  • 跨停牌: 缺口前一交易日为停牌日 -> 单独列出,不参与常规排序

筛选漏斗

478 只成分股经过多层筛选的典型分布:

478 只成分股
 |-- ~200 (43%)  无缺口          -- 60 日内无 low[i] > high[i-1]
 |-- ~110 (23%)  低于阈值        -- 有缺口但幅度 <1%
 |-- ~130 (27%)  MA60 破         -- 缺口后收盘跌破 MA60 (最大淘汰项)
 |--  ~17 (4%)   缺口回补        -- 最低价回补至上沿
 |--   ~6 (1%)   低流动性        -- 20 日均额 <1 亿
 |--   ~1        获取失败
-------------------------------------
     ~5 (1%)     命中

运行

基本用法

uv run python skills/invest-a-gap-scan/scripts/scan.py

参数表

参数默认说明
--universecsi300,a500,star50指数池,逗号分隔,支持空格
--gap-min-pct1.0缺口幅度阈值 %。low[i]/high[i-1]-1,<1% 归为"低于阈值"淘汰
--gap-lookback60缺口回溯交易日数。只搜索最近 N 根 K 线中的缺口
--gap-min-vol-ratio1.0缺口日成交额 / 20 日均额下限。1.0=不过滤,>1.0=要求放量
--min-avg-amount10000000020 日均额门槛(元)。<1 亿归为"低流动性"排除
--min-list-days60最少 K 线根数。不足的标的归为"上市不足"排除
--top30stdout 命中表格行数(md 报告始终全量)
--sourceauto数据源:auto(优先 Tushare)/tushare/baostock
--no-cacheoff强制刷新 K 线缓存 + 成分股缓存
--universe-limit N只扫前 N 只(开发调试用)
--no-save-reportoff不写入 reports/gap-scan/ 目录
--jsonoffstdout 输出 JSON 格式

示例

# 提高缺口阈值
uv run python skills/invest-a-gap-scan/scripts/scan.py --gap-min-pct 2.0

# 要求放量缺口
uv run python skills/invest-a-gap-scan/scripts/scan.py --gap-min-vol-ratio 1.5

# 强制 baostock (无 Tushare token)
uv run python skills/invest-a-gap-scan/scripts/scan.py --source baostock

# 调试: 只扫前 30 只
uv run python skills/invest-a-gap-scan/scripts/scan.py --universe-limit 30

# JSON 输出 (供脚本消费)
uv run python skills/invest-a-gap-scan/scripts/scan.py --json

# 全量刷新
uv run python skills/invest-a-gap-scan/scripts/scan.py --no-cache

算法详解

容忍规则 (Tolerance Rule)

缺口按从新到旧排序,遇到第一个满足全部条件的即命中:

for gap in qualified_gaps (newest first):
    if MA60 从未跌破 since gap_day:
        if gap 从未回补:
            if 量比 >= gap_min_vol_ratio (如启用):
                -> 命中
            else:
                -> 继续检查更老的缺口 (量比不够)
        else:
            -> 继续检查更老的缺口 (已回补)
    else:
        -> 继续检查更老的缺口 (MA60 已破)

不回退到已回补的缺口。这意味着:如果最新缺口已回补但更老的缺口未回补且 MA60 完好,会命中老缺口。

跨停牌缺口

停牌检测基于交易日历 + 日线数据对比:某个交易日在 trade_cal 中存在但该标的不在 daily_by_date 中 -> 标记为停牌。缺口前一交易日为停牌日 -> 归入 across_suspension_hits,不参与常规排序。

注意: 无 Tushare token 时使用自然日估算交易日历(Mon-Fri),此时跳过停牌检测以避免假期误判。

MA60 计算

使用 invest-a-stock 的 lib.technical.sma(60 日简单移动平均)。前 59 个位置 MA60 为 None,_check_ma60_streak 跳过 None 值。NaN close 触发 MA60 失败(利用 not (NaN >= MA60) 恒为 True 的特性)。

前复权 (QFQ)

  • Tushare 路径: 原始价格 x adj_factor / latest_adj_factor -> 以最新日为基准的前复权价格
  • Baostock 路径: adjustflag="2" 直接返回前复权价格,无需额外计算

两种路径的 QFQ 算法存在细微差异,因此缓存按数据源隔离 ({date}/{source}/{ts_code}.pkl)。

数据流

1. build_universe()        指数成分股并集 -> 去重 + ST/退市过滤 -> ~478 只
       |
2. create_source("auto")   有 Token -> TushareBulkSource (按日批量)
                          无 Token -> BaostockSource  (逐股查询)
       |
3. _fetch_trade_cal()      Tushare trade_cal API -> 真实交易日历
                           失败 -> 自然日估算 (Mon-Fri, 跳过停牌检测)
       |
4. kline_cache.load()      命中 -> 跳过拉取
                          未命中 -> fetch_daily_batch + build_stock_kline + cache.save
       |
5. detect_suspensions()    对比 trade_cal vs daily_by_date -> 停牌日期列表
       |
6. scan_all()              逐股: 流动性排除 -> 找缺口 -> 容忍规则 -> 命中/排除/未命中
       |
7. format_brief()          命中表 (缺口幅度降序) + 排除/未命中统计
   format_markdown_report() 详文档 -> reports/gap-scan/{yyyymmdd}.md

数据量

项目数量
交易日范围~180 自然日 (~120 交易日,gap_lookback + 59 bar 确保 MA60 全覆盖)
成分股~478 只 (去重后)
K 线行数~57,000 行
缓存磁盘~25 MB (477 个 pickle 文件)

性能

场景耗时说明
首次扫描 (Tushare)~4 min按日批量 API(~120 日),受限于 Tushare 限速
首次扫描 (Baostock)~5 min逐股查询,每只 ~0.6s,~120 日数据
同日二次扫描~7s全部缓存命中,仅做缺口算法
部分缓存命中~7s + 缺失部分拉取只拉取未命中标的

输出

stdout 摘要

========================================================================
  invest-a-gap-scan v0.2.1 -- 跳空缺口扫描
========================================================================
池构成: 沪深300(300) + 中证A500(500) + 科创50(50) -> 去重 478 只
数据源: baostock (前复权 adjustflag=2) (前复权)
覆盖率: 99.8% (477/478 有K线) | 命中: 5 只 | 跨停牌: 0 只
参数: 缺口>=1.0% | 回溯60日 | MA60 | 日均额>=1.00亿

排除: 获取失败 1 | 低流动性 6
未命中: 无缺口 207 | 低于阈值 111 | MA60破 131 | 缺口回补 17

| 代码 | 名称 | 指数 | 板块 | 缺口日 | 缺口% | 缺口区间 | 现价 | MA60 | MA60% | 距上沿% | 量比 | 20日均额 |
|-----|------|-----|-----|--------|------|---------|-----|------|------|--------|-----|---------|
| 300628.SZ | 亿联网络 | 300 | 创业板 | 20260715 | +4.62% | 33.96~35.53 | 36.71 | 35.64 | +3.00% | +3.32% | 2.15 | 4.40亿 |

命中表列说明

含义
代码/名称标的
指数所属指数 (300=沪深300, 500=A500, 50=科创50)
缺口日缺口形成日期
缺口%跳空幅度
缺口区间缺口下沿~上沿 (即回补位参考区间)
MA60%(现价 - MA60) / MA60,正值=MA60 上方
距上沿%(现价 - 缺口上沿) / 缺口上沿,正值=远离缺口,负值=已回补
量比缺口日成交额 / 20 日均额,>1=放量
20日均额近 20 日平均日成交额

详文档

reports/gap-scan/{yyyymmdd}.md: 全量命中表 + 逐股简析(含催化待查提示)+ 排除/未命中统计 + 运行参数。

缓存

位置

~/.local/share/investment/gap_scan_cache/
|-- universe_20260719.pkl          (成分股列表, 28 KB)
|-- 20260719/                       (按扫描日期)
|   |-- tushare/                    (按数据源隔离)
|   |   |-- 000001.SZ.pkl
|   |   `-- ...
|   `-- baostock/
|       |-- 000001.SZ.pkl
|       `-- ...
`-- 20260718/                       (历史缓存, >3 天自动清理)

缓存策略

  • TTL: 3 天 (基于文件 mtime + 目录级 cleanup_old())
  • Scope: 按日期 + 数据源隔离,tushare/baostock 缓存互不污染
  • 格式: Python pickle (pandas DataFrame)
  • 强制刷新: --no-cache 跳过加载,重新拉取并覆盖

数据源

来源需要用途
Tushare ProTUSHARE_TOKEN (>=120 积分)K 线 (批量按日) + 复权因子 + 交易日历
baostock无 (默认兜底)K 线 (逐股, 前复权 adjustflag=2)
akshare指数成分股 (index_stock_cons)

降级策略

auto:
  1. 检查 TUSHARE_TOKEN -> 有 -> TushareBulkSource (快速批量)
  2. 无 -> BaostockSource (逐股兜底)

交易日历:
  1. Tushare trade_cal API -> 真实日历 + 停牌检测
  2. 失败 -> 自然日估算 (Mon-Fri) + 跳过停牌检测

排除/未命中原因

排除 (扫描前剔除)

原因含义
获取失败API 拉取失败或网络错误
低流动性20 日均额 < --min-avg-amount
上市不足K 线根数 < --min-list-days
数据缺失缺少复权因子 (仅 Tushare 路径)
STST 标的 (名称含 "ST")
退市退市标的 (名称含 "退")

未命中 (扫描后无结果)

原因含义
无缺口回溯窗口内无 low[i] > high[i-1]
低于阈值有缺口但幅度 < --gap-min-pct
MA60破缺口后某日收盘 < MA60
缺口回补缺口后最低价触及或跌破缺口上沿
量比低缺口日量比 < --gap-min-vol-ratio (仅 >1.0 时生效)

联动

对命中的标的,接 /invest-a-stock {symbol} 做深度基本面研究。

合规

  • 禁止买卖/仓位建议
  • 禁止单一目标价
  • 用「缺口区间 / 上沿回补位」等中性表述
  • 输出末尾标注"仅供研究,不构成投资建议"

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.