交易所撮合交易【一】

  • 2019 年 11 月 30 日
  • 筆記

前言

        最近一直在研究股票(幣幣)交易所撮合引擎,從一個連撮合概念都不太清晰我逐步摸索漸漸的有了一些自己的理解和概念。所以打算寫一個撮合引擎,希望集眾人智慧做出一個不錯的引擎。

撮合規則

        本次主要是實現,限價和市價交易;

        撮合規則:                 (一)買價大於等於賣價,可以成交,先掛單為價格訂製者,即為成交價;                 (二)相同價格,以時間早的先成交。                 (三)市價最先成交,以對手價為成交價。

撮合架構與技術定性

架構與技術定性的因素:                 1、撮合的本質是一個單一線性過程,沒有辦法並發的。(如果有請大神指點...)                 2、以單一訂單為撮合邏輯(吃完一個單,再從訂單薄拿出下一個訂單吃),評率太高,耗時長。                 3、資料庫撮合,不考慮,只考慮記憶體撮合。                 4、撮合的時候:只需要拿出最優,不需要排序。                 5、撮合結果:不需要關心和誰交易。結合「第二點」讓每一個price level是可以在邏輯上獨立的。降低頻率。

框架與技術選擇:                 執行緒框架選擇:「倫敦外匯交易所LMAX開源的Disruptor框架」,性能這些就在多做闡述了。                 記憶體框架選擇:開始考慮的是redis,最終決定用Hazelcast主要原因是簡單,快,集群方便。                 資料庫的選擇:因為項目基本上是數據流開發,根據業內對數據流支援畢竟友好的方面考慮選擇mongodb                 吃單的時候,以並行流計算的方式獲得最優匹配。                 撮合完成後用Flink或規則引擎完成k線數據盤口深度數據生成。(還在考慮中)

部分技術的實現

因為之前一些朋友是以訂單薄為撮合薄的,我也實現了下,下面是一些重要細節的程式碼:

//獲取匹配的訂單薄數據  IMap<Long, Order> outMap = hzInstance.getMap(HzltUtil.getMatchKey(coinTeam, isBuy));  /**   * -★   * -使用Java 8 Stream API中的並行流來計算最優   * -能快速的拿到撮合對象,不用排序取值,降低性能消耗   */  Order outOrder = outMap.values().parallelStream().min(HzltUtil::compareOrder).get();    //這種方式最難的,就是整理盤口深度數據了        /**       * -★  	 * -獲取行情深度  	 *  	 * @param coinTeam 交易隊  	 * @param isBuy    是否是買  	 * @return List<Depth>  	 */  	public List<Depth> getMarketDepth(String coinTeam, Boolean isBuy) {  		List<Depth> depths = new ArrayList<Depth>();  		IMap<Long, Order> map = hzInstance.getMap(HzltUtil.getMatchKey(coinTeam, isBuy));  		if (map.size() > 0) {  			/**  			 * -這個流:主要是安價格分組和統計,使用並行流快速歸集。  			 */  			List<Depth> list = map.entrySet().parallelStream().map(mo -> mo.getValue())  					.collect(Collectors.groupingBy(Order::getPrice)).entrySet().parallelStream()  					.map(ml -> new Depth(ml.getKey().toString(),  							ml.getValue().stream().map(o -> o.getUnFinishNumber()).reduce(BigDecimal.ZERO, BigDecimal::add)  									.toString(),  							"0", 1, coinTeam, isBuy))  					.sorted((d1, d2) -> HzltUtil.compareTo(d1, d2)).collect(Collectors.toList());  			/**  			 * -這個流:主要是盤口的累計計算,因涉及排序選擇串列流  			 */  			list.stream().reduce(new Depth("0", "0", "0", 1, coinTeam, isBuy), (one, two) -> {  				one.setTotal((new BigDecimal(one.getTotal()).add(new BigDecimal(two.getNumber()))).toString());  				depths.add(new Depth(two.getPrice(), two.getNumber(), one.getTotal(), two.getPlatform(),  						two.getCoinTeam(), two.getIsBuy()));  				return one;  			});  		} else {  			Depth depth = new Depth("0", "0", "0", 1, coinTeam, isBuy);  			depths.add(depth);  		}  		return depths;  	}

這種方式撮合記錄非常多,對訂單的修改也很頻繁,對資料庫的壓力很大,而且計算的量級也會很龐大,所以拋棄...

未解決的問題

問題一: 當市價把對手盤吃空,是以最後成交價掛單,還是直接撤銷?如果是掛單,是變成限價掛單嗎? 問題二:市價買入後,撮合到最後,剩下的錢連最小單位都買不了,怎麼處理呢?直接撤銷么?有撤銷記錄嗎?

問題三:我看過有的朋友把買賣盤分成2個撮合隊列,那麼同時來買賣市價,是兩個以上次成交價對吃嗎,還是各自吃對手盤呢?

(adsbygoogle = window.adsbygoogle || []).push({});

Tags: