If you are new to trading, you can read trading basics.
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
It will ask for the following parameters :
Parameters | Description | Examples |
| The id of the project's group |
|
| The id of the artifact (project) |
|
| The version of the artifact under the specified group |
|
| The java package |
|
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
Your bot configuration is located in src/main/resources/application.properties
:
## Exchange configuration.cassandre.trading.bot.exchange.name=kucoincassandre.trading.bot.exchange.username=cassandre.crypto.bot@gmail.comcassandre.trading.bot.exchange.passphrase=cassandrecassandre.trading.bot.exchange.key=5df8eea30092f40009cb3c6acassandre.trading.bot.exchange.secret=5f6e91e0-796b-4947-b75e-eaa5c06b6bed## Modes.cassandre.trading.bot.exchange.modes.sandbox=truecassandre.trading.bot.exchange.modes.dry=true## Exchange API calls rates (ms or standard ISO 8601 duration like 'PT5S').cassandre.trading.bot.exchange.rates.account=2000cassandre.trading.bot.exchange.rates.ticker=2000cassandre.trading.bot.exchange.rates.trade=2000## Database configuration.spring.jpa.hibernate.ddl-auto=updatecassandre.trading.bot.database.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDrivercassandre.trading.bot.database.datasource.url=jdbc:hsqldb:mem:cassandrecassandre.trading.bot.database.datasource.username=sacassandre.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.
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.*/@SpringBootApplicationpublic 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 {@Overridepublic Set<CurrencyPairDTO> getRequestedCurrencyPairs() {return Set.of(new CurrencyPairDTO(BTC, USDT));}@Overridepublic Optional<AccountDTO> getTradeAccount(Set<AccountDTO> accounts) {return accounts.stream().filter(a -> "trade".equals(a.getName())).findFirst();}@Overridepublic 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);}@Overridepublic 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);}@Overridepublic 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);}@Overridepublic 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);}@Overridepublic 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);}@Overridepublic 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.
You can create an order or a position by accessing the trade service with a call to getTradeService()
or getPositionService()
.
@Overridepublic 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.
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: default2020-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 successful2020-08-07 10:21:57.673 INFO 29926 --- [ main] uration$$EnhancerBySpringCGLIB$$75b931e7 : ExchangeConfiguration - Supported currency pairs : KCS/USDT, ETH/USDT, BTC/USDT, ETH/BTC2020-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/BTC2020-08-07 10:21:57.742 INFO 29926 --- [ main] s.a.ScheduledAnnotationBeanPostProcessor : No TaskScheduler/ScheduledExecutorService bean found for scheduled processing2020-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}
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