-
Notifications
You must be signed in to change notification settings - Fork 91
/
order.go
232 lines (199 loc) · 7.87 KB
/
order.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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
package types
import (
"bytes"
"crypto/sha256"
"fmt"
"math/big"
"time"
gometrics "github.com/armon/go-metrics"
proto "github.com/cosmos/gogoproto/proto"
"github.com/dydxprotocol/v4-chain/protocol/lib/metrics"
satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types"
)
var _ MatchableOrder = &Order{}
func (o *Order) MustBeValidOrderSide() {
// If `o.Side` is an invalid side, panic.
if o.Side != Order_SIDE_BUY && o.Side != Order_SIDE_SELL {
panic(ErrInvalidOrderSide)
}
}
// MustBeConditionalOrder panics if the order is not a conditional order.
func (o *Order) MustBeConditionalOrder() {
o.OrderId.MustBeConditionalOrder()
}
// IsBuy returns true if this is a buy order, false if not.
// This function is necessary for the `Order` type to implement the `MatchableOrder` interface.
func (o *Order) IsBuy() bool {
return o.Side == Order_SIDE_BUY
}
// GetOrderHash returns the SHA256 hash of this order.
// This function is necessary for the `Order` type to implement the `MatchableOrder` interface.
func (o *Order) GetOrderHash() OrderHash {
orderBytes, err := o.Marshal()
if err != nil {
panic(err)
}
return sha256.Sum256(orderBytes)
}
// GetOrderTextString returns the JSON representation of this order.
func (o *Order) GetOrderTextString() string {
return proto.MarshalTextString(o)
}
// GetBaseQuantums returns the quantums of this order.
// This function is necessary for the `Order` type to implement the `MatchableOrder` interface.
func (o *Order) GetBaseQuantums() satypes.BaseQuantums {
return satypes.BaseQuantums(o.Quantums)
}
// GetBigQuantums returns the quantums of this order. The returned quantums is positive
// if long, negative if short, and zero if `o.Quantums == 0`.
// This function is necessary for the `Order` type to implement the `MatchableOrder` interface.
func (o *Order) GetBigQuantums() *big.Int {
bigQuantums := new(big.Int).SetUint64(o.Quantums)
if !o.IsBuy() {
bigQuantums.Neg(bigQuantums)
}
return bigQuantums
}
// GetOrderSubticks returns the subticks of this order.
// This function is necessary for the `Order` type to implement the `MatchableOrder` interface.
func (o *Order) GetOrderSubticks() Subticks {
return Subticks(o.Subticks)
}
// MustCmpReplacementOrder compares x to y and returns:
// 1 if x > y
// 0 if x = y
// -1 if x < y
// The orders are compared primarily by `GoodTilBlock` for Short-Term orders and `GoodTilBlockTime`
// for stateful orders. If the order expirations are equal, then they are compared by their SHA256 hash.
// Note that this function panics if the order IDs are not equal.
func (x *Order) MustCmpReplacementOrder(y *Order) int {
if x.OrderId != y.OrderId {
panic(
fmt.Sprintf(
"MustCmpReplacementOrder: order ID (%v) does not equal order ID (%v)",
x.OrderId,
y.OrderId,
),
)
}
var orderXExpiration uint32
var orderYExpiration uint32
// If this is a Short-Term order, use the `GoodTilBlock` for comparison.
// Else this is a stateful order, therefore use `GoodTilBlockTime` for comparison.
if x.IsShortTermOrder() {
orderXExpiration = x.GetGoodTilBlock()
orderYExpiration = y.GetGoodTilBlock()
} else {
orderXExpiration = x.GetGoodTilBlockTime()
orderYExpiration = y.GetGoodTilBlockTime()
}
if orderXExpiration > orderYExpiration {
return 1
} else if orderXExpiration < orderYExpiration {
return -1
}
// If both orders have the same expiration, use the SHA256 hash for comparison.
xHash := x.GetOrderHash()
yHash := y.GetOrderHash()
return bytes.Compare(xHash[:], yHash[:])
}
// GetSubaccountId returns the subaccount ID that placed this order.
// This function is necessary for the `Order` type to implement the `MatchableOrder` interface.
func (o *Order) GetSubaccountId() satypes.SubaccountId {
return o.OrderId.SubaccountId
}
// IsLiquidation always returns false since this order is not a liquidation.
// This function is necessary for the `Order` type to implement the `MatchableOrder` interface.
func (o *Order) IsLiquidation() bool {
return false
}
// MustGetOrder returns the underlying `Order` type.
// This function is necessary for the `Order` type to implement the `MatchableOrder` interface.
func (o *Order) MustGetOrder() Order {
return *o
}
// MustGetLiquidatedPerpetualId always panics since there is no underlying perpetual ID for a `Order`.
// This function is necessary for the `Order` type to implement the `MatchableOrder` interface.
func (o *Order) MustGetLiquidatedPerpetualId() uint32 {
panic("MustGetLiquidatedPerpetualId: No liquidated perpetual on an Order type.")
}
// IsReduceOnly returns whether this is a reduce-only order.
func (o *Order) IsReduceOnly() bool {
return o.ReduceOnly
}
// IsTakeProfitOrder returns whether this is order is a conditional take profit order.
func (o *Order) IsTakeProfitOrder() bool {
return o.IsConditionalOrder() && o.ConditionType == Order_CONDITION_TYPE_TAKE_PROFIT
}
// IsStopLossOrder returns whether this is order is a conditional stop loss order.
func (o *Order) IsStopLossOrder() bool {
return o.IsConditionalOrder() && o.ConditionType == Order_CONDITION_TYPE_STOP_LOSS
}
// RequiresImmediateExecution returns whether this order has to be executed immediately.
func (o *Order) RequiresImmediateExecution() bool {
return o.GetTimeInForce() == Order_TIME_IN_FORCE_IOC || o.GetTimeInForce() == Order_TIME_IN_FORCE_FILL_OR_KILL
}
// IsShortTermOrder returns whether this is a Short-Term order.
func (o *Order) IsShortTermOrder() bool {
return o.OrderId.IsShortTermOrder()
}
// 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 *Order) IsStatefulOrder() bool {
return o.OrderId.IsStatefulOrder()
}
// IsConditionalOrder returns whether this order is a conditional order.
func (o *Order) IsConditionalOrder() bool {
return o.OrderId.IsConditionalOrder()
}
// CanTrigger returns if a condition order is eligible to be triggered based on a given
// subticks value. Function will panic if order is not a conditional order.
func (o *Order) CanTrigger(subticks Subticks) bool {
o.MustBeConditionalOrder()
orderTriggerSubticks := Subticks(o.ConditionalOrderTriggerSubticks)
// Take profit buys and stop loss sells trigger when the oracle price goes lower
// than or equal to the trigger price.
if o.ConditionType == Order_CONDITION_TYPE_TAKE_PROFIT && o.IsBuy() ||
o.ConditionType == Order_CONDITION_TYPE_STOP_LOSS && !o.IsBuy() {
return orderTriggerSubticks >= subticks
}
// Take profit sells and stop loss buys trigger when the oracle price goes higher
// than or equal to the trigger price.
return orderTriggerSubticks <= subticks
}
// MustGetUnixGoodTilBlockTime returns an instance of `Time` that represents the order's
// `GoodTilBlockTime`. This function panics when the order is a short-term order or
// when its `GoodTilBlockTime` is zero.
func (o *Order) MustGetUnixGoodTilBlockTime() time.Time {
o.MustBeStatefulOrder()
goodTilBlockTime := o.GetGoodTilBlockTime()
if goodTilBlockTime == 0 {
panic(
fmt.Errorf(
"MustGetUnixGoodTilBlockTime: order (%v) goodTilBlockTime is zero",
o,
),
)
}
return time.Unix(int64(goodTilBlockTime), 0)
}
// MustBeStatefulOrder panics if the order is not a stateful order, else it does nothing.
func (o *Order) MustBeStatefulOrder() {
o.OrderId.MustBeStatefulOrder()
}
// GetClobPairId returns the CLOB pair ID of this order.
// This function implements the `MatchableOrder` interface.
func (o *Order) GetClobPairId() ClobPairId {
return ClobPairId(o.OrderId.GetClobPairId())
}
// GetOrderLabels returns the telemetry labels of this order.
func (o *Order) GetOrderLabels() []gometrics.Label {
return append(
[]gometrics.Label{
metrics.GetLabelForStringValue(metrics.TimeInForce, o.GetTimeInForce().String()),
metrics.GetLabelForBoolValue(metrics.ReduceOnly, o.IsReduceOnly()),
metrics.GetLabelForStringValue(metrics.OrderSide, o.GetSide().String()),
},
o.OrderId.GetOrderIdLabels()...,
)
}