Quickstart

Create and execute your own strategy in seconds

Create your project.

If you don't have an existing spring boot project, you can use our maven archetype to generate a working project with this command :

mvn archetype:generate -DarchetypeGroupId=tech.cassandre.trading.bot \
-DarchetypeArtifactId=cassandre-trading-bot-spring-boot-starter-archetype \
-DarchetypeVersion=2.0.1

Maven Central

It will ask for the following parameters :

Parameters

Description

Examples

groupId

The id of the project's group

com.mycompany.app

artifactId

The id of the artifact (project)

my-app

version

The version of the artifact under the specified group

1.0-SNAPSHOT

package

The java package

com.mycompany.app

The created project has the following structure and files :

my-app
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── mycompany
│ │ └── app
│ │ ├── Application.java
│ │ ├── package-info.java
│ │ └── SimpleStrategy.java
│ └── resources
│ └── application.properties
└── test
└── java
└── com
└── mycompany
└── app
└── SimpleStrategyTest.java

Review configuration.

Your bot configuration is located in src/main/resources/application.properties :

#
# 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
#
# Modes.
cassandre.trading.bot.exchange.modes.sandbox=true
cassandre.trading.bot.exchange.modes.dry=true
#
# Exchange API calls rates (ms or standard ISO 8601 duration like 'PT5S').
cassandre.trading.bot.exchange.rates.account=100
cassandre.trading.bot.exchange.rates.ticker=101
cassandre.trading.bot.exchange.rates.trade=102

Please, create your Kucoin sandbox account and do not make orders with this account, you can see how to do it here.

Explore sources.

The src/main/java/com/mycompany/app/Application.java file is a classical spring boot Application.

package com.mycompany.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Application start.
*/
@SuppressWarnings({ "checkstyle:FinalClass", "checkstyle:HideUtilityClassConstructor" })
@SpringBootApplication
public class Application {
public static void main(final String[] args) {
SpringApplication.run(Application.class, args);
}
}

The src/main/java/com/mycompany/app/SimpleStrategy.java is the strategy executed by the bot :

package com.mycompany.app;
import tech.cassandre.trading.bot.dto.market.TickerDTO;
import tech.cassandre.trading.bot.dto.position.PositionDTO;
import tech.cassandre.trading.bot.dto.trade.OrderDTO;
import tech.cassandre.trading.bot.dto.trade.TradeDTO;
import tech.cassandre.trading.bot.dto.user.AccountDTO;
import tech.cassandre.trading.bot.strategy.BasicCassandreStrategy;
import tech.cassandre.trading.bot.strategy.CassandreStrategy;
import tech.cassandre.trading.bot.util.dto.CurrencyDTO;
import tech.cassandre.trading.bot.util.dto.CurrencyPairDTO;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* Simple strategy.
*/
@CassandreStrategy(name = "Simple strategy")
public final class SimpleStrategy extends BasicCassandreStrategy {
@Override
public Set<CurrencyPairDTO> getRequestedCurrencyPairs() {
// We only ask about ETC/BTC (Base currency : ETH / Quote currency : BTC).
return Set.of(new CurrencyPairDTO(CurrencyDTO.ETH, CurrencyDTO.BTC));
}
@Override
public void onAccountUpdate(final AccountDTO account) {
// Here, we will receive an AccountDTO each time there is a change on your account.
System.out.println("Received information about an account : " + account);
}
@Override
public void onTickerUpdate(final TickerDTO ticker) {
// Here we will receive a TickerDTO each time a new one is available.
System.out.println("Received information about a ticker : " + ticker);
}
@Override
public void onOrderUpdate(final OrderDTO order) {
// Here, we will receive an OrderDTO each time an order data has changed in the exchange.
System.out.println("Received information about an order : " + order);
}
@Override
public void onTradeUpdate(final TradeDTO trade) {
// Here, we will receive a TradeDTO each time a trade data has changed in the exchange.
System.out.println("Received information about a trade : " + trade);
}
@Override
public void onPositionUpdate(final PositionDTO position) {
// Here, we will receive an PositionDTO each a position has changed.
System.out.println("Received information about a position : " + position);
}
}

A Cassandre strategy is a class annotated with @CassandreStrategy and extending BasicCassandreStrategy or BasicTa4jCassandreStrategy.

This is how it works :

  • In the getRequestedCurrencyPairs()method, you have to return the list of currency pairs updates you want to receive.

  • If there is a change on your account data, onAccountUpdate() will be called.

  • When a new ticker is available, onTickerUpdate() will be called.

  • If there is a change in your order data, onOrderUpdate() will be called.

  • If there is a change in your trade data, onTradeUpdate() will be called.

  • If there is a change in your position data, onPositionUpdate() will be called.

Manage orders and positions.

You can create an order or a position by accessing the trade service with a call to getTradeService() or getPositionService().

@Override
public void onTickerUpdate(final TickerDTO ticker) {
getTradeService().createBuyMarketOrder(new CurrencyPairDTO(BTC, USDT), new BigDecimal("0,001"));
}

Cassandre trading bot also provides positions to manage your trade automatically :

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

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

Then we called the createPosition() method. This will automatically create a buy order. From now, for 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 it.

Run the bot and the strategy.

In the project folder, run :

mvn spring-boot:run

The logs should display something like this :

==========================================================================
,-----. ,--.
' .--./ ,--,--. ,---. ,---. ,--,--. ,--,--, ,-| | ,--.--. ,---.
| | ' ,-. | ( .-' ( .-' ' ,-. | | \ ' .-. | | .--' | .-. :
' '--'\ \ '-' | .-' `) .-' `) \ '-' | | || | \ `-' | | | \ --.
`-----' `--`--' `----' `----' `--`--' `--''--' `---' `--' `----'
==========================================================================
2020-08-07 10:21:54.856 INFO 29926 --- [ main] com.mycompany.app.Application : Starting Application on straumat-portable with PID 29926 (started by straumat in /home/straumat/Téléchargements/tmp/my-app)
2020-08-07 10:21:54.864 INFO 29926 --- [ main] com.mycompany.app.Application : No active profile set, falling back to default profiles: default
2020-08-07 10:21:55.646 INFO 29926 --- [ main] org.knowm.xchange.kucoin.KucoinExchange : Calling Remote Init...
2020-08-07 10:21:57.642 INFO 29926 --- [ main] uration$$EnhancerBySpringCGLIB$$75b931e7 : ExchangeConfiguration - Connection to kucoin successful
2020-08-07 10:21:57.673 INFO 29926 --- [ main] uration$$EnhancerBySpringCGLIB$$75b931e7 : ExchangeConfiguration - Supported currency pairs : KCS/USDT, ETH/USDT, BTC/USDT, ETH/BTC
2020-08-07 10:21:57.703 INFO 29926 --- [ main] uration$$EnhancerBySpringCGLIB$$c3390c97 : StrategyConfiguration - Running strategy 'Simple strategy'
2020-08-07 10:21:57.706 INFO 29926 --- [ main] uration$$EnhancerBySpringCGLIB$$c3390c97 : StrategyConfiguration - The strategy requires the following currency pair(s) : ETH/BTC
2020-08-07 10:21:57.742 INFO 29926 --- [ main] s.a.ScheduledAnnotationBeanPostProcessor : No TaskScheduler/ScheduledExecutorService bean found for scheduled processing
2020-08-07 10:21:57.755 INFO 29926 --- [ main] com.mycompany.app.Application : Started Application in 3.227 seconds (JVM running for 3.6)
Received information about an account : AccountDTO{ id='trade', name='trade', features=[TRADING, FUNDING], balances={BTC=BalanceDTO{ currency=BTC, total=0.8987752, available=0.8987752, frozen=0E-7, loaned=0, borrowed=0, withdrawing=0, depositing=0}, ETH=BalanceDTO{ currency=ETH, total=10.9724813, available=10.9723813, frozen=0.0001000, loaned=0, borrowed=0, withdrawing=0, depositing=0}, USDT=BalanceDTO{ currency=USDT, total=238.01198989, available=238.01198989, frozen=0E-8, loaned=0, borrowed=0, withdrawing=0, depositing=0}}}
Received information about an account : AccountDTO{ id='main', name='main', features=[TRADING, FUNDING], balances={BTC=BalanceDTO{ currency=BTC, total=0, available=0, frozen=0, loaned=0, borrowed=0, withdrawing=0, depositing=0}, ETH=BalanceDTO{ currency=ETH, total=0, available=0, frozen=0, loaned=0, borrowed=0, withdrawing=0, depositing=0}, KCS=BalanceDTO{ currency=KCS, total=1000, available=1000, frozen=0, loaned=0, borrowed=0, withdrawing=0, depositing=0}}}
Received information about a ticker : TickerDTO{ currencyPair=ETH/BTC, open=null, last=0.033439, bid=0.033438, ask=0.033443, high=10000, low=0.000001, vwap=null, volume=68411.95652063, quoteVolume=2296.1387778737917, bidSize=null, askSize=null, timestamp=2020-08-07T10:21:58.020+02:00[Europe/Paris]}
Received information about an order : OrderDTO{ type=ASK, originalAmount=0.0001, currencyPair=ETH/BTC, id='5e2b73b7c9ca5c00095587b3', userReference='null', timestamp=2020-01-24T23:46:15+01:00[Europe/Paris], status=NEW, cumulativeAmount=0.0000, averagePrice=10000000, fee=0, leverage='null', limitPrice=10000000}

Package the bot and the strategy.

In the project folder, type :

mvn package spring-boot:repackage

A jar file named my-app-1.0-SNAPSHOT.jar will be created in the target directory. You can run it with :

java -jar target/my-app-1.0-SNAPSHOT.jar