Research, Backtest, and Trade from a Jupyter Notebook in Google Colab
We provide an interactive environment you can use to quickly get started with Alpaca and start exploring what you can build. You can use this environment to research new ideas, backtest them and even place paper trades.
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).