Notebook

Alpha research - Quality companies

220520/19:53

Using a composite factor we can evaluate the quality of a company.

In [1]:
# Research-specific imports
from quantopian.research import run_pipeline
from alphalens.utils import get_clean_factor_and_forward_returns
from alphalens.tears import create_summary_tear_sheet, create_full_tear_sheet


from quantopian.pipeline import Pipeline
from quantopian.pipeline.factors import CustomFactor
from quantopian.pipeline.data.builtin import EquityPricing
from quantopian.pipeline.data import Fundamentals
from quantopian.pipeline.data import factset


from quantopian.pipeline.filters import QTradableStocksUS

import numpy as np
import pandas as pd
from datetime import datetime, timedelta

# Pipeline parameters
USE_SECTORS = True
PIPE_NORMALIZE = True
TRAIN_START="2010-01-01"
TEST_START="2015-01-01"
VAL_START="2018-01-01"
In [2]:
# Utility functions

def price_data(pipeline_output, start=TRAIN_START, end=TEST_START):
    tickerlist = pipeline_output.index.levels[1].unique()
    # Subtract/add one month from bounds
    start_minus_one = datetime.strftime(datetime.strptime(start, "%Y-%m-%d") - timedelta(30), "%Y-%m-%d")
    end_plus_one = datetime.strftime(datetime.strptime(end, "%Y-%m-%d") + timedelta(30), "%Y-%m-%d")
    
    return get_pricing(symbols=tickerlist, start_date=start_minus_one, end_date=end_plus_one, fields="open_price"
    )
In [3]:
def make_pipeline():
    # Measures a company's asset growth rate.
    sector = factset.RBICSFocus.l1_name.latest
    mask = (sector != "Finance")
    
    roic = Fundamentals.roic.latest
    ltd_to_eq = Fundamentals.long_term_debt_equity_ratio.latest
    fcf_yield = Fundamentals.fcf_yield.latest
    
    factor = roic.rank(mask=mask) + \
                ltd_to_eq.rank(ascending=False, mask=mask) + \
                fcf_yield.rank(mask=mask)
        
    return Pipeline(
        columns={'signal': factor.rank(),
                 'sector':sector},screen=(QTradableStocksUS()) & mask)


pipeline_out = run_pipeline(pipeline=make_pipeline(), start_date=TRAIN_START, end_date=TEST_START)
factor_data = pipeline_out["signal"]
sector = pipeline_out["sector"]
factor_data.head()

Pipeline Execution Time: 33.42 Seconds
Out[3]:
2010-01-04 00:00:00+00:00  Equity(2 [HWM])       593.0
                           Equity(24 [AAPL])    3160.0
                           Equity(31 [ABAX])    2915.0
                           Equity(41 [ARCB])    1254.0
                           Equity(52 [ABM])     2556.0
