Add generic error handling (UnknownGenericError in bfxapi/rest/exceptions.py). Add support for new endpoints in _RestAuthenticatedEndpoints class. Extend serializers.py and typings.py.

This commit is contained in:
Davide Casale
2022-12-20 17:48:12 +01:00
parent 7d16c4e4b8
commit 87bb6dc5c7
6 changed files with 154 additions and 14 deletions

View File

@@ -8,7 +8,7 @@ from . import serializers
from .typings import *
from .enums import Config, Precision, Sort
from .exceptions import RequestParametersError, ResourceNotFound, InvalidAuthenticationCredentials
from .exceptions import ResourceNotFound, RequestParametersError, InvalidAuthenticationCredentials, UnknownGenericError
class BfxRestInterface(object):
def __init__(self, host, API_KEY = None, API_SECRET = None):
@@ -47,13 +47,16 @@ class _Requests(object):
if data[1] == 10020:
raise RequestParametersError(f"The request was rejected with the following parameter error: <{data[2]}>")
if data[1] == None:
raise UnknownGenericError("The server replied to the request with a generic error.")
return data
def _POST(self, endpoint, params = None, data = None, _append_authentication_headers = True):
headers = { "Content-Type": "application/json" }
if _append_authentication_headers:
headers = { **headers, **self.__build_authentication_headers(f"{endpoint}", data) }
headers = { **headers, **self.__build_authentication_headers(endpoint, data) }
response = requests.post(f"{self.host}/{endpoint}", params=params, data=json.dumps(data), headers=headers)
@@ -69,6 +72,9 @@ class _Requests(object):
if data[1] == 10100:
raise InvalidAuthenticationCredentials("Cannot authenticate with given API-KEY and API-SECRET.")
if data[1] == None:
raise UnknownGenericError("The server replied to the request with a generic error.")
return data
class _RestPublicEndpoints(_Requests):
@@ -227,4 +233,8 @@ class _RestPublicEndpoints(_Requests):
return self._GET(f"conf/{config}")[0]
class _RestAuthenticatedEndpoints(_Requests):
__PREFIX = "auth/"
def wallets(self) -> List[Wallet]:
return [ serializers.Wallet.parse(*subdata) for subdata in self._POST("auth/r/wallets") ]
def retrieve_orders(self, ids: Optional[List[str]] = None) -> List[Order]:
return [ serializers.Order.parse(*subdata) for subdata in self._POST("auth/r/orders", data={ "id": ids }) ]

View File

@@ -15,16 +15,16 @@ class BfxRestException(BfxBaseException):
pass
class RequestParametersError(BfxRestException):
class ResourceNotFound(BfxRestException):
"""
This error indicates that there are some invalid parameters sent along with an HTTP request.
This error indicates a failed HTTP request to a non-existent resource.
"""
pass
class ResourceNotFound(BfxRestException):
class RequestParametersError(BfxRestException):
"""
This error indicates a failed HTTP request to a non-existent resource.
This error indicates that there are some invalid parameters sent along with an HTTP request.
"""
pass
@@ -34,4 +34,11 @@ class InvalidAuthenticationCredentials(BfxRestException):
This error indicates that the user has provided incorrect credentials (API-KEY and API-SECRET) for authentication.
"""
pass
class UnknownGenericError(BfxRestException):
"""
This error indicates an undefined problem processing an HTTP request sent to the APIs.
"""
pass

View File

@@ -183,4 +183,69 @@ FundingStatistic = _Serializer[typings.FundingStatistic]("FundingStatistic", lab
"FUNDING_BELOW_THRESHOLD"
])
#endregion
#region Serializers definition for Rest Authenticated Endpoints
Wallet = _Serializer[typings.Wallet]("Wallet", labels=[
"WALLET_TYPE",
"CURRENCY",
"BALANCE",
"UNSETTLED_INTEREST",
"AVAILABLE_BALANCE",
"LAST_CHANGE",
"TRADE_DETAILS"
])
Order = _Serializer[typings.Order]("Order", labels=[
"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",
"META"
])
#endregion
#region Serializers definition for Notifications channel
Notification = _Serializer[typings.Notification]("Notification", labels=[
"MTS",
"TYPE",
"MESSAGE_ID",
"_PLACEHOLDER",
"NOTIFY_INFO",
"CODE",
"STATUS",
"TEXT"
])
#endregion

View File

@@ -1,5 +1,13 @@
from decimal import Decimal
from datetime import datetime
from typing import Type, Tuple, List, Dict, TypedDict, Union, Optional, Any
from ..utils.integers import Int16, Int32, Int45, Int64
JSON = Union[Dict[str, "JSON"], List["JSON"], bool, int, float, str, Type[None]]
#region Type hinting for Rest Public Endpoints
class PlatformStatus(TypedDict):
@@ -128,4 +136,54 @@ class FundingStatistic(TypedDict):
FUNDING_AMOUNT_USED: float
FUNDING_BELOW_THRESHOLD: float
#endregion
#region Type hinting for Rest Authenticated Endpoints
class Wallet(TypedDict):
WALLET_TYPE: str
CURRENCY: str
BALANCE: float
UNSETTLED_INTEREST: float
AVAILABLE_BALANCE: float
LAST_CHANGE: str
TRADE_DETAILS: JSON
class Order(TypedDict):
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
META: JSON
#endregion
#region Type hinting for Notifications channel
class Notification(TypedDict):
MTS: int
TYPE: str
MESSAGE_ID: int
NOTIFY_INFO: JSON
CODE: int
STATUS: str
TEXT: str
#endregion

View File

@@ -2,7 +2,7 @@ from decimal import Decimal
from datetime import datetime
from typing import Type, NewType, Tuple, List, Dict, TypedDict, Union, Optional, Any
from typing import Type, Tuple, List, Dict, TypedDict, Union, Optional, Any
from ..utils.integers import Int16, Int32, Int45, Int64
@@ -11,22 +11,22 @@ JSON = Union[Dict[str, "JSON"], List["JSON"], bool, int, float, str, Type[None]]
#region Type hinting for subscription objects
class Subscriptions:
class TradingPairsTicker(TypedDict):
class TradingPairTicker(TypedDict):
chanId: int
symbol: str
pair: str
class FundingCurrenciesTicker(TypedDict):
class FundingCurrencyTicker(TypedDict):
chanId: int
symbol: str
currency: str
class TradingPairsTrades(TypedDict):
class TradingPairTrades(TypedDict):
chanId: int
symbol: str
pair: str
class FundingCurrenciesTrades(TypedDict):
class FundingCurrencyTrades(TypedDict):
chanId: int
symbol: str
currency: str
@@ -280,7 +280,7 @@ class BalanceInfo(TypedDict):
#endregion
#region Serializers definition for Notifications channel
#region Type hinting for Notifications channel
class Notification(TypedDict):
MTS: int

View File

@@ -7,7 +7,7 @@ from bfxapi.websocket.typings import Subscriptions, TradingPairTicker
bfx = Client(WSS_HOST=Constants.PUB_WSS_HOST)
@bfx.wss.on("t_ticker_update")
def on_t_ticker_update(subscription: Subscriptions.TradingPairsTicker, data: TradingPairTicker):
def on_t_ticker_update(subscription: Subscriptions.TradingPairTicker, data: TradingPairTicker):
print(f"Subscription with channel ID: {subscription['chanId']}")
print(f"Data: {data}")