Back to Community
Implementing Frog in the Pan algorithm from Alpha Architects

In the algorithm below I attempted to integrate the Frog In the Pan metric discussed in this post on the Alpha Architect blog. With their Robust Asset Allocation framework. The system first selects the top 100 (top 20% of 500 stocks in universe) performers based on 12-month lookback then sorts on the FIP ID metric discussed in the post and invests in the top 20 stocks (top 20% from 100 winners). At first glance the algorithm seems to add value, it outperforms a simple 12 month look back algorithm significantly. However if you simply adjust the 12-month lookback algorithm to select the top 20 stocks instead of the top 100 the simple 12-month lookback algorithm achieves the same performance as double sorting with FIP ID. I’ve tested the algorithm with various values and number of stocks to select and the relationship seems to hold. So as far as I can tell integrating the Frog in the Pan ID metric into the Robust Asset allocation strategy doesn’t add much value. At least over the period I tested 2006-2015.

Note, both algorithms apply the RAA framework for risk management.

Clone Algorithm
304
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
##########################################################################################
# This algorithm integrates the Frog in the Pan strategy discussed on the Alpha Architect site in this post,
#    http://blog.alphaarchitect.com/2015/11/23/frog-in-the-pan-identifying-the-highest-quality-momentum-stocks/
# with the Robust Asset Allocation model discsussed here,
#    http://blog.alphaarchitect.com/2014/12/02/the-robust-asset-allocation-raa-solution/
###########################################################################################

from datetime import timedelta
from sqlalchemy import or_
from scipy import stats
import statsmodels.api as sm
import talib 
import pandas as pd
import numpy as np
import time


#Base Class for all strategies
class Strategy(object):
    
    def __init__(self, pct_alloc, risk_free_asset, ma_threshold,sell_offset, buy_offset):
        self.alloc = pct_alloc                 #percent of portfolio to allocate to strategy
        self.risk_free_asset = risk_free_asset #risk free asset
        self.ma_threshold = ma_threshold       #moving average theshold
        self.sell_offset = sell_offset         #offset for scheduling when strategy will sell
        self.buy_offset = buy_offset           #offset for scheduling when strategy will buy
        self.sell_day = False                  
        self.buy_day = False    
        self.bought = []                       #maintain list of securites that were bought by strategy
        self.lookback = 252                    #set default lookback period to 1 year (252 days)
 

    def getAlloc(self):
        return self.alloc

    
    def setAlloc(self, alloc):
        self.alloc = alloc
    
    
    #Implement robust allocation algo for strategy
    def robust(self,context,data):
        raise NotImplementedError("implement robust method in Subclass")
    
    
    #Show relevant params at beginning of execution
    def show_params(self):
        raise NotImplementedError("implement show_params method in Subclass")
    
    
    #Implement fundamental queries here
    def handle_data(self,context,data):
        raise NotImplementedError("implement handle_data method in Subclass")
    
    
    #Implement buy, sell and ranking functions here
    def before_trading_start(self,context,data): 
        raise NotImplementedError("implement before_trading_start method in Subclass")

        
#Base class for Equity Strategies
class Equity_Strat(Strategy):
    
    def __init__(self, pct_alloc,risk_free_asset,ma_threshold, index, sell_offset, buy_offset):
        Strategy.__init__(self,pct_alloc,risk_free_asset,ma_threshold,sell_offset, buy_offset)
        
        self.index = index #set 

        
    def robust(self,context,data):
        r = 0.0     
        
        ex_return = self.get_excess_return(context,data)
        if ex_return > 0.0:
            r += 0.5
        if data[self.index].price > data[self.index].mavg(self.ma_threshold):
            r += 0.5
        
        return r
    
    
    def get_excess_return(self,context,data):
        h = history( self.lookback,'1d','price')[[self.index,self.risk_free_asset]]
        h = h.resample('M',how='last')

        pct_change =h.iloc[[0,-1]].pct_change()[1:]
        pct_change = pct_change.dropna(axis=1)
        
        ex_return = pct_change[self.index]-pct_change[self.risk_free_asset]
        
        return ex_return[0]


#Momentum Strategy
class Mom_Strat(Equity_Strat):
   
    def __init__(self, pct_alloc, risk_free_asset, ma_threshold, index, sell_offset, buy_offset):
        Equity_Strat.__init__(self,pct_alloc,risk_free_asset,ma_threshold,index,sell_offset, buy_offset)
        
        self.lower_market_cap = 2000e6     #lower market cap for candidate selection
        self.limit = 500                   #max number of stocks to select for fundamental query
        self.num_positions = 20            #max number of positions the strategy will select
        
        
    def set_sell_day(self,context,data):
        self.sell_day = True
        
    
    def set_buy_day(self,context,data):
        self.buy_day = True
        
   
    def before_trading_start(self,context,data): 
        
        if self.buy_day:
            self.get_candidates()
            update_universe(self.candidates)
            
        
    def handle_data(self,context,data):
        if self.sell_day:
            self.exec_sell(context,data)
            self.sell_day = False
        
        if self.buy_day:
            self.exec_rank(context, data)
            self.exec_buy(context,data)
            self.buy_day = False

        
    def get_candidates(self):
        df_fundamentals = get_fundamentals(
            query(
                fundamentals.valuation.shares_outstanding,
                fundamentals.valuation.market_cap
            )
            #.filter(fundamentals.company_reference.country_id == "USA")
            .filter(or_(fundamentals.company_reference.primary_exchange_id == "NAS", fundamentals.company_reference.primary_exchange_id == "NYSE"))
            .filter(fundamentals.valuation.market_cap != None)
            .filter(fundamentals.valuation.market_cap > self.lower_market_cap ) 
            .filter(fundamentals.valuation.shares_outstanding != None)
            #.filter(fundamentals.share_class_reference.is_depositary_receipt == False)
            .filter(fundamentals.share_class_reference.is_primary_share == True)
            .filter(fundamentals.company_reference.primary_exchange_id != "OTCPK")
            .order_by(fundamentals.valuation.market_cap.desc())
            .limit(self.limit)
        )
    
        self.candidates=df_fundamentals.columns
   
    def get_winners(self,context,data):
        #get TMOM
        h = history( self.lookback,'1d','price')[self.candidates]
        h = h.resample('M',how='last')
        #drop first row because it is nan
        pct_change =h.iloc[[0,-1]].pct_change()[1:]
        #drop any other nan values
        self.pct_change = pct_change.dropna(axis=1)
        #convert dataframe to series for sorting. Then sort in asending order
        self.winners =  self.pct_change.squeeze().order(ascending=False)
        self.winners = self.winners[self.winners > 0.0]
        
        #select top 20% of winners
        num_winners = 100
        self.winners = self.winners[:num_winners]
    
    
    #calc Frog In Pan metric based on percent up and down days
    def get_fip_rank(self,context,data):
        #get TMOM
        h = history( self.lookback,'1d','price')[self.winners.index]
        #drop first row because it is nan
        daily_pct_change =h.pct_change()[1:]
        daily_pct_change = daily_pct_change.dropna(axis=1)
        # count positives
        pos = daily_pct_change[ daily_pct_change > 0 ].count(axis=0)
        neg = daily_pct_change[ daily_pct_change < 0 ].count(axis=0)
        tot = daily_pct_change.count(axis=0)
        
        #ID
        self.fip =  (neg - pos)/tot
        #IDz
        #self.fip =  (neg - pos)/(neg+pos)
        
        
    def exec_rank(self,context,data):
        
        self.get_winners(context,data)
        self.get_fip_rank(context,data)
        
        self.longs = self.fip[self.fip < 0.0]
        self.longs = self.longs[:self.num_positions]
 
    def exec_buy(self,context,data):
        r= self.robust(context,data)
        open_orders = get_open_orders()
            
        l = len(self.longs)
        w = (r*self.alloc)/l if l > 0 else 0 #robust adjusted weight
        
        for s in self.longs.index:
            if s in data and s not in open_orders:
                order_target_percent(s,w)
                if w > 0.0:
                    self.bought.append(s)
        
        log.info(str(type(self).__name__) + " longs list len " + str(len(self.longs)))
        log.info(str(type(self).__name__) + " bought list len: " + str(len(self.bought)))    
    
    def exec_sell(self,context,data):

        open_orders = get_open_orders()
        
        for s in context.portfolio.positions :
            if s in data and s not in open_orders :
                if s in self.bought:
                    order_target_percent(s,0)
                    self.bought.remove(s)
                
        log.info(str(type(self).__name__) + " bought list len: " + str(len(self.bought)))
        
    
    
    def show_params(self,c):
        log.info("************************")
        log.info(" Momentum params ")
        log.info("************************")
        log.info(" lower market cap: " + str(self.lower_market_cap))
        log.info(" stock search limit: " + str(self.limit))
        log.info(" risk free asset: " + str(self.risk_free_asset.symbol))
        log.info(" number of positions: " + str(self.num_positions))
        log.info(" moving average threshold: " + str(self.ma_threshold))
        log.info(" pct alloc: " + str(self.alloc))
        log.info(" sell_offset: " + str(self.sell_offset))
        log.info(" buy_offset: " + str(self.buy_offset))
        log.info(" lookback: " + str(self.lookback))
        

def initialize(context):
    set_commission(commission.PerTrade(cost=0.00))  
    set_slippage(slippage.FixedSlippage(spread=0.00))
    #Common vars
    c = context
    c.lastYear = 2015
    c.lastMonth = 12
    c.index = symbol('SPY')
    c.risk_free_asset = symbol('SHY')
    c.ma_threshold = 200
    
    #register Strategies
    #Mom only
    c.strategies = [Mom_Strat(1.0, c.risk_free_asset, c.ma_threshold, c.index, 1, 2) ]
    
    #show parameters of all strategies
    show_params(c)
    
    for strat in c.strategies:
        schedule_function(strat.set_sell_day,date_rules.month_start(days_offset=strat.sell_offset),
            time_rules.market_open(minutes=120)) 
        schedule_function(strat.set_buy_day,date_rules.month_start(days_offset=strat.buy_offset),
            time_rules.market_open(minutes=120)) 
    
    #as a last step check final state of portfolio. In particular check total value of unfullfilled orders
    schedule_function(check_positions,date_rules.month_end(days_offset=0),
                      time_rules.market_open(minutes=240))
    

def check_positions(context,data):

    today = get_datetime()
    
    if today.year == context.lastYear and today.month == context.lastMonth:
        #log current account
        log.info(" account leverage: " + str(context.account.leverage))
        log.info(" portfolio value: " + str(context.portfolio.portfolio_value))
        log.info(" portfolio cash: " + str(context.portfolio.cash))
        

        #check number and value of unfullfilled trades
        # unfullfilled trades are often the cause of a strategy appearing to use leverage when in reality it would not.
        # often the cause of an unfullfilled trade is a company merging or getting bought out
        
        open_orders = get_open_orders()
        
        c = 0
        tot_val = 0
        for s in context.portfolio.positions:
            if s in open_orders and s.end_date.year < context.lastYear and s.end_date.month < context.lastMonth:
                shrs = context.portfolio.positions[s].amount
                #cost = context.portfolio.positions[s].cost_basis
                current = data[s].price
                c += 1
                tot_val +=  (shrs * current)
        
        log.info(" len open orders: " + str(len(open_orders)))
        log.info(" number of unfullfilled trades: " + str(c) + " value: " + str(tot_val))
    

def show_params(c):
    
    log.info("************************")
    log.info(" Algo params ")
    log.info("************************")
    
    for strat in c.strategies:
        strat.show_params(c)
   
    
