Back to Community
Dollar Volume Pipeline


The attached backtest builds a dollar-volume screen using pipeline. The custom factor tracks the average daily dollar volume traded over the trailing window. The pipeline uses the AvgDailyDollarVolume factor to screen the algorithm's universe to stocks trading more than $100M on average over the past 100 days. The algo maintains a portfolio of: long the 100 most traded stocks, and short the 100 least traded stocks.

This factor is similar to the calculations done for DollarVolumeUniverse. However, pipeline is much faster, calculates dollar volume for all stocks every day (DVU is quarterly), and allows for filtering by rank, percentile, or value.

Dollar volume is a good first pass filter to avoid illiquid assets. We'll undoubtedly add a builtin Factor for dollar volume, but in the meantime this should work pretty well in lieu of calls to set_universe.

happy coding,

Clone Algorithm
Total Returns
Max Drawdown
Benchmark Returns
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
This example screens all securities using dollar-volume traded and 
builds a long/short portfolio.

import numpy as np

from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline import CustomFactor
from import USEquityPricing
from import morningstar

class AvgDailyDollarVolumeTraded(CustomFactor):
    inputs = [USEquityPricing.close, USEquityPricing.volume]
    def compute(self, today, assets, out, close_price, volume):
        dollar_volume = close_price * volume
        avg_dollar_volume = np.mean(dollar_volume, axis=0)
        out[:] = avg_dollar_volume

def initialize(context):
    context.long_leverage = 0.50
    context.short_leverage = -0.50
    # Scedule my rebalance function. Will rebalance the portfolio
    # to match long_list and short_list calculated in before_trading_start
    # using the outperformance factor.
                      time_rule=time_rules.market_close(hours=0, minutes=30), 
    pipe = Pipeline()
    attach_pipeline(pipe, 'dollar_volume')

    # lets screen down to the stocks that have averaged 
    # at least $100M of daily trading dollar volume over the
    # past 100 days.
    dollar_volume = AvgDailyDollarVolumeTraded(window_length=100)
    pipe.add(dollar_volume, 'dollar_volume')
    dv_filter = dollar_volume > 100 * 10**6

def before_trading_start(context, data):
    # Call pipelive_output to get the output
    # Note this is a dataframe where the index is the SIDs for all securities to pass my screen
    # and the colums are the factors which I added to the pipeline
    context.output = pipeline_output('dollar_volume').sort(['dollar_volume'])
    context.long_list = context.output.iloc[-100:]
    context.short_list = context.output.iloc[:100]
    portfolio_stocks = context.long_list.index.union(context.short_list.index)

def handle_data(context, data):  
     # Record and plot the leverage of our portfolio over time. 
    record(leverage = context.account.leverage)       

# 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:
        if long_stock in data:
            order_target_percent(long_stock, long_weight)
    for short_stock in context.short_list.index:
        if short_stock in data:
            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)

There was a runtime error.

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.

4 responses

Thanks Fawce,

A few questions:

  1. I don't see how your code knows if a stock in your lists is available for trading on Quantopian/IB on a given date, when the rebalance function is run. When the pipeline is run, does it filter out all stocks that are inactive within the 10-day window, by looking at their start and end dates in the database? I'd assume that any stock that ends up in your long and short lists would need to be active for the full window, correct?
  2. Is there any way to use pipeline to deal with stocks that have end dates within the backtest timeframe? Say I wanted to avoid stocks that will expire within 20 days? Could I filter those stocks out using pipeline, or would I need to add a screen within the algo?
  3. I suspect that most (all?) expirations are scheduled events. Stocks aren't just de-listed at the drop of a hat. Is there a path to make this information available in Quantopian? One can arbitrarily try to avoid holding stocks about to expire with something like:
for stock in context.stocks:  
        if stock.security_end_date < get_datetime() + datetime.timedelta(days=20):  # de-listed ?  

However, it could result in bias. It also means that the backtest needs to end 20 days before the current date. Under live trading, it seems like there is no way to avoid scheduled de-listings.


Hi Grant,

Thanks for the questions:

  1. My code is ignorant of the start/end of stocks. Knowing the date a stock stops trading would be a form of look ahead bias, so pipeline handles the arrival/departure of stocks internally by masking the assets to exclude defunct stocks. Assets will be masked out the day after their end date. That means that if you have a 10 day look back, an algo-time currently expired stock will not show up in the history window, even if the stock was active in that historical period.
  2. We don't have data for delistings, but the exchanges do publish pending delistings. Nyse's is here. Besides delisting, stocks also expire because of mergers, and we do have acquisition and mergers from EventVestor.
  3. NYSE issues a notice 10 days in advance of delisting. There are many criteria, but the most common reason is falling below market cap or price requirements. You could approximate delisting criteria with pipeline, and just screen out companies that are close to the delisting requirements.

happy coding,

Thanks Fawce,

If I'm looking back 10 days, would pipeline include stocks that have their start dates within the 10-day window (assuming that their end dates are outside the window)? Or does it require the window to be within the start and end dates? It seems like one would want all stocks within the window to have started trading and not ended trading. Otherwise, for example, you could end up with some stocks that only have a few trades, while others will have 10.

Knowing the date a stock stops trading would be a form of look ahead bias.

I agree. However, the problem, of course, is that in backtesting, one can end up with stocks that can't be sold, because the backtester doesn't support transactions beyond the end date. For live trading, it must be the same--nothing can be done via the Quantopian trading platform. IB would need to perform the transactions or a user could do them manually. So, my thinking is that there should be a way with this new pipeline thingy to filter out stocks that are gonna cause headaches in backtesting/live trading.

Could anyone knows, how to get value of dollar_volume in rebalance function?

 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))  

I want to sum 100 stocks's weight(all stocks' dollar_volume )
(one stock's dollar_volume)/(sum all stocks' dollar_volume)

Not use the equal weight.

Thank you very much.