1. 概述
交易機器人是一種可以自動在市場或交易所下單的計算機程序,無需人工干預。
在本教程中,我們將使用 Cassandre 創建一個簡單的加密貨幣交易機器人,該機器人將在我們認為的最佳時機生成頭寸。
2. 機器人概述
交易是指“互相交換一件物品來換取另一件物品”。
在金融市場中,它指的是購買股票、期貨、期權、互換、債券,或者像我們案例中那樣,購買一定數量的加密貨幣。 這裏的想法是在特定價格購買加密貨幣,然後在更高的價格出售以獲利(即使在價格下跌的情況下,通過空頭頭寸也能獲利)。
我們將使用沙盒交易所;沙盒是一個虛擬系統,其中我們擁有“假”資產,可以下達訂單並接收報價單。
首先,讓我們看看我們將做什麼:
- 將 Cassandre Spring Boot Starter 添加到我們的項目
- 添加必要的配置以連接到交易所
- 創建一個策略:
- 接收來自交易所的報價單
- 選擇何時購買
- 在合適的時間購買時,檢查是否有足夠的資產並創建頭寸
- 顯示日誌以查看頭寸何時打開/關閉以及我們賺了多少收益
- 運行對歷史數據的測試,以查看我們是否可以盈利
3. Maven 依賴
讓我們從添加必要的依賴項到我們的 <em >pom.xml</em> 開始,首先添加 Cassandre spring boot starter:
<dependency>
<groupId>tech.cassandre.trading.bot</groupId>
<artifactId>cassandre-trading-bot-spring-boot-starter</artifactId>
<version>4.2.1</version>
</dependency>Cassandre 依賴 XChange 來連接加密貨幣交易所。對於本教程,我們將使用 Kucoin XChange 庫:
<dependency>
<groupId>org.knowm.xchange</groupId>
<artifactId>xchange-kucoin</artifactId>
<version>5.0.8</version>
</dependency>我們還使用 hsqldb 來存儲數據:
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.5.2</version>
</dependency>為了測試我們的交易機器人與歷史數據的對比,我們還添加了我們的 Cassandre Spring Boot 測試啓動器:
<dependency>
<groupId>tech.cassandre.trading.bot</groupId>
<artifactId>cassandre-trading-bot-spring-boot-starter-test</artifactId>
<version>4.2.1</version>
<scope>test</scope>
</dependency>4. 配置
讓我們編輯創建 application.properties 以設置我們的配置:
# Exchange configuration
cassandre.trading.bot.exchange.name=kucoin
cassandre.trading.bot.exchange.username=kucoin.cassandre.test@gmail.com
cassandre.trading.bot.exchange.passphrase=cassandre
cassandre.trading.bot.exchange.key=6054ad25365ac6000689a998
cassandre.trading.bot.exchange.secret=af080d55-afe3-47c9-8ec1-4b479fbcc5e7
# Modes
cassandre.trading.bot.exchange.modes.sandbox=true
cassandre.trading.bot.exchange.modes.dry=false
# 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
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=
配置包含四個類別:
- 交易所配置:我們為建立與 Kucoin 現有沙盒賬户的連接設置的交易所憑據。
- 模式:我們希望使用的模式。在本例中,我們要求 Cassandre 使用沙盒數據。
- 交易所 API 調用速率:指示我們希望從交易所檢索數據的速度(賬户、訂單、交易和報價)。請注意,所有交易所都有我們能夠調用的最大速率。
- 數據庫配置:Cassandre 使用數據庫來存儲倉位、訂單和交易。對於本教程,我們將使用一個簡單的 hsqld 內存數據庫。當然,在生產環境中,我們應該使用持久化數據庫。
現在,讓我們在 application.properties 文件中創建相同的文件,位於我們的測試目錄中,但我們將 cassandre.trading.bot.exchange.modes.dry 更改為 true,因為在測試期間,我們不想將真實訂單發送到沙盒。我們只想模擬它們。
5. 策略
一個交易策略是指旨在實現盈利回報的固定計劃;我們可以通過為 Java 類添加帶有 <@CassandreStrategy> 註解以及擴展 <BasicCassandreStrategy> 類來創建我們的策略。
讓我們在 <MyFirstStrategy.java> 中創建我們的策略類:
@CassandreStrategy
public class MyFirstStrategy 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();
}
}實施 BasicCassandreStrategy 強制我們實現兩個方法:getRequestedCurrencyPairs() & getTradeAccount():
在 getRequestedCurrencyPairs() 中,我們需要返回我們想要從交易所接收的貨幣對列表。貨幣對是兩種不同貨幣的報價,其中一種貨幣相對於另一種貨幣進行報價。在我們的示例中,我們希望使用 BTC/USDT。
為了更清楚地説明,我們可以使用以下 curl 命令手動檢索報價:
curl -s https://api.kucoin.com/api/v1/market/orderbook/level1?symbol=BTC-USDT我們將會得到類似的東西:
{
"time": 1620227845003,
"sequence": "1615922903162",
"price": "57263.3",
"size": "0.00306338",
"bestBid": "57259.4",
"bestBidSize": "0.00250335",
"bestAsk": "57260.4",
"bestAskSize": "0.01"
}價格值表明 1 個 BTC 的價格為 57263.3 USDT。
我們還需要實現的方法是 getTradeAccount()。 在交易所,我們通常有幾個賬户,Cassandre 需要知道哪個賬户是交易賬户。為此,我們需要實現 getTradeAccount() 方法,該方法將我們擁有的賬户列表作為參數傳遞給它,然後從該列表中返回我們想要用於交易的賬户。
在我們的示例中,我們的交易賬户在交易所名為 “trade”,因此我們只需返回它。
6. 創建位置
為了接收新的數據通知,我們可以覆蓋 BasicCassandreStrategy 的以下方法:
- onAccountUpdate() 用於接收關於賬户的更新
- onTickerUpdate() 用於接收新的報價
- onOrderUpdate() 用於接收關於訂單的更新
- onTradeUpdate() )用於接收關於交易的更新
- onPositionUpdate() 用於接收關於位置的更新
- onPositionStatusUpdate() 用於接收關於位置狀態變更的更新
在本教程中,我們將實現一個簡單的算法:我們檢查所有接收到的新報價。如果 1 BTC 的價格低於 56 000 USDT,我們認為現在是時候買入。
為了簡化收益計算,訂單、交易和關閉,Cassandre 提供了用於自動管理位置的類。
要使用它,第一步是使用 PositionRulesDTO 類創建位置規則,例如:
PositionRulesDTO rules = PositionRulesDTO.builder()
.stopGainPercentage(4f)
.stopLossPercentage(25f)
.create();然後,我們使用該規則創建職位:
createLongPosition(new CurrencyPairDTO(BTC, USDT), new BigDecimal("0.01"), rules);此時,卡桑德爾將創建一個0.01 BTC的買單。訂單狀態將變為OPENING,當所有相關的交易到達時,狀態將變為OPENED。從現在開始,對於每一個收到的報價,卡桑德爾將自動計算,結合新的價格,判斷以該價格平倉是否會觸發我們的兩個規則(4%止盈或25%止損)。
如果觸發其中一個規則,卡桑德爾將自動創建一個0.01 BTC的賣單。訂單狀態將變為CLOSING,當所有相關的交易到達時,狀態將變為CLOSED。
這是我們將使用的代碼:
@Override
public void onTickerUpdate(TickerDTO ticker) {
if (new BigDecimal("56000").compareTo(ticker.getLast()) == -1) {
if (canBuy(new CurrencyPairDTO(BTC, USDT), new BigDecimal("0.01"))) {
PositionRulesDTO rules = PositionRulesDTO.builder()
.stopGainPercentage(4f)
.stopLossPercentage(25f)
.build();
createLongPosition(new CurrencyPairDTO(BTC, USDT), new BigDecimal("0.01"), rules);
}
}
}總結如下:
- 對於每一個新的交易對,我們都會檢查價格是否低於 56000。
- 如果我們的交易賬户上有足夠的USDT,我們就會開立 0.01 BTC 的頭寸。
- 從現在開始,對於每一個交易對:
- 如果使用新價格的計算收益超過 4% 的收益或 25% 的虧損,Cassandre 將會關閉我們通過出售購買的 0.01 BTC 創建的頭寸。
7. 跟蹤日誌中的位置演變
我們將最終實現 <em >onPositionStatusUpdate()</em > 函數,以查看位置的開啓/關閉情況:
@Override
public void onPositionStatusUpdate(PositionDTO position) {
if (position.getStatus() == OPENED) {
logger.info("> New position opened : {}", position.getPositionId());
}
if (position.getStatus() == CLOSED) {
logger.info("> Position closed : {}", position.getDescription());
}
}8. 回溯測試 (Backtesting)
回溯測試,簡單來説,是指在歷史數據上測試交易策略的過程。Cassandre 交易機器人允許我們模擬機器人對歷史數據的反應。
第一步是將我們的歷史數據(CSV 或 TSV 文件)放在 src/test/resources 文件夾中。
如果我們在 Linux 系統上,這裏有一個簡單的腳本來生成它們:
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 > tickers-btc-usdt.tsv它將創建一個名為 tickers-btc-usdt.tsv 的文件,其中包含從 startDate (3 個月前) 到 endDate (現在) 的 BTC-USDT 歷史匯率。
第二步是創建我們的虛擬賬户餘額,以模擬我們想要投資的精確資產數量。
在這些文件中,對於每個賬户,我們設置了每種加密貨幣的餘額。例如,這是模擬我們交易賬户資產的 user-trade.csv 的內容:
此文件也必須位於 src/test/resources 文件夾中。
BTC 1
USDT 10000
ETH 10現在,我們可以添加一個測試:
@SpringBootTest
@Import(TickerFluxMock.class)
@DisplayName("Simple strategy test")
public class MyFirstStrategyLiveTest {
@Autowired
private MyFirstStrategy strategy;
private final Logger logger = LoggerFactory.getLogger(MyFirstStrategyLiveTest.class);
@Autowired
private TickerFluxMock tickerFluxMock;
@Test
@DisplayName("Check gains")
public void whenTickersArrives_thenCheckGains() {
await().forever().until(() -> tickerFluxMock.isFluxDone());
HashMap<CurrencyDTO, GainDTO> gains = strategy.getGains();
logger.info("Cumulated gains:");
gains.forEach((currency, gain) -> logger.info(currency + " : " + gain.getAmount()));
logger.info("Position still opened :");
strategy.getPositions()
.values()
.stream()
.filter(p -> p.getStatus().equals(OPENED))
.forEach(p -> logger.info(" - {} " + p.getDescription()));
assertTrue(gains.get(USDT).getPercentage() > 0);
}
}導入來自TickerFluxMock將加載我們src/test/resources文件夾中的歷史數據,並將其發送到我們的策略。然後我們使用await()方法來確保所有從文件中加載的股票已發送到我們的策略。最後,我們顯示已關閉的頭寸、仍未關閉的頭寸以及全局收益。
9. 結論
本教程闡述瞭如何創建與加密貨幣交易所交互的策略,並將其與歷史數據進行測試。
當然,我們的算法相對簡單;但在實際應用中,目標是找到有前景的技術、好的算法以及可靠的數據,以便我們能夠確定何時建立頭寸。例如,我們可以使用技術分析,就像 Cassandre 所做的那樣,集成 ta4j。