top of page
  • Nikhil Adithyan

Algorithmic Trading with SMA in Python

Updated: Apr 19, 2023

Creating and backtesting an SMA trading strategy in python



Disclaimer: This article is strictly for educational purposes and should not be taken as an investment tip.


Introduction


With the increasing number of technological innovations, many industries deprecated their traditional methods and hoped-in to the latest tech ones to keep themselves updated. And so is the financial industry! In the past few years, there has been a lot going with the financial industry. An enormous amount of technological solutions for finance are invented and enhanced. In this article, we are going to see one of the notable technological innovations in finance which became a quantum leap to the stock trading space and that is nothing but Algorithmic Trading! Let’s dig deeper into this topic.


Algorithmic Trading


In most cases, a stock trade is influenced by humans’ emotions or irrational thinking (sometimes). As a result, it reflected bad results in most of the trades done by the trader. People were thinking of a trading method where they could keep their emotions aside and it was this time the concept of Algorithmic trading was invented. Algorithmic trading is the process of enabling computers to trade stocks under certain conditions or rules. A trade will be performed by the computer automatically when the given condition gets satisfied. Also, these conditions are given to the computers by human traders. The conditions or nothing but trading strategies defined by human traders.


In this article, we will be creating a trading strategy using a technical indicator called Simple Moving Average (SMA)


Simple Moving Average


Simple Moving Average (SMA) is nothing but the average price of a specified period of time. It is a technical indicator and widely used in creating trading strategies. Usually, two SMAs are calculated to build a trading strategy, one with a short period of time and the other with longer than the first one. Now, let's get an intuition on the trading strategy we will be building in this article.


About the trading strategy: Our trading strategy is going to be a simple crossover strategy where the computer shoots a trade when the SMA calculated with a shorter period crosses above the SMA calculated with a longer period. Likewise, the computer sells the stock when the SMA calculated with a longer period crosses above the SMA calculates with a shorter period. This is how our condition for the trading strategy looks like:



IF SMA(SHORT PERIOD) > SMA(LONG PERIOD) => BUY
IF SMA(LONG PERIOD) > SMA(SHORT PERIOD) => SELL

With that, we have finished our theory part. Now let’s implement the trading strategy in python and see it in action.


Implementation in Python


Now, we are going to code our trading strategy in python and see how well it works.


Importing Packages


In this step, we are going to import the required packages into our python environment. The primary packages are going to be Pandas to work with dataframes, Matplotlib to create plots, Requests to make API calls, NumPy to work with arrays. The additional packages are Math to perform mathematical calculations and Termcolor to customize fonts in python.


Python Implementation:



import pandas as pd 
import matplotlib.pyplot as plt 
import requests
import math
from termcolor import colored as cl 
import numpy as np

plt.style.use('fivethirtyeight')
plt.rcParams['figure.figsize'] = (15, 8)

We have imported all the required packages into our python environment. Now let’s extract the historical data of Microsoft (MSFT) from IEX Cloud. Before moving on, if you don’t know what is IEX Cloud and how to pull data from it, I highly recommend you to view my article on it (click here to view the article). Let’s pull some data!


Extracting data from IEX Cloud


In this step, we are going to pull the historic data of Microsoft using an API provided by IEX Cloud.


Python Implementation:



def get_historic_data(symbol):
    ticker = symbol
    iex_api_key = 'Tsk_30a2677082d54c7b8697675d84baf94b'
    api_url = f'https://sandbox.iexapis.com/stable/stock/{ticker}/chart/max?token={iex_api_key}'
    df = requests.get(api_url).json()
    
    date = []
    open = []
    high = []
    low = []
    close = []
    
    for i in range(len(df)):
        date.append(df[i]['date'])
        open.append(df[i]['open'])
        high.append(df[i]['high'])
        low.append(df[i]['low'])
        close.append(df[i]['close'])
    
    date_df = pd.DataFrame(date).rename(columns = {0:'date'})
    open_df = pd.DataFrame(open).rename(columns = {0:'open'})
    high_df = pd.DataFrame(high).rename(columns = {0:'high'})
    low_df = pd.DataFrame(low).rename(columns = {0:'low'})
    close_df = pd.DataFrame(close).rename(columns = {0:'close'})
    frames = [date_df, open_df, high_df, low_df, close_df]
    df = pd.concat(frames, axis = 1, join = 'inner')
    return df

