Financial Portfolio rebalancing with Elixir
Tags: elixir, finance
Published: 2019-09-03 21:34:06

---
title: "Financial Portfolio rebalancing with Elixir"
tags: elixir, finance
published: true
---

## What's a portfolio?

> A portfolio is a grouping of financial assets such as stocks, bonds, commodities, currencies and cash equivalents, as well as their fund counterparts, including mutual, exchange-traded and closed funds. A portfolio can also consist of non-publicly tradable securities, like real estate, art, and private investments.

Source: <a href="https://www.investopedia.com/terms/p/portfolio.asp" target="_blank">investopedia</a>


## The “Permanent Portfolio”

The “Permanent Portfolio” was created by Harry Browne in the 1980s. It assumes the economy has four possible states:

* __Prosperity__: A period of economic growth, thriving business, low unemployment rates and low-interest rates.
* __Inflation__:  A period when consumer prices generally are rising.
* __Deflation__: The opposite of inflation.
* __Recession__: A significant decline in economic activity spread across the economy.

## Distribution

Based on those economic conditions, Browne identified the most volatile asset classes, proposing the following distribution:

* __Prosperity__: 25% Stocks
* __Inflation__: 25% Gold
* __Deflation__: 25% Long term government bonds
* __Recession__: 25% Cash

The self-regulation factor of the "Permanent Portfolio" is one of the fundamental concepts of Browne's idea. It covers the most different scenarios of the economy, looking for ways to compensate losses and increase returns. This concept makes the "Permanent Portfolio" appealing to people who don't want to be actively managing a portfolio on a daily (or even monthly) basis.

Let’s take a look at the historical prices of Gold and S&P500 (an index that measures the stock performance of the 500 large companies listed on stock exchanges in the United States).

