
Financial Analyst
- 203 installs
- 451 repo stars
- Updated July 21, 2026
- borghei/claude-skills
Model unit economics, forecast revenue scenarios, and stress-test pricing tiers before committing to packaging, fundraising narratives, or major cost investments.
About
Trains Claude to perform financial analyst work: build scenario models, evaluate unit economics, compare pricing structures, and summarize runway and profitability implications for SaaS and commerce businesses.
- Unit economics modeling
- Revenue and cost forecasts
- Pricing sensitivity analysis
- Break-even and runway views
- Investor-ready metric summaries
Financial Analyst by the numbers
- 203 all-time installs (skills.sh)
- Ranked #462 of 1,106 Finance & Trading skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/borghei/claude-skills --skill financial-analystAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 203 |
|---|---|
| repo stars | ★ 451 |
| Last updated | July 21, 2026 |
| Repository | borghei/claude-skills ↗ |
What it does
Model unit economics, forecast revenue scenarios, and stress-test pricing tiers before committing to packaging, fundraising narratives, or major cost investments.
Files
Financial Analyst Skill
Overview
Production-ready financial analysis toolkit providing ratio analysis, DCF valuation, budget variance analysis, and rolling forecast construction. Designed for financial analysts with 3-6 years experience performing financial modeling, forecasting & budgeting, management reporting, business performance analysis, and investment analysis.
Use when
- The user asks to "run financial ratios", "build a DCF", "analyze budget variance", or "build a forecast"
- A valuation range is needed for an acquisition, fundraise, or board presentation
- Actuals vs budget needs investigation (which variances are material, favorable/unfavorable, department breakdown)
- A rolling 13-week cash flow or driver-based revenue forecast needs construction
- Sensitivity analysis is required to stress-test valuation or forecast assumptions
- The user asks about profitability, liquidity, leverage, efficiency, or valuation metrics with industry context
5-Phase Workflow
Phase 1: Scoping
- Define analysis objectives and stakeholder requirements
- Identify data sources and time periods
- Establish materiality thresholds and accuracy targets
- Select appropriate analytical frameworks
- Validate: materiality threshold is explicit (absolute $ or %), accuracy target is a number, and the decision the analysis supports is named
Phase 2: Data Analysis & Modeling
- Collect and validate financial data (income statement, balance sheet, cash flow)
- Calculate financial ratios across 5 categories (profitability, liquidity, leverage, efficiency, valuation)
- Build DCF models with WACC and terminal value calculations
- Construct budget variance analyses with favorable/unfavorable classification
- Develop driver-based forecasts with scenario modeling
- Validate: input JSON conforms to the expected schema (no missing statements, no mixed periods); WACC inputs sourced within the last quarter; terminal growth rate ≤ long-run GDP growth
Phase 3: Insight Generation
- Interpret ratio trends and benchmark against industry standards
- Identify material variances and root causes
- Assess valuation ranges through sensitivity analysis
- Evaluate forecast scenarios (base/bull/bear) for decision support
- Validate: every material variance has a root-cause hypothesis; DCF sensitivity range is wider than ±15% on WACC and terminal growth
Phase 4: Reporting
- Generate executive summaries with key findings
- Produce detailed variance reports by department and category
- Deliver DCF valuation reports with sensitivity tables
- Present rolling forecasts with trend analysis
- Validate: executive summary leads with the decision-relevant conclusion, not the method; assumptions appendix lists source + last-reviewed date for each
Phase 5: Follow-up
- Track forecast accuracy (target: +/-5% revenue, +/-3% expenses)
- Monitor report delivery timeliness (target: 100% on time)
- Update models with actuals as they become available
- Refine assumptions based on variance analysis
Tools
1. Ratio Calculator (scripts/ratio_calculator.py)
Calculate and interpret financial ratios from financial statement data.
Ratio Categories:
- Profitability: ROE, ROA, Gross Margin, Operating Margin, Net Margin
- Liquidity: Current Ratio, Quick Ratio, Cash Ratio
- Leverage: Debt-to-Equity, Interest Coverage, DSCR
- Efficiency: Asset Turnover, Inventory Turnover, Receivables Turnover, DSO
- Valuation: P/E, P/B, P/S, EV/EBITDA, PEG Ratio
python scripts/ratio_calculator.py sample_financial_data.json
python scripts/ratio_calculator.py sample_financial_data.json --format json
python scripts/ratio_calculator.py sample_financial_data.json --category profitability2. DCF Valuation (scripts/dcf_valuation.py)
Discounted Cash Flow enterprise and equity valuation with sensitivity analysis.
Features:
- WACC calculation via CAPM
- Revenue and free cash flow projections (5-year default)
- Terminal value via perpetuity growth and exit multiple methods
- Enterprise value and equity value derivation
- Two-way sensitivity analysis (discount rate vs growth rate)
python scripts/dcf_valuation.py valuation_data.json
python scripts/dcf_valuation.py valuation_data.json --format json
python scripts/dcf_valuation.py valuation_data.json --projection-years 73. Budget Variance Analyzer (scripts/budget_variance_analyzer.py)
Analyze actual vs budget vs prior year performance with materiality filtering.
Features:
- Dollar and percentage variance calculation
- Materiality threshold filtering (default: 10% or $50K)
- Favorable/unfavorable classification with revenue/expense logic
- Department and category breakdown
- Executive summary generation
python scripts/budget_variance_analyzer.py budget_data.json
python scripts/budget_variance_analyzer.py budget_data.json --format json
python scripts/budget_variance_analyzer.py budget_data.json --threshold-pct 5 --threshold-amt 250004. Forecast Builder (scripts/forecast_builder.py)
Driver-based revenue forecasting with rolling cash flow projection and scenario modeling.
Features:
- Driver-based revenue forecast model
- 13-week rolling cash flow projection
- Scenario modeling (base/bull/bear cases)
- Trend analysis using simple linear regression (standard library)
python scripts/forecast_builder.py forecast_data.json
python scripts/forecast_builder.py forecast_data.json --format json
python scripts/forecast_builder.py forecast_data.json --scenarios base,bull,bearKnowledge Bases
| Reference | Purpose |
|---|---|
references/financial-ratios-guide.md | Ratio formulas, interpretation, industry benchmarks |
references/valuation-methodology.md | DCF methodology, WACC, terminal value, comps |
references/forecasting-best-practices.md | Driver-based forecasting, rolling forecasts, accuracy |
Templates
| Template | Purpose |
|---|---|
assets/variance_report_template.md | Budget variance report template |
assets/dcf_analysis_template.md | DCF valuation analysis template |
assets/forecast_report_template.md | Revenue forecast report template |
Industry Adaptations
SaaS
- Key metrics: MRR, ARR, CAC, LTV, Churn Rate, Net Revenue Retention
- Revenue recognition: subscription-based, deferred revenue tracking
- Unit economics: CAC payback period, LTV/CAC ratio
- Cohort analysis for retention and expansion revenue
Retail
- Key metrics: Same-store sales, Revenue per square foot, Inventory turnover
- Seasonal adjustment factors in forecasting
- Gross margin analysis by product category
- Working capital cycle optimization
Manufacturing
- Key metrics: Gross margin by product line, Capacity utilization, COGS breakdown
- Bill of materials cost analysis
- Absorption vs variable costing impact
- Capital expenditure planning and ROI
Financial Services
- Key metrics: Net Interest Margin, Efficiency Ratio, ROA, Tier 1 Capital
- Regulatory capital requirements
- Credit loss provisioning and reserves
- Fee income analysis and diversification
Healthcare
- Key metrics: Revenue per patient, Payer mix, Days in A/R, Operating margin
- Reimbursement rate analysis by payer
- Case mix index impact on revenue
- Compliance cost allocation
Key Metrics & Targets
| Metric | Target |
|---|---|
| Forecast accuracy (revenue) | +/-5% |
| Forecast accuracy (expenses) | +/-3% |
| Report delivery | 100% on time |
| Model documentation | Complete for all assumptions |
| Variance explanation | 100% of material variances |
Input Data Format
All scripts accept JSON input files. See assets/sample_financial_data.json for the complete input schema covering all four tools.
Dependencies
None - All scripts use Python standard library only (math, statistics, json, argparse, datetime). No numpy, pandas, or scipy required.
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
| All ratios return 0.00 | Missing or zeroed financial statement fields in input JSON | Verify income_statement, balance_sheet, and cash_flow keys are populated with non-zero values; check field names match expected schema |
| DCF yields negative equity value | Net debt exceeds enterprise value, or WACC is set lower than terminal growth rate | Confirm net_debt is accurate; ensure terminal_growth_rate < WACC (typically 2-3% vs 8-12%); review capital structure assumptions |
| Sensitivity table shows "N/A" across entire row | WACC value in that row is less than or equal to every terminal growth rate in the range | Widen the gap between WACC and terminal growth; raise WACC inputs or lower the growth range in assumptions.terminal_growth_rate |
| Budget variance analyzer flags every line as material | Materiality thresholds set too low relative to the data scale | Increase --threshold-pct (e.g., from 5 to 10) and --threshold-amt (e.g., from 25000 to 100000) to match organizational materiality policy |
| Forecast builder produces flat projections | Historical data has fewer than 2 periods, or revenue_growth_rate is set to 0 | Provide at least 3-4 historical periods in historical_periods; set a non-zero revenue_growth_rate in assumptions |
| JSON parsing error on script execution | Malformed JSON input file (trailing commas, unquoted keys, encoding issues) | Validate input with python -m json.tool input_file.json; ensure UTF-8 encoding; remove trailing commas and comments |
| Valuation ratios all show "Insufficient data" | Missing market_data section in input JSON (share price, shares outstanding) | Add the market_data object with share_price, shares_outstanding, and earnings_growth_rate fields to the input file |
Success Criteria
- Forecast Accuracy: Revenue forecasts land within +/-5% of actuals; expense forecasts within +/-3% over rolling 12-month periods
- Variance Coverage: 100% of material variances (exceeding threshold) include documented root-cause explanations and corrective action plans
- Valuation Confidence: DCF-derived equity value falls within 15% of comparable-company and precedent-transaction benchmarks, validated through sensitivity analysis
- Report Timeliness: All financial analysis deliverables (ratio reports, variance analyses, forecast updates) published within agreed SLA -- target 100% on-time delivery
- Model Integrity: Every assumption in DCF and forecast models is documented with source, rationale, and last-reviewed date; WACC inputs refresh quarterly against market data
- Stakeholder Adoption: Financial models and dashboards referenced in at least 80% of executive budget reviews, board presentations, and investment committee decisions
- Analytical Efficiency: End-to-end analysis cycle time (data collection through report delivery) reduced by 40%+ compared to manual spreadsheet workflows, measured per reporting period
Scope & Limitations
This skill covers:
- Quantitative financial ratio analysis across profitability, liquidity, leverage, efficiency, and valuation categories with built-in industry benchmarking
- Discounted Cash Flow (DCF) enterprise and equity valuation using CAPM-based WACC, perpetuity growth and exit multiple terminal value methods, and two-way sensitivity analysis
- Budget variance analysis with materiality filtering, favorable/unfavorable classification, department and category breakdowns, and executive summary generation
- Driver-based revenue forecasting with 13-week rolling cash flow projection, base/bull/bear scenario modeling, and linear regression trend analysis
This skill does NOT cover:
- Real-time market data feeds, live stock price retrieval, or automated data ingestion from ERP/accounting systems (all input is via static JSON files)
- Qualitative analysis such as management quality assessment, competitive moat evaluation, ESG scoring, or regulatory risk judgment
- Tax optimization, transfer pricing, multi-entity consolidation, or jurisdiction-specific accounting treatments (IFRS vs GAAP reconciliation)
- Monte Carlo simulation, options pricing (Black-Scholes), credit risk modeling, or any analysis requiring external libraries beyond the Python standard library
Anti-patterns
| Anti-pattern | Failure mode | Fix |
|---|---|---|
| Building a DCF on a single-scenario forecast | False precision; one number presented as a target price | Always run base/bull/bear; present valuation as a range with sensitivity tables |
| Terminal growth rate ≥ long-run GDP growth | Valuation dominated by terminal value assuming perpetual above-economy growth | Cap terminal growth at 2-3% (long-run GDP proxy); if comps justify higher, flag explicitly |
| WACC inputs more than a quarter old | Rate environment moved; discount rate is wrong; valuation wrong | Refresh risk-free rate, ERP, and beta quarterly; document "last reviewed" date per input |
| Benchmarking ratios against a generic "industry average" | Peer set is wrong; conclusions are wrong | Use a specific comparable-company set (size, geography, business model) — see industry benchmarks in references/financial-ratios-guide.md |
| Reporting every variance instead of filtering by materiality | Stakeholders tune out; real issues buried | Apply a materiality threshold (absolute $ or % of budget); below threshold goes into an appendix, not the report |
| Favorable variance = "good"; unfavorable = "bad" | Misses revenue shortfalls masked by expense underspend; misses over-delivery hiding scope cuts | Always pair the classification with a root-cause note — direction alone is not insight |
| Mixing forecast periods (quarterly actuals against annual budget) | Variances that don't reconcile; trust collapses | Run the tools on matched periods only; if a period is partial, annotate and use period-adjusted comparisons |
| Treating model output as the answer | Model is a reasoning aid, not a decision | Lead the executive summary with the decision; put the model outputs in support |
Integration Points
| Related Skill | Domain | Integration Use Case |
|---|---|---|
c-level-advisor/ceo-advisor | C-Level Advisory | Feed DCF valuation outputs and scenario comparisons into CEO strategic investment decisions and board-ready presentations |
c-level-advisor/cto-advisor | C-Level Advisory | Provide technology investment ROI analysis and CapEx forecasts to support build-vs-buy and infrastructure scaling decisions |
business-growth/revenue-operations | Business & Growth | Connect revenue forecasts and unit-economics metrics (CAC, LTV, payback period) to pipeline and go-to-market planning |
product-team/product-manager | Product Team | Supply budget variance data and RICE-weighted financial projections for feature prioritization and resource allocation |
data-analytics/data-analyst | Data Analytics | Export ratio analysis and forecast outputs as structured JSON for BI dashboard integration and trend visualization |
project-management/project-financial-management | Project Management | Align budget variance analysis with project-level cost tracking, earned value management, and milestone-based funding releases |
Tool Reference
scripts/ratio_calculator.py
Calculate and interpret financial ratios across 5 categories with industry benchmarking.
usage: ratio_calculator.py [-h] [--format {text,json}]
[--category {profitability,liquidity,leverage,efficiency,valuation}]
input_file
positional arguments:
input_file Path to JSON file with financial statement data
(must contain income_statement, balance_sheet,
cash_flow, and optionally market_data objects)
options:
-h, --help Show help message and exit
--format {text,json} Output format (default: text)
--category {profitability,liquidity,leverage,efficiency,valuation}
Calculate only a specific ratio category;
omit to calculate all 5 categories (20 ratios)Ratios computed: ROE, ROA, Gross Margin, Operating Margin, Net Margin, Current Ratio, Quick Ratio, Cash Ratio, Debt-to-Equity, Interest Coverage, DSCR, Asset Turnover, Inventory Turnover, Receivables Turnover, DSO, P/E, P/B, P/S, EV/EBITDA, PEG Ratio.
scripts/dcf_valuation.py
Discounted Cash Flow enterprise and equity valuation with WACC calculation and sensitivity analysis.
usage: dcf_valuation.py [-h] [--format {text,json}]
[--projection-years PROJECTION_YEARS]
input_file
positional arguments:
input_file Path to JSON file with valuation data
(must contain historical and assumptions objects)
options:
-h, --help Show help message and exit
--format {text,json} Output format (default: text)
--projection-years PROJECTION_YEARS
Number of projection years; overrides the value
in the input file (default: 5)Outputs: WACC (CAPM), projected revenue and FCF, terminal value (perpetuity growth + exit multiple), enterprise value, equity value, value per share, and a two-way sensitivity table (WACC vs terminal growth rate).
scripts/budget_variance_analyzer.py
Analyze actual vs budget vs prior year performance with materiality filtering and executive summaries.
usage: budget_variance_analyzer.py [-h] [--format {text,json}]
[--threshold-pct THRESHOLD_PCT]
[--threshold-amt THRESHOLD_AMT]
input_file
positional arguments:
input_file Path to JSON file with budget data
(must contain line_items array with actual,
budget, and optionally prior_year values)
options:
-h, --help Show help message and exit
--format {text,json} Output format (default: text)
--threshold-pct THRESHOLD_PCT
Materiality threshold as percentage (default: 10.0)
--threshold-amt THRESHOLD_AMT
Materiality threshold as dollar amount (default: 50000.0)Outputs: Executive summary (revenue/expense/net impact), all variances with favorability classification, material variances filtered by threshold, department summary, and category summary.
scripts/forecast_builder.py
Driver-based revenue forecasting with rolling cash flow projection and multi-scenario modeling.
usage: forecast_builder.py [-h] [--format {text,json}]
[--scenarios SCENARIOS]
input_file
positional arguments:
input_file Path to JSON file with forecast data
(must contain historical_periods, drivers,
assumptions, cash_flow_inputs, and scenarios objects)
options:
-h, --help Show help message and exit
--format {text,json} Output format (default: text)
--scenarios SCENARIOS
Comma-separated list of scenarios to model
(default: base,bull,bear)Outputs: Trend analysis (linear regression, growth rates, seasonality index), scenario comparison table, per-period forecast detail (revenue, COGS, gross profit, OpEx, operating income), and 13-week rolling cash flow projection with runway calculation.
DCF Valuation Analysis
Report Header
| Field | Value |
|---|---|
| Company | [Company Name] |
| Ticker | [Ticker Symbol] |
| Analysis Date | [Date] |
| Prepared By | [Analyst Name] |
| Current Share Price | $[X] |
| Shares Outstanding | [X]M |
Executive Summary
[2-3 sentence overview of the valuation conclusion, including the implied value range per share compared to the current market price, and whether the stock appears undervalued, fairly valued, or overvalued.]
Valuation Summary
| Method | Enterprise Value | Equity Value | Value Per Share | vs Current Price |
|---|---|---|---|---|
| DCF (Perpetuity Growth) | $[X]M | $[X]M | $[X] | [X]% |
| DCF (Exit Multiple) | $[X]M | $[X]M | $[X] | [X]% |
| Comparable Companies | $[X]M | $[X]M | $[X] | [X]% |
| Blended Estimate | $[X]M | $[X]M | $[X] | [X]% |
Investment Thesis
[Summary of the investment case, including key strengths, risks, and catalysts.]
Historical Financial Summary
| ($M) | FY-4 | FY-3 | FY-2 | FY-1 | LTM |
|---|---|---|---|---|---|
| Revenue | [X] | [X] | [X] | [X] | [X] |
| Revenue Growth | [X]% | [X]% | [X]% | [X]% | [X]% |
| Gross Profit | [X] | [X] | [X] | [X] | [X] |
| Gross Margin | [X]% | [X]% | [X]% | [X]% | [X]% |
| EBITDA | [X] | [X] | [X] | [X] | [X] |
| EBITDA Margin | [X]% | [X]% | [X]% | [X]% | [X]% |
| Net Income | [X] | [X] | [X] | [X] | [X] |
| Free Cash Flow | [X] | [X] | [X] | [X] | [X] |
WACC Calculation
Cost of Equity (CAPM)
| Component | Value | Source |
|---|---|---|
| Risk-Free Rate | [X]% | [10-Year Treasury] |
| Equity Risk Premium | [X]% | [Damodaran / internal] |
| Beta (Levered) | [X] | [Bloomberg / regression] |
| Size Premium | [X]% | [Duff & Phelps] |
| Company-Specific Risk | [X]% | [Analyst judgment] |
| Cost of Equity | [X]% |
Cost of Debt
| Component | Value |
|---|---|
| Pre-Tax Cost of Debt | [X]% |
| Tax Rate | [X]% |
| After-Tax Cost of Debt | [X]% |
Capital Structure
| Component | Market Value ($M) | Weight |
|---|---|---|
| Equity | [X] | [X]% |
| Debt | [X] | [X]% |
| Total Capital | [X] | 100% |
WACC Result: [X]%
Revenue Projections
| ($M) | Year 1 | Year 2 | Year 3 | Year 4 | Year 5 |
|---|---|---|---|---|---|
| Revenue | [X] | [X] | [X] | [X] | [X] |
| Growth Rate | [X]% | [X]% | [X]% | [X]% | [X]% |
Key Revenue Assumptions:
- [Assumption 1 with supporting rationale]
- [Assumption 2 with supporting rationale]
- [Assumption 3 with supporting rationale]
Free Cash Flow Projections
| ($M) | Year 1 | Year 2 | Year 3 | Year 4 | Year 5 |
|---|---|---|---|---|---|
| Revenue | [X] | [X] | [X] | [X] | [X] |
| EBIT | [X] | [X] | [X] | [X] | [X] |
| Taxes on EBIT | ([X]) | ([X]) | ([X]) | ([X]) | ([X]) |
| NOPAT | [X] | [X] | [X] | [X] | [X] |
| D&A | [X] | [X] | [X] | [X] | [X] |
| CapEx | ([X]) | ([X]) | ([X]) | ([X]) | ([X]) |
| Change in NWC | ([X]) | ([X]) | ([X]) | ([X]) | ([X]) |
| Unlevered FCF | [X] | [X] | [X] | [X] | [X] |
| FCF Margin | [X]% | [X]% | [X]% | [X]% | [X]% |
Terminal Value
Perpetuity Growth Method
| Component | Value |
|---|---|
| Terminal FCF | $[X]M |
| Terminal Growth Rate | [X]% |
| WACC | [X]% |
| Terminal Value | $[X]M |
| TV as % of EV | [X]% |
Exit Multiple Method
| Component | Value |
|---|---|
| Terminal EBITDA | $[X]M |
| Exit EV/EBITDA Multiple | [X]x |
| Terminal Value | $[X]M |
| TV as % of EV | [X]% |
Enterprise Value Bridge
| Component | Perpetuity Growth | Exit Multiple |
|---|---|---|
| PV of Projected FCFs | $[X]M | $[X]M |
| PV of Terminal Value | $[X]M | $[X]M |
| Enterprise Value | $[X]M | $[X]M |
| Less: Net Debt | ($[X]M) | ($[X]M) |
| Less: Minority Interest | ($[X]M) | ($[X]M) |
| Equity Value | $[X]M | $[X]M |
| Diluted Shares (M) | [X] | [X] |
| Value Per Share | $[X] | $[X] |
Sensitivity Analysis
WACC vs Terminal Growth Rate (Enterprise Value, $M)
| WACC \ Growth | [g-2]% | [g-1]% | [g]% | [g+1]% | [g+2]% |
|---|---|---|---|---|---|
| [WACC-2]% | [X] | [X] | [X] | [X] | [X] |
| [WACC-1]% | [X] | [X] | [X] | [X] | [X] |
| [WACC]% | [X] | [X] | [X] | [X] | [X] |
| [WACC+1]% | [X] | [X] | [X] | [X] | [X] |
| [WACC+2]% | [X] | [X] | [X] | [X] | [X] |
Implied Share Price Range
| Scenario | Share Price | vs Current | Upside/Downside |
|---|---|---|---|
| Bear Case (WACC+2%, g-2%) | $[X] | [X]% | [X]% |
| Base Case | $[X] | [X]% | [X]% |
| Bull Case (WACC-2%, g+2%) | $[X] | [X]% | [X]% |
Key Risks to Valuation
1. [Risk 1] - [Description and potential impact on value] 2. [Risk 2] - [Description and potential impact on value] 3. [Risk 3] - [Description and potential impact on value]
Comparable Company Analysis
| Company | EV/Revenue | EV/EBITDA | P/E | Growth | Margin |
|---|---|---|---|---|---|
| [Comp 1] | [X]x | [X]x | [X]x | [X]% | [X]% |
| [Comp 2] | [X]x | [X]x | [X]x | [X]% | [X]% |
| [Comp 3] | [X]x | [X]x | [X]x | [X]% | [X]% |
| [Comp 4] | [X]x | [X]x | [X]x | [X]% | [X]% |
| Median | [X]x | [X]x | [X]x | [X]% | [X]% |
| [Target] | [X]x | [X]x | [X]x | [X]% | [X]% |
Conclusion and Recommendation
Valuation Range: $[Low] - $[High] per share
Current Price: $[X]
Recommendation: [Buy / Hold / Sell]
[Final paragraph with investment recommendation rationale, key upside catalysts, and primary risks to monitor.]
---
Analysis generated using Financial Analyst Skill - DCF Valuation Model
{
"_description": "Expected output structure for all 4 scripts. Values are illustrative to show data format.",
"ratio_calculator_output": {
"categories": {
"profitability": {
"roe": {
"value": 0.25,
"formula": "Net Income / Total Equity",
"name": "Return on Equity",
"interpretation": "Good - above average performance"
},
"roa": {
"value": 0.1375,
"formula": "Net Income / Total Assets",
"name": "Return on Assets",
"interpretation": "Excellent - significantly above peers"
},
"gross_margin": {
"value": 0.40,
"formula": "(Revenue - COGS) / Revenue",
"name": "Gross Margin",
"interpretation": "Acceptable - within normal range"
},
"operating_margin": {
"value": 0.16,
"formula": "Operating Income / Revenue",
"name": "Operating Margin",
"interpretation": "Good - above average performance"
},
"net_margin": {
"value": 0.11,
"formula": "Net Income / Revenue",
"name": "Net Margin",
"interpretation": "Good - above average performance"
}
},
"liquidity": {
"current_ratio": {"value": 1.875, "name": "Current Ratio"},
"quick_ratio": {"value": 1.4375, "name": "Quick Ratio"},
"cash_ratio": {"value": 0.625, "name": "Cash Ratio"}
},
"leverage": {
"debt_to_equity": {"value": 0.545, "name": "Debt-to-Equity Ratio"},
"interest_coverage": {"value": 6.67, "name": "Interest Coverage Ratio"},
"dscr": {"value": 2.50, "name": "Debt Service Coverage Ratio"}
},
"efficiency": {
"asset_turnover": {"value": 1.25, "name": "Asset Turnover"},
"inventory_turnover": {"value": 8.57, "name": "Inventory Turnover"},
"receivables_turnover": {"value": 8.33, "name": "Receivables Turnover"},
"dso": {"value": 43.8, "name": "Days Sales Outstanding"}
},
"valuation": {
"pe_ratio": {"value": 81.82, "name": "Price-to-Earnings Ratio"},
"pb_ratio": {"value": 20.45, "name": "Price-to-Book Ratio"},
"ps_ratio": {"value": 9.0, "name": "Price-to-Sales Ratio"},
"ev_ebitda": {"value": 45.7, "name": "EV/EBITDA"},
"peg_ratio": {"value": 6.82, "name": "PEG Ratio"}
}
}
},
"dcf_valuation_output": {
"wacc": 0.085,
"projected_revenue": [55000000, 59950000, 64746000, 69278220, 73434953],
"projected_fcf": [6600000, 7793500, 8416980, 9698951, 10280893],
"terminal_value": {
"perpetuity_growth": 175382225,
"exit_multiple": 176243484
},
"enterprise_value": {
"perpetuity_growth": 149500000,
"exit_multiple": 150100000
},
"equity_value": {
"perpetuity_growth": 142500000,
"exit_multiple": 143100000
},
"value_per_share": {
"perpetuity_growth": 14.25,
"exit_multiple": 14.31
},
"sensitivity_analysis": {
"wacc_values": [0.065, 0.075, 0.085, 0.095, 0.105],
"growth_values": [0.015, 0.020, 0.025, 0.030, 0.035],
"enterprise_value_table": "5x5 nested list of enterprise values",
"share_price_table": "5x5 nested list of share prices"
}
},
"budget_variance_output": {
"executive_summary": {
"period": "Q4 2025",
"company": "Acme Corp",
"total_line_items": 10,
"material_variances_count": 3,
"favorable_count": 4,
"unfavorable_count": 6,
"revenue": {
"actual": 15700000,
"budget": 15500000,
"variance_amount": 200000,
"variance_pct": 1.29
},
"expenses": {
"actual": 13255000,
"budget": 12520000,
"variance_amount": 735000,
"variance_pct": 5.87
},
"net_impact": -535000
},
"material_variances": [
{
"name": "Cost of Goods Sold",
"budget_variance_amount": 600000,
"budget_variance_pct": 8.33,
"favorability": "Unfavorable"
}
],
"department_summary": {
"Sales": {"total_variance": 0, "variance_pct": 0},
"Operations": {"total_variance": 0, "variance_pct": 0}
},
"category_summary": {
"Revenue": {"total_variance": 0, "variance_pct": 0},
"COGS": {"total_variance": 0, "variance_pct": 0}
}
},
"forecast_builder_output": {
"trend_analysis": {
"trend": {
"slope": 650000,
"intercept": 9500000,
"r_squared": 0.98,
"direction": "upward"
},
"average_growth_rate": 0.06,
"seasonality_index": [0.92, 0.97, 1.01, 1.10]
},
"scenario_comparison": {
"comparison": [
{"scenario": "base", "total_revenue": 185000000, "growth_rate": 0.08},
{"scenario": "bull", "total_revenue": 210000000, "growth_rate": 0.12},
{"scenario": "bear", "total_revenue": 165000000, "growth_rate": 0.05}
]
},
"rolling_cash_flow": {
"weeks": 13,
"opening_balance": 2500000,
"closing_balance": 2800000,
"total_inflows": 4200000,
"total_outflows": 3900000,
"minimum_balance": 2100000,
"minimum_balance_week": 4,
"cash_runway_weeks": 12
}
}
}
Revenue Forecast Report
Report Header
| Field | Value |
|---|---|
| Company | [Company Name] |
| Forecast Period | [Start] to [End] |
| Prepared By | [Analyst Name] |
| Date | [Report Date] |
| Forecast Type | [Driver-Based / Trend-Based / Blended] |
Executive Summary
[2-3 sentence overview of the revenue forecast, key assumptions, and confidence level. Highlight the base case total revenue, expected growth rate, and any significant departures from prior forecast or budget.]
Key Metrics at a Glance
| Metric | Value |
|---|---|
| Base Case Total Revenue | $[X]M |
| Expected Growth Rate | [X]% |
| Forecast Confidence | [High / Medium / Low] |
| Revenue Range (Bear to Bull) | $[X]M - $[X]M |
| Primary Revenue Driver | [Driver description] |
Historical Trend Analysis
Revenue Trend
| Period | Revenue | Growth Rate | Gross Margin |
|---|---|---|---|
| [Q/Year-4] | $[X]M | - | [X]% |
| [Q/Year-3] | $[X]M | [X]% | [X]% |
| [Q/Year-2] | $[X]M | [X]% | [X]% |
| [Q/Year-1] | $[X]M | [X]% | [X]% |
| [Current] | $[X]M | [X]% | [X]% |
Trend Statistics
| Metric | Value |
|---|---|
| Average Growth Rate | [X]% |
| Trend Direction | [Upward / Flat / Downward] |
| R-squared (fit quality) | [X] |
| Seasonality Detected | [Yes / No] |
Revenue Drivers
Primary Drivers
| Driver | Current Value | Projected Value | Growth |
|---|---|---|---|
| [Units / Customers / etc.] | [X] | [X] | [X]% |
| [Price / ARPU / etc.] | $[X] | $[X] | [X]% |
| [Conversion / Retention] | [X]% | [X]% | [X]pp |
Driver Assumptions
1. [Driver 1]: [Assumption and rationale] 2. [Driver 2]: [Assumption and rationale] 3. [Driver 3]: [Assumption and rationale]
Scenario Comparison
Summary
| Scenario | Total Revenue | Growth Rate | Op. Income | Gross Margin | Probability |
|---|---|---|---|---|---|
| Bull | $[X]M | [X]% | $[X]M | [X]% | [X]% |
| Base | $[X]M | [X]% | $[X]M | [X]% | [X]% |
| Bear | $[X]M | [X]% | $[X]M | [X]% | [X]% |
Scenario Assumptions
Bull Case:
- [Key assumption 1]
- [Key assumption 2]
- [Trigger: what conditions would cause this scenario]
Base Case:
- [Key assumption 1]
- [Key assumption 2]
Bear Case:
- [Key assumption 1]
- [Key assumption 2]
- [Trigger: what conditions would cause this scenario]
Monthly/Quarterly Forecast Detail (Base Case)
| Period | Revenue | COGS | Gross Profit | OpEx | Op. Income |
|---|---|---|---|---|---|
| [Period 1] | $[X] | $[X] | $[X] | $[X] | $[X] |
| [Period 2] | $[X] | $[X] | $[X] | $[X] | $[X] |
| [Period 3] | $[X] | $[X] | $[X] | $[X] | $[X] |
| [Period 4] | $[X] | $[X] | $[X] | $[X] | $[X] |
| ... | ... | ... | ... | ... | ... |
| Total | $[X] | $[X] | $[X] | $[X] | $[X] |
13-Week Rolling Cash Flow
Summary
| Metric | Value |
|---|---|
| Opening Cash Balance | $[X] |
| Projected Closing Balance | $[X] |
| Net Cash Change | $[X] |
| Minimum Cash Balance | $[X] (Week [N]) |
| Cash Runway | [N] weeks |
Weekly Cash Flow Projection
| Week | Inflows | Outflows | Net Cash Flow | Closing Balance |
|---|---|---|---|---|
| 1 | $[X] | $[X] | $[X] | $[X] |
| 2 | $[X] | $[X] | $[X] | $[X] |
| 3 | $[X] | $[X] | $[X] | $[X] |
| ... | ... | ... | ... | ... |
| 13 | $[X] | $[X] | $[X] | $[X] |
Cash Flow Notes
- Week [N]: [Description of any significant one-time items]
- Week [N]: [Description of any significant one-time items]
Forecast Accuracy Tracking
vs Prior Forecast
| Metric | Prior Forecast | Current Forecast | Change |
|---|---|---|---|
| Revenue | $[X]M | $[X]M | [X]% |
| Growth Rate | [X]% | [X]% | [X]pp |
| Gross Margin | [X]% | [X]% | [X]pp |
Historical Forecast Accuracy (MAPE)
| Period | Forecast | Actual | Error | MAPE |
|---|---|---|---|---|
| [Period-3] | $[X] | $[X] | $[X] | [X]% |
| [Period-2] | $[X] | $[X] | $[X] | [X]% |
| [Period-1] | $[X] | $[X] | $[X] | [X]% |
| Average MAPE | [X]% |
Key Risks and Assumptions
Upside Risks
1. [Risk/opportunity with quantified potential impact] 2. [Risk/opportunity with quantified potential impact]
Downside Risks
1. [Risk with quantified potential impact] 2. [Risk with quantified potential impact]
Critical Assumptions
1. [Assumption that if wrong would materially change the forecast] 2. [Assumption that if wrong would materially change the forecast]
Recommendations
1. [Recommendation 1]: [Specific action with expected impact] 2. [Recommendation 2]: [Specific action with expected impact] 3. [Recommendation 3]: [Specific action with expected impact]
Next Steps
| # | Action | Owner | Due Date |
|---|---|---|---|
| 1 | [Action item] | [Name] | [Date] |
| 2 | [Action item] | [Name] | [Date] |
| 3 | [Action item] | [Name] | [Date] |
---
Report generated using Financial Analyst Skill - Forecast Builder
{
"_description": "Sample financial data covering all 4 scripts: ratio_calculator, dcf_valuation, budget_variance_analyzer, and forecast_builder",
"ratio_analysis": {
"income_statement": {
"revenue": 50000000,
"cost_of_goods_sold": 30000000,
"operating_income": 8000000,
"ebitda": 10000000,
"net_income": 5500000,
"interest_expense": 1200000
},
"balance_sheet": {
"total_assets": 40000000,
"current_assets": 15000000,
"cash_and_equivalents": 5000000,
"accounts_receivable": 6000000,
"inventory": 3500000,
"total_equity": 22000000,
"total_debt": 12000000,
"current_liabilities": 8000000
},
"cash_flow": {
"operating_cash_flow": 7500000,
"total_debt_service": 3000000
},
"market_data": {
"share_price": 45.00,
"shares_outstanding": 10000000,
"market_cap": 450000000,
"earnings_growth_rate": 0.12
}
},
"dcf_valuation": {
"historical": {
"revenue": [38000000, 42000000, 45000000, 48000000, 50000000],
"net_income": [3800000, 4200000, 4500000, 5000000, 5500000],
"net_debt": 7000000,
"shares_outstanding": 10000000
},
"assumptions": {
"projection_years": 5,
"revenue_growth_rates": [0.10, 0.09, 0.08, 0.07, 0.06],
"fcf_margins": [0.12, 0.13, 0.13, 0.14, 0.14],
"default_revenue_growth": 0.05,
"default_fcf_margin": 0.10,
"terminal_growth_rate": 0.025,
"terminal_ebitda_margin": 0.20,
"exit_ev_ebitda_multiple": 12.0,
"wacc_inputs": {
"risk_free_rate": 0.04,
"equity_risk_premium": 0.06,
"beta": 1.1,
"cost_of_debt": 0.055,
"tax_rate": 0.25,
"debt_weight": 0.30,
"equity_weight": 0.70
}
}
},
"budget_variance": {
"company": "Acme Corp",
"period": "Q4 2025",
"line_items": [
{
"name": "Product Revenue",
"type": "revenue",
"department": "Sales",
"category": "Revenue",
"actual": 12500000,
"budget": 12000000,
"prior_year": 10800000
},
{
"name": "Service Revenue",
"type": "revenue",
"department": "Sales",
"category": "Revenue",
"actual": 3200000,
"budget": 3500000,
"prior_year": 2900000
},
{
"name": "Cost of Goods Sold",
"type": "expense",
"department": "Operations",
"category": "COGS",
"actual": 7800000,
"budget": 7200000,
"prior_year": 6700000
},
{
"name": "Salaries & Wages",
"type": "expense",
"department": "Human Resources",
"category": "Personnel",
"actual": 2100000,
"budget": 2200000,
"prior_year": 1950000
},
{
"name": "Marketing & Advertising",
"type": "expense",
"department": "Marketing",
"category": "Sales & Marketing",
"actual": 850000,
"budget": 750000,
"prior_year": 680000
},
{
"name": "Software & Technology",
"type": "expense",
"department": "Engineering",
"category": "Technology",
"actual": 420000,
"budget": 400000,
"prior_year": 350000
},
{
"name": "Office & Facilities",
"type": "expense",
"department": "Operations",
"category": "G&A",
"actual": 180000,
"budget": 200000,
"prior_year": 175000
},
{
"name": "Travel & Entertainment",
"type": "expense",
"department": "Sales",
"category": "Sales & Marketing",
"actual": 95000,
"budget": 120000,
"prior_year": 88000
},
{
"name": "Professional Services",
"type": "expense",
"department": "Finance",
"category": "G&A",
"actual": 310000,
"budget": 250000,
"prior_year": 220000
},
{
"name": "R&D Expenses",
"type": "expense",
"department": "Engineering",
"category": "R&D",
"actual": 1500000,
"budget": 1400000,
"prior_year": 1200000
}
]
},
"forecast": {
"historical_periods": [
{"period": "Q1 2024", "revenue": 10500000, "gross_profit": 4200000, "operating_income": 1575000},
{"period": "Q2 2024", "revenue": 11200000, "gross_profit": 4480000, "operating_income": 1680000},
{"period": "Q3 2024", "revenue": 11800000, "gross_profit": 4720000, "operating_income": 1770000},
{"period": "Q4 2024", "revenue": 12500000, "gross_profit": 5000000, "operating_income": 1875000},
{"period": "Q1 2025", "revenue": 12800000, "gross_profit": 5120000, "operating_income": 1920000},
{"period": "Q2 2025", "revenue": 13500000, "gross_profit": 5400000, "operating_income": 2025000},
{"period": "Q3 2025", "revenue": 14100000, "gross_profit": 5640000, "operating_income": 2115000},
{"period": "Q4 2025", "revenue": 15700000, "gross_profit": 6280000, "operating_income": 2355000}
],
"drivers": {
"units": {
"base_units": 5000,
"growth_rate": 0.04
},
"pricing": {
"base_price": 2800,
"annual_increase": 0.03
}
},
"assumptions": {
"revenue_growth_rate": 0.08,
"gross_margin": 0.40,
"opex_pct_revenue": 0.25,
"forecast_periods": 12
},
"scenarios": {
"base": {
"growth_adjustment": 0.0,
"margin_adjustment": 0.0
},
"bull": {
"growth_adjustment": 0.04,
"margin_adjustment": 0.03
},
"bear": {
"growth_adjustment": -0.03,
"margin_adjustment": -0.02
}
},
"cash_flow_inputs": {
"opening_cash_balance": 2500000,
"weekly_revenue": 350000,
"collection_rate": 0.85,
"collection_lag_weeks": 2,
"weekly_payroll": 160000,
"weekly_rent": 15000,
"weekly_operating": 45000,
"weekly_other": 20000,
"one_time_items": [
{"week": 3, "amount": -250000, "description": "Annual insurance premium"},
{"week": 6, "amount": 500000, "description": "Customer prepayment"},
{"week": 9, "amount": -180000, "description": "Equipment purchase"},
{"week": 13, "amount": -75000, "description": "Quarterly tax payment"}
]
},
"forecast_periods": 12
}
}
Budget Variance Report
Report Header
| Field | Value |
|---|---|
| Company | [Company Name] |
| Period | [Reporting Period] |
| Prepared By | [Analyst Name] |
| Date | [Report Date] |
| Materiality Threshold | [X]% or $[Y]K |
Executive Summary
[2-3 sentence overview of overall performance vs budget, highlighting whether the company is tracking ahead or behind plan and the primary drivers of variance.]
Key Metrics
| Metric | Actual | Budget | Variance ($) | Variance (%) | Status |
|---|---|---|---|---|---|
| Total Revenue | $[X] | $[X] | $[X] | [X]% | [Fav/Unfav] |
| Total Expenses | $[X] | $[X] | $[X] | [X]% | [Fav/Unfav] |
| Net Income | $[X] | $[X] | $[X] | [X]% | [Fav/Unfav] |
| Operating Margin | [X]% | [X]% | [X]pp | - | [Fav/Unfav] |
Material Variances
[Variance Item 1 - e.g., Product Revenue]
| Actual | Budget | Variance | ||
|---|---|---|---|---|
| Amount | $[X] | $[X] | $[X] | [X]% |
Root Cause: [Detailed explanation of why this variance occurred]
Impact: [Quantified impact on profitability and cash flow]
Corrective Action: [Specific steps being taken to address the variance]
Responsible: [Owner] | Target Date: [Date]
---
[Variance Item 2]
| Actual | Budget | Variance | ||
|---|---|---|---|---|
| Amount | $[X] | $[X] | $[X] | [X]% |
Root Cause: [Explanation]
Impact: [Impact]
Corrective Action: [Action items]
Responsible: [Owner] | Target Date: [Date]
---
Department Performance
| Department | Actual | Budget | Variance ($) | Variance (%) | Favorable | Unfavorable |
|---|---|---|---|---|---|---|
| Sales | $[X] | $[X] | $[X] | [X]% | [N] | [N] |
| Operations | $[X] | $[X] | $[X] | [X]% | [N] | [N] |
| Marketing | $[X] | $[X] | $[X] | [X]% | [N] | [N] |
| Engineering | $[X] | $[X] | $[X] | [X]% | [N] | [N] |
| Finance | $[X] | $[X] | $[X] | [X]% | [N] | [N] |
| HR | $[X] | $[X] | $[X] | [X]% | [N] | [N] |
Category Breakdown
| Category | Actual | Budget | Variance ($) | Variance (%) |
|---|---|---|---|---|
| Revenue | $[X] | $[X] | $[X] | [X]% |
| COGS | $[X] | $[X] | $[X] | [X]% |
| Personnel | $[X] | $[X] | $[X] | [X]% |
| Sales & Marketing | $[X] | $[X] | $[X] | [X]% |
| Technology | $[X] | $[X] | $[X] | [X]% |
| G&A | $[X] | $[X] | $[X] | [X]% |
| R&D | $[X] | $[X] | $[X] | [X]% |
Prior Year Comparison
| Metric | Current Actual | Prior Year | YoY Change ($) | YoY Change (%) |
|---|---|---|---|---|
| Revenue | $[X] | $[X] | $[X] | [X]% |
| Gross Profit | $[X] | $[X] | $[X] | [X]% |
| Operating Income | $[X] | $[X] | $[X] | [X]% |
| Net Income | $[X] | $[X] | $[X] | [X]% |
Risks and Opportunities
Risks
1. [Risk description with quantified impact] 2. [Risk description with quantified impact]
Opportunities
1. [Opportunity description with quantified upside] 2. [Opportunity description with quantified upside]
Forecast Impact
Based on current variances, the full-year forecast is adjusted as follows:
| Metric | Original FY Forecast | Revised FY Forecast | Change |
|---|---|---|---|
| Revenue | $[X] | $[X] | $[X] |
| EBITDA | $[X] | $[X] | $[X] |
| Net Income | $[X] | $[X] | $[X] |
Action Items
| # | Action | Owner | Due Date | Status |
|---|---|---|---|---|
| 1 | [Action description] | [Name] | [Date] | [Open/In Progress/Complete] |
| 2 | [Action description] | [Name] | [Date] | [Open/In Progress/Complete] |
| 3 | [Action description] | [Name] | [Date] | [Open/In Progress/Complete] |
---
Report generated using Financial Analyst Skill - Budget Variance Analyzer
Financial Ratios Guide
Comprehensive reference for financial ratio analysis covering formulas, interpretation, and industry benchmarks across five categories.
1. Profitability Ratios
Measure a company's ability to generate earnings relative to revenue, assets, or equity.
Return on Equity (ROE)
Formula: Net Income / Total Shareholders' Equity
Interpretation:
- Measures how effectively management uses equity to generate profits
- Higher ROE indicates more efficient use of equity capital
- Compare against cost of equity - ROE should exceed it
Benchmarks:
| Rating | Range |
|---|---|
| Below Average | < 8% |
| Acceptable | 8% - 15% |
| Good | 15% - 25% |
| Excellent | > 25% |
Caveats: High leverage can inflate ROE. Use DuPont decomposition (ROE = Margin x Turnover x Leverage) for deeper analysis.
Return on Assets (ROA)
Formula: Net Income / Total Assets
Interpretation:
- Measures how efficiently assets generate profit
- Asset-light businesses naturally have higher ROA
- Compare within industry only
Benchmarks:
| Rating | Range |
|---|---|
| Below Average | < 3% |
| Acceptable | 3% - 6% |
| Good | 6% - 12% |
| Excellent | > 12% |
Gross Margin
Formula: (Revenue - COGS) / Revenue
Interpretation:
- Measures production efficiency and pricing power
- Declining gross margin may signal competitive pressure or cost inflation
- Critical for evaluating business model sustainability
Benchmarks by Industry:
| Industry | Typical Range |
|---|---|
| Software/SaaS | 70% - 85% |
| Financial Services | 50% - 70% |
| Retail | 25% - 45% |
| Manufacturing | 20% - 40% |
| Grocery | 25% - 30% |
Operating Margin
Formula: Operating Income / Revenue
Interpretation:
- Measures operational efficiency after all operating expenses
- Excludes interest and taxes for better operational comparison
- Indicates management effectiveness in controlling costs
Benchmarks:
| Rating | Range |
|---|---|
| Below Average | < 5% |
| Acceptable | 5% - 15% |
| Good | 15% - 25% |
| Excellent | > 25% |
Net Margin
Formula: Net Income / Revenue
Interpretation:
- Bottom-line profitability after all expenses
- Affected by tax strategy, capital structure, and one-time items
- Most comprehensive profitability measure
Benchmarks:
| Rating | Range |
|---|---|
| Below Average | < 3% |
| Acceptable | 3% - 10% |
| Good | 10% - 20% |
| Excellent | > 20% |
2. Liquidity Ratios
Measure a company's ability to meet short-term obligations.
Current Ratio
Formula: Current Assets / Current Liabilities
Interpretation:
- Measures short-term solvency
- Too high may indicate inefficient asset use
- Too low signals potential liquidity risk
Benchmarks:
| Rating | Range |
|---|---|
| Concern | < 1.0 |
| Acceptable | 1.0 - 1.5 |
| Healthy | 1.5 - 3.0 |
| Excessive | > 3.0 |
Quick Ratio (Acid Test)
Formula: (Current Assets - Inventory) / Current Liabilities
Interpretation:
- More conservative than current ratio
- Excludes inventory (least liquid current asset)
- Critical for businesses with slow-moving inventory
Benchmarks:
| Rating | Range |
|---|---|
| Concern | < 0.8 |
| Acceptable | 0.8 - 1.0 |
| Healthy | 1.0 - 2.0 |
| Excessive | > 2.0 |
Cash Ratio
Formula: Cash & Equivalents / Current Liabilities
Interpretation:
- Most conservative liquidity measure
- Indicates ability to pay obligations with cash on hand
- Particularly important during credit crunches
Benchmarks:
| Rating | Range |
|---|---|
| Low | < 0.2 |
| Adequate | 0.2 - 0.5 |
| Strong | 0.5 - 1.0 |
| Excessive | > 1.0 |
3. Leverage Ratios
Measure the extent to which a company uses debt financing.
Debt-to-Equity Ratio
Formula: Total Debt / Total Shareholders' Equity
Interpretation:
- Measures financial leverage and risk
- Higher ratio = more reliance on debt financing
- Industry norms vary significantly (utilities vs tech)
Benchmarks:
| Rating | Range |
|---|---|
| Conservative | < 0.3 |
| Moderate | 0.3 - 0.8 |
| Elevated | 0.8 - 2.0 |
| High Risk | > 2.0 |
Interest Coverage Ratio
Formula: Operating Income (EBIT) / Interest Expense
Interpretation:
- Measures ability to service debt from operating earnings
- Below 1.5x is a red flag for lenders
- Critical for credit analysis
Benchmarks:
| Rating | Range |
|---|---|
| Distressed | < 2.0 |
| Adequate | 2.0 - 5.0 |
| Strong | 5.0 - 10.0 |
| Very Strong | > 10.0 |
Debt Service Coverage Ratio (DSCR)
Formula: Operating Cash Flow / Total Debt Service
Interpretation:
- Cash-based measure of debt servicing capacity
- Includes principal repayments (unlike interest coverage)
- Required by many loan covenants
Benchmarks:
| Rating | Range |
|---|---|
| Default Risk | < 1.0 |
| Minimum | 1.0 - 1.5 |
| Comfortable | 1.5 - 2.5 |
| Strong | > 2.5 |
4. Efficiency Ratios
Measure how effectively a company uses its assets and manages operations.
Asset Turnover
Formula: Revenue / Total Assets
Interpretation:
- Measures revenue generated per dollar of assets
- Higher indicates more efficient asset utilization
- Inversely related to profit margins (DuPont)
Benchmarks:
| Industry | Typical Range |
|---|---|
| Retail | 2.0 - 3.0 |
| Manufacturing | 0.8 - 1.5 |
| Utilities | 0.3 - 0.5 |
| Technology | 0.5 - 1.0 |
Inventory Turnover
Formula: COGS / Average Inventory
Interpretation:
- Measures how quickly inventory is sold
- Low turnover suggests overstock or obsolescence risk
- High turnover may indicate strong sales or thin inventory
Benchmarks:
| Rating | Range |
|---|---|
| Slow | < 4x |
| Average | 4x - 8x |
| Efficient | 8x - 12x |
| Very Efficient | > 12x |
Receivables Turnover
Formula: Revenue / Accounts Receivable
Interpretation:
- Measures efficiency of credit and collections
- Higher turnover means faster collections
- Monitor trends for credit policy changes
Benchmarks:
| Rating | Range |
|---|---|
| Slow | < 6x |
| Average | 6x - 10x |
| Efficient | 10x - 15x |
| Very Efficient | > 15x |
Days Sales Outstanding (DSO)
Formula: 365 / Receivables Turnover
Interpretation:
- Average days to collect payment after a sale
- Lower DSO = faster cash conversion
- Compare against payment terms
Benchmarks:
| Rating | Range |
|---|---|
| Excellent | < 30 days |
| Good | 30 - 45 days |
| Acceptable | 45 - 60 days |
| Concern | > 60 days |
5. Valuation Ratios
Measure a company's market value relative to financial metrics.
Price-to-Earnings (P/E) Ratio
Formula: Share Price / Earnings Per Share
Interpretation:
- Most widely used valuation metric
- High P/E suggests growth expectations or overvaluation
- Use trailing (TTM) and forward P/E for comparison
Benchmarks:
| Rating | Range |
|---|---|
| Value | < 10x |
| Fair | 10x - 20x |
| Growth | 20x - 35x |
| Premium | > 35x |
Price-to-Book (P/B) Ratio
Formula: Share Price / Book Value Per Share
Interpretation:
- Compares market value to accounting value
- Below 1.0 may indicate undervaluation or distress
- Most useful for asset-heavy industries
Benchmarks:
| Rating | Range |
|---|---|
| Undervalued | < 1.0 |
| Fair | 1.0 - 2.5 |
| Premium | 2.5 - 5.0 |
| Rich | > 5.0 |
Price-to-Sales (P/S) Ratio
Formula: Market Cap / Revenue
Interpretation:
- Useful for companies without positive earnings
- Compare within industry only
- Lower = potentially better value
Benchmarks:
| Rating | Range |
|---|---|
| Value | < 1.0 |
| Fair | 1.0 - 3.0 |
| Growth | 3.0 - 8.0 |
| Premium | > 8.0 |
EV/EBITDA
Formula: Enterprise Value / EBITDA
Interpretation:
- Capital-structure-neutral valuation metric
- Preferred for M&A analysis and leveraged buyouts
- More comparable across capital structures than P/E
Benchmarks:
| Rating | Range |
|---|---|
| Value | < 6x |
| Fair | 6x - 12x |
| Growth | 12x - 20x |
| Premium | > 20x |
PEG Ratio
Formula: P/E Ratio / Earnings Growth Rate (%)
Interpretation:
- Growth-adjusted P/E ratio
- PEG of 1.0 suggests fair valuation relative to growth
- Below 1.0 may indicate undervaluation
Benchmarks:
| Rating | Range |
|---|---|
| Undervalued | < 0.5 |
| Fair | 0.5 - 1.0 |
| Fully Valued | 1.0 - 2.0 |
| Overvalued | > 2.0 |
Ratio Analysis Best Practices
1. Compare within industry - Ratios vary significantly across sectors 2. Analyze trends - A single period snapshot is insufficient; look at 3-5 year trends 3. Use multiple ratios - No single ratio tells the complete story 4. Consider context - Accounting policies, business cycle, and company stage matter 5. DuPont decomposition - Break ROE into margin, turnover, and leverage components 6. Peer comparison - Compare against direct competitors, not just broad benchmarks 7. Watch for manipulation - Revenue recognition changes, off-balance-sheet items, and one-time adjustments can distort ratios
Forecasting Best Practices
Comprehensive reference for financial forecasting including driver-based models, rolling forecasts, accuracy improvement techniques, and scenario planning.
1. Driver-Based Forecasting
Overview
Driver-based forecasting models financial outcomes based on key business drivers rather than extrapolating from historical trends alone. This approach creates more transparent, actionable, and accurate forecasts.
Identifying Key Drivers
Revenue Drivers:
| Business Model | Primary Drivers |
|---|---|
| SaaS/Subscription | Customers x ARPU x Retention Rate |
| E-commerce | Visitors x Conversion Rate x AOV |
| Manufacturing | Units x Price per Unit |
| Professional Services | Headcount x Utilization x Bill Rate |
| Retail | Stores x Revenue per Store (or sqft) |
| Marketplace | GMV x Take Rate |
Cost Drivers:
| Category | Common Drivers |
|---|---|
| COGS | Revenue x (1 - Gross Margin) or Units x Unit Cost |
| Headcount Costs | Employees x Average Compensation x (1 + Benefits Rate) |
| Sales & Marketing | Revenue x S&M % or CAC x New Customers |
| R&D | Engineering Headcount x Avg Salary |
| G&A | Headcount-based + fixed costs |
| CapEx | Revenue x CapEx Intensity or Project-based |
Building a Driver-Based Model
Step 1: Map the value chain
- Revenue = f(volume drivers, pricing drivers, mix drivers)
- Costs = f(variable drivers, fixed components, step functions)
Step 2: Establish driver relationships
- Linear: Revenue = Units x Price
- Non-linear: Revenue = Base x (1 + Growth Rate)^t
- Step function: Facilities costs that jump at capacity thresholds
Step 3: Validate driver assumptions
- Compare driver values to historical actuals
- Benchmark against industry data
- Stress-test extreme values
Step 4: Build sensitivity
- Identify which drivers have the largest impact on output
- Quantify the range of reasonable values for each driver
- Create scenario combinations
Driver Sensitivity Matrix
Rank drivers by impact and uncertainty:
| High Impact | Low Impact | |
|---|---|---|
| High Uncertainty | Model these carefully, run scenarios | Monitor but don't over-model |
| Low Uncertainty | Get these right; high accuracy needed | Use simple assumptions |
2. Rolling Forecasts
What Is a Rolling Forecast?
A rolling forecast continuously extends the forecast horizon as each period closes. Unlike a static annual budget, a rolling forecast always looks forward the same number of periods (typically 12-18 months).
Rolling Forecast vs Annual Budget
| Feature | Annual Budget | Rolling Forecast |
|---|---|---|
| Time Horizon | Fixed (Jan-Dec) | Rolling (12-18 months) |
| Update Frequency | Once per year | Monthly or quarterly |
| Detail Level | Very detailed | Driver-level |
| Preparation Time | 3-6 months | 2-5 days per cycle |
| Relevance | Declines over time | Stays current |
| Flexibility | Rigid | Adaptive |
Implementation Steps
1. Select the horizon - 12 months rolling is most common (some use 18 months for CapEx planning) 2. Define update cadence - Monthly for volatile businesses; quarterly for stable ones 3. Choose the right detail - Driver-level, not line-item detail 4. Automate data feeds - Reduce manual effort per cycle 5. Separate actuals from forecast - Clear delineation between reported and projected periods 6. Track forecast accuracy - Measure MAPE (Mean Absolute Percentage Error) over time
13-Week Cash Flow Forecast
A specialized rolling forecast for liquidity management:
Structure:
- Week-by-week cash inflows and outflows
- Opening and closing cash balances
- Minimum cash threshold alerts
Key Components:
| Inflows | Outflows |
|---|---|
| Customer collections (by aging) | Payroll (fixed cadence) |
| Other receivables | Rent / Lease payments |
| Asset sales | Vendor payments (by terms) |
| Financing proceeds | Debt service |
| Tax refunds | Tax payments |
| Other income | Capital expenditures |
Collection Modeling:
- Apply collection rates by customer segment or aging bucket
- Model DSO trends to project collection timing
- Account for seasonal patterns in payment behavior
3. Accuracy Improvement
Measuring Forecast Accuracy
Mean Absolute Percentage Error (MAPE):
MAPE = (1/n) x Sum of |Actual - Forecast| / |Actual| x 100%Accuracy Benchmarks:
| MAPE | Rating |
|---|---|
| < 5% | Excellent |
| 5% - 10% | Good |
| 10% - 20% | Acceptable |
| > 20% | Needs improvement |
Weighted MAPE (WMAPE): Use when line items vary significantly in magnitude - weights errors by actual values.
Techniques to Improve Accuracy
1. Bias Detection and Correction
- Track directional bias (consistently over or under forecasting)
- Calculate mean signed error to detect systematic bias
- Adjust driver assumptions to correct persistent bias
2. Variance Analysis Loop
- After each period closes, compare actual vs forecast
- Identify root causes of significant variances
- Update driver assumptions based on learnings
- Document what changed and why
3. Ensemble Approach
- Combine multiple forecasting methods
- Blend statistical (trend) with judgmental (management input)
- Weight methods by their historical accuracy
4. Granularity Optimization
- Forecast at the right level of detail - not too aggregated, not too granular
- Product/segment level usually more accurate than single top-line
- Aggregate bottom-up forecasts for total, then adjust
5. Leading Indicators
- Identify metrics that predict financial outcomes 1-3 months ahead
- Pipeline/bookings predict revenue
- Hiring plans predict headcount costs
- Customer churn signals predict retention revenue
Common Accuracy Killers
1. Anchoring bias - Over-relying on last year's numbers 2. Optimism bias - Systematic overestimation of growth 3. Lack of accountability - No one tracks forecast vs actual 4. Stale assumptions - Not updating for market changes 5. Missing data - Forecasting without key driver inputs 6. Over-precision - False precision in uncertain environments
4. Scenario Planning
Three-Scenario Framework
| Scenario | Description | Probability |
|---|---|---|
| Base Case | Most likely outcome based on current trajectory | 50-60% |
| Bull Case | Favorable conditions, upside realization | 15-25% |
| Bear Case | Adverse conditions, downside risks | 15-25% |
Scenario Construction
Base Case:
- Continuation of current trends
- Management's operational plan
- Market consensus assumptions
- Normal competitive dynamics
Bull Case (apply selectively, not uniformly):
- Faster customer acquisition or market adoption
- Successful product launch or expansion
- Favorable macro conditions
- Competitor weakness or exit
- Margin expansion from operating leverage
Bear Case (be realistic, not catastrophic):
- Slower growth or market contraction
- Increased competition or pricing pressure
- Key customer or contract loss
- Supply chain disruption
- Regulatory headwinds
Scenario Variables
Map each scenario to specific driver values:
| Driver | Bear | Base | Bull |
|---|---|---|---|
| Revenue Growth | +2% | +8% | +15% |
| Gross Margin | 35% | 40% | 43% |
| Customer Churn | 8% | 5% | 3% |
| New Customers/Month | 50 | 100 | 180 |
| Price Increase | 0% | 3% | 5% |
Presenting Scenarios
1. Show the range - Management needs to see the potential outcomes 2. Quantify the gap - Dollar impact of bull vs bear on key metrics 3. Identify triggers - What conditions would cause each scenario 4. Define actions - What levers to pull in each scenario 5. Assign probabilities - Not all scenarios are equally likely
5. Forecast Communication
Stakeholder Needs
| Audience | Needs |
|---|---|
| Board | High-level scenarios, key risks, strategic implications |
| CEO/CFO | Detailed drivers, variance explanations, action items |
| Department Heads | Their specific budget vs forecast, headcount plans |
| Investors | Revenue guidance, margin trajectory, capital allocation |
| Operations | Weekly/monthly targets, resource requirements |
Presentation Framework
1. Executive summary - Key metrics, direction of travel, confidence level 2. Variance bridge - Walk from budget/prior forecast to current forecast 3. Driver analysis - What changed and why 4. Scenario comparison - Range of outcomes 5. Key risks and opportunities - What could change the forecast 6. Action items - Decisions needed based on forecast
Forecast Cadence
| Activity | Frequency | Time Required |
|---|---|---|
| 13-week cash flow update | Weekly | 1-2 hours |
| Rolling forecast update | Monthly | 1-2 days |
| Full reforecast | Quarterly | 3-5 days |
| Annual budget/plan | Annually | 4-8 weeks |
| Board reporting | Quarterly | 2-3 days |
6. Industry-Specific Considerations
SaaS Metrics in Forecasting
- MRR/ARR decomposition: New, expansion, contraction, churn
- Cohort-based forecasting: Forecast by customer cohort for retention accuracy
- Rule of 40: Revenue growth % + Profit margin % should exceed 40%
- Net Revenue Retention: Target > 110% for healthy SaaS
- CAC Payback: Should be < 18 months
Retail Forecasting
- Same-store sales growth as primary organic growth metric
- Seasonal decomposition for accurate monthly/weekly forecasts
- Markdown optimization impact on gross margin
- Inventory turns drive working capital forecasts
Manufacturing Forecasting
- Order backlog as a leading indicator
- Capacity constraints creating step-function cost increases
- Raw material price forecasts for COGS
- Maintenance CapEx vs growth CapEx distinction
- Utilization rates driving unit cost projections
Valuation Methodology Guide
Comprehensive reference for business valuation approaches including DCF analysis, comparable company analysis, and precedent transactions.
1. Discounted Cash Flow (DCF) Methodology
Overview
DCF is an intrinsic valuation method that estimates the present value of a company's expected future free cash flows, discounted at an appropriate rate reflecting the risk of those cash flows.
Core Principle: The value of a business equals the present value of all future cash flows it will generate.
Formula:
Enterprise Value = Sum of [FCF_t / (1 + WACC)^t] + Terminal Value / (1 + WACC)^nWhere:
- FCF_t = Free Cash Flow in year t
- WACC = Weighted Average Cost of Capital
- n = number of projection years
Step 1: Historical Analysis
Before projecting, analyze 3-5 years of historical financials:
- Revenue growth rates - Identify organic vs acquisition-driven growth
- Margin trends - Gross, operating, and net margin trajectories
- Capital intensity - CapEx as % of revenue
- Working capital - Cash conversion cycle trends
- Free cash flow conversion - FCF / Net Income ratio
Step 2: Revenue Projections
Approaches: 1. Top-down: Market size x Market share x Pricing 2. Bottom-up: Units x Price, or Customers x ARPU 3. Growth rate extrapolation: Historical growth with decay
Revenue Projection Best Practices:
- Use 5-7 year explicit projection period
- Growth should converge toward GDP growth by terminal year
- Support assumptions with market data and management guidance
- Model revenue by segment/product line when possible
Step 3: Free Cash Flow Calculation
Unlevered Free Cash Flow (UFCF):
UFCF = EBIT x (1 - Tax Rate)
+ Depreciation & Amortization
- Capital Expenditures
- Changes in Net Working CapitalKey Drivers:
- Operating margin trajectory
- CapEx as % of revenue (maintenance vs growth)
- Working capital requirements (DSO, DIO, DPO)
- Tax rate (effective vs marginal)
Step 4: WACC Calculation
Weighted Average Cost of Capital:
WACC = (E/V x Re) + (D/V x Rd x (1 - T))Where:
- E/V = Equity weight (market value)
- D/V = Debt weight (market value)
- Re = Cost of equity
- Rd = Cost of debt (pre-tax)
- T = Marginal tax rate
Cost of Equity (CAPM)
Re = Rf + Beta x (Rm - Rf) + Size Premium + Company-Specific Risk| Component | Description | Typical Range |
|---|---|---|
| Risk-Free Rate (Rf) | 10-year Treasury yield | 3.5% - 5.0% |
| Equity Risk Premium (ERP) | Market return above risk-free | 5.0% - 7.0% |
| Beta | Systematic risk relative to market | 0.5 - 2.0 |
| Size Premium | Small-cap additional risk | 0% - 5% |
| Company-Specific Risk | Unique risk factors | 0% - 5% |
Beta Estimation:
- Use 2-5 year weekly returns against broad market index
- Unlevered betas for comparability, then re-lever to target capital structure
- Consider industry median beta for stability
Cost of Debt
Rd = Yield on comparable-maturity corporate bonds
OR
Rd = Risk-Free Rate + Credit SpreadCredit Spread by Rating:
| Rating | Typical Spread |
|---|---|
| AAA | 0.5% - 1.0% |
| AA | 1.0% - 1.5% |
| A | 1.5% - 2.0% |
| BBB | 2.0% - 3.0% |
| BB | 3.0% - 5.0% |
| B | 5.0% - 8.0% |
Step 5: Terminal Value
Terminal value typically represents 60-80% of total enterprise value. Use two methods and cross-check.
Perpetuity Growth Method
TV = FCF_n x (1 + g) / (WACC - g)Where g = terminal growth rate (typically 2.0% - 3.0%, should not exceed long-term GDP growth)
Sensitivity: Terminal value is highly sensitive to g. A 0.5% change in g can move enterprise value by 15-25%.
Exit Multiple Method
TV = Terminal Year EBITDA x Exit EV/EBITDA MultipleExit Multiple Selection:
- Use current trading multiples of comparable companies
- Consider whether current multiples are at historical highs/lows
- Apply a discount for lack of marketability if private
Cross-Check: Both methods should yield similar results. Large discrepancies signal inconsistent assumptions.
Step 6: Enterprise to Equity Bridge
Enterprise Value
- Net Debt (Total Debt - Cash)
- Minority Interest
- Preferred Equity
+ Equity Method Investments
= Equity Value
Equity Value / Diluted Shares Outstanding = Value Per ShareStep 7: Sensitivity Analysis
Always present results as a range, not a single point estimate.
Standard Sensitivity Tables: 1. WACC vs Terminal Growth Rate 2. WACC vs Exit Multiple 3. Revenue Growth vs Operating Margin
Scenario Analysis:
- Base case: Management guidance / consensus estimates
- Bull case: Upside scenario with faster growth or margin expansion
- Bear case: Downside scenario with slower growth or margin compression
2. Comparable Company Analysis
Methodology
1. Select peer group - Similar size, industry, growth profile, and margins 2. Calculate trading multiples for each peer 3. Determine appropriate multiple range 4. Apply to target company's metrics
Common Multiples
| Multiple | When to Use |
|---|---|
| EV/Revenue | Pre-profit companies, high-growth tech |
| EV/EBITDA | Most common for mature companies |
| EV/EBIT | When D&A differs significantly across peers |
| P/E | Stable earnings, financial services |
| P/B | Banks, insurance, asset-heavy industries |
| EV/FCF | Capital-light businesses with clean FCF |
Peer Selection Criteria
- Industry: Same or closely adjacent sectors
- Size: Within 0.5x to 2x of target revenue/market cap
- Geography: Same primary markets
- Growth profile: Similar revenue growth rates (within 5-10%)
- Margin profile: Similar operating margin structure
- Business model: Comparable revenue mix and customer base
Premium/Discount Adjustments
| Factor | Adjustment |
|---|---|
| Higher growth | Premium of 1-3x on EV/EBITDA |
| Lower margins | Discount of 1-2x |
| Smaller scale | Discount of 10-20% |
| Private company | Discount of 15-30% (illiquidity) |
| Control premium | Premium of 20-40% (for acquisitions) |
3. Precedent Transaction Analysis
Methodology
1. Identify comparable transactions in same industry 2. Calculate transaction multiples (EV/Revenue, EV/EBITDA) 3. Adjust for market conditions and deal-specific factors 4. Apply adjusted multiples to target
Key Considerations
- Transactions include control premiums (typically 20-40%)
- Market conditions at time of deal affect multiples
- Strategic vs financial buyer valuations differ
- Consider synergy expectations embedded in price
- More recent transactions carry greater relevance
4. Valuation Framework Selection
| Situation | Primary Method | Secondary Method |
|---|---|---|
| Profitable, stable | DCF | Comparable companies |
| High growth, pre-profit | Comparable companies (EV/Revenue) | DCF with scenario analysis |
| M&A target | Precedent transactions | DCF |
| Asset-heavy, cyclical | Asset-based valuation | Normalized DCF |
| Financial institution | Dividend discount model | P/B, P/E comps |
| Distressed | Liquidation value | Restructured DCF |
5. Common Pitfalls
1. Hockey stick projections - Unrealistic growth acceleration in later years 2. Terminal value dominance - If TV > 80% of EV, shorten projection period or question assumptions 3. Circular references - WACC depends on equity value which depends on WACC 4. Ignoring working capital - Can significantly affect FCF 5. Single-point estimates - Always present as a range 6. Stale comparables - Market conditions change; update regularly 7. Confirmation bias - Don't work backward from a desired conclusion 8. Ignoring dilution - Use fully diluted shares (treasury stock method for options)
#!/usr/bin/env python3
"""
Budget Variance Analyzer
Analyzes actual vs budget vs prior year performance with materiality
threshold filtering, favorable/unfavorable classification, and
department/category breakdown.
Usage:
python budget_variance_analyzer.py budget_data.json
python budget_variance_analyzer.py budget_data.json --format json
python budget_variance_analyzer.py budget_data.json --threshold-pct 5 --threshold-amt 25000
"""
import argparse
import json
import sys
from typing import Any, Dict, List, Optional, Tuple
def safe_divide(numerator: float, denominator: float, default: float = 0.0) -> float:
"""Safely divide two numbers, returning default if denominator is zero."""
if denominator == 0 or denominator is None:
return default
return numerator / denominator
class BudgetVarianceAnalyzer:
"""Analyze budget variances with materiality filtering and classification."""
def __init__(
self,
data: Dict[str, Any],
threshold_pct: float = 10.0,
threshold_amt: float = 50000.0,
) -> None:
"""
Initialize the analyzer.
Args:
data: Budget data with line items
threshold_pct: Materiality threshold as percentage (default 10%)
threshold_amt: Materiality threshold as dollar amount (default $50K)
"""
self.line_items: List[Dict[str, Any]] = data.get("line_items", [])
self.period: str = data.get("period", "Current Period")
self.company: str = data.get("company", "Company")
self.threshold_pct = threshold_pct
self.threshold_amt = threshold_amt
self.variances: List[Dict[str, Any]] = []
self.material_variances: List[Dict[str, Any]] = []
self.summary: Dict[str, Any] = {}
def classify_favorability(
self, line_type: str, variance_amount: float
) -> str:
"""
Classify variance as favorable or unfavorable.
Revenue: over budget = favorable
Expense: under budget = favorable
"""
if line_type.lower() in ("revenue", "income", "sales"):
return "Favorable" if variance_amount > 0 else "Unfavorable"
else:
# For expenses, under budget (negative variance) is favorable
return "Favorable" if variance_amount < 0 else "Unfavorable"
def calculate_variances(self) -> List[Dict[str, Any]]:
"""Calculate variances for all line items."""
self.variances = []
for item in self.line_items:
name = item.get("name", "Unknown")
line_type = item.get("type", "expense")
department = item.get("department", "General")
category = item.get("category", "Other")
actual = item.get("actual", 0)
budget = item.get("budget", 0)
prior_year = item.get("prior_year", None)
# Budget variance
budget_var_amt = actual - budget
budget_var_pct = safe_divide(budget_var_amt, budget) * 100
# Prior year variance (if available)
py_var_amt = (actual - prior_year) if prior_year is not None else None
py_var_pct = (
safe_divide(py_var_amt, prior_year) * 100
if prior_year is not None
else None
)
favorability = self.classify_favorability(line_type, budget_var_amt)
is_material = (
abs(budget_var_pct) >= self.threshold_pct
or abs(budget_var_amt) >= self.threshold_amt
)
variance_record = {
"name": name,
"type": line_type,
"department": department,
"category": category,
"actual": actual,
"budget": budget,
"prior_year": prior_year,
"budget_variance_amount": budget_var_amt,
"budget_variance_pct": round(budget_var_pct, 2),
"prior_year_variance_amount": py_var_amt,
"prior_year_variance_pct": (
round(py_var_pct, 2) if py_var_pct is not None else None
),
"favorability": favorability,
"is_material": is_material,
}
self.variances.append(variance_record)
# Filter material variances
self.material_variances = [v for v in self.variances if v["is_material"]]
return self.variances
def department_summary(self) -> Dict[str, Dict[str, Any]]:
"""Summarize variances by department."""
departments: Dict[str, Dict[str, float]] = {}
for v in self.variances:
dept = v["department"]
if dept not in departments:
departments[dept] = {
"total_actual": 0.0,
"total_budget": 0.0,
"total_variance": 0.0,
"favorable_count": 0,
"unfavorable_count": 0,
"line_count": 0,
}
departments[dept]["total_actual"] += v["actual"]
departments[dept]["total_budget"] += v["budget"]
departments[dept]["total_variance"] += v["budget_variance_amount"]
departments[dept]["line_count"] += 1
if v["favorability"] == "Favorable":
departments[dept]["favorable_count"] += 1
else:
departments[dept]["unfavorable_count"] += 1
# Add variance percentage
for dept_data in departments.values():
dept_data["variance_pct"] = round(
safe_divide(
dept_data["total_variance"], dept_data["total_budget"]
)
* 100,
2,
)
return departments
def category_summary(self) -> Dict[str, Dict[str, Any]]:
"""Summarize variances by category."""
categories: Dict[str, Dict[str, float]] = {}
for v in self.variances:
cat = v["category"]
if cat not in categories:
categories[cat] = {
"total_actual": 0.0,
"total_budget": 0.0,
"total_variance": 0.0,
"line_count": 0,
}
categories[cat]["total_actual"] += v["actual"]
categories[cat]["total_budget"] += v["budget"]
categories[cat]["total_variance"] += v["budget_variance_amount"]
categories[cat]["line_count"] += 1
for cat_data in categories.values():
cat_data["variance_pct"] = round(
safe_divide(
cat_data["total_variance"], cat_data["total_budget"]
)
* 100,
2,
)
return categories
def generate_executive_summary(self) -> Dict[str, Any]:
"""Generate an executive summary of the variance analysis."""
total_actual = sum(
v["actual"] for v in self.variances if v["type"].lower() in ("revenue", "income", "sales")
)
total_budget = sum(
v["budget"] for v in self.variances if v["type"].lower() in ("revenue", "income", "sales")
)
total_expense_actual = sum(
v["actual"] for v in self.variances if v["type"].lower() not in ("revenue", "income", "sales")
)
total_expense_budget = sum(
v["budget"] for v in self.variances if v["type"].lower() not in ("revenue", "income", "sales")
)
revenue_variance = total_actual - total_budget
expense_variance = total_expense_actual - total_expense_budget
favorable_count = sum(
1 for v in self.variances if v["favorability"] == "Favorable"
)
unfavorable_count = sum(
1 for v in self.variances if v["favorability"] == "Unfavorable"
)
self.summary = {
"period": self.period,
"company": self.company,
"total_line_items": len(self.variances),
"material_variances_count": len(self.material_variances),
"favorable_count": favorable_count,
"unfavorable_count": unfavorable_count,
"revenue": {
"actual": total_actual,
"budget": total_budget,
"variance_amount": revenue_variance,
"variance_pct": round(
safe_divide(revenue_variance, total_budget) * 100, 2
),
},
"expenses": {
"actual": total_expense_actual,
"budget": total_expense_budget,
"variance_amount": expense_variance,
"variance_pct": round(
safe_divide(expense_variance, total_expense_budget) * 100, 2
),
},
"net_impact": revenue_variance - expense_variance,
"materiality_thresholds": {
"percentage": self.threshold_pct,
"amount": self.threshold_amt,
},
}
return self.summary
def run_analysis(self) -> Dict[str, Any]:
"""Run the complete variance analysis."""
self.calculate_variances()
dept_summary = self.department_summary()
cat_summary = self.category_summary()
exec_summary = self.generate_executive_summary()
return {
"executive_summary": exec_summary,
"all_variances": self.variances,
"material_variances": self.material_variances,
"department_summary": dept_summary,
"category_summary": cat_summary,
}
def format_text(self, results: Dict[str, Any]) -> str:
"""Format results as human-readable text."""
lines: List[str] = []
lines.append("=" * 70)
lines.append("BUDGET VARIANCE ANALYSIS")
lines.append("=" * 70)
summary = results["executive_summary"]
lines.append(f"\n Company: {summary['company']}")
lines.append(f" Period: {summary['period']}")
def fmt_money(val: float) -> str:
sign = "+" if val > 0 else ""
if abs(val) >= 1e6:
return f"{sign}${val / 1e6:,.2f}M"
if abs(val) >= 1e3:
return f"{sign}${val / 1e3:,.1f}K"
return f"{sign}${val:,.2f}"
lines.append(f"\n--- EXECUTIVE SUMMARY ---")
rev = summary["revenue"]
exp = summary["expenses"]
lines.append(
f" Revenue: Actual {fmt_money(rev['actual'])} vs "
f"Budget {fmt_money(rev['budget'])} "
f"({fmt_money(rev['variance_amount'])}, {rev['variance_pct']:+.1f}%)"
)
lines.append(
f" Expenses: Actual {fmt_money(exp['actual'])} vs "
f"Budget {fmt_money(exp['budget'])} "
f"({fmt_money(exp['variance_amount'])}, {exp['variance_pct']:+.1f}%)"
)
lines.append(f" Net Impact: {fmt_money(summary['net_impact'])}")
lines.append(
f" Total Items: {summary['total_line_items']} | "
f"Material: {summary['material_variances_count']} | "
f"Favorable: {summary['favorable_count']} | "
f"Unfavorable: {summary['unfavorable_count']}"
)
# Material variances
material = results["material_variances"]
if material:
lines.append(f"\n--- MATERIAL VARIANCES ---")
lines.append(
f" (Threshold: {self.threshold_pct}% or "
f"${self.threshold_amt:,.0f})"
)
for v in material:
lines.append(
f"\n {v['name']} ({v['department']})"
)
lines.append(
f" Actual: {fmt_money(v['actual'])} | "
f"Budget: {fmt_money(v['budget'])}"
)
lines.append(
f" Variance: {fmt_money(v['budget_variance_amount'])} "
f"({v['budget_variance_pct']:+.1f}%) - {v['favorability']}"
)
# Department summary
dept = results["department_summary"]
if dept:
lines.append(f"\n--- DEPARTMENT SUMMARY ---")
for dept_name, d in dept.items():
lines.append(
f" {dept_name}: Variance {fmt_money(d['total_variance'])} "
f"({d['variance_pct']:+.1f}%) | "
f"Fav: {d['favorable_count']} / Unfav: {d['unfavorable_count']}"
)
# Category summary
cat = results["category_summary"]
if cat:
lines.append(f"\n--- CATEGORY SUMMARY ---")
for cat_name, c in cat.items():
lines.append(
f" {cat_name}: Variance {fmt_money(c['total_variance'])} "
f"({c['variance_pct']:+.1f}%)"
)
lines.append("\n" + "=" * 70)
return "\n".join(lines)
def main() -> None:
"""Main entry point."""
parser = argparse.ArgumentParser(
description="Analyze budget variances with materiality filtering"
)
parser.add_argument(
"input_file",
help="Path to JSON file with budget data",
)
parser.add_argument(
"--format",
choices=["text", "json"],
default="text",
help="Output format (default: text)",
)
parser.add_argument(
"--threshold-pct",
type=float,
default=10.0,
help="Materiality threshold percentage (default: 10)",
)
parser.add_argument(
"--threshold-amt",
type=float,
default=50000.0,
help="Materiality threshold dollar amount (default: 50000)",
)
args = parser.parse_args()
try:
with open(args.input_file, "r") as f:
data = json.load(f)
except FileNotFoundError:
print(f"Error: File '{args.input_file}' not found.", file=sys.stderr)
sys.exit(1)
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON in '{args.input_file}': {e}", file=sys.stderr)
sys.exit(1)
analyzer = BudgetVarianceAnalyzer(
data,
threshold_pct=args.threshold_pct,
threshold_amt=args.threshold_amt,
)
results = analyzer.run_analysis()
if args.format == "json":
print(json.dumps(results, indent=2))
else:
print(analyzer.format_text(results))
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
DCF Valuation Model
Discounted Cash Flow enterprise and equity valuation with WACC calculation,
terminal value estimation, and two-way sensitivity analysis.
Uses standard library only (math, statistics) - NO numpy/pandas/scipy.
Usage:
python dcf_valuation.py valuation_data.json
python dcf_valuation.py valuation_data.json --format json
python dcf_valuation.py valuation_data.json --projection-years 7
"""
import argparse
import json
import math
import sys
from statistics import mean
from typing import Any, Dict, List, Optional, Tuple
def safe_divide(numerator: float, denominator: float, default: float = 0.0) -> float:
"""Safely divide two numbers, returning default if denominator is zero."""
if denominator == 0 or denominator is None:
return default
return numerator / denominator
class DCFModel:
"""Discounted Cash Flow valuation model."""
def __init__(self) -> None:
"""Initialize the DCF model."""
self.historical: Dict[str, Any] = {}
self.assumptions: Dict[str, Any] = {}
self.wacc: float = 0.0
self.projected_revenue: List[float] = []
self.projected_fcf: List[float] = []
self.projection_years: int = 5
self.terminal_value_perpetuity: float = 0.0
self.terminal_value_exit_multiple: float = 0.0
self.enterprise_value_perpetuity: float = 0.0
self.enterprise_value_exit_multiple: float = 0.0
self.equity_value_perpetuity: float = 0.0
self.equity_value_exit_multiple: float = 0.0
self.value_per_share_perpetuity: float = 0.0
self.value_per_share_exit_multiple: float = 0.0
def set_historical_financials(self, historical: Dict[str, Any]) -> None:
"""Set historical financial data."""
self.historical = historical
def set_assumptions(self, assumptions: Dict[str, Any]) -> None:
"""Set projection assumptions."""
self.assumptions = assumptions
self.projection_years = assumptions.get("projection_years", 5)
def calculate_wacc(self) -> float:
"""Calculate Weighted Average Cost of Capital via CAPM."""
wacc_inputs = self.assumptions.get("wacc_inputs", {})
risk_free_rate = wacc_inputs.get("risk_free_rate", 0.04)
equity_risk_premium = wacc_inputs.get("equity_risk_premium", 0.06)
beta = wacc_inputs.get("beta", 1.0)
cost_of_debt = wacc_inputs.get("cost_of_debt", 0.05)
tax_rate = wacc_inputs.get("tax_rate", 0.25)
debt_weight = wacc_inputs.get("debt_weight", 0.30)
equity_weight = wacc_inputs.get("equity_weight", 0.70)
# CAPM: Cost of Equity = Risk-Free Rate + Beta * Equity Risk Premium
cost_of_equity = risk_free_rate + beta * equity_risk_premium
# WACC = (E/V * Re) + (D/V * Rd * (1 - T))
after_tax_cost_of_debt = cost_of_debt * (1 - tax_rate)
self.wacc = (equity_weight * cost_of_equity) + (
debt_weight * after_tax_cost_of_debt
)
return self.wacc
def project_cash_flows(self) -> Tuple[List[float], List[float]]:
"""Project revenue and free cash flow over the projection period."""
base_revenue = self.historical.get("revenue", [])
if not base_revenue:
raise ValueError("Historical revenue data is required")
last_revenue = base_revenue[-1]
revenue_growth_rates = self.assumptions.get("revenue_growth_rates", [])
fcf_margins = self.assumptions.get("fcf_margins", [])
# If growth rates not provided for all years, use average or default
default_growth = self.assumptions.get("default_revenue_growth", 0.05)
default_fcf_margin = self.assumptions.get("default_fcf_margin", 0.10)
self.projected_revenue = []
self.projected_fcf = []
current_revenue = last_revenue
for year in range(self.projection_years):
growth = (
revenue_growth_rates[year]
if year < len(revenue_growth_rates)
else default_growth
)
fcf_margin = (
fcf_margins[year]
if year < len(fcf_margins)
else default_fcf_margin
)
current_revenue = current_revenue * (1 + growth)
fcf = current_revenue * fcf_margin
self.projected_revenue.append(current_revenue)
self.projected_fcf.append(fcf)
return self.projected_revenue, self.projected_fcf
def calculate_terminal_value(self) -> Tuple[float, float]:
"""Calculate terminal value using both perpetuity growth and exit multiple."""
if not self.projected_fcf:
raise ValueError("Must project cash flows before terminal value")
terminal_fcf = self.projected_fcf[-1]
terminal_growth = self.assumptions.get("terminal_growth_rate", 0.025)
exit_multiple = self.assumptions.get("exit_ev_ebitda_multiple", 12.0)
# Perpetuity growth method: TV = FCF * (1+g) / (WACC - g)
if self.wacc > terminal_growth:
self.terminal_value_perpetuity = (
terminal_fcf * (1 + terminal_growth)
) / (self.wacc - terminal_growth)
else:
self.terminal_value_perpetuity = 0.0
# Exit multiple method: TV = Terminal EBITDA * Exit Multiple
terminal_revenue = self.projected_revenue[-1]
ebitda_margin = self.assumptions.get("terminal_ebitda_margin", 0.20)
terminal_ebitda = terminal_revenue * ebitda_margin
self.terminal_value_exit_multiple = terminal_ebitda * exit_multiple
return self.terminal_value_perpetuity, self.terminal_value_exit_multiple
def calculate_enterprise_value(self) -> Tuple[float, float]:
"""Calculate enterprise value by discounting projected FCFs and terminal value."""
if not self.projected_fcf:
raise ValueError("Must project cash flows first")
# Discount projected FCFs
pv_fcf = 0.0
for i, fcf in enumerate(self.projected_fcf):
discount_factor = (1 + self.wacc) ** (i + 1)
pv_fcf += fcf / discount_factor
# Discount terminal values
terminal_discount = (1 + self.wacc) ** self.projection_years
pv_tv_perpetuity = self.terminal_value_perpetuity / terminal_discount
pv_tv_exit = self.terminal_value_exit_multiple / terminal_discount
self.enterprise_value_perpetuity = pv_fcf + pv_tv_perpetuity
self.enterprise_value_exit_multiple = pv_fcf + pv_tv_exit
return self.enterprise_value_perpetuity, self.enterprise_value_exit_multiple
def calculate_equity_value(self) -> Tuple[float, float]:
"""Calculate equity value from enterprise value."""
net_debt = self.historical.get("net_debt", 0)
shares_outstanding = self.historical.get("shares_outstanding", 1)
self.equity_value_perpetuity = (
self.enterprise_value_perpetuity - net_debt
)
self.equity_value_exit_multiple = (
self.enterprise_value_exit_multiple - net_debt
)
self.value_per_share_perpetuity = safe_divide(
self.equity_value_perpetuity, shares_outstanding
)
self.value_per_share_exit_multiple = safe_divide(
self.equity_value_exit_multiple, shares_outstanding
)
return self.equity_value_perpetuity, self.equity_value_exit_multiple
def sensitivity_analysis(
self,
wacc_range: Optional[List[float]] = None,
growth_range: Optional[List[float]] = None,
) -> Dict[str, Any]:
"""
Two-way sensitivity analysis: WACC vs terminal growth rate.
Returns a table of enterprise values using nested lists (no numpy).
"""
if wacc_range is None:
base_wacc = self.wacc
wacc_range = [
round(base_wacc - 0.02, 4),
round(base_wacc - 0.01, 4),
round(base_wacc, 4),
round(base_wacc + 0.01, 4),
round(base_wacc + 0.02, 4),
]
if growth_range is None:
base_growth = self.assumptions.get("terminal_growth_rate", 0.025)
growth_range = [
round(base_growth - 0.01, 4),
round(base_growth - 0.005, 4),
round(base_growth, 4),
round(base_growth + 0.005, 4),
round(base_growth + 0.01, 4),
]
rows = len(wacc_range)
cols = len(growth_range)
# Initialize sensitivity table as nested lists
ev_table = [[0.0] * cols for _ in range(rows)]
share_price_table = [[0.0] * cols for _ in range(rows)]
terminal_fcf = self.projected_fcf[-1] if self.projected_fcf else 0
for i, wacc_val in enumerate(wacc_range):
for j, growth_val in enumerate(growth_range):
if wacc_val <= growth_val:
ev_table[i][j] = float("inf")
share_price_table[i][j] = float("inf")
continue
# Recalculate PV of projected FCFs with this WACC
pv_fcf = 0.0
for k, fcf in enumerate(self.projected_fcf):
pv_fcf += fcf / ((1 + wacc_val) ** (k + 1))
# Terminal value with this growth rate
tv = (terminal_fcf * (1 + growth_val)) / (wacc_val - growth_val)
pv_tv = tv / ((1 + wacc_val) ** self.projection_years)
ev = pv_fcf + pv_tv
ev_table[i][j] = round(ev, 2)
net_debt = self.historical.get("net_debt", 0)
shares = self.historical.get("shares_outstanding", 1)
equity = ev - net_debt
share_price_table[i][j] = round(
safe_divide(equity, shares), 2
)
return {
"wacc_values": wacc_range,
"growth_values": growth_range,
"enterprise_value_table": ev_table,
"share_price_table": share_price_table,
}
def run_full_valuation(self) -> Dict[str, Any]:
"""Run the complete DCF valuation."""
self.calculate_wacc()
self.project_cash_flows()
self.calculate_terminal_value()
self.calculate_enterprise_value()
self.calculate_equity_value()
sensitivity = self.sensitivity_analysis()
return {
"wacc": self.wacc,
"projected_revenue": self.projected_revenue,
"projected_fcf": self.projected_fcf,
"terminal_value": {
"perpetuity_growth": self.terminal_value_perpetuity,
"exit_multiple": self.terminal_value_exit_multiple,
},
"enterprise_value": {
"perpetuity_growth": self.enterprise_value_perpetuity,
"exit_multiple": self.enterprise_value_exit_multiple,
},
"equity_value": {
"perpetuity_growth": self.equity_value_perpetuity,
"exit_multiple": self.equity_value_exit_multiple,
},
"value_per_share": {
"perpetuity_growth": self.value_per_share_perpetuity,
"exit_multiple": self.value_per_share_exit_multiple,
},
"sensitivity_analysis": sensitivity,
}
def format_text(self, results: Dict[str, Any]) -> str:
"""Format valuation results as human-readable text."""
lines: List[str] = []
lines.append("=" * 70)
lines.append("DCF VALUATION ANALYSIS")
lines.append("=" * 70)
def fmt_money(val: float) -> str:
if val == float("inf"):
return "N/A (WACC <= growth)"
if abs(val) >= 1e9:
return f"${val / 1e9:,.2f}B"
if abs(val) >= 1e6:
return f"${val / 1e6:,.2f}M"
if abs(val) >= 1e3:
return f"${val / 1e3:,.1f}K"
return f"${val:,.2f}"
lines.append(f"\n--- WACC ---")
lines.append(f" Weighted Average Cost of Capital: {results['wacc'] * 100:.2f}%")
lines.append(f"\n--- REVENUE PROJECTIONS ---")
for i, rev in enumerate(results["projected_revenue"], 1):
lines.append(f" Year {i}: {fmt_money(rev)}")
lines.append(f"\n--- FREE CASH FLOW PROJECTIONS ---")
for i, fcf in enumerate(results["projected_fcf"], 1):
lines.append(f" Year {i}: {fmt_money(fcf)}")
lines.append(f"\n--- TERMINAL VALUE ---")
lines.append(
f" Perpetuity Growth Method: "
f"{fmt_money(results['terminal_value']['perpetuity_growth'])}"
)
lines.append(
f" Exit Multiple Method: "
f"{fmt_money(results['terminal_value']['exit_multiple'])}"
)
lines.append(f"\n--- ENTERPRISE VALUE ---")
lines.append(
f" Perpetuity Growth Method: "
f"{fmt_money(results['enterprise_value']['perpetuity_growth'])}"
)
lines.append(
f" Exit Multiple Method: "
f"{fmt_money(results['enterprise_value']['exit_multiple'])}"
)
lines.append(f"\n--- EQUITY VALUE ---")
lines.append(
f" Perpetuity Growth Method: "
f"{fmt_money(results['equity_value']['perpetuity_growth'])}"
)
lines.append(
f" Exit Multiple Method: "
f"{fmt_money(results['equity_value']['exit_multiple'])}"
)
lines.append(f"\n--- VALUE PER SHARE ---")
vps = results["value_per_share"]
lines.append(f" Perpetuity Growth Method: ${vps['perpetuity_growth']:,.2f}")
lines.append(f" Exit Multiple Method: ${vps['exit_multiple']:,.2f}")
# Sensitivity table
sens = results["sensitivity_analysis"]
lines.append(f"\n--- SENSITIVITY ANALYSIS (Enterprise Value) ---")
lines.append(f" WACC vs Terminal Growth Rate")
lines.append("")
header = " {:>10s}".format("WACC \\ g")
for g in sens["growth_values"]:
header += f" {g * 100:>8.1f}%"
lines.append(header)
lines.append(" " + "-" * (10 + 10 * len(sens["growth_values"])))
for i, w in enumerate(sens["wacc_values"]):
row = f" {w * 100:>9.1f}%"
for j in range(len(sens["growth_values"])):
val = sens["enterprise_value_table"][i][j]
if val == float("inf"):
row += f" {'N/A':>8s}"
else:
row += f" {fmt_money(val):>8s}"
lines.append(row)
lines.append("\n" + "=" * 70)
return "\n".join(lines)
def main() -> None:
"""Main entry point."""
parser = argparse.ArgumentParser(
description="DCF Valuation Model - Enterprise and equity valuation"
)
parser.add_argument(
"input_file",
help="Path to JSON file with valuation data",
)
parser.add_argument(
"--format",
choices=["text", "json"],
default="text",
help="Output format (default: text)",
)
parser.add_argument(
"--projection-years",
type=int,
default=None,
help="Number of projection years (overrides input file)",
)
args = parser.parse_args()
try:
with open(args.input_file, "r") as f:
data = json.load(f)
except FileNotFoundError:
print(f"Error: File '{args.input_file}' not found.", file=sys.stderr)
sys.exit(1)
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON in '{args.input_file}': {e}", file=sys.stderr)
sys.exit(1)
model = DCFModel()
model.set_historical_financials(data.get("historical", {}))
assumptions = data.get("assumptions", {})
if args.projection_years is not None:
assumptions["projection_years"] = args.projection_years
model.set_assumptions(assumptions)
try:
results = model.run_full_valuation()
except ValueError as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if args.format == "json":
# Handle inf values for JSON serialization
def sanitize(obj: Any) -> Any:
if isinstance(obj, float) and math.isinf(obj):
return None
if isinstance(obj, dict):
return {k: sanitize(v) for k, v in obj.items()}
if isinstance(obj, list):
return [sanitize(v) for v in obj]
return obj
print(json.dumps(sanitize(results), indent=2))
else:
print(model.format_text(results))
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Forecast Builder
Driver-based revenue forecasting with 13-week rolling cash flow projection,
scenario modeling (base/bull/bear), and trend analysis using simple linear
regression (standard library only).
Usage:
python forecast_builder.py forecast_data.json
python forecast_builder.py forecast_data.json --format json
python forecast_builder.py forecast_data.json --scenarios base,bull,bear
"""
import argparse
import json
import math
import sys
from statistics import mean
from typing import Any, Dict, List, Optional, Tuple
def safe_divide(numerator: float, denominator: float, default: float = 0.0) -> float:
"""Safely divide two numbers, returning default if denominator is zero."""
if denominator == 0 or denominator is None:
return default
return numerator / denominator
def simple_linear_regression(
x_values: List[float], y_values: List[float]
) -> Tuple[float, float, float]:
"""
Simple linear regression using standard library.
Returns (slope, intercept, r_squared).
"""
n = len(x_values)
if n < 2 or n != len(y_values):
return (0.0, 0.0, 0.0)
x_mean = mean(x_values)
y_mean = mean(y_values)
ss_xy = sum((x - x_mean) * (y - y_mean) for x, y in zip(x_values, y_values))
ss_xx = sum((x - x_mean) ** 2 for x in x_values)
ss_yy = sum((y - y_mean) ** 2 for y in y_values)
slope = safe_divide(ss_xy, ss_xx)
intercept = y_mean - slope * x_mean
# R-squared
r_squared = safe_divide(ss_xy ** 2, ss_xx * ss_yy) if ss_yy > 0 else 0.0
return (slope, intercept, r_squared)
class ForecastBuilder:
"""Driver-based revenue forecasting with scenario modeling."""
def __init__(self, data: Dict[str, Any]) -> None:
"""Initialize the forecast builder."""
self.historical: List[Dict[str, Any]] = data.get("historical_periods", [])
self.drivers: Dict[str, Any] = data.get("drivers", {})
self.assumptions: Dict[str, Any] = data.get("assumptions", {})
self.cash_flow_inputs: Dict[str, Any] = data.get("cash_flow_inputs", {})
self.scenarios_config: Dict[str, Any] = data.get("scenarios", {})
self.forecast_periods: int = data.get("forecast_periods", 12)
def analyze_trends(self) -> Dict[str, Any]:
"""Analyze historical trends using linear regression."""
if not self.historical:
return {"error": "No historical data available"}
# Extract revenue series
revenues = [p.get("revenue", 0) for p in self.historical]
periods = list(range(1, len(revenues) + 1))
slope, intercept, r_squared = simple_linear_regression(
[float(x) for x in periods],
[float(y) for y in revenues],
)
# Calculate growth rates
growth_rates = []
for i in range(1, len(revenues)):
if revenues[i - 1] > 0:
growth = (revenues[i] - revenues[i - 1]) / revenues[i - 1]
growth_rates.append(growth)
avg_growth = mean(growth_rates) if growth_rates else 0.0
# Seasonality detection (if enough data)
seasonality_index: List[float] = []
if len(revenues) >= 4:
overall_avg = mean(revenues)
if overall_avg > 0:
seasonality_index = [r / overall_avg for r in revenues[-4:]]
return {
"trend": {
"slope": round(slope, 2),
"intercept": round(intercept, 2),
"r_squared": round(r_squared, 4),
"direction": "upward" if slope > 0 else "downward" if slope < 0 else "flat",
},
"growth_rates": [round(g, 4) for g in growth_rates],
"average_growth_rate": round(avg_growth, 4),
"seasonality_index": [round(s, 4) for s in seasonality_index],
"historical_revenues": revenues,
}
def build_driver_based_forecast(
self, scenario: str = "base"
) -> Dict[str, Any]:
"""
Build a driver-based revenue forecast.
Drivers may include: units, price, customers, ARPU, conversion rate, etc.
"""
scenario_adjustments = self.scenarios_config.get(scenario, {})
growth_adjustment = scenario_adjustments.get("growth_adjustment", 0.0)
margin_adjustment = scenario_adjustments.get("margin_adjustment", 0.0)
base_revenue = 0.0
if self.historical:
base_revenue = self.historical[-1].get("revenue", 0)
# Driver-based calculation
unit_drivers = self.drivers.get("units", {})
price_drivers = self.drivers.get("pricing", {})
customer_drivers = self.drivers.get("customers", {})
base_growth = self.assumptions.get("revenue_growth_rate", 0.05)
adjusted_growth = base_growth + growth_adjustment
base_margin = self.assumptions.get("gross_margin", 0.40)
adjusted_margin = base_margin + margin_adjustment
cogs_pct = 1.0 - adjusted_margin
opex_pct = self.assumptions.get("opex_pct_revenue", 0.25)
forecast_periods: List[Dict[str, Any]] = []
current_revenue = base_revenue
# If we have unit and price drivers, use them
has_unit_drivers = bool(unit_drivers) and bool(price_drivers)
if has_unit_drivers:
base_units = unit_drivers.get("base_units", 1000)
unit_growth = unit_drivers.get("growth_rate", 0.03) + growth_adjustment
base_price = price_drivers.get("base_price", 100)
price_growth = price_drivers.get("annual_increase", 0.02)
current_units = base_units
current_price = base_price
for period in range(1, self.forecast_periods + 1):
current_units = current_units * (1 + unit_growth / 12)
if period % 12 == 0:
current_price = current_price * (1 + price_growth)
period_revenue = current_units * current_price
cogs = period_revenue * cogs_pct
gross_profit = period_revenue - cogs
opex = period_revenue * opex_pct
operating_income = gross_profit - opex
forecast_periods.append({
"period": period,
"revenue": round(period_revenue, 2),
"units": round(current_units, 0),
"price": round(current_price, 2),
"cogs": round(cogs, 2),
"gross_profit": round(gross_profit, 2),
"gross_margin": round(adjusted_margin, 4),
"opex": round(opex, 2),
"operating_income": round(operating_income, 2),
})
else:
# Simple growth-based forecast
monthly_growth = (1 + adjusted_growth) ** (1 / 12) - 1
for period in range(1, self.forecast_periods + 1):
current_revenue = current_revenue * (1 + monthly_growth)
cogs = current_revenue * cogs_pct
gross_profit = current_revenue - cogs
opex = current_revenue * opex_pct
operating_income = gross_profit - opex
forecast_periods.append({
"period": period,
"revenue": round(current_revenue, 2),
"cogs": round(cogs, 2),
"gross_profit": round(gross_profit, 2),
"gross_margin": round(adjusted_margin, 4),
"opex": round(opex, 2),
"operating_income": round(operating_income, 2),
})
total_revenue = sum(p["revenue"] for p in forecast_periods)
total_operating_income = sum(p["operating_income"] for p in forecast_periods)
return {
"scenario": scenario,
"growth_rate": round(adjusted_growth, 4),
"gross_margin": round(adjusted_margin, 4),
"forecast_periods": forecast_periods,
"total_revenue": round(total_revenue, 2),
"total_operating_income": round(total_operating_income, 2),
"average_monthly_revenue": round(
safe_divide(total_revenue, len(forecast_periods)), 2
),
}
def build_rolling_cash_flow(self, weeks: int = 13) -> Dict[str, Any]:
"""Build a 13-week rolling cash flow projection."""
cfi = self.cash_flow_inputs
opening_balance = cfi.get("opening_cash_balance", 0)
weekly_revenue = cfi.get("weekly_revenue", 0)
collection_rate = cfi.get("collection_rate", 0.85)
collection_lag_weeks = cfi.get("collection_lag_weeks", 2)
# Weekly expenses
weekly_payroll = cfi.get("weekly_payroll", 0)
weekly_rent = cfi.get("weekly_rent", 0)
weekly_operating = cfi.get("weekly_operating", 0)
weekly_other = cfi.get("weekly_other", 0)
total_weekly_expenses = weekly_payroll + weekly_rent + weekly_operating + weekly_other
# One-time items
one_time_items: List[Dict[str, Any]] = cfi.get("one_time_items", [])
weekly_projections: List[Dict[str, Any]] = []
running_balance = opening_balance
# Revenue pipeline for lagged collections
revenue_pipeline: List[float] = [0.0] * collection_lag_weeks
for week in range(1, weeks + 1):
# Revenue collections (lagged)
revenue_pipeline.append(weekly_revenue)
collections = revenue_pipeline.pop(0) * collection_rate
# One-time items for this week
one_time_inflows = 0.0
one_time_outflows = 0.0
one_time_labels: List[str] = []
for item in one_time_items:
if item.get("week") == week:
amount = item.get("amount", 0)
if amount > 0:
one_time_inflows += amount
else:
one_time_outflows += abs(amount)
one_time_labels.append(item.get("description", ""))
total_inflows = collections + one_time_inflows
total_outflows = total_weekly_expenses + one_time_outflows
net_cash_flow = total_inflows - total_outflows
running_balance += net_cash_flow
weekly_projections.append({
"week": week,
"collections": round(collections, 2),
"one_time_inflows": round(one_time_inflows, 2),
"total_inflows": round(total_inflows, 2),
"payroll": round(weekly_payroll, 2),
"rent": round(weekly_rent, 2),
"operating": round(weekly_operating, 2),
"other_expenses": round(weekly_other, 2),
"one_time_outflows": round(one_time_outflows, 2),
"total_outflows": round(total_outflows, 2),
"net_cash_flow": round(net_cash_flow, 2),
"closing_balance": round(running_balance, 2),
"notes": ", ".join(one_time_labels) if one_time_labels else "",
})
# Summary
total_inflows = sum(w["total_inflows"] for w in weekly_projections)
total_outflows = sum(w["total_outflows"] for w in weekly_projections)
min_balance = min(w["closing_balance"] for w in weekly_projections)
min_balance_week = next(
w["week"]
for w in weekly_projections
if w["closing_balance"] == min_balance
)
return {
"weeks": weeks,
"opening_balance": opening_balance,
"closing_balance": round(running_balance, 2),
"total_inflows": round(total_inflows, 2),
"total_outflows": round(total_outflows, 2),
"net_change": round(total_inflows - total_outflows, 2),
"minimum_balance": round(min_balance, 2),
"minimum_balance_week": min_balance_week,
"cash_runway_weeks": (
round(safe_divide(running_balance, total_weekly_expenses))
if total_weekly_expenses > 0
else None
),
"weekly_projections": weekly_projections,
}
def build_scenario_comparison(
self, scenarios: Optional[List[str]] = None
) -> Dict[str, Any]:
"""Build and compare multiple scenarios."""
if scenarios is None:
scenarios = ["base", "bull", "bear"]
scenario_results: Dict[str, Any] = {}
for scenario in scenarios:
scenario_results[scenario] = self.build_driver_based_forecast(scenario)
# Comparison summary
comparison: List[Dict[str, Any]] = []
for scenario in scenarios:
result = scenario_results[scenario]
comparison.append({
"scenario": scenario,
"total_revenue": result["total_revenue"],
"total_operating_income": result["total_operating_income"],
"growth_rate": result["growth_rate"],
"gross_margin": result["gross_margin"],
"avg_monthly_revenue": result["average_monthly_revenue"],
})
return {
"scenarios": scenario_results,
"comparison": comparison,
}
def run_full_forecast(
self, scenarios: Optional[List[str]] = None
) -> Dict[str, Any]:
"""Run the complete forecast analysis."""
trends = self.analyze_trends()
scenario_comparison = self.build_scenario_comparison(scenarios)
cash_flow = self.build_rolling_cash_flow()
return {
"trend_analysis": trends,
"scenario_comparison": scenario_comparison,
"rolling_cash_flow": cash_flow,
}
def format_text(self, results: Dict[str, Any]) -> str:
"""Format forecast results as human-readable text."""
lines: List[str] = []
lines.append("=" * 70)
lines.append("FINANCIAL FORECAST REPORT")
lines.append("=" * 70)
def fmt_money(val: float) -> str:
if abs(val) >= 1e9:
return f"${val / 1e9:,.2f}B"
if abs(val) >= 1e6:
return f"${val / 1e6:,.2f}M"
if abs(val) >= 1e3:
return f"${val / 1e3:,.1f}K"
return f"${val:,.2f}"
# Trend Analysis
trend = results["trend_analysis"]
if "error" not in trend:
lines.append(f"\n--- TREND ANALYSIS ---")
t = trend["trend"]
lines.append(f" Direction: {t['direction']}")
lines.append(f" R-squared: {t['r_squared']:.4f}")
lines.append(
f" Average Historical Growth: "
f"{trend['average_growth_rate'] * 100:.1f}%"
)
if trend["seasonality_index"]:
lines.append(
f" Seasonality Index (last 4): "
f"{', '.join(f'{s:.2f}' for s in trend['seasonality_index'])}"
)
# Scenario Comparison
comp = results["scenario_comparison"]["comparison"]
lines.append(f"\n--- SCENARIO COMPARISON ---")
lines.append(
f" {'Scenario':<10s} {'Revenue':>14s} {'Op. Income':>14s} "
f"{'Growth':>8s} {'Margin':>8s}"
)
lines.append(" " + "-" * 62)
for c in comp:
lines.append(
f" {c['scenario']:<10s} {fmt_money(c['total_revenue']):>14s} "
f"{fmt_money(c['total_operating_income']):>14s} "
f"{c['growth_rate'] * 100:>7.1f}% "
f"{c['gross_margin'] * 100:>7.1f}%"
)
# Base scenario detail
base = results["scenario_comparison"]["scenarios"].get("base", {})
if base and base.get("forecast_periods"):
lines.append(f"\n--- BASE CASE MONTHLY FORECAST ---")
lines.append(
f" {'Period':>6s} {'Revenue':>12s} {'Gross Profit':>12s} "
f"{'Op. Income':>12s}"
)
lines.append(" " + "-" * 48)
for p in base["forecast_periods"]:
lines.append(
f" {p['period']:>6d} {fmt_money(p['revenue']):>12s} "
f"{fmt_money(p['gross_profit']):>12s} "
f"{fmt_money(p['operating_income']):>12s}"
)
# Cash Flow
cf = results["rolling_cash_flow"]
lines.append(f"\n--- 13-WEEK ROLLING CASH FLOW ---")
lines.append(f" Opening Balance: {fmt_money(cf['opening_balance'])}")
lines.append(f" Closing Balance: {fmt_money(cf['closing_balance'])}")
lines.append(f" Net Change: {fmt_money(cf['net_change'])}")
lines.append(
f" Minimum Balance: {fmt_money(cf['minimum_balance'])} "
f"(Week {cf['minimum_balance_week']})"
)
if cf.get("cash_runway_weeks"):
lines.append(f" Cash Runway: {cf['cash_runway_weeks']:.0f} weeks")
lines.append(f"\n Weekly Detail:")
lines.append(
f" {'Wk':>3s} {'Inflows':>10s} {'Outflows':>10s} "
f"{'Net':>10s} {'Balance':>12s}"
)
lines.append(" " + "-" * 50)
for w in cf["weekly_projections"]:
notes = f" {w['notes']}" if w["notes"] else ""
lines.append(
f" {w['week']:>3d} {fmt_money(w['total_inflows']):>10s} "
f"{fmt_money(w['total_outflows']):>10s} "
f"{fmt_money(w['net_cash_flow']):>10s} "
f"{fmt_money(w['closing_balance']):>12s}{notes}"
)
lines.append("\n" + "=" * 70)
return "\n".join(lines)
def main() -> None:
"""Main entry point."""
parser = argparse.ArgumentParser(
description="Driver-based revenue forecasting with scenario modeling"
)
parser.add_argument(
"input_file",
help="Path to JSON file with forecast data",
)
parser.add_argument(
"--format",
choices=["text", "json"],
default="text",
help="Output format (default: text)",
)
parser.add_argument(
"--scenarios",
type=str,
default="base,bull,bear",
help="Comma-separated list of scenarios (default: base,bull,bear)",
)
args = parser.parse_args()
try:
with open(args.input_file, "r") as f:
data = json.load(f)
except FileNotFoundError:
print(f"Error: File '{args.input_file}' not found.", file=sys.stderr)
sys.exit(1)
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON in '{args.input_file}': {e}", file=sys.stderr)
sys.exit(1)
builder = ForecastBuilder(data)
scenarios = [s.strip() for s in args.scenarios.split(",")]
results = builder.run_full_forecast(scenarios)
if args.format == "json":
print(json.dumps(results, indent=2))
else:
print(builder.format_text(results))
if __name__ == "__main__":
main()