Algo Trading Tutorial Using Alpaca and JavaScript: Part Two
Welcome to part two of this tutorial on using JavaScript and Node.js with Alpaca to do algo-trading.
Please note that this article is for educational and informational purposes only. All screenshots are for illustrative purposes only. The views and opinions expressed are those of the author and do not reflect or represent the views and opinions of Alpaca. Alpaca does not recommend any specific securities or investment strategies.
This article originally appeared on Medium, written by Kendel Chopp
Welcome to part two of my tutorial on using JavaScript and Node.js with Alpaca to do algo-trading. If you did not read part one yet, be sure to check that out here. By the end of this part, you should be able to write a very simple algorithm that will create buy and sell orders in Alpaca based on two simple moving averages. For the full code for this part of the tutorial, check out index.js on my GitHub repository. If you would prefer a video-based version of this tutorial, feel free to check out my video on YouTube.
Background on Simple Moving Averages
If you are not familiar with moving averages, check out this excellent article available on Investopedia. Essentially, what we will do here is look at two moving averages on minute close prices for two different periods: last 20 minutes and last 50 minutes. When the moving averages cross, if the 20 goes about the 50 we will consider that a buy signal, and if the 20 drops below the 50 we will consider that to be a sell signal. This is not a great algorithm for trading but will serve as a proof of concept into how we can write real trading algorithms.
Install lodash and technical indicators
If you have programmed in JavaScript before, there is a decent chance you have used lodash. It provides some extremely helpful methods that make working with objects and arrays very easy, so we will use it in our program. Additionally, we will use a package called technicalindicators
which will allow us to do some quick technical analysis, and more specifically, simple moving average calculations. To install these packages, run these two commands:
npm install --save lodash
npm install --save technicalindicators
Once you have done these, you should be ready to hop back into the code!
Import the new packages
Next, we need to import these packages we just installed. At the top of index.js
, go ahead and add these lines around our Alpaca import:
const _ = require(‘lodash’);
const Alpaca = require(‘@alpacahq/alpaca-trade-api’);
const SMA = require(‘technicalindicators’).SMA;
We are getting lodash as _
and SMA
from the technical indicators package so that we can do some simple moving averages.
Remove printAccount
Next, you can go ahead and delete the following lines which we were only using for testing our connection to Alpaca:
async function printAccount() {
const account = await alpaca.getAccount();
console.log(account);
}
printAccount();
Add Variables
Next, we will initialize some variables which we will use to track the simple moving averages and check when the two averages cross. We will create two variables to track the simple moving averages: sma20
and sma50
. Then, we will create a variable called lastOrder
which will track whether our last order was a buy or sell order. We need to track what the last order was so that we only buy/sell if we are, in fact, crossing over the averages, not just staying above or below. This initialization should look something like the following:
let sma20, sma50;
let lastOrder = ‘SELL’;
We set lastOrder
to ‘SELL’
because we are assuming that we have no shares of the stock we are trading and therefore want our first order to be a buy order.
Initialize Simple Moving Averages
Next, we will write a function called initializeAverages
. This function will establish the baseline for the simple moving averages, and allow us to start trading as the stock price moves. The first thing we need to do in that function is get the previous bars as follows:
async function initializeAverages() {
const initialData = await alpaca.getBars(
‘1Min’,
‘SPY’,
{
limit: 50,
until: new Date()
}
);
}
This will give us the previous 50 one-minute bars and allow us to establish an average on the latest 20 and 50 close prices. So, since we are taking an average of the close prices, we will map the bars we have received into an array of close prices:
async function initializeAverages() {
const initialData = ...
const closeValues = _.map(initialData.SPY, (bar) => bar.c);
}
Here, we are using lodash to take all of the bars in initialData.SPY
and only using the number stored with key c
which is the close value. This will give us an array of close values which we can use now to establish our moving averages:
async function initializeAverages() {
const initialData = ...
const closeValues = ...
sma20 = new SMA({ period: 20, values: closeValues });
sma50 = new SMA({ period: 50, values: closeValues });
console.log(sma20.getResult());
console.log(sma50.getResult());
}
initializeAverages();
Here, we are using our technical indicator package’s SMA
object to find moving averages. When these results print you will notice that sma20
will print 31 moving averages and sma50
will only print one. This is because there are 31 possible consecutive sets of 20 data points which will give us a moving average, but only one possible consecutive set of 50. So, you should have code now that looks like this:
async function initializeAverages() {
const initialData = await alpaca.getBars(
‘1Min’,
‘SPY’,
{
limit: 50,
until: new Date()
}
);
const closeValues = _.map(initialData.SPY, (bar) => bar.c);
sma20 = new SMA({ period: 20, values: closeValues });
sma50 = new SMA({ period: 50, values: closeValues });
console.log(`sma20: ${sma20.getResult()}`);
console.log(`sma50: ${sma50.getResult()}`);
}
initializeAverages();
Create Alpaca Websocket Client
Next, we will create a client that will allow us to use the Alpaca websocket to listen for interesting events like changes in stock prices. First, we will simply create a client
variable and add an onConnect
function:
const client = alpaca.websocket;
client.onConnect(() => {
client.subscribe([‘AM.SPY’]);
setTimeout(() => client.disconnect(), 6000*1000);
});
So, there’s a lot going on here. First, we create the client variable which we grab from Alpaca. Next, we add an event listener that happens whenever we connect to the websocket. In this function, we can subscribe to a list of channels. In this case, we subscribe to AM.SPY
which means listen for the aggregate minute updates for SPY. This will make it so we can get an update every minute with a bar on SPY containing open, close, high, low, volume, and other values. Lastly, we set a timeout at 6000 seconds which will disconnect us from the websocket so that we do not leave that connection open.
Add Channel Listener
Next, we will add a listener for the minute updates. We will parse the data we receive and take the close value from it:
client.onStockAggMin((subject, data) => {
const nextValue = JSON.parse(data)[0].c;
});
In this case, the data will come back in the form of a JSON with an array of bars. Since we only have one channel, the 0th entry will be the one we care about, and yet again we will use the c
key to get the close value. After that, we can recalculate the simple moving averages:
client.onStockAggMin((subject, data) => {
const nextValue = ...
const next20 = sma20.nextValue(nextValue);
const next50 = sma50.nextValue(nextValue);
console.log(`next20: ${next20}`);
console.log(`next50: ${next50}`);
});
This will get the recalculated simple moving averages and print them out. Now, we can use these recalculated simple moving averages to go ahead and decide whether to buy or sell. First, we will cover the buy case which is when the last 20 moving average crosses above the last 50. We can do this with a simple if statement saying if the new 20 moving average is higher than the 50 and we did not buy last then a cross is occurring:
client.onStockAggMin((subject, data) => {
const nextValue = ...
const next20 = ...
const next50 = ...
if (next20 > next50 && lastOrder !== ‘BUY’) {
// Buy!
}
});
We can use the opposite logic to check if we are crossing in the opposite direction:
client.onStockAggMin((subject, data) => {
const nextValue = ...
const next20 = ...
const next50 = ...
if (next20 > next50 && lastOrder !== ‘BUY’) {
// Buy!
} else if (next20 < next50 && lastORder !== ‘SELL’) {
// Sell!
}
});
After that, we can submit the orders to Alpaca and log what happened. In this case, I will tell Alpaca to buy or sell 300 shares of SPY as a market order which expires after the day. Since there is almost definitely a high volume on SPY these orders should be filled quickly. The 300 is somewhat arbitrary based on the fact that my paper account has $100,000 and SPY costs roughly $300 as of making this. Here is what it will look like:
client.onStockAggMin((subject, data) => {
const nextValue = ...
const next20 = ...
const next50 = ...
if (next20 > next50 && lastOrder !== ‘BUY’) {
alpaca.createOrder({
symbol: ‘SPY’,
qty: 300,
side: ‘buy’,
type: ‘market’,
time_in_force: ‘day’
});
lastOrder = ‘BUY’;
console.log(‘\nBUY\n’);
} else if (next20 < next50 && lastOrder !== ‘SELL’) {
alpaca.createOrder({
symbol: ‘SPY’,
qty: 300,
side: ‘sell’,
type: ‘market’,
time_in_force: ‘day’
});
lastOrder = ‘SELL’;
console.log(‘\nSELL\n’);
}
});
Last, but not least, we must now connect to the client which will kick off the onConnect
listener event and subscribe us to the channel that will call the onStockAggMin
handler. To do this we use this simple line:
client.connect();
Summary
That is it! Your code should now look something like this. Now you can run this with node index.js
and let it sit and run. Depending on how SPY is moving you may see lots of orders or very few. I highly recommend you mess around with what stock you trade, the parameters which modify whether you buy or sell, and the quantity you buy or sell. This is just a simple proof of concept into how we can write a very basic trading algorithm. I hope this was helpful, and I look forward to bringing you more helpful tutorials as I continue to learn myself!
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).