
Saas Metrics Coach
- 604 installs
- 23.5k repo stars
- Updated July 17, 2026
- alirezarezvani/claude-skills
SaaS Metrics Coach is a Claude skill that scores pasted MRR, churn, and cost numbers against stage- and segment-specific SaaS benchmarks and tells developers which metric to fix first.
About
SaaS Metrics Coach is a structured input-template skill from alirezarezvani/claude-skills that accepts partial revenue, customer, and cost data and benchmarks it against Enterprise, Mid-Market, SMB, and Consumer/PLG segments. The template covers MRR, expansion, churn, contraction, customer counts, sales and marketing spend, and margin percentages across Early (<$1M ARR), Growth ($1M–$10M), and Scale ($10M+) stages. A developer pastes whatever fields are available and the agent compares results to stage-appropriate SaaS benchmarks, then ranks the highest-impact fixes. Reach for SaaS Metrics Coach when monthly numbers exist but it is unclear whether churn, CAC efficiency, or expansion is the bottleneck.
- Structured input template for MRR, expansion/contraction, customer counts, and S&M spend with partial data supported
- Stage slices: Early (<$1M ARR), Growth ($1M–$10M), Scale ($10M–$50M), Late ($50M+)
- Monthly churn CRITICAL/WATCH/HEALTHY bands by segment: Enterprise, Mid-Market, SMB/PLG, and Consumer
- LTV:CAC status ladder from CRITICAL (<1:1) through POOR and healthier ranges
- Benchmarks cite OpenView 2024, Bessemer Cloud Index, SaaS Capital Index, and Paddle 2025
Saas Metrics Coach by the numbers
- 604 all-time installs (skills.sh)
- Ranked #662 of 3,282 Productivity & Planning skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Jul 31, 2026 (Skillselion catalog sync)
npx skills add https://github.com/alirezarezvani/claude-skills --skill saas-metrics-coachAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 604 |
|---|---|
| repo stars | ★ 23.5k |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 17, 2026 |
| Repository | alirezarezvani/claude-skills ↗ |
How do you benchmark SaaS MRR and churn against stage norms?
Paste your MRR, churn, and cost numbers so an agent can score them against stage- and segment-specific SaaS benchmarks and tell you what to fix first.
Who is it for?
Developers or technical founders running a SaaS product who have partial monthly revenue and cost data and need benchmark-driven prioritization.
Skip if: Teams without any revenue or cost numbers yet, or projects that need financial modeling spreadsheets rather than benchmark coaching.
When should I use this skill?
A developer pastes MRR, churn, expansion, or S&M spend figures and asks whether metrics look healthy for their ARR stage or segment.
What you get
Prioritized metric fix list, benchmark comparison notes, and gap analysis against ARR stage and market segment.
- Prioritized fix list
- Benchmark gap analysis
By the numbers
- Covers 3 ARR stages: Early (<$1M), Growth ($1M–$10M), and Scale ($10M+)
- Supports 4 target market segments in the input template
Files
SaaS Metrics Coach
Act as a senior SaaS CFO advisor. Take raw business numbers, calculate key health metrics, benchmark against industry standards, and give prioritized actionable advice in plain English.
Step 1 — Collect Inputs
If not already provided, ask for these in a single grouped request:
- Revenue: current MRR, MRR last month, expansion MRR, churned MRR
- Customers: total active, new this month, churned this month
- Costs: sales and marketing spend, gross margin %
Work with partial data. Be explicit about what is missing and what assumptions are being made.
Step 2 — Calculate Metrics
Run scripts/metrics_calculator.py with the user's inputs. If the script is unavailable, use the formulas in references/formulas.md.
Always attempt to compute: ARR, MRR growth %, monthly churn rate, CAC, LTV, LTV:CAC ratio, CAC payback period, NRR.
Additional Analysis Tools:
- Use
scripts/quick_ratio_calculator.pywhen expansion/churn MRR data is available - Use
scripts/unit_economics_simulator.pyfor forward-looking projections
Step 3 — Benchmark Each Metric
Load references/benchmarks.md. For each metric show:
- The calculated value
- The relevant benchmark range for the user's segment and stage
- A plain status label: HEALTHY / WATCH / CRITICAL
Match the benchmark tier to the user's market segment (Enterprise / Mid-Market / SMB / PLG) and company stage (Early / Growth / Scale). Ask if unclear.
Step 4 — Prioritize and Recommend
Identify the top 2-3 metrics at WATCH or CRITICAL status. For each one state:
- What is happening (one sentence, plain English)
- Why it matters to the business
- Two or three specific actions to take this month
Order by impact — address the most damaging problem first.
Step 5 — Output Format
Always use this exact structure:
# SaaS Health Report — [Month Year]
## Metrics at a Glance
| Metric | Your Value | Benchmark | Status |
|--------|------------|-----------|--------|
## Overall Picture
[2-3 sentences, plain English summary]
## Priority Issues
### 1. [Metric Name]
What is happening: ...
Why it matters: ...
Fix it this month: ...
### 2. [Metric Name]
...
## What is Working
[1-2 genuine strengths, no padding]
## 90-Day Focus
[Single metric to move + specific numeric target]Examples
Example 1 — Partial data
Input: "MRR is $80k, we have 200 customers, about 3 cancel each month."
Expected output: Calculates ARPA ($400), monthly churn (1.5%), ARR ($960k), LTV estimate. Flags CAC and growth rate as missing. Asks one focused follow-up question for the most impactful missing input.
Example 2 — Critical scenario
Input: "MRR $22k (was $23.5k), 80 customers, lost 9, gained 6, spent $15k on ads, 65% gross margin."
Expected output: Flags negative MoM growth (-6.4%), critical churn (11.25%), and LTV:CAC of 0.64:1 as CRITICAL. Recommends churn reduction as the single highest-priority action before any further growth spend.
Key Principles
- Be direct. If a metric is bad, say it is bad.
- Explain every metric in one sentence before showing the number.
- Cap priority issues at three. More than three paralyzes action.
- Context changes benchmarks. Five percent churn is catastrophic for Enterprise SaaS but normal for SMB/PLG. Always confirm the user's target market before scoring.
Reference Files
references/formulas.md— All metric formulas with worked examplesreferences/benchmarks.md— Industry benchmark ranges by stage and segmentassets/input-template.md— Blank input form to share with usersscripts/metrics_calculator.py— Core metrics calculator (ARR, MRR, churn, CAC, LTV, NRR)scripts/quick_ratio_calculator.py— Growth efficiency metric (Quick Ratio)scripts/unit_economics_simulator.py— 12-month forward projection
Tools
1. Metrics Calculator (scripts/metrics_calculator.py)
Core SaaS metrics from raw business numbers.
# Interactive mode
python scripts/metrics_calculator.py
# CLI mode
python scripts/metrics_calculator.py --mrr 50000 --customers 100 --churned 5 --json2. Quick Ratio Calculator (scripts/quick_ratio_calculator.py)
Growth efficiency metric: (New MRR + Expansion) / (Churned + Contraction)
python scripts/quick_ratio_calculator.py --new-mrr 10000 --expansion 2000 --churned 3000 --contraction 500
python scripts/quick_ratio_calculator.py --new-mrr 10000 --expansion 2000 --churned 3000 --jsonBenchmarks:
- < 1.0 = CRITICAL (losing faster than gaining)
- 1-2 = WATCH (marginal growth)
- 2-4 = HEALTHY (good efficiency)
- \> 4 = EXCELLENT (strong growth)
3. Unit Economics Simulator (scripts/unit_economics_simulator.py)
Project metrics forward 12 months based on growth/churn assumptions.
python scripts/unit_economics_simulator.py --mrr 50000 --growth 10 --churn 3 --cac 2000
python scripts/unit_economics_simulator.py --mrr 50000 --growth 10 --churn 3 --cac 2000 --jsonUse for:
- "What if we grow at X% per month?"
- Runway projections
- Scenario planning (best/base/worst case)
Related Skills
- financial-analyst: Use for DCF valuation, budget variance analysis, and traditional financial modeling. NOT for SaaS-specific metrics like CAC, LTV, or churn.
- business-growth/customer-success: Use for retention strategies and customer health scoring. Complements this skill when churn is flagged as CRITICAL.
SaaS Metrics — Input Template
Fill in what you know and paste to the SaaS Metrics Coach. Leave blanks empty.
---
Context
- Target market: [ ] Enterprise [ ] Mid-Market [ ] SMB [ ] Consumer/PLG
- Stage: [ ] Early (<$1M ARR) [ ] Growth ($1M–$10M) [ ] Scale ($10M+)
Revenue
- Current MRR: $
- MRR last month: $
- Expansion MRR this month (upsells/upgrades): $
- Churned MRR this month: $
- Contraction MRR (downgrades): $
Customers
- Total active customers:
- New customers this month:
- Churned customers this month:
Costs
- Sales & Marketing spend this month: $
- Gross margin %:
- Net profit margin % (optional):
--- Partial data is fine — the coach works with whatever you have.
SaaS Industry Benchmarks
Industry-standard benchmark ranges for SaaS metrics, segmented by company stage and market segment.
Sources:
- OpenView SaaS Benchmarks 2024
- Bessemer Venture Partners Cloud Index
- SaaS Capital Index
- Paddle SaaS Metrics Report 2025
Last updated: March 2026
Stage Definitions
- Early: < $1M ARR
- Growth: $1M–$10M ARR
- Scale: $10M–$50M ARR
- Late: $50M+ ARR
---
Monthly Churn Rate
| Segment | CRITICAL | WATCH | HEALTHY |
|---|---|---|---|
| Enterprise (ACV > $25k) | > 3% | 1–3% | < 1% |
| Mid-Market ($5k–$25k ACV) | > 5% | 2–5% | < 2% |
| SMB / PLG (< $5k ACV) | > 8% | 4–8% | < 4% |
| Consumer | > 10% | 5–10% | < 5% |
LTV:CAC Ratio
| Status | Range |
|---|---|
| CRITICAL | < 1:1 — losing money on every customer |
| POOR | 1:1–2:1 — barely breaking even |
| WATCH | 2:1–3:1 — marginally viable |
| HEALTHY | 3:1–5:1 — industry standard |
| EXCELLENT | > 5:1 — strong unit economics |
| WATCH | > 8:1 — possibly under-investing in growth |
CAC Payback Period
| Status | Range |
|---|---|
| CRITICAL | > 24 months |
| WATCH | 18–24 months |
| HEALTHY | 12–18 months |
| GOOD | 6–12 months |
| EXCELLENT | < 6 months (PLG indicator) |
NRR (Net Revenue Retention)
| Status | Range |
|---|---|
| CRITICAL | < 80% — revenue shrinking from existing base |
| POOR | 80–90% |
| WATCH | 90–100% — flat, not expanding |
| HEALTHY | 100–110% |
| EXCELLENT | 110–120% |
| WORLD-CLASS | > 120% (Snowflake / Datadog territory) |
MoM MRR Growth
| Stage | CRITICAL | WATCH | HEALTHY | EXCELLENT |
|---|---|---|---|---|
| Early (< $1M ARR) | < 5% | 5–10% | 10–20% | > 20% |
| Growth ($1M–$10M) | < 3% | 3–7% | 7–15% | > 15% |
| Scale ($10M+) | < 1% | 1–3% | 3–7% | > 7% |
Gross Margin
| Status | Range |
|---|---|
| CRITICAL | < 50% |
| WATCH | 50–65% |
| HEALTHY | 65–75% |
| EXCELLENT | 75–85% |
| WORLD-CLASS | > 85% (API / infrastructure businesses) |
Rule of 40
| Score | Status |
|---|---|
| < 20 | CONCERNING |
| 20–40 | DEVELOPING |
| 40–60 | HEALTHY |
| > 60 | EXCELLENT |
Quick Reference Card
Metric Must Hit Good Great
---------------------------------------------
Monthly Churn < 5% < 3% < 1%
LTV:CAC > 3:1 > 4:1 > 5:1
CAC Payback < 18 mo < 12 mo < 6 mo
NRR > 100% > 110% > 120%
Gross Margin > 65% > 75% > 80%
MoM Growth > 5% > 10% > 15%SaaS Metric Formulas
Complete reference with worked examples for all metrics calculated by the SaaS Metrics Coach.
ARR (Annual Recurring Revenue)
ARR = MRR × 12Example:
- Current MRR: $50,000
- ARR = $50,000 × 12 = $600,000
When to use: Quick snapshot of annualized revenue run rate. Not the same as actual annual revenue if you have seasonality or one-time fees.
MoM MRR Growth Rate
MoM Growth % = ((MRR_now - MRR_last) / MRR_last) × 100Example:
- Current MRR: $50,000
- Last month MRR: $45,000
- Growth = (($50,000 - $45,000) / $45,000) × 100 = 11.1%
Interpretation:
- Negative = losing revenue
- 0-5% = slow growth (concerning for early stage)
- 5-15% = healthy growth
- >15% = strong growth (early stage)
Monthly Churn Rate
Churn % = (Customers lost / Customers at start of month) × 100Example:
- Customers at start of month: 100
- Customers lost during month: 5
- Churn = (5 / 100) × 100 = 5%
Annualized impact: 5% monthly = ~46% annual churn (compounding effect)
Critical context: Churn tolerance varies by segment:
- Enterprise: >3% is critical
- SMB: >8% is critical
- Always confirm segment before judging severity
ARPA (Avg Revenue Per Account)
ARPA = MRR / Total active customersCAC (Customer Acquisition Cost)
CAC = Total Sales & Marketing spend / New customers acquiredExample: $20k spend / 10 customers → CAC $2,000
LTV (Customer Lifetime Value)
LTV = (ARPA / Monthly Churn Rate) × Gross Margin %Simplified (no gross margin data):
LTV = ARPA / Monthly Churn RateExample:
- ARPA: $500
- Monthly churn: 5% (0.05)
- Gross margin: 70% (0.70)
- LTV = ($500 / 0.05) × 0.70 = $7,000
Simplified (no margin): $500 / 0.05 = $10,000
Why it matters: LTV tells you the total revenue you can expect from an average customer. Must be at least 3x your CAC to have sustainable unit economics.
LTV:CAC Ratio
LTV:CAC = LTV / CACExample: LTV $10k / CAC $2k = 5:1
CAC Payback Period
Payback (months) = CAC / (ARPA × Gross Margin %)
Simplified: Payback = CAC / ARPAExample: CAC $2k / ARPA $500 = 4 months
NRR (Net Revenue Retention)
NRR % = ((MRR_start + Expansion MRR - Churned MRR - Contraction MRR) / MRR_start) × 100Simplified (no expansion data): NRR ≈ (1 - Revenue Churn Rate) × 100
Rule of 40
Score = Annualized MoM Growth % + Net Profit Margin %
Healthy: ≥ 40#!/usr/bin/env python3
"""
SaaS Metrics Calculator — zero external dependencies (stdlib only).
Usage (interactive): python metrics_calculator.py
Usage (CLI): python metrics_calculator.py --mrr 48000 --customers 160 --json
Usage (import):
from metrics_calculator import calculate, report
results = calculate(mrr=48000, mrr_last=42000, customers=160,
churned=4, new_customers=22, sm_spend=18000,
gross_margin=0.72)
print(report(results))
"""
import json
import sys
def calculate(
mrr=None,
mrr_last=None,
customers=None,
churned=None,
new_customers=None,
sm_spend=None,
gross_margin=0.70,
expansion_mrr=0,
churned_mrr=0,
contraction_mrr=0,
profit_margin=None,
):
r, missing = {}, []
# ── Core revenue ─────────────────────────────────────────────────────────
if mrr is not None:
r["MRR"] = round(mrr, 2)
r["ARR"] = round(mrr * 12, 2)
else:
missing.append("ARR/MRR — need current MRR")
if mrr and customers:
r["ARPA"] = round(mrr / customers, 2)
else:
missing.append("ARPA — need MRR + customer count")
# ── Growth ────────────────────────────────────────────────────────────────
if mrr and mrr_last and mrr_last > 0:
r["MoM_Growth_Pct"] = round(((mrr - mrr_last) / mrr_last) * 100, 2)
else:
missing.append("MoM Growth — need last month MRR")
# ── Churn ─────────────────────────────────────────────────────────────────
if churned is not None and customers:
r["Churn_Pct"] = round((churned / customers) * 100, 2)
else:
missing.append("Churn Rate — need churned + total customers")
# ── CAC ───────────────────────────────────────────────────────────────────
if sm_spend and new_customers and new_customers > 0:
r["CAC"] = round(sm_spend / new_customers, 2)
else:
missing.append("CAC — need S&M spend + new customers")
# ── LTV ───────────────────────────────────────────────────────────────────
arpa = r.get("ARPA")
churn_dec = r.get("Churn_Pct", 0) / 100
if arpa and churn_dec > 0:
r["LTV"] = round((arpa / churn_dec) * gross_margin, 2)
else:
missing.append("LTV — need ARPA and churn rate")
# ── LTV:CAC ───────────────────────────────────────────────────────────────
if r.get("LTV") and r.get("CAC") and r["CAC"] > 0:
r["LTV_CAC"] = round(r["LTV"] / r["CAC"], 2)
else:
missing.append("LTV:CAC — need both LTV and CAC")
# ── Payback ───────────────────────────────────────────────────────────────
if r.get("CAC") and arpa and arpa > 0:
r["Payback_Months"] = round(r["CAC"] / (arpa * gross_margin), 1)
else:
missing.append("Payback Period — need CAC and ARPA")
# ── NRR ───────────────────────────────────────────────────────────────────
if mrr_last and mrr_last > 0 and (expansion_mrr or churned_mrr or contraction_mrr):
nrr = ((mrr_last + expansion_mrr - churned_mrr - contraction_mrr) / mrr_last) * 100
r["NRR_Pct"] = round(nrr, 2)
elif r.get("Churn_Pct"):
r["NRR_Est_Pct"] = round((1 - r["Churn_Pct"] / 100) * 100, 2)
missing.append("NRR (accurate) — using churn-only estimate; provide expansion MRR for full NRR")
# ── Rule of 40 ────────────────────────────────────────────────────────────
if r.get("MoM_Growth_Pct") and profit_margin is not None:
r["Rule_of_40"] = round(r["MoM_Growth_Pct"] * 12 + profit_margin, 1)
r["_missing"] = missing
r["_gross_margin"] = gross_margin
return r
def report(r):
labels = [
("MRR", "Monthly Recurring Revenue", "$"),
("ARR", "Annual Recurring Revenue", "$"),
("ARPA", "Avg Revenue Per Account/mo", "$"),
("MoM_Growth_Pct", "MoM MRR Growth", "%"),
("Churn_Pct", "Monthly Churn Rate", "%"),
("CAC", "Customer Acquisition Cost", "$"),
("LTV", "Customer Lifetime Value", "$"),
("LTV_CAC", "LTV:CAC Ratio", ":1"),
("Payback_Months", "CAC Payback Period", " months"),
("NRR_Pct", "NRR (Net Revenue Retention)", "%"),
("NRR_Est_Pct", "NRR Estimate (churn-only)", "%"),
("Rule_of_40", "Rule of 40 Score", ""),
]
lines = ["=" * 54, " SAAS METRICS CALCULATOR", "=" * 54, ""]
for key, label, unit in labels:
val = r.get(key)
if val is None:
continue
if unit == "$":
fmt = f"${val:,.2f}"
elif unit == "%":
fmt = f"{val}%"
elif unit == ":1":
fmt = f"{val}:1"
else:
fmt = f"{val}{unit}"
lines.append(f" {label:<40} {fmt}")
if r.get("_missing"):
lines += ["", " Missing / estimated:"]
for m in r["_missing"]:
lines.append(f" - {m}")
lines.append("=" * 54)
return "\n".join(lines)
# ── Interactive mode ──────────────────────────────────────────────────────────
def _ask(prompt, required=False):
while True:
v = input(f" {prompt}: ").strip()
if not v:
if required:
print(" Required — please enter a value.")
continue
return None
try:
return float(v)
except ValueError:
print(" Enter a number (e.g. 48000 or 72).")
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="SaaS Metrics Calculator")
parser.add_argument("--mrr", type=float, help="Current MRR")
parser.add_argument("--mrr-last", type=float, help="MRR last month")
parser.add_argument("--customers", type=int, help="Total active customers")
parser.add_argument("--churned", type=int, help="Customers churned this month")
parser.add_argument("--new-customers", type=int, help="New customers acquired")
parser.add_argument("--sm-spend", type=float, help="Sales & Marketing spend")
parser.add_argument("--gross-margin", type=float, default=70, help="Gross margin %% (default: 70)")
parser.add_argument("--expansion-mrr", type=float, default=0, help="Expansion MRR")
parser.add_argument("--churned-mrr", type=float, default=0, help="Churned MRR")
parser.add_argument("--contraction-mrr", type=float, default=0, help="Contraction MRR")
parser.add_argument("--profit-margin", type=float, help="Net profit margin %%")
parser.add_argument("--json", action="store_true", help="Output JSON format")
args = parser.parse_args()
# CLI mode
if args.mrr is not None:
inputs = {
"mrr": args.mrr,
"mrr_last": args.mrr_last,
"customers": args.customers,
"churned": args.churned,
"new_customers": args.new_customers,
"sm_spend": args.sm_spend,
"gross_margin": args.gross_margin / 100 if args.gross_margin > 1 else args.gross_margin,
"expansion_mrr": args.expansion_mrr,
"churned_mrr": args.churned_mrr,
"contraction_mrr": args.contraction_mrr,
"profit_margin": args.profit_margin,
}
result = calculate(**inputs)
if args.json:
print(json.dumps(result, indent=2))
else:
print("\n" + report(result))
sys.exit(0)
# Interactive mode
print("\nSaaS Metrics Calculator (press Enter to skip)\n")
gm = _ask("Gross margin % (default 70)", required=False) or 70
inputs = dict(
mrr=_ask("Current MRR ($)", required=True),
mrr_last=_ask("MRR last month ($)"),
customers=_ask("Total active customers"),
churned=_ask("Customers churned this month"),
new_customers=_ask("New customers acquired this month"),
sm_spend=_ask("Sales & Marketing spend this month ($)"),
gross_margin=gm / 100 if gm > 1 else gm,
expansion_mrr=_ask("Expansion MRR (upsells) ($)") or 0,
churned_mrr=_ask("Churned MRR ($)") or 0,
contraction_mrr=_ask("Contraction MRR (downgrades) ($)") or 0,
profit_margin=_ask("Net profit margin % (for Rule of 40, optional)"),
)
print("\n" + report(calculate(**inputs)))
#!/usr/bin/env python3
"""
Quick Ratio Calculator - SaaS growth efficiency metric.
Quick Ratio = (New MRR + Expansion MRR) / (Churned MRR + Contraction MRR)
A ratio > 4 indicates healthy, efficient growth.
A ratio < 1 means you're losing revenue faster than gaining it.
Usage:
python quick_ratio_calculator.py --new-mrr 10000 --expansion 2000 --churned 3000 --contraction 500
python quick_ratio_calculator.py --new-mrr 10000 --expansion 2000 --churned 3000 --contraction 500 --json
"""
import json
import sys
import argparse
def calculate_quick_ratio(new_mrr, expansion_mrr, churned_mrr, contraction_mrr):
"""
Calculate Quick Ratio and provide interpretation.
Args:
new_mrr: New MRR from new customers
expansion_mrr: Expansion MRR from existing customers (upsells)
churned_mrr: MRR lost from churned customers
contraction_mrr: MRR lost from downgrades
Returns:
dict with quick ratio and analysis
"""
# Calculate components
growth_mrr = new_mrr + expansion_mrr
lost_mrr = churned_mrr + contraction_mrr
# Quick Ratio
if lost_mrr == 0:
quick_ratio = float('inf') if growth_mrr > 0 else 0
quick_ratio_display = "∞" if growth_mrr > 0 else "0"
else:
quick_ratio = growth_mrr / lost_mrr
quick_ratio_display = f"{quick_ratio:.2f}"
# Status assessment
if lost_mrr == 0 and growth_mrr > 0:
status = "EXCELLENT"
interpretation = "No revenue loss - perfect retention with growth"
elif quick_ratio >= 4:
status = "EXCELLENT"
interpretation = "Strong, efficient growth - gaining revenue 4x faster than losing it"
elif quick_ratio >= 2:
status = "HEALTHY"
interpretation = "Good growth efficiency - gaining revenue 2x+ faster than losing it"
elif quick_ratio >= 1:
status = "WATCH"
interpretation = "Marginal growth - barely gaining more than losing"
else:
status = "CRITICAL"
interpretation = "Losing revenue faster than gaining - growth is unsustainable"
# Breakdown percentages
if growth_mrr > 0:
new_pct = (new_mrr / growth_mrr) * 100
expansion_pct = (expansion_mrr / growth_mrr) * 100
else:
new_pct = expansion_pct = 0
if lost_mrr > 0:
churned_pct = (churned_mrr / lost_mrr) * 100
contraction_pct = (contraction_mrr / lost_mrr) * 100
else:
churned_pct = contraction_pct = 0
results = {
"quick_ratio": quick_ratio if quick_ratio != float('inf') else None,
"quick_ratio_display": quick_ratio_display,
"status": status,
"interpretation": interpretation,
"components": {
"growth_mrr": round(growth_mrr, 2),
"lost_mrr": round(lost_mrr, 2),
"new_mrr": round(new_mrr, 2),
"expansion_mrr": round(expansion_mrr, 2),
"churned_mrr": round(churned_mrr, 2),
"contraction_mrr": round(contraction_mrr, 2),
},
"breakdown": {
"new_mrr_pct": round(new_pct, 1),
"expansion_mrr_pct": round(expansion_pct, 1),
"churned_mrr_pct": round(churned_pct, 1),
"contraction_mrr_pct": round(contraction_pct, 1),
},
}
return results
def format_report(results):
"""Format quick ratio results as human-readable report."""
lines = []
lines.append("\n" + "=" * 70)
lines.append("QUICK RATIO ANALYSIS")
lines.append("=" * 70)
# Quick Ratio
lines.append(f"\n⚡ QUICK RATIO: {results['quick_ratio_display']}")
lines.append(f" Status: {results['status']}")
lines.append(f" {results['interpretation']}")
# Components
comp = results["components"]
lines.append("\n📊 COMPONENTS")
lines.append(f" Growth MRR (New + Expansion): ${comp['growth_mrr']:,.2f}")
lines.append(f" • New MRR: ${comp['new_mrr']:,.2f}")
lines.append(f" • Expansion MRR: ${comp['expansion_mrr']:,.2f}")
lines.append(f" Lost MRR (Churned + Contraction): ${comp['lost_mrr']:,.2f}")
lines.append(f" • Churned MRR: ${comp['churned_mrr']:,.2f}")
lines.append(f" • Contraction MRR: ${comp['contraction_mrr']:,.2f}")
# Breakdown
bd = results["breakdown"]
lines.append("\n📈 GROWTH BREAKDOWN")
lines.append(f" New customers: {bd['new_mrr_pct']:.1f}%")
lines.append(f" Expansion: {bd['expansion_mrr_pct']:.1f}%")
lines.append("\n📉 LOSS BREAKDOWN")
lines.append(f" Churn: {bd['churned_mrr_pct']:.1f}%")
lines.append(f" Contraction: {bd['contraction_mrr_pct']:.1f}%")
# Benchmarks
lines.append("\n🎯 BENCHMARKS")
lines.append(" < 1.0 = CRITICAL (losing revenue faster than gaining)")
lines.append(" 1-2 = WATCH (marginal growth)")
lines.append(" 2-4 = HEALTHY (good growth efficiency)")
lines.append(" > 4 = EXCELLENT (strong, efficient growth)")
lines.append("\n" + "=" * 70 + "\n")
return "\n".join(lines)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Calculate SaaS Quick Ratio (growth efficiency metric)"
)
parser.add_argument(
"--new-mrr", type=float, required=True, help="New MRR from new customers"
)
parser.add_argument(
"--expansion", type=float, default=0, help="Expansion MRR from upsells (default: 0)"
)
parser.add_argument(
"--churned", type=float, required=True, help="Churned MRR from lost customers"
)
parser.add_argument(
"--contraction", type=float, default=0, help="Contraction MRR from downgrades (default: 0)"
)
parser.add_argument("--json", action="store_true", help="Output JSON format")
args = parser.parse_args()
results = calculate_quick_ratio(
new_mrr=args.new_mrr,
expansion_mrr=args.expansion,
churned_mrr=args.churned,
contraction_mrr=args.contraction,
)
if args.json:
print(json.dumps(results, indent=2))
else:
print(format_report(results))
#!/usr/bin/env python3
"""
Unit Economics Simulator - Project SaaS metrics forward 12 months.
Usage:
python unit_economics_simulator.py --mrr 50000 --growth 10 --churn 3 --cac 2000
python unit_economics_simulator.py --mrr 50000 --growth 10 --churn 3 --cac 2000 --json
"""
import json
import sys
import argparse
def simulate(
mrr,
monthly_growth_pct,
monthly_churn_pct,
cac,
gross_margin=0.70,
sm_spend_pct=0.30,
months=12,
):
"""
Simulate unit economics forward.
Args:
mrr: Starting MRR
monthly_growth_pct: Expected monthly growth rate (%)
monthly_churn_pct: Expected monthly churn rate (%)
cac: Customer acquisition cost
gross_margin: Gross margin (0-1)
sm_spend_pct: Sales & marketing as % of revenue (0-1)
months: Number of months to project
Returns:
dict with monthly projections and summary
"""
results = {
"inputs": {
"starting_mrr": mrr,
"monthly_growth_pct": monthly_growth_pct,
"monthly_churn_pct": monthly_churn_pct,
"cac": cac,
"gross_margin": gross_margin,
"sm_spend_pct": sm_spend_pct,
},
"projections": [],
"summary": {},
}
current_mrr = mrr
cumulative_sm_spend = 0
cumulative_gross_profit = 0
for month in range(1, months + 1):
# Calculate growth and churn
growth_rate = monthly_growth_pct / 100
churn_rate = monthly_churn_pct / 100
# Net growth = growth - churn
net_growth_rate = growth_rate - churn_rate
new_mrr = current_mrr * (1 + net_growth_rate)
# Revenue and costs
monthly_revenue = current_mrr
gross_profit = monthly_revenue * gross_margin
sm_spend = monthly_revenue * sm_spend_pct
net_profit = gross_profit - sm_spend
# Accumulate
cumulative_sm_spend += sm_spend
cumulative_gross_profit += gross_profit
# ARR
arr = current_mrr * 12
results["projections"].append({
"month": month,
"mrr": round(current_mrr, 2),
"arr": round(arr, 2),
"monthly_revenue": round(monthly_revenue, 2),
"gross_profit": round(gross_profit, 2),
"sm_spend": round(sm_spend, 2),
"net_profit": round(net_profit, 2),
"growth_rate_pct": round(net_growth_rate * 100, 2),
})
current_mrr = new_mrr
# Summary
final_mrr = results["projections"][-1]["mrr"]
final_arr = results["projections"][-1]["arr"]
total_revenue = sum(p["monthly_revenue"] for p in results["projections"])
total_net_profit = sum(p["net_profit"] for p in results["projections"])
results["summary"] = {
"starting_mrr": mrr,
"ending_mrr": round(final_mrr, 2),
"ending_arr": round(final_arr, 2),
"mrr_growth_pct": round(((final_mrr - mrr) / mrr) * 100, 2),
"total_revenue_12m": round(total_revenue, 2),
"total_gross_profit_12m": round(cumulative_gross_profit, 2),
"total_sm_spend_12m": round(cumulative_sm_spend, 2),
"total_net_profit_12m": round(total_net_profit, 2),
"avg_monthly_growth_pct": round((monthly_growth_pct - monthly_churn_pct), 2),
}
return results
def format_report(results):
"""Format simulation results as human-readable report."""
lines = []
lines.append("\n" + "=" * 70)
lines.append("UNIT ECONOMICS SIMULATION - 12 MONTH PROJECTION")
lines.append("=" * 70)
# Inputs
inputs = results["inputs"]
lines.append("\n📊 INPUTS")
lines.append(f" Starting MRR: ${inputs['starting_mrr']:,.0f}")
lines.append(f" Monthly Growth: {inputs['monthly_growth_pct']}%")
lines.append(f" Monthly Churn: {inputs['monthly_churn_pct']}%")
lines.append(f" CAC: ${inputs['cac']:,.0f}")
lines.append(f" Gross Margin: {inputs['gross_margin']*100:.0f}%")
lines.append(f" S&M Spend: {inputs['sm_spend_pct']*100:.0f}% of revenue")
# Summary
summary = results["summary"]
lines.append("\n📈 12-MONTH SUMMARY")
lines.append(f" Starting MRR: ${summary['starting_mrr']:,.0f}")
lines.append(f" Ending MRR: ${summary['ending_mrr']:,.0f}")
lines.append(f" Ending ARR: ${summary['ending_arr']:,.0f}")
lines.append(f" MRR Growth: {summary['mrr_growth_pct']:+.1f}%")
lines.append(f" Total Revenue: ${summary['total_revenue_12m']:,.0f}")
lines.append(f" Total Gross Profit: ${summary['total_gross_profit_12m']:,.0f}")
lines.append(f" Total S&M Spend: ${summary['total_sm_spend_12m']:,.0f}")
lines.append(f" Total Net Profit: ${summary['total_net_profit_12m']:,.0f}")
# Monthly breakdown (first 3, last 3)
lines.append("\n📅 MONTHLY PROJECTIONS")
lines.append(f"{'Month':<8} {'MRR':<12} {'ARR':<12} {'Revenue':<12} {'Net Profit':<12}")
lines.append("-" * 70)
projs = results["projections"]
for p in projs[:3]:
lines.append(
f"{p['month']:<8} ${p['mrr']:<11,.0f} ${p['arr']:<11,.0f} "
f"${p['monthly_revenue']:<11,.0f} ${p['net_profit']:<11,.0f}"
)
if len(projs) > 6:
lines.append(" ...")
for p in projs[-3:]:
lines.append(
f"{p['month']:<8} ${p['mrr']:<11,.0f} ${p['arr']:<11,.0f} "
f"${p['monthly_revenue']:<11,.0f} ${p['net_profit']:<11,.0f}"
)
lines.append("\n" + "=" * 70 + "\n")
return "\n".join(lines)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Simulate SaaS unit economics over 12 months"
)
parser.add_argument("--mrr", type=float, required=True, help="Starting MRR")
parser.add_argument(
"--growth", type=float, required=True, help="Monthly growth rate (pct)"
)
parser.add_argument(
"--churn", type=float, required=True, help="Monthly churn rate (pct)"
)
parser.add_argument("--cac", type=float, required=True, help="Customer acquisition cost")
parser.add_argument(
"--gross-margin", type=float, default=70, help="Gross margin %% (default: 70)"
)
parser.add_argument(
"--sm-spend", type=float, default=30, help="S&M spend as %% of revenue (default: 30)"
)
parser.add_argument(
"--months", type=int, default=12, help="Months to project (default: 12)"
)
parser.add_argument("--json", action="store_true", help="Output JSON format")
args = parser.parse_args()
results = simulate(
mrr=args.mrr,
monthly_growth_pct=args.growth,
monthly_churn_pct=args.churn,
cac=args.cac,
gross_margin=args.gross_margin / 100 if args.gross_margin > 1 else args.gross_margin,
sm_spend_pct=args.sm_spend / 100 if args.sm_spend > 1 else args.sm_spend,
months=args.months,
)
if args.json:
print(json.dumps(results, indent=2))
else:
print(format_report(results))
Related skills
How it compares
Choose SaaS Metrics Coach over generic business advice skills when you already have monthly MRR and churn numbers and need stage-specific benchmark scoring rather than strategy brainstorming.
FAQ
What inputs does SaaS Metrics Coach need?
SaaS Metrics Coach uses a structured template for MRR, expansion, churn, contraction, customer counts, sales and marketing spend, and margin percentages. Partial data is accepted; blank fields are left empty and the coach works with whatever is provided.
Which ARR stages does SaaS Metrics Coach benchmark?
SaaS Metrics Coach benchmarks metrics against three ARR stages: Early (<$1M ARR), Growth ($1M–$10M), and Scale ($10M+). Results are also segmented by Enterprise, Mid-Market, SMB, or Consumer/PLG target markets.
Is Saas Metrics Coach safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.