Add support for SimpleNamespace (instead of TypedDict) in bfxapi/labeler.py and bfxapi/notifications.py. Add generics Notification type in notifications.py. Add support for new changes in bfxapi/rest/BfxRestInterface.py.

This commit is contained in:
Davide Casale
2023-01-13 18:15:29 +01:00
parent b5b3e7718a
commit 903f68c6e3
4 changed files with 59 additions and 51 deletions

View File

@@ -2,6 +2,8 @@ from .exceptions import LabelerSerializerException
from typing import Generic, TypeVar, Iterable, Optional, List, Tuple, Any, cast from typing import Generic, TypeVar, Iterable, Optional, List, Tuple, Any, cast
from types import SimpleNamespace
T = TypeVar("T") T = TypeVar("T")
class _Serializer(Generic[T]): class _Serializer(Generic[T]):
@@ -19,4 +21,4 @@ class _Serializer(Generic[T]):
yield label, args[index] yield label, args[index]
def parse(self, *values: Any, skip: Optional[List[str]] = None) -> T: def parse(self, *values: Any, skip: Optional[List[str]] = None) -> T:
return cast(T, dict(self._serialize(*values, skip=skip))) return cast(T, SimpleNamespace(**dict(self._serialize(*values, skip=skip))))

View File

@@ -1,17 +1,21 @@
from typing import List, Dict, Union, Optional, Any, TypedDict, cast from typing import List, Dict, Union, Optional, Any, TypedDict, Generic, TypeVar, cast
from types import SimpleNamespace
from .labeler import _Serializer from .labeler import _Serializer
class Notification(TypedDict): T = TypeVar("T")
class Notification(SimpleNamespace, Generic[T]):
MTS: int MTS: int
TYPE: str TYPE: str
MESSAGE_ID: Optional[int] MESSAGE_ID: Optional[int]
NOTIFY_INFO: Union[Dict[str, Any], List[Dict[str, Any]]] NOTIFY_INFO: T
CODE: Optional[int] CODE: Optional[int]
STATUS: str STATUS: str
TEXT: str TEXT: str
class _Notification(_Serializer): class _Notification(_Serializer, Generic[T]):
__LABELS = [ "MTS", "TYPE", "MESSAGE_ID", "_PLACEHOLDER", "NOTIFY_INFO", "CODE", "STATUS", "TEXT" ] __LABELS = [ "MTS", "TYPE", "MESSAGE_ID", "_PLACEHOLDER", "NOTIFY_INFO", "CODE", "STATUS", "TEXT" ]
def __init__(self, serializer: Optional[_Serializer] = None, iterate: bool = False): def __init__(self, serializer: Optional[_Serializer] = None, iterate: bool = False):
@@ -19,17 +23,17 @@ class _Notification(_Serializer):
self.serializer, self.iterate = serializer, iterate self.serializer, self.iterate = serializer, iterate
def parse(self, *values: Any, skip: Optional[List[str]] = None) -> Notification: def parse(self, *values: Any, skip: Optional[List[str]] = None) -> Notification[T]:
notification = dict(self._serialize(*values)) notification = cast(Notification[T], SimpleNamespace(**dict(self._serialize(*values))))
if isinstance(self.serializer, _Serializer): if isinstance(self.serializer, _Serializer):
if self.iterate == False: NOTIFY_INFO = cast(List[Any], notification.NOTIFY_INFO)
NOTIFY_INFO = notification["NOTIFY_INFO"]
if self.iterate == False:
if len(NOTIFY_INFO) == 1 and isinstance(NOTIFY_INFO[0], list): if len(NOTIFY_INFO) == 1 and isinstance(NOTIFY_INFO[0], list):
NOTIFY_INFO = NOTIFY_INFO[0] NOTIFY_INFO = NOTIFY_INFO[0]
notification["NOTIFY_INFO"] = dict(self.serializer._serialize(*NOTIFY_INFO, skip=skip)) notification.NOTIFY_INFO = cast(T, SimpleNamespace(**dict(self.serializer._serialize(*NOTIFY_INFO, skip=skip))))
else: notification["NOTIFY_INFO"] = [ dict(self.serializer._serialize(*data, skip=skip)) for data in notification["NOTIFY_INFO"] ] else: notification.NOTIFY_INFO = cast(T, [ SimpleNamespace(**dict(self.serializer._serialize(*data, skip=skip))) for data in NOTIFY_INFO ])
return cast(Notification, notification) return notification

