Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
Need help ranking, then calling, two groups separately

Hello all.

First off, thanks a ton in advance for the help! You guys/gals are awesome and are always quick to lend a hand.

I am currently working on an ETF rotation strategy that groups ETFs into two categories: stocks and bonds (whoa, imagine that). I want to evaluate them under the same/similar criteria but not base my entire pipe's rankings on both groups at the same time but rather rank group A during condition A and group B during condition B.

For example, under normal conditions I would rank stock ETFs based on their recent performance and completely disregard the bond ETFs. Then, during a correction or bear market, I would like to disregard stock ETFs and rank the bond ETFs.

How can I accomplish this?

7 responses

Here is a sample of the code I'm running:

Separating the two different groups

def initialize(context):  
    context.stock = [sid(.............)]  
    context.bond = [sid(............)]  

I assume I am good so far as I have identified my trade-able universe and have them separated according to what they are...stocks or bonds.

Next I will make my pipeline. This is where my issue comes about because I have to choose my trade-able universe, either my context.stocks or context.bonds. I don't want to group them together beforehand because as I stated previously I want to analyze them differently.

def make_pipeline(context):

    universe=Filters.StaticAssets(context.stock)  
    # Volatility measure  
#    vol = Annualized_vol_1yr()

    close = recent_close()

    # Factor of returns for each period at close price.  
    returns_5=Factors.Returns(inputs=[USEquityPricing.close],  
                             window_length=5,  
                             mask=universe)

    returns_21=Factors.Returns(inputs=[USEquityPricing.close],  
                            window_length=21,  
                            mask=universe)

    returns_63=Factors.Returns(inputs=[USEquityPricing.close],  
                            window_length=63,  
                            mask=universe)  
    returns_126=Factors.Returns(inputs=[USEquityPricing.close],  
                            window_length=126,  
                            mask=universe)

    returns_1yr=Factors.Returns(inputs=[USEquityPricing.close],  
                             window_length=252,  
                             mask=universe)  
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    # 5 day weight & scores  
    weight_5 = 0  
    score_5d = (weight_5*returns_5)  
    # 21 day weight & scores  
    weight_21 = 1  
    score_21d = (weight_21*returns_21)  
    sma21 = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=21)  
    ma21 = ((close-sma21)/sma21)  
    # 63 day weight & scores  
    weight_63 = 0  
    score_63d = (weight_63*returns_63)  
    # 126 day weight & scores  
    weight_126 = 0  
    score_126d = (weight_126*returns_126)  
    # 1yr weight & scores  
    weight_1yr = 0  
    score_1yr = (weight_1yr*returns_1yr)  
    score = (score_5d + score_21d + score_63d + score_126d + score_1yr)  
    score_rank = score.rank(ascending=False)  
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    # Screen out any ETFs that have posted a loss in the last 5 days  
    gain_5d = (returns_5 > -.005)  
    pipe = Pipeline(  
        columns = {  
            'score': score,  
            "score_rank": score_rank,  
            'ma21': ma21,  
        },  
        screen = (gain_5d)  
    )  
    return pipe  

So as you could probably have guessed, at this point I have effectively analyzed and screened for the context.stocks but the context.bonds have been left behind. Before adding the context.stocks to my rebalancing function I will edit one last time to get the data nice a neat.

def before_trading_start(context, data):  
    context.output = pipeline_output('My_pipeline')  
    # ascending=false for 'higher is better'  
    # ascending=true for 'lower is better'  
    # Choose the best ETF from the screen based on rank  
    context.output = context.output.sort_values(['ma21'],ascending=False).iloc[:3]  
    context.output = context.output.sort_values(['score_rank'],ascending=True).iloc[:1]  

And that's it. The context.stocks have been analyzed, screened, and are now singled out down to one equity...but the bonds are still at step 1.

Since I can't run two "make_pipeline" functions but I don't want to analyze the bonds under the same criteria/at the same time as the stocks, what should I do?

Any help would be much appreciated!

Actually one can now run multiple pipelines. See https://www.quantopian.com/posts/multiple-pipelines-available-in-algorithms .

Maybe just define another similar pipeline for bonds?

Thanks Dan! I guess I should've stated that I looked into that particular thread but it didn't make much sense to me, just kind of threw it out there and left it without much explanation for the inexperienced folks.

If lump all the stock and bond etfs together, analyze them together, how can I separate them afterwards? I know I can separate them, but how can I acheive that based off of something that isn't some sort of factor?

I imagine this would take place during the

context.output = pipeline_output('My_pipeline')  

piece of code but what would I have to do beforehand to be able to just simply segregate them? Set up some sort of for loop in the initialization phase?

Thanks in advance.

Getting a runtime error trying to setup the two pipelines.

    # Create dynamic selectors.  
    attach_pipeline(stock_pipeline(context), 'stock_pipeline')  
    attach_pipeline(bond_pipeline(context), 'bond_pipeline')  
def stock_pipeline(context):

    universe=Filters.StaticAssets(context.stock)

..........

    pipe = Pipeline(  
        columns = {  
            'score': score,  
            "score_rank": score_rank,  
            'ma21': ma21,  
        },  
        screen = (gain_5d)  
    )  
    return pipe  
def bond_pipeline(context):

    universe=Filters.StaticAssets(context.stock)

..........

    pipe = Pipeline(  
        columns = {  
            'score': score,  
            "score_rank": score_rank,  
            'ma21': ma21,  
        },  
        screen = (gain_5d)  
    )  
    return pipe  
def before_trading_start(context, data):  
    context.ouput_stock = pipeline_output('stock_pipeline')  
    context.output_bond = pipeline_output('bond_pipeline')  
    # ascending=false for 'higher is better'  
    # ascending=true for 'lower is better'  
    # Choose the best STOCK ETF from the screen based on rank  
    context.output_stock = context.output_stock.sort_values(['ma21'],ascending=False).iloc[:4]  
    context.output_stock = context.output_stock.sort_values(['score_rank'],ascending=True).iloc[:1]  
    # Choose the best BOND ETF from the screen based on rank  
    context.output_bond = context.output_bond.sort_values(['score_rank'],ascending=True).iloc[:1]  

Any idea where I could be going wrong?

Here's the error:

TypeError: zipline.pipeline.pipeline.__init__() expected a value of type zipline.pipeline.filters.filter.Filter or NoneType for argument 'screen', but got tuple instead.  
There was a runtime error on line 204.  

Try looking at line 204. It should be one of the pipeline definitions. The error says "expected a value of type zipline.pipeline.filters.filter.Filter or NoneType for argument 'screen', but got tuple instead". Looks like you are setting the screen to be 'gain_5d'. Ensure this is a filter. The error says it's a tuple.

Believe I got it working.... will advise if obstacles are again encountered.

https://www.quantopian.com/posts/help-algo-doing-the-same-simple-thing-done-three-different-ways-produces-different-results

Unearthed a completely unrelated, concerning error. Would greatly appreciate any help!