Name: signal, dtype: float64
In [4]:
prices = price_data(factor_data)
In [5]:
merged_data = get_clean_factor_and_forward_returns(
  factor=factor_data, 
  prices=prices,
  groupby=sector,
  periods=(5, 10, 20)
)
merged_data.head()
Dropped 5.3% entries from factor data: 5.3% 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!
Out[5]:
5D 10D 20D factor group factor_quantile
date asset
2010-01-04 00:00:00+00:00 Equity(2 [HWM]) 0.061973 -0.053995 -0.165114 593.0 Non-Energy Materials 1
Equity(24 [AAPL]) -0.002944 -0.023897 -0.082081 3160.0 Technology 5
Equity(31 [ABAX]) 0.013685 0.001171 -0.073836 2915.0 Healthcare 5
Equity(41 [ARCB]) -0.039429 -0.051987 -0.237170 1254.0 Industrials 2
Equity(52 [ABM]) 0.027132 -0.013840 -0.053443 2556.0 Business Services 4
In [6]:
create_summary_tear_sheet(merged_data)
Quantiles Statistics
min max mean std count count %
factor_quantile
1 1.0 1093.0 503.656044 268.104052 357961 20.027471
2 784.0 1736.0 1281.108052 207.172467 357237 19.986964
3 1447.0 2352.0 1888.019661 187.821798 357207 19.985285
4 2013.0 2914.0 2446.434739 185.822167 357237 19.986964
5 2532.0 3469.0 2997.478046 189.322636 357708 20.013316
Returns Analysis
5D 10D 20D
Ann. alpha 0.048 0.047 0.045
beta -0.088 -0.089 -0.083
Mean Period Wise Return Top Quantile (bps) 6.201 6.068 5.263
Mean Period Wise Return Bottom Quantile (bps) -8.609 -8.855 -9.410
Mean Period Wise Spread (bps) 14.810 15.014 14.803
Information Analysis
5D 10D 20D
IC Mean 0.021 0.025 0.030
IC Std. 0.082 0.081 0.079
Risk-Adjusted IC 0.258 0.312 0.385
t-stat(IC) 9.143 11.072 13.672
p-value(IC) 0.000 0.000 0.000
IC Skew 0.025 -0.143 -0.104
IC Kurtosis 0.037 -0.122 -0.060
Turnover Analysis
5D 10D 20D
Quantile 1 Mean Turnover 0.034 0.063 0.117
Quantile 2 Mean Turnover 0.064 0.119 0.219
Quantile 3 Mean Turnover 0.072 0.133 0.242
Quantile 4 Mean Turnover 0.066 0.121 0.220
Quantile 5 Mean Turnover 0.034 0.063 0.115
5D 10D 20D
Mean Factor Rank Autocorrelation 0.989 0.978 0.956
<matplotlib.figure.Figure at 0x7f703d04c4e0>
In [7]:
create_full_tear_sheet(merged_data, by_group=True)
Quantiles Statistics
min max mean std count count %
factor_quantile
1 1.0 1093.0 503.656044 268.104052 357961 20.027471
2 784.0 1736.0 1281.108052 207.172467 357237 19.986964
3 1447.0 2352.0 1888.019661 187.821798 357207 19.985285
4 2013.0 2914.0 2446.434739 185.822167 357237 19.986964
5 2532.0 3469.0 2997.478046 189.322636 357708 20.013316
Returns Analysis
5D 10D 20D
Ann. alpha 0.048 0.047 0.045
beta -0.088 -0.089 -0.083
Mean Period Wise Return Top Quantile (bps) 6.201 6.068 5.263
Mean Period Wise Return Bottom Quantile (bps) -8.609 -8.855 -9.410
Mean Period Wise Spread (bps) 14.810 15.014 14.803
<matplotlib.figure.Figure at 0x7f701ae2dba8>
/venvs/py35/lib/python3.5/site-packages/pandas/core/groupby.py:2193: FutureWarning: 
Setting NaNs in `categories` is deprecated and will be removed in a future version of pandas.
  ordered=self.grouper.ordered))
/venvs/py35/lib/python3.5/site-packages/pandas/indexes/category.py:121: FutureWarning: 
Setting NaNs in `categories` is deprecated and will be removed in a future version of pandas.
  data = data.set_categories(categories)
/venvs/py35/lib/python3.5/site-packages/pandas/indexes/category.py:96: FutureWarning: 
Setting NaNs in `categories` is deprecated and will be removed in a future version of pandas.
  ordered=self.ordered)
Information Analysis
5D 10D 20D
IC Mean 0.021 0.025 0.030
IC Std. 0.082 0.081 0.079
Risk-Adjusted IC 0.258 0.312 0.385
t-stat(IC) 9.143 11.072 13.672
p-value(IC) 0.000 0.000 0.000
IC Skew 0.025 -0.143 -0.104
IC Kurtosis 0.037 -0.122 -0.060
/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/pandas/core/categorical.py:387: FutureWarning: 
Setting NaNs in `categories` is deprecated and will be removed in a future version of pandas.
  return Categorical(data, **kwargs)
/venvs/py35/lib/python3.5/site-packages/alphalens/utils.py:912: UserWarning: Skipping return periods that aren't exact multiples of days.
  + " of days."
Turnover Analysis
5D 10D 20D
Quantile 1 Mean Turnover 0.034 0.063 0.117
Quantile 2 Mean Turnover 0.064 0.119 0.219
Quantile 3 Mean Turnover 0.072 0.133 0.242
Quantile 4 Mean Turnover 0.066 0.121 0.220
Quantile 5 Mean Turnover 0.034 0.063 0.115
5D 10D 20D
Mean Factor Rank Autocorrelation 0.989 0.978 0.956
In [ ]:
 
In [ ]:
 
In [ ]: