Building a Crypto Trading Bot

auto-manage your portfolio with RESTful APIs

Last week I built a simple trading algorithm that buys or sells based on temporal AI-generated Bitcoin price predictions. It delivered 9% average returns in 90 days, which was quite fun to see.

However, it was a dreadfully basic implementation, for several reasons:
It didn’t simulate “noise” such as slippage or various exchange fees that a portfolio will face in the real world.

It also, quite importantly, wasn’t the present real world. I’m happy to boast of great success on historical data, but if the bot hasn’t made any real cash as of yet, many would understandably restrain their enthusiasm.

Past behavior is the best predictor of future behavior — right up until it isn’t. That moment can be quite painful for investors who inadvertently run into it, so let’s set about rebuilding a backtesting algorithm into a true trading bot.

There’s two things to take care of before that cute little bot is ready to sail:

The latter part is quite interesting, and will delve somewhat into economic theory and risk management. But we won’t get anywhere without understanding the data engineering framework, so let’s unpack that here.

Automating your Trading

If you’ve ever invested in a mutual fund, you delegated the process of trading your portfolio to a fund manager who’ll make decisions for you as new data streams in. I would argue that this is a form of automation, not in the least because many of them won’t get you the results you desire.

Most of us would like more control over our portfolios; we’re engaging in carefully-analyzed gambling, after all. But people don’t just guess: they take well-considered risks based on diverse streams of information.

Veteran traders speak in hushed tones of “price signs” and “market crosses”, like weathered sailors discussing patterns in waves, clouds and seagulls that indicate storms ahead.
Most adherents of technical analysis wouldn’t automatically trade simply on such signals, however, and use the signs to augment their own decisions.

But if you think you (or an advanced AI) can outsmart the armies of day traders and wealth managers surfing the price waves, you might want to automate your portfolio: Trading programatically in a script, rather than clicking through a website or app.

To work with a live portfolio, we’ll need a verified account with a crytocurrency exchange, such as Bisq or Binance.

I’m a fan of Coinbase Pro for several reasons.

If you want to follow along, create an account at Coinbase, verify your identity and you should have access to Pro alongside it. Coinbase itself makes it quite easy to transfer money into your account through a bank or crypto wallet, and you can move funds into Pro by clicking the “deposit” button on the left in the above image.

Keys to the Kingdom

So you’ve moved $10 into Pro to start trading. To access your portfolio without logging in and clicking through the website, you’ll need personal API keys. Click your dropdown menu at the top right, then click “API” and then “New API Key”.

After defining a nickname and passphrase, you’ll be shown the API key & its paired secret key. The secret key will only be shown once for security, so keep track of it somewhere safe. We’ll need everything except the nickname to authorize your script to trade.

Requesting Gets

Writing the connection script is fairly enjoyable. I recommend reading through the documentation beforehand; they have tutorial code in several languages. The Python initial example caused me some crashes, however, not including version mismatches.
Fortunately someone wrote a wrapper library that simplifies many of the functions; I’ll still walk through the initial authorization functions so we know what we’re dealing with.

This assumes you’ve stored your API key, secret key and passphrase as strings in a secrets.py file in the working directory.

import json, hmac, hashlib, time, requests, base64
from requests.auth import AuthBase
import secrets # passwords file
# authorization class to access portfolio API
class CoinbaseExchangeAuth(AuthBase):

def __init__(self, api_key, secret_key, passphrase):
self.api_key = api_key
self.secret_key = secret_key
self.passphrase = passphrase

def __call__(self, request): # format call for Coinbase API
timestamp = str(time.time())
message = (timestamp + request.method + request.path_url + (request.body or ''))
message = message.encode('ascii')
hmac_key = base64.b64decode(self.secret_key)
signature = hmac.new(hmac_key, message, hashlib.sha256)
signature_b64 = base64.b64encode(signature.digest())
request.headers.update({
'CB-ACCESS-SIGN': signature_b64,
'CB-ACCESS-TIMESTAMP': timestamp,
'CB-ACCESS-KEY': self.api_key,
'CB-ACCESS-PASSPHRASE': self.passphrase,
'Content-Type': 'application/json'})
return request
# create API auth using your keys
api_url = 'https://api.pro.coinbase.com/'
auth = CoinbaseExchangeAuth(secrets.pro_api_key, secrets.pro_secret_key, secrets.passphrase)
# Get accounts data
r = requests.get(api_url + 'accounts', auth=auth)
# print json formatte accounts info
r.json()

You’ll notice a lot of base-64 decoding in the __call__() . I assume this is part of the added layers of security: We decode our secret key with base-64 and re-encode it as part of the complete signature, similar to a website only storing salted hashes, rather than the full passwords themselves.

The major difference in the documentation (Python 2.7) and Daniel Paquin’s authorization wrapper is that the latter encodes the request message in ASCII before creating a new HMAC signature.

A Bot Buys some Bitcoin

The requests.get(accounts) in the above code will get you a JSON of all crypto accounts in your portfolio.

We could continue doing everything with requests.get() following the documentation, but the wrapper library makes things quite easy compared to the base methods (and you can always inspect the source code to check out the wrapper implementation).

Let’s create an authorization object with 2 lines of code instead of the last paragraph, and buy some Bitcoin with $5 (deposited to Coinbase Pro from Coinbase):

import cbpro
# https://github.com/danpaquin/coinbasepro-python
# create authorization obj wrapper with keys
auth = cbpro.AuthenticatedClient(secrets.pro_api_key, secrets.pro_secret_key, secrets.passphrase)
# market order function: specifying currency pair, direction, magnitude
auth.place_market_order(product_id='BTC-USD', side='buy', funds='5.00')

If your keys are set up properly (they should be, otherwise get_accounts() would crash), you’ve just sent out a market order for $5 worth of BTC. If you want to cancel before it hits, call auth_client.cancel_all(product_id=’BTC-USD’) .

The algorithm from last time contained several buy/sell() and auto_stop() functions. There’s a perfectly-formatted automatic stop function as well:

auth.place_stop_order(product_id='BTC-USD', 
side='buy',
price='200.00',
funds='5')

“When price hits $200, trade $5 USD for BTC”.
We’ll need to modify our method from last time to fit into this API accordingly, perhaps by keeping a rolling-refresh dataframe of the last ~7 days of BTC prices (which can also be accessed live from this API).

Welcome to the Machine

This whole process was much easier than I imagined it would be. Coinbase is a pretty great exchange to work with, especially with low exchange rates (since our algorithm may very well end up trading quite often).

We’ve connected our script to an output (live portfolio). Next time, we’ll look into adding complexity and depth to the trading algorithm itself, before finally setting it up to listen to AI-generated inputs that drive the whole process.

data scientist, machine learning engineer. passionate about ecology, biotech and AI. https://www.linkedin.com/in/mark-s-cleverley/

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store