View File

@@ -97,11 +97,11 @@ class _RestPublicEndpoints(_Requests):
parsers = { "t": serializers.TradingPairTicker.parse, "f": serializers.FundingCurrencyTicker.parse } parsers = { "t": serializers.TradingPairTicker.parse, "f": serializers.FundingCurrencyTicker.parse }
return [ parsers[subdata[0][0]](*subdata) for subdata in data ] return [ cast(Union[TradingPairTicker, FundingCurrencyTicker], parsers[subdata[0][0]](*subdata)) for subdata in data ]
def get_t_tickers(self, pairs: Union[List[str], Literal["ALL"]]) -> List[TradingPairTicker]: def get_t_tickers(self, pairs: Union[List[str], Literal["ALL"]]) -> List[TradingPairTicker]:
if isinstance(pairs, str) and pairs == "ALL": if isinstance(pairs, str) and pairs == "ALL":
return [ cast(TradingPairTicker, subdata) for subdata in self.get_tickers([ "ALL" ]) if cast(str, subdata["SYMBOL"]).startswith("t") ] return [ cast(TradingPairTicker, subdata) for subdata in self.get_tickers([ "ALL" ]) if cast(str, subdata.SYMBOL).startswith("t") ]
data = self.get_tickers([ "t" + pair for pair in pairs ]) data = self.get_tickers([ "t" + pair for pair in pairs ])
@@ -109,7 +109,7 @@ class _RestPublicEndpoints(_Requests):
def get_f_tickers(self, currencies: Union[List[str], Literal["ALL"]]) -> List[FundingCurrencyTicker]: def get_f_tickers(self, currencies: Union[List[str], Literal["ALL"]]) -> List[FundingCurrencyTicker]:
if isinstance(currencies, str) and currencies == "ALL": if isinstance(currencies, str) and currencies == "ALL":
return [ cast(FundingCurrencyTicker, subdata) for subdata in self.get_tickers([ "ALL" ]) if cast(str, subdata["SYMBOL"]).startswith("f") ] return [ cast(FundingCurrencyTicker, subdata) for subdata in self.get_tickers([ "ALL" ]) if cast(str, subdata.SYMBOL).startswith("f") ]
data = self.get_tickers([ "f" + currency for currency in currencies ]) data = self.get_tickers([ "f" + currency for currency in currencies ])
@@ -262,7 +262,7 @@ class _RestAuthenticatedEndpoints(_Requests):
price: Optional[Union[Decimal, str]] = None, lev: Optional[int] = None, price: Optional[Union[Decimal, str]] = None, lev: Optional[int] = None,
price_trailing: Optional[Union[Decimal, str]] = None, price_aux_limit: Optional[Union[Decimal, str]] = None, price_oco_stop: Optional[Union[Decimal, str]] = None, price_trailing: Optional[Union[Decimal, str]] = None, price_aux_limit: Optional[Union[Decimal, str]] = None, price_oco_stop: Optional[Union[Decimal, str]] = None,
gid: Optional[int] = None, cid: Optional[int] = None, gid: Optional[int] = None, cid: Optional[int] = None,
flags: Optional[int] = 0, tif: Optional[Union[datetime, str]] = None, meta: Optional[JSON] = None) -> Notification: flags: Optional[int] = 0, tif: Optional[Union[datetime, str]] = None, meta: Optional[JSON] = None) -> Notification[Order]:
data = { data = {
"type": type, "symbol": symbol, "amount": amount, "type": type, "symbol": symbol, "amount": amount,
"price": price, "lev": lev, "price": price, "lev": lev,
@@ -271,12 +271,12 @@ class _RestAuthenticatedEndpoints(_Requests):
"flags": flags, "tif": tif, "meta": meta "flags": flags, "tif": tif, "meta": meta
} }
return serializers._Notification(serializer=serializers.Order).parse(*self._POST("auth/w/order/submit", data=data)) return serializers._Notification[Order](serializer=serializers.Order).parse(*self._POST("auth/w/order/submit", data=data))
def update_order(self, id: int, amount: Optional[Union[Decimal, str]] = None, price: Optional[Union[Decimal, str]] = None, def update_order(self, id: int, amount: Optional[Union[Decimal, str]] = None, price: Optional[Union[Decimal, str]] = None,
cid: Optional[int] = None, cid_date: Optional[str] = None, gid: Optional[int] = None, cid: Optional[int] = None, cid_date: Optional[str] = None, gid: Optional[int] = None,
flags: Optional[int] = 0, lev: Optional[int] = None, delta: Optional[Union[Decimal, str]] = None, flags: Optional[int] = 0, lev: Optional[int] = None, delta: Optional[Union[Decimal, str]] = None,
price_aux_limit: Optional[Union[Decimal, str]] = None, price_trailing: Optional[Union[Decimal, str]] = None, tif: Optional[Union[datetime, str]] = None) -> Notification: price_aux_limit: Optional[Union[Decimal, str]] = None, price_trailing: Optional[Union[Decimal, str]] = None, tif: Optional[Union[datetime, str]] = None) -> Notification[Order]:
data = { data = {
"id": id, "amount": amount, "price": price, "id": id, "amount": amount, "price": price,
"cid": cid, "cid_date": cid_date, "gid": gid, "cid": cid, "cid_date": cid_date, "gid": gid,
@@ -284,18 +284,18 @@ class _RestAuthenticatedEndpoints(_Requests):
"price_aux_limit": price_aux_limit, "price_trailing": price_trailing, "tif": tif "price_aux_limit": price_aux_limit, "price_trailing": price_trailing, "tif": tif
} }
return serializers._Notification(serializer=serializers.Order).parse(*self._POST("auth/w/order/update", data=data)) return serializers._Notification[Order](serializer=serializers.Order).parse(*self._POST("auth/w/order/update", data=data))
def cancel_order(self, id: Optional[int] = None, cid: Optional[int] = None, cid_date: Optional[str] = None) -> Notification: def cancel_order(self, id: Optional[int] = None, cid: Optional[int] = None, cid_date: Optional[str] = None) -> Notification[Order]:
data = { data = {
"id": id, "id": id,
"cid": cid, "cid": cid,
"cid_date": cid_date "cid_date": cid_date
} }
return serializers._Notification(serializer=serializers.Order).parse(*self._POST("auth/w/order/cancel", data=data)) return serializers._Notification[Order](serializer=serializers.Order).parse(*self._POST("auth/w/order/cancel", data=data))
def cancel_order_multi(self, ids: Optional[List[int]] = None, cids: Optional[List[Tuple[int, str]]] = None, gids: Optional[List[int]] = None, all: bool = False) -> Notification: def cancel_order_multi(self, ids: Optional[List[int]] = None, cids: Optional[List[Tuple[int, str]]] = None, gids: Optional[List[int]] = None, all: bool = False) -> Notification[List[Order]]:
data = { data = {
"ids": ids, "ids": ids,
"cids": cids, "cids": cids,
@@ -304,7 +304,7 @@ class _RestAuthenticatedEndpoints(_Requests):
"all": int(all) "all": int(all)
} }
return serializers._Notification(serializer=serializers.Order, iterate=True).parse(*self._POST("auth/w/order/cancel/multi", data=data)) return serializers._Notification[List[Order]](serializer=serializers.Order, iterate=True).parse(*self._POST("auth/w/order/cancel/multi", data=data))
def get_orders_history(self, symbol: Optional[str] = None, ids: Optional[List[int]] = None, start: Optional[str] = None, end: Optional[str] = None, limit: Optional[int] = None) -> List[Order]: def get_orders_history(self, symbol: Optional[str] = None, ids: Optional[List[int]] = None, start: Optional[str] = None, end: Optional[str] = None, limit: Optional[int] = None) -> List[Order]:
if symbol == None: if symbol == None:
@@ -354,17 +354,17 @@ class _RestAuthenticatedEndpoints(_Requests):
def submit_funding_offer(self, type: FundingOfferType, symbol: str, amount: Union[Decimal, str], def submit_funding_offer(self, type: FundingOfferType, symbol: str, amount: Union[Decimal, str],
rate: Union[Decimal, str], period: int, rate: Union[Decimal, str], period: int,
flags: Optional[int] = 0) -> Notification: flags: Optional[int] = 0) -> Notification[FundingOffer]:
data = { data = {
"type": type, "symbol": symbol, "amount": amount, "type": type, "symbol": symbol, "amount": amount,
"rate": rate, "period": period, "rate": rate, "period": period,
"flags": flags "flags": flags
} }
return serializers._Notification(serializer=serializers.FundingOffer).parse(*self._POST("auth/w/funding/offer/submit", data=data)) return serializers._Notification[FundingOffer](serializer=serializers.FundingOffer).parse(*self._POST("auth/w/funding/offer/submit", data=data))
def cancel_funding_offer(self, id: int) -> Notification: def cancel_funding_offer(self, id: int) -> Notification[FundingOffer]:
return serializers._Notification(serializer=serializers.FundingOffer).parse(*self._POST("auth/w/funding/offer/cancel", data={ "id": id })) return serializers._Notification[FundingOffer](serializer=serializers.FundingOffer).parse(*self._POST("auth/w/funding/offer/cancel", data={ "id": id }))
def get_funding_offers_history(self, symbol: Optional[str] = None, start: Optional[str] = None, end: Optional[str] = None, limit: Optional[int] = None) -> List[FundingOffer]: def get_funding_offers_history(self, symbol: Optional[str] = None, start: Optional[str] = None, end: Optional[str] = None, limit: Optional[int] = None) -> List[FundingOffer]:
if symbol == None: if symbol == None:

