library(tidyverse)
library(lubridate)
library(scales)
library(ggrepel)
library(tidyquant)
library(jsonlite)
theme_set(theme_minimal())
invisible(Sys.setlocale("LC_TIME", "en_US.UTF-8"))

library(broom)
library(purrr)
library(DT)

library(familyoffice) # prop package
library(factoextra) # install required this (1st answer) https://stackoverflow.com/questions/59055291/problem-installing-factoextra-package-in-r
library(ggcorrplot)

I believe that managed futures have been around for quite some time. More recently, ETFs promising a managed futures strategy have been launched. I wonder if these ETFs are uncorrelated with the stock market. Let’s find out.

Managed futures basically can go long or short in futures contracts on a variety of asset classes. The idea is that they can profit from both rising and falling markets. Often, the strategy is trend-following, meaning that they buy when the price is rising and sell when the price is falling, may these be stocks, bonds, commodities, or currencies. Especially commodities and currencies are often used in managed futures strategies.

From what I’ve understand, there are several main approaches to packaging managed futures into an ETF:

  1. Actually have a (trend-following) model yourself and take positions in various asset classes.
  2. Mimic the return of a managed futures index by approximating the holdings of the previous week or month that would have generated the return of the index. This could be done with a linear model.
  3. Invest in a managed futures fund and then package the return of this fund into an ETF.
# source: https://etfdb.com/etfs/asset-class/alternatives/#etfs&sort_name=assets_under_management&sort_order=desc&page=2

etfdb <- "FTLS  First Trust Long/Short Equity ETF   $1,537.78   14.39%  138,165 $63.64  -0.38%  
DBMF    iMGP DBi Managed Futures Strategy ETF   $976.96 9.13%   365,870 $27.48  -0.29%  
QAI NYLI Hedge Multi-Strategy Tracker ETF   $629.90 6.54%   149,294 $32.07  0.03%   
RLY SPDR SSgA Multi-Asset Real Return ETF   $506.88 8.00%   63,355  $28.93  0.52%   
BTAL    AGF U.S. Market Neutral Anti-Beta Fund  $403.25 15.99%  609,150 $19.66  -0.56%  
KMLM    KFA Mount Lucas Managed Futures Index Strategy ETF  $353.64 -3.96%  87,059  $27.68  -0.36%  
CTA Simplify Managed Futures Strategy ETF   $289.80 14.09%  140,845 $26.31  1.43%   
FMF First Trust Managed Futures Strategy Fund   $238.65 3.60%   50,676  $46.84  0.17%   
MNA NYLI Merger Arbitrage ETF   $232.28 4.68%   56,650  $32.88  0.03%   
RSST    Return Stacked U.S. Stocks & Managed Futures ETF    $225.39 15.44%  98,283  $23.33  -0.26%  
FLSP    Franklin Systematic Style Premia ETF    $205.88 10.35%  11,852  $23.89  -0.04%  
WTMF    WisdomTree Managed Futures Strategy Fund    $202.82 0.20%   30,508  $35.14  -0.28%  
CLSE    Convergence Long/Short Equity ETF   $175.88 32.98%  54,780  $22.68  0.00%   
EQLS    Simplify Market Neutral Equity Long/Short ETF   $143.22 -0.80%  7,750   $21.89  0.14%   
PFIX    Simplify Interest Rate Hedge ETF    $125.44 16.58%  59,270  $45.35  2.25%   
QIS Simplify Multi-QIS Alternative ETF  $107.38 -1.56%  6,476   $24.08  0.38%   
PRMN    PlanRock Market Neutral Income ETF  $104.90 0.44%   1,603   $29.01  0.14%   
RSBT    Return Stacked Bonds & Managed Futures ETF  $96.04  -1.10%  28,600  $17.06  0.00%   
UPAR    UPAR Ultra Risk Parity ETF  $82.61  7.97%   17,865  $14.43  0.00%   
HTUS    Capitol Series Trust Hull Tactical US ETF   $78.67  23.43%  17,885  $44.13  -0.07%  
ARB AltShares Merger Arbitrage ETF  $72.72  4.00%   9,161   $27.82  0.04%   
FIAX    Nicholas Fixed Income Alternative ETF   $71.03  5.43%   53,667  $19.66  -0.10%  
VAMO    Cambria Value & Momentum ETF    $51.80  4.74%   2,891   $29.44  -0.54%  
EHLS    Even Herd Long Short ETF    $50.73  N/A 3,502   $21.02  -0.57%  
LBAY    Leatherback Long/Short Alternative Yield ETF    $41.95  8.55%   2,355   $27.46  -0.15%  
TPMN    Timothy Plan Market Neutral ETF $40.05  0.80%   2,591   $23.83  0.38%   
CBLS    Clough Hedged Equity ETF    $35.05  23.11%  3,285   $25.24  0.08%   
HFND    Unlimited HFND Multi-Strategy Return Tracker ETF    $34.83  7.90%   8,120   $22.46  -0.13%  
FFLS    Future Fund Long/Short ETF  $34.45  15.75%  6,979   $23.39  0.52%   
BSR Beacon Selective Risk ETF   $30.92  13.23%  414 $29.36  -0.37%  
MARB    First Trust Merger Arbitrage ETF    $28.87  -0.07%  6,709   $19.92  0.10%   
HDG ProShares Hedge Replication ETF $28.16  4.70%   1,809   $49.69  -0.16%  
ARP PMV Adaptive Risk Parity ETF    $28.03  13.97%  2,065   $28.66  0.10%   
FORH    Formidable ETF  $23.89  4.14%   2,242   $23.26  -0.21%  
LSEQ    Harbor Long-Short Equity ETF    $21.90  13.29%  1,041   $27.19  -0.77%  
RINF    ProShares Inflation Expectations ETF    $21.67  8.65%   5,206   $33.03  0.36%   
HF  DGA Core Plus Absolute Return ETF   $19.00  12.70%  444 $23.29  -0.04%  
ZIVB    -1x Short VIX Mid-Term Futures Strategy ETF $14.32  3.88%   32,614  $19.39  0.10%   
MSVX    LHA Market State Alpha Seeker ETF   $11.02  -7.46%  6,953   $22.05  0.00%   
TOAK    Twin Oak Short Horizon Absolute Return ETF  $10.52  N/A N/A $26.99  0.04%   
MRGR    Proshares Merger ETF    $10.50  5.06%   636 $42.05  0.00%   
CLIX    ProShares Long Online/Short Stores ETF  $9.27   25.73%  1,238   $47.66  0.17%   
ASMF    Virtus Alphasimplex Managed Futures ETF $9.03   N/A 2,611   $24.10  -0.21%  
EVNT    AltShares Event-Driven ETF  $4.88   4.23%   426 $10.61  0.00%   
USE USCF Energy Commodity Strategy Absolute Return Fund $3.96   29.08%  192 $40.70  2.62%   
RYSE    Vest 10 Year Interest Rate Hedge ETF    $2.28   3.69%   4,803   $22.80  0.18%   
RATE    Global X Interest Rate Hedge ETF    $1.86   3.05%   992 $18.70  0.75%   
HYKE    Vest 2 Year Interest Rate Hedge ETF $1.27   N/A 874 $25.35  -0.55%
AQMIX   AQR Managed Futures Strategy Fund Class I   $1454.00    -0.10%  1,000   $10.00  0.00%"

etfdb <- read_lines(etfdb) |> 
  as_tibble() |> 
  separate(value, sep = "\t", into = c("ticker", "name", "aum", "ytd", "volume", "price", "change"))

Besides, managed futures, there are other alternative ETFs around, which might also be able to deliver uncorrelated returns. ETFdb.com lists 49 Alternative ETFs, among which some MF ones that gained a lot of popularity in the past years, e.g. DBMD, CTA, or RSST.

We manually add AQMIX AQR Managed Futures Strategy Fund Class I to the list which is a mutual fund but has in comparison to the ETFs has been around for longer (since 2010).

DT::datatable(etfdb, 
         extensions = c('FixedColumns',"FixedHeader"),
          options = list(scrollX = F, 
                         paging=T,
                         fixedHeader=TRUE))

