欢迎关注「Keegan小钢」公众号获取更多文章缓存
撮合引擎开发:开篇数据结构
撮合引擎开发:数据结构设计post
撮合引擎开发:完结篇code
本小节是该系列文章的最后一篇了,将讲解剩下的一些东西,包括交易委托帐本中订单队列的实现逻辑、更多订单类型的实现逻辑。另外,很多朋友在问,完结后全部代码是否会开源放上 Github?我只能说,长期大几率会开源,但短时间内还没打算开源。
交易委托帐本其实就是由两个订单队列组成的,一个买单队列,一个卖单队列。任何对交易委托帐本的查询和操做,实际上都是查询和操做这两个队列。订单队列的设计也直接影响了撮合的性能,前面文章讲数据结构设计时也有简单聊了订单队列的设计,咱们主要是用二维连接结合 Map 来保存全部订单的,依赖的是 container/list 包。
订单队列的结构体以下:
type orderQueue struct {
sortBy enum.SortDirection
parentList *list.List
elementMap map[string]*list.Element
}
复制代码
sortBy 指订价格排序的方向,买单队列是降序的,而卖单队列则是升序的。parentList 保存整个二维链表的全部订单,第一维以价格排序,第二维以时间排序。elementMap 则是 Key 为价格、Value 为第二维订单链表的键值对。
初始化函数就比较简单了,对几个字段赋值而已,代码以下:
func (q *orderQueue) init(sortBy enum.SortDirection) {
q.sortBy = sortBy
q.parentList = list.New()
q.elementMap = make(map[string]*list.Element)
}
复制代码
除了初始化函数,还提供了另外五个函数:
以上五个函数就只有第一个函数会比较复杂,为了让处理流程更容易理解,我就不贴代码了,画一个完整的流程图给你们看看:
这个流程确实有一点复杂,能够多看几遍好好消化,最好本身动手将其转为代码实现。
其余几个函数就简单了,关于最后一个函数须要补充说明一下。读取深度价格是为了方便处理 market-opponent、market-top五、market-top10 等类型的订单时判断上限价格。请看该函数的代码以理解该函数的逻辑和用法:
func (q *orderQueue) getDepthPrice(depth int) (string, int) {
if q.parentList.Len() == 0 {
return "", 0
}
p := q.parentList.Front()
i := 1
for ; i < depth; i++ {
t := p.Next()
if t != nil {
p = t
} else {
break;
}
}
o := p.Value.(*list.List).Front().Value.(*Order)
return o.Price.String(), i
}
复制代码
咱们引擎总共支持了六种订单类型,以前的文章有简单介绍过,但没有深刻讲解这几种不一样类型的具体业务逻辑应该是怎样的,所以,在此将这部份内容补充上。
普通限价是最简单的,前文也已经展现过代码实现,为了加深理解,我再给你们画一张图:
处理逻辑就是:
IOC 限价与普通限价不一样的地方只有一个,若是新订单和头部订单不匹配时,普通限价单会被添加到订单队列中,而 IOC 限价则是做撤单处理,请看下图:
默认市价单的逻辑也比较简单,它不须要判断价格,只要头部订单不为空,就直接和头部订单匹配成交,其处理逻辑以下图:
最优五档/十档市价单与默认市价单的逻辑也是相似的,不一样点在于:默认市价的成交价格没有上限或下限,但最优五档/十档市价则存在价格上限或下限,超过上下限的委托单不会成交。画图太累,仍是直接贴代码吧,如下是处理买单的:
func dealBuyMarketTop(order *Order, book *orderBook, lastTradePrice *decimal.Decimal, depth int) {
priceStr, _ := book.getSellDepthPrice(depth)
if priceStr == "" {
cancelOrder(order)
return
}
limitPrice, _ := decimal.NewFromString(priceStr)
LOOP:
headOrder := book.getHeadSellOrder()
if headOrder != nil && limitPrice.GreaterThanOrEqual(headOrder.Price) {
matchTrade(headOrder, order, book, lastTradePrice)
if order.Amount.IsPositive() {
goto LOOP
}
} else {
cancelOrder(order)
}
}
复制代码
最后一种类型,对手方最优价,该类型只与对手方一档的价位成交,但与最优五档/十档还有一点不同:最优五档/十档未成交的部分是做撤单处理的,而对手方最优价最后未成交的部分则是转为限价单。请看代码:
func dealBuyMarketOpponent(order *Order, book *orderBook, lastTradePrice *decimal.Decimal) {
priceStr, _ := book.getSellDepthPrice(1)
if priceStr == "" {
cancelOrder(order)
return
}
limitPrice, _ := decimal.NewFromString(priceStr)
LOOP:
headOrder := book.getHeadSellOrder()
if headOrder != nil && limitPrice.GreaterThanOrEqual(headOrder.Price) {
matchTrade(headOrder, order, book, lastTradePrice)
if order.Amount.IsPositive() {
goto LOOP
}
} else {
order.Price = limitPrice
order.Type = enum.TypeLimit
book.addBuyOrder(order)
cache.UpdateOrder(order.ToMap())
log.Info("engine %s, a order has added to the orderbook: %s", order.Symbol, order.ToJson())
}
}
复制代码
至此,整个系列就此完结。不过,个人撮合程序依然会继续迭代升级,另外,也将开始开发其余组件,将会和当前这个撮合引擎结合来用。欢迎关注后续动态。
扫描如下二维码便可关注公众号(公众号名称:Keegan小钢)