101 Formulaic Alphas, a newly published paper from Zura Kakushadze, Geoffrey Lauprete and Igor Tulchinsky, spells out pseudo-code for 101 "alphas," volume and price based factors that can be computed accross a large universe of stocks. The stock-by-stock values of these alphas can be used as a trading signal (positive for buy, negative for sell). The authors propose that while the alphas may not be individually profitable, their aggregate "mega-alpha" signal is capable of generating significant market outperformance.
Here at Quantopian HQ, we're very curious to see if the aggregate signal discussed in 101 Formulaic Alphas is truly capable of generatating returns in excess of the market. That's where you come in. We need the Quantopian community's help turning each of the 101 alphas into a Pipeline algorithm. When we're done, we'll pull together data from all of the algorithms you wrote and share our findings here. We'll also release the aggregated signal data, so you can craft your own mega-alpha algorithm.
For an example, check out my implementation of Alpha #101 below. Alpha #101,
((close - open) / ((high - low) + .001), spells out a factor (in this case a momentum factor) that is computed cross-sectionally by a custom Pipeline factor and used to rank the top 2,500 market cap stocks. Before trading starts each day, these rankings are used to generate baskets of 200 names to buy and 200 to short.
Here are some guidelines to help you get started:
- Your algorithm must implemented using the Pipeline API.
- Your algorithm should hold a minimum of 100 stocks in its portfolio at the end of each trading day.
- Your algorithm should use the alpha factor to rebalance daily.
- Each alpha gets its own thread. Please check for an existing thread for your alpha before starting a new one.
- The title of your thread should match the following format: "101 Alphas Project: Alpha #10"
- Attach the "101 Alphas Project" tag to your post.
- Share a backtest from 1/1/10 to 1/1/15 with your post.
Q: Why Pipeline?
A: The Pipeline API is designed to support exactly the type of cross-sectional strategies described by the alphas in this paper. We also hope that this project will help us identify areas where Pipeline can be improved.
Q: How should I implement my algo's ordering logic?
A: Feel free to test out different trading logic implementations. Just make sure your algorithm holds at least 100 names and trades daily.
Q: What if I don't understand what some of these factors mean?
A: Let's discuss! We'll work together to determine how exactly each of the provided pseudo-code expressions can be translated into an algorithm.
Q: Someone else has already shared the alpha I want to work on. What should I do?
A: Share your alternate interpretation in the existing thread for that alpha. We expect there to be some debate over the proper implementation of some of the alphas.
Q: I'm new to Pipeline. Where should I start?
A: Take a look at the example implementation of Alpha #101 shared below. Also, check out the Pipeline help docs and example algorithms. To get a better sense of an alpha's functionality, try implementing your Pipeline factor in the research environment before moving it into an algorithm. If you do use research to build your alpha algorithm, be sure to share your notebook along with your backtest! Still stuck? Always feel free to send your questions to [email protected].
Got it? Pick an alpha and clone the algo below to get started with your implementation!
|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|
from __future__ import division 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.factors import SimpleMovingAverage from quantopian.pipeline.data import morningstar #((close - open) / ((high - low) + .001) # Create custom factor subclass to calculate a market cap based on yesterday's # close class RandomFactor(CustomFactor): # Pre-declare inputs and window_length inputs = [USEquityPricing.close, USEquityPricing.high, USEquityPricing.low] window_length = 2 # Compute market cap value def compute(self, today, assets, out, close, high, low): out[:] = (close[-1] - close[-2]) / ((high[-1] - low[-1]) + .001) class MarketCap(CustomFactor): # Pre-declare inputs and window_length inputs = [USEquityPricing.close, morningstar.valuation.shares_outstanding] window_length = 1 # Compute market cap value def compute(self, today, assets, out, close, shares): out[:] = close[-1] * shares[-1] def initialize(context): pipe = Pipeline() attach_pipeline(pipe, 'example') # Note that we don't call add_factor on these Factors. # We don't need to store intermediate values if we're not going to use them last_price = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=1) # Construct the custom factor mkt_cap = MarketCap() # Create and apply a filter representing the top 500 equities by MarketCap # every day. mkt_cap_top_500 = mkt_cap.top(2500) remove_penny_stocks = last_price > 1.0 pipe.add(RandomFactor().rank(ascending=False, mask=mkt_cap_top_500), 'our_factor') # Use multiple screens to narrow the universe pipe.set_screen(remove_penny_stocks) set_commission(commission.PerShare(cost=0, min_trade_cost=0)) set_slippage(slippage.FixedSlippage(spread=0)) schedule_function(rebalance, date_rules.every_day(), time_rules.market_open(minutes=30)) context.long_leverage = 0.50 context.short_leverage = -0.50 def before_trading_start(context, data): context.output = pipeline_output('example') context.short_list = context.output.dropna(axis=0).sort(['our_factor'], ascending=True).iloc[:200] context.long_list = context.output.dropna(axis=0).sort(['our_factor'], ascending=True).iloc[-200:] update_universe(context.long_list.index.union(context.short_list.index)) def handle_data(context, data): print "SHORT LIST" log.info("\n" + str(context.short_list.sort(['our_factor'], ascending=True).head())) print "LONG LIST" log.info("\n" + str(context.long_list.sort(['our_factor'], ascending=False).head())) def rebalance(context,data): long_pct = 0.5 / float(len(context.long_list)) short_pct = - 0.5 / float(len(context.short_list)) print(len(context.long_list)) print(len(context.short_list)) log.info("ordering longs") log.info("weight is %s" % (long_pct)) for long_stock in context.long_list.index: if long_stock in data: oo = get_open_orders(long_stock) if len(oo) > 0: continue order_target_percent(long_stock, long_pct) log.info("ordering shorts") log.info("weight is %s" % (short_pct)) for short_stock in context.short_list.index: if short_stock in data: oo = get_open_orders(short_stock) if len(oo) > 0: continue order_target_percent(short_stock, short_pct) for stock in context.portfolio.positions.iterkeys(): oo = get_open_orders(stock) if len(oo) > 0: continue if stock not in context.long_list.index and stock not in context.short_list.index: order_target(stock, 0)