复利信徒——完整实施规范

基于李杰,《复利信徒》(2020)


目录

  1. 概述
  2. 复利哲学
  3. 定义优质公司
  4. 估值框架
  5. 投资三个维度
  6. 组合构建
  7. 持有纪律
  8. 何时卖出
  9. 风险管理
  10. 行为规则
  11. 常见错误
  12. 完整投资生命周期示例
  13. 实施伪代码
  14. 重要语录/原则

1. 概述

李杰的核心信念是,复利——资本随时间的指数增长——是投资中最强大但最不被大多数市场参与者尊重的力量。本书不是关于在一年内找到十倍股。它是关于构建一个在10-20年内产生15-25%年化回报的过程,让复利发挥财富创造的主要作用。

核心论点建立在三个支柱上:

  1. 复利需要时间。 20%的年化回报持有20年,将1变成38.3。同样的回报只持有5年只能将1变成2.5。大多数投资者通过过于频繁交易、过早卖出赢家、 reset 复利时钟来摧毁他们的复利轨迹。

  2. 复利需要质量。 只有具有持久竞争优势的企业才能保持足够长的高资本回报率以使复利发挥作用。ROE为8%的平庸企业在数十年内无法有效复利。复利的引擎是底层业务,而非股票价格。

  3. 复利需要纪律。 投资者必须抵抗中断复利的行为陷阱:在熊市中恐慌性抛售、在牛市中追逐动量、出于恐惧过度多元化、将活动与进步混为一谈。

李杰从中国A股投资者的视角撰写,研究了巴菲特、芒格和西方价值投资经典,然后将这些原则适应于中国资本市场的特定现实——更高的波动性、散户主导的交易、周期性监管冲击和快速发展的经济格局。本书将格雷厄姆-多德基本面主义与中国股市投资的具体挑战连接起来。

操作目标:识别8-15家卓越企业,在其公允价值或低于公允价值时买入,持有3-10年,让底层ROE复合股东价值。只有当业务恶化、估值达到极端高估或出现明显更好的机会时才卖出。


2. 复利哲学

2.1 复利的数学

李杰坚持投资者在任何投资决策之前先内化原始数学:

年化回报 5年 10年 15年 20年 30年
10% 1.61x 2.59x 4.18x 6.73x 17.4x
15% 2.01x 4.05x 8.14x 16.4x 66.2x
20% 2.49x 6.19x 15.4x 38.3x 237x
25% 3.05x 9.31x 28.4x 86.7x 808x

关键洞见:15%和20%的年化回报在5年内看起来差异不大(2.01x vs 2.49x),但在20年内变得 enormous(16.4x vs 38.3x)。这就是为什么保护复利 rate 比 maximize 任何单一年度回报更重要。

2.2 复利的三个敌人

  1. 永久性资本损失。 50%的损失需要100%的收益才能盈亏平衡。单一的灾难性损失可以摧毁十年的复利。避免永久性损失比 capture 最大上行更重要。

  2. 中断。 每次你卖出一个复利者并买入其他东西时,你 reset 复利时钟,产生交易成本和税收,并引入再投资风险。换手的摩擦成本比大多数投资者意识到的要高得多。

  3. 平庸的再投资。 如果你以20%复利5年,然后卖出,以10%再投资接下来的5年,你的10年总CAGR roughly 是15%——不是20%。复利只有在你持续投资于高质量资产时才能全功率运作。

2.3 复利心态


3. 定义优质公司

3.1 ROE框架

李杰使用持续ROE作为质量的主要过滤器。他的层级:

ROE范围 分类 行动
> 20% 持续5+年 卓越 核心持仓候选
15-20% 持续5+年 优秀 强候选
12-15% 持续5+年 良好 如果其他因素强则可接受
< 12% 持续 平庸 避免——无法有效复利

关键规则: ROE必须是持续的,而非周期性的。一家在繁荣年份赚取25% ROE、在衰退年份赚取5%的公司平均ROE为15%,但 NOT 一个优质复利者。真正的复利者通过完整的经济周期保持ROE高于15%。

3.2 杜邦分解——首选驱动因素

并非所有ROE都是 equal。李杰按可持续性对ROE来源进行排名:

  1. 高净利率(最好)。 表明定价权、品牌实力或独特IP。例子:茅台、领先制药公司。高利润率ROE最持久,因为它不依赖于杠杆或极端效率。

  2. 高资产周转率(好)。 表明运营卓越和规模优势。例子:领先零售商、主要消费品分销商。周转率驱动的ROE稳定,但需要持续的运营纪律。

  3. 高财务杠杆(最差)。 表明借来的回报。杠杆驱动的ROE是脆弱的——它在好时期放大收益,在坏时期创造生存风险。避免主要依赖债务的公司ROE。

实施规则: 将ROE分解为利润率、周转率和杠杆。偏好至少60%的ROE来自利润率和周转率 combined,且非金融公司的债务权益比低于1.0。

3.3 护城河清单

李杰用中国市场 specifics 适应巴菲特的护城河框架:

护城河类型 描述 持久性 例子
品牌/定价权 消费者愿意支付溢价;无近似的替代品 非常高 茅台、海天酱油
转换成本 客户因集成、数据或习惯而被锁定 企业软件、银行
网络效应 价值随每个额外用户增加 非常高(罕见) 腾讯、阿里平台
成本优势(规模) 单位经济随 volume 改善;新进入者无法匹配 领先家电制造商
监管/许可证 政府批准创建壁垒 中等(政策风险) 银行许可证、制药批准
无形资产/IP 专利、专有技术、独特知识 可变 领先制药研发管线

最低护城河要求: 一个优质公司必须至少有一个清晰可识别的护城河,该护城河已在至少一次经济衰退中得到测试且完整存活。

3.4 管理层质量评估

因素 寻找什么 红旗
资本配置 高ROIC再投资、纪律严明的并购、回购低估值 帝国建设并购、虚荣项目
股东对齐 重大内部人所有权、持续股息 过度稀释、质押股份
业绩记录 5+年历史上承诺已兑现 频繁战略转向、missed 指导
诚信 透明报告、保守会计 关联交易、激进的收入确认
行业愿景 理解 secular 趋势、主动定位 被反应、复制竞争对手

硬性规则: 如果管理层有股东不友好的资本配置历史(过度稀释、过高的并购、价值破坏的多元化),无论其他优点如何,都要取消资格。

3.5 财务健康过滤器

指标 最低标准
ROE(5年平均) >= 15%
收入增长(5年CAGR) >= 10%
净利率稳定性 5年标准差 < 5个百分点
自由现金流 过去5年中至少4年为正
FCF/净收入 5年平均 >= 70%
债务权益比(非金融) < 1.0
利息保障倍数 > 5x
股息派发 > 0%(一些回报给股东)
商誉/总资产 < 20%

4. 估值框架

4.1 内在价值估算

李杰使用简化的DCF, anchored on 所有者收益,而非报告的EPS:

所有者收益 = 净收入 + 折旧/摊销 - 维护性资本支出
            ≈ 自由现金流(在大多数情况下)

内在价值 = 所有者收益 × 正当市盈率倍数

其中正当市盈率 = f(增长率, ROE可持续性, 护城河持久性)

4.2 正当市盈率表

增长+质量概况 正当市盈率范围
卓越护城河 + 20%+增长 25-35x
强劲护城河 + 15-20%增长 20-28x
良好护城河 + 10-15%增长 15-22x
中等护城河 + 5-10%增长 12-17x
弱护城河 + < 5%增长 8-12x

调整因素:

4.3 安全边际

李杰根据信念程度定义明确的安全边际要求:

信念程度 相对于内在价值所需的折扣
非常高(深度理解、已证实的复利者) >= 20%
高(强分析、一些不确定性) >= 30%
中等(合理论点、重大未知) >= 40%
低(投机性、未证实) 不买

操作规则: 永远不要以高于估计内在价值的价格购买任何股票,无论叙事、动量或错过恐惧如何。安全边际是不可协商的。

4.4 估值交叉验证

没有单一估值方法足够。李杰要求至少两种确认方法:

  1. 相对于历史范围的市盈率。 当前市盈率在公司自身5年和10年市盈率范围内处于什么位置?低于中位数 preferred;低于25百分位是理想的。

  2. PEG比率。 市盈率除以预期收益增长率。PEG < 1.0有吸引力;PEG < 0.7是 compelling;对于非卓越业务 PEG > 1.5 signals 高估。

  3. EV/EBIT。 企业价值相对于运营收益。与不同资本结构的公司比较有用。大多数优质企业的目标EV/EBIT < 15。

  4. 股息收益率底线。 对于成熟复利者,高于历史平均水平的股息收益率提供了估值底线。当收益率处于历史范围的上四分位时,股票可能被低估。

  5. 自由现金流收益率。 自由现金流/市值。对于优质业务,FCF收益率 > 5%有吸引力;> 7%是 compelling。


5. 投资三个维度

李杰的中心分析框架从三个独立维度评估每项投资。必须所有三个维度都 favorable 才能发起持仓。

5.1 维度1:好生意

这回答了:"这是一个能够在长期复合价值的业务吗?"

清单:

评分: 分配0-10。最低门槛:7/10。

5.2 维度2:好价格

这回答了:"我支付的是否低于业务的价值?"

清单:

评分: 分配0-10。最低门槛:6/10。

5.3 维度3:好时机

这回答了:"复利开始运作的条件有利吗?"

李杰明确表示,时机并不意味着技术市场时机或图表模式。它意味着评估宏观和业务周期上下文:

清单:

评分: 分配0-10。最低门槛:5/10。

5.4 三维复合

复合评分 = 业务评分 × 0.50 + 价格评分 × 0.35 + 时机评分 × 0.15

投资最低门槛:复合 >= 7.0
所有三个单独维度必须满足最低门槛。

6. 组合构建

6.1 仓位规模哲学

李杰倡导在高信念想法上集中。多元化是对无知的对冲;复利者寻求对较少名字的深度知识。

组合层级 持仓数量 每持仓分配
核心持仓(最高信念) 3-5 各10-20%
支持持仓(强信念) 3-5 各5-10%
观察持仓(建立信念) 2-5 各2-5%
总组合 8-15 100%

6.2 仓位规模规则

  1. 最大单一持仓: 成本的20%。在上涨至30%之前考虑减仓。
  2. 最小持仓: 组合的2%。低于此,持仓无法 meaningful 影响回报且是干扰。
  3. 新持仓初始规模: 3-5%(观察层级)。只有在投资论点至少在一个盈利周期(2-4个季度)得到确认后才能扩展到核心层级。
  4. 板块集中限制: 单一板块不超过40%。
  5. 现金储备: 维持5-15%现金作为 exceptional 机会的 dry powder。不要持有 > 20%现金 extended 时期——未投资现金是复利的拖累。

6.3 建立持仓

李杰建议金字塔式加仓:

阶段1(初始):在第一个目标价格买入计划完整持仓的30%。
阶段2(确认):在第一次盈利报告确认论点后买入40%。
阶段3(完成):在任何回调或持续确认后买入剩余30%。

时间线:从初始买入到完整持仓3-6个月。

如果股票在完整持仓建立之前上涨20%+,不要追逐。接受较小的持仓并等待回调或将资本重新分配到其他地方。

6.4 再平衡


7. 持有纪律

7.1 长期心态

李杰的核心行为要求:一旦你以公平价格拥有一个优质复利者,你的默认 action 是持有。卖出 burden of proof is on selling,而非 on holding。

持有框架:

7.2 持有在实践中是什么样的

7.3 处理回撤

李杰为回撤情况提供了明确规则:

回撤 业务完整? 行动
-10至-20% 持有。这是正常波动。
-10至-20% 不确定 审查论点。如果论点完整,持有。
-20至-30% 如果低于内在价值 consider adding to position。
-20至-30% 减少持仓30-50%。
> -30% 如果信念高且有现金,强烈买入。
> -30% 立即退出。

关键区别始终是价格下跌(暂时的)和价值下跌(potentially 永久的)之间。 复利者在价格下跌期间加仓,在价值下跌期间退出。


8. 何时卖出

8.1 三个卖出触发因素

李杰将卖出限制在三个明确定义的场景中:

触发1:业务恶化。 证明原始投资合理的竞争优势已实质性削弱。迹象包括:

行动:在得出此结论后1-2周内卖出100%。不要在恶化的业务上平均下跌。

触发2:极端高估。 股价已远远超过任何合理的内在价值 estimate。李杰定义"极端"为:

行动:削减30-50%持仓。保留剩余部分,以防业务增长到其估值。只有当估值达到正当市盈率的2.5倍+时才卖出全部持仓。

触发3:明显更好的机会。 你已识别出一只在所有三个维度(业务质量、价格、时机)上得分更高的新投资,而你无可用现金。这是最罕见的卖出触发因素。

行动:将较低信念持仓 swap no more than 50% to fund 新想法。永远不要卖出100%的已证实复利者来为一个未证实想法提供资金。

8.2 什么不是卖出触发因素


9. 风险管理

9.1 风险层级

李杰按严重程度和可控性对风险进行分类:

风险等级 类型 缓解
1(最高) 永久性资本损失 安全边际、质量过滤器、仓位限制
2 业务模式颠覆 持续护城河监控、板块多元化
3 估值压缩 低于内在价值买入、耐心
4 流动性风险 避免微盘、维持现金缓冲
5(最低) 短期波动 忽略它——这是复利的代价

9.2 投资前风险清单

在发起任何持仓之前,回答这些问题:

  1. 什么可能永久损害这个业务?(技术颠覆、监管变化、竞争进入、管理层欺诈。)
  2. 在最现实的情况下我可能损失多少?(不是最糟糕想象的,而是2-3年内最糟糕 plausible。)
  3. 如果股票明天下跌40%,我会加仓吗? 如果答案不是自信的"是",则持仓过大或信念过低。
  4. 这个持仓是否与现有持仓 create 集中风险?(相同板块、相同宏观敞口、相同客户群。)

9.3 组合层面风险控制

规则 门槛
最大单一持仓(按成本) 20%
最大单一持仓(按市值) 30%(触发削减)
最大单一板块 40%
最大相关 cluster 50%(例如,所有消费+所有中国GDP敏感型)
最小持仓数量 8
最大杠杆 0%(永不使用 margin)
现金储备底线 5%

9.4 反脆弱原则

李杰认为,一个构建良好的复利组合应该从波动性中受益,而不仅仅是生存。当市场恐慌时:

危机期间你能做的最糟糕的事情是成为强制卖家。这就是为什么:永不使用杠杆、始终维持现金、持仓规模使得回撤在心理上可以承受。


10. 行为规则

10.1 纪律框架

规则# 规则 理由
1 永远不要基于单日价格 action 买入或卖出 复利基于业务基本面运作,而非每日噪音
2 永远不要增加已达到最大规模的持仓 集中通过灾难性损失杀死复利
3 无论业务多好,永不跳过估值步骤 为质量支付过高仍会摧毁回报
4 买入前写下论点;每年重新审视 迫使清晰,防止叙事漂移
5 有疑问时,无所事事 复利默认是持有
6 永远不要根据提示、谣言或社交媒体情绪 act 这些吸引了最糟糕的行为冲动
7 跟踪业务指标,而非股价 收入、收益、ROE、FCF——这些才是重要的
8 保持投资日志 记录决策和 reasoning for 未来审查
9 每年审查错误 事后分析建立模式识别
10 以滚动3年最低期限衡量绩效 短期衡量产生短期行为

10.2 情感断路器

当你感到想要进行交易时,李杰规定72小时冷却期:

  1. 写下你想做什么及其原因。
  2. 等待72小时。
  3. 重新阅读你的书面理由。
  4. 如果72小时后 reasoning 仍然成立, proceed。
  5. 如果紧迫感已经消退,冲动是情感的,而非理性的。

例外:唯一豁免72小时规则的情况是欺诈或基本业务失败的证据。在这些情况下,立即卖出。

10.3 认知偏见防御

偏见 如何表现 防御
锚定 固定于购买价格或历史高点 根据当前基本面评估业务,而非过去价格
确认偏见 寻求支持现有持仓的信息 主动寻求反驳证据;分配"魔鬼代言人"审查
损失厌恶 过久持有输家,过早卖出赢家 关注内在价值变化,而非盈亏
近期偏见 在预测中过度权重近期事件 使用5-10年数据窗口,而非1-2个季度
从众 跟随共识进入拥挤交易 维持独立研究过程;对共识持怀疑态度
过度自信 过大规模持仓、低估风险 要求安全边际;硬性仓位限制

11. 常见错误

11.1 摧毁复利的错误

  1. 卖出赢家来买入输家。 "rebalance" by 卖出最佳表现者并添加到最差表现者的 instinct 是背道而驰的。赢家获胜是因为业务正在复利。输家可能正在失败 because the business is 恶化。始终根据业务优点评估,而非盈亏。

  2. 将周期性复苏与 secular 增长混为一谈。 一家收益从周期性低谷翻倍的公司与一家可持续地以15%/年增长收益的公司不同。在周期性峰值收益(看起来像低市盈率)时买入周期性是经典价值陷阱。

  3. 多元化成平庸。 添加第15、20或30个持仓 because it "looks interesting" 稀释了组合的质量。每个新持仓必须清除与现有持仓相同的质量栏。

  4. 忽视资产负债表。 ROE为25%且杠杆为3倍的公司与ROE为25%且无债务的公司不同。杠杆驱动的ROE是脆弱的,可能灾难性崩溃。

  5. 追逐过去表现。 因为股票去年上涨300%就买入。复利 happened 了 previous shareholders。重要的是从今天的价格开始的 forward 回报。

  6. 对流程不耐烦。 以20%/年复利意味着你的资金约每3.6年翻倍。在第一年,绝对收益感觉很小。许多投资者在它有 time to work 之前就放弃了策略。

  7. 将股票投资视为娱乐。 为了刺激而交易,持续检查价格,庆祝每日收益和哀悼每日损失。这种行为与复利不相容。

  8. 买入便宜而非质量。 以8倍市盈率交易的股票可能有原因——业务正在下降。25倍市盈率的股票如果是业务将在十年内以20%复利,可能是更好的价值。

  9. 忽视机会成本。 为"安全"持有现金,而优质业务以公允价值 available。现金赚0-3%,在长期上是 guaranteed 复利 underperformer。

  10. 未能区分波动性和风险。 优质业务中30%的回撤是波动性(暂时的)。由于竞争 displacement 造成的30%下跌是风险(永久的)。对每个的正确反应是 opposite。


12. 完整投资生命周期示例

场景:评估一家领先的主要消费品公司

第一阶段:筛选(第1周)

投资者筛选具有以下条件的公司:

X公司,一家领先的酱油制造商,通过所有筛选:

第二阶段:深度分析(第2-4周)

业务质量(维度1 - 好生意):

估值(维度2 - 好价格):

第三阶段:观察名单和耐心(第1-8个月)

投资者监控季度盈利并等待更好的价格。8个月后,不相关的 broad 市场抛售将股价 push down 25%。

更新估值:

时机(维度3 - 好时机):

复合评分:9 × 0.50 + 8 × 0.35 + 7 × 0.15 = 4.50 + 2.80 + 1.05 = 8.35 远高于7.0最低门槛。继续。

第四阶段:建立持仓(第8-12个月)

完整持仓:组合的10%。分类:支持持仓。

第五阶段:持有和监控(第1-5年)

持仓现在 worth 2.1倍初始投资(包括再投资股息)。CAGR:约16%。继续持有。

第六阶段:最终退出(第7年)

在第7年,新监管框架显著增加整个行业的生产成本合规成本。X公司由于规模可以吸收成本,但净利润率从20%降至15%。ROE降至17%。与此同时,一家技术前沿的竞争对手正在通过直接面向消费者的模式 rapidly gaining share。

评估:护城河正在削弱,虽未 destroy。ROE仍高于15%但 trajectory 为负。竞争格局 shifting。

决定:将持仓从15%减少至7%。将资本重新分配到更高信念持仓。继续监控减少的持仓。

在第8年,利润率稳定在15%但市场份额 loss accelerates。ROE drop to 14%——低于15%最低门槛。卖出触发1(业务恶化)activated。

决定:退出剩余持仓。总持有期:8年。总回报:约3.5倍(包括股息)。CAGR:约17%。


13. 实施伪代码

13.1 质量筛选器

def screen_for_quality(universe):
    """
    First-pass quantitative screen to identify quality compounders.
    Returns list of candidates for deep analysis.
    """
    candidates = []

    for stock in universe:
        financials = get_financials(stock, years=5)

        # ROE filter — must be sustained, not just averaged
        roe_history = financials.roe_by_year  # list of 5 annual ROE values
        if min(roe_history) < 12:
            continue  # ROE dipped below acceptable in at least one year
        if mean(roe_history) < 15:
            continue  # Average ROE too low

        # Revenue growth
        revenue_cagr = cagr(financials.revenue[0], financials.revenue[-1], years=5)
        if revenue_cagr < 10:
            continue

        # Net margin stability
        margin_history = financials.net_margin_by_year
        if std_dev(margin_history) > 5:
            continue  # Margins too volatile

        # Free cash flow quality
        fcf_positive_years = sum(1 for fcf in financials.fcf_by_year if fcf > 0)
        if fcf_positive_years < 4:
            continue

        fcf_to_ni_ratio = mean(financials.fcf_by_year) / mean(financials.net_income_by_year)
        if fcf_to_ni_ratio < 0.70:
            continue  # Earnings quality issue

        # Balance sheet health (non-financial companies)
        if stock.sector != "Financials":
            if financials.debt_to_equity > 1.0:
                continue
            if financials.interest_coverage < 5:
                continue

        # Goodwill check
        if financials.goodwill / financials.total_assets > 0.20:
            continue

        # DuPont quality check
        leverage_contribution = financials.equity_multiplier / (
            financials.net_margin * financials.asset_turnover * financials.equity_multiplier
        )
        if leverage_contribution > 0.40:
            continue  # Too much ROE from leverage

        candidates.append({
            "stock": stock,
            "avg_roe": mean(roe_history),
            "min_roe": min(roe_history),
            "revenue_cagr": revenue_cagr,
            "fcf_quality": fcf_to_ni_ratio,
            "debt_to_equity": financials.debt_to_equity,
            "leverage_pct_of_roe": leverage_contribution,
        })

    # Rank by average ROE descending
    candidates.sort(key=lambda x: x["avg_roe"], reverse=True)
    return candidates

