Skip to content

Instantly share code, notes, and snippets.

@mushketyk
Last active June 6, 2017 19:51
Show Gist options
  • Save mushketyk/28ce28c7cbf2e556aa992b5afe7e29d4 to your computer and use it in GitHub Desktop.
Save mushketyk/28ce28c7cbf2e556aa992b5afe7e29d4 to your computer and use it in GitHub Desktop.
Quantopian alphalens
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