-
Notifications
You must be signed in to change notification settings - Fork 99
/
order_id.go
170 lines (146 loc) · 5.36 KB
/
order_id.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
package types
import (
errorsmod "cosmossdk.io/errors"
"fmt"
"sort"
gometrics "github.com/armon/go-metrics"
"github.com/dydxprotocol/v4-chain/protocol/lib/metrics"
)
const (
OrderIdFlags_ShortTerm = uint32(0)
OrderIdFlags_Conditional = uint32(32)
OrderIdFlags_LongTerm = uint32(64)
)
// IsShortTermOrder returns true if this order ID is for a short-term order, false if
// not (which implies the order ID is for a long-term or conditional order).
// Note that all short-term orders will have the `OrderFlags` field set to 0.
func (o *OrderId) IsShortTermOrder() bool {
return o.OrderFlags == OrderIdFlags_ShortTerm
}
// IsConditionalOrder returns true if this order ID is for a conditional order, false if
// not (which implies the order ID is for a short-term or long-term order).
func (o *OrderId) IsConditionalOrder() bool {
// If the third bit in the first byte is set and no other bits are set,
// this is a conditional order.
// Note that 32 in decimal == 0x20 in hex == 0b00100000 in binary.
return o.OrderFlags == OrderIdFlags_Conditional
}
// IsLongTermOrder returns true if this order ID is for a long-term order, false if
// not (which implies the order ID is for a short-term or conditional order).
func (o *OrderId) IsLongTermOrder() bool {
// If the second bit in the first byte is set and no other bits are set,
// this is a long-term order.
// Note that 64 in decimal == 0x40 in hex == 0b01000000 in binary.
return o.OrderFlags == OrderIdFlags_LongTerm
}
// IsStatefulOrder returns whether this order is a stateful order, which is true for Long-Term
// and conditional orders and false for Short-Term orders.
func (o *OrderId) IsStatefulOrder() bool {
return o.IsLongTermOrder() || o.IsConditionalOrder()
}
// MustBeStatefulOrder panics if the orderId is not a stateful order, else it does nothing.
func (o *OrderId) MustBeStatefulOrder() {
if !o.IsStatefulOrder() {
panic(
fmt.Sprintf(
"MustBeStatefulOrder: called with non-stateful order ID (%+v)",
*o,
),
)
}
}
// MustBeConditionalOrder panics if the orderId is not a conditional order, else it does nothing.
func (o *OrderId) MustBeConditionalOrder() {
if !o.IsConditionalOrder() {
panic(
fmt.Sprintf(
"MustBeConditionalOrder: called with non-conditional order ID (%+v)",
o,
),
)
}
}
// MustBeShortTermOrder panics if the orderId is not a short term order, else it does nothing.
func (o *OrderId) MustBeShortTermOrder() {
if o.IsStatefulOrder() {
panic(
fmt.Sprintf(
"MustBeShortTermOrder: called with stateful order ID (%+v)",
*o,
),
)
}
}
// Validate performs checks on the OrderId. It performs the following checks:
// - Validates subaccount id
// - checks OrderFlags for validity
func (o *OrderId) Validate() error {
subaccountId := o.GetSubaccountId()
if err := subaccountId.Validate(); err != nil {
return err
}
if !o.IsShortTermOrder() && !o.IsStatefulOrder() {
return errorsmod.Wrapf(ErrInvalidOrderFlag, "orderId: %v", o)
}
return nil
}
// SortedOrders is type alias for `*OrderId` which supports deterministic
// sorting. Orders are first ordered by string comparison
// of their `Subaccount` owner, followed by integer comparison of their
// `Subaccount` number, followed by `ClientId` of the order, followed by `OrderFlags`,
// and finally by `ClobPairId` of the order.
// If two `*OrderIds` have equal Owners, Numbers, ClientIds, OrderFlags, and ClobPairId, they
// are assumed to be equal, and their sorted order is not deterministic.
type SortedOrders []OrderId
// The below methods are required to implement `sort.Interface` for sorting using the sort package.
var _ sort.Interface = SortedOrders{}
func (s SortedOrders) Len() int {
return len(s)
}
func (s SortedOrders) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s SortedOrders) Less(i, j int) bool {
si := s[i]
sj := s[j]
if si.SubaccountId.Owner != sj.SubaccountId.Owner {
return si.SubaccountId.Owner < sj.SubaccountId.Owner
}
if si.SubaccountId.Number != sj.SubaccountId.Number {
return si.SubaccountId.Number < sj.SubaccountId.Number
}
if si.ClientId != sj.ClientId {
return si.ClientId < sj.ClientId
}
if si.OrderFlags != sj.OrderFlags {
return si.OrderFlags < sj.OrderFlags
}
if si.ClobPairId != sj.ClobPairId {
return si.ClobPairId < sj.ClobPairId
}
return false
}
// MustSortAndHaveNoDuplicates is a wrapper around SortedOrders, which is for deterministic sorting.
// Mutates input slice.
// This function checks for duplicate OrderIds first, and panics if a duplicate exists.
// Orders are first ordered by string comparison
// of their `Subaccount` owner, followed by integer comparison of their
// `Subaccount` number, followed by `ClientId` of the order, followed by `OrderFlags`,
// and finally by `ClobPairId` of the order.
func MustSortAndHaveNoDuplicates(orderIds []OrderId) {
orderIdSet := make(map[OrderId]struct{}, len(orderIds))
for _, orderId := range orderIds {
if _, exists := orderIdSet[orderId]; exists {
panic(fmt.Errorf("cannot sort orders with duplicate order id %+v", orderId))
}
orderIdSet[orderId] = struct{}{}
}
sort.Sort(SortedOrders(orderIds))
}
// GetOrderIdLabels returns the telemetry labels of this order ID.
func (o *OrderId) GetOrderIdLabels() []gometrics.Label {
return []gometrics.Label{
metrics.GetLabelForIntValue(metrics.OrderFlag, int(o.GetOrderFlags())),
metrics.GetLabelForIntValue(metrics.ClobPairId, int(o.GetClobPairId())),
}
}