Introduction
Among all technical indicators, the most popular and widely recognized are the volume-weighted average price (VWAP) and the time-weighted average price (TWAP). They’re used to identify trends, potential support and resistance levels, and gauge investment performance.
In this article, we dive into how to calculate these indicators and how to create a simple algorithmic trading bot that makes buy and sell decisions based on VWAP.
Using Alpaca to Trade with VWAP
Download Dependencies
First, we must pip install (download) all the required dependencies for the program. In this case, the only required dependency that does not come pre-installed is alpaca-py.
%%capture
!pip install alpaca-py
Import Dependencies
From within alpaca-py, we need to import methods to access historical crypto data, live crypto data, and paper trading account access. We also need matplotlib in order to plot graphs of the VWAP and TWAP and their relation to the Bitcoin Price.
from alpaca.data.historical import CryptoHistoricalDataClient
from alpaca.data.requests import CryptoBarsRequest
from alpaca.data import CryptoDataStream
from alpaca.data.timeframe import TimeFrame
from alpaca.trading.client import TradingClient
from alpaca.trading.requests import MarketOrderRequest
from alpaca.trading.enums import OrderSide, TimeInForce
import matplotlib.pyplot as plt
Define Variables
The next step is to define most of the general variables we need throughout the program. For the API and Secret keys, you can access those on the main dashboard of your paper trading account overview.
For the trading strategy, we’ll buy and sell Bitcoin and each transaction will consist of 1 quantity.
# API Credentials
API_KEY = '********************'
SECRET_KEY = '****************************************'
trading_client = TradingClient(API_KEY, SECRET_KEY, paper=True)
# Define variables
symbol = 'BTCUSD'
qty_per_trade = 1
start_date = "2022-08-08"
Create Function to Check Account Positions
Next, we have to create a function to check whether the trading account currently holds any Bitcoin. If it does, we can return a 1 indicating true. If there is no Bitcoin, then the function will return 0.
This is important because in an upcoming function which handles the buying and selling, we can focus on buying only if there is currently no Bitcoin in the account. Otherwise, we’ll monitor the sell signal to see if the position should be closed.
# Check Whether Account Currently Holds Symbol
def check_positions(symbol):
positions = trading_client.get_all_positions()
if symbol in str(positions):
return 1
return 0
Retrieve Historical Bitcoin Price Data
Using alpaca-py’s cryptocurrency historical data client, we can send a request to get hourly data for Bitcoin from the start date specified earlier. In order to manipulate the data further, we can turn the resulting bars data into a dataframe.
# no keys required for crypto data
client = CryptoHistoricalDataClient()
request_params = CryptoBarsRequest(
symbol_or_symbols=["BTC/USD"],
timeframe=TimeFrame.Hour,
start=start_date
)
bars = client.get_crypto_bars(request_params)
df = bars.df
df
Create Function to Calculate VWAP
To calculate the VWAP, we have to sum the average of the high, low, and close multiplied by the volume and divide that figure by the cumulative volume values. We can assign the new vwap value to a dataframe column.
# Create VWAP function
def vwap(dataframe):
v = df['volume'].values
tp = (df['low'] + df['close'] + df['high']).div(3).values
return df.assign(vwap=(tp * v).cumsum() / v.cumsum())
vwap_df = vwap(df)
vwap_df
Plot Bitcoin Price and VWAP
Using the new dataframe calculated in the previous function, we can now visualize the price of Bitcoin as well as its VWAP on the same timeframe.
# Plot Price vs VWAP
plt.subplots()
plt.plot(vwap_df["close"].to_numpy())
plt.plot(vwap_df["vwap"].to_numpy())
plt.show()
Create Function to Calculate TWAP
The time-weighted average price can be calculated by taking the cumulative sum of averaging the low, close, and high for each price bar and dividing that by the time period (or number of price bars).
# Create TWAP function
def twap(dataframe, period):
tp = (df['low'] + df['close'] + df['high']).divide(3)
return df.assign(twap=(tp.rolling(period).sum().divide(period)))
twap_df = twap(df, 5)
twap_df
Plot Bitcoin Price and TWAP
Again, we can use the new TWAP dataframe calculated in the previous function to visualize the price of Bitcoin as well as it’s TWAP on the same timeframe.
# Plot Price vs TWAP
plt.subplots()
plt.plot(twap_df["close"].to_numpy())
plt.plot(twap_df["twap"].to_numpy())
plt.show()
Create Function to Trade Bitcoin via VWAP Strategy
Finally, create a function to retrieve the data, calculate the VWAP indicator signals, and execute the buy/sell orders. First, the function takes in an input of bar data which is used for the live current close price.
The trend of the VWAP shows if buyers or sellers are in control at a particular moment. If the VWAP is rising, then buyers are in control and vice versa. If the price is above the VWAP from a particular start date, then that means the average long participant in the stock is making money. Similarly, if the price is below the VWAP from a particular start date, then that means the average short participant in the stock from that period is making money.
In the case that the closing price is above the VWAP indicator and the VWAP is an uptrend, a buy signal is issued. If the closing price falls below the VWAP indicator and the VWAP is in a downtrend, then a sell signal is issued. By using the latest values from the dataframe, we create boolean values for buy and sell signals.
We can set the code to buy 1 quantity of Bitcoin if there is currently no position and if a buy signal is true. Likewise, we can set the code to sell 1 quantity of Bitcoin if there is currently a position and if a sell signal is true.
Wrapping the entire function in a try/except block ensures that the program will not break due to errors, and will simply print out the error message. Since this is a trading bot and is intended to run throughout market hours, it’s best if the program is continuously running.
# VWAP Indicator Bot Function
def vwap_bot():
try:
position = check_positions(symbol=symbol)
should_buy = vwap_df['close'][-1] > vwap_df["vwap"][-1] and vwap_df["vwap"][-1] > vwap_df["vwap"][-2]
should_sell = vwap_df['close'][-1] < vwap_df["vwap"][-1] and vwap_df["vwap"][-1] < vwap_df["vwap"][-2]
print(f"Price: {vwap_df['close'][-1]}")
print("VWAP: {}".format(vwap_df["vwap"][-1]))
print(f"Position: {position} | Should Buy: {should_buy}")
# Check if No Position and Buy Signal is True
if position == 0 and should_buy == True:
# preparing orders
market_order_data = MarketOrderRequest(
symbol=symbol,
qty=qty_per_trade,
side=OrderSide.BUY,
time_in_force=TimeInForce.GTC
)
# Market order
market_order = trading_client.submit_order(
order_data=market_order_data
)
message = f'Symbol: {symbol} | Side: Buy | Quantity: {qty_per_trade}'
print(message)
# Check if Long Position and Sell Signal is True
elif position > 0 and should_sell == True:
# preparing orders
market_order_data = MarketOrderRequest(
symbol=symbol,
qty=qty_per_trade,
side=OrderSide.Sell,
time_in_force=TimeInForce.GTC
)
# Market order
market_order = trading_client.submit_order(
order_data=market_order_data
)
message = f'Symbol: {symbol} | Side: Sell | Quantity: {qty_per_trade}'
print(message)
print("-"*20)
except Exception as e:
print (e)
Set Up Alpaca Live Crypto Data
The last step of building the Python bot is to start streaming live market data for Bitcoin from Alpaca. Luckily, Alpaca makes this process extremely easy.
First, create an instance of the data streaming API by calling the CryptoDataStream method in which we pass the API keys. Then, create an asynchronous function to receive the live bar data; and within this function, we can call the vwap_bot function.
Here we subscribe to crypto data and start streaming live data.
crypto_stream = CryptoDataStream(API_KEY, SECRET_KEY)
# async handler
async def quote_data_handler(data):
print(data)
vwap_bot()
crypto_stream.subscribe_quotes(quote_data_handler, "BTCUSD")
crypto_stream.run()
Conclusion
In this tutorial, we covered everything from using historical data to calculate the volume-weighted average price and time-weighted average price to executing live orders in Alpaca’s paper trading account. To access and run the code from this article in a Google Colab Notebook, check out this link.
We hope you learned a thing or two about using the VWAP and TWAP with the alpaca-py Python SDK!
Please note that this article is for informational purposes only. The example above is for illustrative purposes only. Actual crypto prices may vary depending on the market price at that particular time. Alpaca Crypto LLC does not recommend any specific cryptocurrencies.
Cryptocurrency is highly speculative in nature, involves a high degree of risks, such as volatile market price swings, market manipulation, flash crashes, and cybersecurity risks. Cryptocurrency is not regulated or is lightly regulated in most countries. Cryptocurrency trading can lead to large, immediate and permanent loss of financial value. You should have appropriate knowledge and experience before engaging in cryptocurrency trading. For additional information please click here.
Cryptocurrency services are made available by Alpaca Crypto LLC ("Alpaca Crypto"), a FinCEN registered money services business (NMLS # 2160858), and a wholly-owned subsidiary of AlpacaDB, Inc. Alpaca Crypto is not a member of SIPC or FINRA. Cryptocurrencies are not stocks and your cryptocurrency investments are not protected by either FDIC or SIPC. Please see the Disclosure Library for more information.
This is not an offer, solicitation of an offer, or advice to buy or sell cryptocurrencies, or open a cryptocurrency account in any jurisdiction where Alpaca Crypto is not registered or licensed, as applicable.