撮合引擎開(kāi)發(fā):開(kāi)篇
撮合引擎開(kāi)發(fā):MVP版本
撮合引擎開(kāi)發(fā):數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)
撮合引擎開(kāi)發(fā):對(duì)接黑箱
撮合引擎開(kāi)發(fā):解密黑箱流程
前言
自從有人在微信群里開(kāi)價(jià)5萬(wàn)求購(gòu)Golang版的撮合引擎之后源哩,我就想自己開(kāi)發(fā)一款怎虫,畢竟,以我的經(jīng)驗(yàn)來(lái)說(shuō),開(kāi)發(fā)個(gè)高性能的撮合引擎并沒(méi)什么難度舷蒲。
說(shuō)干就干裳凸,于是犀盟,利用業(yè)余時(shí)間慢慢開(kāi)發(fā)出了一款Golang版的高性能撮合引擎螃征,前前后后花了大概一個(gè)月的時(shí)間。再想想自己好久沒(méi)更新文章了略步,我的個(gè)人IP都已經(jīng)生銹了描扯,也應(yīng)該發(fā)大招磨一磨了。因此決定趟薄,干脆就以連載的方式绽诚,分享下我是如何設(shè)計(jì)與實(shí)現(xiàn)這款價(jià)值超5萬(wàn)的撮合引擎的。
本來(lái)竟趾,想發(fā)成掘金小冊(cè)憔购,收點(diǎn)稿費(fèi)宫峦,畢竟這是個(gè)具有很大商業(yè)價(jià)值的軟件岔帽,但問(wèn)了掘金的人員,他們目前不接收這類(lèi)主題导绷。最終決定免費(fèi)發(fā)布犀勒,還可以多發(fā)幾個(gè)渠道,說(shuō)不定還能給我多帶來(lái)些關(guān)注量妥曲。
好了贾费,下面開(kāi)始進(jìn)入撮合引擎系列的正題。
撮合引擎簡(jiǎn)介
撮合引擎是所有撮合交易系統(tǒng)的核心組件檐盟,不管是股票交易系統(tǒng)——包括現(xiàn)貨交易褂萧、期貨交易、期權(quán)交易等葵萎,還是數(shù)字貨幣交易系統(tǒng)——包括幣幣交易导犹、合約交易、杠桿交易等羡忘,以及各種不同的貴金屬交易系統(tǒng)谎痢、大宗商品交易系統(tǒng)等,雖然各種不同交易系統(tǒng)的交易標(biāo)的不同卷雕,但只要都是采用撮合交易模式节猿,都離不開(kāi)撮合引擎。
撮合引擎是可以具有通用性的漫雕,一套具有通用性的撮合引擎實(shí)現(xiàn)理論上可以應(yīng)用到任何撮合交易系統(tǒng)中滨嘱,而無(wú)需做任何代碼上的調(diào)整峰鄙。即是說(shuō),同一套撮合引擎實(shí)現(xiàn)太雨,既可以應(yīng)用在股票交易系統(tǒng)先馆,也可以應(yīng)用在數(shù)字貨幣交易系統(tǒng),可以用于現(xiàn)貨交易躺彬,也可以用于合約交易等煤墙。
那么,一套具有通用性的撮合引擎應(yīng)該具備哪些功能呢宪拥?確定該問(wèn)題的答案之前仿野,我們先簡(jiǎn)單梳理一下一個(gè)完整的交易流程是怎樣的?一般會(huì)包括以下步驟:
- 系統(tǒng)開(kāi)放某個(gè)交易標(biāo)的的交易功能她君。
- 用戶(hù)提交該交易標(biāo)的的買(mǎi)賣(mài)申報(bào)脚作,即委托單。
- 系統(tǒng)驗(yàn)證委托單是否有效缔刹,包括交易標(biāo)的是否處于可交易的狀態(tài)球涛、訂單的價(jià)格和數(shù)量是否符合要求等。
- 確定該委托單的掛單(Maker)費(fèi)率和吃單(Taker)費(fèi)率校镐。
- 檢查用戶(hù)的資產(chǎn)賬戶(hù)情況亿扁,包括賬戶(hù)狀態(tài)是否交易受限,是否有足夠資金用于下單等鸟廓。
- 將詳細(xì)的委托單數(shù)據(jù)持久化到數(shù)據(jù)庫(kù)从祝,并凍結(jié)用戶(hù)賬戶(hù)中相應(yīng)數(shù)量的資金。
- 將委托單進(jìn)行撮合處理引谜,即在交易委托賬本(OrderBook)中尋找能與該委托單匹配成交的訂單牍陌,匹配的結(jié)果可能是:全部成交、部分成交或無(wú)匹配员咽。全部成交或部分成交時(shí)毒涧,可能在交易委托賬本中存在一個(gè)或多個(gè)匹配的訂單,即會(huì)產(chǎn)生一條或多條成交記錄贝室。當(dāng)無(wú)匹配或部分成交時(shí)契讲,委托單的部分?jǐn)?shù)據(jù)包括剩余未成交的數(shù)量會(huì)暫時(shí)保存到交易委托賬本中,等待與后續(xù)的委托單匹配撮合档玻。
- 將撮合產(chǎn)生的成交記錄持久化到數(shù)據(jù)庫(kù)怀泊,并根據(jù)歷史成交記錄生成市場(chǎng)數(shù)據(jù),如K線(xiàn)數(shù)據(jù)误趴、今日漲跌幅等霹琼。
- 更新數(shù)據(jù)庫(kù)中所有成交訂單的委托單數(shù)據(jù),以及更新訂單用戶(hù)的資產(chǎn)賬戶(hù)余額。
- 將更新的訂單數(shù)據(jù)枣申、市場(chǎng)數(shù)據(jù)等發(fā)送給到前臺(tái)售葡。
整個(gè)交易流程中涉及到多個(gè)服務(wù),包括用戶(hù)服務(wù)忠藤、賬戶(hù)服務(wù)挟伙、訂單服務(wù)、撮合服務(wù)模孩、市場(chǎng)數(shù)據(jù)服務(wù)等尖阔。其中,只有第7步是撮合引擎處理的榨咐。從單一職責(zé)原則來(lái)說(shuō)介却,撮合引擎就應(yīng)該只做一件事,那就是負(fù)責(zé)撮合訂單块茁。撮合之前的委托單持久化齿坷、凍結(jié)資金等,以及撮合之后生成K線(xiàn)數(shù)據(jù)等数焊,都不應(yīng)該屬于撮合引擎的職責(zé)永淌。
撮合競(jìng)價(jià)方式
撮合競(jìng)價(jià)方式一般有兩種,一是集合競(jìng)價(jià)佩耳,二是連續(xù)競(jìng)價(jià)遂蛀。股票交易系統(tǒng)一般會(huì)在不同交易時(shí)間段采用不同的競(jìng)價(jià)方式,比如在開(kāi)盤(pán)或收盤(pán)時(shí)采用集合競(jìng)價(jià)蚕愤,從而產(chǎn)生開(kāi)盤(pán)價(jià)或收盤(pán)價(jià)答恶,其余時(shí)間采用連續(xù)競(jìng)價(jià)。而大多數(shù)字貨幣交易系統(tǒng)則沒(méi)有集合競(jìng)價(jià)萍诱,只有連續(xù)競(jìng)價(jià),開(kāi)盤(pán)價(jià)一般是在開(kāi)始交易之前就設(shè)定好的污呼。
集合競(jìng)價(jià)
所謂集合競(jìng)價(jià)裕坊,是指對(duì)一段時(shí)間內(nèi)接收的買(mǎi)賣(mài)委托單一次性集中撮合的競(jìng)價(jià)方式。以深滬的股票交易系統(tǒng)為例燕酷,在每個(gè)交易日的 9:15~9:25 期間是集合競(jìng)價(jià)時(shí)間籍凝。在該時(shí)間段內(nèi),系統(tǒng)陸續(xù)接收到的委托單不會(huì)即時(shí)成交苗缩,而是先將所有委托單按照價(jià)格優(yōu)先饵蒂、時(shí)間優(yōu)先的原則排序,并在此基礎(chǔ)上酱讶,找出一個(gè)基準(zhǔn)價(jià)格退盯,使它能同時(shí)滿(mǎn)足以下三個(gè)條件:
- 可實(shí)現(xiàn)最大成交量的價(jià)格;
- 高于該價(jià)格的買(mǎi)單與低于該價(jià)格的賣(mài)單能全部成交的價(jià)格;
- 與該價(jià)格相同的買(mǎi)方或賣(mài)方至少有一方全部成交的價(jià)格渊迁。
在 9:25 分結(jié)束的時(shí)候慰照,該基準(zhǔn)價(jià)格就被確定為成交價(jià)格,所有高于該價(jià)格的買(mǎi)單與低于該價(jià)格的賣(mài)單都將以該價(jià)格成交琉朽。未能成交的委托單毒租,則自動(dòng)轉(zhuǎn)入連續(xù)競(jìng)價(jià)。
不過(guò)箱叁,如果滿(mǎn)足以上三個(gè)條件的價(jià)格存在兩個(gè)或兩個(gè)以上呢墅垮?對(duì)此,深交所和上交所的處理方案有所不同耕漱,深交所會(huì)取距前收盤(pán)價(jià)最近的價(jià)格為成交價(jià)噩斟,而上交所則取使未成交量最小的價(jià)格為成交價(jià),如果未成交量最小的價(jià)格仍不止一個(gè)孤个,則取中間價(jià)為成交價(jià)剃允。
集合競(jìng)價(jià)的主要目的就是為了確定開(kāi)盤(pán)價(jià)或收盤(pán)價(jià)。
連續(xù)競(jìng)價(jià)
所謂連續(xù)競(jìng)價(jià)齐鲤,也是我們所熟悉的競(jìng)價(jià)方式斥废,是指對(duì)買(mǎi)賣(mài)委托單逐筆連續(xù)撮合的競(jìng)價(jià)方式。用戶(hù)的掛單给郊,只要滿(mǎn)足成交條件牡肉,就能即時(shí)成交。而集合競(jìng)價(jià)淆九,則要等到最后一刻才會(huì)成交统锤。
連續(xù)競(jìng)價(jià)時(shí),依然要滿(mǎn)足價(jià)格優(yōu)先炭庙、時(shí)間優(yōu)先的成交原則:
- 價(jià)格優(yōu)先:買(mǎi)單則價(jià)格較高者能優(yōu)先成交饲窿,賣(mài)單則是價(jià)格較低者能優(yōu)先成交。
- 時(shí)間優(yōu)先:買(mǎi)賣(mài)方向和價(jià)格相同的委托單焕蹄,先申報(bào)的委托單會(huì)比后申報(bào)的委托單優(yōu)先成交逾雄。
另外,買(mǎi)入價(jià)必須大于或等于賣(mài)出價(jià)才能撮合成交腻脏。當(dāng)買(mǎi)入價(jià)等于賣(mài)出價(jià)時(shí)鸦泳,成交價(jià)就是買(mǎi)入價(jià)或賣(mài)出價(jià)。當(dāng)買(mǎi)入價(jià)大于賣(mài)出價(jià)時(shí)永品,則還要參考前一筆成交價(jià)來(lái)確定最新成交價(jià)做鹰。假設(shè)買(mǎi)入價(jià)為 B,賣(mài)出價(jià)為 S鼎姐,前一筆成交價(jià)為 P钾麸,最新成交價(jià)為 N更振,那么:
- 如果 P >= B,則 N = B
- 如果 P <= S喂走,則 N = S
- 如果 B > P > S殃饿,則 N = P
一套通用的撮合引擎應(yīng)該兩種競(jìng)價(jià)方式都支持,但對(duì)于同一交易標(biāo)的來(lái)說(shuō)芋肠,兩種競(jìng)價(jià)方式不能同時(shí)進(jìn)行乎芳,因此設(shè)計(jì)上需要考慮如何在兩種競(jìng)價(jià)方式之間切換,具體的實(shí)現(xiàn)思路在后續(xù)章節(jié)我們?cè)僬归_(kāi)來(lái)講帖池。
質(zhì)量需求
我們的撮合引擎除了要滿(mǎn)足以上所說(shuō)的功能需求奈惑,還應(yīng)該滿(mǎn)足一些質(zhì)量需求,尤其對(duì)可用性睡汹、可伸縮性和性能的要求較高肴甸。另外,為了達(dá)到通用囚巴,也要滿(mǎn)足可復(fù)用性的需求原在。
先說(shuō)下可復(fù)用性,我們期望的是該撮合引擎既能用于股票交易系統(tǒng)彤叉,也能用于數(shù)字貨幣交易系統(tǒng)庶柿,既能用于幣幣交易,也能用于合約交易秽浇。因此浮庐,該撮合引擎要避免引入與具體系統(tǒng)強(qiáng)相關(guān)的業(yè)務(wù)邏輯,以加強(qiáng)它的可復(fù)用性柬焕。
再看看性能审残,要衡量一個(gè)撮合引擎的性能,就看它處理每個(gè)交易對(duì)的 TPS 有多高斑举,即每秒鐘能處理多少筆相同交易對(duì)的委托單搅轿。以前,基于數(shù)據(jù)庫(kù)的撮合技術(shù)懂昂,TPS 一般只有10筆/秒介时。而現(xiàn)在基本都是采用內(nèi)存撮合技術(shù),TPS 很容易就能達(dá)到1000筆/秒凌彬,如果使用獨(dú)占的高性能服務(wù)器,1萬(wàn)筆/秒甚至更高的 TPS 都不難達(dá)到循衰。
接著談?wù)効缮炜s性铲敛,我們的每一個(gè)撮合引擎既可以同時(shí)處理多個(gè)交易標(biāo)的,也可以只處理單個(gè)交易標(biāo)的会钝。當(dāng)交易標(biāo)的和并發(fā)量增多的時(shí)候伐蒋,可以增加服務(wù)器工三,部署成撮合引擎集群,分別用來(lái)處理不同的交易標(biāo)的先鱼,從而能夠?qū)崿F(xiàn)負(fù)載均衡俭正。
最后聊聊可用性,高可用主要體現(xiàn)在兩點(diǎn)焙畔,一是故障率要低掸读,二是對(duì)故障維修的時(shí)間要短。要降低故障率宏多,那撮合引擎就需要有較高的健壯性儿惫,對(duì)于可能導(dǎo)致引擎出故障的各種異常情況要考慮好并設(shè)計(jì)好解決方案。另外伸但,還可以采用多機(jī)熱備份技術(shù)來(lái)提高可用性肾请,而且要保證互備服務(wù)器之間的數(shù)據(jù)一致,那就需要引入內(nèi)存狀態(tài)機(jī)復(fù)制方案更胖,實(shí)現(xiàn)上會(huì)復(fù)雜很多铛铁。
不過(guò),我們并非一下子就要達(dá)到很高的質(zhì)量要求却妨,因?yàn)橐笤礁叨穑浼軜?gòu)和實(shí)現(xiàn)會(huì)越復(fù)雜。我們可以先從簡(jiǎn)單的版本開(kāi)始管呵,然后不斷升級(jí)迭代梳毙。
小結(jié)
我們目的是實(shí)現(xiàn)一套通用的撮合引擎,要支持集合競(jìng)價(jià)和連續(xù)競(jìng)價(jià)捐下,還要實(shí)現(xiàn)一些質(zhì)量需求账锹,提高系統(tǒng)的可復(fù)用性、性能坷襟、可伸縮性奸柬、可用性等。后續(xù)章節(jié)會(huì)對(duì)這些需求不斷深入探討其設(shè)計(jì)與實(shí)現(xiàn)婴程。另外廓奕,我們將采用不斷升級(jí)迭代的方式來(lái)設(shè)計(jì)和實(shí)現(xiàn)多個(gè)版本的撮合引擎。
留兩個(gè)思考題:
- 集合競(jìng)價(jià)結(jié)束的時(shí)候档叔,如果不存在符合那三個(gè)條件的基準(zhǔn)價(jià)格桌粉,那開(kāi)盤(pán)價(jià)又將如何確定?
- 對(duì)于單個(gè)交易對(duì)衙四,是否可通過(guò)橫向增加服務(wù)器的方式提高其性能铃肯?