Data

We download price data from Yahoo Finance.

etf_prices <- tq_get(etfdb$ticker, from = 0)
etf_prices |> saveRDS("data/uncorrelatedetfs/etf_prices.RDS")

spy <- tq_get("SPY", from = 0)
spy |> saveRDS("data/uncorrelatedetfs/spy.RDS")

agg <- tq_get("AGG", from = 0)
agg |> saveRDS("data/uncorrelatedetfs/agg.RDS")
etf_prices <- readRDS("data/uncorrelatedetfs/etf_prices.RDS")
spy <- readRDS("data/uncorrelatedetfs/spy.RDS")
agg <- readRDS("data/uncorrelatedetfs/agg.RDS")
etf_prices |> 
  group_by(symbol) |> 
  summarise(from = min(date),
            to = max(date)) |> 
  ungroup() |> 
  left_join(etfdb, by = c("symbol" = "ticker")) |>
  mutate(aum = parse_number(aum),
         MF = ifelse(grepl("Managed", name), "MF", "Other")) |>
  ggplot() +
  geom_segment(aes(x = from, xend = to, y = reorder(symbol, desc(from)), yend = reorder(symbol, desc(from)),
                   color = aum),
               size = 2) +
  # highlight managed futures
  geom_segment(data = ~. |> filter(MF == "MF"),
               aes(x = from, xend = to, y = reorder(symbol, desc(from)), yend = reorder(symbol, desc(from)),
                   color = aum),
               size = 2) +
  geom_text(aes(x = from, y = reorder(symbol, desc(from)), label = symbol), 
            nudge_x = -1, hjust = 1) +
  # red dot if MF
  geom_point(data = ~. |> filter(MF == "MF"),
             aes(x = from, y = reorder(symbol, desc(from))),
             size = 2, color = "red") +
  labs(title = "Alt ETFs Inception and AUM (red dot = Managed Futures)",
       x = NULL, y = NULL, color = "AUM M$") +
  scale_color_viridis_c() +
  theme(legend.position = "bottom")

Returns

etf_returns <- etf_prices |> 
  group_by(symbol) |> 
  transmute(symbol, date, r = adjusted/lag(adjusted)-1) |> 
  ungroup() |> 
  drop_na()
annual_rets <- etf_returns |> 
  group_by(symbol, year = year(date)) |> 
  filter(n() > 240 | year == 2024) |> 
  summarise(r = prod(1+r)-1)
  
annual_rets |> 
  group_by(symbol) |> 
  mutate(minyear = min(year)) |> 
  ggplot(aes(x = year, y = reorder(symbol, -minyear), fill = r>0)) +
  geom_tile(color = "white") +
  geom_text(aes(label = scales::percent(r, accuracy = 0.1)), size = 3, color = "white") +
  scale_fill_manual(values = c("red", "darkgreen")) +
  scale_x_continuous(breaks = seq(2000, 2050, 1)) +
  labs(title = "Annual Returns",
       x = NULL, y = NULL, fill = NULL) +
  theme(legend.position = "none")

The main selling point of managed futures is that they deliver positive returns while being uncorrelated with other asset classes. We look at the uncorrelatedness in a moment, but for now in the chart above, we see that the returns of can vary quite a bit from year to year. Interestingly, in 2024 so far, most of the ETFs have delivered positive returns (only 7/48 red).

pos_years <- annual_rets |> 
  filter(year < 2024) |> 
  group_by(r = r > 0) |> 
  count() |> 
  ungroup() |> 
  mutate(percent = n/sum(n)) |> 
  filter(r)

Excluding 2024, 62% of the annual returns were positive.

Stock and Bond Market Correlation

The big question is: Are these ETFs uncorrelated?

stock_bond_rets <- bind_rows(spy |> transmute(date, symbol, r = adjusted/lag(adjusted)-1),
                             agg |> transmute(date, symbol, r = adjusted/lag(adjusted)-1)) |>
  drop_na() |> 
  pivot_wider(names_from = symbol, values_from = r) |> 
  drop_na()

