Merge pull request #15 from Davi0kProgramsThings/master

Sync branch `feature/rest` with branch `master`.
This commit is contained in:
Davide Casale
2023-01-13 16:09:17 +01:00
committed by GitHub
8 changed files with 122 additions and 138 deletions

View File

@@ -340,4 +340,7 @@ class _RestAuthenticatedEndpoints(_Requests):
"flags": flags "flags": flags
} }
return serializers._Notification(serializer=serializers.FundingOffer).parse(*self._POST("auth/w/funding/offer/submit", data=data)) return serializers._Notification(serializer=serializers.FundingOffer).parse(*self._POST("auth/w/funding/offer/submit", data=data))
def cancel_funding_offer(self, id: int) -> Notification:
return serializers._Notification(serializer=serializers.FundingOffer).parse(*self._POST("auth/w/funding/offer/cancel", data={ "id": id }))

View File

@@ -1,12 +1,10 @@
import traceback, json, asyncio, hmac, hashlib, time, uuid, websockets import traceback, json, asyncio, hmac, hashlib, time, uuid, websockets
from typing import Tuple, Union, Literal, TypeVar, Callable, cast from typing import Literal, TypeVar, Callable, cast
from enum import Enum
from pyee.asyncio import AsyncIOEventEmitter from pyee.asyncio import AsyncIOEventEmitter
from .typings import Inputs from ._BfxWebsocketInputs import _BfxWebsocketInputs
from .handlers import Channels, PublicChannelsHandler, AuthenticatedChannelsHandler from .handlers import Channels, PublicChannelsHandler, AuthenticatedChannelsHandler
from .exceptions import ConnectionNotOpen, TooManySubscriptions, WebsocketAuthenticationRequired, InvalidAuthenticationCredentials, EventNotSupported, OutdatedClientVersion from .exceptions import ConnectionNotOpen, TooManySubscriptions, WebsocketAuthenticationRequired, InvalidAuthenticationCredentials, EventNotSupported, OutdatedClientVersion
@@ -230,29 +228,4 @@ class _BfxWebsocketBucket(object):
@_require_websocket_connection @_require_websocket_connection
async def _close(self, code=1000, reason=str()): async def _close(self, code=1000, reason=str()):
await self.websocket.close(code=code, reason=reason) await self.websocket.close(code=code, reason=reason)
class _BfxWebsocketInputs(object):
def __init__(self, __handle_websocket_input):
self.__handle_websocket_input = __handle_websocket_input
async def order_new(self, data: Inputs.Order.New):
await self.__handle_websocket_input("on", data)
async def order_update(self, data: Inputs.Order.Update):
await self.__handle_websocket_input("ou", data)
async def order_cancel(self, data: Inputs.Order.Cancel):
await self.__handle_websocket_input("oc", data)
async def order_multiple_operations(self, *args: Tuple[str, Union[Inputs.Order.New, Inputs.Order.Update, Inputs.Order.Cancel]]):
await self.__handle_websocket_input("ox_multi", args)
async def offer_new(self, data: Inputs.Offer.New):
await self.__handle_websocket_input("fon", data)
async def offer_cancel(self, data: Inputs.Offer.Cancel):
await self.__handle_websocket_input("foc", data)
async def calc(self, *args: str):
await self.__handle_websocket_input("calc", list(map(lambda arg: [arg], args)))

View File

@@ -0,0 +1,78 @@
from decimal import Decimal
from datetime import datetime
from typing import Union, Optional, List, Tuple
from .typings import JSON
from .enums import OrderType, FundingOfferType
def _strip(dictionary):
return { key: value for key, value in dictionary.items() if value != None}
class _BfxWebsocketInputs(object):
def __init__(self, __handle_websocket_input):
self.__handle_websocket_input = __handle_websocket_input
async def submit_order(self, type: OrderType, symbol: str, amount: Union[Decimal, str],
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,
gid: Optional[int] = None, cid: Optional[int] = None,
flags: Optional[int] = 0, tif: Optional[Union[datetime, str]] = None, meta: Optional[JSON] = None):
data = _strip({
"type": type, "symbol": symbol, "amount": amount,
"price": price, "lev": lev,
"price_trailing": price_trailing, "price_aux_limit": price_aux_limit, "price_oco_stop": price_oco_stop,
"gid": gid, "cid": cid,
"flags": flags, "tif": tif, "meta": meta
})
await self.__handle_websocket_input("on", data)
async 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,
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):
data = _strip({
"id": id, "amount": amount, "price": price,
"cid": cid, "cid_date": cid_date, "gid": gid,
"flags": flags, "lev": lev, "delta": delta,
"price_aux_limit": price_aux_limit, "price_trailing": price_trailing, "tif": tif
})
await self.__handle_websocket_input("ou", data)
async def cancel_order(self, id: Optional[int] = None, cid: Optional[int] = None, cid_date: Optional[str] = None):
data = _strip({
"id": id,
"cid": cid,
"cid_date": cid_date
})
await self.__handle_websocket_input("oc", data)
async 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):
data = _strip({
"ids": ids,
"cids": cids,
"gids": gids,
"all": int(all)
})
await self.__handle_websocket_input("oc_multi", data)
async def submit_funding_offer(self, type: FundingOfferType, symbol: str, amount: Union[Decimal, str],
rate: Union[Decimal, str], period: int,
flags: Optional[int] = 0):
data = {
"type": type, "symbol": symbol, "amount": amount,
"rate": rate, "period": period,
"flags": flags
}
await self.__handle_websocket_input("fon", data)
async def cancel_funding_offer(self, id: int):
await self.__handle_websocket_input("foc", { "id": id })
async def calc(self, *args: str):
await self.__handle_websocket_input("calc", list(map(lambda arg: [arg], args)))

View File

@@ -146,7 +146,13 @@ class AuthenticatedChannelsHandler(object):
("bu",): serializers.BalanceInfo ("bu",): serializers.BalanceInfo
} }
EVENTS = [ "notification", *list(__abbreviations.values()) ] EVENTS = [
"notification",
"on-req-notification", "ou-req-notification", "oc-req-notification",
"oc_multi-notification",
"fon-req-notification", "foc-req-notification",
*list(__abbreviations.values())
]
def __init__(self, event_emitter, strict = False): def __init__(self, event_emitter, strict = False):
self.event_emitter, self.strict = event_emitter, strict self.event_emitter, self.strict = event_emitter, strict
@@ -168,4 +174,13 @@ class AuthenticatedChannelsHandler(object):
raise BfxWebsocketException(f"Event of type <{type}> not found in self.__handlers.") raise BfxWebsocketException(f"Event of type <{type}> not found in self.__handlers.")
def __notification(self, stream): def __notification(self, stream):
return self.event_emitter.emit("notification", serializers.Notification.parse(*stream)) if stream[1] == "on-req" or stream[1] == "ou-req" or stream[1] == "oc-req":
return self.event_emitter.emit(f"{stream[1]}-notification", serializers._Notification(serializer=serializers.Order).parse(*stream))
if stream[1] == "oc_multi-req":
return self.event_emitter.emit(f"{stream[1]}-notification", serializers._Notification(serializer=serializers.Order, iterate=True).parse(*stream))
if stream[1] == "fon-req" or stream[1] == "foc-req":
return self.event_emitter.emit(f"{stream[1]}-notification", serializers._Notification(serializer=serializers.FundingOffer).parse(*stream))
return self.event_emitter.emit("notification", serializers._Notification(serializer=None).parse(*stream))

View File

