Monthly Archive February 2021

ByJames Burnett

AlgoTrading: An intro to back-testing with RSI and Python.

The following code implements an very basic buy signal idea using a the relative strength index (RSI) and a counter to determine if a stock has dipped. This code will back-test the idea using the 60 minute time frame with 1000 hours of historical data.

Once you feel that your trading strategy works you could then use the same code to implement an automated or algo trading bot.

In order to use this code you will need a free API key from alphavantage.co. You will also need install the MatPlotLib Python module (pip install matplotlib) and the Pandas python module (pip install pandas).

Copy the contents into a file called “rsi-test.py” and run the program like this.

$ python ./rsi-test.py AMD

The Code.

import sys
import matplotlib.pyplot as plt
import pandas as pd
import mykeys


def RSI(prices, n=14):
    deltas = (prices-prices.shift(1)).fillna(0)
    avg_of_gains = deltas[1:n+1][deltas > 0].sum() / n
    avg_of_losses = -deltas[1:n+1][deltas < 0].sum() / n rsi_series = pd.Series(0.0, deltas.index) up = lambda x: x if x > 0 else 0
    down = lambda x: -x if x < 0 else 0
    i = n+1
    for d in deltas[n+1:]:
        avg_of_gains = ((avg_of_gains * (n-1)) + up(d)) / n
        avg_of_losses = ((avg_of_losses * (n-1)) + down(d)) / n
        if avg_of_losses != 0:
            rs = avg_of_gains / avg_of_losses
            rsi_series[i] = 100 - (100 / (1 + rs))
        else:
            rsi_series[i] = 100
        i += 1
    return rsi_series


api_key = 

ticker = sys.argv[1] 

rsi_length = 12

timeseries = "60min"

api_url = 'https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=' + ticker + '&interval=' + timeseries + '&apikey=' + api_key + '&datatype=csv&outputsize=full'

data = pd.read_csv(api_url)

stock_data = pd.DataFrame(index=range(0,len(data)),columns=['timestamp', 'open','high','low','close','volume'])

stock_data = stock_data.reindex(index=stock_data.index[::-1])

rsi_fairval = []



for i in range(0,len(data)):
    stock_data['timestamp'][i] = data['timestamp'][i]
    stock_data['close'][i] = float(data['close'][i])
    stock_data['high'][i] = float(data['high'][i])
    stock_data['low'][i] = float(data['low'][i])
    stock_data['open'][i] = float(data['open'][i])
    stock_data['volume'][i] = int(data['volume'][i])
    rsi_fairval.append(50)
 
 


stock_data.index = stock_data.timestamp

stock_data = stock_data.dropna()

timestamp = stock_data['timestamp']

close = stock_data['close']

high = stock_data['high']

low = stock_data['low']

open = stock_data['open']

volume = stock_data['volume']

rsi = RSI(close,rsi_length)

rsi_counter = 0

for now in range(0,len(close)):
    if now > rsi_length:
        if rsi[now] < 33:
            if rsi_counter < 2: print("OVER_SOLD",rsi_counter,timestamp[now], close[now], rsi[now]) if rsi_counter >= 2:
                print("      BUY", rsi_counter, close[now])

            rsi_counter = rsi_counter + 1

        if rsi[now] >= 50 and rsi_counter > 0:
            rsi_counter = 0
            print("RECOVERED",rsi_counter,timestamp[now], close[now], rsi[now])
        if rsi_counter < 0:
            rsi_counter = 0
       


plt.rcParams.update({
    "lines.color": "white",
    "patch.edgecolor": "white",
    "text.color": "white",
    "axes.facecolor": "black",
    "axes.edgecolor": "lightgray",
    "axes.labelcolor": "white",
    "xtick.color": "white",
    "ytick.color": "white",
    "grid.color": "lightgray",
    "figure.facecolor": "black",
    "figure.edgecolor": "black",
    "savefig.facecolor": "black",
    "savefig.edgecolor": "black"})

#Define our chart with 2 subcharts and a size of 1200x1200.
fig, axs = plt.subplots(2, 1,figsize=(18,9))



####Sub Graph 1