13.2 估值模型

def evaluate_valuation(stock, financials, market_data):
    """
    Multi-method valuation assessment.
    Returns valuation score (0-10) and detailed metrics.
    """
    metrics = {}

    # 1. P/E relative to historical range
    current_pe = market_data.pe_ratio
    pe_history = market_data.pe_history_5yr  # list of daily P/E values
    pe_percentile = percentile_rank(current_pe, pe_history)
    metrics["pe_percentile"] = pe_percentile

    # 2. PEG ratio
    expected_growth = estimate_forward_growth(stock, financials)
    peg = current_pe / expected_growth if expected_growth > 0 else float('inf')
    metrics["peg"] = peg

    # 3. FCF yield
    fcf_yield = financials.latest_fcf / market_data.market_cap * 100
    metrics["fcf_yield"] = fcf_yield

    # 4. EV/EBIT
    ev = market_data.market_cap + financials.total_debt - financials.cash
    ev_ebit = ev / financials.latest_ebit if financials.latest_ebit > 0 else float('inf')
    metrics["ev_ebit"] = ev_ebit

    # 5. Dividend yield relative to history (if applicable)
    if market_data.dividend_yield > 0:
        dy_history = market_data.dividend_yield_history_5yr
        dy_percentile = percentile_rank(market_data.dividend_yield, dy_history)
        metrics["dy_percentile"] = dy_percentile  # Higher = more attractive
    else:
        metrics["dy_percentile"] = None

    # Justified P/E estimation
    moat_strength = assess_moat(stock)  # returns "exceptional", "strong", "good", "moderate", "weak"
    justified_pe = get_justified_pe(moat_strength, expected_growth)
    metrics["justified_pe_low"] = justified_pe[0]
    metrics["justified_pe_high"] = justified_pe[1]

    # Margin of safety
    intrinsic_value_per_share = mean(justified_pe) * financials.latest_eps
    current_price = market_data.price
    margin_of_safety = (intrinsic_value_per_share - current_price) / intrinsic_value_per_share
    metrics["margin_of_safety"] = margin_of_safety

    # Scoring
    score = 0
    if pe_percentile < 25:
        score += 3
    elif pe_percentile < 50:
        score += 2
    elif pe_percentile < 75:
        score += 1

    if peg < 0.7:
        score += 2
    elif peg < 1.0:
        score += 1.5
    elif peg < 1.2:
        score += 1

    if fcf_yield > 7:
        score += 2
    elif fcf_yield > 5:
        score += 1.5
    elif fcf_yield > 4:
        score += 1

    if margin_of_safety > 0.40:
        score += 3
    elif margin_of_safety > 0.30:
        score += 2
    elif margin_of_safety > 0.20:
        score += 1

    metrics["valuation_score"] = min(score, 10)
    return metrics


def get_justified_pe(moat_strength, growth_rate):
    """Map moat + growth to justified P/E range."""
    pe_table = {
        "exceptional": {20: (25, 35), 15: (22, 30), 10: (18, 25), 5: (15, 20)},
        "strong":      {20: (22, 30), 15: (20, 28), 10: (16, 22), 5: (13, 18)},
        "good":        {20: (18, 25), 15: (15, 22), 10: (13, 18), 5: (11, 15)},
        "moderate":    {20: (15, 20), 15: (12, 17), 10: (10, 14), 5: (8, 12)},
        "weak":        {20: (10, 15), 15: (8, 12),  10: (7, 10),  5: (5, 8)},
    }
    # Find nearest growth bucket
    growth_bucket = min(pe_table[moat_strength].keys(),
                        key=lambda g: abs(g - growth_rate))
    return pe_table[moat_strength][growth_bucket]

13.3 组合管理系统

