feat: 增加宁德时代的图表

This commit is contained in:
R524809
2026-09-24 17:14:03 +08:00
parent 3a3d19d5dd
commit f646ee6718
15 changed files with 1459 additions and 240 deletions
+3 -1
View File
@@ -79,7 +79,9 @@ def main():
print(f"{path.relative_to(OUT_DIR.parent.parent)} {len(rows)} 行")
# ---------- 2) 股价 → 试算(图 3) ----------
rows = price_grid(620, 20, (265, 290, 303, NOW, 378, 450, TARGET_2028))
# 价格区间与表 1 保持一致(220–700):站内「现价」输入框是三张图共用的,
# 若这里只到 620,手输 650 时图 3 会取不到值(插值落在数据区外 → NaN)。
rows = price_grid(700, 20, (265, 290, 303, NOW, 378, 450, TARGET_2028))
lines = [
"price,market_cap,pe_base,dividend_yield,cagr_price,cagr_total"
]
+147
View File
@@ -0,0 +1,147 @@
#!/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()