msft = get_historic_data('MSFT')
msft = msft.set_index('date')
msft = msft[msft.index >= '2020-01-01']
msft.index = pd.to_datetime(msft.index)

msft.to_csv('msft.csv')
msft = pd.read_csv('msft.csv').set_index('date')
msft.index = pd.to_datetime(msft.index)
msft.tail()

Output:



Code Explanation: First we are defining a function named ‘get_historic_data’ that takes a stock’s ticker as the parameter. Inside the function, we are storing the API key and the URL into their respective variables, and then using the ‘GET’ method provided by the Request package, we are extracting the data in a JSON format. Next, we are doing some data manipulation tasks to clean and make the data usable. Finally, we are returning the dataframe. After finished defining the function, we are calling it and stored the data into the ‘msft’ variable. Let’s calculate the SMA values out of the extracted data.


SMA Calculation


In this step, we are going to calculate two SMA values (SMA 20, 50) and append those values to our dataframe.


Python Implementation:



def sma(data, n):
    sma = data.rolling(window = n).mean()
    return pd.DataFrame(sma)

n = [20, 50]
for i in n:
    msft[f'sma_{i}'] = sma(msft['close'], i)
    
msft.tail()
    

Output:



Code Explanation: Firstly, we are defining a function named ‘sma’ that takes data and the number of periods as the parameters. Inside the function, we are using the ‘rolling’ function provided by the Pandas package to calculate the SMA for the given number of periods. We are storing the calculated values into the ‘sma’ variable and returned it. Next, we are calling the function and calculated two SMA values, the shorter one with 20 as the number of periods, and the longer one with 50 as the number of periods. Now, let’s make a plot out of the calculated SMA values.


Plotting the SMA Values


In this step, we are going to plot the calculated SMA values to make more sense out of them.


Python Implementation:



plt.plot(msft['close'], label = 'MSFT', linewidth = 5, alpha = 0.3)
plt.plot(msft['sma_20'], label = 'SMA 20')
plt.plot(msft['sma_50'], label = 'SMA 50')
plt.title('MSFT Simple Moving Averages (20, 50)')
plt.legend(loc = 'upper left')
plt.show()

Output:



Code Explanation: Using the ‘plot’ function provided by the Matplotlib package, we have plotted the SMA values along with the ‘close’ prices of Microsoft. Now let’s observe the graph. The light blue line represents the ‘close’ prices of Microsoft, and the red and gold line represents SMA 20 & SMA 50 respectively. It is observed that the golden line (SMA 50) is way smoother than the red line (SMA 20) because the specified period of time for the golden line’s values is considerably higher than the red line’s values.


Now that we have our SMA values. So let’s proceed in creating the trading strategy.


Creating a Trading Strategy


In this step, we are going to implement the discussed trading strategy in python.


Python Implementation:



def implement_sma_strategy(data, short_window, long_window):
    sma1 = short_window
    sma2 = long_window
    buy_price = []
    sell_price = []
    sma_signal = []
    signal = 0
    
    for i in range(len(data)):
        if sma1[i] > sma2[i]:
            if signal != 1:
                buy_price.append(data[i])
                sell_price.append(np.nan)
                signal = 1
                sma_signal.append(signal)
            else:
                buy_price.append(np.nan)
                sell_price.append(np.nan)
                sma_signal.append(0)
        elif sma2[i] > sma1[i]:
            if signal != -1:
                buy_price.append(np.nan)
                sell_price.append(data[i])
                signal = -1
                sma_signal.append(-1)
            else:
                buy_price.append(np.nan)
                sell_price.append(np.nan)
                sma_signal.append(0)
        else:
            buy_price.append(np.nan)
            sell_price.append(np.nan)
            sma_signal.append(0)
            
    return buy_price, sell_price, sma_signal

sma_20 = msft['sma_20']
sma_50 = msft['sma_50']

buy_price, sell_price, signal = implement_sma_strategy(msft['close'], sma_20, sma_50)

Code Explanation: First, we are defining a function named ‘implement_sma_strategy’ which takes data, and the period of time for both short and long SMA as the parameters.


Inside the function, we are first storing the specified period of time into the ‘sma1’ and the ‘sma2’ variable. Next, we are storing three empty lists in which the values will be appended while creating the trading strategy.


