小书战胜市场 — 完整实施规范

作者:Joel Greenblatt 首次出版:2005年 核心策略:魔法公式——一个简单的、机械的量化价值投资策略,结合高 earnings yield 与高 return on capital 来识别以便宜价格购买的优秀公司。


目录

  1. 哲学概述
  2. 魔法公式 — 核心方法论
  3. Earnings Yield 计算
  4. Return on Capital 计算
  5. 排名和选择系统
  6. 入场和出场规则
  7. 投资组合构建
  8. 风险管理规则
  9. 行为规则
  10. 常见错误
  11. 交易生命周期示例
  12. 实施伪代码
  13. 关键语录

1. 哲学概述

Joel Greenblatt 的 genius 在于蒸馏。他提取了价值投资的本质——以低于平均价格购买高于平均的业务——并将其简化为两个可机械应用的可衡量变量。魔法公式设计得足够简单,让个人投资者能够实施,但足够严格,足以长期跑赢专业基金经理。

核心洞察

价值投资有效,因为市场在短期内经常是非理性的。价格因情绪反应、机构约束和行为偏见而偏离内在价值。魔法公式通过系统识别以下公司来利用这一点:

  1. 优秀业务(高 return on capital employed)
  2. 以 bargain 价格提供(高 earnings yield)

组合至关重要。便宜但糟糕业务的公司(value traps)被 return on capital 要求过滤掉。贵但优秀的公司(growth traps)被 earnings yield 要求过滤掉。只有在两个维度都得分良好的公司才能通过筛选。

为什么有效 — Greenblatt 的论证

因素 解释
短期低效 市场因对坏消息的反应、忽视、机构抛售压力在1-3年内错误定价股票
长期有效 在3-5年内,价格随着基本面变得不可否认而趋向内在价值
行为障碍 公式有效恰恰是因为它令人不舒服——它迫使你购买不受喜欢的股票并持有 through underperformance
简单性障碍 大多数专业人士无法向董事会和客户 justification 一个"简单"的策略——复杂性偏见创造机会

历史表现(Greenblatt 的回测)


2. 魔法公式 — 核心方法论

2.1 两个因素

因素 指标 它衡量什么 为什么重要
Earnings Yield EBIT / 企业价值 相对于盈利力量股票有多便宜 识别 bargain 价格
Return on Capital EBIT / (净营运资本 + 净固定资产) 业务将投入资本转化为盈利的效率 识别优秀业务

2.2 为什么用 EBIT 和企业价值(而不是 P/E 比率)

Greenblatt 故意避免传统 P/E 比率因为:

P/E 问题

EBIT/EV 优势

2.3 为什么用有形资本(而不是总资产或权益)

传统 ROE 被杠杆扭曲——公司可以通过承担债务来拥有高 ROE。Greenblatt 的资本回报使用有形运营资产:


3. Earnings Yield 计算

3.1 公式

Earnings Yield = EBIT / 企业价值

其中:

3.2 详细成分定义

EBIT

企业价值

3.3 解读

Earnings Yield 解读
> 15% 非常便宜 — strong bargain candidate
10-15% 便宜 — good value territory
7-10% 中等 — fair value range
4-7% 贵 — growth expectations priced in
< 4% 非常贵 — requires high growth to justify

3.4 比较基准

将 earnings yield 与无风险利率(10年期国债收益率)比较:


4. Return on Capital 计算

4.1 公式

Return on Capital = EBIT / (净营运资本 + 净固定资产)

其中:

4.2 有形资本 employed

分母代表业务运营所需的有形资本:

4.3 解读

Return on Capital 解读
> 50% 卓越 — likely has a strong moat (brand, network effects, or asset-light model)
25-50% 优秀 — strong competitive position
15-25% 良好 — above-average business quality
10-15% 平均 — typical business
< 10% 低于平均 — commodity business or competitive challenges

4.4 高 Return on Capital 信号

业务持续高回报有形资本告诉你:

  1. 它有定价权(客户愿意支付溢价价格)
  2. 它高效运营(相对于盈利所需资本较低)
  3. 它可能有竞争护城河(否则竞争会侵蚀回报)
  4. 它可以以有吸引力的 rate 再投资利润(增长创造价值)

5. 排名和选择系统

5.1 双排名过程

这是魔法公式的机械核心:

第1步:从符合条件的可投资 universe 开始(所有满足最低标准的股票)

第2步:按 Earnings Yield 排名所有公司(最高 = 排名1,最低 = 排名N)

