Alpaca Trading API Guide – A Step-by-step Guide

14 min read

Get 10-day Free Algo Trading Course

Last Updated on October 22, 2020

Alpaca Trading Homepage Image
Alpaca’s Homepage

Table of Contents

  1. What is the Alpaca Trading API?
  2. Why should I use the Alpaca Trading API?
  3. Does the Alpaca API allow backtesting?
  4. How do I get started with the Alpaca API?
  5. Testing for connectivity
  6. How do I get historical data from the Alpaca API?
  7. How do I use Websockets to stream data with the Alpaca API?
  8. How can I use indicators with the Alpaca API?
  9. How can I fire order in the Alpaca API?
  10. How do I set a stop loss or take profit?
  11. Which stocks can you trade with Alpaca?
  12. How do I find out what time the market closes?
  13. Putting it all together – a fully functioning trading script
  14. Final thoughts on the Alpaca API

What is the Alpaca Trading API?

It is an interface that allows you to trade automatically with the stock broker Alpaca.

More specifically, the trading API allows you to send orders directly to Alpaca’s servers to automate the process of trading, bypassing a traditional client.

Why should I use the Alpaca Trading API?

There are several reasons you might want to use Alpaca’s trading API. A common use is to build an automated trading system. But many people have used it for other purposes as well such as creating a trading dashboard or a custom client app.

The main draw to Alpaca is that it doesn’t charge commissions on trades. Have a look at our Alpaca Stock Brokerage Review where we discuss the pros and cons of the broker and outline how they actually make money.

Does the Alpaca API allow backtesting?

Alpaca’s trading API does not come with backtesting functionalities. However, they have created an integration with a backtesting library called Backtrader.

You can more about backtesting with Backtrader here: Backtrader for Backtesting (Python) – A Complete Guide

» If you are looking for trading ideas to backtest, check out this guide: 4 Quantitative Trading Strategies that (might) Work Today

How do I get started with the Alpaca API?

  1. Create an account with Alpaca – You can either sign up for a live account or a paper trading account to get started. Navigate to the Alpaca home page – https://alpaca.markets/ and click on Sign up. At this time, Alpaca only accepts US clients for live accounts although you should still be able to open a paper trading account if you are not in the US.
  2. Get an API Key – After creating an account, log in to view your API key and secret key. The endpoint used to make calls to the REST API should also be displayed. Take note of all three of these values.
  3. Install the Alpaca Python Library – Alpaca has a library, otherwise known as the client SDK, which simplifies connecting to the API. To install it, type in pip3 install alpaca-trade-api from your command prompt. 

Notes about the Alpaca library – 

  • The Alpaca API library only works with Python 3.6 and above as it uses asynchronous functions that are not supported in older versions of Python.
  • Rather than installing via pip, you can download the zip file from GitHub – https://github.com/alpacahq/alpaca-trade-api-python/ and place the alpaca_trade_api folder in your projects folder after extracting. The library has several dependencies, a full list can be found in the setup.py script.
  • Alpaca has launched V2 of their trading API and will discontinue the earlier version soon. For this reason, it is recommended to stick with V2 functions only.
  • Alpaca currently imposes a rate limit of 200 requests per minute

Testing for connectivity

import alpaca_trade_api as tradeapi

#authentication and connection details
api_key = 'Insert_your_api_key_here'
api_secret = 'Insert_your_api_secret_here'
base_url = 'https://paper-api.alpaca.markets'

#instantiate REST API
api = tradeapi.REST(api_key, api_secret, base_url, api_version='v2')

#obtain account information
account = api.get_account()
print(account)

The above code instantiates the REST class which will be used for all of the calls to the REST API. We then use the get_account() function to get details about our account.

Your output should contain your account details in a JSON format. If you received a 401 authentication error, your API key or secret might have been typed incorrectly. 

Make sure the base URL matches the endpoint you noted down when obtaining your API keys.

We’ve hardcoded our API key and secret since we are testing Alpaca out with a paper trading account. In a live environment, however, it is a good idea to take the extra security precaution of storing your authentication details in environment variables

A cool feature of the Alpaca library is that it can automatically retrieve values you’ve stored as environment variables. Just make sure to use the following naming convention:

APCA_API_KEY_ID=<key_id>
APCA_API_SECRET_KEY=<secret_key>
APCA_API_BASE_URL=url

If you decide to store your API info as environment variables, you only need to pass through the API version when instantiating the REST class, like this: 

api = tradeapi.REST(api_version='v2')

How do I get historical data from the Alpaca API?

Alpaca offers both an in-house source for data as well as a third-party solution via Polygon. Currently, the Polygon service is only offered to users that have a live funded account.

For more details on the difference between the two data providers, check out our Alpaca Stock Brokerage Review.

We will use the in-house source to get historic data for Apple (AAPL).

aapl = api.get_barset('AAPL', 'day')

In the above example, we are making a call for historical prices for Apple stock which has the ticker AAPL. 

The data that is returned within a custom class created by the library. There a few things we can do at this point.

If you like working with Pandas DataFrame, the library will automatically create a dataframe of the returned data. We can access it by appending .df at the end of the variable we’ve created, like so:

print(aapl.df)

Alternatively, if you’re not a fan of Pandas, the raw data can be accessed in a dictionary format as follows –

print(aapl._raw)

The example above retrieved daily candles. We can easily change this for other time frames. The valid intervals are: 1Min, 5Min, 15Min and 1D

Here is a code example for obtaining 15-minute closing price data for Tesla: 

tsla = api.get_barset(‘TSLA’, ’15Min’)

By default, the data is limited to the last 100 bars. We can increase this to as high as 1000 bars by passing through a limit into the function.

aapl = api.get_barset(‘AAPL’, ‘day’, limit=1000)

How do I use WebSockets to stream data with the Alpaca API?

Alpaca offers WebSockets which will push information to you without having to constantly make request calls to the API.

There are two main functions the Alpaca websocket provides – market price data updates and account updates. We will go through an example of setting up a websocket to listen for account updates.

This is a very useful function. Once you’ve sent your order, it can confirm that the order has been submitted to the exchange. It can also let you know when the orders fills or if you only got a partial fill. 

Here is the code: 

conn = tradeapi.stream2.StreamConn(api_key, api_secret, base_url)

@conn.on(r'^account_updates$')
async def on_account_updates(conn, channel, account):
    print('account', account)

@conn.on(r'^trade_updates$')
async def on_trade_updates(conn, channel, trade):
    print('trade', trade)

def ws_start():
	conn.run(['account_updates', 'trade_updates'])

#start WebSocket in a thread
ws_thread = threading.Thread(target=ws_start, daemon=True)
ws_thread.start()

The last half of the code snippet above serves to run the WebSocket in a thread. Otherwise, it would block your main script. Make sure to include import threading at the top of your script.

There are several other useful pieces of information that can be had from implementing WebSockets, here is a full list: – https://alpaca.markets/docs/api-documentation/api-v2/streaming/#common-events

How can I use indicators with the Alpaca API?

In the past, Alpaca offered data via the Alpha Vantage API which had built-in support for indicators. They have since transitioned to an in-house solution and do not offer indicator data through the API.

There are several options to get indicator values. You could continue to use the Alpha Vantage API by signing up directly for a key on their website. Or, you can use a third-party library to calculate indicators.

Since the data returned by the Alpaca library comes in Pandas format, Pandas can be used to calculate various things like a moving average or a standard deviation.

There are also several third-party libraries that simplify creating technical indicators. Here are a few popular ones:

  • Pandas TA – The Pandas Technical Analysis libraries offers over 120 indicators and works well in this scenario since the returned data is already in a Pandas Dataframe
  • TA-LIB – This is one of the most popular libraries out there. Several brokers use it in fact. It isn’t natively supported in Python but a wrapper is available.

How can I fire order in the Alpaca API?

api.submit_order(symbol='TSLA', 
		qty=1, 
		side='buy', 
		time_in_force='gtc', 
		type='limit', 
		limit_price=400.00, 
		client_order_id='001')

The above code snippets sends an order to buy one share of TSLA at a limit price of $400.

