mirror of
https://github.com/aljazceru/bitfinex-api-py.git
synced 2025-12-19 06:44:22 +01:00
Rename BfxWebsocketClient, BfxWebsocketBucket and BfxWebsocketInputs to BfxWebSocketClient, BfxWebSocketBucket and BfxWebSocketInputs.
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
from typing import List, Literal, Optional
|
from typing import List, Literal, Optional
|
||||||
|
|
||||||
from .rest import BfxRestInterface
|
from .rest import BfxRestInterface
|
||||||
from .websocket import BfxWebsocketClient
|
from .websocket import BfxWebSocketClient
|
||||||
from .urls import REST_HOST, WSS_HOST
|
from .urls import REST_HOST, WSS_HOST
|
||||||
|
|
||||||
class Client:
|
class Client:
|
||||||
@@ -27,7 +27,7 @@ class Client:
|
|||||||
credentials=credentials
|
credentials=credentials
|
||||||
)
|
)
|
||||||
|
|
||||||
self.wss = BfxWebsocketClient(
|
self.wss = BfxWebSocketClient(
|
||||||
host=wss_host,
|
host=wss_host,
|
||||||
credentials=credentials,
|
credentials=credentials,
|
||||||
wss_timeout=wss_timeout,
|
wss_timeout=wss_timeout,
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from .test_rest_serializers import TestRestSerializers
|
from .test_rest_serializers import TestRestSerializers
|
||||||
from .test_websocket_serializers import TestWebsocketSerializers
|
from .test_websocket_serializers import TestWebSocketSerializers
|
||||||
from .test_labeler import TestLabeler
|
from .test_labeler import TestLabeler
|
||||||
from .test_notification import TestNotification
|
from .test_notification import TestNotification
|
||||||
|
|
||||||
def suite():
|
def suite():
|
||||||
return unittest.TestSuite([
|
return unittest.TestSuite([
|
||||||
unittest.makeSuite(TestRestSerializers),
|
unittest.makeSuite(TestRestSerializers),
|
||||||
unittest.makeSuite(TestWebsocketSerializers),
|
unittest.makeSuite(TestWebSocketSerializers),
|
||||||
unittest.makeSuite(TestLabeler),
|
unittest.makeSuite(TestLabeler),
|
||||||
unittest.makeSuite(TestNotification),
|
unittest.makeSuite(TestNotification),
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from ..labeler import _Type
|
|||||||
|
|
||||||
from ..websocket import serializers
|
from ..websocket import serializers
|
||||||
|
|
||||||
class TestWebsocketSerializers(unittest.TestCase):
|
class TestWebSocketSerializers(unittest.TestCase):
|
||||||
def test_websocket_serializers(self):
|
def test_websocket_serializers(self):
|
||||||
for serializer in map(serializers.__dict__.get, serializers.__serializers__):
|
for serializer in map(serializers.__dict__.get, serializers.__serializers__):
|
||||||
self.assertTrue(issubclass(serializer.klass, _Type),
|
self.assertTrue(issubclass(serializer.klass, _Type),
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
from .client import BfxWebsocketClient, BfxWebsocketBucket, BfxWebsocketInputs
|
from .client import BfxWebSocketClient, BfxWebSocketBucket, BfxWebSocketInputs
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
from .bfx_websocket_client import BfxWebsocketClient
|
from .bfx_websocket_client import BfxWebSocketClient
|
||||||
from .bfx_websocket_bucket import BfxWebsocketBucket
|
from .bfx_websocket_bucket import BfxWebSocketBucket
|
||||||
from .bfx_websocket_inputs import BfxWebsocketInputs
|
from .bfx_websocket_inputs import BfxWebSocketInputs
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ def _require_websocket_connection(function: F) -> F:
|
|||||||
|
|
||||||
return cast(F, wrapper)
|
return cast(F, wrapper)
|
||||||
|
|
||||||
class BfxWebsocketBucket:
|
class BfxWebSocketBucket:
|
||||||
VERSION = 2
|
VERSION = 2
|
||||||
|
|
||||||
MAXIMUM_SUBSCRIPTIONS_AMOUNT = 25
|
MAXIMUM_SUBSCRIPTIONS_AMOUNT = 25
|
||||||
@@ -81,7 +81,7 @@ class BfxWebsocketBucket:
|
|||||||
|
|
||||||
@_require_websocket_connection
|
@_require_websocket_connection
|
||||||
async def subscribe(self, channel, sub_id=None, **kwargs):
|
async def subscribe(self, channel, sub_id=None, **kwargs):
|
||||||
if len(self.subscriptions) + len(self.pendings) == BfxWebsocketBucket.MAXIMUM_SUBSCRIPTIONS_AMOUNT:
|
if len(self.subscriptions) + len(self.pendings) == BfxWebSocketBucket.MAXIMUM_SUBSCRIPTIONS_AMOUNT:
|
||||||
raise TooManySubscriptions("The client has reached the maximum number of subscriptions.")
|
raise TooManySubscriptions("The client has reached the maximum number of subscriptions.")
|
||||||
|
|
||||||
subscription = {
|
subscription = {
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ import traceback, json, asyncio, hmac, hashlib, time, socket, random, websockets
|
|||||||
|
|
||||||
from pyee.asyncio import AsyncIOEventEmitter
|
from pyee.asyncio import AsyncIOEventEmitter
|
||||||
|
|
||||||
from .bfx_websocket_bucket import _HEARTBEAT, F, _require_websocket_connection, BfxWebsocketBucket
|
from .bfx_websocket_bucket import _HEARTBEAT, F, _require_websocket_connection, BfxWebSocketBucket
|
||||||
|
|
||||||
from .bfx_websocket_inputs import BfxWebsocketInputs
|
from .bfx_websocket_inputs import BfxWebSocketInputs
|
||||||
from ..handlers import PublicChannelsHandler, AuthenticatedEventsHandler
|
from ..handlers import PublicChannelsHandler, AuthenticatedEventsHandler
|
||||||
from ..exceptions import WebsocketAuthenticationRequired, InvalidAuthenticationCredentials, EventNotSupported, \
|
from ..exceptions import WebSocketAuthenticationRequired, InvalidAuthenticationCredentials, EventNotSupported, \
|
||||||
ZeroConnectionsError, ReconnectionTimeoutError, OutdatedClientVersion
|
ZeroConnectionsError, ReconnectionTimeoutError, OutdatedClientVersion
|
||||||
|
|
||||||
from ...utils.json_encoder import JSONEncoder
|
from ...utils.json_encoder import JSONEncoder
|
||||||
@@ -22,7 +22,7 @@ from ...utils.logger import ColorLogger, FileLogger
|
|||||||
def _require_websocket_authentication(function: F) -> F:
|
def _require_websocket_authentication(function: F) -> F:
|
||||||
async def wrapper(self, *args, **kwargs):
|
async def wrapper(self, *args, **kwargs):
|
||||||
if hasattr(self, "authentication") and not self.authentication:
|
if hasattr(self, "authentication") and not self.authentication:
|
||||||
raise WebsocketAuthenticationRequired("To perform this action you need to " \
|
raise WebSocketAuthenticationRequired("To perform this action you need to " \
|
||||||
"authenticate using your API_KEY and API_SECRET.")
|
"authenticate using your API_KEY and API_SECRET.")
|
||||||
|
|
||||||
await _require_websocket_connection(function)(self, *args, **kwargs)
|
await _require_websocket_connection(function)(self, *args, **kwargs)
|
||||||
@@ -50,8 +50,8 @@ class _Delay:
|
|||||||
return (self.__backoff_delay == _Delay.BACKOFF_MIN) \
|
return (self.__backoff_delay == _Delay.BACKOFF_MIN) \
|
||||||
and self.__initial_delay or self.__backoff_delay
|
and self.__initial_delay or self.__backoff_delay
|
||||||
|
|
||||||
class BfxWebsocketClient:
|
class BfxWebSocketClient:
|
||||||
VERSION = BfxWebsocketBucket.VERSION
|
VERSION = BfxWebSocketBucket.VERSION
|
||||||
|
|
||||||
MAXIMUM_CONNECTIONS_AMOUNT = 20
|
MAXIMUM_CONNECTIONS_AMOUNT = 20
|
||||||
|
|
||||||
@@ -78,11 +78,11 @@ class BfxWebsocketClient:
|
|||||||
|
|
||||||
self.handler = AuthenticatedEventsHandler(event_emitter=self.event_emitter)
|
self.handler = AuthenticatedEventsHandler(event_emitter=self.event_emitter)
|
||||||
|
|
||||||
self.inputs = BfxWebsocketInputs(handle_websocket_input=self.__handle_websocket_input)
|
self.inputs = BfxWebSocketInputs(handle_websocket_input=self.__handle_websocket_input)
|
||||||
|
|
||||||
if log_filename is None:
|
if log_filename is None:
|
||||||
self.logger = ColorLogger("BfxWebsocketClient", level=log_level)
|
self.logger = ColorLogger("BfxWebSocketClient", level=log_level)
|
||||||
else: self.logger = FileLogger("BfxWebsocketClient", level=log_level, filename=log_filename)
|
else: self.logger = FileLogger("BfxWebSocketClient", level=log_level, filename=log_filename)
|
||||||
|
|
||||||
self.event_emitter.add_listener("error",
|
self.event_emitter.add_listener("error",
|
||||||
lambda exception: self.logger.error(f"{type(exception).__name__}: {str(exception)}" + "\n" +
|
lambda exception: self.logger.error(f"{type(exception).__name__}: {str(exception)}" + "\n" +
|
||||||
@@ -97,13 +97,13 @@ class BfxWebsocketClient:
|
|||||||
self.logger.info("With connections set to 0 it will not be possible to subscribe to any public channel. " \
|
self.logger.info("With connections set to 0 it will not be possible to subscribe to any public channel. " \
|
||||||
"Attempting a subscription will cause a ZeroConnectionsError to be thrown.")
|
"Attempting a subscription will cause a ZeroConnectionsError to be thrown.")
|
||||||
|
|
||||||
if connections > BfxWebsocketClient.MAXIMUM_CONNECTIONS_AMOUNT:
|
if connections > BfxWebSocketClient.MAXIMUM_CONNECTIONS_AMOUNT:
|
||||||
self.logger.warning(f"It is not safe to use more than {BfxWebsocketClient.MAXIMUM_CONNECTIONS_AMOUNT} " \
|
self.logger.warning(f"It is not safe to use more than {BfxWebSocketClient.MAXIMUM_CONNECTIONS_AMOUNT} " \
|
||||||
f"buckets from the same connection ({connections} in use), the server could momentarily " \
|
f"buckets from the same connection ({connections} in use), the server could momentarily " \
|
||||||
"block the client with <429 Too Many Requests>.")
|
"block the client with <429 Too Many Requests>.")
|
||||||
|
|
||||||
for _ in range(connections):
|
for _ in range(connections):
|
||||||
self.buckets += [BfxWebsocketBucket(self.host, self.event_emitter, self.events_per_subscription)]
|
self.buckets += [BfxWebSocketBucket(self.host, self.event_emitter, self.events_per_subscription)]
|
||||||
|
|
||||||
await self.__connect()
|
await self.__connect()
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ class BfxWebsocketClient:
|
|||||||
|
|
||||||
self.websocket = websocket
|
self.websocket = websocket
|
||||||
|
|
||||||
coroutines = [ BfxWebsocketBucket.connect(bucket) for bucket in self.buckets ]
|
coroutines = [ BfxWebSocketBucket.connect(bucket) for bucket in self.buckets ]
|
||||||
|
|
||||||
tasks = [ asyncio.create_task(coroutine) for coroutine in coroutines ]
|
tasks = [ asyncio.create_task(coroutine) for coroutine in coroutines ]
|
||||||
|
|
||||||
@@ -150,13 +150,13 @@ class BfxWebsocketClient:
|
|||||||
|
|
||||||
if isinstance(message, dict):
|
if isinstance(message, dict):
|
||||||
if message["event"] == "info" and "version" in message:
|
if message["event"] == "info" and "version" in message:
|
||||||
if BfxWebsocketClient.VERSION != message["version"]:
|
if BfxWebSocketClient.VERSION != message["version"]:
|
||||||
raise OutdatedClientVersion("Mismatch between the client version and the server " \
|
raise OutdatedClientVersion("Mismatch between the client version and the server " \
|
||||||
"version. Update the library to the latest version to continue (client version: " \
|
"version. Update the library to the latest version to continue (client version: " \
|
||||||
f"{BfxWebsocketClient.VERSION}, server version: {message['version']}).")
|
f"{BfxWebSocketClient.VERSION}, server version: {message['version']}).")
|
||||||
elif message["event"] == "info" and message["code"] == 20051:
|
elif message["event"] == "info" and message["code"] == 20051:
|
||||||
rcvd = websockets.frames.Close(code=1012,
|
rcvd = websockets.frames.Close(code=1012,
|
||||||
reason="Stop/Restart Websocket Server (please reconnect).")
|
reason="Stop/Restart WebSocket Server (please reconnect).")
|
||||||
|
|
||||||
raise websockets.exceptions.ConnectionClosedError(rcvd=rcvd, sent=None)
|
raise websockets.exceptions.ConnectionClosedError(rcvd=rcvd, sent=None)
|
||||||
elif message["event"] == "auth":
|
elif message["event"] == "auth":
|
||||||
@@ -267,12 +267,12 @@ class BfxWebsocketClient:
|
|||||||
|
|
||||||
def on(self, *events, callback = None):
|
def on(self, *events, callback = None):
|
||||||
for event in events:
|
for event in events:
|
||||||
if event not in BfxWebsocketClient.EVENTS:
|
if event not in BfxWebSocketClient.EVENTS:
|
||||||
raise EventNotSupported(f"Event <{event}> is not supported. To get a list " \
|
raise EventNotSupported(f"Event <{event}> is not supported. To get a list " \
|
||||||
"of available events print BfxWebsocketClient.EVENTS")
|
"of available events print BfxWebSocketClient.EVENTS")
|
||||||
|
|
||||||
def _register_event(event, function):
|
def _register_event(event, function):
|
||||||
if event in BfxWebsocketClient.ONCE_EVENTS:
|
if event in BfxWebSocketClient.ONCE_EVENTS:
|
||||||
self.event_emitter.once(event, function)
|
self.event_emitter.once(event, function)
|
||||||
else: self.event_emitter.on(event, function)
|
else: self.event_emitter.on(event, function)
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from typing import Union, Optional, List, Tuple
|
|||||||
from .. enums import OrderType, FundingOfferType
|
from .. enums import OrderType, FundingOfferType
|
||||||
from ...utils.json_encoder import JSON
|
from ...utils.json_encoder import JSON
|
||||||
|
|
||||||
class BfxWebsocketInputs:
|
class BfxWebSocketInputs:
|
||||||
def __init__(self, handle_websocket_input):
|
def __init__(self, handle_websocket_input):
|
||||||
self.__handle_websocket_input = handle_websocket_input
|
self.__handle_websocket_input = handle_websocket_input
|
||||||
|
|
||||||
|
|||||||
@@ -1,66 +1,66 @@
|
|||||||
from .. exceptions import BfxBaseException
|
from .. exceptions import BfxBaseException
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"BfxWebsocketException",
|
"BfxWebSocketException",
|
||||||
|
|
||||||
"ConnectionNotOpen",
|
"ConnectionNotOpen",
|
||||||
"TooManySubscriptions",
|
"TooManySubscriptions",
|
||||||
"ZeroConnectionsError",
|
"ZeroConnectionsError",
|
||||||
"ReconnectionTimeoutError",
|
"ReconnectionTimeoutError",
|
||||||
"WebsocketAuthenticationRequired",
|
"WebSocketAuthenticationRequired",
|
||||||
"InvalidAuthenticationCredentials",
|
"InvalidAuthenticationCredentials",
|
||||||
"EventNotSupported",
|
"EventNotSupported",
|
||||||
"HandlerNotFound",
|
"HandlerNotFound",
|
||||||
"OutdatedClientVersion"
|
"OutdatedClientVersion"
|
||||||
]
|
]
|
||||||
|
|
||||||
class BfxWebsocketException(BfxBaseException):
|
class BfxWebSocketException(BfxBaseException):
|
||||||
"""
|
"""
|
||||||
Base class for all custom exceptions in bfxapi/websocket/exceptions.py.
|
Base class for all custom exceptions in bfxapi/websocket/exceptions.py.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class ConnectionNotOpen(BfxWebsocketException):
|
class ConnectionNotOpen(BfxWebSocketException):
|
||||||
"""
|
"""
|
||||||
This error indicates an attempt to communicate via websocket before starting the connection with the servers.
|
This error indicates an attempt to communicate via websocket before starting the connection with the servers.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class TooManySubscriptions(BfxWebsocketException):
|
class TooManySubscriptions(BfxWebSocketException):
|
||||||
"""
|
"""
|
||||||
This error indicates a subscription attempt after reaching the limit of simultaneous connections.
|
This error indicates a subscription attempt after reaching the limit of simultaneous connections.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class ZeroConnectionsError(BfxWebsocketException):
|
class ZeroConnectionsError(BfxWebSocketException):
|
||||||
"""
|
"""
|
||||||
This error indicates an attempt to subscribe to a public channel while the number of connections is 0.
|
This error indicates an attempt to subscribe to a public channel while the number of connections is 0.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class ReconnectionTimeoutError(BfxWebsocketException):
|
class ReconnectionTimeoutError(BfxWebSocketException):
|
||||||
"""
|
"""
|
||||||
This error indicates that the connection has been offline for too long without being able to reconnect.
|
This error indicates that the connection has been offline for too long without being able to reconnect.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class WebsocketAuthenticationRequired(BfxWebsocketException):
|
class WebSocketAuthenticationRequired(BfxWebSocketException):
|
||||||
"""
|
"""
|
||||||
This error indicates an attempt to access a protected resource without logging in first.
|
This error indicates an attempt to access a protected resource without logging in first.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class InvalidAuthenticationCredentials(BfxWebsocketException):
|
class InvalidAuthenticationCredentials(BfxWebSocketException):
|
||||||
"""
|
"""
|
||||||
This error indicates that the user has provided incorrect credentials (API-KEY and API-SECRET) for authentication.
|
This error indicates that the user has provided incorrect credentials (API-KEY and API-SECRET) for authentication.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class EventNotSupported(BfxWebsocketException):
|
class EventNotSupported(BfxWebSocketException):
|
||||||
"""
|
"""
|
||||||
This error indicates a failed attempt to subscribe to an event not supported by the BfxWebsocketClient.
|
This error indicates a failed attempt to subscribe to an event not supported by the BfxWebSocketClient.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class HandlerNotFound(BfxWebsocketException):
|
class HandlerNotFound(BfxWebSocketException):
|
||||||
"""
|
"""
|
||||||
This error indicates that a handler was not found for an incoming message.
|
This error indicates that a handler was not found for an incoming message.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class OutdatedClientVersion(BfxWebsocketException):
|
class OutdatedClientVersion(BfxWebSocketException):
|
||||||
"""
|
"""
|
||||||
This error indicates a mismatch between the client version and the server WSS version.
|
This error indicates a mismatch between the client version and the server WSS version.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ __serializers__ = [
|
|||||||
"Wallet", "Balance",
|
"Wallet", "Balance",
|
||||||
]
|
]
|
||||||
|
|
||||||
#region Serializers definition for Websocket Public Channels
|
#region Serializers definition for WebSocket Public Channels
|
||||||
|
|
||||||
TradingPairTicker = generate_labeler_serializer(
|
TradingPairTicker = generate_labeler_serializer(
|
||||||
name="TradingPairTicker",
|
name="TradingPairTicker",
|
||||||
@@ -170,7 +170,7 @@ DerivativesStatus = generate_labeler_serializer(
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Serializers definition for Websocket Authenticated Channels
|
#region Serializers definition for WebSocket Authenticated Channels
|
||||||
|
|
||||||
Order = generate_labeler_serializer(
|
Order = generate_labeler_serializer(
|
||||||
name="Order",
|
name="Order",
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from .. notification import Notification
|
|||||||
|
|
||||||
from ..utils.json_encoder import JSON
|
from ..utils.json_encoder import JSON
|
||||||
|
|
||||||
#region Type hinting for Websocket Public Channels
|
#region Type hinting for WebSocket Public Channels
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class TradingPairTicker(_Type):
|
class TradingPairTicker(_Type):
|
||||||
@@ -111,7 +111,7 @@ class DerivativesStatus(_Type):
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Type hinting for Websocket Authenticated Channels
|
#region Type hinting for WebSocket Authenticated Channels
|
||||||
@dataclass
|
@dataclass
|
||||||
class Order(_Type):
|
class Order(_Type):
|
||||||
id: int
|
id: int
|
||||||
|
|||||||
Reference in New Issue
Block a user