After finally digging into Quantopian I found that many of the algorithms were so profitable thanks to infinite borrowing. After dealing with this in my own code in various ways, I decided to create a standardized template that everyone is welcome to use. Besides limiting your percent invested, another added benefit is that I have initialized a pandas dataframe that stores the closing prices. Therefore you can call pandas pd.stats.moments in your algo, instead of coding something up in the batch_transform. Its really just a few functions that holds everything together
def update_portvals(context):
""" updates account information on demand as opposed to beginning of frame"""
context.total_equity = context.cash + context.portvalue
context.pct_invested = context.portvalue / context.total_equity
context.pct_cash = context.cash / context.total_equity
def generate_order(sym, size, context):
"""circuit breaker 1: enough cash for transaction?"""
if context.cash < size * context.df_prices[sym].ix[-1]:
return
"""deduct price from cash and add to port value"""
context.cash -= size * context.df_prices[sym].ix[-1]
context.portvalue += size * context.df_prices[sym].ix[-1]
"""update account values"""
update_portvals(context)
"""circuit breaker 2: will this transaction set percent invested
greater than .95, before slippage and commissions?"""
if context.pct_invested > .95:
"""if breaker was fliped, reset values to before order called"""
context.cash += size * context.df_prices[sym].ix[-1]
context.portvalue -= size * context.df_prices[sym].ix[-1]
"""update account values"""
update_portvals(context)
return
order(sym, size)
log.info(str(size) + ' ' + str(sym.symbol) + ' at ' + str(context.df_prices[sym].ix[-1]))
def liquidate_position(sym, context):
""" sells off the entire position """
if context.portfolio.positions[sym.sid].amount is not 0:
to_liquidate = -context.portfolio.positions[sym.sid].amount
order(sym, to_liquidate)
log.info(str(to_liquidate) + ' ' + str(sym.symbol) + ' at ' + str(context.df_prices[sym].ix[-1]) )
"""
not updating portvals since transactions will not fill until next bar
this prevents the algo from overdrafting this bar on cash received next
"""
@batch_transform(window_length=1, refresh_period=1)
def update_data(datapanel):
"""" gets most recent bar"""
if len(datapanel.price.index) is 0:
return
return datapanel.price
Look at the backtest source to see how to implement it for yourself. I imagine many of the more sophisticated users out there have already dealt with this in there own way. Nevertheless, hopefully this can provide a stepping stone to get new pythonistas developing their own strategies.