├── 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 | --------------------------------------------------------------------------------