Back to Community
OLMAR Sector Specific

Here is a sector specific version of Grant's OLMAR algo. I really like the implementation, so I thought I would try to break it down by sector. My first pass was on the financial sector using the vanguard etf as a benchmark. The results are bad. Any ideas on what I'm doing wrong?

Thanks for the help.

John

Clone Algorithm
13
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Adapted from:
# Li, Bin, and Steven HOI. "On-Line Portfolio Selection with Moving Average Reversion." The 29th International Conference on Machine Learning (ICML2012), 2012.
# http://icml.cc/2012/papers/168.pdf

import numpy as np
from scipy import optimize
import pandas as pd
import datetime as dt
import math

def initialize(context):
    #################################
    # OLMAR
    #################################
    context.eps = 1.005
    context.stock_limit = 20
    print 'context.eps = ' + str(context.eps)
    
    #a good reference for all sectors, industry groups and industries
    # http://corporate.morningstar.com/us/documents/methodologydocuments/methodologypapers/equityclassmethodology.pdf
    # context.sector_mappings = {101.0: "Basic Materials",  
    #                        102.0: "Consumer Cyclical",  
    #                        103.0: "Financial Services",  
    #                        104.0: "Real Estate",  
    #                        205.0: "Consumer Defensive",  
    #                        206.0: "Healthcare",  
    #                        207.0: "Utilites",  
    #                        308.0: "Communication Services",  
    #                        309.0: "Energy",  
    #                        310.0: "Industrials",  
    #                        311.0: "Technology"}  

    context.sector = 103.0 

    set_benchmark(
        sid(25904) # VFH (Vanguard Financials ETF)
        # sid(25906) # VHT (Vanguard Health Care ETF)
        # sid(25905) # VGT (Vanguard Information Technology ETF)
        # sid(26667) # VDE (Vanguard Energy ETF)
        # sid(25902) # VCR (Vanguard Consumer Discretionary ETF)
        # sid(22445) # IBB (iShares Nasdaq Biotechnology Index Fund)
        # sid(22887) # EDV VANGUARD treasury
        # sid(25899) # VB = Vanguard small cap
        # sid(25898)  # VAW (Vanguard Materials ETF)
                 )
    
    schedule_function(trade, date_rules.every_day(), time_rules.market_open(minutes=1))
    
def before_trading_start(context): 
    
    fundamental_df = get_fundamentals(
        query(
            fundamentals.valuation.market_cap,
        )
        .filter(fundamentals.asset_classification.morningstar_sector_code == context.sector) 
        .filter(fundamentals.valuation.market_cap != None)
        .filter(fundamentals.share_class_reference.security_type == 'ST00000001')
        .order_by(fundamentals.valuation.market_cap.desc()).limit(context.stock_limit)) 
    update_universe(fundamental_df.columns.values)
    context.stocks = [stock for stock in fundamental_df]
    
def handle_data(context, data):        
    record(leverage = context.account.leverage)

def get_allocation(context,data,n,prices):
      
    prices = pd.ewma(prices,span=390).as_matrix(context.stocks)
    
    b_t = []
    
    for stock in context.stocks:
        b_t.append(context.portfolio.positions[stock].amount*data[stock].price)
         
    m = len(b_t)
    b_0 = np.ones(m) / m  # equal-weight portfolio
    denom = np.sum(b_t)

    if denom == 0.0:
        b_t = np.copy(b_0)
    else:     
        b_t = np.divide(b_t,denom)
    
    x_tilde = []
    
    for i, stock in enumerate(context.stocks):
        mean_price = np.mean(prices[:,i])
        x_tilde.append(mean_price/prices[-1,i]) 
        
    bnds = []
    limits = [0,1]
    
    for stock in context.stocks:
        bnds.append(limits)
            
    bnds = tuple(tuple(x) for x in bnds)
     
    cons = ({'type': 'eq', 'fun': lambda x:  np.sum(x)-1.0},
            {'type': 'ineq', 'fun': lambda x:  np.dot(x,x_tilde) - context.eps})
    
    res= optimize.minimize(norm_squared, b_0, args=b_t,jac=norm_squared_deriv,method='SLSQP',constraints=cons,bounds=bnds, options={'disp': False,  'maxiter': 100, 'iprint': 1, 'ftol': 1e-6})
    
    allocation = res.x
    allocation[allocation<0] = 0 
    allocation = allocation/np.sum(allocation)
    
    if res.success and (np.dot(allocation,x_tilde)-context.eps > 0):
        print('using allocation')
        return (allocation,np.dot(allocation,x_tilde))
    else:
        print('equally weighted')
        return (b_t,1)

def trade(context,data):
    
    # check if data exists
    for stock in context.stocks[:]:
        if stock not in data:
            print('removing stock since its not in the data')
            print(stock)
            context.stocks.remove(stock)
        else:
            #remove stocks if the price is higher or lower than want
            price = data[stock]['price']
            if price < 1.0 or price > 1000.0:
                print('removing stock with data')
                print(data[stock])
                context.stocks.remove(stock)

                    # check for de-listed stocks & leveraged ETFs
    for stock in context.stocks:  
        if stock.security_end_date < get_datetime():  # de-listed ?  
            context.stocks.remove(stock)
        if stock in security_lists.leveraged_etf_list: # leveraged ETF?
            context.stocks.remove(stock)
            
    # if there are no stocks besides for the inverse ETF then return
    # this prevents a known crash in paper trading
    if len(context.stocks) < context.stock_limit/4:
        return

    # check for open orders      
    if get_open_orders():
        return
    
    # find average weighted allocation over range of trailing window lengths
    a = np.zeros(len(context.stocks))
    w = 0
    prices = history(8*390,'1m','price')
    for n in range(1,9):
        (a,w) = get_allocation(context,data,n,prices.tail(n*390))
        a += w*a
        w += w
    
    allocation = a/w
    allocation = allocation/np.sum(allocation)
    
    allocate(context,data,allocation)

def allocate(context, data, desired_port):
        
    print('buying percent for stock')
    for i, stock in enumerate(context.stocks):
        print(desired_port[i])
        print(stock)
        order_target_percent(stock, desired_port[i])
    
    for stock in data:
        if stock not in all_needed_symbols(context, data):
            print('selling all stock since not in all_needed_symbols')
            print(stock)
            order_target_percent(stock,0)
    
def norm_squared(b,*args):
    
    b_t = np.asarray(args)
    delta_b = b - b_t
     
    return 0.5*np.dot(delta_b,delta_b.T)

def norm_squared_deriv(b,*args):
    
    b_t = np.asarray(args)
    delta_b = b - b_t
        
    return delta_b

def all_needed_symbols(context, data):
    all_symbols = []
    
    for stock in context.stocks[:]:
        all_symbols.append(stock)
    return all_symbols


There was a runtime error.
3 responses

This has been researched by Grant as well. The sector ETFs are highly correlated with market. For OLMAR to be successful, we need a high idiosyncratic component in stocks and some degree of mean reversion. If you search for OLMAR in the community, you will see a lot of research and backtests. Thanks!!

Shiv,

I may be missing your point and I've seen the posts around using ETFs in OLMAR but I'm not trying to build a sector ETF version of OLMAR. Check out the code and the transactions and you can see that its trading financial stocks exclusively.

I'm taking a basket of stocks in a sector and seeing if they beat the ETF as a benchmark. I figure if you can do that on a set of sectors then you can diversify based on the sectors themselves and have a lower beta accordingly. Does that make sense? Do you think that is not possible to out perform a sector using OLMAR like Grant's original version does for the market as a whole?

Thanks,

John

John,

Not sure what's up. You might try running an equal weight allocation, to see how it compares with the optimization routine.

Grant