Skip to content

Commit 05fddfe

Browse files
committed
Merge branch 'release/2019.1.5'
2 parents 5563d00 + ced5e08 commit 05fddfe

File tree

4 files changed

+69
-49
lines changed

4 files changed

+69
-49
lines changed

.travis.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ language: python
33
python:
44
- 2.7
55
- 3.4
6+
- 3.5
7+
- 3.6
8+
- 3.7
69
- pypy
710

811
install:
912
- pip install .[performance]
1013

1114
script:
1215
- make test
13-
- python tests/performance/performance_test.py --freq 20
16+
- python tests/performance/performance_test.py --freq 20 --num-orders 500

lightmatchingengine/lightmatchingengine.py renamed to lightmatchingengine/lightmatchingengine.pyx

Lines changed: 56 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,18 @@
11
#!/usr/bin/python3
2-
class Side(object):
3-
"""
4-
Side
5-
"""
2+
cpdef enum Side:
63
BUY = 1
74
SELL = 2
85

9-
class OrderBook(object):
10-
"""
11-
Order book
12-
"""
13-
def __init__(self):
14-
"""
15-
Constructor
16-
"""
17-
self.bids = {}
18-
self.asks = {}
19-
self.order_id_map = {}
206

7+
cdef class Order:
8+
cdef public int order_id
9+
cdef public str instmt
10+
cdef public double price
11+
cdef public double qty
12+
cdef public double cum_qty
13+
cdef public double leaves_qty
14+
cdef public Side side
2115

22-
class Order(object):
23-
"""
24-
Order
25-
"""
2616
def __init__(self, order_id, instmt, price, qty, side):
2717
"""
2818
Constructor
@@ -36,10 +26,28 @@ def __init__(self, order_id, instmt, price, qty, side):
3626
self.side = side
3727

3828

39-
class Trade(object):
40-
"""
41-
Trade
42-
"""
29+
cdef class OrderBook:
30+
cdef public dict bids
31+
cdef public dict asks
32+
cdef public dict order_id_map
33+
34+
def __init__(self):
35+
"""
36+
Constructor
37+
"""
38+
self.bids = {}
39+
self.asks = {}
40+
self.order_id_map = {}
41+
42+
43+
cdef class Trade:
44+
cdef public int order_id
45+
cdef public str instmt
46+
cdef public double trade_price
47+
cdef public double trade_qty
48+
cdef public Side trade_side
49+
cdef public int trade_id
50+
4351
def __init__(self, order_id, instmt, trade_price, trade_qty, trade_side, trade_id):
4452
"""
4553
Constructor
@@ -52,10 +60,11 @@ def __init__(self, order_id, instmt, trade_price, trade_qty, trade_side, trade_i
5260
self.trade_id = trade_id
5361

5462

55-
class LightMatchingEngine(object):
56-
"""
57-
Light matching engine
58-
"""
63+
cdef class LightMatchingEngine:
64+
cdef public dict order_books
65+
cdef public int curr_order_id
66+
cdef public int curr_trade_id
67+
5968
def __init__(self):
6069
"""
6170
Constructor
@@ -64,7 +73,7 @@ def __init__(self):
6473
self.curr_order_id = 0
6574
self.curr_trade_id = 0
6675

67-
def add_order(self, instmt, price, qty, side):
76+
cpdef add_order(self, str instmt, double price, double qty, Side side):
6877
"""
6978
Add an order
7079
:param instmt Instrument name
@@ -74,14 +83,17 @@ def add_order(self, instmt, price, qty, side):
7483
:return The order and the list of trades.
7584
Empty list if there is no matching.
7685
"""
86+
cdef list trades = []
87+
cdef int order_id
88+
cdef Order order
89+
7790
assert side == Side.BUY or side == Side.SELL, \
7891
"Invalid side %s" % side
7992

8093
# Locate the order book
8194
order_book = self.order_books.setdefault(instmt, OrderBook())
8295

8396
# Initialization
84-
trades = []
8597
self.curr_order_id += 1
8698
order_id = self.curr_order_id
8799
order = Order(order_id, instmt, price, qty, side)
@@ -92,7 +104,7 @@ def add_order(self, instmt, price, qty, side):
92104
else None
93105
while best_price is not None and \
94106
(price == 0.0 or price >= best_price ) and \
95-
order.leaves_qty > 0:
107+
order.leaves_qty >= 1e-9:
96108
best_price_qty = sum([ask.leaves_qty for ask in order_book.asks[best_price]])
97109
match_qty = min(best_price_qty, order.leaves_qty)
98110
assert match_qty > 0, "Match quantity must be larger than zero"
@@ -105,7 +117,7 @@ def add_order(self, instmt, price, qty, side):
105117
Side.BUY, self.curr_trade_id))
106118

107119
# Generate the passive executions
108-
while match_qty > 0:
120+
while match_qty >= 1e-9:
109121
# The order hit
110122
hit_order = order_book.asks[best_price][0]
111123
# The order quantity hit
@@ -117,7 +129,7 @@ def add_order(self, instmt, price, qty, side):
117129
hit_order.cum_qty += order_match_qty
118130
hit_order.leaves_qty -= order_match_qty
119131
match_qty -= order_match_qty
120-
if hit_order.leaves_qty == 0:
132+
if hit_order.leaves_qty < 1e-9:
121133
del order_book.asks[best_price][0]
122134

123135
# If the price does not have orders, delete the particular price depth
@@ -129,7 +141,7 @@ def add_order(self, instmt, price, qty, side):
129141
else None
130142

131143
# Add the remaining order into the depth
132-
if order.leaves_qty > 0:
144+
if order.leaves_qty > 0.0:
133145
depth = order_book.bids.setdefault(price, [])
134146
depth.append(order)
135147
order_book.order_id_map[order_id] = order
@@ -139,10 +151,10 @@ def add_order(self, instmt, price, qty, side):
139151
else None
140152
while best_price is not None and \
141153
(price == 0.0 or price <= best_price) and \
142-
order.leaves_qty > 0:
154+
order.leaves_qty >= 1e-9:
143155
best_price_qty = sum([bid.leaves_qty for bid in order_book.bids[best_price]])
144156
match_qty = min(best_price_qty, order.leaves_qty)
145-
assert match_qty > 0, "Match quantity must be larger than zero"
157+
assert match_qty >= 1e-9, "Match quantity must be larger than zero"
146158

147159
# Generate aggressive order trade first
148160
self.curr_trade_id += 1
@@ -152,7 +164,7 @@ def add_order(self, instmt, price, qty, side):
152164
Side.SELL, self.curr_trade_id))
153165

154166
# Generate the passive executions
155-
while match_qty > 0:
167+
while match_qty >= 1e-9:
156168
# The order hit
157169
hit_order = order_book.bids[best_price][0]
158170
# The order quantity hit
@@ -164,7 +176,7 @@ def add_order(self, instmt, price, qty, side):
164176
hit_order.cum_qty += order_match_qty
165177
hit_order.leaves_qty -= order_match_qty
166178
match_qty -= order_match_qty
167-
if hit_order.leaves_qty == 0:
179+
if hit_order.leaves_qty < 1e-9:
168180
del order_book.bids[best_price][0]
169181

170182
# If the price does not have orders, delete the particular price depth
@@ -176,20 +188,25 @@ def add_order(self, instmt, price, qty, side):
176188
else None
177189

178190
# Add the remaining order into the depth
179-
if order.leaves_qty > 0:
191+
if order.leaves_qty >= 1e-9:
180192
depth = order_book.asks.setdefault(price, [])
181193
depth.append(order)
182194
order_book.order_id_map[order_id] = order
183195

184196
return order, trades
185197

186-
def cancel_order(self, order_id, instmt):
198+
cpdef cancel_order(self, int order_id, str instmt):
187199
"""
188200
Cancel order
189201
:param order_id Order ID
190202
:param instmt Instrument
191203
:return The order if the cancellation is successful
192204
"""
205+
cdef Order order
206+
cdef double order_price
207+
cdef Side side
208+
cdef int index
209+
193210
assert instmt in self.order_books.keys(), \
194211
"Instrument %s is not valid in the order book" % instmt
195212
order_book = self.order_books[instmt]
@@ -238,6 +255,3 @@ def cancel_order(self, order_id, instmt):
238255
order.leaves_qty = 0
239256

240257
return order
241-
242-
243-

setup.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from setuptools import setup, find_packages
1+
from setuptools import setup, find_packages, Extension
22

33
setup(
44
name="lightmatchingengine",
@@ -14,12 +14,15 @@
1414

1515
use_scm_version=True,
1616
install_requires=[],
17-
setup_requires=['setuptools_scm'],
17+
setup_requires=['setuptools_scm', 'cython'],
18+
ext_modules=[Extension(
19+
'lightmatchingengine.lightmatchingengine',
20+
['lightmatchingengine/lightmatchingengine.pyx'])],
1821
tests_require=[
1922
'pytest'
2023
],
21-
extra_requires={
22-
'performance': ['pandas']
24+
extras_require={
25+
'performance': ['pandas', 'docopt', 'tabulate', 'tqdm']
2326
},
2427

2528
classifiers=[

tests/performance/performance_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,13 @@ def run(args):
7979
order.order_id, order.side, order.price, order.qty)
8080

8181
# Save the order if there is any quantity left
82-
if order.leaves_qty > 0:
82+
if order.leaves_qty > 0.0:
8383
orders[order.order_id] = order
8484

8585
# Remove the trades
8686
for trade in trades:
8787
if (trade.order_id != order.order_id and
88-
orders[trade.order_id].leaves_qty == 0.0):
88+
orders[trade.order_id].leaves_qty < 1e-9):
8989
del orders[trade.order_id]
9090

9191
# Save the statistics

0 commit comments

Comments
 (0)