This is a continuation of Last time.
LESSON 11 Putting It All Together Let's mobilize all that we've learned so far to implement a simple mean reversion strategy [^ 1].
Planning the Strategy First, let's make a strategic plan. If the 10-day simple moving average (hereafter SMA) is higher than the 30-day SMA, the stock price will fall, and vice versa, the stock price will rise. It takes advantage of the nature of mean reversion.
Selecting Assets to Trade
This type of strategy usually uses pipeline to set the stock, but uses the sid ()
learned so far. I will.
The code below sets 5 stocks. [^ 2]
# MSFT, UNH, CTAS, JNS, COG
context.security_list = [sid(5061), sid(7792), sid(1941), sid(24556), sid(1746)]
Setting a Rebalance Schedule
The code below runs the rebalance ()
function (discussed below) near the beginning of the week.
schedule_function(rebalance,
date_rules.week_start(days_offset=0),
time_rules.market_open())
Computing Weights Calculate the weight of your portfolio. According to the strategy mentioned above, we will long or short the stocks that meet the conditions.
In the code below, if the difference between the 10-day SMA and the 30-day SMA is large, the weight is increased, and the whole is normalized as 1.
def compute_weights(context, data):
# Get the 30-day price history for each security in our list.
hist = data.history(context.security_list, 'price', 30, '1d')
# Create 10-day and 30-day trailing windows.
prices_10 = hist[-10:]
prices_30 = hist
# 10-day and 30-day simple moving average (SMA)
sma_10 = prices_10.mean()
sma_30 = prices_30.mean()
# Weights are based on the relative difference between the short and long SMAs
raw_weights = (sma_30 - sma_10) / sma_30
# Normalize our weights
normalized_weights = raw_weights / raw_weights.abs().sum()
# Return our normalized weights. These will be used when placing orders later.
return normalized_weights
Order Execution
The contents of the rebalance ()
function scheduled in Setting a Rebalance Schedule. The stock set according to the weights calculated by the compute_weights
function above is long or short.
def rebalance(context, data):
# Calculate our target weights.
weights = compute_weights(context, data)
# Place orders for each of our securities.
for security in context.security_list:
if data.can_trade(security):
order_target_percent(security, weights[security])
Recording and Plotting
Record and visualize the number of longs and shorts.
The code below implements the recording code in the record_vars ()
function and executes it in the schedule_function ()
function. Scheduled for daily closing.
schedule_function(record_vars,
date_rules.every_day(),
time_rules.market_close())
The contents of the record_vars ()
function are as follows. You can check the number of stocks and the amount of positions in your portfolio by referring to context.portfolio.positions
. Here, we simply count the number of longs and the number of shorts.
def record_vars(context, data):
# Check how many long and short positions we have.
longs = shorts = 0
for position in context.portfolio.positions.itervalues():
if position.amount > 0:
longs += 1
elif position.amount < 0:
shorts += 1
# Record our variables.
record(leverage=context.account.leverage, long_count=longs, short_count=shorts)
Putting It Together
This strategy rebalances the portfolio on a weekly basis and records it on a daily basis. So you don't need the handle_data ()
function, which runs every minute.
The code up to the above can be summarized as the following code.
You can clone the code from here.
def initialize(context):
"""
initialize() is called once at the start of the program. Any one-time
startup logic goes here.
"""
# An assortment of securities from different sectors:
# MSFT, UNH, CTAS, JNS, COG
context.security_list = [sid(5061), sid(7792), sid(1941), sid(24556), sid(1746)]
# Rebalance every Monday (or the first trading day if it's a holiday)
# at market open.
schedule_function(rebalance,
date_rules.week_start(days_offset=0),
time_rules.market_open())
# Record variables at the end of each day.
schedule_function(record_vars,
date_rules.every_day(),
time_rules.market_close())
def compute_weights(context, data):
"""
Compute weights for each security that we want to order.
"""
# Get the 30-day price history for each security in our list.
hist = data.history(context.security_list, 'price', 30, '1d')
# Create 10-day and 30-day trailing windows.
prices_10 = hist[-10:]
prices_30 = hist
# 10-day and 30-day simple moving average (SMA)
sma_10 = prices_10.mean()
sma_30 = prices_30.mean()
# Weights are based on the relative difference between the short and long SMAs
raw_weights = (sma_30 - sma_10) / sma_30
# Normalize our weights
normalized_weights = raw_weights / raw_weights.abs().sum()
# Determine and log our long and short positions.
short_secs = normalized_weights.index[normalized_weights < 0]
long_secs = normalized_weights.index[normalized_weights > 0]
log.info("This week's longs: " + ", ".join([long_.symbol for long_ in long_secs]))
log.info("This week's shorts: " + ", ".join([short_.symbol for short_ in short_secs]))
# Return our normalized weights. These will be used when placing orders later.
return normalized_weights
def rebalance(context, data):
"""
This function is called according to our schedule_function settings and calls
order_target_percent() on every security in weights.
"""
# Calculate our target weights.
weights = compute_weights(context, data)
# Place orders for each of our securities.
for security in context.security_list:
if data.can_trade(security):
order_target_percent(security, weights[security])
def record_vars(context, data):
"""
This function is called at the end of each day and plots our leverage as well
as the number of long and short positions we are holding.
"""
# Check how many long and short positions we have.
longs = shorts = 0
for position in context.portfolio.positions.itervalues():
if position.amount > 0:
longs += 1
elif position.amount < 0:
shorts += 1
# Record our variables.
record(leverage=context.account.leverage, long_count=longs, short_count=shorts)
This completes Tutorial 1. There are four Tutorials in all, and Tutorial 2 is a description of Pipeline. We may do Tutorial 2 if requested, but we will end it for the time being. Thank you for your support.
LESSON 10 <-> Continue to Tutorial 2?
[^ 1]: Also known as the return reversal method. It is a method that uses the nature of mean reversion to contradict stocks that deviate from the mean value and try to make a profit. [^ 2]: The selected stock has no special meaning.
Recommended Posts