BitMex websocket:(需要修改,有此是不需要的)
import sys
import websocket
import threading
import traceback
import ssl
from time import sleep
import json
import decimal
import logging
from MidClass.settings import settings
from MidClass.auth.APIKeyAuth import generate_expires, generate_signature
from MidClass.utils.log import setup_custom_logger
from MidClass.utils.math import toNearest
from future.utils import iteritems
from future.standard_library import hooks
with hooks(): # Python 2/3 compat
from urllib.parse import urlparse, urlunparse
# Connects to Exchange websocket for streaming realtime data.
# The Marketmaker still interacts with this as if it were a REST Endpoint, but now it can get
# much more realtime data without heavily polling the API.
#
# The Websocket offers a bunch of data as raw properties right on the object.
# On connect, it synchronously asks for a push of all this data then returns.
# Right after, the MM can start using its data. It will be updated in realtime, so the MM can
# poll as often as it wants.
class BitMEXWebsocket():
# Don't grow a table larger than this amount. Helps cap memory usage.
MAX_TABLE_LEN = 200
def __init__(self):
self.logger = logging.getLogger('root')
self.__reset()
def __del__(self):
self.exit()
def connect(self, endpoint="", symbol="XBTN15", shouldAuth=True):
'''Connect to the websocket and initialize data stores.'''
self.logger.debug("Connecting WebSocket.")
self.symbol = symbol
self.shouldAuth = shouldAuth
# We can subscribe right in the connection querystring, so let's build that.
# Subscribe to all pertinent endpoints
subscriptions = [sub + ':' + symbol for sub in ["quote", "trade"]] # 自己动手修改需要返回数据
subscriptions += ["instrument"] # We want all of them
if self.shouldAuth:
subscriptions += [sub + ':' + symbol for sub in ["order", "execution"]]
subscriptions += ["margin", "position"]
# Get WS URL and connect.
urlParts = list(urlparse(endpoint))
urlParts[0] = urlParts[0].replace('http', 'ws')
urlParts[2] = "/realtime?subscribe=" + ",".join(subscriptions)
wsURL = urlunparse(urlParts)
self.logger.info("Connecting to %s" % wsURL)
self.__connect(wsURL)
self.logger.info('Connected to WS. Waiting for data images, this may take a moment...')
# Connected. Wait for partials
self.__wait_for_symbol(symbol)
if self.shouldAuth:
self.__wait_for_account()
self.logger.info('Got all market data. Starting.')
#
# Data methods
#
def get_instrument(self, symbol):
instruments = self.data['instrument']
matchingInstruments = [i for i in instruments if i['symbol'] == symbol]
if len(matchingInstruments) == 0:
raise Exception("Unable to find instrument or index with symbol: " + symbol)
instrument = matchingInstruments[0]
# Turn the 'tickSize' into 'tickLog' for use in rounding
# http://stackoverflow.com/a/6190291/832202
instrument['tickLog'] = decimal.Decimal(str(instrument['tickSize'])).as_tuple().exponent * -1
return instrument
def get_ticker(self, symbol):
'''Return a ticker object. Generated from instrument.'''
instrument = self.get_instrument(symbol)
# If this is an index, we have to get the data from the last trade.
if instrument['symbol'][0] == '.':
ticker = {}
ticker['mid'] = ticker['buy'] = ticker['sell'] = ticker['last'] = instrument['markPrice']
# Normal instrument
else:
bid = instrument['bidPrice'] or instrument['lastPrice']
ask = instrument['askPrice'] or instrument['lastPrice']
ticker = {
"last": instrument['lastPrice'],
"buy": bid,
"sell": ask,
"mid": (bid + ask) / 2
}
# The instrument has a tickSize. Use it to round values.
return {k: toNearest(float(v or 0), instrument['tickSize']) for k, v in iteritems(ticker)}
def funds(self):
return self.data['margin'][0]
def market_depth(self, symbol):
raise NotImplementedError('orderBook is not subscribed; use askPrice and bidPrice on instrument')
# return self.data['orderBook25'][0]
def open_orders(self, clOrdIDPrefix):
orders = self.data['order']
# Filter to only open orders (leavesQty > 0) and those that we actually placed
return [o for o in orders if str(o['clOrdID']).startswith(clOrdIDPrefix) and o['leavesQty'] > 0]
def position(self, symbol):
positions = self.data['position']
pos = [p for p in positions if p['symbol'] == symbol]
if len(pos) == 0:
# No position found; stub it
return {'avgCostPrice': 0, 'avgEntryPrice': 0, 'currentQty': 0, 'symbol': symbol}
return pos[0]
def recent_trades(self):
return self.data['trade']
#
# Lifecycle methods
#
def error(self, err):
self._error = err
self.logger.error(err)
self.exit()
def exit(self):
self.exited = True
self.ws.close()
#
# Private methods
#
def __connect(self, wsURL):
'''Connect to the websocket in a thread.'''
self.logger.debug("Starting thread")
ssl_defaults = ssl.get_default_verify_paths()
sslopt_ca_certs = {'ca_certs': ssl_defaults.cafile}
self.ws = websocket.WebSocketApp(wsURL,
on_message=self.__on_message,
on_close=self.__on_close,
on_open=self.__on_open,
on_error=self.__on_error,
header=self.__get_auth()
)
setup_custom_logger('websocket', log_level=settings.LOG_LEVEL)
self.wst = threading.Thread(target=lambda: self.ws.run_forever(sslopt=sslopt_ca_certs))
self.wst.daemon = True
self.wst.start()
self.logger.info("Started thread")
# Wait for connect before continuing
conn_timeout = 5
while (not self.ws.sock or not self.ws.sock.connected) and conn_timeout and not self._error:
sleep(1)
conn_timeout -= 1
if not conn_timeout or self._error:
self.logger.error("Couldn't connect to WS! Exiting.")
self.exit()
sys.exit(1)
def __get_auth(self):
'''Return auth headers. Will use API Keys if present in settings.'''
if self.shouldAuth is False:
return []
self.logger.info("Authenticating with API Key.")
# To auth to the WS using an API key, we generate a signature of a nonce and
# the WS API endpoint.
nonce = generate_expires()
return [
"api-expires: " + str(nonce),
"api-signature: " + generate_signature(settings.API_SECRET, 'GET', '/realtime', nonce, ''),
"api-key:" + settings.API_KEY
]
def __wait_for_account(self):
'''On subscribe, this data will come down. Wait for it.'''
# Wait for the keys to show up from the ws
while not {'margin', 'position', 'order'} <= set(self.data):
sleep(0.1)
def __wait_for_symbol(self, symbol):
'''On subscribe, this data will come down. Wait for it.'''
while not {'instrument', 'trade', 'quote'} <= set(self.data):
sleep(0.1)
def __send_command(self, command, args):
'''Send a raw command.'''
self.ws.send(json.dumps({"op": command, "args": args or []}))
def __on_message(self, message):
'''Handler for parsing WS messages.'''
message = json.loads(message)
self.logger.debug(json.dumps(message))
table = message['table'] if 'table' in message else None
action = message['action'] if 'action' in message else None
try:
if 'subscribe' in message:
if message['success']:
self.logger.debug("Subscribed to %s." % message['subscribe'])
else:
self.error("Unable to subscribe to %s. Error: \"%s\" Please check and restart." %
(message['request']['args'][0], message['error']))
elif 'status' in message:
if message['status'] == 400:
self.error(message['error'])
if message['status'] == 401:
self.error("API Key incorrect, please check and restart.")
elif action:
if table not in self.data:
self.data[table] = []
if table not in self.keys:
self.keys[table] = []
# There are four possible actions from the WS:
# 'partial' - full table image
# 'insert' - new row
# 'update' - update row
# 'delete' - delete row
if action == 'partial':
self.logger.debug("%s: partial" % table)
self.data[table] += message['data']
# Keys are communicated on partials to let you know how to uniquely identify
# an item. We use it for updates.
self.keys[table] = message['keys']
elif action == 'insert':
self.logger.debug('%s: inserting %s' % (table, message['data']))
self.data[table] += message['data']
# Limit the max length of the table to avoid excessive memory usage.
# Don't trim orders because we'll lose valuable state if we do.
if table not in ['order', 'orderBookL2'] and len(self.data[table]) > BitMEXWebsocket.MAX_TABLE_LEN:
self.data[table] = self.data[table][(BitMEXWebsocket.MAX_TABLE_LEN // 2):]
elif action == 'update':
self.logger.debug('%s: updating %s' % (table, message['data']))
# Locate the item in the collection and update it.
for updateData in message['data']:
item = findItemByKeys(self.keys[table], self.data[table], updateData)
if not item:
continue # No item found to update. Could happen before push
# Log executions
if table == 'order':
is_canceled = 'ordStatus' in updateData and updateData['ordStatus'] == 'Canceled'
if 'cumQty' in updateData and not is_canceled:
contExecuted = updateData['cumQty'] - item['cumQty']
if contExecuted > 0:
instrument = self.get_instrument(item['symbol'])
self.logger.info("Execution: %s %d Contracts of %s at %.*f" %
(item['side'], contExecuted, item['symbol'],
instrument['tickLog'], item['price']))
# Update this item.
item.update(updateData)
# Remove canceled / filled orders
if table == 'order' and item['leavesQty'] <= 0:
self.data[table].remove(item)
elif action == 'delete':
self.logger.debug('%s: deleting %s' % (table, message['data']))
# Locate the item in the collection and remove it.
for deleteData in message['data']:
item = findItemByKeys(self.keys[table], self.data[table], deleteData)
self.data[table].remove(item)
else:
raise Exception("Unknown action: %s" % action)
except:
self.logger.error(traceback.format_exc())
def __on_open(self):
self.logger.debug("Websocket Opened.")
def __on_close(self):
self.logger.info('Websocket Closed')
self.exit()
def __on_error(self, ws, error):
if not self.exited:
self.error(error)
def __reset(self):
self.data = {}
self.keys = {}
self.exited = False
self._error = None
def findItemByKeys(keys, table, matchData):
for item in table:
matched = True
for key in keys:
if item[key] != matchData[key]:
matched = False
if matched:
return item
if __name__ == "__main__":
# create console handler and set level to debug
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
# create formatter
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
# add formatter to ch
ch.setFormatter(formatter)
logger.addHandler(ch)
ws = BitMEXWebsocket()
ws.logger = logger
ws.connect("https://testnet.bitmex.com/api/v1")
while(ws.ws.sock.connected):
sleep(1)
BitMex REST API:(需要修改,有此是不需要的)
"""Exchange API Connector."""
from __future__ import absolute_import
import requests
import time
import datetime
import json
import base64
import uuid
import logging
from MidClass.auth import APIKeyAuthWithExpires
from MidClass.utils import constants, errors
from MidClass.ws.ws_thread import BitMEXWebsocket
import numpy as np
import pandas as pd
pd.set_option('expand_frame_repr', False)
pd.set_option('display.max_rows', None)
# https://www.bitmex.com/api/explorer/
class Exchange(object):
"""Exchange API Connector."""
def __init__(self, base_url=None, symbol=None, apiKey=None, apiSecret=None,
orderIDPrefix='mm_bitmex_', shouldWSAuth=True, postOnly=False, timeout=7):
"""Init connector."""
self.logger = logging.getLogger('root')
self.base_url = base_url
self.symbol = symbol
self.postOnly = postOnly
if (apiKey is None):
raise Exception("Please set an API key and Secret to get started. See " +
"https://github.com/BitMEX/sample-market-maker/#getting-started for more information."
)
self.apiKey = apiKey
self.apiSecret = apiSecret
if len(orderIDPrefix) > 13:
raise ValueError("settings.ORDERID_PREFIX must be at most 13 characters long!")
self.orderIDPrefix = orderIDPrefix
self.retries = 0 # initialize counter
# Prepare HTTPS session
self.session = requests.Session()
# These headers are always sent
self.session.headers.update({'user-agent': 'liquidbot-' + constants.VERSION})
self.session.headers.update({'content-type': 'application/json'})
self.session.headers.update({'accept': 'application/json'})
# Create websocket for streaming data
self.ws = BitMEXWebsocket()
self.ws.connect(base_url, symbol, shouldAuth=shouldWSAuth)
self.timeout = timeout
def __del__(self):
self.exit()
def exit(self):
self.ws.exit()
#
# Public methods
#
def ticker_data(self, symbol=None):
"""Get ticker data."""
if symbol is None:
symbol = self.symbol
return self.ws.get_ticker(symbol)
def instrument(self, symbol):
"""Get an instrument's details."""
return self.ws.get_instrument(symbol)
def instruments(self, filter=None):
query = {}
if filter is not None:
query['filter'] = json.dumps(filter)
return self._curl_bitmex(path='instrument', query=query, verb='GET')
def kline(self, symbol, interval='5m'):
"""Get the Kline"""
path = "trade/bucketed"
end_time = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S")
query = {
'binSize': interval,
'partial': 'false',
'symbol': symbol,
'count': 200,
'reverse': 'true',
'endTime': end_time
}
content = self._curl_bitmex(path=path, query=query, verb='GET')
dataframe = pd.DataFrame(content, dtype=float)
dataframe.sort_values(by='timestamp', axis=0, ascending=True, inplace=True)
dataframe.reset_index(drop=True, inplace=True)
dataframe['candle_begin_time'] = (pd.to_datetime(dataframe['timestamp']).values - np.datetime64('1970-01-01T00:00:00Z')) / np.timedelta64(1, 's') - pd.Timedelta(interval).seconds
# dataframe['candle_begin_time'] = pd.to_datetime(dataframe['candle_begin_time'], format="%Y-%m-%dT%H:%M:%S.%fZ", errors='coerce')
dataframe['candle_begin_time_GMT8'] = pd.to_datetime(dataframe['candle_begin_time'], unit='s') + timedelta(hours=8)
dataframe = dataframe[['candle_begin_time_GMT8', 'open', 'high', 'low', 'close', 'homeNotional']]
dataframe.rename(columns={'homeNotional': 'volume'}, inplace=True)
return dataframe
def market_depth(self, symbol):
"""Get market depth / orderbook."""
return self.ws.market_depth(symbol)
def recent_trades(self):
"""Get recent trades.
Returns
-------
A list of dicts:
{u'amount': 60,
u'date': 1306775375,
u'price': 8.7401099999999996,
u'tid': u'93842'},
"""
return self.ws.recent_trades()
#
# Authentication required methods
#
def authentication_required(fn):
"""Annotation for methods that require auth."""
def wrapped(self, *args, **kwargs):
if not (self.apiKey):
msg = "You must be authenticated to use this method"
raise errors.AuthenticationError(msg)
else:
return fn(self, *args, **kwargs)
return wrapped
@authentication_required
def funds(self):
"""Get your current balance."""
return self.ws.funds()
@authentication_required
def position(self, symbol):
"""Get your open position."""
return self.ws.position(symbol)
@authentication_required
def isolate_margin(self, symbol, leverage, rethrow_errors=False):
"""Set the leverage on an isolated margin position"""
path = "position/leverage"
postdict = {
'symbol': symbol,
'leverage': leverage
}
return self._curl_bitmex(path=path, postdict=postdict, verb="POST", rethrow_errors=rethrow_errors)
@authentication_required
def delta(self):
return self.position(self.symbol)['homeNotional']
@authentication_required
def buy(self, quantity, price):
"""Place a buy order.
Returns order object. ID: orderID
"""
return self.place_order(quantity, price)
@authentication_required
def sell(self, quantity, price):
"""Place a sell order.
Returns order object. ID: orderID
"""
return self.place_order(-quantity, price)
@authentication_required
def place_order(self, quantity, price):
"""Place an order."""
if price < 0:
raise Exception("Price must be positive.")
endpoint = "order"
# Generate a unique clOrdID with our prefix so we can identify it.
clOrdID = self.orderIDPrefix + base64.b64encode(uuid.uuid4().bytes).decode('utf8').rstrip('=\n')
postdict = {
'symbol': self.symbol,
'orderQty': quantity,
'price': price,
'clOrdID': clOrdID
}
return self._curl_bitmex(path=endpoint, postdict=postdict, verb="POST")
@authentication_required
def amend_bulk_orders(self, orders):
"""Amend multiple orders."""
# Note rethrow; if this fails, we want to catch it and re-tick
return self._curl_bitmex(path='order/bulk', postdict={'orders': orders}, verb='PUT', rethrow_errors=True)
@authentication_required
def create_bulk_orders(self, orders):
"""Create multiple orders."""
for order in orders:
order['clOrdID'] = self.orderIDPrefix + base64.b64encode(uuid.uuid4().bytes).decode('utf8').rstrip('=\n')
order['symbol'] = self.symbol
if self.postOnly:
order['execInst'] = 'ParticipateDoNotInitiate'
return self._curl_bitmex(path='order/bulk', postdict={'orders': orders}, verb='POST')
@authentication_required
def open_orders(self):
"""Get open orders."""
return self.ws.open_orders(self.orderIDPrefix)
@authentication_required
def http_open_orders(self):
"""Get open orders via HTTP. Used on close to ensure we catch them all."""
path = "order"
orders = self._curl_bitmex(
path=path,
query={
'filter': json.dumps({'ordStatus.isTerminated': False, 'symbol': self.symbol}),
'count': 500
},
verb="GET"
)
# Only return orders that start with our clOrdID prefix.
return [o for o in orders if str(o['clOrdID']).startswith(self.orderIDPrefix)]
@authentication_required
def cancel(self, orderID):
"""Cancel an existing order."""
path = "order"
postdict = {
'orderID': orderID,
}
return self._curl_bitmex(path=path, postdict=postdict, verb="DELETE")
@authentication_required
def withdraw(self, amount, fee, address):
path = "user/requestWithdrawal"
postdict = {
'amount': amount,
'fee': fee,
'currency': 'XBt',
'address': address
}
return self._curl_bitmex(path=path, postdict=postdict, verb="POST", max_retries=0)
def _curl_bitmex(self, path, query=None, postdict=None, timeout=None, verb=None, rethrow_errors=False,
max_retries=None):
"""Send a request to Exchange Servers."""
# Handle URL
url = self.base_url + path
if timeout is None:
timeout = self.timeout
# Default to POST if data is attached, GET otherwise
if not verb:
verb = 'POST' if postdict else 'GET'
# By default don't retry POST or PUT. Retrying GET/DELETE is okay because they are idempotent.
# In the future we could allow retrying PUT, so long as 'leavesQty' is not used (not idempotent),
# or you could change the clOrdID (set {"clOrdID": "new", "origClOrdID": "old"}) so that an amend
# can't erroneously be applied twice.
if max_retries is None:
max_retries = 0 if verb in ['POST', 'PUT'] else 3
# Auth: API Key/Secret
auth = APIKeyAuthWithExpires(self.apiKey, self.apiSecret)
def exit_or_throw(e):
if rethrow_errors:
raise e
else:
exit(1)
def retry():
self.retries += 1
if self.retries > max_retries:
raise Exception("Max retries on %s (%s) hit, raising." % (path, json.dumps(postdict or '')))
return self._curl_bitmex(path, query, postdict, timeout, verb, rethrow_errors, max_retries)
# Make the request
response = None
try:
self.logger.info("sending req to %s: %s" % (url, json.dumps(postdict or query or '')))
req = requests.Request(verb, url, json=postdict, auth=auth, params=query)
prepped = self.session.prepare_request(req)
response = self.session.send(prepped, timeout=timeout)
# Make non-200s throw
response.raise_for_status()
except requests.exceptions.HTTPError as e:
if response is None:
raise e
# 401 - Auth error. This is fatal.
if response.status_code == 401:
self.logger.error("API Key or Secret incorrect, please check and restart.")
self.logger.error("Error: " + response.text)
if postdict:
self.logger.error(postdict)
# Always exit, even if rethrow_errors, because this is fatal
exit(1)
# 404, can be thrown if order canceled or does not exist.
elif response.status_code == 404:
if verb == 'DELETE':
self.logger.error("Order not found: %s" % postdict['orderID'])
return
self.logger.error("Unable to contact the Exchange API (404). " +
"Request: %s \n %s" % (url, json.dumps(postdict)))
exit_or_throw(e)
# 429, ratelimit; cancel orders & wait until X-RateLimit-Reset
elif response.status_code == 429:
self.logger.error("Ratelimited on current request. Sleeping, then trying again. Try fewer " +
"order pairs or contact [email protected] to raise your limits. " +
"Request: %s \n %s" % (url, json.dumps(postdict)))
# Figure out how long we need to wait.
ratelimit_reset = response.headers['X-RateLimit-Reset']
to_sleep = int(ratelimit_reset) - int(time.time())
reset_str = datetime.datetime.fromtimestamp(int(ratelimit_reset)).strftime('%X')
# We're ratelimited, and we may be waiting for a long time. Cancel orders.
self.logger.warning("Canceling all known orders in the meantime.")
self.cancel([o['orderID'] for o in self.open_orders()])
self.logger.error("Your ratelimit will reset at %s. Sleeping for %d seconds." % (reset_str, to_sleep))
time.sleep(to_sleep)
# Retry the request.
return retry()
# 503 - Exchange temporary downtime, likely due to a deploy. Try again
elif response.status_code == 503:
self.logger.warning("Unable to contact the Exchange API (503), retrying. " +
"Request: %s \n %s" % (url, json.dumps(postdict)))
time.sleep(3)
return retry()
elif response.status_code == 400:
error = response.json()['error']
message = error['message'].lower() if error else ''
# Duplicate clOrdID: that's fine, probably a deploy, go get the order(s) and return it
if 'duplicate clordid' in message:
orders = postdict['orders'] if 'orders' in postdict else postdict
IDs = json.dumps({'clOrdID': [order['clOrdID'] for order in orders]})
orderResults = self._curl_bitmex('/order', query={'filter': IDs}, verb='GET')
for i, order in enumerate(orderResults):
if (
order['orderQty'] != abs(postdict['orderQty']) or
order['side'] != ('Buy' if postdict['orderQty'] > 0 else 'Sell') or
order['price'] != postdict['price'] or
order['symbol'] != postdict['symbol']):
raise Exception('Attempted to recover from duplicate clOrdID, but order returned from API ' +
'did not match POST.\nPOST data: %s\nReturned order: %s' % (
json.dumps(orders[i]), json.dumps(order)))
# All good
return orderResults
elif 'insufficient available balance' in message:
self.logger.error('Account out of funds. The message: %s' % error['message'])
exit_or_throw(Exception('Insufficient Funds'))
# If we haven't returned or re-raised yet, we get here.
self.logger.error("Unhandled Error: %s: %s" % (e, response.text))
self.logger.error("Endpoint was: %s %s: %s" % (verb, path, json.dumps(postdict)))
exit_or_throw(e)
except requests.exceptions.Timeout as e:
# Timeout, re-run this request
self.logger.warning("Timed out on request: %s (%s), retrying..." % (path, json.dumps(postdict or '')))
return retry()
except requests.exceptions.ConnectionError as e:
self.logger.warning("Unable to contact the Exchange API (%s). Please check the URL. Retrying. " +
"Request: %s %s \n %s" % (e, url, json.dumps(postdict)))
time.sleep(1)
return retry()
# Reset retry counter on success
self.retries = 0
return response.json()
使用BitMex官方交易机器人:https://github.com/BitMEX/sample-market-maker
文件结构:
settings.py
custom_strategy.py
market_maket
-----_init_.py
-----_settings_base.py
------bitmex.py
------market_maker.py
------settings.py
-----auth
------_init_.py
------AccessTokenAuth.py
------APIKeyAuth.py
------APIKeyAuthWithExpires.py
-----utils
------_init_.py
------constants.py
------dotdict.py
------errors.py
------log.py
------math.py
-----ws
------_init_.py
------ws_thread.py
首先settings.py的设置
基本参数等,DRY_RUN = True不能下单,DRY_RUN = False能下单
再修改custom_strategy.py(放到与settings.py参数设置的同级主目录)
对class CustomOrderManager(OrderManager):修改为自己的策略
添加主函数执行
def run() -> None:
order_manager = CustomOrderManager()
# Try/except just keeps ctrl-c from printing an ugly stacktrace
try:
order_manager.run_loop()
except (KeyboardInterrupt, SystemExit):
sys.exit()
if __name__ == '__main__':
run()
别人用ccxt写的代码供参考
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver import DesiredCapabilities
from selenium.common.exceptions import NoSuchElementException
from multiprocessing import Process
from subprocess import Popen
from time import gmtime, strftime
from os import system
import subprocess
import pyperclip
import datetime
import time
import os
import sys
import ccxt
import ccxt.async as ccxt_async
import threading
import json
from threading import Thread
import asyncio
import math
target_multiplier = 1
post_only = {'execInst': 'ParticipateDoNotInitiate','id':123}
bitmex = ccxt.bitmex({
'apiKey': '',
'secret': '',
})
market = 'BTC/USD'
bitmex.enableRateLimit = False
print(ccxt.__version__)
#print (dir (ccxt.bitmex ()))
#time.sleep(999)
def trader():
while True:
#bitmex.enableRateLimit = True
while True:
try:
time.sleep(2) ## Global sleep
#bitmex.privateDeleteOrderAll () ## Clear orders
orderbook = bitmex.fetch_order_book (market)
break
except (ccxt.ExchangeError, ccxt.NetworkError) as error:
print('Got an error in fetch orderbook or clearing orders', type(error).__name__, error.args)
time.sleep(1)
continue
bid = orderbook['bids'][0][0] if len (orderbook['bids']) > 0 else None
ask = orderbook['asks'][0][0] if len (orderbook['asks']) > 0 else None
cur_price_average = (ask + bid) / 2
#cur_price_average = bitmex.fetch_ticker(market)
print('======================================')
print('Current averaged price(XBT/USD):', cur_price_average)
while True:
try:
freebalance = bitmex.fetch_balance() ##error 502
break
except (ccxt.ExchangeError, ccxt.NetworkError) as error:
print('Got an error in fetch balance', type(error).__name__, error.args)
time.sleep(1)
continue
current_target1x = cur_price_average * freebalance['BTC']['free']
print('Current freebalance(XBT):', freebalance['BTC']['free'] if 'BTC' in freebalance else 'Empty BTC account')
print('Current target 1x Leverage:', current_target1x)
while True:
try:
#current_bitmex_position = bitmex.private_get_position()[0]['currentQty']
current_bitmex_position = bitmex.private_get_position()
current_bitmex_position = [item for item in current_bitmex_position if item['symbol'] == 'XBTUSD'][0]['currentQty']
print('Current bitmex position($):',current_bitmex_position)
break
except (ccxt.ExchangeError, ccxt.NetworkError) as error:
print('Got an error getting private position', type(error).__name__, error.args)
time.sleep(1)
continue
while True:
try:
contracts_amount=int(input('Input conract amount:'))
break
except ValueError:
print ('Not a number')
continue
#LONG SIDE
if (contracts_amount > 0):
while True:
try:
#bitmex.privateDeleteOrderAll ()
orderbookLong = bitmex.fetch_order_book (market)
BasicBid = orderbookLong['bids'][0][0] if len (orderbook['bids']) > 0 else None
resultLong = bitmex.create_order(symbol='BTC/USD', type='limit', side='buy', amount=contracts_amount, price=BasicBid, params = post_only)
break
except (ccxt.ExchangeError, ccxt.NetworkError) as error:
print('Got an error placing Long', type(error).__name__, error.args)
time.sleep(1)
continue
while(contracts_amount > 0):
print("BITMEX Goind LONG with:", contracts_amount)
try:
while True:
CurrentOpenOrderPrice = bitmex.fetchOpenOrders()[0]['price']
print('CurrentOpenOrderPrice:',CurrentOpenOrderPrice)
orderbookLong = bitmex.fetch_order_book (market)
BestBid = orderbookLong['bids'][0][0] if len (orderbook['bids']) > 0 else None
print('BestBid:',BestBid)
if (BestBid != CurrentOpenOrderPrice):
bitmex.edit_order(resultLong['id'],'BTC/USD','limit',resultLong['side'],price=BestBid)
print('======Price changed to', BestBid)
time.sleep(0.5)
tempLongID = resultLong['id']
print('resultLong id',resultLong['id'])
time.sleep(1)
if (resultLong['id'] != tempLongID):
break
except (ccxt.ExchangeError, ccxt.NetworkError) as error:
print('Got an error in amending price Long:', type(error).__name__, error.args)
time.sleep(1)
continue
except (IndexError) as error:
print('Order Long filled', type(error).__name__, error.args)
break
#SHORT SIDE
elif (contracts_amount < 0):
while True:
try:
#bitmex.privateDeleteOrderAll ()
orderbookShort = bitmex.fetch_order_book (market)
BasicAsk = orderbookShort['asks'][0][0] if len (orderbook['asks']) > 0 else None
resultShort = bitmex.create_order(symbol='BTC/USD', type='limit', side='sell', amount=contracts_amount, price=BasicAsk, params = post_only)
break
except (ccxt.ExchangeError, ccxt.NetworkError) as error:
print('Got an error placing Short', type(error).__name__, error.args)
time.sleep(1)
continue
while(contracts_amount < 0):
#bitmex.enableRateLimit = True
print("BITMEX Goind SHORT with:", contracts_amount)
try:
while True:
CurrentOpenOrderPrice = bitmex.fetchOpenOrders()[0]['price']
print('CurrentOpenOrderPrice:',CurrentOpenOrderPrice)
orderbook = bitmex.fetch_order_book (market)
BestAsk = orderbook['asks'][0][0] if len (orderbook['asks']) > 0 else None
print('BestAsk:',BestAsk)
if (BestAsk != CurrentOpenOrderPrice):
bitmex.edit_order(resultShort['id'],'BTC/USD','limit',resultShort['side'],price=BestAsk)
print('======Price changed to', BestAsk)
time.sleep(0.5)
tempShortID = resultShort['id']
print('resultShort id',resultShort['id'])
time.sleep(1)
if (resultShort['id'] != tempShortID):
break
except (ccxt.ExchangeError, ccxt.NetworkError) as error:
print('Got an error in amending price Short:', type(error).__name__, error.args)
time.sleep(1)
continue
except (IndexError) as error:
print('Order Short filled', type(error).__name__, error.args)
break
if __name__ == '__main__':
Thread(target = trader).start()