<span class="image">
![S&P vs Gold](https://thepracticaldev.s3.amazonaws.com/i/6thlksaoal9v8b548cb1.png)
</span>

Source: <a href="https://finance.yahoo.com/quote/%5EGSPC/chart?p=%5EGSPC#eyJpbnRlcnZhbCI6Im1vbnRoIiwicGVyaW9kaWNpdHkiOjEsImNhbmRsZVdpZHRoIjo1LjcwNTg4MjM1Mjk0MTE3Nywidm9sdW1lVW5kZXJsYXkiOnRydWUsImFkaiI6dHJ1ZSwiY3Jvc3NoYWlyIjp0cnVlLCJjaGFydFR5cGUiOiJsaW5lIiwiZXh0ZW5kZWQiOmZhbHNlLCJtYXJrZXRTZXNzaW9ucyI6e30sImFnZ3JlZ2F0aW9uVHlwZSI6Im9obGMiLCJjaGFydFNjYWxlIjoibGluZWFyIiwicGFuZWxzIjp7ImNoYXJ0Ijp7InBlcmNlbnQiOjEsImRpc3BsYXkiOiJeR1NQQyIsImNoYXJ0TmFtZSI6ImNoYXJ0IiwidG9wIjowfX0sImxpbmVXaWR0aCI6Miwic3RyaXBlZEJhY2tncm91ZCI6dHJ1ZSwiZXZlbnRzIjp0cnVlLCJjb2xvciI6IiM3MmQzZmYiLCJldmVudE1hcCI6eyJjb3Jwb3JhdGUiOnsiZGl2cyI6dHJ1ZSwic3BsaXRzIjp0cnVlfSwic2lnRGV2Ijp7fX0sInN5bWJvbHMiOlt7InN5bWJvbCI6Il5HU1BDIiwic3ltYm9sT2JqZWN0Ijp7InN5bWJvbCI6Il5HU1BDIn0sInBlcmlvZGljaXR5IjoxLCJpbnRlcnZhbCI6Im1vbnRoIn0seyJzeW1ib2wiOiJHTEQiLCJzeW1ib2xPYmplY3QiOnsic3ltYm9sIjoiR0xEIn0sInBlcmlvZGljaXR5IjoxLCJpbnRlcnZhbCI6Im1vbnRoIiwiaWQiOiJHTEQiLCJwYXJhbWV0ZXJzIjp7ImNvbG9yIjoiI2UwOWMwMCIsIndpZHRoIjoyLCJpc0NvbXBhcmlzb24iOnRydWUsImNoYXJ0TmFtZSI6ImNoYXJ0Iiwic3ltYm9sT2JqZWN0Ijp7InN5bWJvbCI6IkdMRCJ9LCJwYW5lbCI6ImNoYXJ0IiwiYWN0aW9uIjoiYWRkLXNlcmllcyIsInNoYXJlWUF4aXMiOnRydWUsInN5bWJvbCI6IkdMRCIsImdhcERpc3BsYXlTdHlsZSI6InRyYW5zcGFyZW50IiwibmFtZSI6IkpaTU4yUEVFSkMiLCJvdmVyQ2hhcnQiOnRydWUsInVzZUNoYXJ0TGVnZW5kIjp0cnVlLCJoZWlnaHRQZXJjZW50YWdlIjowLjcsIm9wYWNpdHkiOjEsImhpZ2hsaWdodGFibGUiOnRydWUsInR5cGUiOiJsaW5lIiwic3R5bGUiOiJzdHhfbGluZV9jaGFydCJ9fV0sImN1c3RvbVJhbmdlIjp7InN0YXJ0IjoxMTIwMTY4ODAwMDAwLCJlbmQiOjE1NjE5MzIwMDAwMDB9LCJ3aWR0aCI6Miwic3R1ZGllcyI6eyJ2b2wgdW5kciI6eyJ0eXBlIjoidm9sIHVuZHIiLCJpbnB1dHMiOnsiaWQiOiJ2b2wgdW5kciIsImRpc3BsYXkiOiJ2b2wgdW5kciJ9LCJvdXRwdXRzIjp7IlVwIFZvbHVtZSI6IiMwMGIwNjEiLCJEb3duIFZvbHVtZSI6IiNGRjMzM0EifSwicGFuZWwiOiJjaGFydCIsInBhcmFtZXRlcnMiOnsid2lkdGhGYWN0b3IiOjAuNDUsImNoYXJ0TmFtZSI6ImNoYXJ0In19fSwicmFuZ2UiOnsiZHRMZWZ0IjoiMjAwNS0wNi0yMVQyMjowMDowMC4wMDBaIiwiZHRSaWdodCI6IjIwMTktMDgtMjJUMTI6MDM6MTcuNjgyWiIsInBlcmlvZGljaXR5Ijp7ImludGVydmFsIjoibW9udGgiLCJwZXJpb2QiOjF9LCJwYWRkaW5nIjowfX0%3D" target="_blank">Yahoo Finance</a>

In 2008, the year of the global financial crisis, stock investors definitively got worried about the market, and as a result, the value of the S&P index plunges. For 4 years, the S&P value stayed low, only getting back to its pre-crisis value after 2013. In case your investments were purely stocks,
it wouldn't be a fun ride.

At the same time, around 2008, the price of gold has started rising. When comparing it to the S&P value at the same time, its behaviour presents almost an inverted shape. The gold value has not only covered the stock losses but also has contributed to a profitable year.

While many investors were incredibly affected by the 2008 crisis, "Permanent Portfolio" holders had the following returns:

* 2008: +1.97%
* 2009: +7.8%
* 2010: +14.5%
* 2011: +11.36%
* 2012: +6.8%
* 2013: -2.4%
* 2014: +9.1%

Despite a small glitch in 2013, the returns from the "Permanent Portfolio" were always positive. Considering the whole period between 2008 and 2014, the portfolio had an average return of 7% per year.

That's one of the properties of the "Permanent Portfolio", which makes its usage so appealing to more risk-averse humans like myself.

Fast forward to 2019, many people are concerned about a new financial crisis. Germany (where I live) is about to declare "technical recession" (two consecutive quarterly contractions in the economy). If you look at the chart, you can again see the unbalanced form between S&P and gold.

Of course that by looking at this chart, I (an amateur investor) can't draw any conclusions about whether there's a recession coming next or not. What's essential to highlight is that the behaviour of the "Permanent Portfolio" is again supporting a potential crisis scenario.

## Rebalancing a portfolio

The portfolio distribution (Stocks: 25%, Gold: 25%, Long term government bonds: 25%, Cash: 25%), will change over time. Some individual classes will increase, while others will decrease. That change shifts the asset classes desired allocation.

The “Permanent Portfolio” embraces the fact that the market is cyclical. There will be prosperity times where stocks value will rise
and gold will plunge, but there will also be times where the stock market is not the best option.

Rebalancing a portfolio means getting the asset classes back to their desired allocations (25% each),
by selling the ones that are overweight and buying the underweight ones.

The financial crisis in 2008 has created an unbalanced portfolio situation. It "forced" the portfolio holder to sell parts of the gold and use the profits to buy stocks (at an extremely low value) to balance it back to the desired weights.

In the long run (remember the market is cyclical), by continuously rebalancing a portfolio, you are using
the built-in volatility of the portfolio to "buy cheap and sell high".

## Using Elixir to rebalance a “Permanent Portfolio”

Let's use Elixir to rebalance a permanent portfolio. For the purpose of this tutorial, we will use the market data from the free [Alpha Vantage stock APIs](https://www.alphavantage.co/). There are also other [finance APIs](https://medium.com/@patrick.collins_58673/stock-api-landscape-5c6e054ee631) you might want to consider as your stock market data source:

* Sell overweight items (e.g. current allocation is higher than 25%)
* Buy underweight items (e.g. current allocation is lower than 25%), using the profits from the overweight sell.
* Reduce the number of operations (e.g. buy and sell), because every transaction also has a cost.
* Take decisions based on real-time information.

Let's imagine the following interface:

```elixir
allocations = %{"IAU" => 10, "TLT" => 10, "VGSH" => 10, "VTI" => 10}
orders = Rebalancex.rebalance_portfolio(allocations)
```

Based on a number of `allocations` (the amount of units that a specific item has), you can call the `Rebalancex.rebalance_portfolio`,
which will return a number of orders to be performed.

Those orders contain information if a given symbol must be bought, sold or kept as it is:

```elixir
[
  [:sell, "IAU", 4],
  [:sell, "TLT", 1],
  [:sell, "VGSH", 1],
  [:buy, "VTI", 8]
]
```

Considering the points above, that's the code responsible for rebalancing the portfolio:

```elixir
defmodule Rebalancex do
  alias Rebalancex.Quote

  @underweight 0.25
  @overweight 0.27

  def rebalance_portfolio(allocations, quote_service \\ Quote) do
    portfolio = %{cash: 0, allocations: allocations}
    prices = get_prices(allocations, quote_service)

    new_portfolio = do_rebalance_portfolio(portfolio, prices)
    create_order(portfolio, new_portfolio)
  end

  defp do_rebalance_portfolio(portfolio, prices) do
    positions = get_positions(portfolio.allocations, prices)
    weights = get_weights(positions)

    underweight_symbol = get_underweight(weights)
    overweight_symbol = get_overweight(weights)

    portfolio
    |> maybe_buy_underweight(underweight_symbol, prices)
    |> maybe_sell_overweight(overweight_symbol, prices)
    |> maybe_rebalance_again(underweight_symbol, overweight_symbol, prices)
  end

  defp get_prices(allocations, quote_service) do
    allocations
    |> Enum.reduce(%{}, fn {symbol, _}, acc ->
      Map.put(acc, symbol, quote_service.price_for(symbol))
    end)
  end

  defp get_price(prices, symbol) do
    Map.fetch!(prices, symbol)
  end

  defp get_positions(portfolio, prices) do
    portfolio
    |> Enum.reduce(%{}, fn {symbol_name, units}, acc ->
      Map.put(acc, symbol_name, get_price(prices, symbol_name) * units)
    end)
  end

  defp get_weights(positions) do
    total_value = Enum.reduce(positions, 0, fn {_, position}, acc -> position + acc end)

    positions
    |> Enum.reduce(%{}, fn {symbol_name, position}, acc ->
      Map.put(acc, symbol_name, position / total_value)
    end)
  end

  defp get_underweight(weights) do
    {symbol, _weight} =
      weights
      |> Enum.filter(fn {_, value} -> value < @underweight end)
      |> Enum.min_by(fn {_, value} -> value end, fn -> {nil, nil} end)

    symbol
  end

  defp get_overweight(weights) do
    {symbol, _weight} =
      weights
      |> Enum.filter(fn {_, value} -> value > @overweight end)
      |> Enum.max_by(fn {_, value} -> value end, fn -> {nil, nil} end)

    symbol
  end

  defp maybe_buy_underweight(portfolio, nil, _) do
    portfolio
  end

  defp maybe_buy_underweight(portfolio, symbol, prices) do
    price = get_price(prices, symbol)
    maybe_buy_underweight(portfolio, symbol, price, portfolio.cash)
  end

  defp maybe_buy_underweight(portfolio, symbol, price, cash) when cash > price do
    portfolio
    |> incr(symbol)
    |> withdraw(price)
  end

  defp maybe_buy_underweight(portfolio, _symbol, _price, _cash) do
    portfolio
  end

  defp maybe_sell_overweight(portfolio, nil, _prices) do
    portfolio
  end

  defp maybe_sell_overweight(portfolio, symbol, prices) do
    price = get_price(prices, symbol)

    portfolio
    |> decr(symbol)
    |> deposit(price)
  end

  defp maybe_rebalance_again(portfolio, nil, nil, _prices) do
    portfolio
  end

  defp maybe_rebalance_again(portfolio, _, _, prices) do
    do_rebalance_portfolio(portfolio, prices)
  end

  defp incr(%{allocations: allocations} = portfolio, symbol) do
    new_allocations = Map.put(allocations, symbol, allocations[symbol] + 1)
    %{portfolio | allocations: new_allocations}
  end

  defp decr(%{allocations: allocations} = portfolio, symbol) do
    new_allocations = Map.put(allocations, symbol, allocations[symbol] - 1)
    %{portfolio | allocations: new_allocations}
  end

  defp deposit(%{cash: cash} = portfolio, amount), do: %{portfolio | cash: cash + amount}

  defp withdraw(%{cash: cash} = portfolio, amount), do: %{portfolio | cash: cash - amount}

  defp create_order(%{allocations: old_allocations}, %{allocations: new_allocations}) do
    Enum.map(old_allocations, fn {symbol, old_units} ->
      cond do
        new_allocations[symbol] > old_units ->
          [:buy, symbol, new_allocations[symbol] - old_units]

        new_allocations[symbol] < old_units ->
          [:sell, symbol, old_units - new_allocations[symbol]]

        true ->
          [:keep, symbol]
      end
    end)
  end
end
```

Some things to point out:

* The <a href="https://github.com/elvio/rebalancex/blob/master/lib/quote.ex" target="_blank">`Rebalancex.Quote`</a> module fetches real-time prices using the <a href="http://alphavantage.co" target="_blank">AlphaVantage</a> API.
* It buys or sells single units at a time and re-evaluates how the weights were affected.
* It keeps rebalancing the portfolio until there's no underweight or overweight item in it.
* The order is created by comparing the original portfolio to the rebalanced one.

## Demo

I created a project to visualize the evolution of a “Permanent Portfolio” value over time.
Starting with the amount of $10k, you can change different variables like monthly deposits,
rebalancing strategy, region and see the impact on the final value.

<a class="image" href="https://elviovicosa.com/permanent-portfolio" target="_blank">
![Permanent Portfolio Demo](https://thepracticaldev.s3.amazonaws.com/i/3117q1lprm1q7wxm4fja.png)
</a>

The demo project is:

* An API built using Elixir and Plug.
* A frontend application built using React and Redux.
* Data visualization using D3.

Visit <a href="https://elviovicosa.com/permanent-portfolio" target="_blank">https://elviovicosa.com/permanent-portfolio</a>
to test it live.