def before_trading_start(context,data): 
    for strat in context.strategies:
        strat.before_trading_start(context, data)

            
def handle_data(context, data):

    #Call each individual algorithm's handle_data method.
    for strat in context.strategies:
        strat.handle_data(context, data)
    
    record(leverage = context.account.leverage)
    
There was a runtime error.
14 responses

This is the results of a simple 12-month lookback algorithm that selects the top 100 winners. At first glance the FIP algorithm seems to add value.

Clone Algorithm
467
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
##########################################################################################
# This is an attempt to implement the Robust Asset Allocation (RAA) strategy described here,
#    http://blog.alphaarchitect.com/2014/12/02/the-robust-asset-allocation-raa-solution/
# and described in detail in 'DIY Financial Advisor' by W Gray, J Vogel, D Foulke
#
# Notes on implementation
#    - all strategies are required to implement a robust asset allocation function
#    - as a simplification the framework assumes all existing assets in a strategy are sold before new assets are ranked and then bought
#    - the implementation ensures that strategies that do fundamental queries are executed on distinct days so that a maximum number of securities relevant to the particular strategy are examined (i.e. the 500 securities in 'data' are selected by the specific strategy and there is no overlap with other strategies)
#    -Since stocks in the Q universe are generally restricted to those that trade on a US exchange, no distinct allocation is made to domestic and international equities, rather fundamental queries are allowed to invest in ADRs, and in securities not necessarily domiciled in the US
#     -a Gold etf (GSG) is used to approximate an allocation to commodities. This is done to allow for a longer backtest.
#    - the Value strategy implemented is the Magic formula
#    - the momentum strategy is Time Series Momentum with a 12 month lookback
#    - the strategy simply goes to cash when out of the market instead of investing in treasuries. An improvement to the framework would be to add a Portfolio Manager class to manage all trades from all strategies and put any cash in an alternative
###########################################################################################

from datetime import timedelta
from sqlalchemy import or_
from scipy import stats
import statsmodels.api as sm
import talib 
import pandas as pd
import numpy as np
import time


#Base Class for all strategies
class Strategy(object):
    
    def __init__(self, pct_alloc, risk_free_asset, ma_threshold,sell_offset, buy_offset):
        self.alloc = pct_alloc                 #percent of portfolio to allocate to strategy
        self.risk_free_asset = risk_free_asset #risk free asset
        self.ma_threshold = ma_threshold       #moving average theshold
        self.sell_offset = sell_offset         #offset for scheduling when strategy will sell
        self.buy_offset = buy_offset           #offset for scheduling when strategy will buy
        self.sell_day = False                  
        self.buy_day = False    
        self.bought = []                       #maintain list of securites that were bought by strategy
        self.lookback = 252                    #set default lookback period to 1 year (252 days)
 

    def getAlloc(self):
        return self.alloc

    
    def setAlloc(self, alloc):
        self.alloc = alloc
    
    
    #Implement robust allocation algo for strategy
    def robust(self,context,data):
        raise NotImplementedError("implement robust method in Subclass")
    
    
    #Show relevant params at beginning of execution
    def show_params(self):
        raise NotImplementedError("implement show_params method in Subclass")
    
    
    #Implement fundamental queries here
    def handle_data(self,context,data):
        raise NotImplementedError("implement handle_data method in Subclass")
    
    
    #Implement buy, sell and ranking functions here
    def before_trading_start(self,context,data): 
        raise NotImplementedError("implement before_trading_start method in Subclass")
    
    
#Class for ETF strategy   - currently limited to holding 1 ETF
class ETF_Strat(Strategy):
    
    def __init__(self, pct_alloc, risk_free_asset, ma_threshold, etf, sell_offset, buy_offset):
        Strategy.__init__(self,pct_alloc,risk_free_asset,ma_threshold,sell_offset, buy_offset)
        
        self.etf = etf    #set ETF for strategy

        
    #calculate excess return over risk free asset    
    def get_excess_return(self,context,data):
        
        h = history( self.lookback,'1d','price')[[self.etf,self.risk_free_asset]]
        h = h.resample('M',how='last')
        
        pct_change =h.iloc[[0,-1]].pct_change()[1:]
        
        pct_change = pct_change.dropna(axis=1)
        
        ex_return = pct_change[self.etf]-pct_change[self.risk_free_asset]
        return ex_return[0]

    
    #implement RAA strategy 
    def robust(self,context,data):
        r = 0.0
    
        #if etf time series MOM returns are greater than risk free asset then invest 1/2 weight if etf price > moving avg then invest 1/2 weight. If both then invest full weight
        ex_return = self.get_excess_return(context,data)
        if ex_return > 0.0:
            r += 0.5
        if data[self.etf].price > data[self.etf].mavg(self.ma_threshold):
            r += 0.5

        return r
    
    
    #buy etf based on RAA allocation    
    def exec_buy(self,context,data):
        open_orders = get_open_orders()
        r = self.robust(context,data)
        w = r * self.alloc #RAA adjusted weight
        
        if self.etf in data and self.etf not in open_orders and w > 0.0:
            order_target_percent(self.etf,w)
            self.bought.append(self.etf)
       
        log.info(str(type(self).__name__) + " bought " + str(self.etf.symbol) + " w=" + str(w))

        
    #sell etf
    def exec_sell(self,context,data):
        open_orders = get_open_orders()
        
        #if self.etf in context.portfolio.positions and self.etf in data and self.etf in self.bought and self.etf not in open_orders:
        if self.etf in context.portfolio.positions and self.etf in self.bought and self.etf not in open_orders:
            order_target_percent(self.etf,0)
            self.bought.remove(self.etf)
        
    
    def set_sell_day(self,context,data):
        self.sell_day = True
        
    
    def set_buy_day(self,context,data):
        self.buy_day = True
        
        
    def handle_data(self,context,data):
        if self.sell_day:
            self.exec_sell(context,data)
            self.sell_day = False
        
        if self.buy_day:
            self.exec_buy(context,data)
            self.buy_day = False
        

    def before_trading_start(self,context,data): 
        pass
    
    
    def show_params(self,c):
        log.info("************************")
        log.info(" ETF params ")
        log.info("************************")
        log.info(" etf name: " + str(self.etf.symbol))
        log.info(" risk free asset: " + str(self.risk_free_asset.symbol))
        log.info(" market ma threshold: " + str(self.ma_threshold))
        log.info(" lookback: " + str(self.lookback))
        log.info(" pct alloc: " + str(self.alloc))
        log.info(" sell_offset: " + str(self.sell_offset))
        log.info(" buy_offset: " + str(self.buy_offset))

        
#Base class for Equity Strategies
class Equity_Strat(Strategy):
    
    def __init__(self, pct_alloc,risk_free_asset,ma_threshold, index, sell_offset, buy_offset):
        Strategy.__init__(self,pct_alloc,risk_free_asset,ma_threshold,sell_offset, buy_offset)
        
        self.index = index #set 

        
    def robust(self,context,data):
        r = 0.0     
        
        ex_return = self.get_excess_return(context,data)
        if ex_return > 0.0:
            r += 0.5
        if data[self.index].price > data[self.index].mavg(self.ma_threshold):
            r += 0.5
        
        return r
    
    
    def get_excess_return(self,context,data):
        h = history( self.lookback,'1d','price')[[self.index,self.risk_free_asset]]
        h = h.resample('M',how='last')

        pct_change =h.iloc[[0,-1]].pct_change()[1:]
        pct_change = pct_change.dropna(axis=1)
        
        ex_return = pct_change[self.index]-pct_change[self.risk_free_asset]
        
        return ex_return[0]


#Momentum Strategy
# -- Sort equities on best 1 year performance
class Mom_Strat(Equity_Strat):
   
    def __init__(self, pct_alloc, risk_free_asset, ma_threshold, index, sell_offset, buy_offset):
        Equity_Strat.__init__(self,pct_alloc,risk_free_asset,ma_threshold,index,sell_offset, buy_offset)
        
        self.lower_market_cap = 2000e6     #lower market cap for candidate selection
        self.limit = 500                   #max number of stocks to select for fundamental query
        self.num_positions = 100            #max number of positions the strategy will select
        
        
    def set_sell_day(self,context,data):
        self.sell_day = True
        
    
    def set_buy_day(self,context,data):
        self.buy_day = True
        
   
    def before_trading_start(self,context,data): 
        
        if self.buy_day:
            self.get_candidates()
            update_universe(self.candidates)
            
        
    def handle_data(self,context,data):
        if self.sell_day:
            self.exec_sell(context,data)
            self.sell_day = False
        
        if self.buy_day:
            self.exec_rank(context, data)
            self.exec_buy(context,data)
            self.buy_day = False

        
    def get_candidates(self):
        df_fundamentals = get_fundamentals(
            query(
                fundamentals.valuation.shares_outstanding,
                fundamentals.valuation.market_cap
            )
            #.filter(fundamentals.company_reference.country_id == "USA")
            .filter(or_(fundamentals.company_reference.primary_exchange_id == "NAS", fundamentals.company_reference.primary_exchange_id == "NYSE"))
            .filter(fundamentals.valuation.market_cap != None)
            .filter(fundamentals.valuation.market_cap > self.lower_market_cap ) 
            .filter(fundamentals.valuation.shares_outstanding != None)
            #.filter(fundamentals.share_class_reference.is_depositary_receipt == False)
            .filter(fundamentals.share_class_reference.is_primary_share == True)
            .filter(fundamentals.company_reference.primary_exchange_id != "OTCPK")
            .order_by(fundamentals.valuation.market_cap.desc())
            .limit(self.limit)
        )
    
        self.candidates=df_fundamentals.columns
   
    
    def exec_rank(self,context,data):
        
        #get TMOM
        h = history( self.lookback,'1d','price')[self.candidates]
        h = h.resample('M',how='last') #get monthly returns
        
        pct_change =h.iloc[[0,-1]].pct_change()[1:]
        pct_change = pct_change.dropna(axis=1)
        
        #convert dataframe to series for sorting. Then sort in descending order
        self.longs =  pct_change.squeeze().order(ascending=False)
        self.longs = self.longs[self.longs > 0.0]
        self.longs = self.longs[:self.num_positions]
    
 
    def exec_buy(self,context,data):
        r= self.robust(context,data)
        open_orders = get_open_orders()
            
        l = len(self.longs)
        w = (r*self.alloc)/l if l > 0 else 0 #robust adjusted weight
        
        for s in self.longs.index:
            if s in data and s not in open_orders:
                order_target_percent(s,w)
                if w > 0.0:
                    self.bought.append(s)
        
        log.info(str(type(self).__name__) + " longs list len " + str(len(self.longs)))
        log.info(str(type(self).__name__) + " bought list len: " + str(len(self.bought)))    
    
    def exec_sell(self,context,data):

        open_orders = get_open_orders()
        
        for s in context.portfolio.positions :
            if s in data and s not in open_orders :
                if s in self.bought:
                    order_target_percent(s,0)
                    self.bought.remove(s)
                
        log.info(str(type(self).__name__) + " bought list len: " + str(len(self.bought)))
        
    
    
    def show_params(self,c):
        log.info("************************")
        log.info(" Momentum params ")
        log.info("************************")
        log.info(" lower market cap: " + str(self.lower_market_cap))
        log.info(" stock search limit: " + str(self.limit))
        log.info(" risk free asset: " + str(self.risk_free_asset.symbol))
        log.info(" number of positions: " + str(self.num_positions))
        log.info(" moving average threshold: " + str(self.ma_threshold))
        log.info(" pct alloc: " + str(self.alloc))
        log.info(" sell_offset: " + str(self.sell_offset))
        log.info(" buy_offset: " + str(self.buy_offset))
        log.info(" lookback: " + str(self.lookback))
       
        

