Build a Dollar Cost Averaging (DCA) Service with Cloudflare Workers and the Alpaca API
Crypto is my first financial love. It’s weird, fresh and addresses some of the most fundamental issues with existing traditional financial foundations. The existing financial landscape is a sea of isolated islands, crypto reverses the islands for the sea introducing aquatic collaboration and amorphousness. Where once social, economic and geographic borders defined who was in and who was out now inclusion and access rule the day.
This isn’t a post about the benefits of crypto however, this is a technical post about setting up dollar cost averaging (DCA) purchases of traditional stocks and ETFs using the Alpaca API. I love crypto but I also understand the value of a diversified portfolio and of playing the long game as technical revolutions happen slowly by utilizing today’s tools to build tomorrow’s changes.
TL;DR
Prerequisites
Before we can make any progress you’ll need to setup two obvious prerequisites. A Cloudflare account and an Alpaca account, let’s set those up.
Cloudflare account
The goal here is to get to a point after generating a Cloudflare account where you can see and access the Workers page.
.
.
.
.
.
.
.
.
.
.
.
Alpaca account
The goal here is to get to a point after generating an Alpaca account where you can access Paper Trading. (Alpaca’s testnet)
.
.
.
.
.
.
Prepare The App
Today’s app will be written in JavaScript and utilize Cloudflare’s Wrangler CLI tool to help us deploy the app to the Cloudflare Worker environment. Let’s go ahead and get that tool installed and our app initialized.
$ npm i @cloudflare/wrangler -g
If you encounter errors follow any additional instructions here.
Next we’ll create our app directory and initialize both the wrangler app and npm.
$ mkdir easy-dca-wrangler
$ cd easy-dca-wrangler
$ wrangler init
$ npm init -y
Cool, now let’s get some basic files changed and added.
// UPDATE package.json{
"private": true,
"main": "./src/index.js",
"scripts": {
"deploy": "wrangler publish"
},
"dependencies": {
"bluebird": "^3.7.2",
"lodash": "^4.17.21"
},
"devDependencies": {
"@babel/core": "^7.13.10",
"@babel/plugin-proposal-optional-chaining": "^7.13.8",
"@babel/preset-env": "^7.13.10",
"babel-loader": "^8.2.2"
}
}
.
// CREATE babel.config.json{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "current"
}
}
]
],
"plugins": [
"@babel/plugin-proposal-optional-chaining"
]
}
.
// UPDATE wrangler.tomlname = "easy-dca-wrangler"
type = "webpack"
account_id = "{change-me-to-your-cloudflare-account-id}" # You can find this on the sidebar of the worker page in the Cloudflare dashboard. Right now it's labeled "API tokens".
webpack_config = "webpack.config.js"
workers_dev = true[triggers]
crons = ["0 * * * *", "0 0 * * *"]
.
// CREATE webpack.config.jsmodule.exports = {
entry: './src/index.js',
target: 'webworker',
module: {
rules: [
{
test: /\.c?js$/, exclude: /node_modules/, loader: 'babel-loader'
}
]
}
}
Perfect, now let’s get things installed and move onto the meat of our project.
$ npm i
Before we can get much further we need to deploy a “dummy” worker just as a namespace placeholder while we get some secret Alpaca env vars added to the project. To do that let’s create a ./src
directory and add a dead basic index.js
file.
$ mkdir src
$ touch ./src/index.js
Let’s open and fill out that index.js
file in our code editor now.
Perfect, nothing really at all going on here, just some basic Cloudflare Worker endpoint boilerplate to get a placeholder worker successfully deployed to our account. Let’s do that now.
$ npm run deploy
If successful you should now be able to setup a Key Value namespace under this worker and and tie it into our new worker.
$ wrangler kv:namespace create "STORE"
This command will output a tiny toml array that looks something like this:
kv_namespaces = [
{ binding = "STORE", id = "{some-id-looking-string}" }
]
Copy this whole key and value toml pair and paste it into your wrangler.toml
file. It would then look something like this:
// UPDATE wrangler.tomlname = "easy-dca-wrangler"
type = "webpack"
account_id = "{change-me-to-your-cloudflare-account-id}" # You can find this on the sidebar of the worker page in the Cloudflare dashboard. Right now it's labeled "API tokens".
webpack_config = "webpack.config.js"
workers_dev = true
kv_namespaces = [
{ binding = "STORE", id = "{some-id-looking-string}" }
][triggers]
crons = ["0 * * * *", "0 0 * * *"]
Okay great! We’ve added and attached a Key Value database to our worker. Now we just need to update our ./src/index.js
to include our actual DCA business logic.
Before we do that we need to create a really simple utility JS file containing some simple utils which will make our life a little easier later on.
$ mkdir ./src/@js
$ touch ./src/@js/utils.js
To that ./src/@js/utils.js
file we will add the following content:
Don’t worry too much about the contents here, it’s just two exported functions, one for handling fetch
responses and another for handling any thrown errors and responding with an appropriately formatted Response
object. If you do much Cloudflare Worker development these are two really nice helper functions you may want to incorporate into future projects.
With the helpers prepared we can finally focus our attention on filling out our ./src/index.js
page. You’ll be happy to learn it’s actually not much longer than our utils file.
So simple? That’s right! Just the way I like it. Let’s walk through it.
The primary function performing all the logic is the handleScheduled
function. In fact the handleFetch
function just allows us to call that function using a REST API endpoint to test it since it’s intended to only be called by the Cloudflare Worker cron trigger. (which can make testing a little tricky)
So what’s happening in handleScheduled
function? It’s a promise chain starting with a selection of every item in the STORE
we setup earlier. We haven’t added anything to that database yet but don’t worry, we will. Next it maps the response array of that list down into a simple array of name strings. It will expect the names to be formatted like "{symbol:amount}"
e.g. "VTI:25"
. Which will signal to the app that whenever the handleScheduled
function is triggered it should place an order for $25 of VTI. Yay fractional shares!
After formatting all the orders into an array the next step is to create a serialized promise map of those symbol:amount pairs and actually begin placing orders via the Alpaca API. The request is a simple application/json
POST request with a stringified body of:
{
symbol,
notional,
side: 'buy',
type: 'market',
time_in_force: 'day'
}
The astute observer will notice two BIG variables we have yet to assign though. APCA_PAPER_API_KEY_ID
and APCA_PAPER_API_SECRET_KEY
. Yes, very astute of you. Let’s get those assigned and added to Cloudflare.
On the Alpaca side make sure you’ve selected your Paper Trading account (give’s you a fat stack of play money).
.
.
.
.
Next generate a new paper trading API key. Once generated the UI will show you both an API Key ID and an API Secret Key. These are what you’ll copy and paste to your Cloudflare Worker env var as secrets so don’t close this view just yet.
.
.
.
Next back in your terminal let’s get those variables added to your Cloudflare Worker.
$ wrangler secret put APCA_PAPER_API_KEY_ID
// Next step will ask to paste the API Key ID from Alpaca$ wrangler secret put APCA_PAPER_API_SECRET_KEY
// Next step will ask to paste the API Secret Key from Alpaca
Super! That’s all you have to do, those secrets will now be accessible via the variables you just assigned.
At this point we should be good to deploy.
$ npm run deploy
If all goes well your application should be successfully deployed and depending on your cron triggers will begin placing DCA orders automatically.
OR WILL IT!?
We haven’t added any values to the STORE
yet so no it won’t, silly.
Let’s do that now.
$ wrangler kv:key put VTI:25 '' --binding STORE
$ wrangler kv:key put SPY:25 '' --binding STORE
... etc
The Wrangler CLI KV docs are super so be sure and peruse them to understand all the things you can do to add, modify, delete and view your Keys and their Values.
You can also use the Cloudflare Worker dashboard UI to add, remove and modify entries.
You can also use the same dashboard to view, modify and monitor the triggers.
For instance let’s go ahead and add one that triggers every minute so we can more quickly see our Alpaca order queue filling up.
Click save and then in a few minutes you should see some orders flowing into Alpaca for the stocks or ETFs you’ve added to the STORE
.
Insane! So easy! So powerful! Remember though with great power comes great responsibility so if you plan on moving this to the Live Trading API please consider the risks and make any appropriate and necessary changes. (Like removing entirely the handleFetch
function and listener as well as adjusting the cron triggers both in the dashboard and wrangler.toml
file)
This was a fun little app to build. If you’re interested in exploring other fun financial applications follow me on Twitter, this is kinda my thing.