View File

@@ -1,15 +1,17 @@
from typing import Type, Tuple, List, Dict, TypedDict, Union, Optional, Any from typing import Type, Tuple, List, Dict, TypedDict, Union, Optional, Any
from types import SimpleNamespace
from .. notification import Notification from .. notification import Notification
JSON = Union[Dict[str, "JSON"], List["JSON"], bool, int, float, str, Type[None]] JSON = Union[Dict[str, "JSON"], List["JSON"], bool, int, float, str, Type[None]]
#region Type hinting for Rest Public Endpoints #region Type hinting for Rest Public Endpoints
class PlatformStatus(TypedDict): class PlatformStatus(SimpleNamespace):
OPERATIVE: int OPERATIVE: int
class TradingPairTicker(TypedDict): class TradingPairTicker(SimpleNamespace):
SYMBOL: Optional[str] SYMBOL: Optional[str]
BID: float BID: float
BID_SIZE: float BID_SIZE: float
@@ -22,7 +24,7 @@ class TradingPairTicker(TypedDict):
HIGH: float HIGH: float
LOW: float LOW: float
class FundingCurrencyTicker(TypedDict): class FundingCurrencyTicker(SimpleNamespace):
SYMBOL: Optional[str] SYMBOL: Optional[str]
FRR: float FRR: float
BID: float BID: float
@@ -39,52 +41,52 @@ class FundingCurrencyTicker(TypedDict):
LOW: float LOW: float
FRR_AMOUNT_AVAILABLE: float FRR_AMOUNT_AVAILABLE: float
class TickersHistory(TypedDict): class TickersHistory(SimpleNamespace):
SYMBOL: str SYMBOL: str
BID: float BID: float
ASK: float ASK: float
MTS: int MTS: int
class TradingPairTrade(TypedDict): class TradingPairTrade(SimpleNamespace):
ID: int ID: int
MTS: int MTS: int
AMOUNT: float AMOUNT: float
PRICE: float PRICE: float
class FundingCurrencyTrade(TypedDict): class FundingCurrencyTrade(SimpleNamespace):
ID: int ID: int
MTS: int MTS: int
AMOUNT: float AMOUNT: float
RATE: float RATE: float
PERIOD: int PERIOD: int
class TradingPairBook(TypedDict): class TradingPairBook(SimpleNamespace):
PRICE: float PRICE: float
COUNT: int COUNT: int
AMOUNT: float AMOUNT: float
class FundingCurrencyBook(TypedDict): class FundingCurrencyBook(SimpleNamespace):
RATE: float RATE: float
PERIOD: int PERIOD: int
COUNT: int COUNT: int
AMOUNT: float AMOUNT: float
class TradingPairRawBook(TypedDict): class TradingPairRawBook(SimpleNamespace):
ORDER_ID: int ORDER_ID: int
PRICE: float PRICE: float
AMOUNT: float AMOUNT: float
class FundingCurrencyRawBook(TypedDict): class FundingCurrencyRawBook(SimpleNamespace):
OFFER_ID: int OFFER_ID: int
PERIOD: int PERIOD: int
RATE: float RATE: float
AMOUNT: float AMOUNT: float
class Statistic(TypedDict): class Statistic(SimpleNamespace):
MTS: int MTS: int
VALUE: float VALUE: float
class Candle(TypedDict): class Candle(SimpleNamespace):
MTS: int MTS: int
OPEN: float OPEN: float
CLOSE: float CLOSE: float
@@ -92,7 +94,7 @@ class Candle(TypedDict):
LOW: float LOW: float
VOLUME: float VOLUME: float
class DerivativesStatus(TypedDict): class DerivativesStatus(SimpleNamespace):
KEY: Optional[str] KEY: Optional[str]
MTS: int MTS: int
DERIV_PRICE: float DERIV_PRICE: float
@@ -107,7 +109,7 @@ class DerivativesStatus(TypedDict):
CLAMP_MIN: float CLAMP_MIN: float
CLAMP_MAX: float CLAMP_MAX: float
class Liquidation(TypedDict): class Liquidation(SimpleNamespace):
POS_ID: int POS_ID: int
MTS: int MTS: int
SYMBOL: str SYMBOL: str
@@ -117,14 +119,14 @@ class Liquidation(TypedDict):
IS_MARKET_SOLD: int IS_MARKET_SOLD: int
PRICE_ACQUIRED: float PRICE_ACQUIRED: float
class Leaderboard(TypedDict): class Leaderboard(SimpleNamespace):
MTS: int MTS: int
USERNAME: str USERNAME: str
RANKING: int RANKING: int
VALUE: float VALUE: float
TWITTER_HANDLE: Optional[str] TWITTER_HANDLE: Optional[str]
class FundingStatistic(TypedDict): class FundingStatistic(SimpleNamespace):
TIMESTAMP: int TIMESTAMP: int
FRR: float FRR: float
AVG_PERIOD: float AVG_PERIOD: float
@@ -136,7 +138,7 @@ class FundingStatistic(TypedDict):
#region Type hinting for Rest Authenticated Endpoints #region Type hinting for Rest Authenticated Endpoints
class Wallet(TypedDict): class Wallet(SimpleNamespace):
WALLET_TYPE: str WALLET_TYPE: str
CURRENCY: str CURRENCY: str
BALANCE: float BALANCE: float
@@ -145,7 +147,7 @@ class Wallet(TypedDict):
LAST_CHANGE: str LAST_CHANGE: str
TRADE_DETAILS: JSON TRADE_DETAILS: JSON
class Order(TypedDict): class Order(SimpleNamespace):
ID: int ID: int
GID: int GID: int
CID: int CID: int
@@ -169,7 +171,7 @@ class Order(TypedDict):
ROUTING: str ROUTING: str
META: JSON META: JSON
class Position(TypedDict): class Position(SimpleNamespace):
SYMBOL: str SYMBOL: str
STATUS: str STATUS: str
AMOUNT: float AMOUNT: float
@@ -188,7 +190,7 @@ class Position(TypedDict):
COLLATERAL_MIN: float COLLATERAL_MIN: float
META: JSON META: JSON
class FundingOffer(TypedDict): class FundingOffer(SimpleNamespace):
ID: int ID: int
SYMBOL: str SYMBOL: str
MTS_CREATE: int MTS_CREATE: int
@@ -204,7 +206,7 @@ class FundingOffer(TypedDict):
HIDDEN: int HIDDEN: int
RENEW: bool RENEW: bool
class Trade(TypedDict): class Trade(SimpleNamespace):
ID: int ID: int
SYMBOL: str SYMBOL: str
MTS_CREATE: int MTS_CREATE: int
@@ -218,7 +220,7 @@ class Trade(TypedDict):
FEE_CURRENCY: str FEE_CURRENCY: str
CID: int CID: int
class OrderTrade(TypedDict): class OrderTrade(SimpleNamespace):
ID: int ID: int
SYMBOL: str SYMBOL: str
MTS_CREATE: int MTS_CREATE: int
@@ -230,7 +232,7 @@ class OrderTrade(TypedDict):
FEE_CURRENCY: str FEE_CURRENCY: str
CID: int CID: int
class Ledger(TypedDict): class Ledger(SimpleNamespace):
ID: int ID: int
CURRENCY: str CURRENCY: str
MTS: int MTS: int
@@ -238,7 +240,7 @@ class Ledger(TypedDict):
BALANCE: float BALANCE: float
description: str description: str
class FundingCredit(TypedDict): class FundingCredit(SimpleNamespace):
ID: int ID: int
SYMBOL: str SYMBOL: str
SIDE: int SIDE: int