Skip to content

Commit 4d61f59

Browse files
author
Chris Busbey
committed
ordermatch
1 parent cab534f commit 4d61f59

File tree

6 files changed

+472
-0
lines changed

6 files changed

+472
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22

33
* TradeClient is a simple console based trading client
44
* Executor is a server that fills every limit order it receives
5+
* OrderMatch is a primitive matching engine
56

67
All examples have been ported from [QuickFIX](http://quickfixengine.org)

config/ordermatch.cfg

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
[DEFAULT]
2+
SocketAcceptPort=5001
3+
SenderCompID=ISLD
4+
TargetCompID=TW
5+
ResetOnLogon=Y
6+
FileLogPath=tmp
7+
8+
[SESSION]
9+
BeginString=FIX.4.0
10+
11+
[SESSION]
12+
BeginString=FIX.4.1
13+
14+
[SESSION]
15+
BeginString=FIX.4.2
16+
17+
[SESSION]
18+
BeginString=FIX.4.3
19+
20+
[SESSION]
21+
BeginString=FIX.4.4
22+
23+
[SESSION]
24+
BeginString=FIXT.1.1
25+
DefaultApplVerID=7

ordermatch/market.go

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"github.com/quickfixgo/quickfix/enum"
6+
"sort"
7+
"time"
8+
)
9+
10+
type orderList struct {
11+
orders []*Order
12+
sortBy func(o1, o2 *Order) bool
13+
}
14+
15+
func (l orderList) Len() int { return len(l.orders) }
16+
func (l orderList) Swap(i, j int) {
17+
l.orders[i], l.orders[j] = l.orders[j], l.orders[i]
18+
}
19+
func (l orderList) Less(i, j int) bool { return l.sortBy(l.orders[i], l.orders[j]) }
20+
21+
func (l *orderList) Insert(order *Order) {
22+
l.orders = append(l.orders, order)
23+
sort.Sort(l)
24+
}
25+
26+
func (l *orderList) Remove(clordID string) (order *Order) {
27+
for i := 0; i < len(l.orders); i++ {
28+
if l.orders[i].ClOrdID == clordID {
29+
order = l.orders[i]
30+
l.orders = append(l.orders[:i], l.orders[i+1:]...)
31+
return
32+
}
33+
}
34+
35+
return
36+
}
37+
38+
func bids() (b orderList) {
39+
b.sortBy = func(i, j *Order) bool {
40+
if i.Price > j.Price {
41+
return true
42+
} else if i.Price < j.Price {
43+
return false
44+
}
45+
46+
return i.insertTime.Before(j.insertTime)
47+
}
48+
49+
return
50+
}
51+
52+
func offers() (o orderList) {
53+
o.sortBy = func(i, j *Order) bool {
54+
if i.Price < j.Price {
55+
return true
56+
} else if i.Price < j.Price {
57+
return false
58+
}
59+
60+
return i.insertTime.Before(j.insertTime)
61+
}
62+
63+
return
64+
}
65+
66+
//Market is a simple CLOB
67+
type Market struct {
68+
Bids orderList
69+
Offers orderList
70+
}
71+
72+
//NewMarket returns an initialized Market instance
73+
func NewMarket() *Market {
74+
return &Market{bids(), offers()}
75+
}
76+
77+
func (m Market) Display() {
78+
fmt.Println("BIDS:")
79+
fmt.Println("-----")
80+
fmt.Println()
81+
82+
for _, bid := range m.Bids.orders {
83+
fmt.Printf("%+v\n", bid)
84+
}
85+
86+
fmt.Println()
87+
fmt.Println("OFFERS:")
88+
89+
for _, offer := range m.Offers.orders {
90+
fmt.Printf("%+v\n", offer)
91+
}
92+
}
93+
94+
func (m *Market) Insert(order Order) {
95+
order.insertTime = time.Now()
96+
if order.Side == enum.Side_BUY {
97+
m.Bids.Insert(&order)
98+
} else {
99+
m.Offers.Insert(&order)
100+
}
101+
}
102+
func (m *Market) Cancel(clordID, side string) (order *Order) {
103+
if side == enum.Side_BUY {
104+
order = m.Bids.Remove(clordID)
105+
} else {
106+
order = m.Offers.Remove(clordID)
107+
}
108+
109+
if order != nil {
110+
order.Cancel()
111+
}
112+
113+
return
114+
}
115+
116+
func (m *Market) Match() (matched []Order) {
117+
for m.Bids.Len() > 0 && m.Offers.Len() > 0 {
118+
bestBid := m.Bids.orders[0]
119+
bestOffer := m.Offers.orders[0]
120+
121+
price := bestOffer.Price
122+
quantity := bestBid.OpenQuantity()
123+
if offerQuant := bestOffer.OpenQuantity(); offerQuant < quantity {
124+
quantity = offerQuant
125+
}
126+
127+
bestBid.Execute(price, quantity)
128+
bestOffer.Execute(price, quantity)
129+
130+
matched = append(matched, *bestBid, *bestOffer)
131+
132+
if bestBid.IsClosed() {
133+
m.Bids.orders = m.Bids.orders[1:]
134+
}
135+
136+
if bestOffer.IsClosed() {
137+
m.Offers.orders = m.Offers.orders[1:]
138+
}
139+
}
140+
141+
return
142+
}

ordermatch/order.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package main
2+
3+
import (
4+
"time"
5+
)
6+
7+
type Order struct {
8+
ClOrdID string
9+
Symbol string
10+
SenderCompID string
11+
TargetCompID string
12+
Side string
13+
OrdType string
14+
Price float64
15+
Quantity float64
16+
ExecutedQuantity float64
17+
openQuantity *float64
18+
AvgPx float64
19+
insertTime time.Time
20+
}
21+
22+
func (o Order) IsClosed() bool {
23+
return o.OpenQuantity() == 0
24+
}
25+
26+
func (o Order) OpenQuantity() float64 {
27+
if o.openQuantity == nil {
28+
return o.Quantity - o.ExecutedQuantity
29+
}
30+
31+
return *o.openQuantity
32+
}
33+
34+
func (o *Order) Execute(price, quantity float64) {
35+
o.ExecutedQuantity += quantity
36+
}
37+
38+
func (o *Order) Cancel() {
39+
openQuantity := float64(0)
40+
o.openQuantity = &openQuantity
41+
}

0 commit comments

Comments
 (0)