After that, we are implementing the trading strategy through a for-loop. Inside the for-loop, we are passing certain conditions, and if the conditions are satisfied, the respective values will be appended to the empty lists. If the condition to buy the stock gets satisfied, the buying price will be appended to the ‘buy_price’ list, and the signal value will be appended as 1 representing to buy the stock. Similarly, if the condition to sell the stock gets satisfied, the selling price will be appended to the ‘sell_price’ list, and the signal value will be appended as -1 representing to sell the stock.


Finally, we are returning the lists appended with values. Then, we are calling the created function and stored the values into the respective variables. The list doesn't make any sense unless we plot the values. So, let’s plot the values of the created trading lists.


Plotting the Trading lists


In this step, we are going to plot the created trading lists to make sense out of them.


Python Implementation:



plt.plot(msft['close'], alpha = 0.3, label = 'MSFT')
plt.plot(sma_20, alpha = 0.6, label = 'SMA 20')
plt.plot(sma_50, alpha = 0.6, label = 'SMA 50')
plt.scatter(msft.index, buy_price, marker = '^', s = 200, color = 'darkblue', label = 'BUY SIGNAL')
plt.scatter(msft.index, sell_price, marker = 'v', s = 200, color = 'crimson', label = 'SELL SIGNAL')
plt.legend(loc = 'upper left')
plt.title('MSFT SMA CROSSOVER TRADING SIGNALS')
plt.show()

Output:



Code Explanation: We are plotting the SMA values along with the buy and sell signals generated by the trading strategy. We can observe that whenever the red line (SMA 20) crosses above the golden line (SMA 50), a buy signal is plotted in blue color, similarly, whenever the golden line crosses above the red line, a sell signal is plotted in red color.


Now, using the trading signals, let’s create our position on the stock.


Creating our Position


In this step, we are going to create a list that indicates 1 if we hold the stock or 0 if we don’t own or hold the stock.


Python Implementation:



position = []
for i in range(len(signal)):
    if signal[i] > 1:
        position.append(0)
    else:
        position.append(1)
        
for i in range(len(msft['close'])):
    if signal[i] == 1:
        position[i] = 1
    elif signal[i] == -1:
        position[i] = 0
    else:
        position[i] = position[i-1]

sma_20 = pd.DataFrame(sma_20).rename(columns = {0:'sma_20'})
sma_50 = pd.DataFrame(sma_50).rename(columns = {0:'sma_50'}) 
signal = pd.DataFrame(signal).rename(columns = {0:'sma_signal'}).set_index(msft.index)
position = pd.DataFrame(position).rename(columns = {0:'sma_position'}).set_index(msft.index)

frames = [sma_20, sma_50, signal, position]
strategy = pd.concat(frames, join = 'inner', axis = 1)
strategy = strategy.reset_index().drop('date', axis = 1)

Output:



Code Explanation: First, we are creating an empty list named ‘position’. We are passing two for-loops, one is to generate values for the ‘position’ list to just match the length of the ‘signal’ list. The other for-loop is the one we are using to generate actual position values. Inside the second for-loop, we are iterating over the values of the ‘signal’ list, and the values of the ‘position’ list get appended concerning which condition gets satisfied. The value of the position remains 1 if we hold the stock or remains 0 if we sold or don’t own the stock. Finally, we are doing some data manipulations to combine all the created lists into one dataframe.


