Initial commit
This commit is contained in:
318
skills/xlsx/scenes/finance.md
Executable file
318
skills/xlsx/scenes/finance.md
Executable file
@@ -0,0 +1,318 @@
|
||||
# Financial Model Specialist Guide
|
||||
|
||||
Load this reference when the task involves: financial statements, budgets, forecasts, DCF models, LBO, valuation, P&L, balance sheets, cash flow, or any investment banking deliverable.
|
||||
|
||||
Also load `engines/design.md` → use **Finance** scene overrides (IB text color rules, section dividers).
|
||||
|
||||
---
|
||||
|
||||
## Financial Model Architecture
|
||||
|
||||
### Standard Sheet Structure
|
||||
```
|
||||
Assumptions Sheet:
|
||||
- All inputs, growth rates, margins, multiples
|
||||
- Blue font for every changeable number
|
||||
- Yellow background for key assumptions
|
||||
- Source citations in adjacent cells or comments
|
||||
|
||||
Income Statement / P&L:
|
||||
- Revenue → COGS → Gross Profit → OpEx → EBIT → Interest → Tax → Net Income
|
||||
- All values are formulas referencing Assumptions
|
||||
|
||||
Balance Sheet:
|
||||
- Assets = Liabilities + Equity (must balance!)
|
||||
- Include balance check row: =Assets-Liabilities-Equity (should be 0)
|
||||
|
||||
Cash Flow Statement:
|
||||
- Operating → Investing → Financing → Net Change
|
||||
- Ending Cash = Beginning Cash + Net Change
|
||||
|
||||
Valuation / Output:
|
||||
- DCF, comparables, or whatever model the user needs
|
||||
- Green font for values pulled from other sheets
|
||||
```
|
||||
|
||||
### Formula Construction Rules
|
||||
|
||||
```python
|
||||
# ✅ CORRECT: Reference assumptions
|
||||
sheet['C10'] = '=C9*(1+Assumptions!$B$5)' # Growth rate from assumptions
|
||||
|
||||
# ❌ WRONG: Hardcoded magic number
|
||||
sheet['C10'] = '=C9*1.05'
|
||||
|
||||
# ✅ CORRECT: Protected division
|
||||
sheet['D15'] = '=IF(C15=0,"-",B15/C15)'
|
||||
|
||||
# ✅ CORRECT: Consistent formula across periods
|
||||
# If D10 = '=D9*(1+Assumptions!$B$5)' then E10 must follow the same pattern
|
||||
```
|
||||
|
||||
### Assumptions Sheet Layout
|
||||
```
|
||||
B4: "Key Assumptions" (section header, bold)
|
||||
B6: "Revenue Growth Rate" C6: 0.05 (blue font, yellow bg)
|
||||
B7: "Gross Margin" C7: 0.65 (blue font, yellow bg)
|
||||
B8: "OpEx as % Revenue" C8: 0.30 (blue font, yellow bg)
|
||||
B9: "Tax Rate" C9: 0.21 (blue font, yellow bg)
|
||||
B10: "Discount Rate (WACC)" C10: 0.10 (blue font, yellow bg)
|
||||
B11: "Terminal Growth Rate" C11: 0.02 (blue font, yellow bg)
|
||||
```
|
||||
|
||||
### Source Documentation for Hardcodes
|
||||
|
||||
Every hardcoded input MUST have a source citation:
|
||||
|
||||
```python
|
||||
# In cell comment
|
||||
ws['C6'].comment = Comment(
|
||||
"Source: Company 10-K, FY2024, Page 45, Revenue Growth",
|
||||
"Z.ai"
|
||||
)
|
||||
|
||||
# Or in adjacent cell (if end of table)
|
||||
ws['D6'] = "Source: Management guidance, Q3 2024 earnings call"
|
||||
ws['D6'].font = Font(size=8, italic=True, color="808080")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Number Formatting (CRITICAL)
|
||||
|
||||
> Finance-specific formats below. For general number formats, see `engines/design.md §10`.
|
||||
> Finance formats take priority when both apply.
|
||||
|
||||
```python
|
||||
FINANCE_FORMATS = {
|
||||
# Currency — zeros as dash, negatives in parentheses
|
||||
'currency': '$#,##0;($#,##0);"-"',
|
||||
'currency_k': '$#,##0,"K";($#,##0,"K");"-"',
|
||||
'currency_mm': '$#,##0.0,,"M";($#,##0.0,,"M");"-"',
|
||||
|
||||
# Percentages — one decimal
|
||||
'pct': '0.0%;(0.0%);"-"',
|
||||
|
||||
# Multiples — for EV/EBITDA, P/E etc.
|
||||
'multiple': '0.0"x";(0.0"x");"-"',
|
||||
|
||||
# Years — MUST be text, not number (avoids "2,024")
|
||||
'year': '@',
|
||||
|
||||
# Integer with thousands separator
|
||||
'integer': '#,##0;(#,##0);"-"',
|
||||
|
||||
# Two decimal places
|
||||
'decimal': '#,##0.00;(#,##0.00);"-"',
|
||||
|
||||
# Shares (millions)
|
||||
'shares': '#,##0.0,,"M"',
|
||||
}
|
||||
|
||||
# Apply
|
||||
cell.number_format = FINANCE_FORMATS['currency_mm']
|
||||
```
|
||||
|
||||
**Always specify units in column headers**: "Revenue ($mm)", "Shares (M)", "Growth (%)"
|
||||
|
||||
---
|
||||
|
||||
## IB Model Layout Rules
|
||||
|
||||
> All colors below use **design tokens from `engines/design.md`**. Do not hardcode hex values.
|
||||
> Finance-specific overrides (IB text color rules, section dividers) are in `design.md §2.4`.
|
||||
|
||||
### Section Headers
|
||||
```python
|
||||
# Dark background, white bold text, merged across data width
|
||||
# Uses PRIMARY from design.md (or Finance palette PRIMARY from design.md)
|
||||
ws.merge_cells('B10:H10')
|
||||
ws['B10'] = 'Income Statement'
|
||||
ws['B10'].fill = PatternFill('solid', fgColor=PRIMARY)
|
||||
ws['B10'].font = Font(name=FONT_NAME, size=12, bold=HEADER_BOLD, color='FFFFFF')
|
||||
```
|
||||
|
||||
### Data Alignment
|
||||
- Column labels (years, quarters): **right-aligned**
|
||||
- Row labels (line items): **left-aligned**
|
||||
- Submetrics: **indented** (add 2-3 spaces prefix)
|
||||
|
||||
```python
|
||||
# Parent line item
|
||||
ws['B12'] = 'Revenue'
|
||||
ws['B12'].font = Font(name=FONT_NAME, bold=HEADER_BOLD)
|
||||
|
||||
# Sub line item (indented)
|
||||
ws['B13'] = ' Product Revenue'
|
||||
ws['B14'] = ' Service Revenue'
|
||||
```
|
||||
|
||||
### Totals Formatting
|
||||
```python
|
||||
# Uses design tokens — see engines/design.md §6.3
|
||||
total_border = Border(top=Side(style='thin', color=PRIMARY))
|
||||
for col in range(3, 9): # C through H
|
||||
cell = ws.cell(row=total_row, column=col)
|
||||
cell.font = Font(name=FONT_NAME, bold=HEADER_BOLD)
|
||||
cell.border = total_border
|
||||
```
|
||||
|
||||
### Grid Lines
|
||||
```python
|
||||
ws.sheet_view.showGridLines = False # Standard — defined in design.md §7.3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Balance Check Pattern
|
||||
|
||||
For any financial model with a balance sheet:
|
||||
|
||||
```python
|
||||
# Balance check row (should always be 0)
|
||||
check_row = bs_end + 2
|
||||
ws.cell(row=check_row, column=2, value='Balance Check')
|
||||
for col in range(3, last_col + 1):
|
||||
letter = get_column_letter(col)
|
||||
ws.cell(row=check_row, column=col).value = \
|
||||
f'={letter}{assets_total_row}-{letter}{liab_total_row}-{letter}{equity_total_row}'
|
||||
# Conditional: red if not zero
|
||||
ws.conditional_formatting.add(
|
||||
f'{letter}{check_row}',
|
||||
CellIsRule(operator='notEqual', formula=['0'],
|
||||
font=Font(color='FF0000', bold=True))
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sensitivity / Scenario Tables
|
||||
|
||||
```python
|
||||
# Two-way data table: vary growth rate (rows) × discount rate (cols)
|
||||
# Row headers: growth rates
|
||||
growth_rates = [0.02, 0.03, 0.04, 0.05, 0.06]
|
||||
# Col headers: discount rates
|
||||
discount_rates = [0.08, 0.09, 0.10, 0.11, 0.12]
|
||||
|
||||
# Write headers
|
||||
for i, g in enumerate(growth_rates):
|
||||
ws.cell(row=start_row + i + 1, column=start_col, value=g)
|
||||
ws.cell(row=start_row + i + 1, column=start_col).number_format = '0.0%'
|
||||
ws.cell(row=start_row + i + 1, column=start_col).font = Font(color='0000FF')
|
||||
|
||||
for j, d in enumerate(discount_rates):
|
||||
ws.cell(row=start_row, column=start_col + j + 1, value=d)
|
||||
ws.cell(row=start_row, column=start_col + j + 1).number_format = '0.0%'
|
||||
ws.cell(row=start_row, column=start_col + j + 1).font = Font(color='0000FF')
|
||||
|
||||
# Fill formulas for each combination
|
||||
# Yellow background for the cell matching base case assumptions
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Projection Period Patterns
|
||||
|
||||
```python
|
||||
# Historical + Projected columns
|
||||
years = ['FY2022', 'FY2023', 'FY2024', 'FY2025E', 'FY2026E', 'FY2027E']
|
||||
|
||||
for i, year in enumerate(years):
|
||||
col = start_col + i
|
||||
cell = ws.cell(row=header_row, column=col, value=year)
|
||||
cell.font = Font(name=FONT_NAME, bold=HEADER_BOLD)
|
||||
cell.alignment = Alignment(horizontal='center')
|
||||
|
||||
# Visual separator between historical and projected
|
||||
if year.endswith('E') and not years[i-1].endswith('E'):
|
||||
# Add left border to mark transition
|
||||
for row in range(header_row, last_row + 1):
|
||||
ws.cell(row=row, column=col).border = Border(
|
||||
left=Side(style='medium', color=PRIMARY))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Additional Model Templates
|
||||
|
||||
### Template: P&L (Profit & Loss) Statement
|
||||
|
||||
```
|
||||
Sheet: "P&L"
|
||||
Row 1: Company Name + Period
|
||||
Row 3: Headers (Month/Quarter columns)
|
||||
|
||||
Revenue Section:
|
||||
Product Revenue =Assumptions!B5 * (1+Assumptions!C5)
|
||||
Service Revenue =Assumptions!B6 * (1+Assumptions!C6)
|
||||
Total Revenue =SUM(above)
|
||||
|
||||
COGS Section:
|
||||
Direct Costs =Total_Revenue * Assumptions!gross_margin
|
||||
Gross Profit =Total_Revenue - Direct_Costs
|
||||
Gross Margin % =IFERROR(Gross_Profit/Total_Revenue, 0)
|
||||
|
||||
OpEx Section:
|
||||
S&M, R&D, G&A (each from Assumptions)
|
||||
Total OpEx =SUM(S&M:G&A)
|
||||
EBITDA =Gross_Profit - Total_OpEx
|
||||
EBITDA Margin % =IFERROR(EBITDA/Total_Revenue, 0)
|
||||
|
||||
Below the Line:
|
||||
D&A, Interest, Tax
|
||||
Net Income =EBITDA - D&A - Interest - Tax
|
||||
```
|
||||
|
||||
### Template: Budget vs Actual
|
||||
|
||||
```
|
||||
Sheet: "Budget vs Actual"
|
||||
Columns: Category | Budget | Actual | Variance | Var %
|
||||
|
||||
Key formulas:
|
||||
Variance = =Actual - Budget
|
||||
Var % = =IFERROR(Variance/Budget, 0)
|
||||
|
||||
Conditional formatting:
|
||||
Var % > 0 → Green font (favorable)
|
||||
Var % < -10% → Red font + red fill (unfavorable)
|
||||
Var % -10~0 → Orange font (watch)
|
||||
|
||||
Summary section:
|
||||
Total Budget =SUM(Budget range)
|
||||
Total Actual =SUM(Actual range)
|
||||
Overall Var % =IFERROR((Total_Actual-Total_Budget)/Total_Budget, 0)
|
||||
```
|
||||
|
||||
### Template: SaaS Metrics Dashboard
|
||||
|
||||
```
|
||||
Sheet: "SaaS Metrics"
|
||||
KPIs (each with formula, not hardcoded):
|
||||
MRR =SUMPRODUCT(Users * ARPU)
|
||||
ARR =MRR * 12
|
||||
Net Revenue Retention = =IFERROR((Starting_MRR + Expansion - Contraction - Churn) / Starting_MRR, 0)
|
||||
CAC =IFERROR(Total_S&M / New_Customers, 0)
|
||||
LTV =IFERROR(ARPU * Gross_Margin / Monthly_Churn_Rate, 0)
|
||||
LTV:CAC Ratio =IFERROR(LTV / CAC, 0)
|
||||
Payback Months =IFERROR(CAC / (ARPU * Gross_Margin), 0)
|
||||
|
||||
Chart: MRR waterfall (starting → new → expansion → contraction → churn → ending)
|
||||
Chart: LTV:CAC trend line
|
||||
```
|
||||
|
||||
### Template: Project Budget Tracker
|
||||
|
||||
```
|
||||
Sheet: "Project Budget"
|
||||
Columns: Phase | Task | Planned Cost | Actual Cost | Remaining | % Spent | Status
|
||||
|
||||
Key formulas:
|
||||
Remaining = =Planned - Actual
|
||||
% Spent = =IFERROR(Actual/Planned, 0)
|
||||
Status = =IF(% Spent>1, "Over Budget", IF(% Spent>0.9, "At Risk", "On Track"))
|
||||
|
||||
Phase subtotals with SUBTOTAL function
|
||||
Grand total row with project-level health indicator
|
||||
```
|
||||
Reference in New Issue
Block a user