Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions binance/async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,20 @@ def __init__(
private_key_pass: Optional[str] = None,
https_proxy: Optional[str] = None,
time_unit: Optional[str] = None,
verbose: bool = False,
):
self.https_proxy = https_proxy
self.loop = loop or get_loop()
self._session_params: Dict[str, Any] = session_params or {}

# Convert https_proxy to requests_params format for BaseClient
if https_proxy and requests_params is None:
requests_params = {'proxies': {'http': https_proxy, 'https': https_proxy}}
elif https_proxy and requests_params is not None:
if 'proxies' not in requests_params:
requests_params['proxies'] = {}
requests_params['proxies'].update({'http': https_proxy, 'https': https_proxy})

super().__init__(
api_key,
api_secret,
Expand All @@ -62,6 +63,7 @@ def __init__(
private_key,
private_key_pass,
time_unit=time_unit,
verbose=verbose,
)

@classmethod
Expand All @@ -80,6 +82,7 @@ async def create(
private_key_pass: Optional[str] = None,
https_proxy: Optional[str] = None,
time_unit: Optional[str] = None,
verbose: bool = False,
):
self = cls(
api_key,
Expand All @@ -94,7 +97,8 @@ async def create(
private_key,
private_key_pass,
https_proxy,
time_unit
time_unit,
verbose
)
self.https_proxy = https_proxy # move this to the constructor

Expand Down Expand Up @@ -175,6 +179,20 @@ async def _request(
**kwargs,
) as response:
self.response = response

if self.verbose:
response_text = await response.text()
self.logger.debug(
"\nRequest: %s %s\nRequestHeaders: %s\nRequestBody: %s\nResponse: %s\nResponseHeaders: %s\nResponseBody: %s",
method.upper(),
uri,
headers,
data,
response.status,
dict(response.headers),
response_text[:1000] if response_text else None
)

return await self._handle_response(response)

async def _handle_response(self, response: aiohttp.ClientResponse):
Expand Down
11 changes: 11 additions & 0 deletions binance/base_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import asyncio
import hashlib
import hmac
import logging
import time
from Crypto.PublicKey import RSA, ECC
from Crypto.Hash import SHA256
Expand Down Expand Up @@ -167,6 +168,7 @@ def __init__(
private_key_pass: Optional[str] = None,
loop: Optional[asyncio.AbstractEventLoop] = None,
time_unit: Optional[str] = None,
verbose: bool = False,
):
"""Binance API Client constructor

Expand All @@ -184,10 +186,19 @@ def __init__(
:type private_key_pass: optional - str
:param time_unit: Time unit to use for requests. Supported values: "MILLISECOND", "MICROSECOND"
:type time_unit: optional - str
:param verbose: Enable verbose logging for debugging
:type verbose: bool

"""

self.tld = tld
self.verbose = verbose
self.logger = logging.getLogger(__name__)

# Set logger level based on verbose flag
# Users can override this by configuring logging externally
if verbose:
self.logger.setLevel(logging.DEBUG)
self.API_URL = self.API_URL.format(base_endpoint, tld)
self.MARGIN_API_URL = self.MARGIN_API_URL.format(base_endpoint, tld)
self.WEBSITE_URL = self.WEBSITE_URL.format(tld)
Expand Down
15 changes: 15 additions & 0 deletions binance/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def __init__(
private_key_pass: Optional[str] = None,
ping: Optional[bool] = True,
time_unit: Optional[str] = None,
verbose: bool = False,
):
super().__init__(
api_key,
Expand All @@ -46,6 +47,7 @@ def __init__(
private_key,
private_key_pass,
time_unit=time_unit,
verbose=verbose,
)

# init DNS and SSL cert
Expand Down Expand Up @@ -88,6 +90,19 @@ def _request(
data = f"{url_encoded_data}&signature={signature}"

self.response = getattr(self.session, method)(uri, headers=headers, data=data, **kwargs)

if self.verbose:
self.logger.debug(
"\nRequest: %s %s\nRequestHeaders: %s\nRequestBody: %s\nResponse: %s\nResponseHeaders: %s\nResponseBody: %s",
method.upper(),
uri,
headers,
data,
self.response.status_code,
dict(self.response.headers),
self.response.text[:1000] if self.response.text else None
)

return self._handle_response(self.response)

@staticmethod
Expand Down
21 changes: 14 additions & 7 deletions binance/ws/streams.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import time
from enum import Enum
from typing import Optional, List, Dict, Callable, Any
import logging

from binance.ws.constants import KEEPALIVE_TIMEOUT
from binance.ws.keepalive_websocket import KeepAliveWebsocket
Expand Down Expand Up @@ -40,10 +41,11 @@ class BinanceSocketManager:
WEBSOCKET_DEPTH_20 = "20"

def __init__(
self,
client: AsyncClient,
self,
client: AsyncClient,
user_timeout=KEEPALIVE_TIMEOUT,
max_queue_size: int = 100,
verbose: bool = False,
):
"""Initialise the BinanceSocketManager

Expand All @@ -52,6 +54,8 @@ def __init__(
:param user_timeout: Timeout for user socket in seconds
:param max_queue_size: Max size of the websocket queue, defaults to 100
:type max_queue_size: int
:param verbose: Enable verbose logging for WebSocket connections
:type verbose: bool
"""
self.STREAM_URL = self.STREAM_URL.format(client.tld)
self.FSTREAM_URL = self.FSTREAM_URL.format(client.tld)
Expand All @@ -65,8 +69,12 @@ def __init__(
self.testnet = self._client.testnet
self.demo = self._client.demo
self._max_queue_size = max_queue_size
self.verbose = verbose
self.ws_kwargs = {}

if verbose:
logging.getLogger('binance.ws').setLevel(logging.DEBUG)

def _get_stream_url(self, stream_url: Optional[str] = None):
if stream_url:
return stream_url
Expand Down Expand Up @@ -871,7 +879,7 @@ def multiplex_socket(self, streams: List[str]):

def options_multiplex_socket(self, streams: List[str]):
"""Start a multiplexed socket using a list of socket names.

https://developers.binance.com/docs/derivatives/option/websocket-market-streams

"""
Expand Down Expand Up @@ -966,7 +974,6 @@ def margin_socket(self):
stream_url = self.STREAM_DEMO_URL
return self._get_account_socket("margin", stream_url=stream_url)


def futures_socket(self):
"""Start a websocket for futures data

Expand Down Expand Up @@ -1040,7 +1047,7 @@ def options_ticker_socket(self, symbol: str):

API Reference: https://developers.binance.com/docs/derivatives/option/websocket-market-streams/24-hour-TICKER

Stream provides real-time 24hr ticker information for all symbols. Only symbols whose ticker info
Stream provides real-time 24hr ticker information for all symbols. Only symbols whose ticker info
changed will be sent. Updates every 1000ms.

:param symbol: The option symbol to subscribe to (e.g. "BTC-220930-18000-C")
Expand All @@ -1065,7 +1072,7 @@ def options_ticker_by_expiration_socket(self, symbol: str, expiration_date: str)

def options_recent_trades_socket(self, symbol: str):
"""Subscribe to a real-time trade information stream.

API Reference: https://developers.binance.com/docs/derivatives/option/websocket-market-streams/Trade-Streams

Stream pushes raw trade information for a specific symbol or underlying asset.
Expand All @@ -1080,7 +1087,7 @@ def options_kline_socket(
self, symbol: str, interval=AsyncClient.KLINE_INTERVAL_1MINUTE
):
"""Subscribe to a Kline/Candlestick data stream.

API Reference: https://developers.binance.com/docs/derivatives/option/websocket-market-streams/Kline-Candlestick-Streams

Stream pushes updates to the current klines/candlestick every 1000ms (if existing).
Expand Down
20 changes: 19 additions & 1 deletion binance/ws/threaded_stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,28 @@ def __init__(
session_params: Optional[Dict[str, Any]] = None,
https_proxy: Optional[str] = None,
_loop: Optional[asyncio.AbstractEventLoop] = None,
verbose: bool = False,
):
"""Initialise the BinanceSocketManager"""
"""Initialise the ThreadedApiManager
:param api_key: Binance API key
:param api_secret: Binance API secret
:param requests_params: optional - Dictionary of requests params
:param tld: optional - Top level domain, default is 'com'
:param testnet: optional - Use testnet endpoint
:param session_params: optional - Session params for aiohttp
:param https_proxy: optional - Proxy URL
:param _loop: optional - Event loop
:param verbose: Enable verbose logging for WebSocket connections
:type verbose: bool
"""
super().__init__()
self._loop: asyncio.AbstractEventLoop = get_loop() if _loop is None else _loop
self._client: Optional[AsyncClient] = None
self._running: bool = True
self._socket_running: Dict[str, bool] = {}
self._log = logging.getLogger(__name__)
self.verbose = verbose
self._client_params = {
"api_key": api_key,
"api_secret": api_secret,
Expand All @@ -34,8 +48,12 @@ def __init__(
"testnet": testnet,
"session_params": session_params,
"https_proxy": https_proxy,
"verbose": verbose,
}

if verbose:
logging.getLogger('binance.ws').setLevel(logging.DEBUG)

async def _before_socket_listener_start(self): ...

async def socket_listener(self):
Expand Down
12 changes: 12 additions & 0 deletions docs/faqs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,15 @@ Check recvWindow is an integer and not a string.
*A2*: You may need to regenerate your API Key and Secret

*A3*: You may be attempting to access the API from a Chinese IP address, these are now restricted by Binance.

*Q: How can I debug API issues?*

*A*: Enable verbose mode to see detailed request and response information:

.. code:: python
client = Client(api_key, api_secret, verbose=True)
This will log all HTTP requests and responses, including headers, body, and status codes. This is particularly helpful for debugging authentication issues, understanding API behavior, and troubleshooting network problems.

See the Logging section in the `Getting Started guide <overview.html#verbose-mode>`_ for more details.
Loading
Loading