diff --git a/src/charts/spending_debt.py b/src/charts/spending_debt.py new file mode 100644 index 0000000..d7c5c55 --- /dev/null +++ b/src/charts/spending_debt.py @@ -0,0 +1,74 @@ +"""Spending and Debt Charts — Hyperscaler Capex, Tech Debt, NVIDIA Revenue""" +import matplotlib +matplotlib.use("Agg") +import matplotlib.pyplot as plt +import numpy as np +from src.data.ai_infrastructure import hyperscaler_capex_annual, hyperscaler_capex_meta +from src.utils.styling import get_theme, EXPORT_DPI, get_company_colors, GRAY_DARK +from src.utils.export import save_chart_tight + + +def plot_hyperscaler_capex() -> str: + plt.rcParams.update(get_theme()) + fig, ax = plt.subplots(figsize=(14, 8)) + + company_colors = get_company_colors() + companies = ["Microsoft", "Alphabet", "Meta", "Amazon"] + years = list(range(2020, 2027)) + + # Organize data by company + data = {c: [] for c in companies} + for entry in hyperscaler_capex_annual: + data[entry["company"]].append(entry["capex_billions"]) + + # Fill missing years with 0 + for c in companies: + while len(data[c]) < len(years): + data[c].append(0) + + # Stacked area + y_offset = np.zeros(len(years)) + totals = [] + for c in companies: + values = np.array(data[c]) + ax.fill_between(years, y_offset, y_offset + values, + alpha=0.8, color=company_colors.get(c, "#666"), label=c) + ax.plot(years, y_offset + values, color=company_colors.get(c, "#666"), linewidth=1) + y_offset += values + totals.append(sum(values)) + + # Total annotations + for i, (year, total) in enumerate(zip(years, y_offset)): + label = f"${total:.0f}B" + if year >= 2026: + label += "*" + ax.text(year, total + 5, label, ha="center", fontsize=9, fontweight="bold") + + # Dashed vertical line at 2026 to mark guided/projected boundary + ax.axvline(x=2025.5, color=GRAY_DARK, linestyle="--", alpha=0.4, linewidth=1) + + ax.set_title("Hyperscaler AI Infrastructure Capex — 2020 to 2026", fontsize=16, fontweight="bold") + ax.set_xlabel("Year", fontsize=12) + ax.set_ylabel("Capex (Billions USD)", fontsize=12) + ax.legend(loc="upper left", fontsize=10) + ax.grid(True, alpha=0.3) + ax.text(0.02, 0.97, "*2026 = guided/projected", transform=ax.transAxes, + fontsize=9, style="italic", color="gray", va="top") + + # AI-related capex share note + ax.text(0.98, 0.03, "80-90% of 2025/2026 capex is AI-related", + transform=ax.transAxes, fontsize=9, style="italic", + color="gray", ha="right", va="bottom") + + path = save_chart_tight(fig, "05_hyperscaler_capex.png") + plt.close(fig) + return path + + +def main(): + path = plot_hyperscaler_capex() + print(f"Chart saved: {path}") + + +if __name__ == "__main__": + main()