以下情境为教学假设,人物与数字不代表经核实的真实案例。
三个案例的估值锚经常被放在同一张表里:LedgerLite 是 3.2× ARR = $768,768,Tidecast 是 1.8× 收入 = $248,708,Nord Studio 是 4.5× SDE = $154,440。三个数字并列时,最自然的读法是「LedgerLite 最贵、Tidecast 最便宜、Nord 居中」。这个读法在数学上就不成立,因为三个倍数的分母不是同一种东西。
倍数是一个比率,不是一个价格标签。3.2× 只有在说明「3.2 倍的什么」之后才有意义;离开分母,3.2 和 4.5 只是两个小数。这一节先把三种分母拆开,再讨论各自适用于什么主体,最后用 DCF 做一次反向校验。4.3 已经演示过把三个锚换算成收入倍数之后的排序变化,本节不重复那套换算,而是回答更前面的问题:什么时候应该用哪一种分母。
| 案例 | 披露锚 | 分母 | 分母金额 | 估值金额 |
|---|---|---|---|---|
| LedgerLite | 3.2× ARR | 年度经常性收入 | $240,240 | $768,768 |
| Tidecast | 1.8× 收入 | 净收入 | $138,171 | $248,708 |
| Nord Studio | 4.5× SDE | 卖方自由现金流 | $34,320 | $154,440 |
| Nord Studio(折价) | 3.0× SDE | 卖方自由现金流 | $34,320 | $102,960 |
分母决定倍数,倍数不决定价格
把同一门生意放到三种分母上,倍数的绝对值会立刻失去可比性。先看 LedgerLite:它只有订阅收入,所以 ARR 和收入恰好都是 $240,240。用同一个 3.2× 去乘,ARR 和收入都得到 $768,768;但用 3.2× 去乘 SDE $130,240,只能得到 $416,768。三个结果都来自同一个 3.2×,价格却相差 $352,000。
| 分母 | LedgerLite 金额 | 3.2× 的结果 | 这个结果回答的问题 |
|---|---|---|---|
| ARR | $240,240 | $768,768 | 买方为续费基数付多少钱 |
| 收入 | $240,240 | $768,768 | 买方为当前收入流付多少钱 |
| SDE | $130,240 | $416,768 | 买方为可支配现金付多少钱 |
Nord Studio 更能说明问题。它的产品订阅年化收入是 90 家 × $39 × 12 = $42,120,总收入是 $154,320,SDE 是 $34,320。同一个 4.5× 乘下去,三种分母给出三个完全不同的世界:
| 分母 | Nord 金额 | 4.5× 的结果 | 读法 |
|---|---|---|---|
| 产品 ARR | $42,120 | $189,540 | 只为可续费的产品线定价 |
| 总收入 | $154,320 | $694,440 | 把 4.5× 用在以服务为主的收入上 |
| SDE | $34,320 | $154,440 | 案例披露的锚 |
$694,440 不是 Nord 的真实报价,它只是「4.5× 总收入」这个算式的结果。Nord 的服务收入占 72.7%,续约率 70%,把这部分收入按 ARR 式的 4.5× 定价,等于假设咨询服务像订阅一样可以自动续费。这个假设不成立,所以这个数字只能用来演示误用,不能拿来谈判。
倍数的定义只有一个:
估值 = 分母 × 倍数
倍数 = 估值 ÷ 分母
这个式子看起来简单,但它要求分母和倍数必须来自同一套口径。3.2× ARR 里的 3.2 是「每一美元年度经常性收入值 3.2 美元」;3.2× SDE 里的 3.2 是「每一美元卖方自由现金流值 3.2 美元」。两个 3.2 只是数字碰巧相同,经济含义完全不同。混用的本质,是把「每单位续费基数」的价格套到「每单位现金」上,或者反过来。
把三种口径写成代码
下面的 multiple.ts 把三种分母固化成三个入口:quoteArr 只接受完全经常性的基数,quoteRevenue 允许混合收入但返回持续性警告,quoteSde 强制要求说明所有者薪酬是否已扣除。compareQuotes 在不同分母、不同薪酬口径或不同经常性占比之间直接拒绝比较,toRevenueMultiple 则把任意报价换算成收入倍数,供跨口径核对。
// file: src/value/multiple.ts
// tier: L1
// verify: node --test test/value/multiple.test.ts
import { type Cents } from "../core/money.ts";
import { type Bps, applyRate, divide } from "../core/rate.ts";
import { type Result, fail, ok } from "../core/result.ts";
export type MultipleBase = "arr" | "revenue" | "sde";
export type MultipleQuote = {
base: MultipleBase;
multipleBps: Bps;
baseCents: Cents;
valueCents: Cents;
recurringShareBps: Bps;
ownerSalaryDeducted: boolean | null;
warnings: string[];
};
function checkAmount(amount: Cents, label: string): string | null {
if (!Number.isInteger(amount) || amount <= 0) {
return `${label} must be a positive integer amount in cents`;
}
return null;
}
function checkMultiple(multipleBps: Bps): string | null {
if (!Number.isInteger(multipleBps) || multipleBps <= 0) {
return "multiple must be a positive integer in basis points";
}
return null;
}
function checkShare(shareBps: Bps): string | null {
if (!Number.isInteger(shareBps) || shareBps < 0 || shareBps > 10000) {
return "recurring share must be between 0 and 10000 bps";
}
return null;
}
/**
* ARR is by definition fully recurring. Passing total revenue as ARR is the
* most common valuation mistake, so a partial recurring share is rejected.
*/
export function quoteArr(
arrCents: Cents,
multipleBps: Bps,
recurringShareBps: Bps = 10000,
): Result<MultipleQuote> {
const amountError = checkAmount(arrCents, "arr");
if (amountError) {
return fail(amountError);
}
const multipleError = checkMultiple(multipleBps);
if (multipleError) {
return fail(multipleError);
}
const shareError = checkShare(recurringShareBps);
if (shareError) {
return fail(shareError);
}
if (recurringShareBps !== 10000) {
return fail("arr base must be fully recurring; pass only the recurring amount");
}
return ok({
base: "arr",
multipleBps,
baseCents: arrCents,
valueCents: applyRate(arrCents, multipleBps),
recurringShareBps,
ownerSalaryDeducted: null,
warnings: [],
});
}
/**
* Revenue may mix recurring and one-time income. The quote is valid, but the
* warnings keep the caller from describing it as recurring or as ARR.
*/
export function quoteRevenue(
revenueCents: Cents,
multipleBps: Bps,
recurringShareBps: Bps = 0,
): Result<MultipleQuote> {
const amountError = checkAmount(revenueCents, "revenue");
if (amountError) {
return fail(amountError);
}
const multipleError = checkMultiple(multipleBps);
if (multipleError) {
return fail(multipleError);
}
const shareError = checkShare(recurringShareBps);
if (shareError) {
return fail(shareError);
}
const warnings: string[] = [];
if (recurringShareBps < 10000) {
warnings.push("revenue base mixes recurring and one-time income");
}
if (recurringShareBps === 0) {
warnings.push("no recurring share: do not label this multiple as ARR");
}
return ok({
base: "revenue",
multipleBps,
baseCents: revenueCents,
valueCents: applyRate(revenueCents, multipleBps),
recurringShareBps,
ownerSalaryDeducted: null,
warnings,
});
}
/**
* SDE only means the same thing when owner compensation has been handled the
* same way. The flag is mandatory; a false value is allowed but warned.
*/
export function quoteSde(
sdeCents: Cents,
multipleBps: Bps,
ownerSalaryDeducted: boolean,
): Result<MultipleQuote> {
const amountError = checkAmount(sdeCents, "sde");
if (amountError) {
return fail(amountError);
}
const multipleError = checkMultiple(multipleBps);
if (multipleError) {
return fail(multipleError);
}
if (typeof ownerSalaryDeducted !== "boolean") {
return fail("sde quote must state whether owner salary was deducted");
}
const warnings: string[] = [];
if (!ownerSalaryDeducted) {
warnings.push(
"owner salary not deducted; this sde is not comparable to a post-salary sde",
);
}
return ok({
base: "sde",
multipleBps,
baseCents: sdeCents,
valueCents: applyRate(sdeCents, multipleBps),
recurringShareBps: 0,
ownerSalaryDeducted,
warnings,
});
}
/** Recover the multiple from a price and a base, for checking a quoted anchor. */
export function impliedMultiple(
valueCents: Cents,
baseCents: Cents,
): Result<Bps> {
const valueError = checkAmount(valueCents, "valuation");
if (valueError) {
return fail(valueError);
}
const baseError = checkAmount(baseCents, "base");
if (baseError) {
return fail(baseError);
}
return ok(divide(valueCents, baseCents));
}
/**
* Comparing two multiples is only meaningful when their denominators and
* adjustments match. Different bases are a hard failure, not a warning.
*/
export function compareQuotes(
first: MultipleQuote,
second: MultipleQuote,
): Result<{ differenceBps: Bps }> {
if (first.base !== second.base) {
return fail(
`cannot compare ${first.base} multiple with ${second.base} multiple`,
);
}
if (
first.base === "sde" &&
first.ownerSalaryDeducted !== second.ownerSalaryDeducted
) {
return fail("sde quotes differ on owner salary treatment");
}
if (
first.base === "revenue" &&
first.recurringShareBps !== second.recurringShareBps
) {
return fail("revenue quotes differ on recurring share; normalize the base first");
}
return ok({ differenceBps: first.multipleBps - second.multipleBps });
}
/** Convert any quote into a revenue multiple so the denominators match. */
export function toRevenueMultiple(
quote: MultipleQuote,
revenueCents: Cents,
): Result<Bps> {
const revenueError = checkAmount(revenueCents, "revenue");
if (revenueError) {
return fail(revenueError);
}
return ok(divide(quote.valueCents, revenueCents));
}
代码里最值得注意的不是乘法,而是三个校验。第一,quoteArr 拒绝把带有非经常性收入的总额当作 ARR;这条校验拦住的正是「用总收入乘 ARR 倍数」的常见错误。第二,quoteRevenue 不拒绝混合收入,但会返回警告,因为收入倍数本身可以用于买断制,只是不能暗示持续性。第三,quoteSde 把「是否扣所有者薪酬」变成必填参数;没有这个参数,两个 SDE 倍数可能一个含老板工资、一个不含,数字上能比,经济上不能比。
ARR 倍数:只对经常性收入有意义
ARR 的定义是年度经常性收入:
MRR = 付费客户数 × 每户月收入
ARR = MRR × 12
LedgerLite 有 260 家付费客户,ARPA 是 $77/月,所以 MRR = 260 × 77 = $20,020,ARR = 20,020 × 12 = $240,240。3.2× ARR 就是 3.2 × 240,240 = $768,768。
ARR 倍数之所以存在,是因为买方买的不是「今年收到多少钱」,而是「明年还能收到多少钱」。LedgerLite 的月流失率是 1.5%,年流失约 16.6%,GRR 83%,NRR 102%。这组数字告诉买方:现有客户群每年会漏掉一部分,但扩张收入略高于流失,续费基数大致能稳住。3.2× 买的是这个续费基数,以及维护这个基数所需的代码、合同和客户关系。
ARR 倍数的适用边界很窄:主体必须有经常性收入。Tidecast 是买断制游戏,没有订阅,ARR 为零,用 ARR 倍数给它定价没有分母。Nord 只有产品订阅那一部分可以叫 ARR:90 家 × $39 × 12 = $42,120。如果要用 ARR 倍数,只能乘这 $42,120,不能乘 $154,320。乘出来的 $189,540 也只覆盖产品线,不覆盖占 72.7% 的咨询服务。
| 案例 | 可计入 ARR 的部分 | 金额 | 能否用 ARR 倍数 |
|---|---|---|---|
| LedgerLite | 全部订阅收入 | $240,240 | 可以 |
| Tidecast | 无经常性收入 | $0 | 不可以 |
| Nord Studio | 仅产品订阅 | $42,120 | 只能用于产品线 |
还有一个容易忽略的边界:ARR 不是现金。LedgerLite 有 30% 年付客户,年末递延收入 $22,000,占 ARR 的 9.2%。买方在尽调时会把递延收入视为尚未履行的服务义务,可能从价格中扣除;反过来,预收现金又让卖方在交割时更有底气。ARR 倍数定的是「续费基数的价格」,最终到手金额还要经过现金、负债和营运资金调整,这条桥在 4.1 节已经拆过,这里不展开。
收入倍数:买断制可用,但不能暗示持续性
Tidecast 的 1.8× 用的是净收入,不是标价收入。标价收入是 $19.99 × 9,600 份 = $191,904;扣掉 4% 退款、Steam 30% 分成和官网 5% 支付费之后,净得是 $138,171。1.8 × 138,171 = $248,707.8,四舍五入为 $248,708。
| 项 | 金额 | 说明 |
|---|---|---|
| 标价收入 | $191,904 | 19.99 × 9,600 |
| 退款 | −$7,676 | 4% |
| 平台分成 | −$44,215 | Steam 30% |
| 支付费 | −$1,842 | 官网 5% |
| 净收入 | $138,171 | 净得率 72.0% |
收入倍数对买断制是可用的,因为买方确实在买一条当前收入流。但它和 ARR 倍数回答的问题不同:ARR 倍数问「续费基数值多少」,收入倍数问「这条收入流值多少」。Tidecast 没有续费基数,买家付 1.8× 之后,必须自己承担下一部作品的风险。续作预算 $120,000、预计首年销量 7,000 份,这些是买方的追加投入,不在 1.8× 里。
收入倍数最大的风险是被用来暗示持续性。把 1.8× 写进一页纸,很容易让读者以为「明年还会有 $138,171」。实际上,Tidecast 的销量高度集中:12 月 2,400 份,是月均 800 份的 3 倍;Q4 占全年销量 38%。这是一条有季节性的、以单部作品为载体的收入流,不是一条会自动续期的收入流。倍数本身不包含「下一部作品」的承诺。
Nord 的例子说明误用收入倍数会把价格推到什么程度。Nord 的总收入是 $154,320,其中咨询 $112,200、产品订阅 $42,120。用 4.5× 乘总收入得到 $694,440,相当于为一项续约率 70%、依赖两位合伙人的服务业务支付近 4.5 倍年收入。这个价格没有任何案例事实支持;它只是提醒:收入倍数可以用,但必须先问这条收入明年还在不在。
| 分母 | 持续性来自哪里 | 买方在为什么付费 | 典型误用 |
|---|---|---|---|
| ARR | 订阅续费与扩张 | 续费基数 | 把一次性收入算进 ARR |
| 收入 | 当前作品或合同 | 当前收入流 | 把收入倍数说成可持续 |
| SDE | 现有经营现金流 | 可支配现金 | 不说明所有者薪酬 |
SDE 倍数:先问有没有扣所有者薪酬
SDE 是卖方自由现金流,常用定义是:
SDE = 毛利 − 运营费用
它衡量的是「买方接手后,在支付管理人员工资之前能拿到的现金」。三个案例的 SDE 算法一致,但所有者薪酬的处理并不一致:
| 案例 | 毛利 | 运营费用或工资 | SDE | 所有者薪酬处理 |
|---|---|---|---|---|
| LedgerLite | $202,240 | 运营费用 $72,000 | $130,240 | 创始人薪酬 $60,000 单独列示 |
| Tidecast | $138,171(净得) | 营销、服务器、工具 $17,000 | $121,171 | 一人经营,未单列工资 |
| Nord Studio | $130,320 | 两人工资 $96,000 | $34,320 | 两位合伙人工资已扣除 |
LedgerLite 的 SDE 是 202,240 − 72,000 = $130,240,创始人薪酬 $60,000 在 SDE 之后才扣,税前利润是 $70,240。Nord 的 SDE 是 130,320 − 96,000 = $34,320,两位合伙人的工资已经包含在 $96,000 里。Tidecast 没有单独列工资,SDE 是 138,171 − 12,000 − 3,000 − 2,000 = $121,171。
这三个 SDE 不能直接放在一起比,因为「所有者薪酬是否已扣」不同。LedgerLite 的 SDE 是「扣掉创始人工资之前」的现金;Nord 的 SDE 是「扣掉两位合伙人工资之后」的现金;Tidecast 的 SDE 没有工资行,买方接手后如果雇人运营,还要再扣一笔市场工资。倍数相同,含义不同。
案例给出的锚是 4.5× SDE = $154,440,折价后 3.0× SDE = $102,960。这里的 4.5× 和 3.0× 都建立在 Nord 已扣两人工资的 SDE 上。如果换成一个未扣所有者薪酬的 SDE,同样的 4.5× 会高估买方可支配现金,因为买方还要支付替代管理者的工资。SDE 倍数适合利润薄、所有者薪酬影响大的小生意,但前提是双方对「扣没扣」有一致的定义。
DCF 只做校验,不做报价
DCF 的公式不复杂:
估值 = Σ 未来自由现金流 ÷ (1 + 折现率)^t + 终值 ÷ (1 + 折现率)^n
复杂的是三个输入:未来现金流预测、折现率、终值假设。对一人公司来说,这三项都不稳定。预测期越长,假设越多;折现率要反映关键人依赖、客户集中和平台风险;终值往往是总估值里最大的一块,而终值又最依赖「几年后还能不能继续经营」这种很难验证的判断。
所以本章不把 DCF 当作报价工具,而把它当作校验工具。看到 3.2× ARR,可以反推:买方要拿到这个回报,需要 LedgerLite 的续费基数保持多稳?案例给的月流失 1.5%、GRR 83%、NRR 102% 能不能支撑?看到 1.8× 收入,可以反推:买方是否已经把下一部作品的成功概率算进去?看到 4.5× SDE,可以反推:Nord 的服务收入能不能脱离两位合伙人独立存在?如果答案是否定的,DCF 会把倍数里的乐观假设暴露出来,而不会自动给出一个更高的价格。
DCF 和倍数不是两套互相替代的方法,而是两种不同的提问方式。倍数问「同类资产最近按什么价格成交」,DCF 问「这门生意未来能产生多少现金」。前者依赖可比交易,后者依赖经营假设。独立开发者通常没有足够多的可比交易,所以倍数只能作为入口;真正决定价格的,还是 11.2 要拆的五个驱动,以及 11.3 要做的区间。
小结
- 倍数 = 估值 ÷ 分母。离开分母,3.2、1.8、4.5 只是小数,不能直接比大小。
- 同一门生意换分母,价格会变:LedgerLite 的 3.2× 在 ARR 和收入上是 $768,768,在 SDE 上是 $416,768;Nord 的 4.5× 在产品 ARR 上是 $189,540,在总收入上是 $694,440,在 SDE 上是 $154,440。
- ARR 倍数只对经常性收入有意义;Tidecast 没有 ARR,Nord 只有产品线的 $42,120 可以计入。
- 收入倍数对买断制可用,但它买的是当前收入流,不承诺明年还有;Tidecast 的 1.8× 净收入 = $248,708。
- SDE 倍数必须先说明所有者薪酬是否扣除;Nord 的 4.5× = $154,440、折价 3.0× = $102,960 都建立在已扣两人工资的 SDE 上。
- DCF 在本书里只做校验:用未来现金流反推倍数隐含的留存、增长和可转移性假设。
阅读导航:上一节:10.3 复盘会与决策记录 · 下一节:11.2 增长、毛利、集中度对倍数的影响 。
常见错误与边界
- 把 3.2、1.8、4.5 直接比大小。 三个倍数的分母分别是 ARR、净收入和 SDE;不换分母就比大小,等于拿长度比重量。
- 把总收入当 ARR。 Nord 的总收入是 $154,320,可计入 ARR 的只有产品订阅 $42,120;用总收入乘 4.5× 会得到 $694,440 这种没有经济含义的数字。
- 把 ARR 当现金。 LedgerLite 年末递延收入 $22,000,占 ARR 9.2%;ARR 是续费基数,不是已经到手的现金。
- 把净收入当标价收入。 Tidecast 的 1.8× 用的是扣退款、平台分成和支付费后的 $138,171,不是标价 $191,904。
- 用收入倍数暗示持续性。 收入倍数可以给买断制定价,但 1.8× 不包含下一部作品的承诺;Tidecast 的 Q4 占全年销量 38%,季节性本身就是风险。
- 不说明 SDE 是否扣所有者薪酬。 LedgerLite 的 SDE 在创始人工资之前,Nord 的 SDE 在两人工资之后;两个 4.5× 不是同一种 4.5×。
- 把 SDE 当净利润。 SDE 还没扣买方接手后的管理人员工资和所得税;4.5× SDE 不等于 4.5× 净利润。
- 只看倍数不看绝对值。 4.5× 一个 $34,320 的 SDE 只有 $154,440;1.8× 一个 $138,171 的净收入是 $248,708。
- 把 DCF 当精确答案。 一人公司的终值假设占比很高,折现率稍变,结果就会大幅变化;DCF 适合校验,不适合单独报价。
- 忽略交易结构。 资产收购、股权收购、earn-out、托管和营运资金调整都会改变最终到手金额;本节只比较「估值 ÷ 分母」这一层,结构条款见 11.3 与附录 B 第 7 节 。
11.1 只回答「用哪个分母」。11.2 继续往下拆:同一个分母上,为什么有的生意能拿到更高倍数,有的只能拿到折价。五个驱动会给出可观测的答案,11.3 再把它们变成区间。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。