第3步:按 Return on Capital 排名所有公司(最高 = 排名1,最低 = 排名N)

第4步:计算综合排名 = Earnings Yield 排名 + Return on Capital 排名

第5步:按综合排名排序(最低综合排名 = 最佳总分)

第6步:选择 top 20-30 家公司

5.2 Universe 过滤 — 排除

在排名前排除:

排除类别 原因
金融公司(银行、保险、REITs) 它们的资本结构使 EBIT/EV 和 ROC 毫无意义
公用事业 受监管回报使 ROC 分析不可靠
外国 ADR 会计差异和数据质量问题
市值 < $5000万的公司(或 < $2亿 for conservative approach) 流动性和数据可靠性问题
EBIT 为负的公司 根据定义无法按 earnings yield 排名

5.3 排名示例

公司 EY 排名 ROC 排名 综合排名 选中?
公司 A 5 12 17 是(Top 30)
公司 B 150 2 152 否(好但贵)
公司 C 3 200 203 否(便宜但糟糕)
公司 D 8 8 16 是(Top 30)
公司 E 25 5 30 是(Top 30)

公式自然避免:


6. 入场和出场规则

6.1 入场规则

时机 — 分批入场

Greenblatt 建议随着时间构建投资组合以避免单点时间风险:

  1. 每2-3个月 invest approximately 20-33% of allocated capital
  2. 在9-12个月内,你将拥有一个完全投资的20-30只股票投资组合
  3. 每批5-7只股票从 current top-ranked list 中选择

替代 — 一次性入场

6.2 出场规则 — 严格的年度重新平衡

出场纪律完全是机械的:

对于 tax-loss positions(亏损股票)

对于盈利仓位

关键规则:不要仅仅因为你喜欢就持有股票 beyond the rebalancing date。系统是机械的。如果股票一年后仍然是 top-ranked,它会自然被重新购买。

6.3 重新平衡频率

方法 频率 优点 缺点
年度(推荐) 每年一次 税收效率,低 turnover,少 transaction costs 全年持有;一些持仓可能恶化
半年度 每年两次 更快适应变化的基本面 更高 transaction costs,更多 short-term gains
季度 每季度四次 最响应 最高 costs,大多数 taxable events,noise trading

Greenblatt 的建议:年度重新平衡 with staggered entry(所以你大致每月重新平衡一次,但每个持仓持有约1年)。


7. 投资组合构建

7.1 仓位配置

Equal weight:向每个持仓分配相等美元金额。

投资组合规模 股票数 每只权重
最小可行 20 5.0% each
推荐 30 3.3% each
最大 30 3.3% each

为什么 equal weight(而不是基于信念)

7.2 投资组合换手率

7.3 现金管理

7.4 行业集中度

公式可能在某些市场条件下产生行业集中投资组合(例如,当油价便宜时许多能源股)。Greenblatt 不建议行业上限,因为集中是发现最佳机会的特点。但是,保守投资者可以选择:


8. 风险管理规则

8.1 主要风险控制

  1. 多元化:20-30只股票消除单只股票风险
  2. 机械纪律:基于规则的系统防止情绪化决策
  3. 年度重新平衡:强制退出恶化持仓
  4. 质量过滤:Return on capital 要求筛选掉低质量 value traps
  5. Universe 过滤:最小市值 eliminate micro-cap risks

8.2 预期回撤

关键理解:魔法公式将在 extended periods 内跑输市场。

8.3 跟踪误差风险

这是最大的实际风险——不是亏钱,而是跑输市场:

跑输期间 预期频率 行为挑战
1个季度 很常见 轻度不适
1年 ~25%的年份 严重怀疑
连续2年 偶尔发生 强烈诱惑放弃
连续3年 稀有但可能 大多数投资者在此 capitulate

成功的投资者是那些在跑输期间保持纪律的人。

8.4 公式不保护的 Against


9. 行为规则

9.1 最重要的规则:遵循系统

Greenblatt 强调公式的优势恰恰来自它创造的不适。它选择的股票不受欢迎、不受欢迎,往往有负面新闻流。购买它们 FEELS wrong。这就是公式有效的原因——大多数人们无法做到。

9.2 保持纪律的规则

  1. 不要 override the formula:如果股票出现在 top-ranked list,购买它 regardless of your personal feelings about the company
  2. 不要在重新平衡日期之后持有赢家:对持仓"动心" undermine the mechanical system
  3. 不要因坏消息 early selling:除非重新平衡日期到来,持有持仓
  4. 不要检查每日表现:审查投资组合每月不超过一次;最好每季度一次
  5. 不要每日与市场比较:仅在3-5年期间判断结果
  6. 不要 tinkering with the formula:添加主观过滤器 degrade the system's effectiveness

9.3 承诺合同

在开始之前,写下并签署:

"我承诺遵循魔法公式至少3年。我将购买排名最高的股票。我将年度重新平衡。我不会 override the system。我理解公式大约每4年中有1年会跑输。我将仅在3年最低期限后判断结果。"

9.4 当公式跑输时做什么

  1. 重新阅读书的第13章(Greenblatt 解释为什么需要耐心的解释)
  2. 审查长期历史证据
  3. 记住:公式有效恰恰因为它很困难。如果容易,每个人都会做,edge 会消失
  4. 与你的"investment buddy"交谈(也遵循系统的人)
  5. 不要 look at what you "would have" made in the S&P 500

10. 常见错误

错误1:从排名列表中 Cherry-Picking

错误2:在1-2年跑输后放弃策略

错误3:使用报告收益而不是 EBIT

错误4:将公式应用于金融和公用事业

错误5:运行股票太少的公式

错误6:过于频繁重新平衡

错误7:试图 Timing Entry


11. 交易生命周期示例

初始设置(1年1月)

资本:$100,000 allocated to the Magic Formula strategy。 计划:在3个月内 staggered entry,~每月10只股票,30只股票 total。

第1月:第一批

  1. 筛选 universe:所有美国股票 > $2亿市值,排除金融/公用事业
  2. 计算所有符合条件的公司的 EBIT/EV 和 EBIT/有形资本
  3. 按 Earnings Yield 排名(分配排名1到N)
  4. 按 Return on Capital 排名(分配排名1到N)
  5. 将排名相加获得综合分数
  6. 按综合分数排序(最低 = 最佳)
  7. 从当前排名中选择 top 10 股票
排名 公司 EY 排名 ROC 排名 综合 分配
1 AutoParts Co 8 3 11 $3,333
2 Software Inc 15 1 16 $3,333
3 Retail Chain 4 14 18 $3,333
4 Pharma Corp 6 15 21 $3,333
5 Media Group 2 22 24 $3,333
6 Tech Services 20 5 25 $3,333
7 Consumer Brand 12 16 28 $3,333
8 Industrial Co 7 23 30 $3,333
9 Health Devices 25 7 32 $3,333
10 Energy Equip 3 30 33 $3,333

总投入:$33,330。剩余:$66,670 在货币市场。

第2月:第二批

重复排名过程。选择接下来的10只股票(可能与第1月重叠如果仍排名靠前)。再投资 $33,330。

第3月:第三批

重复。投资组合现在持有30只股票,每只约 $3,333。完全投资。

第6月:年中审查(仅观察)

第12月:第一次重新平衡(1年12月)

1月 batch(10只股票)的税收aware 重新平衡:

股票 回报 行动 税收处理
AutoParts Co +22% 在1月1日之后卖出(长期收益) 再等2周
Software Inc +45% 在1月1日之后卖出(长期收益) 再等2周
Retail Chain -30% 现在卖出(短期损失 for tax benefit) 立即卖出
Pharma Corp +8% 在1月1日之后卖出(长期收益) 再等2周
Media Group -15% 现在卖出(短期损失 for tax benefit) 立即卖出
Tech Services +35% 在1月1日之后卖出(长期收益) 再等2周
Consumer Brand +12% 在1月1日之后卖出(长期收益) 再等2周
Industrial Co -5% 现在卖出(短期损失 for tax benefit) 立即卖出
Health Devices +55% 在1月1日之后卖出(长期收益) 再等2周
Energy Equip -20% 现在卖出(短期损失 for tax benefit) 立即卖出

立即卖出4个输家。等2周卖出6个赢家。用新筛选的当前排名最高的股票替换全部10只。

第1年结果

第3年结果


12. 实施伪代码

