## Thursday, 2 July 2015

### Quantopian Algorithmic Trading Strategy: Mean Reversion

Mean reversion is a tendency for a stochastic process to remain near, or tend to return over time to a long-run average value. Interest rates and implied volatilities tend to exhibit mean reversion

The following algorithm chooses a basket of high volume securities and ranks them on the basis of 5 day returns. Stocks with the top 20% highest returns are shorted and the algo goes long in bottom 20% stocks with the lowest returns. The investment thesis behind this strategy is that the previously high performing stocks from the previous week will worsen in performance this week, whilst the lowest performing stocks from the previous week will deliver in performance.

import numpy as np

def initialize(context):
set_universe(universe.DollarVolumeUniverse(99, 100)
set_slippage(slippage.VolumeShareSlippage(volume_limit=0.025, price_impact=0.10))
context.long_leverage = 0.5
context.short_leverage = -0.5
context.lower_percentile = 20
context.upper_percentile = 80
context.returns_lookback = 5

We firstly import the Python libraries we use in the algorithm. Defining the initialise function will set the stocks used in trading and establish parameters. Context refers to an empty Python dictionary, augmented so properties can be accessed via dot and bracket. In this case, we use the top 1% of stocks based on their average trading volume per day. For live trading, we assume a a \$0.0075 per share commission cost with a \$1 minimum cost per trade. Market impact assumptions are also defined, the algo limits trading up to 0.025 of the traded volume for any given minute, and price impact is 0.1. I will be experimenting with slippage where there is additional flexibility for each stock's spread in my next post. We specify going short in the top 20% of stocks with highest returns from their previous 5 day returns.

schedule_function(rebalance,
date_rules.week_start(days_offset=0),
time_rules.market_open(hours = 1, minutes = 00))

def handle_data(context,data):
record(leverage = context.account.leverage)
longs = shorts = 0
for position in context.portfolio.positions.itervalues():
if position.amount > 0:
longs += 1
if position.amount < 0:
shorts += 1
record(long_count=longs, short_count=shorts)

The schedule function specifies the portfolio is rebalanced every Monday an hour after markets open (no significant difference found in this timing). Handle_data is called whenever there is a market event that occurs for any of the algorithm's specified stocks. Context and data represent parameters. We monitor and record portfolio leverage, and number of long and short positions. The for loop ensures that the counts of long and short positions are added to the plot based on position sizes.

def rebalance(context,data):
prices = history(context.returns_lookback, '1d', 'price')
returns = (prices.iloc[-1] - prices.iloc) / prices.iloc
returns = returns.dropna()
open_orders = get_open_orders()
if open_orders:
eligible_secs = [sec for sec in data if sec not in open_orders]
returns = returns[eligible_secs]
lower, upper = np.percentile(returns, [context.lower_percentile,
context.upper_percentile])
long_secs = returns[returns <= lower]
short_secs = returns[returns >= upper]
long_weight = context.long_leverage / len(long_secs)
short_weight = context.short_leverage / len(short_secs)
if security in long_secs:
order_target_percent(security, long_weight)
elif security in short_secs:
order_target_percent(security, short_weight)
else:
order_target(security, 0)

log.info("This week's longs: "+", ".join([long_.symbol for long_ in long_secs.index]))
log.info("This week's shorts: "  +", ".join([short_.symbol for short_ in short_secs.index]))

Returns are calculated for the past 5 days based on prices. The algorithm drops stocks where prices are not available and removes stocks that are still in open orders. Cut-offs are defined in terms of return percentiles. We select the stocks based on their performance to go long and short in, then allocate even weights in each long short position. The if, elif and else statement instructs buying/selling of securities depending on their returns, or otherwise close the position. The log shows the securities in each long and short portfolio for each week.

Holding all other variables constant while changing the lower and upper percentile to 5 and 95% respectively yields a higher return of 8.5% compared to 2.9%.

Key terms

Alpha: The excess return relative to the return of the benchmark index is a fund's alpha.

Beta: A measure of the volatility, or systematic risk, of a security or a portfolio in comparison to the market as a whole.

Sharpe ratio: The Sharpe ratio is the average return earned in excess of the risk-free rate per unit of volatility or total risk. Simply put, it is the risk adjusted return.

Sortino: A modification of the Sharpe ratio that differentiates harmful volatility from general volatility by taking into account the standard deviation of negative asset returns, called downside deviation. The Sortino ratio subtracts the risk-free rate of return from the portfolio’s return, and then divides that by the downside deviation. A large Sortino ratio indicates there is a low probability of a large loss.

Information Ratio: A ratio of portfolio returns above the returns of a benchmark (usually an index) to the volatility of those returns. The information ratio (IR) measures a portfolio manager's ability to generate excess returns relative to a benchmark, but also attempts to identify the consistency of the investor. This ratio will identify if a manager has beaten the benchmark by a lot in a few months or a little every month. The higher the IR the more consistent a manager is and consistency is an ideal trait.

Max drawdown: An indicator of the risk of a portfolio chosen based on a certain strategy. It measures the largest single drop from peak to bottom in the value of a portfolio (before a new peak is achieved).