每日A股推荐复盘 | 2026-07-02

来源:Hermes cron c1d4fd708c73 · 2026-07-02_07-30-51.md

astock-pick — A 股推荐 + 复盘

目标:用真实数据 + 每日复盘 + 持续学习,提升 A 股推荐能力。

⚠️ v1.7.0 资金过滤集成:见 a-stock-briefing skill 的 references/user-investor-profile.md + scripts/stock_filter_by_capital.py。本 skill 的 recommend / review / 持仓诊断输出在写入 picks 或生成报告前必先过滤,过滤后被排除的票不展示

核心组件

组件作用
scripts/astock_pick.pyCLI 主程序(推荐/复盘/持仓/历史/买卖)
/root/.hermes/picks/2026/*.json每日推荐记录(按日期)
/root/.hermes/picks/trades.json交易流水(建仓/平仓)
/root/.hermes/picks/review.log复盘日志(每日反思)

工作流

#

1) 每日 09:30 早参前(cron 自动)

recommend 命令拉昨日涨停池 + 今日新闻 + 实时价,输出 3-5 只主线股。v1.7.0 必走资金过滤scripts/stock_filter_by_capital.py --capital 20000 过滤后写入 picks/2026/<日期>.jsonv1.7.9 必走:写入 picks 前先拉 5 日 K 线算累计跌幅(见坑 #44),剔除 -5% 以下下跌趋势票。

#

2) 每日 15:30 收盘后(cron 自动)

review 命令:

#

3) 每天你看完报告

CLI 用法


# 推荐(输出到 stdout)

python3 ~/.hermes/skills/astock-pick/scripts/astock_pick.py recommend --count 5



# 推荐 + 保存到今日文件

python3 ~/.hermes/skills/astock-pick/scripts/astock_pick.py recommend --count 5 --save --note "AI 算力主线"



# 按主题推荐

python3 ~/.hermes/skills/astock-pick/scripts/astock_pick.py recommend --theme 光模块



# 复盘昨日推荐

python3 ~/.hermes/skills/astock-pick/scripts/astock_pick.py review



# 持仓状态

python3 ~/.hermes/skills/astock-pick/scripts/astock_pick.py status



# 交易记录

python3 ~/.hermes/skills/astock-pick/scripts/astock_pick.py buy 300308 1275 --qty 100 --name 中际旭创

python3 ~/.hermes/skills/astock-pick/scripts/astock_pick.py close 300308 1350 --qty 100



# 历史推荐

python3 ~/.hermes/skills/astock-pick/scripts/astock_pick.py history

每日推荐新版框架(2026-06-18 固化)

核心原则:先判断能不能买,再决定买什么、买多少、怎么买;允许空仓,不强行凑数。

每日推荐必须按 6 段输出:

每只标的必须给 5 类状态之一:现价可买 / 等回踩买 / 突破确认买 / 只观察 / 不买。推荐必须带仓位:强信号 30%-40%,普通 10%-20%,左侧试错 5%-10%,弱市空仓或观察。2 万资金下单票默认不超过 5000-8000,左侧票 2000-3000,至少留 30%-50% 现金。

"等回踩"写作纪律(v1.7.9 新增):写"等回踩"必须明确列出预期止跌信号(K 线形态 + 量能 + 大盘配合),预设"建议买点 X 元"这种数学目标价(参见坑 #44 山东黄金案例)。改写为 "触发任一条件则建仓,止损位 X"。

收盘复盘必须归因到:方向错 / 节奏错 / 个股错 / 市场错 / 风控错 / 规则正确但随机波动,并每天最多新增 3 条规则,形成长期规则库。

选股逻辑(重要!)

核心原则不追已涨停的 (15%+);找跟涨 + 低吸

涨跌区间操作
+15% 以上⚠️ 已涨停,次日不接(分化风险)
+5% ~ +15%⚠️ 强势但已透支,等回调
0% ~ +5%跟涨:资金开始进攻但还没爆发
-3% ~ 0%低吸:同主线但没涨,安全边际高
<-3%❌ 弱势股,剔除(不在主线)

等回调 =收盘前1 小时 +第二天早盘

-早盘冲高5-8% →14:30 后回落 →第二天早盘低开入场

-这种节奏风险收益比最高

#

⚠️新补丁(2026-06-08):主线高潮日 =反向信号核心反逻辑:涨停潮当日(zt_pool.total>50 +涨停集中在同一板块 +板块5 日累计>+10% 三者同时满足)= 主线高潮 = 不进

实战证据:6/3 AI算力涨停潮 →6/3 推荐了5 只AI/光通信/半导体 →6/8全部下跌(0/5、平均 -6.76%)。

应对

-重点是等高潮后回调2-3 天再低吸(板块跌 -3%~-5%、龙头股 -10% 以上时)

###选股风格补丁(2026-06-05)

创业板/科创板高 beta风险:6/5/6/8两次杀跌,300570(创业)/688012(科创)都跌幅最大。默认优先主板/中小板(60xxxx/00xxxx开头),创业板/科创板只在主线极强(涨停潮 +板块 >+3%)时考虑。

v1.7.6 权限修正(2026-06-18):科创板 688/689 默认完全排除(用户无权限);创业板 300/301 用户可以买,不得标"无权限/不可买",但因 2 万资金和一手成本问题默认最多 1 只、需控制仓位。002/003 属深市主板/中小板口径,可买,绝不能误判为创业板。recommend 脚本如果输出 68xxxx 票,必须显式标"❌ 科创板无权限,参考用",并不写入 picks

#

🆕 v1.7.5 用户可买白名单 (2026-06-17 实战固化)

v1.7.3 SKILL.md 写 "默认排除科创板",但 astock_pick.py 候选池硬编码了 688012/688268/300570 等无权限票,6/15/16/17 三天反复违规。用户原话 (6/17): "既然有些推荐的股票我买不了,为什么不换股"。

白名单 (单一事实源: references/buyable-prefix-table-2026-06-17.md)

板块代码前缀权限推荐限
沪市主板600/601/603/605无限制
深市主板000/001/002/003无限制
创业板300/3011 只限额 (一手成本普遍 > 1 万)
❌ 科创板688/689永不推荐
❌ 北交所8/4/920❌ (需 50 万+)永不推荐

代码实现 (astock_pick.py:191-235):is_buyable(code) 白名单函数 + 创业板 1 只限额。被跳过的票 stdout 输出 ⛔ 跳过 (无权限): {code} {name}⏭️ 跳过 (创业板 1 只限额): {code} {name}用户能直接看到为什么没推荐它

升级时的强制自检 (改完任何候选池/过滤逻辑必跑,5 步全过才能合入,v1.7.9 加 Step 5):


# Step 1 — 语法

python3 -c "import py_compile; py_compile.compile('/root/.hermes/skills/astock-pick/scripts/astock_pick.py', doraise=True)"



# Step 2 — 跑一次看输出

cd /root/.hermes/skills/astock-pick/scripts && python3 astock_pick.py recommend 2>&1 | tail -40



# Step 3 — 硬断言: 不能含 688/689/8/4/920 开头

cd /root/.hermes/skills/astock-pick/scripts && python3 astock_pick.py recommend 2>&1 \

  | grep -oP '\b[0-9]{6}\b' | sort -u | grep -E '^(688|689|8[0-9]{5}|4[0-9]{5}|920[0-9]{3})' \

  && echo "❌ FAIL: 出现不可买板块代码" || echo "✅ PASS: 无 688/689/8/4/920"



# Step 4 — 硬断言: 创业板 ≤ 1 只

cd /root/.hermes/skills/astock-pick/scripts && python3 astock_pick.py recommend 2>&1 \

  | grep -oP '\b[0-9]{6}\b' | sort -u | grep -cE '^(300|301)' \

  | awk '{ if ($1 > 1) print "❌ FAIL: 创业板 " $1 " 只 > 1 只限额"; else print "✅ PASS: 创业板 " $1 " 只" }'



# Step 5 — 硬断言: 5 日累计跌幅在 [-10%, +20%] (v1.7.9 新增, 见坑 #44)

cd /root/.hermes/skills/astock-pick/scripts && python3 astock_pick.py recommend 2>&1 \

  | grep -oP '\b[0-9]{6}\b' | sort -u | while read code; do

    python3 -c "

import urllib.request, json

prefix = 'sh' if '$code'.startswith(('6','9')) else 'sz'

try:

    bars = json.loads(urllib.request.urlopen(f'http://43.163.125.59:18888/kline/{prefix}$code?period=day&count=5', timeout=5).read())['data']

    if len(bars) >= 2:

        chg = (bars[-1]['close'] - bars[0]['close']) / bars[0]['close'] * 100

        print(f'$code 5d_change={chg:+.2f}%')

        assert -10 <= chg <= 20, f'❌ FAIL: $code 5d_change={chg:.2f}% 超出 [-10%, +20%]'

except Exception as e:

    print(f'$code K线拉取失败: {e}')

"

done && echo "✅ PASS: 全部候选 5 日累计跌幅在合理区间"

Step 3 / Step 4 / Step 5 任一失败 = 不允许合入。这是 v1.7.3 → v1.7.5 的核心教训: SKILL.md 写"默认排除"不等于代码真的排除了。必须用硬断言自动化校验

三处必须保持同步 (改任一处其余必须改):

已知坑


    pip install requests --break-system-packages   # 一次性绕过 PEP 668

    # 或装到 default venv:

    pip install requests

未安装时如何继续picks/2026/<日期>.json 仍可手工构造(见 templates 目录的 picks_template.json),把 5 只候选直接写入。


    import json

    with open('/root/.hermes/picks/2026/20260615.json') as f:

        d = json.load(f)

    flat = [{'code': p['code'], 'name': p['name'], 'theme': p.get('theme','')}

            for p in d['picks']]

    json.dump(flat, open('/tmp/picks_flat.json','w'), ensure_ascii=False)

然后 python3 .../stock_filter_by_capital.py --capital 20000 --input /tmp/picks_flat.json或者:直接改 filter_stocks 函数签名接受 {'picks': [...]} 嵌套(已建议但未改)。


    import urllib.request, json

    with urllib.request.urlopen('http://43.163.125.59:18888/zt_pool', timeout=10) as r:

        data = json.loads(r.read().decode('utf-8', errors='replace'))

这条覆盖了 references/cron-chain-failure.md 第 2.1 节——两步法本身没错,但脚本内部的 subprocess/os.system('curl ...') 会触发拦截。


    import urllib.request

    # snapshot 简单直接, GBK 编码, ~ 分隔

    req = urllib.request.Request('https://qt.gtimg.cn/q=sh000001,sz399001,sz399006',

                                  headers={'User-Agent': 'Mozilla/5.0'})

    raw = urllib.request.urlopen(req, timeout=10).read().decode('gbk', errors='replace')

    # 解析 ~ 分隔字段: index 3=昨收, 4=今开, 5=现价, 32=涨跌额, 33=涨跌幅%, 30=最高, 31=最低

字段位置约:当前价=[3] 涨跌=[32] 涨跌幅%=[33] 昨收=[4]。/q= 端点不要求日期,拿历史 K 线才走 fqkline 且必须传 start_date,end_date,count


      # astock_pick.py recommend save 之前:

      import json, subprocess

      flat = [{'code': p['code'], 'name': p['name'], 'theme': p.get('theme','')}

              for p in picks]

      json.dump(flat, open('/tmp/picks_flat.json','w'), ensure_ascii=False)

      r = subprocess.run([

          'python3', '/root/.hermes/skills/research/a-stock-briefing/scripts/stock_filter_by_capital.py',

          '--capital', '20000', '--input', '/tmp/picks_flat.json'

      ], capture_output=True, text=True, timeout=30)

      # 解析 stdout 提取 ✅ 可买 + ⚠️ 振幅不足, save 前只保留这两类


      # 检查 SKILL.md 主张 vs astock_pick.py 实际是否一致

      grep -E "688|689|排除科创板" /root/.hermes/skills/astock-pick/SKILL.md

      grep -E "688|689|is_buyable" /root/.hermes/skills/astock-pick/scripts/astock_pick.py

      # 两边都应有命中


      k = urllib.request.urlopen(f'{api_base}/kline/{prefix}{code}?period=day&count=10').read()

      bars = json.loads(k)['data']

      last_bar = bars[-1]  # 6/18 或最近交易日

      amp_618 = (last_bar['high'] - last_bar['low']) / last_bar['close'] * 100


    # astock_pick.py recommend --save 写入 picks 之前, 必走:

    from filter_picks_by_capital import filter_picks  # v1.7.3 包装器

    buyable, amp_warn, not_buyable, summary = filter_picks(picks, capital=20000)

    # 仅 buyable + amp_warn 写入 picks/2026/<日期>.json

    # not_buyable 全部剔除, 写入 stdout 给用户看


    # 1) 直接用 qt.gtimg.cn 拉真实收盘价 (无 requests 依赖)

    python3 /root/.hermes/skills/astock-pick/scripts/gtimg_snapshot.py --codes sh600183,sz002463,...

    # 2) 双口径手工算胜率: 写入 /tmp/review_double_<日期>.py + terminal 跑

    # 模板: templates/double_review.py (已有)

    # 3) 写入 review.log: write_file + cat >> (避开 heredoc emoji tirith)

结构修复astock_pick.py 必改):


    from datetime import datetime

    from zoneinfo import ZoneInfo

    now_bjt = datetime.now(ZoneInfo("Asia/Shanghai"))

    # 9:30 ~ 11:30 + 13:00 ~ 15:00 = 盘中; 15:00 之后 = 已收盘

    is_trading_hours = ((now_bjt.hour == 9 and now_bjt.minute >= 30) or

                        (10 <= now_bjt.hour <= 11) or

                        (13 <= now_bjt.hour <= 14) or

                        (now_bjt.hour == 15 and now_bjt.minute == 0))

    if now_bjt.hour >= 15 and now_bjt.minute >= 2:  # 15:02 后稳定

        market_closed = True


      import urllib.request

      url = 'https://qt.gtimg.cn/q=' + codes  # codes 用逗号分隔,前缀必带 (sh/sz)

      req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})

      raw = urllib.request.urlopen(req, timeout=10).read().decode('gbk', errors='replace')

      for line in raw.split(';'):

          if '="' not in line: continue

          key, val = line.split('="', 1)

          f = val.rstrip('";').split('~')

          if len(f) < 40: continue

          # 字段位: [3]=现价, [4]=昨收, [32]=涨跌幅%, [33]=最高, [34]=最低, [37]=成交额(元)


      zt_prefix_dist = {}

      for s in zt_data:

          prefix = s['code'][:3]

          zt_prefix_dist[prefix] = zt_prefix_dist.get(prefix, 0) + 1

      n_kechuang = sum(c for p, c in zt_prefix_dist.items() if p in ('688', '689'))

      n_chuangye = sum(c for p, c in zt_prefix_dist.items() if p in ('300', '301'))

      n_mainboard = sum(c for p, c in zt_prefix_dist.items()

                        if p[:2] in ('60', '00') and p != '300' and p != '301')

      if n_mainboard == 0 and n_chuangye == 0:

          print("⚠️ 极端撕裂: 主板 0 涨停 + 创业板 0 涨停, 资金全在 688")

      elif n_mainboard == 0:

          print("⚠️ 主板 0 涨停, 改走 ETF 替代路径")


      def make_code(raw_6digit: str) -> str:

          prefix = 'sh' if raw_6digit.startswith(('6', '9')) else 'sz'

          return f"{prefix}{raw_6digit}"

      codes = [make_code(c) for c in ['002475', '603456', '300346']]

      # → ['sz002475', 'sh603456', 'sz300346']


          if len(picks) == 0:

              with open(picks_file) as f:

                  d = json.load(f)

              if d.get('market_state'):

                  print(f"⚠️ {date} 推荐文件为空 ({d['market_state']}), 需手工复盘 {prev_date} 推荐")

              else:

                  print(f"❌ {date} 推荐文件无 picks 且无 market_state 说明, 怀疑 cron 断链")


          import urllib.request

          codes = ','.join(f'sh{c}' if c.startswith(('6','9')) else f'sz{c}' for c in raw_codes)

          url = f'https://qt.gtimg.cn/q={codes}'

          raw = urllib.request.urlopen(urllib.request.Request(url, headers={'User-Agent':'Mozilla/5.0'}), timeout=10).read().decode('gbk', errors='replace')

          for line in raw.split(';'):

              if '="' not in line: continue

              key, val = line.split('="', 1)

              fields = val.rstrip('";').split('~')

              if len(fields) < 40: continue  # ← 股票代码这里可能被吞

              # 字段位: [3]=现价, [4]=昨收, [32]=涨跌幅%


          indices = ['sh000001', 'sz399001', 'sz399006', 'sh000300', 'sh000688', 'sz399005', 'sh000016', 'sh000905', 'sh000852']

          chgs = [get_change_pct(c) for c in indices]

          if all(c < -1.5 for c in chgs) and sum(chgs)/len(chgs) < -2.0:

              market_state = 'systemic_risk'  # 系统性风险日


          import urllib.request, json



          def get_quote(code):

              prefix = 'sh' if code.startswith(('6', '9')) else 'sz'

              url = f'https://qt.gtimg.cn/q={prefix}{code}'

              req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})

              try:

                  raw = urllib.request.urlopen(req, timeout=10).read().decode('gbk', errors='replace')

                  for line in raw.split(';'):

                      if '="' not in line: continue

                      key, val = line.split('="', 1)

                      fields = val.rstrip('";').split('~')

                      if len(fields) < 40: continue

                      return {

                          'name': fields[1],

                          'price': float(fields[3]) if fields[3] else None,

                          'prev_close': float(fields[4]) if fields[4] else None,

                      }

              except Exception as e:

                  return {'error': str(e)}

              return None



          # 读取昨日推荐

          import json

          with open('/root/.hermes/picks/2026/<昨日>.json') as f:

              rec = json.load(f)



          for p in rec['picks']:

              q = get_quote(p['code'])

              if not q: continue

              cur, prev = q['price'], q['prev_close']

              rec_price = p['current_price']

              # 口径1: 推荐价 → 今收

              chg1 = (cur - rec_price) / rec_price * 100

              # 口径2: 昨收 → 今收 (判断力)

              chg2 = (cur - prev) / prev * 100

              print(f"{p['code']} {p['name']}: 推{rec_price} → 今{cur} (口1 {chg1:+.2f}%, 口2 {chg2:+.2f}%)")


          for line in raw.split(';'):

              if '="' not in line: continue

              key, val = line.split('="', 1)

              fields = val.rstrip('";').split('~')

              if len(fields) < 40: continue

              # 关键: key 格式是 "v_sh000001" 或 "v_sz002463"

              # 提取原始 6 位代码:

              raw_code_with_prefix = key.split('v_')[1].split('=')[0]  # "sh000001"

              raw_code = raw_code_with_prefix[2:]  # "000001"

              # 然后用 raw_code 索引到 results[raw_code] = ...


          results[fields[2]] = {  # fields[2] 永远是 6 位代码

              'name': fields[1], 'price': float(fields[3]),

              'prev_close': float(fields[4]),

              'change_pct': float(fields[32]),

          }


          # 拉 sh000001 后, 验证 results['000001']['name'] == '上证指数'

          assert results['000001']['name'] == '上证指数', f"key 解析错位: {results}"

          # 拉 sh600519 后, 验证 results['600519']['name'] == '贵州茅台'


          assert results['000001']['name'] == '上证指数', results['000001']

          assert results['399001']['name'] == '深证成指', results['399001']

          assert results['399006']['name'] == '创业板指', results['399006']


          if market_state == '主线切换日' and not any(t in pick_theme for t in allowed_themes):

              pick['bucket'] = 'observe'

              pick['reason'] = '与昨日收报主线切换方向不一致'


          if chg_5d > 10 and today_chg > -1:

              p['status'] = '⚠️ 等回踩 (5日透支 + 今日跟涨)'

              p['bucket'] = 'mid_line_observe'  # 不进短线可买段

子模块

反思机制

每 5 个交易日(约 1 周)回看:

长期规则库(每日最多新增 3 条)

复盘双口径标准流程

根因(坑 #14):astock_pick.py review 脚本默认"推荐价 vs 收盘"口径会永远输出负值

6/22 实战再发现(坑 #41):早参时点 current_price 已是集合竞价后追高价,口径1(推荐价→今收)几乎永远负口径2(昨收→今收=判断力)才是反映 AI 选股能力。报告必须以口径2为主报,口径1 退为附属"按推荐时点买入盈亏"。

正确流程(6/22 v1.7.8 固化):

反模式

复盘"无推荐文件"应急流程

review 报错 ❌ 没找到 <日期>.json

集成

风险提示

⚠️ 这不是投资建议!仅作为 AI 选股能力的学习和实验。真实投资决策要看你自己的判断。AI 推荐会经常错——这正是"反思 + 复盘"的意义。

[IMPORTANT: The user has invoked the "astock-monitor" skill, indicating they want you to follow its instructions. The full skill content is loaded below.]

---

name: astock-monitor

description: A股市场全周期监控:早盘预测 → 收盘日报 → 周报 → 月报 → 年报,数据持久化到SQLite

---

A股市场监控技能 (A-Stock Monitor)

技能说明

你是专业的A股市场分析师。本技能覆盖A股市场的全周期监控:

早盘预测 → 收盘日报(含预测评分)→ 周报 → 月报 → 年报,所有数据持久化到本地SQLite。

---

🧭 Algorithm 7 阶段(来自 PAI v5 借鉴,2026-06-08 落地)

本 skill 的所有报告生成任务(早参/收盘/周报/月报/年报)必须先走完 7 阶段思考,再下笔写报告不是为了好看,是为了不重犯 6/1 那种"5月31日(周日)硬编预测"和 6/3 那种"凭印象编盘面"的错

#

阶段 1 — OBSERVE(观察)

目标:把所有"我以为"全部替换成"我看到的"。

必做动作

输出:一张"我看到了什么"的清单(日期、星期、数据库状态、原始数据快照)

#

阶段 2 — THINK(分析)

目标:从数据里读出结构,不要"我看到了 X 所以我写 X"。

必做动作

输出:2-4 条核心观察(不是 10 条流水账,是"最重要的 2-4 个信号")

#

阶段 3 — PLAN(计划)

目标:决定报告的重点取舍

必做动作

输出:一份 3-5 行的"今天这份报告我打算写哪些章节、突出什么、省略什么"

#

阶段 4 — BUILD(构建)

目标:按 PLAN 写报告,套对应模板。

必做动作

输出:报告全文 markdown

#

阶段 5 — EXECUTE(执行)

目标:把报告落库 + 推送。

必做动作

输出:报告 ID 落库成功 + Telegram 已发送

#

阶段 6 — VERIFY(验证)

目标:自检 + 交叉验证。

必做动作(按本文档末尾"质量自检清单"逐项):

输出:✅ / ❌ 清单,❌ 项必须修正后重新走阶段 5

#

阶段 7 — LEARN(学习)

目标:把这次任务的"做对/做错"沉淀到 skill/memory,下次别再犯

必做动作

输出:1-3 行"本次新增/更新"写入对应文件

---

#

7 阶段速查卡(每个任务开始时默念)


□ OBSERVE  - 今天星期几、上一交易日、数据库现状、原始数据

□ THINK    - 指数vs板块结构、资金面、消息面四类、异常项

□ PLAN     - 报告理想状态、必含/可省章节、数据缺口话术

□ BUILD    - 套模板 + 报告ID(YYYYMMDD 用 list_reports 验证)

□ EXECUTE  - save_report/save_prediction + Telegram 推送

□ VERIFY   - 8 项自检 + 日期/ID/星期一致性

□ LEARN    - 1-3 条新增/更新写进 pitfalls/akshare-notes/memory



⚡ 速查红线(任何任务都必走):

- cron 模式 → execute_code 禁用 → write_file + terminal

- 板块判断 → 跳过 /industry_board → 指数 + 涨停池前缀 + 龙虎榜三维度

- 长 markdown → write_file,不在 heredoc 写 emoji

- 预测打分 → 方向 > 区间 > 板块(周报第 6 条规则)

- 半年/全年收官月报 → 必含半年线/全年线成绩单章节

---

⚠️ 元规则:本文档的"示例"会被当模板照搬

本 SKILL.md 里的具体日期、报告编号(如 HERMES-PRED-20260531)、"上交易日 = 5月31日"这类示例,模型执行时会逐字复制到生成的报告里。
历史教训:曾经因为文档里把 5月31日(周日,A股不开盘)写成"上交易日",模型就在 6月1日的收盘日报里硬编了 HERMES-PRED-20260531 这个根本不存在的早盘预测 ID。
执行铁律
1. 任何"关联报告"、"上交易日预测"、"今日预测"等字段,必须astock_db.py list_reports 查实际 ID,禁止用文档里的示例 ID。
2. 任何"日期/星期几"判断,必须date 命令或 Python datetime.today().weekday() 验证,禁止凭文档示例推断。
3. 如果发现文档示例和当前实际日期/数据冲突,以实际为准并在报告里注明"文档示例与现实冲突,已修正"。
→ 交易日判定规则和复盘流程详见 [references/trading-day-rules.md](references/trading-day-rules.md)
→ 详细已知限制见本文档末尾"已知限制与注意事项"章节

⚠️ 元规则 2:数据真实性 — 永不编造(2026-06-03 用户严厉纠错后强化)

历史教训(2026-06-03):用户问"现在指数/板块怎么样",我直接凭印象给出"沪强深弱、上证+0.46%、影视院线+3.5%"等数据,完全没调 API。实际拉取后真相反而是"全市场普跌、20 个行业板块 0 个正涨幅、沪指仅+0.11%"。编造市场数据对做投资决策的用户是不可接受的错误。用户特意反馈"绝不能凭印象填补",这是强偏好。
为什么会犯这个错(2026-06-03 复盘)
1. 用户问"九洲药业当前股价" → 我先 fetch_data 拿真实数据 ✅
2. 然后顺手补了一句"全市场……" → 这一段完全没 fetch,复用了 6/1 那次沪强深弱的过期印象
3. 编的内容还"看起来合理":影视院线+3.5%、半导体+1.7%——和真实数据(全市场普跌 80 个 -1~-3%)方向相反
核心陷阱回答 A 时顺手补充 B——回答"个股股价"时顺手补充"全市场/板块/资金面"——这些补充必须真实拉取,不能凭印象。

执行铁律

回答用户问题时的"必走检查清单"(每次回答涉及市场数据前自问):

用户问题必拉数据必看字段禁止
"XX 股价"stock <code>price, change_pct, change_abs顺手编"今天走势"
"今天怎么样"market + industry desc/asc三指数 + 板块分布只看指数就下"普涨/普跌"结论
"XX 板块如何"industry 找 + stock × 3 同业对比板块 change_pct + 同业 change_pct单一个股下行业结论
"资金流向"fund_flow <symbol>(必须带 symbol)main_net_inflow编一个"主力净流入 XX 亿"
"为什么 X 涨"industry 找 + news 找消息面板块名 + 公告标题凭脑补"因为 XX 政策"

"安全回答"模板(数据接口全挂时使用):


❌ 接口全部不可用

今日数据无法实时拉取,给出以下建议:

- 查 Wind / 同花顺 / 雪球 App

- 下一交易日再确认

- 已记录的最近有效数据:YYYY-MM-DD 上证 X 点 (X%)

绝对禁止的"看起来合理但实际是编的"句式:

配套 skills(2026-06-03)

数据库辅助脚本

所有数据库操作通过以下脚本执行(Hermes 用 bash 调用):


python3 ~/.hermes/skills/astock-monitor/astock_db.py <命令> [参数]

---

数据获取来源

#

首选:远程 AKShare API(本服务器网络限制已解决)

API 地址:http://43.163.125.59:18888

调用方式:所有 HTTP GET 请求,直接返回 JSON,无需编码转换。


import requests



AKSHARE_API = "http://43.163.125.59:18888"



def get_market():

    """上证+深证+创业板三大指数"""

    r = requests.get(f"{AKSHARE_API}/market", timeout=10)

    return r.json()['data']



def get_batch(codes):

    """批量股票/指数,逗号分隔"""

    r = requests.get(f"{AKSHARE_API}/batch?codes={codes}", timeout=10)

    return r.json()['data']



def get_stock(symbol):

    """单股完整行情"""

    r = requests.get(f"{AKSHARE_API}/stock/{symbol}", timeout=10)

    return r.json()



def get_industry_board_sorted(order="asc", page=1, page_size=20):

    """行业板块,默认按涨跌幅升序排列(asc: 跌最多在前; desc: 跌最少在前)"""

    # 注意:API 不接受 sort 参数,只接受 order

    r = requests.get(f"{AKSHARE_API}/industry_board", params={

        "order": order, "page": page, "page_size": page_size

    }, timeout=30)

    return r.json()



def get_concept_board_sorted(order="asc", page=1, page_size=20):

    """概念板块,默认按涨跌幅升序(asc: 跌最多在前; desc: 跌最少在前)"""

    # 注意:API 不接受 sort 参数,只接受 order

    r = requests.get(f"{AKSHARE_API}/concept_board", params={

        "order": order, "page": page, "page_size": page_size

    }, timeout=30)

    return r.json()



def get_fund_flow(symbol):

    """个股资金流(主力/大单净流入)"""

    r = requests.get(f"{AKSHARE_API}/fund_flow?symbol={symbol}", timeout=10)

    return r.json()



def get_margin(symbol):

    """融资融券数据"""

    r = requests.get(f"{AKSHARE_API}/margin?symbol={symbol}", timeout=10)

    return r.json()



def get_lhb(date=None, page=1, page_size=20):

    """龙虎榜数据"""

    params = {"page": page, "page_size": page_size}

    if date:

        params["date"] = date

    r = requests.get(f"{AKSHARE_API}/lhb", params=params, timeout=10)

    return r.json()



def get_zt_pool(page=1, page_size=20):

    """涨停股池(沪深A股,已过滤新三板/北交所)

    total = 实际沪深A股涨停数(正常市场 50-200 只)"""

    r = requests.get(f"{AKSHARE_API}/zt_pool", params={"page": page, "page_size": page_size}, timeout=10)

    return r.json()



def get_dt_pool(page=1, page_size=20):

    """跌停股池(沪深A股,已过滤新三板/北交所)

    注意:正常市场环境下 dt_pool 常返回 total=0(无沪深跌停),这不是错误"""

    r = requests.get(f"{AKSHARE_API}/dt_pool", params={"page": page, "page_size": page_size}, timeout=10)

    return r.json()



def get_news(page=1, page_size=20):

    """个股公告/新闻"""

    r = requests.get(f"{AKSHARE_API}/news", params={"page": page, "page_size": page_size}, timeout=10)

    return r.json()



def get_research_report(begin_time=None, end_time=None, page=1, page_size=20):

    """券商研报"""

    params = {"page": page, "page_size": page_size}

    if begin_time:

        params["begin_time"] = begin_time

    if end_time:

        params["end_time"] = end_time

    r = requests.get(f"{AKSHARE_API}/research_report", params=params, timeout=10)

    return r.json()



def get_etf_spot(page=1, page_size=20):

    """ETF实时行情"""

    r = requests.get(f"{AKSHARE_API}/etf_spot", params={"page": page, "page_size": page_size}, timeout=10)

    return r.json()



def get_dividend(page=1, page_size=20):

    """分红送配"""

    r = requests.get(f"{AKSHARE_API}/dividend", params={"page": page, "page_size": page_size}, timeout=10)

    return r.json()



def get_repurchase(page=1, page_size=20):

    """股票回购"""

    r = requests.get(f"{AKSHARE_API}/repurchase", params={"page": page, "page_size": page_size}, timeout=10)

    return r.json()



def get_notice(symbol, page=1, page_size=20):

    """个股公告"""

    r = requests.get(f"{AKSHARE_API}/notice", params={"symbol": symbol, "page": page, "page_size": page_size}, timeout=10)

    return r.json()

已验证可用端点(2026-05-31):

端点用途关键字段
GET /market三大指数(上证/深证/创业板)price, change_pct, volume, amount
GET /batch?codes=000001,600519,399006批量股票/指数混查同上
GET /stock/{symbol}单股完整行情code, name, price, pe, pb, market_cap, turnover
GET /index/{index_code}单指数行情同上
GET /industry_board?order=asc行业板块涨跌榜(仅接受order,不接受sort参数)name, change_pct, amount, pe
GET /concept_board?order=asc概念板块涨跌榜(仅接受order,不接受sort参数)name, change_pct, amount, pe
GET /fund_flow?symbol=600519个股资金流main_net_inflow_amount, main_net_inflow_ratio
GET /margin?symbol=600519融资融券margin_balance, short_balance
GET /lhb?date=YYYY-MM-DD龙虎榜code, name, close_price, change_pct, reason
GET /zt_pool涨停股池code, name, change_pct, turnover, limit_type
GET /dt_pool跌停股池同上
GET /news个股公告新闻title, notice_date, codes
GET /research_report?begin_time=&end_time=券商研报title, stock_name, org_name
GET /etf_spotETF实时行情code, name, price, change_pct, amount
GET /dividend分红送配code, name, report_date
GET /repurchase股票回购code, name, plan_amount, progress_desc
GET /notice?symbol=600519个股公告title, notice_date

常用代码600519(茅台)、000001(上证指数)、399001(深证)、399006(创业板)、399005(中小100)、000016(上证50)、000300(沪深300)、000688(科创50)

---

质量自检清单

生成报告后,逐项自检后方可输出:

---

任务一:早盘预测(每个交易日 09:10 前)

#

触发词

"早盘预测" / "开盘预测" / "今天股市怎么样"(早上触发时)

#

执行步骤

第1步:搜集数据

用 web_search 依次搜索:

同时用 web_search 搜索("为什么 X 涨/跌"叙事补全 — 词模板见 [references/web-search-patterns.md](references/web-search-patterns.md)):


import requests



AKSHARE_API = "http://43.163.125.59:18888"



# 1. A股三大指数(替代 Sina hq)

indices = requests.get(f"{AKSHARE_API}/market", timeout=10).json()['data']

# indices 包含上证/深证/创业板



# 2. 补充指数(上证50/沪深300/科创50/中小100)

extra = requests.get(f"{AKSHARE_API}/batch?codes=000016,000300,000688,399005", timeout=10).json()['data']



# 3. 行业板块涨跌(潜在热点方向)

industry = requests.get(f"{AKSHARE_API}/industry_board", params={

    "order": "asc", "page": 1, "page_size": 10

}, timeout=30).json()['data']

# 升序:跌最多的在前,用于观察恐慌方向



# 4. 概念板块(热门题材)

concept = requests.get(f"{AKSHARE_API}/concept_board", params={

    "order": "asc", "page": 1, "page_size": 10

}, timeout=30).json()['data']



# 5. 今日涨停情绪(判断市场热度)

zt = requests.get(f"{AKSHARE_API}/zt_pool", params={"page": 1, "page_size": 20}, timeout=10).json()

第2步:生成预测报告

按以下格式输出(报告编号 HERMES-PRED-YYYYMMDD):


# 🌅 Hermes 早盘预测

| YYYY-MM-DD

**报告编号**:HERMES-PRED-YYYYMMDD



## 一、隔夜外盘

| 市场 | 点位 | 涨跌幅 |

|------|------|--------|

| 道琼斯 | | |

| 纳斯达克 | | |

| 标普500 | | |

| 日经225 | | |

| 恒生指数 | | |



## 二、今日预测

- **方向**:看多 / 看空 / 震荡

- **置信度**:低 / 中 / 高

- **上证区间预测**:[ ___ , ___ ]

- **预测依据**:

  1. ...

  2. ...

  3. ...



## 三、今日潜在热点板块

1. **板块名** — 触发逻辑

2. ...



## 四、关键点位

- 上证支撑:___  压力:___

- 破位警戒:___  突破信号:___



## 五、风险提示

...



---

*仅供研究参考,不构成投资建议*

第3步:保存到数据库


python3 ~/.hermes/skills/astock-monitor/astock_db.py save_report \

  --id "HERMES-PRED-$(date +%Y%m%d)" \

  --type "morning_pred" \

  --date "$(date +%Y-%m-%d)" \

  --content "《报告全文》"



python3 ~/.hermes/skills/astock-monitor/astock_db.py save_prediction \

  --date "$(date +%Y-%m-%d)" \

  --direction "bullish|bearish|sideways" \

  --confidence "low|medium|high" \

  --sh-low 3000 \

  --sh-high 3100

---

任务二:收盘日报(每个交易日 15:10 后)

#

触发词

"收盘日报" / "今天收盘怎么样" / "日报"

#

执行步骤

第0步:检查历史数据(⚠️ 必做,不要直接跳到第1步)


# 列出所有报告,确认是否有上月/上周/上交易日数据

python3 ~/.hermes/skills/astock-monitor/astock_db.py list_reports



# 查今日早盘预测

python3 ~/.hermes/skills/astock-monitor/astock_db.py get_reports \

  --type morning_pred --date "$(date +%Y-%m-%d)"



# 查最近预测状态(包括未评分的)

python3 ~/.hermes/skills/astock-monitor/astock_db.py pred_stats --period week

关键检查

关键执行原则(避免触发 Hermes 安全审批阻塞 / cron 阻塞):

fetch_data.py 已支持的所有子命令(2026-06-03 更新):


python3 ~/.hermes/skills/astock-monitor/fetch_data.py market           # 三大指数

python3 ~/.hermes/skills/astock-monitor/fetch_data.py stock 603456     # 单只个股

python3 ~/.hermes/skills/astock-monitor/fetch_data.py index 000300     # 单指数

python3 ~/.hermes/skills/astock-monitor/fetch_data.py batch 600438,601012   # 批量(指数混查可,股票会被误判)

python3 ~/.hermes/skills/astock-monitor/fetch_data.py industry [asc|desc]

python3 ~/.hermes/skills/astock-monitor/fetch_data.py concept [asc|desc]

python3 ~/.hermes/skills/astock-monitor/fetch_data.py zt

python3 ~/.hermes/skills/astock-monitor/fetch_data.py dt

python3 ~/.hermes/skills/astock-monitor/fetch_data.py lhb

python3 ~/.hermes/skills/astock-monitor/fetch_data.py news

python3 ~/.hermes/skills/astock-monitor/fetch_data.py fund_flow       # 板块资金流

python3 ~/.hermes/skills/astock-monitor/fetch_data.py all              # 一次性抓所有

已验证的场景案例(2026-06-01 收盘日报):

→ 脚本源码与完整用法见 [scripts/fetch_data.py](scripts/fetch_data.py)

第1步:读取今日早盘预测


python3 ~/.hermes/skills/astock-monitor/astock_db.py get_reports \

  --type morning_pred --date "$(date +%Y-%m-%d)"

第2步:搜集收盘数据

用 web_search 搜索:

同时用远程 AKShare API 获取实时数据。推荐直接用现成的 fetch_data.py 脚本(避免 inline + URL 触发安全审批):


python3 ~/.hermes/skills/astock-monitor/fetch_data.py all

# 或者按需分次抓:

python3 ~/.hermes/skills/astock-monitor/fetch_data.py market

python3 ~/.hermes/skills/astock-monitor/fetch_data.py industry desc

python3 ~/.hermes/skills/astock-monitor/fetch_data.py concept desc

python3 ~/.hermes/skills/astock-monitor/fetch_data.py zt

python3 ~/.hermes/skills/astock-monitor/fetch_data.py dt

python3 ~/.hermes/skills/astock-monitor/fetch_data.py lhb

如必须用内联 Python(不推荐,会被安全检查卡),参考上文"关键执行原则":


import requests



AKSHARE_API = "http://43.163.125.59:18888"



# 1. A股收盘指数

indices = requests.get(f"{AKSHARE_API}/market", timeout=10).json()['data']

extra = requests.get(f"{AKSHARE_API}/batch?codes=000016,000300,000688,399005", timeout=10).json()['data']



# 2. 行业板块涨跌(板块复盘)

industry = requests.get(f"{AKSHARE_API}/industry_board", params={

    "order": "desc", "page": 1, "page_size": 20

}, timeout=30).json()['data']



# 3. 概念板块(题材复盘)

concept = requests.get(f"{AKSHARE_API}/concept_board", params={

    "order": "desc", "page": 1, "page_size": 20

}, timeout=30).json()['data']



# 4. 龙虎榜(主力动态)

lhb = requests.get(f"{AKSHARE_API}/lhb", params={"page": 1, "page_size": 20}, timeout=10).json()



# 5. 涨停/跌停股池(市场情绪)

zt = requests.get(f"{AKSHARE_API}/zt_pool", timeout=10).json()

dt = requests.get(f"{AKSHARE_API}/dt_pool", timeout=10).json()

第3步:生成日报(报告编号 HERMES-DAILY-YYYYMMDD)

格式:[见原技能文档]

第4步:保存数据


# 保存日报

python3 ~/.hermes/skills/astock-monitor/astock_db.py save_report \

  --id "HERMES-DAILY-$(date +%Y%m%d)" \

  --type "daily_summary" \

  --date "$(date +%Y-%m-%d)" \

  --content "《日报全文》"



# 更新预测评分

python3 ~/.hermes/skills/astock-monitor/astock_db.py update_prediction \

  --date "$(date +%Y-%m-%d)" \

  --actual "bullish|bearish|sideways" \

  --score 75 \

  --note "偏差原因简述"



# 保存市场快照

python3 ~/.hermes/skills/astock-monitor/astock_db.py save_market \

  --date "$(date +%Y-%m-%d)" \

  --type close \

  --sh 3200 --sz 10500 --chinext 2000 \

  --volume 8500 --northbound 15 --sentiment 6

---

任务三:每周周报(每周五 15:30 后)

#

触发词

"周报" / "本周总结" / "这周A股" / cron 周五 15:30 自动触发

#

执行步骤

第0步:周报特定的数据特点(必读)

周报与日报的核心差异:

第1步:读取本周日报 + 预测统计


# 本周日报(往往不完整,只看有的)

python3 ~/.hermes/skills/astock-monitor/astock_db.py get_reports \

  --type daily_summary --limit 5



# 本周预测统计

python3 ~/.hermes/skills/astock-monitor/astock_db.py pred_stats --period week



# 看月报"下月展望"(用于本周对比验证)

python3 ~/.hermes/skills/astock-monitor/astock_db.py get_reports \

  --type monthly --limit 1

第2步:拉取今日(周五)实时数据


python3 ~/.hermes/skills/astock-monitor/fetch_data.py all

第3步:补全本周缺失的日数据(关键步骤)

⚠️ 数据库里 5 天日报大概率不全,按以下优先级补齐:


  baostock-fetch k sh.000001 --start YYYY-MM-DD --end YYYY-MM-DD   # 上证指数

  baostock-fetch k sz.399001 --start YYYY-MM-DD --end YYYY-MM-DD   # 深证成指

  baostock-fetch k sz.399006 --start YYYY-MM-DD --end YYYY-MM-DD   # 创业板指

  baostock-fetch k sz.399005 --start YYYY-MM-DD --end YYYY-MM-DD   # 中小 100

  baostock-fetch k sh.000300 --start YYYY-MM-DD --end YYYY-MM-DD   # 沪深 300

  baostock-fetch k sh.000016 --start YYYY-MM-DD --end YYYY-MM-DD   # 上证 50

  baostock-fetch k sh.000905 --start YYYY-MM-DD --end YYYY-MM-DD   # 中证 500

  baostock-fetch k sh.000852 --start YYYY-MM-DD --end YYYY-MM-DD   # 中证 1000

  # 科创 50(sh.000688)BaoStock 返回 0 行(字段名错位),用 web_search 或 AKShare /index/{code}

用真实收盘价计算周涨跌幅,禁止估算。自检:拉完打印 rows[0]['code'] 确认是 sh.000001 而非 sz.000001

第4步:生成周报(报告编号 HERMES-WEEKLY-YYYYMMDD)

必含章节

第5步:保存周报 + 下周预测


# 保存周报

python3 ~/.hermes/skills/astock-monitor/astock_db.py save_report \

  --id "HERMES-WEEKLY-$(date +%Y%m%d)" \

  --type "weekly" \

  --date "$(date +%Y-%m-%d)" \

  --content "《周报全文》"



# 保存下周预测(用周报"下周展望"的方向 + 区间)

# 注意:save_prediction 的 date 字段填"本周五日期",含义是"对该日当周/下周的预测"

python3 ~/.hermes/skills/astock-monitor/astock_db.py save_prediction \

  --date "$(date +%Y-%m-%d)" \

  --direction "bearish|bullish|sideways" \

  --confidence "low|medium|high" \

  --sh-low 3950 \

  --sh-high 4150

第6步:周报特定的数据陷阱

🆕 "全周普跌"时的 order=asc/desc bug 表现(2026-06-05 周报实战发现):

🆕 指数大跌但 100 只涨停的"撕裂行情"(2026-06-05 实战发现):

🆕 周报"周涨跌幅"计算公式

🆕 周报风险提示必加项 — 亚太科技股映射风险(2026-06-26 周报实战发现):

🆕 周报"数据时间线"陷阱 — 龙虎榜 T+1(2026-06-26 周报实战发现):

🆕 "全周普跌"边界 — 区分日数据 vs 周线(2026-06-26 周报实战发现):

🆕 周报"预测评分"核心原则 — 方向是核心,区间只是辅助(2026-06-26 周报实战修正):

#

周报模板(参考)

参见 [references/weekly-report-template.md](references/weekly-report-template.md) 的完整模板和示例

---

任务四:月报(每月最后交易日 / 手动触发)

#

触发词

"月报" / "本月总结" / "月度报告"

#

执行步骤

第1步:读取本月数据


python3 ~/.hermes/skills/astock-monitor/astock_db.py get_reports \

  --type weekly --limit 5



python3 ~/.hermes/skills/astock-monitor/astock_db.py pred_stats --period month

第2步:搜索月度宏观数据(CPI、PMI、货币政策等)

第3步:拉取月线数据(必走 BaoStock)


# 月报必备的 9 大指数月线——必须用 BaoStock 而非 AKShare(AKShare 只有当天)

baostock-fetch trade_dates --start YYYY-MM-01 --end YYYY-MM-31   # 先确认交易日历

for code in sh.000001 sz.399001 sz.399006 sz.399005 sh.000300 sh.000016 sh.000905 sh.000852; do

  baostock-fetch k $code --start YYYY-MM-01 --end YYYY-MM-DD

done

# 科创 50(sh.000688)BaoStock 字段错位 → 用 AKShare /index/{code} 或 web_search 补

月线计算公式(禁止估算):

第4步:板块判断工作流(🆕 2026-06-29 + 6/30 实战修正)

⚠️ fetch_data.py allindustry_desc / industry_asc 经常返回 null 或跨日缓存——2026-06-29 + 2026-06-30 连续两日都是这样,且 6/29 看到的"全负"实际是 6/26 系统性杀跌日的缓存(隔 3 日),与同日 AKShare /market 实时指数方向完全相反。月报不能直接用板块涨幅榜做"主线分布"判断

月报板块判断三维度替代(必走):

第5步:半年线 / 全年线附加章节(🆕 2026-06-30 实战发现)

⚠️ 半年度收官日(6/30)和全年度收官日(12/31)的月报必须额外呈现"半年线成绩单" / "全年线成绩单"——这是用户复盘的重要数据点。2026-06-30 实战:用户对"上半年 +64.25% 科创 50 / +35.58% 创业板 / 个股涨跌幅中位数 -14.0%"等信息有强反馈需求。

半年线 / 全年线章节必含

第6步:生成月报(报告编号 HERMES-MONTHLY-YYYYMM)

结构:

第7步:预测复盘打分明示引用周报打分规则

月报"预测复盘"章节给每月所有预测评分时,沿用周报打分规则(方向 > 区间 > 板块):
- 方向正确 + 区间内 + 板块验证 ≥50% → 80-90
- 方向正确 + 区间内 + 板块验证 <50% → 65-75
- 方向正确 + 区间外 → 50-60
- 方向反向 + 区间外 → 10-25
完整规则见本文"周报"任务六第 6 条。不要重新发明打分逻辑。

第8步:保存


python3 ~/.hermes/skills/astock-monitor/astock_db.py save_report \

  --id "HERMES-MONTHLY-$(date +%Y%m)" \

  --type "monthly" \

  --date "$(date +%Y-%m-%d)" \

  --content "《月报全文》"



python3 ~/.hermes/skills/astock-monitor/astock_db.py save_prediction \

  --date "$(date +%Y-%m-%d)" \

  --direction "bullish|bearish|sideways" \

  --confidence "low|medium|high" \

  --sh-low 4000 \

  --sh-high 4250

---

任务五:年报(手动触发 / 每年12月31日)

#

触发词

"年报" / "年度总结" / "今年A股回顾"

#

执行步骤

第1步:读取全年数据


python3 ~/.hermes/skills/astock-monitor/astock_db.py get_reports \

  --type monthly --limit 12



python3 ~/.hermes/skills/astock-monitor/astock_db.py pred_stats --period year

第2步:搜索全年宏观数据和重大事件

第3步:生成年报(报告编号 HERMES-ANNUAL-YYYY)

结构:

第4步:保存


python3 ~/.hermes/skills/astock-monitor/astock_db.py save_report \

  --id "HERMES-ANNUAL-$(date +%Y)" \

  --type "annual" \

  --date "$(date +%Y-%m-%d)" \

  --content "《年报全文》"

---

查询历史数据

当用户问"查看历史报告"、"上周说了什么"、"预测准确率"时:


# 列出所有报告

python3 ~/.hermes/skills/astock-monitor/astock_db.py list_reports



# 查看某类报告

python3 ~/.hermes/skills/astock-monitor/astock_db.py get_reports --type daily_summary --limit 10



# 查看预测统计

python3 ~/.hermes/skills/astock-monitor/astock_db.py pred_stats --period month

---

数据库位置

~/.hermes/skills/astock-monitor/astock.db

#

数据库表结构(重要:直接SQL查询时使用这些列名)

reports 表

predictions 表(注意:列名与CLI参数不同)

market_data 表(注意:列名与CLI参数不同)

直接SQL查询示例(用于调试):


import sqlite3

conn = sqlite3.connect('/root/.hermes/skills/astock-monitor/astock.db')

c = conn.cursor()

c.execute("SELECT pred_date, direction, confidence FROM predictions ORDER BY pred_date DESC")

# 注意:用 pred_date,不是 date

---

已知限制与注意事项

新坑登记处:本次及历次报告任务中发现的所有"接口行为异常 / API bug / cron 错配 / 推送失败"等,LEARN 阶段必须追加到 [references/pitfalls.md](references/pitfalls.md)(按时间倒序,只追加不删)。

#

🆕 板块成分股接口(2026-06-02 实战发现)

/industry_board/{board_code}/concept_board/{board_code} 这两个接口路由存在 + OpenAPI spec 定义返回 200,但实际 data: null(东方财富源 rc=102 限流)。当前 akshare 远程 API 无法提供板块成分股

兜底方案

避免瞎编:不要在 API 失败时编造一个看似合理的成分股列表。要么用兜底名单 + 明确说明"非完整名单",要么直接告诉用户"接口不可用,需要查 Wind/同花顺"。

#

🆕 cron schedule 时区陷阱(2026-06-02 实战发现)

本 skill 配套 5 个 cron 任务,实测发现全部错配时区——因为最初的 cron 是用"北京小时" 写的,但 hermes cron schedule 字段是 UTC

Job当时 schedule实际北京跑错的原因
968d0c94b4db 每日A股早参0 1 * * 1-509:00✅ 凑巧对(作者算出 1=9-8)
506c5b882bf9 每日市场收报0 7 * * 1-515:00✅ 凑巧对
~~2fc02a348941 A股早参~~10 9 * * 1-517:10❌ 错配(应该是 10 1 * * 1-5
~~a72ca776c856 A股收盘日报~~15 15 * * 1-523:15❌ 错配(应该是 15 7 * * 1-5
~~3645f39f0253 A股周报~~30 15 * * 5周五 23:30❌ 错配(应该是 30 7 * * 5
~~65ca0a04850b A股月报~~30 7 28-31 * 5月末周五 15:30✅ 凑巧对
~~1607f8503848 A股年报~~30 7 31 12 *12-31 15:30✅ 凑巧对

新装本 skill 必读:每个 cron 任务 schedule 字段填的是 UTC 数字,不是北京小时。装好后立即跑 hermes cron list 验证每个任务的实际北京跑的时间是不是预期的。

修复方法:用正确 UTC 时间重建(删除 + 重建):


hermes cron rm JOB_ID

hermes cron create "<正确 UTC>" --name "..." --skill astock-monitor --deliver origin

用户反馈模式:"为什么现在给我推早盘?" / "为什么 X 应该在 Y 时间跑却 Z 时间跑?"——遇到这种反馈先怀疑 schedule 时区错配,列出来让用户确认。

详细检测+修复脚本见 references/hermes-cron-no-agent-gotchas.md 第 8 节(这是 polysilicon-monitor skill 的共享参考文档,跨 skill 通用)。

重要:"用户说删 X" ≠ "删所有看起来错配的 X"——只删用户明确指出的那一个,其他同类错配的先汇报给用户决定(见参考文档第 9 节"范围纪律")。

#

🆕 "超跌" vs "今日杀跌" — 静态 PE/PB 代理法

用户问"超跌"时常见误区:把当日跌幅榜当作超跌反弹候选。今日杀跌 ≠ 长期超跌

当前数据接口限制

静态代理法(按此汇报,不要用直觉判断):

条件含义
pe < 0 AND pb < 1长期超跌嫌疑(亏损 + 资金不看好)
pb < 0.8严重破净,可能逼近估值底
change_pct < -3% AND pe > 0 AND pb > 1今日杀跌但估值正常(可能只是技术性回调)
pe > 100 AND change_pct < -3%高位题材杀跌,不是机会是泡沫破

汇报模板(必须区分这三类,不要混着说):

详见 [references/akshare-api-notes.md](references/akshare-api-notes.md) "实战发现的细节" 第 2 节。

#

⚠️ 关键陷阱:首日不一定是冷启动(先查库再下结论)

错误示例:报告里写 "5月31日早盘预测 (HERMES-PRED-20260531)" — 但 5月31日是周日,A股不开盘,根本不会有这条预测!

正确做法:list_reports 查最近 morning_pred 的 ID 和日期,按查到的实际值填

#

服务器网络限制(已解决:远程 AKShare API + 多源备份)

本服务器(IP 103.7.137.80)是中国内地 VPS,国际出口带宽极小,实测:

首选:远程 AKShare API(http://43.163.125.59:18888)

第二备份:腾讯财经 qt.gtimg.cn(无需登录,单次请求稳定)——见下文详细说明

第三备份:Tushare Pro(如果远程 AKShare 不可用,注册 https://tushare.pro 获取 token):


uv venv /tmp/tushare_env --python 3.11 -q

/tmp/tushare_env/bin/pip install tushare -q

/tmp/tushare_env/bin/python -c "

import tushare as ts

ts.set_token('你的token')

pro = ts.pro_api()

df = pro.daily(ts_code='000001.SZ')

"

#

已知接口行为(必读)

/industry_board/concept_board — 真实参数(2026-06-15 实测修正)


  r = requests.get(f"{API}/industry_board?limit=500", timeout=30).json()

  data_sorted = sorted(r['data'], key=lambda x: x.get("change_pct", 0), reverse=True)

  top5 = data_sorted[:5]  # 涨幅榜

  bot5 = data_sorted[-5:][::-1]  # 跌幅榜

  return {

      "data": top5 + bot5,

      "note": "⚠️ /industry_board 源是 push2delay (15分钟延迟), 实际是上一交易日收盘数据"

  }


  # 涨最多(true desc order)

  r = requests.get(f"{API}/industry_board?sort=desc&sortby=change_pct&page_size=10", timeout=30)

  # 跌最多(true asc order)

  r = requests.get(f"{API}/industry_board?sort=asc&sortby=change_pct&page_size=10", timeout=30)

  # 成交额榜(注意:server 端 sortby 不生效,必须客户端 reverse)

  r = requests.get(f"{API}/industry_board?sort=desc&sortby=amount&limit=500", timeout=30)

/zt_pool/dt_pool — 新三板/北交所已过滤

板块接口 timeout 要大

/fund_flow 个股资金流接口格式异常

/research_report 研报接口 timeout

#

🆕 接口行为细节(2026-06-03 新增)

/industry_board/concept_board 分页行为(按 page_size 不同)

/fund_flow 必须带 symbol 参数,否则 422

/research_report 不支持个股筛选


  reports = requests.get(f"{API}/research_report", params={"begin_time": "2026-05-01", "end_time": "2026-06-03", "page_size": 200}, timeout=30).json()['data']

  tongwei_reports = [r for r in reports if r.get('stock_code') == '600438']

/batch 把股票代码误判为指数

/news 字段双层嵌套 + codes 是数组


  news = requests.get(f"{API}/news").json()

  items = news.get('data', [])

  for x in items:

      if '600438' in x.get('codes', []):

          print(x['title'])

无 K 线接口 — 技术分析受限

指数涨 ≠ 全市场涨 — 真实盘面结构判断

接口字段名 = 英文(不是中文)

#

🆕 "指数涨 ≠ 全市场涨" 模板(盘面结构汇报必用)


📊 真实盘面结构:

- 指数层:上证 +0.11% / 深证 +1.47% / 创业板 +2.55%(看似强势)

- 板块层:20 个行业板块全负,最大跌 -4.86%(实际普跌)

- 结论:⚠️ 权重股拉指数 + 中小盘普跌(典型分化行情)

#

指数/股票代码参考

⚠️ 用 BaoStock 拉指数 K 线必须显式带前缀(如 sh.000001 / sz.399006),否则会路由到股票代码(sz.000001 = 平安银行,价格 11 元而非 4000+ 点)。详见 references/baostock-local-source.md "指数代码必须显式加 sh./sz. 前缀" 一节。
类型代码名称BaoStock prefix
上证指数000001上证指数sh.000001
深证成指399001深证成指sz.399001
创业板指399006创业板指sz.399006
中小100399005中小100sz.399005
上证50000016上证50sh.000016
沪深300000300沪深300sh.000300
科创50000688科创50sh.000688(此条 0 行,需换 web_search 或 AKShare /index)
中证500000905中证500sh.000905
中证1000000852中证1000sh.000852
中证消费000932中证消费
中证医药000933中证医药

#

🆕 本机 BaoStock:历史 K 线 / 财务 / 交易日历补充源(2026-06-18 部署)

远程 AKShare API 负责实时行情、板块、涨跌停、公告;BaoStock 负责补 AKShare 缺口:历史 K 线、交易日历、复权因子、财报、指数成分股、宏观货币数据。

本机命令已部署:


baostock-fetch k 600438 --start 2026-06-01 --end 2026-06-18

baostock-fetch finance profit 600438 --year 2026 --quarter 1

baostock-fetch trade_dates --start 2026-06-01 --end 2026-06-18

使用边界:BaoStock 不做实时行情;盘中价格/板块仍用 AKShare 或腾讯行情。

完整说明、命令清单、实测样例见 [references/baostock-local-source.md](references/baostock-local-source.md)。

#

🆕 第二备份:腾讯财经 qt.gtimg.cn(A股实时行情)

→ 备份数据源(腾讯/Sina/东财/Tushare/BaoStock)详见 [references/backup-data-sources.md](references/backup-data-sources.md)

→ 抓"为什么今天跌"的盘后叙事搜索词模板见 [references/web-search-patterns.md](references/web-search-patterns.md)

适用场景:远程 AKShare API 不可用或北向资金接口延迟时

核心用法(无需登录,无需 Referer):


import requests



def get_qt_quote(codes):

    """批量获取A股指数/股票实时行情

    codes: 逗号分隔,如 "sh000001,sz399001,sz399006,sh000300,sh000688"



    🆕 坑 #56 修复 (2026-06-29): 用 fields[2] (6位代码) 作为字典 key,

    避免 key 字符串解析错位. 旧版用 key.split('v_')[1].split('~')[0]

    会在混合 indices/stocks 时返回错位数据.

    """

    url = f"https://qt.gtimg.cn/q={codes}"

    r = requests.get(url, timeout=10)

    r.encoding = 'gbk'  # 关键:腾讯返回GBK

    quotes = {}

    for line in r.text.split(';'):

        if '="' not in line:

            continue

        # 格式: v_sh000001="1~上证指数~000001~..."

        key, val = line.split('="', 1)

        fields = val.rstrip('";').split('~')

        if len(fields) < 40:  # 坑 #53: 字段不足 40 位可能是股票代码被吞

            continue

        # 关键修复: 直接用 fields[2] (永远是 6 位代码) 作为字典 key

        # 不要用 key 字符串 (坑 #56)

        code_6digit = fields[2]

        quotes[code_6digit] = {

            'name': fields[1],

            'code': fields[2],

            'price': float(fields[3]) if fields[3] else None,

            'change_pct': float(fields[32]) if fields[32] else None,

            'change_abs': float(fields[31]) if fields[31] else None,

            'volume': fields[6],       # 成交量(手)

            'amount': float(fields[37]) if fields[37] else None,  # ⚠️ 单位是"元",不是"亿元"

            'high': float(fields[33]),

            'low': float(fields[34]),

            'open': float(fields[5]),

            'prev_close': float(fields[4]),

        }



    # 🆕 smoke test (坑 #56 验证): 拉指数后必须验证 name

    if '000001' in quotes:

        assert quotes['000001']['name'] == '上证指数', \

            f"qtimg 解析错位: {quotes.get('000001')}"

    return quotes



# 使用

codes = "sh000001,sz399001,sz399006,sh000300,sh000688,sh000016,sz399005"

quotes = get_qt_quote(codes)

# quotes['000001'] = {'name': '上证指数', 'price': 4057.74, 'change_pct': -0.27, ...}

关键字段位(不完整列表,需以实测为准)

限制

已验证可用代码格式

#

🆕 第三备份:东方财富 push2 备选端口(仅指数行情)

适用场景:需要拉单只指数 K线/历史数据


import requests

import json



# 拉 K线(替代不稳定的 qt.gtimg.cn)

def get_kline(code="sh000001", count=5):

    """拉指数K线数据"""

    url = "https://web.ifzq.gtimg.cn/appstock/app/fqkline/get"

    params = {"param": f"{code},day,,,{count},qfq"}

    r = requests.get(url, params=params, timeout=10)

    data = r.json()['data'][code]

    return data  # 包含 day[[日期,开,收,高,低,成交量,成交额,...], ...] 和 qt



# 用法

kline = get_kline("sh000001", 5)

for row in kline.get('day', []):

    print(f"{row[0]}: 开={row[1]} 收={row[2]} 高={row[3]} 低={row[4]}")

注意

#

美股行情(备用:Sina,仍需 GBK decode)

如远程 AKShare API 不可用,美股数据用 Sina hq(GBK 编码):


curl -s -H "Referer: https://finance.sina.com.cn" \

  "https://hq.sinajs.cn/list=gb_$dji,gb_$inx,gb_$compq" | decode GBK

道琼斯 $dji、标普500 $inx、纳斯达克 $compq

#

黄金期货(避险情绪,仍需 GBK decode)

如需黄金期货数据:


curl -s -H "Referer: https://finance.sina.com.cn" \

  "https://hq.sinajs.cn/list=hf_GC" | decode GBK

---

🆕 紧急情况处理流程(必读)

当远程 AKShare API 完全不可用时,按以下优先级回退:

当某个端点返回 502/403/timeout 时:

---

5 端点降级工具 (2026-06-15 新增)

6/15 在 43.163.125.59 server 加了 5 个新端点 (hot_up / hot_keyword / hot_search_baidu / news_economic_baidu / news_main_cx), 客户端降级方案在 [references/hot-endpoints-fallback.md](references/hot-endpoints-fallback.md), 工具脚本在 [scripts/news_fetcher.py](scripts/news_fetcher.py)。

免责声明

所有分析仅供研究参考,不构成投资建议。

The user has provided the following instruction alongside the skill invocation: [IMPORTANT: You are running as a scheduled cron job. DELIVERY: Your final response will be automatically delivered to the user — do NOT use send_message or try to deliver the output yourself. Just produce your report/output as your final response and the system handles the rest. SILENT: If there is genuinely nothing new to report, respond with exactly "[SILENT]" (nothing else) to suppress delivery. Never combine [SILENT] with content — either report your findings normally, or say [SILENT] and nothing more.]

你是 A 股投资复盘助手。每天 15:30 北京时间(A 股收盘后),自动执行以下任务:


   python3 /root/.hermes/skills/astock-pick/scripts/astock_pick.py review


   python3 /root/.hermes/skills/astock-pick/scripts/astock_pick.py status

【A 股每日复盘 - YYYY-MM-DD】

📊 昨日推荐 vs 今日表现

📈 胜率统计

💭 反思

🔄 明日策略

重要纪律

首次执行:用 cron 的 "Last run" 字段判断今天是否已经跑过,如果没跑过,今天 15:30 后立即跑一次。

荐股输出框架(2026-06-18 用户确认)

后续每日推荐必须使用 astock-pick 的“每日推荐新版框架”:

Error


RuntimeError: HTTP 500: unknown error, 999 (1000)