You've successfully subscribed to Alpaca Learn | Developer-First API for Crypto and Stocks
Great! Next, complete checkout for full access to Alpaca Learn | Developer-First API for Crypto and Stocks
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.
Success! Your billing info is updated.
Billing info update failed.
Search
Algorithmic Trading Basics

Research, Backtest, and Trade from a Jupyter Notebook in Google Colab

Rahul Chowdhury
Rahul Chowdhury

Alpaca offers free live and historical market data for stocks and cryptocurrencies, and also offers free paper and live trading environments. These services are accessed by API and you can use them for a variety of things, like research and backtesting using market data, paper trading crypto, etc.

In this article, we provide an interactive environment you can use to quickly get started with Alpaca and start exploring what you can build. You can follow along in the article or use this Google Colab notebook, which has the same content, to follow along. You can use this environment to research new ideas, backtest them and even place paper trades.

Getting Started

We’ll be using interactive code blocks in this article. To follow along and run the code provided in this article, you'll need to use your Alpaca API paper keys.

You can find your keys on your dashboard. If you're having trouble finding your keys, follow this tutorial.

If you don't have an Alpaca account, sign up here for free API keys: Free Stock and Crypto Data with $0 Commission Trading

Input your keys below once you have them and then run the code cell.

API_KEY = "Your API Key"
SECRET_KEY = "Your API Secret Key"
				

        

Before we get started,  we will also need to import some libraries we’ll be using.  

Run the code cell below to install them. If you see `waiting for kernel`, give it a few moments to complete.

%%capture
!pip install backtrader
!pip install alpaca_trade_api
!pip install matplotlib==3.2.2
!pip install plotly
				
;

Now we’re ready to start digging into the details. Make sure to not skip any code cells because future code cells may depend on earlier code cells.

Market Data

The REST API allows you to access historical market data, account data, and place trades.
Here are some of the types of the market data Alpaca offers:

  • Bar data is available in custom timeframes (1 day, 30 mins, 5 mins, etc)
  • Quote and Trade data is also available
from alpaca_trade_api.rest import REST, TimeFrame
from alpaca_trade_api.stream import Stream

rest_api = REST(API_KEY, SECRET_KEY, 'https://paper-api.alpaca.markets')

        

Let's take a look at daily bar data for SPY between Jan 1st 2021 and March 30th 2021. Run the code cell below to see its output. The data is output as a dataframe. You can uncomment the other lines to see quote and trade data for SPY.

# retrieve daily bar data for SPY in a dataframe 
spy_bars = rest_api.get_bars('SPY', TimeFrame.Day, '2021-01-01', '2021-03-30').df
spy_bars.head(3)

# quote and trade data also available for equities
# spy_quotes = rest_api.get_quotes('SPY', '2021-01-01', '2021-01-05').df
# spy_trades = rest_api.get_trades('SPY', '2021-01-01', '2021-01-05').df
				

        

Cryptocurrency Data

Historical market data is also available for cryptocurrencies.

Currently, alpaca supports `Bitcoin (BTCUSD)`, `Ethereum (ETHUSD)`, `Litecoin (LTCUSD)`, and `Bitcoin Cash (BCHUSD)`

Let's take a look at quote data for Bitcoin between Jan 1st 2021 and Jan 5th 2021.

# retrieve quote data for Bitcoin in a dataframe
bitcoin_quotes = rest_api.get_crypto_quotes('BTCUSD', '2021-01-01', '2021-01-02').df
bitcoin_quotes.head(3)

# bar and trade data also available for crypto
# bitcoin_bars = rest_api.get_crypto_bars('BTCUSD', TimeFrame.Day, '2020-01-01', '2021-01-05').df
# bitcoin_trades = rest_api.get_crypto_trades('BTCUSD', '2021-01-01', '2021-01-05').df
				

        

Using Jupyter notebooks allow you to easily visualize data and test ideas. We can use pandas operations to do statistical analysis, like calculating volatility using standard deviation of returns.

In addition, We can visualize our data using various charts. For example, we can use plotly to make beautiful candle charts of SPY's daily bar data.  

Let’s add the 5 day SMA on to our chart. We can calculate the 5 day SMA over that period using the rolling mean pandas operation, and add it onto our chart.

# plotly imports
import plotly.graph_objects as go
import plotly.express as px

# SPY bar data candlestick plot
candlestick_fig = go.Figure(data=[go.Candlestick(x=spy_bars.index,
                open=spy_bars['open'],
                high=spy_bars['high'],
                low=spy_bars['low'],
                close=spy_bars['close'])])

