Add support for various new authenticated channels. Add new typings in bfxapi/websocket/typings.py. Add BfxWebsocketException in bfxapi/websocket/errors.py.

This commit is contained in:
Davide Casale
2022-11-11 18:54:38 +01:00
parent 2c70d299b3
commit a03a82d57a
5 changed files with 483 additions and 45 deletions

View File

@@ -4,7 +4,7 @@ from pyee.asyncio import AsyncIOEventEmitter
from .handlers import Channels, PublicChannelsHandler, AuthenticatedEventsHandler
from .errors import ConnectionNotOpen, AuthenticationCredentialsError
from .errors import BfxWebsocketException, ConnectionNotOpen, InvalidAuthenticationCredentials
HEARTBEAT = "hb"
@@ -42,7 +42,7 @@ class BfxWebsocketClient(object):
elif isinstance(message, dict) and message["event"] == "auth":
if message["status"] == "OK":
self.event_emitter.emit("authenticated", message)
else: raise AuthenticationCredentialsError("Cannot authenticate with given API-KEY and API-SECRET.")
else: raise InvalidAuthenticationCredentials("Cannot authenticate with given API-KEY and API-SECRET.")
elif isinstance(message, list) and ((chanId := message[0]) or True) and message[1] != HEARTBEAT:
if chanId == 0:
self.handlers["authenticated"].handle(message[1], message[2])

View File

@@ -1,3 +1,3 @@
from .BfxWebsocketClient import BfxWebsocketClient
from .handlers import Channels
from .errors import ConnectionNotOpen, AuthenticationCredentialsError
from .errors import BfxWebsocketException, ConnectionNotOpen, InvalidAuthenticationCredentials

View File

@@ -1,11 +1,24 @@
class ConnectionNotOpen(Exception):
__all__ = [
"BfxWebsocketException",
"ConnectionNotOpen",
"InvalidAuthenticationCredentials"
]
class BfxWebsocketException(Exception):
"""
Base class for all exceptions defined in bfx/websocket/errors.py.
"""
pass
class ConnectionNotOpen(BfxWebsocketException):
"""
This error indicates an attempt to communicate via websocket before starting the connection with the servers.
"""
pass
class AuthenticationCredentialsError(Exception):
class InvalidAuthenticationCredentials(BfxWebsocketException):
"""
This error indicates that the user has provided incorrect credentials (API-KEY and API-SECRET) for authentication.
"""

View File

