概述
撮合引擎是交易系统中的核心组件,负责实现买卖订单的成交。它通过价格优先、时间优先的原则,确保买单价格大于等于卖单价格时双方成交。
关键概念
- 撮合交易:允许买卖双方各自提交订单并报价,按价格和时间优先级顺序成交。
- 价格优先、时间优先:买盘价格从高到低排序,卖盘价格从低到高排序。
- 订单:包含价格、数量、方向(买或卖)等信息。
- 订单簿(OrderBook):存储买卖盘信息,确保订单按价格排序,好的订单簿结构设计是撮合效率的一个关键,这里采用
红黑树
实现,因为它是一种自平衡的二叉排序树,插入和删除的效率都是O(logN)。
订单类设计
Go语言实现订单类Order
如下:
1
2
3
4
5
6
7
8
9
10
|
import (
"github.com/shopspring/decimal"
)
type Order struct {
SequenceId int64
Direction Direction
Price decimal.Decimal
Amount decimal.Decimal
}
|
订单方向枚举
Go语言实现订单方向枚举如下:
1
2
3
4
5
6
|
type Direction int
const (
BUY Direction = iota
SELL
)
|
订单簿(OrderBook)设计
使用红黑树实现,这里引入了github.com/emirpasic/gods这个包。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
import (
"github.com/emirpasic/gods/trees/redblacktree"
"github.com/emirpasic/gods/utils"
"github.com/shopspring/decimal"
)
type OrderKey struct {
Price decimal.Decimal
Id int64
}
type OrderBook struct {
*redblacktree.Tree
}
func NewOrderBook(direction Direction) *OrderBook {
switch direction {
case BUY:
return &OrderBook{Tree: redblacktree.NewWith(BuyComparator)}
case SELL:
return &OrderBook{Tree: redblacktree.NewWith(SellComparator)}
}
return nil
}
func BuyComparator(a, b interface{}) int {
order1 := a.(OrderKey)
order2 := b.(OrderKey)
cmp := order2.Price.Cmp(order1.Price)
if cmp == 0 {
return utils.Int64Comparator(order1.Id, order2.Id)
}
return cmp
}
func SellComparator(a, b interface{}) int {
order1 := a.(OrderKey)
order2 := b.(OrderKey)
cmp := order1.Price.Cmp(order2.Price)
if cmp == 0 {
return utils.Int64Comparator(order1.Id, order2.Id)
}
return cmp
}
func (b *OrderBook) Add(order *Order) {
b.Tree.Put(OrderKey{Price: order.Price, Id: order.SequenceId}, order)
}
func (b *OrderBook) Remove(order *Order) {
b.Tree.Remove(OrderKey{Price: order.Price, Id: order.SequenceId})
}
func (b *OrderBook) GetFirst() *Order {
iterator := b.Tree.Iterator()
if iterator.First() {
return iterator.Value().(*Order)
}
return nil
}
func (b *OrderBook) GetLast() *Order {
iterator := b.Tree.Iterator()
if iterator.Last() {
return iterator.Value().(*Order)
}
return nil
}
|
撮合引擎(MatchEngine)设计
撮合引擎包含买盘和卖盘,以及最新成交价。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
import (
"github.com/shopspring/decimal"
)
type MatchEngine struct {
buyBook *OrderBook
sellBook *OrderBook
marketPrice decimal.Decimal
}
func NewMatchEngine() *MatchEngine {
return &MatchEngine{
buyBook: NewOrderBook(BUY),
sellBook: NewOrderBook(SELL),
marketPrice: decimal.Zero,
}
}
func (me *MatchEngine) ProcessOrder(order *Order) *MatchResult {
switch order.Direction {
case BUY:
return me.processOrder(order, me.sellBook, me.buyBook)
case SELL:
return me.processOrder(order, me.buyBook, me.sellBook)
default:
panic("Invalid direction")
}
}
|
处理订单逻辑
处理订单时,尝试在对手盘匹配合适的订单,如果匹配成功则成交,否则将订单放入订单簿。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
func (me *MatchEngine) processOrder(takerOrder *Order, makerBook, anotherBook *OrderBook) *MatchResult {
result := &MatchResult{TakerOrder: takerOrder}
for {
makerOrder := makerBook.GetFirst()
if makerOrder == nil {
break
}
if takerOrder.Direction == BUY && takerOrder.Price.Cmp(makerOrder.Price) < 0 {
break
} else if takerOrder.Direction == SELL && takerOrder.Price.Cmp(makerOrder.Price) > 0 {
break
}
me.marketPrice = makerOrder.Price
matchedAmount := decimal.Min(takerOrder.Amount, makerOrder.Amount)
result.MatchRecords = append(result.MatchRecords, &MatchRecord{Price: makerOrder.Price, Amount: matchedAmount, TakerOrder: takerOrder, MakerOrder: makerOrder})
takerOrder.Amount = takerOrder.Amount.Sub(matchedAmount)
makerOrder.Amount = makerOrder.Amount.Sub(matchedAmount)
if makerOrder.Amount.Sign() == 0 {
makerBook.Remove(makerOrder)
}
if takerOrder.Amount.Sign() == 0 {
break
}
}
if takerOrder.Amount.Sign() > 0 {
anotherBook.Add(takerOrder)
}
return result
}
|
撮合结果(MatchResult)和撮合记录(MatchRecord)设计
撮合结果记录撮合匹配记录,包括成交价格、数量等信息。
1
2
3
4
5
6
7
8
9
10
11
|
type MatchResult struct {
TakerOrder *Order
MatchRecords []*MatchRecord
}
type MatchRecord struct {
Price decimal.Decimal
Amount decimal.Decimal
TakerOrder *Order
MakerOrder *Order
}
|
测试撮合引擎
可以通过构造一系列订单并输入到撮合引擎来验证其功能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
func TestMatchEngine(t *testing.T) {
me := NewMatchEngine()
orders := []*Order{
{SequenceId: 1, Direction: BUY, Price: decimal.NewFromFloat(2082.34), Amount: decimal.NewFromFloat(1)},
{SequenceId: 2, Direction: SELL, Price: decimal.NewFromFloat(2087.6), Amount: decimal.NewFromFloat(2)},
{SequenceId: 3, Direction: BUY, Price: decimal.NewFromFloat(2088), Amount: decimal.NewFromFloat(1)},
{SequenceId: 4, Direction: SELL, Price: decimal.NewFromFloat(2082), Amount: decimal.NewFromFloat(1)},
// 添加更多订单...
}
for _, order := range orders {
result := me.ProcessOrder(order)
// 打印或处理撮合结果...
log.Printf("takeOrder: %+v, matchRecords: %+v", result.TakerOrder, result.MatchRecords)
}
}
|
小结
实现撮合引擎的关键在于设计合理的数据结构来高效管理订单,并正确实现撮合逻辑。在实际应用中,还需要考虑更多的因素,如并发处理、订单的时效性、手续费计算等,以满足交易所的实际业务需求。同时,要不断进行测试和优化,确保撮合引擎的性能和稳定性。
参考文献:
https://liaoxuefeng.com/blogs/all/2021-12-10-exchange-match-engine/index.html