Back to Community
Yearly rebalance with quality and value investing

May be of use for the busy investor. Very simple.

-Rebalance yearly for less tax on capital gains and maintenance

-Screen stocks based on:
--top 50% by EBITDA/EV
--top 20% by ROIC
--required liquidity ratios and dollar volumes

-Pick the remaining stocks with the lowest ulcer index

-Allocate using minimum variance optimization

-Switch to long term treasuries and gold when markets decline. Gold to hedge inflation and treasuries to hedge deflation.

Result:
Not much better than timing with the SPY. Perhaps tweak fundamental factors?

Warning:
Backtest does not include slippage and transaction costs.

Clone Algorithm
223
Loading...
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
# Import the libraries we will use here
import math
import numpy as np
import pandas
from scipy.optimize import minimize
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline import CustomFactor
from quantopian.pipeline.factors import SimpleMovingAverage,AverageDollarVolume,Latest
from quantopian.pipeline.data import morningstar

def get_vwaps(context,data,orders):
    vwap_hist = data.history(orders, ['price', 'volume'], context._day_minutes, '1m')
    vwap = (vwap_hist['price'] * vwap_hist['volume']).sum() / vwap_hist['volume'].sum()
    return vwap

class OrderHandler:
    queue = {}
    safety_factor = 0.95
    vwap_order = True
    # vwap_order = False
    iceberg_order = True
    # iceberg_order = False
    limit_order = True
    # limit_order = False
    
    def push(self,security,leverage):
        self.queue[security] = leverage
        
    def clearQueue(self):
        self.queue = {}
        
    def run(self,context,data):
        if get_open_orders(): return
        if not self.queue: return
        
        orders = []
        for security in self.queue:
            orders.append(security)
        currents = data.current(orders,['price','volume','low','high'])
        
        vwaps = get_vwaps(context,data,orders)
        ceiling = max(context.curr_risk*context.risk_leverage,context.portfolio.portfolio_value)
        remaining = max(ceiling-context.portfolio.portfolio_value*context.account.leverage,0)
        if context.max_drawdown*context.risk_leverage <= 1.0 and context.hedge_shortable == False:
            remaining = context.portfolio.cash
        
        for security in self.queue.keys():
            leverage = self.queue[security]
            delisted = security.end_date < get_datetime()
            
            if delisted: 
                self.queue.pop(security,None)
                continue
            if not data.can_trade(security): continue
            
            price = currents['price'][security]
            vwap = vwaps[security]
            if math.isnan(vwap):
                vwap = price
            
            if security == context.riskless:
                target = int( (context.portfolio.portfolio_value * leverage * self.safety_factor) / price )
            else:
                target = int( (context.curr_risk * leverage * self.safety_factor ) / price )
                
            shares = context.portfolio.positions[security].amount
            delta_shares = target - shares
            if self.iceberg_order:
                sign = np.sign(delta_shares)
                delta_shares = sign*min(currents['volume'][security]*context.max_pct_liquidity,np.abs(delta_shares))
                delta_shares = int(delta_shares)
            
            delta_lev = delta_shares*price
            if shares < 0:
                delta_lev *= -1
                
            if delta_shares > 0:
                if context.real_money:
                    if (context.portfolio.cash != context.account.settled_cash): # Live Robinhood trading
                        continue
                elif context.simulate_no_margin and context.last_sale and (get_datetime() - context.last_sale).days < 3:
                    continue
            
            if (target - shares) == 0:
                self.queue.pop(security,None)
                
            elif (remaining >= delta_lev):
            
                limit = (currents['low'][security]+currents['high'][security])/2.0
                if self.vwap_order:
                    if delta_shares > 0 and (vwap < price): continue
                    if delta_shares < 0 and (vwap > price): continue
                    if self.limit_order:
                        limit = vwap
                    
                if self.limit_order:
                    if math.isnan(limit): continue
                    order(security,delta_shares,style=LimitOrder(limit))
                else:
                    order(security,delta_shares)
                    
                remaining -= max(delta_lev,0)
        
orderHandler = OrderHandler()

class Mean(CustomFactor):
    inputs = [USEquityPricing.close]

    def compute(self, today, asset_ids, out, close):
        r = np.diff(close,axis=0)/close[:-1]        
        out[:] = np.mean(r,axis=0)
        
