Hello Quants,

This is my most ambitious algorithm to date. Unfortunately, I believe I may have bitten off more than I can chew. In essence I have noticed certain algorithms that do well when a market is trending up, others when it is trending down, and I have pick up a few specific items that are generally useful to any algorithm. The one I have posted represents a collection of code taken from these other algorithms that when utilized together should be fairly robust. I prefer to predict stock performance using fundamental analysis, thus it is reflected in the algorithm. These modifications reflect my own personal preferences and hopefully will lead to an algo that could trade my own money. However, this algorithm produces a few errors I have not been able to correct. Firstly, the “#” have removed code on lines 102, 103 (identical code on 120, 121) that I would like to use. And secondly, I would like to remove “long_filter(context, data)” and replace it with the “long_trending(context, data)” on line 239 (I assume fixes for line 239 can be applied appropriately for line 240 because they apply to similar code in separate functions). I would be very appreciative if someone could take a gander at this and apply some fixes. Suggestions always welcome. I also would not mind working with others who have an interest in fundamental analysis.

2
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
import pandas as pd
import numpy as np
import talib
import math
from datetime import timedelta

def initialize(context):
context.num_stocks = 20 # number of stocks in portfolio
context.mawindow = 200 # Period which stocks must excel in, there are 21 trading days per month
set_benchmark(symbol('SPY'))
context.flagged_stocks = ['GLBC']
set_do_not_order_list(security_lists.leveraged_etf_list)
#set_slippage(slippage.VolumeShareSlippage(volume_limit=0.25, price_impact=0.1))
#####
context.long_df = None
context.short_df = None
context.needs_rebalance = False
context.stock_weights = {}  # Dictionary of stocks and their respective weights
context.cached_universe = None
context.sector_mappings = { # Sector mappings
101.0: "Basic Materials",
102.0: "Consumer Cyclical",
103.0: "Financial Services",
104.0: "Real Estate",
205.0: "Consumer Defensive",
206.0: "Healthcare",
207.0: "Utilites",
308.0: "Communication Services",
309.0: "Energy",
310.0: "Industrials",
311.0: "Technology"
}
context.sector_mappingsetf = symbols(
'XLB', # Materials Select Sector SPDR : Inception - 16/12/98
'XLE', # Energy Select Sector SPDR : Inception - 16/12/98
'XLF', # Financial Select Sector SPDR : Inception - 16/12/98
'XLI', # Industrials Select Sector SPDR : Inception - 16/12/98
'XLK', # Technology Select Sector SPDR : Inception - 16/12/98
'XLP', # Consumer Staples Select Sector SPDR : Inception - 16/12/98
'XLU', # Utilities Select Sector SPDR : Inception - 16/12/98
'XLV', # Health Care Select Sector SPDR : Inception - 16/12/98
'XLY', # Consumer Discretionary Select Sector SPDR : Inception - 16/12/98
)
context.XLB = sid(19654)
context.XLE = sid(19655)
context.XLF = sid(19656)
context.XLI = sid(19657)
context.XLK = sid(19658)
context.XLP = sid(19659)
context.XLU = sid(19660)
context.XLV = sid(19661)
context.XLY = sid(19662)
schedule_function(rebalance, # Rebalance monthly on the first day of the month at market open
date_rule=date_rules.month_start(),
time_rule=time_rules.market_open())

fundamental_df = get_fundamentals(
query(
fundamentals.valuation_ratios.pe_ratio,
fundamentals.asset_classification.morningstar_sector_code
)
.filter(fundamentals.valuation.market_cap                          >= 100e6  ) # >25e6 is 25,000,000
.filter(fundamentals.valuation_ratios.pe_ratio                     < 25      )
.filter(fundamentals.operation_ratios.current_ratio                >= 1.5    )
.filter(fundamentals.operation_ratios.roa                          > 0.02    ) # should be > 0.05, banks > 0.015
.order_by(fundamentals.valuation_ratios.pe_ratio.asc()) # desc for highest first, asc for lowest first
.limit(20)
)
return fundamental_df

fundamental_df = get_fundamentals(
query(
fundamentals.valuation_ratios.pe_ratio,
fundamentals.asset_classification.morningstar_sector_code
)
.filter(fundamentals.valuation.market_cap                          >= 100e6  ) # >25e6 is 25,000,000
.filter(fundamentals.valuation_ratios.pe_ratio                     > 25      )
.filter(fundamentals.operation_ratios.current_ratio                <= 1.5    )
.filter(fundamentals.operation_ratios.roa                          < 0.00    ) # should be > 0.05, banks > 0.015
.order_by(fundamentals.valuation_ratios.pe_ratio.desc()) # desc for highest first, asc for lowest first
.limit(20)
)
return fundamental_df