class Value_Strat(Equity_Strat):
    
    def __init__(self, pct_alloc, risk_free_asset, ma_threshold, index, sell_offset, buy_offset):
        
        Equity_Strat.__init__(self,pct_alloc,risk_free_asset,ma_threshold,index,sell_offset, buy_offset)
    
        self.lower_market_cap = 2000e6
        self.limit = 500
        self.num_positions = 30
        self.rebalance_month = 12 #June
        self.longs = []
        #self.alloc = pct_alloc
        #self.w = self.pct_alloc/self.num_positions

    
    def set_sell_day(self,context,data):
         today = get_datetime()
         if today.month == self.rebalance_month:
             self.sell_day = True
        
    
    def set_buy_day(self,context,data):
        today = get_datetime()
        if today.month == self.rebalance_month:
            self.buy_day = True
        
   
    def before_trading_start(self,context,data): 
        
        if self.sell_day:
            update_universe(self.bought)

        if self.buy_day:
            self.get_candidates()
            update_universe(self.candidates)
    
    
    def handle_data(self,context,data):
        
        if self.sell_day:
            self.exec_sell(context,data)
            self.sell_day = False
        
        if self.buy_day:
            self.exec_rank(context,data)
            self.exec_buy(context,data)
            self.buy_day = False
 
    
    def get_candidates(self):
        excluded_sectors = [103, 207]
        sector_code = fundamentals.asset_classification.morningstar_sector_code
        fundamental_df = get_fundamentals(
            query(
                sector_code,
                fundamentals.valuation.market_cap,
                fundamentals.valuation.enterprise_value,
                fundamentals.cash_flow_statement.capital_expenditure,
                fundamentals.operation_ratios.roic,
                fundamentals.income_statement.ebit,
                fundamentals.income_statement.ebitda,
                fundamentals.balance_sheet.total_assets,            
        )
        #.filter(fundamentals.company_reference.country_id == "USA")
        .filter(or_(fundamentals.company_reference.primary_exchange_id == "NAS", fundamentals.company_reference.primary_exchange_id == "NYSE"))
        .filter(fundamentals.share_class_reference.is_primary_share == True)
        .filter(fundamentals.company_reference.primary_exchange_id != "OTCPK")
        #.filter(fundamentals.share_class_reference.is_depositary_receipt == False)
        .filter(~sector_code.in_(excluded_sectors))
        .filter(fundamentals.valuation.market_cap > self.lower_market_cap ) 
        .filter(fundamentals.valuation_ratios.ev_to_ebitda > 0)
        .order_by(fundamentals.valuation.market_cap.desc())
        .limit(self.limit)
        )
        
        self.candidates = fundamental_df
        
        
    def exec_rank(self,context,data):
    
        #get MF components
        earnings_yield = self.candidates.ix['ebit'] / self.candidates.ix['enterprise_value']
        roic = self.candidates.ix['roic'].copy()
    
        #sort MF components
        earnings_yield.sort(ascending=False)
        roic.sort(ascending=False)

        #get ranks for components
        earnings_yield_ranking = pd.Series(range(len(earnings_yield)), index=earnings_yield.index)
        roic_ranking = pd.Series(range(len(roic)), index=roic.index) 
    
        #get combined ranking - ranks = earnings_yield_ranking + roic_ranking
        ranks = {}
        for s in self.candidates.columns:
            v_rank = earnings_yield_ranking.loc[s.sid]
            q_rank = roic_ranking.loc[s.sid]
            ranks[s] = v_rank+q_rank
    
        #convert to df and sort
        mf_ranks = pd.Series(ranks,name='Symbol')
        mf_ranks.sort(ascending = True)
        
        self.longs = mf_ranks.index.tolist()
        self.longs = self.longs[:self.num_positions]

     
    def exec_buy(self,context,data):
        
        r= self.robust(context,data)
        open_orders = get_open_orders()
        
        l = len(self.longs)
        w = (r*self.alloc)/self.num_positions if l > 0 else 0
            
        for s in self.longs:
            if s in data and s not in open_orders:
                order_target_percent(s,w)
                if w > 0.0:
                    self.bought.append(s)
            
        log.info(str(type(self).__name__) + " longs list len " + str(len(self.longs)))
        log.info(str(type(self).__name__) + " bought " + str(len(self.bought)) + " stocks")

        
    def exec_sell(self,context,data):
        open_orders = get_open_orders()
        
        for s in context.portfolio.positions:
            if s in data and s in self.bought and s not in open_orders:
                order_target_percent(s,0)
                self.bought.remove(s)
            
        log.info(str(type(self).__name__) + " bought list len: " + str(len(self.bought)))

        
    def show_params(self,c):
        log.info("************************")
        log.info(" Value params ")
        log.info("************************")
        log.info(" lower market cap: " + str(self.lower_market_cap))
        log.info(" stock search limit: " + str(self.limit))
        log.info(" risk free asset: " + str(self.risk_free_asset.symbol))
        log.info(" number of positions: " + str(self.num_positions))
        log.info(" moving average threshold: " + str(self.ma_threshold))
        log.info(" pct alloc: " + str(self.alloc))
        log.info(" sell_offset: " + str(self.sell_offset))
        log.info(" buy_offset: " + str(self.buy_offset))        
        log.info(" rebalance month: " + str(self.rebalance_month))

        

def initialize(context):
    set_commission(commission.PerTrade(cost=0.00))  
    set_slippage(slippage.FixedSlippage(spread=0.00))
    #Common vars
    c = context
    c.lastYear = 2015
    c.lastMonth = 12
    c.index = symbol('SPY')
    c.risk_free_asset = symbol('SHY')
    c.ma_threshold = 200
    
    #register Strategies
    #IVY_4 allocations - no allocation to world equities
    # replace GSG with Gold to get longer backtest
    '''
    c.strategies = [ETF_Strat(0.25,c.risk_free_asset,c.ma_threshold,symbol('SPY'),1,2),
                    ETF_Strat(0.25,c.risk_free_asset,c.ma_threshold,symbol('IYR'),1,2),
                    ETF_Strat(0.25,c.risk_free_asset,c.ma_threshold,symbol('GLD'),1,2),
                    ETF_Strat(0.25,c.risk_free_asset,c.ma_threshold,symbol('IEF'),1,2)]
    '''
   
    #RAA Balanced - 40% allocation to equities
    '''
    c.strategies = [Value_Strat(0.2,c.risk_free_asset, c.ma_threshold, c.index,11,13),
                    Mom_Strat(0.2, c.risk_free_asset, c.ma_threshold, c.index, 1, 2) ,
                    ETF_Strat(0.2,c.risk_free_asset,c.ma_threshold,symbol('IYR'),1,2),
                    ETF_Strat(0.2,c.risk_free_asset,c.ma_threshold,symbol('GLD'),1,2),
                    ETF_Strat(0.2,c.risk_free_asset,c.ma_threshold,symbol('IEF'),1,2)]
    '''    
    
    #RAA Moderate Tilt - 60% allocation to equities
    '''
    c.strategies = [Value_Strat(0.3,c.risk_free_asset, c.ma_threshold, c.index,11,13),
                    Mom_Strat(0.3, c.risk_free_asset, c.ma_threshold, c.index, 1, 2) ,
                    ETF_Strat(0.1,c.risk_free_asset,c.ma_threshold,symbol('IYR'),1,2),
                    ETF_Strat(0.1,c.risk_free_asset,c.ma_threshold,symbol('GLD'),1,2),
                    ETF_Strat(0.2,c.risk_free_asset,c.ma_threshold,symbol('IEF'),1,2)]
    '''
        
    
    #RAA Aggressive Tilt - 80% allocation to equities
    '''
    c.strategies = [Value_Strat(0.4,c.risk_free_asset, c.ma_threshold, c.index,21,22),
                    Mom_Strat(0.4, c.risk_free_asset, c.ma_threshold, c.index, 1, 2) ,
                    ETF_Strat(0.05,c.risk_free_asset,c.ma_threshold,symbol('IYR'),1, 2),
                    ETF_Strat(0.05,c.risk_free_asset,c.ma_threshold,symbol('GLD'),1,2),
                    ETF_Strat(0.1,c.risk_free_asset,c.ma_threshold,symbol('IEF'),1,2)]
    '''
    #Mom only
    c.strategies = [Mom_Strat(1.0, c.risk_free_asset, c.ma_threshold, c.index, 1, 2) ]
    #Val only
    #c.strategies = [Value_Strat(1.0,c.risk_free_asset, c.ma_threshold, c.index,11,13) ]
    #Mom/Val 50/50
    #c.strategies = [Value_Strat(0.5,c.risk_free_asset, c.ma_threshold, c.index,21,22),
    #                Mom_Strat(0.5, c.risk_free_asset, c.ma_threshold, c.index, 1, 2) ]
    
    #show parameters of all strategies
    show_params(c)
    
    for strat in c.strategies:
        schedule_function(strat.set_sell_day,date_rules.month_start(days_offset=strat.sell_offset),
            time_rules.market_open(minutes=120)) 
        schedule_function(strat.set_buy_day,date_rules.month_start(days_offset=strat.buy_offset),
            time_rules.market_open(minutes=120)) 
    
    #as a last step check final state of portfolio. In particular check total value of unfullfilled orders
    schedule_function(check_positions,date_rules.month_end(days_offset=0),
                      time_rules.market_open(minutes=240))
    

def check_positions(context,data):

    today = get_datetime()
    
    if today.year == context.lastYear and today.month == context.lastMonth:
        #log current account
        log.info(" account leverage: " + str(context.account.leverage))
        log.info(" portfolio value: " + str(context.portfolio.portfolio_value))
        log.info(" portfolio cash: " + str(context.portfolio.cash))
        

        #check number and value of unfullfilled trades
        # unfullfilled trades are often the cause of a strategy appearing to use leverage when in reality it would not.
        # often the cause of an unfullfilled trade is a company merging or getting bought out
        
        open_orders = get_open_orders()
        
        c = 0
        tot_val = 0
        for s in context.portfolio.positions:
            if s in open_orders and s.end_date.year < context.lastYear and s.end_date.month < context.lastMonth:
                shrs = context.portfolio.positions[s].amount
                #cost = context.portfolio.positions[s].cost_basis
                current = data[s].price
                c += 1
                tot_val +=  (shrs * current)
        
        log.info(" len open orders: " + str(len(open_orders)))
        log.info(" number of unfullfilled trades: " + str(c) + " value: " + str(tot_val))
    

def show_params(c):
    
    log.info("************************")
    log.info(" Algo params ")
    log.info("************************")
    
    for strat in c.strategies:
        strat.show_params(c)
   
    
def before_trading_start(context,data): 
    for strat in context.strategies:
        strat.before_trading_start(context, data)

            
def handle_data(context, data):

    #Call each individual algorithm's handle_data method.
    for strat in context.strategies:
        strat.handle_data(context, data)
    
    record(leverage = context.account.leverage)
    
There was a runtime error.

However running the same 12-month lookback algo but only selecting the top 20 winners, produces similar results to the FIP algorithm above.

