Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
Need help weighting stocks based on rank, location, etc in a list

As the title states, I'm in need of help weighting stocks based on their location on a list (ranked)

I am re-sorting my outputs before the rebalancing takes place, so the equities are in the correct order before purchase.

def before_trading_start(context, data):  
    # ascending=false for 'higher is better'  
    # ascending=true for 'lower is better'  
    context.output = pipeline_output('My_pipeline')  
    context.long_list = context.output.sort_values(['filter 1'],  
                                                   ascending=False).iloc[:150]  
    context.long_list = context.long_list.sort_values(['filter 2'],  
                                                   ascending=False).iloc[:50]  
    context.long_list = context.long_list.sort_values(['filter 3'],  
                                                   ascending=False).iloc[:10]  
    # for weighting purposes, largest factor first  
    context.long_list = context.long_list.sort_values(['filter 1'],  
                                                   ascending=False).iloc[:10]  

So at this point, the equities are in order. Good. Now, let's buy the 10 equities in that list weighing each according to an arbitrary weight (just fiddling around with it, no alpha with these weights). This is where things go not so good. I tried creating a laaaarge for loop, to no avail:

    for stock in context.long_list.index:  
        if context.long_list.index[0]:  
            long_weight = .13  
            order_target_percent(stock, long_weight)  
            log.info("Bought " + str(stock))  
        if context.long_list.index[1]:  
            long_weight = .115  
            order_target_percent(stock, long_weight)  
            log.info("Bought " + str(stock))  
        if context.long_list.index[2]:  
            long_weight = .106  
            order_target_percent(stock, long_weight)  
            log.info("Bought " + str(stock))  
        if context.long_list.index[3]:  
            long_weight = .101  
            order_target_percent(stock, long_weight)  
            log.info("Bought " + str(stock))  
        if context.long_list.index[4]:  
            long_weight = .097  
            order_target_percent(stock, long_weight)  
            log.info("Bought " + str(stock))  
        if context.long_list.index[5]:  
            long_weight = .094  
            order_target_percent(stock, long_weight)  
            log.info("Bought " + str(stock))  
        if context.long_list.index[6]:  
            long_weight = .092  
            order_target_percent(stock, long_weight)  
            log.info("Bought " + str(stock))  
        if context.long_list.index[7]:  
            long_weight = .09  
            order_target_percent(stock, long_weight)  
            log.info("Bought " + str(stock))  
        if context.long_list.index[8]:  
            long_weight = .088  
            order_target_percent(stock, long_weight)  
            log.info("Bought " + str(stock))  
        if context.long_list.index[9]:  
            long_weight = .086  
            order_target_percent(stock, long_weight)  
            log.info("Bought " + str(stock))  

Using this approach, leverage ended up scary large. No dice there.

Then I tried to apply the weights to each equity before the purchase for loop, again to no avail:

    for stock in context.long_list.index:  
        for stock in context.long_list.index[0]:  
            long_weight = .13  
        for stock in context.long_list.index[1]:  
            long_weight = .115  
        for stock in context.long_list.index[2]:  
            long_weight = .106  
        for stock in context.long_list.index[3]:  
            long_weight = .101  
        for stock in context.long_list.index[4]:  
            long_weight = .097  
        for stock in context.long_list.index[5]:  
            long_weight = .094  
        for stock in context.long_list.index[6]:  
            long_weight = .092  
        for stock in context.long_list.index[7]:  
            long_weight = .09  
        for stock in context.long_list.index[8]:  
            long_weight = .088  
        for stock in context.long_list.index[9]:  
            long_weight = .086  

Using this approach, the algo didn't run, but after some fiddling and finagling, it ran, but used .086 as the overall weight for each equity.

I am at a loss. Could anyone help out by pointing me in the right direction with my code, linking a simple-to-understand algo that implements a similar weighing strategy, etc?

Many thanks in advance.

Joe

5 responses

Forget the 'Ifs' and 'fors'. Set up a series with the desired weights and order from that


    # Determine how we want to weight the securities  
    # Make a series of weights in the same order as the df index  
    context.weights =  pd.Series([.13, .115, .106, .101, .097, .094, .092, .09, .088, .086],  
                         index = context.securities_to_trade.index)

....
    # Loop through the securities and their weights  
    for security, weight in context.weights.iteritems():  
        if data.can_trade(security):  
            order_target_percent(security, weight)



Better yet, use the 'order_optimal_portfolio' method so no need to loop through the securities or check if they can trade

    # Set the weight objective  
    weight_objective = opt.TargetWeights(context.weights)  


    # Execute the order_optimal_portfolio method with above objective and constraint  
    # No need to loop through the stocks.  
    # The order_optimal_portfolio does all the ordering at one time  
    # Also closes any positions not in 'context.weights'  
    # As a bonus also checks for 'can_trade'  
    order_optimal_portfolio(objective = weight_objective, constraints = [])

See attached algorithm. Modify the schedule function to trade either using the order methods or using the optimize method. Good luck.

How can optimize be used to close just two positions in a list? (say for example because their pnl is dropping quickly or for whatever reason)

On another note, this is switching to Dan's optimize while flipping long & short using the minus sign here

def my_orders_using_optimize(context, data):  
    '''  
    Use Optimize to place orders all at once  
    '''  
    weight_objective = opt.TargetWeights( -context.weights )  

@Blue. Hadn't thought about the use case of simply closing some positions using 'order_optimal_portfolio'. Interesting...

Here's what I came up with. Maybe somebody else has a better way?

  # Make a list securities to close. Below is just for testing  
    close_these_positions_list = context.securities_to_trade.head(2).index.tolist()


  # Make a target weight objective of 0 weight for securities to close  
    close_weights = pd.Series(0, index = close_these_positions_list)  
    weight_objective = opt.TargetWeights(close_weights)  


    # Create a set of the current holdings we DONT want to close  
    # This is simply the current holdings minus anything we want to close  
    # Use this set to create a Frozen constraint  
    current_holdings_set = set(context.portfolio.positions.keys())  
    securities_to_close_set = set(close_these_positions_list)  
    securities_to_freeze = current_holdings_set - securities_to_close_set  
    freeze_constraint = opt.Frozen(securities_to_freeze)  


    # Execute the order_optimal_portfolio method with above objective and constraint  
    order_optimal_portfolio(objective = weight_objective, constraints = [freeze_constraint])  

A little bit cumbersome to just close a couple of positions. Maybe another route is to use the 'CannotHold' constraint?

The previously-mentioned codes work well until there gets to be fewer stocks than weights, then it crashes the algo. For example, in your source code you plan on 10 stocks passing the screen, thus you're fine, but my screen is a bit more rigorous and when <10 stocks pass, the algo ceases.