axs[0].set_title('Stock Chart For: ' + ticker,color='black')
axs[0].set_facecolor('#000000')
axs[0].plot(timestamp,close)
axs[0].set_xticks([0,len(close)-1], minor=False)
axs[0].set_ylabel(ticker + ' CLOSE',fontsize=18,color='white')
axs[0].tick_params(axis='x', colors='red')
axs[0].tick_params(axis='y', colors='green')
axs[0].grid(True,color='#292b2e')


fig.tight_layout()


####Sub Graph 2

axs[1].set_title('Relative Strength Index',color='black')
axs[1].set_facecolor('#000000')
axs[1].plot(timestamp,rsi)
axs[1].plot(timestamp,rsi_fairval,color='yellow')

axs[1].set_xticks([0, len(close)-1], minor=False)
axs[1].set_yticks([0, 30,50, 70, 100], minor=False)
axs[1].tick_params(axis='x', colors='red')
axs[1].tick_params(axis='y', colors='green')
axs[1].grid(True,color='#292b2e')
axs[1].set_ylabel('RSI',fontsize=18,color='white')
print("Done.")
plt.show()



ByJames Burnett

What happens if you buy one share of SPY every month?

This code will simulate the purchase of one share of stock every thirty days for as much historical data as the data provider can provide. In the case of the SPY index fund, the data goes back to 1999 and we can see a clear picture for what would have happened if we purchased one share of spy every thirty days.

Even with three major stock market events investors who consistently invested into a major index fund like SPY would be sitting on a pretty pile of cash (provided they liquidate their position).

The major 2000 dot-com crash, the bankers crash of 2008 and 2020 COVID-19 pandemic crash shows that, according to historical data, consistent investing has always lead to a positive outcome. With that said, history won’t necessarily always repeat itself. This experiment was merely a way to get some practice back-testing some of my investing strategies.

For the image above you can see what you would have paid for SPY with the orange line and what SPY would be worth today with the blue-ish (colorblind person here…) line.

Instead of testing in a stock trading platform I simply wrote a Python program to back-test the idea for me, saving me hours of work.

The Code.

To run this code you will need to install the pandas Python module (pip install pandas) as well as the MatPlot graphing module for Python (pip install matplotlib). You’ll also need an API key to down the stock data from Alpha advantage. You can read up on how I download stock data with Python here.

import pandas as pd
import sys
import matplotlib.pyplot as plt
import studies
import mykeys


api_key = mykeys.api_key

ticker = sys.argv[1]  

api_url = 'https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=' + ticker + '&apikey=' + api_key + '&outputsize=full&datatype=csv'

data = pd.read_csv(api_url)

stock_data = pd.DataFrame(index=range(0,len(data)),columns=['timestamp', 'open','high','low','close','volume'])

stock_data = stock_data.reindex(index=stock_data.index[::-1])

for i in range(0,len(data)):
    #if "2020" in data['timestamp'][i]:
    ts = data['timestamp'][i]
    stock_data['timestamp'][i] = data['timestamp'][i]
    stock_data['close'][i] = float(data['close'][i])
    stock_data['high'][i] = float(data['high'][i])
    stock_data['low'][i] = float(data['low'][i])
    stock_data['open'][i] = float(data['open'][i])
    stock_data['volume'][i] = int(data['volume'][i])

stock_data.index = stock_data.timestamp

stock_data = stock_data.dropna()

timestamp = stock_data['timestamp']

close = stock_data['close']

high = stock_data['high']

low = stock_data['low']

open = stock_data['open']

volume = stock_data['volume']

share_count = 0

stock_price_sum = 0

last_price = close[len(close)-1]

net_values = []

spent_values = []

close_values = []

ts_values = []

counter = 1

total_cost = 0

total_val = 0

for now in range(0,len(close)):
    if counter == 30:
        share_count = share_count + 1

        stock_price_sum = round(stock_price_sum + close[now],2)

        net_value = round(share_count * close[now],2)

        net_values.append(net_value) 

        spent_values.append(stock_price_sum)

        close_values.append(close[now])

        ts_values.append(timestamp[now])

        total_val = net_value

        total_cost = stock_price_sum

        print(timestamp[now],close[now]," - Total Shares:", share_count, " Spent: ", stock_price_sum, " Net Value: ", net_value)

        counter = 1
    else:
        counter = counter + 1


gain = 100 - ((total_cost / total_val) * 100)