Clone Algorithm
467
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
##########################################################################################
# This is an attempt to implement the Robust Asset Allocation (RAA) strategy described here,
#    http://blog.alphaarchitect.com/2014/12/02/the-robust-asset-allocation-raa-solution/
# and described in detail in 'DIY Financial Advisor' by W Gray, J Vogel, D Foulke
#
# Notes on implementation
#    - all strategies are required to implement a robust asset allocation function
#    - as a simplification the framework assumes all existing assets in a strategy are sold before new assets are ranked and then bought
#    - the implementation ensures that strategies that do fundamental queries are executed on distinct days so that a maximum number of securities relevant to the particular strategy are examined (i.e. the 500 securities in 'data' are selected by the specific strategy and there is no overlap with other strategies)
#    -Since stocks in the Q universe are generally restricted to those that trade on a US exchange, no distinct allocation is made to domestic and international equities, rather fundamental queries are allowed to invest in ADRs, and in securities not necessarily domiciled in the US
#     -a Gold etf (GSG) is used to approximate an allocation to commodities. This is done to allow for a longer backtest.
#    - the Value strategy implemented is the Magic formula
#    - the momentum strategy is Time Series Momentum with a 12 month lookback
#    - the strategy simply goes to cash when out of the market instead of investing in treasuries. An improvement to the framework would be to add a Portfolio Manager class to manage all trades from all strategies and put any cash in an alternative
###########################################################################################

from datetime import timedelta
from sqlalchemy import or_
from scipy import stats
import statsmodels.api as sm
import talib 
import pandas as pd
import numpy as np
import time


#Base Class for all strategies
class Strategy(object):
    
    def __init__(self, pct_alloc, risk_free_asset, ma_threshold,sell_offset, buy_offset):
        self.alloc = pct_alloc                 #percent of portfolio to allocate to strategy
        self.risk_free_asset = risk_free_asset #risk free asset
        self.ma_threshold = ma_threshold       #moving average theshold
        self.sell_offset = sell_offset         #offset for scheduling when strategy will sell
        self.buy_offset = buy_offset           #offset for scheduling when strategy will buy
        self.sell_day = False                  
        self.buy_day = False    
        self.bought = []                       #maintain list of securites that were bought by strategy
        self.lookback = 252                    #set default lookback period to 1 year (252 days)
 

    def getAlloc(self):
        return self.alloc

    
    def setAlloc(self, alloc):
        self.alloc = alloc
    
    
    #Implement robust allocation algo for strategy
    def robust(self,context,data):
        raise NotImplementedError("implement robust method in Subclass")
    
    
    #Show relevant params at beginning of execution
    def show_params(self):
        raise NotImplementedError("implement show_params method in Subclass")
    
    
    #Implement fundamental queries here
    def handle_data(self,context,data):
        raise NotImplementedError("implement handle_data method in Subclass")
    
    
    #Implement buy, sell and ranking functions here
    def before_trading_start(self,context,data): 
        raise NotImplementedError("implement before_trading_start method in Subclass")
    
    
#Class for ETF strategy   - currently limited to holding 1 ETF
class ETF_Strat(Strategy):
    
    def __init__(self, pct_alloc, risk_free_asset, ma_threshold, etf, sell_offset, buy_offset):
        Strategy.__init__(self,pct_alloc,risk_free_asset,ma_threshold,sell_offset, buy_offset)
        
        self.etf = etf    #set ETF for strategy

        
    #calculate excess return over risk free asset    
    def get_excess_return(self,context,data):
        
        h = history( self.lookback,'1d','price')[[self.etf,self.risk_free_asset]]
        h = h.resample('M',how='last')
        
        pct_change =h.iloc[[0,-1]].pct_change()[1:]
        
        pct_change = pct_change.dropna(axis=1)
        
        ex_return = pct_change[self.etf]-pct_change[self.risk_free_asset]
        return ex_return[0]

    
    #implement RAA strategy 
    def robust(self,context,data):
        r = 0.0
    
        #if etf time series MOM returns are greater than risk free asset then invest 1/2 weight if etf price > moving avg then invest 1/2 weight. If both then invest full weight
        ex_return = self.get_excess_return(context,data)
        if ex_return > 0.0:
            r += 0.5
        if data[self.etf].price > data[self.etf].mavg(self.ma_threshold):
            r += 0.5

        return r
    
    
    #buy etf based on RAA allocation    
    def exec_buy(self,context,data):
        open_orders = get_open_orders()
        r = self.robust(context,data)
        w = r * self.alloc #RAA adjusted weight
        
        if self.etf in data and self.etf not in open_orders and w > 0.0:
            order_target_percent(self.etf,w)
            self.bought.append(self.etf)
       
        log.info(str(type(self).__name__) + " bought " + str(self.etf.symbol) + " w=" + str(w))

        
    #sell etf
    def exec_sell(self,context,data):
        open_orders = get_open_orders()
        
        #if self.etf in context.portfolio.positions and self.etf in data and self.etf in self.bought and self.etf not in open_orders:
        if self.etf in context.portfolio.positions and self.etf in self.bought and self.etf not in open_orders:
            order_target_percent(self.etf,0)
            self.bought.remove(self.etf)
        
    
    def set_sell_day(self,context,data):
        self.sell_day = True
        
    
    def set_buy_day(self,context,data):
        self.buy_day = True
        
        
    def handle_data(self,context,data):
        if self.sell_day:
            self.exec_sell(context,data)
            self.sell_day = False
        
        if self.buy_day:
            self.exec_buy(context,data)
            self.buy_day = False
        

    def before_trading_start(self,context,data): 
        pass
    
    
    def show_params(self,c):
        log.info("************************")
        log.info(" ETF params ")
        log.info("************************")
        log.info(" etf name: " + str(self.etf.symbol))
        log.info(" risk free asset: " + str(self.risk_free_asset.symbol))
        log.info(" market ma threshold: " + str(self.ma_threshold))
        log.info(" lookback: " + str(self.lookback))
        log.info(" pct alloc: " + str(self.alloc))
        log.info(" sell_offset: " + str(self.sell_offset))
        log.info(" buy_offset: " + str(self.buy_offset))

        
#Base class for Equity Strategies
class Equity_Strat(Strategy):
    
    def __init__(self, pct_alloc,risk_free_asset,ma_threshold, index, sell_offset, buy_offset):
        Strategy.__init__(self,pct_alloc,risk_free_asset,ma_threshold,sell_offset, buy_offset)
        
        self.index = index #set 

        
    def robust(self,context,data):
        r = 0.0     
        
        ex_return = self.get_excess_return(context,data)
        if ex_return > 0.0:
            r += 0.5
        if data[self.index].price > data[self.index].mavg(self.ma_threshold):
            r += 0.5
        
        return r
    
    
    def get_excess_return(self,context,data):
        h = history( self.lookback,'1d','price')[[self.index,self.risk_free_asset]]
        h = h.resample('M',how='last')

        pct_change =h.iloc[[0,-1]].pct_change()[1:]
        pct_change = pct_change.dropna(axis=1)
        
        ex_return = pct_change[self.index]-pct_change[self.risk_free_asset]
        
        return ex_return[0]


#Momentum Strategy
# -- Sort equities on best 1 year performance
class Mom_Strat(Equity_Strat):
   
    def __init__(self, pct_alloc, risk_free_asset, ma_threshold, index, sell_offset, buy_offset):
        Equity_Strat.__init__(self,pct_alloc,risk_free_asset,ma_threshold,index,sell_offset, buy_offset)
        
        self.lower_market_cap = 2000e6     #lower market cap for candidate selection
        self.limit = 500                   #max number of stocks to select for fundamental query
        self.num_positions = 20            #max number of positions the strategy will select
        
        
    def set_sell_day(self,context,data):
        self.sell_day = True
        
    
    def set_buy_day(self,context,data):
        self.buy_day = True
        
   
    def before_trading_start(self,context,data): 
        
        if self.buy_day:
            self.get_candidates()
            update_universe(self.candidates)
            
        
    def handle_data(self,context,data):
        if self.sell_day:
            self.exec_sell(context,data)
            self.sell_day = False
        
        if self.buy_day:
            self.exec_rank(context, data)
            self.exec_buy(context,data)
            self.buy_day = False

        
    def get_candidates(self):
        df_fundamentals = get_fundamentals(
            query(
                fundamentals.valuation.shares_outstanding,
                fundamentals.valuation.market_cap
            )
            #.filter(fundamentals.company_reference.country_id == "USA")
            .filter(or_(fundamentals.company_reference.primary_exchange_id == "NAS", fundamentals.company_reference.primary_exchange_id == "NYSE"))
            .filter(fundamentals.valuation.market_cap != None)
            .filter(fundamentals.valuation.market_cap > self.lower_market_cap ) 
            .filter(fundamentals.valuation.shares_outstanding != None)
            #.filter(fundamentals.share_class_reference.is_depositary_receipt == False)
            .filter(fundamentals.share_class_reference.is_primary_share == True)
            .filter(fundamentals.company_reference.primary_exchange_id != "OTCPK")
            .order_by(fundamentals.valuation.market_cap.desc())
            .limit(self.limit)
        )
    
        self.candidates=df_fundamentals.columns
   
    
    def exec_rank(self,context,data):
        
        #get TMOM
        h = history( self.lookback,'1d','price')[self.candidates]
        h = h.resample('M',how='last') #get monthly returns
        
        pct_change =h.iloc[[0,-1]].pct_change()[1:]
        pct_change = pct_change.dropna(axis=1)
        
        #convert dataframe to series for sorting. Then sort in descending order
        self.longs =  pct_change.squeeze().order(ascending=False)
        self.longs = self.longs[self.longs > 0.0]
        self.longs = self.longs[:self.num_positions]
    
 
    def exec_buy(self,context,data):
        r= self.robust(context,data)
        open_orders = get_open_orders()
            
        l = len(self.longs)
        w = (r*self.alloc)/l if l > 0 else 0 #robust adjusted weight
        
        for s in self.longs.index:
            if s in data and s not in open_orders:
                order_target_percent(s,w)
                if w > 0.0:
                    self.bought.append(s)
        
        log.info(str(type(self).__name__) + " longs list len " + str(len(self.longs)))
        log.info(str(type(self).__name__) + " bought list len: " + str(len(self.bought)))    
    
    def exec_sell(self,context,data):

        open_orders = get_open_orders()
        
        for s in context.portfolio.positions :
            if s in data and s not in open_orders :
                if s in self.bought:
                    order_target_percent(s,0)
                    self.bought.remove(s)
                
        log.info(str(type(self).__name__) + " bought list len: " + str(len(self.bought)))
        
    
    
    def show_params(self,c):
        log.info("************************")
        log.info(" Momentum params ")
        log.info("************************")
        log.info(" lower market cap: " + str(self.lower_market_cap))
        log.info(" stock search limit: " + str(self.limit))
        log.info(" risk free asset: " + str(self.risk_free_asset.symbol))
        log.info(" number of positions: " + str(self.num_positions))
        log.info(" moving average threshold: " + str(self.ma_threshold))
        log.info(" pct alloc: " + str(self.alloc))
        log.info(" sell_offset: " + str(self.sell_offset))
        log.info(" buy_offset: " + str(self.buy_offset))
        log.info(" lookback: " + str(self.lookback))
       
        

