A while ago I have written a post about leveraged etfs. While many people think that leveraged etfs decay over time, this is actually not the case. What we see is in fact a non-symmetric distribution, with a high probability of decay, but with the same expected per timestep as the non-leveraged distribution.

To prove this, we can use Monte-Carlo simulation (which is a fancy name for a brute-force approach) to test this hypothesis.

Before we start, there is one thing that needs a little bit of explanation: *libraries*

Python has only a limited set of built-in functions. Everyhing else needs to be loaded from optional modules (Don't get confused, *modules* and *libraries* are the same thing). We will use `%pylab`

magic to load most of the scientific libraries, including plotting functions.

We will be looking at modules later on, for now it is enough just to start the notebook and run the `%pylab inline`

command. `inline`

causes the figures to appear inside the notebok instead of a popup window.

In [1]:

```
%pylab inline
```

In [2]:

```
%run common.py
```

Suppose we have an etf with normally distributed daily returns. Average daily return is 0 and standard deviation is 1%. In other words chances of going up 1% are equal to chances of going down 1%

In [3]:

```
returns = randn(10000) # create a 1000-day returns vector.
#Note: don't use 'return' as a variable name, this keyword is reserved.
plot(returns) #plot the time series
grid(True) #use grid in the plot
#-------add labels to the chart
title('Daily returns')
xlabel('day')
ylabel('return [%]')
```

Out[3]:

Let's see how this distribution looks on a histogram and check if `mean`

(average) and `std`

(standard deviation) are what we expect
Note: for printing *std* I am using here string formatting in the form of `%.2f`

, which means that a floating number is printed with two decimals precision.

In [4]:

```
h = hist(returns,100) # make a histogram of returns, using 100 bins.
#h is used as a return variable to avoid text output
print('Mean:' , returns.mean()) # print mean
print('Standard deviation:%.2f' % returns.std()) # print sigma, note the 2 decimal precision notation
```

Suppose we would start on day 0 with an investment of `$100`

. On day n our investment would then have a value of `100*r[t]*r[t+1]*...r[n]`

, which is achieved by the cumulative product function. Don't forget to divide % returns by 100 and add 1 to the daily returns as we are multiplying by 1.05 in case of 5% return

In [5]:

```
price = 100*(returns/100+1).cumprod() # normalized price
plot(price) # plot cumulative sum of returns
title('normalized price') # set plot title
xlabel('day number') # add x label to the chart
ylabel('price [$]') # add y label
```

Out[5]:

You will notice that each time you run the code in the previous cells, the result is different. This is of course because we used the random number generator.
Now we can do the same calculation, but for many simulated prices at once. We could use a loop for this and repeat the above calculation many times, but there is a more elegant way. (note: the `returns`

variable from previous calculations is overwritten with a matrix)

In [6]:

```
returns = randn(1000,5000) # generate 1000x5000 random return series
prices = 100*(returns/100+1).cumprod(axis=0) # normalized price, you need to specify along which axis
#(0 is along columns) the cumulative product is calculated.
h = plot(prices[:,:500]) # plot cumulative sum of returns. Because plotting all 5000 prices would take
# a long time, only first 500 are plotted
xlabel('day number')
ylabel('price [$]')
```

Out[6]:

The final price (on the 1000th day) of the `prices`

matrix (or table if you like spreadsheet terminology) is just the last row. Let's plot a histogram of it.

In [7]:

```
finalPrice = prices[-1,:] # get the last row of prices table
h = hist(finalPrice,50) # plot histogram with 50 bins
xlabel('price [$]') # add axis labels
ylabel('frequency [#]')
print('Mean:', finalPrice.mean()) # show mean value
print('Std:', finalPrice.std()) # show standard deviation
```

The mean of resulting distribution is very close to our starting value of `100$`

, but the distribution is now skewed to the right. Calculating standard deviation for this distribution can be misleading, because it is not a normal distribution any more.

**conclusion** : an equal chance of going up or down in % does not change long term expected value. The resulting distribution is however skewed, therefore we see a price decline more often, but it is compensated by the fat tails on the up side.

Let's calculate and plot a 3x leveraged result. We can reuse the return matrix from previous calculation

In [8]:

```
returns3x = returns*3. # leveraged returns are 3x the non-leveraged returns
prices3x = 100*(returns3x/100+1).cumprod(axis=0) # normalized price, you need to specify along which axis
finalPrice3x = prices3x[-1,:] # get the last row of prices table
h1 = hist(finalPrice3x,500) # plot histogram with 500 bins. more bins are needed to keep the bin width small.
h2 = hist(finalPrice,100) # plot histogram with 100 bins
xlim([0,200]) # set x axis limits to 0..200
legend(['3x leverage','1x leverage'])
print('Mean 3x:', finalPrice3x.mean())
```

That is funny: while the 3x leveraged etf most of the time finishes under the `$50`

mark, the fat tail on the right keeps the long term average at `$100`

The tail on the right is in fact so large, that I had to limit the x-axis.

Long term expected value does not change with increasing etf leverage, but the *likelyhood* of ending up with a loss *increases*.

The above calculation is done for an etf that has no alpha bias (average daily return is 0%). Most of the stocks however do have a positive bias. Investigate what would happen with if an etf would be based on a normal distribution with an average daily return being 0.001%