Files
oh_my_bot_app/lib/pybinancefutures.py
2025-12-06 22:31:19 +09:00

629 lines
20 KiB
Python

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) == '<Response [200]>':
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) == '<Response [200]>':
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) == '<Response [200]>':
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) == '<Response [200]>':
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)