撮合引擎開發:開篇數據庫
撮合引擎開發:MVP版本服務器
撮合引擎開發:對接黑箱數據結構
前言
自從有人在微信羣裏開價5萬求購Golang版的撮合引擎以後,我就想本身開發一款,畢竟,以個人經驗來講,開發個高性能的撮合引擎並沒什麼難度。併發
說幹就幹,因而,利用業餘時間慢慢開發出了一款Golang版的高性能撮合引擎,前先後後花了大概一個月的時間。再想一想本身很久沒更新文章了,個人我的IP都已經生鏽了,也應該發大招磨一磨了。所以決定,乾脆就以連載的方式,分享下我是如何設計與實現這款價值超5萬的撮合引擎的。負載均衡
原本,想發成掘金小冊,收點稿費,畢竟這是個具備很大商業價值的軟件,但問了掘金的人員,他們目前不接收這類主題。最終決定免費發佈,還能夠多發幾個渠道,說不定還能給我多帶來些關注量。post
好了,下面開始進入撮合引擎系列的正題。性能
撮合引擎簡介
撮合引擎是全部撮合交易系統的核心組件,不論是股票交易系統——包括現貨交易、期貨交易、期權交易等,仍是數字貨幣交易系統——包括幣幣交易、合約交易、槓桿交易等,以及各類不一樣的貴金屬交易系統、大宗商品交易系統等,雖然各類不一樣交易系統的交易標的不一樣,但只要都是採用撮合交易模式,都離不開撮合引擎。設計
撮合引擎是能夠具備通用性的,一套具備通用性的撮合引擎實現理論上能夠應用到任何撮合交易系統中,而無需作任何代碼上的調整。便是說,同一套撮合引擎實現,既能夠應用在股票交易系統,也能夠應用在數字貨幣交易系統,能夠用於現貨交易,也能夠用於合約交易等。
那麼,一套具備通用性的撮合引擎應該具有哪些功能呢?肯定該問題的答案以前,咱們先簡單梳理一下一個完整的交易流程是怎樣的?通常會包括如下步驟:
- 系統開放某個交易標的的交易功能。
- 用戶提交該交易標的的買賣申報,即委託單。
- 系統驗證委託單是否有效,包括交易標的是否處於可交易的狀態、訂單的價格和數量是否符合要求等。
- 肯定該委託單的**掛單(Maker)費率和吃單(Taker)**費率。
- 檢查用戶的資產帳戶狀況,包括帳戶狀態是否交易受限,是否有足夠資金用於下單等。
- 將詳細的委託單數據持久化到數據庫,並凍結用戶帳戶中相應數量的資金。
- 將委託單進行撮合處理,即在**交易委託帳本(OrderBook)**中尋找能與該委託單匹配成交的訂單,匹配的結果多是:所有成交、部分紅交或無匹配。所有成交或部分紅交時,可能在交易委託帳本中存在一個或多個匹配的訂單,即會產生一條或多條成交記錄。當無匹配或部分紅交時,委託單的部分數據包括剩餘未成交的數量會暫時保存到交易委託帳本中,等待與後續的委託單匹配撮合。
- 將撮合產生的成交記錄持久化到數據庫,並根據歷史成交記錄生成市場數據,如K線數據、今日漲跌幅等。
- 更新數據庫中全部成交訂單的委託單數據,以及更新訂單用戶的資產帳戶餘額。
- 將更新的訂單數據、市場數據等發送給到前臺。
整個交易流程中涉及到多個服務,包括用戶服務、帳戶服務、訂單服務、撮合服務、市場數據服務等。其中,只有第7步是撮合引擎處理的。從單一職責原則來講,撮合引擎就應該只作一件事,那就是負責撮合訂單。撮合以前的委託單持久化、凍結資金等,以及撮合以後生成K線數據等,都不該該屬於撮合引擎的職責。
撮合競價方式
撮合競價方式通常有兩種,一是集合競價,二是連續競價。股票交易系統通常會在不一樣交易時間段採用不一樣的競價方式,好比在開盤或收盤時採用集合競價,從而產生開盤價或收盤價,其他時間採用連續競價。而大多數字貨幣交易系統則沒有集合競價,只有連續競價,開盤價通常是在開始交易以前就設定好的。
集合競價
所謂集合競價,是指對一段時間內接收的買賣委託單一次性集中撮合的競價方式。以深滬的股票交易系統爲例,在每一個交易日的 9:15~9:25 期間是集合競價時間。在該時間段內,系統陸續接收到的委託單不會即時成交,而是先將全部委託單按照價格優先、時間優先的原則排序,並在此基礎上,找出一個基準價格,使它能同時知足如下三個條件:
- 可實現最大成交量的價格;
- 高於該價格的買單與低於該價格的賣單能所有成交的價格;
- 與該價格相同的買方或賣方至少有一方所有成交的價格。
在 9:25 分結束的時候,該基準價格就被肯定爲成交價格,全部高於該價格的買單與低於該價格的賣單都將以該價格成交。未能成交的委託單,則自動轉入連續競價。
不過,若是知足以上三個條件的價格存在兩個或兩個以上呢?對此,深交所和上交所的處理方案有所不一樣,深交所會取距前收盤價最近的價格爲成交價,而上交所則取使未成交量最小的價格爲成交價,若是未成交量最小的價格仍不止一個,則取中間價爲成交價。
集合競價的主要目的就是爲了肯定開盤價或收盤價。
連續競價
所謂連續競價,也是咱們所熟悉的競價方式,是指對買賣委託單逐筆連續撮合的競價方式。用戶的掛單,只要知足成交條件,就能即時成交。而集合競價,則要等到最後一刻纔會成交。
連續競價時,依然要知足價格優先、時間優先的成交原則:
- 價格優先:買單則價格較高者能優先成交,賣單則是價格較低者能優先成交。
- 時間優先:買賣方向和價格相同的委託單,先申報的委託單會比後申報的委託單優先成交。
另外,買入價必須大於或等於賣出價才能撮合成交。當買入價等於賣出價時,成交價就是買入價或賣出價。當買入價大於賣出價時,則還要參考前一筆成交價來肯定最新成交價。假設買入價爲 B,賣出價爲 S,前一筆成交價爲 P,最新成交價爲 N,那麼:
- 若是 P >= B,則 N = B
- 若是 P <= S,則 N = S
- 若是 B > P > S,則 N = P
一套通用的撮合引擎應該兩種競價方式都支持,但對於同一交易標的來講,兩種競價方式不能同時進行,所以設計上須要考慮如何在兩種競價方式之間切換,具體的實現思路在後續章節咱們再展開來說。
質量需求
咱們的撮合引擎除了要知足以上所說的功能需求,還應該知足一些質量需求,尤爲對可用性、可伸縮性和性能的要求較高。另外,爲了達到通用,也要知足可複用性的需求。
先說下可複用性,咱們指望的是該撮合引擎既能用於股票交易系統,也能用於數字貨幣交易系統,既能用於幣幣交易,也能用於合約交易。所以,該撮合引擎要避免引入與具體系統強相關的業務邏輯,以增強它的可複用性。
再看看性能,要衡量一個撮合引擎的性能,就看它處理每一個交易對的 TPS 有多高,即每秒鐘能處理多少筆相同交易對的委託單。之前,基於數據庫的撮合技術,TPS 通常只有10筆/秒。而如今基本都是採用內存撮合技術,TPS 很容易就能達到1000筆/秒,若是使用獨佔的高性能服務器,1萬筆/秒甚至更高的 TPS 都不難達到。
接着談談可伸縮性,咱們的每個撮合引擎既能夠同時處理多個交易標的,也能夠只處理單個交易標的。當交易標的和併發量增多的時候,能夠增長服務器,部署成撮合引擎集羣,分別用來處理不一樣的交易標的,從而可以實現負載均衡。
最後聊聊可用性,高可用主要體如今兩點,一是故障率要低,二是對故障維修的時間要短。要下降故障率,那撮合引擎就須要有較高的健壯性,對於可能致使引擎出故障的各類異常狀況要考慮好並設計好解決方案。另外,還能夠採用多機熱備份技術來提升可用性,並且要保證互備服務器之間的數據一致,那就須要引入內存狀態機複製方案,實現上會複雜不少。
不過,咱們並不是一會兒就要達到很高的質量要求,由於要求越高,其架構和實現會越複雜。咱們能夠先從簡單的版本開始,而後不斷升級迭代。
小結
咱們目的是實現一套通用的撮合引擎,要支持集合競價和連續競價,還要實現一些質量需求,提升系統的可複用性、性能、可伸縮性、可用性等。後續章節會對這些需求不斷深刻探討其設計與實現。另外,咱們將採用不斷升級迭代的方式來設計和實現多個版本的撮合引擎。
留兩個思考題:
- 集合競價結束的時候,若是不存在符合那三個條件的基準價格,那開盤價又將如何肯定?
- 對於單個交易對,是否可經過橫向增長服務器的方式提升其性能?