@@ -1,5 +1,7 @@
from enum import Enum
from .errors import BfxWebsocketException
class Channels(str, Enum):
TICKER = "ticker"
TRADES = "trades"
@@ -46,33 +48,45 @@ class PublicChannelsHandler(object):
self.event_emitter.emit("status", subscription, parameters[0])
class AuthenticatedEventsHandler(object):
def __init__(self, event_emitter):
self.event_emitter = event_emitter
def __init__(self, event_emitter, strict = False):
self.event_emitter, self.strict = event_emitter, strict
self.__handlers = {
"bu": self.__bu_event_handler,
"ws": self.__ws_event_handler,
"wu": self.__wu_event_handler,
"os": self.__os_event_handler,
"on": self.__on_event_handler
"on": self.__on_event_handler,
"ou": self.__ou_event_handler,
"oc": self.__oc_event_handler,
"ps": self.__ps_event_handler,
"pn": self.__pn_event_handler,
"pu": self.__pu_event_handler,
"pc": self.__pc_event_handler,
"fos": self.__fos_event_handler,
"fon": self.__fon_event_handler,
"fou": self.__fou_event_handler,
"foc": self.__foc_event_handler,
}
def handle(self, type, parameters):
def handle(self, type, stream):
if type in self.__handlers:
self.__handlers[type](*parameters)
self.__handlers[type](*stream)
elif self.strict == True:
raise BfxWebsocketException(f"Event of type <{type}> not found in self.__handlers.")
def __bu_event_handler(self, *parameters):
self.event_emitter.emit("balance_update", _label_array_elements(
def __bu_event_handler(self, *stream):
self.event_emitter.emit("balance_update", _label_stream_data(
[
"AUM",
"AUM_NET"
],
*parameters
*stream
))
def __ws_event_handler(self, *parameters):
def __ws_event_handler(self, *stream):
self.event_emitter.emit("wallet_snapshot", [
_label_array_elements(
_label_stream_data(
[
"WALLET_TYPE",
"CURRENCY",
@@ -82,12 +96,12 @@ class AuthenticatedEventsHandler(object):
"DESCRIPTION",
"META"
],
*parameter
) for parameter in parameters
*substream
) for substream in stream
])
def __wu_event_handler(self, *parameters):
self.event_emitter.emit("wallet_update", _label_array_elements(
def __wu_event_handler(self, *stream):
self.event_emitter.emit("wallet_update", _label_stream_data(
[
"WALLET_TYPE",
"CURRENCY",
@@ -97,12 +111,12 @@ class AuthenticatedEventsHandler(object):
"DESCRIPTION",
"META"
],
*parameters
*stream
))
def __os_event_handler(self, *parameters):
def __os_event_handler(self, *stream):
self.event_emitter.emit("order_snapshot", [
_label_array_elements(
_label_stream_data(
[
"ID",
"GID",
@@ -137,12 +151,12 @@ class AuthenticatedEventsHandler(object):
"_PLACEHOLDER",
"META"
],
*parameter
) for parameter in parameters
*substream
) for substream in stream
])
def __on_event_handler(self, *parameters):
self.event_emitter.emit("new_order", _label_array_elements(
def __on_event_handler(self, *stream):
self.event_emitter.emit("new_order", _label_stream_data(
[
"ID",
"GID",
@@ -177,13 +191,315 @@ class AuthenticatedEventsHandler(object):
"_PLACEHOLDER",
"_PLACEHOLDER"
],
*parameters
*stream
))
def _label_array_elements(labels, *args):
def __ou_event_handler(self, *stream):
self.event_emitter.emit("order_update", _label_stream_data(
[
"ID",
"GID",
"CID",
"SYMBOL",
"MTS_CREATE",
"MTS_UPDATE",
"AMOUNT",
"AMOUNT_ORIG",
"ORDER_TYPE",
"TYPE_PREV",
"MTS_TIF",
"_PLACEHOLDER",
"FLAGS",
"ORDER_STATUS",
"_PLACEHOLDER",
"_PLACEHOLDER",
"PRICE",
"PRICE_AVG",
"PRICE_TRAILING",
"PRICE_AUX_LIMIT",
"_PLACEHOLDER",
"_PLACEHOLDER",
"_PLACEHOLDER",
"NOTIFY",
"HIDDEN",
"PLACED_ID",
"_PLACEHOLDER",
"_PLACEHOLDER",
"ROUTING",
"_PLACEHOLDER",
"_PLACEHOLDER",
"_PLACEHOLDER"
],
*stream
))
def __oc_event_handler(self, *stream):
self.event_emitter.emit("order_cancel", _label_stream_data(
[
"ID",
"GID",
"CID",
"SYMBOL",
"MTS_CREATE",
"MTS_UPDATE",
"AMOUNT",
"AMOUNT_ORIG",
"ORDER_TYPE",
"TYPE_PREV",
"MTS_TIF",
"_PLACEHOLDER",
"FLAGS",
"ORDER_STATUS",
"_PLACEHOLDER",
"_PLACEHOLDER",
"PRICE",
"PRICE_AVG",
"PRICE_TRAILING",
"PRICE_AUX_LIMIT",
"_PLACEHOLDER",
"_PLACEHOLDER",
"_PLACEHOLDER",
"NOTIFY",
"HIDDEN",
"PLACED_ID",
"_PLACEHOLDER",
"_PLACEHOLDER",
"ROUTING",
"_PLACEHOLDER",
"_PLACEHOLDER",
"_PLACEHOLDER"
],
*stream
))
def __ps_event_handler(self, *stream):
self.event_emitter.emit("position_snapshot", [
_label_stream_data(
[
"SYMBOL",
"STATUS",
"AMOUNT",
"BASE_PRICE",
"MARGIN_FUNDING",
"MARGIN_FUNDING_TYPE",
"PL",
"PL_PERC",
"PRICE_LIQ",
"LEVERAGE",
"FLAG",
"POSITION_ID",
"MTS_CREATE",
"MTS_UPDATE",
"_PLACEHOLDER",
"TYPE",
"_PLACEHOLDER",
"COLLATERAL",
"COLLATERAL_MIN",
"META"
],
*substream
)
for substream in stream
])
def __pn_event_handler(self, *stream):
self.event_emitter.emit("new_position", _label_stream_data(
[
"SYMBOL",
"STATUS",
"AMOUNT",
"BASE_PRICE",
"MARGIN_FUNDING",
"MARGIN_FUNDING_TYPE",
"PL",
"PL_PERC",
"PRICE_LIQ",
"LEVERAGE",
"FLAG",
"POSITION_ID",
"MTS_CREATE",
"MTS_UPDATE",
"_PLACEHOLDER",
"TYPE",
"_PLACEHOLDER",
"COLLATERAL",
"COLLATERAL_MIN",
"META"
],
*stream
))
def __pu_event_handler(self, *stream):
self.event_emitter.emit("position_update", _label_stream_data(
[
"SYMBOL",
"STATUS",
"AMOUNT",
"BASE_PRICE",
"MARGIN_FUNDING",
"MARGIN_FUNDING_TYPE",
"PL",
"PL_PERC",
"PRICE_LIQ",
"LEVERAGE",
"FLAG",
"POSITION_ID",
"MTS_CREATE",
"MTS_UPDATE",
"_PLACEHOLDER",
"TYPE",
"_PLACEHOLDER",
"COLLATERAL",
"COLLATERAL_MIN",
"META"
],
*stream
))
def __pc_event_handler(self, *stream):
self.event_emitter.emit("position_cancel", _label_stream_data(
[
"SYMBOL",
"STATUS",
"AMOUNT",
"BASE_PRICE",
"MARGIN_FUNDING",
"MARGIN_FUNDING_TYPE",
"PL",
"PL_PERC",
"PRICE_LIQ",
"LEVERAGE",
"FLAG",
"POSITION_ID",
"MTS_CREATE",
"MTS_UPDATE",
"_PLACEHOLDER",
"TYPE",
"_PLACEHOLDER",
"COLLATERAL",
"COLLATERAL_MIN",
"META"
],
*stream
))
def __fos_event_handler(self, *stream):
self.event_emitter.emit("funding_offer_snapshot", [
_label_stream_data(
[
"ID",
"SYMBOL",
"MTS_CREATED",
"MTS_UPDATED",
"AMOUNT",
"AMOUNT_ORIG",
"OFFER_TYPE",
"_PLACEHOLDER",
"_PLACEHOLDER",
"FLAGS",
"STATUS",
"_PLACEHOLDER",
"_PLACEHOLDER",
"_PLACEHOLDER",
"RATE",
"PERIOD",
"NOTIFY",
"HIDDEN",
"_PLACEHOLDER",
"RENEW",
"_PLACEHOLDER",
],
*substream
)
for substream in stream
])
def __fon_event_handler(self, *stream):
self.event_emitter.emit("funding_offer_new", _label_stream_data(
[
"ID",
"SYMBOL",
"MTS_CREATED",
"MTS_UPDATED",
"AMOUNT",
"AMOUNT_ORIG",
"TYPE",
"_PLACEHOLDER",
"_PLACEHOLDER",
"FLAGS",
"STATUS",
"_PLACEHOLDER",
"_PLACEHOLDER",
"_PLACEHOLDER",
"RATE",
"PERIOD",
"NOTIFY",
"HIDDEN",
"_PLACEHOLDER",
"RENEW",
"RATE_REAL"
],
*stream
))
def __fou_event_handler(self, *stream):
self.event_emitter.emit("funding_offer_update", _label_stream_data(
[
"ID",
"SYMBOL",
"MTS_CREATED",
"MTS_UPDATED",
"AMOUNT",
"AMOUNT_ORIG",
"TYPE",
"_PLACEHOLDER",
"_PLACEHOLDER",
"FLAGS",
"STATUS",
"_PLACEHOLDER",
"_PLACEHOLDER",
"_PLACEHOLDER",
"RATE",
"PERIOD",
"NOTIFY",
"HIDDEN",
"_PLACEHOLDER",
"RENEW",
"RATE_REAL"
],
*stream
))
def __foc_event_handler(self, *stream):
self.event_emitter.emit("funding_offer_cancel", _label_stream_data(
[
"ID",
"SYMBOL",
"MTS_CREATED",
"MTS_UPDATED",
"AMOUNT",
"AMOUNT_ORIG",
"TYPE",
"_PLACEHOLDER",
"_PLACEHOLDER",
"FLAGS",
"STATUS",
"_PLACEHOLDER",
"_PLACEHOLDER",
"_PLACEHOLDER",
"RATE",
"PERIOD",
"NOTIFY",
"HIDDEN",
"_PLACEHOLDER",
"RENEW",
"RATE_REAL"
],
*stream
))
def _label_stream_data(labels, *args, IGNORE = [ "_PLACEHOLDER" ]):
if len(labels) != len(args):
raise Exception("<labels> and <*args> arguments should contain the same amount of elements.")
raise BfxWebsocketException("<labels> and <*args> arguments should contain the same amount of elements.")
_PLACEHOLDER = "_PLACEHOLDER"
return { label: args[index] for index, label in enumerate(labels) if label != _PLACEHOLDER }
return { label: args[index] for index, label in enumerate(labels) if label not in IGNORE }

View File

@@ -1,16 +1,125 @@
from typing import TypedDict, List, Optional
from typing import Type, List, Dict, TypedDict, Union, Optional, Any
class BalanceUpdateStream(TypedDict):
AUM: float
AUM_NET: float
JSON = Union[Dict[str, Any], List[Any], int, str, float, bool, Type[None]]
class WalletUpdateStream(TypedDict):
WALLET_TYPE: str
CURRENCY: str
BALANCE: float
UNSETTLED_INTEREST: float
BALANCE_AVAILABLE: Optional[float]
DESCRIPTION: str
META: dict
BalanceUpdateStream = TypedDict("BalanceUpdateStream", {
"AUM": float,
"AUM_NET": float
})
WalletSnapshotStream = List[WalletUpdateStream]
WalletSnapshotStream = List[TypedDict("WalletSnapshotStream", {
"WALLET_TYPE": str,
"CURRENCY": str,
"BALANCE": float,
"UNSETTLED_INTEREST": float,
"BALANCE_AVAILABLE": Optional[float],
"DESCRIPTION": str,
"META": JSON
})]
WalletUpdateStream = TypedDict("WalletUpdateStream", {
"WALLET_TYPE": str,
"CURRENCY": str,
"BALANCE": float,
"UNSETTLED_INTEREST": float,
"BALANCE_AVAILABLE": Optional[float],
"DESCRIPTION": str,
"META": JSON
})
OrderSnapshotStream = List[TypedDict("OrderSnapshotStream", {
"ID": int,
"GID": int,
"CID": int,
"SYMBOL": str,
"MTS_CREATE": int,
"MTS_UPDATE": int,
"AMOUNT": float,
"AMOUNT_ORIG": float,
"ORDER_TYPE": str,
"TYPE_PREV": str,
"MTS_TIF": int,
"FLAGS": int,
"STATUS": str,
"PRICE": float,
"PRICE_AVG": float,
"PRICE_TRAILING": float,
"PRICE_AUX_LIMIT": float,
"NOTIFY": int,
"HIDDEN": int,
"PLACED_ID": int,
"ROUTING": str,
"META": JSON
})]
NewOrderStream = TypedDict("NewOrderStream", {
"ID": int,
"GID": int,
"CID": int,
"SYMBOL": str,
"MTS_CREATE": int,
"MTS_UPDATE": int,
"AMOUNT": float,
"AMOUNT_ORIG": float,
"ORDER_TYPE": str,
"TYPE_PREV": str,
"MTS_TIF": int,
"FLAGS": int,
"ORDER_STATUS": str,
"PRICE": float,
"PRICE_AVG": float,
"PRICE_TRAILING": float,
"PRICE_AUX_LIMIT": float,
"NOTIFY": int,
"HIDDEN": int,
"PLACED_ID": int,
"ROUTING": str
})
OrderUpdateStream = TypedDict("OrderUpdateStream", {
"ID": int,
"GID": int,
"CID": int,
"SYMBOL": str,
"MTS_CREATE": int,
"MTS_UPDATE": int,
"AMOUNT": float,
"AMOUNT_ORIG": float,
"ORDER_TYPE": str,
"TYPE_PREV": str,
"MTS_TIF": int,
"FLAGS": int,
"ORDER_STATUS": str,
"PRICE": float,
"PRICE_AVG": float,
"PRICE_TRAILING": float,
"PRICE_AUX_LIMIT": float,
"NOTIFY": int,
"HIDDEN": int,
"PLACED_ID": int,
"ROUTING": str
})
OrderCancelStream = TypedDict("OrderCancelStream", {
"ID": int,
"GID": int,
"CID": int,
"SYMBOL": str,
"MTS_CREATE": int,
"MTS_UPDATE": int,
"AMOUNT": float,
"AMOUNT_ORIG": float,
"ORDER_TYPE": str,
"TYPE_PREV": str,
"MTS_TIF": int,
"FLAGS": int,
"ORDER_STATUS": str,
"PRICE": float,
"PRICE_AVG": float,
"PRICE_TRAILING": float,
"PRICE_AUX_LIMIT": float,
"NOTIFY": int,
"HIDDEN": int,
"PLACED_ID": int,
"ROUTING": str
})