148 lines
6.7 KiB
Python
148 lines
6.7 KiB
Python
#!/usr/bin/env python3
|
||
"""甜蜜击球区 · 腾讯控股(00700.HK):股价 → 估值指标 投影表生成器
|
||
|
||
数据层定位:本脚本是 data/sweetspot/tencent_*.csv 的唯一生成入口(CSV 是运行时投影,
|
||
禁止手工编辑)。上游底稿是资料库节点「腾讯控股(00700.HK)甜蜜击球区检验」
|
||
(https://www.workbuddy.cn/space/d/Ey5NeiPls71FC12zn0ieSX,2026-09-17 版)。
|
||
|
||
与宁德那套(build_sweet_spot_catl.py)的两处口径差异,都是腾讯特有的:
|
||
1. 纵轴是 EV/EBIT,不是 PE。腾讯的「类现金」是 5,260 亿投资组合(股权、会波动),
|
||
只扣净现金(582 亿)几乎不改变倍数 —— 必须连投资组合一起扣,才是真实的经营资产价格。
|
||
2. 三情景是 EBIT(悲观 2,400 / 基准 2,585 / 乐观 2,900 亿元),不是归母净利。
|
||
分子分母成对:分母扣了投资组合,分子就不能含投资收益,所以用经营盈利口径。
|
||
|
||
计算基准(改这几个常量就能整表重算):
|
||
总股本 90.95 亿股
|
||
汇率 1 HKD = 0.86154 CNY(中国外汇交易中心 2026-09-17 中间价)
|
||
净现金 581.9 亿元
|
||
上市投资 4,872.0 亿元(公允价值)
|
||
非上市投资 387.9 亿元(账面)
|
||
非经营资产 5,841.8 亿元(= 净现金 + 上市 + 非上市,占市值 17.5%)
|
||
TTM EBIT 2,585.43 亿元(= 2,415.62 − 1,176.70 + 1,346.51)
|
||
折旧情景 5 年直线 → 稳态 EBIT 2,247 亿元(占当前 EBIT 13.1%)
|
||
现价 426.8 港元(2026-09-17 收盘)
|
||
甜蜜区 8x 白送 / 10x 甜点 / 12x 上沿(巴菲特 10x 税前锚)
|
||
目标价 12x 上沿 470.5 / 10x 甜点 404.5 港元(基准 EBIT,不给增长)
|
||
|
||
用法:
|
||
python3 scripts/build_sweet_spot_tencent.py
|
||
"""
|
||
|
||
from pathlib import Path
|
||
|
||
SHARES = 90.95 # 亿股
|
||
FX = 0.86154 # 1 HKD = ? CNY
|
||
NET_CASH = 581.9 # 亿元
|
||
LISTED = 4872.0 # 亿元(上市投资,公允价值)
|
||
UNLISTED = 387.9 # 亿元(非上市投资,账面)
|
||
NON_OP = NET_CASH + LISTED + UNLISTED # 5,841.8 亿元
|
||
EBIT = {"bear": 2400.0, "base": 2585.43, "bull": 2900.0}
|
||
EBIT_DEP = 2247.0 # 5 年折旧情景下的稳态 EBIT(亿元)
|
||
NOW = 426.8 # 港元
|
||
EXIT_MULT = {"high": 12.0, "mid": 10.0} # 三年后退出倍数(甜蜜区上沿 / 甜点)
|
||
PRICE_BOTTOM = 240 # 港元,数据区下限
|
||
PRICE_TOP = 640 # 港元,数据区上限
|
||
|
||
OUT_DIR = Path(__file__).resolve().parent.parent / "data" / "sweetspot"
|
||
|
||
# 数据点里必须出现的价位:甜蜜区标尺 5 档 + 现价。
|
||
# 它们要能被折线的数据标签命中(label.position 不接受函数,只能逐点覆盖 data)。
|
||
KEY_PRICES = (
|
||
8 * EBIT["base"] + NON_OP,
|
||
9 * EBIT["base"] + NON_OP,
|
||
10 * EBIT["base"] + NON_OP,
|
||
NOW * SHARES * FX,
|
||
12 * EBIT["base"] + NON_OP,
|
||
)
|
||
KEY_PRICES = tuple(value / (SHARES * FX) for value in KEY_PRICES)
|
||
|
||
|
||
def price_grid(top, step=20, keys=()):
|
||
"""等距网格 + 关键价位去重后升序,保证关键价位本身就是数据点(可被打标签)。"""
|
||
prices = {float(p) for p in range(PRICE_BOTTOM, top + 1, step)}
|
||
prices.update(round(float(k), 1) for k in keys)
|
||
return sorted(prices)
|
||
|
||
|
||
def price_to_hkd(value):
|
||
"""目标价公式:股价 =(目标倍数 × EBIT + 非经营资产)÷ 股本 ÷ 汇率。"""
|
||
return value / (SHARES * FX)
|
||
|
||
|
||
def mult_at(price, ebit, deduct=NON_OP):
|
||
"""EV/EBIT =(市值 − 扣除的非经营资产)÷ EBIT。deduct=0 即不扣任何资产。"""
|
||
return (price * SHARES * FX - deduct) / ebit
|
||
|
||
|
||
def main():
|
||
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
||
|
||
header = (
|
||
"# 由 scripts/build_sweet_spot_tencent.py 生成,请勿手工编辑。\n"
|
||
"# 口径:总股本 90.95 亿股 · 汇率 0.86154 · 非经营资产 5,841.8 亿元"
|
||
"(净现金 581.9 + 上市投资 4,872.0 + 非上市投资 387.9)· "
|
||
"TTM EBIT 2,585.43 亿元 · 三情景 EBIT 2,400/2,585/2,900 亿元 · 现价 426.8 港元(2026-09-17)\n"
|
||
"# 倍数与年化收益保留两位小数(与宁德两份表同精度;倍数一位会把 10.68x 与 10.7x 揉成一个读数)\n"
|
||
)
|
||
|
||
# ---------- 1) 股价 → 倍数对照(图 1、图 2 共用) ----------
|
||
rows = price_grid(PRICE_TOP, 20, KEY_PRICES)
|
||
lines = [
|
||
"price,market_cap,mult_bear,mult_base,mult_bull,mult_net_cash,mult_gross,non_op_pct"
|
||
]
|
||
for px in rows:
|
||
cap = px * SHARES * FX
|
||
lines.append(
|
||
",".join([
|
||
f"{px:.2f}",
|
||
f"{cap:.1f}",
|
||
f"{mult_at(px, EBIT['bear']):.2f}",
|
||
f"{mult_at(px, EBIT['base']):.2f}",
|
||
f"{mult_at(px, EBIT['bull']):.2f}",
|
||
f"{mult_at(px, EBIT['base'], NET_CASH):.2f}",
|
||
f"{mult_at(px, EBIT['base'], 0):.2f}",
|
||
f"{NON_OP / cap * 100:.1f}",
|
||
])
|
||
)
|
||
path = OUT_DIR / "tencent_price_ev.csv"
|
||
path.write_text(header + "\n".join(lines) + "\n", encoding="utf-8")
|
||
print(f"{path.relative_to(OUT_DIR.parent.parent)} {len(rows)} 行")
|
||
|
||
# ---------- 2) 股价 → 三年退出试算(图 3) ----------
|
||
# 目标价三年不变(不给增长):退出在甜蜜区上沿 / 甜点 / 折旧后的上沿,年化只由买入价决定。
|
||
targets = {
|
||
"cagr_12x": price_to_hkd(EXIT_MULT["high"] * EBIT["base"] + NON_OP),
|
||
"cagr_10x": price_to_hkd(EXIT_MULT["mid"] * EBIT["base"] + NON_OP),
|
||
"cagr_12x_dep": price_to_hkd(EXIT_MULT["high"] * EBIT_DEP + NON_OP),
|
||
}
|
||
rows = price_grid(PRICE_TOP, 20, KEY_PRICES)
|
||
lines = ["price,market_cap,mult_base,cagr_12x,cagr_10x,cagr_12x_dep"]
|
||
for px in rows:
|
||
cap = px * SHARES * FX
|
||
lines.append(
|
||
",".join([
|
||
f"{px:.2f}",
|
||
f"{cap:.1f}",
|
||
f"{mult_at(px, EBIT['base']):.2f}",
|
||
*[f"{((value / px) ** (1 / 3) - 1) * 100:.2f}" for value in (
|
||
targets["cagr_12x"], targets["cagr_10x"], targets["cagr_12x_dep"],
|
||
)],
|
||
])
|
||
)
|
||
path = OUT_DIR / "tencent_price_returns.csv"
|
||
path.write_text(header + "\n".join(lines) + "\n", encoding="utf-8")
|
||
print(f"{path.relative_to(OUT_DIR.parent.parent)} {len(rows)} 行")
|
||
|
||
print("\n目标价(港元):" + " | ".join(f"{k} {v:.1f}" for k, v in targets.items()))
|
||
print("现价倍数:" + " | ".join([
|
||
f"悲观 {mult_at(NOW, EBIT['bear']):.2f}x",
|
||
f"基准 {mult_at(NOW, EBIT['base']):.2f}x",
|
||
f"乐观 {mult_at(NOW, EBIT['bull']):.2f}x",
|
||
f"只扣净现金 {mult_at(NOW, EBIT['base'], NET_CASH):.2f}x",
|
||
f"不扣 {mult_at(NOW, EBIT['base'], 0):.2f}x",
|
||
]))
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|