class MagicFormulaInvestor:
    def __init__(self, config):
        self.capital = config['initial_capital']
        self.target_positions = config.get('target_positions', 30)
        self.min_market_cap = config.get('min_market_cap', 200_000_000)
        self.rebalance_frequency = 'annual'
        self.portfolio = {}
        self.purchase_dates = {}
        self.excluded_sectors = ['Financials', 'Utilities']

    def screen_universe(self, market_data):
        universe = []
        for stock in market_data.all_stocks():
            if stock.market_cap < self.min_market_cap:
                continue
            if stock.sector in self.excluded_sectors:
                continue
            if stock.ebit <= 0:
                continue
            if stock.is_adr:
                continue
            universe.append(stock)
        return universe

    def calculate_earnings_yield(self, stock):
        excess_cash = max(stock.cash - stock.revenue * 0.03, 0)
        enterprise_value = (stock.market_cap + stock.total_debt - excess_cash)
        if enterprise_value <= 0:
            return 0
        return stock.ebit / enterprise_value

    def calculate_return_on_capital(self, stock):
        net_working_capital = stock.current_assets - stock.current_liabilities
        net_fixed_assets = stock.ppe_net
        tangible_capital = net_working_capital + net_fixed_assets
        if tangible_capital <= 0:
            return float('inf')
        return stock.ebit / tangible_capital

    def rank_stocks(self, universe):
        for stock in universe:
            stock.earnings_yield = self.calculate_earnings_yield(stock)
            stock.return_on_capital = self.calculate_return_on_capital(stock)

        ey_sorted = sorted(universe, key=lambda s: s.earnings_yield, reverse=True)
        for rank, stock in enumerate(ey_sorted, 1):
            stock.ey_rank = rank

        roc_sorted = sorted(universe, key=lambda s: s.return_on_capital, reverse=True)
        for rank, stock in enumerate(roc_sorted, 1):
            stock.roc_rank = rank

        for stock in universe:
            stock.combined_rank = stock.ey_rank + stock.roc_rank

        return sorted(universe, key=lambda s: s.combined_rank)

    def select_top_stocks(self, ranked_universe, count):
        return ranked_universe[:count]

    def execute_staggered_entry(self, market_data, months=3):
        stocks_per_batch = self.target_positions // months
        capital_per_batch = self.capital / months

        for month in range(months):
            universe = self.screen_universe(market_data)
            ranked = self.rank_stocks(universe)
            available = [s for s in ranked if s.ticker not in self.portfolio]
            selections = self.select_top_stocks(available, stocks_per_batch)
            amount_per_stock = capital_per_batch / len(selections)

            for stock in selections:
                self.buy(stock, amount_per_stock)
                self.purchase_dates[stock.ticker] = market_data.current_date

    def annual_rebalance(self, current_date, market_data):
        sells_now = []
        sells_later = []

        for ticker, position in self.portfolio.items():
            purchase_date = self.purchase_dates[ticker]
            holding_period = (current_date - purchase_date).days

            if holding_period >= 350:
                if position.unrealized_return < 0:
                    sells_now.append(ticker)
                else:
                    sells_later.append(ticker)

        for ticker in sells_now:
            self.sell(ticker)

        for ticker in sells_later:
            self.schedule_sell(ticker, days_until=14)

        universe = self.screen_universe(market_data)
        ranked = self.rank_stocks(universe)
        replacements_needed = len(sells_now)
        available = [s for s in ranked if s.ticker not in self.portfolio]
        new_stocks = self.select_top_stocks(available, replacements_needed)

        for stock in new_stocks:
            amount = self.capital / self.target_positions
            self.buy(stock, amount)

13. 关键语录

"魔法公式有效因为它是一种系统性方式购买高于平均的业务 at below-average prices。"

"公式不会每年有效。这是 guarantee。而这正是它长期有效的原因。"

"在没有任何想法的情况下选择个别股票就像在炸药工厂里挥舞燃烧的火柴。你可能活下来,但你还是个白痴。"

"如果公式每年都有效,每个人都会使用它,它就会停止有效。跑输期间是保持其有效性的原因。"

"投资的秘密是找出一些东西的价值——然后少付很多。"

"大多数人排名事物:他们想要最好的。魔法公式只是使用两个简单的排名并结合它们。就这样。它优雅是因为它省略了什么。"

"公式在三年或更长的每个可衡量期间都跑赢了市场平均。但很少有人有纪律坚持它。"

"Ben Graham 发现廉价股票 as a group 跑赢市场平均。魔法公式只是添加了洞察:廉价的好业务做得更好。"

"你可以机械地遵循公式,或者将公式的输出作为你自己研究的起点。无论哪种方式,它都给你一个巨大的 head start。"

"在短期内,Market 先生是一台投票机。长期来看,他是一台称重机。魔法公式旨在购买称重机最终将奖励的东西。"


基于 Joel Greenblatt 的"The Little Book That Beats the Market"实施。本规范捕获了完整的魔法公式系统——一种机械的、量化价值投资策略,专为寻求通过纪律性、基于规则的投资跑赢市场的个人投资者设计。