class Value_Strat(Equity_Strat):
    
    def __init__(self, pct_alloc, risk_free_asset, ma_threshold, index, sell_offset, buy_offset):
        
        Equity_Strat.__init__(self,pct_alloc,risk_free_asset,ma_threshold,index,sell_offset, buy_offset)
    
        self.lower_market_cap = 2000e6
        self.limit = 500
        self.num_positions = 30
        self.rebalance_month = 12 #June
        self.longs = []
        #self.alloc = pct_alloc
        #self.w = self.pct_alloc/self.num_positions

    
    def set_sell_day(self,context,data):
         today = get_datetime()
         if today.month == self.rebalance_month:
             self.sell_day = True
        
    
    def set_buy_day(self,context,data):
        today = get_datetime()
        if today.month == self.rebalance_month:
            self.buy_day = True
        
   
    def before_trading_start(self,context,data): 
        
        if self.sell_day:
            update_universe(self.bought)

        if self.buy_day:
            self.get_candidates()
            update_universe(self.candidates)
    
    
    def handle_data(self,context,data):
        
        if self.sell_day:
            self.exec_sell(context,data)
            self.sell_day = False
        
        if self.buy_day:
            self.exec_rank(context,data)
            self.exec_buy(context,data)
            self.buy_day = False
 
    
    def get_candidates(self):
        excluded_sectors = [103, 207]
        sector_code = fundamentals.asset_classification.morningstar_sector_code
        fundamental_df = get_fundamentals(
            query(
                sector_code,
                fundamentals.valuation.market_cap,
                fundamentals.valuation.enterprise_value,
                fundamentals.cash_flow_statement.capital_expenditure,
                fundamentals.operation_ratios.roic,
                fundamentals.income_statement.ebit,
                fundamentals.income_statement.ebitda,
                fundamentals.balance_sheet.total_assets,            
        )
        #.filter(fundamentals.company_reference.country_id == "USA")
        .filter(or_(fundamentals.company_reference.primary_exchange_id == "NAS", fundamentals.company_reference.primary_exchange_id == "NYSE"))
        .filter(fundamentals.share_class_reference.is_primary_share == True)
        .filter(fundamentals.company_reference.primary_exchange_id != "OTCPK")
        #.filter(fundamentals.share_class_reference.is_depositary_receipt == False)
        .filter(~sector_code.in_(excluded_sectors))
        .filter(fundamentals.valuation.market_cap > self.lower_market_cap ) 
        .filter(fundamentals.valuation_ratios.ev_to_ebitda > 0)
        .order_by(fundamentals.valuation.market_cap.desc())
        .limit(self.limit)
        )
        
        self.candidates = fundamental_df
        
        
    def exec_rank(self,context,data):
    
        #get MF components
        earnings_yield = self.candidates.ix['ebit'] / self.candidates.ix['enterprise_value']
        roic = self.candidates.ix['roic'].copy()
    
        #sort MF components
        earnings_yield.sort(ascending=False)
        roic.sort(ascending=False)

        #get ranks for components
        earnings_yield_ranking = pd.Series(range(len(earnings_yield)), index=earnings_yield.index)
        roic_ranking = pd.Series(range(len(roic)), index=roic.index) 
    
        #get combined ranking - ranks = earnings_yield_ranking + roic_ranking
        ranks = {}
        for s in self.candidates.columns:
            v_rank = earnings_yield_ranking.loc[s.sid]
            q_rank = roic_ranking.loc[s.sid]
            ranks[s] = v_rank+q_rank
    
        #convert to df and sort
        mf_ranks = pd.Series(ranks,name='Symbol')
        mf_ranks.sort(ascending = True)
        
        self.longs = mf_ranks.index.tolist()
        self.longs = self.longs[:self.num_positions]

     
    def exec_buy(self,context,data):
        
        r= self.robust(context,data)
        open_orders = get_open_orders()
        
        l = len(self.longs)
        w = (r*self.alloc)/self.num_positions if l > 0 else 0
            
        for s in self.longs:
            if s in data and s not in open_orders:
                order_target_percent(s,w)
                if w > 0.0:
                    self.bought.append(s)
            
        log.info(str(type(self).__name__) + " longs list len " + str(len(self.longs)))
        log.info(str(type(self).__name__) + " bought " + str(len(self.bought)) + " stocks")

        
    def exec_sell(self,context,data):
        open_orders = get_open_orders()
        
        for s in context.portfolio.positions:
            if s in data and s in self.bought and s not in open_orders:
                order_target_percent(s,0)
                self.bought.remove(s)
            
        log.info(str(type(self).__name__) + " bought list len: " + str(len(self.bought)))

        
    def show_params(self,c):
        log.info("************************")
        log.info(" Value params ")
        log.info("************************")
        log.info(" lower market cap: " + str(self.lower_market_cap))
        log.info(" stock search limit: " + str(self.limit))
        log.info(" risk free asset: " + str(self.risk_free_asset.symbol))
        log.info(" number of positions: " + str(self.num_positions))
        log.info(" moving average threshold: " + str(self.ma_threshold))
        log.info(" pct alloc: " + str(self.alloc))
        log.info(" sell_offset: " + str(self.sell_offset))
        log.info(" buy_offset: " + str(self.buy_offset))        
        log.info(" rebalance month: " + str(self.rebalance_month))

        

def initialize(context):
    set_commission(commission.PerTrade(cost=0.00))  
    set_slippage(slippage.FixedSlippage(spread=0.00))
    #Common vars
    c = context
    c.lastYear = 2015
    c.lastMonth = 12
    c.index = symbol('SPY')
    c.risk_free_asset = symbol('SHY')
    c.ma_threshold = 200
    
    #register Strategies
    #IVY_4 allocations - no allocation to world equities
    # replace GSG with Gold to get longer backtest
    '''
    c.strategies = [ETF_Strat(0.25,c.risk_free_asset,c.ma_threshold,symbol('SPY'),1,2),
                    ETF_Strat(0.25,c.risk_free_asset,c.ma_threshold,symbol('IYR'),1,2),
                    ETF_Strat(0.25,c.risk_free_asset,c.ma_threshold,symbol('GLD'),1,2),
                    ETF_Strat(0.25,c.risk_free_asset,c.ma_threshold,symbol('IEF'),1,2)]
    '''
   
    #RAA Balanced - 40% allocation to equities
    '''
    c.strategies = [Value_Strat(0.2,c.risk_free_asset, c.ma_threshold, c.index,11,13),
                    Mom_Strat(0.2, c.risk_free_asset, c.ma_threshold, c.index, 1, 2) ,
                    ETF_Strat(0.2,c.risk_free_asset,c.ma_threshold,symbol('IYR'),1,2),
                    ETF_Strat(0.2,c.risk_free_asset,c.ma_threshold,symbol('GLD'),1,2),
                    ETF_Strat(0.2,c.risk_free_asset,c.ma_threshold,symbol('IEF'),1,2)]
    '''    
    
    #RAA Moderate Tilt - 60% allocation to equities
    '''
    c.strategies = [Value_Strat(0.3,c.risk_free_asset, c.ma_threshold, c.index,11,13),
                    Mom_Strat(0.3, c.risk_free_asset, c.ma_threshold, c.index, 1, 2) ,
                    ETF_Strat(0.1,c.risk_free_asset,c.ma_threshold,symbol('IYR'),1,2),
                    ETF_Strat(0.1,c.risk_free_asset,c.ma_threshold,symbol('GLD'),1,2),
                    ETF_Strat(0.2,c.risk_free_asset,c.ma_threshold,symbol('IEF'),1,2)]
    '''
        
    
    #RAA Aggressive Tilt - 80% allocation to equities
    '''
    c.strategies = [Value_Strat(0.4,c.risk_free_asset, c.ma_threshold, c.index,21,22),
                    Mom_Strat(0.4, c.risk_free_asset, c.ma_threshold, c.index, 1, 2) ,
                    ETF_Strat(0.05,c.risk_free_asset,c.ma_threshold,symbol('IYR'),1, 2),
                    ETF_Strat(0.05,c.risk_free_asset,c.ma_threshold,symbol('GLD'),1,2),
                    ETF_Strat(0.1,c.risk_free_asset,c.ma_threshold,symbol('IEF'),1,2)]
    '''
    #Mom only
    c.strategies = [Mom_Strat(1.0, c.risk_free_asset, c.ma_threshold, c.index, 1, 2) ]
    #Val only
    #c.strategies = [Value_Strat(1.0,c.risk_free_asset, c.ma_threshold, c.index,11,13) ]
    #Mom/Val 50/50
    #c.strategies = [Value_Strat(0.5,c.risk_free_asset, c.ma_threshold, c.index,21,22),
    #                Mom_Strat(0.5, c.risk_free_asset, c.ma_threshold, c.index, 1, 2) ]
    
    #show parameters of all strategies
    show_params(c)
    
    for strat in c.strategies:
        schedule_function(strat.set_sell_day,date_rules.month_start(days_offset=strat.sell_offset),
            time_rules.market_open(minutes=120)) 
        schedule_function(strat.set_buy_day,date_rules.month_start(days_offset=strat.buy_offset),
            time_rules.market_open(minutes=120)) 
    
    #as a last step check final state of portfolio. In particular check total value of unfullfilled orders
    schedule_function(check_positions,date_rules.month_end(days_offset=0),
                      time_rules.market_open(minutes=240))
    

def check_positions(context,data):

    today = get_datetime()
    
    if today.year == context.lastYear and today.month == context.lastMonth:
        #log current account
        log.info(" account leverage: " + str(context.account.leverage))
        log.info(" portfolio value: " + str(context.portfolio.portfolio_value))
        log.info(" portfolio cash: " + str(context.portfolio.cash))
        

        #check number and value of unfullfilled trades
        # unfullfilled trades are often the cause of a strategy appearing to use leverage when in reality it would not.
        # often the cause of an unfullfilled trade is a company merging or getting bought out
        
        open_orders = get_open_orders()
        
        c = 0
        tot_val = 0
        for s in context.portfolio.positions:
            if s in open_orders and s.end_date.year < context.lastYear and s.end_date.month < context.lastMonth:
                shrs = context.portfolio.positions[s].amount
                #cost = context.portfolio.positions[s].cost_basis
                current = data[s].price
                c += 1
                tot_val +=  (shrs * current)
        
        log.info(" len open orders: " + str(len(open_orders)))
        log.info(" number of unfullfilled trades: " + str(c) + " value: " + str(tot_val))
    

def show_params(c):
    
    log.info("************************")
    log.info(" Algo params ")
    log.info("************************")
    
    for strat in c.strategies:
        strat.show_params(c)
   
    
def before_trading_start(context,data): 
    for strat in context.strategies:
        strat.before_trading_start(context, data)

            
def handle_data(context, data):

    #Call each individual algorithm's handle_data method.
    for strat in context.strategies:
        strat.handle_data(context, data)
    
    record(leverage = context.account.leverage)
    
There was a runtime error.

PvR 146%

As per Wes Gray's recommendation I reran the Frog algo and allowed it to choose the top 50 stocks.

Clone Algorithm
304
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
##########################################################################################
# This algorithm integrates the Frog in the Pan strategy discussed on the Alpha Architect site in this post,
#    http://blog.alphaarchitect.com/2015/11/23/frog-in-the-pan-identifying-the-highest-quality-momentum-stocks/
# with the Robust Asset Allocation model discsussed here,
#    http://blog.alphaarchitect.com/2014/12/02/the-robust-asset-allocation-raa-solution/
###########################################################################################

from datetime import timedelta
from sqlalchemy import or_
from scipy import stats
import statsmodels.api as sm
import talib 
import pandas as pd
import numpy as np
import time


#Base Class for all strategies
class Strategy(object):
    
    def __init__(self, pct_alloc, risk_free_asset, ma_threshold,sell_offset, buy_offset):
        self.alloc = pct_alloc                 #percent of portfolio to allocate to strategy
        self.risk_free_asset = risk_free_asset #risk free asset
        self.ma_threshold = ma_threshold       #moving average theshold
        self.sell_offset = sell_offset         #offset for scheduling when strategy will sell
        self.buy_offset = buy_offset           #offset for scheduling when strategy will buy
        self.sell_day = False                  
        self.buy_day = False    
        self.bought = []                       #maintain list of securites that were bought by strategy
        self.lookback = 252                    #set default lookback period to 1 year (252 days)
 

    def getAlloc(self):
        return self.alloc

    
    def setAlloc(self, alloc):
        self.alloc = alloc
    
    
    #Implement robust allocation algo for strategy
    def robust(self,context,data):
        raise NotImplementedError("implement robust method in Subclass")
    
    
    #Show relevant params at beginning of execution
    def show_params(self):
        raise NotImplementedError("implement show_params method in Subclass")
    
    
    #Implement fundamental queries here
    def handle_data(self,context,data):
        raise NotImplementedError("implement handle_data method in Subclass")
    
    
    #Implement buy, sell and ranking functions here
    def before_trading_start(self,context,data): 
        raise NotImplementedError("implement before_trading_start method in Subclass")

        
#Base class for Equity Strategies
class Equity_Strat(Strategy):
    
    def __init__(self, pct_alloc,risk_free_asset,ma_threshold, index, sell_offset, buy_offset):
        Strategy.__init__(self,pct_alloc,risk_free_asset,ma_threshold,sell_offset, buy_offset)
        
        self.index = index #set 

        
    def robust(self,context,data):
        r = 0.0     
        
        ex_return = self.get_excess_return(context,data)
        if ex_return > 0.0:
            r += 0.5
        if data[self.index].price > data[self.index].mavg(self.ma_threshold):
            r += 0.5
        
        return r
    
    
    def get_excess_return(self,context,data):
        h = history( self.lookback,'1d','price')[[self.index,self.risk_free_asset]]
        h = h.resample('M',how='last')

        pct_change =h.iloc[[0,-1]].pct_change()[1:]
        pct_change = pct_change.dropna(axis=1)
        
        ex_return = pct_change[self.index]-pct_change[self.risk_free_asset]
        
        return ex_return[0]


#Momentum Strategy
class Mom_Strat(Equity_Strat):
   
    def __init__(self, pct_alloc, risk_free_asset, ma_threshold, index, sell_offset, buy_offset):
        Equity_Strat.__init__(self,pct_alloc,risk_free_asset,ma_threshold,index,sell_offset, buy_offset)
        
        self.lower_market_cap = 2000e6     #lower market cap for candidate selection
        self.limit = 500                   #max number of stocks to select for fundamental query
        self.num_positions = 50            #max number of positions the strategy will select
        
        
    def set_sell_day(self,context,data):
        self.sell_day = True
        
    
    def set_buy_day(self,context,data):
        self.buy_day = True
        
   
    def before_trading_start(self,context,data): 
        
        if self.buy_day:
            self.get_candidates()
            update_universe(self.candidates)
            
        
    def handle_data(self,context,data):
        if self.sell_day:
            self.exec_sell(context,data)
            self.sell_day = False
        
        if self.buy_day:
            self.exec_rank(context, data)
            self.exec_buy(context,data)
            self.buy_day = False

        
    def get_candidates(self):
        df_fundamentals = get_fundamentals(
            query(
                fundamentals.valuation.shares_outstanding,
                fundamentals.valuation.market_cap
            )
            #.filter(fundamentals.company_reference.country_id == "USA")
            .filter(or_(fundamentals.company_reference.primary_exchange_id == "NAS", fundamentals.company_reference.primary_exchange_id == "NYSE"))
            .filter(fundamentals.valuation.market_cap != None)
            .filter(fundamentals.valuation.market_cap > self.lower_market_cap ) 
            .filter(fundamentals.valuation.shares_outstanding != None)
            #.filter(fundamentals.share_class_reference.is_depositary_receipt == False)
            .filter(fundamentals.share_class_reference.is_primary_share == True)
            .filter(fundamentals.company_reference.primary_exchange_id != "OTCPK")
            .order_by(fundamentals.valuation.market_cap.desc())
            .limit(self.limit)
        )
    
        self.candidates=df_fundamentals.columns
   
    def get_winners(self,context,data):
        #get TMOM
        h = history( self.lookback,'1d','price')[self.candidates]
        h = h.resample('M',how='last')
        #drop first row because it is nan
        pct_change =h.iloc[[0,-1]].pct_change()[1:]
        #drop any other nan values
        self.pct_change = pct_change.dropna(axis=1)
        #convert dataframe to series for sorting. Then sort in asending order
        self.winners =  self.pct_change.squeeze().order(ascending=False)
        self.winners = self.winners[self.winners > 0.0]
        
        #select top 20% of winners
        num_winners = 100
        self.winners = self.winners[:num_winners]
    
    
    #calc Frog In Pan metric based on percent up and down days
    def get_fip_rank(self,context,data):
        #get TMOM
        h = history( self.lookback,'1d','price')[self.winners.index]
        #drop first row because it is nan
        daily_pct_change =h.pct_change()[1:]
        daily_pct_change = daily_pct_change.dropna(axis=1)
        # count positives
        pos = daily_pct_change[ daily_pct_change > 0 ].count(axis=0)
        neg = daily_pct_change[ daily_pct_change < 0 ].count(axis=0)
        tot = daily_pct_change.count(axis=0)
        
        #ID
        self.fip =  (neg - pos)/tot
        #IDz
        #self.fip =  (neg - pos)/(neg+pos)
        
        
    def exec_rank(self,context,data):
        
        self.get_winners(context,data)
        self.get_fip_rank(context,data)
        
        self.longs = self.fip[self.fip < 0.0]
        self.longs = self.longs[:self.num_positions]
 
    def exec_buy(self,context,data):
        r= self.robust(context,data)
        open_orders = get_open_orders()
            
        l = len(self.longs)
        w = (r*self.alloc)/l if l > 0 else 0 #robust adjusted weight
        
        for s in self.longs.index:
            if s in data and s not in open_orders:
                order_target_percent(s,w)
                if w > 0.0:
                    self.bought.append(s)
        
        log.info(str(type(self).__name__) + " longs list len " + str(len(self.longs)))
        log.info(str(type(self).__name__) + " bought list len: " + str(len(self.bought)))    
    
    def exec_sell(self,context,data):

        open_orders = get_open_orders()
        
        for s in context.portfolio.positions :
            if s in data and s not in open_orders :
                if s in self.bought:
                    order_target_percent(s,0)
                    self.bought.remove(s)
                
        log.info(str(type(self).__name__) + " bought list len: " + str(len(self.bought)))
        
    
    
    def show_params(self,c):
        log.info("************************")
        log.info(" Momentum params ")
        log.info("************************")
        log.info(" lower market cap: " + str(self.lower_market_cap))
        log.info(" stock search limit: " + str(self.limit))
        log.info(" risk free asset: " + str(self.risk_free_asset.symbol))
        log.info(" number of positions: " + str(self.num_positions))
        log.info(" moving average threshold: " + str(self.ma_threshold))
        log.info(" pct alloc: " + str(self.alloc))
        log.info(" sell_offset: " + str(self.sell_offset))
        log.info(" buy_offset: " + str(self.buy_offset))
        log.info(" lookback: " + str(self.lookback))
        

def initialize(context):
    set_commission(commission.PerTrade(cost=0.00))  
    set_slippage(slippage.FixedSlippage(spread=0.00))
    #Common vars
    c = context
    c.lastYear = 2015
    c.lastMonth = 12
    c.index = symbol('SPY')
    c.risk_free_asset = symbol('SHY')
    c.ma_threshold = 200
    
    #register Strategies
    #Mom only
    c.strategies = [Mom_Strat(1.0, c.risk_free_asset, c.ma_threshold, c.index, 1, 2) ]
    
    #show parameters of all strategies
    show_params(c)
    
    for strat in c.strategies:
        schedule_function(strat.set_sell_day,date_rules.month_start(days_offset=strat.sell_offset),
            time_rules.market_open(minutes=120)) 
        schedule_function(strat.set_buy_day,date_rules.month_start(days_offset=strat.buy_offset),
            time_rules.market_open(minutes=120)) 
    
    #as a last step check final state of portfolio. In particular check total value of unfullfilled orders
    schedule_function(check_positions,date_rules.month_end(days_offset=0),
                      time_rules.market_open(minutes=240))
    

def check_positions(context,data):

    today = get_datetime()
    
    if today.year == context.lastYear and today.month == context.lastMonth:
        #log current account
        log.info(" account leverage: " + str(context.account.leverage))
        log.info(" portfolio value: " + str(context.portfolio.portfolio_value))
        log.info(" portfolio cash: " + str(context.portfolio.cash))
        

        #check number and value of unfullfilled trades
        # unfullfilled trades are often the cause of a strategy appearing to use leverage when in reality it would not.
        # often the cause of an unfullfilled trade is a company merging or getting bought out
        
        open_orders = get_open_orders()
        
        c = 0
        tot_val = 0
        for s in context.portfolio.positions:
            if s in open_orders and s.end_date.year < context.lastYear and s.end_date.month < context.lastMonth:
                shrs = context.portfolio.positions[s].amount
                #cost = context.portfolio.positions[s].cost_basis
                current = data[s].price
                c += 1
                tot_val +=  (shrs * current)
        
        log.info(" len open orders: " + str(len(open_orders)))
        log.info(" number of unfullfilled trades: " + str(c) + " value: " + str(tot_val))
    

def show_params(c):
    
    log.info("************************")
    log.info(" Algo params ")
    log.info("************************")
    
    for strat in c.strategies:
        strat.show_params(c)
   
    
def before_trading_start(context,data): 
    for strat in context.strategies:
        strat.before_trading_start(context, data)

            
def handle_data(context, data):

    #Call each individual algorithm's handle_data method.
    for strat in context.strategies:
        strat.handle_data(context, data)
    
    record(leverage = context.account.leverage)
    
There was a runtime error.

Here are the results of the simple 12-month lookback with the cutoff set to 50 stocks. Again, the FIP algo, doesn't seem to add a lot of value.

Clone Algorithm
467
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
##########################################################################################
# This is an attempt to implement the Robust Asset Allocation (RAA) strategy described here,
#    http://blog.alphaarchitect.com/2014/12/02/the-robust-asset-allocation-raa-solution/
# and described in detail in 'DIY Financial Advisor' by W Gray, J Vogel, D Foulke
#
# Notes on implementation
#    - all strategies are required to implement a robust asset allocation function
#    - as a simplification the framework assumes all existing assets in a strategy are sold before new assets are ranked and then bought
#    - the implementation ensures that strategies that do fundamental queries are executed on distinct days so that a maximum number of securities relevant to the particular strategy are examined (i.e. the 500 securities in 'data' are selected by the specific strategy and there is no overlap with other strategies)
#    -Since stocks in the Q universe are generally restricted to those that trade on a US exchange, no distinct allocation is made to domestic and international equities, rather fundamental queries are allowed to invest in ADRs, and in securities not necessarily domiciled in the US
#     -a Gold etf (GSG) is used to approximate an allocation to commodities. This is done to allow for a longer backtest.
#    - the Value strategy implemented is the Magic formula
#    - the momentum strategy is Time Series Momentum with a 12 month lookback
#    - the strategy simply goes to cash when out of the market instead of investing in treasuries. An improvement to the framework would be to add a Portfolio Manager class to manage all trades from all strategies and put any cash in an alternative
###########################################################################################

