Back to Community
Custom Factor question

Karen Ruben originally shared below algo. I am very hard time understanding one line (line 41). How could denominator be 60 days old data? Could anyone explain to me how the data is arranged when you get "window_length = 60"? Thank you

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 comes from a request in the forums. 
The post can be found here:

The request was: 

I am stuck trying to build a stock ranking system with two signals:
1. Trading Volume/Shares Outstanding.
2. Price of current day / Price of 60 days ago.
Then rank Russell 2000 stocks every month, long the top 5%, short the bottom 5%.


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

# Create custom factor #1 Trading Volume/Shares Outstanding
class Factor1(CustomFactor):   
    # Pre-declare inputs and window_length
    inputs = [USEquityPricing.volume, morningstar.valuation.shares_outstanding] 
    window_length = 1
    # Compute factor1 value
    def compute(self, today, assets, out, volume, shares):       
        out[:] = volume[-1]/shares[-1]

# Create custom factor #2 Price of current day / Price of 60 days ago.        
class Factor2(CustomFactor):   
    # Pre-declare inputs and window_length
    inputs = [USEquityPricing.close] 
    window_length = 60
    # Compute factor2 value
    def compute(self, today, assets, out, close):       
        out[:] = close[-1]/close[0]
# Create custom factor to calculate a market cap based on yesterday's close
# We'll use this to get the top 2000 stocks by market cap
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):
    context.long_leverage = 0.50
    context.short_leverage = -0.50
    pipe = Pipeline()
    attach_pipeline(pipe, 'ranked_2000')
    #add the two factors defined to the pipeline
    factor1 = Factor1()
    pipe.add(factor1, 'factor_1') 
    factor2 = Factor2()
    pipe.add(factor2, 'factor_2')
    # Create and apply a filter representing the top 2000 equities by MarketCap every day
    # This is an approximation of the Russell 2000
    mkt_cap = MarketCap()
    top_2000 =
    # Rank factor 1 and add the rank to our pipeline
    factor1_rank = factor1.rank(mask=top_2000)
    pipe.add(factor1_rank, 'f1_rank')
    # Rank factor 2 and add the rank to our pipeline
    factor2_rank = factor2.rank(mask=top_2000)
    pipe.add(factor2_rank, 'f2_rank')
    # Take the average of the two factor rankings, add this to the pipeline
    combo_raw = (factor1_rank+factor2_rank)/2
    pipe.add(combo_raw, 'combo_raw') 
    # Rank the combo_raw and add that to the pipeline
    pipe.add(combo_raw.rank(mask=top_2000), 'combo_rank')      
    # Scedule my rebalance function
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('ranked_2000')
    #there are some NaNs in factor 2, I'm removing those
    ranked_2000 = context.output.fillna(0)
    ranked_2000 = context.output[context.output.factor_2 > 0]
    # Narrow down the securities to only the top 500 & update my universe
    context.long_list = ranked_2000.sort(['combo_rank'], ascending=False).iloc[:100]
    context.short_list = ranked_2000.sort(['combo_rank'], ascending=False).iloc[-100:]   

def handle_data(context, data):  
     # Record and plot the leverage of our portfolio over time. 
    record(leverage = context.account.leverage)
    print "Long List""\n" + str(context.long_list.sort(['combo_rank'], ascending=True).head(10)))
    print "Short List""\n" + str(context.short_list.sort(['combo_rank'], ascending=False).head(10)))

# 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:
  "ordering longs")
  "weight is %s" % (long_weight))
            order_target_percent(long_stock, long_weight)
    for short_stock in context.short_list.index:
        if short_stock in data:
  "ordering shorts")
  "weight is %s" % (short_weight))
            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.
3 responses

In numpy array indexing, [0] is the top row and [-1] is the bottom row. If it's 60 days of data, arranged oldest to newest, the newest is at the bottom at [-1] and the oldest, 60 days old, is at the top, at [0].

Then is the oldest called as [-60] or [0]? Would either work in this case?

Should be equivalent yeah.