# r ~ SPY + AGG
# grouped by symbol (later also by year)

stock_corr <- etf_returns |> 
  left_join(stock_bond_rets, by = "date") |> 
  group_by(symbol) |>
  filter(n() > 252) |>
  tq_mutate_xy(
    x          = SPY,
    y          = r,
    mutate_fun = runCor, 
    # runCor args
    n          = 252,
    use        = "pairwise.complete.obs",
    # tq_mutate args
    col_rename = "stock_corr"
  )

stock_corr |> 
  drop_na() |> 
  ggplot(aes(x = date, y = stock_corr, color = symbol)) +
  geom_line(size = 0.1) +
  geom_text_repel(aes(label = ifelse(date == max(date), symbol, NA_character_)), 
                      nudge_x = 1, nudge_y = 0.01, size = 2.3, direction = "y") +
  geom_hline(yintercept = 0, linetype = "dashed", color = "red", size = 0.1) +
  scale_y_continuous(limits = c(-1, 1)) +
  labs(title = "Stock Market Correlation (rolling 252 days)", 
       x = NULL, y = NULL, color = NULL) +
  theme(legend.position = "none")

bond_corr <- etf_returns |> 
  left_join(stock_bond_rets, by = "date") |> 
  group_by(symbol) |>
  filter(n() > 252) |>
  tq_mutate_xy(
    x          = AGG,
    y          = r,
    mutate_fun = runCor, 
    # runCor args
    n          = 252,
    use        = "pairwise.complete.obs",
    # tq_mutate args
    col_rename = "bond_corr"
  )

bond_corr |> 
  drop_na() |> 
  ggplot(aes(x = date, y = bond_corr, color = symbol)) +
  geom_line(size = 0.1) +
  geom_text_repel(aes(label = ifelse(date == max(date), symbol, NA_character_)), 
                      nudge_x = 1, nudge_y = 0.01, size = 2.3, direction = "y") +
  geom_hline(yintercept = 0, linetype = "dashed", color = "blue", size = 0.1) +
  scale_y_continuous(limits = c(-1, 1)) +
  labs(title = "Bond Market Correlation (rolling 252 days)", 
       x = NULL, y = NULL, color = NULL) +
  theme(legend.position = "none")

While stock correlations seem to tend to be in the positive range (some like BTAL AGF U.S. Market Neutral Anti-Beta Fund were actually negatively correlated to stocks), bond correlations are more around zero.

Stock and Bond Market Betas

We would also like to know the stock and bond market betas.

stock_beta <- etf_returns |> 
  left_join(stock_bond_rets, by = "date") |> 
  group_by(symbol) |> 
  nest() |> 
  mutate(
    model = map(data, ~ lm(r ~ SPY + AGG, data = .x)),
    tidied = map(model, tidy)
  ) |> 
  unnest(tidied) |> 
  filter(term %in% c("SPY", "AGG")) |> 
  select(symbol, term, estimate, p.value) |> 
  pivot_wider(names_from = term, values_from = c(estimate, p.value)) |> 
  rename(coefSPY = estimate_SPY, 
         pvalSPY = p.value_SPY, 
         coefAGG = estimate_AGG, 
         pvalAGG = p.value_AGG) |> 
  left_join(
    etf_returns |> group_by(symbol) |> summarise(from = min(date)),
    by = "symbol"
  )

stock_beta |> 
  ggplot(aes(x = coefSPY, y = coefAGG, color = from)) +
  geom_point() +
  geom_text_repel(aes(label = symbol), nudge_x = 0.01, nudge_y = 0.01) +
  labs(title = "Stock and Bond Market Betas",
       x = "Stock Beta", y = "Bond Beta", color = "Inception") +
  theme(legend.position = "none")

The stock betas are mostly positive, but some are negative. The bond betas are mostly around zero.

DT::datatable(stock_beta |> 
                left_join(etfdb, by = c("symbol" = "ticker")) |>
                select(symbol, name, coefSPY, pvalSPY, coefAGG, pvalAGG), 
         extensions = c('FixedColumns',"FixedHeader"),
          options = list(scrollX = F, 
                         paging=T,
                         fixedHeader=TRUE)) |> 
  # coefSPY, coefAGG, pvalSPY, pvalAGG all show max 2 digits 0.01
  formatRound(columns = c("coefSPY", "coefAGG", "pvalSPY", "pvalAGG"), 
              digits = 2, )
stock_beta |> 
  left_join(etfdb, by = c("symbol" = "ticker")) |>
  mutate(MF = ifelse(grepl("Managed", name), "MF", "Other")) |>
  ggplot(aes(x = coefSPY, y = coefAGG, color =  MF)) +
  geom_point() +
  geom_text_repel(aes(label = ifelse(MF == "MF", symbol, NA_character_)),
                      nudge_x = 0.01, nudge_y = 0.01, size = 3) +
  scale_color_manual(values = c("red", "black")) +
  labs(title = "Stock and Bond Market Betas",
       x = "Stock Beta", y = "Bond Beta", color = "Managed Futures")

We see that the main managed futures ETFs have a stock beta around 0 indicating that they are indeed uncorrelated with the stock market. The bond beta is around zero or in the negative range. The latter would mean that as bond prices rise, the managed futures ETFs tend to fall in price, or some positive dependency on yields.

Some exceptions are RSST Return Stacked U.S. Stocks & Managed Futures ETF with a stock beta of 1.5, but the fund name suggests that it is not a pure managed futures ETF. Also, ASMF Virtus Alphasimplex Managed Futures ETF had a positive stock beta of 0.6, however, it only having launched in May 2024, this stat not be very reliable.

Focus on Managed Futures

The following table shows the managed futures ETFs sorted by the percentage of months with positive returns.

mf_returns_m <- etf_returns |> 
  left_join(etfdb, by = c("symbol" = "ticker")) |>
  # only MF
  filter(grepl("Managed", name)) |> 
  group_by(symbol, name, date = floor_date(date, "month")) |>
  filter(n() > 18) |>
  summarise(r = prod(1+r)-1)

stock_bond_rets_m <- stock_bond_rets |> 
  pivot_longer(cols = c(SPY, AGG), names_to = "symbol", values_to = "r") |> 
  group_by(symbol, name = "", date = floor_date(date, "month")) |>
  filter(n() > 18) |>
  summarise(r = prod(1+r)-1)

mf_returns_m |> 
  bind_rows(stock_bond_rets_m) |> 
  group_by(symbol, name, return = ifelse(r > 0, "up", "down")) |> 
  count() |> 
  ungroup() |> 
  pivot_wider(names_from = return, values_from = n) |>
  mutate(percentUP = up/(up+down)) |> 
  arrange(percentUP) |> 
  DT::datatable(~., 
         extensions = c('FixedColumns',"FixedHeader"),
          options = list(scrollX = F, 
                         paging=T,
                         pageLength = 12,
                         fixedHeader=TRUE)) |>
  formatPercentage(columns = c("percentUP"), digits = 1) 

Return Characteristics

The following charts show the cumulative returns.

mf_returns_m |> 
  bind_rows(stock_bond_rets_m) |> 
  # histogram for each (facet wrap)
  ggplot(aes(x = r)) +
  geom_histogram(bins = 30) +
  geom_vline(xintercept = 0, linetype = "dashed", color = "red") +
  facet_wrap(~symbol, scales = "free") +
  labs(title = "Monthly Returns",
       x = NULL, y = NULL, fill = NULL) +
  scale_x_continuous(labels = scales::percent_format(accuracy = 0.1), 
                     limits = c(-0.15, 0.15)) +
  theme(legend.position = "none")

The returns are somewhat symmetrically distributed around zero, especially compared to the SPY and AGG returns. Stock returns (SPY) are more skewed to the left meaning that there are some months with stronger negative returns.