from datetime import timedelta
from sqlalchemy import or_
from scipy import stats
import statsmodels.api as sm
import talib 
import pandas as pd
import numpy as np
import time


#Base Class for all strategies
class Strategy(object):
    
    def __init__(self, pct_alloc, risk_free_asset, ma_threshold,sell_offset, buy_offset):
        self.alloc = pct_alloc                 #percent of portfolio to allocate to strategy
        self.risk_free_asset = risk_free_asset #risk free asset
        self.ma_threshold = ma_threshold       #moving average theshold
        self.sell_offset = sell_offset         #offset for scheduling when strategy will sell
        self.buy_offset = buy_offset           #offset for scheduling when strategy will buy
        self.sell_day = False                  
        self.buy_day = False    
        self.bought = []                       #maintain list of securites that were bought by strategy
        self.lookback = 252                    #set default lookback period to 1 year (252 days)
 

    def getAlloc(self):
        return self.alloc

    
    def setAlloc(self, alloc):
        self.alloc = alloc
    
    
    #Implement robust allocation algo for strategy
    def robust(self,context,data):
        raise NotImplementedError("implement robust method in Subclass")
    
    
    #Show relevant params at beginning of execution
    def show_params(self):
        raise NotImplementedError("implement show_params method in Subclass")
    
    
    #Implement fundamental queries here
    def handle_data(self,context,data):
        raise NotImplementedError("implement handle_data method in Subclass")
    
    
    #Implement buy, sell and ranking functions here
    def before_trading_start(self,context,data): 
        raise NotImplementedError("implement before_trading_start method in Subclass")
    
    
#Class for ETF strategy   - currently limited to holding 1 ETF
class ETF_Strat(Strategy):
    
    def __init__(self, pct_alloc, risk_free_asset, ma_threshold, etf, sell_offset, buy_offset):
        Strategy.__init__(self,pct_alloc,risk_free_asset,ma_threshold,sell_offset, buy_offset)
        
        self.etf = etf    #set ETF for strategy

        
    #calculate excess return over risk free asset    
    def get_excess_return(self,context,data):
        
        h = history( self.lookback,'1d','price')[[self.etf,self.risk_free_asset]]
        h = h.resample('M',how='last')
        
        pct_change =h.iloc[[0,-1]].pct_change()[1:]
        
        pct_change = pct_change.dropna(axis=1)
        
        ex_return = pct_change[self.etf]-pct_change[self.risk_free_asset]
        return ex_return[0]

    
    #implement RAA strategy 
    def robust(self,context,data):
        r = 0.0
    
        #if etf time series MOM returns are greater than risk free asset then invest 1/2 weight if etf price > moving avg then invest 1/2 weight. If both then invest full weight
        ex_return = self.get_excess_return(context,data)
        if ex_return > 0.0:
            r += 0.5
        if data[self.etf].price > data[self.etf].mavg(self.ma_threshold):
            r += 0.5

        return r
    
    
    #buy etf based on RAA allocation    
    def exec_buy(self,context,data):
        open_orders = get_open_orders()
        r = self.robust(context,data)
        w = r * self.alloc #RAA adjusted weight
        
        if self.etf in data and self.etf not in open_orders and w > 0.0:
            order_target_percent(self.etf,w)
            self.bought.append(self.etf)
       
        log.info(str(type(self).__name__) + " bought " + str(self.etf.symbol) + " w=" + str(w))

        
    #sell etf
    def exec_sell(self,context,data):
        open_orders = get_open_orders()
        
        #if self.etf in context.portfolio.positions and self.etf in data and self.etf in self.bought and self.etf not in open_orders:
        if self.etf in context.portfolio.positions and self.etf in self.bought and self.etf not in open_orders:
            order_target_percent(self.etf,0)
            self.bought.remove(self.etf)
        
    
    def set_sell_day(self,context,data):
        self.sell_day = True
        
    
    def set_buy_day(self,context,data):
        self.buy_day = True
        
        
    def handle_data(self,context,data):
        if self.sell_day:
            self.exec_sell(context,data)
            self.sell_day = False
        
        if self.buy_day:
            self.exec_buy(context,data)
            self.buy_day = False
        

    def before_trading_start(self,context,data): 
        pass
    
    
    def show_params(self,c):
        log.info("************************")
        log.info(" ETF params ")
        log.info("************************")
        log.info(" etf name: " + str(self.etf.symbol))
        log.info(" risk free asset: " + str(self.risk_free_asset.symbol))
        log.info(" market ma threshold: " + str(self.ma_threshold))
        log.info(" lookback: " + str(self.lookback))
        log.info(" pct alloc: " + str(self.alloc))
        log.info(" sell_offset: " + str(self.sell_offset))
        log.info(" buy_offset: " + str(self.buy_offset))

        
#Base class for Equity Strategies
class Equity_Strat(Strategy):
    
    def __init__(self, pct_alloc,risk_free_asset,ma_threshold, index, sell_offset, buy_offset):
        Strategy.__init__(self,pct_alloc,risk_free_asset,ma_threshold,sell_offset, buy_offset)
        
        self.index = index #set 

        
    def robust(self,context,data):
        r = 0.0     
        
        ex_return = self.get_excess_return(context,data)
        if ex_return > 0.0:
            r += 0.5
        if data[self.index].price > data[self.index].mavg(self.ma_threshold):
            r += 0.5
        
        return r
    
    
    def get_excess_return(self,context,data):
        h = history( self.lookback,'1d','price')[[self.index,self.risk_free_asset]]
        h = h.resample('M',how='last')

        pct_change =h.iloc[[0,-1]].pct_change()[1:]
        pct_change = pct_change.dropna(axis=1)
        
        ex_return = pct_change[self.index]-pct_change[self.risk_free_asset]
        
        return ex_return[0]


#Momentum Strategy
# -- Sort equities on best 1 year performance
class Mom_Strat(Equity_Strat):
   
    def __init__(self, pct_alloc, risk_free_asset, ma_threshold, index, sell_offset, buy_offset):
        Equity_Strat.__init__(self,pct_alloc,risk_free_asset,ma_threshold,index,sell_offset, buy_offset)
        
        self.lower_market_cap = 2000e6     #lower market cap for candidate selection
        self.limit = 500                   #max number of stocks to select for fundamental query
        self.num_positions = 50           #max number of positions the strategy will select
        
        
    def set_sell_day(self,context,data):
        self.sell_day = True
        
    
    def set_buy_day(self,context,data):
        self.buy_day = True
        
   
    def before_trading_start(self,context,data): 
        
        if self.buy_day:
            self.get_candidates()
            update_universe(self.candidates)
            
        
    def handle_data(self,context,data):
        if self.sell_day:
            self.exec_sell(context,data)
            self.sell_day = False
        
        if self.buy_day:
            self.exec_rank(context, data)
            self.exec_buy(context,data)
            self.buy_day = False

        
    def get_candidates(self):
        df_fundamentals = get_fundamentals(
            query(
                fundamentals.valuation.shares_outstanding,
                fundamentals.valuation.market_cap
            )
            #.filter(fundamentals.company_reference.country_id == "USA")
            .filter(or_(fundamentals.company_reference.primary_exchange_id == "NAS", fundamentals.company_reference.primary_exchange_id == "NYSE"))
            .filter(fundamentals.valuation.market_cap != None)
            .filter(fundamentals.valuation.market_cap > self.lower_market_cap ) 
            .filter(fundamentals.valuation.shares_outstanding != None)
            #.filter(fundamentals.share_class_reference.is_depositary_receipt == False)
            .filter(fundamentals.share_class_reference.is_primary_share == True)
            .filter(fundamentals.company_reference.primary_exchange_id != "OTCPK")
            .order_by(fundamentals.valuation.market_cap.desc())
            .limit(self.limit)
        )
    
        self.candidates=df_fundamentals.columns
   
    
    def exec_rank(self,context,data):
        
        #get TMOM
        h = history( self.lookback,'1d','price')[self.candidates]
        h = h.resample('M',how='last') #get monthly returns
        
        pct_change =h.iloc[[0,-1]].pct_change()[1:]
        pct_change = pct_change.dropna(axis=1)
        
        #convert dataframe to series for sorting. Then sort in descending order
        self.longs =  pct_change.squeeze().order(ascending=False)
        self.longs = self.longs[self.longs > 0.0]
        self.longs = self.longs[:self.num_positions]
    
 
    def exec_buy(self,context,data):
        r= self.robust(context,data)
        open_orders = get_open_orders()
            
        l = len(self.longs)
        w = (r*self.alloc)/l if l > 0 else 0 #robust adjusted weight
        
        for s in self.longs.index:
            if s in data and s not in open_orders:
                order_target_percent(s,w)
                if w > 0.0:
                    self.bought.append(s)
        
        log.info(str(type(self).__name__) + " longs list len " + str(len(self.longs)))
        log.info(str(type(self).__name__) + " bought list len: " + str(len(self.bought)))    
    
    def exec_sell(self,context,data):

        open_orders = get_open_orders()
        
        for s in context.portfolio.positions :
            if s in data and s not in open_orders :
                if s in self.bought:
                    order_target_percent(s,0)
                    self.bought.remove(s)
                
        log.info(str(type(self).__name__) + " bought list len: " + str(len(self.bought)))
        
    
    
    def show_params(self,c):
        log.info("************************")
        log.info(" Momentum params ")
        log.info("************************")
        log.info(" lower market cap: " + str(self.lower_market_cap))
        log.info(" stock search limit: " + str(self.limit))
        log.info(" risk free asset: " + str(self.risk_free_asset.symbol))
        log.info(" number of positions: " + str(self.num_positions))
        log.info(" moving average threshold: " + str(self.ma_threshold))
        log.info(" pct alloc: " + str(self.alloc))
        log.info(" sell_offset: " + str(self.sell_offset))
        log.info(" buy_offset: " + str(self.buy_offset))
        log.info(" lookback: " + str(self.lookback))
       
        

