Open mshmurda opened 3 years ago
To avoid hitting the API rate limit, you'll want to take a look at the headers that are returned by Binance on each call.
If you take a look at line 196 of client.py, you'll see that it's setting self.response equal to the response from Binance:
self.response = getattr(self.session, method)(uri, **kwargs)
The "self" in question, is the Client object created previously. This means we'll have access to the headers. Below is a link to an overly simplistic approach to avoiding rate limits. Please note that you can use up to 1200 weight per minute, and the threshold of 6 below is simply for demonstration purposes.
https://gist.github.com/uneasyguy/2f2053e918e773791991bfe60060d6d8
You could also modify the client to automatically reduce the rate, at least in case of the non-async client:
from __future__ import annotations
import logging
import time
from datetime import datetime
import requests
from binance.exceptions import BinanceAPIException
import binance
logger = logging.getLogger(__name__)
author = ""
from functools import wraps
def rate_limiter(func, client_obj: binance.client, available_weight_1m=1150, delay_buffer_sec=5):
used_api_weight = 0
last_call_ts = time.time()
@wraps(func)
def wrapper(*args, **kwargs):
nonlocal used_api_weight
nonlocal last_call_ts
while True:
sec_elapsed = time.time() - last_call_ts
seconds_into_current_minute = datetime.now().second
if available_weight_1m <= used_api_weight:
logging.debug(f"{available_weight_1m=} < {used_api_weight=}, {sec_elapsed=}")
wait_seconds = 60 + delay_buffer_sec - seconds_into_current_minute - sec_elapsed
if wait_seconds > 0:
logging.warning(
f"Last call from {sec_elapsed:.2f} sec ago hit {used_api_weight}. Sleep for {wait_seconds:.0f} sec."
)
time.sleep(wait_seconds)
last_call_ts = time.time()
try:
result = func(*args, **kwargs)
response: requests.Response = client_obj.response
used_api_weight = int(response.headers.get("x-mbx-used-weight-1m", 0))
logging.debug(f"Already used {used_api_weight} API weight, {seconds_into_current_minute=}.")
return result
except BinanceAPIException as e:
if e.status_code in [429, 418]:
response: requests.Response = e.response
wait_seconds = int(response.headers.get("Retry-After", 66))
logging.warning(f"API instructs us to wait for {wait_seconds} seconds ({e.status_code}).")
time.sleep(wait_seconds)
return wrapper
def main():
logging.basicConfig(level=logging.DEBUG)
try:
client = binance.Client()
client._request = rate_limiter(client._request, client, available_weight_1m=20)
for _ in range(15):
client.ping()
except Exception as e:
print(f"Whoops, we ran into the following error: {e}")
if __name__ == "__main__":
main()
Describe the bug I run receive the error:
requests.exceptions.SSLError: HTTPSConnectionPool(host='api.binance.us', port=443): Max retries exceeded with url: /api/v1/ping (Caused by SSLError("Can't connect to HTTPS URL because the SSL module is not available."))
which I think occurs when I reach the API rate limit. When calling the Client module (below) there is the option to pass requests parameters. I have looked through the documentation but could not find any examples of what these parameters can be.
class binance.client.Client(api_key=None, api_secret=None, requests_params=None, tld='us')
Does anyone know what kind of params might be useful for not reaching the API rate limit?
**Environment