├── input
├── state
├── condition
├── contract
├── leverage
├── z19
├── M20
├── usd
├── setting
└── apikey_real
├── data
└── bitmex_bot.gif
├── .gitignore
├── curses-2.2.1+utf8-cp37-cp37m-win_amd64.whl
├── README.md
├── requirements.txt
├── LICENSE
├── ui.py
└── bitmex_bot.py
/input/state:
--------------------------------------------------------------------------------
1 | Opened
--------------------------------------------------------------------------------
/input/condition:
--------------------------------------------------------------------------------
1 | None
--------------------------------------------------------------------------------
/input/contract:
--------------------------------------------------------------------------------
1 | XBTH20
--------------------------------------------------------------------------------
/input/leverage:
--------------------------------------------------------------------------------
1 | 0
2 | 0
--------------------------------------------------------------------------------
/input/z19:
--------------------------------------------------------------------------------
1 | 9213.50
2 | 9214.00
--------------------------------------------------------------------------------
/input/M20:
--------------------------------------------------------------------------------
1 | 10959.50
2 | 10960.00
--------------------------------------------------------------------------------
/input/usd:
--------------------------------------------------------------------------------
1 | 10693.50
2 | 10694.00
--------------------------------------------------------------------------------
/input/setting:
--------------------------------------------------------------------------------
1 | 5
2 | 304.0
3 | -200.0
--------------------------------------------------------------------------------
/data/bitmex_bot.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strader07/bitmex_algo_trader/HEAD/data/bitmex_bot.gif
--------------------------------------------------------------------------------
/input/apikey_real:
--------------------------------------------------------------------------------
1 | test
2 | bxz29HwfEVZGWfPkUTn356D_
3 | f2qC-8AHcN_C1gOj8yXRdM1C9UZy8R10C_SIR1KfDkcYpiZt
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 | *.pyc
--------------------------------------------------------------------------------
/curses-2.2.1+utf8-cp37-cp37m-win_amd64.whl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strader07/bitmex_algo_trader/HEAD/curses-2.2.1+utf8-cp37-cp37m-win_amd64.whl
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # bitmex_algo_trader
2 | Algorithmic trading bot using bitmex API with a simple UI in terminal
3 |
4 |
5 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | aiodns==1.1.1
2 | aiofile==3.1.0
3 | aiohttp==3.6.2
4 | async-timeout==3.0.1
5 | attrs==20.2.0
6 | caio==0.6.1
7 | cchardet==2.1.6
8 | ccxt==1.34.26
9 | certifi==2020.6.20
10 | cffi==1.14.2
11 | chardet==3.0.4
12 | cryptofeed==1.5.1
13 | cryptography==3.1
14 | idna==2.10
15 | keyboard==0.13.5
16 | multidict==4.7.6
17 | numpy==1.19.2
18 | pandas==1.1.2
19 | pycares==3.1.1
20 | pycparser==2.20
21 | python-dateutil==2.8.1
22 | pytz==2020.1
23 | requests==2.24.0
24 | six==1.15.0
25 | sortedcontainers==2.2.2
26 | typing-extensions==3.7.4.3
27 | urllib3==1.25.10
28 | websockets==8.1
29 | yapic.json==1.5.1
30 | yarl==1.1.0
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Jing Sun
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/ui.py:
--------------------------------------------------------------------------------
1 | from tkinter import Spinbox, Label,Tk, Button, ttk
2 | import ccxt,os,sys,threading
3 | import bitmex_bot
4 |
5 | if(os.path.exists('input/apikey_real')==False):
6 | print("Please make config file !")
7 | sys.exit()
8 | f=open('input/apikey_real','r')
9 | lines=f.readlines()
10 | f.close
11 | if(len(lines)!=3):
12 | print('Bad config!')
13 | sys.exit()
14 |
15 | btmx = ccxt.bitmex({
16 | 'apiKey': lines[1][:-1],
17 | 'secret':lines[2],
18 | 'enableRateLimit': False,
19 | 'options': {
20 | 'api-expires': 60,
21 | },
22 | #to resolve clock lack of synchronisation with server
23 | })
24 |
25 | if lines[0].strip() == 'test':
26 | btmx.urls['api'] = btmx.urls['test']
27 |
28 | def g_hotkey_process():
29 | window = Tk()
30 | window.title("Real bot control")
31 | window.geometry('250x200')
32 |
33 | f=open('setting','r')
34 | Quantity=f.readline()[:-1]
35 | MaxDifference=f.readline()[:-1]
36 | MinDifference=f.readline()
37 | f.close()
38 |
39 | def SaveChange():
40 | f=open('input/setting','w')
41 | f.write(QtySpin.get()+'\n')
42 | f.write(MaxSpin.get()+'\n')
43 | f.write (MinSpin.get())
44 | f.close()
45 | def MaxChange():
46 | if(float(MaxSpin.get())float(max_diff) and float(diff_close)"+max_diff,width))
170 | cond='OpenClose'
171 | elif float(diff_open)>float(max_diff):
172 | stdscr.addstr(rows*2+13,0,n(state+"Open condition satisfied! "+diff_open+">"+max_diff,width))
173 | cond='Open'
174 | elif float(diff_close) 0:
196 | timeout = int(e.args[0][pos + len(sstr):-1])
197 | time.sleep(timeout)
198 | else:
199 | time.sleep(2)
200 |
201 | def bitmex_post_order(symbol,orderSide, ordType, orderQty, orderPrice):
202 | if ordType=="Market":
203 | params={"symbol":symbol,\
204 | "side":orderSide,\
205 | "orderQty":orderQty,\
206 | "ordType":"Market"}
207 | elif ordType=="Limit":
208 | params={"symbol":symbol,\
209 | "side":orderSide,\
210 | "orderQty":orderQty,\
211 | "ordType":"Limit",\
212 | "price":orderPrice}
213 | res = None
214 | while not res:
215 | try:
216 | res = btmx.private_post_order(params)
217 | log(res)
218 | except Exception as e:
219 | log(e)
220 | handle_timeout(e)
221 | return res
222 |
223 | def bitmex_close_pos(symbol):
224 | res = None
225 | params = {'symbol': symbol, 'execInst': 'Close'}
226 | while not res:
227 | try:
228 | res = btmx.private_post_order(params)
229 | log(res)
230 | except Exception as e:
231 | log(e)
232 | handle_timeout(e)
233 | def g_hotkey_process():
234 | window = Tk()
235 | window.title(":)")
236 | window.geometry('250x100')
237 |
238 | f=open('input/setting','r')
239 | Quantity=f.readline()[:-1]
240 | MaxDifference=f.readline()[:-1]
241 | MinDifference=f.readline()
242 | f.close()
243 |
244 | def SaveChange():
245 | f=open('input/setting','w')
246 | f.write(QtySpin.get()+'\n')
247 | f.write(MaxSpin.get()+'\n')
248 | f.write(MinSpin.get())
249 | f.close()
250 |
251 | QtyLabel = Label(window, text="Quantity")
252 | QtyLabel.grid(column=0, row=0)
253 | QtySpin = Spinbox(window, from_=1, to=2, width=7, command=SaveChange) # limited to 2 during testing
254 | QtySpin.delete(0, "end")
255 | QtySpin.insert(0, Quantity)
256 | QtySpin.grid(column=1,row=0)
257 |
258 | MaxLabel = Label(window, text="Max difference")
259 | MaxLabel.grid(column=0, row=1)
260 | MaxSpin = Spinbox(window, from_=-200, to=500, width=7, command=SaveChange)
261 | MaxSpin.delete(0, "end")
262 | MaxSpin.insert(0, MaxDifference)
263 | MaxSpin.grid(column=1,row=1)
264 |
265 | MinLabel = Label(window, text="Min difference")
266 | MinLabel.grid(column=0, row=2)
267 | MinSpin = Spinbox(window, from_=-200, to=500, width=7, command=SaveChange)
268 | MinSpin.delete(0, "end")
269 | MinSpin.insert(0, MinDifference)
270 | MinSpin.grid(column=1,row=2)
271 |
272 | window.mainloop()
273 |
274 | def trade():
275 | f=open('input/condition','r')
276 | cond=f.readline()
277 | f.close()
278 |
279 | stdscr1 = curses.initscr()
280 | stdscr1.addstr(rows*2+14,0,n('',width))
281 | stdscr1.addstr(rows*2+15,0,n(cond+" "+str(datetime.now()),width))
282 | stdscr1.refresh()
283 |
284 | f=open('input/setting','r')
285 | Quantity=f.readline()[:-1]
286 | f.close()
287 |
288 | f=open('input/state','r')
289 | state=f.readline()
290 | f.close()
291 |
292 | f=open('input/contract','r')
293 | contract=f.readlines()[0]
294 | f.close()
295 |
296 | tths=[]
297 |
298 | def openUsd():
299 | bitmex_post_order('XBTUSD','Buy','Market',Quantity,'')
300 | def openM20():
301 | bitmex_post_order(contract,'Sell','Market',Quantity,'')
302 | def closeUsd():
303 | bitmex_post_order('XBTUSD','Sell','Market',Quantity,'')
304 | def closeM20():
305 | bitmex_post_order(contract,'Buy','Market',Quantity,'')
306 |
307 | if((cond=='Open' or cond=='OpenClose') and state!='Opened'):
308 | th=threading.Thread(target=openUsd)
309 | tths.append(th)
310 | th1=threading.Thread(target=openM20)
311 | tths.append(th1)
312 | th.start()
313 | th1.start()
314 | f=open('input/state','w')
315 | f.write('Opened')
316 | f.close()
317 | elif((cond=='Close' or cond=='OpenClose') and state=='Opened'):
318 | th=threading.Thread(target=closeM20)
319 | tths.append(th)
320 | th1=threading.Thread(target=closeUsd)
321 | tths.append(th1)
322 | th.start()
323 | th1.start()
324 | f=open('input/state','w')
325 | f.write('Closed')
326 | f.close()
327 |
328 | t=threading.Timer(0.1, trade)
329 | t.start()
330 |
331 | import platform
332 | def main():
333 | f=open('input/contract','r')
334 | contract=f.readlines()[0]
335 | f.close()
336 |
337 | print("Hello XBTUSD, "+contract+" ! Orderbooks loading....")
338 |
339 | if(platform.system()=='Windows'):
340 | keyboard.add_hotkey('g', ui.g_hotkey_process)
341 | os.system('mode con: cols=100 lines='+str((rows+4)*2+10))
342 |
343 | ths=[]
344 | th=threading.Thread(target=trade)
345 | ths.append(th)
346 | th.start()
347 |
348 | f1 = FeedHandler()
349 | usd_handler=Bitmex(pairs=['XBTUSD'], channels=[L2_BOOK], callbacks={L2_BOOK: BookCallback(xbtusd_book)})
350 | f1.add_feed(usd_handler)
351 |
352 | M20_handler=Bitmex(pairs=[contract], channels=[L2_BOOK], callbacks={L2_BOOK: BookCallback(xbtM20_book)})
353 | f1.add_feed(M20_handler)
354 | f1.run()
355 |
356 | if __name__ == '__main__':
357 | main()
358 | curses.endwin() #Close screen
359 |
--------------------------------------------------------------------------------