plt.rcParams.update({
    "lines.color": "white",
    "patch.edgecolor": "white",
    "text.color": "white",
    "axes.facecolor": "black",
    "axes.edgecolor": "lightgray",
    "axes.labelcolor": "white",
    "xtick.color": "white",
    "ytick.color": "white",
    "grid.color": "lightgray",
    "figure.facecolor": "black",
    "figure.edgecolor": "black",
    "savefig.facecolor": "black",
    "savefig.edgecolor": "black"})

#Define our chart with 2 subcharts and a size of 1200x1200.
fig, axs = plt.subplots(1, 1,figsize=(18,9))




####Sub Graph 1

axs.set_title('Cost/Value: $' + str(total_cost) + '/' + str(total_val) ,color='white')
axs.set_facecolor('#000000')
axs.plot(ts_values,net_values)
axs.plot(ts_values,spent_values)
l = len(close_values)-1
axs.set_xticks([0,l * 0.20,l * 0.40,l * 0.60,l * 0.80, l], minor=False)
#axs[1].set_yticks([0, 30,50, 70, 100], minor=False)
axs.tick_params(axis='x', colors='red')
axs.tick_params(axis='y', colors='green')
axs.grid(True,color='#292b2e')
axs.set_ylabel('NET VALUE v.s. NET SPENT',fontsize=18,color='white')
axs.spines['bottom'].set_visible(False)
axs.spines['top'].set_visible(False)
axs.spines['right'].set_visible(False)
axs.spines['left'].set_visible(False)
#axs.set_xticklabels([])
#axs.set_yticklabels([])

print("Done.")

plt.show()

ByJames Burnett

Forecast stock prices with Python and FbProphet.

Forecasting is a sub-discipline of prediction that uses data based on time to produce an output. Using Python and the FBProphet python module we can train a module using time series data (data that is associated with a date and time) to forecast what would happen to the data over the course of similar time periods.

For example, if you want to Forecast a stock price you could get all the important past dates related to that stock to build your model. You could input specific dates of interest like corporate earnings, dividend payout dates and holidays and FbProphet could then correlate price changes on or around those dates. Based on what happened around those dates in the past could then be used to forecast data in the future based on the date.

The model in theory could forecast the price of a stock on our around corporate earning reports or even during the Christmas holidays.

The code below will download stock data and generate a forecast by using only the dates of the stock price. No holidays or special dates have been inputted so the model will only forecast based on what the price did on a previous day of a previous month and/or year.

The Code

You will need to install Pandas (pip install pandas), the Alpaca trading api module (pip install alpaca_trade_api) and Fbprophet (pip install fbprophet) to run the code. I highly recommend that you run this code in a Linux environment (Debian if you’re cool) as several custom libraries will need to be installed.

import alpaca_trade_api as tradeapi
import pandas as pd
from fbprophet import Prophet
from fbprophet.plot import plot_plotly
import plotly.offline as py
import matplotlib.pyplot as plt
import mykeys
import sys

KEY = ""
    
SECRET_KEY = ""

api = tradeapi.REST(KEY, SECRET_KEY, base_url='https://paper-api.alpaca.markets', api_version='v2') 

account = api.get_account()

api.list_positions()



def main():
    ticker = sys.argv[1]

    now = pd.Timestamp.now(tz='US/Eastern')

    
    barset = api.get_barset( [ticker], '15Min',  limit = 1000)

    timestamp = []

    close = []

    volume = []

    future_periods = 6


    for bar in barset[ticker]:
        td = pd.Timestamp(bar.t)

        ts = td.strftime("%Y-%m-%d %H:%M:%S")

        timestamp.append(ts)

        volume.append(bar.v)

        close.append(bar.c)

        print(ticker, ts,bar.c)
    
  
    df = pd.DataFrame( { "ds" : timestamp,  "y": close, "volume": volume})

    m = Prophet()

    m.fit(df)
    
    future = m.make_future_dataframe(periods=future_periods)

    forecast = m.predict(future)
    
    fig = m.plot(forecast)
    
    fig = m.plot_components(forecast)
    
    plt.show()
    
    pass

if __name__ == '__main__':
    main()
ByJames Burnett

Two ways to download stock data with Python.

There are two ways that I download stock data with Python. The first way is using the AlphaAdvantage API and the second way is by using the Alpaca.Markets API, both are free. This stock data will be put into easy to use Python data types and can be used to create stock trading bots, stock data analysis programs or anything else you can think of. You could even combine this code with Python code to calculate the RSI to create stock alerts.

