#!/usr/bin/env python3 """甜蜜击球区 · 宁德时代(300750):股价 → 估值指标 投影表生成器 数据层定位:本脚本是 data/sweetspot/*.csv 的唯一生成入口(CSV 是运行时投影, 禁止手工编辑)。上游底稿是资料库节点「宁德时代_股价PE对照表」 (https://www.workbuddy.cn/space/d/EsAWMaLKt4ECKPDvESKKvT,2026-09-15 版)。 计算基准(改这几个常量就能整表重算): 总股本 46.27 亿股 净现金 2,263.8 亿元 (每股 48.93 元) 2026E 归母 悲观 900 / 中性 964 / 乐观 1,000 亿元(65 家机构一致预期) 分红率 50%(2023–2025 年实际值) 现价 316.21 元(2026-09-15 收盘) 2028E 目标 1,400 亿 × 20x → 605 元(老唐法三年后合理估值,用于试算年化收益) 无风险利率 1.68%(10 年期国债,仅用于股息率倍数的脚注说明) 精度例外:PE 与股息率保留两位小数。CLAUDE.md 默认一位,但 PE 一位会把 15.18x 与 15.2x 揉成同一个读数,而「现价到底落在哪个区间」正是这张表要回答的 问题(买入区上沿 15.5x 与合理区下沿贴得极近),故按行业惯例取两位。 用法: python3 scripts/build_sweet_spot_catl.py """ from pathlib import Path SHARES = 46.27 # 亿股 NET_CASH = 2263.8 # 亿元 NOW = 316.21 # 元 PAYOUT = 0.50 NI = {"bear": 900.0, "base": 964.0, "bull": 1000.0} TARGET_2028 = 605.0 # 元(2028E 1,400 亿 × 20x ÷ 46.27 亿股) OUT_DIR = Path(__file__).resolve().parent.parent / "data" / "sweetspot" def price_grid(top, step=20, keys=()): """等距网格 + 关键价位去重后升序,保证关键价位本身就是数据点(可被打标签)。""" prices = {float(p) for p in range(220, top + 1, step)} prices.update(float(k) for k in keys) return sorted(prices) def pct(value): return value * 100 def main(): OUT_DIR.mkdir(parents=True, exist_ok=True) header = ( "# 由 scripts/build_sweet_spot_catl.py 生成,请勿手工编辑。\n" "# 口径:总股本 46.27 亿股 · 净现金 2,263.8 亿元(每股 48.93 元)· " "2026E 归母 900/964/1,000 亿元 · 分红率 50% · 现价 316.21 元(2026-09-15)\n" "# PE 与股息率保留两位小数(见脚本 docstring 的精度例外说明)\n" ) # ---------- 1) 股价 → PE 对照(图 1、图 2 共用) ---------- rows = price_grid(700, 20, (265, 290, 303, NOW, 378, 605, 617)) lines = [ "price,market_cap,pe_bear,pe_base,pe_bull,pe_ex_cash,dividend_yield,net_cash_pct" ] for px in rows: cap = px * SHARES lines.append( ",".join([ f"{px:.2f}", f"{cap:.1f}", f"{cap / NI['bear']:.2f}", f"{cap / NI['base']:.2f}", f"{cap / NI['bull']:.2f}", f"{(cap - NET_CASH) / NI['base']:.2f}", f"{pct(NI['base'] * PAYOUT / cap):.2f}", f"{pct(NET_CASH / cap):.1f}", ]) ) path = OUT_DIR / "catl_price_pe.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) ---------- rows = price_grid(620, 20, (265, 290, 303, NOW, 378, 450, TARGET_2028)) lines = [ "price,market_cap,pe_base,dividend_yield,cagr_price,cagr_total" ] for px in rows: cap = px * SHARES dy = NI["base"] * PAYOUT / cap cagr = (TARGET_2028 / px) ** (1 / 3) - 1 # 总收益按 (1 + 价差年化) × (1 + 股息率) − 1:股息以买入价口径年年复投, # 与资料库试算器的「未含分红」口径并列展示,两者差额即分红的贡献。 lines.append( ",".join([ f"{px:.2f}", f"{cap:.1f}", f"{cap / NI['base']:.2f}", f"{pct(dy):.2f}", f"{pct(cagr):.1f}", f"{pct((1 + cagr) * (1 + dy) - 1):.1f}", ]) ) path = OUT_DIR / "catl_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)} 行") if __name__ == "__main__": main()