I seem to be running into a TimeoutException in my code below. I am still learning python so I suspect I am doing something inefficiently that is causing the timeout but I can't identify what it is. My before_trading_start method gets a set of 50 stocks based on some fundamentals. If I lower this number to say 5 then I don't run into the TimeoutException, but 50 equities does not see like that large of a number.
I have two methods scheduled to run daily. The first looks at the set of stocks generated from before_trading_start and filters this list down some based on volume criteria. The second method then determines moving average and %R for each stock in the set, based on these numbers it will decide where to buy or sell.
The TimeoutException is thrown from line 123, but using the debugger I found the timeout must really be occurring around line 143 where moving average is calculated. The full code is below with comments on what I believe are the problem areas.
Is there any optimization I can make to avoid this error? Or have I done something else wrong that is causing this?
#
# 03/28/2014 - 08/18/2015 $10,000 Minute
# TimeoutException: Too much time spent in handle_data call
# There was a runtime error on line 123.
import numpy as np
import math
import operator
# Set up Williams %R by ta lib
willer = ta.WILLR(timeperiod=30)
# Put any initialization logic here. The context object will be passed to
# the other methods in your algorithm.
def initialize(context):
# Max shares to buy in a day
# TODO: move this down to day constants
context.max_shares_buy = 5
###
# Daily Constants
###
# Define Daily Trades
context.daily_stocks = []
# Set high loss tolerance since these are volatile stocks
context.d_stop_loss_perc = 0.50
# Buy stock at -1% current price
context.d_limit_buy_perc = 0.99
context.d_percent_of_portfolio = .40
context.d_budget = 0
context.d_min_profit = 2.25
###
# Recommender Constants
###
# Define WILLER Constants
context.LOW_W = -85
context.HIGH_W = -15
context.willer_recommendation_weight = 1
# Define MA Constants
context.fma_days = 50
context.sma_days = 200
context.ma_recommendation_weight = 1
###
# Scheduling Functions
###
# Schedule daily sorting
# Executes every day one minute after open
schedule_function(daily_stocks, date_rule=date_rules.every_day(), time_rule=time_rules.market_open(hours=0, minutes=1))
# Schedule daily buys
# Executes every day 10min after market open
schedule_function(daily_trades_buy, date_rule=date_rules.every_day(), time_rule=time_rules.market_open(hours=0, minutes=10))
# Executed every day before trade start, divvy out cash for the day
def before_trading_start(context):
cash = context.portfolio.cash
context.d_budget = context.d_percent_of_portfolio * cash
# Query for securities based on market_cap size
context.fundamental_df = get_fundamentals(
query(
# market cap
fundamentals.valuation.market_cap,
)
# Filter where market_cap > 10billion == large cap
.filter(fundamentals.valuation.market_cap > 10000000000)
.order_by(fundamentals.valuation.market_cap.desc())
.limit(50)
)
# Set stock results into context
context.fundamental_stocks = context.fundamental_df.columns.values
# Update universe
update_universe(context.fundamental_df.columns.values)
# Will be called on every trade event for the securities you specify.
def handle_data(context, data):
pass
"""
Sorts most recent stocks from fundmaentals and gets the ones with the highest average trading volume in the past 90 days.
"""
def daily_stocks(context, data):
# Filter by Volume past 90 days 1M < stock
avg_vol_stocks = get_avg_volume(90, 1000000)
# Converge
context.daily_stocks = []
for equity in context.fundamental_stocks:
if equity in avg_vol_stocks:
context.daily_stocks.append(equity)
for equity in context.daily_stocks:
print(equity.symbol)
# DAILY
# Daily trades are made every day soon after market open. The trades are made over a
# predefined set of stocks. We will first rank these stocks by WILLER and MA Recommenders.
def daily_trades_buy(context, data):
cash = context.d_budget
# fails here occasionally with a Timeout error
# Go to method below for more info, line 195
sorted_stocks = sort_stocks_by_ma_willer(context, data, context.daily_stocks)
for stockTuple in sorted_stocks:
# do stuff
recommendation = stockTuple[1]
stock = stockTuple[0]
print(stock)
###
# Recommenders
###
"""
Determine if the moving average recommends a buy or a sell for a given stock.
Requires: context, data, and stock
Returns: 1 for a buy and -1 for a sell, 0 for neutral
"""
def determine_ma_recommendation(context, data, stock):
if stock in data:
# calculate fast moving average
# I think it is failing in the mavg method
fast_moving_average = data[stock].mavg(context.fma_days)
# calculate slow moving average
slow_moving_average = data[stock].mavg(context.sma_days)
if (fast_moving_average > slow_moving_average):
return 1
elif (fast_moving_average < slow_moving_average):
return -1
else:
return 0
else: # stock may no longer be selling, recommend sell
return -1
"""
Determine if the Willer %R recommends a buy or a sell.
Requires: context, data, stock
Returns: 1 fora buy and -1 for a sell, 0 for neutral
"""
def determine_willer_recommendation(context, data, stock):
if stock in data:
willer_data = willer(data)
stock_willer = willer_data[stock]
# first 14 days, the william value will be numpy.nan
if not np.isnan(stock_willer):
if stock_willer > context.HIGH_W:
return -1
elif stock_willer < context.LOW_W:
return 1
else:
return 0
else:
return 0
else: # stock is not in data, may no longer be trading, recommend a sell
return -1
###
# Helpers
###
"""
Sorts stocks by best MA and WIlLER recommendations first.
Requires: context, data, list of stocks to sort
Returns: the sorted stocks dictionary, key = stock, value = recommendation
"""
def sort_stocks_by_ma_willer(context, data, stocks):
sorted_stocks = {}
for stock in stocks:
if stock in data:
# MA Recommendation
# Seems to fail inside this method. line 139
ma_recommendation = determine_ma_recommendation(context, data, stock)
# WILLER Recommendation
willer_recommendation = determine_willer_recommendation(context, data, stock)
# Determine Recommendation
recommendation = (ma_recommendation * context.ma_recommendation_weight) + (willer_recommendation * context.willer_recommendation_weight)
sorted_stocks[stock] = recommendation
sorted_stocks = sorted(sorted_stocks.items(), key = operator.itemgetter(1))
return sorted_stocks
"""
Gets the average volume for all stocks in universe of the past x days. Filters out any stocks that do not have an average volume above minsize.
Requires: days, minsize
Returns: list of Equities
"""
def get_avg_volume(days, minsize):
# Gets history for all stocks in universe of past 90 days
volume_history_df = history(bar_count=days, frequency='1d', field='volume')
average_volume_series = volume_history_df.mean()
# Descending sort
average_volume_series.sort(axis=0, ascending=False)
# Get largest
avg_vol_stocks = []
for s, v in average_volume_series.iteritems():
if v > minsize:
avg_vol_stocks.append(s)
return avg_vol_stocks