Last active
June 6, 2017 19:51
-
-
Save mushketyk/28ce28c7cbf2e556aa992b5afe7e29d4 to your computer and use it in GitHub Desktop.
Quantopian alphalens
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import math | |
import numpy as np | |
from quantopian.research import run_pipeline | |
from quantopian.pipeline import Pipeline | |
from quantopian.pipeline.data.builtin import USEquityPricing | |
from quantopian.pipeline.factors import AverageDollarVolume, CustomFactor, AnnualizedVolatility | |
from quantopian.pipeline.filters.morningstar import Q3000US | |
from quantopian.pipeline.classifiers.morningstar import Sector | |
from quantopian.pipeline.data import morningstar as mstar | |
from quantopian.pipeline.filters.morningstar import IsPrimaryShare | |
from quantopian.pipeline.data.zacks import EarningsSurprises | |
class RateOfChange(CustomFactor): | |
""" | |
Compute rate of change for a stock | |
""" | |
def compute(self, today, assets, out, data): | |
out[:] = (data[len(data) - 1] - data[0]) / data[0] | |
def make_pipeline(): | |
# Base universe set to the Q500US | |
base_universe = Q3000US() | |
# Set dollar volume | |
high_dollar_volume = AverageDollarVolume(window_length=7) > 500000 | |
# Ensure that the price greater than $1 | |
price_in_range_filter = PriceInRange(inputs=[USEquityPricing.close]) > 0 | |
universe_mask = universe_filters() & base_universe | |
# Factor of yesterday's close price. | |
yesterday_close = USEquityPricing.close.latest | |
rate_of_change = RateOfChange(inputs=[USEquityPricing.close], window_length=100, mask=universe_mask) | |
high_momentum = (rate_of_change > 0.3) | |
pipe = Pipeline( | |
screen = universe_mask, | |
columns = { | |
'close': yesterday_close, | |
'rate_of_change': rate_of_change, | |
'high_momentum': high_momentum | |
} | |
) | |
return pipe | |
# Constants that need to be global | |
COMMON_STOCK= 'ST00000001' | |
SECTOR_NAMES = { | |
101: 'Basic Materials', | |
102: 'Consumer Cyclical', | |
103: 'Financial Services', | |
104: 'Real Estate', | |
205: 'Consumer Defensive', | |
206: 'Healthcare', | |
207: 'Utilities', | |
308: 'Communication Services', | |
309: 'Energy', | |
310: 'Industrials', | |
311: 'Technology' , | |
} | |
# Average Dollar Volume without nanmean, so that recent IPOs are truly removed | |
class ADV_adj(CustomFactor): | |
inputs = [USEquityPricing.close, USEquityPricing.volume] | |
window_length = 252 | |
def compute(self, today, assets, out, close, volume): | |
close[np.isnan(close)] = 0 | |
out[:] = np.mean(close * volume, 0) | |
def universe_filters(): | |
""" | |
Create a Pipeline producing Filters implementing common acceptance criteria. | |
Returns | |
------- | |
zipline.Filter | |
Filter to control tradeablility | |
""" | |
# Equities with an average daily volume greater than 750000. | |
high_volume = (AverageDollarVolume(window_length=252) > 750000) | |
# Not Misc. sector: | |
sector_check = Sector().notnull() | |
# Equities that morningstar lists as primary shares. | |
# NOTE: This will return False for stocks not in the morningstar database. | |
primary_share = IsPrimaryShare() | |
# Equities for which morningstar's most recent Market Cap value is above $300m. | |
have_market_cap = mstar.valuation.market_cap.latest > 300000000 | |
# Equities not listed as depositary receipts by morningstar. | |
# Note the inversion operator, `~`, at the start of the expression. | |
not_depositary = ~mstar.share_class_reference.is_depositary_receipt.latest | |
# Equities that listed as common stock (as opposed to, say, preferred stock). | |
# This is our first string column. The .eq method used here produces a Filter returning | |
# True for all asset/date pairs where security_type produced a value of 'ST00000001'. | |
common_stock = mstar.share_class_reference.security_type.latest.eq(COMMON_STOCK) | |
# Equities whose exchange id does not start with OTC (Over The Counter). | |
# startswith() is a new method available only on string-dtype Classifiers. | |
# It returns a Filter. | |
not_otc = ~mstar.share_class_reference.exchange_id.latest.startswith('OTC') | |
# Equities whose symbol (according to morningstar) ends with .WI | |
# This generally indicates a "When Issued" offering. | |
# endswith() works similarly to startswith(). | |
not_wi = ~mstar.share_class_reference.symbol.latest.endswith('.WI') | |
# Equities whose company name ends with 'LP' or a similar string. | |
# The .matches() method uses the standard library `re` module to match | |
# against a regular expression. | |
not_lp_name = ~mstar.company_reference.standard_name.latest.matches('.* L[\\. ]?P\.?$') | |
# Equities with a null entry for the balance_sheet.limited_partnership field. | |
# This is an alternative way of checking for LPs. | |
not_lp_balance_sheet = mstar.balance_sheet.limited_partnership.latest.isnull() | |
# Highly liquid assets only. Also eliminates IPOs in the past 12 months | |
# Use new average dollar volume so that unrecorded days are given value 0 | |
# and not skipped over | |
# S&P Criterion | |
liquid = ADV_adj() > 250000 | |
# Add logic when global markets supported | |
# S&P Criterion | |
domicile = True | |
# Keep it to liquid securities | |
ranked_liquid = ADV_adj().rank(ascending=False) < 1500 | |
universe_filter = (high_volume & primary_share & have_market_cap & not_depositary & | |
common_stock & not_otc & not_wi & not_lp_name & not_lp_balance_sheet & | |
liquid & domicile & sector_check & liquid & ranked_liquid) | |
return universe_filter | |
p = run_pipeline(make_pipeline(), '2017-01-01', '2017-05-05') | |
p | |
assets = p.index.levels[1].unique() | |
len(assets) | |
pricing = get_pricing(assets, start_date='2016-12-01', end_date='2017-06-05', fields='open_price') | |
import alphalens | |
factor_data = alphalens.utils.get_clean_factor_and_forward_returns(p['rate_of_change'], prices=pricing, quantiles=1, periods=(1,5,10)) | |
alphalens.tears.create_full_tear_sheet(factor_data) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment