feat: 增加宁德时代图表

This commit is contained in:
R524809
2026-09-21 17:45:21 +08:00
parent a15b3f8027
commit 3a3d19d5dd
24 changed files with 1903 additions and 5 deletions
+21 -3
View File
@@ -53,11 +53,29 @@ button {
background: var(--accent);
border-color: var(--accent);
border-radius: 50%;
font-size: 22px;
line-height: 1;
}
.reload-button:hover { opacity: .88; transform: rotate(-45deg); }
/* 图标继承按钮的 color(currentColor),尺寸只在这里定义,换图标不用动 JS。
20px 是配 36px 圆钮的光学最优值:新图标的弧线比原 ↻ 字形细,19px 会显轻。 */
.reload-button .reload-icon {
display: block;
width: 20px;
height: 20px;
fill: currentColor;
}
/* 悬停转一圈:刷新语义的标准反馈,比原先 ↻ 字形的 -45° 倾斜更贴合新图标 */
.reload-button:hover { opacity: .88; }
.reload-button:hover .reload-icon { animation: reload-spin .6s ease-in-out; }
@keyframes reload-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
.reload-button:hover .reload-icon { animation: none; }
}
.export-button:hover { color: var(--accent); border-color: var(--accent); }
/* ── 合并队列(购物车)────────────────────────────── */
+7
View File
@@ -0,0 +1,7 @@
company,gp_per_wh,margin_data
宁德时代,0.122,20.63
比亚迪*,0.097,21.00
LG新能源†,-0.010,-0.70
中创新航*,0.077,16.71
国轩高科*,0.063,14.02
亿纬锂能,0.078,16.19
1 company gp_per_wh margin_data
2 宁德时代 0.122 20.63
3 比亚迪* 0.097 21.00
4 LG新能源† -0.010 -0.70
5 中创新航* 0.077 16.71
6 国轩高科* 0.063 14.02
7 亿纬锂能 0.078 16.19
+7
View File
@@ -0,0 +1,7 @@
period,catl,byd,calb,guoxuan,eve
2021,0.784,0.720,0.660,0.683,
2022,0.978,0.800,0.872,0.840,
2023,0.889,0.700,0.618,0.720,
2024,0.664,0.550,0.496,0.626,
2025,0.585,0.470,0.459,0.473,0.516
2026H1,0.591,0.460,0.459,0.452,0.483
1 period catl byd calb guoxuan eve
2 2021 0.784 0.720 0.660 0.683
3 2022 0.978 0.800 0.872 0.840
4 2023 0.889 0.700 0.618 0.720
5 2024 0.664 0.550 0.496 0.626
6 2025 0.585 0.470 0.459 0.473 0.516
7 2026H1 0.591 0.460 0.459 0.452 0.483
+7
View File
@@ -0,0 +1,7 @@
company,gp_2023,gp_2026h1
宁德时代,0.198,0.122
比亚迪*,0.154,0.097
LG新能源†,0.119,-0.010
中创新航*,0.080,0.077
国轩高科*,0.112,0.063
亿纬锂能,,0.078
1 company gp_2023 gp_2026h1
2 宁德时代 0.198 0.122
3 比亚迪* 0.154 0.097
4 LG新能源† 0.119 -0.010
5 中创新航* 0.080 0.077
6 国轩高科* 0.112 0.063
7 亿纬锂能 0.078
+7
View File
@@ -0,0 +1,7 @@
period,catl,peer_avg,gap_multiple
2021,0.172,0.080,2.15
2022,0.168,0.118,1.42
2023,0.198,0.096,2.06
2024,0.159,0.087,1.83
2025,0.139,0.075,1.85
2026H1,0.122,0.073,1.68
1 period catl peer_avg gap_multiple
2 2021 0.172 0.080 2.15
3 2022 0.168 0.118 1.42
4 2023 0.198 0.096 2.06
5 2024 0.159 0.087 1.83
6 2025 0.139 0.075 1.85
7 2026H1 0.122 0.073 1.68
+7
View File
@@ -0,0 +1,7 @@
period,catl,byd,lges,calb,guoxuan,eve
2021,0.172,0.144,0.067,0.037,0.123,
2022,0.168,0.144,0.091,0.090,0.147,
2023,0.198,0.154,0.119,0.080,0.112,
2024,0.159,0.121,0.032,0.079,0.095,
2025,0.139,0.103,0.068,0.077,0.068,0.080
2026H1,0.122,0.097,-0.010,0.077,0.063,0.078
1 period catl byd lges calb guoxuan eve
2 2021 0.172 0.144 0.067 0.037 0.123
3 2022 0.168 0.144 0.091 0.090 0.147
4 2023 0.198 0.154 0.119 0.080 0.112
5 2024 0.159 0.121 0.032 0.079 0.095
6 2025 0.139 0.103 0.068 0.077 0.068 0.080
7 2026H1 0.122 0.097 -0.010 0.077 0.063 0.078
+7
View File
@@ -0,0 +1,7 @@
period,catl,byd,lges,calb,guoxuan,global_total
2021,99.5,26.4,59.4,8.0,6.7,301.5
2022,191.6,70.4,70.4,20.0,14.1,517.9
2023,259.7,111.4,95.8,33.4,17.1,705.5
2024,339.3,153.7,96.3,39.4,28.5,894.4
2025,464.7,194.8,108.8,62.8,53.5,1187.0
2026H1,242.7,87.7,52.6,31.2,28.0,608.5
1 period catl byd lges calb guoxuan global_total
2 2021 99.5 26.4 59.4 8.0 6.7 301.5
3 2022 191.6 70.4 70.4 20.0 14.1 517.9
4 2023 259.7 111.4 95.8 33.4 17.1 705.5
5 2024 339.3 153.7 96.3 39.4 28.5 894.4
6 2025 464.7 194.8 108.8 62.8 53.5 1187.0
7 2026H1 242.7 87.7 52.6 31.2 28.0 608.5
+7
View File
@@ -0,0 +1,7 @@
period,catl,byd,lges,calb,guoxuan
2021,33.0,8.7,19.7,2.6,2.2
2022,37.0,13.6,13.6,3.9,2.7
2023,36.8,15.8,13.6,4.7,2.4
2024,37.9,17.2,10.8,4.4,3.2
2025,39.2,16.4,9.2,5.3,4.5
2026H1,39.9,14.4,8.6,5.1,4.6
1 period catl byd lges calb guoxuan
2 2021 33.0 8.7 19.7 2.6 2.2
3 2022 37.0 13.6 13.6 3.9 2.7
4 2023 36.8 15.8 13.6 4.7 2.4
5 2024 37.9 17.2 10.8 4.4 3.2
6 2025 39.2 16.4 9.2 5.3 4.5
7 2026H1 39.9 14.4 8.6 5.1 4.6
+7
View File
@@ -0,0 +1,7 @@
period,catl,byd,lges,calb,guoxuan,eve
2021,22.00,20.00,4.31,5.60,18.00,
2022,17.17,18.00,4.74,10.32,17.50,
2023,22.27,22.00,6.41,13.00,15.50,
2024,23.94,22.00,2.25,15.89,15.14,
2025,23.84,22.00,5.69,16.69,14.39,15.50
2026H1,20.63,21.00,-0.70,16.71,14.02,16.19
1 period catl byd lges calb guoxuan eve
2 2021 22.00 20.00 4.31 5.60 18.00
3 2022 17.17 18.00 4.74 10.32 17.50
4 2023 22.27 22.00 6.41 13.00 15.50
5 2024 23.94 22.00 2.25 15.89 15.14
6 2025 23.84 22.00 5.69 16.69 14.39 15.50
7 2026H1 20.63 21.00 -0.70 16.71 14.02 16.19
+7
View File
@@ -0,0 +1,7 @@
period,catl,byd,lges,calb,guoxuan
2021,95.1,,73.4,95.2,41.0
2022,83.3,71.0,73.6,93.8,46.0
2023,70.5,72.0,69.3,75.6,48.0
2024,76.3,70.0,57.8,78.7,46.0
2025,96.9,82.0,47.6,,71.0
2026H1,94.9,,52.8,,
1 period catl byd lges calb guoxuan
2 2021 95.1 73.4 95.2 41.0
3 2022 83.3 71.0 73.6 93.8 46.0
4 2023 70.5 72.0 69.3 75.6 48.0
5 2024 76.3 70.0 57.8 78.7 46.0
6 2025 96.9 82.0 47.6 71.0
7 2026H1 94.9 52.8
+7
View File
@@ -0,0 +1,7 @@
period,sales_gwh,revenue_yi,asp,cost_per_wh,gp_per_wh,gross_margin
2021,116.7,914.9,0.784,0.612,0.172,22.00
2022,242.0,2365.9,0.978,0.810,0.168,17.17
2023,321.0,2852.5,0.889,0.691,0.198,22.27
2024,381.0,2530.4,0.664,0.505,0.159,23.94
2025,541.0,3165.1,0.585,0.446,0.139,23.84
2026H1,325,1921.2,0.591,0.469,0.122,20.63
1 period sales_gwh revenue_yi asp cost_per_wh gp_per_wh gross_margin
2 2021 116.7 914.9 0.784 0.612 0.172 22.00
3 2022 242.0 2365.9 0.978 0.810 0.168 17.17
4 2023 321.0 2852.5 0.889 0.691 0.198 22.27
5 2024 381.0 2530.4 0.664 0.505 0.159 23.94
6 2025 541.0 3165.1 0.585 0.446 0.139 23.84
7 2026H1 325 1921.2 0.591 0.469 0.122 20.63
+7
View File
@@ -0,0 +1,7 @@
period,china,korea,japan,others,global_total
2021,48.2,30.2,12.0,9.6,301.5
2022,60.4,23.7,7.3,8.6,517.9
2023,63.5,23.2,6.1,7.2,705.5
2024,67.1,18.5,3.9,10.5,894.4
2025,70.4,15.3,3.7,10.6,1187.0
2026H1,72.4,11.7,3.7,12.2,608.5
1 period china korea japan others global_total
2 2021 48.2 30.2 12.0 9.6 301.5
3 2022 60.4 23.7 7.3 8.6 517.9
4 2023 63.5 23.2 6.1 7.2 705.5
5 2024 67.1 18.5 3.9 10.5 894.4
6 2025 70.4 15.3 3.7 10.6 1187.0
7 2026H1 72.4 11.7 3.7 12.2 608.5
+11
View File
@@ -0,0 +1,11 @@
rank,company,region,gwh,share,yoy,global_total
1,宁德时代,中国,242.7,39.9,25.3,608.5
2,比亚迪,中国,87.7,14.4,1.6,608.5
3,LG新能源,韩国,52.6,8.6,8.4,608.5
4,中创新航,中国,31.2,5.1,39.5,608.5
5,国轩高科,中国,28.0,4.6,43.3,608.5
6,松下,日本,22.7,3.7,10.2,608.5
7,亿纬锂能,中国,20.9,3.4,51.7,608.5
8,SK On,韩国,19.0,3.1,-6.7,608.5
9,蜂巢能源,中国,15.7,2.6,40.9,608.5
10,欣旺达,中国,14.5,2.4,13.7,608.5
1 rank company region gwh share yoy global_total
2 1 宁德时代 中国 242.7 39.9 25.3 608.5
3 2 比亚迪 中国 87.7 14.4 1.6 608.5
4 3 LG新能源 韩国 52.6 8.6 8.4 608.5
5 4 中创新航 中国 31.2 5.1 39.5 608.5
6 5 国轩高科 中国 28.0 4.6 43.3 608.5
7 6 松下 日本 22.7 3.7 10.2 608.5
8 7 亿纬锂能 中国 20.9 3.4 51.7 608.5
9 8 SK On 韩国 19.0 3.1 -6.7 608.5
10 9 蜂巢能源 中国 15.7 2.6 40.9 608.5
11 10 欣旺达 中国 14.5 2.4 13.7 608.5
+7
View File
@@ -0,0 +1,7 @@
period,catl,byd,lges,panasonic,sk_on,samsung_sdi,calb,guoxuan,eve,sunwoda,svolt,farasis,global_total
2021,99.5,26.4,59.4,36.3,17.3,14.5,8.0,6.7,,2.6,,2.4,301.5
2022,191.6,70.4,70.4,38.0,27.8,24.3,20.0,14.1,,9.2,,7.4,517.9
2023,259.7,111.4,95.8,42.8,34.7,33.1,33.4,17.1,16.2,10.5,,,705.5
2024,339.3,153.7,96.3,35.1,39.0,29.6,39.4,28.5,20.3,18.8,,,894.4
2025,464.7,194.8,108.8,44.2,44.5,28.9,62.8,53.5,31.3,,28.5,,1187.0
2026H1,242.7,87.7,52.6,22.7,19.0,,31.2,28.0,20.9,14.5,15.7,,608.5
1 period catl byd lges panasonic sk_on samsung_sdi calb guoxuan eve sunwoda svolt farasis global_total
2 2021 99.5 26.4 59.4 36.3 17.3 14.5 8.0 6.7 2.6 2.4 301.5
3 2022 191.6 70.4 70.4 38.0 27.8 24.3 20.0 14.1 9.2 7.4 517.9
4 2023 259.7 111.4 95.8 42.8 34.7 33.1 33.4 17.1 16.2 10.5 705.5
5 2024 339.3 153.7 96.3 35.1 39.0 29.6 39.4 28.5 20.3 18.8 894.4
6 2025 464.7 194.8 108.8 44.2 44.5 28.9 62.8 53.5 31.3 28.5 1187.0
7 2026H1 242.7 87.7 52.6 22.7 19.0 31.2 28.0 20.9 14.5 15.7 608.5
+7
View File
@@ -0,0 +1,7 @@
period,catl,byd,lges,panasonic,sk_on,samsung_sdi,calb,guoxuan,eve,sunwoda,svolt,farasis,cr3,cr5,cr10,china_top10_count,china_share
2021,33.0,8.7,19.7,12.0,5.7,4.8,2.6,2.2,,0.9,,0.8,64.7,79.1,90.4,6,48.2
2022,37.0,13.6,13.6,7.3,5.4,4.7,3.9,2.7,,1.8,,1.4,64.2,76.9,91.4,6,60.4
2023,36.8,15.8,13.6,6.1,4.9,4.7,4.7,2.4,2.3,1.5,,,66.2,77.2,92.8,6,63.5
2024,37.9,17.2,10.8,3.9,4.4,3.3,4.4,3.2,2.3,2.1,,,65.9,74.7,89.5,6,67.1
2025,39.2,16.4,9.2,3.7,3.7,2.4,5.3,4.5,2.6,,2.4,,64.8,74.6,89.4,6,70.4
2026H1,39.9,14.4,8.6,3.7,3.1,,5.1,4.6,3.4,2.4,2.6,,62.9,72.6,87.8,7,72.4
1 period catl byd lges panasonic sk_on samsung_sdi calb guoxuan eve sunwoda svolt farasis cr3 cr5 cr10 china_top10_count china_share
2 2021 33.0 8.7 19.7 12.0 5.7 4.8 2.6 2.2 0.9 0.8 64.7 79.1 90.4 6 48.2
3 2022 37.0 13.6 13.6 7.3 5.4 4.7 3.9 2.7 1.8 1.4 64.2 76.9 91.4 6 60.4
4 2023 36.8 15.8 13.6 6.1 4.9 4.7 4.7 2.4 2.3 1.5 66.2 77.2 92.8 6 63.5
5 2024 37.9 17.2 10.8 3.9 4.4 3.3 4.4 3.2 2.3 2.1 65.9 74.7 89.5 6 67.1
6 2025 39.2 16.4 9.2 3.7 3.7 2.4 5.3 4.5 2.6 2.4 64.8 74.6 89.4 6 70.4
7 2026H1 39.9 14.4 8.6 3.7 3.1 5.1 4.6 3.4 2.4 2.6 62.9 72.6 87.8 7 72.4
+36
View File
@@ -0,0 +1,36 @@
# 由 scripts/build_sweet_spot_catl.py 生成,请勿手工编辑。
# 口径:总股本 46.27 亿股 · 净现金 2,263.8 亿元(每股 48.93 元)· 2026E 归母 900/964/1,000 亿元 · 分红率 50% · 现价 316.21 元(2026-09-15)
# PE 与股息率保留两位小数(见脚本 docstring 的精度例外说明)
price,market_cap,pe_bear,pe_base,pe_bull,pe_ex_cash,dividend_yield,net_cash_pct
220.00,10179.4,11.31,10.56,10.18,8.21,4.74,22.2
240.00,11104.8,12.34,11.52,11.10,9.17,4.34,20.4
260.00,12030.2,13.37,12.48,12.03,10.13,4.01,18.8
265.00,12261.6,13.62,12.72,12.26,10.37,3.93,18.5
280.00,12955.6,14.40,13.44,12.96,11.09,3.72,17.5
290.00,13418.3,14.91,13.92,13.42,11.57,3.59,16.9
300.00,13881.0,15.42,14.40,13.88,12.05,3.47,16.3
303.00,14019.8,15.58,14.54,14.02,12.20,3.44,16.1
316.21,14631.0,16.26,15.18,14.63,12.83,3.29,15.5
320.00,14806.4,16.45,15.36,14.81,13.01,3.26,15.3
340.00,15731.8,17.48,16.32,15.73,13.97,3.06,14.4
360.00,16657.2,18.51,17.28,16.66,14.93,2.89,13.6
378.00,17490.1,19.43,18.14,17.49,15.79,2.76,12.9
380.00,17582.6,19.54,18.24,17.58,15.89,2.74,12.9
400.00,18508.0,20.56,19.20,18.51,16.85,2.60,12.2
420.00,19433.4,21.59,20.16,19.43,17.81,2.48,11.6
440.00,20358.8,22.62,21.12,20.36,18.77,2.37,11.1
460.00,21284.2,23.65,22.08,21.28,19.73,2.26,10.6
480.00,22209.6,24.68,23.04,22.21,20.69,2.17,10.2
500.00,23135.0,25.71,24.00,23.14,21.65,2.08,9.8
520.00,24060.4,26.73,24.96,24.06,22.61,2.00,9.4
540.00,24985.8,27.76,25.92,24.99,23.57,1.93,9.1
560.00,25911.2,28.79,26.88,25.91,24.53,1.86,8.7
580.00,26836.6,29.82,27.84,26.84,25.49,1.80,8.4
600.00,27762.0,30.85,28.80,27.76,26.45,1.74,8.2
605.00,27993.4,31.10,29.04,27.99,26.69,1.72,8.1
617.00,28548.6,31.72,29.61,28.55,27.27,1.69,7.9
620.00,28687.4,31.87,29.76,28.69,27.41,1.68,7.9
640.00,29612.8,32.90,30.72,29.61,28.37,1.63,7.6
660.00,30538.2,33.93,31.68,30.54,29.33,1.58,7.4
680.00,31463.6,34.96,32.64,31.46,30.29,1.53,7.2
700.00,32389.0,35.99,33.60,32.39,31.25,1.49,7.0
1 # 由 scripts/build_sweet_spot_catl.py 生成,请勿手工编辑。
2 # 口径:总股本 46.27 亿股 · 净现金 2,263.8 亿元(每股 48.93 元)· 2026E 归母 900/964/1,000 亿元 · 分红率 50% · 现价 316.21 元(2026-09-15)
3 # PE 与股息率保留两位小数(见脚本 docstring 的精度例外说明)
4 price,market_cap,pe_bear,pe_base,pe_bull,pe_ex_cash,dividend_yield,net_cash_pct
5 220.00,10179.4,11.31,10.56,10.18,8.21,4.74,22.2
6 240.00,11104.8,12.34,11.52,11.10,9.17,4.34,20.4
7 260.00,12030.2,13.37,12.48,12.03,10.13,4.01,18.8
8 265.00,12261.6,13.62,12.72,12.26,10.37,3.93,18.5
9 280.00,12955.6,14.40,13.44,12.96,11.09,3.72,17.5
10 290.00,13418.3,14.91,13.92,13.42,11.57,3.59,16.9
11 300.00,13881.0,15.42,14.40,13.88,12.05,3.47,16.3
12 303.00,14019.8,15.58,14.54,14.02,12.20,3.44,16.1
13 316.21,14631.0,16.26,15.18,14.63,12.83,3.29,15.5
14 320.00,14806.4,16.45,15.36,14.81,13.01,3.26,15.3
15 340.00,15731.8,17.48,16.32,15.73,13.97,3.06,14.4
16 360.00,16657.2,18.51,17.28,16.66,14.93,2.89,13.6
17 378.00,17490.1,19.43,18.14,17.49,15.79,2.76,12.9
18 380.00,17582.6,19.54,18.24,17.58,15.89,2.74,12.9
19 400.00,18508.0,20.56,19.20,18.51,16.85,2.60,12.2
20 420.00,19433.4,21.59,20.16,19.43,17.81,2.48,11.6
21 440.00,20358.8,22.62,21.12,20.36,18.77,2.37,11.1
22 460.00,21284.2,23.65,22.08,21.28,19.73,2.26,10.6
23 480.00,22209.6,24.68,23.04,22.21,20.69,2.17,10.2
24 500.00,23135.0,25.71,24.00,23.14,21.65,2.08,9.8
25 520.00,24060.4,26.73,24.96,24.06,22.61,2.00,9.4
26 540.00,24985.8,27.76,25.92,24.99,23.57,1.93,9.1
27 560.00,25911.2,28.79,26.88,25.91,24.53,1.86,8.7
28 580.00,26836.6,29.82,27.84,26.84,25.49,1.80,8.4
29 600.00,27762.0,30.85,28.80,27.76,26.45,1.74,8.2
30 605.00,27993.4,31.10,29.04,27.99,26.69,1.72,8.1
31 617.00,28548.6,31.72,29.61,28.55,27.27,1.69,7.9
32 620.00,28687.4,31.87,29.76,28.69,27.41,1.68,7.9
33 640.00,29612.8,32.90,30.72,29.61,28.37,1.63,7.6
34 660.00,30538.2,33.93,31.68,30.54,29.33,1.58,7.4
35 680.00,31463.6,34.96,32.64,31.46,30.29,1.53,7.2
36 700.00,32389.0,35.99,33.60,32.39,31.25,1.49,7.0
+32
View File
@@ -0,0 +1,32 @@
# 由 scripts/build_sweet_spot_catl.py 生成,请勿手工编辑。
# 口径:总股本 46.27 亿股 · 净现金 2,263.8 亿元(每股 48.93 元)· 2026E 归母 900/964/1,000 亿元 · 分红率 50% · 现价 316.21 元(2026-09-15)
# PE 与股息率保留两位小数(见脚本 docstring 的精度例外说明)
price,market_cap,pe_base,dividend_yield,cagr_price,cagr_total
220.00,10179.4,10.56,4.74,40.1,46.7
240.00,11104.8,11.52,4.34,36.1,42.0
260.00,12030.2,12.48,4.01,32.5,37.8
265.00,12261.6,12.72,3.93,31.7,36.9
280.00,12955.6,13.44,3.72,29.3,34.1
290.00,13418.3,13.92,3.59,27.8,32.4
300.00,13881.0,14.40,3.47,26.3,30.7
303.00,14019.8,14.54,3.44,25.9,30.3
316.21,14631.0,15.18,3.29,24.1,28.2
320.00,14806.4,15.36,3.26,23.7,27.7
340.00,15731.8,16.32,3.06,21.2,24.9
360.00,16657.2,17.28,2.89,18.9,22.3
378.00,17490.1,18.14,2.76,17.0,20.2
380.00,17582.6,18.24,2.74,16.8,20.0
400.00,18508.0,19.20,2.60,14.8,17.8
420.00,19433.4,20.16,2.48,12.9,15.7
440.00,20358.8,21.12,2.37,11.2,13.8
450.00,20821.5,21.60,2.31,10.4,12.9
460.00,21284.2,22.08,2.26,9.6,12.0
480.00,22209.6,23.04,2.17,8.0,10.4
500.00,23135.0,24.00,2.08,6.6,8.8
520.00,24060.4,24.96,2.00,5.2,7.3
540.00,24985.8,25.92,1.93,3.9,5.9
560.00,25911.2,26.88,1.86,2.6,4.5
580.00,26836.6,27.84,1.80,1.4,3.2
600.00,27762.0,28.80,1.74,0.3,2.0
605.00,27993.4,29.04,1.72,0.0,1.7
620.00,28687.4,29.76,1.68,-0.8,0.9
1 # 由 scripts/build_sweet_spot_catl.py 生成,请勿手工编辑。
2 # 口径:总股本 46.27 亿股 · 净现金 2,263.8 亿元(每股 48.93 元)· 2026E 归母 900/964/1,000 亿元 · 分红率 50% · 现价 316.21 元(2026-09-15)
3 # PE 与股息率保留两位小数(见脚本 docstring 的精度例外说明)
4 price,market_cap,pe_base,dividend_yield,cagr_price,cagr_total
5 220.00,10179.4,10.56,4.74,40.1,46.7
6 240.00,11104.8,11.52,4.34,36.1,42.0
7 260.00,12030.2,12.48,4.01,32.5,37.8
8 265.00,12261.6,12.72,3.93,31.7,36.9
9 280.00,12955.6,13.44,3.72,29.3,34.1
10 290.00,13418.3,13.92,3.59,27.8,32.4
11 300.00,13881.0,14.40,3.47,26.3,30.7
12 303.00,14019.8,14.54,3.44,25.9,30.3
13 316.21,14631.0,15.18,3.29,24.1,28.2
14 320.00,14806.4,15.36,3.26,23.7,27.7
15 340.00,15731.8,16.32,3.06,21.2,24.9
16 360.00,16657.2,17.28,2.89,18.9,22.3
17 378.00,17490.1,18.14,2.76,17.0,20.2
18 380.00,17582.6,18.24,2.74,16.8,20.0
19 400.00,18508.0,19.20,2.60,14.8,17.8
20 420.00,19433.4,20.16,2.48,12.9,15.7
21 440.00,20358.8,21.12,2.37,11.2,13.8
22 450.00,20821.5,21.60,2.31,10.4,12.9
23 460.00,21284.2,22.08,2.26,9.6,12.0
24 480.00,22209.6,23.04,2.17,8.0,10.4
25 500.00,23135.0,24.00,2.08,6.6,8.8
26 520.00,24060.4,24.96,2.00,5.2,7.3
27 540.00,24985.8,25.92,1.93,3.9,5.9
28 560.00,25911.2,26.88,1.86,2.6,4.5
29 580.00,26836.6,27.84,1.80,1.4,3.2
30 600.00,27762.0,28.80,1.74,0.3,2.0
31 605.00,27993.4,29.04,1.72,0.0,1.7
32 620.00,28687.4,29.76,1.68,-0.8,0.9
+1040
View File
File diff suppressed because it is too large Load Diff
+26
View File
@@ -46,6 +46,15 @@ import {
moutaiBaseWineVolumeChart, moutaiFlyingPriceChart, moutaiPriceAdjustmentChart,
moutaiTonPriceChart, moutaiDividendChart, moutaiDistributorsChart,
} from './moutai.js';
import {
peerInstallChart, peerUtilizationChart, peerShareChart,
peerGpPerWhChart, peerAspChart, peerMarginChart, peer2026h1Chart,
peerGpGapChart, peerGpChangeChart, catlUnitEconomicsChart, catlVolumeGpChart,
globalCrChart, globalTop10Chart, globalRegionShareChart,
} from './catl.js';
import {
sweetCatlPricePeChart, sweetCatlNetCashChart, sweetCatlAnyPriceChart,
} from './sweet_spot.js';
/** 图表构建函数注册表。manifest 只引用这里的 key,不直接引用 JS 函数。 */
export const chartRegistry = {
@@ -114,6 +123,23 @@ export const chartRegistry = {
'moutai-ton-price': moutaiTonPriceChart,
'moutai-dividend': moutaiDividendChart,
'moutai-distributors': moutaiDistributorsChart,
'catl-unit-economics': catlUnitEconomicsChart,
'catl-volume-gp': catlVolumeGpChart,
'peer-install': peerInstallChart,
'peer-utilization': peerUtilizationChart,
'peer-share': peerShareChart,
'peer-gp-per-wh': peerGpPerWhChart,
'peer-asp': peerAspChart,
'peer-margin': peerMarginChart,
'peer-2026h1': peer2026h1Chart,
'peer-gp-gap': peerGpGapChart,
'peer-gp-change': peerGpChangeChart,
'global-cr': globalCrChart,
'global-top10': globalTop10Chart,
'global-region-share': globalRegionShareChart,
'sweet-catl-price-pe': sweetCatlPricePeChart,
'sweet-catl-net-cash': sweetCatlNetCashChart,
'sweet-catl-any-price': sweetCatlAnyPriceChart,
};
export function getChartBuilder(key) {
+421
View File
@@ -0,0 +1,421 @@
import {
PALETTE, FONT, FINANCIAL_COLORS, axisCommon, titleStyle, standardLegend,
lineSeries, footnote, watermarks, LAYOUT, tooltipOf,
} from '../theme.js';
/**
* 甜蜜击球区 · 宁德时代(300750)—— 股价 ↔ 2026E 估值 的换算图表
*
* 上游底稿:资料库节点「宁德时代_股价PE对照表」
* https://www.workbuddy.cn/space/d/EsAWMaLKt4ECKPDvESKKvT(2026-09-15 版)
* 数据由 scripts/build_sweet_spot_catl.py 投影成 data/sweetspot/*.csv。
*
* 【口径】所有 PE 都是**静态预期 PE**:市值 ÷ 2026E 归母净利(悲观 900 / 中性 964 /
* 乐观 1,000 亿元,65 家机构一致预期)。扣现金 PE =(市值 − 净现金 2,263.8 亿元)÷
* 2026E 净利,即"只给主营电池业务定价"。股息率按 2023–2025 年实际分红率 50% 测算。
*
* 【甜蜜区框架】8x 白送 · 10x 甜点 · 12x 上沿 是巴菲特锚;资料库原表用的是五档
* 更宽的区域(按中性情景 PE 划分),因为它的分子是"含净现金的市值"。两套档位不要
* 混读:本文所有区域带一律用原表的五档,定义集中在 PE_ZONES,改档位只改这一处。
*
* 【图 1】股价 → PE 对照(PE 轴上的横向区域带)
* 【图 2】含现金 PE vs 扣现金 PE(沙色带 = 净现金的厚度)
* 【图 3】任意股价试算(股价轴上的纵向区域带 + 三年预期年化收益)
*
* 精度例外:PE 保留两位小数。项目默认一位,但"现价落在买入区还是合理区"取决于
* 15.18x 与 15.5x 这条边界,一位小数会把 15.18 读成 15.2、与 15.5 分不清。
*
* 硬编码说明:区域边界的**股价**不是写死的,而是从 CSV 的 pe_base 反解(peToPrice),
* 所以改股本或利润预期后纵向区域带会自动跟着走。本文只有两个手工常量:现价 NOW 与
* 净现金 NET_CASH(仅用于文案),都与 build_sweet_spot_catl.py 同源。
*/
/** 现价(2026-09-15 收盘)。与生成脚本同源,重跑脚本时要同步。 */
const NOW = 316.21;
/** 净现金(亿元)。仅用于图 2 的文案;沙色带的几何量取自 CSV 的 pe_base − pe_ex_cash。 */
const NET_CASH = 2263.8;
/** 2028E 目标价(1,400 亿 × 20x ÷ 46.27 亿股),图 3 的年化收益基准。 */
const TARGET_2028 = 605;
/**
* 五档估值区域(按中性情景 964 亿 PE 的上沿划分)。
* 原表把 15.5–25x 细分为「合理偏低」与「合理」两档、但共用同一个配色,
* 这里并为一条色带,避免在 PE 轴上叠出两块无差别的底色。
*/
const PE_ZONES = [
{ name: '击球区', upTo: 13, color: PALETTE.green, note: '<13x' },
{ name: '买入区', upTo: 15.5, color: PALETTE.blue, note: '13–15.5x' },
{ name: '合理', upTo: 25, color: PALETTE.orange, note: '15.5–25x' },
{ name: '偏贵', upTo: 30, color: PALETTE.red, note: '25–30x' },
{ name: '卖出区', upTo: Infinity, color: PALETTE.purple, note: '>30x' },
];
const PE_MIN = 8;
const PE_MAX = 38;
const PRICE_MIN = 200;
const PRICE_MAX = 720;
const SOURCE_NOTE = '数据来源:宁德时代 2026 中报(本地财报库)· 65 家机构 2026E 一致预期';
/** 十六进制转 rgba —— 区域带要低透明度叠在网格上,不能直接铺饱和色 */
function tint(hex, alpha) {
const value = parseInt(hex.slice(1), 16);
return `rgba(${(value >> 16) & 255},${(value >> 8) & 255},${value & 255},${alpha})`;
}
/** 取某价位的某个指标;缺失返回 NaN,让标签渲染成 NaN 而不是抛错 */
function metricAt(rows, price, key) {
const row = rows.find((item) => item.price === price);
return row ? row[key] : Number.NaN;
}
/** 数值轴(PE / 收益率共用),与 catl.js 的 valueAxis 同一套样式 */
const valueAxis = (name, extra = {}) => ({
...axisCommon,
type: 'value',
name,
nameLocation: 'middle',
nameGap: 56,
nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT },
...extra,
});
/**
* 股价轴:数值轴。
* 不画竖向网格(规范只保留浅色横线)—— 图 3 的纵向区域带本身就是参考线,
* 图 1、图 2 靠「现价」竖线定位。
*/
const priceAxis = (extra = {}) => ({
...axisCommon,
type: 'value',
name: '股价(元)',
nameLocation: 'middle',
nameGap: 34,
min: PRICE_MIN,
max: PRICE_MAX,
axisLine: { show: true, lineStyle: { color: PALETTE.axisLine } },
splitLine: { show: false },
...extra,
});
const GRID = { left: 78, right: 78, top: LAYOUT.gridTop, bottom: 80 };
function baseOption({
title, subtitle, xAxis, yAxis, series, legend, width, height,
tooltipFormatter, extraNote, sourceNote, grid, extraGraphics = [],
}) {
const notes = [sourceNote ?? SOURCE_NOTE];
if (extraNote) notes.push(extraNote);
return {
backgroundColor: '#fff',
title: { ...titleStyle, text: title, subtext: subtitle },
legend: standardLegend(legend, { itemGap: 22 }),
// 三行脚注(来源 + 两行口径)会顶到 height-66,而横轴刻度底边在 height-52。
// 本项目这几张图的横轴带轴名(「股价(元)」),它比刻度再低约 32px;实测
// grid.bottom=118 时轴名与脚注只剩 7px 净空(同站三行脚注的图通常有 23px),
// 所以统一提到 134 —— 改脚注行数时这里要跟着重算。
grid: { ...GRID, ...grid },
tooltip: tooltipOf(series, tooltipFormatter),
xAxis,
yAxis,
series,
graphic: [...watermarks(width, height), ...extraGraphics, footnote(notes.join('\n') + ' | @Joey知行记')],
};
}
/** 反解 PE 阈值对应的股价:在 pe_base 单调递增的序列上线性插值 */
function peToPrice(rows, pe) {
for (let i = 1; i < rows.length; i += 1) {
const prev = rows[i - 1];
const cur = rows[i];
if ((prev.pe_base - pe) * (cur.pe_base - pe) <= 0) {
const ratio = (pe - prev.pe_base) / (cur.pe_base - prev.pe_base);
return prev.price + ratio * (cur.price - prev.price);
}
}
return null;
}
/**
* PE 轴上的横向区域带(图 1、图 2 共用)。
* 必须挂在**第一个系列**上:markArea 在系列自己的分组里、先于该系列图元绘制,
* 而系列分组按声明顺序绘制 —— 挂在后面的系列上会盖住前一条线。
*/
function peBandMarkAreas() {
let low = PE_MIN;
return PE_ZONES.map((zone) => {
const band = [{
yAxis: low,
itemStyle: { color: tint(zone.color, 0.07) },
label: {
show: true,
position: 'insideTopLeft',
distance: 10,
formatter: `${zone.name} ${zone.note}`,
color: zone.color,
fontSize: 12,
fontWeight: 600,
fontFamily: FONT,
},
}, { yAxis: Math.min(zone.upTo, PE_MAX) }];
low = zone.upTo;
return band;
});
}
/**
* 股价轴上的纵向区域带(图 3 用)。
* 标签贴底:顶部留给折线标签与「现价」竖线的标注,贴底才不会撞。
*/
function priceBandMarkAreas(rows) {
const edges = [PRICE_MIN, ...PE_ZONES
.map((zone) => peToPrice(rows, zone.upTo))
.filter((price) => price !== null), PRICE_MAX];
return PE_ZONES.map((zone, index) => [{
xAxis: edges[index],
itemStyle: { color: tint(zone.color, 0.08) },
label: {
show: true,
position: 'insideBottom',
distance: 12,
formatter: zone.name,
color: zone.color,
fontSize: 12,
fontWeight: 600,
fontFamily: FONT,
},
}, { xAxis: edges[index + 1] }]);
}
/**
* 股价 → 指标折线。
* 只在 labeledPrices 上的点标数值:30+ 个数据点全标会糊成一片,
* 而这几个点正好是决策价位。
*/
function priceLine({
name, rows, key, color, width = 2.6, labeledPrices = [],
unit = '', decimals = 2, position = 'right', distance = 8,
}) {
const line = lineSeries({ name, data: [], color, width });
return {
...line,
label: { show: false },
data: rows.map((row) => ({
value: [row.price, row[key]],
label: labeledPrices.includes(row.price)
? {
...line.label,
show: true,
position,
distance,
formatter: `${row[key].toFixed(decimals)}${unit}`,
}
: { show: false },
})),
};
}
/**
* 现价竖线:三张图共用。
* label 一律关掉 —— markLine 的标签会**跟着竖线旋转 90°**,长串中文变成一列竖排字,
* 极难读。文字改用 nowGraphics() 以 graphic 单独排,保持水平。
*/
function nowMarkLine() {
return {
silent: true,
symbol: 'none',
lineStyle: { type: 'dashed', width: 1.6, color: PALETTE.brown },
label: { show: false },
data: [{ xAxis: NOW }],
};
}
/** 现价竖线上方的水平标注,位置由 grid 反推(图表尺寸变化时会重算) */
function nowGraphics(width, text) {
const x = GRID.left
+ ((NOW - PRICE_MIN) / (PRICE_MAX - PRICE_MIN)) * (width - GRID.left - GRID.right);
return [{
type: 'text',
left: Math.round(x + 8),
top: LAYOUT.gridTop + 4,
silent: true,
style: { text, fill: PALETTE.brown, fontSize: 12, fontWeight: 600, fontFamily: FONT },
}];
}
/**
* 图 1:股价 → 2026E PE 对照 —— 资料库原表第 1 节的图形版
*
* 一条直线就是一个情景:PE 与股价严格成正比,所以三档利润在图上表现为三条**平行**的
* 窄带(±6% 的利润预期落到 PE 上不足 1.7x)。这张图能回答"现价落在哪个区域",
* 但回答不了"贵还是便宜"—— 那取决于利润能不能兑现,属于分母问题,不在本图射程内。
*/
export function sweetCatlPricePeChart(rows, { width, height }) {
const nowPe = metricAt(rows, NOW, 'pe_base');
const hero = priceLine({
name: '中性 964 亿',
rows,
key: 'pe_base',
color: FINANCIAL_COLORS.netProfit,
width: 3.5,
labeledPrices: [265, NOW, 480, 605],
unit: 'x',
position: 'right',
});
hero.markLine = nowMarkLine();
const bear = {
...lineSeries({ name: '悲观 900 亿', data: [], color: FINANCIAL_COLORS.operatingCashFlow, width: 2.4 }),
label: { show: false },
data: rows.map((row) => [row.price, row.pe_bear]),
};
// markArea 挂在第一个系列上(见 peBandMarkAreas 注释)
bear.markArea = { silent: true, data: peBandMarkAreas() };
const bull = {
...lineSeries({ name: '乐观 1,000 亿', data: [], color: FINANCIAL_COLORS.investingCashFlow, width: 2.4 }),
label: { show: false },
data: rows.map((row) => [row.price, row.pe_bull]),
};
return baseOption({
title: '股价 → 2026E PE 对照',
subtitle: `单位:倍 · 三档利润情景 · 区域底带按中性 964 亿划分 · 现价 ${NOW} 元`,
xAxis: priceAxis(),
yAxis: valueAxis('PE(倍)', { min: PE_MIN, max: PE_MAX, interval: 5 }),
series: [bear, hero, bull],
legend: ['悲观 900 亿', '中性 964 亿', '乐观 1,000 亿'],
width,
height,
grid: { bottom: 134 },
extraGraphics: nowGraphics(width, `现价 ${NOW} 元 = 中性 ${nowPe.toFixed(2)}x`),
tooltipFormatter: (value) => `${Number(Array.isArray(value) ? value[1] : value).toFixed(2)}x`,
extraNote: 'PE = 市值 ÷ 2026E 归母净利,与股价严格成正比;三档情景只差 ±6% 利润,落到 PE 上不足 1.7x\n'
+ '原表把 15.5–25x 细分为「合理偏低」与「合理」,此处并为一条色带;扣现金 PE 见图 2',
});
}
/**
* 图 2:净现金让 PE 便宜了多少 —— 资料库原表第 2 节右图的图形版
*
* 含现金 PE 与扣现金 PE 是两条**严格平行**的线,间距恒为 净现金 ÷ 2026E 净利
* (约 2.35x)—— 间距与股价无关,但折价占市值的比例随股价下降而放大
* (220 元 22.2% → 700 元 7.0%)。所以这张图要看的不是两条线的高低,
* 而是中间那层沙色:它是一块不产生经营利润、但实实在在归股东的资产。
*
* 实现说明:ECharts 没有"两个系列之间填充",只能用 stack —— 下段是扣现金 PE、
* 上段是差额,差额段线宽 0、面积填沙色,上沿即含现金 PE。stack 的叠放顺序不能改
* (改了基准就错),于是用 z 控制绘制层级,防止沙色覆盖掉扣现金那条实线。
*/
export function sweetCatlNetCashChart(rows, { width, height }) {
const gap = rows[0].pe_base - rows[0].pe_ex_cash;
const nowEx = metricAt(rows, NOW, 'pe_ex_cash');
const cashLine = {
...lineSeries({ name: '扣现金 PE', data: [], color: PALETTE.brown, width: 3.5 }),
label: { show: false },
stack: 'pe',
z: 3,
areaStyle: { opacity: 0 },
data: rows.map((row) => [row.price, row.pe_ex_cash]),
};
cashLine.markLine = nowMarkLine();
cashLine.markArea = { silent: true, data: peBandMarkAreas() };
const band = {
...lineSeries({ name: '净现金差额', data: [], color: PALETTE.sand, width: 0 }),
label: { show: false },
stack: 'pe',
z: 2,
lineStyle: { color: PALETTE.sand, width: 0, opacity: 0 },
itemStyle: { color: PALETTE.sand },
showSymbol: false,
// opacity 必须显式写:areaStyle 的默认 opacity 是 0.7,只给 rgba 底色会被再乘一次
areaStyle: { color: PALETTE.sand, opacity: 0.7 },
data: rows.map((row) => [row.price, row.pe_base - row.pe_ex_cash]),
};
const gross = {
...lineSeries({ name: '含现金 PE', data: [], color: FINANCIAL_COLORS.investingCashFlow, width: 3 }),
label: { show: false },
z: 4,
data: rows.map((row) => [row.price, row.pe_base]),
};
return baseOption({
title: '净现金让 PE 便宜了多少',
subtitle: `单位:倍 · 中性 964 亿口径 · 沙色带即净现金 ${NET_CASH.toLocaleString('zh-CN')} 亿元对应的 PE`,
xAxis: priceAxis(),
yAxis: valueAxis('PE(倍)', { min: PE_MIN, max: PE_MAX, interval: 5 }),
series: [cashLine, band, gross],
// 「净现金差额」不进图例(线宽 0,图例色块会是空的),靠副标题与脚注交代
legend: ['含现金 PE', '扣现金 PE'],
width,
height,
grid: { bottom: 134 },
extraGraphics: nowGraphics(width, `现价 ${NOW} 元 = 扣现金 ${nowEx.toFixed(2)}x`),
tooltipFormatter: (value) => `${Number(Array.isArray(value) ? value[1] : value).toFixed(2)}x`,
extraNote: '含现金 PE = 市值 ÷ 2026E 净利;扣现金 PE =(市值 − 净现金)÷ 2026E 净利,即只给主营电池业务定价\n'
+ `两条线严格平行:差额恒为 ${gap.toFixed(2)}x,但占市值比例从 220 元的 22.2% 收窄到 700 元的 7.0% —— 股价越低,这层折价越厚`,
});
}
/**
* 图 3:任意股价试算 —— 资料库原表第 5 节计算器的图形版
*
* 计算器要输入一个价格才出结果,图表反过来:220–700 元全档都画出来,任意价位
* 沿竖线直接读。两条线是同一个目标价(605 元 = 2028E 1,400 亿 × 20x)下的三年
* 年化收益,差额即 50% 分红率的贡献。纵轴穿过 0 的位置就是"三年白干"—— 正好
* 是 605 元这条卖点线,所以零线本身就是一条结论。
*
* 纵向底带把估值区域直接画在股价轴上:横带看 PE、竖带看股价,是同一套档位。
* 击球区上沿 270.9 元、买入区上沿 322.9 元、合理区上沿 520.9 元(由 pe_base 反解)。
*/
export function sweetCatlAnyPriceChart(rows, { width, height }) {
const hero = priceLine({
name: '含分红年化',
rows,
key: 'cagr_total',
color: FINANCIAL_COLORS.netProfit,
width: 3.5,
labeledPrices: [265, NOW, 450, TARGET_2028],
unit: '%',
decimals: 1,
position: 'top',
distance: 10,
});
hero.markLine = nowMarkLine();
// markArea 挂在第一个系列上,否则会盖住同图上先声明的折线
hero.markArea = { silent: true, data: priceBandMarkAreas(rows) };
const priceOnly = priceLine({
name: '仅价差年化',
rows,
key: 'cagr_price',
color: FINANCIAL_COLORS.investingCashFlow,
width: 2.6,
labeledPrices: [265, NOW, 450],
unit: '%',
decimals: 1,
position: 'bottom',
distance: 10,
});
return baseOption({
title: '任意股价试算:三年预期年化收益',
subtitle: '单位:% · 目标价 605 元(2028E 1,400 亿 × 20x)· 纵带为估值区域',
xAxis: priceAxis(),
yAxis: valueAxis('年化收益(%)', { min: -10, max: 50, interval: 10 }),
series: [hero, priceOnly],
legend: ['含分红年化', '仅价差年化'],
width,
height,
grid: { bottom: 134 },
extraGraphics: nowGraphics(width, `现价 ${NOW} 元 = 含分红年化 ${metricAt(rows, NOW, 'cagr_total').toFixed(1)}%`),
tooltipFormatter: (value) => `${Number(Array.isArray(value) ? value[1] : value).toFixed(1)}%`,
extraNote: `年化收益 =(${TARGET_2028} ÷ 股价)^(1/3) − 1;含分红口径再乘 (1 + 股息率),股息按当前价每年派现、不复投\n`
+ '纵带边界由 pe_base 反解:击球区上沿 270.9 元 | 买入区上沿 322.9 元 | 合理区上沿 520.9 元',
});
}
+56
View File
@@ -361,6 +361,62 @@ export function stackedSharePoint(value, { pxPerUnit, labelColor, dy = 0, minIns
};
}
/**
* tooltip 彩色圆点标记。
*
* 为什么不直接用 ECharts 的默认 marker:折线系列的 visualDrawType 恒为 'fill'
* (Series.js 原型默认值,只有轨迹图 / 箱线图改成 'stroke'),而空心圆折线的
* itemStyle.color 必须是 #fff —— tooltip 取到的 marker 就是白色,在白底提示框上
* 等于隐形。这里按系列语义色自绘标记,不依赖 ECharts 内部取色。
*/
export function tooltipMarker(color) {
return `<span style="display:inline-block;width:10px;height:10px;border-radius:50%;`
+ `background:${color};margin-right:6px;vertical-align:-1px"></span>`;
}
/**
* 标准 tooltip:每一项前面带该系列语义色的圆点。
*
* 不能用 ECharts 默认 marker:折线系列 visualDrawType 为 'fill',
* 而空心圆折线的 itemStyle.color 是 #fff,默认 marker 会被取成白色而在白底上隐形。
* 取色规则:
* - 柱:优先用该数据点自身的颜色(支持逐项着色,如按公司或国别上色)
* - 折线:用 lineStyle.color(空心圆的 itemStyle.color 是白色,不能作语义色)
*
* @param {Array} series 已构造好的系列数组
* @param {(value: number) => string} formatValue 默认数值格式化
* @param {Object<string, (value: number) => string>} [formatBySeries] 按系列名覆盖
* —— 柱线组合图两条序列单位不同(如 GWh 与 %)时必需
*/
export function tooltipOf(series, formatValue, formatBySeries) {
const seriesColors = {};
series.forEach((item) => {
const color = item.lineStyle?.color ?? item.itemStyle?.color;
if (item.name && color) seriesColors[item.name] = color;
});
return {
trigger: 'axis',
axisPointer: { type: 'shadow' },
textStyle: { fontFamily: FONT, fontSize: 13 },
formatter: (params) => {
const list = Array.isArray(params) ? params : [params];
if (list.length === 0) return '';
const head = `<span style="color:${PALETTE.ink};font-weight:600">`
+ `${list[0].axisValueLabel ?? list[0].name ?? ''}</span>`;
const body = list.map((item) => {
const fallback = seriesColors[item.seriesName] ?? PALETTE.grey;
const color = item.seriesType === 'bar' ? (item.color || fallback) : fallback;
const fmt = formatBySeries?.[item.seriesName] ?? formatValue;
const raw = item.value;
const value = raw === null || raw === undefined || raw === '' ? '—' : fmt(raw);
return `${tooltipMarker(color)}${item.seriesName}:${value}`;
});
return [head, ...body].join('<br/>');
},
};
}
/** 底部注释 */
export function footnote(text) {
return {
+25 -2
View File
File diff suppressed because one or more lines are too long
+36
View File
@@ -88,6 +88,42 @@
{ "id": "price-shareholder", "name": "价格与股东回报", "chartRefs": ["moutai-flying-price", "moutai-price-adjustment", "moutai-dividend"] }
]
},
{
"id": "catl",
"name": "宁德时代",
"chartCatalog": {
"catl-unit-economics": { "key": "catl-unit-economics", "title": "动力电池单 Wh 经济性", "csv": "data/catl/catl_unit_economics.csv", "height": 700, "filename": "宁德时代动力电池单Wh经济性" },
"catl-volume-gp": { "key": "catl-volume-gp", "title": "动力电池销量与单 Wh 毛利", "csv": "data/catl/catl_unit_economics.csv", "height": 700, "filename": "宁德时代销量与单Wh毛利" },
"peer-install": { "key": "peer-install", "title": "Top5 动力电池装车量走势", "csv": "data/catl/catl_peer_install.csv", "height": 700, "filename": "Top5动力电池装车量走势" },
"peer-utilization": { "key": "peer-utilization", "title": "Top5 动力电池产能利用率", "csv": "data/catl/catl_peer_utilization.csv", "height": 700, "filename": "Top5动力电池产能利用率" },
"peer-share": { "key": "peer-share", "title": "Top5 动力电池全球市占率走势", "csv": "data/catl/catl_peer_install_share.csv", "height": 700, "filename": "Top5动力电池全球市占率走势" },
"peer-gp-per-wh": { "key": "peer-gp-per-wh", "title": "Top6 动力电池单 Wh 毛利走势", "csv": "data/catl/catl_peer_gp_per_wh.csv", "height": 720, "filename": "Top6动力电池单Wh毛利走势" },
"peer-asp": { "key": "peer-asp", "title": "Top6 动力电池 ASP 对比", "csv": "data/catl/catl_peer_asp.csv", "height": 700, "filename": "Top6动力电池ASP对比" },
"peer-margin": { "key": "peer-margin", "title": "Top6 毛利率与营业利润率", "csv": "data/catl/catl_peer_margin.csv", "height": 700, "filename": "Top6毛利率与营业利润率" },
"peer-2026h1": { "key": "peer-2026h1", "title": "2026H1 单 Wh 毛利与盈利能力", "csv": "data/catl/catl_peer_2026h1.csv", "height": 700, "filename": "2026H1单Wh毛利与盈利能力" },
"peer-gp-gap": { "key": "peer-gp-gap", "title": "宁德 vs 二线厂均值:单 Wh 毛利差距", "csv": "data/catl/catl_peer_gp_gap.csv", "height": 700, "filename": "宁德vs二线厂单Wh毛利差距" },
"peer-gp-change": { "key": "peer-gp-change", "title": "单 Wh 毛利:2026H1 vs 2023", "csv": "data/catl/catl_peer_gp_change.csv", "height": 700, "filename": "单Wh毛利2026H1对比2023" },
"global-cr": { "key": "global-cr", "title": "全球装车量集中度 CR3 / CR5 / CR10", "csv": "data/catl/global_top10_share.csv", "height": 700, "filename": "全球动力电池装车量集中度" },
"global-top10": { "key": "global-top10", "title": "2026H1 全球装车量前十与同比增速", "csv": "data/catl/global_top10_2026h1.csv", "height": 720, "filename": "2026H1全球装车量前十与同比增速" },
"global-region-share": { "key": "global-region-share", "title": "中日韩厂商全球份额结构", "csv": "data/catl/global_region_share.csv", "height": 700, "filename": "中日韩厂商全球份额结构" }
},
"tabs": [
{ "id": "unit-economics", "name": "单位经济", "chartRefs": ["catl-unit-economics", "catl-volume-gp"] },
{ "id": "peer", "name": "同业对比", "chartRefs": ["peer-install", "peer-utilization", "peer-share", "global-cr", "global-top10", "global-region-share", "peer-gp-per-wh", "peer-asp", "peer-margin", "peer-2026h1", "peer-gp-gap", "peer-gp-change"] }
]
},
{
"id": "sweet-spot",
"name": "甜蜜击球区",
"chartCatalog": {
"sweet-catl-price-pe": { "key": "sweet-catl-price-pe", "title": "股价 → 2026E PE 对照", "csv": "data/sweetspot/catl_price_pe.csv", "height": 740, "filename": "宁德时代股价PE对照" },
"sweet-catl-net-cash": { "key": "sweet-catl-net-cash", "title": "净现金让 PE 便宜了多少", "csv": "data/sweetspot/catl_price_pe.csv", "height": 740, "filename": "宁德时代净现金与PE" },
"sweet-catl-any-price": { "key": "sweet-catl-any-price", "title": "任意股价试算:三年预期年化收益", "csv": "data/sweetspot/catl_price_returns.csv", "height": 740, "filename": "宁德时代任意股价试算" }
},
"tabs": [
{ "id": "catl", "name": "宁德时代", "chartRefs": ["sweet-catl-price-pe", "sweet-catl-net-cash", "sweet-catl-any-price"] }
]
},
{
"id": "brk",
"name": "巴菲特致股东信",
+108
View File
@@ -0,0 +1,108 @@
#!/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()