From the output being shown, we can see that from row 181–185, our position in the stock has remained 1 (since there isn't any change in the SMA signal) but our position suddenly turned to 0 as we sold the stock when the SMA trading signal represents a sell signal (-1).


Now it’s time to do implement some backtesting process!


Backtesting


Before moving on, it is essential to know what backtesting is. Backtesting is the process of seeing how well our trading strategy has performed on the given stock data. In our case, we are going to implement a backtesting process for our SMA crossover strategy over the Microsoft stock data.


Python Implementation:



msft_ret = pd.DataFrame(np.diff(msft['close'])).rename(columns = {0:'returns'})
sma_strategy_ret = []

for i in range(len(msft_ret)):
    try:
        returns = msft_ret['returns'][i]*strategy['sma_position'][i]
        sma_strategy_ret.append(returns)
    except:
        pass
    
sma_strategy_ret_df = pd.DataFrame(sma_strategy_ret).rename(columns = {0:'sma_returns'})

investment_value = 100000
number_of_stocks = math.floor(investment_value/msft['close'][1])
sma_investment_ret = []

for i in range(len(sma_strategy_ret_df['sma_returns'])):
    returns = number_of_stocks*sma_strategy_ret_df['sma_returns'][i]
    sma_investment_ret.append(returns)

sma_investment_ret_df = pd.DataFrame(sma_investment_ret).rename(columns = {0:'investment_returns'})
total_investment_ret = round(sum(sma_investment_ret_df['investment_returns']), 2)
print(cl('Profit gained from the strategy by investing $100K in MSFT : ${} in 1 Year'.format(total_investment_ret), attrs = ['bold']))

Output:



Profit gained from the strategy by investing $100K in MSFT : $19971.23 in 1 Year

Code Explanation: First, we are calculating the returns of the Microsoft stock using the ‘diff’ function provided by the NumPy package and we have stored it as a dataframe into the ‘msft_ret’ variable. Next, we are passing a for-loop to iterate over the values of the ‘msft_ret’ variable to calculate the returns we gained from our SMA trading strategy, and these returns values are appended to the ‘sma_strategy_ret’ list. Next, we are converting the ‘sma_strategy_ret’ list into a dataframe and stored it into the ‘sma_strategy_ret_df’ variable.


Next comes the backtesting process. We are going to backtest our strategy by investing a hundred thousand USD into our trading strategy. So first, we are storing the amount of investment into the ‘investment_value’ variable. After that, we are calculating the number of Microsoft stocks we can buy using the investment amount. You can notice that I’ve used the ‘floor’ function provided by the Math package because, while dividing the investment amount by the closing price of Microsoft stock, it spits out an output with decimal numbers. The number of stocks should be an integer but not a decimal number. Using the ‘floor’ function, we can cut out the decimals. Remember that the ‘floor’ function is way more complex than the ‘round’ function. Then, we are passing a for-loop to find the investment returns followed by some data manipulations tasks.


Finally, we are printing the total return we got by investing a hundred thousand into our trading strategy and it is revealed that we have made an approximate profit of twenty thousand USD in one year. That’s not bad!


Final Thoughts!


We’ve come along a long way from exploring what is Algorithmic Trading and SMA to implement and backtesting our trading strategy in python. There are still a lot of spaces to improve our code and gain better results. Some ways are to use Machine Learning to find the best stock to implement the trading strategy, tune and improve the trading strategy, etc. There are also a lot of packages that can be used to conduct technical analysis on stocks but in this article, we have used only the preliminary packages so that we can get an intuition on what exactly we are trying to build. That’s it! If you forgot to follow any of the coding parts, don’t worry. I’ve provided the full source code used in this article. Hope you’ve learned something useful from this article.


Full code:



import pandas as pd 
import matplotlib.pyplot as plt 
import requests
import math
from termcolor import colored as cl 
import numpy as np

plt.style.use('fivethirtyeight')
plt.rcParams['figure.figsize'] = (15, 8)

def get_historic_data(symbol):
    ticker = symbol
    iex_api_key = 'Tsk_30a2677082d54c7b8697675d84baf94b'
    api_url = f'https://sandbox.iexapis.com/stable/stock/{ticker}/chart/max?token={iex_api_key}'
    df = requests.get(api_url).json()
    
    date = []
    open = []
    high = []
    low = []
    close = []
    
    for i in range(len(df)):
        date.append(df[i]['date'])
        open.append(df[i]['open'])
        high.append(df[i]['high'])
        low.append(df[i]['low'])
        close.append(df[i]['close'])
    
    date_df = pd.DataFrame(date).rename(columns = {0:'date'})
    open_df = pd.DataFrame(open).rename(columns = {0:'open'})
    high_df = pd.DataFrame(high).rename(columns = {0:'high'})
    low_df = pd.DataFrame(low).rename(columns = {0:'low'})
    close_df = pd.DataFrame(close).rename(columns = {0:'close'})
    frames = [date_df, open_df, high_df, low_df, close_df]
    df = pd.concat(frames, axis = 1, join = 'inner')
    return df

msft = get_historic_data('MSFT')
msft = msft.set_index('date')
msft = msft[msft.index >= '2020-01-01']
msft.index = pd.to_datetime(msft.index)

msft.to_csv('msft.csv')
msft = pd.read_csv('msft.csv').set_index('date')
msft.index = pd.to_datetime(msft.index)
msft.tail()

def sma(data, n):
    sma = data.rolling(window = n).mean()
    return pd.DataFrame(sma)

n = [20, 50]
for i in n:
    msft[f'sma_{i}'] = sma(msft['close'], i)
    
msft.tail()

plt.plot(msft['close'], label = 'MSFT', linewidth = 5, alpha = 0.3)
plt.plot(msft['sma_20'], label = 'SMA 20')
plt.plot(msft['sma_50'], label = 'SMA 50')
plt.title('MSFT Simple Moving Averages (20, 50)')
plt.legend(loc = 'upper left')
plt.show()

def implement_sma_strategy(data, short_window, long_window):
    sma1 = short_window
    sma2 = long_window
    buy_price = []
    sell_price = []
    sma_signal = []
    signal = 0
    
    for i in range(len(data)):
        if sma1[i] > sma2[i]:
            if signal != 1:
                buy_price.append(data[i])
                sell_price.append(np.nan)
                signal = 1
                sma_signal.append(signal)
            else:
                buy_price.append(np.nan)
                sell_price.append(np.nan)
                sma_signal.append(0)
        elif sma2[i] > sma1[i]:
            if signal != -1:
                buy_price.append(np.nan)
                sell_price.append(data[i])
                signal = -1
                sma_signal.append(-1)
            else:
                buy_price.append(np.nan)
                sell_price.append(np.nan)
                sma_signal.append(0)
        else:
            buy_price.append(np.nan)
            sell_price.append(np.nan)
            sma_signal.append(0)
            
    return buy_price, sell_price, sma_signal

sma_20 = msft['sma_20']
sma_50 = msft['sma_50']

buy_price, sell_price, signal = implement_sma_strategy(msft['close'], sma_20, sma_50)

plt.plot(msft['close'], alpha = 0.3, label = 'MSFT')
plt.plot(sma_20, alpha = 0.6, label = 'SMA 20')
plt.plot(sma_50, alpha = 0.6, label = 'SMA 50')
plt.scatter(msft.index, buy_price, marker = '^', s = 200, color = 'darkblue', label = 'BUY SIGNAL')
plt.scatter(msft.index, sell_price, marker = 'v', s = 200, color = 'crimson', label = 'SELL SIGNAL')
plt.legend(loc = 'upper left')
plt.title('MSFT SMA CROSSOVER TRADING SIGNALS')
plt.show()

position = []
for i in range(len(signal)):
    if signal[i] > 1:
        position.append(0)
    else:
        position.append(1)
        
for i in range(len(msft['close'])):
    if signal[i] == 1:
        position[i] = 1
    elif signal[i] == -1:
        position[i] = 0
    else:
        position[i] = position[i-1]

sma_20 = pd.DataFrame(sma_20).rename(columns = {0:'sma_20'})
sma_50 = pd.DataFrame(sma_50).rename(columns = {0:'sma_50'}) 
signal = pd.DataFrame(signal).rename(columns = {0:'sma_signal'}).set_index(msft.index)
position = pd.DataFrame(position).rename(columns = {0:'sma_position'}).set_index(msft.index)

frames = [sma_20, sma_50, signal, position]
strategy = pd.concat(frames, join = 'inner', axis = 1)
strategy = strategy.reset_index().drop('date', axis = 1)

msft_ret = pd.DataFrame(np.diff(msft['close'])).rename(columns = {0:'returns'})
sma_strategy_ret = []

for i in range(len(msft_ret)):
    try:
        returns = msft_ret['returns'][i]*strategy['sma_position'][i]
        sma_strategy_ret.append(returns)
    except:
        pass
    
sma_strategy_ret_df = pd.DataFrame(sma_strategy_ret).rename(columns = {0:'sma_returns'})

investment_value = 100000
number_of_stocks = math.floor(investment_value/msft['close'][1])
sma_investment_ret = []

for i in range(len(sma_strategy_ret_df['sma_returns'])):
    returns = number_of_stocks*sma_strategy_ret_df['sma_returns'][i]
    sma_investment_ret.append(returns)

sma_investment_ret_df = pd.DataFrame(sma_investment_ret).rename(columns = {0:'investment_returns'})
total_investment_ret = round(sum(sma_investment_ret_df['investment_returns']), 2)
print(cl('Profit gained from the strategy by investing $100K in MSFT : ${} in 1 Year'.format(total_investment_ret), attrs = ['bold']))

 

4 comments
bottom of page