Notebook
In [1]:
# Import Pipeline
from quantopian.pipeline import Pipeline
from quantopian.research import run_pipeline

# Import filter/screen
from quantopian.pipeline.filters import QTradableStocksUS

# Import data
from quantopian.pipeline.data import USEquityPricing
from quantopian.pipeline.data.factset import Fundamentals as ff
from quantopian.pipeline.data.morningstar import Fundamentals as mf

# Import classifier
from quantopian.pipeline.classifiers.fundamentals import Sector

# Import Alphalens
from alphalens.utils import get_clean_factor_and_forward_returns
from alphalens.tears import create_full_tear_sheet
In [2]:
# Constants
start_date = '2003-1-1'
end_date = '2020-2-1'

factor_name = 'fcf_yld'

sector_labels, sector_labels[-1] = dict(Sector.SECTOR_NAMES), "Unknown"

group_neutral = True

long_short = True
In [3]:
# Create pipeline function
def make_pipeline():
    
    # Get our primary data
    
    sector = Sector()
    
    fcf = mf.free_cash_flow.latest
    ev = mf.enterprise_value.latest
    
    # Calculate factor
    
    fcf_yld = fcf / ev
    
    #Create screens
    
    factor_screen = fcf_yld.notnull()
    
    sector_screen = sector.notnull()
    
    # Return pipeline
    
    return Pipeline(
        columns={
            factor_name: fcf_yld,
            'sector': sector
        },
        screen=QTradableStocksUS() & factor_screen & sector_screen,
    )
In [4]:
# Get factor data from pipeline
factor_data = run_pipeline(make_pipeline(), start_date, end_date)

Pipeline Execution Time: 1 Minute, 6.40 Seconds
In [5]:
factor_data.head()
Out[5]:
fcf_yld sector
2003-01-02 00:00:00+00:00 Equity(2 [ARNC]) 0.002078 101
Equity(24 [AAPL]) 0.017921 311
Equity(41 [ARCB]) 0.001965 310
Equity(60 [ABS]) 0.010970 205
Equity(62 [ABT]) 0.017620 206
In [6]:
# Get pricing data
pricing_data = get_pricing(factor_data.index.levels[1], start_date, end_date, fields='open_price')
In [7]:
pricing_data.head()
Out[7]:
Equity(2 [ARNC]) Equity(24 [AAPL]) Equity(31 [ABAX]) Equity(39 [DDC]) Equity(41 [ARCB]) Equity(52 [ABM]) Equity(53 [ABMD]) Equity(60 [ABS]) Equity(62 [ABT]) Equity(64 [GOLD]) ... Equity(53083 [TPTX]) Equity(53084 [PLMR]) Equity(53087 [MNRL]) Equity(53089 [PINS]) Equity(53095 [ZM]) Equity(53116 [BYND]) Equity(53123 [SCPL]) Equity(53134 [PSN]) Equity(53148 [KTB]) Equity(53158 [UBER])
2003-01-02 00:00:00+00:00 53.069 0.892 3.485 16.224 21.065 10.468 3.820 20.067 11.834 12.635 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2003-01-03 00:00:00+00:00 54.267 0.920 3.631 16.397 22.593 10.543 3.990 20.760 11.630 12.890 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2003-01-06 00:00:00+00:00 55.189 0.934 3.738 17.290 22.397 10.435 3.750 20.822 11.712 13.245 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2003-01-07 00:00:00+00:00 56.226 0.919 3.775 16.868 22.888 10.624 4.010 20.866 11.849 12.816 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2003-01-08 00:00:00+00:00 50.926 0.906 3.831 16.438 22.790 10.367 3.849 20.999 11.441 12.791 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

5 rows × 4501 columns

In [8]:
# Get factor and returns data
merged_data = get_clean_factor_and_forward_returns(
    factor=factor_data[factor_name],
    prices=pricing_data,
    periods=(1,5,10),
    quantiles=5,
    groupby=factor_data['sector'],
    groupby_labels = sector_labels,
    binning_by_group = group_neutral,
)
Dropped 0.4% entries from factor data: 0.4% 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!
In [9]:
merged_data.tail()
Out[9]:
1D 5D 10D factor group factor_quantile
date asset
2020-01-16 00:00:00+00:00 Equity(53083 [TPTX]) 0.078967 0.086479 0.105716 -0.011467 HEALTHCARE 2
Equity(53084 [PLMR]) 0.018449 0.078358 0.113391 0.016881 FINANCIAL_SERVICES 3
Equity(53087 [MNRL]) 0.005638 -0.051256 -0.125064 -0.074799 ENERGY 1
Equity(53089 [PINS]) 0.080036 0.026529 0.005845 -0.000099 COMMUNICATION_SERVICES 2
Equity(53095 [ZM]) -0.017179 -0.038846 -0.047821 0.002669 COMMUNICATION_SERVICES 2
In [10]:
# Create tear-sheet to analyse
create_full_tear_sheet(merged_data, long_short=long_short, by_group=True, group_neutral=group_neutral)
Quantiles Statistics
min max mean std count count %
factor_quantile
1 -141.491687 0.012416 -0.042931 0.454920 1509689 20.255242
2 -0.060626 0.025277 -0.001754 0.008584 1481262 19.873842
3 -0.027127 0.040363 0.007457 0.006743 1480849 19.868300
4 -0.013519 0.083592 0.015855 0.008268 1481262 19.873842
5 0.000006 941.911847 0.052910 0.922265 1500263 20.128775
Returns Analysis
1D 5D 10D
Ann. alpha 0.038 0.029 0.025
beta -0.052 -0.066 -0.066
Mean Period Wise Return Top Quantile (bps) 1.574 1.225 1.056
Mean Period Wise Return Bottom Quantile (bps) -0.956 -0.641 -0.534
Mean Period Wise Spread (bps) 2.529 1.885 1.609
<matplotlib.figure.Figure at 0x7fb4459a4320>
Information Analysis
1D 5D 10D
IC Mean 0.009 0.014 0.016
IC Std. 0.053 0.056 0.055
Risk-Adjusted IC 0.175 0.242 0.283
t-stat(IC) 11.459 15.861 18.531
p-value(IC) 0.000 0.000 0.000
IC Skew 0.032 0.100 0.067
IC Kurtosis 0.341 0.327 0.155
/venvs/py35/lib/python3.5/site-packages/statsmodels/nonparametric/kdetools.py:20: VisibleDeprecationWarning: using a non-integer number instead of an integer will result in an error in the future
  y = X[:m/2+1] + np.r_[0,X[m/2+1:],0]*1j
Turnover Analysis
10D 1D 5D
Quantile 1 Mean Turnover 0.124 0.017 0.068
Quantile 2 Mean Turnover 0.172 0.026 0.098
Quantile 3 Mean Turnover 0.188 0.030 0.109
Quantile 4 Mean Turnover 0.192 0.030 0.110
Quantile 5 Mean Turnover 0.146 0.020 0.080
1D 5D 10D
Mean Factor Rank Autocorrelation 0.989 0.943 0.886
In [ ]: