以下情境为教学假设,人物与数字不代表经核实的真实案例。
股权常被写成一个数字:你占 50%,他占 50%。但 50% 的股份、50% 的投票、50% 的分红,是三件可以分开的事。融资、引入合伙人、设置期权池,改的往往不是同一个数字。把三层拆开,才能回答「谁说了算」「谁拿钱」「谁可以拦住谁」。
Nord Studio 是这一节的主案例:两位合伙人各 50%,没有期权池,也没有书面退出条款。它的年收入 154,320 美元,SDE 34,320 美元,4.5 倍 SDE 的估值锚是 154,440 美元。下面所有比例和金额都从这三个数推出,不引入第二套事实。
Tidecast 是一人公司:年净得 138,171 美元,没有合伙人,也没有 50/50 结构。它的股权问题不是僵局,而是单一所有者离开时 IP、账号和发行协议如何承继;Nord 至少有两个决策人,Tidecast 的关键人风险更集中。
三层结构:所有权、表决权、分红权
第一层是所有权,按 fully diluted 的股份数算。它决定「公司值钱时你分到多少」,也是融资稀释最直接的刻度。第二层是表决权,按每股投票数算。第三层是分红权,按分红单位算;默认一票一权、一股一分红,但优先股、利润分享权、同股不同权都会让三层分叉。
| 层 | 计算口径 | Nord 50/50 | 同股不同权示例 | 被什么改变 |
|---|---|---|---|---|
| 所有权 | 持股数 ÷ 总股本 | 50% / 50% | 45% / 45% / 10% | 新股发行、老股转让、期权池 |
| 表决权 | 投票数 ÷ 总投票数 | 50% / 50% | 49.45% / 49.45% / 1.10% | 每股投票数、一致行动、否决权 |
| 分红权 | 分红单位 ÷ 总分红单位 | 50% / 50% | 45% / 45% / 10% | 分红上限、优先股、利润分享权 |
Nord 的两层完全重合:450,000 股对 450,000 股,每股一票,每股一个分红单位。重合让事情看起来简单,也让问题被藏起来:一旦两个人意见相反,50% 对 50% 就是两个人都没有多数。
先记住一个口径:所有权看「发行了多少股」,表决权看「这些股带多少票」,分红权看「这些股分多少利润」。三者可以相同,但不能假设相同。
50/50 僵局:成本可以算出来
50/50 本身不是错误,错误是「50/50 且没有打破僵局的机制」。普通决议需要超过半数;两位合伙人各 50%,任何一位都能让决议停在原地。停一天不是抽象损失,它可以用 SDE 计价。
Nord 的年 SDE 是 34,320 美元,月均 2,860 美元。僵局持续 3 个月,损失 3 × 2,860 = 8,580 美元;6 个月是 17,160 美元;12 个月是 34,320 美元,正好是一整年的 SDE。
| 僵局时长 | 按 SDE 计价的机会成本 | 占年 SDE |
|---|---|---|
| 3 个月 | 8,580 美元 | 25.0% |
| 6 个月 | 17,160 美元 | 50.0% |
| 12 个月 | 34,320 美元 | 100.0% |
如果僵局拖住的是一笔出售,成本还可以用现值算。Nord 的估值锚是 154,440 美元;假设教学折现率 20%,延迟 12 个月出售的现值损失是 154,440 × (1 − 1 ÷ 1.2) = 25,740 美元,相当于估值锚的 16.7%。这不是「少赚一点」,而是把 154,440 美元的报价拖成了 128,700 美元的现值。
打破僵局的结构有四类,代价不同:
- 51/49:一方有普通多数,决策快,但少数方失去制衡,通常会要求否决权或董事会席位作为交换。
- 主席决定票:50/50 加一条「平票时主席多一票」,成本低,但主席人选本身就是新的争夺点。
- 分级表决:把日常经营和重大事项分开,日常按多数,重大事项要求更高比例。
- 买卖选择权(shotgun):一方报价,另一方选择按同价买入或卖出;它把僵局变成一次性博弈,但需要双方都有现金。
这些是结构算术,不是法律意见;具体条款要由当地执业律师起草。
引入第三位股东:45/45/10 与表决权杠杆
Nord 要引入第三位股东,有三条路径,比例完全不同。第一条是新发 100,000 股给第三人:原股本 900,000 股,新总股本 1,000,000 股,两位合伙人各 45%,第三人 10%。第二条是新发 225,000 股:新总股本 1,125,000 股,两位合伙人各 40%,第三人 20%。第三条是其中一位合伙人转让 100,000 股老股:总股本不变,转让方降到 40%,受让方 10%,另一位仍是 50%。
| 路径 | 合伙人 A | 合伙人 B | 第三人 C | 总股本 |
|---|---|---|---|---|
| 新发 100,000 股 | 45% | 45% | 10% | 1,000,000 |
| 新发 225,000 股 | 40% | 40% | 20% | 1,125,000 |
| A 转让 100,000 股 | 40% | 50% | 10% | 900,000 |
新发和老股转让的区别,不只是比例:新发把 100,000 股的价值从两位合伙人身上稀释出来,老股转让只是换了一个股东,公司没有拿到新钱。把两者写成同一张「融资后股权表」,会漏掉「钱有没有进公司」这一行。
比例之外,还要看表决权。45/45/10 的表决权看起来是 45%、45%、10%,但用 Banzhaf 权力指数算,第三位股东的权力是三分之一。原因是普通决议需要超过 50%,任何两位股东结盟都能过:A+B 是 90%,A+C 是 55%,B+C 是 55%。在三场获胜联盟里,C 出现在两场,A 和 B 也各出现在两场;总 pivotal 次数是 6,每人 2 次,所以每人 33.3%。
| 股权表 | 所有权 | 表决权(一票一股) | Banzhaf 权力 |
|---|---|---|---|
| 50/50 | 50 / 50 | 50 / 50 | 50.0 / 50.0 |
| 45/45/10 | 45 / 45 / 10 | 45 / 45 / 10 | 33.3 / 33.3 / 33.3 |
| 45/45/10,创始人 10 票/股 | 45 / 45 / 10 | 49.45 / 49.45 / 1.10 | 33.3 / 33.3 / 33.3 |
| 51/29/20 | 51 / 29 / 20 | 51 / 29 / 20 | 100 / 0 / 0 |
| 45/45/10,A+B 一致行动 | 45 / 45 / 10 | 45 / 45 / 10 | 50.0 / 50.0 / 0 |
这张表最反直觉的是第三行:同股不同权把第三人的投票比例从 10% 压到 1.10%,但没有改变他的 Banzhaf 权力。两位创始人各 49.45% 的投票,谁都不能单独过半;第三人那 1.10% 仍然是打破平票的那一票。只要两位创始人股份相等,同股不同权就压不掉第三人的 pivotal 地位。
第四行是另一种极端:51/29/20 里,51% 的股东单独构成获胜联盟,Banzhaf 权力全部归他,29% 和 20% 都是零。第五行是第三种做法:A 和 B 签一致行动协议,合计 90%,第三人即使持股 10%,也永远不在获胜联盟里,权力归零。
所以「控制权」有三种来源:股份多数、投票权倍数、以及联盟。三者可以叠加,也可以互相替代。融资时投资人常说的「保护性条款」,就是第四条来源:不靠投票多数,而是在特定事项上要求同意。
同股不同权、一致行动与否决权
同股不同权解决的是「融资后创始人被稀释到 50% 以下」的问题。做法是给创始人股份更高的每股投票数,例如 10 票/股。Nord 的 45/45/10 在 10 票/股下,投票权变成 49.45%、49.45%、1.10%;两位创始人合计 98.9%,第三人只有 1.10%。但如上表,第三人的 Banzhaf 权力仍是三分之一,因为两位创始人之间的平衡没有被打破。
一致行动解决的是「两个股东能不能当成一个投票单位」。A 和 B 签一致行动协议后,45% + 45% = 90%,在任何普通决议上都过;第三人 10% 既不能阻止,也不能成为关键票。代价是 A 和 B 之间要先统一意见,僵局从「三个人谈」变成「两个人先谈」。
否决权解决的是「少数股东怎么保护自己」。它不是持股比例,而是一张事项清单:预算、招聘、借款、发行新股、出售公司、改变业务方向。第三人只有 10%,但如果清单里有「出售公司需第三人同意」,他就能拦住一笔 102,960 美元的拖售。否决权的成本是决策延迟;按前面的口径,3 个月延迟就是 8,580 美元的 SDE。
把三种安排放在一起,Nord 的选择不是「哪种最好」,而是「哪种代价可以接受」:同股不同权保创始人控制,但投资人会要求否决权;一致行动保两个人同步,但把第三人推到对立面;否决权保少数股东,但把决策变慢。每一种都要用数字标价,而不是只写进章程。
把结构写成代码
下面的 equity.ts 把三层结构固化成函数:equityLayers 同时返回所有权、表决权和分红权,issueShares 处理新股发行,banzhaf 用获胜联盟计算权力指数,deadlockCost 和 delayCost 把僵局与延迟折成金额。所有股份是整数,比率是基点,非法输入返回 Result 错误。
// file: src/capital/equity.ts
// tier: L1
// verify: node --test test/capital/equity.test.ts
import { type Cents } from "../core/money.ts";
import { type Bps, divide } from "../core/rate.ts";
import { type Result, fail, ok } from "../core/result.ts";
export type Holder = {
name: string;
shares: number;
votesPerShare: number;
dividendUnits?: number;
};
export type EquityLayer = {
name: string;
ownershipBps: Bps;
votingBps: Bps;
dividendBps: Bps;
};
export type EquityStructure = {
holders: EquityLayer[];
totalShares: number;
totalVotes: number;
totalDividendUnits: number;
dualClass: boolean;
};
export type VotingPower = {
name: string;
powerBps: Bps;
};
function validate(holders: Holder[]): Result<null> {
if (holders.length === 0) {
return fail("at least one holder is required");
}
const names = new Set<string>();
for (const holder of holders) {
if (holder.name.trim() === "") {
return fail("holder name must not be empty");
}
if (names.has(holder.name)) {
return fail(`duplicate holder: ${holder.name}`);
}
names.add(holder.name);
if (!Number.isInteger(holder.shares) || holder.shares <= 0) {
return fail(`invalid shares for ${holder.name}`);
}
if (!Number.isInteger(holder.votesPerShare) || holder.votesPerShare < 0) {
return fail(`invalid votes for ${holder.name}`);
}
const units = holder.dividendUnits ?? holder.shares;
if (!Number.isInteger(units) || units < 0) {
return fail(`invalid dividend units for ${holder.name}`);
}
}
return ok(null);
}
/**
* Three layers of the same cap table: ownership by shares, voting by
* votes, and dividends by dividend units. By default one share carries
* one vote and one dividend unit; both can be overridden per holder.
*/
export function equityLayers(holders: Holder[]): Result<EquityStructure> {
const checked = validate(holders);
if (!checked.ok) {
return checked;
}
const totalShares = holders.reduce((acc, holder) => acc + holder.shares, 0);
const totalVotes = holders.reduce(
(acc, holder) => acc + holder.shares * holder.votesPerShare,
0,
);
const totalDividendUnits = holders.reduce(
(acc, holder) => acc + (holder.dividendUnits ?? holder.shares),
0,
);
if (totalVotes <= 0) {
return fail("total votes must be positive");
}
if (totalDividendUnits <= 0) {
return fail("total dividend units must be positive");
}
const layers = holders.map((holder) => ({
name: holder.name,
ownershipBps: divide(holder.shares, totalShares),
votingBps: divide(holder.shares * holder.votesPerShare, totalVotes),
dividendBps: divide(
holder.dividendUnits ?? holder.shares,
totalDividendUnits,
),
}));
const dualClass = new Set(holders.map((holder) => holder.votesPerShare)).size > 1;
return ok({
holders: layers,
totalShares,
totalVotes,
totalDividendUnits,
dualClass,
});
}
/** Add a newly issued holder without changing anyone else's share count. */
export function issueShares(
holders: Holder[],
name: string,
newShares: number,
votesPerShare: number,
dividendUnits?: number,
): Result<EquityStructure> {
if (!Number.isInteger(newShares) || newShares <= 0) {
return fail("new shares must be a positive integer");
}
const candidate: Holder = { name, shares: newShares, votesPerShare };
if (dividendUnits !== undefined) {
candidate.dividendUnits = dividendUnits;
}
return equityLayers([...holders, candidate]);
}
/** SDE lost while a decision waits, using a straight months/12 proration. */
export function deadlockCost(
annualSdeCents: Cents,
months: number,
): Result<Cents> {
if (!Number.isInteger(annualSdeCents) || annualSdeCents < 0) {
return fail("annual SDE must be a non-negative integer amount");
}
if (!Number.isInteger(months) || months < 0) {
return fail("months must be a non-negative integer");
}
return ok(Math.round((annualSdeCents * months) / 12));
}
/** Present-value loss from delaying a sale, with an explicit discount rate. */
export function delayCost(
valueCents: Cents,
months: number,
annualDiscountBps: Bps,
): Result<Cents> {
if (!Number.isInteger(valueCents) || valueCents < 0) {
return fail("value must be a non-negative integer amount");
}
if (!Number.isInteger(months) || months < 0) {
return fail("months must be a non-negative integer");
}
if (!Number.isInteger(annualDiscountBps) || annualDiscountBps < 0) {
return fail("discount rate must be a non-negative integer in basis points");
}
const years = months / 12;
const factor = 1 - 1 / Math.pow(1 + annualDiscountBps / 10000, years);
return ok(Math.round(valueCents * factor));
}
/**
* Banzhaf power for a small cap table: count the winning coalitions in
* which each holder is pivotal, then divide by all pivotal appearances.
* A 10% holder can carry a third of the power when the other two are tied.
*/
export function banzhaf(holders: Holder[]): Result<VotingPower[]> {
const checked = validate(holders);
if (!checked.ok) {
return checked;
}
if (holders.length > 12) {
return fail("banzhaf supports at most 12 holders");
}
const votes = holders.map((holder) => holder.shares * holder.votesPerShare);
const totalVotes = votes.reduce((acc, value) => acc + value, 0);
if (totalVotes <= 0) {
return fail("total votes must be positive");
}
const majority = Math.floor(totalVotes / 2) + 1;
const pivotal = new Array<number>(holders.length).fill(0);
const subsets = 1 << holders.length;
for (let mask = 1; mask < subsets; mask += 1) {
let coalition = 0;
for (let i = 0; i < holders.length; i += 1) {
if (mask & (1 << i)) {
coalition += votes[i];
}
}
if (coalition < majority) {
continue;
}
for (let i = 0; i < holders.length; i += 1) {
if ((mask & (1 << i)) && coalition - votes[i] < majority) {
pivotal[i] += 1;
}
}
}
const totalPivotal = pivotal.reduce((acc, value) => acc + value, 0);
if (totalPivotal === 0) {
return fail("no pivotal votes in this cap table");
}
return ok(
holders.map((holder, index) => ({
name: holder.name,
powerBps: divide(pivotal[index], totalPivotal),
})),
);
}
代码有四个刻意的边界。第一,多数门槛是 floor(totalVotes / 2) + 1,所以恰好 50% 不算多数,50/50 必须找到第二个盟友。第二,banzhaf 只支持 12 位以内的股东,因为它是枚举所有联盟;股东更多时要用近似或按投票块合并。第三,dividendUnits 可以独立于股份数,用来表示利润分享权或分红上限,但默认等于股份。第四,所有股份和金额都是整数,比例用基点,避免浮点在多轮融资后累积误差。
小结
- 股权有三层:所有权看股份,表决权看票数,分红权看分红单位;三层可以重合,也可以分开。
- Nord 的 50/50 在两层上完全重合,但没有打破僵局的机制;3 个月僵局按 SDE 计价是 8,580 美元,12 个月是 34,320 美元。
- 延迟出售 12 个月,在 20% 教学折现率下损失 25,740 美元,相当于 154,440 美元估值锚的 16.7%。
- 引入第三人后,45/45/10 的所有权是 45/45/10,但 Banzhaf 权力是 33.3/33.3/33.3;10% 的股东是打破平票的关键票。
- 同股不同权把第三人的投票比例压到 1.10%,但只要两位创始人股份相等,他的 pivotal 权力不变。
- 51/29/20 让 51% 的股东拿走全部 Banzhaf 权力;一致行动协议则让 A+B 的 90% 变成稳定多数,第三人归零。
- 控制权可以来自股份多数、投票权倍数、联盟与否决权;每一种都要用比例和金额标价。
阅读导航:上一节:1.3 什么时候不该融资 · 下一节:2.2 期权池与归属 。
常见错误与边界
- 把所有权当成表决权。 45% 的股份不一定有 45% 的表决权;同股不同权、一致行动、否决权都会改变实际控制。
- 把 50/50 当成公平。 公平是结果,不是结构;没有平票机制时,50/50 把每一次分歧都变成停摆。
- 只算僵局的直接损失。 僵局还会延迟招聘、产品和出售;3 个月 8,580 美元只是 SDE 口径,不含机会成本。
- 把新发和老股转让混为一谈。 新发 100,000 股让总股本变成 1,000,000,老股转让仍是 900,000;前者公司拿到钱,后者没有。
- 认为同股不同权一定保住控制。 两位创始人股份相等时,第三人那 1.10% 的投票仍是关键票;要真正归零,需要一致行动或一方超过 50%。
- 用持股比例替代 Banzhaf 权力。 45/45/10 里第三人只有 10% 股份,却有 33.3% 的权力;51/29/20 里 29% 的股东权力是 0。
- 忽略否决权的延迟成本。 一张事项清单可以让 10% 的股东拦住出售;拦住 3 个月就是 8,580 美元的 SDE。
- 把一致行动协议当成永远有效。 协议有期限、退出和违约条款;一方退出后,90% 的联盟可能瞬间变成 45/45/10。
- 在融资后才想起期权池。 期权池放在 pre-money 还是 post-money,稀释的是不同的人;结构要在签字前算,不是签字后补。
- 把结构算术当成法律意见。 本节只算比例和金额;章程、股东协议、投票权安排要由当地执业律师起草。
股权结构解决「谁说了算」,下一节解决「还没进来的人拿什么」。两者共用同一张 cap table,但一个是现在,一个是未来。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。