If you’d like to submit a market order, use type='market' and remove the limit_price parameter.

If you’d like to short sell, use side='sell'. It’s a good idea to check first to make sure you’re able to sell that particular security.

We’ve assigned a string value of 001 as an order id to this order so that we can reference it with ease later on. This is optional. If you do decide to include a client_order_id, make sure the value is unique otherwise the library will raise an exception.

If you’re using the built-in WebSockets feature, you will automatically be notified that the order has been submitted. Otherwise, you can make a request to check on your position as follows.

position = api.get_position('TSLA')

Alternatively, you can check on positions based on the order id you’ve assigned as follows:

position = api.get_order_by_client_order_id('001')

How do I set a stop loss or take profit?

When submitting an order, you can attach either a stop loss, take profit, or both. Here is an example: 

api.submit_order(symbol='TSLA', 
		qty=1, 
		side='buy', 
		time_in_force='gtc', 
		type='limit', 
		limit_price=400.00, 
		client_order_id=001, 
		order_class='bracket', 
		stop_loss=dict(stop_price='360.00'), 
		take_profit=dict(limit_price='440.00'))

We’ve added a parameter to indicate this is a bracket order which is required when setting a stop loss and take profit. Further, the stop loss and take profit need to be nested, so we use dict() with the required parameters and the price point as a string.

Which stocks can you trade with Alpaca?

active_assets = api.list_assets(status='active')

The above code snippet will return all the active stocks available for trading with Alpaca. It will include information such as the exchange the stock trades on and if you can short it.

You can reverse search as well. Say if you already have a stock that you want to trade, you can check to make sure Alpaca offers it with the following code:

aapl_asset = api.get_asset('AAPL')

If an asset is not found on Alpaca’s system, it will raise an error. For this reason, its a good idea to use a try/except block when running the above code.

How do I find out what time the market closes?

print(api.get_clock())

This is a useful function as it returns when the market closes, when it will open next, as well as the server time.

This way you can make sure you are using the same time format the API is using and you can pause your algo when the market is not open.

Putting it all together – a fully functioning trading script

We will go through a fully automated trading system that utilizes the Alpaca API. The objective is to show a practical use case for the functionality described in the guide thus far. Therefore, there is not much emphasis on the actual trading strategy, and we don’t expect it to be a profitable one.

The strategy we will use is a simple breakout system. 

Breakout Example

The image above provides a visual of the strategy, the grey box contains ten candles and we will filter out the absolute high and low. If the price rallies above the high, we will buy. If it falls below the low, we will submit a sell order.

The chart above eventually provided a sell signal. The next step is to determine our stop loss and take profit. First, we calculate the distance between the range high and range low $251.74 – $250.67 = $1.07.

We will set our stop loss $1.07 higher than our entry point and our take profit $1.07 lower than our entry point.

Let’s get started.

import alpaca_trade_api as tradeapi
import threading
from time import sleep 
import json
import logging

These are all our imports. We have gone over the first two already. The time module will be used to put the script at sleep when needed. The JSON module is needed so that we can export a list of our trades to a CSV. Lastly, we will use logging to keep a log file of any errors that may pop up.

#init
logging.basicConfig(filename='errlog.log',level=logging.WARNING,  
                format='%(asctime)s:%(levelname)s:%(message)s')

api_key = 'insert_api_key'
api_secret = 'insert_api_secret'
base_url = 'https://paper-api.alpaca.markets'

api = tradeapi.REST(api_key, api_secret, base_url, api_version='v2')

trade_msg = []
order_msg = []
past_trades = []

searching_for_trade = False
order_sent = False
order_submitted = False
active_trade = False
done_for_the_day = False

Here we initialize the API and the logging function. We are also defining several variables. Their use will become clear as we go through the script.

api.cancel_all_orders()

In this next part, we have our first experience with the API. We are calling API to cancel any open orders. It’s there in case our script got interrupted for whatever reason and we have to restart in the middle of a market day.

#check if market is open
clock = api.get_clock()

if clock.is_open:
	pass
else:
	time_to_open = clock.next_open - clock.timestamp
	sleep(time_to_open.total_seconds())

Next, we use the clock function to see if the markets are open. Ideally, we would start the script before the markets open. The code above checks what time the markets open and sleeps until then.

if len(api.list_positions()) == 0:
	searching_for_trade = True
else:
	active_trade = True

The following if statement checks to see if we have any open positions. Again, this is just some extra error checking in case our script was interrupted in the middle of a market day. In most cases, we won’t have a trade open when starting the script.

#init WebSocket
conn = tradeapi.stream2.StreamConn(api_key, api_secret, base_url)

@conn.on(r'^account_updates$')
async def on_account_updates(conn, channel, account):
	order_msg.append(account)

@conn.on(r'^trade_updates$')
async def on_trade_updates(conn, channel, trade):
	trade_msg.append(trade)
	if 'fill' in trade.event:
		past_trades.append([trade.order['updated_at'], trade.order['symbol'], trade.order['side'], 
					trade.order['filled_qty'], trade.order['filled_avg_price']])
		with open('past_trades.csv', 'w') as f:
			json.dump(past_trades, f, indent=4) 

def ws_start():
	conn.run(['account_updates', 'trade_updates'])

#start WebSocket in a thread
ws_thread = threading.Thread(target=ws_start, daemon=True)
ws_thread.start()
sleep(10)

We’ve already discussed the WebSocket. A small addition here is that some of the parameters from filled orders are appended to a list and then saved to a file. This way, we will have a clean list of any orders we’ve executed for our records.

The script sleeps for 10 seconds after the WebSocket is called just to give it enough time to start and authenticate.

#functions
def time_to_market_close():
	clock = api.get_clock()
	closing = clock.next_close - clock.timestamp
	return round(closing.seconds/60)

def send_order(direction):
	if time_to_market_close() > 20:
		if direction == 'buy':
			sl = high-range_size
			tp = high+range_size
		elif direction == 'sell':
			sl = low+range_size
			tp = low-range_size

		api.submit_order(symbol='AAPL', qty=100, side=direction, type='market', time_in_force='day', order_class='bracket', stop_loss=dict(stop_price=str(sl)), take_profit=dict(limit_price=str(tp)))
 		return True, False
	
	else:
		return False, True

Our functions come next. The first one simply calculates how many minutes are left until the market close. We will need to check this as we don’t want to execute any new orders just before the market closes. The intention is not to carry any trades overnight.

The second function is what we will use to submit orders. We first check to see if there is at least 20 minutes left until the market close. If so, we do a quick calculation of our take profit and stop loss and then submit a market order, similar to our prior example.

#main loop
while True:

	try:

We will now start the main loop. We’ve enclosed the entire loop in a try/except block as the Alpaca API raises an exception if something goes wrong.

candlesticks = api.get_barset('AAPL', 'minute', limit=10)
high = candlesticks['AAPL'][0].h
low = candlesticks['AAPL'][0].l
for candle in candlesticks['AAPL']:
	if candle.h > high:
		high = candle.h
	elif candle.l < low:
		low = candle.l
range_size = high - low
if range_size / candlesticks['AAPL'][0].c < 0.003:
	range_size = candlesticks['AAPL'][0].c * 0.003

The first part of our main loop is the code used to determine our range. It utilizes the barset function of the Alpaca API to get the last 10 bars. At that point, it iterates through the bars to pick out the highest and lowest values.

The API won’t allow us to send orders if the stop loss or take profit is less than 0.1% away from that price. For this reason, we’ve added an extra check to ensure our range is bigger than 0.3% of the current price. Although it only needs to be 0.1%, we added the extra buffer as there could be a price difference between the time we send the order and when it executes.

Our main script will have five main loops. We will go through them now.

while searching_for_trade:
	clock = api.get_clock()
	sleep(60-clock.timestamp.second)
	candlesticks = api.get_barset('AAPL', 'minute', limit=1)
	if candlesticks['AAPL'][0].c > high:
		searching_for_trade = False
		order_sent, done_for_the_day = send_order('buy')

	elif candlesticks['AAPL'][0].c < low:
		searching_for_trade = False
		order_sent, done_for_the_day = send_order('sell')

