-
Notifications
You must be signed in to change notification settings - Fork 3
/
Trade.py
389 lines (279 loc) · 10.6 KB
/
Trade.py
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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
from colors import c
import base64
import uuid
from config import G_DEFAULT_BITMEX_SYMBOL # if you dont have this declared in config go do that
from Exceptions import InvalidArgError
class Trade:
'''
class for making orders to send thru the Bitmex API.
Attributes:
`symbol: str`
symbol for trades
`orderIdPrefex: str`
prefex for order IDs
Methods:
`market_buy`
`market_sell`
`limit_buy`
`limit_sell`
`stop_order`
`close`
`make_order`
each method returns an order that can be used in a single or bulk order
(ie bitmex.place_order(my_order) or bitmex.place_bulk_order([order1, order2]))
'''
def __init__(self, symbol, orderIDPrefex="traderbot_"):
self.symbol = symbol
self.orderIDPrefex = orderIDPrefex
def market_buy(self,quantity):
'''
returns an order to Buy at market price.
Parameters:
`quantity: int`
number of contracts
Returns:
`order: dict`
market buy order
'''
side="Buy"
order = self.make_order(
quantity=quantity,
side=side,
)
return order
def market_sell(self,quantity):
'''
Make a order for Selling at market price.
Parameters:
`quantity: int`
number of contracts
Returns:
`order: dict`
market sell order
'''
side="Sell"
order = self.make_order(
quantity=quantity,
side=side,
)
return order
def limit_buy(self,quantity, price):
'''Make a limit buy order.
Parameters:
`quantity: int`
number of contracts
`price: float`
price, must be val;id tickSize, ie must be integer or .5 between int
Returns:
`order: dict`
limit buy order
'''
self.is_tickSize_valid(price)
side = "Buy"
order = self.make_order(
quantity=quantity,
price=price,
side=side,
)
return order
def limit_sell(self,quantity, price):
"""Make a limit sell / short order.
Parameters:
`quantity: int`
number of contracts
`price: float`
price, must be val;id tickSize, ie must be integer or .5 between int
Returns:
`order: dict`
limit sell order
"""
self.is_tickSize_valid(price)
side="Sell"
order = self.make_order(
quantity=quantity,
price=price,
side=side,
)
return order
def stop_order(self,quantity, stopPx, price=None, execInst=None):
'''
Creates a stop order.
Use a price below the current price for stop-sell orders and buy-if-touched orders.
Use execInst of 'MarkPrice' or 'LastPrice' to define the current price used for triggering.
Parameters:
`quantity: int`
number of contracts
`stopPx: float`
stop price
`price: float`
price
`execInst:str`
Returns:
`order: dict`
stop order
'''
if price:
self.is_tickSize_valid(price)
order = self.make_order(
quantity=quantity,
stopPx=stopPx,
price=price,
execInst=execInst
)
return order
def close(self,quantity=None, side=None, symbol=None, orderIDPrefix=None):
'''
Returns a close order ready to send to Bitmex.
cancel other active limit orders with the same side and symbol if the open quantity exceeds the current position.
Side or quantity required.
--------------------------
Parameters:
`quantity: int`
number of contracts to close
`side: str`
Buy or Sell
`symbol: str`
symbol to apply close to
`orderIDPrefix: str`
label prefix of close, if none supplied uses default
Returns:
`order: dict`
close order
'''
if symbol == None:
symbol = self.symbol
if orderIDPrefix == None:
orderIDPrefix = self.orderIDPrefex
if quantity == None and side == None:
raise InvalidArgError([quantity, side],"Side or quantity required to close.")
clOrdID = orderIDPrefix + base64.b64encode(uuid.uuid4().bytes).decode('utf8').rstrip('=\n')
order = {
'symbol': symbol,
'clOrdID': clOrdID,
'execInst': 'Close'
}
if quantity: order['orderQty'] = quantity,
if side: order['side'] = side
return order
def make_order(
self,
quantity=None,
symbol=None,
price=None,
side=None,
orderType=None,
displayQty=None,
stopPx=None,
pegOffsetValue=None,
pegPriceType=None,
timeInForce=None,
execInst=None,
orderIDPrefix=None):
"""
Make your own order.
Parameters:
`quantity: int`
number of contracts, each contract is worth 1 USD of Bitcoin.
`symbol: str`
symbol of trade
`price: float`
price of trade, must be approprate ticksize, either an integer or .5
If no price supplied, order is market sell or buy.
`side: str`
buy or sell
`orderType: str`
type of order (Market, Limit, Stop, StopLimit, MarketIfTouched, LimitIfTouched, or Pegged)
`displayQty: int`
what quantity yopu want order to display, 0 hides order
`stopPx: float`
optional trigger price, use a price below the current price for stop-sell orders and buy-if-touched orders.
`pegOffsetValue: float`
value of pegOffset
`pegPriceType:str`
pegOffset type (LastPeg, MidPricePeg, MarketPeg, PrimaryPeg, or TrailingStopPeg)
`timeInForce: str`
(Day, GoodTillCancel, ImmediateOrCancel, or FillOrKill)
`execInst: str`
(ParticipateDoNotInitiate, AllOrNone, MarkPrice, IndexPrice, LastPrice, Close, ReduceOnly, or Fixed)
`orderIDPrefix:str`
labels order id with prefix
For more info on orders see Bitmex API explorer.
Returns:
`order: dict`
an order ready to be sent in a bulk or single order.
"""
# Read Me - This creates all the orders below, so dont break it
if price and price < 0:
raise InvalidArgError(price,"Order Price must be positive.")
if price:
self.is_tickSize_valid(price)
if not quantity and execInst != 'Close':
raise InvalidArgError([quantity, execInst],"Must supply order quantity.")
if side and side != 'Sell' and side != 'Buy':
raise InvalidArgError(side,"Side must be 'Sell' or 'Buy'.")
if orderType and (
orderType != 'Market' or
orderType != 'Limit' or
orderType != 'Stop' or
orderType != 'StopLimit' or
orderType != 'MarketIfTouched' or
orderType != 'LimitIfTouched' or
orderType != 'Pegged'
):
raise InvalidArgError(orderType,"orderType must be Market, Limit, Stop, StopLimit, MarketIfTouched, LimitIfTouched, or Pegged")
if displayQty and displayQty < 0:
raise InvalidArgError(displayQty,"DisplayQty is negative, must be 0 to hide order or positive.")
if pegPriceType and (
pegPriceType != 'LastPeg' or
pegPriceType != 'MidPricePeg' or
pegPriceType != 'MarketPeg' or
pegPriceType != 'PrimaryPeg' or
pegPriceType != 'TrailingStopPeg'
):
raise InvalidArgError(pegPriceType,"pegPriceType must be LastPeg, MidPricePeg, MarketPeg, PrimaryPeg, or TrailingStopPeg.")
if timeInForce and (
timeInForce != 'Day' or
timeInForce != 'GoodTillCancel' or
timeInForce != 'ImmediateOrCancel' or
timeInForce != 'FillOrKill'
):
raise InvalidArgError(timeInForce,"timeInForce must be Day, GoodTillCancel, ImmediateOrCancel, or FillOrKill")
if execInst and (
execInst != 'ParticipateDoNotInitiate' or
execInst != 'AllOrNone' or
execInst != 'MarkPrice' or
execInst != 'IndexPrice' or
execInst != 'LastPrice' or
execInst != 'Close' or
execInst != 'ReduceOnly' or
execInst != 'Fixed'
):
raise InvalidArgError(execInst,"execInst must be ParticipateDoNotInitiate, AllOrNone, MarkPrice, IndexPrice, LastPrice, Close, ReduceOnly, or Fixed.")
if execInst and execInst == 'AllOrNone' and displayQty != 0:
raise InvalidArgError([execInst, displayQty],"execInst is 'AllOrNone', displayQty must be 0" + c[0])
if symbol == None:
symbol = self.symbol
if orderIDPrefix == None:
orderIDPrefix = self.orderIDPrefex
# Generate a unique clOrdID with our prefix so we can identify it.
clOrdID = orderIDPrefix + base64.b64encode(uuid.uuid4().bytes).decode('utf8').rstrip('=\n')
order = {
'symbol': symbol,
'clOrdID': clOrdID,
}
# add to order if supplied
if quantity: order['orderQty'] = quantity
if side: order['side'] = side
if price: order['price'] = price
if displayQty: order['displayQty'] = displayQty
if stopPx: order['stopPx'] = stopPx
if pegOffsetValue: order['pegOffsetValue'] = pegOffsetValue
if timeInForce: order['timeInForce'] = timeInForce
if execInst: order['execInst'] = execInst
if orderType: order['ordType'] = orderType
return order
def is_tickSize_valid(self,price):
"""Check for valid tickSize, ie if the price is an integer or .5 away."""
if not isinstance(price, int):
if not isinstance(price + 0.5, int):
raise InvalidArgError(price,"Invalid Tick Size! Prices must be set at integer or .5 between")