Thursday, 2 February 2023

Playing around with leveraged ETFs; or how to get positive skew without trend following

 As readers of my books will know, I don't recommend leveraged ETFs as a way to get leverage. Their ways are very dark and mysterious. But like many dark and mysterious things, they are also kind of funky and cool. In this post I will explore their general funkiness, and I will also show you how you can use them to produce a positive skewed return without the general faff of alternative ways of doing that: building a trend following strategy or trading options. 

There is some simple python code in this post, but you don't need to be a pythonista to follow.


A simple model for leveraged ETF payoffs

As I was feeling unusually patriotic when I wrote this post, I decided to use the following FTSE 100 2x leveraged as my real life examples of leveraged ETFs:

Long: https://www.justetf.com/uk/etf-profile.html?isin=IE00B4QNJJ23

Short: https://www.justetf.com/uk/etf-profile.html?isin=IE00B4QNK008

It's very easy to work out how much a 2xleveraged ETF will be worth at some terminal point in the future. Assuming the current value is 1, and given a set of daily percentage returns, and specifying a long or short ETF:

def terminal_value_of_etf(returns: np.array, long: bool = True) -> float:
if long:
leveraged_returns = returns * 2
else:
leveraged_returns = returns * -2

terminal_value = (leveraged_returns + 1).cumprod()[-1]

return terminal_value

Now the best things in life are free, but ETFs aren't. We have to pay trading and management costs. The management costs on my two examples are around 0.55% a year (one is 0.5%, the other 0.6%) and the spread cost come in at 0.05% per trade. If we hold for a year then that will set us back 0.65%; call it 0.75% if we also have to pay commission (that would be the commission on a £5k trade if you're paying £5 a go).

Assuming we hold the ETFs for a year (~256 business days), we can then generate some random returns with some Gaussian noise and some given parameters, and get the terminal value. Finally, it's probably easier to think in terms of percentage gain or loss:

from random import gauss
def random_returns(annual_mean=0, annual_std=0.16, count=256):
return np.array([gauss(annual_mean / 256, annual_std / 16) for _ in range(count)])


def one_year_return_given_random_returns(
long: bool = True, annual_mean=0, annual_std=0.16, cost=0.0075
) -> float:
returns = random_returns(annual_mean=annual_mean, annual_std=annual_std)
value = terminal_value_of_etf(
returns, long=long
)

return (value - cost - 1.0) / 1.0


Let's generate a bunch of these random terminal payoffs and see what they look like.

x = [one_year_return_given_random_returns(long=False) for _ in range(100000)]

import pandas as pd
def plot_distr_x_and_title(x: list):
x_series = pd.Series(x)
x_series.plot.hist(bins=50)
plt.title(
"Mean %.1f%% Median %.1f%% 1%% left tail %.1f%% 1%% right tail %.1f%% skew %.3f"
% (
x_series.mean() * 100,
x_series.median() * 100,
x_series.quantile(0.01) * 100,
x_series.quantile(0.99) * 100,
x_series.skew(),
)
)

plot_distr_x_and_title(x)


Well the mean makes sense - it's equal to our costs (as the mean of the Gaussian noise here is zero), but where is that glorious fat right tail coming from? It's what will happen with compounded gains and losses. Think about it like this; if we get unlucky and lose say 0.1% every day then the cumulative product of 0.999^256 is 0.77; a loss of 23%. But if we make 0.1% a day then 1.001^256 is 1.29; a gain of 29%. 

Note that we'd get exactly the same graph with a short leveraged ETF, again with the mean of the noisy returns equal to zero.

What if the standard deviation was higher; say 32% a year?

Interesting.

 

ETF payoffs versus drift

Now what will the payoff look like if the underlying return has some drift? To be more interesting, let's plot the ETF return vs the total cumulative return of the underlying index for each of the random samples, playing around with the drift to get a good range of possible index returns.

def one_year_return_and_index_return_given_random_returns(
long: bool = True, annual_mean=0, annual_std=0.16, cost=0.0075
):
returns = random_returns(annual_mean=annual_mean, annual_std=annual_std)
index_return = ((returns + 1).cumprod() - 1)[-1]
value = terminal_value_of_etf(returns, long=long)
etf_return = (value - cost - 1.0) / 1.0

return index_return, etf_return


index_returns = np.arange(start=-0.25, stop=0.25, step=0.0001)
all_index_returns = []
all_etf_returns = []
for mean_drift in index_returns:
for _ in range(100):
results = one_year_return_and_index_return_given_random_returns(
annual_mean=mean_drift
)
all_index_returns.append(results[0])
all_etf_returns.append(results[1])

to_scatter = pd.DataFrame(
dict(index_returns=all_index_returns, etf_returns=all_etf_returns)
)

to_scatter.plot.scatter(x="index_returns", y="etf_returns")

Looks like an option payoff doesn't it?

Double the fun

So.... if owning a long (or short) 2xleveraged ETF is a bit like owning an option, then owning a long AND a short leveraged ETF will be a bit like owning a straddle? And since the payoff from owning a straddle is a bit like the payoff from trend following...

So let's simulate what happens if we buy both a long AND a short leveraged ETF.

def one_year_return_and_index_return_given_random_returns_for_long_and_short(
annual_mean=0, annual_std=0.16, cost=0.0075
):
returns = random_returns(annual_mean=annual_mean, annual_std=annual_std)
index_return = returns.mean()*len(returns)
long_value = terminal_value_of_etf(returns, long=True)
short_value = terminal_value_of_etf(returns, long=False)

long_etf_return = (long_value - cost - 1.0) / 1.0
short_etf_return = (short_value - cost - 1.0) / 1.0

total_return = (long_etf_return + short_etf_return) / 2.0

return index_return, total_return


index_returns = np.arange(start=-0.25, stop=0.25, step=0.0001)
all_index_returns = []
all_etf_returns = []
for mean_drift in index_returns:
for _ in range(100):
results = (
one_year_return_and_index_return_given_random_returns_for_long_and_short(
annual_mean=mean_drift
)
)
all_index_returns.append(results[0])
all_etf_returns.append(results[1])

to_scatter = pd.DataFrame(
dict(index_returns=all_index_returns, etf_returns=all_etf_returns)
)

to_scatter.plot.scatter(x="index_returns", y="etf_returns")




Certainly looks like the payoff of a long straddle, or trend following. We never lose more than 7% - which is a bit like the premium of the option - but if the index moves a fair bit in eithier direction then we make serious bank.


And in conclusion...

This has been a nice bit of fun, but am I seriously suggesting that buying a paired set of ETFs is a serious substitute for a trend following strategy? I'd like to think that trend following has a positive expectancy, whereas this is certainly a bit more like owning a long straddle; paying a premium if prices don't move very much and then getting a non linear payoff if they move a lot.

And my usual advice still stands - leveraged ETFs are not for the faint hearted, and have no place in most investors portfolios.

7 comments:

  1. There's a typo in `one_year_return_and_index_return_given_random_returns_for_long_and_short`. Instead of `total_return = long_etf_return + short_etf_return / 2.0`, it should be `total_return = (long_etf_return + short_etf_return) / 2.0`. That will remove the big skew to the left.

    ReplyDelete
  2. The function `gauss` is not mentioned or defined. I have replaced it with `return np.random.normal(annual_mean / 256, annual_std / 16, count)`

    ReplyDelete
  3. "We never lose more than 7% - which is a bit like the premium of the option - but if the index moves a fair bit in eithier direction then we make serious bank."

    Why is it so? When you run both a long and short, wouldn't they just erase each others profits? I assume you have equal weight?

    ReplyDelete
  4. Please explain why leverage from futures trading is better than leveraged ETFs for a balanced portfolio that deploys leverage. Is it because of the daily leverage reset in levered ETFs? If so, can you please explain that a bit.

    ReplyDelete
    Replies
    1. Just google 'dangers of leveraged ETFs'; but yes it's the daily reset (also fees)

      Delete

Comments are moderated. So there will be a delay before they are published. Don't bother with spam, it wastes your time and mine.