← 返回 Skill 列表
extension
分类: 数据与分析无需 API Key

XBJHB-Shaanxi-Securities-Market-Analyzer

该方案通过获取陕西省证券业协会官网分支机构公开数据,实现对用户指定的地区(城市)营业部(简称或全称)进行数据获取和市占率分析。

person作者: user_962fa9e0hubcommunity

区域市场占有率分析(陕西省证券业协会数据)

口径提示(2026-09-02 起):综合市占率采用方案B——客户资产 40% + 股基交易额 35% + 客户数 25%, 营业收入、净利润不参与加权,另设「盈利能力观察」章节列示。详见下方专章。

v1.3.4 提示(2026-09-12 · 视觉规范 V2 统一):单网点与多主体两类 HTML 报告现已共用同一套视觉规范 V2, 样式与交互全部收敛到三份共享资产 assets/report_style_v2.css / assets/report_script_v2.js / assets/report_template.html(视觉来源=用户定稿的 西部证券陕西省内对标开源证券竞争力分析报告_优化版.html)。 最重要的一条:改视觉只改这三份资产,禁止在渲染脚本里再内联一份 CSS, 也禁止新建第二份样式表;趋势图章节 id 必须是 sec-trend(写错则折线图空白)。 详见「单网点 HTML 报告(视觉规范 V2)」与「HTML 可视化报告(视觉规范 V2)」两章。

v1.3.3 提示(2026-09-12):省级链路已补齐两个脚本——scripts/province_collect.py(省级数据采集) 与 scripts/province_workbook.py(省级 6-Sheet Excel 审计底稿)。 最重要的一条:协会接口 gstjdata 的 city 参数必须传空串 "" 才返回全省, 传"陕西省/全省/陕西"一律 0 条;用模式一脚本跑 --city "陕西全省" 会静默采到 0 家并覆盖缓存, 省级数据请一律走 province_collect.py。详见「模式二 → 全省对标」与「省级 Excel 审计底稿」专章。

v1.3.2 提示(2026-09-10):单网点定版渲染器 scripts/branch_report_render.py 已完成全参数化修复, 支持任意城市与任意季度区间(此前实质是「汉中 2023Q1–2026Q2 案例模板」,换参数会崩溃或输出错误叙事)。 交付前请务必执行「单网点 HTML 交付前自查清单」第 8 条跨城市/跨区间回归检查。 版本标记九处统一为 1.3.4:SKILL.md frontmatter、_meta.json、_skillhub_meta.json, 以及 branch_report_render.py / group_compare.py / province_collect.py / province_workbook.py / sxsfa_region_analysis.py / verify_report_v2.py 六个脚本 docstring。

用途

