Business Metrics Calculator
nimrodfisher/data-analytics-skills
Standard business metric calculation with industry benchmarks.
SaaS and subscription business revenue intelligence. An agent skill from LeoYeAI/openclaw-master-skills.
$ npx skills add LeoYeAI/openclaw-master-skills --skill subscription-revenue-tracker -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install LeoYeAI/openclaw-master-skills subscription-revenue-tracker --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/LeoYeAI/openclaw-master-skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/subscription-revenue-tracker .claude/skills/subscription-revenue-tracker && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "subscription-revenue-tracker" agent skill from https://github.com/LeoYeAI/openclaw-master-skills/tree/main/skills/subscription-revenue-tracker into .claude/skills/subscription-revenue-tracker/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "subscription-revenue-tracker", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/LeoYeAI/openclaw-master-skills/tree/main/skills/subscription-revenue-trackerType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add LeoYeAI/openclaw-master-skills --skill subscription-revenue-tracker -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install LeoYeAI/openclaw-master-skills subscription-revenue-tracker --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/LeoYeAI/openclaw-master-skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/subscription-revenue-tracker .agents/skills/subscription-revenue-tracker && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "subscription-revenue-tracker" agent skill from https://github.com/LeoYeAI/openclaw-master-skills/tree/main/skills/subscription-revenue-tracker into .agents/skills/subscription-revenue-tracker/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "subscription-revenue-tracker", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add LeoYeAI/openclaw-master-skills --skill subscription-revenue-tracker -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install LeoYeAI/openclaw-master-skills subscription-revenue-tracker --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/LeoYeAI/openclaw-master-skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/subscription-revenue-tracker .cursor/skills/subscription-revenue-tracker && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "subscription-revenue-tracker" agent skill from https://github.com/LeoYeAI/openclaw-master-skills/tree/main/skills/subscription-revenue-tracker into .cursor/skills/subscription-revenue-tracker/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "subscription-revenue-tracker", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/LeoYeAI/openclaw-master-skills.git --path skills/subscription-revenue-tracker--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add LeoYeAI/openclaw-master-skills --skill subscription-revenue-tracker -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install LeoYeAI/openclaw-master-skills subscription-revenue-tracker --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/LeoYeAI/openclaw-master-skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/subscription-revenue-tracker .gemini/skills/subscription-revenue-tracker && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "subscription-revenue-tracker" agent skill from https://github.com/LeoYeAI/openclaw-master-skills/tree/main/skills/subscription-revenue-tracker into .gemini/skills/subscription-revenue-tracker/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "subscription-revenue-tracker", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install LeoYeAI/openclaw-master-skills subscription-revenue-trackerInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add LeoYeAI/openclaw-master-skills --skill subscription-revenue-tracker -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/LeoYeAI/openclaw-master-skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/subscription-revenue-tracker .github/skills/subscription-revenue-tracker && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "subscription-revenue-tracker" agent skill from https://github.com/LeoYeAI/openclaw-master-skills/tree/main/skills/subscription-revenue-tracker into .github/skills/subscription-revenue-tracker/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "subscription-revenue-tracker", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add LeoYeAI/openclaw-master-skills --skill subscription-revenue-tracker -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install LeoYeAI/openclaw-master-skills subscription-revenue-tracker --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/LeoYeAI/openclaw-master-skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/subscription-revenue-tracker .opencode/skills/subscription-revenue-tracker && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "subscription-revenue-tracker" agent skill from https://github.com/LeoYeAI/openclaw-master-skills/tree/main/skills/subscription-revenue-tracker into .opencode/skills/subscription-revenue-tracker/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "subscription-revenue-tracker", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
subscription-revenue-trackerSaaS and subscription business revenue intelligence. An agent skill from LeoYeAI/openclaw-master-skills.
Subscription Revenue Tracker is an agent skill from LeoYeAI/openclaw-master-skills. SaaS and subscription business revenue intelligence. Track MRR/ARR, calculate churn rate, net revenue retention (NRR), customer lifetime value (LTV), cohort analysis, and payback periods. Connects to Stripe, Chargebee, or CSV exports for automated metric computation. Outputs investor-ready dashboards, board decks, and QBO journal entries for deferred revenue. Use when: building SaaS financial models, calculating subscription KPIs, preparing investor updates, analyzing cohort retention, or booking deferred revenue…
Its SKILL.md is about 5k tokens, which your agent loads only when the skill is triggered. The skill folder holds 1 other file (for example `_meta.json`).
It sits in Business, Finance & HR, covering OKRs and executive reporting, Fundraising and pitch decks and Product analytics. It works with Stripe. The repository describes itself as: 🧠 Curated collection of 1209+ best OpenClaw skills — weekly updated by MyClaw.ai. The licence is MIT.
6 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit e5199b5. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
jqstripecurlFrom the folder's file list and the shell code blocks in SKILL.md.
Hosts in commands or code, which the agent is likely to contact:
api.stripe.comFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Subscription Revenue Tracker loads about 5k tokens when it runs. Until then it costs about 187 tokens; SKILL.md has 423 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from LeoYeAI/openclaw-master-skills at commit e5199b5, republished under its MIT licence (© LeoYeAI). 423 words, ~5,009 tokens.
.claude/skills/subscription-revenue-tracker/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub.Track MRR/ARR, churn, NRR, cohort retention, and LTV for SaaS and subscription businesses. Produces investor-grade metrics and clean GL entries.
| Metric | Formula | Why It Matters |
|---|---|---|
| MRR | Sum of all active recurring monthly revenue | Pulse of the business |
| ARR | MRR × 12 | Annualized scale metric for investors |
| New MRR | Revenue from new customers this month | Growth engine |
| Expansion MRR | Upgrades / upsells from existing customers | Efficiency signal |
| Contraction MRR | Downgrades from existing customers | Negative signal |
| Churned MRR | Revenue lost from cancellations | Retention health |
| Net New MRR | New + Expansion − Contraction − Churned | Net growth |
| Gross Churn Rate | Churned MRR / Beginning MRR | Revenue decay rate |
| Net Revenue Retention (NRR) | (Beginning + Expansion − Contraction − Churned) / Beginning | Growth from existing base |
| LTV | ARPU / Gross Churn Rate | Customer economic value |
| CAC | Sales + Marketing Spend / New Customers | Acquisition cost |
| LTV:CAC | LTV / CAC | Unit economics health (target: >3x) |
| Payback Period | CAC / (ARPU × Gross Margin) | Months to recover acquisition cost |
# List all active subscriptions with their amounts
stripe subscriptions list \
--status=active \
--limit=100 \
--expand[]=data.items.data \
2>&1 | jq '
.data[] | {
id: .id,
customer: .customer,
status: .status,
current_period_start: (.current_period_start | strftime("%Y-%m-%d")),
mrr: (.items.data[0].price.unit_amount / 100 *
(if .items.data[0].price.recurring.interval == "year" then 1/12 else 1 end))
}
'Get MRR summary via Stripe API (no CLI):
curl "https://api.stripe.com/v1/subscriptions?status=active&limit=100&expand[]=data.items.data" \
-u sk_live_YOUR_KEY: | jq '
[.data[] |
(.items.data[0].price.unit_amount / 100) *
(if .items.data[0].price.recurring.interval == "year" then 1/12 else 1 end)
] | add
'Python: Full MRR waterfall from Stripe events:
import stripe
from datetime import datetime, timezone
from collections import defaultdict
from dateutil.relativedelta import relativedelta
stripe.api_key = "sk_live_YOUR_KEY"
def get_mrr_waterfall(year: int, month: int) -> dict:
"""
Calculate MRR waterfall for a given month.
Returns: new, expansion, contraction, churned, net_new MRR.
"""
# Period boundaries
period_start = datetime(year, month, 1, tzinfo=timezone.utc)
period_end = period_start + relativedelta(months=1)
prev_start = period_start - relativedelta(months=1)
# Get subscriptions active at start of period (denominator)
beginning_subs = _get_active_subscriptions_at(prev_start)
ending_subs = _get_active_subscriptions_at(period_end)
# Categorize by customer
beginning_customers = {s.customer: _get_mrr(s) for s in beginning_subs}
ending_customers = {s.customer: _get_mrr(s) for s in ending_subs}
new_mrr = 0.0
expansion_mrr = 0.0
contraction_mrr = 0.0
churned_mrr = 0.0
all_customers = set(beginning_customers) | set(ending_customers)
for cust_id in all_customers:
begin_val = beginning_customers.get(cust_id, 0.0)
end_val = ending_customers.get(cust_id, 0.0)
delta = end_val - begin_val
if begin_val == 0 and end_val > 0:
new_mrr += end_val
elif begin_val > 0 and end_val == 0:
churned_mrr += begin_val
elif delta > 0:
expansion_mrr += delta
elif delta < 0:
contraction_mrr += abs(delta)
beginning_mrr = sum(beginning_customers.values())
return {
"period": f"{year}-{month:02d}",
"beginning_mrr": beginning_mrr,
"new_mrr": new_mrr,
"expansion_mrr": expansion_mrr,
"contraction_mrr": contraction_mrr,
"churned_mrr": churned_mrr,
"net_new_mrr": new_mrr + expansion_mrr - contraction_mrr - churned_mrr,
"ending_mrr": beginning_mrr + new_mrr + expansion_mrr - contraction_mrr - churned_mrr,
"gross_churn_rate": churned_mrr / beginning_mrr if beginning_mrr else 0,
"nrr": (beginning_mrr + expansion_mrr - contraction_mrr - churned_mrr) / beginning_mrr if beginning_mrr else 0,
}
def _get_mrr(subscription) -> float:
"""Extract normalized monthly value from a Stripe subscription."""
item = subscription.get("items", {}).get("data", [{}])[0]
price = item.get("price", {})
amount = price.get("unit_amount", 0) / 100
qty = item.get("quantity", 1)
interval = price.get("recurring", {}).get("interval", "month")
if interval == "year":
return (amount * qty) / 12
elif interval == "week":
return (amount * qty) * 4.333
return amount * qty
def _get_active_subscriptions_at(timestamp: datetime) -> list:
"""Get subscriptions that were active at a given timestamp."""
ts = int(timestamp.timestamp())
subs = stripe.Subscription.list(
status="all",
created={"lte": ts},
limit=100
)
return [
s for s in subs.auto_paging_iter()
if s.current_period_start <= ts <= (s.canceled_at or ts + 1)
]Track retention by signup cohort — the gold standard for understanding retention quality:
import pandas as pd
import numpy as np
def build_cohort_table(subscription_events: pd.DataFrame) -> pd.DataFrame:
"""
Build monthly cohort retention table.
Input columns: customer_id, event_type (started/churned), event_month (YYYY-MM)
Output: matrix of cohort × months_since_start → retention percentage
Example output:
cohort | M+0 | M+1 | M+2 | M+3 | M+6 | M+12
2025-01 | 100% | 87% | 79% | 74% | 65% | 54%
2025-02 | 100% | 91% | 83% | 78% | -- | --
"""
# Assign cohort (month of first subscription)
first_sub = (subscription_events[subscription_events.event_type == "started"]
.groupby("customer_id")["event_month"]
.min()
.reset_index()
.rename(columns={"event_month": "cohort"}))
df = subscription_events.merge(first_sub, on="customer_id")
df["cohort"] = pd.to_datetime(df["cohort"])
df["event_month"] = pd.to_datetime(df["event_month"])
df["months_since_start"] = (
(df["event_month"].dt.year - df["cohort"].dt.year) * 12 +
(df["event_month"].dt.month - df["cohort"].dt.month)
)
# Active customers per cohort per month
active = (df[df.event_type != "churned"]
.groupby(["cohort", "months_since_start"])["customer_id"]
.nunique()
.reset_index()
.rename(columns={"customer_id": "active_customers"}))
cohort_table = active.pivot(
index="cohort",
columns="months_since_start",
values="active_customers"
)
# Normalize to cohort size (M+0 = 100%)
cohort_sizes = cohort_table[0]
retention_table = cohort_table.divide(cohort_sizes, axis=0) * 100
return retention_table.round(1)
def average_retention_curve(cohort_table: pd.DataFrame, min_cohorts: int = 3) -> pd.Series:
"""
Compute average retention curve across cohorts with enough data.
Used for LTV projection.
"""
# Only include cohorts with at least min_cohorts data points per period
valid_cols = cohort_table.columns[cohort_table.notna().sum() >= min_cohorts]
return cohort_table[valid_cols].mean()def calculate_ltv(arpu: float, gross_margin: float, monthly_churn_rate: float) -> dict:
"""
Calculate Customer Lifetime Value and payback metrics.
Args:
arpu: Average Revenue Per User per month
gross_margin: Gross margin % (0.0-1.0)
monthly_churn_rate: Monthly revenue churn rate (0.0-1.0)
Returns:
LTV, gross profit LTV, and key benchmarks
"""
if monthly_churn_rate <= 0:
raise ValueError("Churn rate must be > 0 for LTV calculation")
avg_customer_lifetime_months = 1 / monthly_churn_rate
ltv_revenue = arpu * avg_customer_lifetime_months
ltv_gross_profit = ltv_revenue * gross_margin
return {
"arpu_monthly": arpu,
"arpu_annual": arpu * 12,
"monthly_churn_rate": monthly_churn_rate,
"avg_lifetime_months": avg_customer_lifetime_months,
"ltv_revenue": ltv_revenue,
"ltv_gross_profit": ltv_gross_profit,
"benchmarks": {
"saas_target_ltv_cac": ">3x",
"saas_target_payback": "<12 months",
"saas_target_nrr": ">100%",
}
}
def payback_period(cac: float, arpu: float, gross_margin: float) -> dict:
"""
Calculate CAC payback period in months.
Healthy SaaS: <12 months
Great SaaS: <6 months
Struggling: >18 months
"""
monthly_gross_profit_per_customer = arpu * gross_margin
if monthly_gross_profit_per_customer <= 0:
return {"payback_months": float("inf"), "status": "never — negative gross margin"}
months = cac / monthly_gross_profit_per_customer
if months < 6:
status = "excellent"
elif months < 12:
status = "healthy"
elif months < 18:
status = "acceptable"
else:
status = "concerning"
return {
"cac": cac,
"arpu_monthly": arpu,
"gross_margin": gross_margin,
"payback_months": round(months, 1),
"payback_status": status,
}For businesses without Stripe — import from any billing system:
# Expected CSV format:
# customer_id, plan_name, mrr, start_date, end_date (blank if active), currency
import pandas as pd
from datetime import datetime
def load_subscriptions_from_csv(path: str, as_of_date: str = None) -> pd.DataFrame:
"""
Load subscription data from a CSV export.
Handles Chargebee, Recurly, Zuora, or manual exports.
Required columns: customer_id, mrr, start_date
Optional: end_date, plan_name, currency
"""
df = pd.read_csv(path)
df["start_date"] = pd.to_datetime(df["start_date"])
if "end_date" in df.columns:
df["end_date"] = pd.to_datetime(df["end_date"], errors="coerce")
if as_of_date:
cutoff = pd.Timestamp(as_of_date)
# Active = started before cutoff AND (not ended OR ended after cutoff)
df = df[
(df["start_date"] <= cutoff) &
(df.get("end_date", pd.NaT).isna() | (df.get("end_date", pd.NaT) > cutoff))
]
# Standardize MRR (handle annual → monthly)
if "billing_interval" in df.columns:
df.loc[df.billing_interval == "annual", "mrr"] /= 12
return df
def compute_metrics_from_csv(csv_path: str, period: str) -> dict:
"""
Full metrics computation from CSV for a given YYYY-MM period.
"""
year, month = map(int, period.split("-"))
# Current and previous month active subs
period_end = datetime(year, month, 28) # safe month-end
prev_period_end = datetime(year, month - 1 if month > 1 else 12, 28)
current = load_subscriptions_from_csv(csv_path, period_end.strftime("%Y-%m-%d"))
previous = load_subscriptions_from_csv(csv_path, prev_period_end.strftime("%Y-%m-%d"))
curr_by_cust = current.groupby("customer_id")["mrr"].sum()
prev_by_cust = previous.groupby("customer_id")["mrr"].sum()
new_customers = curr_by_cust.index.difference(prev_by_cust.index)
churned_customers = prev_by_cust.index.difference(curr_by_cust.index)
existing = curr_by_cust.index.intersection(prev_by_cust.index)
expansion = (curr_by_cust[existing] - prev_by_cust[existing]).clip(lower=0).sum()
contraction = (prev_by_cust[existing] - curr_by_cust[existing]).clip(lower=0).sum()
beginning_mrr = prev_by_cust.sum()
return {
"period": period,
"customer_count": len(curr_by_cust),
"mrr": curr_by_cust.sum(),
"arr": curr_by_cust.sum() * 12,
"beginning_mrr": beginning_mrr,
"new_mrr": curr_by_cust[new_customers].sum(),
"expansion_mrr": expansion,
"contraction_mrr": contraction,
"churned_mrr": prev_by_cust[churned_customers].sum(),
"net_new_mrr": curr_by_cust.sum() - beginning_mrr,
"gross_churn_rate": prev_by_cust[churned_customers].sum() / beginning_mrr if beginning_mrr else 0,
"nrr": (beginning_mrr + expansion - contraction - prev_by_cust[churned_customers].sum()) / beginning_mrr if beginning_mrr else 0,
"arpu": curr_by_cust.mean(),
}Subscription revenue must be recognized over the service period (ASC 606 / IFRS 15):
from datetime import date
from dateutil.relativedelta import relativedelta
def deferred_revenue_schedule(
invoice_date: date,
invoice_amount: float,
service_start: date,
service_end: date,
description: str
) -> list[dict]:
"""
Generate monthly revenue recognition journal entries for an annual subscription.
At invoice: Debit A/R, Credit Deferred Revenue
Monthly: Debit Deferred Revenue, Credit Revenue
Returns list of journal entries ready for QBO import.
"""
total_days = (service_end - service_start).days
entries = []
# Initial: recognize deferred revenue liability
entries.append({
"date": invoice_date.isoformat(),
"type": "invoice_booking",
"description": f"Book deferred revenue — {description}",
"debit_account": "Accounts Receivable",
"credit_account": "Deferred Revenue",
"amount": invoice_amount,
})
# Monthly recognition
current_date = date(service_start.year, service_start.month, 1)
while current_date <= service_end:
# Days in this period
period_end = min(
date(current_date.year, current_date.month + 1, 1) - relativedelta(days=1),
service_end
)
period_days = (period_end - max(current_date, service_start)).days + 1
period_revenue = invoice_amount * (period_days / total_days)
entries.append({
"date": current_date.isoformat(),
"type": "revenue_recognition",
"description": f"Revenue recognition — {description} ({current_date.strftime('%b %Y')})",
"debit_account": "Deferred Revenue",
"credit_account": "Subscription Revenue",
"amount": round(period_revenue, 2),
"period_days": period_days,
})
current_date = date(current_date.year, current_date.month, 1) + relativedelta(months=1)
return entries
# Example: $12,000 annual subscription
entries = deferred_revenue_schedule(
invoice_date=date(2026, 1, 1),
invoice_amount=12000.00,
service_start=date(2026, 1, 1),
service_end=date(2026, 12, 31),
description="Acme Corp — Enterprise Plan"
)
# → 13 entries: 1 booking + 12 monthly recognition of $1,000 eachdef generate_investor_summary(metrics_history: list[dict]) -> dict:
"""
Generate board/investor MRR summary from 12 months of metrics.
Returns formatted dict suitable for pitch deck tables or Sheets export.
"""
if len(metrics_history) < 2:
raise ValueError("Need at least 2 months of data for growth calculations")
latest = metrics_history[-1]
prev_month = metrics_history[-2]
twelve_months_ago = metrics_history[0] if len(metrics_history) >= 12 else None
mom_growth = (latest["mrr"] - prev_month["mrr"]) / prev_month["mrr"] if prev_month["mrr"] else 0
yoy_growth = None
if twelve_months_ago and twelve_months_ago["mrr"]:
yoy_growth = (latest["mrr"] - twelve_months_ago["mrr"]) / twelve_months_ago["mrr"]
# Rule of 40: YoY Revenue Growth % + EBITDA Margin % >= 40 is healthy SaaS
# (requires EBITDA margin input from financial model)
return {
"as_of": latest["period"],
"mrr": f"${latest['mrr']:,.0f}",
"arr": f"${latest['arr']:,.0f}",
"mrr_mom_growth": f"{mom_growth:.1%}",
"mrr_yoy_growth": f"{yoy_growth:.1%}" if yoy_growth is not None else "N/A",
"nrr": f"{latest['nrr']:.1%}",
"gross_churn": f"{latest['gross_churn_rate']:.2%}",
"customer_count": latest["customer_count"],
"arpu": f"${latest['arpu']:,.0f}",
"new_mrr": f"${latest['new_mrr']:,.0f}",
"expansion_mrr": f"${latest['expansion_mrr']:,.0f}",
"churned_mrr": f"${latest['churned_mrr']:,.0f}",
"net_new_mrr": f"${latest['net_new_mrr']:,.0f}",
"benchmarks": {
"nrr_status": "elite" if latest["nrr"] > 1.20 else "strong" if latest["nrr"] > 1.10 else "healthy" if latest["nrr"] > 1.00 else "concerning",
"churn_status": "excellent" if latest["gross_churn_rate"] < 0.01 else "healthy" if latest["gross_churn_rate"] < 0.02 else "high",
}
}| Metric | Best in Class | Healthy | Watch |
|---|---|---|---|
| MoM MRR Growth | >15% | 5-15% | <5% |
| Gross Churn (monthly) | <0.5% | 0.5-2% | >2% |
| NRR | >120% | 100-120% | <100% |
| LTV:CAC | >5x | 3-5x | <3x |
| CAC Payback | <6 mo | 6-12 mo | >12 mo |
| Rule of 40 | >40 | 20-40 | <20 |
© LeoYeAI, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 1 other file in skills/subscription-revenue-tracker of LeoYeAI/openclaw-master-skills.
Open the folder on GitHubat commit e5199b5
Subscription Revenue Tracker next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Subscription Revenue Tracker this skillLeoYeAI/openclaw-master-skills | 2.2k | — | ~5k | Automated safety check: Pass | MIT | |
| Business Metrics Calculatornimrodfisher/data-analytics-skills | 470 | — | ~668 | Automated safety check: Pass | MIT | |
| Financial Comparison Glossary Ignacio Adrian Lererlawve-ai/awesome-legal-skills | 847 | — | ~1.1k | Automated safety check: Pass | Custom licence | |
| Officecli Data DashboardFerroxLabs/wayland | 608 | 4 repos | ~9.2k | Automated safety check: Pass | AGPL-3.0 | |
| Analytics Strategyrampstackco/claude-skills | 945 | — | ~2.4k | Automated safety check: Pass | MIT | |
| Money Financeiamzifei/show-me-the-money | 1k | — | ~2.2k | Automated safety check: Pass | Custom licence |
nimrodfisher/data-analytics-skills
Standard business metric calculation with industry benchmarks.
lawve-ai/awesome-legal-skills
A skill your agent uses when a calculator, financial model, investor memo, due diligence report, risk review, dashboard, or client-facing explanation needs clear distinctions between accounting and…
FerroxLabs/wayland
A skill your agent uses to build a multi-element Excel dashboard - Dashboard sheet on open, multiple formula-driven KPI cards, multiple charts, sparklines, and conditional formatting - from CSV or…
rampstackco/claude-skills
Design measurement frameworks including event taxonomy, KPI hierarchy, dashboard architecture, attribution models, and analytics implementation strategy.
iamzifei/show-me-the-money
Financial tracking, revenue analytics, expense management, and pricing optimization.
bex-co/beancount-io
Import a bank or card CSV, OFX/QFX, or QIF export into an existing Beancount ledger.
LeoYeAI/openclaw-master-skills
Manages pipelines on a DevOps quality and efficiency platform through its OpenAPI: list workspaces and templates, create, update, run and cancel pipelines, and read run records.
LeoYeAI/openclaw-master-skills
Patches OpenClaw's Feishu extension so an edited document triggers an isolated agent session that reads the doc and replies inline, turning it into a live chat space.
LeoYeAI/openclaw-master-skills
Multi-context memory management system for OpenClaw agents with group-isolated storage, global shared memory, workspace organization, and group-specific skills isolation.
LeoYeAI/openclaw-master-skills
Runs a brand's AI-search visibility work end to end: diagnosing how AI platforms represent it, repositioning it, producing AI-optimized content and monitoring ongoing mentions.
LeoYeAI/openclaw-master-skills
Installs and authenticates the gws CLI, then automates Gmail, Drive, Sheets, Calendar, Docs, Chat and Tasks with ready-made recipes, persona bundles and security audits.
LeoYeAI/openclaw-master-skills
Runs four advisor roles, a fitness coach, nutritionist, data analyst and TCM practitioner, to build a health profile and track workouts, diet and wellness over time.
Works with
Categories
SaaS and subscription business revenue intelligence. An agent skill from LeoYeAI/openclaw-master-skills. Subscription Revenue Tracker is an agent skill from LeoYeAI/openclaw-master-skills. SaaS and subscription business revenue intelligence.
Subscription Revenue Tracker fits situations like: : building SaaS financial models; calculating subscription KPIs; preparing investor updates; analyzing cohort retention.
Run `npx skills add LeoYeAI/openclaw-master-skills --skill subscription-revenue-tracker -a claude-code`. Or copy the skill folder (skills/subscription-revenue-tracker in LeoYeAI/openclaw-master-skills) into .claude/skills/subscription-revenue-tracker in your project. Claude Code loads it when a task matches its description.
Run `npx skills add LeoYeAI/openclaw-master-skills --skill subscription-revenue-tracker -a codex`. Or copy the skill folder (skills/subscription-revenue-tracker in LeoYeAI/openclaw-master-skills) into .agents/skills/subscription-revenue-tracker in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add LeoYeAI/openclaw-master-skills --skill subscription-revenue-tracker -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/subscription-revenue-tracker, .gemini/skills/subscription-revenue-tracker, .github/skills/subscription-revenue-tracker and .opencode/skills/subscription-revenue-tracker in your project.
Going by SKILL.md and its folder, Subscription Revenue Tracker needs the command-line tools its instructions call (jq, stripe and curl). Our summary lists: Python 3.
SKILL.md names 1 domain. In commands or code: api.stripe.com; the agent is likely to contact it when it follows the instructions. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Subscription Revenue Tracker is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 5k tokens (SKILL.md is roughly 20k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Subscription Revenue Tracker: Business Metrics Calculator (nimrodfisher/data-analytics-skills, 470 stars), Financial Comparison Glossary Ignacio Adrian Lerer (lawve-ai/awesome-legal-skills, 847 stars), Officecli Data Dashboard (FerroxLabs/wayland, 608 stars) and Analytics Strategy (rampstackco/claude-skills, 945 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
LeoYeAI (a GitHub user) maintains it in LeoYeAI/openclaw-master-skills, which has 2,161 GitHub stars. The repository holds 1,235 skills in this directory. The repository was last updated on July 20, 2026.
Source: LeoYeAI/openclaw-master-skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.