Quickstart

Create and execute your own strategy in seconds

If you are new to trading, you can read trading basics.

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-basic-archetype \
-DarchetypeVersion=3.0.0

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
│   └── tech
│   └── cassandre
│   └── SimpleStrategyTest.java
└── resources
├── tickers-btc-usdt.tsv
├── user-main.tsv
└── user-trade.tsv

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=2000
cassandre.trading.bot.exchange.rates.ticker=2000
cassandre.trading.bot.exchange.rates.trade=2000
#
# Database configuration.
spring.jpa.hibernate.ddl-auto=update
cassandre.trading.bot.database.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver
cassandre.trading.bot.database.datasource.url=jdbc:hsqldb:mem:cassandre
cassandre.trading.bot.database.datasource.username=sa
cassandre.trading.bot.database.datasource.password=

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.
*/
@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() {
return Set.of(new CurrencyPairDTO(BTC, USDT));
}
@Override
public Optional<AccountDTO> getTradeAccount(Set<AccountDTO> accounts) {
return accounts.stream()
.filter(a -> "trade".equals(a.getName()))
.findFirst();
}
@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);
}
@Override
public void onPositionStatusUpdate(final PositionDTO position) {
// Here, we will receive an PositionDTO each a position has changed.
System.out.println("Received information about a position status : " + position);
}
}

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

This is how it works :

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

  • On the exchange, you usually have several accounts and Cassandre needs to know which one of your account is the trading one (for example, to calculate if you have enough assets to buy/sell). So you have to implement the getTradeAccount() method who gives you as a parameter the list of accounts and from that list, you have to return only one.

  • 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.

  • If there is a change in your position status, onPositionStatusUpdate() 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