Introduction

It has been almost a year since I released alpha version of universal-portfolios. Although I didn't expect anyone to be interested in such niche area, people like Paul Perry took the idea and pushed it further and started a fruitful discussion on Quantopian. Paul's ideas have waken me up from sleep and so I decided it's time to get serious about this and spent some weeks refining my approach to portfolio management. New version of universal-portfolios contains all tools I use to manage my own portfolio. Feel free to get inspired, but please stay suspicious, don't believe anything unless you try it yourself and know that the saying "History repeats itself" doesn't really apply to stock market.

Load data

Nothing fancy, just init things and load ETFs we're going to work with SPY, TLT, XLF, XLE, XLU, XLK, XLB, XLP, XLY, XLI, XLV (S&P ETF and its components plus ETF for Treasury Bonds).

In [1]:
%matplotlib inline
%load_ext autoreload
%autoreload 2
%config InlineBackend.figure_format = 'svg'

import numpy as np
import pandas as pd
import seaborn as sns
import datetime as dt
import matplotlib.pyplot as plt

import universal as up
from universal import tools, algos
from universal.algos import *

sns.set_context("notebook")
plt.rcParams["figure.figsize"] = (16, 8)
In [2]:
# ignore logged warnings
import logging
logging.getLogger().setLevel(logging.ERROR)
In [3]:
from pandas_datareader.data import DataReader

# load assets
symbols = ['SPY', 'TLT', 'XLF', 'XLE', 'XLU', 'XLK', 'XLB', 'XLP', 'XLY', 'XLI', 'XLV']
S = DataReader(symbols, 'yahoo', start=dt.datetime(2003,1,1))['Adj Close']

# create other frequencies
mS = S.resample('M').last()
wS = S.resample('W').last()

Can we beat uniform allocation (UCRP)?

Uniform portfolio, $\frac{1}{N}$ portfolio or uniform constant rebalanced portfolio (UCRP) means you invest equal proportion (that is $\frac{1}{N}$ where $N$ is number of assets) of your capital to each asset. Sounds too simple? In practice, it's very hard to beat uniform portfolio.

Note that we use weekly data. Daily data can be used as well, but the results are almost the same and doing analysis on weekly data is faster.

In [4]:
algo = CRP()
result = algo.run(wS)
print(result.summary())
result.plot();
Summary:
    Profit factor: 1.30
    Sharpe ratio: 0.71 ± 0.27
    Ulcer index: 1.13
    Information ratio (wrt UCRP): 0.00
    Appraisal ratio (wrt UCRP): -0.00 ± 0.24
    UCRP sharpe: 0.71 ± 0.27
    Beta / Alpha: 1.00 / -0.000%
    Annualized return: 10.23%
    Annualized volatility: 15.53%
    Longest drawdown: 835 days
    Max drawdown: 47.76%
    Winning days: 59.4%
    Annual turnover: 0.6
        
2020-08-31T19:19:50.730440 image/svg+xml Matplotlib v3.3.1, https://matplotlib.org/

Some algorithms such as Universal portfolio even use best constant rebalanced portfolio (BCRP) as benchmark. BCRP is a portfolio whose weights are constant in time and is determined in hindsight (=can't be traded) to maximize total return. For our portfolio BCRP puts 57% to XLY and 43% to XLE.

In [5]:
algo = BCRP()
result = algo.run(wS)
print(result.summary())
result.plot();
Summary:
    Profit factor: 1.29
    Sharpe ratio: 0.78 ± 0.27
    Ulcer index: 1.22
    Information ratio (wrt UCRP): 0.41
    Appraisal ratio (wrt UCRP): 0.31 ± 0.24
    UCRP sharpe: 0.71 ± 0.27
    Beta / Alpha: 1.07 / 3.216%
    Annualized return: 14.01%
    Annualized volatility: 19.28%
    Longest drawdown: 821 days
    Max drawdown: 50.70%
    Winning days: 56.7%
    Annual turnover: 0.0
        
2020-08-31T19:19:51.627823 image/svg+xml Matplotlib v3.3.1, https://matplotlib.org/