mirror of
https://github.com/aljazceru/bitfinex-api-py.git
synced 2026-02-03 21:04:23 +01:00
Merge pull request #6 from Davi0kProgramsThings/fix/refactoring
Split BfxRestInterface methods in t_ and f_ handlers.
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
from .websocket import BfxWebsocketClient
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from enum import Enum
|
||||
|
||||
class Constants(str, Enum):
|
||||
@@ -10,7 +12,7 @@ class Constants(str, Enum):
|
||||
PUB_WSS_HOST = "wss://api-pub.bitfinex.com/ws/2"
|
||||
|
||||
class Client(object):
|
||||
def __init__(self, WSS_HOST: str = Constants.WSS_HOST, API_KEY: str = None, API_SECRET: str = None, log_level: str = "WARNING"):
|
||||
def __init__(self, WSS_HOST: str = Constants.WSS_HOST, API_KEY: Optional[str] = None, API_SECRET: Optional[str] = None, log_level: str = "WARNING"):
|
||||
self.wss = BfxWebsocketClient(
|
||||
host=WSS_HOST,
|
||||
API_KEY=API_KEY,
|
||||
|
||||
35
bfxapi/exceptions.py
Normal file
35
bfxapi/exceptions.py
Normal file
@@ -0,0 +1,35 @@
|
||||
__all__ = [
|
||||
"BfxBaseException",
|
||||
|
||||
"LabelerSerializerException",
|
||||
"IntegerUnderflowError",
|
||||
"IntegerOverflowflowError"
|
||||
]
|
||||
|
||||
class BfxBaseException(Exception):
|
||||
"""
|
||||
Base class for every custom exception in bfxapi/rest/exceptions.py and bfxapi/websocket/exceptions.py.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
class LabelerSerializerException(BfxBaseException):
|
||||
"""
|
||||
This exception indicates an error thrown by the _Serializer class in bfxapi/labeler.py.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
class IntegerUnderflowError(BfxBaseException):
|
||||
"""
|
||||
This error indicates an underflow in one of the integer types defined in bfxapi/utils/integers.py.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
class IntegerOverflowflowError(BfxBaseException):
|
||||
"""
|
||||
This error indicates an overflow in one of the integer types defined in bfxapi/utils/integers.py.
|
||||
"""
|
||||
|
||||
pass
|
||||
22
bfxapi/labeler.py
Normal file
22
bfxapi/labeler.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from .exceptions import LabelerSerializerException
|
||||
|
||||
from typing import Generic, TypeVar, Iterable, Optional, List, Tuple, Any, cast
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
class _Serializer(Generic[T]):
|
||||
def __init__(self, name: str, labels: List[str], IGNORE: List[str] = [ "_PLACEHOLDER" ]):
|
||||
self.name, self.__labels, self.__IGNORE = name, labels, IGNORE
|
||||
|
||||
def __serialize(self, *args: Any, skip: Optional[List[str]]) -> Iterable[Tuple[str, Any]]:
|
||||
labels = list(filter(lambda label: label not in (skip or list()), self.__labels))
|
||||
|
||||
if len(labels) > len(args):
|
||||
raise LabelerSerializerException("<labels> and <*args> arguments should contain the same amount of elements.")
|
||||
|
||||
for index, label in enumerate(labels):
|
||||
if label not in self.__IGNORE:
|
||||
yield label, args[index]
|
||||
|
||||
def parse(self, *values: Any, skip: Optional[List[str]] = None) -> T:
|
||||
return cast(T, dict(self.__serialize(*values, skip=skip)))
|
||||
@@ -1,20 +1,41 @@
|
||||
import requests
|
||||
import time, hmac, hashlib, json, requests
|
||||
|
||||
from http import HTTPStatus
|
||||
|
||||
from typing import List, Union, Literal, Optional, Any
|
||||
from typing import List, Union, Literal, Optional, Any, cast
|
||||
|
||||
from . import serializers
|
||||
|
||||
from .typings import *
|
||||
from .enums import Configs
|
||||
from .exceptions import RequestParametersError, ResourceNotFound
|
||||
from .enums import Config, Precision, Sort
|
||||
from .exceptions import RequestParametersError, ResourceNotFound, InvalidAuthenticationCredentials
|
||||
|
||||
class BfxRestInterface(object):
|
||||
def __init__(self, host):
|
||||
self.host = host
|
||||
def __init__(self, host, API_KEY = None, API_SECRET = None):
|
||||
self.public = _RestPublicEndpoints(host=host)
|
||||
|
||||
def __GET(self, endpoint, params = None):
|
||||
self.auth = _RestAuthenticatedEndpoints(host=host, API_KEY=API_KEY, API_SECRET=API_SECRET)
|
||||
|
||||
class _Requests(object):
|
||||
def __init__(self, host, API_KEY = None, API_SECRET = None):
|
||||
self.host, self.API_KEY, self.API_SECRET = host, API_KEY, API_SECRET
|
||||
|
||||
def __build_authentication_headers(self, endpoint, data):
|
||||
nonce = str(int(time.time()) * 1000)
|
||||
|
||||
signature = hmac.new(
|
||||
self.API_SECRET.encode("utf8"),
|
||||
f"/api/v2/{endpoint}{nonce}{json.dumps(data)}".encode("utf8"),
|
||||
hashlib.sha384
|
||||
).hexdigest()
|
||||
|
||||
return {
|
||||
"bfx-nonce": nonce,
|
||||
"bfx-signature": signature,
|
||||
"bfx-apikey": self.API_KEY
|
||||
}
|
||||
|
||||
def _GET(self, endpoint, params = None):
|
||||
response = requests.get(f"{self.host}/{endpoint}", params=params)
|
||||
|
||||
if response.status_code == HTTPStatus.NOT_FOUND:
|
||||
@@ -28,136 +49,182 @@ class BfxRestInterface(object):
|
||||
|
||||
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) }
|
||||
|
||||
response = requests.post(f"{self.host}/{endpoint}", params=params, data=json.dumps(data), headers=headers)
|
||||
|
||||
if response.status_code == HTTPStatus.NOT_FOUND:
|
||||
raise ResourceNotFound(f"No resources found at endpoint <{endpoint}>.")
|
||||
|
||||
data = response.json()
|
||||
|
||||
if len(data) and data[0] == "error":
|
||||
if data[1] == 10020:
|
||||
raise RequestParametersError(f"The request was rejected with the following parameter error: <{data[2]}>")
|
||||
|
||||
if data[1] == 10100:
|
||||
raise InvalidAuthenticationCredentials("Cannot authenticate with given API-KEY and API-SECRET.")
|
||||
|
||||
return data
|
||||
|
||||
class _RestPublicEndpoints(_Requests):
|
||||
def platform_status(self) -> PlatformStatus:
|
||||
return serializers.PlatformStatus.parse(*self.__GET("platform/status"))
|
||||
return serializers.PlatformStatus.parse(*self._GET("platform/status"))
|
||||
|
||||
def tickers(self, symbols: List[str]) -> List[Union[TradingPairTicker, FundingCurrencyTicker]]:
|
||||
data = self.__GET("tickers", params={ "symbols": ",".join(symbols) })
|
||||
data = self._GET("tickers", params={ "symbols": ",".join(symbols) })
|
||||
|
||||
return [
|
||||
{
|
||||
"t": serializers.TradingPairTicker.parse,
|
||||
"f": serializers.FundingCurrencyTicker.parse
|
||||
}[subdata[0][0]](*subdata)
|
||||
parsers = { "t": serializers.TradingPairTicker.parse, "f": serializers.FundingCurrencyTicker.parse }
|
||||
|
||||
return [ parsers[subdata[0][0]](*subdata) for subdata in data ]
|
||||
|
||||
for subdata in data
|
||||
]
|
||||
def t_tickers(self, pairs: Union[List[str], Literal["ALL"]]) -> List[TradingPairTicker]:
|
||||
if isinstance(pairs, str) and pairs == "ALL":
|
||||
return [ cast(TradingPairTicker, subdata) for subdata in self.tickers([ "ALL" ]) if cast(str, subdata["SYMBOL"]).startswith("t") ]
|
||||
|
||||
def ticker(self, symbol: str) -> Union[TradingPairTicker, FundingCurrencyTicker]:
|
||||
data = self.__GET(f"ticker/{symbol}")
|
||||
data = self.tickers([ "t" + pair for pair in pairs ])
|
||||
|
||||
return {
|
||||
"t": serializers.TradingPairTicker.parse,
|
||||
"f": serializers.FundingCurrencyTicker.parse
|
||||
}[symbol[0]](*data, skip=["SYMBOL"])
|
||||
return cast(List[TradingPairTicker], data)
|
||||
|
||||
def tickers_history(self, symbols: List[str], start: Optional[int] = None, end: Optional[int] = None, limit: Optional[int] = None) -> TickerHistories:
|
||||
def f_tickers(self, currencies: Union[List[str], Literal["ALL"]]) -> List[FundingCurrencyTicker]:
|
||||
if isinstance(currencies, str) and currencies == "ALL":
|
||||
return [ cast(FundingCurrencyTicker, subdata) for subdata in self.tickers([ "ALL" ]) if cast(str, subdata["SYMBOL"]).startswith("f") ]
|
||||
|
||||
data = self.tickers([ "f" + currency for currency in currencies ])
|
||||
|
||||
return cast(List[FundingCurrencyTicker], data)
|
||||
|
||||
def t_ticker(self, pair: str) -> TradingPairTicker:
|
||||
return serializers.TradingPairTicker.parse(*self._GET(f"ticker/t{pair}"), skip=["SYMBOL"])
|
||||
|
||||
def f_ticker(self, currency: str) -> FundingCurrencyTicker:
|
||||
return serializers.FundingCurrencyTicker.parse(*self._GET(f"ticker/f{currency}"), skip=["SYMBOL"])
|
||||
|
||||
def tickers_history(self, symbols: List[str], start: Optional[str] = None, end: Optional[str] = None, limit: Optional[int] = None) -> List[TickersHistory]:
|
||||
params = {
|
||||
"symbols": ",".join(symbols),
|
||||
"start": start, "end": end,
|
||||
"limit": limit
|
||||
}
|
||||
|
||||
data = self.__GET("tickers/hist", params=params)
|
||||
data = self._GET("tickers/hist", params=params)
|
||||
|
||||
return [ serializers.TickerHistory.parse(*subdata) for subdata in data ]
|
||||
return [ serializers.TickersHistory.parse(*subdata) for subdata in data ]
|
||||
|
||||
def trades(self, symbol: str, limit: Optional[int] = None, start: Optional[str] = None, end: Optional[str] = None, sort: Optional[int] = None) -> Union[TradingPairTrades, FundingCurrencyTrades]:
|
||||
params = { "symbol": symbol, "limit": limit, "start": start, "end": end, "sort": sort }
|
||||
|
||||
data = self.__GET(f"trades/{symbol}/hist", params=params)
|
||||
def t_trades(self, pair: str, limit: Optional[int] = None, start: Optional[str] = None, end: Optional[str] = None, sort: Optional[Sort] = None) -> List[TradingPairTrade]:
|
||||
params = { "limit": limit, "start": start, "end": end, "sort": sort }
|
||||
data = self._GET(f"trades/{'t' + pair}/hist", params=params)
|
||||
return [ serializers.TradingPairTrade.parse(*subdata) for subdata in data ]
|
||||
|
||||
return [
|
||||
{
|
||||
"t": serializers.TradingPairTrade.parse,
|
||||
"f": serializers.FundingCurrencyTrade.parse
|
||||
}[symbol[0]](*subdata)
|
||||
def f_trades(self, currency: str, limit: Optional[int] = None, start: Optional[str] = None, end: Optional[str] = None, sort: Optional[Sort] = None) -> List[FundingCurrencyTrade]:
|
||||
params = { "limit": limit, "start": start, "end": end, "sort": sort }
|
||||
data = self._GET(f"trades/{'f' + currency}/hist", params=params)
|
||||
return [ serializers.FundingCurrencyTrade.parse(*subdata) for subdata in data ]
|
||||
|
||||
for subdata in data
|
||||
]
|
||||
def t_book(self, pair: str, precision: Literal["P0", "P1", "P2", "P3", "P4"], len: Optional[Literal[1, 25, 100]] = None) -> List[TradingPairBook]:
|
||||
return [ serializers.TradingPairBook.parse(*subdata) for subdata in self._GET(f"book/{'t' + pair}/{precision}", params={ "len": len }) ]
|
||||
|
||||
def book(self, symbol: str, precision: str, len: Optional[int] = None) -> Union[TradingPairBooks, FundingCurrencyBooks, TradingPairRawBooks, FundingCurrencyRawBooks]:
|
||||
data = self.__GET(f"book/{symbol}/{precision}", params={ "len": len })
|
||||
|
||||
return [
|
||||
{
|
||||
"t": precision == "R0" and serializers.TradingPairRawBook.parse or serializers.TradingPairBook.parse,
|
||||
"f": precision == "R0" and serializers.FundingCurrencyRawBook.parse or serializers.FundingCurrencyBook.parse,
|
||||
}[symbol[0]](*subdata)
|
||||
def f_book(self, currency: str, precision: Literal["P0", "P1", "P2", "P3", "P4"], len: Optional[Literal[1, 25, 100]] = None) -> List[FundingCurrencyBook]:
|
||||
return [ serializers.FundingCurrencyBook.parse(*subdata) for subdata in self._GET(f"book/{'f' + currency}/{precision}", params={ "len": len }) ]
|
||||
|
||||
for subdata in data
|
||||
]
|
||||
def t_raw_book(self, pair: str, len: Optional[Literal[1, 25, 100]] = None) -> List[TradingPairRawBook]:
|
||||
return [ serializers.TradingPairRawBook.parse(*subdata) for subdata in self._GET(f"book/{'t' + pair}/R0", params={ "len": len }) ]
|
||||
|
||||
def stats(
|
||||
def f_raw_book(self, currency: str, len: Optional[Literal[1, 25, 100]] = None) -> List[FundingCurrencyRawBook]:
|
||||
return [ serializers.FundingCurrencyRawBook.parse(*subdata) for subdata in self._GET(f"book/{'f' + currency}/R0", params={ "len": len }) ]
|
||||
|
||||
def stats_hist(
|
||||
self,
|
||||
resource: str, section: Literal["hist", "last"],
|
||||
sort: Optional[int] = None, start: Optional[str] = None, end: Optional[str] = None, limit: Optional[int] = None
|
||||
) -> Union[Stat, Stats]:
|
||||
resource: str,
|
||||
sort: Optional[Sort] = None, start: Optional[str] = None, end: Optional[str] = None, limit: Optional[int] = None
|
||||
) -> List[Statistic]:
|
||||
params = { "sort": sort, "start": start, "end": end, "limit": limit }
|
||||
data = self._GET(f"stats1/{resource}/hist", params=params)
|
||||
return [ serializers.Statistic.parse(*subdata) for subdata in data ]
|
||||
|
||||
data = self.__GET(f"stats1/{resource}/{section}", params=params)
|
||||
def stats_last(
|
||||
self,
|
||||
resource: str,
|
||||
sort: Optional[Sort] = None, start: Optional[str] = None, end: Optional[str] = None, limit: Optional[int] = None
|
||||
) -> Statistic:
|
||||
params = { "sort": sort, "start": start, "end": end, "limit": limit }
|
||||
data = self._GET(f"stats1/{resource}/last", params=params)
|
||||
return serializers.Statistic.parse(*data)
|
||||
|
||||
if section == "last":
|
||||
return serializers.Stat.parse(*data)
|
||||
return [ serializers.Stat.parse(*subdata) for subdata in data ]
|
||||
|
||||
def candles(
|
||||
def candles_hist(
|
||||
self,
|
||||
resource: str, section: Literal["hist", "last"],
|
||||
sort: Optional[int] = None, start: Optional[str] = None, end: Optional[str] = None, limit: Optional[int] = None
|
||||
) -> Union[Candle, Candles]:
|
||||
resource: str,
|
||||
sort: Optional[Sort] = None, start: Optional[str] = None, end: Optional[str] = None, limit: Optional[int] = None
|
||||
) -> List[Candle]:
|
||||
params = { "sort": sort, "start": start, "end": end, "limit": limit }
|
||||
|
||||
data = self.__GET(f"candles/{resource}/{section}", params=params)
|
||||
|
||||
if section == "last":
|
||||
return serializers.Candle.parse(*data)
|
||||
data = self._GET(f"candles/{resource}/hist", params=params)
|
||||
return [ serializers.Candle.parse(*subdata) for subdata in data ]
|
||||
|
||||
def derivatives_status(self, type: str, keys: List[str] = None) -> DerivativeStatuses:
|
||||
def candles_last(
|
||||
self,
|
||||
resource: str,
|
||||
sort: Optional[Sort] = None, start: Optional[str] = None, end: Optional[str] = None, limit: Optional[int] = None
|
||||
) -> Candle:
|
||||
params = { "sort": sort, "start": start, "end": end, "limit": limit }
|
||||
data = self._GET(f"candles/{resource}/last", params=params)
|
||||
return serializers.Candle.parse(*data)
|
||||
|
||||
def derivatives_status(self, type: str, keys: List[str]) -> List[DerivativesStatus]:
|
||||
params = { "keys": ",".join(keys) }
|
||||
|
||||
data = self.__GET(f"status/{type}", params=params)
|
||||
data = self._GET(f"status/{type}", params=params)
|
||||
|
||||
return [ serializers.DerivativesStatus.parse(*subdata) for subdata in data ]
|
||||
|
||||
def derivatives_status_history(
|
||||
self,
|
||||
type: str, symbol: str,
|
||||
sort: Optional[int] = None, start: Optional[str] = None, end: Optional[str] = None, limit: Optional[int] = None
|
||||
) -> DerivativeStatuses:
|
||||
sort: Optional[Sort] = None, start: Optional[str] = None, end: Optional[str] = None, limit: Optional[int] = None
|
||||
) -> List[DerivativesStatus]:
|
||||
params = { "sort": sort, "start": start, "end": end, "limit": limit }
|
||||
|
||||
data = self.__GET(f"status/{type}/{symbol}/hist", params=params)
|
||||
data = self._GET(f"status/{type}/{symbol}/hist", params=params)
|
||||
|
||||
return [ serializers.DerivativesStatus.parse(*subdata, skip=[ "KEY" ]) for subdata in data ]
|
||||
|
||||
def liquidations(self, sort: Optional[int] = None, start: Optional[str] = None, end: Optional[str] = None, limit: Optional[int] = None) -> Liquidations:
|
||||
def liquidations(self, sort: Optional[Sort] = None, start: Optional[str] = None, end: Optional[str] = None, limit: Optional[int] = None) -> List[Liquidation]:
|
||||
params = { "sort": sort, "start": start, "end": end, "limit": limit }
|
||||
|
||||
data = self.__GET("liquidations/hist", params=params)
|
||||
data = self._GET("liquidations/hist", params=params)
|
||||
|
||||
return [ serializers.Liquidation.parse(*subdata[0]) for subdata in data ]
|
||||
|
||||
def leaderboards(
|
||||
def leaderboards_hist(
|
||||
self,
|
||||
resource: str, section: Literal["hist", "last"],
|
||||
sort: Optional[int] = None, start: Optional[str] = None, end: Optional[str] = None, limit: Optional[int] = None
|
||||
) -> Union[Leaderboard, Leaderboards]:
|
||||
resource: str,
|
||||
sort: Optional[Sort] = None, start: Optional[str] = None, end: Optional[str] = None, limit: Optional[int] = None
|
||||
) -> List[Leaderboard]:
|
||||
params = { "sort": sort, "start": start, "end": end, "limit": limit }
|
||||
|
||||
data = self.__GET(f"rankings/{resource}/{section}", params=params)
|
||||
|
||||
if section == "last":
|
||||
return serializers.Leaderboard.parse(*data)
|
||||
data = self._GET(f"rankings/{resource}/hist", params=params)
|
||||
return [ serializers.Leaderboard.parse(*subdata) for subdata in data ]
|
||||
|
||||
def funding_stats(self, symbol: str, start: Optional[str] = None, end: Optional[str] = None, limit: Optional[int] = None) -> FundingStats:
|
||||
def leaderboards_last(
|
||||
self,
|
||||
resource: str,
|
||||
sort: Optional[Sort] = None, start: Optional[str] = None, end: Optional[str] = None, limit: Optional[int] = None
|
||||
) -> Leaderboard:
|
||||
params = { "sort": sort, "start": start, "end": end, "limit": limit }
|
||||
data = self._GET(f"rankings/{resource}/last", params=params)
|
||||
return serializers.Leaderboard.parse(*data)
|
||||
|
||||
def funding_stats(self, symbol: str, start: Optional[str] = None, end: Optional[str] = None, limit: Optional[int] = None) -> List[FundingStatistic]:
|
||||
params = { "start": start, "end": end, "limit": limit }
|
||||
|
||||
data = self.__GET(f"funding/stats/{symbol}/hist", params=params)
|
||||
data = self._GET(f"funding/stats/{symbol}/hist", params=params)
|
||||
|
||||
return [ serializers.FundingStat.parse(*subdata) for subdata in data ]
|
||||
return [ serializers.FundingStatistic.parse(*subdata) for subdata in data ]
|
||||
|
||||
def conf(self, config: Configs) -> Any:
|
||||
return self.__GET(f"conf/{config}")[0]
|
||||
def conf(self, config: Config) -> Any:
|
||||
return self._GET(f"conf/{config}")[0]
|
||||
|
||||
class _RestAuthenticatedEndpoints(_Requests):
|
||||
__PREFIX = "auth/"
|
||||
@@ -1,6 +1,6 @@
|
||||
from enum import Enum
|
||||
|
||||
class Configs(str, Enum):
|
||||
class Config(str, Enum):
|
||||
MAP_CURRENCY_SYM = "pub:map:currency:sym"
|
||||
MAP_CURRENCY_LABEL = "pub:map:currency:label"
|
||||
MAP_CURRENCY_UNIT = "pub:map:currency:unit"
|
||||
@@ -22,4 +22,15 @@ class Configs(str, Enum):
|
||||
INFO_TX_STATUS = "pub:info:tx:status"
|
||||
|
||||
SPEC_MARGIN = "pub:spec:margin",
|
||||
FEES = "pub:fees"
|
||||
FEES = "pub:fees"
|
||||
|
||||
class Precision(str, Enum):
|
||||
P0 = "P0"
|
||||
P1 = "P1"
|
||||
P2 = "P2"
|
||||
P3 = "P3"
|
||||
P4 = "P4"
|
||||
|
||||
class Sort(int, Enum):
|
||||
ASCENDING = +1
|
||||
DESCENDING = -1
|
||||
@@ -1,11 +1,16 @@
|
||||
from .. exceptions import BfxBaseException
|
||||
|
||||
__all__ = [
|
||||
"BfxRestException",
|
||||
|
||||
"RequestParametersError",
|
||||
"ResourceNotFound"
|
||||
"ResourceNotFound",
|
||||
"InvalidAuthenticationCredentials"
|
||||
]
|
||||
|
||||
class BfxRestException(Exception):
|
||||
class BfxRestException(BfxBaseException):
|
||||
"""
|
||||
Base class for all exceptions defined in bfxapi/rest/exceptions.py.
|
||||
Base class for all custom exceptions in bfxapi/rest/exceptions.py.
|
||||
"""
|
||||
|
||||
pass
|
||||
@@ -22,4 +27,11 @@ class ResourceNotFound(BfxRestException):
|
||||
This error indicates a failed HTTP request to a non-existent resource.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
class InvalidAuthenticationCredentials(BfxRestException):
|
||||
"""
|
||||
This error indicates that the user has provided incorrect credentials (API-KEY and API-SECRET) for authentication.
|
||||
"""
|
||||
|
||||
pass
|
||||
@@ -1,27 +1,6 @@
|
||||
from typing import Generic, TypeVar, Iterable, Optional, List, Any
|
||||
|
||||
from . import typings
|
||||
|
||||
from .exceptions import BfxRestException
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
class _Serializer(Generic[T]):
|
||||
def __init__(self, name: str, labels: List[str], IGNORE: List[str] = [ "_PLACEHOLDER" ]):
|
||||
self.name, self.__labels, self.__IGNORE = name, labels, IGNORE
|
||||
|
||||
def __serialize(self, *args: Any, skip: Optional[List[str]]) -> Iterable[T]:
|
||||
labels = list(filter(lambda label: label not in (skip or list()), self.__labels))
|
||||
|
||||
if len(labels) > len(args):
|
||||
raise BfxRestException("<labels> and <*args> arguments should contain the same amount of elements.")
|
||||
|
||||
for index, label in enumerate(labels):
|
||||
if label not in self.__IGNORE:
|
||||
yield label, args[index]
|
||||
|
||||
def parse(self, *values: Any, skip: Optional[List[str]] = None) -> T:
|
||||
return dict(self.__serialize(*values, skip=skip))
|
||||
from .. labeler import _Serializer
|
||||
|
||||
#region Serializers definition for Rest Public Endpoints
|
||||
|
||||
@@ -63,7 +42,7 @@ FundingCurrencyTicker = _Serializer[typings.FundingCurrencyTicker]("FundingCurre
|
||||
"FRR_AMOUNT_AVAILABLE"
|
||||
])
|
||||
|
||||
TickerHistory = _Serializer[typings.TickerHistory]("TickerHistory", labels=[
|
||||
TickersHistory = _Serializer[typings.TickersHistory]("TickersHistory", labels=[
|
||||
"SYMBOL",
|
||||
"BID",
|
||||
"_PLACEHOLDER",
|
||||
@@ -120,7 +99,7 @@ FundingCurrencyRawBook = _Serializer[typings.FundingCurrencyRawBook]("FundingCur
|
||||
"AMOUNT"
|
||||
])
|
||||
|
||||
Stat = _Serializer[typings.Stat]("Stat", labels=[
|
||||
Statistic = _Serializer[typings.Statistic]("Statistic", labels=[
|
||||
"MTS",
|
||||
"VALUE"
|
||||
])
|
||||
@@ -189,7 +168,7 @@ Leaderboard = _Serializer[typings.Leaderboard]("Leaderboard", labels=[
|
||||
"TWITTER_HANDLE"
|
||||
])
|
||||
|
||||
FundingStat = _Serializer[typings.FundingStat]("FundingStat", labels=[
|
||||
FundingStatistic = _Serializer[typings.FundingStatistic]("FundingStatistic", labels=[
|
||||
"TIMESTAMP",
|
||||
"_PLACEHOLDER",
|
||||
"_PLACEHOLDER",
|
||||
|
||||
@@ -2,140 +2,130 @@ from typing import Type, Tuple, List, Dict, TypedDict, Union, Optional, Any
|
||||
|
||||
#region Type hinting for Rest Public Endpoints
|
||||
|
||||
PlatformStatus = TypedDict("PlatformStatus", {
|
||||
"OPERATIVE": int
|
||||
})
|
||||
class PlatformStatus(TypedDict):
|
||||
OPERATIVE: int
|
||||
|
||||
TradingPairTicker = TypedDict("TradingPairTicker", {
|
||||
"SYMBOL": Optional[str],
|
||||
"BID": float,
|
||||
"BID_SIZE": float,
|
||||
"ASK": float,
|
||||
"ASK_SIZE": float,
|
||||
"DAILY_CHANGE": float,
|
||||
"DAILY_CHANGE_RELATIVE": float,
|
||||
"LAST_PRICE": float,
|
||||
"VOLUME": float,
|
||||
"HIGH": float,
|
||||
"LOW": float
|
||||
})
|
||||
class TradingPairTicker(TypedDict):
|
||||
SYMBOL: Optional[str]
|
||||
BID: float
|
||||
BID_SIZE: float
|
||||
ASK: float
|
||||
ASK_SIZE: float
|
||||
DAILY_CHANGE: float
|
||||
DAILY_CHANGE_RELATIVE: float
|
||||
LAST_PRICE: float
|
||||
VOLUME: float
|
||||
HIGH: float
|
||||
LOW: float
|
||||
|
||||
FundingCurrencyTicker = TypedDict("FundingCurrencyTicker", {
|
||||
"SYMBOL": Optional[str],
|
||||
"FRR": float,
|
||||
"BID": float,
|
||||
"BID_PERIOD": int,
|
||||
"BID_SIZE": float,
|
||||
"ASK": float,
|
||||
"ASK_PERIOD": int,
|
||||
"ASK_SIZE": float,
|
||||
"DAILY_CHANGE": float,
|
||||
"DAILY_CHANGE_RELATIVE": float,
|
||||
"LAST_PRICE": float,
|
||||
"VOLUME": float,
|
||||
"HIGH": float,
|
||||
"LOW": float,
|
||||
"FRR_AMOUNT_AVAILABLE": float
|
||||
})
|
||||
class FundingCurrencyTicker(TypedDict):
|
||||
SYMBOL: Optional[str]
|
||||
FRR: float
|
||||
BID: float
|
||||
BID_PERIOD: int
|
||||
BID_SIZE: float
|
||||
ASK: float
|
||||
ASK_PERIOD: int
|
||||
ASK_SIZE: float
|
||||
DAILY_CHANGE: float
|
||||
DAILY_CHANGE_RELATIVE: float
|
||||
LAST_PRICE: float
|
||||
VOLUME: float
|
||||
HIGH: float
|
||||
LOW: float
|
||||
FRR_AMOUNT_AVAILABLE: float
|
||||
|
||||
TickerHistory = TypedDict("TickerHistory", {
|
||||
"SYMBOL": str,
|
||||
"BID": float,
|
||||
"ASK": float,
|
||||
"MTS": int
|
||||
})
|
||||
class TickersHistory(TypedDict):
|
||||
SYMBOL: str
|
||||
BID: float
|
||||
ASK: float
|
||||
MTS: int
|
||||
|
||||
TickerHistories = List[TickerHistory]
|
||||
class TradingPairTrade(TypedDict):
|
||||
ID: int
|
||||
MTS: int
|
||||
AMOUNT: float
|
||||
PRICE: float
|
||||
|
||||
(TradingPairTrade, FundingCurrencyTrade) = (
|
||||
TypedDict("TradingPairTrade", { "ID": int, "MTS": int, "AMOUNT": float, "PRICE": float }),
|
||||
TypedDict("FundingCurrencyTrade", { "ID": int, "MTS": int, "AMOUNT": float, "RATE": float, "PERIOD": int })
|
||||
)
|
||||
class FundingCurrencyTrade(TypedDict):
|
||||
ID: int
|
||||
MTS: int
|
||||
AMOUNT: float
|
||||
RATE: float
|
||||
PERIOD: int
|
||||
|
||||
(TradingPairTrades, FundingCurrencyTrades) = (List[TradingPairTrade], List[FundingCurrencyTrade])
|
||||
class TradingPairBook(TypedDict):
|
||||
PRICE: float
|
||||
COUNT: int
|
||||
AMOUNT: float
|
||||
|
||||
class FundingCurrencyBook(TypedDict):
|
||||
RATE: float
|
||||
PERIOD: int
|
||||
COUNT: int
|
||||
AMOUNT: float
|
||||
|
||||
class TradingPairRawBook(TypedDict):
|
||||
ORDER_ID: int
|
||||
PRICE: float
|
||||
AMOUNT: float
|
||||
|
||||
class FundingCurrencyRawBook(TypedDict):
|
||||
OFFER_ID: int
|
||||
PERIOD: int
|
||||
RATE: float
|
||||
AMOUNT: float
|
||||
|
||||
(TradingPairBook, FundingCurrencyBook) = (
|
||||
TypedDict("TradingPairBook", { "PRICE": float, "COUNT": int, "AMOUNT": float }),
|
||||
TypedDict("FundingCurrencyBook", { "RATE": float, "PERIOD": int, "COUNT": int, "AMOUNT": float })
|
||||
)
|
||||
class Statistic(TypedDict):
|
||||
MTS: int
|
||||
VALUE: float
|
||||
|
||||
(TradingPairBooks, FundingCurrencyBooks) = (List[TradingPairBook], List[FundingCurrencyBook])
|
||||
class Candle(TypedDict):
|
||||
MTS: int
|
||||
OPEN: float
|
||||
CLOSE: float
|
||||
HIGH: float
|
||||
LOW: float
|
||||
VOLUME: float
|
||||
|
||||
(TradingPairRawBook, FundingCurrencyRawBook) = (
|
||||
TypedDict("TradingPairRawBook", { "ORDER_ID": int, "PRICE": float, "AMOUNT": float }),
|
||||
TypedDict("FundingCurrencyRawBook", { "OFFER_ID": int, "PERIOD": int, "RATE": float, "AMOUNT": float }),
|
||||
)
|
||||
class DerivativesStatus(TypedDict):
|
||||
KEY: Optional[str]
|
||||
MTS: int
|
||||
DERIV_PRICE: float
|
||||
SPOT_PRICE: float
|
||||
INSURANCE_FUND_BALANCE: float
|
||||
NEXT_FUNDING_EVT_TIMESTAMP_MS: int
|
||||
NEXT_FUNDING_ACCRUED: float
|
||||
NEXT_FUNDING_STEP: int
|
||||
CURRENT_FUNDING: float
|
||||
MARK_PRICE: float
|
||||
OPEN_INTEREST: float
|
||||
CLAMP_MIN: float
|
||||
CLAMP_MAX: float
|
||||
|
||||
(TradingPairRawBooks, FundingCurrencyRawBooks) = (List[TradingPairRawBook], List[FundingCurrencyRawBook])
|
||||
class Liquidation(TypedDict):
|
||||
POS_ID: int
|
||||
MTS: int
|
||||
SYMBOL: str
|
||||
AMOUNT: float
|
||||
BASE_PRICE: float
|
||||
IS_MATCH: int
|
||||
IS_MARKET_SOLD: int
|
||||
PRICE_ACQUIRED: float
|
||||
|
||||
Stat = TypedDict("Stat", {
|
||||
"MTS": int,
|
||||
"VALUE": float
|
||||
})
|
||||
class Leaderboard(TypedDict):
|
||||
MTS: int
|
||||
USERNAME: str
|
||||
RANKING: int
|
||||
VALUE: float
|
||||
TWITTER_HANDLE: Optional[str]
|
||||
|
||||
Stats = List[Stat]
|
||||
|
||||
Candle = TypedDict("Candle", {
|
||||
"MTS": int,
|
||||
"OPEN": float,
|
||||
"CLOSE": float,
|
||||
"HIGH": float,
|
||||
"LOW": float,
|
||||
"VOLUME": float
|
||||
})
|
||||
|
||||
Candles = List[Candle]
|
||||
|
||||
DerivativesStatus = TypedDict("DerivativesStatus", {
|
||||
"KEY": Optional[str],
|
||||
"MTS": int,
|
||||
"DERIV_PRICE": float,
|
||||
"SPOT_PRICE": float,
|
||||
"INSURANCE_FUND_BALANCE": float,
|
||||
"NEXT_FUNDING_EVT_TIMESTAMP_MS": int,
|
||||
"NEXT_FUNDING_ACCRUED": float,
|
||||
"NEXT_FUNDING_STEP": int,
|
||||
"CURRENT_FUNDING": float,
|
||||
"MARK_PRICE": float,
|
||||
"OPEN_INTEREST": float,
|
||||
"CLAMP_MIN": float,
|
||||
"CLAMP_MAX": float
|
||||
})
|
||||
|
||||
DerivativeStatuses = List[DerivativesStatus]
|
||||
|
||||
Liquidation = TypedDict("Liquidation", {
|
||||
"POS_ID": int,
|
||||
"MTS": int,
|
||||
"SYMBOL": str,
|
||||
"AMOUNT": float,
|
||||
"BASE_PRICE": float,
|
||||
"IS_MATCH": int,
|
||||
"IS_MARKET_SOLD": int,
|
||||
"PRICE_ACQUIRED": float
|
||||
})
|
||||
|
||||
Liquidations = List[Liquidation]
|
||||
|
||||
Leaderboard = TypedDict("Leaderboard", {
|
||||
"MTS": int,
|
||||
"USERNAME": str,
|
||||
"RANKING": int,
|
||||
"VALUE": float,
|
||||
"TWITTER_HANDLE": Optional[str]
|
||||
})
|
||||
|
||||
Leaderboards = List[Leaderboard]
|
||||
|
||||
FundingStat = TypedDict("FundingStat", {
|
||||
"TIMESTAMP": int,
|
||||
"FRR": float,
|
||||
"AVG_PERIOD": float,
|
||||
"FUNDING_AMOUNT": float,
|
||||
"FUNDING_AMOUNT_USED": float,
|
||||
"FUNDING_BELOW_THRESHOLD": float
|
||||
})
|
||||
|
||||
FundingStats = List[FundingStat]
|
||||
class FundingStatistic(TypedDict):
|
||||
TIMESTAMP: int
|
||||
FRR: float
|
||||
AVG_PERIOD: float
|
||||
FUNDING_AMOUNT: float
|
||||
FUNDING_AMOUNT_USED: float
|
||||
FUNDING_BELOW_THRESHOLD: float
|
||||
|
||||
#endregion
|
||||
9
bfxapi/utils/decimal.py
Normal file
9
bfxapi/utils/decimal.py
Normal file
@@ -0,0 +1,9 @@
|
||||
import json
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
class DecimalEncoder(json.JSONEncoder):
|
||||
def default(self, obj):
|
||||
if isinstance(obj, Decimal):
|
||||
return str(obj)
|
||||
return json.JSONEncoder.default(self, obj)
|
||||
35
bfxapi/utils/integers.py
Normal file
35
bfxapi/utils/integers.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from typing import cast, TypeVar
|
||||
|
||||
from .. exceptions import IntegerUnderflowError, IntegerOverflowflowError
|
||||
|
||||
__all__ = [ "Int16", "Int32", "Int45", "Int64" ]
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
class _Int(int):
|
||||
def __new__(cls: T, integer: int) -> T:
|
||||
assert hasattr(cls, "_BITS"), "_Int must be extended by a class that has a static member _BITS (indicating the number of bits with which to represent the integers)."
|
||||
|
||||
bits = cls._BITS - 1
|
||||
|
||||
min, max = -(2 ** bits), (2 ** bits) - 1
|
||||
|
||||
if integer < min:
|
||||
raise IntegerUnderflowError(f"Underflow. Cannot store <{integer}> in {cls._BITS} bits integer. The min and max bounds are {min} and {max}.")
|
||||
|
||||
if integer > max:
|
||||
raise IntegerOverflowflowError(f"Overflow. Cannot store <{integer}> in {cls._BITS} bits integer. The min and max bounds are {min} and {max}.")
|
||||
|
||||
return cast(T, super().__new__(int, integer))
|
||||
|
||||
class Int16(_Int):
|
||||
_BITS = 16
|
||||
|
||||
class Int32(_Int):
|
||||
_BITS = 32
|
||||
|
||||
class Int45(_Int):
|
||||
_BITS = 45
|
||||
|
||||
class Int64(_Int):
|
||||
_BITS = 64
|
||||
@@ -1,25 +1,40 @@
|
||||
import traceback, json, asyncio, hmac, hashlib, time, uuid, websockets
|
||||
|
||||
from typing import Tuple, Union, Literal, TypeVar, Callable, cast
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from pyee.asyncio import AsyncIOEventEmitter
|
||||
|
||||
from .typings import Inputs, Tuple, Union
|
||||
from .typings import Inputs
|
||||
from .handlers import Channels, PublicChannelsHandler, AuthenticatedChannelsHandler
|
||||
from .exceptions import ConnectionNotOpen, TooManySubscriptions, WebsocketAuthenticationRequired, InvalidAuthenticationCredentials, EventNotSupported, OutdatedClientVersion
|
||||
|
||||
from ..utils.decimal import DecimalEncoder
|
||||
|
||||
from ..utils.logger import Formatter, CustomLogger
|
||||
|
||||
_HEARTBEAT = "hb"
|
||||
|
||||
def _require_websocket_connection(function):
|
||||
F = TypeVar("F", bound=Callable[..., Literal[None]])
|
||||
|
||||
def _require_websocket_connection(function: F) -> F:
|
||||
async def wrapper(self, *args, **kwargs):
|
||||
if self.websocket == None or self.websocket.open == False:
|
||||
raise ConnectionNotOpen("No open connection with the server.")
|
||||
|
||||
await function(self, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
return cast(F, wrapper)
|
||||
|
||||
def _require_websocket_authentication(function: F) -> F:
|
||||
async def wrapper(self, *args, **kwargs):
|
||||
if self.authentication == False:
|
||||
raise WebsocketAuthenticationRequired("To perform this action you need to authenticate using your API_KEY and API_SECRET.")
|
||||
|
||||
await _require_websocket_connection(function)(self, *args, **kwargs)
|
||||
|
||||
return cast(F, wrapper)
|
||||
|
||||
class BfxWebsocketClient(object):
|
||||
VERSION = 2
|
||||
@@ -118,22 +133,13 @@ class BfxWebsocketClient(object):
|
||||
for bucket in self.buckets:
|
||||
await bucket._close(code=code, reason=reason)
|
||||
|
||||
def __require_websocket_authentication(function):
|
||||
async def wrapper(self, *args, **kwargs):
|
||||
if self.authentication == False:
|
||||
raise WebsocketAuthenticationRequired("To perform this action you need to authenticate using your API_KEY and API_SECRET.")
|
||||
|
||||
await _require_websocket_connection(function)(self, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
@__require_websocket_authentication
|
||||
@_require_websocket_authentication
|
||||
async def notify(self, info, MESSAGE_ID=None, **kwargs):
|
||||
await self.websocket.send(json.dumps([ 0, "n", MESSAGE_ID, { "type": "ucm-test", "info": info, **kwargs } ]))
|
||||
|
||||
@__require_websocket_authentication
|
||||
@_require_websocket_authentication
|
||||
async def __handle_websocket_input(self, input, data):
|
||||
await self.websocket.send(json.dumps([ 0, input, None, data]))
|
||||
await self.websocket.send(json.dumps([ 0, input, None, data], cls=DecimalEncoder))
|
||||
|
||||
def __bucket_open_signal(self, index):
|
||||
if all(bucket.websocket != None and bucket.websocket.open == True for bucket in self.buckets):
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
from .. exceptions import BfxBaseException
|
||||
|
||||
__all__ = [
|
||||
"BfxWebsocketException",
|
||||
|
||||
"ConnectionNotOpen",
|
||||
"TooManySubscriptions",
|
||||
"WebsocketAuthenticationRequired",
|
||||
@@ -7,9 +11,9 @@ __all__ = [
|
||||
"OutdatedClientVersion"
|
||||
]
|
||||
|
||||
class BfxWebsocketException(Exception):
|
||||
class BfxWebsocketException(BfxBaseException):
|
||||
"""
|
||||
Base class for all exceptions defined in bfxapi/websocket/exceptions.py.
|
||||
Base class for all custom exceptions in bfxapi/websocket/exceptions.py.
|
||||
"""
|
||||
|
||||
pass
|
||||
@@ -35,13 +39,6 @@ class WebsocketAuthenticationRequired(BfxWebsocketException):
|
||||
|
||||
pass
|
||||
|
||||
class InvalidAuthenticationCredentials(BfxWebsocketException):
|
||||
"""
|
||||
This error indicates that the user has provided incorrect credentials (API-KEY and API-SECRET) for authentication.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
class EventNotSupported(BfxWebsocketException):
|
||||
"""
|
||||
This error indicates a failed attempt to subscribe to an event not supported by the BfxWebsocketClient.
|
||||
@@ -54,4 +51,11 @@ class OutdatedClientVersion(BfxWebsocketException):
|
||||
This error indicates a mismatch between the client version and the server WSS version.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
class InvalidAuthenticationCredentials(BfxWebsocketException):
|
||||
"""
|
||||
This error indicates that the user has provided incorrect credentials (API-KEY and API-SECRET) for authentication.
|
||||
"""
|
||||
|
||||
pass
|
||||
@@ -1,25 +1,6 @@
|
||||
from typing import Generic, TypeVar, Iterable, List, Any
|
||||
|
||||
from . import typings
|
||||
|
||||
from .exceptions import BfxWebsocketException
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
class _Serializer(Generic[T]):
|
||||
def __init__(self, name: str, labels: List[str]):
|
||||
self.name, self.__labels = name, labels
|
||||
|
||||
def __serialize(self, *args: Any, IGNORE: List[str] = [ "_PLACEHOLDER" ]) -> Iterable[T]:
|
||||
if len(self.__labels) != len(args):
|
||||
raise BfxWebsocketException("<self.__labels> and <*args> arguments should contain the same amount of elements.")
|
||||
|
||||
for index, label in enumerate(self.__labels):
|
||||
if label not in IGNORE:
|
||||
yield label, args[index]
|
||||
|
||||
def parse(self, *values: Any) -> T:
|
||||
return dict(self.__serialize(*values))
|
||||
from .. labeler import _Serializer
|
||||
|
||||
#region Serializers definition for Websocket Public Channels
|
||||
|
||||
@@ -315,7 +296,7 @@ BalanceInfo = _Serializer[typings.BalanceInfo]("BalanceInfo", labels=[
|
||||
|
||||
#region Serializers definition for Notifications channel
|
||||
|
||||
Notification = _Serializer("Notification", labels=[
|
||||
Notification = _Serializer[typings.Notification]("Notification", labels=[
|
||||
"MTS",
|
||||
"TYPE",
|
||||
"MESSAGE_ID",
|
||||
|
||||
@@ -2,301 +2,294 @@ from decimal import Decimal
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from typing import Type, Tuple, List, Dict, TypedDict, Union, Optional, Any
|
||||
from typing import Type, NewType, Tuple, List, Dict, TypedDict, Union, Optional, Any
|
||||
|
||||
int16 = int32 = int45 = int64 = int
|
||||
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 subscription objects
|
||||
|
||||
class Subscriptions:
|
||||
TradingPairsTicker = TypedDict("Subscriptions.TradingPairsTicker", {
|
||||
"chanId": int,
|
||||
"symbol": str,
|
||||
"pair": str
|
||||
})
|
||||
class TradingPairsTicker(TypedDict):
|
||||
chanId: int
|
||||
symbol: str
|
||||
pair: str
|
||||
|
||||
FundingCurrenciesTicker = TypedDict("Subscriptions.FundingCurrenciesTicker", {
|
||||
"chanId": int,
|
||||
"symbol": str,
|
||||
"currency": str
|
||||
})
|
||||
class FundingCurrenciesTicker(TypedDict):
|
||||
chanId: int
|
||||
symbol: str
|
||||
currency: str
|
||||
|
||||
TradingPairsTrades = TypedDict("Subscriptions.TradingPairsTrades", {
|
||||
"chanId": int,
|
||||
"symbol": str,
|
||||
"pair": str
|
||||
})
|
||||
class TradingPairsTrades(TypedDict):
|
||||
chanId: int
|
||||
symbol: str
|
||||
pair: str
|
||||
|
||||
FundingCurrenciesTrades = TypedDict("Subscriptions.FundingCurrenciesTrades", {
|
||||
"chanId": int,
|
||||
"symbol": str,
|
||||
"currency": str
|
||||
})
|
||||
class FundingCurrenciesTrades(TypedDict):
|
||||
chanId: int
|
||||
symbol: str
|
||||
currency: str
|
||||
|
||||
Book = TypedDict("Subscriptions.Book", {
|
||||
"chanId": int,
|
||||
"symbol": str,
|
||||
"prec": str,
|
||||
"freq": str,
|
||||
"len": str,
|
||||
"subId": int,
|
||||
"pair": str
|
||||
})
|
||||
class Book(TypedDict):
|
||||
chanId: int
|
||||
symbol: str
|
||||
prec: str
|
||||
freq: str
|
||||
len: str
|
||||
subId: int
|
||||
pair: str
|
||||
|
||||
Candles = TypedDict("Subscriptions.Candles", {
|
||||
"chanId": int,
|
||||
"key": str
|
||||
})
|
||||
class Candles(TypedDict):
|
||||
chanId: int
|
||||
key: str
|
||||
|
||||
DerivativesStatus = TypedDict("Subscriptions.DerivativesStatus", {
|
||||
"chanId": int,
|
||||
"key": str
|
||||
})
|
||||
class DerivativesStatus(TypedDict):
|
||||
chanId: int
|
||||
key: str
|
||||
|
||||
#endregion
|
||||
|
||||
#region Type hinting for Websocket Public Channels
|
||||
|
||||
TradingPairTicker = TypedDict("TradingPairTicker", {
|
||||
"BID": float,
|
||||
"BID_SIZE": float,
|
||||
"ASK": float,
|
||||
"ASK_SIZE": float,
|
||||
"DAILY_CHANGE": float,
|
||||
"DAILY_CHANGE_RELATIVE": float,
|
||||
"LAST_PRICE": float,
|
||||
"VOLUME": float,
|
||||
"HIGH": float,
|
||||
"LOW": float
|
||||
})
|
||||
class TradingPairTicker(TypedDict):
|
||||
BID: float
|
||||
BID_SIZE: float
|
||||
ASK: float
|
||||
ASK_SIZE: float
|
||||
DAILY_CHANGE: float
|
||||
DAILY_CHANGE_RELATIVE: float
|
||||
LAST_PRICE: float
|
||||
VOLUME: float
|
||||
HIGH: float
|
||||
LOW: float
|
||||
|
||||
FundingCurrencyTicker = TypedDict("FundingCurrencyTicker", {
|
||||
"FRR": float,
|
||||
"BID": float,
|
||||
"BID_PERIOD": int,
|
||||
"BID_SIZE": float,
|
||||
"ASK": float,
|
||||
"ASK_PERIOD": int,
|
||||
"ASK_SIZE": float,
|
||||
"DAILY_CHANGE": float,
|
||||
"DAILY_CHANGE_RELATIVE": float,
|
||||
"LAST_PRICE": float,
|
||||
"VOLUME": float,
|
||||
"HIGH": float,
|
||||
"LOW": float,
|
||||
"FRR_AMOUNT_AVAILABLE": float
|
||||
})
|
||||
class FundingCurrencyTicker(TypedDict):
|
||||
FRR: float
|
||||
BID: float
|
||||
BID_PERIOD: int
|
||||
BID_SIZE: float
|
||||
ASK: float
|
||||
ASK_PERIOD: int
|
||||
ASK_SIZE: float
|
||||
DAILY_CHANGE: float
|
||||
DAILY_CHANGE_RELATIVE: float
|
||||
LAST_PRICE: float
|
||||
VOLUME: float
|
||||
HIGH: float
|
||||
LOW: float
|
||||
FRR_AMOUNT_AVAILABLE: float
|
||||
|
||||
(TradingPairTrade, FundingCurrencyTrade) = (
|
||||
TypedDict("TradingPairTrade", { "ID": int, "MTS": int, "AMOUNT": float, "PRICE": float }),
|
||||
TypedDict("FundingCurrencyTrade", { "ID": int, "MTS": int, "AMOUNT": float, "RATE": float, "PERIOD": int })
|
||||
)
|
||||
class TradingPairTrade(TypedDict):
|
||||
ID: int
|
||||
MTS: int
|
||||
AMOUNT: float
|
||||
PRICE: float
|
||||
|
||||
(TradingPairTrades, FundingCurrencyTrades) = (List[TradingPairTrade], List[FundingCurrencyTrade])
|
||||
class FundingCurrencyTrade(TypedDict):
|
||||
ID: int
|
||||
MTS: int
|
||||
AMOUNT: float
|
||||
RATE: float
|
||||
PERIOD: int
|
||||
|
||||
(TradingPairBook, FundingCurrencyBook) = (
|
||||
TypedDict("TradingPairBook", { "PRICE": float, "COUNT": int, "AMOUNT": float }),
|
||||
TypedDict("FundingCurrencyBook", { "RATE": float, "PERIOD": int, "COUNT": int, "AMOUNT": float })
|
||||
)
|
||||
class TradingPairBook(TypedDict):
|
||||
PRICE: float
|
||||
COUNT: int
|
||||
AMOUNT: float
|
||||
|
||||
class FundingCurrencyBook(TypedDict):
|
||||
RATE: float
|
||||
PERIOD: int
|
||||
COUNT: int
|
||||
AMOUNT: float
|
||||
|
||||
class TradingPairRawBook(TypedDict):
|
||||
ORDER_ID: int
|
||||
PRICE: float
|
||||
AMOUNT: float
|
||||
|
||||
class FundingCurrencyRawBook(TypedDict):
|
||||
OFFER_ID: int
|
||||
PERIOD: int
|
||||
RATE: float
|
||||
AMOUNT: float
|
||||
|
||||
(TradingPairBooks, FundingCurrencyBooks) = (List[TradingPairBook], List[FundingCurrencyBook])
|
||||
class Candle(TypedDict):
|
||||
MTS: int
|
||||
OPEN: float
|
||||
CLOSE: float
|
||||
HIGH: float
|
||||
LOW: float
|
||||
VOLUME: float
|
||||
|
||||
(TradingPairRawBook, FundingCurrencyRawBook) = (
|
||||
TypedDict("TradingPairRawBook", { "ORDER_ID": int, "PRICE": float, "AMOUNT": float }),
|
||||
TypedDict("FundingCurrencyRawBook", { "OFFER_ID": int, "PERIOD": int, "RATE": float, "AMOUNT": float }),
|
||||
)
|
||||
|
||||
(TradingPairRawBooks, FundingCurrencyRawBooks) = (List[TradingPairRawBook], List[FundingCurrencyRawBook])
|
||||
|
||||
Candle = TypedDict("Candle", {
|
||||
"MTS": int,
|
||||
"OPEN": float,
|
||||
"CLOSE": float,
|
||||
"HIGH": float,
|
||||
"LOW": float,
|
||||
"VOLUME": float
|
||||
})
|
||||
|
||||
Candles = List[Candle]
|
||||
|
||||
DerivativesStatus = TypedDict("DerivativesStatus", {
|
||||
"TIME_MS": int,
|
||||
"DERIV_PRICE": float,
|
||||
"SPOT_PRICE": float,
|
||||
"INSURANCE_FUND_BALANCE": float,
|
||||
"NEXT_FUNDING_EVT_TIMESTAMP_MS": int,
|
||||
"NEXT_FUNDING_ACCRUED": float,
|
||||
"NEXT_FUNDING_STEP": int,
|
||||
"CURRENT_FUNDING": float,
|
||||
"MARK_PRICE": float,
|
||||
"OPEN_INTEREST": float,
|
||||
"CLAMP_MIN": float,
|
||||
"CLAMP_MAX": float
|
||||
})
|
||||
class DerivativesStatus(TypedDict):
|
||||
TIME_MS: int
|
||||
DERIV_PRICE: float
|
||||
SPOT_PRICE: float
|
||||
INSURANCE_FUND_BALANCE: float
|
||||
NEXT_FUNDING_EVT_TIMESTAMP_MS: int
|
||||
NEXT_FUNDING_ACCRUED: float
|
||||
NEXT_FUNDING_STEP: int
|
||||
CURRENT_FUNDING: float
|
||||
MARK_PRICE: float
|
||||
OPEN_INTEREST: float
|
||||
CLAMP_MIN: float
|
||||
CLAMP_MAX: float
|
||||
|
||||
#endregion
|
||||
|
||||
#region Type hinting for Websocket Authenticated Channels
|
||||
|
||||
Order = TypedDict("Order", {
|
||||
"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
|
||||
})
|
||||
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
|
||||
|
||||
Orders = List[Order]
|
||||
class Position(TypedDict):
|
||||
SYMBOL: str
|
||||
STATUS: str
|
||||
AMOUNT: float
|
||||
BASE_PRICE: float
|
||||
MARGIN_FUNDING: float
|
||||
MARGIN_FUNDING_TYPE: int
|
||||
PL: float
|
||||
PL_PERC: float
|
||||
PRICE_LIQ: float
|
||||
LEVERAGE: float
|
||||
POSITION_ID: int
|
||||
MTS_CREATE: int
|
||||
MTS_UPDATE: int
|
||||
TYPE: int
|
||||
COLLATERAL: float
|
||||
COLLATERAL_MIN: float
|
||||
META: JSON
|
||||
|
||||
Position = TypedDict("Position", {
|
||||
"SYMBOL": str,
|
||||
"STATUS": str,
|
||||
"AMOUNT": float,
|
||||
"BASE_PRICE": float,
|
||||
"MARGIN_FUNDING": float,
|
||||
"MARGIN_FUNDING_TYPE": int,
|
||||
"PL": float,
|
||||
"PL_PERC": float,
|
||||
"PRICE_LIQ": float,
|
||||
"LEVERAGE": float,
|
||||
"POSITION_ID": int,
|
||||
"MTS_CREATE": int,
|
||||
"MTS_UPDATE": int,
|
||||
"TYPE": int,
|
||||
"COLLATERAL": float,
|
||||
"COLLATERAL_MIN": float,
|
||||
"META": JSON,
|
||||
})
|
||||
class TradeExecuted(TypedDict):
|
||||
ID: int
|
||||
SYMBOL: str
|
||||
MTS_CREATE: int
|
||||
ORDER_ID: int
|
||||
EXEC_AMOUNT: float
|
||||
EXEC_PRICE: float
|
||||
ORDER_TYPE: str
|
||||
ORDER_PRICE: float
|
||||
MAKER:int
|
||||
CID: int
|
||||
|
||||
Positions = List[Position]
|
||||
class TradeExecutionUpdate(TypedDict):
|
||||
ID: int
|
||||
SYMBOL: str
|
||||
MTS_CREATE: int
|
||||
ORDER_ID: int
|
||||
EXEC_AMOUNT: float
|
||||
EXEC_PRICE: float
|
||||
ORDER_TYPE: str
|
||||
ORDER_PRICE: float
|
||||
MAKER:int
|
||||
FEE: float
|
||||
FEE_CURRENCY: str
|
||||
CID: int
|
||||
|
||||
TradeExecuted = TypedDict("TradeExecuted", {
|
||||
"ID": int,
|
||||
"SYMBOL": str,
|
||||
"MTS_CREATE": int,
|
||||
"ORDER_ID": int,
|
||||
"EXEC_AMOUNT": float,
|
||||
"EXEC_PRICE": float,
|
||||
"ORDER_TYPE": str,
|
||||
"ORDER_PRICE": float,
|
||||
"MAKER":int,
|
||||
"CID": int
|
||||
})
|
||||
class FundingOffer(TypedDict):
|
||||
ID: int
|
||||
SYMBOL: str
|
||||
MTS_CREATED: int
|
||||
MTS_UPDATED: int
|
||||
AMOUNT: float
|
||||
AMOUNT_ORIG: float
|
||||
OFFER_TYPE: str
|
||||
FLAGS: int
|
||||
STATUS: str
|
||||
RATE: float
|
||||
PERIOD: int
|
||||
NOTIFY: int
|
||||
HIDDEN: int
|
||||
RENEW: int
|
||||
|
||||
TradeExecutionUpdate = TypedDict("TradeExecutionUpdate", {
|
||||
"ID": int,
|
||||
"SYMBOL": str,
|
||||
"MTS_CREATE": int,
|
||||
"ORDER_ID": int,
|
||||
"EXEC_AMOUNT": float,
|
||||
"EXEC_PRICE": float,
|
||||
"ORDER_TYPE": str,
|
||||
"ORDER_PRICE": float,
|
||||
"MAKER":int,
|
||||
"FEE": float,
|
||||
"FEE_CURRENCY": str,
|
||||
"CID": int
|
||||
})
|
||||
class FundingCredit(TypedDict):
|
||||
ID: int
|
||||
SYMBOL: str
|
||||
SIDE: int
|
||||
MTS_CREATE: int
|
||||
MTS_UPDATE: int
|
||||
AMOUNT: float
|
||||
FLAGS: int
|
||||
STATUS: str
|
||||
RATE: float
|
||||
PERIOD: int
|
||||
MTS_OPENING: int
|
||||
MTS_LAST_PAYOUT: int
|
||||
NOTIFY: int
|
||||
HIDDEN: int
|
||||
RENEW: int
|
||||
RATE_REAL: float
|
||||
NO_CLOSE: int
|
||||
POSITION_PAIR: str
|
||||
|
||||
FundingOffer = TypedDict("FundingOffer", {
|
||||
"ID": int,
|
||||
"SYMBOL": str,
|
||||
"MTS_CREATED": int,
|
||||
"MTS_UPDATED": int,
|
||||
"AMOUNT": float,
|
||||
"AMOUNT_ORIG": float,
|
||||
"OFFER_TYPE": str,
|
||||
"FLAGS": int,
|
||||
"STATUS": str,
|
||||
"RATE": float,
|
||||
"PERIOD": int,
|
||||
"NOTIFY": int,
|
||||
"HIDDEN": int,
|
||||
"RENEW": int,
|
||||
})
|
||||
class FundingLoan(TypedDict):
|
||||
ID: int
|
||||
SYMBOL: str
|
||||
SIDE: int
|
||||
MTS_CREATE: int
|
||||
MTS_UPDATE: int
|
||||
AMOUNT: float
|
||||
FLAGS: int
|
||||
STATUS: str
|
||||
RATE: float
|
||||
PERIOD: int
|
||||
MTS_OPENING: int
|
||||
MTS_LAST_PAYOUT: int
|
||||
NOTIFY: int
|
||||
HIDDEN: int
|
||||
RENEW: int
|
||||
RATE_REAL: float
|
||||
NO_CLOSE: int
|
||||
|
||||
FundingOffers = List[FundingOffer]
|
||||
class Wallet(TypedDict):
|
||||
WALLET_TYPE: str
|
||||
CURRENCY: str
|
||||
BALANCE: float
|
||||
UNSETTLED_INTEREST: float
|
||||
BALANCE_AVAILABLE: float
|
||||
DESCRIPTION: str
|
||||
META: JSON
|
||||
|
||||
FundingCredit = TypedDict("FundingCredit", {
|
||||
"ID": int,
|
||||
"SYMBOL": str,
|
||||
"SIDE": int,
|
||||
"MTS_CREATE": int,
|
||||
"MTS_UPDATE": int,
|
||||
"AMOUNT": float,
|
||||
"FLAGS": int,
|
||||
"STATUS": str,
|
||||
"RATE": float,
|
||||
"PERIOD": int,
|
||||
"MTS_OPENING": int,
|
||||
"MTS_LAST_PAYOUT": int,
|
||||
"NOTIFY": int,
|
||||
"HIDDEN": int,
|
||||
"RENEW": int,
|
||||
"RATE_REAL": float,
|
||||
"NO_CLOSE": int,
|
||||
"POSITION_PAIR": str
|
||||
})
|
||||
class BalanceInfo(TypedDict):
|
||||
AUM: float
|
||||
AUM_NET: float
|
||||
|
||||
FundingCredits = List[FundingCredit]
|
||||
#endregion
|
||||
|
||||
FundingLoan = TypedDict("FundingLoan", {
|
||||
"ID": int,
|
||||
"SYMBOL": str,
|
||||
"SIDE": int,
|
||||
"MTS_CREATE": int,
|
||||
"MTS_UPDATE": int,
|
||||
"AMOUNT": float,
|
||||
"FLAGS": int,
|
||||
"STATUS": str,
|
||||
"RATE": float,
|
||||
"PERIOD": int,
|
||||
"MTS_OPENING": int,
|
||||
"MTS_LAST_PAYOUT": int,
|
||||
"NOTIFY": int,
|
||||
"HIDDEN": int,
|
||||
"RENEW": int,
|
||||
"RATE_REAL": float,
|
||||
"NO_CLOSE": int
|
||||
})
|
||||
#region Serializers definition for Notifications channel
|
||||
|
||||
FundingLoans = List[FundingLoan]
|
||||
|
||||
Wallet = TypedDict("Wallet", {
|
||||
"WALLET_TYPE": str,
|
||||
"CURRENCY": str,
|
||||
"BALANCE": float,
|
||||
"UNSETTLED_INTEREST": float,
|
||||
"BALANCE_AVAILABLE": float,
|
||||
"DESCRIPTION": str,
|
||||
"META": JSON
|
||||
})
|
||||
|
||||
Wallets = List[Wallet]
|
||||
|
||||
BalanceInfo = TypedDict("BalanceInfo", {
|
||||
"AUM": float,
|
||||
"AUM_NET": float
|
||||
})
|
||||
class Notification(TypedDict):
|
||||
MTS: int
|
||||
TYPE: str
|
||||
MESSAGE_ID: int
|
||||
NOTIFY_INFO: JSON
|
||||
CODE: int
|
||||
STATUS: str
|
||||
TEXT: str
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -304,55 +297,50 @@ BalanceInfo = TypedDict("BalanceInfo", {
|
||||
|
||||
class Inputs:
|
||||
class Order:
|
||||
New = TypedDict("Inputs.Order.New", {
|
||||
"gid": Optional[int32],
|
||||
"cid": int45,
|
||||
"type": str,
|
||||
"symbol": str,
|
||||
"amount": Union[Decimal, str],
|
||||
"price": Union[Decimal, str],
|
||||
"lev": int,
|
||||
"price_trailing": Union[Decimal, str],
|
||||
"price_aux_limit": Union[Decimal, str],
|
||||
"price_oco_stop": Union[Decimal, str],
|
||||
"flags": int16,
|
||||
"tif": Union[datetime, str],
|
||||
"meta": JSON
|
||||
})
|
||||
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
|
||||
|
||||
Update = TypedDict("Inputs.Order.Update", {
|
||||
"id": int64,
|
||||
"cid": int45,
|
||||
"cid_date": str,
|
||||
"gid": int32,
|
||||
"price": Union[Decimal, str],
|
||||
"amount": Union[Decimal, str],
|
||||
"lev": int,
|
||||
"delta": Union[Decimal, str],
|
||||
"price_aux_limit": Union[Decimal, str],
|
||||
"price_trailing": Union[Decimal, str],
|
||||
"flags": int16,
|
||||
"tif": Union[datetime, str]
|
||||
})
|
||||
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]
|
||||
|
||||
Cancel = TypedDict("Inputs.Order.Cancel", {
|
||||
"id": int64,
|
||||
"cid": int45,
|
||||
"cid_date": str
|
||||
})
|
||||
class Cancel(TypedDict, total=False):
|
||||
id: Union[Int64, int]
|
||||
cid: Union[Int45, int]
|
||||
cid_date: Union[datetime, str]
|
||||
|
||||
class Offer:
|
||||
New = TypedDict("Inputs.Offer.New", {
|
||||
"type": str,
|
||||
"symbol": str,
|
||||
"amount": Union[Decimal, str],
|
||||
"rate": Union[Decimal, str],
|
||||
"period": int,
|
||||
"flags": int16
|
||||
})
|
||||
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]
|
||||
|
||||
Cancel = TypedDict("Inputs.Offer.Cancel", {
|
||||
"id": int
|
||||
})
|
||||
class Cancel(TypedDict, total=False):
|
||||
id: Union[Int32, int]
|
||||
|
||||
#endregion
|
||||
@@ -1,13 +1,15 @@
|
||||
from collections import OrderedDict
|
||||
|
||||
from typing import List
|
||||
|
||||
from bfxapi import Client, Constants
|
||||
|
||||
from bfxapi.websocket import BfxWebsocketClient
|
||||
from bfxapi.websocket.enums import Channels, Errors
|
||||
from bfxapi.websocket.typings import Subscriptions, TradingPairBooks, TradingPairBook
|
||||
from bfxapi.websocket.typings import Subscriptions, TradingPairBook
|
||||
|
||||
class OrderBook(object):
|
||||
def __init__(self, symbols: list[str]):
|
||||
def __init__(self, symbols: List[str]):
|
||||
self.__order_book = {
|
||||
symbol: {
|
||||
"bids": OrderedDict(), "asks": OrderedDict()
|
||||
@@ -50,7 +52,7 @@ def on_subscribed(subscription):
|
||||
print(f"Subscription successful for pair <{subscription['pair']}>")
|
||||
|
||||
@bfx.wss.on("t_book_snapshot")
|
||||
def on_t_book_snapshot(subscription: Subscriptions.Book, snapshot: TradingPairBooks):
|
||||
def on_t_book_snapshot(subscription: Subscriptions.Book, snapshot: List[TradingPairBook]):
|
||||
for data in snapshot:
|
||||
order_book.update(subscription["symbol"], data)
|
||||
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
from collections import OrderedDict
|
||||
|
||||
from typing import List
|
||||
|
||||
from bfxapi import Client, Constants
|
||||
|
||||
from bfxapi.websocket import BfxWebsocketClient
|
||||
from bfxapi.websocket.enums import Channels, Errors
|
||||
from bfxapi.websocket.typings import Subscriptions, TradingPairRawBooks, TradingPairRawBook
|
||||
from bfxapi.websocket.typings import Subscriptions, TradingPairRawBook
|
||||
|
||||
class RawOrderBook(object):
|
||||
def __init__(self, symbols: list[str]):
|
||||
def __init__(self, symbols: List[str]):
|
||||
self.__raw_order_book = {
|
||||
symbol: {
|
||||
"bids": OrderedDict(), "asks": OrderedDict()
|
||||
@@ -50,7 +52,7 @@ def on_subscribed(subscription):
|
||||
print(f"Subscription successful for pair <{subscription['pair']}>")
|
||||
|
||||
@bfx.wss.on("t_raw_book_snapshot")
|
||||
def on_t_raw_book_snapshot(subscription: Subscriptions.Book, snapshot: TradingPairRawBooks):
|
||||
def on_t_raw_book_snapshot(subscription: Subscriptions.Book, snapshot: List[TradingPairRawBook]):
|
||||
for data in snapshot:
|
||||
raw_order_book.update(subscription["symbol"], data)
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import asyncio
|
||||
|
||||
from bfxapi import Client, Constants
|
||||
from bfxapi.websocket import Channels
|
||||
from bfxapi.websocket.enums import Channels
|
||||
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):
|
||||
print(f"Subscription channel ID: {subscription['chanId']}")
|
||||
print(f"Subscription with channel ID: {subscription['chanId']}")
|
||||
|
||||
print(f"Data: {data}")
|
||||
|
||||
|
||||
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
7
setup.py
7
setup.py
@@ -11,11 +11,16 @@ setup(
|
||||
description="Official Bitfinex Python API",
|
||||
keywords="bitfinex,api,trading",
|
||||
install_requires=[
|
||||
"certifi~=2022.9.24",
|
||||
"certifi~=2022.12.7",
|
||||
"charset-normalizer~=2.1.1",
|
||||
"idna~=3.4",
|
||||
"mypy~=0.991",
|
||||
"mypy-extensions~=0.4.3",
|
||||
"pyee~=9.0.4",
|
||||
"requests~=2.28.1",
|
||||
"tomli~=2.0.1",
|
||||
"types-requests~=2.28.11.5",
|
||||
"types-urllib3~=1.26.25.4",
|
||||
"typing_extensions~=4.4.0",
|
||||
"urllib3~=1.26.13",
|
||||
"websockets~=10.4",
|
||||
|
||||
Reference in New Issue
Block a user