class Variance(CustomFactor):
    inputs = [USEquityPricing.close]

    def compute(self, today, asset_ids, out, close):
        r = np.diff(close,axis=0)/close[:-1]        
        out[:] = np.var(r,axis=0)  

class UlcerIndex(CustomFactor):
    inputs = [USEquityPricing.close]

    def compute(self, today, asset_ids, out, close):
        max = np.maximum.accumulate(close,axis=0)
        dd = (max-close)/max
        dd = dd**2.0
        ui = np.sqrt(np.mean(dd,axis=0))
        out[:] = ui
        
class Value(CustomFactor):
    inputs = [morningstar.valuation_ratios.ev_to_ebitda]

    def compute(self, today, asset_ids, out, ratio):
        out[:] = 1.0/ratio[-1]
        
class Quality(CustomFactor):
    inputs = [morningstar.operation_ratios.roic]
    def compute(self, today, asset_ids, out, roic):
        out[:] = roic[-1]
        
class Growth(CustomFactor):
    inputs = [morningstar.earnings_ratios.diluted_eps_growth]

    def compute(self, today, asset_ids, out, epsg):
        out[:] = epsg[-1]
        
class Volume(CustomFactor):
    inputs = [USEquityPricing.volume]

    def compute(self, today, asset_ids, out, vol):
        out[:] = np.nan_to_num(np.mean(vol,axis=0))
        
# The initialize function is the place to set your tradable universe and define any parameters. 
def initialize(context):
    set_slippage(slippage.VolumeShareSlippage(volume_limit=1, price_impact=0.0))
    set_commission(commission.PerShare(cost=0, min_trade_cost=0))
    set_symbol_lookup_date('2016-01-01')
    set_long_only()
    
    context.morningstar_sectors = {
        symbol('XLB'):101, # materials
        symbol('XLY'):102, # consumer discretionary
        symbol('XLF'):103, # financials
        symbol('VNQ'):104, # real estate
        symbol('XLP'):205, # consumer staples
        symbol('XLV'):206, # health care
        symbol('XLU'):207, # utilities
        symbol('IYZ'):308, # telecommunication
        symbol('XLE'):309, # energy
        symbol('XLI'):310, # industrials
        symbol('XLK'):311, # technology
    }
    context.alternate = symbols( # Switch to these stocks when hedging systematic risk
        'TLT', # 20+ Treasury 
        # 'IEF', # 7-10 Treasury
        'GLD', # Gold
        # 'AGG',
    )
    context.fixed = symbols(
            # 'EDV',
            # 'VTI',
            # 'VNQ',
            # 'VNQI',
            # 'VPL',
            # 'VGK',
            # 'VWO',
            # 'VDE',
            # 'BIV',
            
            # 'VTI', # USA
            # 'IYR', # REIT
            # 'EZU', # Europe
            # 'EWJ', # Japan
            # 'EEM', # Emerging
            # 'XLE', # Energy
            # 'TLT', # 20+ Treasury 
            # 'IEF', # 7-10 Treasury
            # 'LQD', # Corporate bonds
            # 'EMB', # Emerging market bonds
            # 'GLD', # Gold
            # 'FXE', # Euro
            # 'FXY', # Yen
    )
    context.fixed_yield = dict.fromkeys(context.fixed,1.00)
    custom_yield = {
        # 'EDV' :1.025,
        # 'VTI' :1.02,
        # 'VNQ' :1.025,
        # 'VNQI':1.025,
        # 'VPL' :1.02,
        # 'VGK' :1.02,
        # 'VWO' :1.02,
        # 'VDE' :1.02,
        # 'BIV' :1.02,
        
        'AGG':1.015,
        'VTI':1.02,
        'IYR':1.03,
        'EZU':1.02,
        'EWJ':1.01,
        'EEM':1.02,
        'XLE':1.02,
        'TLT':1.02,
        'IEF':1.015,
        'LQD':1.03,
        'EMB':1.03,
    }
    
    for sec in context.fixed:
        if sec.symbol in custom_yield:
            context.fixed_yield[sec] = custom_yield[sec.symbol]
    
    # OPTIONS ###################################
    # context.global_picks = False
    context.global_picks = True
    context.fixed_timing = False
    context.longs = []
    context.shorts = []
    context.leverage = {}
    context.benchmark = symbol('SPY')
    context.riskless = symbol('SHY')
    context.riskless_apr = 1.005 # Assumed rate of riskless investment
    # context.riskless = symbol('BSV')
    # context.riskless_apr = 1.01 # Assumed rate of riskless investment
    context.ignore = [ # Stocks to ignore when picking
        context.riskless,   
        context.benchmark,
    ]
    context.max_drawdown = 1.0 # Portion of portfolio to risk
    context.risk_leverage = 1.0 # Portion leverage = max_drawdown * risk_leverage
    context.curr_risk = context.portfolio.portfolio_value*context.max_drawdown
    context.max_risk = context.curr_risk
    context.port_ceil = context.portfolio.portfolio_value
    context.exit_period = 9
    context.recalculate_period_days = 252
    context.pick_period = 9
    context.trade_period_days = 1
    context.lookback = 21*12 # Number of previous days to sample
    context.short_lookback = 21*6
    # context.hold_cash = False # Use or don't use excess cash to invest in riskless
    context.hold_cash = True # Use or don't use excess cash to invest in riskless
    context.weight_mode = 2
    context.real_money = False
    context.simulate_no_margin = True
    context.last_sale = None
    context.pipeline_output = None
    
    context.sector_rotation = False
    context.sector_lookback = 21*6
    context.top_sectors = 3
    context.bot_sectors = 3
    
    context.hedge = True
    # context.hedge = False
    context.hedge_shortable = False
    # context.hedge_shortable = True
    context.hedge_systematic = True
    # context.hedge_systematic = False
    # context.hedge_drawdown_protection = True
    context.hedge_drawdown_protection = False
    context.hedge_with_fixed = True
    # context.hedge_with_fixed = False
    context.hedge_short_long_ratio = 1.0
    
    context._day_minutes = 0
    context._day_count = -1   
    context.force_recalculate = True
    context._risk_on = True
    
    context.borrowing_rate = 1.02
    context.accrued_interest = 0
    
    context.long_size = 10
    context.short_size = 0
    
    context.max_pct_liquidity = 0.01
    context.min_dollar_volume = context.curr_risk/context.long_size/context.max_pct_liquidity
    # context.min_dollar_volume = 1e7
    # END #######################################
        
    if context.global_picks == True:
        pipe = Pipeline()
        attach_pipeline(pipe, name='my_pipeline')
        no_etfs = morningstar.valuation.market_cap.latest > 0
        # income = (morningstar.earnings_ratios.diluted_eps_growth > 0)\
        # | (morningstar.valuation_ratios.dividend_yield > 0)
        liquidity = (morningstar.operation_ratios.interest_coverage > 1.5)\
        & (morningstar.operation_ratios.current_ratio.latest > 1.0)
        # & (morningstar.valuation_ratios.payout_ratio.latest < 1.0)
        dollar_volume = AverageDollarVolume(window_length = context.short_lookback)
        min_dv = dollar_volume >= context.min_dollar_volume
        mask = no_etfs & min_dv & liquidity
        f0 = 1.0/UlcerIndex(window_length = context.lookback)
        # f0 = f0.percentile_between(80.0,100.0)
        f1 = Value(window_length = 1)
        f1 = f1.percentile_between(50.0,100.0)
        f2 = Quality(window_length = 1)
        f2 = f2.percentile_between(80.0,100.0)
        # f3 = Latest([morningstar.operation_ratios.quick_ratio])
        # f3 = f3.percentile_between(90.0,100.0,mask=mask)
        # f4 = Growth(window_length = 1)
        # f4 = f4.percentile_between(90.0,100.0,mask=mask)
        
        pipe.add(f0,'f0')
        # pipe.add(f1,'f1')
        # pipe.add(f2,'f2')
        # pipe.add(f3,'f3')
        # pipe.add(f4,'f4')
        pipe.add(morningstar.asset_classification.morningstar_sector_code.latest,'sector_code')
        pipe.set_screen(f2 & f1 & mask)
        # pipe.set_screen(mask)
        
    schedule_function(day_start,
        date_rules.every_day(),
        time_rules.market_open(hours = 0, minutes = 1))
        
    schedule_function(day_end,
        date_rules.every_day(),
        time_rules.market_close(hours = 0, minutes = 5))
        
def simulate_borrowing(context,data):
    shorts = 0
    longs = 0
    cash = context.portfolio.cash
    for sec,v in context.portfolio.positions.iteritems():
        delisted = 0
        if sec.end_date < get_datetime():
            delisted += np.abs(v.amount*v.last_sale_price)
            
        shares = v.amount
        if shares < 0:
            shorts += np.abs(shares)*v.last_sale_price - delisted
        else:
            longs += shares*v.last_sale_price - delisted
            
    daily_interest = (context.borrowing_rate-1.0)/252.0
    borrowed = shorts + max(0-(cash-1.5*shorts),0) # 1.5 = 150% short requirement
    context.accrued_interest += (borrowed + context.accrued_interest) * daily_interest
    
def before_trading_start(context,data):
    context._day_minutes = 0
    context._day_count += 1
    simulate_borrowing(context,data)

    if context.global_picks and context._day_count % context.pick_period == 0:
        context.pipeline_output = pipeline_output('my_pipeline').dropna(axis=0)
                
    # UPDATE RISK SIZE ##########################
    
    new_port = context.portfolio.portfolio_value*context.max_drawdown
    diff = context.portfolio.portfolio_value - context.port_ceil
    if diff > 0:
        context.curr_risk = new_port
        context.max_risk = new_port
        context.port_ceil = context.portfolio.portfolio_value
    else:
        context.curr_risk = context.max_risk + diff
        context.curr_risk = max(0,context.curr_risk)
        
def exit_callback(context,data):
    if (context._day_count % 252) != 0: return context.alternate,context.alternate
    longs,shorts = context.longs,context.shorts
    '''
    longs,shorts = [],[]
    profit_take = 999.0
    profit_loss = 0.0
    
    prices = data.history(context.benchmark, 'price', context.lookback, '1d')
    a = prices[-1]/prices[0] - 1.0
    b = prices[-1]/prices.mean()
    market_down = a <= 0 and b <= 1

    positions = context.portfolio.positions
    prices = data.current(context.longs,'price')
    longs = []
    costs = pandas.Series(0,index=context.longs)
    
    for sec in context.longs:
        pos = positions[sec]
        if pos.amount == 0: continue
        costs[sec] = pos.cost_basis
        
    ratios = prices/costs
    longs = ratios[(ratios >= profit_take) | (ratios <= profit_loss)].index
    if not market_down:
        longs = np.union1d(longs,context.alternate)
    '''        
    return longs,shorts
        
def exit_positions(context,data):
    if (context._day_count % context.exit_period) != 0: return
    prices = data.history(context.benchmark, 'price', context.lookback, '1d')
    a = prices[-1]/prices[0] - 1.0
    b = prices[-1]/prices.mean()
    market_down = a <= 0 and b <= 1
    
    if market_down and context.hedge and context.hedge_systematic:
        context._risk_on = False
        longs,shorts = context.longs,context.shorts
    else:
        context._risk_on = True
        longs,shorts = exit_callback(context,data)
        
    remove = np.union1d(longs,shorts)
    for sec in remove:
        context.leverage.pop(sec,None)
        orderHandler.push(sec,0)
    context.longs = np.setdiff1d(context.longs,remove)
    context.shorts = np.setdiff1d(context.shorts,remove)
            
def pipeline_filter(context,data):

    picks = context.pipeline_output
    picks = picks[~picks.index.isin(context.ignore)]
    picks = picks[~picks.index.isin(security_lists.leveraged_etf_list)]
    if context.hedge_with_fixed:
        picks = picks[~picks.index.isin(context.fixed)]
    if context.sector_rotation:
        prices = data.history(context.morningstar_sectors.keys(), 'price', context.sector_lookback, '1d')
        sector_return = (prices.iloc[-1]/prices.iloc[0] - 1.0).sort(inplace=False)
        top = sector_return.tail(context.top_sectors).index
        top_code = [context.morningstar_sectors[sec] for sec in top]
        bot = sector_return.head(context.bot_sectors).index
        bot_code = [context.morningstar_sectors[sec] for sec in bot]
        
        top_picks = picks[picks.loc[:,'sector_code'].isin(top_code)]
        top_picks = top_picks.drop('sector_code',axis=1).rank().sum(axis=1).order()
        bot_picks = picks[picks.loc[:,'sector_code'].isin(bot_code)]
        bot_picks = bot_picks.drop('sector_code',axis=1).rank().sum(axis=1).order()
    else:
        top_picks = picks.drop('sector_code',axis=1).rank().sum(axis=1).order()
        bot_picks = top_picks
    longs = top_picks[-context.long_size:]
    shorts = bot_picks[:context.short_size]
    
    longs = longs.index
    shorts = np.setdiff1d(shorts.index,context.longs)
    return longs,shorts
            
def repick(context,data): 
    if context._day_count % context.pick_period != 0: return
    # Find new stocks to go long/short
    # Exit any old stocks
    # Combine remaining stocks with new stocks
    
    fixed = context.fixed        
    prices = data.history(context.benchmark, 'price', context.lookback, '1d')
    a = prices[-1]/prices[0] - 1.0
    b = prices[-1]/prices.mean()
    market_down = a <= 0 and b <= 1    

    if context.global_picks:
        longs,shorts = pipeline_filter(context,data)
    else:
        longs,shorts = [],[]
        
    if market_down and context.hedge and context.hedge_systematic:
        fixed = context.alternate
        longs = []
        shorts = []
    
    if context.global_picks == False or context.hedge_with_fixed:
        prices = data.history(fixed, 'price', context.lookback, '1d').dropna(axis=1)
        prices = prices.loc[:,prices.columns.isin(fixed)]
        pratio = prices.iloc[-1]/prices.mean()
        
        r = prices.pct_change().dropna()+1.0
        rmean = np.log(r).mean()
        
        # yld = pandas.Series(context.fixed_yield.values(),index=context.fixed_yield.keys())
        # yld = np.log(yld)/252.0
        # rmean += yld
        
        if context.fixed_timing:
            long_condition = (rmean > 0) | (pratio > 1)
            flongs = rmean[long_condition]
            fshorts = rmean[~long_condition]
            flongs.sort()
            fshorts.sort()
            
            flongs = flongs[-context.long_size:].index
            fshorts = fshorts[:context.short_size].index
            fshorts = np.setdiff1d(fshorts,flongs)
        else:
            flongs = fixed
            fshorts = []

        if context.hedge_with_fixed:
            longs = np.setdiff1d(longs,flongs)[:max(0,context.long_size-len(flongs))]
            longs = np.union1d(longs,flongs)
            shorts = np.setdiff1d(shorts,fshorts)[:max(0,context.short_size-len(fshorts))]
            shorts = np.setdiff1d(np.union1d(shorts, fshorts), longs)[-context.short_size:]
        else:
            longs,shorts = flongs,fshorts
       
    remain_longs,remain_shorts = context.longs,context.shorts
    new_longs = np.setdiff1d(longs,remain_longs)[:max(context.long_size-len(remain_longs),0)]
    longs = np.union1d(remain_longs,new_longs)
    new_shorts = np.setdiff1d(shorts,remain_shorts)[:max(context.short_size-len(remain_shorts),0)]
    shorts = np.union1d(remain_shorts,new_shorts)
    shorts = np.setdiff1d(shorts,longs)
    
    if len(np.setdiff1d(longs,remain_longs)) > 0 or len(np.setdiff1d(shorts,remain_shorts)) > 0:
        context.longs = longs
        context.shorts = shorts            
        context.force_recalculate = True
            
        securities = np.concatenate((context.longs,context.shorts))
        context.leverage = dict.fromkeys(securities,0)
        
weight_modes = {
    0:'naive',
    1:'max_div',
    2:'min_var',
}
        
def min_corr(x,*args):
    corr = args[0]
    return np.dot(np.dot(x.T,corr),x)
    
def min_var(x,*args):
    cov = args[0]
    return np.dot(np.dot(x.T,cov),x)
    
def jacobian(x,*args):
    mat = args[0]
    return 2*np.dot(x.T,mat)
    
