2.1 Moving Averages

It is frequently reported in the popular press that comparing moving averages gives predictive information about a stock’s momentum. For example, this interview published on Forbes suggests that comparing 15 and 30 day simple moving averages gives an indication of when to buy and sell.

This notebook defines formulas which add moving average columns. Some strategies will use these formulas to define bull and bear signals.

Import Packages

In [1]:
import numpy as np
import pandas as pd
import cPickle as pickle
shortUniverse = True

Load Pickle Data

First, we’ll load the pickle we developed in the previous section. The new prices dataframe is given a new name to reflect edits made during this notebook.

In [2]:
if shortUniverse:
    with open('intermediaries/prices.p', 'rb') as handle:
        prices = pickle.load(handle)
else:
    with open('intermediaries/prices-full.p', 'rb') as handle:
        prices = pickle.load(handle)

Adding Moving Average Columns

The following formula defines simple moving averages of closed prices over a given window size. Lagged values are also added as a column in order to allow strategies to easily detect the day a crossover occurred. For a five day window, these columns would have the name “ma_5” and “ma_5_lag_1“, respectively.

For illustrative purposes, the MA5 is added to the prices dataframe. The first ten rows are displayed below.

In [3]:
def add_MA(df, lag):
    df['ma_'+str(lag)]= df.groupby('symbol')['close'].apply(pd.rolling_mean, lag)
    df['ma_'+str(lag)+'_lag_1']= df.groupby('symbol')['ma_'+str(lag)].shift(1)
    return df

Please note that today’s “moving average” column includes today’s price.

In [4]:
prices = add_MA(prices, lag=5)

Adding Moving Average Columns

An additional formula was generated for volume-weighted moving averages.

Unlike the simple moving average, which is based on closing prices alone, these moving averages estimate the “typical price” shares traded in a given day (average of high, low, close) and weight the typical prive by volume. For illustrative purposes, vwma_5 and vwma_5_lag_1 are added below.

In [5]:
def add_VWMA(df, lag):
    df['typical'] = (df['low'] + df['high'] + df['close']) / 3
    df['VP'] = df['volume'] * df['typical']
    df['VPLag' + str(lag)]= df.groupby('symbol')['VP'].apply(pd.rolling_mean, lag)
    df['VLag' + str(lag)]= df.groupby('symbol')['volume'].apply(pd.rolling_mean, lag)
    df['vwma_' + str(lag)] = df['VPLag' + str(lag)] / df['VLag' + str(lag)]
    df['vwma_' + str(lag) + '_lag_1'] = df.groupby('symbol')['vwma_' + str(lag)].shift(1)
    df = df.drop(['typical', 'VP', 'VPLag' + str(lag), 'VLag' + str(lag)], axis=1)
    return df
In [6]:
prices = add_VWMA(prices, lag=5)

Output Example

The first 10 observations are displayed below.

In [7]:
prices.head(10)
Out[7]:
sector symbol open high low close volume ret_cc ret_oc ret_co ma_5 ma_5_lag_1 vwma_5 vwma_5_lag_1
Date
2015-01-02 XLP PG NaN NaN NaN NaN 7251400 NaN NaN NaN NaN NaN NaN NaN
2015-01-05 XLP PG 86.615816 87.354970 86.251032 86.404627 8626100 NaN -0.002441 NaN NaN NaN NaN NaN
2015-01-06 XLP PG 86.692607 86.932593 85.684668 86.011046 7791200 -0.004565 -0.007893 3.327379e-03 NaN NaN NaN NaN
2015-01-07 XLP PG 86.337431 86.750208 85.972648 86.462222 5986600 0.005232 0.001444 3.787508e-03 NaN NaN NaN NaN
2015-01-08 XLP PG 86.855801 87.575760 86.519815 87.450962 6823300 0.011371 0.006829 4.541708e-03 NaN NaN NaN NaN
2015-01-09 XLP PG 87.527760 87.527760 86.510222 86.635012 4872800 -0.009374 -0.010252 8.778023e-04 41.446339 NaN 41.464811 NaN
2015-01-12 XLP PG 86.855801 86.990193 85.924654 86.318227 5247600 -0.003663 -0.006209 2.545257e-03 41.543118 41.446339 41.603177 41.464811
2015-01-13 XLP PG 87.143781 87.834943 86.174235 86.683012 6727100 0.004217 -0.005301 9.518630e-03 41.576023 41.543118 41.667414 41.603177
2015-01-14 XLP PG 86.135844 86.548621 85.847857 86.385424 6418200 -0.003439 0.002893 -6.332297e-03 41.492793 41.576023 41.579814 41.667414
2015-01-15 XLP PG 86.385424 86.874998 85.761458 86.260634 6768200 -0.001446 -0.001446 -3.866073e-09 41.274075 41.492793 41.345197 41.579814

Save Formulas

No new price file is created. However, both formulas have been added to the separate document “helper_functions.py” which can be imported into future tests.

Potential Extensions

Exponentially weighted averages could be added in a future version.