Simple Moving Average Strategy Using Python

Michael Wirtz
4 min readNov 23, 2020

Introduction

Since I began coding, writing my own trading algorithm has been a top priority on my to-do list. I know that it is incredibly hard to make them profitable, as it takes a team of individuals working on and updating the algorithm on a daily basis. As a first-attempt personal project, I do not expect this algorithm to start making me any money. A simple working algorithm is the goal for now.

I initially began with a YouTube video series, hoping that a guided tour of the space would grant me immediate understanding. Unfortunately, there are many added complexities when you want to automate the interaction between code and your online broker, and I was quickly lost. While the video series is something that I will go back to in the future, I will start with a simple time-series analysis and work my way forward.

Simple MA Strategy

Goal:

  • Input: list of stock tickers
  • Output: subset list made from input list made up of stock tickers in which the 30-day moving average has crossed the 200-day moving average in the last 10 days

Visualize Moving Average Strategy

I first downloaded TSLA price data from Yahoo Finance ranging from 2010 to 2020. I wanted to visualize the 30–200 moving average strategy. I first plotted the price:

Next, I plotted the 200 and 30-day moving averages against this price plot:

And, finally, I placed markers on the plot to indicate buy and sell signals:

Now that I had an idea of the strategy implementation, it was time to put it into action.

Returning Subset List of Tickers

As you may have noticed, I am using a 30-day moving average instead of the classical 50-day moving average. This was simply done to ensure that I was not left empty handed with my output. Furthermore, no real-world implementation will be made with the code in this blog. Everything done here was more for curiosity’s sake than anything else.

Anyway, here are the steps I took:

Step 1: Necessary Imports

import pandas_datareader as pdr
import datetime
from datetime import date

Step 2: Writing Function

def ticker_satisfy(list_of_tickers):

# Getting today's date
today = date.today()
# Splitting today's date into day, month, year
end_day = today.day
end_month = today.month
end_year = today.year
# Getting the date 365 days before today
year_ago = date.today() - datetime.timedelta(days=365)
# Splitting year_ago date into day, month, year
start_day = year_ago.day
start_month = year_ago.month
start_year = year_ago.year

buy_list = []

# iterating over list to return satisfying tickers
for ticker in list_of_tickers:

try:
# Grabs daily price data of specified ticker with specified start and end dates
data = pdr.get_data_yahoo(ticker,
start=datetime.datetime(start_year, start_month, start_day),
end=datetime.datetime(end_year, end_month, end_day))
# Creating columns to assess crossover
data['SMA30'] = data['Adj Close'].rolling(window=30).mean()
data['SMA200'] = data['Adj Close'].rolling(window=200).mean()
data['above'] = np.where(data['SMA30'] > data['SMA200'], True, False)

# Dropping null values from dataframe
data = data.dropna()

# Resetting index for simplified slicing
data.reset_index(inplace=True)

# Creating variables for comparison
last_10 = data.iloc[len(data)-10:, :].above.any()
eleventh = data.iloc[len(data)-11].above
except:
continue

# Assessing crossover in last 10 days
if eleventh == False and last_10 == True:
buy_list.append(ticker)
else:
pass

return buy_list

A couple things to point out about the above function:

  1. The try and except statement was used after the function kept running into errors for simplicity’s sake. I did not investigate those errors.
  2. The dates are set up where you can run the function on any given day, and it will return you a list of stocks that satisfy the strategy criteria relative to the current day.
  3. No effort was yet made as to the optimization of the function. With a list of about 3,500 ticker symbols, I killed the kernel after the function was going on an hour of run time. Therefore, as you will see below, I limited the function to an input of only 100 ticker symbols.

Step 3: Getting the output

I downloaded the NYSE ticker symbols as a CSV. I then set variable “tickers” to the first 100 ticker symbols in the CSV. Here are the symbols below:

['DDD','MMM','WBAI','EGHT','AHC','AOS','ATEN','AIR','AAN','ABB','ABT','ABBV','ANF','AGD','AWP','ACP','JEQ','ASGI','AOD','ABM','AKR','ACEL','ACN','ACCO','ATV','AYI','GOLF','ADX','PEO','ADCT','AGRO','ADNT','ADT','ATGE','AAP','WMS','ASIX','AVK','IMPX.U','ACM','AEFC','AEB','AEG','AENZ','AER','AJRD','AMG','MGR','MGRB','AFL','MITT','MITT^A','MITT^B','MITT^C','AGCO','A','AEM','ADC','AL','AL^A','APD','AJAX.U','ALP^Q','ALG','AGI','ALK','AIN','ALB','ACI','AA','ALC','ALEX','ALX','ARE','AQN','AQNA','AQNB','BABA','Y','ATI','ALLE','ALE','ADS','AFB','AWF','AB','AIO','CBH','NCV','NCV^A','NCZ','NCZ^A','ACV','NFJ','NIE','ALSN','ALL','ALL^B','ALL^G','ALL^H']

After running this list of tickers through the function, the output was as follows:

['AIR',
'ABBV',
'AWP',
'ACCO',
'AGRO',
'AFL',
'ADC',
'ALK',
'AIN',
'ALEX',
'Y',
'ATI',
'ALLE',
'ADS']

And, just to make sure the function was properly returning ticker symbols, I plotted the first ticker in the list ‘AIR’:

As you can see from the graph above, the 30-day moving average just passed the 200-day moving average in the last couple days.

Conclusion

Given that the function returned a suitable list, I think we can chalk this exercise up to a success. As we continue to explore python within the field of finance, the goals will get more complex, and the outcomes, hopefully, will incrementally be more useful in real-world applications.

--

--