{/* 此页面由 website/scripts/generate-skill-docs.py 从技能的 SKILL.md 自动生成。请编辑源文件 SKILL.md,而非此页面。 */}

Excel 创作

使用 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

:::info 以下是 Hermes 在触发此技能时加载的完整技能定义。这是技能激活时代理所看到的指令。 :::

excel-author

使用 openpyxl 在磁盘上生成 .xlsx 文件。遵循以下银行级规范,使模型可审计、灵活,且可由非构建者评审。

改编自 Anthropic 的 xlsx-authoraudit-xls 技能,来自 anthropics/financial-services 仓库。原版的 MCP / Office-JS / Cowork 特定分支已被移除 —— 此技能假定无头 Python。

输出约定

  • 写入 ./out/<名称>.xlsx。如 ./out/ 不存在则创建。
  • 在最终消息中返回相对路径,以便下游工具获取。
  • 每个文件一个逻辑模型。除非明确要求,不要追加到现有工作簿。

设置

pip install "openpyxl>=3.0"

核心规范(不可商议)

蓝色/黑色/绿色单元格颜色

  • 蓝色Font(color="0000FF"))—— 人类输入的硬编码值。营收驱动因素、WACC 输入、终值增长率、市场数据。
  • 黑色(默认)—— 公式。每个衍生单元格都是实时的 Excel 公式。
  • 绿色Font(color="006100"))—— 指向其他工作表或外部文件的链接。

评审者可以通过颜色扫描,立即看出哪些是假设,哪些是计算结果。

公式优先于硬编码

每个计算单元格必须是公式字符串,绝不能是在 Python 中计算后粘贴为数值的数字。

# 错误 —— 潜在的静默错误
ws["D20"] = revenue_prior_year * (1 + growth)
 
# 正确 —— 当用户更改假设时自动调整
ws["D20"] = "=D19*(1+$B$8)"

唯一允许的硬编码数字:

  1. 原始历史输入(实际营收、已报告 EBITDA 等)
  2. 用户应可调整的假设驱动因素(增长率、WACC 输入、终值 g)
  3. 当前市场数据(股价、债务余额)—— 附带单元格注释说明来源和日期

如果您发现自己用 Python 计算值并写入结果,请停下。

跨表引用的命名区域

为从其他工作表、演示文稿或备忘录引用的任何数据使用命名区域。

from openpyxl.workbook.defined_name import DefinedName
wb.defined_names["WACC"] = DefinedName("WACC", attr_text="Inputs!$C$8")
# 然后在其他地方:
calc["D30"] = "=D29/WACC"

平衡检查表

包含一个 Checks 表,将所有内容联系起来并显示 TRUE/FALSE:

  • 资产负债表平衡(资产 = 负债 + 权益)
  • 现金流与资产负债表的期间现金变动衔接
  • 分部加总与合并总计衔接
  • 计算范围内无意外硬编码

示例:

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("来源:10-K FY2024, p.47, 营收行", "analyst")

格式:来源:[系统/文档], [日期], [引用], [URL 如适用]

绝不延迟添加来源。绝不写 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()
 
# --- 输入表 ---
inp = wb.active
inp.title = "Inputs"
inp["A1"] = "市场数据与关键输入"
inp["A1"].font = HEADER_FONT
inp["A1"].fill = HEADER_FILL
inp.merge_cells("A1:C1")
 
inp["B3"] = "FY2024 营收"
inp["C3"] = 1_250_000_000
inp["C3"].font = BLUE
inp["C3"].comment = Comment("来源:10-K FY2024 p.47", "model")
 
inp["B4"] = "增长率"
inp["C4"] = 0.12
inp["C4"].font = BLUE
 
# --- 计算表 ---
calc = wb.create_sheet("DCF")
calc["B2"] = "预测营收"
calc["C2"] = "=Inputs!C3*(1+Inputs!C4)"   # 公式,黑色
 
# --- 检查表 ---
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"] = "现金流预测"
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(行)x 终值增长率(列)敏感性
wacc_axis = [0.08, 0.085, 0.09, 0.095, 0.10]        # 中心行 = 基准 9.0%
term_axis = [0.02, 0.025, 0.03, 0.035, 0.04]        # 中心列 = 基准 3.0%
 
start_row = 40
ws.cell(row=start_row, column=1).value = "隐含股价 ($)"
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
        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"
        )
 
# 突出显示中心单元格(基准情况)
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 无头重新计算
libreoffice --headless --calc --convert-to xlsx ./out/model.xlsx --outdir ./out/

或使用 Python 重新计算辅助函数(参见此技能的 scripts/recalc.py)。

模型布局规划

在编写任何公式前:

  1. 定义所有部分的起始行位置
  2. 写入所有标题和标签
  3. 写入所有部分分隔符和空白行
  4. 然后使用固定的行位置写入公式

这可以防止在公式编写后插入标题行导致的级联公式断裂模式。

与用户逐节验证

对于大型模型(DCF、三表模型、LBO),在继续前停下并向用户展示中间结果。在构建下游敏感性表格之前捕获错误的利润率假设可以节省一小时。

检查点模式:

  • 输入块完成后 → 展示原始输入,确认后再预测
  • 营收预测完成后 → 确认顶线和增长率
  • FCF 构建完成后 → 确认完整计划
  • WACC 完成后 → 确认输入
  • 估值完成后 → 确认股权桥梁
  • 然后构建敏感性表格

何时不使用此技能

  • 用户在实时 Excel 会话中且有 Office MCP 可用 —— 直接操作其实时工作簿。
  • 纯表格数据导出无公式 —— csvpandas.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