At this point, as the variable suggests, we are searching for a trade. The first step is to check the time and put the script to sleep until a fresh one-minute bar is available.

This is an inefficient way to go about things. If you use a real account, you will have access to Polygon data which is available through a WebSocket. In that scenario, new candles will be automatically pushed to you as they become available. Since we are using a paper trading account, we will manually query for new data using this method.

Once we have a new bar, we check to see if the price has crossed a new low or high. If so, we will send the appropriate buy or sell order.

When we send an order, we set the searching_for_trade variable to False, which signals to our script that its time to move on to the next loop.

The exception to this is if there is less than 20 minutes left to the market close as we don’t want to initiate new positions at that time. In that case, we set done_for_the_day to True so that the script can skip to the last loop which we will cover shortly.

while order_sent:
	sleep(1)
	for item in trade_msg:
		if item.event == 'new':
			order_submitted = True
			order_sent = False

The order sent loop is straight forward. Recall that we are storing our WebSocket data as a list item into a variable. We are simply checking this list to see if a contains a new event, or in other words, a new trade order.

This is an oversimplified error check. It works, but in your script, you might be interested in a more comprehensive check.

We’ve added a one second sleep here in order to avoid overusing the CPU as an infinite loop is being used.

while order_submitted:
	sleep(1)
	for item in trade_msg:
		if item.order['filled_qty'] == '100':
			order_submitted = False
			active_trade = True
			trade_msg = []

Next is the order_submitted loop which is very similar to the last loop. The difference here is that we are checking to make sure the order is filled before moving to the next step. 

Once the order is filled, we are also clearing out our trade message list. We do this to prepare it for the next trade. We didn’t see any use to keeping this data as you can always access old orders through the API.

while active_trade:
	for i in range(time_to_market_close()-5):
		sleep(60)
		if len(api.list_positions()) == 0:
			active_trade = False
			searching_for_trade = True
			break
	if active_trade:
		done_for_the_day = True
		active_trade = False

We now move on to the active_trade loop. This is an important one as we need to keep an eye on when the market closes. If we are still in a trade about five minutes to market close, we will just close it at market to avoid holding a position overnight.

This loop will query the API for open positions every minute. If no open positions have been found, it tells us that our stop loss or take profit has been hit. In this scenario, we can break out of the loop and start the process of searching for a new trade signal over again.

If the for loop runs out and we are still in an open position, the active_trade variable will still be True. This means there is five minutes left until the market close so we will signal that we are done for the day.

while done_for_the_day:
     api.close_all_positions()
     clock = api.get_clock()
     next_market_open = clock.next_open - clock.timestamp
     sleep(next_market_open.total_seconds())
     searching_for_trade = True

The done for the day loop simply closes all open positions. We can call this command as our strategy only has one open position at a time. 

Next, we call the clock function to found out when the market opens next and put the script back to sleep until then. Finally, we set searching_for_trade = True so that the script can start the process all over again once it wakes up at the next market open.

except Exception as e:
	logging.exception(e)

Lastly, we will log any exceptions so that we can keep an eye on any errors that might come up with the API.

And there you have it, a full trading script using the Alpaca API.

You are welcome to use this code and build on it. We would recommend doing some further error checking and also looking into handling specific exceptions that are commonly raised by the API.

Final thoughts on the Alpaca API

Overall, the Alpaca API does a good job at what it is supposed to do. 

We did run into a few quirks in our testing. For example, when making calls for historical data it took on average 15 seconds to get a response. This might not be practical for trading systems where speed is of the essence.

There were also a few discrepancies in the Alpaca documentation. Although to be fair, they appear to be in the middle of a migration to a new version. 

If you’re happy with the API, and plan to continue using it, we recommend taking a look at the rest.py source file included in the library to get a better understanding of all of the functions available.

help(tradeapi.REST)

You can also run the above line of code to get all of the functions available within the REST class.

The code used in the examples is available on GitHub. From the GitHub page, click the green button on the right “Clone or download” to download or clone the code.

Jignesh Davda