Writing a crypto trading bot

April 13, 2021

After tracking cryptocurrencies for some time (see this blog post), I wondered if it would be possible to write an automated trading bot, for fun and profit. As multiple cryptocurrency trading platforms have APIs, it should be easy to write a program that trades for you!

Working with a crypto API

As usual, I started with an empty Python script and had a look around what libraries exist for using APIs. I found the amazing CCXT, which has support for major trading platforms like Bitfinex or Kraken (and also supports multiple programming languages).

After installing CCTX with pip and creating API keys for Bitfinex, it's was really simple to use the API, for example to get your current balance:

import ccxt

bitfinex = ccxt.bitfinex()
bitfinex.apiKey = 'my-api-key'
bitfinex.secret = 'my-secret'

for c in bitfinex.fetch_balance()['info']:
    print c['currency'], ':', c['available']

which yields something like this, depending on your assets:

btc : 0.0
ltc : 0.9989958
usd : 0.000001
xrp : 0.0

It seems I have nearly one Litecoin left and like a millionth of a US dollar :-)

Trading strategy and Botting

So, with the basics set up, I started to think on how a trading Bot actually works. Being completely ignorant on finances, trading, or stocks, I tried to come up with something simple of my own.

The simplest strategy I could come up with was to buy if the stock was rising, and sell it if it was falling. Something like this:

I started with a simple setup of a "market", providing the market value for each day, and the "bot" part, which would be called (using a function tick()) and then decide what action it wants to take:

  • WAIT: Do nothing for now.
  • BUY: Buy as much as possible.
  • SELL: Sell everything we own.

But what means "rising" or "falling", and how to quantify that in numbers? One way to check if we are currently rising is to compare the current value to the average of a certain time period, like a few days or weeks:

Testing with some data

As a test data set, I used a year's data from Ripple (XRP):

You can download the data set here: historical_data.json.

I played around with the Bot's "memory", or how long it would keep "old" results before replacing them with newer ones.

For example, with parameters:

  • BUY at 60% of average,
  • SELL at 40% of average and
  • memory of 180 days

we get the following results:

Tick 1 : 0.173865 , crypto: 0  / fiat: 100 , decision: WAIT
Tick 2 : 0.185398 , crypto: 0  / fiat: 100 , decision: WAIT
...
Tick 81 : 0.192127 , crypto: 520.489051513  / fiat: 0.0 , decision: BUY
 > lower: 0.194971 , upper: 0.212617
...
Tick 91 : 0.213166 , crypto: 0.0  / fiat: 110.950569155 , decision: SELL
 > lower: 0.200742 , upper: 0.209682
...
Tick 241 : 0.488287 , crypto: 244.443394829  / fiat: 0.0 , decision: BUY
 > lower: 0.5065 , upper: 0.851811
...
Tick 260 : 0.90726 , crypto: 0.0  / fiat: 221.773714393 , decision: SELL
 > lower: 0.639423 , upper: 0.854734
...
Tick 281 : 0.688035 , crypto: 322.329117549  / fiat: 0.0 , decision: BUY
 > lower: 0.749427 , upper: 0.900568
...
Tick 362 : 0.440587 , crypto: 322.329117549  / fiat: 0.0 , decision: WAIT
From 100 to 142.014018913 , profit: 42.01 %, buys: 4 , sells: 3

We made quite some profit, 42% over a year (but we're still holding crypto, so we would net to sell to get the actual profit).

Now, if we change the parameters to BUY 55% and SELL 45% to give it a bit more freedom (quicker trades), we get the following:

Tick 1 : 0.173865 , crypto: 0  / fiat: 100 , decision: WAIT
...
Tick 81 : 0.192127 , crypto: 520.489051513  / fiat: 0.0 , decision: BUY
 > lower: 0.201836 , upper: 0.209194
...
Tick 91 : 0.213166 , crypto: 0.0  / fiat: 110.950569155 , decision: SELL
 > lower: 0.202673 , upper: 0.204335
...
Tick 94 : 0.201842 , crypto: 549.690199041  / fiat: 0.0 , decision: BUY
 > lower: 0.202673 , upper: 0.204335
Tick 95 : 0.205009 , crypto: 0.0  / fiat: 112.691438015 , decision: SELL
 > lower: 0.202674 , upper: 0.204335
...
Tick 101 : 0.198519 , crypto: 567.66071769  / fiat: 0.0 , decision: BUY
 > lower: 0.20281 , upper: 0.205009
...
Tick 103 : 0.209076 , crypto: 0.0  / fiat: 118.684232212 , decision: SELL
 > lower: 0.203187 , upper: 0.205266
...
Tick 233 : 0.639423 , crypto: 185.611453157  / fiat: 0.0 , decision: BUY
 > lower: 0.64161 , upper: 0.784129
...
Tick 259 : 0.84038 , crypto: 0.0  / fiat: 155.984153004 , decision: SELL
 > lower: 0.666385 , upper: 0.791047
...
Tick 278 : 0.780559 , crypto: 199.836467203  / fiat: 0.0 , decision: BUY
 > lower: 0.791047 , upper: 0.854734
...
Tick 362 : 0.440587 , crypto: 199.836467203  / fiat: 0.0 , decision: WAIT
From 100 to 88.0453495757 , profit: -11.95 %, buys: 5 , sells: 4

Oh no, we lost 12% of our money! The bot now is much more "aggressive" in buying / selling, and even changes his mind in one tick, as you can see on ticks 94 / 95, where it first buys and then sells immediately. I tried some more parameters and unfortunately, they all lead to very different results :-)

If you want to play around yourself, the source is on Github: crypto-bot-simulation.

Conclusion

It was a fun experiment, but playing just a little bit with the parameters such as memory changed the outcome completely. Also there's a good chance that the strategy which worked in the Ripple case is a good example of "overfitting", as I played around until it gave some profitable results.

As the results we're too volatile, I didn't implement the part were it actually buys or sells crypto at a market API. I guess I'll continue to stay out of finances and trading :-)