# calculating 5 day SMA using pandas rolling mean
sma = spy_bars['close'].rolling(5).mean().dropna()

# creating a line plot for our sma
sma_fig = px.line(x=sma.index, y=sma)

# adding both plots onto one chart
fig = go.Figure(data=candlestick_fig.data + sma_fig.data)

# displaying our chart
fig.show()
				

        

Backtesting

We can use backtrader along with Alpaca’s Market Data API to backtest a strategy’s performance with historical data. Backtrader allows you to focus on writing reusable trading strategies, indicators and analyzers instead of having to spend time building infrastructure.  

Below we wrote a function that allows you to easily backtest a strategy written in backtrader. The function takes in as a parameter the strategy you wish to backtest, the symbols the strategy trades on, start and end dates of the backtest, the timeframe of the strategy, and the initial cash of the backtest. Remember to run the code cell below before moving on!

import backtrader as bt
import matplotlib as mpl
mpl.rcParams['figure.dpi'] = 140 # chart resolution

def run_backtest(strategy, symbols, start, end, timeframe=TimeFrame.Day, cash=10000):
    '''params:
        strategy: the strategy you wish to backtest, an instance of backtrader.Strategy
        symbols: the symbol (str) or list of symbols List[str] you wish to backtest on
        start: start date of backtest in format 'YYYY-MM-DD'
        end: end date of backtest in format: 'YYYY-MM-DD'
        timeframe: the timeframe the strategy trades on (size of bars) -
                   1 min: TimeFrame.Minute, 1 day: TimeFrame.Day, 5 min: TimeFrame(5, TimeFrameUnit.Minute)
        cash: the starting cash of backtest
    '''

    # initialize backtrader broker
    cerebro = bt.Cerebro(stdstats=True)
    cerebro.broker.setcash(cash)

    # add strategy
    cerebro.addstrategy(strategy)

    # add analytics
    # cerebro.addobserver(bt.observers.Value)
    # cerebro.addobserver(bt.observers.BuySell)
    cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='mysharpe')
    
    # historical data request
    if type(symbols) == str:
        symbol = symbols
        alpaca_data = rest_api.get_bars(symbol, timeframe, start, end,  adjustment='all').df
        data = bt.feeds.PandasData(dataname=alpaca_data, name=symbol)
        cerebro.adddata(data)
    elif type(symbols) == list or type(symbols) == set:
        for symbol in symbols:
            alpaca_data = rest_api.get_bars(symbol, timeframe, start, end, adjustment='all').df
            data = bt.feeds.PandasData(dataname=alpaca_data, name=symbol)
            cerebro.adddata(data)

    # run
    initial_portfolio_value = cerebro.broker.getvalue()
    print(f'Starting Portfolio Value: {initial_portfolio_value}')
    results = cerebro.run()
    final_portfolio_value = cerebro.broker.getvalue()
    print(f'Final Portfolio Value: {final_portfolio_value} ---> Return: {(final_portfolio_value/initial_portfolio_value - 1)*100}%')

    strat = results[0]
    print('Sharpe Ratio:', strat.analyzers.mysharpe.get_analysis()['sharperatio'])
    cerebro.plot(iplot= False)

				

        

Backtesting Example 1: Simple Moving Average Crossover

Below is an example of a simple backtrader strategy. It is a simple moving average crossover strategy which trades when the 13 day SMA crosses over the 25 day SMA, and sells when it crosses under.  In this example, a crossover indicator is created which is 1 when the fast SMA crosses over the slow SMA, -1 when the fast crosses below the slow, and 0 if there is no crossover.

Take note of the structure of the strategy. We can define parameters for the strategy in the params dictionary. Any indicators can be defined in __init__. You can find a full list of indicators in the backtrader documentation. The next() method is called each iteration of the backtest.  

At the end you can run run_backtest(SmaCross, 'AAPL', '2019-01-01', '2021-11-01', TimeFrame.Day, 10000), which will compute the backtest and output the results.

class SmaCross(bt.Strategy):
  # list of parameters which are configurable for the strategy
    params = dict(
        pfast=13,  # period for the fast moving average
        pslow=25   # period for the slow moving average
    )

    def __init__(self):
        sma1 = bt.ind.SMA(period=self.p.pfast)  # fast moving average
        sma2 = bt.ind.SMA(period=self.p.pslow)  # slow moving average
        self.crossover = bt.ind.CrossOver(sma1, sma2)  # crossover signal
  
    def next(self):
        if not self.position and self.crossover > 0:  # not in the market
            self.buy()
        elif self.position and self.crossover < 0:  # in the market & cross to the downside
            self.close()  # close long position


