Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
Helping out a beginner (pipeline ranking)

Basically, I am trying to rank stocks with the highest combination of two metrics and a market cap of at least $50 000 000 using pipeline.

I followed https://www.youtube.com/watch?v=J8RzPVtW4X8 to rank the stocks, but I keep getting this error:

AttributeError: 'UserBinaryExpression' object has no attribute 'rank'
There was a runtime error on line 38.

Can someone please help me fix this error? I cannot figure out how to fix this. Not knowing if the rest of my algo works is making this harder.

This is my code:

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

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, name='my_pipeline')  
    #***Trying to filter out stocks with a market cap below $50 mill  
    mkt_cap = MarketCap()  
    mkt_cap_price = mkt_cap > 50000000  
    pipe.set_screen(mkt_cap_price)  
    #***I am trying to get these fundamentals from the last year.  
    ent_val = fundamentals.valuation.enterprise_value

    ebt = fundamentals.income_statement.ebit

    at = fundamentals.operation_ratios.assets_turnover  
    earnings_yield = ebt/ent_val  
    #I want to add the earnings_yield + roc and find 30 stocks with the highest combination of these two fundamentals.  
    pos = earnings_yield + at

    pos_rank = pos.rank(ascending=False)  
    pipe.add(pos_rank, 'pos_rank')

    context.my_leverage = 1  


def before_trading_start(context, data):  
    context.output = pipeline_output('my_pipeline')  
    log.info((my_pipeline.sort('rank_pos')).head(30))  
    context.my_pipeline = my_pipeline.sort(['rev_rank'], ascending=False).iloc[:40]

    update_universe(context.my_pipeline.index)

def handle_data(context, data):  
    pass  

I know this does not make much sense, but I am trying to learn. The documentation did not help.

6 responses

The following at least seems to be free of syntax errors, though I don't know if it captures your intent...

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

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, name='my_pipeline')  
    #***Trying to filter out stocks with a market cap below $50 mill  
    mkt_cap = MarketCap()  
    mkt_cap_price = mkt_cap > 50000000  
    pipe.set_screen(mkt_cap_price)  
    #***I am trying to get these fundamentals from the last year.  
    ent_val = morningstar.valuation.enterprise_value.latest

    ebt = morningstar.income_statement.ebit.latest

    at = morningstar.operation_ratios.assets_turnover.latest  
    earnings_yield = ebt/ent_val  
    #I want to add the earnings_yield + roc and find 30 stocks with the highest combination of these two fundamentals.  
    pos = earnings_yield + at

    pos_rank = pos.rank(ascending=False)  
    pipe.add(pos_rank, 'pos_rank')

    context.my_leverage = 1  


def before_trading_start(context, data):  
    my_pipeline = pipeline_output('my_pipeline')  
    log.info((my_pipeline.sort('pos_rank')).head(30))  
    context.my_pipeline = my_pipeline.sort(['pos_rank'], ascending=False).iloc[:40]

    update_universe(context.my_pipeline.index)

def handle_data(context, data):  
    pass  

Thank you! It doesn't do exactly what I want, yet because I want the fundamentals to be from the latest 12-month period. Otherwise, it seems to be working. Is there an alternative to '.latest' that gives the fundamentals from the latest 12-month period? The documentation is not helping. I see latest used but don't see where there are alternatives.

Hey John,

Just taking a look at your code, it seems like you're trying to mix pipeline and fundamental queries. Take a look at the API documentation about working with fundamentals. There's a sample algorithm near the bottom of the docs.

As a guide, you can use get_fundamentals( query( ...)) to get fundamental data for SID's, and then update your universe to include those companies.

Disclaimer

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.

John, here's what I've figured out...

If you want to get data for a specific date, then you need to use get_fundamentals. The pipeline API does not let you specify a date directly. But if you're willing to tolerate some slack, you can specify a window size that approximates an year.

The window size argument in a pipeline factor reflects the number of trading days you want to consider for the factor. For an year, I have tried using 250 trading days. Not quite right, but close enough for my experiments. In fact you can leave the window size specification out of the custom factor definition, and supply one when you're creating a custom factor instance. This would look something like this in code:

class InitialMarketCap(CustomFactor):  
    # Pre-declare inputs and window_length  
    inputs = [USEquityPricing.close, morningstar.valuation.shares_outstanding]  

    # Compute market cap value  for the start of the window  
    def compute(self, today, assets, out, close, shares):  
        out[:] = close[0] * shares[0]  

def initialize(context):  
    ...  
    market_cap = InitialMarketCap(window_length=250)  
    ...  

You can imagine writing a similar custom factor for ebit .Naturally writing a custom factor each time you want to get the value at the start of the window is tedious. Take a look at the definition and use of the MedianValue custom factor here:

https://www.quantopian.com/help#quantopian_pipeline_CustomFactor

You could write a similar one to get the initial value of any single factor.

In your case, if all you want is the value of a metric at a specific period, consider using the get_fundamentals() call instead. Python's date/time library supports straightforward date arithmetic. Getting familiar with it will serve you well:

https://docs.python.org/2/library/datetime.html

Hope that helps.

Sunil

Thanks for the responses and help. I want to use Pipeline because I can rank the stock easier with it. I am purposely not trying to use get_fundamentals(). If I can not get the fundamentals of a year's period, I may have to end up using get_fundamentals(). I will see what I can do and update you if I find a good solution.

Ranking with results from get_fundamentals() is just as straightforward. See http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.rank.html#pandas.DataFrame.rank

I don't think this aspect of Quantopian is adequately stressed in their documentation, all the results from the pipeline API and get_fundamentals() are pandas DataFrame instances. So there are a wide variety of operations you can apply from pandas and numpy on the results you retrieve. I would also suggest trying a few queries and operations in the research module. It is much easier to interactively try something there.

Sunil