-
-
Notifications
You must be signed in to change notification settings - Fork 272
/
orderstore.go
171 lines (133 loc) · 3.45 KB
/
orderstore.go
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
package core
import (
"sync"
"github.com/c9s/bbgo/pkg/types"
)
type OrderStore struct {
// any created orders for tracking trades
mu sync.Mutex
orders map[uint64]types.Order
Symbol string
// RemoveCancelled removes the canceled order when receiving a cancel order update event
// It also removes the order even if it's partially filled
// by default, only 0 filled canceled order will be removed.
RemoveCancelled bool
// RemoveFilled removes the fully filled order when receiving a filled order update event
RemoveFilled bool
// AddOrderUpdate adds the order into the store when receiving an order update when the order does not exist in the current store.
AddOrderUpdate bool
C chan types.Order
}
func NewOrderStore(symbol string) *OrderStore {
return &OrderStore{
Symbol: symbol,
orders: make(map[uint64]types.Order),
C: make(chan types.Order),
}
}
func (s *OrderStore) AllFilled() bool {
s.mu.Lock()
defer s.mu.Unlock()
// If any order is new or partially filled, we return false
for _, o := range s.orders {
switch o.Status {
case types.OrderStatusCanceled, types.OrderStatusRejected:
continue
case types.OrderStatusNew, types.OrderStatusPartiallyFilled:
return false
case types.OrderStatusFilled:
// do nothing for the filled order
}
}
// If we pass through the for loop, then all the orders filled
return true
}
func (s *OrderStore) NumOfOrders() (num int) {
s.mu.Lock()
num = len(s.orders)
s.mu.Unlock()
return num
}
func (s *OrderStore) Orders() (orders []types.Order) {
s.mu.Lock()
defer s.mu.Unlock()
for _, o := range s.orders {
orders = append(orders, o)
}
return orders
}
func (s *OrderStore) Exists(oID uint64) (ok bool) {
s.mu.Lock()
defer s.mu.Unlock()
_, ok = s.orders[oID]
return ok
}
// Get a single order from the order store by order ID
// Should check ok to make sure the order is returned successfully
func (s *OrderStore) Get(oID uint64) (order types.Order, ok bool) {
s.mu.Lock()
defer s.mu.Unlock()
order, ok = s.orders[oID]
return order, ok
}
func (s *OrderStore) Add(orders ...types.Order) {
s.mu.Lock()
defer s.mu.Unlock()
for _, o := range orders {
old, ok := s.orders[o.OrderID]
if ok && o.Tag == "" && old.Tag != "" {
o.Tag = old.Tag
}
s.orders[o.OrderID] = o
}
}
func (s *OrderStore) Remove(o types.Order) {
s.mu.Lock()
defer s.mu.Unlock()
delete(s.orders, o.OrderID)
}
func (s *OrderStore) Update(o types.Order) bool {
s.mu.Lock()
defer s.mu.Unlock()
old, ok := s.orders[o.OrderID]
if ok {
o.Tag = old.Tag
s.orders[o.OrderID] = o
}
return ok
}
func (s *OrderStore) BindStream(stream types.Stream) {
hasSymbol := s.Symbol != ""
stream.OnOrderUpdate(func(order types.Order) {
// if we have symbol defined, we should filter out the orders that we are not interested in
if hasSymbol && order.Symbol != s.Symbol {
return
}
s.HandleOrderUpdate(order)
})
}
func (s *OrderStore) HandleOrderUpdate(order types.Order) {
switch order.Status {
case types.OrderStatusNew, types.OrderStatusPartiallyFilled, types.OrderStatusFilled:
if s.AddOrderUpdate {
s.Add(order)
} else {
s.Update(order)
}
if s.RemoveFilled && order.Status == types.OrderStatusFilled {
s.Remove(order)
}
case types.OrderStatusCanceled:
if s.RemoveCancelled {
s.Remove(order)
} else if order.ExecutedQuantity.IsZero() {
s.Remove(order)
}
case types.OrderStatusRejected:
s.Remove(order)
}
select {
case s.C <- order:
default:
}
}