Tutorial: quickly develop and test your crypto trading bot

Get into the world of automated trading of cryptocurrencies in few minutes.

I wrote this step-by-step tutorial to quickly develop your crypto trading bot in Java with Cassandre, Ta4j, and your favorite crypto exchange. Project sources are available here. If you are new to this field, you can read our introductions to trading and technical analysis.

Prerequisites: you only need Java and Maven, but having curl and jq can make things easier.

Create your bot.

Create your project.

We are going to use Cassandre trading bot framework, a spring boot starter we developed to automatically deal with exchanges, accounts, tickers, orders, positions, strategy... and let you focus on developing your strategy.

You can create your bot with our archetype thanks to this command :

mvn -B archetype:generate \
-DarchetypeGroupId=tech.cassandre.trading.bot \
-DarchetypeArtifactId=cassandre-trading-bot-spring-boot-starter-basic-ta4j-archetype \
-DarchetypeVersion=2.0.1 \
-DgroupId=tech.cassandre.trading.bot.tutorial.ta \
-DartifactId=crypto-trading-bot-tutorial \
-Dversion=1.0-SNAPSHOT \
-Dpackage=tech.cassandre.trading.bot.tutorial.ta

This will create a directory crypto-trading-bot-tutorial with the sources.

You can run your bot with this command : mvn spring-boot:run.

Create your sandbox account.

To test our bot, we will create a sandbox account, it will behave like a real account (assets, tickers, orders, API...) but, of course, with fake assets. This way, we can thoroughly test our bot.

  • Go to the Kucoin sandbox and create an account.

  • Move your assets (ETH, BTC...) from your main account to your trading account (Click on Main Account link and then click on transfer in front of the cryptocurrency).

  • Click on your profile on the top right of the screen and then API management, you will have to set up security before accessing the create API menu.

  • Choose an API Name and API Passphrase. In the following configuration screen, set permissions to ̀General and Trade and IP limit to No Limit.

This will give you the required credentials.

Configure your bot.

Edit src/main/resources/application.properties and set your parameters :

# Exchange configuration.
cassandre.trading.bot.exchange.name=kucoin
cassandre.trading.bot.exchange.username=cassandre.crypto.bot@gmail.com
cassandre.trading.bot.exchange.passphrase=cassandre
cassandre.trading.bot.exchange.key=5df8eea30092f40009cb3c6a
cassandre.trading.bot.exchange.secret=5f6e91e0-796b-4947-b75e-eaa5c06b6bed
#
# Exchange modes.
cassandre.trading.bot.exchange.modes.sandbox=true
cassandre.trading.bot.exchange.modes.dry=true
#
# Exchange API calls rates.
cassandre.trading.bot.exchange.rates.account=PT1S
cassandre.trading.bot.exchange.rates.ticker=PT1S
cassandre.trading.bot.exchange.rates.trade=PT1S

Exchange configuration.

  • cassandre.trading.bot.exchange.name allows you to choose which exchange you want to connect to. Here we choose kucoin.

  • Set username, passphrase, key, and secret with the information you retrieved while creating your kucoin sandbox account.

Exchange modes.

  • cassandre.trading.bot.exchange.modes.sandbox allows you to use the exchange sandbox with fake assets instead of your real account. We set it to true during the development, and we will put it to false when deploying in production.

  • cassandre.trading.bot.exchange.modes.dry allows you to mock orders creation. Each time an order is created in your code, Cassandre will simulate a successful order and a successful trade. This way, you can simulate your strategy and see the results. We will use this during the tests to calculate gains.

Exchange API calls rates.

With those parameters, we tell Cassandre to get information from exchange every second.

How the bot works.

Cassandre allows you to create different kinds of strategy; in this tutorial, we choose a technical analysis based strategy. The way it works is straightforward. Your trading strategy is defined in class src/main/java/tech/cassandre/trading/bot/tutorial/ta/SimpleTa4jStrategy.java.

This class is annotated with @CassandreStrategy and extends BasicTa4jCassandreStrategy.

Cassandre takes care of pushing all the data your strategy needs and allows you to create orders or positions thanks to getTradeService() and getPositionService().

In your strategy, if you want to be notified of updated data, you can override the following methods :

  • onAccountUpdate() to receive updates about your account.

  • onTickerUpdate()to receive new tickers.

  • onOrderUpdate()to receive updates about your orders.

  • onTradeUpdate() to receive updates about your trades.

  • onPositionUpdate() to receive updates about your positions.

In this tutorial, we use the specific ta4j template, so we will see in the next chapter which particular methods we have to implement.

Create your strategy.

Technical indicator choice.

There is a lot of technical indicators; ta4 implements lots of them.

For this tutorial, we chose the Simple Moving Average indicator. It calculates the average of a selected range of prices, usually closing prices, by the number of periods in that range. The SMA can help determine if an asset price will continue or reverse a bull or bear trend.

In this tutorial, we will implement a Moving Average Length of 10 days.

Configure your strategy.

The archetype created your strategy in src/main/java/tech/cassandre/trading/bot/tutorial/ta/SimpleTa4jStrategy.java. To configure it, Cassandre forces you to implement these methods :

getRequestedCurrencyPair()

Implements this method to tell the bot which currency pair ticker your strategy will receive. We ask for BTC/USDT.

@Override
public CurrencyPairDTO getRequestedCurrencyPair() {
return new CurrencyPairDTO(BTC, USDT);
}

getMaximumBarCount()

As we wrote in the technical indicator choice part, we chose a moving average length of 10 days; this means we have to tell Cassandre that we want to keep to 10 bars.

@Override
public int getMaximumBarCount() {
return 10;
}

getDelayBetweenTwoBars()

Implements this method to set the time between two bars are added. As we configured it, Cassandre will retrieve a new ticker every second, but we only want to add a bar every day.

@Override
public Duration getDelayBetweenTwoBars() {
return Duration.ofDays(1);
}

Write your strategy.

In this method, we create the ta4j strategy. We use the closing price and the simple moving average.

@Override
public Strategy getStrategy() {
ClosePriceIndicator closePrice = new ClosePriceIndicator(getSeries());
SMAIndicator sma = new SMAIndicator(closePrice, getMaximumBarCount());
return new BaseStrategy(new OverIndicatorRule(sma, closePrice), new UnderIndicatorRule(sma, closePrice));
}

Cassandre will automatically run your strategy. If our indicator calculates it’s time to buy, it will call the shouldEnter() method of your strategy. If it’s time to sell, it will call the shouldExit() method.

If it’s time to buy, we will create a position.

// Create rules.
PositionRulesDTO rules = PositionRulesDTO.builder()
.stopGainPercentage(5)
.stopLossPercentage(10)
.create();
// Create position.
getPositionService().createPosition(
new CurrencyPairDTO(BTC, USDT),
new BigDecimal("0.1"),
rules);

First, we created a rule saying this position should be closed if the gain is at least 5% or if the loss is more than 10%.

Then we called the createPosition() method. This will automatically create a buy order. From now, with every ticker received, Cassandre will check the gain or loss made on this position, if it triggers one of the rules, Cassandre will automatically create a sell order to close this position.

To log what’s happening when it runs, we implement two methods where we will log what is happening :

@Override
public void onTickerUpdate(TickerDTO ticker) {
// Display all received tickers
System.out.println("New ticker " + ticker);
}

and

@Override
public void onPositionUpdate(PositionDTO position) {
if (position.getStatus().equals(OPENED)) {
System.out.println(" > Position " + position.getId() + " opened");
}
if (position.getStatus().equals(CLOSED)) {
System.out.println(" >> Position " + position.getId() + " closed - gain : " + position.getPositionGain().getAmount());
}
}

Test your strategy.

Get real data.

We now need to retrieve historical data to test our strategy against real data. You can do this with this API. Three months of data are stored in the file src/test/resources/btc-usdt.csv.

You can update this file with new data with this command :

startDate=`date --date="3 months ago" +"%s"`
endDate=`date +"%s"`
curl -s "https://api.kucoin.com/api/v1/market/candles?type=1day&symbol=BTC-USDT&startAt=${startDate}&endAt=${endDate}" \
| jq -r -c ".data[] | @tsv" \
| tac $1 > src/test/resources/tickers-btc-usdt.tsv

If you have the cassandre-trading-bot-spring-boot-starter-test in your POM, the data in src/test/resources will be automatically loaded by Cassandre and pushed to your bot during the test.

Write your test.

Your test is tech/cassandre/trading/bot/tutorial/ta/SimpleTa4jStrategyTest.java.

To import real data, your test must import TickerFluxMock.class with the code: @Import(TickerFluxMock.class).

Now we write the tests :

@Test
@DisplayName("Check gains")
public void gainTest() {
await().forever().until(() -> tickerFluxMock.isFluxDone());
final BigDecimal gains = strategy.getPositions()
.values()
.stream()
.filter(p -> p.getStatus().equals(CLOSED))
.map(p -> p.getGain().getAmount().getValue())
.reduce(BigDecimal.ZERO, BigDecimal::add);
System.out.println("Cumulated gains : " + gains);
assertTrue(gains.compareTo(BigDecimal.ZERO) > 0);

System.out.println("Position still opened :");
strategy.getPositions()
.values()
.stream()
.filter(p -> p.getStatus().equals(OPENED))
.forEach(p -> System.out.println(" - " + p.getId()));
}

The first thing we do with the await() method is to wait until all data from btc-usdt.csv are imported.

Then, we calculate the gains of every closed position, and we check that the profits are superior to zero.

The last thing we do is to display the list of positions that are not closed.

Conclusion.

We’re done with this first article on how to develop and test your first crypto trading bot. If you want more, follow us on Twitter, or you can :