Skip to content

Instantly share code, notes, and snippets.

@scubamut
Last active February 12, 2019 11:46
Show Gist options
  • Save scubamut/1c4521356bf9354a2de59516e3408e8a to your computer and use it in GitHub Desktop.
Save scubamut/1c4521356bf9354a2de59516e3408e8a to your computer and use it in GitHub Desktop.
# https://www.quantopian.com/posts/introducing-the-pipeline-api
"""
This example comes from a request in the forums.
The post can be found here: https://www.quantopian.com/posts/ranking-system-based-on-trading-volume-slash-shares-outstanding
The request was:
I am stuck trying to build a stock ranking system with two signals:
1. Trading Volume/Shares Outstanding.
2. Price of current day / Price of 60 days ago.
Then rank Russell 2000 stocks every month, long the top 5%, short the bottom 5%.
"""
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline import CustomFactor
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.data import morningstar
# Create custom factor #1 Trading Volume/Shares Outstanding
class Liquidity(CustomFactor):
# Pre-declare inputs and window_length
inputs = [USEquityPricing.volume, morningstar.valuation.shares_outstanding]
window_length = 1
# Compute factor1 value
def compute(self, today, assets, out, volume, shares):
out[:] = volume[-1]/shares[-1]
# Create custom factor #2 Price of current day / Price of 60 days ago.
class Momentum(CustomFactor):
# Pre-declare inputs and window_length
inputs = [USEquityPricing.close]
window_length = 60
# Compute factor2 value
def compute(self, today, assets, out, close):
out[:] = close[-1]/close[0]
# Create custom factor to calculate a market cap based on yesterday's close
# We'll use this to get the top 2000 stocks by market cap
class MarketCap(CustomFactor):
# Pre-declare inputs and window_length
inputs = [USEquityPricing.close, morningstar.valuation.shares_outstanding]
window_length = 1
# Compute market cap value
def compute(self, today, assets, out, close, shares):
out[:] = close[-1] * shares[-1]
def initialize(context):
pipe = Pipeline()
attach_pipeline(pipe, 'ranked_2000')
# Add the two factors defined to the pipeline
liquidity = Liquidity()
pipe.add(liquidity, 'liquidity')
momentum = Momentum()
pipe.add(momentum, 'momentum')
# Create and apply a filter representing the top 2000 equities by MarketCap every day
# This is an approximation of the Russell 2000
mkt_cap = MarketCap()
top_2000 = mkt_cap.top(2000)
# Rank factor 1 and add the rank to our pipeline
liquidity_rank = liquidity.rank(mask=top_2000)
pipe.add(liquidity_rank, 'liq_rank')
# Rank factor 2 and add the rank to our pipeline
momentum_rank = momentum.rank(mask=top_2000)
pipe.add(momentum_rank, 'mom_rank')
# Take the average of the two factor rankings, add this to the pipeline
combo_raw = (liquidity_rank+momentum_rank)/2
pipe.add(combo_raw, 'combo_raw')
# Rank the combo_raw and add that to the pipeline
pipe.add(combo_raw.rank(mask=top_2000), 'combo_rank')
# Set a screen to ensure that only the top 2000 companies by market cap
# with a momentum factor greater than 0 are returned
pipe.set_screen(top_2000 & (momentum>0))
# Scedule my rebalance function
schedule_function(func=rebalance,
date_rule=date_rules.month_start(days_offset=0),
time_rule=time_rules.market_open(hours=0,minutes=30),
half_days=True)
# Schedule my plotting function
schedule_function(func=record_vars,
date_rule=date_rules.every_day(),
time_rule=time_rules.market_close(),
half_days=True)
# set my leverage
context.long_leverage = 0.50
context.short_leverage = -0.50
def before_trading_start(context, data):
# Call pipelive_output to get the output
context.output = pipeline_output('ranked_2000')
# Narrow down the securities to only the top 200 & update my universe
context.long_list = context.output.sort(['combo_rank'], ascending=False).iloc[:100]
context.short_list = context.output.sort(['combo_rank'], ascending=False).iloc[-100:]
def record_vars(context, data):
# Record and plot the leverage of our portfolio over time.
record(leverage = context.account.leverage)
print "Long List"
log.info("\n" + str(context.long_list.sort(['combo_rank'], ascending=True).head(10)))
print "Short List"
log.info("\n" + str(context.short_list.sort(['combo_rank'], ascending=True).head(10)))
# This rebalancing is called according to our schedule_function settings.
def rebalance(context,data):
long_weight = context.long_leverage / float(len(context.long_list))
short_weight = context.short_leverage / float(len(context.short_list))
for long_stock in context.long_list.index:
log.info("ordering longs")
log.info("weight is %s" % (long_weight))
order_target_percent(long_stock, long_weight)
for short_stock in context.short_list.index:
log.info("ordering shorts")
log.info("weight is %s" % (short_weight))
order_target_percent(short_stock, short_weight)
for stock in context.portfolio.positions.iterkeys():
if stock not in context.long_list.index and stock not in context.short_list.index:
order_target(stock, 0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment