设计清算系统


清算系统就是处理撮合结果,将买卖双方冻结的USD和BTC分别交换到对方的可用余额,就使得买卖双方真正完成了资产交换。

因此,我们设计清算系统,需要引用AssetServiceOrderService

当撮合引擎输出MatchResult后,ClearingService需要处理该结果,该清算方法代码框架如下:

  1. public void clearMatchResult(MatchResult result) {
  2. OrderEntity taker = result.takerOrder;
  3. switch (taker.direction) {
  4. case BUY -> {
  5. // TODO
  6. }
  7. // TODO
  8. }
  9. default -> throw new IllegalArgumentException("Invalid direction.");
  10. }

对Taker买入成交的订单,处理时需要注意,成交价格是按照Maker的报价成交的,而Taker冻结的金额是按照Taker订单的报价冻结的,因此,解冻后,部分差额要退回至Taker可用余额:

对Taker卖出成交的订单,只需将冻结的BTC转入Maker,将Maker冻结的USD转入Taker即可:

  1. case SELL -> {
  2. for (MatchDetailRecord detail : result.matchDetails) {
  3. OrderEntity maker = detail.makerOrder();
  4. BigDecimal matched = detail.quantity();
  5. // 卖方BTC转入买方账户:
  6. assetService.transfer(Transfer.FROZEN_TO_AVAILABLE, taker.userId, maker.userId, AssetEnum.BTC, matched);
  7. // 买方USD转入卖方账户:
  8. // 删除完全成交的Maker:
  9. if (maker.unfilledQuantity.signum() == 0) {
  10. orderService.removeOrder(maker.id);
  11. }
  12. }
  13. if (taker.unfilledQuantity.signum() == 0) {
  14. orderService.removeOrder(taker.id);
  15. }
  16. }

当用户取消订单时,ClearingService需要取消订单冻结的USD或BTC,然后将订单从OrderService中删除:

这样,我们就完成了清算系统的实现。

下面是问题解答。

如果有交易手续费,则首先需要思考:手续费应该定义在哪?

如果我们把手续费定义为一个配置,注入到ClearingService

  1. public class ClearingService {
  2. @Value("${exchange.fee-rate:0.0005}")
  3. }

那么问题来了:对于同一个订单输入序列,设定手续费为万分之五,和设定手续费为万分之二,执行后交易引擎的状态和输出结果是不同的!这就使得交易引擎不再是一个确定性状态机,无法重复执行交易序列。

此外,不同用户通常可以有不同的交易费率,例如机构的费率比个人低,做市商的费率可以为0。

要支持不同用户不同的费率,以及保证交易引擎是一个确定性状态机,手续费必须作为订单的一个不变属性,从外部输入,这样交易引擎不再关心如何读取费率。

带手续费的订单在创建时,针对买单,冻结金额不再是价格x数量,而是:

首先,需要修改OrderService创建订单时的冻结逻辑。其次,在清算时,除了买卖双方交换资产,还需要设定一个系统用户,专门接收手续费,将买方手续费从冻结的金额转入系统手续费用户,而卖方获得转入的金额会扣除手续费。

可以为挂单和吃单设置不同的手续费率吗?

可以,需要给订单添加两个费率属性:takerFeeRatemakerFeeRate,买方下单冻结时,额外冻结的金额按冻结。

清算逻辑会复杂一些,要针对Taker和Maker分别计算不同的费率。

可以,通常可以给makerFeeRate设置负费率,以鼓励做市。清算逻辑会更复杂一些,因为针对负费率的Maker,需要从系统手续费用户转账给Maker。

参考源码

可以从GitHub或下载源码。

GitHub ▸ ▸ warpexchange

▸ build)

)

▤ schema.sql)

)

▤ pom.xml)

)

▸ src/main)

)

▸ bean)

)

▤ OrderBookItemBean.java)

)

▤ AssetEnum.java)

)

▤ OrderStatus.java)

)

▸ support)

)

▸ trade)

)

▸ support)

)

▸ util)

)

▸ resources)

)

▤ pom.xml)

)

▸ src/main)

)

▤ ConfigApplication.java)

)

▤ application.yml)

)

▸ config-repo)

)

▤ application-test.yml)

)

▤ push.yml)

)

▤ trading-api.yml)

)

▤ trading-sequencer.yml)

)

▤ ui.yml)

)

▤ pom.xml)

)

▸ src/main)

)

▤ PushApplication.java)

)

▤ application.yml)

)

▸ quotation)

)

▸ java/com/itranswarp/exchange)

)

▸ resources)

)

▤ pom.xml)

)

▸ src/main)

)

▤ TradingApiApplication.java)

)

▤ application.yml)

)

▸ src)

)

▸ java/com/itranswarp/exchange)

)

▤ Asset.java)

)

▤ Transfer.java)

)

▤ ClearingService.java)

)

▤ MatchDetailRecord.java)

)

▤ MatchResult.java)

)

▤ OrderKey.java)

)

▤ OrderService.java)

)

▸ resources)

)

▸ test/java/com/itranswarp/exchange)

)

▤ AssetServiceTest.java)

)

▤ MatchEngineTest.java)

)

▸ trading-sequencer)

)

▸ java/com/itranswarp/exchange)

)

▸ resources)

)

▤ pom.xml)

)

▸ src/main)

)

▤ UIApplication.java)

)

▤ application.yml)

)

▤ .gitignore)

)

▤ README.md)

清算系统只负责根据撮合引擎输出的结果进行清算,清算的本质就是根据成交价格和数量对买卖双方的对应资产互相划转。清算系统本身没有状态。

读后有收获可以支付宝请作者喝咖啡: