Hi,
I'm new to both Quantopian and Python.
I'd be very grateful if somebody could take a look at my code below, and tell me how I could (i) correct and (ii) execute it as a trading strategy. I'm trying to move away from Portfolio 123 and import this strategy into Quantopian
I took the strategy from 'The Lazy Fundamental Analyst Healthcare' by Fred Piard. For reference, the strategy is labelled 'Russell 2000 Healthcare Hedged' on pg 74. Eventually, I hope to move all the strategies I find interesting in this book to Quantopian
Essentially, the strategy is as follows:
- Rank 20 healthcare companies (as classified by GICS) with the lowest Price/Sales
- The universe is the Russell 2000
- 50% of portfolio AUM is long these 20 healthcare stocks, 50% of AUM is short S&P index
- The 20 stocks each have a position size of 2.5% of portfolio AUM
- Portfolio is rebalanced every 4 weeks
- No leverage utilised
As I'm new to Python, I took some related examples (https://www.quantopian.com/posts/introducing-the-pipeline-api) I found on the forum and meshed it together. The code doesn't work so would definitely appreciate any pointers
Thank you.
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline import CustomFactor
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.data import morningstar
from quantopian.pipeline.classifiers.morningstar import Sector
# Create Price to Sales custom factor
class Price_to_sales(CustomFactor):
# Pre-declare inputs and window_length
inputs = [morningstar.valuation_ratios.ps_ratio]
window_length = 1
# Compute factor1 value
def compute(self, today, assets, out, ps_ratio):
out[:] = ps_ratio[-1]
def initialize(context):
pipe = Pipeline()
attach_pipeline(pipe, 'ranked_2000')
my_sectors = [206] #"Healthcare"
sector_filter = Sector().element_of(my_sectors)
# Create list of all criteria for securities worth investing in
tradable_securities = {
'not_wi':not_wi,
'primary_share':primary_share,
'common_stock':common_stock,
'not_otc':not_otc
}
pipe_screen = (sector_filter)
# Create, register and name a pipeline
pipe = Pipeline(columns=tradable_securities, screen=pipe_screen)
# Add the factor defined to the pipeline
price_to_sales = Price_to_sales()
pipe.add(price_to_sales, 'price to sales')
# Create and apply a filter representing the top 2000 equities by MarketCap every day
# This is an approximation of the Russell 2000
mkt_cap = MarketCap()
top_2000 = mkt_cap.top(2000)
# Rank price to sales and add the rank to our pipeline
price_to_sales_rank = price_to_sales.rank(mask=top_2000)
pipe.add(price_to_sales_rank, 'ps_rank')
# Set a screen to ensure that only the top 2000 companies by market cap
# which are healthcare companies with positive PS ratios are screened
pipe.set_screen(top_2000 & (price_to_sales>0))
# Scedule my rebalance function
schedule_function(func=rebalance,
date_rule=date_rules.month_start(days_offset=0),
time_rule=time_rules.market_open(hours=0,minutes=30),
half_days=True)
# Schedule my plotting function
schedule_function(func=record_vars,
date_rule=date_rules.every_day(),
time_rule=time_rules.market_close(),
half_days=True)
# set my leverage
context.long_leverage = 0.00
context.short_leverage = -0.00
def before_trading_start(context, data):
# Call pipelive_output to get the output
context.output = pipeline_output('ranked_2000')
# Narrow down the securities to only the 20 with lowest price to sales & update my universe
context.long_list = context.output.sort(['ps_rank'], ascending=False).iloc[:20]
context.spy = sid(8554)
context.short_list = context.spy
def record_vars(context, data):
# Record and plot the leverage of our portfolio over time.
record(leverage = context.account.leverage)
print "Long List"
log.info("\n" + str(context.long_list.sort(['ps_rank'], ascending=True).head(10)))
print "Short List"
log.info("\n" + str(context.short_list)
# This rebalancing is called according to our schedule_function settings.
def rebalance(context, data):
long_weight = context.long_leverage / float(len(context.long_list))
short_weight = context.short_leverage / float(len(context.short_list))
for long_stock in context.long_list.index:
log.info("ordering longs")
log.info("weight is %s" % (long_weight))
order_target_percent(long_stock, long_weight)
for short_stock in context.short_list.index:
log.info("ordering shorts")
log.info("weight is %s" % (short_weight))
order_target_percent(short_stock, short_weight)
for stock in context.portfolio.positions.iterkeys():
if stock not in context.long_list.index and stock not in context.short_list.index:
order_target(stock, 0)