In [1]:
import pandas as pd
import pandas_datareader.data as web
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import seaborn
from datetime import datetime, timedelta

%matplotlib inline

# Parameters
VOL_PERIOD = 13
RETURN_PERIOD_1 = 63
RETURN_PERIOD_2 = 126
WEIGHTS = (.5,.3,.2)

#Banking, Biotech, Broker-Dealer, HealthCare, Pharmaceuticals, Gold, Homebuilders, Insurance, Internet, Oils, Oil Services, Retail, Semiconductor, Software, Transports, and Utilities


portfolio = {
    'equity':("VTI", "VEU", "SHY"),
    'sector':("XLF", "IBB", "IAI", "XLV", "XPH", "GDX", "XHB", "KIE", "FDN", "OIH", "XLE", "SMH", "IGV", "IYT", "XLU", "SHY" ),
    'credit':("HYG", "CIU", "SHY"), 
    'real estate':("REM", "VNQ", "SHY"), 
    'stress':("GLD", "TLT", "SHY")
}

In [2]:
def overall_weight(row):
    return WEIGHTS[0]*row['Period 1 Return Rank'] + WEIGHTS[1]*row['Period 2 Return Rank'] + WEIGHTS[2]*row['Std Dev Rank']

def make_table(sym):
    end = datetime.today()
    lookback = 2 * max(RETURN_PERIOD_1, RETURN_PERIOD_2)
    start = end - timedelta(lookback)
    df = web.DataReader(sym, 'yahoo', start, end)
    df.drop(["Open", "High", "Low", "Close", "Volume"], axis=1, inplace=True)
    df['Std Dev'] = pd.rolling_std(df['Adj Close'], VOL_PERIOD)
    df['Period 1 Return'] = df.pct_change(RETURN_PERIOD_1)['Adj Close']
    df['Period 2 Return'] = df.pct_change(RETURN_PERIOD_2)['Adj Close']
    return df

def get_latest(sym):
    df = make_table(sym).tail(1)
    df.index = [sym]
    return df

def cluster_table(symbols):
    dfs = map(get_latest, symbols)
    cdf = pd.concat(dfs, axis=0)
    cdf_r = cdf.rank('rows', ascending=False)[['Period 1 Return', 'Period 2 Return']]
    cdf_r['Std Dev'] = cdf.rank('rows')[['Std Dev']]
    df = pd.merge(cdf, cdf_r, left_index=True, right_index=True, how='inner', suffixes=('', ' Rank'))
    overall_df = pd.DataFrame(df.apply(overall_weight, axis=1), columns=["Overall Weight Rank"]).rank('rows')
    df = pd.merge(df, overall_df, left_index=True, right_index=True, how='inner')
    df = df.sort_index(axis=1)
    df = df.sort_values('Overall Weight Rank')
    return df

In [3]:
# Equity Model
cluster_table(portfolio['equity'])


Out[3]:
Adj Close Overall Weight Rank Period 1 Return Period 1 Return Rank Period 2 Return Period 2 Return Rank Std Dev Std Dev Rank
SHY 85.040001 1 0.004107 1 0.004108 1 0.050800 1
VTI 104.110001 2 -0.048380 2 -0.027673 2 2.317809 3
VEU 45.869999 3 -0.061137 3 -0.078077 3 1.309197 2

In [4]:
# Credit Model
cluster_table(portfolio['credit'])


Out[4]:
Adj Close Overall Weight Rank Period 1 Return Period 1 Return Rank Period 2 Return Period 2 Return Rank Std Dev Std Dev Rank
CIU 109.040001 1.5 0.010660 1 -0.005354 2 0.234271 2
SHY 85.040001 1.5 0.004107 2 0.004108 1 0.050800 1
HYG 84.889999 3.0 -0.029592 3 -0.041861 3 1.120165 3

In [5]:
# Real Estate Model
cluster_table(portfolio['real estate'])


Out[5]:
Adj Close Overall Weight Rank Period 1 Return Period 1 Return Rank Period 2 Return Period 2 Return Rank Std Dev Std Dev Rank
SHY 85.040001 1 0.004107 2 0.004108 1 0.050800 1
VNQ 79.410004 2 0.021313 1 -0.009600 2 1.570349 3
REM 10.290000 3 -0.033323 3 -0.083756 3 0.196573 2

In [6]:
# Econ Stress Model
cluster_table(portfolio['stress'])


Out[6]:
Adj Close Overall Weight Rank Period 1 Return Period 1 Return Rank Period 2 Return Period 2 Return Rank Std Dev Std Dev Rank
TLT 123.889999 1 0.050273 1 -0.045346 3 0.757257 2
SHY 85.040001 2 0.004107 3 0.004108 1 0.050800 1
GLD 113.309998 3 0.042890 2 -0.019810 2 2.224286 3

In [7]:
# Sector Overweight
cluster_table(portfolio['sector'])


Out[7]:
Adj Close Overall Weight Rank Period 1 Return Period 1 Return Rank Period 2 Return Period 2 Return Rank Std Dev Std Dev Rank
XLU 44.720001 1.0 0.041104 2 0.032945 4 0.595995 4
SHY 85.040001 2.0 0.004107 3 0.004108 5 0.050800 1
KIE 69.959999 3.0 -0.008126 7 0.042092 3 1.105397 6
GDX 16.900000 4.0 0.095269 1 -0.143002 13 1.195989 8
SMH 53.610001 5.0 0.002806 4 -0.032136 8 1.569334 10
IGV 100.540001 6.0 -0.017657 8 0.047641 2 2.192671 13
FDN 71.129997 7.0 -0.021461 9 0.082319 1 2.036093 12
XHB 35.619999 8.0 -0.027462 10 0.002143 6 0.858194 5
IYT 147.500000 9.0 -0.004011 5 -0.041904 9 3.321393 14
XLF 23.430000 10.0 -0.072683 12 -0.019995 7 0.336713 2
OIH 31.610001 11.0 -0.007535 6 -0.153908 15 1.860273 11
IAI 39.430000 12.5 -0.126886 14 -0.065510 11 0.464737 3
XLV 69.099998 12.5 -0.098994 13 -0.050493 10 1.151666 7
XLE 69.150002 14.0 -0.038376 11 -0.143904 14 3.337703 15
IBB 314.760010 15.0 -0.208325 15 -0.116845 12 7.105252 16
XPH 48.790001 16.0 -0.265883 16 -0.243778 16 1.320438 9