以下情境为教学假设,人物与数字不代表经核实的真实案例。
报价是交易里最响的数字,也是最少说明问题的数字。同样是 248,708 美元,全部现金交割和 70% 现金加 30% earn-out,卖方在交割日拿到的钱差 74,612 美元;如果 earn-out 的计量口径在一年后把收入算低一点,最终到手还会再少一截。对价结构不是付款方式的细节,它决定了卖方承担多少未来风险、买方保留多少调整价格的空间。
这一节把对价拆成三种工具:现金、卖方票据和 earn-out。现金是交割日就能花的钱;卖方票据是卖方借钱给买方,按月收回本金和利息;earn-out 是卖方把一部分价格押在未来的经营指标上。三种工具的确定性、时间和风险不同,不能用一个「总价」比较。所有折现率、利率、期限和里程碑参数都是教学假设,不构成投资、法律或税务意见。
三种对价,三种确定性
| 工具 | 何时拿到 | 确定性 | 主要风险 | 卖方要问 |
|---|---|---|---|---|
| 现金 | 交割日 | 最高 | 交割条件、营运资金调整 | 交割日净额是多少 |
| 卖方票据 | 交割后按月或到期 | 中 | 买方违约、清偿顺序、担保 | 利率、期限、有无担保 |
| earn-out | 里程碑达成后 | 最低 | 计量口径、经营控制、支付上限 | 谁控制指标、怎么算、封顶多少 |
现金的确定性最高,但不是「报价乘以现金比例」这么简单。交割日实际到账要扣掉托管、营运资金调整和交割费用;托管通常从现金里切出来,比例和期限见 7.3 陈述保证、赔偿与托管 。本节先把托管放在一边,只看现金、票据和 earn-out 三者的时间价值。
卖方票据的本质是信贷。卖方把一部分价格借给买方,换取利息和分期还款;如果买方违约,卖方要从担保、清偿顺序和谈判里找回损失。票据的利率高于银行贷款,不是因为买方慷慨,而是因为卖方承担了信用风险。票据的期限越长、担保越弱,卖方越应该用更高的折现率看它。
earn-out 的本质是期权。卖方放弃一部分确定的价格,换取未来经营达标后的补偿;买方用这部分价格对冲「买贵了」的风险。earn-out 的争议通常不在比例,而在计量:收入按什么口径算、退款和折扣怎么处理、买方能不能通过削减营销让指标刚好差一点、支付有没有上限。比例是谈判桌上的数字,计量是交割后一年的战场。
70/30 的到手时间与现值
先看最简单的结构:70% 现金,30% earn-out,12 个月后支付,折现率按 12% 年化教学假设。Tidecast 的案例事实表只给出 248,708 美元的整体出售锚,没有规定对价结构;为了横向比较,这里给它套用同一组 70/30 教学假设。LedgerLite 的案例结构本来就是 70% 现金加 30% earn-out,12 个月,ARR 不低于基期 95% 时全额支付。
| 案例 | 报价 | 交割日现金 | 12 个月 earn-out | 全额到手时间 | 现值 | 时间价值折损 |
|---|---|---|---|---|---|---|
| Tidecast | 248,708 | 174,096 | 74,612 | 第 12 个月 | 240,310 | 8,398 |
| LedgerLite | 768,768 | 538,138 | 230,630 | 第 12 个月 | 742,810 | 25,958 |
Tidecast 的 74,612 美元 earn-out 按 12% 折现到交割日,现值是 66,215 美元;加上 174,096 美元现金,总现值 240,310 美元,比报价少 8,398 美元,约 3.4%。LedgerLite 的 230,630 美元 earn-out 现值是 204,673 美元;加上 538,138 美元现金,总现值 742,810 美元,比报价少 25,958 美元,同样是 3.4%。
两个案例的百分比折损一样,因为结构、期限和折现率一样;绝对金额不同,因为报价不同。这就是为什么「30% earn-out」不能直接和「30% 折扣」比较:前者是时间价值加履约风险,后者是价格直接下调。如果 earn-out 最终没有全额支付,卖方的损失会远大于 3.4%。
到手时间也要分开看。现金在交割日到账;earn-out 在第 12 个月支付,前提是指标达标;如果指标不达标,支付可能延后、减少或归零。报价上的「全额到手时间」是第 12 个月,但这是最好情况,不是承诺。卖方在排现金流时,应该把 earn-out 当成第 12 个月才可能到账的钱,而不是交割日就能用的钱。
卖方票据:利率与期限
卖方票据把一部分价格变成债权。它比 earn-out 确定,因为还款义务不依赖经营指标;它又比现金不确定,因为买方可能违约。票据的利率、期限和担保决定它值多少。一个常见的教学结构是 60% 现金、15% 卖方票据、25% earn-out;票据年利率 8%,期限 24 个月,本金按月等额摊还,利息按未偿余额计算。
以 LedgerLite 的 768,768 美元为例,现金是 461,261 美元,票据本金 115,315 美元,earn-out 目标 192,192 美元。票据 24 个月、8% 年利率,总利息 9,610 美元;第一个月还款 5,574 美元,最后一个月 4,837 美元。按 12% 折现率,票据现值 110,900 美元,低于本金 115,315 美元;整个对价的现值是 742,722 美元,名义总额 778,378 美元,时间价值折损 35,656 美元。
| 组成 | 金额 | 时间 | 现值 | 说明 |
|---|---|---|---|---|
| 现金 | 461,261 | 交割日 | 461,261 | 未扣托管与调整 |
| 卖方票据本金 | 115,315 | 24 个月 | 110,900 | 8% 年利率,按月摊还 |
| 卖方票据利息 | 9,610 | 24 个月 | 含在上行 | 按未偿余额计算 |
| earn-out | 192,192 | 12 个月 | 170,561 | 95% 指标达标时全额 |
| 合计 | 778,378 | 742,722 | 名义总额高于报价 |
这张表说明两件事。第一,票据利率低于卖方折现率时,票据的现值低于本金;8% 的票面利率覆盖不了 12% 的机会成本,卖方拿到的利息不足以补偿时间和信用风险。第二,名义总额 778,378 美元高于报价 768,768 美元,因为票据利息和 earn-out 被加进了「总价」;但现值仍然低于报价。名义总额和现值不是一回事,谈判时要把两者分开。
票据的期限也会改变现值。24 个月比 12 个月多一年时间价值,但也多一年信用暴露;如果买方在第 18 个月违约,卖方能收回多少取决于担保和清偿顺序。票据如果有资产抵押、母公司担保或银行保函,风险会低一些;如果是无担保的普通债权,卖方应该用更高的折现率,或者要求更短的期限。票据的利率不是越高越好:过高的利率会把买方的现金流压紧,反而提高违约概率。
earn-out 的计量争议
earn-out 的支付通常由一个或几个指标触发。LedgerLite 的案例是 ARR 不低于基期 95% 时全额支付 30%;Tidecast 的教学版本可以用首 12 个月净得不低于基期 90% 作为指标。指标本身只是一句话,真正决定支付的是计量口径:收入按总额还是净额、退款和折扣算不算、年付客户怎么确认、汇率用哪一天、关联方收入算不算、买方的营销和定价决策算不算。
为了把争议量化,假设 earn-out 采用线性支付:指标低于 80% 不支付,达到 95% 支付 100%,中间按比例。LedgerLite 的 230,630 美元 earn-out 在不同指标下的支付如下。
| 实际 ARR / 基期 | 支付比例 | 支付金额 | 比全额少 |
|---|---|---|---|
| 95.0% | 100.0% | 230,630 | 0 |
| 94.0% | 93.3% | 215,247 | 15,383 |
| 90.0% | 66.7% | 153,761 | 76,869 |
| 80.0% | 0% | 0 | 230,630 |
指标从 95% 掉到 94%,支付比例从 100% 掉到 93.3%,卖方少拿 15,383 美元。这个差距不是来自收入真的少了 1%,而是来自「95% 是全额支付的阈值」这个结构:阈值附近,1 个百分点的指标变化会被放大成 6.7 个百分点的支付变化。如果合同写的是「达到 95% 才支付,否则为零」,94.9% 和 94.0% 的结果一样,都是零;线性支付至少让卖方在阈值以下还能拿一部分。
Tidecast 的教学版本更能说明口径问题。earn-out 是 74,612 美元,指标是首 12 个月净得不低于 138,171 美元的 90%,即 124,354 美元;低于 80% 不支付,达到 90% 支付 100%。如果买方把退款、平台分成或促销折扣算进「净得」的方式和卖方不同,实际指标从 90% 掉到 88%,支付比例变成 80%,卖方拿到 59,690 美元,少拿 14,922 美元。收入只差 2 个百分点,支付差 20 个百分点。
计量争议的根源是控制权。交割后,买方控制定价、营销、产品路线和收入确认;卖方只能看报表。如果 earn-out 写得太粗,买方可以通过削减营销、推迟新版本或改变打包方式,让指标刚好落在阈值以下;如果写得太细,双方会在每个月的报表上吵架。可执行的 earn-out 通常要写清四件事:指标定义、计算期间、买方的经营承诺、争议解决方式。指标定义包括收入口径、退款、折扣、汇率和关联方;经营承诺包括预算、营销投入和产品维护;争议解决包括审计权和专家裁定。
写成代码:earn-out.ts
下面的 earn-out.ts 把三种对价写成一个函数。earnOutPayout 处理 earn-out 的地板、阈值和上限:低于地板支付零,达到阈值支付上限,中间按线性比例。priceConsideration 把报价拆成现金、票据和 earn-out,计算票据利息、每月还款、各组成部分的现值和全额到手时间。
// file: src/capital/earn-out.ts
// tier: L1
// verify: node --test test/capital/earn-out.test.ts
import { type Cents } from "../core/money.ts";
import { type Bps, applyRate } from "../core/rate.ts";
import { type Result, fail, ok } from "../core/result.ts";
export type EarnOutTerms = {
floorBps: Bps;
thresholdBps: Bps;
capBps: Bps;
};
export type EarnOutPayout = {
payoutBps: Bps;
payoutCents: Cents;
atCap: boolean;
belowFloor: boolean;
};
export type ConsiderationInput = {
priceCents: Cents;
cashBps: Bps;
noteBps: Bps;
earnOutBps: Bps;
noteAnnualRateBps: Bps;
noteTermMonths: number;
earnOutTermMonths: number;
annualDiscountRateBps: Bps;
earnOut: EarnOutTerms;
earnOutActualBps: Bps;
};
export type Consideration = {
priceCents: Cents;
cashCents: Cents;
notePrincipalCents: Cents;
earnOutTargetCents: Cents;
noteInterestCents: Cents;
earnOutPayoutCents: Cents;
nominalCents: Cents;
presentValueCents: Cents;
cashPresentValueCents: Cents;
notePresentValueCents: Cents;
earnOutPresentValueCents: Cents;
timeValueDiscountCents: Cents;
monthsToFullPayment: number;
noteFirstPaymentCents: Cents;
noteLastPaymentCents: Cents;
};
function validBps(value: Bps): boolean {
return Number.isInteger(value) && value >= 0 && value <= 10000;
}
/**
* Earn-out payout follows a linear ramp: nothing below the floor, the full cap
* at the threshold, and a proportional share in between. Real contracts often
* use a cliff instead of a ramp; the model makes the measurement dispute
* visible either way. Floor, threshold and cap are teaching assumptions.
*/
export function earnOutPayout(
targetCents: Cents,
terms: EarnOutTerms,
actualBps: Bps,
): Result<EarnOutPayout> {
if (!Number.isInteger(targetCents) || targetCents < 0) {
return fail("earn-out target must be a non-negative integer amount");
}
if (
!validBps(terms.floorBps) ||
!validBps(terms.thresholdBps) ||
!validBps(terms.capBps)
) {
return fail("earn-out terms must be between 0 and 10000 basis points");
}
if (terms.floorBps >= terms.thresholdBps) {
return fail("earn-out floor must be below the threshold");
}
if (!Number.isInteger(actualBps) || actualBps < 0) {
return fail("actual metric must be a non-negative integer in basis points");
}
if (actualBps <= terms.floorBps) {
return ok({
payoutBps: 0,
payoutCents: 0,
atCap: false,
belowFloor: true,
});
}
if (actualBps >= terms.thresholdBps) {
return ok({
payoutBps: terms.capBps,
payoutCents: applyRate(targetCents, terms.capBps),
atCap: true,
belowFloor: false,
});
}
const payoutBps = Math.round(
(terms.capBps * (actualBps - terms.floorBps)) /
(terms.thresholdBps - terms.floorBps),
);
return ok({
payoutBps,
payoutCents: applyRate(targetCents, payoutBps),
atCap: false,
belowFloor: false,
});
}
/**
* Splits the headline price into cash at close, a seller note and an earn-out.
* The note amortizes principal in equal monthly installments and pays interest
* on the outstanding balance. Present value discounts every monthly payment at
* the teaching annual rate. This is arithmetic, not a valuation opinion.
*/
export function priceConsideration(
input: ConsiderationInput,
): Result<Consideration> {
if (!Number.isInteger(input.priceCents) || input.priceCents <= 0) {
return fail("price must be a positive integer amount in cents");
}
if (
!validBps(input.cashBps) ||
!validBps(input.noteBps) ||
!validBps(input.earnOutBps)
) {
return fail("consideration shares must be between 0 and 10000 bps");
}
if (input.cashBps + input.noteBps + input.earnOutBps !== 10000) {
return fail("cash, note and earn-out shares must add up to 100%");
}
if (!validBps(input.noteAnnualRateBps)) {
return fail("note rate must be between 0 and 10000 bps");
}
if (!validBps(input.annualDiscountRateBps)) {
return fail("discount rate must be between 0 and 10000 bps");
}
if (
!Number.isInteger(input.noteTermMonths) ||
input.noteTermMonths <= 0
) {
return fail("note term must be a positive integer month count");
}
if (
!Number.isInteger(input.earnOutTermMonths) ||
input.earnOutTermMonths <= 0
) {
return fail("earn-out term must be a positive integer month count");
}
const cashCents = applyRate(input.priceCents, input.cashBps);
const notePrincipalCents = applyRate(input.priceCents, input.noteBps);
const earnOutTargetCents = applyRate(input.priceCents, input.earnOutBps);
const payout = earnOutPayout(
earnOutTargetCents,
input.earnOut,
input.earnOutActualBps,
);
if (!payout.ok) {
return fail(payout.error);
}
const monthlyPrincipal = Math.floor(
notePrincipalCents / input.noteTermMonths,
);
const lastPrincipal =
notePrincipalCents - monthlyPrincipal * (input.noteTermMonths - 1);
const monthlyNoteRate = input.noteAnnualRateBps / 10000 / 12;
const monthlyDiscountRate = input.annualDiscountRateBps / 10000 / 12;
let outstanding = notePrincipalCents;
let noteInterestCents = 0;
let notePresentValueCents = 0;
let firstPaymentCents = 0;
let lastPaymentCents = 0;
for (let month = 1; month <= input.noteTermMonths; month += 1) {
const principal =
month < input.noteTermMonths ? monthlyPrincipal : lastPrincipal;
const interest = Math.round(outstanding * monthlyNoteRate);
const payment = principal + interest;
if (month === 1) {
firstPaymentCents = payment;
}
lastPaymentCents = payment;
noteInterestCents += interest;
notePresentValueCents +=
payment / Math.pow(1 + monthlyDiscountRate, month);
outstanding -= principal;
}
const earnOutPresentValueCents = Math.round(
payout.value.payoutCents /
Math.pow(1 + monthlyDiscountRate, input.earnOutTermMonths),
);
const roundedNotePresentValueCents = Math.round(notePresentValueCents);
const presentValueCents =
cashCents + roundedNotePresentValueCents + earnOutPresentValueCents;
const nominalCents =
cashCents +
notePrincipalCents +
noteInterestCents +
payout.value.payoutCents;
return ok({
priceCents: input.priceCents,
cashCents,
notePrincipalCents,
earnOutTargetCents,
noteInterestCents,
earnOutPayoutCents: payout.value.payoutCents,
nominalCents,
presentValueCents,
cashPresentValueCents: cashCents,
notePresentValueCents: roundedNotePresentValueCents,
earnOutPresentValueCents,
timeValueDiscountCents: nominalCents - presentValueCents,
monthsToFullPayment: Math.max(
input.noteTermMonths,
input.earnOutTermMonths,
),
noteFirstPaymentCents: firstPaymentCents,
noteLastPaymentCents: lastPaymentCents,
});
}
把 Tidecast 的 70/30 代进去,现金 174,096 美元,earn-out 74,612 美元,现值 240,310 美元,时间价值折损 8,398 美元,全额到手在第 12 个月。LedgerLite 的 70/30 是现金 538,138 美元、earn-out 230,630 美元、现值 742,810 美元、折损 25,958 美元。60/15/25 的票据结构是现金 461,261 美元、票据本金 115,315 美元、earn-out 192,192 美元;票据利息 9,610 美元,现值合计 742,722 美元,名义总额 778,378 美元,全额到手在第 24 个月。
代码里的折现率、票据利率、期限和 earn-out 参数都是输入,不是写死的行业标准。读者可以用自己的机会成本替换 12%,用自己的票据利率替换 8%,看现值怎么变。代码只做算术,不做信用判断;票据能不能收回、earn-out 能不能达标,仍然要看买方的信用、合同条款和经营控制。
小结
- 报价不等于到手:现金、卖方票据和 earn-out 的确定性、时间和风险不同,要分开算。
- Tidecast 的 70/30 结构是现金 174,096 美元加 12 个月 earn-out 74,612 美元,按 12% 折现现值 240,310 美元,折损 8,398 美元。
- LedgerLite 的 70/30 结构是现金 538,138 美元加 12 个月 earn-out 230,630 美元,现值 742,810 美元,折损 25,958 美元。
- 卖方票据是信贷:60/15/25 结构下,115,315 美元票据、8% 年利率、24 个月,总利息 9,610 美元,现值 110,900 美元。
- 名义总额 778,378 美元高于报价 768,768 美元,但现值只有 742,722 美元;名义和现值要分开看。
- earn-out 的争议在计量:LedgerLite 的指标从 95% 掉到 94%,支付从 230,630 美元降到 215,247 美元,少拿 15,383 美元。
- Tidecast 的指标从 90% 掉到 88%,支付从 74,612 美元降到 59,690 美元,少拿 14,922 美元。
- 折现率、票据利率、期限和里程碑参数都是教学假设;代码只做算术,不做信用判断。
阅读导航:上一节:7.1 资产收购与股权收购 · 下一节:7.3 陈述保证、赔偿与托管 。
常见错误与边界
- 把报价当到手。 70% 现金加 30% earn-out 的报价,交割日只拿到 70%;剩下的 30% 要等指标达标,还不一定全额。
- 把 earn-out 当折扣。 earn-out 的 30% 不是价格下调 30%,而是时间价值加履约风险;折现后的损失可能远大于或小于 30%。
- 忽略票据的信用风险。 票据利率高于银行贷款,是因为卖方承担信用风险;无担保、无清偿优先级的票据不能按面值算。
- 用名义总额比较结构。 票据利息会把名义总额推高,但现值可能更低;谈判要看现值,不是看总价。
- 把票据利率定得过高。 利率越高,买方现金流越紧,违约概率越高;票据的利率要和买方的偿付能力一起看。
- earn-out 只写比例,不写口径。 收入按总额还是净额、退款和折扣怎么算、汇率用哪一天,都会改变支付。
- 忽略经营控制。 交割后买方控制定价和营销;没有经营承诺和审计权,earn-out 可以被「管理」到阈值以下。
- 把阈值当成线性。 如果合同是「达到才支付」,94.9% 和 94.0% 都可能是零;线性支付和 cliff 支付的差别要在谈判时写清。
- 忘记托管。 托管通常从现金里切出来,比例和期限会影响交割日净额;本节把托管留到 7.3,不代表它可以忽略。
- 把模型当信用判断。 代码只算现值,不算买方会不会违约、指标会不会达标;这些要靠合同条款和尽调。
本节的边界是「现金、卖方票据与 earn-out 的对价结构」:只讨论比例、利率、期限、里程碑、计量口径、支付上限和现值,不展开托管、赔偿上限或交割清单。所有折现率、利率、期限和里程碑参数都是教学假设,不构成投资、法律或税务意见。托管与赔偿见 7.3 陈述保证、赔偿与托管 ,交割资金流见 8.1 交割清单与资金流 。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。