The example I sent wasn't the strategy I was referring to - just a demo to show that the order used Quantopian data and not the fetched Yahoo data. I've attached the strategy FYI, together wit the zipline code. The Quantopian return for the period 1 Jan 2007 - 1 Mar 2014 was ~150% vs ~250% with zipline and Yahoo data.
Here is the zipline code as well, in case you want to check it out:
# inspired by Cliff Smith
# http://seekingalpha.com/article/1928941-new-tactical-asset-allocation-strategy-to-grow-retirement-savings-at-reduced-risk
from zipline.utils import tradingcalendar
from zipline.algorithm import TradingAlgorithm
from zipline.finance.commission import PerShare, PerTrade
from zipline.finance import slippage
from zipline.transforms import MovingAverage
from zipline.utils.factory import load_bars_from_yahoo
from zipline.transforms import batch_transform
import logbook
log = logbook.Logger('Transform', level=1)
import pytz
import datetime as dt
from math import sqrt
import pandas as pd
window = 89
def endpoints(start, end, period='m') :
dates = tradingcalendar.get_trading_days(start, end)
if isinstance(period, int) :
dates = [dates[i] for i in range(0, len(dates), period)]
else :
if period == 'm' : months = 1
elif period == 'q' : months = 3
elif period == 'b' : months = 6
elif period == 'y' : months = 12
monthend_dates = [dates[i - 1] for i in range(1,len(dates))\
if dates[i].month > dates[i-1].month\
or dates[i].year > dates[i-1].year ]+ list([dates[-1]])
dates = [monthend_dates[i] for i in range(0,len(monthend_dates),months)]
return dates
@batch_transform
def get_past_prices(data):
prices = data['price']
return prices
class AllAssetsExceptBonds(TradingAlgorithm):
def initialize(self):
log.info('Initializing.......')
self.get_past_prices = get_past_prices(window_length=window)
self.add_transform(MovingAverage, 'mavg', ['price'],
market_aware=True,
window_length=window)
# STOCKS = [ 'SHY', 'CSD', 'IJR', 'MDY', 'PBE', 'GURU', 'EFA', 'EPP', 'EWA', 'IEV', \
# 'ADRE', 'DEM', 'RWO', 'RWX', 'GLD']
self.stocks = STOCKS
self.top_n = 2
# Use the fixed slippage model, which will just add/subtract a specified spread
# spread/2 will be added on buys and subtracted on sells per share
# commission will be charged per trade
self.slippage = slippage.FixedSlippage(spread=0.0)
self.commission = PerTrade(10.0)
# want to rebalance on the last trading day of the month
# because of the event-driven nature of zipline, this means
# that we need to apply the ranking formula a day earlier
start = dt.datetime(2007,1,1, 0, 0, 0, 0, pytz.utc)
end = dt.datetime(2014,3,1, 0, 0, 0, 0, pytz.utc)
self.event_dates = endpoints(start, end, period='q' )
# log.info('Finished Initializing.......')
def handle_data(self, data):
hist = self.get_past_prices.handle_data(data)
#circuit breaker in case transform returns none
if hist is None:
return
# log.info('*********************Handle Data')
#circuit breaker, only calculate on 2nd last trading day of month
if self.get_datetime() in self.event_dates:
log.info('\n\nDATE\n\n %s' % self.get_datetime())
# for debugging
for security in self.stocks:
if self.portfolio.positions[security].amount > 0:
log.debug ('{} {}'.format([self.portfolio.positions[security][key]\
for key in [ 'sid', 'amount', 'cost_basis']],\
self.portfolio.portfolio_value))
# calculate strategy data
d_returns = hist / hist.shift(1) - 1
perf_88 = (hist.ix[-1] / hist.ix[0] - 1).fillna(0).replace(inf, 0)
log.info('\n\nPRICE\n\n %s' % hist[:5])
log.info('\n\nPERF_88\n\n %s' % perf_88)
vol_88 = d_returns.std() * sqrt(252)
log.info('\n\nVOL_88\n\n %s' % vol_88)
rs = (perf_88.rank() * 0.65 + vol_88.rank(ascending=False) * 0.35).fillna(0)
# log.info('\n\nPERF_88_MAX\n\n %s' % perf_88.max())
rs_modified = rs * 10 + perf_88.max()- perf_88 / perf_88.max()
ranks = rs_modified.rank(ascending=False)
# log.info('\n\nRS\n\n %s' % rs)
# log.info('\n\nRS_MODIFIED\n\n %s' % rs_modified)
# log.info('\n\nRANKS\n\n %s' % ranks)
weights = pd.Series(0., index=ranks.index)
for security in ranks.index:
if ranks[security] <= self.top_n :
# log.info('\n\nTEST SECURITY %s' % security)
# log.info('\n\nPRICE %s' % data[security]['price'])
# log.info('\n\nMAVG %s' % data[security].mavg['price'])
if ranks[security] < ranks['SHY'] and data[security].price >= data[security].mavg['price'] :
# log.info('\n\nADD SECURITY %s' % security)
weights[security] = weights[security] + 1. / self.top_n
else:
# log.info('\n\nREPLACE %s' % security)
weights['SHY'] = weights['SHY'] + 1. / self.top_n # replace security with cash
# log.info('\n\nWEIGHTS\n\n %s' % weights)
for security in weights.index:
if weights[security] == 0. and self.portfolio.positions[security].amount > 0:
log.info('\n\nLIQUIDATE SECURITY %s' % security)
log.info('\n\nCURRENT POSITION\n\n %s' % self.portfolio.positions[security].amount)
self.order_target_percent(security, 0) # close position
elif weights[security] > 0:
log.info('\n\nREBALANCE SECURITY %s' % security)
log.info('\n\nSECURITY WEIGHT%s' % weights[security])
self.order_target_percent(security, weights[security])
#this is just for debugging purposes
for security in weights.index:
if weights[security] > 0:
log.debug('{} {}'.format(security, weights[security]))
start = dt.datetime(2007, 1, 1, 0, 0, 0, 0, pytz.utc)
end = dt.datetime(2014, 3, 1, 0, 0, 0, 0, pytz.utc)
datapath = 'E:\\Temp\\data_all.pkl'
STOCKS = [ 'SHY', 'CSD', 'IJR', 'MDY', 'PBE', 'GURU', 'EFA', 'EPP', 'EWA', 'IEV', \
'ADRE', 'DEM', 'RWO', 'RWX', 'GLD']
Algo = AllAssetsExceptBonds()
#this uses zipline to load data
try :
data = pd.read_pickle(datapath)
except :
data = load_bars_from_yahoo(indexes={}, stocks=STOCKS, start=start, end=end)
data.to_pickle(datapath)
# this uses finlib to load data
#data = get_history(STOCKS, start, end,'G:\\Google Drive\\Python Projects\\PyScripter Projects\\Computational Investing\\Data\\')
#data.major_axis = data.major_axis.tz_localize(pytz.utc)
#data.minor_axis = np.array(['open', 'high', 'low', 'close', 'volume', 'price'], dtype=object)
# check for data problems
for key in data.keys() :
if data[key].first_valid_index() != data[key].index.min() :
print 'WARNING: ', key, 'only has valid data from ', data[key].first_valid_index()
for symbol in STOCKS :
bad_idx = pd.isnull(data[symbol][data[symbol].index >= data[symbol].first_valid_index()]).any(1).nonzero()[0]
print symbol, ': bad data len =', len(bad_idx), '===>', bad_idx
# in most cases, just forward fill
data = data.fillna(method='ffill').fillna(0)
results = Algo.run(data)