profitable 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -1
- data/README.md +244 -13
- data/app/controllers/profitable/dashboard_controller.rb +1 -0
- data/app/views/profitable/dashboard/index.html.erb +4 -0
- data/context7.json +4 -0
- data/lib/profitable/metrics.rb +664 -0
- data/lib/profitable/mrr_calculator.rb +23 -2
- data/lib/profitable/processors/braintree_processor.rb +3 -1
- data/lib/profitable/processors/paddle_billing_processor.rb +2 -1
- data/lib/profitable/processors/paddle_classic_processor.rb +2 -1
- data/lib/profitable/processors/stripe_processor.rb +4 -0
- data/lib/profitable/version.rb +1 -1
- data/lib/profitable.rb +1 -517
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: db964563468a5d97aba1c885e241da6c9938c01a4e5bc491448effdd492fcb82
|
|
4
|
+
data.tar.gz: d8cc7bc12503aaabf2572841632d3d83c629258aa8b7727233a72c6f48f2f5f1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bc7391cee28cb2e0b11c7e29583fb2651e4a35e34199f04c142322d2b7dad436f42b1523c3299b6f8a38988b376a753ef6ef7f5c5367213a262ea754b01148ef
|
|
7
|
+
data.tar.gz: '08d67a50ca5b2b9f20202b6fc77802e2abd8249aca8a670377080c2c37759c5175b4d886f8a0ebd7031eccf61e4eab730606491fd88d483008f46a0ccefb8da0'
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# `profitable`
|
|
2
2
|
|
|
3
|
+
## [0.5.0] - 2026-03-19
|
|
4
|
+
- Add `Profitable.ttm_revenue` for trailing twelve-month revenue
|
|
5
|
+
- Add `Profitable.ttm` as a founder-friendly alias for `ttm_revenue`
|
|
6
|
+
- Add `Profitable.revenue_run_rate`, `estimated_arr_valuation`, `estimated_ttm_revenue_valuation`, and `estimated_revenue_run_rate_valuation`
|
|
7
|
+
- Make revenue metrics net of refunds when `amount_refunded` is present
|
|
8
|
+
- Make subscriber and MRR metrics distinguish between current billable subscriptions and historical period events
|
|
9
|
+
- Count `new_customers` from first monetization date rather than signup date
|
|
10
|
+
- Count `new_subscribers` / `new_mrr` from when a subscription becomes billable, not when a free trial starts
|
|
11
|
+
- Handle additional Pay status variants like `on_trial`, `cancelled`, and `deleted`
|
|
12
|
+
- Keep grace-period subscriptions billable until `ends_at`
|
|
13
|
+
- Exclude metered Stripe items from fixed run-rate MRR calculations
|
|
14
|
+
- Surface TTM revenue in the built-in dashboard
|
|
15
|
+
- Extract shared metric logic to `lib/profitable/metrics.rb` for cleaner architecture
|
|
16
|
+
|
|
3
17
|
## [0.4.0] - 2026-02-10
|
|
4
18
|
- Add monthly summary (12mo) and daily summary (30d) tables to dashboard
|
|
5
19
|
- Add `period_data` method for efficient batch computation of period metrics
|
|
@@ -32,4 +46,4 @@
|
|
|
32
46
|
|
|
33
47
|
## [0.1.0] - 2024-08-29
|
|
34
48
|
|
|
35
|
-
- Initial test release (not production ready)
|
|
49
|
+
- Initial test release (not production ready)
|
data/README.md
CHANGED
|
@@ -28,6 +28,24 @@ Then run `bundle install`.
|
|
|
28
28
|
|
|
29
29
|
Provided you have a valid [`pay`](https://github.com/pay-rails/pay) installation (`Pay::Customer`, `Pay::Subscription`, `Pay::Charge`, etc.) everything is already set up and you can just start using [`Profitable` methods](#main-profitable-methods) right away.
|
|
30
30
|
|
|
31
|
+
Current MRR processor coverage is verified for `stripe`, `braintree`, `paddle_billing`, and `paddle_classic`.
|
|
32
|
+
|
|
33
|
+
For Stripe, metered subscription items are intentionally excluded from fixed run-rate metrics like `mrr`, `arr`, `new_mrr`, and `churned_mrr`.
|
|
34
|
+
|
|
35
|
+
> [!IMPORTANT]
|
|
36
|
+
> `profitable` does **not** yet normalize MRR for every processor that `pay` supports.
|
|
37
|
+
> If a subscription comes from an unsupported processor such as `lemon_squeezy`, it will currently contribute `0` to processor-adapter-dependent metrics until an adapter is added.
|
|
38
|
+
>
|
|
39
|
+
> Verified processor-adapter coverage today:
|
|
40
|
+
> - `stripe`
|
|
41
|
+
> - `braintree`
|
|
42
|
+
> - `paddle_billing`
|
|
43
|
+
> - `paddle_classic`
|
|
44
|
+
>
|
|
45
|
+
> Metrics that depend on processor-specific subscription amount parsing include `mrr`, `arr`, `new_mrr`, `churned_mrr`, `mrr_growth`, `mrr_growth_rate`, `lifetime_value`, `time_to_next_mrr_milestone`, and MRR-derived fields in summaries.
|
|
46
|
+
>
|
|
47
|
+
> Metrics based primarily on `Pay::Charge` and generic subscription lifecycle fields are much more portable across processors, including `all_time_revenue`, `revenue_in_period`, `ttm_revenue`, `revenue_run_rate`, customer counts, subscriber counts, and churn calculations.
|
|
48
|
+
|
|
31
49
|
## Mount the `/profitable` dashboard
|
|
32
50
|
|
|
33
51
|
`profitable` also provides a simple dashboard to see your main business metrics.
|
|
@@ -52,25 +70,31 @@ All methods return numbers that can be converted to a nicely-formatted, human-re
|
|
|
52
70
|
|
|
53
71
|
### Revenue metrics
|
|
54
72
|
|
|
55
|
-
- `Profitable.mrr`: Monthly Recurring Revenue (MRR)
|
|
56
|
-
- `Profitable.arr`: Annual Recurring Revenue (ARR)
|
|
57
|
-
- `Profitable.
|
|
58
|
-
- `Profitable.
|
|
73
|
+
- `Profitable.mrr`: Monthly Recurring Revenue (MRR) from subscriptions that are billable right now
|
|
74
|
+
- `Profitable.arr`: Annual Recurring Revenue (ARR), calculated as current `mrr * 12`, not trailing revenue
|
|
75
|
+
- `Profitable.ttm`: Founder-friendly shorthand alias for `ttm_revenue`
|
|
76
|
+
- `Profitable.ttm_revenue`: Trailing twelve-month revenue, net of refunds when `amount_refunded` is present
|
|
77
|
+
- `Profitable.revenue_run_rate(in_the_last: 30.days)`: Recent revenue annualized (useful for TrustMRR-style revenue multiples)
|
|
78
|
+
- `Profitable.all_time_revenue`: Net revenue since launch
|
|
79
|
+
- `Profitable.revenue_in_period(in_the_last: 30.days)`: Net revenue (recurring and non-recurring) in the specified period
|
|
59
80
|
- `Profitable.recurring_revenue_in_period(in_the_last: 30.days)`: Only recurring revenue in the specified period
|
|
60
81
|
- `Profitable.recurring_revenue_percentage(in_the_last: 30.days)`: Percentage of revenue that is recurring in the specified period
|
|
61
|
-
- `Profitable.new_mrr(in_the_last: 30.days)`:
|
|
82
|
+
- `Profitable.new_mrr(in_the_last: 30.days)`: Full MRR from subscriptions that first became billable in the specified period
|
|
62
83
|
- `Profitable.churned_mrr(in_the_last: 30.days)`: MRR lost due to churn in the specified period
|
|
63
84
|
- `Profitable.average_revenue_per_customer`: Average revenue per customer (ARPC)
|
|
64
85
|
- `Profitable.lifetime_value`: Estimated customer lifetime value (LTV)
|
|
65
|
-
- `Profitable.estimated_valuation(at: "3x")`:
|
|
86
|
+
- `Profitable.estimated_valuation(at: "3x")`: ARR-based valuation heuristic
|
|
87
|
+
- `Profitable.estimated_arr_valuation(at: "3x")`: Explicit ARR-based valuation heuristic
|
|
88
|
+
- `Profitable.estimated_ttm_revenue_valuation(at: "2x")`: TTM revenue-based valuation heuristic
|
|
89
|
+
- `Profitable.estimated_revenue_run_rate_valuation(at: "2x", in_the_last: 30.days)`: Recent revenue run-rate valuation heuristic
|
|
66
90
|
|
|
67
91
|
### Customer metrics
|
|
68
92
|
|
|
69
|
-
- `Profitable.total_customers`: Total number of customers who have ever
|
|
70
|
-
- `Profitable.total_subscribers`: Total number of customers who have ever
|
|
71
|
-
- `Profitable.active_subscribers`: Number of customers with
|
|
72
|
-
- `Profitable.new_customers(in_the_last: 30.days)`: Number of
|
|
73
|
-
- `Profitable.new_subscribers(in_the_last: 30.days)`: Number of
|
|
93
|
+
- `Profitable.total_customers`: Total number of customers who have ever monetized through a paid charge or a paid subscription state
|
|
94
|
+
- `Profitable.total_subscribers`: Total number of customers who have ever reached a paid subscription state (trial-only subscriptions do not count)
|
|
95
|
+
- `Profitable.active_subscribers`: Number of customers with subscriptions that are billable right now
|
|
96
|
+
- `Profitable.new_customers(in_the_last: 30.days)`: Number of first-time customers added in the period based on first monetization date, not signup date
|
|
97
|
+
- `Profitable.new_subscribers(in_the_last: 30.days)`: Number of customers whose subscriptions first became billable in the specified period
|
|
74
98
|
- `Profitable.churned_customers(in_the_last: 30.days)`: Number of customers who churned in the specified period
|
|
75
99
|
|
|
76
100
|
### Other metrics
|
|
@@ -104,11 +128,24 @@ Profitable.churn(in_the_last: 3.months).to_readable # => "12%"
|
|
|
104
128
|
Profitable.new_mrr(in_the_last: 24.hours).to_readable(2) # => "$123.45"
|
|
105
129
|
|
|
106
130
|
# Get the estimated valuation at 5x ARR (defaults to 3x if no multiple is specified)
|
|
107
|
-
Profitable.
|
|
131
|
+
Profitable.estimated_arr_valuation(multiple: 5).to_readable # => "$500,000"
|
|
132
|
+
|
|
133
|
+
# Get trailing twelve-month revenue
|
|
134
|
+
Profitable.ttm_revenue.to_readable # => "$123,456"
|
|
135
|
+
|
|
136
|
+
# Founder-friendly shorthand for trailing twelve-month revenue
|
|
137
|
+
Profitable.ttm.to_readable # => "$123,456"
|
|
108
138
|
|
|
109
|
-
#
|
|
139
|
+
# Get recent revenue annualized (useful for TrustMRR-style revenue multiples)
|
|
140
|
+
Profitable.revenue_run_rate(in_the_last: 30.days).to_readable # => "$96,000"
|
|
141
|
+
|
|
142
|
+
# `estimated_valuation` remains as a backwards-compatible alias of `estimated_arr_valuation`
|
|
110
143
|
Profitable.estimated_valuation(at: "4.5x").to_readable # => "$450,000"
|
|
111
144
|
|
|
145
|
+
# Be explicit about the denominator when comparing marketplace comps
|
|
146
|
+
Profitable.estimated_ttm_revenue_valuation(2).to_readable
|
|
147
|
+
Profitable.estimated_revenue_run_rate_valuation(2.7, in_the_last: 30.days).to_readable
|
|
148
|
+
|
|
112
149
|
# Get the time to next MRR milestone
|
|
113
150
|
Profitable.time_to_next_mrr_milestone.to_readable # => "26 days left to $10,000 MRR"
|
|
114
151
|
```
|
|
@@ -129,11 +166,205 @@ For more precise calculations, you can access the raw numeric value:
|
|
|
129
166
|
Profitable.mrr # => 123456
|
|
130
167
|
```
|
|
131
168
|
|
|
169
|
+
Revenue methods are net of refunds when `amount_refunded` is present on `pay_charges`.
|
|
170
|
+
|
|
132
171
|
### Notes on specific metrics
|
|
133
172
|
|
|
134
173
|
- `mrr_growth_rate`: This calculation compares the MRR at the start and end of the specified period. It assumes a linear growth rate over the period, which may not reflect short-term fluctuations. For more accurate results, consider using shorter periods or implementing a more sophisticated growth calculation method if needed.
|
|
135
174
|
- `time_to_next_mrr_milestone`: This estimation is based on the current MRR and the recent growth rate. It assumes a constant growth rate, which may not reflect real-world conditions. The calculation may be inaccurate for very new businesses or those with irregular growth patterns.
|
|
136
175
|
|
|
176
|
+
## Metric guide: TTM, Revenue, Profit, ARR, and MRR
|
|
177
|
+
|
|
178
|
+
`profitable` exposes both standard recurring revenue metrics (`MRR`, `ARR`) and trailing actuals (`TTM revenue`) on purpose.
|
|
179
|
+
|
|
180
|
+
These metrics are related, but they are not interchangeable:
|
|
181
|
+
|
|
182
|
+
| Metric | What it means | Best for | What it is **not** |
|
|
183
|
+
| --- | --- | --- | --- |
|
|
184
|
+
| `MRR` | Monthly Recurring Revenue from subscriptions that are billable right now | Operating cadence, near-term momentum, tracking upgrades/downgrades | It's **not** monthly cash collected from all sources |
|
|
185
|
+
| `ARR` | Annual Recurring Revenue, calculated as the current recurring base annualized | Forecasting recurring scale, board/investor reporting, recurring-revenue quality | It's **not** a historical last-12-month revenue |
|
|
186
|
+
| `MRR * 12` | Simple annualization of the current monthly recurring base | Fast ARR approximation when the base is normalized monthly | It's **not** TTM revenue or TTM profit |
|
|
187
|
+
| `TTM revenue` | Actual revenue collected over the last 12 months | Buyer-facing historical actuals, smoothing seasonality, sanity-checking ARR | It's **not** a forward recurring run-rate |
|
|
188
|
+
| `TTM profit` | Actual profit over the last 12 months | Small bootstrapped SaaS exits, ROI-minded buyers, earnings-based multiples | It's **not** something `profitable` can derive from `pay` alone |
|
|
189
|
+
|
|
190
|
+
### The distinction
|
|
191
|
+
|
|
192
|
+
- `ARR` is a run-rate metric. Stripe describes it as revenue you "expect to earn in a year" and notes that `ARR = MRR × 12`.
|
|
193
|
+
- `TTM` is a trailing metric. CFI defines it as the "most recent 12-month period" and uses it for reported actuals such as revenue and EBITDA.
|
|
194
|
+
- `TTM revenue` tells you what customers actually paid over the last year.
|
|
195
|
+
- `TTM profit` tells you what the business actually kept after costs. This is often what smaller acquisition buyers care about most, but it requires cost data outside `pay`.
|
|
196
|
+
- In acquire-style market reports, `TTM` can refer to **both** `TTM profit` and `TTM revenue` depending on the chart. The denominator must always be stated explicitly.
|
|
197
|
+
- In `profitable`, the shorthand method `ttm` is defined to mean `ttm_revenue` because the gem does not yet model costs or profit.
|
|
198
|
+
|
|
199
|
+
In other words:
|
|
200
|
+
|
|
201
|
+
- `ARR` answers: "What is my current recurring run-rate? What do I expect to earn in a year?"
|
|
202
|
+
- `TTM revenue` answers: "What did I actually collect over the last year?"
|
|
203
|
+
|
|
204
|
+
### What `profitable` calculates
|
|
205
|
+
|
|
206
|
+
- `Profitable.mrr`: Monthly Recurring Revenue (MRR) from subscriptions that are billable right now
|
|
207
|
+
- `Profitable.arr`: Annual Recurring Revenue (ARR), calculated from current MRR
|
|
208
|
+
- `Profitable.ttm`: shorthand alias for `ttm_revenue`
|
|
209
|
+
- `Profitable.ttm_revenue`: trailing 12-month revenue, net of refunds when `amount_refunded` is present
|
|
210
|
+
- `Profitable.revenue_run_rate`: recent revenue annualized to a yearly run-rate
|
|
211
|
+
- `Profitable.estimated_valuation`: ARR-multiple heuristic
|
|
212
|
+
- `Profitable.estimated_ttm_revenue_valuation`: TTM revenue heuristic
|
|
213
|
+
- `Profitable.estimated_revenue_run_rate_valuation`: recent revenue run-rate heuristic
|
|
214
|
+
|
|
215
|
+
`profitable` does **not** calculate `TTM profit`, because payroll, contractor spend, hosting, support, software tools, taxes, and owner add-backs do not live inside `pay`.
|
|
216
|
+
|
|
217
|
+
### Which metric matters in which situation?
|
|
218
|
+
|
|
219
|
+
- If you're operating the business week to week: `MRR` is usually the best pulse metric.
|
|
220
|
+
- If you want to understand your current subscription run-rate: `ARR` is the right metric.
|
|
221
|
+
- If you're preparing buyer materials for a bootstrapped SaaS exit: add `TTM revenue` and your own `TTM profit`.
|
|
222
|
+
- If your business has meaningful one-time revenue, services, setup fees, or seasonal swings: `TTM revenue` matters more than `ARR`.
|
|
223
|
+
- If you are speaking to serious SaaS buyers about revenue quality: pair `ARR` with churn, growth, concentration, and margins.
|
|
224
|
+
|
|
225
|
+
### What the market says
|
|
226
|
+
|
|
227
|
+
These are short excerpts from current market and finance sources, followed by why they matter for `profitable`.
|
|
228
|
+
|
|
229
|
+
- [Acquire.com Biannual Multiples Report (Jan 2026)](https://blog.acquire.com/acquire-com-biannual-acquisition-multiples-report-jan-2026/): "anchor valuation on profit"
|
|
230
|
+
Acquire says the January 2026 report is focused "entirely on profit multiples," which is highly relevant for smaller bootstrapped SaaS exits.
|
|
231
|
+
In the same report, some visual breakdowns segment businesses by `TTM revenue` bands, so it is important not to assume one bare `TTM` label means the same thing everywhere.
|
|
232
|
+
|
|
233
|
+
- [Acquire.com Biannual Multiples Report, January 2024](https://blog.acquire.com/wp-content/uploads/2024/01/Acquire-Biannual-Multiples-Report-Jan-2024.pdf): "4.3x TTM profit"
|
|
234
|
+
The earlier report gives a concrete historical benchmark for how these profit multiples looked on the marketplace.
|
|
235
|
+
|
|
236
|
+
- [Acquire.com 2025 webinar recap](https://blog.acquire.com/the-secrets-behind-2024s-biggest-exits-webinar-recap/): "$100k-$1M in TTM revenue"
|
|
237
|
+
Acquire says that cohort averaged `4.35x`, which is a useful live-market anchor for micro-SaaS exits.
|
|
238
|
+
|
|
239
|
+
- [Acquire.com SaaS valuation multiples guide](https://blog.acquire.com/saas-valuation-multiples/): "5x to 15x ARR"
|
|
240
|
+
Stronger recurring SaaS businesses are also routinely discussed in ARR-multiple terms, especially when growth and retention are strong.
|
|
241
|
+
|
|
242
|
+
- [Stripe on ARR](https://stripe.com/us/resources/more/acv-vs-arr-what-each-metric-really-means-and-when-they-matter): "ARR = £50,000 x 12 = £600,000"
|
|
243
|
+
This is the clearest shorthand for why `ARR` is a run-rate, not a trailing actual.
|
|
244
|
+
|
|
245
|
+
- [CFI on TTM](https://corporatefinanceinstitute.com/resources/valuation/railing-twelve-months-ttm-definition/): "most recent 12-month period"
|
|
246
|
+
This is why `ttm_revenue` belongs beside `arr`: it measures trailing actuals, not a projection.
|
|
247
|
+
|
|
248
|
+
- [Quiet Light on selling SaaS](https://quietlight.com/sell-your-saas-business-for-the-best-price/): "EBITDA or SDE"
|
|
249
|
+
Quiet Light explicitly says smaller SaaS businesses are often valued on earnings, not revenue, which is why `TTM profit` matters.
|
|
250
|
+
|
|
251
|
+
- [Quiet Light on larger SaaS](https://quietlight.com/sell-your-saas-business-for-the-best-price/): "ARR of $1M or more"
|
|
252
|
+
The same source says larger SaaS businesses may qualify for revenue multiples, which is why `ARR` becomes more important as the business scales.
|
|
253
|
+
|
|
254
|
+
- [Software Equity Group, 3Q25 SaaS M&A](https://softwareequity.com/blog/saas-ma-deal-volume-and-valuations): "5.4x"
|
|
255
|
+
SEG reported average SaaS M&A valuations of `5.4x` revenue in 3Q25, which is useful context for larger, more institutional software transactions.
|
|
256
|
+
|
|
257
|
+
- [TrustMRR live listing example](https://trustmrr.com/startup/appalchemy): "$164,819 TTM revenue"
|
|
258
|
+
Live marketplaces increasingly show `TTM revenue`, `TTM profit`, and `ARR` side by side, which matches how buyers actually compare deals.
|
|
259
|
+
|
|
260
|
+
- [TrustMRR FAQ](https://trustmrr.com/faq): "asking price divided by annualized revenue"
|
|
261
|
+
TrustMRR explicitly defines its marketplace multiple as asking price divided by `last 30 days revenue × 12`, so its multiple is a revenue run-rate multiple, not an ARR multiple.
|
|
262
|
+
|
|
263
|
+
- [TrustMRR FAQ](https://trustmrr.com/faq): "Only aggregate revenue metrics"
|
|
264
|
+
TrustMRR says it only pulls revenue-level aggregates from payment providers, which is another reason its native multiple is revenue-based rather than profit-based.
|
|
265
|
+
|
|
266
|
+
- [TrustMRR FAQ](https://trustmrr.com/faq): "profit margin for the last 30 days"
|
|
267
|
+
TrustMRR asks sellers to provide profit margin separately when listing for sale, which reinforces that profit-based heuristics need cost inputs outside the payment provider.
|
|
268
|
+
|
|
269
|
+
### How to use these metrics responsibly
|
|
270
|
+
|
|
271
|
+
- `estimated_valuation` is intentionally simple. It is kept as a backwards-compatible ARR heuristic. Prefer `estimated_arr_valuation` in new code when you want the denominator to be explicit.
|
|
272
|
+
- Do not compare an ARR multiple and a TTM profit multiple as if they were the same kind of number. They are based on different denominators.
|
|
273
|
+
- A `4x TTM profit` deal, a `2x TTM revenue` deal, and an `8x ARR` deal can all describe reasonable SaaS outcomes in different buyer segments.
|
|
274
|
+
- If two businesses both have `$300k ARR`, the one with lower churn, better margins, lower concentration, and cleaner growth usually deserves the higher multiple.
|
|
275
|
+
- If two businesses both have `$300k TTM revenue`, the one with stronger profit and more recurring revenue usually deserves the higher price.
|
|
276
|
+
|
|
277
|
+
### Typical multiples by SaaS type and size
|
|
278
|
+
|
|
279
|
+
These are rough, source-backed heuristics. They are not interchangeable.
|
|
280
|
+
|
|
281
|
+
| SaaS profile | Common denominator | Rough multiple | Source |
|
|
282
|
+
| --- | --- | --- | --- |
|
|
283
|
+
| Smaller profitable SaaS on Acquire.com (2024-2025 confirmed transactions) | `TTM profit` | `3.9x` median | [Acquire.com Jan 2026 report](https://blog.acquire.com/acquire-com-biannual-acquisition-multiples-report-jan-2026/) |
|
|
284
|
+
| Micro-SaaS under `$100k` TTM revenue | `TTM profit` | `3.55x` average | [Acquire.com webinar recap](https://blog.acquire.com/the-secrets-behind-2024s-biggest-exits-webinar-recap/) |
|
|
285
|
+
| Micro-SaaS with `$100k-$1M` TTM revenue | `TTM profit` | `4.35x` average | [Acquire.com webinar recap](https://blog.acquire.com/the-secrets-behind-2024s-biggest-exits-webinar-recap/) |
|
|
286
|
+
| TrustMRR marketplace listings | `Annualized last 30d revenue` | often roughly `0.6x-5.5x` ask multiples | [TrustMRR homepage snapshot](https://trustmrr.com/) |
|
|
287
|
+
| Mid-6-figure ARR SaaS | `TTM revenue` | `2x-4x` revenue | [Acquire.com founder-driven acquisition recap](https://blog.acquire.com/how-founders-can-drive-their-own-acquisition-process-webinar-recap/) |
|
|
288
|
+
| Older Acquire.com SaaS baseline | `TTM revenue` or `TTM profit` | `2-3x revenue` or `5x profit` | [Acquire.com 7-8 figures webinar recap](https://blog.acquire.com/how-to-sell-your-company-playbook-webinar/) |
|
|
289
|
+
| Strong recurring SaaS with high growth and retention | `ARR` | `5x-15x ARR` | [Acquire.com SaaS valuation multiples guide](https://blog.acquire.com/saas-valuation-multiples/) |
|
|
290
|
+
|
|
291
|
+
How to read this table:
|
|
292
|
+
|
|
293
|
+
- Smaller bootstrapped SaaS buyers on Acquire-style marketplaces often underwrite on `TTM profit`.
|
|
294
|
+
- If profit is low or intentionally reinvested, buyers may fall back to `TTM revenue`.
|
|
295
|
+
- TrustMRR listing multiples are a secondary comparison set: they are based on recent revenue run-rate, specifically `last 30 days revenue × 12`.
|
|
296
|
+
- Higher-quality SaaS with real scale, low churn, and strong growth is more likely to be discussed in `ARR` terms.
|
|
297
|
+
|
|
298
|
+
### Rough valuation formulas from `profitable`
|
|
299
|
+
|
|
300
|
+
You can only multiply a metric by a multiple if the denominator matches.
|
|
301
|
+
|
|
302
|
+
#### 1. ARR multiple
|
|
303
|
+
|
|
304
|
+
This is already built into the gem:
|
|
305
|
+
|
|
306
|
+
```ruby
|
|
307
|
+
# Example: 6x ARR
|
|
308
|
+
Profitable.estimated_arr_valuation(multiple: 6).to_readable
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Use this when:
|
|
312
|
+
|
|
313
|
+
- your business is strongly recurring,
|
|
314
|
+
- churn and retention are solid,
|
|
315
|
+
- and you want a run-rate-based heuristic.
|
|
316
|
+
|
|
317
|
+
#### 2. TTM revenue multiple
|
|
318
|
+
|
|
319
|
+
This is useful when buyers care more about trailing actuals than annualized run-rate:
|
|
320
|
+
|
|
321
|
+
```ruby
|
|
322
|
+
ttm_revenue_cents = Profitable.ttm_revenue.to_i
|
|
323
|
+
|
|
324
|
+
low_estimate_cents = (ttm_revenue_cents * 2.0).round
|
|
325
|
+
high_estimate_cents = (ttm_revenue_cents * 4.0).round
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Use this when:
|
|
329
|
+
|
|
330
|
+
- the business has meaningful one-time revenue,
|
|
331
|
+
- profit is thin because you are reinvesting,
|
|
332
|
+
- or the buyer is thinking in revenue-band terms.
|
|
333
|
+
|
|
334
|
+
#### 2b. Recent revenue run-rate multiple
|
|
335
|
+
|
|
336
|
+
This is the closest match to TrustMRR-style marketplace multiples:
|
|
337
|
+
|
|
338
|
+
```ruby
|
|
339
|
+
# Default: annualized last-30-days revenue
|
|
340
|
+
Profitable.revenue_run_rate.to_readable
|
|
341
|
+
Profitable.estimated_revenue_run_rate_valuation(2.7).to_readable
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Use this when:
|
|
345
|
+
|
|
346
|
+
- you're comparing against TrustMRR listings,
|
|
347
|
+
- the market is quoting a multiple on recent revenue rather than ARR,
|
|
348
|
+
- and you want the denominator to match the marketplace comp.
|
|
349
|
+
|
|
350
|
+
#### 3. TTM profit multiple
|
|
351
|
+
|
|
352
|
+
`profitable` cannot calculate this yet because it does not know your costs.
|
|
353
|
+
|
|
354
|
+
```ruby
|
|
355
|
+
# You need to compute this outside of profitable:
|
|
356
|
+
ttm_profit_cents = your_ttm_profit_cents
|
|
357
|
+
|
|
358
|
+
low_estimate_cents = (ttm_profit_cents * 3.5).round
|
|
359
|
+
high_estimate_cents = (ttm_profit_cents * 4.35).round
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
Use this when:
|
|
363
|
+
|
|
364
|
+
- the business is a smaller profitable micro-SaaS,
|
|
365
|
+
- the buyer is focused on ROI and cash flow,
|
|
366
|
+
- or you're comparing yourself to Acquire.com-style marketplace comps.
|
|
367
|
+
|
|
137
368
|
## Development
|
|
138
369
|
|
|
139
370
|
After checking out the repo, install dependencies:
|
|
@@ -4,6 +4,7 @@ module Profitable
|
|
|
4
4
|
@mrr = Profitable.mrr
|
|
5
5
|
@mrr_growth_rate = Profitable.mrr_growth_rate
|
|
6
6
|
@total_customers = Profitable.total_customers
|
|
7
|
+
@ttm_revenue = Profitable.ttm_revenue
|
|
7
8
|
@all_time_revenue = Profitable.all_time_revenue
|
|
8
9
|
@estimated_valuation = Profitable.estimated_valuation
|
|
9
10
|
@average_revenue_per_customer = Profitable.average_revenue_per_customer
|
|
@@ -53,6 +53,10 @@
|
|
|
53
53
|
<h2><%= @mrr.to_readable %></h2>
|
|
54
54
|
<p>MRR</p>
|
|
55
55
|
</div>
|
|
56
|
+
<div class="card">
|
|
57
|
+
<h2><%= @ttm_revenue.to_readable %></h2>
|
|
58
|
+
<p>TTM revenue</p>
|
|
59
|
+
</div>
|
|
56
60
|
<div class="card">
|
|
57
61
|
<h2><%= @estimated_valuation.to_readable %></h2>
|
|
58
62
|
<p>Valuation at 3x ARR</p>
|
data/context7.json
ADDED