放量上涨选股 + MA5 卖出条件提示
A股「放量上涨」信号引擎。不设虚拟资金、不做模拟持仓与模拟成交,只输出信号与报告。
适用场景
用户给出一条「放量上涨选股 + 每交易日四槽位检测 + 整体报告 + 信号去重」的规格,要求部署成能自动跑的系统。
典型:D1成交量 ≥ D2成交量×2 且 D1收盘 ≥ D2收盘×1.05 → 四个槽位 09:35 / 10:30 / 13:00 / 14:00
→ 每日一份整体 HTML 报告(只列触发买入 / 卖出信号的股票)。
卖出信号走信号观察名单而不是模拟持仓:名单 = 已触发买入、尚未触发卖出的股票,只用来确定「哪些票要复核卖出条件」,不含股数、成本、可卖数量,不受 T+1 股数约束;但信号层仍有 T+1 互斥(当日新买入信号当日不登记卖出,当日已卖出信号当日不回买)。
SkillHub 安装
将下面这句话发送给另一台电脑上的 WorkBuddy:
请根据 https://skillhub.cn/install/skillhub.md,安装 @user_dd0fc5e5/vol-up-signal-engine
安装完成并重新加载技能后,按下方流程运行。不要复制发布者电脑上的用户目录或 Python 路径。
快速部署(照做)
- 从当前已加载技能的 base directory 取得
SKILL绝对路径。 - 选择本机可用的 Python 3 绝对路径作为
PY:优先使用 WorkBuddy 托管运行时,其次使用当前sys.executable,最后才使用 PATH 中的python3/python。 - 以下命令中的
<技能实际安装目录>与<Python 3绝对路径>必须替换为本机实际值。
SKILL="<技能实际安装目录>"
PY="<Python 3绝对路径>"
# 1) 每年更新一次节假日表(用上证指数K线反推,无需手工维护)
"$PY" "$SKILL/scripts/vol_up_engine.py" sync-holidays --year "$(date +%Y)"
# 2) 交易日判定自检
"$PY" "$SKILL/scripts/vol_up_engine.py" check-day
# 3) 正式运行(工作日四槽位由自动化触发;手动运行会被标记 MANUAL 槽位)
"$PY" "$SKILL/scripts/vol_up_engine.py" run
# 3.5) 生成四条定时任务的计划,交给智能体批量建任务(见文末「自动建任务」)
"$PY" "$SKILL/scripts/vol_up_engine.py" task-plan --out outputs/volume_up_strategy/automation_tasks.json
# 调试用
"$PY" "$SKILL/scripts/vol_up_engine.py" run --slot 14:00 --now "2026-10-16 14:00"
"$PY" "$SKILL/scripts/vol_up_engine.py" run --pool-limit 80 --work-dir _smoke/volume_up_strategy
"$PY" "$SKILL/scripts/vol_up_engine.py" run --json
"$PY" "$SKILL/scripts/vol_up_engine.py" run --refresh-pool --rebuild-cache
IWENCAI_API_KEY 为可选项:已配置时优先从 i问财获取当日沪深全A股快照;未配置、配额耗尽或接口失败时自动降级到公开的全A股名单源,不阻断运行,也不会把 Key 写入技能包或状态文件。
参数集中在 scripts/config.json(唯一事实来源,改参数即改策略)。
完整策略规格(九节,全部保留)
一、运行时间
- 按北京时间运行,只在实际交易日运行。
- 每次运行第一步必须核对当天是否为交易日,非交易日直接结束,不做任何检测、不改动任何文件。
- 交易日判定采用三重条件,全部满足才算交易日:
(1) 当天不是周六、周日;
(2) 当天不在节假日表中;
(3) 能取到上证指数当天的日线K线。
判据不得依赖任何单只个股的当日K线。 - 每个交易日运行四个槽位:09:35、10:30、13:00、14:00,容差正负十五分钟。
- 不在交易时段(09:30-11:30、13:00-15:00)内的时间不执行检测。
- 同一个「交易日 + 槽位」重复运行,只更新报告,不重复登记信号事件。
二、买入信号
- 买入信号条件(只使用已完成日线,D1 为最近一个已完成交易日,D2 为 D1 的前一交易日):
D1 成交量 ≥ D2 成交量 × 2,且 D1 收盘价 ≥ D2 收盘价 × 1.05,两个条件都含边界(大于等于即成立)。 - 股票池为运行当日的沪深全A股,每次运行开盘前取当日快照。
- 同一只股票同一个信号只登记一次,不重复记录、不重复推送;多只候选按股票代码升序依次处理。
- 信号参考价统一取 D1 收盘价。
- 盘中实时价只用于展示和盘中预警,不得当作收盘价使用,也不得参与任何信号判定。
三、卖出信号、T+1 与盘中预警
- 信号观察名单:已触发买入信号、尚未触发对应卖出信号的股票。名单只用于确定卖出复核对象,不含股数、成本、可卖数量。
- 每个交易日首次运行时,同步记录本次运行的槽位;若首次运行发生在下午槽位,需在报告中注明快照实际取自哪个槽位。
- 卖出信号:观察名单内的股票,已完成日线 D1 收盘价 < MA5(D1) 时记一次卖出信号,等于不记。
MA5 按含 D1 在内的最近五个已完成收盘价计算,数据不足五根则不触发卖出信号。 - T+1 约束:当日新买入的股票当日不得卖出;当日已卖出的股票当日不回买。
- 触发卖出信号后该股当日移出观察名单(了结);同一事件ID 不重复登记,也不因移出名单而回补。
- 盘中实时价低于 MA5 只作「盘中预警」,收盘确认前不算正式卖出信号,不登记事件、不移出名单。
- 当日已登记买入信号的股票,同日不再登记卖出信号;当日已登记卖出信号的股票,同日不再登记买入信号。
- 停牌、数据缺失、行情过期的股票不产生新买入信号;观察名单内的保留在名单中、不做卖出判定,并在报告中写明异常原因。
四、状态文件与去重
- 状态文件保存在
outputs/volume_up_strategy/state/signal_state.json,仅包含:
成分股快照(当日沪深全A股代码与简称)、快照取自的槽位、已处理槽位列表、
信号事件记录signal_history、信号观察名单watchlist、每只股票最近一次买入信号、最后更新时间。
不包含任何资金、持仓、成交字段(无剩余现金、持仓数量、可卖数量、成本价、买入金额、今日新买入、今日已卖出、trade_history、手续费与印花税)。 - 每条信号带唯一事件ID,事件ID = 交易日 + 股票代码 + 买卖方向,不含槽位。
- 同一事件ID只登记一次。
- 写状态文件时先写临时文件,写完再原子替换。
- 状态文件损坏或格式不符时,禁止登记新信号并报告错误,不得清空、不得重置、不得擅自初始化。
五、输出报告(整体报告)
- 每次运行只生成一份整体 HTML 报告,不为单只股票生成报告;
文件名vol_up_signal_YYYYMMDD.html,统一输出到reports目录。 - 同一交易日后续槽位运行覆盖刷新同一份文件,报告中标注本次槽位与生成时间。
- 只收录本次运行触发买入或卖出信号的股票;无信号时正文只显示一行
「今日无符合策略要求的买入或卖出信号」。 - 标题栏:策略名称、报告日期、运行槽位、D1 与 D2 日期、生成时间;下方两个计数徽标(买入信号 N 只、卖出信号 N 只)。
- 买入信号表(按代码升序):代码、简称、信号日期(D1)、D1 收盘价、D2 收盘价、较 D2 涨幅、量比、
MA5、MA5 与 MA15 排列关系、盘中实时价、备注。 - 卖出信号表(按代码升序):代码、简称、信号日期(D1)、D1 收盘价、MA5、较 MA5 偏离幅度、
D1 量比、MA5 与 MA15 排列关系、盘中实时价、备注。 - 备注栏写明异常原因(停牌 / 数据缺失 / 行情过期),无异常留空。
- 配色:浅色背景、深色文字;上涨与买入用红色,下跌与卖出用绿色。
六、每次运行的顺序
严格按以下顺序执行,不得颠倒:
核对交易日 → 读取状态(损坏则禁止登记新信号)→ 取成分股快照 → 取日线并确定 D1 与 D2
→ 判定买入信号 → 判定卖出信号与盘中预警 → 原子写入状态 → 生成 HTML 报告 → 输出运行摘要。
其中 D1 与 D2 的确定方式:先取该股日线序列,剔除当天尚未完成的K线,再在剩余序列中取最后一根为 D1、倒数第二根为 D2。
禁止用「今天日期减一天」去推算 D2。
七、输出摘要
每次运行最后输出一段摘要,包含:
运行时间与槽位、D1 与 D2 的日期、本次新增买入信号、本次卖出信号、异常/拦截标的及原因、
信号观察名单只数、报告内当日信号数、报告输出路径。
另附「重点标的」清单,列出报告内全部触发买入或卖出信号的股票代码与简称;
无则写「今日无符合策略要求的买入或卖出信号」。
八、数据源
- 成分股名单优先来自 i问财(同花顺问财);若本机未配置
IWENCAI_API_KEY、配额耗尽或接口不可用,则自动降级到东方财富 / 新浪公开全A股名单并在摘要中标注实际来源。日线来自腾讯自选股公开行情接口,无需 API Key。 - 请求行情时逐只间隔约零点二五秒。
- 节假日表需每年更新一次。
- 手动在非槽位时间运行会被标记为 MANUAL 槽位,正常评估,信号去重依然生效。
九、自动化部署
每个槽位单独建一条自动化任务,共四条:周一至周五的 09:35、10:30、13:00、14:00。
不能合并成一条,因为单条定时规则的「小时」与「分钟」是笛卡尔积,无法精确表达四个不同时点。
非交易日由脚本自行跳过。
> ⚠️ 定时任务不能随技能包分发。 它绑定使用者本机的 Python 绝对路径、工作目录与
> Key 读取方式,只能在各自账号里建一次。不要给使用者一张表让他手抄 —— 照下面
> 的「自动建任务」流程走,一条命令生成计划、由智能体批量落地。
引擎落点(规格 → 实现对照)
| 规格 | 实现 |
|---|---|
| 一.3 三重交易日判定 | check_trading_day():① 非周末 ② 不在 holidays.json ③ 腾讯 sh000001 当日日线。不看任何个股K线 |
| 一.3 判据加固 | 09:35 刚开盘时指数K线可能未落库 → 默认重试 3 次 × 20s,避免误判非交易日 |
| 一.4 / 一.5 槽位与时段 | resolve_slot() / in_trade_session();非槽位时间 → MANUAL |
| 二.1 买入条件 | v1 >= v2*vol_multiple and c1 >= c2*price_gain,边界含等于 |
| 二.2 全A股快照 | get_pool():i问财「全部A股」为主,配额/鉴权失败自动降级东方财富全A股列表 |
| 二.3 代码升序 | order = sorted(snapshot) |
| 二.5 实时价只展示 | 实时价仅进入卡片与「预警」判定,绝不参与 buy/sell |
| 三.1 槽位记录 | 状态 constituents.snapshot_slot + processed_slots[today] |
| 三.2 卖出条件 | c1 < ma5(严格小于);仅对 watchlist 内股票判定;MA5 必须含 D1 且满 5 根,否则不触发 |
| 三.3 盘中预警 | rt < ma5 → 只进摘要提示,不进 HTML 正文,不写卖出事件 |
| 三.4 T+1 | today_bought 排除当日卖出;today_sold 排除当日回买 |
| 三.5 异常拦截 | 无行情 / 无已完成日线 / 最后一根早于 D1 / D1 或 D2 量价缺失 |
| 四.1 状态字段 | 无任何资金、持仓、成交字段;含 watchlist 观察名单 |
| 四.2 / 四.3 事件ID | {交易日}_{代码}_{BUY\|SELL},signal_history 去重 |
| 四.4 原子写入 | 临时文件 + os.replace(),见 atomic_write_json() |
| 四.5 损坏保护 | load_state() 返回 ok=False → 跳过事件登记,报告按本次判定只读生成,绝不清空/重置 |
| 五 报告 | assets/report_overall.html 单份整体报告,只列触发买入/卖出信号的股票 |
| 六 运行顺序 | cmd_run() 严格按序;D1/D2 由「已收盘交易日序列」倒数取,绝不做日期减法 |
数据源与取数策略(关键工程决策)
| 用途 | 来源 | 调用量 |
|---|---|---|
| 交易日判据 + 交易日历 | 腾讯 sh000001 日线 |
1 次/轮 |
| 成分股名单 | i问财「全部A股」→ 失败降级东方财富 | 28 次/日(pool_refresh=daily 当日缓存) |
| D1/D2 量价 + MA5/MA15 | 腾讯自选股日线逐只(qfq) |
全量一次/日(按 D1 缓存 cache/kline_<D1>.json) |
| 盘中实时价 | 腾讯批量快照 qt.gtimg.cn(60 只/请求,GBK) |
约 93 次/轮 |
两个决定性优化:
- D1 是已完成交易日,其量价与 MA 在同一交易日 4 个槽位内完全不变 → 首次运行全量拉一次并落盘缓存,后续槽位只刷实时价。否则每轮要 5000+ 次请求。
- i问财 有每日调用次数配额(耗尽时返回「您今天的次数已用完」,HTTP 401)→ 全A股分页每轮 28 次调用不可持续。故:① 池快照当日只取一次;② 失败自动降级到东方财富全A股列表(无配额),并在摘要中打 WARN。
目录结构
outputs/volume_up_strategy/
├── state/signal_state.json # 唯一状态源(信号事件 / 观察名单 / 快照 / 槽位)
├── cache/kline_YYYYMMDD.json # D1 日线缓存(同交易日内复用)
└── reports/vol_up_signal_YYYYMMDD.html # 每日一份整体报告
自动化部署(四条)
单条 RRULE 的 BYHOUR 与 BYMINUTE 是笛卡尔积,无法表达四个不同时点 → 必须四条:
| 槽位 | rrule |
|---|---|
| 09:35 | FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;BYHOUR=9;BYMINUTE=35 |
| 10:30 | FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;BYHOUR=10;BYMINUTE=30 |
| 13:00 | FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;BYHOUR=13;BYMINUTE=0 |
| 14:00 | FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;BYHOUR=14;BYMINUTE=0 |
自动化 prompt 必须自包含:写清 Python 与技能的绝对路径、执行命令、工作目录;若使用者明确配置了 i问财 Key,再加入不含 Key 明文的环境加载命令。约定输出处理——整段搬运脚本 stdout 的运行摘要,[SKIP] 开头的输出只需一句话确认。
自动建任务(智能体照此执行)
给智能体的指令:部署完成后,不要只是把四条 rrule 打印给用户看,要实际建出这四条定时任务。
第 1 步:生成任务计划
SKILL="<技能根目录>"
PY="<Python 绝对路径>"
"$PY" "$SKILL/scripts/vol_up_engine.py" task-plan \
--out outputs/volume_up_strategy/automation_tasks.json
生成器会自动探测本机 Python 解释器、读取 config.json 里的 slots,并把四条任务的
名称 / rrule / 自包含 prompt 全部物化成 JSON。若自动探测的路径不对,用
--python-exe "<正确路径>" 覆盖后重新生成。
第 2 步:读计划、逐条建任务
读 automation_tasks.json,对 tasks 数组里的每一条调用平台自动化工具,字段一一对应:
| JSON 字段 | 落到自动化工具的参数 |
|---|---|
name |
任务名称 |
scheduleType |
调度类型(recurring) |
rrule |
重复规则 |
status |
状态(ACTIVE) |
cwds |
工作目录 |
prompt |
任务 prompt(整段原样使用,不要改写) |
必须建四条,逐条调用,不要试图把四条合并成一条 —— 合并会丢失时点精度。
第 3 步:验收
建完列出四条任务的名称、rrule 与下次运行时间。然后做一次哨兵自检:
"$PY" "$SKILL/scripts/vol_up_engine.py" check-day
交易日 ✅ / 非交易日 ❌ 双向都要对应得上,说明调度与引擎口径一致。
第 4 步:排查历史重复任务(重要)
建任务前先列一遍已有的自动化,若发现同一时点、指向旧路径(例如
/workspace/volume_up_strategy/strategy.py 这类已不存在的脚本)的历史任务,
停掉它们(置为暂停),否则会与新任务同时段双跑、互相污染状态文件。
此设计的边界(诚实地告知使用者)
- 脚本只生成计划,不直接建任务:自动化是平台侧能力,需经由智能体的平台工具创建,
纯 Python 脚本调不到。 - 计划里的 prompt 不含任何 Key 明文;如需强制使用 i问财,可用
--key-cmd传入只负责从本机安全位置加载环境变量的命令。 - 未配置 i问财 Key 时,引擎会自动降级到公开名单源,仍可直接运行。
- 换机器后必须重新运行
task-plan—— 解释器与技能的绝对路径因机器而异。
输出约定
[SKIP] …非交易日…→ 非交易日直接结束,不改动任何文件[SKIP] …不在交易时段…→ 交易时段外不执行检测[INFO]/[OK]/[WARN]/[ERROR]+ 末尾运行摘要与重点标的清单--json额外输出一行机器可读摘要
踩坑清单
见 references/pitfalls.md(i问财配额、腾讯域名回退、指数K线 volume 占位值、停牌股识别、节假日表反推口径等)。