Find rank of current day return against past x days

Hi, new to Quantopian and Python.I need create a ranking function to rank current days returns against returns of previous X days

Rank = # days with return lower than today / # of days in lookback

So first need to find returns for past X days then compare that list with todays return, count the number of days with a lower return than today and divide by total days in lookback. Lookback will be around 100 days. This function must be able to work on a universe of stocks.

Any help is appreciated!

9 responses

Hello Beau,

1. Please clarify what you mean by "returns." Are you wanting to analyze price movements, with a positive daily return for a given security being an appreciation in price from the close of one day to the close of the next (or from open to close of the same day)? Or are you looking to establish positions in securities (long/short) and then rank the performance of each security? Also, if you are establishing positions, would you only consider realized returns, after closing out positions?
2. If you are interested in a "universe of stocks" I suggest having a look at the set_universe functionality described on the Quantopian help page (although at this point, to keep things simple, you might start with a short, fixed list of securities).
3. The Python coding seems pretty straightforward. Are you familiar with MATLAB? If so, Python numpy is very similar (e.g. see http://wiki.scipy.org/NumPy_for_Matlab_Users).
4. You can use the batch transform described on the Quantopian help page to accumulate a trailing window of daily OHLCV data.

Grant

Hi Grant, thanks for the reply and link. I haven't used Matlab since college but this is helpful nonetheless...

I'm looking to make the calculation on price movements, "with a positive daily return for a given security being an appreciation in price from the close of one day to the close of the next". Stated another way, I want to look at the changes in close price over the last 100 days and count the number of days where that change is less than the most recent change.

Seems easy enough to do with a loop of some sort, but I want to be most efficient since I'm going to be using a universe of stocks. And I kinda figured this would go into a batch transform but wanted to verify that as well...

Thanks,
Beau

Hello Beau,

Yes, the batch transform should do the trick. I've attached an example. It returns the prices in the columns of a numpy ndarray. The columns are ordered by sid in the list context.stocks. You can use numpy to compute the differences, do the counting, etc.

I don't have time now to work on an example...give it a go and post here if you get stuck.

Grant

68
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
 Returns 1 Month 3 Month 6 Month 12 Month
 Alpha 1 Month 3 Month 6 Month 12 Month
 Beta 1 Month 3 Month 6 Month 12 Month
 Sharpe 1 Month 3 Month 6 Month 12 Month
 Sortino 1 Month 3 Month 6 Month 12 Month
 Volatility 1 Month 3 Month 6 Month 12 Month
 Max Drawdown 1 Month 3 Month 6 Month 12 Month
def initialize(context):

context.stocks = []

set_universe(universe.DollarVolumeUniverse(98, 98.1))

def handle_data(context, data):

context.stocks = [sid for sid in data]

prices = get_data(data,context.stocks)

print prices
print type(prices)

@batch_transform(refresh_period=1, window_length=5)
def get_data(datapanel,sids):
p = datapanel['price'].as_matrix(sids)
return p
This backtest was created using an older version of the backtester. Please re-run this backtest to see results using the latest backtester. Learn more about the recent changes.
There was a runtime error.

Hello Beau,

I made a little headway. I think that I now have a row vector that contains the ranking you are looking for (not yet normalized). The next steps sorta depend on what you want to do. Are you looking to trade based on the info., or something else?

Grant

9
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
 Returns 1 Month 3 Month 6 Month 12 Month
 Alpha 1 Month 3 Month 6 Month 12 Month
 Beta 1 Month 3 Month 6 Month 12 Month
 Sharpe 1 Month 3 Month 6 Month 12 Month
 Sortino 1 Month 3 Month 6 Month 12 Month
 Volatility 1 Month 3 Month 6 Month 12 Month
 Max Drawdown 1 Month 3 Month 6 Month 12 Month
import numpy as np

def initialize(context):

context.stocks = []

set_universe(universe.DollarVolumeUniverse(98, 98.1))

def handle_data(context, data):

context.stocks = [sid for sid in data]

result = get_data(data,context.stocks)
if result == None:
return

print result[0]
print result[1]
print result[2]

@batch_transform(refresh_period=1, window_length=5)
def get_data(datapanel,sids):
p = datapanel['price'].as_matrix(sids)
delta_p = np.subtract(p[1:],p[0:-1])
delta_p_today = np.tile(delta_p[-1:,:],(delta_p.shape[0],1))
comp = np.less(delta_p,delta_p_today)
rank = np.sum(comp,axis=0)
return (p,delta_p,rank)
This backtest was created using an older version of the backtester. Please re-run this backtest to see results using the latest backtester. Learn more about the recent changes.
There was a runtime error.

Thanks Grant, I think I understand it although how do I then reference a particular stock in the returned rank list? I'm using a loop to loop through a universe of stocks to issue orders so need to be able to pull out the corresponding rank for my signal calculation

Thanks,
Beau

Is it possible to use the ta lib rsi within a batch_transform?

Beau,

Glad that you're getting a feel for things.

I expect that there is a canned function in numpy that will allow you to do a sort/rank and also provide an array of the original indices. For example, if the original array of values is [5 7 2 4], then in ascending order, the array would be [2 4 5 7] with corresponding original indices 2, 3, 0, 1.

Regarding TA-Lib RSI with the batch transform, the answer is yes, it can be done as an unsupported hack. However, you might want to wait until some new functionality gets released described on https://github.com/quantopian/quantopian-drafts/blob/master/data-history-draft.md and discussed on https://www.quantopian.com/posts/draft-proposal-of-new-data-history-api.

Grant

The proposal looks much more intuitive and easy to use. Wonder how long it will take to release...

Hi,

In the ensuing 2 years since the last post in this thread, we've launched history and (more recently) pipeline. Attached is a backtest showing the metric described by the original poster using pipeline. I rank all securities with that factor and build a long/short portfolio with the top 100 vs the bottom 100.

happy coding,
fawce

9
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
 Returns 1 Month 3 Month 6 Month 12 Month
 Alpha 1 Month 3 Month 6 Month 12 Month
 Beta 1 Month 3 Month 6 Month 12 Month
 Sharpe 1 Month 3 Month 6 Month 12 Month
 Sortino 1 Month 3 Month 6 Month 12 Month
 Volatility 1 Month 3 Month 6 Month 12 Month
 Max Drawdown 1 Month 3 Month 6 Month 12 Month
"""
This example comes from a request in the forums.
The post can be found here: https://www.quantopian.com/posts/find-rank-of-current-day-return-against-past-x-days

The request was:

- Look back N days
- Score all stocks as # days lower than today / # of days in lookback

"""
import numpy as np

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

# Share of days outperforming the last day's return.
class OutperformanceDays(CustomFactor):

# Pre-declare inputs and window_length
inputs = [USEquityPricing.close]

# Compute factor1 value
def compute(self, today, assets, out, close_price):
t0 = close_price[:-1]
t1 = close_price[1:]
rets = (t1 - t0)/t0
above_today = rets > rets[-1]
above_count = np.sum(above_today, axis=0).astype(float)
# we're not calculating the returns for day 0, so
# denominator is len - 1
scores = above_count / float(len(close_price) - 1)
out[:] = scores

def initialize(context):
context.long_leverage = 0.50
context.short_leverage = -0.50

# Scedule my rebalance function. Will rebalance the portfolio
# to match long_list and short_list calculated in before_trading_start
# using the outperformance factor.
schedule_function(func=rebalance,
date_rule=date_rules.month_start(days_offset=0),
time_rule=time_rules.market_close(hours=0, minutes=30),
half_days=True)

pipe = Pipeline()
attach_pipeline(pipe, 'outperformance')

#add the outperformance factor to the pipeline
returns_factor = OutperformanceDays(window_length=101)

# Call pipelive_output to get the output
# Note this is a dataframe where the index is the SIDs for all securities to pass my screen
# and the colums are the factors which I added to the pipeline
context.output = pipeline_output('outperformance').sort(['outperformance_rank'])

context.long_list = context.output.iloc[-100:]
context.short_list = context.output.iloc[:100]
portfolio_stocks = context.long_list.index.union(context.short_list.index)

update_universe(portfolio_stocks)

def handle_data(context, data):

# Record and plot the leverage of our portfolio over time.
record(leverage = context.account.leverage)

# 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:
if long_stock in data:
order_target_percent(long_stock, long_weight)

for short_stock in context.short_list.index:
if short_stock in data:
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)


There was a runtime error.
Disclaimer

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by Quantopian. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. No information contained herein should be regarded as a suggestion to engage in or refrain from any investment-related course of action as none of Quantopian nor any of its affiliates is undertaking to provide investment advice, act as an adviser to any plan or entity subject to the Employee Retirement Income Security Act of 1974, as amended, individual retirement account or individual retirement annuity, or give advice in a fiduciary capacity with respect to the materials presented herein. If you are an individual retirement or other investor, contact your financial advisor or other fiduciary unrelated to Quantopian about whether any given investment idea, strategy, product or service described herein may be appropriate for your circumstances. All investments involve risk, including loss of principal. Quantopian makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances.