Capstone Backtest: Equal-Weight Portfolio vs SPY in Pandas | Pandas for Finance Ep14
0views
C
CelesteAI
Description
Episode 14 of Pandas for Finance. The capstone — a real, end-to-end portfolio backtest assembled from every concept in the previous 13 episodes.
Source code: https://github.com/GoCelesteAI/pandas-for-finance
The setup: an equal-weight portfolio of five mega-cap tech stocks (AAPL, MSFT, GOOGL, AMZN, NVDA) versus SPY as the benchmark, with 4 percent risk-free rate for the Sharpe calculation. The output: an annualized return, vol, Sharpe, max drawdown, and final wealth multiplier for each side, plus a per-stock Sharpe leaderboard. Mega-cap tech finished at +38.92 percent annualized vs SPY's +16.46 percent — but with higher vol and a deeper drawdown.
What You'll Build:
- backtest.py — load the cached prices, filter to the universe, pivot wide for portfolio math, compute daily returns, build an equal-weight portfolio, and produce a full backtest report.
- The pivot pattern: df[df["Ticker"].isin([...])]
.pivot(index="Date", columns="Ticker", values="Adj Close")
.ffill() — wide format makes every per-row portfolio operation a one-liner.
- The equal-weight calculation: a pd.Series of weights times the daily-returns DataFrame, summed across the row. That's the daily portfolio return in one line.
- A reusable stats(returns) function that returns annualized return, annualized vol, Sharpe, max drawdown, and final wealth multiplier — five numbers that summarize any return stream.
- The per-stock Sharpe leaderboard — sort the same metrics per ticker to see which holdings drive the portfolio's risk-adjusted return.
Timestamps:
0:00 - Intro — Episode 14 starts here
0:23 - Preview — equal-weight portfolio vs SPY
1:10 - Open nvim, write backtest.py
1:14 - Display options + constants
1:33 - Load + pivot to wide format
1:55 - Daily returns + equal-weight portfolio
2:18 - The stats function (Sharpe, drawdown, final wealth)
2:46 - Build the comparison report
3:30 - Save and run
3:36 - Backtest report (Portfolio +38.9% vs SPY +16.5%)
4:30 - Recap
5:18 - End screen
Key Takeaways:
1. Pivot to wide for portfolio math. Filter to the universe + benchmark, then .pivot(index="Date", columns="Ticker", values="Adj Close"). Each ticker becomes a column. Every per-row operation — daily returns, weighted sums, correlation — becomes vectorized. .ffill() after the pivot fills any gaps that come from one ticker being missing on a date the others traded.
2. Equal-weight portfolio in two lines. weights = pd.Series(1 / N, index=tickers); port_ret = (returns[tickers] * weights).sum(axis=1). That's it. No loops, no broadcasting tricks — pandas aligns the weights Series to the columns of the returns DataFrame automatically. For other weight schemes (market-cap, risk-parity, momentum), only the weights line changes.
3. A reusable stats() function for any return stream. Annualized return is (1 + mean) * 252 - 1. Annualized vol is std sqrt(252). Sharpe is (ann_return - rf) / ann_vol. Max drawdown is (wealth / wealth.expanding().max() - 1).min(). Final wealth is wealth.iloc[-1]. Five lines, every metric a quant report needs.
4. Compare portfolio to benchmark in one DataFrame. pd.DataFrame({"Portfolio": stats(port_ret), "SPY": stats(bench_ret)}) — two named columns, five rows of metrics, a single print. The whole thesis of "did mega-cap tech beat the market?" answered in one table.
5. The per-stock Sharpe leaderboard tells you what's driving the portfolio. Sort the ticker-level Sharpe ratios — NVDA at +1.48 carries the equal-weight portfolio. AMZN at +0.64 is exactly SPY's Sharpe — a drag in this universe. This decomposition is how you decide whether to drop the laggards in the next iteration.
This channel is run by Claude AI. Tutorials AI-produced; reviewed and published by Codegiz. Source code at codegiz.com.
#Pandas #Python #Finance #Backtest #Portfolio #Sharpe #DataAnalytics #PythonForFinance #LearnPandas #ClaudeAI
---
Generated by Claude AI · part of the Pandas for Finance series