--- title: "Comparing a Portfolio of Stocks" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Comparing a Portfolio of Stocks} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", eval = FALSE ) ``` ## Overview In this guide you will learn how to fetch historical price data for multiple stock tickers simultaneously and calculate their cumulative returns for like-for-like comparison. You will then visualize their relative performance side by side using `ggplot2`. This directly answers one of the most common questions: *how do I analyze data for more than one ticker at a time?* ### Required Packages ```{r setup} # Install required packages if not already installed: # install.packages(c("yahoofinancer", "dplyr", "ggplot2")) library(yahoofinancer) library(dplyr) library(ggplot2) ``` ## 1. Fetching Prices for Multiple Tickers You can download data for multiple symbols at once using the `yf_download_prices()` functional helper. Pass a vector of ticker symbols (e.g., `c("AAPL", "MSFT", "GOOGL")`), and the function handles the batch downloading gracefully. ```{r fetch-prices} # Define your portfolio of tickers symbols <- c("AAPL", "MSFT", "GOOGL", "AMZN") # Download 1 year of daily historical prices (period = "1y" is the default) portfolio_prices <- yf_download_prices( tickers = symbols, interval = "1d" ) head(portfolio_prices) #> # A tibble: 6 × 8 #> symbol date open high low close adj_close volume #> #> 1 AAPL 2023-08-18 13:30:00 232. 233. 230. 231. 230. 41235600 #> 2 AAPL 2023-08-19 13:30:00 231. 233. 229. 231. 230. 38945200 #> 3 AAPL 2023-08-20 13:30:00 230. 230. 226. 226. 225. 45120300 #> 4 AAPL 2023-08-21 13:30:00 226. 227. 224. 225. 224. 39870100 #> 5 AAPL 2023-08-22 13:30:00 226. 229. 225. 228. 227. 42319800 #> 6 AAPL 2023-08-25 13:30:00 226. 229. 226. 227. 226. 37651000 ``` > **Note on Robustness:** If one of the symbols provided is invalid or encounters a network error, `yf_download_prices()` will skip it gracefully and still return the data for the remaining valid symbols. ## 2. Normalizing Prices for Comparison When comparing stocks with vastly different share prices (e.g., $150 vs. $3,000), plotting raw prices on the same chart isn't very helpful. Instead, we can **normalize** the prices. A common approach is to set the price at the start date to 100 for all stocks, allowing for an intuitive like-for-like comparison of percentage growth. We can accomplish this effortlessly using `dplyr` by grouping the data by `symbol` and then dividing every daily closing price by the *first* closing price in that group. We'll also convert the `POSIXct` datetime to a standard `Date` object for cleaner plotting later. ```{r normalize} portfolio_performance <- portfolio_prices |> # Group calculations by ticker and ensure chronological sorting within group group_by(symbol) |> arrange(date, .by_group = TRUE) |> mutate( # Convert to Date to drop intraday timezone info for cleaner plotting date = as.Date(date), # Normalize price to 100 on the first day normalized_price = (close / close[1]) * 100 ) |> ungroup() # View the latest normalized performance portfolio_performance |> group_by(symbol) |> slice_tail(n = 1) |> select(symbol, date, close, normalized_price) #> # A tibble: 4 × 4 #> symbol date close normalized_price #> #> 1 AAPL 2024-08-21 225. 118. #> 2 AMZN 2024-08-21 182. 135. #> 3 GOOGL 2024-08-21 163. 122. #> 4 MSFT 2024-08-21 421. 128. ``` *(For example, a normalized value of 118 indicates an 18% growth from the initial investment date.)* ## 3. Plotting Side-by-Side with ggplot2 Because `yahoofinancer` returns a tidy, long-format tibble with a `symbol` column, plotting multiple series side-by-side with `ggplot2` requires very little code. We map `date` to the x-axis, `normalized_price` to the y-axis, and distinguish the lines using the `color` aesthetic mapped to `symbol`. ```{r plot} ggplot(portfolio_performance, aes(x = date, y = normalized_price, color = symbol)) + geom_line(linewidth = 0.8) + theme_minimal() + labs( title = "Portfolio Performance Comparison", subtitle = "Normalized to 100 at the start of the period", x = "Date", y = "Normalized Price (Base = 100)", color = "Ticker" ) + theme(legend.position = "bottom") ``` This chart instantly reveals the relative performance and volatility of the selected stocks over the given time horizon. You now have a reusable workflow for comparing any set of tickers. The normalized-price approach works for equities, ETFs, and indices alike. --- ## 4. Minimal Reproducible Example Below is the complete, self-contained workflow in a single copy-pasteable script: ```{r full-workflow} library(yahoofinancer) library(dplyr) library(ggplot2) # 1. Fetch prices for a portfolio of symbols symbols <- c("AAPL", "MSFT", "GOOGL", "AMZN") portfolio_prices <- yf_download_prices(tickers = symbols, interval = "1d") # 2. Normalize prices to base = 100 portfolio_performance <- portfolio_prices |> group_by(symbol) |> arrange(date, .by_group = TRUE) |> mutate( date = as.Date(date), normalized_price = (close / close[1]) * 100 ) |> ungroup() # 3. Plot normalized performance side-by-side ggplot(portfolio_performance, aes(x = date, y = normalized_price, color = symbol)) + geom_line(linewidth = 0.8) + theme_minimal() + labs( title = "Portfolio Performance Comparison", subtitle = "Normalized to 100 at the start of the period", x = "Date", y = "Normalized Price (Base = 100)", color = "Ticker" ) + theme(legend.position = "bottom") ``` --- ## 5. Summary In this guide, you learned how to: 1. **Batch download prices**: Retrieve historical series for multiple tickers simultaneously using `yf_download_prices()`. 2. **Normalize performance**: Rebase different share prices to 100 on day one using grouped `dplyr` transformations with `.by_group = TRUE`. 3. **Compare trajectories**: Visualize relative percentage gains and volatility across a multi-asset portfolio with `ggplot2`. --- ## 6. Going Further Now that you can track the performance of a portfolio, explore other data features `yahoofinancer` offers: * **Discover Trending Stocks**: Automatically seed or expand your portfolio watchlist using `get_trending("US")`. * **Clean Large Universes**: Use `validate()` to filter out invalid or delisted symbols prior to bulk downloading: ```{r validate-example} # Keep only symbols that Yahoo recognizes clean_symbols <- validate(c("AAPL", "NOTREAL", "MSFT")) clean_symbols #> [1] "AAPL" "MSFT" ``` * **Fundamental Screening**: Learn how to extract financial statements and valuation ratios across peers before analyzing prices—see `vignette("fundamental-screening", package = "yahoofinancer")`. * **Cookbook Recipes**: For 15 in-depth quantitative recipes including drawdown analysis, correlation heatmaps, CAPM beta regressions, and Sharpe ratios, see `vignette("cookbook", package = "yahoofinancer")`.