def get_k(context,data,shorts=False):
    if shorts:
        sign = -1
        securities = context.shorts
    else:
        sign = 1
        securities = context.longs
        
    prices = data.history(securities, 'price', context.short_lookback, '1d').dropna(axis=1)
    r = prices.pct_change().dropna()
    k = None
        
    r = r[r.columns[r.columns.isin(securities)]]
    securities = r.columns
    
    k = pandas.Series(0, index=securities)
    
    if k.size > 0: # Most diversified portfolio
        if weight_modes[context.weight_mode] == 'naive':
            k += 1.0/len(securities)
        else:
            cov = r.cov().values
            corr = r.corr().values
                            
            min_bnd = 0
            max_bnd = 1.0
            
            bounds = tuple((min_bnd,max_bnd) for x in securities)
            constr = (
                {'type': 'eq', 'fun': lambda x:  1.0-np.sum(x)},
            )
            x0 = k.values
            x0 += 1.0/k.size
            
            if weight_modes[context.weight_mode] == 'max_div':
                result = minimize(min_corr, x0, jac=jacobian, args=(corr), constraints=constr, bounds=bounds)
            elif weight_modes[context.weight_mode] == 'min_var':
                result = minimize(min_var, x0, jac=jacobian, args=(cov), constraints=constr, bounds=bounds)

            if result.success:
                x0 = result.x
                
            k = pandas.Series(x0,index=k.index)
            if weight_modes[context.weight_mode] == 'max_div':
                volatility = r.std()
                k = k/volatility
            
    k = k / k.abs().sum() * context.risk_leverage
    k = k.fillna(0)
        
    return k*sign
            
def recalculate(context,data):
    if (not context.force_recalculate) and context._day_count % context.recalculate_period_days != 0: return
    
    k = get_k(context,data)
    securities = k.index
    prices = data.history(np.union1d(context.leverage.keys(),[context.benchmark]), 'price', context.lookback, '1d').bfill()
        
    if context.hedge: 
        longs = k
        shorts = pandas.Series(0,index=context.shorts)
        
        if context.hedge_drawdown_protection:
            total_r = prices.iloc[-1]/prices.iloc[0] - 1.0
            pratio = prices.iloc[-1]/prices.mean()
            
            signal = (total_r <= 0) & (pratio <=1)
            signal = total_r[signal].index
            longs[longs.index.isin(signal)] -= longs[longs.index.isin(signal)]
            shorts[~shorts.index.isin(signal)] -= shorts[~shorts.index.isin(signal)]
            
        if context.hedge_shortable:
            shorts = get_k(context,data,shorts=True)
            shorts *= context.hedge_short_long_ratio
        k = pandas.concat((longs,shorts))
        securities = k.index
        
        # r = prices.pct_change().dropna()
        # r = r[r.columns[r.columns.isin(k.index)]]
        # cov = r.iloc[-context.short_lookback:].cov()
        
        # k_factor = 1
        # k *= k_factor
            
    if k.abs().sum() > context.risk_leverage:
        k = k / k.abs().sum() * context.risk_leverage
        k = k.fillna(value=0)
    
    # Trading volume control
    v = data.history(securities, 'volume', context.lookback, '1d').fillna(0)
    dvol = (v * prices).mean()
    dvol = dvol[securities]
    
    dollars = context.curr_risk*k
    d_pct = (dollars / (dvol*max(context.recalculate_period_days/context.trade_period_days,1))).abs()
    epsilon = (context.max_pct_liquidity/d_pct).min()
    epsilon = min(1.0, epsilon)
    k = k * epsilon
        
    # QUEUE ORDERS #########################
    orderHandler.clearQueue()
    my_secs = np.concatenate((securities,context.ignore),axis=0)
    
    for sec in context.portfolio.positions:
        if sec.end_date < get_datetime():
            if sec not in context.ignore:
                context.ignore.append(sec)
        elif (sec not in my_secs) and data.can_trade(sec):
            orderHandler.push(sec,0)
    
    for i,sec in enumerate(securities):
        context.leverage[sec] = k[sec]
        orderHandler.push(sec,k[sec])
        
    context.force_recalculate = False
    
    log.info('---')
    log.info('Target allocation:')
    log.info(k)
    
