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-3-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, 8.59 Seconds
In [5]:
factor_data.head()
Out[5]:
fcf_yld sector
2003-01-02 00:00:00+00:00 Equity(2 [HWM]) 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 [HWM]) 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(53134 [PSN]) Equity(53145 [NXTC]) Equity(53148 [KTB]) Equity(53158 [UBER]) Equity(53179 [FSLY]) Equity(53181 [AVTR]) Equity(53196 [CTVA]) Equity(53238 [RVLV]) Equity(53244 [AMCR]) Equity(53248 [CRWD])
2003-01-02 00:00:00+00:00 53.035 0.890 3.485 16.224 20.992 10.468 3.820 20.067 11.834 12.593 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2003-01-03 00:00:00+00:00 54.233 0.918 3.631 16.397 22.515 10.543 3.990 20.760 11.630 12.848 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2003-01-06 00:00:00+00:00 55.154 0.932 3.738 17.290 22.320 10.435 3.750 20.822 11.712 13.201 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2003-01-07 00:00:00+00:00 56.190 0.917 3.775 16.868 22.808 10.624 4.010 20.866 11.849 12.774 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2003-01-08 00:00:00+00:00 50.893 0.904 3.831 16.438 22.711 10.367 3.849 20.999 11.441 12.749 ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

5 rows × 4547 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.5% entries from factor data: 0.5% 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-02-13 00:00:00+00:00 Equity(53148 [KTB]) 0.042726 -0.009919 -0.166836 -0.001281 CONSUMER_CYCLICAL 1
Equity(53158 [UBER]) -0.022233 -0.000977 -0.222819 -0.015046 TECHNOLOGY 1
Equity(53179 [FSLY]) 0.009362 0.006687 -0.162728 -0.008893 TECHNOLOGY 1
Equity(53181 [AVTR]) -0.004522 -0.005088 -0.122668 0.011475 BASIC_MATERIALS 3
Equity(53196 [CTVA]) -0.000652 0.021838 -0.135267 -0.064888 BASIC_MATERIALS 1
In [ ]:
# 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.012287 -0.042750 0.452359 1539482 20.249026
2 -0.060626 0.024909 -0.001723 0.008531 1511022 19.874687
3 -0.027127 0.040363 0.007450 0.006720 1511128 19.876082
4 -0.013519 0.083592 0.015847 0.008265 1511022 19.874687
5 0.000006 941.911847 0.052809 0.913270 1530092 20.125518
Returns Analysis
1D 5D 10D
Ann. alpha 0.038 0.029 0.025
beta -0.053 -0.067 -0.066
Mean Period Wise Return Top Quantile (bps) 1.562 1.226 1.066
Mean Period Wise Return Bottom Quantile (bps) -1.032 -0.667 -0.540
Mean Period Wise Spread (bps) 2.594 1.912 1.626
<matplotlib.figure.Figure at 0x7f894bbcdba8>
Information Analysis
1D 5D 10D
IC Mean 0.009 0.014 0.016
IC Std. 0.052 0.056 0.055
Risk-Adjusted IC 0.175 0.243 0.286
t-stat(IC) 11.518 15.973 18.761
p-value(IC) 0.000 0.000 0.000
IC Skew 0.039 0.103 0.078
IC Kurtosis 0.340 0.325 0.154
/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
/venvs/py35/lib/python3.5/site-packages/alphalens/utils.py:912: UserWarning: Skipping return periods that aren't exact multiples of days.
  + " of days."
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]: