Graph

Once you have an alpha-generating, statistically-significant strategy, it's time to perform walk-forward testing. Walk-forward testing uses live market data with a paper trading account -- paper trading is a virtual test account that doesn't use real money.


Hello, world! My name is Leo Smigel, and I write about "Analyzing what works in trading and investing" at analyzingalpha.com

Trading & Investing Using Data Science
Analyzing what works in trading and investing using data science.

In this two-part series, we're going to create an RSI stack strategy using Alpaca and Backtrader.

In part one, we'll cover connecting the Backtrader backtesting framework to the Alpaca API-first brokerage and load in data for multiple time frames.

In part two of the series, we're going to create an RSI stack indicator to determine if a security is overbought/oversold on multiple time frames.

If you're unfamiliar with either Backtrader or Alpaca, check out my favorite Python trading tools.

Contents

  1. Getting API Key
  2. Installation
  3. Strategy
  4. Create Strategy Code
  5. Running & Plotting

Get an API Key

Before you can start paper trading, you will need to create an account with Alpaca. Once you've created an account, go to your dashboard.

You'll need to generate both an `API Key Id` and a `Secret Key`. Please be aware that the `Secret Key` will disappear after you refresh or navigate away from your dashboard as it's only displayed once for security purposes.

Alpaca | Algo Trading Commission Free with REST API
Lower your cost, Maximize your profits. Algo trading commission free.

Installation

Since we've covered it in a previous article, I'll assume you've already installed Backtrader using conda or pip. With your environment activated, install the alpaca-backtrader-api python library.

pip install alpaca-trade-api

Also, make sure the version of matplotlib is 3.2.2 as you will experience a plotting error if it is the current version of 3.3.0


The Strategy

The relative strength indicator or RSI is a momentum indicator used in technical analysis. It's used to measure the magnitude of recent price changes to determine oversold and overbought conditions. Why do we care? RSI overbought/oversold entries are great bounce plays, especially if there is larger time frame support. If you're interested, you can read more about the RSI indicator.

Relative Strength Index (RSI)
The Relative Strength Index (RSI) is a momentum indicator that measures the magnitude of recent price changes to analyze overbought or oversold conditions.

Let's load and graph the data using multiple time frames.

I'll cover each significant section piece-by-piece. Additionally, all of the code will be available in the Analyzing Alpha GitHub Repo.

leosmigel/analyzingalpha
Contribute to leosmigel/analyzingalpha development by creating an account on GitHub.

Secure Your Credentials

It's never a good idea to place security information within code. It's even worse to place plaintext security credentials in code and then push it to a git repo. With this in mind, let's create a file called `local-settings.py` to store our api information and make sure that we add it to `.gitignore`. `local-settings.py` will need to be in the same directory as the `alpaca-backtrader.py`.

local-settings.py

alpaca_paper = {
    'api_key': 'YOUR_API_KEY',
    'api_secret': 'YOUR_API_SECRET',
}

Next, we'll create `alpaca-backtrader.py` and add our imports and API information.

alpaca-backtrader.py

import alpaca_backtrader_api as Alpaca
import backtrader as bt
import pytz
from datetime import datetime
from local_settings import alpaca_paper

ALPACA_KEY_ID = alpaca_paper['api_key']
ALPACA_SECRET_KEY = alpaca_paper['api_secret']
ALPACA_PAPER = True

Simple enough, right? Let's now add our dates, a ticker, and three time frames. Generally speaking, if you're day trading you'll be using the 15min, 30min, and hourly time frame; and if you're swing trading, you'll be using the hourly, 4-hour, and daily time frame.


Add Dates and Time Frames

fromdate = datetime(2020,8,5)
todate = datetime(2020,8,10)

tickers = ['SPY']
timeframes = {
    '15Min':15,
    '30Min':30,
    '1H':60,
}

Create the Strategy

Next up, we'll create our strategy class and initialize cerebro. For now, our goal is to load and understand our data. With this in mind, we'll print each line for each `next` and plot our selected time frame lines.

If the following code is unfamiliar, please read Getting Started with Backtrader.

Backtrader: Getting Started Backtesting
Backtrader is an open-source python framework for trading and backtesting. Backtrader allows you to focus on writing reusable trading strategies, indicators, and analyzers instead of having to spend time building infrastructure.
class RSIStack(bt.Strategy):

	def next(self):
    	for i in range(0,len(self.datas)):
        	print(f'{self.datas[i].datetime.datetime(ago=0)} \
        	{self.datas[i].p.dataname}: OHLC: \
              	o:{self.datas[i].open[0]} \
              	h:{self.datas[i].high[0]} \
              	l:{self.datas[i].low[0]} \
              	c:{self.datas[i].close[0]} \
              	v:{self.datas[i].volume[0]}' )

cerebro = bt.Cerebro()
cerebro.addstrategy(RSIStack)
cerebro.broker.setcash(100000)
cerebro.broker.setcommission(commission=0.0)

Add the Datastore

A store in backtrader is the interface with a broker. While the implementation for various brokers will be different, a store handles connectivity with the broker to access your account, orders, and positions; and provides access to data feeds from the broker. Additionally, if you're not happy with the implementation, you can expand on it or create your own store .

With that out of the way, let's create our store using `AlpacaStore` and create a `DataFactory`.

store = Alpaca.AlpacaStore(
    key_id=ALPACA_KEY_ID,
    secret_key=ALPACA_SECRET_KEY,
    paper=ALPACA_PAPER
)

if not ALPACA_PAPER:
    print(f"LIVE TRADING")
    broker = store.getbroker()
    cerebro.setbroker(broker)

DataFactory = store.getdata

for ticker in tickers:
    for timeframe, minutes in timeframes.items():
        print(f'Adding ticker {ticker} using {timeframe} timeframe at {minutes} minutes.')

        d = DataFactory(
            dataname=ticker,
            timeframe=bt.TimeFrame.Minutes,
            compression=minutes,
            fromdate=fromdate,
            todate=todate,
            historical=True)

        cerebro.adddata(d)

The `DataFactory` returns data that we can then add to backtrader. We loop through our list of tickers, which is just SPY in this case to make things easy to see when we plot, and our timeframes.


Running & Plotting

Now all that's left to do is to analyze the output. Let's check to make sure both our printed data and our plotted version make sense.

2020-08-06 14:00:00 SPY:OHLC: o:332.295 h:332.51 l:331.98 c:332.29 v:180462.0
2020-08-06 14:00:00 SPY:OHLC: o:332.295 h:332.61 l:331.98 c:332.51 v:270946.0
2020-08-06 14:00:00 SPY:OHLC: o:332.295 h:332.62 l:331.13 c:331.78 v:627680.0
2020-08-06 14:15:00 SPY:OHLC: o:332.29 h:332.61 l:332.18 c:332.51 v:90484.0
2020-08-06 14:00:00 SPY:OHLC: o:332.295 h:332.61 l:331.98 c:332.51 v:270946.0
2020-08-06 14:00:00 SPY:OHLC: o:332.295 h:332.62 l:331.13 c:331.78 v:627680.0
2020-08-06 14:30:00 SPY:OHLC: o:332.48 h:332.62 l:331.23 c:331.36 v:196294.0
2020-08-06 14:30:00 SPY:OHLC: o:332.48 h:332.62 l:331.13 c:331.78 v:356734.0
2020-08-06 14:00:00 SPY:OHLC: o:332.295 h:332.62 l:331.13 c:331.78 v:627680.0
2020-08-06 14:45:00 SPY:OHLC: o:331.39 h:331.81 l:331.13 c:331.78 v:160440.0
2020-08-06 14:30:00 SPY:OHLC: o:332.48 h:332.62 l:331.13 c:331.78 v:356734.0
2020-08-06 14:00:00 SPY:OHLC: o:332.295 h:332.62 l:331.13 c:331.78 v:627680.0
2020-08-06 15:00:00 SPY:OHLC: o:331.74 h:332.005 l:331.64 c:331.725 v:63096.0

Checking TradingView shows that the prices are accurate and all data falls within a penny. The first start datetime of the backtest is at 14:00 UTC and not 13:30 UTC. The reason for this is that we need to start on a timeframe where our lowest timeframe (hourly in this case) can create one full bar.

cerebro.run()
print("Final Portfolio Value: %.2f" % cerebro.broker.getvalue())
cerebro.plot(style='candlestick', barup='green', bardown='red')

The plot shows the same thing.

Candlestick Chart

Analyzing the graph shows that everything is looking as it should.


Wrapping Up

If you made it this far, congratulations. You've created an Alpaca account, installed the Backtrader framework, and grabbed data from the `AlpacaStore` to create a multiple-timeframe trading strategy.

In part two of this series, which will be released next month, we're going to finish creating our RSI stack strategy and get you in a position to be able to create your very own algorithmic trading strategies.

In the meantime, feel free to head over to Analyzing Alpha to learn more about algorithmic trading and visit Alpaca to learn more about their fantastic API-first approach.

Once again all of the code will be available in the Analyzing Alpha GitHub Repo.

leosmigel/analyzingalpha
Contribute to leosmigel/analyzingalpha development by creating an account on GitHub.

Thanks again for reading, and see you next month!


*Keep in mind that all investments involve risk and the past performance of a security or financial product does not guarantee future results or returns.

Follow @AlpacaHQ on Twitter!

Brokerage services are provided by Alpaca Securities LLC ("Alpaca"), member FINRA/SIPC, a wholly-owned subsidiary of AlpacaDB, Inc. Technology and services are offered by AlpacaDB, Inc.