from sys import stdout import time import pandas as pd import websocket import requests import urllib import json import hmac import hashlib import threading ''' That library have got just a part of Documentation If you want to know what does certain function use can find more on ! ! ! https://binance-docs.github.io/apidocs/futures/en ! ! ! ''' class MarketData: def __init__(self, testnet: bool = False, symbol: str = 'btcusdt', interval: str = '1m'): ''' To use TESTNET Binance Futures API -> testnet = True To change currency pair -> symbol = 'ethusdt' To change interval -> interval = '5m' (m -> minutes h -> hours d -> days w -> weeks M -> months; Valid values: [1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M]) ''' if testnet == True: self.http_way = 'http://testnet.binancefuture.com/fapi/v1/' else: self.http_way = 'http://fapi.binance.com/fapi/v1/' self.wss_way = 'wss://fstream.binance.com/ws/' self.interval = interval self.symbol = symbol.lower() def ping(self): return requests.get(f'{self.http_way}ping').json() def server_time(self): return requests.get(f'{self.http_way}time').json() def exchange_info(self): return requests.get(f'{self.http_way}exchangeInfo').json() def order_book(self, limit: int = 100): ''' To change limit -> limit = 1000 (Valid limits:[5, 10, 20, 50, 100, 500, 1000]) ''' return requests.get(f'{self.http_way}depth?limit={limit}').json() def recent_trades(self, limit: int = 500): ''' To change limit -> limit = 1000 (max 1000) ''' return requests.get(f'{self.http_way}trades?symbol={self.symbol}&limit={limit}').json() def historical_trades(self, limit: int = 500): ''' To change limit -> limit = 1000 (max 1000) ''' return requests.get(f'{self.http_way}historicalTrades?symbol={self.symbol}&limit={limit}').json() def aggregate_trades(self, fromId: int = None, startTime: int = None, endTime: int = None, limit: int = 500): ''' To change limit -> limit = 1000 (max 1000) To use fromId -> fromId = 1231 To use start time and end time -> startTime = 1573661424937 -> endTime = 1573661428706 ''' return requests.get( f'{self.http_way}aggTrades?symbol={self.symbol}&fromId={fromId}&startTime={startTime}&endTime={endTime}&limit={limit}').json() def candles_data(self, interval: str = '1m', startTime: int = None, endTime: int = None, limit: int = 500): ''' To change interval -> interval = '5m' (Valid values: [1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M]) To use limit -> limit = 1231 (Default 500; max 1500) To use start time and end time -> startTime = 1573661424937 -> endTime = 1573661428706 ''' return requests.get( f'{self.http_way}klines?symbol={self.symbol}&interval={interval}&startTime={startTime}&endTime={endTime}&limit={limit}').json() def mark_price(self): return requests.get(f'{self.http_way}premiumIndex?symbol={self.symbol}').json() def funding_rate(self, startTime: int = None, endTime: int = None, limit: int = 100): ''' To change limit -> limit = 1000 (max 1096) To use start time and end time -> startTime = 1573661424937 -> endTime = 1573661428706 ''' return requests.get( f'{self.http_way}klines?symbol={self.symbol}&startTime={startTime}&endTime={endTime}&limit={limit}').json() def ticker_price_24h(self, symbol: bool = False): if symbol is True: return requests.get(f'{self.http_way}ticker/24hr?symbol={self.symbol}').json() else: return requests.get(f'{self.http_way}ticker/24hr').json() def ticker_price_symbol(self, symbol: bool = False): if symbol is True: return requests.get(f'{self.http_way}ticker/price?symbol={self.symbol}').json() else: return requests.get(f'{self.http_way}ticker/price').json() def ticker_orderbook_symbol(self, symbol: bool = False): if symbol is True: return requests.get(f'{self.http_way}ticker/bookTicker?symbol={self.symbol}').json() else: return requests.get(f'{self.http_way}ticker/bookTicker').json() def load_last_candles(self, days=30): limit = 1440 one_hour_in_milliseconds = 3600000 one_day_in_milliseconds = one_hour_in_milliseconds * 24 startTime = int(round(time.time() * 1000)) - (one_day_in_milliseconds * 30) data = [] for k in range(days): r = requests.get( f"{self.http_way}klines?symbol={self.symbol}&interval={self.interval}&limit={limit}&starttime={startTime}") startTime += one_day_in_milliseconds response = r.json() for i in range(len(response)): data.append(response[i]) stdout.write(f'\r{k + 1} of {days}') stdout.flush() print('\n') ''' last_req = requests.get(f"{self.http_way}klines?symbol={self.symbol}&interval={self.interval}&limit=1") last_res = last_req.json() last_res = last_res[0] if last_res[0] != data[-1][0]: print("New candle added!") data.append(last_res) del data[0] ''' df = pd.DataFrame(data) df = df.iloc[:, :6] df.columns = (['Date', 'Open', 'High', 'Low', 'Close', 'Volume']) df['Date'] = pd.to_datetime(df['Date'], unit='ms') df['Date'] = df['Date'].map(lambda x: x.strftime("%Y-%m-%d %H:%M")) df = df.astype({'Open': 'float64', 'High': 'float64', 'Low': 'float64', 'Close': 'float64', 'Volume': 'float64'}) return df # %% class WebsocketMarket: def __init__(self, on_message=lambda ws, message: (stdout.write(f'\r{json.loads(message)}'), stdout.flush()), on_error=lambda ws, error: print(error), on_close=lambda ws: print("### closed ###"), testnet: bool = False, symbol: str = 'btcusdt', speed: str = '100ms'): ''' To use TESTNET Binance Futures API -> testnet = True To change currency pair -> symbol = 'ethusdt' To change speed -> speed = '250ms' ''' if testnet == True: self.wss_way = 'wss://stream.binancefuture.com/ws/' else: self.wss_way = 'wss://fstream.binance.com/ws/' self.interval = '1m' self.symbol = symbol.lower() self.speed = speed self.on_message = on_message self.on_error = on_error self.on_close = on_close @staticmethod def parced(func): def parced_func(ws, msg): return func(ws, json.loads(msg)) return parced_func def open_socket(self, way): thread = threading.Thread(target=lambda: self._open_socket(way)) thread.start() def _open_socket(self, way): websocket.enableTrace(False) on_message_with_parce = WebsocketMarket.parced(self.on_message) self.ws = websocket.WebSocketApp(way, on_message=on_message_with_parce, on_close=self.on_close, on_error=self.on_error) self.ws.run_forever() def aggregate_trade_socket(self): self.open_socket(f'{self.wss_way}{self.symbol}@aggTrade') def mark_price_socket(self): self.open_socket(f'{self.wss_way}{self.symbol}@markPrice') def candle_socket(self): self.open_socket(f'{self.wss_way}{self.symbol}@kline_{self.interval}') def individual_symbol_mini_ticker(self): self.open_socket(f'{self.wss_way}{self.symbol}@miniTicker') def individual_symbol_ticker(self): self.open_socket(f'{self.wss_way}{self.symbol}@ticker') def all_book_ticker(self): self.open_socket(f'{self.wss_way}!bookTicker') def partial_book_depth_socket(self, levels: int = 20): ''' To change count of top bids and asks -> levels = 5 (5, 10 or 20 values are valid) ''' self.open_socket(f'{self.wss_way}{self.symbol}@depth{levels}@{self.speed}') def diff_book_depth_socket(self): self.open_socket(f'{self.wss_way}{self.symbol}@depth@{self.speed}') # %% class Client: def __init__(self, api_key: str, sec_key: str, testnet: bool = False, symbol: str = 'BTCUSDT'): ''' In any case you must give your API key and API secret to work with Client To use TESTNET Binance Futures API -> testnet = True To change currency pair -> symbol = 'ethusdt' ''' self.api_key = api_key self.sec_key = sec_key self.http_way = 'http://fapi.binance.com/fapi/v1/' self.symbol = symbol self.X_MBX_APIKEY = {"X-MBX-APIKEY": self.api_key} if testnet == True: self.http_way = 'http://testnet.binancefuture.com/fapi/v1/' self.wss_way = 'wss://stream.binancefuture.com/ws/' else: self.http_way = 'http://fapi.binance.com/fapi/v1/' self.wss_way = 'wss://fstream.binance.com/ws/' def open_socket(self, way, on_message, on_error, on_close): websocket.enableTrace(False) self.ws = websocket.WebSocketApp(way, on_message=on_message, on_error=on_error, on_close=on_close) self.ws.run_forever() def _get_request(self, req, query): r = requests.get(self.request_url(req=req, query=query, signature=self.get_sign(query=query)), headers=self.X_MBX_APIKEY) try: return r.json() except: if str(r) == '': return dict([]) else: return r def _post_request(self, req, query): r = requests.post(self.request_url(req=req, query=query, signature=self.get_sign(query=query)), headers=self.X_MBX_APIKEY) try: return r.json() except: if str(r) == '': return dict([]) else: return r def _delete_request(self, req, query): r = requests.delete(self.request_url(req=req, query=query, signature=self.get_sign(query=query)), headers=self.X_MBX_APIKEY) try: return r.json() except: if str(r) == '': return dict([]) else: return r def _put_request(self, req, query): r = requests.put(self.request_url(req=req, query=query, signature=self.get_sign(query=query)), headers=self.X_MBX_APIKEY) try: return r.json() except: if str(r) == '': return dict([]) else: return r @staticmethod def timestamp(): return int(time.time() * 1000) - 1000 return int(time.time() * 1000) def get_sign(self, query): return hmac.new(self.sec_key.encode('utf-8'), query.encode('utf-8'), hashlib.sha256).hexdigest() def request_url(self, req, query, signature): return self.http_way + req + query + '&signature=' + signature def new_order(self, side: str, orderType: str, quantity: float, timeInForce: float = None, reduceOnly: bool = False, price: float = None, newClientOrderId: str = None, stopPrice: float = None, workingType: str = None): ''' POST Choose side: SELL or BUY Choose quantity: 0.001 Choose price: 7500 To change order type -> orderType = 'MARKET' To change time in force -> timeInForce = 'IOC' ''' req = 'order?' querystring = {'symbol': self.symbol, 'side': side, 'type': orderType, 'quantity': quantity, 'reduceOnly': reduceOnly} if timeInForce is not None: querystring['timeInForce'] = timeInForce if price is not None: querystring['price'] = price if newClientOrderId is not None: querystring['newClientOrderId'] = newClientOrderId if stopPrice is not None: querystring['stopPrice'] = stopPrice if workingType is not None: querystring['workingType'] = workingType querystring['timestamp'] = self.timestamp() querystring = urllib.parse.urlencode(querystring) return self._post_request(req, querystring) def query_order(self, orderId): ''' GET Choose orderId: 156316486 ''' req = 'order?' querystring = urllib.parse.urlencode({'symbol': self.symbol, 'orderId': orderId, 'timestamp': self.timestamp()}) return self._get_request(req, querystring) def cancel_order(self, orderId): ''' DELETE Choose orderId: 156316486 ''' req = 'order?' querystring = urllib.parse.urlencode({'symbol': self.symbol, 'orderId': orderId, 'timestamp': self.timestamp()}) return self._delete_request(req, querystring) def current_open_orders(self): ''' GET ''' req = 'openOrders?' querystring = urllib.parse.urlencode({'timestamp': self.timestamp()}) return self._get_request(req, querystring) def all_orders(self, limit: int = 1000, startTime: int = None, endTime: int = None): ''' GET To change limit of output orders -> limit = 1000 (max value is 1000) To use start time and end time -> startTime = 1573661424937 -> endTime = 1573661428706 ''' req = 'allOrders?' querystring = urllib.parse.urlencode({'symbol': self.symbol, 'timestamp': self.timestamp(), 'limit': limit, 'startTime': startTime, 'endTime': endTime}) return self._get_request(req, querystring) def balance(self): ''' GET ''' req = 'balance?' querystring = urllib.parse.urlencode({'timestamp': self.timestamp()}) return self._get_request(req, querystring) def account_info(self): ''' GET ''' req = 'account?' querystring = urllib.parse.urlencode({'timestamp': self.timestamp()}) return self._get_request(req, querystring) def change_leverage(self, leverage): ''' POST To change leverage -> leverage = 25 (from 1 to 125 are valid values) ''' req = 'leverage?' querystring = urllib.parse.urlencode({'symbol': self.symbol, 'leverage': leverage, 'timestamp': self.timestamp()}) return self._post_request(req, querystring) def position_info(self): '''GET''' req = 'positionRisk?' querystring = urllib.parse.urlencode({'timestamp': self.timestamp()}) return self._get_request(req, querystring) def trade_list(self, limit: int = 1000, startTime: int = None, endTime: int = None): ''' GET To change limit of output orders -> limit = 1000 (max value is 1000) To use start time and end time -> startTime = 1573661424937 -> endTime = 1573661428706 ''' req = 'userTrades?' querystring = urllib.parse.urlencode({'symbol': self.symbol, 'timestamp': self.timestamp(), 'limit': limit, 'startTime': startTime, 'endTime': endTime}) return self._get_request(req, querystring) def income_history(self, limit: int = 1000): ''' GET To change limit of output orders -> limit = 1000 (max value is 1000) ''' req = 'income?' querystring = urllib.parse.urlencode({'symbol': self.symbol, 'timestamp': self.timestamp(), 'limit': limit}) return self._get_request(req, querystring) def start_stream(self): ''' POST ''' req = 'listenKey?' querystring = urllib.parse.urlencode({'timestamp': self.timestamp()}) return self._post_request(req, querystring) def get_listen_key(self): return self.start_stream()['listenKey'] def keepalive_stream(self): ''' PUT ''' req = 'listenKey?' querystring = urllib.parse.urlencode({'timestamp': self.timestamp()}) return self._put_request(req, querystring) def close_stream(self): ''' DELETE ''' req = 'listenKey?' querystring = urllib.parse.urlencode({'timestamp': self.timestamp()}) return self._delete_request(req, querystring) def user_update_socket(self, on_message, on_error, on_close): listen_key = self.get_listen_key() self.open_socket(f'{self.wss_way}{listen_key}', on_message, on_error, on_close) def stop_user_update_socket(self): self.close_stream() def on_new_candle_loaded(ws, candle): print(ws, candle)