The TradableStocksUS Universe

Now that we've covered the basic components of the Pipeline API, let's construct a pipeline that we might want to use in an algorithm.

To do so, we will create a filter to narrow down the full universe of US stocks to a subset of tradable securities, defined as those securities that meet all of the following criteria:

  • Common stocks only: no preferred stocks, ADRs, limited partnerships (LPs), or ETFs. ADRs are issuances in the US equity market for stocks that trade on other exchanges. Frequently, there is inherent risk associated with depositary receipts due to currency fluctuations so we exclude them from our pipeline. LP securities are not tradable with most brokers, so we exclude them as well. In the case of the US Stock dataset, selecting for "Common Stock" in the usstock_SecurityType2 field will automatically exclude preferred stocks, ETFs, ADRs, and LPs, as the latter all have different values for usstock_SecurityType2.
  • Primary shares only: for companies with multiple share classes, select only the primary share class
  • Dollar volume: to ensure that stocks in our universe are relatively easy to trade when entering and exiting positions, include only stocks that have average daily dollar volume of \$2.5M or more over the trailing 200 days.
  • Not too cheap: if a stock's price is lower than \$5, the bid-ask spread becomes larger relative to the price, and the transaction cost becomes too high.
  • 200 continuous days of price and volume: If a stock has any missing data for the previous 200 days, the company is excluded. This targets stocks with trading halts, IPOs, and other situations that make them harder to assess.

Former Quantopian users may notice that this universe is modeled on Quantopian's most popular universe, QTradableStocksUS, described in this archived Quantopian forum post. To reduce data dependencies, we have omitted one rule, namely that market cap must be over \$500M. See the note further down if you have a Sharadar fundamentals subscription and would like to add this market cap filter.

Creating Our Universe

Let's create a filter for each criterion and combine them together to create a TradableStocksUS filter.

In [1]:
from zipline.pipeline import Pipeline
from zipline.research import run_pipeline
from zipline.pipeline.data import EquityPricing, master
from zipline.pipeline.factors import SimpleMovingAverage, AverageDollarVolume, Latest
from zipline.pipeline.filters import AllPresent, All

Now we can define our filters. As discussed in the lesson on masking, we use masks in the later steps of our asset funnel to reduce computational load.

In [2]:
def TradableStocksUS():
    # Equities listed as common stock (not preferred stock, ETF, ADR, LP, etc)
    common_stock = master.SecuritiesMaster.usstock_SecurityType2.latest.eq('Common Stock')

    # Filter for primary share equities; primary shares can be identified by a
    # null usstock_PrimaryShareSid field (i.e. no pointer to a primary share)
    is_primary_share = master.SecuritiesMaster.usstock_PrimaryShareSid.latest.isnull()

    # combine the security type filters to begin forming our universe
    tradable_stocks = common_stock & is_primary_share

    # also require high dollar volume
    tradable_stocks = AverageDollarVolume(window_length=200, mask=tradable_stocks) >= 2.5e6

    # also require price > $5. Note that we use Latest(...) instead of EquityPricing.close.latest
    # so that we can pass a mask
    tradable_stocks = Latest([EquityPricing.close], mask=tradable_stocks) > 5

    # also require no missing data for 200 days
    tradable_stocks = AllPresent(inputs=[EquityPricing.close], window_length=200, mask=tradable_stocks)
    tradable_stocks = All([EquityPricing.volume.latest > 0], window_length=200, mask=tradable_stocks)
    
    return tradable_stocks

Note that when defining our filters, we used several Classifier methods and built-in Filters that we haven't yet seen including isnull, All, and AllPresent. Documentation on these methods is available in the API Reference for Classifiers and Filters.

If you have a Sharadar fundamentals subscription and would like to add a market cap filter to your universe to fully re-create the QTradableStocksUS universe, you can do so by adding the following line to the above function:

# also require market cap over $500M
tradable_stocks = Latest([sharadar.Fundamentals.slice(dimension='ARQ', period_offset=0).MARKETCAP], mask=tradable_stocks) >= 500e6

Code Reuse

Our universe may be useful to us in numerous notebooks and Zipline algorithms, so a practical next step is to transfer the pipeline code to a .py file to facilitate code reuse. We have done so in tradable_stocks.py. The universe can now be imported in any notebook or Zipline algorithm as follows:

In [3]:
from codeload.pipeline_tutorial.tradable_stocks import TradableStocksUS

universe = TradableStocksUS()

We'll import and use this universe in the next lesson.