class CompoundingPortfolio:
    """
    Portfolio management implementing Li Jie's compounding framework.
    """
    MAX_SINGLE_POSITION_COST = 0.20      # 20% at cost
    MAX_SINGLE_POSITION_MARKET = 0.30    # 30% at market → trigger trim
    MAX_SECTOR_WEIGHT = 0.40           # 40% per sector
    MIN_POSITION_SIZE = 0.02             # 2% minimum
    MIN_HOLDINGS = 8
    MAX_HOLDINGS = 15
    CASH_FLOOR = 0.05                    # 5% minimum cash
    CASH_CEILING = 0.20                  # 20% maximum cash

    def __init__(self):
        self.positions = {}   # ticker -> Position
        self.cash_pct = 1.0
        self.journal = []

    def evaluate_new_investment(self, stock, financials, market_data):
        """Full three-dimension evaluation."""
        # Dimension 1: Business quality
        business_score = self.score_business(stock, financials)
        if business_score < 7:
            return {"decision": "REJECT", "reason": "Business score below threshold",
                    "business_score": business_score}

        # Dimension 2: Valuation
        valuation = evaluate_valuation(stock, financials, market_data)
        price_score = valuation["valuation_score"]
        if price_score < 6:
            return {"decision": "WATCHLIST", "reason": "Valuation not attractive enough",
                    "business_score": business_score, "price_score": price_score}

        # Dimension 3: Timing
        timing_score = self.score_timing(stock, market_data)
        if timing_score < 5:
            return {"decision": "WATCHLIST", "reason": "Timing unfavorable",
                    "business_score": business_score, "price_score": price_score,
                    "timing_score": timing_score}

        # Composite score
        composite = business_score * 0.50 + price_score * 0.35 + timing_score * 0.15
        if composite < 7.0:
            return {"decision": "WATCHLIST", "reason": "Composite below threshold",
                    "composite": composite}

        return {
            "decision": "BUY",
            "composite": composite,
            "business_score": business_score,
            "price_score": price_score,
            "timing_score": timing_score,
            "suggested_size": self.calculate_initial_size(composite),
            "margin_of_safety": valuation["margin_of_safety"],
        }

    def calculate_initial_size(self, composite_score):
        """Phase 1 initial allocation (30% of planned full position)."""
        if composite_score >= 9.0:
            full_target = 0.15  # 15% for highest conviction
        elif composite_score >= 8.0:
            full_target = 0.10  # 10% for high conviction
        else:
            full_target = 0.05  # 5% for moderate conviction
        return full_target * 0.30  # Phase 1 = 30% of target

    def quarterly_review(self):
        """
        Quarterly portfolio review — the core maintenance loop.
        """
        actions = []
        for ticker, position in self.positions.items():
            latest = get_latest_financials(ticker)
            market = get_market_data(ticker)

            # Check sell triggers
            sell_signal = self.check_sell_triggers(position, latest, market)
            if sell_signal:
                actions.append(sell_signal)
                continue

            # Check position sizing
            current_weight = position.market_value / self.total_value
            if current_weight > self.MAX_SINGLE_POSITION_MARKET:
                trim_amount = current_weight - 0.20  # trim back to 20%
                actions.append({
                    "ticker": ticker,
                    "action": "TRIM",
                    "reason": f"Position exceeds {self.MAX_SINGLE_POSITION_MARKET:.0%}",
                    "trim_pct": trim_amount / current_weight,
                })
            elif current_weight < self.MIN_POSITION_SIZE:
                actions.append({
                    "ticker": ticker,
                    "action": "REVIEW",
                    "reason": "Position below minimum meaningful size",
                    "options": ["Add to reach minimum", "Exit completely"],
                })

            # Log holding status
            self.journal.append({
                "date": today(),
                "ticker": ticker,
                "action": "HOLD",
                "roe": latest.roe,
                "revenue_growth": latest.revenue_growth_yoy,
                "current_pe": market.pe_ratio,
            })

        return actions

    def check_sell_triggers(self, position, financials, market_data):
        """
        Evaluate the three sell triggers.
        Returns sell action or None.
        """
        # Trigger 1: Business deterioration
        if financials.roe < 12:
            return {
                "ticker": position.ticker,
                "action": "SELL_ALL",
                "trigger": "BUSINESS_DETERIORATION",
                "reason": f"ROE dropped to {financials.roe:.1f}%, below 12% floor",
            }

        roe_trend = financials.roe_last_3_years
        if all(roe_trend[i] < roe_trend[i-1] for i in range(1, len(roe_trend))):
            if roe_trend[-1] < 15:
                return {
                    "ticker": position.ticker,
                    "action": "REDUCE_50",
                    "trigger": "BUSINESS_DETERIORATION",
                    "reason": "ROE declining for 3 consecutive years, now below 15%",
                }

        if financials.market_share_change < -2:  # Lost > 2 pct pts share
            return {
                "ticker": position.ticker,
                "action": "REDUCE_50",
                "trigger": "BUSINESS_DETERIORATION",
                "reason": "Significant market share loss",
            }

        # Trigger 2: Extreme overvaluation
        justified_pe = get_justified_pe(
            position.moat_assessment,
            financials.expected_growth
        )
        justified_pe_mid = mean(justified_pe)

        if market_data.pe_ratio > justified_pe_mid * 2.5:
            return {
                "ticker": position.ticker,
                "action": "SELL_ALL",
                "trigger": "EXTREME_OVERVALUATION",
                "reason": f"P/E ({market_data.pe_ratio:.1f}x) > 2.5x justified ({justified_pe_mid:.1f}x)",
            }
        elif market_data.pe_ratio > justified_pe_mid * 2.0:
            return {
                "ticker": position.ticker,
                "action": "TRIM_50",
                "trigger": "OVERVALUATION",
                "reason": f"P/E ({market_data.pe_ratio:.1f}x) > 2.0x justified ({justified_pe_mid:.1f}x)",
            }

        return None  # No sell trigger

    def handle_drawdown(self, ticker, drawdown_pct, business_intact):
        """
        Drawdown response framework.
        drawdown_pct: negative number (e.g., -0.25 for 25% decline)
        business_intact: boolean from qualitative assessment
        """
        if drawdown_pct > -0.10:
            return "HOLD — normal fluctuation"

        if -0.20 <= drawdown_pct <= -0.10:
            if business_intact:
                return "HOLD — normal volatility, business intact"
            else:
                return "REVIEW — investigate business concern"

        if -0.30 <= drawdown_pct < -0.20:
            if business_intact:
                return "CONSIDER_ADD — buy more if below intrinsic value and cash available"
            else:
                return "REDUCE_30_TO_50 — cut position, business may be impaired"

        if drawdown_pct < -0.30:
            if business_intact:
                return "STRONG_BUY — exceptional opportunity if conviction high"
            else:
                return "EXIT_IMMEDIATELY — permanent impairment likely"

13.4 投资日志模板

JOURNAL_ENTRY_TEMPLATE = {
    "date": "YYYY-MM-DD",
    "ticker": "stock_code",
    "action": "BUY | SELL | HOLD | ADD | TRIM",
    "thesis_summary": "2-3 sentence investment thesis",
    "three_dimensions": {
        "business_score": 0,   # 0-10
        "price_score": 0,      # 0-10
        "timing_score": 0,     # 0-10
        "composite": 0.0,      # weighted
    },
    "key_metrics": {
        "roe": 0.0,
        "pe": 0.0,
        "margin_of_safety": 0.0,
        "fcf_yield": 0.0,
    },
    "risk_assessment": "What could go wrong? What is the max loss scenario?",
    "position_size": 0.0,      # % of portfolio
    "review_date": "YYYY-MM-DD",  # next scheduled review
    "emotional_state": "calm | anxious | excited | fearful",
    # If emotional_state != "calm", apply 72-hour cooling period
}

14. 重要语录/原则

"复利的第一条铁律是:永远不要亏损。第二条铁律是:永远不要忘记第一条。" "The first iron law of compounding: never lose money. The second iron law: never forget the first."

"时间是优秀企业的朋友,是平庸企业的敌人。" "Time is the friend of a wonderful business and the enemy of a mediocre one."

"投资中最难的不是找到好公司,而是在找到好公司后什么都不做。" "The hardest part of investing is not finding good companies — it is doing nothing after you have found them."

"真正的复利信徒不关注一年赚了多少,只关注十年后能赚多少。" "A true compound interest believer does not care how much he earns in a year; he only cares about how much he will earn in a decade."

"好公司不等于好投资。好公司加好价格才等于好投资。" "A good company does not equal a good investment. A good company at a good price equals a good investment."

"大多数投资者不是死于熊市的恐惧,而是死于牛市的贪婪。" "Most investors do not die from bear market fear — they die from bull market greed."

"持有优秀公司的最大成本不是资金,而是耐心。" "The greatest cost of holding an excellent company is not capital — it is patience."

"当你的股票下跌30%而你想卖出时,问自己:如果我没有持有这只股票,我会在这个价格买入吗?如果答案是肯定的,那就继续持有。" "When your stock drops 30% and you want to sell, ask yourself: if I did not already own it, would I buy it at this price? If the answer is yes, keep holding."

"投资组合应该像一个球队,每个成员都必须是精英,而不是像一个村庄,什么人都有。" "A portfolio should be like a championship team where every member is elite, not like a village where anyone is welcome."

"频繁交易是复利的天敌。每一次买卖都是对复利链条的一次打断。" "Frequent trading is the mortal enemy of compounding. Every buy and sell interrupts the compounding chain."


实现规范结束。