@@ -2,6 +2,8 @@ from . import typings
from .. labeler import _Serializer from .. labeler import _Serializer
from .. notification import _Notification
#region Serializers definition for Websocket Public Channels #region Serializers definition for Websocket Public Channels
TradingPairTicker = _Serializer[typings.TradingPairTicker]("TradingPairTicker", labels=[ TradingPairTicker = _Serializer[typings.TradingPairTicker]("TradingPairTicker", labels=[
@@ -292,19 +294,4 @@ BalanceInfo = _Serializer[typings.BalanceInfo]("BalanceInfo", labels=[
"AUM_NET", "AUM_NET",
]) ])
#endregion
#region Serializers definition for Notifications channel
Notification = _Serializer[typings.Notification]("Notification", labels=[
"MTS",
"TYPE",
"MESSAGE_ID",
"_PLACEHOLDER",
"NOTIFY_INFO",
"CODE",
"STATUS",
"TEXT"
])
#endregion #endregion

View File

@@ -1,10 +1,6 @@
from decimal import Decimal
from datetime import datetime
from typing import Type, 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 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]]
@@ -278,69 +274,4 @@ class BalanceInfo(TypedDict):
AUM: float AUM: float
AUM_NET: float AUM_NET: float
#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
#region Type hinting for Websocket Authenticated Inputs
class Inputs:
class Order:
class New(TypedDict, total=False):
gid: Union[Int32, int]
cid: Union[Int45, int]
type: str
symbol: str
amount: Union[Decimal, str]
price: Union[Decimal, str]
lev: Union[Int32, int]
price_trailing: Union[Decimal, str]
price_aux_limit: Union[Decimal, str]
price_oco_stop: Union[Decimal, str]
flags: Union[Int16, int]
tif: Union[datetime, str]
meta: JSON
class Update(TypedDict, total=False):
id: Union[Int64, int]
cid: Union[Int45, int]
cid_date: str
gid: Union[Int32, int]
price: Union[Decimal, str]
amount: Union[Decimal, str]
lev: Union[Int32, int]
delta: Union[Decimal, str]
price_aux_limit: Union[Decimal, str]
price_trailing: Union[Decimal, str]
flags: Union[Int16, int]
tif: Union[datetime, str]
class Cancel(TypedDict, total=False):
id: Union[Int64, int]
cid: Union[Int45, int]
cid_date: Union[datetime, str]
class Offer:
class New(TypedDict, total=False):
type: str
symbol: str
amount: Union[Decimal, str]
rate: Union[Decimal, str]
period: Union[Int32, int]
flags: Union[Int16, int]
class Cancel(TypedDict, total=False):
id: Union[Int32, int]
#endregion #endregion

View File

@@ -21,6 +21,6 @@ notification = bfx.rest.auth.submit_funding_offer(
print("Offer notification:", notification) print("Offer notification:", notification)
offers = bfx.rest.auth.get_active_funding_offers() offers = bfx.rest.auth.get_active_funding_offers(symbol="fUSD")
print("Offers:", offers) print("Offers:", offers)

View File

@@ -3,9 +3,8 @@
import os import os
from bfxapi.client import Client, Constants from bfxapi.client import Client, Constants
from bfxapi.utils.cid import generate_unique_cid
from bfxapi.websocket.enums import Error, OrderType from bfxapi.websocket.enums import Error, OrderType
from bfxapi.websocket.typings import Inputs from bfxapi.websocket.typings import Notification, Order
bfx = Client( bfx = Client(
WSS_HOST=Constants.WSS_HOST, WSS_HOST=Constants.WSS_HOST,
@@ -18,30 +17,28 @@ def on_wss_error(code: Error, msg: str):
print(code, msg) print(code, msg)
@bfx.wss.on("authenticated") @bfx.wss.on("authenticated")
async def on_open(event): async def on_authenticated(event):
print(f"Auth event {event}") print(f"Authentication: {event}.")
order: Inputs.Order.New = { await bfx.wss.inputs.submit_order(
"gid": generate_unique_cid(), type=OrderType.EXCHANGE_LIMIT,
"type": OrderType.EXCHANGE_LIMIT, symbol="tBTCUSD",
"symbol": "tBTCUST", amount="0.1",
"amount": "0.1", price="10000.0"
"price": "10000.0" )
}
await bfx.wss.inputs.order_new(order)
print(f"Order sent") print("The order has been sent.")
@bfx.wss.on("notification") @bfx.wss.on("on-req-notification")
async def on_notification(notification): async def on_notification(notification: Notification):
print(f"Notification {notification}") print(f"Notification: {notification}.")
@bfx.wss.on("order_new") @bfx.wss.on("order_new")
async def on_order_new(order_new: Inputs.Order.New): async def on_order_new(order_new: Order):
print(f"Order new {order_new}") print(f"Order new: {order_new}")
@bfx.wss.on("subscribed") @bfx.wss.on("subscribed")
def on_subscribed(subscription): def on_subscribed(subscription):
print(f"Subscription successful <{subscription}>") print(f"Subscription successful for <{subscription}>.")
bfx.wss.run() bfx.wss.run()