基于陕西省证券业协会官网(https://www.sxsfa.org.cn/marketData/industryData)公开季度数据, 对指定地区(城市)的证券分支机构进行区域市场占有率分析,自动完成「参数确认 → 数据采集 → 分析表生成」全流程,输出符合审计底稿规范的 4-Sheet Excel 分析表。

何时使用

  • 用户要求分析某地区某证券营业部的市场占有率 / 竞争地位
  • 用户要求抓取陕西省证券业协会网站的分支机构季度数据
  • 用户要求对标同城营业部、整理横向/纵向对比表
  • 审计场景:营业部离岗/离任审计、分支机构经营分析、区域竞争格局研究

两种分析模式(先判断用哪个)

| 模式 | 适用场景 | 脚本 | 产出 | |------|---------|------|------| | 单网点 | 分析某一家具体营业部的区域地位 | sxsfa_region_analysis.py | 4-Sheet Excel | | 多主体对标 | 按券商主体对比(如"西部证券在汉中的营业部排名,对标开源证券") | group_compare.py | JSON + HTML 报告 |

判断依据:用户说的是一个网点("XX营业部")→ 单网点;说的是一家券商在该区域的全部网点或两家券商对比 → 多主体对标。


综合市占率口径(方案B · 2026-09-02 起生效)

口径定义

综合市占率 = 客户资产份额×40% + 股基交易额份额×35% + 客户数份额×25%
营业收入、净利润 —— 不参与加权,仅作「盈利能力观察」指标列示

代码层面(scripts/sxsfa_region_analysis.py):

  • WEIGHTS = {"assets": 0.40, "trading": 0.35, "clients": 0.25} —— 仅三维,用于加权合成
  • OBSERVE = ("revenue", "profit") —— 观察类,不加权
  • ALL_METRICS = WEIGHTS + OBSERVE —— 用于总量汇总与单项份额计算(观察类仍要算份额)

改权重时务必区分这三个常量:若把 revenue/profit 直接塞回 WEIGHTS, 综合分会被负值利润与基数效应污染;若从 ALL_METRICS 中删掉,观察表会 KeyError。

为什么营收、净利润不加权

| # | 问题 | 实证 | |---|------|------| | 1 | 负份额:亏损主体净利润为负,份额为负值,直接抵扣综合分,份额之和不再闭合 | 开源证券咸阳期初净利润 −22 万元 → 份额 −1.17% | | 2 | 分母趋零爆炸:区域整体接近盈亏平衡时份额趋向无穷,区域合计为负时符号翻转 | 咸阳压力测试:区域净利 100 万 → 西部"份额" 1,783%;1 万 → 17,830%;−50 万 → −3,566% | | 3 | 基数效应:区域盈利面整体改善会稀释高盈利主体的份额,份额下降 ≠ 竞争力下降 | 咸阳区域净利 +427.9%、西部净利 +320.0%(增 3.2 倍),西部份额却 −19.21pp | | 4 | 重复计权:佣金收入 ≈ 交易额 × 费率,与股基交易额高度相关;营收还含总部业务归属与非佣金收入 | 西部营收份额 57.29% ≫ 资产份额 38.26% |

方案B 对结论的影响(务必向用户说明)

同一份数据换口径后结论方向可能反转,重算历史报告时必须重新解读,不能直接套用旧结论:

| 地区 | 主体 | 旧口径(30/25/20/15/10) | 方案B(40/35/25) | |------|------|------------------------|-------------------| | 咸阳 | 西部证券 | 45.93 → 39.82(−6.11pp) | 37.26 → 32.35(−4.90pp) | | 咸阳 | 开源证券 | 7.63 → 7.81(+0.17pp) | 8.53 → 8.35(−0.18pp,方向反转) | | 汉中 | 西部证券 | 50.42 → 49.27(−1.15pp) | 41.80 → 43.46(+1.65pp,方向反转) | | 汉中 | 开源证券 | 10.64 → 12.76(+2.11pp) | 12.03 → 14.34(+2.31pp) |

披露要求(必须出现在报告页脚)

综合市占率口径=客户资产40%+股基交易额35%+客户数25%(方案B); 营业收入、净利润不计入综合市占率(存在负值、分母趋零及基数效应),仅作盈利能力观察指标


模式一:单网点分析

第一步:与用户确认 5 项参数(必须逐项确认,不得跳过)

向用户确认以下参数,全部确认后方可执行。建议一次列出完整清单,请用户核对或直接回答:

| # | 参数 | 说明 | 示例 | |---|------|------|------| | 1 | 查询地区 | 城市名,可带或不带"市"后缀,脚本自动规范化 | 汉中市 / 汉中 | | 2 | 目标营业部 | 简称或全称,脚本按关键词自动匹配全称 | 汉中东大街 / 西部证券股份有限公司汉中东大街证券营业部 | | 3 | 期初季度 | 年份+季度,格式灵活(2023Q1 / 2023年一季度 / 2023-1) | 2023Q1 | | 4 | 期末季度 | 同上;期初不得晚于期末 | 2026Q2 | | 5 | 保存路径 | 生成 xlsx 与 json 数据的本地目录 | D:/区域市占率分析 |

确认方式:直接向用户复述参数清单并请其确认,或用 AskUserQuestion 分组确认。 若用户只给了部分参数,缺项主动追问,补齐后再执行。

第二步:运行采集+生成脚本

脚本位于 scripts/sxsfa_region_analysis.py,用参数模式执行(无需加载脚本内容到上下文):

python <skill_dir>/scripts/sxsfa_region_analysis.py \
  --city "汉中市" --target "汉中东大街" \
  --start "2023Q1" --end "2026Q2" \
  --outdir "D:/区域市占率分析"
  • 脚本会自动完成:城市名规范化(含"市"后缀校验)→ 季度序列生成 → 营业部级+城市级数据采集(写入 {城市}_quarterly_data.json 缓存)→ 目标营业部自动匹配(精确匹配 → 关键词包含匹配,多匹配会报错列出候选)→ 生成 xlsx。
  • 也可用 --interactive 进入交互模式由脚本逐项提问(供用户直接命令行使用)。
  • 如需自定义输出文件名:--outname "自定义.xlsx"。
  • Python 依赖:openpyxl(见 scripts/requirements.txt);其他用标准库,无需联网代理。

第三步:运行定版 HTML 渲染(单网点定版报告)

生成 xlsx 后,若需「旧模板视觉 + 方案B」的定版 HTML 报告(交付标准样式),再运行:

python <skill_dir>/scripts/branch_report_render.py \
  --city "汉中市" --target "西部证券股份有限公司汉中东大街证券营业部" \
  --start "2023Q1" --end "2026Q2" --outdir "D:/区域市占率分析"
  • 前置:同 outdir 须已存在 {城市}_quarterly_data.json 缓存与 {城市}地区分支机构区域市场占有率分析表.xlsx(即先跑第二步)。
  • --target 支持简称;与光大证券等重名营业部并存时(同城同名),必须传完整全称(脚本会列出候选)。
  • 视觉规范读 assets/report_style_v2.css + assets/report_script_v2.js + assets/report_template.html (三份共享资产,与多主体对标报告同一套;不要再去改别的样式文件); 输出默认 {城市}地区分支机构区域市场占有率分析报告.html。
  • 报告结构见下「单网点 HTML 报告(定版 · 视觉规范 V2)」专章。

第四步:校验输出并反馈

  • 确认 xlsx 已生成、4 个 Sheet 齐全(01_横向对比 / 02_纵向趋势 / 03_区域背景 / 04_集中度与梯队)。
  • 若已生成定版 HTML,按下述专章「交付前自查清单」逐项核对(重点:无旧五维口径残留、审计关注置末、方案B披露完整)。
  • 向用户报告关键结论:目标机构综合市占率排名及变化、期初/期末客户资产、 区域合计、机构变动(退出/新进)等(脚本终端输出已包含核心信息,可直接转述)。

第五步:展示交付物

用 present_files 展示生成的 xlsx、定版 HTML(若生成)与 json 缓存文件路径。


模式二:多主体对标(v1.3.1)

按公司主体聚合某地区全部网点,对比两家券商的区域竞争力,同时保留各网点的个体排名。

第一步:与用户确认 6 项参数

| # | 参数 | 说明 | 示例 | |---|------|------|------| | 1 | 查询地区 | 城市名,自动规范化 | 汉中市 / 汉中 | | 2 | 主体 A | 主要分析对象(券商简称) | 西部证券 | | 3 | 对标主体 B | 对标对象(券商简称) | 开源证券 | | 4 | 期初季度 | 2023Q1 / 2023年一季度 | 2023Q1 | | 5 | 期末季度 | 同上 | 2026Q2 | | 6 | 保存路径 | 本地目录 | D:/区域对标分析 |

另需确认交付形式:HTML 报告 / JSON 数据 / 两者。

全省对标(省级主体聚合 · 2026-09-03 扩展;2026-09-12 补齐采集脚本)

模式二同样支持陕西省全省维度(全省 300+ 家分支、70+ 券商主体),适用于"省内券商 vs 券商"分析:

# ① 先采集省级缓存(此前需人工准备,2026-09-12 起由脚本生成)
python <skill_dir>/scripts/province_collect.py \
  --start "2023Q1" --end "2026Q2" --outdir "F:/区域对标分析" [--force]

# ② 再跑对标(命中①的缓存即秒级出结果)
python <skill_dir>/scripts/group_compare.py \
  --city "陕西全省" --group-a "西部证券" --group-b "开源证券" \
  --start "2023Q1" --end "2026Q2" --outdir "F:/区域对标分析" --html

⚠️ 最关键的一条(2026-09-12 实测):协会接口 /industrydata/gstjdata 的 city 参数 必须传空串 "" 才返回全省全部营业部(2026Q2 → 315 家 / 10 个地市); 传 "陕西省" / "全省" / "陕西" 一律返回 0 条。 sxsfa_region_analysis.py 的 collect() 会把 city 原样传给接口, 因此用模式一的脚本跑 --city "陕西全省" 会静默采到 0 家并把缓存覆盖为空—— 省级数据只能走 province_collect.py(或先备份缓存)。踩坑记录:2026-09-12 首次用模式一脚本 采省级,14 个季度全部 0 家,缓存被写成空壳,后从备份恢复并用新脚本重采。

  • normalize_city 已放行省级别名(陕西全省 / 全省 / 陕西省 / 陕西),统一返回 陕西全省。
  • 缓存文件固定为 {outdir}/陕西全省_quarterly_data.json,须含顶层 "city": "陕西全省" 与 quarters[].branches[].{name,city,assets,trading,clients,revenue,profit}(branch 须带 city 字段, 供网点明细按城市归类)。province_collect.py 直接产出该结构,不需要二次改写。
  • 省级聚合会对所有含券商前缀的分支/分公司归并(如 西部证券…57 家、开源…54 家期末), branch 数量逐季变化反映机构进退(开源 33→54 家即为其省内扩张体现)。注意分公司也计入网点数 (如「西部证券股份有限公司宝鸡分公司」)。
  • HTML 内展示标题/正文的「陕西全省」建议统一后处理为「陕西省」;文件名可用 --outname 自定义(如 西部证券陕西省内对标开源证券竞争力分析报告.html)。

省级 Excel 审计底稿(2026-09-12 新增)

python <skill_dir>/scripts/province_workbook.py \
  --outdir "F:/区域对标分析" --start "2023Q1" --end "2026Q2" \
  --group-a "西部证券" --group-b "开源证券"

产出 {主体A}陕西省内市场数据底稿.xlsx(6 Sheet,A4 / 仿宋 / 审计底稿版式):

| Sheet | 内容 | |-------|------| | 00_说明 | 数据来源(含 city="" 取数说明)、两种口径、份额分母、主体归并规则、机构变动五态 | | 01_主体对标趋势 | 逐季度:主体A/对标B 网点数·客户资产·资产份额·股基交易额·交易额份额·客户数·客户数份额·营收·净利·综合市占率 + 综合差(pp) | | 02_{主体A}分地市 | 期初/期末双时点 × 10 地市:份额 + 主口径综合市占率 与 城市口径综合市占率 两列并置对照 | | 03_{主体A}机构明细 | 期初/期末双时点逐机构:资产、机构级市占率与全省排名、排名变动五态(新进/退出) | | 04_全省主体排名 | 期末 70+ 主体按综合市占率降序,期初对照 + 变动(pp) + 排名变动;主体A/对标B 行高亮 | | 05_全省市场背景 | 逐季度全省汇总规模 + 两主体综合市占率走势 + 主体A 资产占全省比 |

实现要点(易错,务必遵守):

  1. write_table 的 pct_cols 是 1 基 values 索引(判定式为 (cidx-1) in pct_cols,cidx 从 2 起), 极易与列号错位导致"客户资产被渲染成 2505564524.00%"这类低级错误。 本脚本改用 apply_pct_by_header():写入后按表头关键字(份额 / 市占率 / 变动率)统一套 0.00%, 对字符串单元(pp 变动列、排名变动列)无副作用。
  2. 表头列数必须 = 1(label 列)+ len(values)。首列表头即"机构名称/券商主体"等 label 列名; 漏掉它就整体错位一列。
  3. 机构级 vs 主体级口径:Sheet03 的市占率与排名必须按单个机构数值÷全省总量计算, 不可用主体份额与主体排名填充(否则同券商全部网点排名相同)。 校验式:Σ(各机构期末综合市占率) ≈ 主体A 期末综合市占率(安康/陕西实测 13.59% 吻合)。
  4. write_table 的 totals= / highlights= 传的是行序号(从数据行 1 起算)。
  5. 两时点地市顺序统一按期末主口径综合市占率降序,便于期初/期末逐行对照。

第二步:运行对标脚本

python <skill_dir>/scripts/group_compare.py \
  --city "汉中市" --group-a "西部证券" --group-b "开源证券" \
  --start "2023Q1" --end "2026Q2" \
  --outdir "D:/区域对标分析" --html
  • 脚本复用 sxsfa_region_analysis.py 的采集与季度解析能力,数据缓存命名 {城市}_quarterly_data.json,同目录命中缓存则跳过采集。
  • 主体名称须为券商简称(如"西部证券""开源证券"),脚本从营业部全称自动归并; 期末季度找不到该主体时会列出当地全部主体供选择。
  • 内置历史主体归并:国泰君安/海通 → 国泰海通证券。
  • 输出:{城市}_{主体A}_vs_{主体B}.json + {城市}营业部竞争力对标分析报告.html。

第三步:校验并交付

用以下 10 条规则校验 HTML(可照此写校验脚本):顶部 1 chip / hero 无纹理 / KPI 四卡无 inline 样式 / 结论按主体分段 / 增减幅度全染色 / 无 ++X%·--Xpp / 涨绿跌红 / hbar-val 染色 / 双断点 / 页脚口径。

「同业对标」板块硬性结构

「同业对标」必须采用双卡片结构,禁止将所有字段堆入单一表格:

| # | 卡片 | 标题 | 内容 | 列/图条 | |---|------|------|------|---------| | 1 | 表格卡片 | 关键指标双时点对比 | 3 项计权指标(客户资产/股基交易额/客户数)+ 末行「综合市占率」 | 7 列(指标 + 主体A 3 列 + 主体B 3 列),只要市占率期初/期末/变动,不要绝对值与增幅列 | | 2 | 图条卡片 | 期末关键指标对比(紫=主体A 青=对标B) | 客户资产 / 股基交易额 / 综合市占率 三个核心指标的紫+青双条 hbar | 双条比例以双方最大值归一化,紫 #8b5cf6、青 #0ea5e9;末尾双色图例 |

营业收入、净利润不进本板块(不参与加权),改由下方「盈利能力观察」章节承载。

「盈利能力观察」章节(方案B 新增,必选)

位置:紧随「同业对标」之后,「区域主体排名」之前。章节标题 盈利能力观察 <span class="num">营业收入 / 净利润 · 不计入综合市占率</span>。

  • 表格 8 列:观察指标(合并两行,副标「不计入综合市占率」)/ 主体 / 期初(万元) / 期末(万元) / 增幅 / 期初份额 / 期末份额 / 份额变动
  • 每个指标 3 行:主体A(紫 #6d28d9)、主体B(青 #0369a1)、区域合计(灰底,份额列 colspan=3 显示「份额分母」)
  • 增幅安全性:期初 ≤ 0(亏损或为 0)时增幅不具业务含义,显示 n/a,禁止输出 -1333.2% 这类失真数值
  • 份额出现负值时,在指标名后加 <span class="chip chip-down">负值</span>
  • 口径说明 insight:须给出区域营收/净利润增幅、两主体份额变动,并明确 「净利润份额波动主要受区域盈利基数影响,属分母基数效应,不作为竞争力评价依据」; 当区域净利润合计 ≤ 0 时,改提示「份额不具可比性,本报告不据以评价」

表格与图条都要染色加粗;下方的「差距变化」insight 单独放一行,不再嵌入表格。 绝对禁止:在主体对比表中堆叠绝对值列、增幅列、"期初领先/期末领先"列(表格过宽,违反整体版式规范)。

「城市维度」章节(省级对标 · V1.3.1 新增)

位置:紧随「盈利能力观察」之后、「区域主体排名」之前。仅当省级对标(数据 branch 带 city 字段且地市数 > 1)时由 group_compare.py 自动生成(city_section(R));单城市对标自动跳过。

  • 一指标一图,依次罗列,综合市占率排第一位,共 5 张水平条形图卡(配置见 CITY_FIGS): ① 综合市占率(深 #6366f1/浅 #c7d2fe)→ ② 客户资产(#8b5cf6/#ddd6fe)→ ③ 股基交易额 (#3b82f6/#bae6fd)→ ④ 客户数(#10b981/#a7f3d0)→ ⑤ 营业收入·观察(#06b6d4/#a5f3fc)。
  • 口径:城市维度沿用旧报告 5 指标加权(客户资产30%+股基交易额25%+客户数20%+营收15%+净利10%, CITY_WEIGHTS),用于地市穿透;与主报告方案B(40/35/25)并存,仅本章节使用,不改变综合市占率结论。
  • 分母:主体A 该地某指标 / 全省该指标总量 ×100(单指标份额);综合 = 5 指标份额 × 权重加权。
  • 主体归并必须用 company_of(),不得用 '西部证券' in name——名称含"西部证券"的还有 申万宏源西部证券有限公司(西安 3 家网点,独立主体),in 判断会将其误并入西部证券, 导致西安综合市占率虚高约 0.4pp(12.76→9.86 与正确值 12.35→9.41 之差即源于此)。
  • 每图宽度按"图内最大值为 100%"归一化;城市按期初综合市占率降序;行尾附变动幅度 (+0.08pp ↑)/(-0.83pp ↓);图例标注期初(浅)/期末(深)两时点。
  • 章节首尾各一条 insight:首条「城市穿透」点出综合排第一城市与逆势增长城市;尾条「城市观察要点」 动态生成头部 3 市合计变动、股基交易额下行城市数、客户资产逆势提升城市,并给审计/调研建议。

数值单位与空值处理(v1.3.1 修复)

| 场景 | 正确写法 | 错误写法 | |------|---------|---------| | 份额/市占率变动 | -6.11pp(用 bpp()) | -6.11(缺单位,用 b() 会漏 pp) | | 绝对值增幅 | +39.2%(用 b(v,1,"%")) | 混用 pp | | 期初无数据 | 新进 | 崩溃或显示 None | | 期末无数据 | 退出(排名列显示 —) | None - float 触发 TypeError |

网点明细表排名列必须按「新进 / 退出 / 持平 / ↑n / ↓n」五态判定, 不得用 (r0 - r1) if (r0 and r1) else None 后直接比较 dr > 0(退出机构会崩溃)。


输出说明(模式一 · 4-Sheet Excel)

| Sheet | 内容 | 说明 | |-------|------|------| | 01_横向对比 | 期初 vs 期末双时点对比 | 21 列:5 指标双时点+变动率、资产市占率/综合市占率期初期末及变动、排名变化;两时点并集机构,退出/新进自动标注;目标机构行高亮 | | 02_纵向趋势 | 目标机构季度趋势 | 全部季度:资产/交易额/客户数/营收/净利润 + 4 项市占率 + 综合排名 | | 03_区域背景 | 城市级季度汇总 | 机构数/资产/交易额/客户数/营收/利润/两融/产品销售/营销人员 | | 04_集中度与梯队 | 期末季度集中度 | CR3/CR5/CR10、HHI、梯队划分(龙头/头部/腰部/尾部)、5 类风险信号 |

  • 综合市占率权重(方案B):客户资产 40% + 股票基金交易额 35% + 客户数 25% (营业收入、净利润不加权,仅作观察指标;Excel 注2 已同步该口径说明)
  • 样式:A4、仿宋 14pt、标题仿宋加粗 20pt(审计底稿规范)
  • 数据单位:金额万元、客户数个

单网点 HTML 报告(定版 · 视觉规范 V2 · 2026-09-12 统一)

单网点 HTML 与多主体对标 HTML 共用同一套视觉规范 V2(紫蓝设计令牌 + 固定导航 + 阅读进度条 + hero 首屏 + 章节编号徽标 + 卡片体系 + KPI 数字滚动 + 条形增长动画 + 交互式 SVG 折线图 + 滚动揭示), 由 scripts/branch_report_render.py 自动渲染。

共享视觉资产(改一处两份报告同步生效,禁止各写一份):

| 文件 | 作用 | |------|------| | assets/report_style_v2.css | 全部样式(设计令牌 + 14 个分区),单网点与多主体共用 | | assets/report_script_v2.js | 全部交互(进度条/导航、数字滚动、滚动揭示、条形动画、选项卡、趋势图) | | assets/report_template.html | 报告外壳({{CSS}}/{{JS}}/{{趋势数据}}/{{正文}} 等 12 个占位符) |

禁止再往任何一个脚本里内联大段 CSS,也禁止新建 branch_report_style.css 之类的第二份样式表; 报告仍是自包含单文件 HTML——脚本读资产文件后内联进 <style> / <script>,可离线打开。

线性章节顺序(交付标准,禁止乱序)

| # | 章节 | 锚点 id | 说明 | |---|------|---------|------| | 1 | 首屏 KPI 四卡 | —(hero 下方 .kpis) | 综合市占率(方案B)/ 排名变动 / 资产市占率 / 股基交易额市占率 | | 2 | 市占率关键指标对比 | sec-2 | 期初浅/期末深双条,六指标(含营收/净利「观察」、综合为方案B值) | | 3 | 经营规模指标增幅 | sec-3 | 条形 + 橙色区域刻度;净利润行区域增幅可超自身则不加刻度 | | 4 | 详细对比表 · 全部指标 | sec-4 | 8 列;末行综合行(方案B 值 + 排名) | | 5 | 市占率横向对比 · 汇总视角 | sec-5 | 归一化期末一览(左)+ 增幅对照表(右) | | 6 | 盈利能力观察 | sec-6 | 营收/净利两行 + 区域合计行(份额分母)+ 口径说明(基数效应) | | 7 | 季度趋势 | sec-trend | 四维市占率折线(综合/资产/交易/客户)+ NQ 季明细表 + 趋势观察 | | 8 | 区域市场背景 | sec-8 | region-highlight 三卡 + 双时点对比表(人均资产修正值) | | 9 | 同业对标 | sec-9 | 目标券商 vs 对标券商:主体自动归并,B = 期末综合市占率最高的其他券商 | | 10 | 审计关注 · 风险信号识别 | sec-10 | 置于整篇最后(footer 前);卡片套 .risk-panel 琥珀面板,下挂 5 张风险卡 |

⚠ sec-trend 是硬约束,不是命名偏好:report_script_v2.js 的滚动揭示回调里 if (el.closest('#sec-trend') && !trendDrawn) drawTrend(),id 写错则折线图永远是空白容器。 章节编号由 sec(..., no=N) 显式传入(1 为 hero 下方 KPI 总览,正文从 2 起), 不要依赖自增——漏传一处会导致后续章节编号整体错位、导航锚点失配。

硬性规则

  1. 方案B 全套口径:综合市占率 = 客户资产40% + 股基交易额35% + 客户数25%;任何"30%+25%+20%+15%+10%"字样不得出现; 页脚权重公式不带"方案B"前缀(公式 + "营业收入、净利润不计入综合市占率,仅作盈利能力观察指标"即可)。
  2. 营收/净利一律打「观察」标记(obs-tag / 表头注明),份额仍按分支求和分母列示。
  3. 观察份额分母口径:取该城市全部营业部 branch 求和(份额闭合、与历史报告一致)。注意 Excel「03_区域背景」 的营业收入/净利润列取自已方 API 城市级字段,可能与 branch 求和相差约一倍——HTML 一律用 branch 求和口径, 必要处在说明中披露差异。
  4. 审计关注置末:原版中风险模块位于详细对比表后,定版规定其必须为全篇最后(footer 之前), 并在其顶部置入「核心结论」摘要块(标题不带任何方案字样)。
  5. 折线图内不绘制机构名,图例置于图表标题行右侧(同 flex 行)。
  6. 涨=绿(#10b981)/ 跌=红(#ef4444),与旧模板 pos-num/neg-num、delta-up/down、pill-up/down 一致。
  7. 同业对标为自动归并(_grp_name 取全称"证券"前缀);B 找不到(仅一家券商)时输出降级说明,不崩溃。
  8. 风险信号 5 卡(大客户依赖/人均客户资产/盈利质量/客户规模/排名稳定性)数值须动态取自目标与区域数据,不得硬编码。
  9. 数据期间在 hero f-item「数据期间」中显示为 2024Q1 → 2026Q2(10 个季度); 按参考件规范一律纯文本(不加 <span class="accent"> 强调色、不做季度标黄), 季度串一律由 START/END/NQ 拼出,禁止写死年份。
  10. 指标调色板(2026-09-03 定):客户资产紫 #6366f1、股基交易额蓝 #5b8def、客户数主题绿 #10b981、 营业收入(观察)青 #06b6d4、净利润(观察)琥珀橙 #f97316、综合市占率主题橘黄 #f59e0b (相应浅色:c7d2fe / bfdbfe / a7f3d0 / a5f3fc / fed7aa / fde68a)。KPI 图标与趋势折线同步该色。
  11. 外显文案不得出现「方案B /(方案B)/(方案B口径)/ · 方案B / 方案B口径」字样(口径只以页脚/图注的 权重公式形式保留:综合市占率=客户资产40%+股基交易额35%+客户数25%,并注明营业收入、净利润不计入); header 不设「口径」chip;图表标题、标签、同业对标结论等处一律不出现内部分案名。
  12. 同业对标表头券商名(<th colspan="3">)须 text-align:center。
  13. 渲染器必须完全参数化(2026-09-10 修复,最关键):branch_report_render.py 早期版本把 「2023Q1 / 2026Q2 / 14 个季度 / 汉中市 / 风险卡 5 项数值 / 涨跌方向 pos-num」写死在正文与风险卡中, 仅对「2023Q1–2026Q2 · 汉中」成立;换城市或换区间会直接崩溃 (TypeError: unsupported operand type(s) for /: 'NoneType' and 'float'——g1(tgt,'客户资产2023Q1') 取不到列返回 None),或虽不崩溃却输出张冠李戴的叙事。现已全部改为由 START / END / NQ / CITY 与数据推导,新增下列约定:
    • 季度串一律 f-string 拼接:f'客户资产{START}'、f'排名变化{START}→{END}'、tg('资产市占率', START);
    • 季度数用 NQ = len(trend);生成时间用 datetime.date.today();
    • 措辞与配色统一由 bcls / bsuf / barrow / rk_word_of / gap_word / kcls 推导 (阈值 FLAT = 0.005),禁止写死「升至/回落/持平/份额小幅上升/快于区域整体」; 新增 CSS 类 .delta-flat(中性灰,用于持平态 KPI);
    • 风险 5 卡全部按阈值动态判定(大客户依赖 = 资产市占 − 客户市占 >8% 触发; 人均客户资产 / 客户规模按与区域机构均值的倍数分档;盈利质量按净利润率对比;排名稳定性按 rk_word);
    • 趋势观察文案由 _ws / _as / _ranks / _drv 推导(区间峰值与低点所属季度、最大拖累/支撑分项);
    • 必须剔除横向对比表尾的「【合计】【均值】」行再统计机构数,否则并集家数虚高 (安康案例:10 家 → 正确 8 家);
    • KPI kpi-value 必须写 {w1*100:.2f} 而非 {pct(w1)},否则渲染成 29.15%% 双百分号;
    • KPI 图标配色按规则 10(综合橘黄/客户资产紫/股基交易额蓝),排名卡用中性灰 #6b7280。
  14. 视觉规范 V2 统一(2026-09-12 起,禁止回退):单网点与多主体两份报告必须共用 assets/report_style_v2.css + assets/report_script_v2.js + assets/report_template.html。
    • 条形/进度条动画一律走 CSS 变量 --w(内联 style="…;--w:37.2%"), 由 .reveal.in .b-seg{width:var(--w)} 触发过渡;禁止再由 Python 直接写 width:37.2% (那样条形会在页面载入瞬间直接顶到位,失去增长动画)。
    • 趋势图只输出容器 + 数据:<div class="chart-wrap"><svg id="trendChart" viewBox="0 0 1040 280"></svg><div id="trendTip"></div></div> 与 window.REPORT_TREND = {quarters:[…], series:[{name,color,data:[…100 为基数…]}]}; 禁止再由 Python 手算 X()/Y()/polyline/circle 坐标(旧写法坐标算错会静默出图)。 注意 data 是百分数数值(29.15 而非 0.2915),JS 内直接加 % 显示。
    • KPI 数字滚动:<span class="count" data-num="29.15" data-suf="%">29.15%</span>; 当 data-num 不是数字时(如排名卡)不要加 .count,否则数字永远停在 0。 .count 的初始文本必须是最终值,保证禁用 JS 时依然可读。
    • 章节编号显式传入 sec(..., no=N);季度趋势必须 sid='sec-trend';审计关注用 cls='risk-panel'。

交付前自查清单(单网点 HTML)

第 9–13 条已固化为脚本,直接跑一条命令即可(退出码 0 表示全过):

python scripts/verify_report_v2.py --outdir "F:/区域市占率分析"      # 或逐个传 html 路径

两类报告通用(24 项:占位符 / 令牌 / 进度条 / 导航 / 锚点 / 章节与编号连续性 / KPI 滚动 / 趋势容器与数据注入 / --w 变量 / 内联体积 / 旧类名残留 / 页脚…)。 下面 9–13 条列出的是该脚本的具体判定口径,便于人工复核与排错。

  1. 10 章顺序如上,审计关注位于最后一节;外显文本全文无「方案B /(方案B)/ 口径」chip 等字样
  2. §2 六组 g-icon 色序 = 资产紫 / 交易蓝 / 客户绿 / 营收青 / 净利琥珀 / 综合橘黄;趋势折线含综合橘、客户绿
  3. 无旧口径残留:19.60 / 0.08pp / "30% + 交易额25% + …" / 旧综合结论(如 50.42→49.27)均不得出现
  4. 方案B数值正确:综合市占率=16.80%→17.28%(汉中案例)类同源校验
  5. 营收/净利行带「观察」标记;净利份额为负变动时标红(neg-num)
  6. 区域净利/营收合计与增幅与 branch 求和一致(案例:5,262.84 万 / +399.7%)
  7. 同业对标表头券商名居中;表格开闭、SVG 合法;文件以 </html> 结尾
  8. 跨城市/跨区间回归:换城市或换区间后,全文不得出现上一案例的城市名、旧区间季度串(如 2023Q1)、 旧季度数(如 14 个季度)、旧风险卡数值(如 9.94 / 42,474);%% 出现次数必须为 0; 「两时点并集 N 家」的 N 应等于横向对比表实际机构行数(不含【合计】【均值】行)。 ⚠ 注意定位:单网点 HTML 内只有「指标级」详细对比表(§4,行=指标),机构级横向对比表在 Excel 01_横向对比 Sheet(行=机构)。核对并集家数须去 Excel Sheet1 数机构行,不要拿 §4 指标表去比, 否则会误判为 0 行。安康案例:N=8 ↔ Sheet1 机构行 8(已剔除【合计】【均值】)。
  9. V2 外壳六件套齐全(可用 grep -c 直查):class="progress" id="progress"、 class="nav" id="nav"、<header class="hero">、class="kpis"、id="trendChart"、 window.REPORT_TREND 各 ≥1;<section class="sec" id= 计数 = 9(§2–§10); class="sec-no" 计数 = 9 且编号连续 2…10。
  10. 零占位符残留:grep -o '{{[^}]*}}' 报告.html 必须为空(说明 12 个 {{…}} 全部替换成功)。
  11. 导航锚点可跳转:每个 data-nav="X" 都能在正文找到 id="X"; 趋势项必须是 sec-trend(写成 sec-7 会让折线图空白)。
  12. 无旧模板类名残留(仅查正文,<style>/<script> 内的兼容定义不算): kpi-grid、header-left、header-title、risk-title、hbar、sparkline、hero-title、 var(--sub)、class="tag" 一律为 0。
  13. 共享资产未被内联复制:branch_report_render.py 内不得出现大段 CSS 文本 (EXTRA_CSS 必须是空串),CSS/JS 一律 open(CSS_PATH) / open(JS_PATH) 读取。
  14. hero 三字段纯文本:数据期间 / 数据范围 / 分析主体 的 .f-val 内不得出现 <span class="accent"> 或琥珀色季度(qhl() 已随 1.3.4 移除)。

HTML 可视化报告(视觉规范 V2 · 2026-09-12 起)

除 Excel 底稿外,可将分析结果生成为自包含单文件 HTML 报告(内联 CSS/JS,无外部依赖、可离线打开)。

视觉规范来源

V2 视觉规范取自用户认可的定稿参考件 西部证券陕西省内对标开源证券竞争力分析报告_优化版.html,并已参数化为三份共享资产 (report_style_v2.css / report_script_v2.js / report_template.html)。 任何对视觉的调整都改这三份资产,两份报告同步生效;不要在渲染脚本里二次覆盖样式。

V2 相对旧模板的增量:

| 模块 | 说明 | |------|------| | 设计令牌 | CSS 变量集中管理(--brand #4f46e5 / --brand2 #7c3aed / --up #0f9d6b / --down #e11d48 等) | | 阅读进度条 | .progress 顶部细条,随滚动填充 | | 固定导航 | .nav 滚动 240px 后淡入;data-nav 与章节 id 联动高亮当前章节 | | hero 首屏 | 径向渐变装饰球 + chip + h1 + desc + .hero-fields 三列 | | 章节骨架 | <section class="sec" id="…"><div class="sec-head reveal"><div class="sec-no">N</div>… | | 卡片体系 | .card / .alert / .insight / .blk(浅紫底提示块) | | KPI 卡 | .kpis 四列栅格 + .k-label/.k-val/.k-chg/.k-note,载入后数字滚动 | | 双段对比条 | .b-track.two + .b-seg(.b-seg 内联 --w,reveal.in 时过渡) | | 选项卡 | .tabs / .tab[data-tab] / .panel[data-panel](切页时重触条形动画) | | 趋势图 | 空 SVG 容器 + window.REPORT_TREND 数据注入;JS 绘制网格/面积/折线/数据点,含画线动画与悬停提示 | | 滚动揭示 | IntersectionObserver 给 .reveal 加 .in;.js .reveal 初始 opacity:0 |

何时生成

  • 用户要求"生成报告""可视化报告""HTML 报告""看效果",或明确要看图形化呈现
  • 用户提供了参考截图/参考 HTML,要求按某种版式呈现分析结论

生成方式

  1. 以模板为骨架:读 assets/report_template.html,保留其中全部结构不动, 仅按占位符字典替换。占位符共 12 个:{{导航品牌}} {{导航链接}} {{报告标题}} {{数据来源标签}} {{报告描述}} {{数据期间}} {{数据范围}} {{分析主体}} {{CSS}} {{JS}} {{趋势数据}} {{正文}}。替换后必须复核 {{ 残留为 0。
  2. 严格遵循样式规范:配色、卡片、段落、染色、响应式规则见 references/report_style_guide.md——生成前必读。
  3. 数据取自 Excel 分析结果或 {城市}_quarterly_data.json,勿编造。
  4. 视觉自检:可用无头 Chrome 截屏自查(chrome --headless=new --window-size=1440,9000 --virtual-time-budget=12000 --screenshot=out.png file:///…)。注意:截图会捕捉到 数字滚动与折线描边动画的中间态(headless 下 rAF 不随 --virtual-time-budget 推进), 表现为"KPI 数字偏小一半""折线只画到第 3 个点"——这是动画中间态而非缺陷, 核对终态请另做一份 QA 副本:在主脚本 <script> 之前插入 document.querySelectorAll('.count').forEach(el=>{el.dataset.done='1'}) 并给所有 .reveal 补 .in。

关键硬性规则(易错,务必逐条自查)

| # | 规则 | |---|------| | 1 | 顶部仅 1 个 chip(数据来源),无版本徽章,hero-fields 固定 3 列(数据期间/数据范围/分析主体) | | 2 | 紫色 hero 背景不加纹理/背景图,仅保留右上角单光晕 + 底部渐变分隔线 | | 3 | KPI 四卡视觉完全一致:主数字 k-val 一律黑色,禁止 inline 染色与单卡特殊底色;仅 k-chg 染涨跌色 | | 4 | 结论段落按主体分段(每个对象一个 <p>,段首 【主体名】 深棕加粗) | | 5 | 段落内所有增减幅度必须 <b class="up"> / <b class="down"> 染色加粗,不得有裸数值 | | 6 | 禁止 ++X%、禁止 --Xpp(应为中文冒号 净利润:-2.97pp) | | 7 | 涨/跌染色沿用优化版参考件的设计令牌:--up 绿 #0f9d6b、--down 红 #e11d48(份额类报告的既有规范,不是行情涨跌色,勿按"涨红跌绿"改写) | | 8 | 条形宽度一律走 --w CSS 变量(见硬性规则 14),不得直接写 width: | | 9 | 双断点响应式(1024px / 768px)必须保留;@media (prefers-reduced-motion) 下 .reveal 直出、无位移 | | 10 | 页脚注明数据来源与综合市占率口径(须写明方案B权重及营收/净利润不加权) | | 11 | {{数据期间}} / {{数据范围}} / {{分析主体}} 三个 hero 字段一律纯文本(与参考件一致)——不用 <span class="accent"> 强调色、不做季度标黄(qhl() 已随 1.3.4 移除);数字与季度串均由 START/END/NQ/数据推导 | | 12 | 折线图主体名一律不在 SVG 内绘制(边缘会被截断),改由卡片内图例展示;图例与 <h3> 同一行右侧(display:flex;justify-content:space-between),不再独占一行 | | 13 | 章节编号显式传入且连续;季度趋势章节 id 必须为 sec-trend,否则折线图空白 | | 14 | 生成后立即做结构自检({{ 残留 = 0、data-nav 全部有对应 id、section.sec 计数与预期一致),不要只靠肉眼;两份样例见 assets/samples/ |

交付

用 present_files 展示 HTML(会自动打开预览面板)。交付前按 references/report_style_guide.md 第八节「生成检查清单」逐项核对。

版本历史

| 版本 | 变更 | |------|------| | 1.3.4(2026-09-12 视觉规范 V2 统一) | 按用户指定参考件 西部证券陕西省内对标开源证券竞争力分析报告_优化版.html 抽取视觉规范,统一两份 HTML 报告模板,并把该规范参数化为三份共享资产:<br>① 新增共享资产 assets/report_style_v2.css(约 22.4 KB,14 个分区:设计令牌/基础/进度条导航/hero/章节骨架/卡片/KPI/对比条/表格/选项卡/趋势图/单网点专用区块/页脚揭示/响应式)、assets/report_script_v2.js(约 10.1 KB,7 个模块:进度条+导航高亮、KPI 数字滚动、IntersectionObserver 滚动揭示、条形动画触发、选项卡、SVG 趋势图绘制与悬停、兜底;均「存在即启用」,两份报告共用同一份文件)、assets/report_template.html(12 个 {{占位符}} 的报告外壳)。<br>② group_compare.py 全量改造:删除内联旧 CSS/手写 sparkline,改读共享资产;spark_svg() → trend_data_js() + trend_chart_html()(容器 + 数据注入,坐标与动画交给 JS);hbar-* → brow/b-label/b-track.two/b-seg;章节骨架改为 <section class="sec" id="sec-xxx"> + sec-head/sec-no/sec-title;新增「城市维度」选项卡(.tabs/.tab/.panel);条形宽度改内联 --w 变量;KPI 主数字包 .count[data-num] 支持滚动。<br>③ branch_report_render.py 全量改造:资源路径切到 V2 三件套,删除 EXTRA_CSS(改为空串)与旧 header/kpi-grid 外壳,改为读 report_template.html 做占位符替换;sec() 增 no/sid/cls 三个参数(章节编号显式传入 + sec-trend 锚点 + risk-panel 琥珀面板);趋势图改容器+数据注入,删除手算 X()/Y()/polyline/circle 死代码;4 处内联 width: 改 --w 变量。<br>④ 修复 3 处改造期缺陷:sec8 漏传 no 导致章节 id 错位为 sec-7、导航 sec-8 锚点失配(现 9 个章节编号连续 2…10);sec10 原为裸 <div class="risk-section"> 未走 sec(),编号缺失(现 no=10);去内嵌 .risk-section 后风险卡失去琥珀底,新增 .card.risk-panel 规则补齐。<br>⑤ 新增自查项:V2 外壳六件套计数、12 个占位符零残留、导航锚点可跳转、旧模板类名零残留、共享资产未被内联复制(详见交付前自查清单 9–13)。<br>⑥ assets/ 清理与样例重出:废止并移出旧样式表 branch_report_style.css(V2 三件套成为唯一视觉源);assets/samples/ 两份定版样例(单网点·汉中东大街 / 多主体·西部 vs 开源·陕西全省)按 V2 重新渲染。<br>⑦ 补一道省级空结果保护(本轮实测中再次踩到):sxsfa_region_analysis.collect() 在采到 0 家营业部时改为抛错并拒绝写盘,不再静默把 陕西全省_quarterly_data.json 覆盖成空壳;同时明确「省级缓存必须放在 outdir 内」这一前置条件。<br>⑧ 新增结构校验脚本 scripts/verify_report_v2.py:把交付前自查清单第 9–13 条固化为 24 项可执行校验(占位符残留 / 设计令牌 / 进度条导航 / 导航锚点可跳转 / 章节编号连续性 / KPI 数字滚动 / 趋势容器与数据注入 / --w 变量 / 内联体积 / 旧模板类名回流 / hero 纯文本…),支持 --outdir 批量校验,退出码 0/1 可直接串联进批处理。<br>⑨ hero 三字段回归纯文本:移除 qhl()(琥珀色季度标黄)与 <span class="accent"> 数字强调,与参考件完全对齐(此前多主体报告仍带旧标黄)。<br>回归验证(安康市 · 西部证券安康兴安东路 · 2024Q1–2026Q2;西部 vs 开源):单网点报告 67,438 B、多主体报告 53,370 B,加上 assets/samples/ 两份样例(汉中 14 季 / 陕西全省 14 季 × 315 家)共 4/4 份报告经 verify_report_v2.py 全部通过(内联 CSS 22,658 字符 · JS 10,097 字符 · 零占位符 · 零旧类名);无头 Chrome 截屏确认 hero、KPI 四卡(29.15% / 2→2 / 36.10% / 29.01%)、双条对比图、折线图(10 季 × 4 系列)、同业对标表、琥珀审计面板与页脚均正常;数据与旧版完全一致(综合 31.84%→29.15% −2.69pp、排名 2→2)。 | | 1.3.3(2026-09-12 省级采集与底稿能力补齐) | 实战陕西全省(西部证券 vs 开源证券 · 2023Q1–2026Q2)时暴露并修复省级链路断点:<br>① 省级采集缺失:模式一脚本 sxsfa_region_analysis.py 的 collect() 把 city 原样传接口,而 /industrydata/gstjdata 只有传空串 city="" 才返回全省(传"陕西省/全省/陕西"均 0 条)。用模式一跑 --city "陕西全省" 会静默采到 0 家并把缓存覆盖为空壳,导致 group_compare 报"期末未找到主体"。新增 scripts/province_collect.py 补齐省级采集(--force 强制重采、缓存命中即跳过),产出与 group_compare 期望完全一致的 陕西全省_quarterly_data.json(含 branch 级 city 字段)。<br>② 新增省级 Excel 审计底稿 scripts/province_workbook.py:6 Sheet(00_说明 / 01_主体对标趋势 / 02_{主体A}分地市 / 03_{主体A}机构明细 / 04_全省主体排名 / 05_全省市场背景),A4 横版、仿宋、审计底稿版式。02 Sheet 并置「主口径综合市占率(40/35/25)」与「城市口径(30/25/20/15/10)」两列供口径对照。<br>③ 修复 write_table 的 pct_cols 错位陷阱:其判定式为 (cidx-1) in pct_cols(1 基 values 索引,cidx 从 2 起),本包历史调用多处偏移一列,会出现"股基交易额被渲染成 2505564524.00%"这类错误;新脚本改用 apply_pct_by_header() 按表头关键字统一套百分比格式。<br>④ 修正表头/数据列错位:write_table 要求 len(headers) == 1(label列) + len(values),此前 03/04 Sheet 漏掉 label 列表头导致整体右移一列。<br>⑤ 修正机构级口径:03 Sheet 的市占率与排名须按单个机构数值÷全省总量,不能用主体份额/主体排名填充(否则同券商全部网点排名相同);校验式 Σ(机构期末综合市占率) ≈ 主体期末综合市占率(实测 13.59% 吻合)。<br>⑥ 新增「发布打包」章节:固化 skillhub zip 规范(桌面输出、顶层目录取 slug、排除 __pycache__ 与安装侧元数据、中文条目须置 UTF-8 标志、打包前后自检项)与可复用打包脚本。<br>回归验证:陕西全省 2023Q1–2026Q2 重采后结果与 2026-09-03 报告完全一致(西部 15.95→13.59%、开源 14.42→16.29%、网点 56→57 / 33→54),说明协会数据未回溯修订;底稿 7 组校验(表头对齐 / 列格式统一 / 主体对标数值 / 分地市份额区间与西安 12.35%→9.41% / 机构并集 59=56+57−54 且新进 3 退出 2 / 主体排名 76 家且份额合计 100% / 与 JSON 交叉核对)全部通过;对标 HTML 8 章节(含城市维度)、无 ++·--Xpp·%%、双断点完整。 | | 1.3.2(2026-09-10 单网点渲染器全参数化修复) | 实战安康案例(安康市 · 西部证券安康兴安东路营业部 · 2024Q1–2026Q2)时暴露并修复 branch_report_render.py 的硬编码缺陷族(该脚本此前实质是「汉中案例模板」,仅对 2023Q1–2026Q2 成立):<br>① 季度串硬编码:g1(tgt,'客户资产2023Q1') 等 11 处写死季度 → 换区间直接 TypeError: unsupported operand type(s) for /: 'NoneType' and 'float';改为 f'客户资产{START}'、f'排名变化{START}→{END}'、tg(..., START/END)。<br>② 季度数硬编码:14 个季度 / 连续 14 季居首 → NQ = len(trend)。<br>③ 城市名硬编码:正文 4 处写死「汉中市」→ CITY。<br>④ 涨跌方向硬编码:KPI 与核心结论一律 pos-num/「升至」/「份额小幅上升」→ 新增 bcls / bsuf / barrow / rk_word_of / gap_word / kcls(阈值 FLAT=0.005)统一推导,并新增 CSS 类 .delta-flat。<br>⑤ 风险 5 卡数值硬编码:9.94 / 7.93 / 4.22 / 76.4% / 57.7% / 42,474 / 20,838 全部写死 → 改为按阈值动态判定(大客户依赖 = 资产市占−客户市占>8% 触发;人均客户资产、客户规模按区域机构均值倍数分档;盈利质量按净利润率对比;排名稳定性按 rk_word)。<br>⑥ 趋势观察文案硬编码(2025Q2 低点 18.83%、2026Q1 峰值 21.50%)→ 由 _ws / _as / _ranks / _drv 推导峰值低点季度与最大拖累/支撑分项。<br>⑦ 汇总行污染机构数:横向对比表尾的「【合计】【均值】」行被计入 len(recs),安康「两时点并集」虚高为 10 家 → 过滤 【 开头行,修正为 8 家(退出 1 / 新进 2)。<br>⑧ KPI 双百分号:{pct(w1)} + <span class="kpi-unit">%</span> 渲染为 29.15%% → 改用 {w1*100:.2f}。<br>⑨ KPI 图标配色回归规则 10(综合橘黄/客户资产紫/股基交易额蓝),排名卡改中性灰 #6b7280;生成时间改 datetime.date.today()。<br>回归验证:汉中(2023Q1–2026Q2,14 季)重渲染结果与文档案例完全一致(综合 16.80→17.28% +0.48pp、连续 14 季居首);安康案例交付前自查 11/11 通过(无旧城市名/旧区间/旧季度数/旧风险值残留,%% 计数 0)。 | | 1.3.1(2026-09-03 城市维度整合) | 新增**「城市维度」章节**(省级对标自动生成):group_compare.py 新增 CITY_WEIGHTS(旧报告 5 指标加权 30/25/20/15/10)、CITY_FIGS(5 张图卡配色)与 city_section(R),位于「盈利能力观察」之后、「区域主体排名」之前,一指标一图、综合市占率排第一位(综合→客户资产→股基交易额→客户数→营业收入·观察)。修正主体归并缺陷:城市维度改用 company_of() 归并,排除「申万宏源西部证券有限公司」西安 3 家网点(旧手工版 '西部证券' in name 误并,西安综合市占率虚高约 0.4pp:12.76→9.86 修正为 12.35→9.41)。更新 assets/samples/ 多主体样例为含城市维度的定版。实战:陕西全省西部 vs 开源(西部在西安 12.35%→9.41% 主城下行、安康/榆林逆势;开源 14.42→16.29% 全省第 1)。 | | 1.3.0(2026-09-03 重封定版,桌面 zip 覆盖) | 将 2026-09-02 定版以来全部实战经验正式并入本包:<br>① 单网点定版 HTML 渲染器 scripts/branch_report_render.py(参数化 city/target/start/end/outdir,目标自动匹配、同业对标券商主体自动归并、风险 5 卡数值动态计算、观察份额分母取分支求和口径)+ assets/branch_report_style.css(旧版单网点视觉体系);单网点 HTML 十章节线性编排(市占率横向对比 / 盈利能力观察 / 季度趋势为新增模块,审计关注·风险信号识别置末并含核心结论摘要)。<br>② 指标调色板定版:客户资产紫 #6366f1 / 股基交易额蓝 #5b8def / 客户数主题绿 #10b981 / 营业收入(观察)青 #06b6d4 / 净利润(观察)琥珀橙 #f97316 / 综合市占率主题橘黄 #f59e0b(KPI 图标、双条图、一览条、趋势折线全同步)。<br>③ 外显去口径字样:HTML 对外不出现「方案B /(方案B)」等内部口径名,仅以权重公式披露;hero 强调数字与期间季度统一琥珀 #fbbf24。<br>④ 全省对标扩展:normalize_city 放行省级别名(陕西全省/全省/陕西省/陕西 → 陕西全省),支持陕西省全省 300+ 家营业部 × 14 季的主体聚合对标。<br>⑤ 模板样例并入 assets/samples/:单网点(汉中东大街营业部)与多主体(西部 vs 开源 · 陕西全省)两份定版 HTML。<br>实战:汉中东大街(综合 16.80→17.28%,连续 14 季居首)、陕西全省西部 vs 开源(西部 15.95→13.59% 全省第 2;开源 14.42→16.29% 全省第 1)。 | | 1.0.0 | 初版:参数确认 → 协会数据采集 → 4-Sheet Excel 分析表生成 | | 1.2.1 | 新增 HTML 可视化报告能力:沉淀 assets/report_template.html 模板与 references/report_style_guide.md 视觉规范(顶部 4 层布局、KPI 四卡一致、分主体段落、全局涨跌染色、双断点响应式) | | 1.3.0 | 新增多主体对标模式:scripts/group_compare.py 按券商主体聚合地区全部网点,输出「核心结论/归因分解/同业对标/区域主体排名/网点明细/季度趋势」6 章节 HTML 报告 + JSON;「同业对标」采用双卡片结构(关键指标双时点对比表 6 列 + 期末关键指标紫青双条对比图);内置归因分解(各指标份额变动×权重)与历史主体归并(国泰君安+海通→国泰海通)。修订:原「主体对比」单表过宽(11 列堆叠绝对值/增幅/份额/领先),按 H 盘定稿规范拆为「关键指标双时点对比」表 + 「期末关键指标对比」紫青双条对比图双卡片,移除多余列避免列堆挤。 | | 1.3.1 | 修复两处缺陷:①空值崩溃——网点明细排名变动用 (r0-r1) if (r0 and r1) else None 后直接 dr>0 比较,退出机构(期末无排名)触发 TypeError: '>' not supported between NoneType and int;改为「新进/退出/持平/↑n/↓n」五态判定;b()/bpp() 增加 None 安全;②单位缺失——份额变动误用 b() 输出 -6.11(漏 pp),新增 bpp() 统一输出 -6.11pp,并修正网点数措辞(增/减/保持,避免「由5家增至5家」)。实战:咸阳地区西部证券 vs 开源证券(2023Q1–2026Q2)。该版未单独打包,其修复已并入 1.3.0 定版 | | 1.3.0(2026-09-02 定版) | 本版为 1.3.x 系列合并定版,取代此前 1.3.0 与 1.3.1 两个包,包含全部修复与三项新增能力:<br>①综合市占率改用方案B——权重由 30/25/20/15/10 改为 客户资产40%+股基交易额35%+客户数25%,营业收入、净利润退出加权(负值/分母趋零/基数效应/重复计权四项理由,见专章);引入 OBSERVE / ALL_METRICS 常量分离「计权」与「采集」范围;新增**「盈利能力观察」章节**(8 列表 + 区域合计行 + 口径说明 insight,增幅期初≤0 显示 n/a,负值份额打「负值」chip);归因分解由 5 条减为 3 条,KPI 第四卡由「净利润市占率」改为「客户数市占率」;页脚与 Excel 注2 同步新口径。<br>②hero「数据期间」仅季度标黄(2023 原色 + Q1 琥珀黄),年份数字恢复原色。<br>③折线图图例移至标题同行右侧(display:flex;justify-content:space-between),图例不再独占一行,SVG 内仍不绘制主体名。<br>实战重算:咸阳(西部 37.26→32.35%,开源 8.53→8.35%)、汉中(西部 41.80→43.46%,开源 12.03→14.34%),两份报告章节均由 6 增至 7,校验 11/11 通过。<br>⚠ 方案B下咸阳开源、汉中西部变动方向较旧口径反转,历史结论须重新解读 | | 2026-09-03 全省对标扩展(未打包、未升版本号) | 模式二支持省级维度:normalize_city 放行省级别名(统一返回 陕西全省);全省数据文件 {陕西全省}_quarterly_data.json 需带 city 键与 branch 级 city 字段;cache 命中即可复用 304+ 家×14 季全省缓存。实战:西部证券 vs 开源证券(陕西全省 2023Q1–2026Q2)——西部 15.95%→13.59%(-2.36pp,56→57 家),开源 14.42%→16.29%(+1.87pp,33→54 家,升为全省主体第 1),输出 HTML(hero 期间 Q1/Q2 内联琥珀标黄、盈利能力观察、方案B 页脚)| | 2026-09-03 执行链完善(未打包、未升版本号) | 单网点 HTML 定版渲染能力并入模式一执行链:新增 scripts/branch_report_render.py(参数化:city/target/start/end/outdir,目标自动匹配、券商主体自动归并并对标、风险信号数值动态计算)与 assets/branch_report_style.css(旧版单网点视觉体系 + 扩展类)。定版报告采用旧模板视觉并按 10 章线性编排(市占率对比/增幅/详细表 → 市占率横向对比【新】→ 盈利能力观察【新】→ 季度趋势【新】→ 区域背景 → 同业对标 → 审计关注·风险信号识别【置末、含方案B核心结论摘要】);观察份额分母统一取分支求和口径(并与 Excel 03 城市级营收/净利字段的约 1/2 差异做披露)。实战重输出:汉中东大街营业部(综合市占率 16.80→17.28% +0.48pp,连续 14 季居首)。旧口径备份于 _旧口径备份_20260903/。如需正式升版(建议 1.4.0)与 zip 打包,待用户显式确认后执行 |

注意事项

  1. 数据范围:网站最早可查 2022 年四季度(2022Q1-Q3 无数据)。期初早于 2022Q4 时脚本会得到空数据,需提示用户调整。
  2. 网络:脚本已内置 SSL 绕过与 3 次重试;接口公开无风控,但建议每季度加 0.2s 延时(脚本已内置)。
  3. 机构变动:期间内可能有营业部退出/新进(如国泰海通合并、区域调整),横向对比按两时点并集处理,变动情况自动标注在注2 与 Sheet4 底部。
  4. 目标匹配:关键词匹配到多家时会报错并列出候选全称,让用户提供更精确简称后重跑;匹配失败时同样列出该地区全部机构名单供选择。
  5. 缓存复用:同城市、同季度区间的 json 缓存存在时直接复用(跳过采集),换参数后自动重新采集。
  6. 期初/期末不在册:目标机构若期初或期末不在该地区名单,脚本会告警;横向对比表中对应时点显示"—",请向用户说明原因。
  7. 省级(全省)取数:接口 gstjdata 的 city 必须传空串才返回全省;若需要省级数据,必须用 scripts/province_collect.py,不要用 sxsfa_region_analysis.py --city "陕西全省"。 自 1.3.4 起 collect() 增加了空结果保护:采到 0 家营业部时直接抛错并拒绝写盘, 不会再静默覆盖 陕西全省_quarterly_data.json(此前的静默覆盖会导致后续 group_compare 报"期末未找到主体",排查成本很高)。 ⚠ 仍然要注意:缓存必须放在 outdir 里再跑 group_compare——若 outdir 下没有省级缓存, 脚本会尝试自动采集省级数据(命中上述保护后报错),而不是复用别的目录里的缓存。 换省级季度区间后请带 --force 重采,或先手工备份缓存文件。
  8. 覆盖前先备份:脚本对同名 json / xlsx / html 是直接覆盖写。批量重跑前建议把既有交付物 移入 _备份_YYYYMMDD/ 子目录(本技能实战中已形成该惯例)。

发布打包(skillhub zip · 2026-09-12 定)

用户说「更新 skill zip / 打包技能 / 导出技能包」时按本节执行,产物固定放桌面。

命名与结构

| 项 | 规范 | |----|------| | 文件名 | XBJHB-Shaanxi-Securities-Market-Analyzer_V{版本}.zip | | 保存位置 | C:\Users\Administrator\Desktop\ | | zip 顶层目录 | shaanxi-securities-market-analyzer/(= skillhub slug,不带 __skillhub 后缀) | | 打包内容 | SKILL.md、assets/、references/、scripts/ | | 排除 | __pycache__/、*.pyc、_meta.json、_skillhub_meta.json、_icon.png(后三者为安装侧元数据与图标,不进上传包) |

两个易错点

  1. 中文文件名编码:Python 打包须对含中文的条目置 UTF-8 标志,否则 Windows 解压乱码 —— zi = zipfile.ZipInfo.from_file(path, arcname) 后 zi.flag_bits |= 0x800。
  2. 顶层目录名用 slug 而非 frontmatter name:本包二者不同(frontmatter name: sxsfa-region-share-analysis、 slug shaanxi-securities-market-analyzer),skillhub 侧以 slug 为准,历史包(xbjhb-*、bank-transfer-*)均如此。

打包前:版本号九处保持一致(SKILL.md frontmatter、_meta.json、_skillhub_meta.json、 branch_report_render.py / group_compare.py / province_collect.py / province_workbook.py / sxsfa_region_analysis.py / verify_report_v2.py 六个脚本 docstring),并对 scripts/*.py 逐个 py_compile 语法校验。

打包前另需确认:assets/ 下只有 V2 三件套(report_style_v2.css / report_script_v2.js / report_template.html)+ samples/,不应再残留旧样式表 branch_report_style.css(已随 1.3.4 废止);assets/samples/ 两份样例须为本版重新渲染的成品。

打包后自检:zipfile.testzip() 返回 None;条目数与预期一致且无 __pycache__ 残留; 解压到临时目录后 6 个脚本语法全部通过;包内 SKILL.md 的 version 与文件名版本一致。

可复用打包脚本

import os, zipfile
SRC  = r"C:\Users\Administrator\.workbuddy\skills\shaanxi-securities-market-analyzer__skillhub"
SLUG = "shaanxi-securities-market-analyzer"
VER  = "V1.3.4"
OUT  = rf"C:\Users\Administrator\Desktop\XBJHB-Shaanxi-Securities-Market-Analyzer_{VER}.zip"

rels = ["SKILL.md"]
for d in ("assets", "references", "scripts"):
    for dp, dn, fn in os.walk(os.path.join(SRC, d)):
        dn[:] = [x for x in dn if x != "__pycache__"]
        rels += [os.path.relpath(os.path.join(dp, f), SRC).replace("\\", "/")
                 for f in sorted(fn) if not f.endswith((".pyc", ".pyo", ".bak"))]

with zipfile.ZipFile(OUT, "w", zipfile.ZIP_DEFLATED, compresslevel=9) as z:
    for rel in sorted(rels):
        p = os.path.join(SRC, rel)
        zi = zipfile.ZipInfo.from_file(p, f"{SLUG}/{rel}")
        zi.flag_bits |= 0x800          # UTF-8 文件名(中文样例名必需)
        zi.compress_type = zipfile.ZIP_DEFLATED
        z.writestr(zi, open(p, "rb").read())

API 参考

接口细节、字段口径与返回结构见 references/api_notes.md;脚本异常排查时按该文档核对。