/
orders.go
188 lines (169 loc) · 4.39 KB
/
orders.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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
package orders
import (
"container/ring"
"errors"
"github.com/insan1k/proto-order-data/internal/domain/order"
"time"
)
const (
initialOrdersTooBigErr = "cannot allocate amount of orders greater then ring capacity, " +
"allocate an empty Orders and populate it with another operation, or increase size of ring"
emptyOrders = "orders struct is currently empty"
)
//Orders implements a type that holds multiple orders data... this could be the bids of an orders
// book or the X latest orders this structure implies that orders are sorted in a useful manner
type Orders struct {
asset string
ring *ring.Ring
first *Element
last *Element
maxSize int
currentSize int
}
//Asset returns the asset of these current orders
func (o Orders) Asset() string {
return o.asset
}
//NewOrders receives a list of orders and returns them in the Orders struct
func NewOrders(asset string, maxSize int, orders ...*order.Order) (o Orders, err error) {
o.asset = asset
if len(orders) > maxSize {
err = errors.New(initialOrdersTooBigErr)
return
}
o.ring = ring.New(maxSize)
o.maxSize = maxSize
isFirst := true
isLast := false
for index := 0; index < maxSize; index++ {
o.ring.Value = &Element{
Index: index,
Order: nil,
tags: nil,
}
o.next()
}
o.moveToFirst()
for c, currentOrder := range orders {
if c == len(orders)-1 {
isLast = true
}
if isFirst {
o.first = o.get()
o.first.SetTags(First)
isFirst = false
}
if isLast {
o.last = o.get()
o.last.SetTags(Last)
isLast = false
}
o.currentSize = c
o.set(currentOrder)
o.ring.Next()
}
return
}
//First returns a copy of the first order
func (o Orders) First() (order.Order, error) {
if o.currentSize == 0 {
return order.EmptyOrder(), errors.New(emptyOrders)
}
return *o.first.Order, nil
}
//Last returns a copy of the last order
func (o Orders) Last() (order.Order, error) {
if o.currentSize == 0 {
return order.EmptyOrder(), errors.New(emptyOrders)
}
return *o.last.Order, nil
}
//TimeStart returns the time we received the first order
func (o Orders) TimeStart() time.Time {
return o.first.Order.Inf.Seen()
}
//TimeEnd returns the time we received the last order
func (o Orders) TimeEnd() time.Time {
return o.last.Order.Inf.Seen()
}
//TimePeriod returns the time period of orders inside
func (o Orders) TimePeriod() time.Duration {
return o.TimeEnd().Sub(o.TimeStart())
}
//Cap returns the maximum capacity of this Orders struct
func (o Orders) Cap() (i int) {
return o.maxSize
}
//Len returns the utilized capacity of this Orders struct
func (o Orders) Len() (i int) {
return o.currentSize
}
func (o *Orders) next() {
o.ring = o.ring.Next()
}
func (o *Orders) get() *Element {
return (o.ring.Value).(*Element)
}
func (o *Orders) set(order *order.Order) {
this := o.get()
this.Order = order
// notice that when setting the element we remove it's tags this is due to the fact that the tags of an element
// may no longer make sense after setting it
this.tags = nil
}
//Insert adds order.Order into ring
func (o *Orders) Insert(order *order.Order) {
//check if we're operating under an empty Orders ring
if o.currentSize == 0 {
o.set(order)
o.first = o.get()
o.last = o.get()
o.first.SetTags(First)
o.last.SetTags(Last)
o.currentSize++
return
}
//otherwise we go straight to the last
o.moveToLast()
//remove it's tag as the lest element
o.last.RemoveTag(Last)
// the next element will be the order we're inserting, so we go there
o.next()
// we set it's order reference
o.set(order)
//it will also be the new last, so we update it's reference
o.last = o.get()
//and we set it's tag
o.last.SetTags(Last)
//if our orders are already at cap we need to move the first as well
if o.currentSize == o.maxSize {
// the first one will be the element immediately after the new last, so we go there
o.next()
// we update our reference to first
o.first = o.get()
// and we set it's tag
o.first.SetTags(First)
} else {
//otherwise we increment the current size until we're at capacity
o.currentSize++
}
}
func (o *Orders) moveToLast() {
var lastIndex int
if o.currentSize == 0 {
lastIndex = 0
} else {
lastIndex = o.last.Index
}
// figure out where we are in the ring
e := o.get()
if e.Index-lastIndex != 0 {
//go to the last element
o.ring = o.ring.Move(lastIndex - e.Index)
}
}
func (o *Orders) moveToFirst() {
o.moveToLast()
o.next()
}
//todo: sorting functions