[PYTHON] Quantopian Tutorial LESSON 11

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)

in conclusion

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

Quantopian Tutorial LESSON 10
Quantopian Tutorial LESSON 1, 2
Quantopian Tutorial LESSON 9
Quantopian Tutorial LESSON 3
Quantopian Tutorial LESSON 7
Quantopian Tutorial LESSON 4
Quantopian Tutorial LESSON 11
sqlalchemy tutorial
PyODE Tutorial 2
Python tutorial
PyODE Tutorial 1
PyODE Tutorial 3
TensorFlow tutorial tutorial