前言
无论是股票交易系统,还是数字货币交易系统,都离不开撮合交易引擎,这是交易平台的心脏。同时,一个优秀的架构设计也会让交易平台的运维和持续开发更加容易。本文基于对开源项目的深入研究,总结了数字货币交易系统的架构设计。
关于撮合交易系统
撮合技术主要是从数据库撮合技术向内存撮合技术发展,这是因为数据库撮合技术越来越无法满足金融交易对于高可靠性、高性能、强安全性、可扩展性以及易维护性的需求。金融(币币)交易撮合系统中包括以下几个核心模块:
关于技术选型
一个交易所平台的技术架构主要考虑安全性、分布式、易扩展、容错性、低延时、高并发等特性,以及熔断机制、服务注册和发现、消息服务、服务网关、安全认证、内存数据库、关系型数据库等各种选项,最终形成了如下技术选型:
关于交易所架构设计
基于SpringCloud开发基于微服务架构的交易平台,首先需要对SpringCloud的基础架构有所了解,我们熟知的SpringCloud微服务架构如下图所示:
由于篇幅关系,本文就不对SpringCloud的技术架构进行详细解读了。
在SpringCloud这个优秀的微服务框架基础之上,如何构建一个交易系统呢?开源项目CoinExchange对交易所的架构做了如下架构设计:
将撮合交易引擎、API等拆分作为单独的服务,基于SpringCloud构建了一个精简的交易所架构。
部署图如下:
关于撮合交易引擎
采用内存撮合的方式进行,以Kafka做撮合订单信息传输,MongoDB持久化订单成交明细,MySQL记录订单总体成交。其中行情模块主要负责订单成交持久化、行情生成、行情推送等服务,包括:
内存撮合交易支持的模式
撮合逻辑过程如下图所示:
示例代码如下:
/**
* 限价委托单与限价队列匹配
* @param lpList 限价对手单队列
* @param focusedOrder 交易订单
*/
public void matchLimitPriceWithLPList(TreeMap lpList, ExchangeOrder focusedOrder,boolean canEnterList){
List exchangeTrades = new ArrayList<>();
List completedOrders = new ArrayList<>();
synchronized (lpList) {
Iterator> mergeOrderIterator = lpList.entrySet().iterator();
boolean exitLoop = false;
while (!exitLoop && mergeOrderIterator.hasNext()) {
Map.Entry entry = mergeOrderIterator.next();
MergeOrder mergeOrder = entry.getValue();
Iterator orderIterator = mergeOrder.iterator();
//买入单需要匹配的价格不大于委托价,否则退出
if (focusedOrder.getDirection() == ExchangeOrderDirection.BUY && mergeOrder.getPrice().compareTo(focusedOrder.getPrice()) > 0) {
break;
}
//卖出单需要匹配的价格不小于委托价,否则退出
if (focusedOrder.getDirection() == ExchangeOrderDirection.SELL && mergeOrder.getPrice().compareTo(focusedOrder.getPrice()) < 0) {
break;
}
while (orderIterator.hasNext()) {
ExchangeOrder matchOrder = orderIterator.next();
//处理匹配
ExchangeTrade trade = processMatch(focusedOrder, matchOrder);
exchangeTrades.add(trade);
//判断匹配单是否完成
if (matchOrder.isCompleted()) {
//当前匹配的订单完成交易,删除该订单
orderIterator.remove();
completedOrders.add(matchOrder);
}
//判断交易单是否完成
if (focusedOrder.isCompleted()) {
//交易完成
completedOrders.add(focusedOrder);
//退出循环
exitLoop = true;
break;
}
}
if(mergeOrder.size() == 0){
mergeOrderIterator.remove();
}
}
}
//如果还没有交易完,订单压入列表中
if (focusedOrder.getTradedAmount().compareTo(focusedOrder.getAmount()) < 0 && canEnterList) {
addLimitPriceOrder(focusedOrder);
}
//每个订单的匹配批量推送
handleExchangeTrade(exchangeTrades);
if(completedOrders.size() > 0){
orderCompleted(completedOrders);
TradePlate plate = focusedOrder.getDirection() == ExchangeOrderDirection.BUY ? sellTradePlate : buyTradePlate;
sendTradePlateMessage(plate);
}
}
/**
* 限价委托单与市价队列匹配
* @param mpList 市价对手单队列
* @param focusedOrder 交易订单
*/
public void matchLimitPriceWithMPList(LinkedList mpList,ExchangeOrder focusedOrder){
List exchangeTrades = new ArrayList<>();
List completedOrders = new ArrayList<>();
synchronized (mpList) {
Iterator iterator = mpList.iterator();
while (iterator.hasNext()) {
ExchangeOrder matchOrder = iterator.next();
ExchangeTrade trade = processMatch(focusedOrder, matchOrder);
logger.info(">>>>>"+trade);
if(trade != null){
exchangeTrades.add(trade);
}
//判断匹配单是否完成,市价单amount为成交量
if(matchOrder.isCompleted()){
iterator.remove();
completedOrders.add(matchOrder);
}
//判断吃单是否完成,判断成交量是否完成
if (focusedOrder.isCompleted()) {
//交易完成
completedOrders.add(focusedOrder);
//退出循环
break;
}
}
}
//如果还没有交易完,订单压入列表中
if (focusedOrder.getTradedAmount().compareTo(focusedOrder.getAmount()) < 0) {
addLimitPriceOrder(focusedOrder);
}
//每个订单的匹配批量推送
handleExchangeTrade(exchangeTrades);
orderCompleted(completedOrders);
}
/**
* 市价委托单与限价对手单列表交易
* @param lpList 限价对手单列表
* @param focusedOrder 待交易订单
*/
public void matchMarketPriceWithLPList(TreeMap lpList, ExchangeOrder focusedOrder){
List exchangeTrades = new ArrayList<>();
List completedOrders = new ArrayList<>();
synchronized (lpList) {
Iterator> mergeOrderIterator = lpList.entrySet().iterator();
boolean exitLoop = false;
while (!exitLoop && mergeOrderIterator.hasNext()) {
Map.Entry entry = mergeOrderIterator.next();
MergeOrder mergeOrder = entry.getValue();
Iterator orderIterator = mergeOrder.iterator();
while (orderIterator.hasNext()) {
ExchangeOrder matchOrder = orderIterator.next();
//处理匹配
ExchangeTrade trade = processMatch(focusedOrder, matchOrder);
if (trade != null) {
exchangeTrades.add(trade);
}
//判断匹配单是否完成
if (matchOrder.isCompleted()) {
//当前匹配的订单完成交易,删除该订单
orderIterator.remove();
completedOrders.add(matchOrder);
}
//判断焦点订单是否完成
if (focusedOrder.isCompleted()) {
completedOrders.add(focusedOrder);
//退出循环
exitLoop = true;
break;
}
}
if(mergeOrder.size() == 0){
mergeOrderIterator.remove();
}
}
}
//如果还没有交易完,订单压入列表中,市价买单按成交量算
if (focusedOrder.getDirection() == ExchangeOrderDirection.SELL&&focusedOrder.getTradedAmount().compareTo(focusedOrder.getAmount()) < 0
|| focusedOrder.getDirection() == ExchangeOrderDirection.BUY&& focusedOrder.getTurnover().compareTo(focusedOrder.getAmount()) < 0) {
addMarketPriceOrder(focusedOrder);
}
//每个订单的匹配批量推送
handleExchangeTrade(exchangeTrades);
if(completedOrders.size() > 0){
orderCompleted(completedOrders);
TradePlate plate = focusedOrder.getDirection() == ExchangeOrderDirection.BUY ? sellTradePlate : buyTradePlate;
sendTradePlateMessage(plate);
}
}
每个币种对应不同的数据访问方式,大部分区块链项目的钱包操作方式是相同的或十分相似的,比如BTC、LTC、BCH、BSV、BCD等比特币衍生币,其API操作方式几乎一样;再比如ETH,当你掌握一个合约币种的操作,其他基于ETH发行的数字货币的操作方式几乎一样。所以,基本上当你花时间弄懂了一个,就懂了一堆币种。
本项目使用的钱包操作方案也是不同的,也尽可能的为大家展示了不同用法:
一般而言,当交易所来往资金量不大的时候,你可以自己摸索,但是当交易所资金量大了以后,如果你对自己操作钱包不太放心,你也可以使用第三方的钱包服务,当然,这需要你与钱包服务商进行谈判,付个年费什么的。
下图是关于交易平台充值逻辑的一个简单时序图:
总结
通过以上的说明及图示,我们基本上对交易所的整体架构有了一定的认知。
感谢
最后感谢开源交易所项目给与我学习的机会!
Java开源交易平台项目:https://gitee.com/cexchange/CoinExchange