class Value_Strat(Equity_Strat):
    
    def __init__(self, pct_alloc, risk_free_asset, ma_threshold, index, sell_offset, buy_offset):
        
        Equity_Strat.__init__(self,pct_alloc,risk_free_asset,ma_threshold,index,sell_offset, buy_offset)
    
        self.lower_market_cap = 2000e6
        self.limit = 500
        self.num_positions = 30
        self.rebalance_month = 12 #June
        self.longs = []
        #self.alloc = pct_alloc
        #self.w = self.pct_alloc/self.num_positions

    
    def set_sell_day(self,context,data):
         today = get_datetime()
         if today.month == self.rebalance_month:
             self.sell_day = True
        
    
    def set_buy_day(self,context,data):
        today = get_datetime()
        if today.month == self.rebalance_month:
            self.buy_day = True
        
   
    def before_trading_start(self,context,data): 
        
        if self.sell_day:
            update_universe(self.bought)

        if self.buy_day:
            self.get_candidates()
            update_universe(self.candidates)
    
    
    def handle_data(self,context,data):
        
        if self.sell_day:
            self.exec_sell(context,data)
            self.sell_day = False
        
        if self.buy_day:
            self.exec_rank(context,data)
            self.exec_buy(context,data)
            self.buy_day = False
 
    
    def get_candidates(self):
        excluded_sectors = [103, 207]
        sector_code = fundamentals.asset_classification.morningstar_sector_code
        fundamental_df = get_fundamentals(
            query(
                sector_code,
                fundamentals.valuation.market_cap,
                fundamentals.valuation.enterprise_value,
                fundamentals.cash_flow_statement.capital_expenditure,
                fundamentals.operation_ratios.roic,
                fundamentals.income_statement.ebit,
                fundamentals.income_statement.ebitda,
                fundamentals.balance_sheet.total_assets,            
        )
        #.filter(fundamentals.company_reference.country_id == "USA")
        .filter(or_(fundamentals.company_reference.primary_exchange_id == "NAS", fundamentals.company_reference.primary_exchange_id == "NYSE"))
        .filter(fundamentals.share_class_reference.is_primary_share == True)
        .filter(fundamentals.company_reference.primary_exchange_id != "OTCPK")
        #.filter(fundamentals.share_class_reference.is_depositary_receipt == False)
        .filter(~sector_code.in_(excluded_sectors))
        .filter(fundamentals.valuation.market_cap > self.lower_market_cap ) 
        .filter(fundamentals.valuation_ratios.ev_to_ebitda > 0)
        .order_by(fundamentals.valuation.market_cap.desc())
        .limit(self.limit)
        )
        
        self.candidates = fundamental_df
        
        
    def exec_rank(self,context,data):
    
        #get MF components
        earnings_yield = self.candidates.ix['ebit'] / self.candidates.ix['enterprise_value']
        roic = self.candidates.ix['roic'].copy()
    
        #sort MF components
        earnings_yield.sort(ascending=False)
        roic.sort(ascending=False)

        #get ranks for components
        earnings_yield_ranking = pd.Series(range(len(earnings_yield)), index=earnings_yield.index)
        roic_ranking = pd.Series(range(len(roic)), index=roic.index) 
    
        #get combined ranking - ranks = earnings_yield_ranking + roic_ranking
        ranks = {}
        for s in self.candidates.columns:
            v_rank = earnings_yield_ranking.loc[s.sid]
            q_rank = roic_ranking.loc[s.sid]
            ranks[s] = v_rank+q_rank
    
        #convert to df and sort
        mf_ranks = pd.Series(ranks,name='Symbol')
        mf_ranks.sort(ascending = True)
        
        self.longs = mf_ranks.index.tolist()
        self.longs = self.longs[:self.num_positions]

     
    def exec_buy(self,context,data):
        
        r= self.robust(context,data)
        open_orders = get_open_orders()
        
        l = len(self.longs)
        w = (r*self.alloc)/self.num_positions if l > 0 else 0
            
        for s in self.longs:
            if s in data and s not in open_orders:
                order_target_percent(s,w)
                if w > 0.0:
                    self.bought.append(s)
            
        log.info(str(type(self).__name__) + " longs list len " + str(len(self.longs)))
        log.info(str(type(self).__name__) + " bought " + str(len(self.bought)) + " stocks")

        
    def exec_sell(self,context,data):
        open_orders = get_open_orders()
        
        for s in context.portfolio.positions:
            if s in data and s in self.bought and s not in open_orders:
                order_target_percent(s,0)
                self.bought.remove(s)
            
        log.info(str(type(self).__name__) + " bought list len: " + str(len(self.bought)))

        
    def show_params(self,c):
        log.info("************************")
        log.info(" Value params ")
        log.info("************************")
        log.info(" lower market cap: " + str(self.lower_market_cap))
        log.info(" stock search limit: " + str(self.limit))
        log.info(" risk free asset: " + str(self.risk_free_asset.symbol))
        log.info(" number of positions: " + str(self.num_positions))
        log.info(" moving average threshold: " + str(self.ma_threshold))
        log.info(" pct alloc: " + str(self.alloc))
        log.info(" sell_offset: " + str(self.sell_offset))
        log.info(" buy_offset: " + str(self.buy_offset))        
        log.info(" rebalance month: " + str(self.rebalance_month))

        

def initialize(context):
    set_commission(commission.PerTrade(cost=0.00))  
    set_slippage(slippage.FixedSlippage(spread=0.00))
    #Common vars
    c = context
    c.lastYear = 2015
    c.lastMonth = 12
    c.index = symbol('SPY')
    c.risk_free_asset = symbol('SHY')
    c.ma_threshold = 200
    
    #register Strategies
    #IVY_4 allocations - no allocation to world equities
    # replace GSG with Gold to get longer backtest
    '''
    c.strategies = [ETF_Strat(0.25,c.risk_free_asset,c.ma_threshold,symbol('SPY'),1,2),
                    ETF_Strat(0.25,c.risk_free_asset,c.ma_threshold,symbol('IYR'),1,2),
                    ETF_Strat(0.25,c.risk_free_asset,c.ma_threshold,symbol('GLD'),1,2),
                    ETF_Strat(0.25,c.risk_free_asset,c.ma_threshold,symbol('IEF'),1,2)]
    '''
   
    #RAA Balanced - 40% allocation to equities
    '''
    c.strategies = [Value_Strat(0.2,c.risk_free_asset, c.ma_threshold, c.index,11,13),
                    Mom_Strat(0.2, c.risk_free_asset, c.ma_threshold, c.index, 1, 2) ,
                    ETF_Strat(0.2,c.risk_free_asset,c.ma_threshold,symbol('IYR'),1,2),
                    ETF_Strat(0.2,c.risk_free_asset,c.ma_threshold,symbol('GLD'),1,2),
                    ETF_Strat(0.2,c.risk_free_asset,c.ma_threshold,symbol('IEF'),1,2)]
    '''    
    
    #RAA Moderate Tilt - 60% allocation to equities
    '''
    c.strategies = [Value_Strat(0.3,c.risk_free_asset, c.ma_threshold, c.index,11,13),
                    Mom_Strat(0.3, c.risk_free_asset, c.ma_threshold, c.index, 1, 2) ,
                    ETF_Strat(0.1,c.risk_free_asset,c.ma_threshold,symbol('IYR'),1,2),
                    ETF_Strat(0.1,c.risk_free_asset,c.ma_threshold,symbol('GLD'),1,2),
                    ETF_Strat(0.2,c.risk_free_asset,c.ma_threshold,symbol('IEF'),1,2)]
    '''
        
    
    #RAA Aggressive Tilt - 80% allocation to equities
    '''
    c.strategies = [Value_Strat(0.4,c.risk_free_asset, c.ma_threshold, c.index,21,22),
                    Mom_Strat(0.4, c.risk_free_asset, c.ma_threshold, c.index, 1, 2) ,
                    ETF_Strat(0.05,c.risk_free_asset,c.ma_threshold,symbol('IYR'),1, 2),
                    ETF_Strat(0.05,c.risk_free_asset,c.ma_threshold,symbol('GLD'),1,2),
                    ETF_Strat(0.1,c.risk_free_asset,c.ma_threshold,symbol('IEF'),1,2)]
    '''
    #Mom only
    c.strategies = [Mom_Strat(1.0, c.risk_free_asset, c.ma_threshold, c.index, 1, 2) ]
    #Val only
    #c.strategies = [Value_Strat(1.0,c.risk_free_asset, c.ma_threshold, c.index,11,13) ]
    #Mom/Val 50/50
    #c.strategies = [Value_Strat(0.5,c.risk_free_asset, c.ma_threshold, c.index,21,22),
    #                Mom_Strat(0.5, c.risk_free_asset, c.ma_threshold, c.index, 1, 2) ]
    
    #show parameters of all strategies
    show_params(c)
    
    for strat in c.strategies:
        schedule_function(strat.set_sell_day,date_rules.month_start(days_offset=strat.sell_offset),
            time_rules.market_open(minutes=120)) 
        schedule_function(strat.set_buy_day,date_rules.month_start(days_offset=strat.buy_offset),
            time_rules.market_open(minutes=120)) 
    
    #as a last step check final state of portfolio. In particular check total value of unfullfilled orders
    schedule_function(check_positions,date_rules.month_end(days_offset=0),
                      time_rules.market_open(minutes=240))
    

def check_positions(context,data):

    today = get_datetime()
    
    if today.year == context.lastYear and today.month == context.lastMonth:
        #log current account
        log.info(" account leverage: " + str(context.account.leverage))
        log.info(" portfolio value: " + str(context.portfolio.portfolio_value))
        log.info(" portfolio cash: " + str(context.portfolio.cash))
        

        #check number and value of unfullfilled trades
        # unfullfilled trades are often the cause of a strategy appearing to use leverage when in reality it would not.
        # often the cause of an unfullfilled trade is a company merging or getting bought out
        
        open_orders = get_open_orders()
        
        c = 0
        tot_val = 0
        for s in context.portfolio.positions:
            if s in open_orders and s.end_date.year < context.lastYear and s.end_date.month < context.lastMonth:
                shrs = context.portfolio.positions[s].amount
                #cost = context.portfolio.positions[s].cost_basis
                current = data[s].price
                c += 1
                tot_val +=  (shrs * current)
        
        log.info(" len open orders: " + str(len(open_orders)))
        log.info(" number of unfullfilled trades: " + str(c) + " value: " + str(tot_val))
    

def show_params(c):
    
    log.info("************************")
    log.info(" Algo params ")
    log.info("************************")
    
    for strat in c.strategies:
        strat.show_params(c)
   
    
def before_trading_start(context,data): 
    for strat in context.strategies:
        strat.before_trading_start(context, data)

            
def handle_data(context, data):

    #Call each individual algorithm's handle_data method.
    for strat in context.strategies:
        strat.handle_data(context, data)
    
    record(leverage = context.account.leverage)
    
There was a runtime error.

Very interesting share. Attached is the tear-sheet for the first version.

Loading notebook preview...
Notebook previews are currently unavailable.
Disclaimer

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by Quantopian. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. No information contained herein should be regarded as a suggestion to engage in or refrain from any investment-related course of action as none of Quantopian nor any of its affiliates is undertaking to provide investment advice, act as an adviser to any plan or entity subject to the Employee Retirement Income Security Act of 1974, as amended, individual retirement account or individual retirement annuity, or give advice in a fiduciary capacity with respect to the materials presented herein. If you are an individual retirement or other investor, contact your financial advisor or other fiduciary unrelated to Quantopian about whether any given investment idea, strategy, product or service described herein may be appropriate for your circumstances. All investments involve risk, including loss of principal. Quantopian makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances.

The one referred to in the tearsheet with ending chart value of ~277% started with $1M and transacted $2M so ending returns per dollar activated (PvR) are half of what is shown in that original chart.

Mark, I like the class layout of this algorithm....The ability to have multiple strategies running in the same algorithm.

However, this one does not run with minute. I found the issue and had it develope candidates regardless of being a buy day or not, but then it runs out of memory.

Any tips on using this layout to run algorithms with minute data?

Hi John,

If you replace any references to mavg with numpy.mean you should be able to get it to run in minute mode, thought there may be other discrepancies in the algorithm. Have a look at this thread.

Why does the algorithm not run from Feb08-May09, there is no performance data? Thanks.

Its part of the Robust Asset Allocation model. It stopped running because it saw momentum and excess returns drop off. Look for an Article about RAA on Alpha Architect. I think the link is in the comments of the source code

I update the framework here, to work in minute mode.

Regards,
Mark

Is it possible to modify the value strategy to filter for intrinsic value?

A great example is the Chepakovich Valuation Model found here: http://x-fin.com/valuation/chepakovich-valuation-model/

I get an error below when i clone and build this strategy - how do i fix it?

There was a runtime error.
AttributeError: 'Mom_Strat' object has no attribute 'candidates'
... USER ALGORITHM:153, in get_winners
h = history( self.lookback,'1d','price')[self.candidates]