Alpha Advantage

First, you will want to head over to Alpha Advantage and pick up a free API key. Once that is down you can test out this code which will download stock data and print it out to the console. You can test this code without getting an API but you are limited to only getting data on the IBM stock symbol.

You will also need to make sure you have the Python Pandas module installed (pip install pandas).

Code to download stock data with Alpha Advatnage.

import pandas as pd
import sys
api_key = 'demo'

ticker = sys.argv[1]  

api_url = 'https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=' + ticker + '&interval=5min&apikey=' + api_key + '&datatype=csv'

data = pd.read_csv(api_url)

for i in range(0,len(data)):
    ts = data['timestamp'][i]
    c = float(data['close'][i])
    h = float(data['high'][i])
    l = float(data['low'][i])
    o = float(data['open'][i])
    v = int(data['volume'][i])
    print(ts,c,h,l,o,v)

Output.

$ python alphaadvantage.py IBM
2021-02-12 19:50:00 120.9 120.9 120.9 120.9 300
2021-02-12 19:30:00 120.9 120.9 120.9 120.9 412
2021-02-12 19:25:00 120.9001 120.9001 120.9001 120.9001 525
2021-02-12 18:50:00 120.91 120.91 120.91 120.91 200
2021-02-12 18:25:00 120.9 120.9 120.9 120.9 275
2021-02-12 18:10:00 120.9001 120.9001 120.9001 120.9001 291

Alpaca.Markets

In order to use the Alpaca Markets data you will need to go to their website and get an API. Once you have your API keys you can run this code against any stock symbol. You will also need to install the alpaca trade API python module (pip install alpaca_trade_api) and the Pandas python module (pip install pandas). This code will download the last 1000 bars (like a candle bar) of data in a 15 minute time series.

You can change the 15Min to 1Min and 5Min as well as put Day for daily data.

The tickers variable is a Python List (array) and the last I knew you can pass 100 stock symbols to Alpaca servers.

Code to download stock data with Alpca.

This code will get stock data for AMD, MSFT and XOM and print the data out to the console.

import pandas as pd
import alpaca_trade_api as tradeapi


KEY = ""
    
SECRET_KEY = ""

url = 'https://paper-api.alpaca.markets'

api  = tradeapi.REST(KEY, SECRET_KEY, base_url=url, api_version='v2')

tickers = ['AMD','MSFT','XOM']

barset= api.get_barset( tickers, '15Min',  limit = 1000)


for ticker in tickers:
    for bar in barset[ticker]:
        print(ticker,bar.t, bar.c, bar.v, bar.l, bar.h,bar.o)
ByJames Burnett

Calculate the Relative Strength Index (RSI) with Python.

The Python RSI function takes a Pandas DataFrame as a parameter and calculates the average gains and the average losses and then returns the RSI as a new DataFrame. To use this function you will need stock prices data that is in a time series format (each row as a date/time column) and you will need to install the Pandas Python module. Normally this data is downloaded from a stock data provider and converted into a DataFrame with the closing price and date/time of the closing price.

Install Pandas.

pip install pandas

The Code.

Essential the code will return a Pandas Series object which is a 1 dimensional Numpy ndarray with a time column or other label attaches.

def RSI(prices, n=14):
    deltas = (prices-prices.shift(1)).fillna(0)
    avg_of_gains = deltas[1:n+1][deltas > 0].sum() / n
    avg_of_losses = -deltas[1:n+1][deltas < 0].sum() / n rsi_series = pd.Series(0.0, deltas.index) up = lambda x: x if x > 0 else 0
    down = lambda x: -x if x < 0 else 0
    i = n+1
    for d in deltas[n+1:]:
        avg_of_gains = ((avg_of_gains * (n-1)) + up(d)) / n
        avg_of_losses = ((avg_of_losses * (n-1)) + down(d)) / n
        if avg_of_losses != 0:
            rs = avg_of_gains / avg_of_losses
            rsi_series[i] = 100 - (100 / (1 + rs))
        else:
            rsi_series[i] = 100
        i += 1
    return rsi_series

The formula for calculating the RSI is:

RS = Simple Moving Average Price Up / Simple Moving Average Price Down

RSI = 100 - 100 / (1 +RS)