Notebook
In [1]:
from quantopian.pipeline import Pipeline
from quantopian.research import run_pipeline
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline import CustomFactor
from quantopian.pipeline.filters import StaticAssets

import numpy as np
import alphalens

#import anything else as need be
In [2]:
#Momentum Custom Factor

class Momentum(CustomFactor):
    def compute(self, today, asset_ids, out, values):
        out[:] = values[-1] / values[0]
In [3]:
def make_pipeline():
    
    portfolio = (StaticAssets(symbols([
        'SPY',
        'VEU',
        'TLT',
    ])))
    #put alpha to test here
    moment = Momentum(inputs = [USEquityPricing.close], mask=portfolio, window_length = 252)
    
    #specify any filters here into universe. Make sure alphas have .notnull() in universe.
    universe = (moment.notnull())
    
    #Use print statements to make sure the BETTER one is ranked HIGHER. Eg Rank 150 is BETTER than rank 149.
    #first do it without ranking them, so you can ensure raw numbers make sense.
    testingFactor = moment.rank(method='average', 
                    ascending=True,
                    mask = universe)
    
    #Seperate into the desired quantiles, top X%, whatever you want here.
    longs = testingFactor.top(1)
    
    #if long and short strategy, specify which sections you would long/short. Returns a pipeline object.
    pipe = Pipeline(columns = {'testingFactor':testingFactor,
                              'Momentum': moment,
                              'long': longs},
                   screen = universe)
    
    return pipe
In [4]:
#Makes a dataframe with the given universe, ranked by the testing factor.
results = run_pipeline(make_pipeline(), start_date = '2008-04-01', end_date = '2009-01-01')

#print the results to make sure it fuckin worked. Print once without rankings to verify.
#results
In [5]:
#Gets each unique stock ticker and puts it in assets
assets = results.index.levels[1].unique()

#gets pricing data. Needs a month before and after. Dunno why.
pricing = get_pricing(assets, start_date='2008-03-01', end_date='2009-02-01', fields='open_price')
In [6]:
#Look up documentation to see how to customize this. This all organizes info so alphalens can get to work.
factor_data = alphalens.utils.get_clean_factor_and_forward_returns(factor = results['testingFactor'],  
                                                                   prices = pricing,  
                                                                   quantiles =3,
                                                                   periods = (1, 30,60))

# Runs alphalens
alphalens.tears.create_full_tear_sheet(factor_data)
Dropped 21.2% entries from factor data: 21.2% in forward returns computation and 0.0% in binning phase (set max_loss=0 to see potentially suppressed Exceptions).
max_loss is 35.0%, not exceeded: OK!
Quantiles Statistics
min max mean std count count %
factor_quantile
1 1.0 1.0 1.0 0.0 152 33.333333
2 2.0 2.0 2.0 0.0 152 33.333333
3 3.0 3.0 3.0 0.0 152 33.333333
Returns Analysis
1D 30D 60D
Ann. alpha -0.124 0.131 0.249
beta -1.002 -0.714 -0.642
Mean Period Wise Return Top Quantile (bps) 12.394 24.232 25.437
Mean Period Wise Return Bottom Quantile (bps) -5.235 -11.137 -11.293
Mean Period Wise Spread (bps) 17.628 35.033 36.469
<matplotlib.figure.Figure at 0x7f657ea60fd0>
Information Analysis
1D 30D 60D
IC Mean 0.089 0.411 0.612
IC Std. 0.798 0.604 0.321
Risk-Adjusted IC 0.111 0.681 1.904
t-stat(IC) 1.373 8.396 23.470
p-value(IC) 0.172 0.000 0.000
IC Skew -0.204 -0.833 -1.440
IC Kurtosis -1.607 -0.585 5.683
Turnover Analysis
1D 30D 60D
Quantile 1 Mean Turnover 0.033 0.246 0.620
Quantile 2 Mean Turnover 0.086 0.287 0.652
Quantile 3 Mean Turnover 0.053 0.041 0.043
1D 30D 60D
Mean Factor Rank Autocorrelation 0.957 0.857 0.663