以下情境为教学假设,人物与数字不代表经核实的真实案例。
11.1 解决了「用哪个分母」:ARR、收入、SDE 各自回答不同问题,不能混用。但同一个分母上,倍数仍然会差很多。LedgerLite 的 3.2× ARR 能站住,Nord 的 4.5× SDE 却被买方压到 3.0× = $102,960;Tidecast 的 1.8× 收入既没有续费基数,也没有稳定的下一部作品承诺。分母相同或相近,价格却不同,差别来自倍数背后的驱动。
这一节把倍数拆成五个驱动:经常性收入占比、增长率、毛利率、客户集中度、创始人依赖。每个驱动都回答三个问题:看什么指标、往哪个方向改善、对倍数是加分还是减分。五个驱动不是一张行业倍数表,而是一套可复算的相对关系;所有评分都是教学设定,不是市场统计。
五个驱动,一张评分表
先把五个驱动放在一张表里。这张表只给方向和观测口径,不给「行业平均倍数」,因为独立开发者的交易样本太少,任何所谓行业区间都很容易变成伪精确。
| 驱动 | 观测指标 | 改善方向 | 对倍数的影响方向 |
|---|---|---|---|
| 经常性收入占比 | ARR ÷ 总收入、续费率、NRR | 把服务转成订阅、提高年付比例 | 占比越高,倍数越高 |
| 增长率 | MRR 净增、NRR、续约率、客户数净增 | 稳定新增、扩张收入、降低流失 | 增长越稳,倍数越高 |
| 毛利率 | 毛利 ÷ 收入、变动成本率 | 降云成本、提价、调整收入结构 | 毛利率越高,倍数越高 |
| 客户集中度 | 最大客户、前五大、前三大、HHI | 分散客户、降低单点依赖 | 集中度越高,倍数越低 |
| 创始人依赖 | 关键流程由谁处理、交接文档、团队 | 文档化、外包、雇人、产品化 | 依赖越低,倍数越高 |
这五个驱动不是并列的。经常性收入占比决定分母的质量,增长率决定这个分母明年会不会变大,毛利率决定每增长一元收入能留下多少,客户集中度和创始人依赖决定买方接手后能不能维持。前三个更像「这门生意好不好」,后两个更像「这门生意能不能交给别人」。
三个案例在五个驱动上的事实并不完整,而且口径不同。能直接用的数字只有这些:
| 驱动 | LedgerLite | Tidecast | Nord Studio |
|---|---|---|---|
| 经常性收入占比 | 100% | 0% | 27.3% |
| 增长相关事实 | NRR 102%、GRR 83%、月流失 1.5% | 单部作品、Q4 占销量 38% | 咨询续约 70%、产品月流失 3.0% |
| 毛利率或净得率 | 84.2% | 72.0% 净得率 | 84.4% |
| 客户或渠道集中度 | 前五 31%、最大 9% | Steam 80% 销量、平台 100% | 前 3 家 48% |
| 创始人依赖 | 1 人 + 外包 | 1 人 + 外包美术,IP 可授权 | 2 名合伙人,服务关系为主 |
这张表里没有「增长率」的统一数字,因为三个案例的增长机制根本不同:LedgerLite 看的是续费与扩张,Tidecast 看的是单部作品的销量曲线,Nord 看的是咨询续约和产品流失。用同一个增长率去横比,会把三种不同的东西压成一个数字。
经常性收入占比:分母的质量
经常性收入占比回答的是「这个分母明年还在不在」。LedgerLite 是 100% 订阅,ARR $240,240 全部来自 260 家付费客户;Tidecast 是 0% 经常性,$138,171 净收入来自 9,600 份买断销量;Nord 是 27.3% 产品订阅,$42,120 来自 90 家客户,其余 $112,200 是咨询。
| 案例 | 经常性收入 | 一次性或服务收入 | 占比 | 倍数锚 |
|---|---|---|---|---|
| LedgerLite | $240,240 | $0 | 100% | 3.2× ARR = $768,768 |
| Tidecast | $0 | $138,171 | 0% | 1.8× 收入 = $248,708 |
| Nord Studio | $42,120 | $112,200 | 27.3% | 4.5× SDE = $154,440 |
改善方向很清楚:把可重复的服务产品化,把月付转成年付,把一次性交付转成订阅。但改善不是免费的。Nord 要把咨询转成产品,需要先把服务流程标准化,再承担产品研发成本;Tidecast 要做订阅,等于改变商业模式,不是把 $19.99 改成 $9.99/月那么简单。
对倍数的影响方向是正向的:经常性占比越高,买方越能把今年的收入外推到明年,倍数越高。但这不是线性的。4.3 用 2.5×–4.0× 和 1.0×–2.0× 两组教学区间做过线性插值,那是一个可复算的基准,不是市场报价。真实交易里,占比从 0 到 27.3% 带来的倍数提升,可能小于从 27.3% 到 100% 带来的提升,因为前者还没有证明续费能力。
Nord 的 27.3% 是一个典型的中间态:产品订阅有续费,但只占四分之一;咨询续约率 70%,意味着每年有三成服务收入要重新赢回来。买方看到这个结构,不会把 $154,320 全部当作经常性收入,也不会只用 $42,120 定价,而是会用 SDE 分母,再根据服务占比打折。这就是 4.5× 被压到 3.0× 的第一个原因。
增长率与毛利率:一个看方向,一个看留存
增长率决定分母会不会变大,毛利率决定变大的部分能留下多少。两个指标必须一起看:高增长低毛利,增长越快现金越紧;低增长高毛利,现金流稳但估值缺少想象空间。
先看增长。LedgerLite 的 NRR 是 102%,GRR 是 83%,月流失 1.5%。这三个数字放在一起,说明现有客户每年会流失一部分,但扩张收入略高于流失,净收入还在增长。Tidecast 没有续费基数,增长来自单部作品销量:年销 9,600 份,12 月 2,400 份,是月均 800 份的 3 倍,Q4 占全年 38%。这是发布期的脉冲,不是持续增长。Nord 的咨询续约率 70%,产品月流失 3.0%,两条线都在漏,增长要靠不断获新客。
| 案例 | 增长观测 | 增长质量 | 对倍数的含义 |
|---|---|---|---|
| LedgerLite | NRR 102%、GRR 83%、月流失 1.5% | 扩张略高于流失 | 续费基数大致稳住,倍数有支撑 |
| Tidecast | 年销 9,600 份、Q4 占 38% | 发布期脉冲,无续费 | 买方要另算下一部作品的风险 |
| Nord Studio | 咨询续约 70%、产品月流失 3.0% | 两条线都在漏 | 服务收入需要持续获客,倍数被压 |
再看毛利率。LedgerLite 的毛利率是 84.2%,毛利 $202,240;Nord 是 84.4%,毛利 $130,320;Tidecast 的净得率是 72.0%,因为平台分成、退款和支付费先扣掉了 28%。毛利率高不代表估值一定高,但它决定了每一元新增收入能留下多少。LedgerLite 每多 $1 收入,约 $0.84 可以用于覆盖运营费用和利润;Tidecast 每多 $1 标价收入,先被渠道拿走约 $0.28。
改善毛利率的方向包括:降低云成本、提高价格、把低毛利的外包或支付成本转出去、改变收入结构。LedgerLite 的变动成本是 $38,000,其中云 $18,000、支付 $12,000、支持 $8,000;Nord 的直接成本是外包 $18,000、工具 $6,000;Tidecast 的主要漏损是 Steam 30% 分成和 4% 退款。这些数字都能从事实表复算,不需要额外假设。
对倍数的影响方向:增长率越高、增长越稳,倍数越高;毛利率越高,倍数越高。但两个驱动会互相抵消。Tidecast 的净得率 72.0% 不低,可增长是脉冲式的,买方不会因为一年卖得好就给一个可持续的倍数。Nord 的毛利率 84.4% 很高,但服务收入续约率只有 70%,高毛利也换不来高倍数。LedgerLite 的两个数字都不错,所以它的 3.2× ARR 在三案例里最稳。
客户集中度与创始人依赖:两个折价源
客户集中度和创始人依赖是买方最常用的两个折价理由。它们不影响「今年赚多少」,但影响「明年还能不能赚」。
客户集中度看的是收入分布。LedgerLite 前五家客户占 31%,最大一家 9%,长尾较厚;Tidecast 的 Steam 渠道占销量 80%、净收入约 74.7%,平台依赖接近 100%;Nord 前 3 家客户占 48%,服务收入占 72.7%。三个案例的集中类型不同:LedgerLite 是客户集中但分散度尚可,Tidecast 是渠道集中,Nord 是客户与服务线双重集中。7.2 已经把「客户集中」和「渠道集中」的口径拆开,这里直接沿用那个区分。
| 案例 | 集中度事实 | 风险类型 | 对倍数的影响 |
|---|---|---|---|
| LedgerLite | 前五 31%、最大 9% | 客户集中,但长尾厚 | 小幅折价 |
| Tidecast | Steam 80% 销量、平台 100% | 渠道集中 | 平台规则变化会直接冲击收入 |
| Nord Studio | 前 3 家 48%、服务 72.7% | 客户与服务线双重集中 | 折价的主要来源之一 |
创始人依赖看的是「这门生意离了创始人还能不能转」。LedgerLite 是一人公司,但代码、合同、外包流程相对可交接;Tidecast 是一人开发,但 IP 组合可以授权或整体出售,代码、美术、音乐、商标构成可转移资产;Nord 是两位合伙人,服务收入依赖个人关系和交付能力,而且两位合伙人各 50%、无书面退出条款,交接难度最高。
| 案例 | 创始人依赖 | 可转移性 | 对倍数的影响 |
|---|---|---|---|
| LedgerLite | 1 人 + 外包,关键流程仍由创始人掌握 | 中等 | 需要交接计划和过渡期 |
| Tidecast | 1 人 + 外包美术,IP 可授权 | 较高(但音乐授权不可转授权) | 资产可卖,续作能力仍依赖创始人 |
| Nord Studio | 2 名合伙人,服务关系为主 | 较低 | 买方必须绑定合伙人或重建团队 |
改善方向:客户集中度靠分散获客、增加长尾客户、降低单一大客户或单一平台的占比;创始人依赖靠文档化、标准化交付、培养第二负责人、把关键关系从个人转成合同和流程。这两个改善都需要时间,而且会先增加成本、后体现效果,所以买方通常不会为「正在改善」付全价。
对倍数的影响方向:集中度越高,倍数越低;创始人依赖越高,倍数越低。Tidecast 的可转移性其实不差,IP 可以授权,但平台集中度接近 100%,两个驱动一正一负;Nord 的两个驱动都偏负,所以它的 4.5× 锚被压到 3.0×;LedgerLite 的集中度可控、依赖中等,折价最小。
把评分变成调整系数
为了让五个驱动可以复算,本节用一套教学评分:每个驱动 0–100 分,权重分别是经常性收入占比 25%、增长率 20%、毛利率 20%、客户集中度 20%、创始人依赖 15%。权重之和是 100%。评分和权重都是教学参数,不是市场统计,也不代表任何行业标准。
三个案例的评分如下。评分依据只使用事实表里已有的数字和定性描述;没有数字的驱动,按相对位置给分,并在表后说明理由。
| 驱动 | LedgerLite | Tidecast | Nord Studio |
|---|---|---|---|
| 经常性收入占比 | 100 | 0 | 27 |
| 增长率 | 65 | 35 | 30 |
| 毛利率 | 84 | 72 | 84 |
| 客户集中度 | 70 | 20 | 25 |
| 创始人依赖 | 60 | 75 | 20 |
| 加权得分 | 77.8 | 36.65 | 37.55 |
评分理由:LedgerLite 的经常性占比 100%,直接给 100;增长率 65 反映 NRR 102% 略高于流失、但不是高速扩张;毛利率 84 对应 84.2%;客户集中度 70 对应前五 31%、最大 9%;创始人依赖 60 对应一人公司但流程可交接。Tidecast 的经常性占比 0;增长率 35 反映单部作品的脉冲式销量;毛利率 72 对应净得率 72.0%;客户集中度 20 对应 Steam 80%;创始人依赖 75 是因为 IP 可授权、可整体出售,但续作仍依赖创始人。Nord 的经常性占比 27;增长率 30 对应咨询续约 70%、产品月流失 3.0%;毛利率 84 对应 84.4%;客户集中度 25 对应前 3 家 48%;创始人依赖 20 对应两位合伙人、服务关系为主、无书面退出条款。
把加权得分转成倍数调整系数,用一个教学映射:以 50 分为中性,每高或低 1 分,倍数调整 80 个基点,也就是 0.8%。得分 100 对应 +40%,得分 0 对应 −40%。这个映射只用来演示方向,不是定价公式。
| 案例 | 加权得分 | 调整系数 | 以 3.2× 为统一基数的结果 |
|---|---|---|---|
| LedgerLite | 77.8 | 1.2224× | 3.9117× |
| Tidecast | 36.65 | 0.8932× | 2.8582× |
| Nord Studio | 37.55 | 0.9004× | 2.8813× |
这张表最后一行要特别小心:它把三个案例都放到 3.2× 这个统一基数上,只是为了比较驱动带来的相对差异,不是说 Tidecast 可以用 3.2× ARR,也不是说 Nord 应该用 3.2× 的某个分母。Tidecast 没有 ARR,Nord 的 4.5× SDE 是另一个分母;跨分母套用系数,会犯 11.1 已经指出的错误。
五个驱动里,最弱的一项通常决定买方的折价叙事。LedgerLite 最弱的是创始人依赖 60 分,买方会要求交接计划和过渡期;Tidecast 最弱的是经常性收入 0 分,买方会把下一部作品的风险单独计价;Nord 最弱的是创始人依赖 20 分,其次是客户集中度 25 分,这正好对应它从 4.5× 被压到 3.0× 的原因。改善倍数最有效的动作,往往不是把最强项做得更强,而是把最弱项补到及格线。
把五驱动写成评分函数
下面的 drivers.ts 把评分、权重、加权得分和调整系数固化成函数。validateProfile 拒绝 0–100 之外的分数;weightedScoreBps 用基点表示加权得分;driverContributions 拆出每个驱动对总分的贡献;weakestDriver 找出买方最可能用来压价的那一项;applyDriverAdjustment 把系数乘回一个基准倍数。所有金额和比率仍然沿用整数与基点口径,不引入浮点漂移。
// file: src/value/drivers.ts
// tier: L1
// verify: node --test test/value/drivers.test.ts
import { type Bps } from "../core/rate.ts";
import { type Result, fail, ok } from "../core/result.ts";
export type DriverKey =
| "recurringShare"
| "growth"
| "grossMargin"
| "concentration"
| "transferability";
export type DriverProfile = {
recurringShare: number;
growth: number;
grossMargin: number;
concentration: number;
transferability: number;
};
export type DriverContribution = {
key: DriverKey;
score: number;
weightedBps: number;
};
export type DriverAdjustment = {
scoreBps: Bps;
coefficientBps: Bps;
baseMultipleBps: Bps;
adjustedMultipleBps: Bps;
};
export const DRIVER_KEYS: DriverKey[] = [
"recurringShare",
"growth",
"grossMargin",
"concentration",
"transferability",
];
/** Teaching weights, not observed market weights. They sum to 10000 bps. */
export const DRIVER_WEIGHTS_BPS: Record<DriverKey, Bps> = {
recurringShare: 2500,
growth: 2000,
grossMargin: 2000,
concentration: 2000,
transferability: 1500,
};
export const NEUTRAL_SCORE = 50;
/**
* Teaching mapping: every point above or below the neutral score moves the
* multiple by 80 bps, capped by the 0-100 score range at roughly +/-40%.
*/
export const SCORE_TO_COEFFICIENT_BPS = 80;
const MIN_SCORE = 0;
const MAX_SCORE = 100;
function checkScore(value: number): string | null {
if (!Number.isInteger(value) || value < MIN_SCORE || value > MAX_SCORE) {
return "driver scores must be integers between 0 and 100";
}
return null;
}
export function validateProfile(profile: DriverProfile): Result<DriverProfile> {
for (const key of DRIVER_KEYS) {
const error = checkScore(profile[key]);
if (error) {
return fail(error);
}
}
return ok(profile);
}
/** Weighted score expressed in basis points of the 0-100 scale. */
export function weightedScoreBps(profile: DriverProfile): Result<Bps> {
const validation = validateProfile(profile);
if (!validation.ok) {
return validation;
}
let total = 0;
for (const key of DRIVER_KEYS) {
total += profile[key] * DRIVER_WEIGHTS_BPS[key];
}
return ok(Math.round(total / 100));
}
export function driverContributions(
profile: DriverProfile,
): Result<DriverContribution[]> {
const validation = validateProfile(profile);
if (!validation.ok) {
return validation;
}
return ok(
DRIVER_KEYS.map((key) => ({
key,
score: profile[key],
weightedBps: Math.round((profile[key] * DRIVER_WEIGHTS_BPS[key]) / 100),
})),
);
}
/** The lowest-scoring driver is the one a buyer will use to argue a discount. */
export function weakestDriver(
profile: DriverProfile,
): Result<{ key: DriverKey; score: number }> {
const validation = validateProfile(profile);
if (!validation.ok) {
return validation;
}
let weakestKey = DRIVER_KEYS[0];
let weakestScore = profile[weakestKey];
for (const key of DRIVER_KEYS) {
if (profile[key] < weakestScore) {
weakestKey = key;
weakestScore = profile[key];
}
}
return ok({ key: weakestKey, score: weakestScore });
}
/**
* Convert the weighted score into a multiplier on the base multiple.
* 10000 bps means no adjustment; above means a premium, below a discount.
*/
export function coefficientBps(profile: DriverProfile): Result<Bps> {
const score = weightedScoreBps(profile);
if (!score.ok) {
return score;
}
const pointsAboveNeutral = score.value / 100 - NEUTRAL_SCORE;
return ok(10000 + Math.round(pointsAboveNeutral * SCORE_TO_COEFFICIENT_BPS));
}
export function applyDriverAdjustment(
baseMultipleBps: Bps,
profile: DriverProfile,
): Result<DriverAdjustment> {
if (!Number.isInteger(baseMultipleBps) || baseMultipleBps <= 0) {
return fail("base multiple must be a positive integer in basis points");
}
const score = weightedScoreBps(profile);
if (!score.ok) {
return score;
}
const coefficient = coefficientBps(profile);
if (!coefficient.ok) {
return coefficient;
}
return ok({
scoreBps: score.value,
coefficientBps: coefficient.value,
baseMultipleBps,
adjustedMultipleBps: Math.round(
(baseMultipleBps * coefficient.value) / 10000,
),
});
}
代码里有一个刻意的设计:coefficientBps 不直接返回小数倍数,而是返回基点。10000 个基点表示不调整,12224 表示上浮 22.24%,8932 表示下调 10.68%。这样调完之后仍然可以用整数运算,也便于在文章和表格里复算。另一个设计是 weakestDriver 的并列处理:按 DRIVER_KEYS 的顺序取第一个最低分,保证同样的输入每次得到同样的输出。
小结
- 倍数不是由分母单独决定的;同一个分母上,五个驱动决定溢价还是折价。
- 五个驱动是:经常性收入占比、增长率、毛利率、客户集中度、创始人依赖。
- 每个驱动都要回答三件事:看什么指标、往哪个方向改善、对倍数是加分还是减分。
- LedgerLite 的加权得分 77.8,调整系数 1.2224×;Tidecast 36.65,0.8932×;Nord 37.55,0.9004×。评分和权重都是教学参数。
- 最弱项通常决定折价叙事:LedgerLite 是创始人依赖,Tidecast 是经常性收入,Nord 是创始人依赖与客户集中度。
- 改善倍数最有效的动作,是把最弱项补到及格线,而不是把最强项做得更强。
阅读导航:上一节:11.1 三种估值口径:ARR 倍数、SDE 倍数、DCF · 下一节:11.3 估值区间与谈判区间 。
常见错误与边界
- 把五个驱动当成行业公式。 25%/20%/20%/20%/15% 的权重和 0–100 评分都是教学参数,不是市场统计;真实交易没有这么整齐。
- 用同一个增长率横比三个案例。 LedgerLite 看 NRR 和流失,Tidecast 看单部作品销量,Nord 看咨询续约和产品流失;三种增长机制不能压成一个数字。
- 把高毛利率当成高倍数。 Nord 的毛利率 84.4%,但服务续约率 70%、前 3 家客户 48%,高毛利换不来高倍数。
- 把平台集中度当客户集中度。 Tidecast 的 Steam 80% 是渠道集中,不是客户集中;改善动作是分散渠道,不是分散客户。
- 把 IP 可转移当成业务可转移。 Tidecast 的 IP 可以授权或出售,但续作仍依赖创始人;音乐授权还不可转授权。
- 忽略创始人依赖的双面性。 一人公司决策快,但买方接手后要补管理能力;两位合伙人看似有分工,但没有书面退出条款,交接风险更高。
- 只补最强项。 把 LedgerLite 的经常性占比从 100% 再往上加没有空间;真正影响折价的是创始人依赖 60 分。
- 把调整系数直接乘到任意分母上。 系数是在同一个分母内部比较驱动差异用的;跨分母套用会重犯 11.1 的错误。
- 把「正在改善」按已完成定价。 分散客户、降低创始人依赖都需要时间,买方通常只为已经发生的变化付钱。
- 忘了交易结构。 驱动评分影响的是倍数区间,不是最终到手金额;earn-out、托管和营运资金调整会在 11.3 单独处理。
11.2 把倍数的「为什么」拆成了五个可观测的驱动。11.3 要把这些驱动变成三档情景:乐观、基准、悲观,并给出一个区间而不是一个点。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。