run_backtest(SmaCross, 'AAPL', '2019-01-01', '2021-11-01', TimeFrame.Day, 10000)
				

        

Backtesting Example 2: All Weather Portfolio

Another class of strategies are rebalancing strategies. These strategies attempt to match a desired portfolio. Some rebalancing strategies change the securities in their portfolio based on market conditions, others maintain a steady allocation through all conditions.

A famous rebalancing strategy is called the All Weather Portfolio by Ray Dalio. It attempts to produce steady returns through all market conditions by investing in a variety of sectors.

We'll create the portfolio by allocating to these industry ETFs.

  • 30% Vanguard Total Stock Market ETF (VTI)
  • 40% iShares 20+ Year Treasury ETF (TLT)
  • 15% iShares 7 – 10 Year Treasury ETF (IEF)
  • 7.5% SPDR Gold Shares ETF (GLD)
  • 7.5% PowerShares DB Commodity Index Tracking Fund (DBC)
class AllWeatherStrategy(bt.Strategy):

    def __init__(self):
        # the last year we rebalanced (initialized to -1)
        self.year_last_rebalanced = -1 
        self.weights = { "VTI" : 0.30 , "TLT" : 0.40, "IEF": 0.15, "GLD" : 0.075, "DBC" : 0.075 }

    def next(self):
        # if we’ve already rebalanced this year
        if self.datetime.date().year == self.year_last_rebalanced:
            return
            
        # update year last balanced
        self.year_last_rebalanced = self.datetime.date().year
        
        # enumerate through each security
        for i,d in enumerate(self.datas):
            # rebalance portfolio with desired target percents
            symbol = d._name
            self.order_target_percent(d, target=self.weights[symbol])

run_backtest(AllWeatherStrategy, ["VTI", "TLT", "IEF", "GLD", "DBC"] , '2015-01-01', '2021-11-01', TimeFrame.Day, 10000)
				

        

Paper Trading

We can use Alpaca’s trade API to place paper trades on our Alpaca account. Paper trading is free and doesn’t require you to fund your account. Let’s place trades so that we can create the All Weather Portfolio in our paper account.

import random 

percent_allocations = {'VTI': 0.30, 'TLT': 0.40, 'IEF': 0.15, 'GLD': 0.075, 'DBC': 0.075}


# get total account value
account_equity = float(rest_api.get_account().equity)


# how many dollars we want to allocate to each symbol
dollar_value_allocations = {symbol: percent * account_equity for symbol, percent in percent_allocations.items()}

# liquidate all existing positions before rebalanc
rest_api.close_all_positions()


# Rebalance portfolio
for symbol, dollars_alloc in dollar_value_allocations.items():
     
    # market price of current ETF
    market_price = rest_api.get_latest_bar(symbol).close
    
    # how many shares we want
    target_holdings = dollars_alloc // market_price
     
    # how many shares we have to buy to match target
    order_quantity = target_holdings
     
    # submit market order for this ETF
    print(f"Submitting market order for {order_quantity} shares of {symbol}")
    rest_api.submit_order(symbol, order_quantity, 'buy', 'market', client_order_id=f'colab_{random.randrange(10000000)}')
				

        

Conclusion

Now you can quickly get started with Alpaca! All the code in this article can also be found in this Google Colab Jupyter Notebook. From the notebook, you can make changes according to your own interests and goals. You can backtest your own strategy by writing up your own backtrader.Strategy class. Learn more about how you can customize the run_backtest function for your own needs in the backtrader documentation. Enjoy!


This article is solely for informational purposes only. All images are for illustrative purposes only. Alpaca does not recommend any specific investments or investment strategies. All investments involve risk of losses and the past performance of a security, or financial product does not guarantee future results or returns. Keep in mind that while diversification may help spread risk it does not assure a profit, or protect against loss. Before investing you should carefully consider your investment objectives, time horizon, and overall risk tolerance as well as the information stated in the product offering prospectuses.

Alpaca does not prepare, edit, or endorse Third Party Content. Alpaca does not guarantee the accuracy, timeliness, completeness or usefulness of Third Party Content, and is not responsible or liable for any content, advertising, products, or other materials on or available from third party sites.

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.

This is not an offer, solicitation of an offer, or advice to buy or sell securities, or open a brokerage account in any jurisdiction where Alpaca is not registered (Alpaca is registered only in the United States).




Algorithmic Trading BasicsMarket Data APIBacktestingTrading APIPython

Rahul Chowdhury

I'm a software engineer at Alpaca working to make our developers' lives easier. Previously I worked at QuantConnect where I built many algorithmic trading strategies.