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.

305
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.sell_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
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):

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):

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

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

self.exec_rank(context, data)

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]

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(" lookback: " + str(self.lookback))

def initialize(context):
#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))
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)

for strat in context.strategies:

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.

474
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.sell_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
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):

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
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 handle_data(self,context,data):
if self.sell_day:
self.exec_sell(context,data)
self.sell_day = False

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))

#Base class for Equity Strategies
class Equity_Strat(Strategy):

def __init__(self, pct_alloc,risk_free_asset,ma_threshold, index, 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):

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

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

self.exec_rank(context, data)

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]

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(" lookback: " + str(self.lookback))

class Value_Strat(Equity_Strat):

def __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

today = get_datetime()
if today.month == self.rebalance_month:

if self.sell_day:
update_universe(self.bought)

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

self.exec_rank(context,data)

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]

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(" rebalance month: " + str(self.rebalance_month))

def initialize(context):
#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))
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)

for strat in context.strategies:

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.

474
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.sell_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
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):

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
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 handle_data(self,context,data):
if self.sell_day:
self.exec_sell(context,data)
self.sell_day = False

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))

#Base class for Equity Strategies
class Equity_Strat(Strategy):

def __init__(self, pct_alloc,risk_free_asset,ma_threshold, index, 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):

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

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

self.exec_rank(context, data)

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]

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(" lookback: " + str(self.lookback))

class Value_Strat(Equity_Strat):

def __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

today = get_datetime()
if today.month == self.rebalance_month:

if self.sell_day:
update_universe(self.bought)

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

self.exec_rank(context,data)

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]

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(" rebalance month: " + str(self.rebalance_month))

def initialize(context):
#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))
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)

for strat in context.strategies:

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.

305
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.sell_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
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):

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):

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

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

self.exec_rank(context, data)

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]

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(" lookback: " + str(self.lookback))

def initialize(context):
#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))
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)

for strat in context.strategies:

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.

474
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.sell_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
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):

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
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 handle_data(self,context,data):
if self.sell_day:
self.exec_sell(context,data)
self.sell_day = False

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))

#Base class for Equity Strategies
class Equity_Strat(Strategy):

def __init__(self, pct_alloc,risk_free_asset,ma_threshold, index, 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):

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

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

self.exec_rank(context, data)

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]

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(" lookback: " + str(self.lookback))

class Value_Strat(Equity_Strat):

def __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

today = get_datetime()
if today.month == self.rebalance_month:

if self.sell_day:
update_universe(self.bought)

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

self.exec_rank(context,data)

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]

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(" rebalance month: " + str(self.rebalance_month))

def initialize(context):
#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))
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)

for strat in context.strategies:

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.

8
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]