# scatterplot SPY vs individual ETFs
mf_returns_m |> 
  left_join(stock_bond_rets_m |> 
              pivot_wider(names_from = symbol, values_from = r),
            by = "date") |> 
  ggplot(aes(x = SPY, y = r)) +
  geom_point(size = 0.3) +
  geom_smooth(method = "lm") +
  facet_wrap(~symbol, scales = "free") +
  scale_x_continuous(labels = scales::percent_format(accuracy = 0.1), limits = c(-0.1, 0.1)) +
  scale_y_continuous(labels = scales::percent_format(accuracy = 0.1), limits = c(-0.1, 0.1)) +
  labs(title = "Monthly Returns vs SPY",
       x = "SPY", y = NULL) +
  theme(legend.position = "none")

etf_returns |> 
  bind_rows(stock_bond_rets |> pivot_longer(cols = c(SPY, AGG), names_to = "symbol", values_to = "r")) |>
  group_by(symbol, date = floor_date(date, "month")) |>
  filter(n() > 18) |>
  summarise(r = prod(1+r)-1) |>
  summarise(annualizedret = prod(1+r)^(12/n())-1,
            annualizedvola = sd(r)*sqrt(12),
            SR = annualizedret/annualizedvola,
            since = min(date)) |> 
  DT::datatable(~., 
         extensions = c('FixedColumns',"FixedHeader"),
          options = list(scrollX = F, 
                         paging=T,
                         fixedHeader=TRUE)) |>
  formatPercentage(columns = c("annualizedret"), digits = 1) |>
  formatPercentage(columns = c("annualizedvola"), digits = 1)

PCA

Since December 2020

ret_mat <- mf_returns_m |> 
  bind_rows(stock_bond_rets_m) |> 
  group_by(symbol) |> 
  filter(min(date) < as.Date("2022-01-01")) |>
  select(symbol, date, r) |>
  pivot_wider(names_from = symbol, values_from = r) |> 
  drop_na() |> 
  arrange(date) |> 
  as.data.frame() |> 
  column_to_rownames(var = "date") |> 
  as.matrix()

pca_results <- ret_mat |> 
  prcomp(center = TRUE, scale. = TRUE)

pca_results$rotation |> 
  as.data.frame() |> 
  rownames_to_column(var = "symbol") |> 
  arrange(desc(PC1)) |> 
  select(symbol, PC1, PC2, PC3, PC4) |>
  DT::datatable(~., 
         extensions = c('FixedColumns',"FixedHeader"),
          options = list(scrollX = F, 
                         paging=T,
                         fixedHeader=TRUE)) |>
  formatPercentage(columns = c("PC1", "PC2", "PC3", "PC4"), digits = 1)
pca_results |> fviz_pca_biplot()

Since April 2022

ret_mat <- mf_returns_m |> 
  bind_rows(stock_bond_rets_m) |> 
  group_by(symbol) |> 
  filter(min(date) < as.Date("2023-01-01")) |>
  select(symbol, date, r) |>
  pivot_wider(names_from = symbol, values_from = r) |> 
  drop_na() |> 
  arrange(date) |> 
  as.data.frame() |> 
  column_to_rownames(var = "date") |> 
  as.matrix()

pca_results <- ret_mat |> 
  prcomp(center = TRUE, scale. = TRUE)

pca_results$rotation |> 
  as.data.frame() |> 
  rownames_to_column(var = "symbol") |> 
  arrange(desc(PC1)) |> 
  select(symbol, PC1, PC2, PC3, PC4) |>
  DT::datatable(~., 
         extensions = c('FixedColumns',"FixedHeader"),
          options = list(scrollX = F, 
                         paging=T,
                         fixedHeader=TRUE)) |>
  formatPercentage(columns = c("PC1", "PC2", "PC3", "PC4"), digits = 1)
pca_results |> fviz_pca_biplot()

Correlations (since March 2022)

cor_mat <- ret_mat |> cor()

ggcorrplot(cor_mat,
           hc.order = TRUE,
           type = "lower",
           lab = TRUE, 
           colors = c("#E46726", "white", "#6D9EC1"))

There is clear correlation between the ETFs, especially the managed futures ones. The stock and bond market ETFs are negatively correlated with the managed futures ETFs. One exception is the WTMF WisdomTree Managed Futures Strategy Fund which is somwewhat positively correlated with the stock market (0.46).