Welcome to another Quantopian tutorial, where we're learning about utilizing the Pipeline API. In the previous tutorial, we covered how to grab data from the pipeline and how to manipulate that data a bit. We'll continue building on that here, mainly by adding an actual trading strategy around the data we have. The code up to this point:
from quantopian.pipeline import Pipeline from quantopian.algorithm import attach_pipeline, pipeline_output from quantopian.pipeline.data.builtin import USEquityPricing from quantopian.pipeline.factors import SimpleMovingAverage def initialize(context): pipe = Pipeline() attach_pipeline(pipe, 'pipeline_tutorial') _50ma = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=50) _200ma = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=200) pipe.add(_50ma, '_50ma') pipe.add(_200ma, '_200ma') pipe.add(_50ma/_200ma, 'ma_ratio') pipe.set_screen(_50ma/_200ma > 1.0) def before_trading_start(context, data): output = pipeline_output('pipeline_tutorial') context.my_universe = output.sort('ma_ratio', ascending=False).iloc[:100] update_universe(context.my_universe.index) def handle_data(context, data): log.info("\n" + str(context.my_universe.head())) log.info("\n" + str(len(context.my_universe)))
Now we need to do a couple things. First, we want to buy all of the companies we can that are in our universe, and then we also want to sell off the companies that are no longer in our universe. If the company isn't in our universe, then it means it does not meet our parameters. To do all of this, we can use the handle_data
function:
def handle_data(context, data): cash = context.portfolio.cash purchase_value = 1500 for stock in context.my_universe.index: if stock not in context.portfolio.positions: if purchase_value < cash: try: order_target_value(stock, purchase_value) cash -= purchase_value except: pass
First, we're accounting for how much money we have, an amount of money we want to invest per company, and then we begin iterating through the companies in our universe. If the company is not already in our portfolio, and if we have the cash to invest, then we're going to make the order. We encase this in a try/except simply due to issues with some tickers, despite the lookup date. So this our way of acquiring positions in companies, now we need to exit companies we aren't interested in:
for stock in context.portfolio.positions: if stock not in context.my_universe.index and stock not in context.stocks_sold: order_target_value(stock, 0) record('Leverage',context.account.leverage)
Here, we're looking for companies that are in our portfolio, but not in our universe. If this is the case, we make the target value of our ownership in the companies zero.
Logically, this makes total sense to me, but leverage gets out of hand due to this second for loop.
Logically, it really shouldn't have any issues, since the target value is zero, but it does. Thus, we're going to add in one final check, just to make sure we don't do any double sells, which is what appears to be happening. First, within our initialize
function:
def initialize(context): set_symbol_lookup_date('2007-01-04') pipe = Pipeline() attach_pipeline(pipe, 'pipeline_tutorial') _50ma = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=50) _200ma = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=200) pipe.add(_50ma, '_50ma') pipe.add(_200ma, '_200ma') pipe.add(_50ma/_200ma, 'ma_ratio') pipe.set_screen(_50ma/_200ma > 1) context.stocks_sold = []
The only change here is the last line, with the context.stocks_sold list definition. The idea here is to actually track every stock sale. If we have sold the stock, we don't want to sell it again, so we'll add the stock to the list if we sell it. Any time we buy a stock, we'll also check to see if that stock is currently in the stocks_sold list. If it is, we'll remove it, since we're re-buying it and may want to sell it later.
def handle_data(context, data): cash = context.portfolio.cash purchase_value = 1500 for stock in context.my_universe.index: if stock not in context.portfolio.positions: if purchase_value < cash: try: order_target_value(stock, purchase_value) cash -= purchase_value #################################### if stock in context.stocks_sold: context.stocks_sold.remove(stock) except: pass for stock in context.portfolio.positions: if stock not in context.my_universe.index and stock not in context.stocks_sold: order_target_value(stock, 0) ################################# context.stocks_sold.append(stock) record('Leverage',context.account.leverage)
Additions to the script are noted with the # sign.
That's all for now. For more tutorials, head: