tidyquantThe R package tidyquant combines tools for quantitative
finance with the tidyverse.
Traditional financial packages in R often represent time series using
special objects such as xts. tidyquant
provides access to many of the same tools while returning ordinary
tibbles that can be manipulated with dplyr,
tidyr and ggplot2.
A typical tidyquant workflow is:
download data
↓
transform prices
↓
compute returns or indicators
↓
visualize
↓
analyze assets or portfolios
Load tidyquant and the tidyverse:
library(tidyquant)
library(tidyverse)
tidyquant supplies the financial functions, while the
tidyverse provides familiar tools such as filter(),
mutate(), group_by() and
ggplot().
tq_get()The central data-retrieval function is:
tq_get()
It retrieves financial data from several sources and returns the result as a tibble.
For example, we can download Apple stock prices:
apple <- tq_get(
"AAPL",
from = "2024-01-01",
to = "2025-12-31"
)
apple
## # A tibble: 501 × 8
## symbol date open high low close volume adjusted
## <chr> <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 AAPL 2024-01-02 187. 188. 184. 186. 82488700 183.
## 2 AAPL 2024-01-03 184. 186. 183. 184. 58414500 182.
## 3 AAPL 2024-01-04 182. 183. 181. 182. 71983600 180.
## 4 AAPL 2024-01-05 182. 183. 180. 181. 62379700 179.
## 5 AAPL 2024-01-08 182. 186. 182. 186. 59144500 183.
## 6 AAPL 2024-01-09 184. 185. 183. 185. 42841800 183.
## 7 AAPL 2024-01-10 184. 186. 184. 186. 46792900 184.
## 8 AAPL 2024-01-11 187. 187. 184. 186. 49128400 183.
## 9 AAPL 2024-01-12 186. 187. 185. 186. 40477800 184.
## 10 AAPL 2024-01-16 182. 184. 181. 184. 65603000 181.
## # ℹ 491 more rows
"AAPL" is Apple’s ticker symbol.
The arguments
from = "2024-01-01"
to = "2025-12-31"
specify the period we want to retrieve.
The resulting tibble contains variables such as:
symbol
date
open
high
low
close
volume
adjusted
The price variables have the usual financial interpretation:
open opening price
high highest price during the day
low lowest price during the day
close closing price
volume trading volume
adjusted price adjusted for events such as splits and dividends
Because the result is an ordinary tibble, we can immediately use tidyverse tools:
apple |>
select(date, open, high, low, close, volume)
## # A tibble: 501 × 6
## date open high low close volume
## <date> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2024-01-02 187. 188. 184. 186. 82488700
## 2 2024-01-03 184. 186. 183. 184. 58414500
## 3 2024-01-04 182. 183. 181. 182. 71983600
## 4 2024-01-05 182. 183. 180. 181. 62379700
## 5 2024-01-08 182. 186. 182. 186. 59144500
## 6 2024-01-09 184. 185. 183. 185. 42841800
## 7 2024-01-10 184. 186. 184. 186. 46792900
## 8 2024-01-11 187. 187. 184. 186. 49128400
## 9 2024-01-12 186. 187. 185. 186. 40477800
## 10 2024-01-16 182. 184. 181. 184. 65603000
## # ℹ 491 more rows
select() keeps only the variables that we are interested
in.
We can plot the closing price using ordinary
ggplot2:
apple |>
ggplot(aes(x = date, y = close)) +
geom_line() +
labs(
title = "Apple stock price",
x = NULL,
y = "Price (USD)"
)
One of the useful features of tq_get() is that it
accepts a vector of symbols.
For example:
stocks <- tq_get(
c("AAPL", "MSFT", "NVDA"),
from = "2024-01-01",
to = "2025-12-31"
)
stocks
## # A tibble: 1,503 × 8
## symbol date open high low close volume adjusted
## <chr> <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 AAPL 2024-01-02 187. 188. 184. 186. 82488700 183.
## 2 AAPL 2024-01-03 184. 186. 183. 184. 58414500 182.
## 3 AAPL 2024-01-04 182. 183. 181. 182. 71983600 180.
## 4 AAPL 2024-01-05 182. 183. 180. 181. 62379700 179.
## 5 AAPL 2024-01-08 182. 186. 182. 186. 59144500 183.
## 6 AAPL 2024-01-09 184. 185. 183. 185. 42841800 183.
## 7 AAPL 2024-01-10 184. 186. 184. 186. 46792900 184.
## 8 AAPL 2024-01-11 187. 187. 184. 186. 49128400 183.
## 9 AAPL 2024-01-12 186. 187. 185. 186. 40477800 184.
## 10 AAPL 2024-01-16 182. 184. 181. 184. 65603000 181.
## # ℹ 1,493 more rows
We request three stocks:
AAPL Apple
MSFT Microsoft
NVDA Nvidia
The result is a single tibble in long format.
The variable symbol identifies the asset associated with
each observation.
We can therefore write:
stocks |>
ggplot(aes(x = date, y = adjusted)) +
geom_line() +
facet_wrap(~ symbol, scales = "free_y")
facet_wrap(~ symbol) creates a separate panel for each
stock.
scales = "free_y" allows each stock to have its own
price scale.
This illustrates an important feature of tidyquant:
operations can easily be scaled from one asset to many assets.
Yahoo Finance also provides cryptocurrency symbols, so
tq_get() can retrieve Bitcoin:
bitcoin <- tq_get(
"BTC-USD",
from = "2020-01-01",
to = "2025-12-31"
)
bitcoin
## # A tibble: 2,192 × 8
## symbol date open high low close volume adjusted
## <chr> <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 BTC-USD 2020-01-01 7195. 7254. 7175. 7200. 18565664997 7200.
## 2 BTC-USD 2020-01-02 7203. 7212. 6935. 6985. 20802083465 6985.
## 3 BTC-USD 2020-01-03 6984. 7414. 6915. 7345. 28111481032 7345.
## 4 BTC-USD 2020-01-04 7345. 7427. 7310. 7411. 18444271275 7411.
## 5 BTC-USD 2020-01-05 7410. 7544. 7401. 7411. 19725074095 7411.
## 6 BTC-USD 2020-01-06 7410. 7782. 7409. 7769. 23276261598 7769.
## 7 BTC-USD 2020-01-07 7769. 8178. 7768. 8164. 28767291327 8164.
## 8 BTC-USD 2020-01-08 8162. 8397. 7957. 8080. 31672559265 8080.
## 9 BTC-USD 2020-01-09 8082. 8082. 7842. 7879. 24045990466 7879.
## 10 BTC-USD 2020-01-10 7878. 8167. 7727. 8167. 28714583844 8167.
## # ℹ 2,182 more rows
BTC-USD means the price of Bitcoin expressed in US
dollars.
We can visualize it exactly as we did for Apple:
bitcoin |>
ggplot(aes(x = date, y = close)) +
geom_line() +
labs(
title = "Bitcoin price",
x = NULL,
y = "USD"
)
This illustrates an important difference from a
cryptocurrency-specific package such as crypto2.
With tidyquant, Bitcoin is simply one financial asset
among many.
For example:
assets <- tq_get(
c("BTC-USD", "ETH-USD", "AAPL", "MSFT"),
from = "2020-01-01",
to = "2025-12-31"
)
This produces one dataset containing Bitcoin, Ethereum, Apple and Microsoft.
tq_transmute()Prices are often converted into returns before performing financial analysis.
tidyquant provides the function:
tq_transmute()
It applies financial time-series functions and returns the transformed observations. It is especially useful when the transformation changes the periodicity of the data—for example, from daily prices to monthly returns.
Suppose we want monthly Apple returns:
apple_returns <- apple |>
tq_transmute(
select = adjusted,
mutate_fun = periodReturn,
period = "monthly",
type = "arithmetic",
col_rename = "return"
)
apple_returns
## # A tibble: 24 × 2
## date return
## <date> <dbl>
## 1 2024-01-31 -0.00668
## 2 2024-02-29 -0.0185
## 3 2024-03-28 -0.0513
## 4 2024-04-30 -0.00671
## 5 2024-05-31 0.130
## 6 2024-06-28 0.0956
## 7 2024-07-31 0.0544
## 8 2024-08-30 0.0324
## 9 2024-09-30 0.0175
## 10 2024-10-31 -0.0304
## # ℹ 14 more rows
Let us examine the arguments.
select = adjusted
says that we want to use the adjusted price.
mutate_fun = periodReturn
specifies the financial function that will perform the calculation.
period = "monthly"
converts the daily observations into monthly returns.
Finally,
type = "arithmetic"
requests ordinary percentage returns.
The resulting variable return is approximately
\[ r_t = \frac{P_t}{P_{t-1}} - 1. \]
Thus:
0.05
means a return of 5%, while
-0.03
means a loss of 3%.
The real advantage becomes visible when we combine
group_by() with tq_transmute():
returns <- assets |>
group_by(symbol) |>
tq_transmute(
select = adjusted,
mutate_fun = periodReturn,
period = "monthly",
type = "arithmetic",
col_rename = "return"
)
returns
## # A tibble: 288 × 3
## # Groups: symbol [4]
## symbol date return
## <chr> <date> <dbl>
## 1 BTC-USD 2020-01-31 0.299
## 2 BTC-USD 2020-02-29 -0.0803
## 3 BTC-USD 2020-03-31 -0.251
## 4 BTC-USD 2020-04-30 0.345
## 5 BTC-USD 2020-05-31 0.0927
## 6 BTC-USD 2020-06-30 -0.0341
## 7 BTC-USD 2020-07-31 0.239
## 8 BTC-USD 2020-08-31 0.0316
## 9 BTC-USD 2020-09-30 -0.0767
## 10 BTC-USD 2020-10-31 0.278
## # ℹ 278 more rows
The key instruction is:
group_by(symbol)
It tells R to calculate returns independently for each asset.
The same operation is therefore applied to Bitcoin, Ethereum, Apple and Microsoft.
We can visualize the result:
returns |>
ggplot(aes(x = date, y = return)) +
geom_line() +
facet_wrap(~ symbol)
This pattern is fundamental:
group_by(asset)
↓
apply the same financial analysis
↓
combine the results
It allows analyses that work for one asset to scale easily to hundreds of assets.
tq_mutate(): adding financial indicatorstq_mutate() is related to tq_transmute(),
but it keeps the original observations and adds new variables.
For example, we can calculate a 30-day simple moving average of Bitcoin:
bitcoin_ma <- bitcoin |>
tq_mutate(
select = close,
mutate_fun = SMA,
n = 30,
col_rename = "sma30"
)
bitcoin_ma
## # A tibble: 2,192 × 9
## symbol date open high low close volume adjusted sma30
## <chr> <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 BTC-USD 2020-01-01 7195. 7254. 7175. 7200. 18565664997 7200. NA
## 2 BTC-USD 2020-01-02 7203. 7212. 6935. 6985. 20802083465 6985. NA
## 3 BTC-USD 2020-01-03 6984. 7414. 6915. 7345. 28111481032 7345. NA
## 4 BTC-USD 2020-01-04 7345. 7427. 7310. 7411. 18444271275 7411. NA
## 5 BTC-USD 2020-01-05 7410. 7544. 7401. 7411. 19725074095 7411. NA
## 6 BTC-USD 2020-01-06 7410. 7782. 7409. 7769. 23276261598 7769. NA
## 7 BTC-USD 2020-01-07 7769. 8178. 7768. 8164. 28767291327 8164. NA
## 8 BTC-USD 2020-01-08 8162. 8397. 7957. 8080. 31672559265 8080. NA
## 9 BTC-USD 2020-01-09 8082. 8082. 7842. 7879. 24045990466 7879. NA
## 10 BTC-USD 2020-01-10 7878. 8167. 7727. 8167. 28714583844 8167. NA
## # ℹ 2,182 more rows
Here:
select = close
selects the Bitcoin closing price.
mutate_fun = SMA
asks for a simple moving average.
SMA() is actually a function from the TTR
package. tidyquant provides a convenient interface to
it.
Finally:
n = 30
means that each value is based on the previous 30 observations.
The resulting tibble contains all the original variables plus:
sma30
We can plot both series:
bitcoin_ma |>
ggplot(aes(x = date)) +
geom_line(aes(y = close, color = "Bitcoin")) +
geom_line(aes(y = sma30, color = "30-day moving average")) +
scale_color_manual(
values = c(
"Bitcoin" = "steelblue",
"30-day moving average" = "red"
)
) +
labs(
title = "Bitcoin and its 30-day moving average",
x = NULL,
y = "USD",
color = NULL
)
The first line represents the Bitcoin price and the second its 30-day moving average.
You can inspect the many financial functions available through this interface with:
tq_mutate_fun_options()
The package can use numerous transformations from xts,
quantmod and TTR.
tidyquant also extends ggplot2 with
financial geometries.
For example, we can make a candlestick chart:
apple |>
filter(date >= as.Date("2025-10-01")) |>
ggplot(aes(
x = date,
open = open,
high = high,
low = low,
close = close
)) +
geom_candlestick()
Before plotting, we use:
filter(date >= as.Date("2025-10-01"))
to select a relatively short period, because candlestick charts become difficult to read when too many observations are displayed.
Each candlestick summarizes four pieces of information:
opening price
highest price
lowest price
closing price
tidyquant also provides specialized tools for moving
averages, Bollinger Bands and other financial charts.
tidyquant can retrieve the companies belonging to major
stock indexes.
First inspect the available indexes:
tq_index_options()
## [1] "DOW" "DOWGLOBAL" "SP400" "SP500" "SP600"
Then, for example:
sp500 <- tq_index("SP500")
sp500
## # A tibble: 505 × 8
## symbol company identifier sedol weight sector shares_held local_currency
## <chr> <chr> <chr> <chr> <dbl> <chr> <dbl> <chr>
## 1 NVDA NVIDIA CORP 67066G104 2379… 0.0830 - 296930485 USD
## 2 AAPL APPLE INC 037833100 2046… 0.0726 - 179068000 USD
## 3 MSFT MICROSOFT C… 594918104 2588… 0.0570 - 91109404 USD
## 4 AMZN AMAZON.COM … 023135106 2000… 0.0365 - 120438174 USD
## 5 GOOGL ALPHABET IN… 02079K305 BYVY… 0.0302 - 71996873 USD
## 6 AVGO BROADCOM INC 11135F101 BDZ7… 0.0255 - 58376409 USD
## 7 META META PLATFO… 30303M102 B7TL… 0.0246 - 27057003 USD
## 8 GOOG ALPHABET IN… 02079K107 BYY8… 0.0242 - 58322589 USD
## 9 MU MICRON TECH… 595112103 2588… 0.0182 - 13857966 USD
## 10 TSLA TESLA INC 88160R101 B616… 0.0150 - 34576232 USD
## # ℹ 495 more rows
tq_index("SP500") returns the constituents of the
S&P 500 as a tibble.
The data include information such as stock symbol, company name, sector and index weight.
We can use ordinary dplyr operations. For instance, here
you select the first top-10 companies in the index by weight:
sp500 |>
arrange(-weight) |>
head(10)
## # A tibble: 10 × 8
## symbol company identifier sedol weight sector shares_held local_currency
## <chr> <chr> <chr> <chr> <dbl> <chr> <dbl> <chr>
## 1 NVDA NVIDIA CORP 67066G104 2379… 0.0830 - 296930485 USD
## 2 AAPL APPLE INC 037833100 2046… 0.0726 - 179068000 USD
## 3 MSFT MICROSOFT C… 594918104 2588… 0.0570 - 91109404 USD
## 4 AMZN AMAZON.COM … 023135106 2000… 0.0365 - 120438174 USD
## 5 GOOGL ALPHABET IN… 02079K305 BYVY… 0.0302 - 71996873 USD
## 6 AVGO BROADCOM INC 11135F101 BDZ7… 0.0255 - 58376409 USD
## 7 META META PLATFO… 30303M102 B7TL… 0.0246 - 27057003 USD
## 8 GOOG ALPHABET IN… 02079K107 BYY8… 0.0242 - 58322589 USD
## 9 MU MICRON TECH… 595112103 2588… 0.0182 - 13857966 USD
## 10 TSLA TESLA INC 88160R101 B616… 0.0150 - 34576232 USD
Similarly, we can retrieve companies listed on an exchange:
tq_exchange_options()
## [1] "AMEX" "NASDAQ" "NYSE"
The package currently provides lists for NASDAQ, NYSE and AMEX.
For example:
nasdaq <- tq_exchange("NASDAQ")
nasdaq
## # A tibble: 4,022 × 7
## symbol company last.sale.price market.cap country ipo.year industry
## <chr> <chr> <dbl> <dbl> <chr> <int> <chr>
## 1 AACG ATA Creativity G… 0.820 56119957 China 2008 "Other …
## 2 AACI Armada Acquisiti… 10.1 0 United… 2026 "Blank …
## 3 AACIW Armada Acquisiti… 0.238 0 United… 2026 "Blank …
## 4 AACO Abony Acquisitio… 10 0 United… 2026 ""
## 5 AACOU Abony Acquisitio… 10.1 0 United… 2026 ""
## 6 AACP Apogee Acquisiti… 10.1 0 United… 2026 "Blank …
## 7 AACPR Apogee Acquisiti… 0.17 0 United… 2026 "Blank …
## 8 AACPU Apogee Acquisiti… 10.3 0 United… 2026 "Blank …
## 9 AAL American Airline… 13.6 9009411033 United… NA "Air Fr…
## 10 AAME Atlantic America… 1.3 26516396 United… NA "Life I…
## # ℹ 4,012 more rows
The result is again a tibble containing information about listed companies.
We could investigate the industries represented on the exchange:
nasdaq |>
count(industry, sort = TRUE)
## # A tibble: 148 × 2
## industry n
## <chr> <int>
## 1 "Biotechnology: Pharmaceutical Preparations" 469
## 2 "Blank Checks" 311
## 3 "Major Banks" 222
## 4 "" 205
## 5 "Computer Software: Prepackaged Software" 195
## 6 "Biotechnology: Biological Products (No Diagnostic Substances)" 158
## 7 "EDP Services" 145
## 8 "Finance: Consumer Services" 140
## 9 "Industrial Machinery/Components" 133
## 10 "Medical/Dental Instruments" 107
## # ℹ 138 more rows
Once again, the advantage is that the financial information is immediately available in tidy form.
tidyquant can also combine the returns of several assets
into a portfolio.
Suppose we have monthly returns for:
BTC-USD
ETH-USD
AAPL
MSFT
We can define an equally weighted portfolio:
weights <- tibble(
symbol = c("BTC-USD", "ETH-USD", "AAPL", "MSFT"),
weight = c(0.25, 0.25, 0.25, 0.25)
)
Every asset receives 25% of the portfolio.
We can then calculate portfolio returns:
portfolio <- returns |>
tq_portfolio(
assets_col = symbol,
returns_col = return,
weights = weights,
col_rename = "portfolio_return"
)
portfolio
## # A tibble: 94 × 2
## date portfolio_return
## <date> <dbl>
## 1 2020-01-31 0.192
## 2 2020-02-28 -0.0350
## 3 2020-02-29 0.0433
## 4 2020-03-31 -0.219
## 5 2020-04-30 0.304
## 6 2020-05-29 0.0229
## 7 2020-05-31 0.0580
## 8 2020-06-30 0.0384
## 9 2020-07-31 0.260
## 10 2020-08-31 0.170
## # ℹ 84 more rows
assets_col = symbol identifies the column containing the
assets.
returns_col = return identifies their returns.
weights = weights specifies how much of the portfolio is
invested in each asset.
tq_portfolio() then combines the individual asset
returns into one portfolio return for each period. It is a tidy wrapper
around PerformanceAnalytics::Return.portfolio().
We can visualize the portfolio:
portfolio |>
ggplot(aes(x = date, y = portfolio_return)) +
geom_col()
Each bar represents the portfolio return for one month.
The package also provides:
tq_performance()
for computing financial performance measures.
These calculations work on returns rather than prices.
For example, we could calculate a Sharpe ratio for each asset:
performance <- returns |>
group_by(symbol) |>
tq_performance(
Ra = return,
performance_fun = SharpeRatio,
Rf = 0
)
performance
## # A tibble: 4 × 2
## # Groups: symbol [4]
## symbol `SharpeRatio(Rf=0%,p=95%)`
## <chr> <dbl>
## 1 BTC-USD 0.276
## 2 ETH-USD 0.288
## 3 AAPL 0.270
## 4 MSFT 0.283
group_by(symbol) performs the calculation separately for
each asset.
Ra = return
identifies the return variable.
performance_fun = SharpeRatio
asks for the Sharpe ratio from the PerformanceAnalytics
package.
Rf = 0
sets the risk-free return to zero for this simple example.
The important point is not the particular measure but the architecture:
tidy returns
↓
group_by()
↓
PerformanceAnalytics function
↓
tidy result
A list of available performance functions can be obtained with:
tq_performance_fun_options()
tq_get() is not restricted to ordinary stock prices.
You can inspect the available retrieval methods with:
tq_get_options()
## [1] "stock.prices" "stock.prices.japan" "dividends"
## [4] "splits" "economic.data" "quandl"
## [7] "quandl.datatable" "tiingo" "tiingo.iex"
## [10] "tiingo.crypto" "alphavantager" "alphavantage"
## [13] "rblpapi"
Depending on the source, tidyquant can provide stock
prices, dividends, stock splits and various financial or economic
datasets. Some external services require an API key.
The core of tidyquant can be summarized with a small
number of functions:
| Function | Purpose |
|---|---|
tq_get() |
Retrieve financial data |
tq_index() |
Retrieve companies in a stock index |
tq_exchange() |
Retrieve companies on an exchange |
tq_mutate() |
Add financial transformations or indicators |
tq_transmute() |
Transform financial data, especially when changing periodicity |
tq_portfolio() |
Combine asset returns into portfolio returns |
tq_performance() |
Calculate financial performance measures |
This small collection of functions is deliberately central to the package’s design.