Excel Author
使用 openpyxl 以无头模式构建可审计的 Excel 工作簿 — 采用蓝/黑/绿单元格约定、公式优于硬编码、命名范围、平衡检查和敏感性表。适用于财务模型、审计输出和对账。
技能元数据
| 来源 | 可选 — 使用 hermes skills install official/finance/excel-author 安装 |
| 路径 | optional-skills/finance/excel-author |
| 版本 | 1.0.0 |
| 作者 | Anthropic(由 Nous Research 改编) |
| 许可证 | Apache-2.0 |
| 平台 | linux, macos, windows |
| 标签 | excel, openpyxl, finance, spreadsheet, modeling |
| 相关技能 | pptx-author, dcf-model, comps-analysis, lbo-model, 3-statement-model |
参考:完整 SKILL.md
以下是 Hermes 在触发此技能时加载的完整技能定义。这是技能激活时代理看到的指令。
excel-author
使用 openpyxl 在磁盘上生成 .xlsx 文件。遵循以下银行级约定,以确保模型可审计、灵活,并可由构建者以外的人员审查。
改编自 anthropics/financial-services 仓库中 Anthropic 的 xlsx-author 和 audit-xls 技能。原始技能中特定于 MCP / Office-JS / Cowork 的分支已被移除 — 此技能假定使用无头 Python。
输出契约
- 写入
./out/<name>.xlsx。如果./out/不存在则创建它。 - 在最终消息中返回相对路径,以便下游工具可以获取它。
- 每个文件对应一个逻辑模型。除非明确要求,否则不要追加到现有工作簿中。
设置
pip install "openpyxl>=3.0"
核心约定(不可协商)
蓝/黑/绿单元格颜色
- 蓝色 (
Font(color="0000FF")) — 人类输入的硬编码输入。收入驱动因素、WACC 输入、永续增长率、市场数据。 - 黑色(默认)— 公式。每个派生单元格都是实时的 Excel 公式。
- 绿色 (
Font(color="006100")) — 链接到另一个工作表或外部文件。
审查者随后可以扫描工作表,并立即区分哪些是假设,哪些是计算得出的。
公式优于硬编码
每个计算单元格必须是公式字符串,绝不能是在 Python 中计算后作为值粘贴的数字。
# WRONG — silent bug waiting to happen
ws["D20"] = revenue_prior_year * (1 + growth)
# CORRECT — flexes when the user changes the assumption
ws["D20"] = "=D19*(1+$B$8)"
唯一允许的硬编码数字:
- 原始历史输入(实际收入、报告的 EBITDA 等)
- 用户旨在调整假设的驱动因素(增长率、WACC 输入、永续增长率 g)
- 当前市场数据(股价、债务余额)— 附带单元格注释记录来源 + 日期
如果你发现自己在 Python 中计算一个值并写入结果,请立即停止。
用于跨工作表引用的命名范围
对于从另一个工作表、演示文稿或备忘录中引用的任何数据,使用命名范围。
from openpyxl.workbook.defined_name import DefinedName
wb.defined_names["WACC"] = DefinedName("WACC", attr_text="Inputs!$C$8")
# then elsewhere:
calc["D30"] = "=D29/WACC"
平衡检查标签页
包含一个 Checks 标签页,用于关联所有内容并显示 TRUE/FALSE:
- 资产负债表平衡(资产 = 负债 + 权益)
- 现金流量与资产负债表中期间现金变动的关联
- 分部之和与合并总额的关联
- 计算范围内没有 rogue 硬编码
示例:
checks = wb.create_sheet("Checks")
checks["A2"] = "BS balances"
checks["B2"] = "=IS!D20-IS!D21-IS!D22"
checks["C2"] = "=ABS(B2)<0.01" # TRUE/FALSE
每个硬编码输入都添加单元格注释
在创建单元格时添加注释,而不是稍后添加。
from openpyxl.comments import Comment
ws["C2"] = 1_250_000_000
ws["C2"].font = Font(color="0000FF")
ws["C2"].comment = Comment("Source: 10-K FY2024, p.47, revenue line", "analyst")
格式:Source: [System/Document], [Date], [Reference], [URL if applicable]。
切勿推迟记录来源。切勿编写 TODO: add source。
骨架:典型财务模型
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.comments import Comment
from openpyxl.utils import get_column_letter
from pathlib import Path
BLUE = Font(color="0000FF")
BLACK = Font(color="000000")
GREEN = Font(color="006100")
BOLD = Font(bold=True)
HEADER_FILL = PatternFill("solid", fgColor="1F4E79")
HEADER_FONT = Font(color="FFFFFF", bold=True)
wb = Workbook()
# --- Inputs tab ---
inp = wb.active
inp.title = "Inputs"
inp["A1"] = "MARKET DATA & KEY INPUTS"
inp["A1"].font = HEADER_FONT
inp["A1"].fill = HEADER_FILL
inp.merge_cells("A1:C1")
inp["B3"] = "Revenue FY2024"
inp["C3"] = 1_250_000_000
inp["C3"].font = BLUE
inp["C3"].comment = Comment("Source: 10-K FY2024 p.47", "model")
inp["B4"] = "Growth Rate"
inp["C4"] = 0.12
inp["C4"].font = BLUE
# --- Calc tab ---
calc = wb.create_sheet("DCF")
calc["B2"] = "Projected Revenue"
calc["C2"] = "=Inputs!C3*(1+Inputs!C4)" # formula, black
# --- Checks tab ---
chk = wb.create_sheet("Checks")
chk["A2"] = "BS balances"
chk["B2"] = "=ABS(BS!D20-BS!D21-BS!D22)<0.01"
Path("./out").mkdir(exist_ok=True)
wb.save("./out/model.xlsx")
带有合并单元格的章节标题
openpyxl 特性:合并单元格时,在左上角单元格设置值,并单独设置整个范围的样式。
ws["A7"] = "CASH FLOW PROJECTION"
ws["A7"].font = HEADER_FONT
ws.merge_cells("A7:H7")
for col in range(1, 9): # A..H
ws.cell(row=7, column=col).fill = HEADER_FILL
敏感性表
使用循环构建,而不是为每个单元格硬编码公式。规则:
- 奇数行/列(5×5 或 7×7)— 保证有一个真正的中心单元格。
- 中心单元格 = 基准情形。 中间行/列标题必须等于模型的实际 WACC 和永续增长率 g,以便中心输出等于基准情形隐含的股价。这是健全性检查。
- 高亮显示中心单元格,使用中蓝色填充 (
"BDD7EE") 并加粗。 - 用完整的重计算公式填充每个单元格 — 绝不使用近似值。
# 5x5 WACC (rows) x terminal growth (cols) sensitivity
wacc_axis = [0.08, 0.085, 0.09, 0.095, 0.10] # center row = base 9.0%
term_axis = [0.02, 0.025, 0.03, 0.035, 0.04] # center col = base 3.0%
start_row = 40
ws.cell(row=start_row, column=1).value = "Implied Share Price ($)"
ws.cell(row=start_row, column=1).font = BOLD
for j, g in enumerate(term_axis):
ws.cell(row=start_row+1, column=2+j).value = g
ws.cell(row=start_row+1, column=2+j).font = BLUE
for i, w in enumerate(wacc_axis):
r = start_row + 2 + i
ws.cell(row=r, column=1).value = w
ws.cell(row=r, column=1).font = BLUE
for j, g in enumerate(term_axis):
c = 2 + j
# Full DCF recalc formula (simplified for illustration).
# In a real model this references the full projection block.
ws.cell(row=r, column=c).value = (
f"=SUMPRODUCT(FCF_range,1/(1+{w})^year_offset) + "
f"FCF_terminal*(1+{g})/({w}-{g})/(1+{w})^terminal_year"
)
# Highlight center cell (base case)
center = ws.cell(row=start_row+2+len(wacc_axis)//2,
column=2+len(term_axis)//2)
center.fill = PatternFill("solid", fgColor="BDD7EE")
center.font = BOLD
交付前重新计算
openpyxl 写入公式字符串但不计算它们。Excel 会在打开时重新计算,但下游消费者(自动检查脚本、CI)需要计算后的值。
在交付前运行 LibreOffice 或专用的重计算步骤:
# LibreOffice headless recalc
libreoffice --headless --calc --convert-to xlsx ./out/model.xlsx --outdir ./out/
或者使用 Python 重计算辅助工具(参见此技能中的 scripts/recalc.py)。
模型布局规划
在编写任何公式之前:
- 定义所有部分行的位置
- 编写所有标题和标签
- 编写所有部分分隔符和空行
- 然后使用锁定的行位置编写公式
这可以防止“级联公式破坏”模式,即在编写公式后插入标题行会导致所有下游引用发生偏移。
与用户逐步验证
对于大型模型(DCF、三张报表、LBO),请在继续之前暂停并向用户展示中间产物。在构建下游敏感性表之前发现错误的利润率假设,可以节省一小时的时间。
检查点模式:
- 输入块(Inputs block)完成后 → 展示原始输入,在预测前确认
- 收入预测完成后 → 确认顶层收入(top line)及增长率
- 自由现金流(FCF)构建完成后 → 确认完整时间表
- WACC 完成后 → 确认输入项
- 估值完成后 → 确认股权桥(equity bridge)
- 然后构建敏感性表
何时不使用此技能
- 用户在拥有 Office MCP 的实时 Excel 会话中 — 应直接操作其实时工作簿。
- 纯表格数据导出且无公式 — 使用
csv或pandas.to_excel更简单。 - 具有高度交互性的仪表盘/图表 — 请使用专业的 BI 工具。
归属
约定(蓝色/黑色/绿色、公式优于硬编码、命名范围、敏感性规则)改编自 Anthropic 的 Claude for Financial Services 插件套件,采用 Apache-2.0 许可证。原文:https://github.com/anthropics/financial-services/tree/main/plugins/vertical-plugins/financial-analysis/skills/xlsx-author