AI Infrastructure Market Pulse

Executed Notebook

This notebook asks how a defined AI-infrastructure market-price basket moved over the selected window. The basket is a public price proxy for attention around compute, cloud, chips, platforms, and adjacent high-interest names.

The output is a basket definition, a source card, trend-index figures, return-versus-trend comparison, and edge-trimmed residual events. Revenue, capex, margins, and valuation require official financial data.

In [1]
from pathlib import Path
import os

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

from examples.hot_trends.data import (
    HotTrendDataError,
    append_real_snapshot,
    build_arxiv_monthly_counts,
    fetch_coingecko_market_chart,
    fetch_defillama_stablecoin_chains,
    fetch_github_repo_metadata,
    fetch_github_stargazers,
    fetch_huggingface_models,
    fetch_wikipedia_pageviews,
    source_audit_table,
)
from examples.hot_trends.decomposition import (
    component_summary,
    decompose_table,
    editorial_priority,
    residual_event_table,
)
from examples.hot_trends.scoring import article_publication_phrasing

pd.set_option("display.max_columns", 80)
pd.set_option("display.max_rows", 80)
plt.rcParams.update({"axes.grid": True})

CACHE_DIR = Path("examples/hot_trends/cache")
OUTPUT_DIR = Path("examples/hot_trends/outputs")
CACHE_DIR.mkdir(parents=True, exist_ok=True)
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)

def save_table(df, name):
    path = OUTPUT_DIR / f"{name}.csv"
    df.to_csv(path, index=False)
    print(f"saved: {path.as_posix()}")

1. Fetch prices through yfinance

In [2]
try:
    import yfinance as yf
except Exception as exc:
    raise ImportError("Install yfinance to run this notebook: python -m pip install yfinance") from exc

basket_definition = pd.DataFrame([
    {"ticker": "NVDA", "role": "GPU accelerator supplier", "boundary": "large-cap chip proxy, not pure infrastructure revenue"},
    {"ticker": "AMD", "role": "GPU/CPU accelerator challenger", "boundary": "mixed client, data-center, and gaming exposure"},
    {"ticker": "AVGO", "role": "networking and custom silicon", "boundary": "diversified semiconductor and software exposure"},
    {"ticker": "MSFT", "role": "cloud and AI platform", "boundary": "large diversified software/cloud company"},
    {"ticker": "GOOGL", "role": "cloud, TPU, and AI platform", "boundary": "advertising remains a major business line"},
    {"ticker": "AMZN", "role": "cloud infrastructure", "boundary": "retail and marketplace exposure included"},
    {"ticker": "META", "role": "AI compute demand and model deployment", "boundary": "advertising platform, not infrastructure vendor"},
    {"ticker": "TSLA", "role": "AI narrative and autonomous systems proxy", "boundary": "auto and energy exposure dominates fundamentals"},
])
tickers = basket_definition["ticker"].tolist()
start_date = "2024-01-01"
raw = yf.download(tickers, start=start_date, progress=False, auto_adjust=True)["Close"]
if raw.empty:
    raise HotTrendDataError("yfinance returned no market data")
prices = raw.reset_index().melt(id_vars="Date", var_name="ticker", value_name="price").rename(columns={"Date": "date"})
prices = prices.dropna(subset=["price"])
display(basket_definition)
prices.head(20)

2. Source card and price audit

In [3]
audit = source_audit_table(prices, value_col="price", entity_col="ticker", time_col="date")
source_card = pd.DataFrame([{
    "source": "Yahoo Finance through yfinance",
    "endpoint": "yfinance.download",
    "access_date": pd.Timestamp.today().date().isoformat(),
    "query_params": f"tickers={','.join(tickers)}; start={start_date}; auto_adjust=True; field=Close",
    "time_range": f"{pd.to_datetime(prices['date']).min().date()} to {pd.to_datetime(prices['date']).max().date()}",
    "cache_path": "not cached; outputs saved to examples/hot_trends/outputs",
    "interpretation_scope": "public market-price proxy for a defined basket; not fundamentals, valuation, or investment advice",
}])
display(source_card)
audit

3. Decompose normalized log prices

In [4]
components = decompose_table(prices, entity_col="ticker", time_col="date", value_col="price", method="MA_BASELINE", period=21, trend_window=63, transform="log")
summary = editorial_priority(component_summary(components, entity_col="ticker", time_col="date"), entity_col="ticker")
summary

Visualization: AI infrastructure price trend index

The y-axis is an index with each ticker starting at 1. Solid trend lines make basket movement comparable across different share prices. This is a price-proxy view; benchmark and financial-statement evidence are needed before performance or valuation claims.