def long_filter(context, data):
long_filter_stocks = []

for stock in long_filter_stocks:
volume = history(30, '1d', 'volume')
vol_min = 0
volume_avg = volume.mean()
vol_min = (context.portfolio.portfolio_value/context.num_stocks)
current_date = get_datetime()
if (stock.end_date - current_date).days < 31:
del(long_filter_stocks[stock])
if (stock.symbol == 'SPY') or (stock.symbol in context.flagged_stocks) or (stock in security_lists.leveraged_etf_list):
del(long_filter_stocks[stock])
#if volume_avg<vol_min:
#del(long_filter_stocks[stock])
return long_filter_stocks

def short_filter(context, data):
short_filter_stocks = []

for stock in short_filter_stocks:
volume = history(30, '1d', 'volume')
vol_min = 0
volume_avg = volume.mean()
vol_min = (context.portfolio.portfolio_value/context.num_stocks)
current_date = get_datetime()
if (stock.end_date - current_date).days < 31:
del(short_filter_stocks[stock])
if (stock.symbol == 'SPY') or (stock.symbol in context.flagged_stocks) or (stock in security_lists.leveraged_etf_list):
del(short_filter_stocks[stock])
#if volume_avg<vol_min:
#del(short_filter_stocks[stock])
return short_filter_stocks

def long_trending(context, data):
for sector in context.sector_mappings:
lb = long_filter(context, data)
longs = lb.sort()
for sid in longs.index:
context.sector_mappings[sid]=sector
moving_avgs = prices.mean()
for b in context.XLB:
price = data[b].price
for x in longs:
if price < moving_avgs[b] and sector == 101.0:
for e in context.XLE:
price = data[e].price
for x in longs:
if price < moving_avgs[e] and sector == 309.0:
for f in context.XLF:
price = data[f].price
for x in longs:
if price < moving_avgs[f] and sector == 103.0:
for i in context.XLI:
price = data[i].price
for x in longs:
if price < moving_avgs[i] and sector == 310.0:
for k in context.XLK:
price = data[k].price
for x in longs:
if price < moving_avgs[k] and sector == 311.0:
for p in context.XLP:
price = data[p].price
for x in longs:
if price < moving_avgs[p] and sector == 205.0:
for u in context.XLU:
price = data[u].price
for x in longs:
if price < moving_avgs[u] and sector == 207.0:
for v in context.XLV:
price = data[v].price
for x in longs:
if price < moving_avgs[v] and sector == 206.0:
for y in context.XLY:
price = data[y].price
for x in longs:
if price < moving_avgs[y] and sector == 102.0:

def short_trending(context, data):
for sector in context.sector_mappings:
sb = short_filter(context, data)
short = sb.sort()
for sid in shorts.index:
context.sector_mappings[sid]=sector
moving_avgs = prices.mean()
for b in context.XLB:
price = data[b].price
for x in shorts:
if price > moving_avgs[b] and sector == 101.0:
for e in context.XLE:
price = data[e].price
for x in shorts:
if price > moving_avgs[e] and sector == 309.0:
for f in context.XLF:
price = data[f].price
for x in shorts:
if price > moving_avgs[f] and sector == 103.0:
for i in context.XLI:
price = data[i].price
for x in shorts:
if price > moving_avgs[i] and sector == 310.0:
for k in context.XLK:
price = data[k].price
for x in shorts:
if price > moving_avgs[k] and sector == 311.0:
for p in context.XLP:
price = data[p].price
for x in shorts:
if price > moving_avgs[p] and sector == 205.0:
for u in context.XLU:
price = data[u].price
for x in shorts:
if price > moving_avgs[u] and sector == 207.0:
for v in context.XLV:
price = data[v].price
for x in shorts:
if price > moving_avgs[v] and sector == 206.0:
for y in context.XLY:
price = data[y].price
for x in shorts:
if price > moving_avgs[y] and sector == 102.0:

context.df_longs = long_filter(context, data) #long_trending(context, data)
context.df_shorts = short_filter(context, data) #short_trending(context, data)
update_universe(context.df_longs + context.df_shorts)

def rebalance(context, data):
for stock in context.portfolio.positions: # Exit all positions before starting new ones
if stock not in context.df_longs or stock not in context.df_shorts and stock in data:
order_target_percent(stock, 0)
else:
pass

weight = create_weights(context, context.num_stocks) # Create weights for each stock

for stock in context.df_longs: # Rebalance all stocks to target weights
if stock in data:
if weight != 0:
log.info("Ordering %0.0f%% percent of %s in %s"
% (weight * 100,
stock.symbol,
context.sector_mappings[context.df_longs[stock]['morningstar_sector_code']]))
order_target_percent(stock, (weight*0.95))

for stock in context.df_shorts: # Rebalance all stocks to target weights
if stock in data:
if weight != 0:
log.info("Ordering %0.0f%% percent of %s in %s"
% (weight * 100,
stock.symbol,
context.sector_mappings[context.df_shorts[stock]['morningstar_sector_code']]))
order_target_percent(stock, (-weight*0.95))

def compute_conditional_weights(context, data, prices):

moving_avgs = prices.mean()
num_sectors_trending = 0

for etf in context.sector_mappingsetf:
price = data[etf].price
if price > moving_avgs[etf]:
num_sectors_trending +=1

record(num_sectors=num_sectors_trending)
if num_sectors_trending >=6:
context.sell_count = 0.0
if num_sectors_trending <= 5:
log.info("Fewer than five sectors trending, increasing allocation to short.")
if num_sectors_trending <= 5 or num_sectors_trending >=3:
context.sell_count = (round(context.num_stocks*0.50))
elif num_sectors_trending <= 2:
context.sell_count = (round(context.num_stocks*0.75))

def create_weights(context, num_stocks):
weight = 1.0/(context.num_stocks)
return weight

def handle_data(context, data):
prices = history(context.mawindow, '1d', 'price')

orders = has_orders(context, data)
if orders:
log.warn('has open orders')
if not orders and context.needs_rebalance:
rebalance(context, data) #prices maybe?
context.needs_rebalance = False
record(exposure = context.account.net_leverage) # net leverage should be 0.
record(leverage=context.account.leverage) # gross leverage should be 2

def has_orders(context, data):
# Return true if there are pending orders.
has_orders = False
for stock in data:
orders = get_open_orders(stock)
if orders:
for oo in orders:
message = 'Open order for {amount} shares in {stock}'
message = message.format(amount=oo.amount, stock=stock)
log.info(message)

has_orders = True
return has_orders

There was a runtime error.
6 responses

Hi Darth,

I took a look at your code and I see both of the errors that you are reporting. The first (when lines 102 and 103 are uncommented), is related to the fact that your universe appears to be empty in combination with the fact that you are using the history() function to try to get the history of a specific stock. It's important to note that the history() function gets the volume history (in your case) for all stocks in your universe. If you would then like to look at the history of one stock, you can access it by doing something like this:

history(30, '1d', 'volume')[stock]

The second problem you are encountering seems to be related to the context.sector_mappings data structure. In your for loop on line 126, it seems that you are trying to write sectors for specific sids back to the structure that contains the sector code -> sector name mapping. Python doesn't let you add to a data structure that it is looping through so this is why you are seeing an error. In order to help you solve this one, do you think you could describe to me what the intended purpose of this code bit is?

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.

Hello Jamie,

Thank you for your very swift response!

I’ll go through my logic from start to finish just in case it gives you an idea for a workaround.

Def long_basket/def short_basket: the purpose of this function is to select ~20 stocks from all those available and group them based on fundamental criteria. One group has specific parameters that are desirable for a long position, the other for a short position. In this scenario, they are sorted by PE ratio accordingly.

Def long_filter/def short_filter: the purpose of this function is to take the 20 stocks from the long_basket and remove those that do not meet a minimum average volume scalable by account size, remove stocks that are scheduled to delist, remove the SPY etf, remove flagged stocks and remove leveraged ETFs.

Def long_trending/def short_trending: the purpose of this function is to take the remaining stocks from the long_trending, add the sector mappings so we know what sector it is in, and remove those that where their corresponding ETF is below the 200day moving average. For example if an energy stock is in the long_trending group but XLE is below it’s 200 day MA, then it would be removed. If a stock is in the short_trending group and its corresponding sector ETF is above it’s 200 day moving average, then it is removed from the group. Lastly, the groups are paired down to a max number of stocks based on the number of trending ETFs. For example in the compute_conditional_weights, if it is determined that 2 ETFs have a current daily price above their 200 DMA of the 9 ETFs in sector_mappingsetf then this corresponds with choosing 25% of the context.num_stocks (in this case 5) to go long with and 75% (in this case 15) stocks to go short with. The compute_conditional_weights settings can let me go as long or as short as I want once I find out the optimum balance.

Def rebalance: This section would long 5 stocks and short 15 stocks if context.num_stocks was = 20. Long 4 stocks and short 12 stocks if context.num_stocks was = 16, etc.

Def has_orders: This prevents ordering when orders are pending.

If you have any other questions, please don’t hesitate to ask. Thank you for taking a look at the algo.

I suggest you take a look at this example from the help docs. The example looks at sector codes from a fundamentals query as well as shows how to update your universe with stocks returned from the query. I think this should help you with the problems I mentioned in my previous post!

Hello Jamie,