Back to Community
Q1500US vs get_fundamentals

Hi,

I tried to get list os securities similar to Q1500US using this get_fundamentals query:

get_fundamentals(  
            query(fundamentals.asset_classification.morningstar_sector_code)  
            .filter(fundamentals.asset_classification.morningstar_sector_code.isnot(None))  
            .filter(fundamentals.share_class_reference.currency_id == "USD")  
            .filter(fundamentals.company_reference.country_id == "USA")  
            .filter(not_(fundamentals.share_class_reference.exchange_id.like('OTC%')))  
            .filter(not_(fundamentals.share_class_reference.symbol.like('%.WI')))  
            .filter(fundamentals.share_class_reference.is_depositary_receipt == False)  
            .filter(fundamentals.share_class_reference.is_primary_share == "1")  
            .filter(fundamentals.share_class_reference.security_type == "ST00000001")  
            .filter(not_(fundamentals.balance_sheet.limited_partnership.isnot(None)))  
            .filter(not_(fundamentals.company_reference.standard_name.like('.* L[\\. ]?P\.?$')))  
            .filter(fundamentals.valuation.market_cap > 500000000))  

plus the code ranking get_fundamentals result by average dollar volume for 200 days and capping sectors to 30% (see attached algo).

Surprisingly the difference between Q1500 and this method is quite substantial (most of the time it's around 10%, max 50%).

What am I missing here? According to the documentation https://www.quantopian.com/help#quantopian_pipeline_filters_Q1500US the results should be at least similar.

Clone Algorithm
7
Loading...
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
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
"""
Compare set of stock in Q1500US universe with the set of stocks
from get_fundamentals.

The goal of this code is to make it easier to minimize the difference by
playing with get_fundamentals query.

Medodology and set of filters for get_fundamentals were taken from
these forum posts and documentation:
https://www.quantopian.com/posts/the-tradeable500us-is-almost-here
https://www.quantopian.com/posts/pipeline-trading-universe-best-practice
https://www.quantopian.com/help#quantopian_pipeline_filters_Q1500US
https://www.quantopian.com/posts/the-q500us-and-q1500us
"""

from collections import defaultdict
from numpy import isnan
from sqlalchemy import not_

from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline.classifiers.morningstar import Sector
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.filters.morningstar import Q1500US

UNIVERSE_SIZE = 1500       # amount of securities in the universe
SECTOR_CAP = 0.3           # sector cap
MIN_MARKET_CAP = 500000000 # minimum market cap to consider

def initialize(context):
    """
    Called once at the start of the algorithm.
    """
    context.month = None

    attach_pipeline(make_pipeline(), 'my_pipeline')
    
    schedule_function(compare,
                      date_rule=date_rules.month_start(),
                      time_rule=time_rules.market_open())

def make_pipeline():
    """
    A function to create our dynamic stock selector (pipeline). Documentation on
    pipeline can be found here: https://www.quantopian.com/help#pipeline-title
    """
    # Base universe set to the Q500US
    base_universe = Q1500US()

    # Factor of yesterday's close price.
    yesterday_close = USEquityPricing.close.latest

    pipe = Pipeline(
        screen = base_universe,
        columns = {
            'close': yesterday_close,
        }
    )
    return pipe

def before_trading_start(context, data):
    """
    Called every day before market open.
    """
    month = get_datetime().month
    if context.month != month:
        context.output = pipeline_output('my_pipeline')

        # These are the securities that we are interested in trading each day.
        context.q1500us = context.output.index

        context.q1500us_f = get_fundamentals(
            query(fundamentals.asset_classification.morningstar_sector_code)
            .filter(fundamentals.asset_classification.morningstar_sector_code.isnot(None))
            .filter(fundamentals.share_class_reference.currency_id == "USD")                
            .filter(fundamentals.company_reference.country_id == "USA")
            .filter(not_(fundamentals.share_class_reference.exchange_id.like('OTC%')))
            .filter(not_(fundamentals.share_class_reference.symbol.like('%.WI')))
            .filter(fundamentals.share_class_reference.is_depositary_receipt == False)
            .filter(fundamentals.share_class_reference.is_primary_share == "1")
            .filter(fundamentals.share_class_reference.security_type == "ST00000001")
            .filter(not_(fundamentals.balance_sheet.limited_partnership.isnot(None)))
            .filter(not_(fundamentals.company_reference.standard_name.like('.* L[\\. ]?P\.?$')))
            .filter(fundamentals.valuation.market_cap > MIN_MARKET_CAP))

        context.month = month

def compare(context, data):
    prices = data.history(list(context.q1500us_f), 'price', 200, '1d')
    volumes = data.history(list(context.q1500us_f), 'volume', 200, '1d')
    avg_dvol = (prices * volumes).mean()
    
    dvol_sorted = sorted([(avg_dvol[sec], sec) for sec in context.q1500us_f \
                         if data.can_trade(sec)], reverse=True)
    
    sector_capped = []
    sectors = defaultdict(int)
    for _dvol, sec in dvol_sorted:
        sector = context.q1500us_f[sec]['morningstar_sector_code']
        if sectors[sector] >= UNIVERSE_SIZE * SECTOR_CAP:
            continue
        sectors[sector] += 1
        sector_capped.append(str(sec.symbol))
        if len(sector_capped) >= UNIVERSE_SIZE:
            break
    
    q1500us_f = set(sector_capped)
    
    #q1500us_f = set([str(sec.symbol) for _dvol, sec in dvol_sorted][:UNIVERSE_SIZE])
    q1500us = set(str(sec.symbol) for sec in context.q1500us)
                
    diff = q1500us.symmetric_difference(q1500us_f)
    log.info("diff: %s %s" % (len(diff), sorted(diff)))
    #log.info("Q1500US: %s" % q1500us)
    #log.info("Q1500US_F: %s" % q1500us_f)
There was a runtime error.