In [5]
top_tickers = summary["ticker"].head(6).tolist()
fig, ax = plt.subplots(figsize=(11, 4.5))
for ticker in top_tickers:
    panel = components.loc[components["ticker"].eq(ticker)].sort_values("date").copy()
    panel["date"] = pd.to_datetime(panel["date"])
    base = float(panel["observed"].iloc[0])
    observed_index = np.exp(panel["observed"] - base)
    trend_index = np.exp(panel["trend"] - base)
    ax.plot(panel["date"], observed_index, linewidth=1.0, alpha=0.35)
    ax.plot(panel["date"], trend_index, linewidth=1.8, label=ticker)
ax.set_title("AI infrastructure observed and trend index")
ax.set_ylabel("index, first observation = 1")
ax.legend(ncol=3, loc="best")
plt.tight_layout()
plt.show()

4. Cross-sectional AI infrastructure table

In [6]
latest = prices.sort_values("date").groupby("ticker").tail(1).rename(columns={"price": "latest_price"})
first = prices.sort_values("date").groupby("ticker").head(1).rename(columns={"price": "first_price"})[["ticker", "first_price"]]
returns = latest.merge(first, on="ticker", how="left")
returns["total_return_proxy"] = returns["latest_price"] / returns["first_price"] - 1.0
returns.sort_values("total_return_proxy", ascending=False)

Visualization: return versus trend slope

The x-axis is simple total return over the sampled window. The y-axis is DeTime trend slope; marker size reflects residual shock magnitude. Read the scatter as a basket diagnostic, not as a recommendation.

In [7]
scatter_data = returns.merge(summary[["ticker", "trend_slope_per_step", "max_abs_residual_z", "editorial_priority_score"]], on="ticker", how="left")
fig, ax = plt.subplots(figsize=(7.2, 4.8))
sc = ax.scatter(
    scatter_data["total_return_proxy"],
    scatter_data["trend_slope_per_step"],
    s=80 + scatter_data["max_abs_residual_z"].fillna(0) * 35,
    c=scatter_data["editorial_priority_score"],
    cmap="viridis",
)
for _, row in scatter_data.iterrows():
    ax.annotate(str(row["ticker"]), (row["total_return_proxy"], row["trend_slope_per_step"]), fontsize=8, xytext=(4, 4), textcoords="offset points")
ax.axvline(0, color="0.45", linewidth=0.8)
ax.axhline(0, color="0.45", linewidth=0.8)
ax.set_xlabel("total return proxy")
ax.set_ylabel("trend slope per step")
ax.set_title("AI infrastructure return versus DeTime trend slope")
fig.colorbar(sc, ax=ax, label="editorial priority score")
plt.tight_layout()
plt.show()

5. Residual events

In [8]
events = residual_event_table(components, entity_col="ticker", time_col="date", top_n=25, trim_edges=63)
events

Visualization: AI infrastructure residual heatmap

The heatmap shows weekly residual z-scores by ticker after decomposition. Clusters suggest dates worth matching against launches, earnings, or macro events; edge-trimmed residual tables reduce first/last-window artifacts.

In [9]
residual_grid = components.copy()
residual_grid["residual_z"] = residual_grid.groupby("ticker")["residual"].transform(lambda s: (s - s.median()) / (1.4826 * (s - s.median()).abs().median() + 1e-12))
heat = residual_grid.pivot_table(index="ticker", columns="date", values="residual_z", aggfunc="mean").reindex(summary["ticker"].tolist()).dropna(how="all")
heat = heat.T
heat.index = pd.to_datetime(heat.index)
heat = heat.resample("W").mean().T
values = heat.to_numpy(dtype=float)
absmax = float(np.nanmax(np.abs(values))) if np.isfinite(values).any() else 1.0
fig, ax = plt.subplots(figsize=(11, 4.8))
im = ax.imshow(values, aspect="auto", cmap="RdBu_r", vmin=-absmax, vmax=absmax)
ax.set_yticks(range(len(heat.index)))
ax.set_yticklabels(heat.index)
tick_step = max(1, len(heat.columns) // 8)
xticks = list(range(0, len(heat.columns), tick_step))
ax.set_xticks(xticks)
ax.set_xticklabels([heat.columns[i].strftime("%Y-%m-%d") for i in xticks], rotation=45, ha="right")
ax.set_title("Weekly AI infrastructure residual z-score heatmap")
fig.colorbar(im, ax=ax, label="robust residual z")
plt.tight_layout()
plt.show()

6. Publication language

In [10]
phrasing = article_publication_phrasing()
phrasing
In [11]
save_table(source_card, "07_ai_infra_source_card")
save_table(basket_definition, "07_ai_infra_basket_definition")
save_table(audit, "07_ai_infra_market_audit")
save_table(summary, "07_ai_infra_component_summary")
save_table(returns, "07_ai_infra_return_proxy")
save_table(events, "07_ai_infra_residual_events")
save_table(phrasing, "07_ai_infra_publication_phrasing")