def order_riskless(context,data):
    if not context.hold_cash: 
        k = np.sum(np.abs(context.leverage.values()))
        riskless_pct = max(1.0-context.max_drawdown*k,0)
        orderHandler.push(context.riskless,riskless_pct)
        
# The handle_data function is run every bar.    
def handle_data(context,data):
    context._day_minutes += 1
    
    if (context._day_count % context.trade_period_days == 0):
        orderHandler.run(context,data)
        
    check_last_sale(context)
        
def day_start(context,data):
    exit_positions(context,data)
    repick(context,data)
    recalculate(context,data)
    order_riskless(context,data)
        
def day_end(context,data):
    long = 0
    short = 0
    total = 0
    
    for security,v in context.portfolio.positions.iteritems():
        asset = v.amount * v.last_sale_price
        if asset > 0:
            long += asset
        else:
            short += np.abs(asset)
            
    long = long/context.portfolio.portfolio_value
    short = short/context.portfolio.portfolio_value
    total += long + short
    
    record(long = long)
    record(short = short)
    risk_record(context,data)
    # logging(context,data)
    
def logging(context,data):
    log.info('---')
    log.info('Risk size: {}'.format(context.curr_risk))
    log.info('Port ceiling: {}'.format(context.port_ceil))
    log.info('Portfolio value: {}'.format(context.portfolio.portfolio_value))
    log.info('Accrued interest: {}'.format(context.accrued_interest))
    
    pos_sum = 0
    delisted_sum = 0
    positions = 0
    delisted = 0
    
    for sec,v in context.portfolio.positions.iteritems():
        if sec in context.leverage.keys():
            pos_sum += np.abs(v.amount*v.last_sale_price)
            positions += 1
        elif sec.end_date < get_datetime():
            delisted_sum += np.abs(v.amount*v.last_sale_price)
            delisted += 1
            
    log.info('Real lev: {}'.format(pos_sum/context.portfolio.portfolio_value))
    log.info('Delisted lev: {}'.format(delisted_sum/context.portfolio.portfolio_value))
    log.info('Positions: {}'.format(positions))
    log.info('Delisted: {}'.format(delisted))
    
def risk_record(context,data):
    if not '_init_rpr' in context:
        context._init_rpr = True
        context._max_shorts = 0
        context._min_cash = context.portfolio.cash
        context._peak_leverage = 0
        
    shorts = 0
    cash = context.portfolio.cash
    for sec,v in context.portfolio.positions.iteritems():
        delisted = 0
        if sec.end_date < get_datetime():
            delisted += np.abs(v.amount*v.last_sale_price)
            
        shares = v.amount
        if shares < 0:
            shorts += np.abs(shares*v.last_sale_price) - delisted
    
    context._max_shorts = max(context._max_shorts,shorts)
    context._min_cash = min(context._min_cash,cash)
    context._peak_leverage = max(context._peak_leverage,context.account.leverage)
    
    record(min_cash = context._min_cash)
    # record(max_shorts = context._max_shorts)
    record(peak_leverage = context._peak_leverage)
    record(risk_on = context._risk_on)

def check_last_sale(context):
    """
    To be used at the end of each bar. This checks if there were
    any sales made and sets that to `context.last_sale`.
    `context.last_sale` is then used in `cash_settlement_date` to
    simulate a T+3 Cash Settlement date
    
    To only be used for backtesting!
    """
    open_orders = get_open_orders()
    most_recent_trade = []
    # If there are open orders check for the most recent sale
    if open_orders:
        for sec, order in open_orders.iteritems():
            for oo in order:
                if oo.amount < 0:
                    most_recent_trade.append(oo.created)
    if len(most_recent_trade) > 0:
        context.last_sale = max(most_recent_trade)
There was a runtime error.
4 responses

Here's the notebook. The algorithm seems to consistently invests in companies with competitive advantages.

Loading notebook preview...

Minh, interesting algo. I tried it with the current date and I received the following message. TypeError: Cannot cast ufunc add output from dtype('float64') to dtype('int64') with casting rule 'same_kind'
... USER ALGORITHM:583, in get_k
x0 +=1.0/k.size

Obviusly I am a beginner with python. Can you help with that?

Make line 583 like this:

x0 = x0 + 1.0/k.size  

thanks!!!!