From 8660fac2bb3c59f62f20fcf2b304ed8e3477fcbb Mon Sep 17 00:00:00 2001 From: fintzd <35842457+fintzd@users.noreply.github.com> Date: Mon, 24 May 2021 13:28:01 +0200 Subject: [PATCH 01/68] fix: delete get_seed_trades.py (#134) * Delete get_seed_trades.py The request here is made with REST not WS, so it is not the correct place for this example. * Moved the file to where it belongs --- bfxapi/examples/{ws => rest}/get_seed_trades.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename bfxapi/examples/{ws => rest}/get_seed_trades.py (100%) diff --git a/bfxapi/examples/ws/get_seed_trades.py b/bfxapi/examples/rest/get_seed_trades.py similarity index 100% rename from bfxapi/examples/ws/get_seed_trades.py rename to bfxapi/examples/rest/get_seed_trades.py From e7218aef921975ea2611608ba838f609ed168d6b Mon Sep 17 00:00:00 2001 From: Richard Hoekstra Date: Mon, 24 May 2021 13:32:50 +0200 Subject: [PATCH 02/68] feat: implement funding trades endpoint (rest) (#135) * feat: support funding_trades endpoint * chore: bump version, add changelog entry * chore: add final newline to funding_trade.py * fix: 'maker' is now placeholder in fundingtrade Co-authored-by: Richard Hoekstra Co-authored-by: Robert Kowalski --- CHANGELOG | 3 ++ bfxapi/models/__init__.py | 3 +- bfxapi/models/funding_trade.py | 55 ++++++++++++++++++++++++++++++++++ bfxapi/rest/bfx_rest.py | 18 ++++++++++- bfxapi/version.py | 2 +- 5 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 bfxapi/models/funding_trade.py diff --git a/CHANGELOG b/CHANGELOG index a4db05e..18e171f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +1.1.15 +-) Implemented Funding Trades (rest) + 1.1.14 -) bfx_websockets.py ERRORS dictionary now contains a message for error number 10305 diff --git a/bfxapi/models/__init__.py b/bfxapi/models/__init__.py index 87b07ea..76b5ce9 100644 --- a/bfxapi/models/__init__.py +++ b/bfxapi/models/__init__.py @@ -19,5 +19,6 @@ from .withdraw import Withdraw from .ticker import Ticker from .funding_ticker import FundingTicker from .ledger import Ledger +from .funding_trade import FundingTrade -NAME = 'models' +NAME = "models" diff --git a/bfxapi/models/funding_trade.py b/bfxapi/models/funding_trade.py new file mode 100644 index 0000000..07f6ea5 --- /dev/null +++ b/bfxapi/models/funding_trade.py @@ -0,0 +1,55 @@ +""" +Module used to describe all of the different data types +""" + +class FundingTradeModel: + """ + Enum used to index the different values in a raw funding trade array + """ + ID = 0 + SYMBOL = 1 + MTS_CREATE = 2 + OFFER_ID = 3 + AMOUNT = 4 + RATE = 5 + PERIOD = 6 + +class FundingTrade: + """ + ID integer Offer ID + SYMBOL string The currency of the offer (fUSD, etc) + MTS_CREATE int Millisecond Time Stamp when the offer was created + OFFER_ID int The ID of the offer + AMOUNT float Amount the offer is for + RATE float Rate of the offer + PERIOD int Period of the offer + """ + + def __init__(self, tid, symbol, mts_create, offer_id, amount, rate, period): + self.tid = tid + self.symbol = symbol + self.mts_create = mts_create + self.offer_id = offer_id + self.amount = amount + self.rate = rate + self.period = period + + @staticmethod + def from_raw_rest_trade(raw_trade): + """ + Generate a Ticker object from a raw ticker array + """ + # [[636040,"fUST",1574077528000,41237922,-100,0.0024,2,null]] + return FundingTrade( + raw_trade[FundingTradeModel.ID], + raw_trade[FundingTradeModel.SYMBOL], + raw_trade[FundingTradeModel.MTS_CREATE], + raw_trade[FundingTradeModel.OFFER_ID], + raw_trade[FundingTradeModel.AMOUNT], + raw_trade[FundingTradeModel.RATE], + raw_trade[FundingTradeModel.PERIOD] + ) + + def __str__(self): + return "FundingTrade '{}' x {} @ {} for {} days".format( + self.symbol, self.amount, self.rate, self.period) diff --git a/bfxapi/rest/bfx_rest.py b/bfxapi/rest/bfx_rest.py index e8d23f7..a4d9174 100644 --- a/bfxapi/rest/bfx_rest.py +++ b/bfxapi/rest/bfx_rest.py @@ -10,7 +10,7 @@ import datetime from ..utils.custom_logger import CustomLogger from ..utils.auth import generate_auth_headers, calculate_order_flags, gen_unique_cid -from ..models import Wallet, Order, Position, Trade, FundingLoan, FundingOffer +from ..models import Wallet, Order, Position, Trade, FundingLoan, FundingOffer, FundingTrade from ..models import FundingCredit, Notification, Ledger @@ -456,6 +456,22 @@ class BfxRest: raw_trades = await self.post(endpoint, params=params) return [Trade.from_raw_rest_trade(rt) for rt in raw_trades] + async def get_funding_trades(self, symbol, start, end, limit=25): + """ + Get all of the funding trades between the start and end period associated with API_KEY + - Requires authentication. + + # Attributes + @param symbol string: pair symbol i.e fUSD + @param start int: millisecond start time + @param end int: millisecond end time + @param limit int: max number of items in response + @return Array + """ + endpoint = "auth/r/funding/trades/{}/hist".format(symbol) + raw_trades = await self.post(endpoint) + return [FundingTrade.from_raw_rest_trade(rt) for rt in raw_trades] + async def get_funding_offers(self, symbol): """ Get all of the funding offers associated with API_KEY - Requires authentication. diff --git a/bfxapi/version.py b/bfxapi/version.py index 33ea9de..acd9c7c 100644 --- a/bfxapi/version.py +++ b/bfxapi/version.py @@ -2,4 +2,4 @@ This module contains the current version of the bfxapi lib """ -__version__ = '1.1.14' +__version__ = '1.1.15' From 42b15e690e027147345972967cbf2778f0ada291 Mon Sep 17 00:00:00 2001 From: Dario Moceri <31732142+itsdeka@users.noreply.github.com> Date: Mon, 24 May 2021 13:33:32 +0200 Subject: [PATCH 03/68] fix docs (#139) --- docs/ws_v2.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/ws_v2.md b/docs/ws_v2.md index d173659..783e74f 100644 --- a/docs/ws_v2.md +++ b/docs/ws_v2.md @@ -58,6 +58,7 @@ https://github.com/Crypto-toolbox/btfxwss - `notification` (Notification): incoming account notification - `error` (array): error from the websocket - `order_closed` (Order, Trade): when an order has been closed + - `order_update` (Order, Trade): when an order has been updated - `order_new` (Order, Trade): when an order has been created but not closed. Note: will not be called if order is executed and filled instantly - `order_confirmed` (Order, Trade): When an order has been submitted and received - `wallet_snapshot` (array[Wallet]): Initial wallet balances (Fired once) From a58c772e4b565115d38515980bf621f5be53a798 Mon Sep 17 00:00:00 2001 From: Dario Moceri <31732142+itsdeka@users.noreply.github.com> Date: Mon, 24 May 2021 14:30:36 +0200 Subject: [PATCH 04/68] added ws example (#137) * This is an example of how is it possible to spawn multiple bfx ws instances to comply to the open subscriptions number contraint (max. 25) * fix * typo * bump version * made the list smaller * added newline Co-authored-by: Robert Kowalski --- CHANGELOG | 1 + bfxapi/examples/ws/multiple_instances.py | 181 +++++++++++++++++++++++ 2 files changed, 182 insertions(+) create mode 100644 bfxapi/examples/ws/multiple_instances.py diff --git a/CHANGELOG b/CHANGELOG index 18e171f..c0bded5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,5 @@ 1.1.15 +-) Added an example to show how it is possible to spawn multiple bfx ws instances to comply with the open subscriptions number constraint (max. 25) -) Implemented Funding Trades (rest) 1.1.14 diff --git a/bfxapi/examples/ws/multiple_instances.py b/bfxapi/examples/ws/multiple_instances.py new file mode 100644 index 0000000..ede3071 --- /dev/null +++ b/bfxapi/examples/ws/multiple_instances.py @@ -0,0 +1,181 @@ +""" +This is an example of how it is possible to spawn multiple +bfx ws instances to comply with the open subscriptions number constraint (max. 25) + +(https://docs.bitfinex.com/docs/requirements-and-limitations) +""" + +import sys +sys.path.append('../../../') +import asyncio +import json +from datetime import datetime +from functools import partial +import websockets as ws +from bfxapi import Client +import math +import random + +MAX_CHANNELS = 25 + + +def get_random_list_of_tickers(): + tickers = ["FILUST", "FTTUSD", "FTTUST", "FUNUSD", "GNOUSD", "GNTUSD", "GOTEUR", "GOTUSD", "GTXUSD", "ZRXUSD"] + return random.sample(tickers, 1) + + +class Instance: + def __init__(self, _id): + self.id = _id + self.bfx = Client(logLevel='INFO') + self.subscriptions = {'trades': {}, 'ticker': {}} + self.is_ready = False + + def run(self): + self.bfx.ws.run() + self.bfx.ws.on('error', log_error) + self.bfx.ws.on('new_trade', log_trade) + self.bfx.ws.on('new_ticker', log_ticker) + self.bfx.ws.on('subscribed', partial(on_subscribe, self)) + self.bfx.ws.on('unsubscribed', partial(on_unsubscribed, self)) + self.bfx.ws.on('connected', partial(on_connected, self)) + self.bfx.ws.on('stopped', partial(on_stopped, self)) + + async def subscribe(self, symbols): + for symbol in symbols: + print(f'Subscribing to {symbol} channel') + await self.bfx.ws.subscribe_ticker(symbol) + await self.bfx.ws.subscribe_trades(symbol) + self.subscriptions['trades'][symbol] = None + self.subscriptions['ticker'][symbol] = None + + async def unsubscribe(self, symbols): + for symbol in symbols: + if symbol in self.subscriptions['trades']: + print(f'Unsubscribing to {symbol} channel') + trades_ch_id = self.subscriptions['trades'][symbol] + ticker_ch_id = self.subscriptions['ticker'][symbol] + if trades_ch_id: + await self.bfx.ws.unsubscribe(trades_ch_id) + else: + del self.subscriptions['trades'][symbol] + if ticker_ch_id: + await self.bfx.ws.unsubscribe(ticker_ch_id) + else: + del self.subscriptions['ticker'][symbol] + + +class Routine: + is_stopped = False + + def __new__(cls, _loop, _ws, interval=1, start_delay=10): + instance = super().__new__(cls) + instance.interval = interval + instance.start_delay = start_delay + instance.ws = _ws + instance.task = _loop.create_task(instance.run()) + return instance.task + + async def run(self): + await asyncio.sleep(self.start_delay) + await self.do() + while True: + await asyncio.sleep(self.interval) + await self.do() + + async def do(self): + subbed_tickers = get_all_subscriptions_tickers() + print(f'Subscribed tickers: {subbed_tickers}') + + # if ticker is not in subbed tickers, then we subscribe to the channel + to_sub = [f"t{ticker}" for ticker in get_random_list_of_tickers() if ticker not in subbed_tickers] + for ticker in to_sub: + print(f'To subscribe: {ticker}') + instance = get_available_instance() + if instance and instance.is_ready: + print(f'Subscribing on instance {instance.id}') + await instance.subscribe([ticker]) + else: + instances_to_create = math.ceil(len(to_sub) / MAX_CHANNELS) + create_instances(instances_to_create) + break + + to_unsub = [f"t{ticker}" for ticker in subbed_tickers if ticker in get_random_list_of_tickers()] + if len(to_unsub) > 0: + print(f'To unsubscribe: {to_unsub}') + for instance in instances: + await instance.unsubscribe(to_unsub) + + def stop(self): + self.task.cancel() + self.is_stopped = True + + +instances = [] + + +def get_all_subscriptions_tickers(): + tickers = [] + for instance in instances: + for ticker in instance.subscriptions['trades']: + tickers.append(ticker) + return tickers + + +def count_open_channels(instance): + return len(instance.subscriptions['trades']) + len(instance.subscriptions['ticker']) + + +def create_instances(instances_to_create): + for _ in range(0, instances_to_create): + instance = Instance(len(instances)) + instance.run() + instances.append(instance) + + +def get_available_instance(): + for instance in instances: + if count_open_channels(instance) + 1 <= MAX_CHANNELS: + return instance + return None + + +def log_error(err): + print("Error: {}".format(err)) + + +def log_trade(trade): + print(trade) + + +def log_ticker(ticker): + print(ticker) + + +async def on_subscribe(instance, subscription): + print(f'Subscribed to {subscription.symbol} channel {subscription.channel_name}') + instance.subscriptions[subscription.channel_name][subscription.symbol] = subscription.chan_id + + +async def on_unsubscribed(instance, subscription): + print(f'Unsubscribed to {subscription.symbol} channel {subscription.channel_name}') + instance.subscriptions[subscription.channel_name][subscription.symbol] = subscription.chan_id + del instance.subscriptions[subscription.channel_name][subscription.symbol] + + +async def on_connected(instance): + print(f"Instance {instance.id} is connected") + instance.is_ready = True + + +async def on_stopped(instance): + print(f"Instance {instance.id} is dead, removing it from instances list") + instances.pop(instance.id) + + +def run(): + loop = asyncio.get_event_loop() + task = Routine(loop, ws, interval=5) + loop.run_until_complete(task) + +run() From 4a8d3e48b0e8547a3b6bdda6f1db6435f8304242 Mon Sep 17 00:00:00 2001 From: Dario Moceri <31732142+itsdeka@users.noreply.github.com> Date: Tue, 25 May 2021 11:33:08 +0200 Subject: [PATCH 05/68] Added 'ids' parameter to get_order_history() (#121) * Added 'ids' parameter to get_order_history() * fix Co-authored-by: Robert Kowalski --- CHANGELOG | 1 + bfxapi/rest/bfx_rest.py | 5 ++++- setup.py | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index c0bded5..54f19e5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,5 @@ 1.1.15 +-) Added 'ids' parameter to get_order_history() -) Added an example to show how it is possible to spawn multiple bfx ws instances to comply with the open subscriptions number constraint (max. 25) -) Implemented Funding Trades (rest) diff --git a/bfxapi/rest/bfx_rest.py b/bfxapi/rest/bfx_rest.py index a4d9174..9a97057 100644 --- a/bfxapi/rest/bfx_rest.py +++ b/bfxapi/rest/bfx_rest.py @@ -397,7 +397,7 @@ class BfxRest: raw_orders = await self.post(endpoint) return [Order.from_raw_order(ro) for ro in raw_orders] - async def get_order_history(self, symbol, start, end, limit=25, sort=-1): + async def get_order_history(self, symbol, start, end, limit=25, sort=-1, ids=None): """ Get all of the orders between the start and end period associated with API_KEY - Requires authentication. @@ -407,11 +407,14 @@ class BfxRest: @param start int: millisecond start time @param end int: millisecond end time @param limit int: max number of items in response + @param ids list of int: allows you to retrieve specific orders by order ID (ids: [ID1, ID2, ID3]) @return Array """ endpoint = "auth/r/orders/{}/hist".format(symbol) params = "?start={}&end={}&limit={}&sort={}".format( start, end, limit, sort) + if ids: + params += "&id=" + ",".join(str(id) for id in ids) raw_orders = await self.post(endpoint, params=params) return [Order.from_raw_order(ro) for ro in raw_orders] diff --git a/setup.py b/setup.py index 7de3efd..c638f7a 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from os import path here = path.abspath(path.dirname(__file__)) setup( name='bitfinex-api-py', - version='1.1.14', + version='1.1.15', description='Official Bitfinex Python API', long_description='A Python reference implementation of the Bitfinex API for both REST and websocket interaction', long_description_content_type='text/markdown', From 98825aae106e3bddc0842be92964d7fbb1704eee Mon Sep 17 00:00:00 2001 From: itsdeka Date: Thu, 10 Jun 2021 18:17:13 +0200 Subject: [PATCH 06/68] Added Margin Info endpoint --- CHANGELOG | 3 ++ .../examples/rest/get_authenticated_data.py | 9 ++++ bfxapi/models/__init__.py | 2 + bfxapi/models/margin_info.py | 46 ++++++++++++++++++ bfxapi/models/margin_info_base.py | 47 +++++++++++++++++++ bfxapi/rest/bfx_rest.py | 18 ++++++- bfxapi/version.py | 2 +- 7 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 bfxapi/models/margin_info.py create mode 100644 bfxapi/models/margin_info_base.py diff --git a/CHANGELOG b/CHANGELOG index 54f19e5..64f29fd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +1.1.16 +-) Implemented Margin Info (rest) + 1.1.15 -) Added 'ids' parameter to get_order_history() -) Added an example to show how it is possible to spawn multiple bfx ws instances to comply with the open subscriptions number constraint (max. 25) diff --git a/bfxapi/examples/rest/get_authenticated_data.py b/bfxapi/examples/rest/get_authenticated_data.py index 052b22d..f065bfa 100644 --- a/bfxapi/examples/rest/get_authenticated_data.py +++ b/bfxapi/examples/rest/get_authenticated_data.py @@ -79,6 +79,14 @@ async def log_funding_credits_history(): print ("Funding credit history:") [ print (c) for c in credit ] +async def log_margin_info(): + m1 = await bfx.rest.get_margin_info('tBTCUSD') + print (m1) + m2 = await bfx.rest.get_margin_info('sym_all') + print (m2) + m3 = await bfx.rest.get_margin_info('base') + print (m3) + async def run(): await log_wallets() await log_active_orders() @@ -90,6 +98,7 @@ async def run(): await log_funding_offer_history() await log_funding_credits() await log_funding_credits_history() + await log_margin_info() t = asyncio.ensure_future(run()) diff --git a/bfxapi/models/__init__.py b/bfxapi/models/__init__.py index 76b5ce9..b9b4b17 100644 --- a/bfxapi/models/__init__.py +++ b/bfxapi/models/__init__.py @@ -20,5 +20,7 @@ from .ticker import Ticker from .funding_ticker import FundingTicker from .ledger import Ledger from .funding_trade import FundingTrade +from .margin_info import MarginInfo +from .margin_info_base import MarginInfoBase NAME = "models" diff --git a/bfxapi/models/margin_info.py b/bfxapi/models/margin_info.py new file mode 100644 index 0000000..514be41 --- /dev/null +++ b/bfxapi/models/margin_info.py @@ -0,0 +1,46 @@ +""" +Module used to describe all of the different data types +""" + +import datetime + +class MarginInfoModel: + """ + Enum used to index the different values in a raw margin info array + """ + TRADABLE_BALANCE = 0 + GROSS_BALANCE = 1 + BUY = 2 + SELL = 3 + +class MarginInfo: + """ + SYMBOL string + TRADABLE BALANCE float + GROSS_BALANCE float + BUY + SELL + """ + + def __init__(self, symbol, tradable_balance, gross_balance, buy, sell): + # pylint: disable=invalid-name + self.symbol = symbol + self.tradable_balance = tradable_balance + self.gross_balance = gross_balance + self.buy = buy + self.sell = sell + + @staticmethod + def from_raw_margin_info(raw_margin_info): + """ + Generate a MarginInfo object from a raw margin info array + """ + symbol = raw_margin_info[1] + tradable_balance = raw_margin_info[2][MarginInfoModel.TRADABLE_BALANCE] + gross_balance = raw_margin_info[2][MarginInfoModel.GROSS_BALANCE] + buy = raw_margin_info[2][MarginInfoModel.BUY] + sell = raw_margin_info[2][MarginInfoModel.SELL] + return MarginInfo(symbol, tradable_balance, gross_balance, buy, sell) + + def __str__(self): + return "Margin Info {}".format(self.symbol) diff --git a/bfxapi/models/margin_info_base.py b/bfxapi/models/margin_info_base.py new file mode 100644 index 0000000..200bc52 --- /dev/null +++ b/bfxapi/models/margin_info_base.py @@ -0,0 +1,47 @@ +""" +Module used to describe all of the different data types +""" + +import datetime + +class MarginInfoBaseModel: + """ + Enum used to index the different values in a raw margin info array + """ + USER_PL = 0 + USER_SWAPS = 1 + MARGIN_BALANCE = 2 + MARGIN_NET = 3 + MARGIN_MIN = 4 + +class MarginInfoBase: + """ + USER_PL float + USER_SWAPS float + MARGIN_BALANCE float + MARGIN_NET float + MARGIN_MIN float + """ + + def __init__(self, user_pl, user_swaps, margin_balance, margin_net, margin_min): + # pylint: disable=invalid-name + self.user_pl = user_pl + self.user_swaps = user_swaps + self.margin_balance = margin_balance + self.margin_net = margin_net + self.margin_min = margin_min + + @staticmethod + def from_raw_margin_info(raw_margin_info): + """ + Generate a MarginInfoBase object from a raw margin info array + """ + user_pl = raw_margin_info[1][MarginInfoBaseModel.USER_PL] + user_swaps = raw_margin_info[1][MarginInfoBaseModel.USER_SWAPS] + margin_balance = raw_margin_info[1][MarginInfoBaseModel.MARGIN_BALANCE] + margin_net = raw_margin_info[1][MarginInfoBaseModel.MARGIN_NET] + margin_min = raw_margin_info[1][MarginInfoBaseModel.MARGIN_MIN] + return MarginInfoBase(user_pl, user_swaps, margin_balance, margin_net, margin_min) + + def __str__(self): + return "Margin Info Base" diff --git a/bfxapi/rest/bfx_rest.py b/bfxapi/rest/bfx_rest.py index 9a97057..ba7d9b9 100644 --- a/bfxapi/rest/bfx_rest.py +++ b/bfxapi/rest/bfx_rest.py @@ -10,7 +10,7 @@ import datetime from ..utils.custom_logger import CustomLogger from ..utils.auth import generate_auth_headers, calculate_order_flags, gen_unique_cid -from ..models import Wallet, Order, Position, Trade, FundingLoan, FundingOffer, FundingTrade +from ..models import Wallet, Order, Position, Trade, FundingLoan, FundingOffer, FundingTrade, MarginInfoBase, MarginInfo from ..models import FundingCredit, Notification, Ledger @@ -385,6 +385,22 @@ class BfxRest: raw_wallets = await self.post(endpoint) return [Wallet(*rw[:5]) for rw in raw_wallets] + async def get_margin_info(self, symbol='base'): + """ + Get account margin information (like P/L, Swaps, Margin Balance, Tradable Balance and others). + Use different keys (base, SYMBOL, sym_all) to retrieve different kinds of data. + + @return Array + """ + endpoint = f"auth/r/info/margin/{symbol}" + raw_margin_info = await self.post(endpoint) + if symbol == 'base': + return MarginInfoBase.from_raw_margin_info(raw_margin_info) + elif symbol == 'sym_all': + return [MarginInfo.from_raw_margin_info(record) for record in raw_margin_info] + else: + return MarginInfo.from_raw_margin_info(raw_margin_info) + async def get_active_orders(self, symbol): """ Get all of the active orders associated with API_KEY - Requires authentication. diff --git a/bfxapi/version.py b/bfxapi/version.py index acd9c7c..3bf093c 100644 --- a/bfxapi/version.py +++ b/bfxapi/version.py @@ -2,4 +2,4 @@ This module contains the current version of the bfxapi lib """ -__version__ = '1.1.15' +__version__ = '1.1.16' From a28b26323875b321bdb5614a601a4c64c73605f1 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Mon, 21 Jun 2021 21:45:50 +0200 Subject: [PATCH 07/68] added claim_position to bfx_rest.py --- bfxapi/rest/bfx_rest.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/bfxapi/rest/bfx_rest.py b/bfxapi/rest/bfx_rest.py index ba7d9b9..fba9b60 100644 --- a/bfxapi/rest/bfx_rest.py +++ b/bfxapi/rest/bfx_rest.py @@ -940,6 +940,28 @@ class BfxRest: raw_notification = await self.post(endpoint, payload) return Notification.from_raw_notification(raw_notification) + async def claim_position(self, position_id, amount): + """ + The claim feature allows the use of funds you have in your Margin Wallet + to settle a leveraged position as an exchange buy or sale + + # Attributes + @param position_id: id of the position + @param amount: amount to claim + @return Array [ MTS, TYPE, MESSAGE_ID, null, [SYMBOL, POSITION_STATUS, + AMOUNT, BASE_PRICE, MARGIN_FUNDING, MARGIN_FUNDING_TYPE, PLACEHOLDER, + PLACEHOLDER, PLACEHOLDER, PLACEHOLDER, PLACEHOLDER, POSITION_ID, MTS_CREATE, + MTS_UPDATE, PLACEHOLDER, POS_TYPE, PLACEHOLDER, COLLATERAL, MIN_COLLATERAL, + META], CODE, STATUS, TEXT] + """ + payload = { + "id": position_id, + "amount": f"{amount * -1}" + } + endpoint = "auth/w/position/claim" + message = await self.post(endpoint, payload) + return message + async def get_auth_pulse_hist(self, is_public=None): """ Allows you to retrieve your private pulse history or the public pulse history with an additional UID_LIKED field. From 062b13a38275384ac6e4c77293bb26cfcf9154b2 Mon Sep 17 00:00:00 2001 From: Dario Moceri <31732142+itsdeka@users.noreply.github.com> Date: Fri, 25 Jun 2021 11:37:56 +0200 Subject: [PATCH 08/68] fix example for ws (#151) --- bfxapi/examples/ws/multiple_instances.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bfxapi/examples/ws/multiple_instances.py b/bfxapi/examples/ws/multiple_instances.py index ede3071..89fa677 100644 --- a/bfxapi/examples/ws/multiple_instances.py +++ b/bfxapi/examples/ws/multiple_instances.py @@ -88,7 +88,7 @@ class Routine: print(f'Subscribed tickers: {subbed_tickers}') # if ticker is not in subbed tickers, then we subscribe to the channel - to_sub = [f"t{ticker}" for ticker in get_random_list_of_tickers() if ticker not in subbed_tickers] + to_sub = [f"t{ticker}" for ticker in get_random_list_of_tickers() if f"t{ticker}" not in subbed_tickers] for ticker in to_sub: print(f'To subscribe: {ticker}') instance = get_available_instance() @@ -100,7 +100,7 @@ class Routine: create_instances(instances_to_create) break - to_unsub = [f"t{ticker}" for ticker in subbed_tickers if ticker in get_random_list_of_tickers()] + to_unsub = [f"t{ticker}" for ticker in subbed_tickers if f"t{ticker}" in get_random_list_of_tickers()] if len(to_unsub) > 0: print(f'To unsubscribe: {to_unsub}') for instance in instances: From 6df56657d3169038c597e9c2d3fde022ec2d98fc Mon Sep 17 00:00:00 2001 From: itsdeka Date: Fri, 25 Jun 2021 12:20:37 +0200 Subject: [PATCH 09/68] -) updated CHANGELOG -) updated example get_authenticated_data.py -) updated string formatter margin_info.py and margin_info_base.py --- CHANGELOG | 1 + bfxapi/examples/rest/get_authenticated_data.py | 13 +++++++------ bfxapi/models/margin_info.py | 3 ++- bfxapi/models/margin_info_base.py | 3 ++- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 64f29fd..836ffc8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,6 @@ 1.1.16 -) Implemented Margin Info (rest) +-) Implemented claim position (rest) 1.1.15 -) Added 'ids' parameter to get_order_history() diff --git a/bfxapi/examples/rest/get_authenticated_data.py b/bfxapi/examples/rest/get_authenticated_data.py index f065bfa..9cf2adc 100644 --- a/bfxapi/examples/rest/get_authenticated_data.py +++ b/bfxapi/examples/rest/get_authenticated_data.py @@ -80,12 +80,13 @@ async def log_funding_credits_history(): [ print (c) for c in credit ] async def log_margin_info(): - m1 = await bfx.rest.get_margin_info('tBTCUSD') - print (m1) - m2 = await bfx.rest.get_margin_info('sym_all') - print (m2) - m3 = await bfx.rest.get_margin_info('base') - print (m3) + margin_info = await bfx.rest.get_margin_info('tBTCUSD') + print(margin_info) + sym_all = await bfx.rest.get_margin_info('sym_all') # list of Margin Info + for margin_info in sym_all: + print(margin_info) + base = await bfx.rest.get_margin_info('base') + print(base) async def run(): await log_wallets() diff --git a/bfxapi/models/margin_info.py b/bfxapi/models/margin_info.py index 514be41..ab376d7 100644 --- a/bfxapi/models/margin_info.py +++ b/bfxapi/models/margin_info.py @@ -43,4 +43,5 @@ class MarginInfo: return MarginInfo(symbol, tradable_balance, gross_balance, buy, sell) def __str__(self): - return "Margin Info {}".format(self.symbol) + return "Margin Info {} buy={} sell={} tradable_balance={} gross_balance={}" \ + "".format(self.symbol, self.buy, self.sell, self. tradable_balance, self. gross_balance) diff --git a/bfxapi/models/margin_info_base.py b/bfxapi/models/margin_info_base.py index 200bc52..806fc5f 100644 --- a/bfxapi/models/margin_info_base.py +++ b/bfxapi/models/margin_info_base.py @@ -44,4 +44,5 @@ class MarginInfoBase: return MarginInfoBase(user_pl, user_swaps, margin_balance, margin_net, margin_min) def __str__(self): - return "Margin Info Base" + return "Margin Info Base user_pl={} user_swaps={} margin_balance={} margin_net={} margin_min={}" \ + "".format(self.user_pl, self.user_swaps, self.margin_balance, self.margin_net, self.margin_min) From f899feb1ca6cb83c7fee431834a5200ee8ad0635 Mon Sep 17 00:00:00 2001 From: nkasimova Date: Fri, 9 Jul 2021 13:21:05 +0300 Subject: [PATCH 10/68] Fixed a bug in close position flag --- bfxapi/models/order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bfxapi/models/order.py b/bfxapi/models/order.py index 1bb6928..1a8348c 100644 --- a/bfxapi/models/order.py +++ b/bfxapi/models/order.py @@ -67,7 +67,7 @@ class OrderFlags: as flags """ HIDDEN = 64 - CLOSE = 12 + CLOSE = 512 REDUCE_ONLY = 1024 POST_ONLY = 4096 OCO = 16384 From 18eba161e537cea0f855d1eb2c8eaa105a7d5dfe Mon Sep 17 00:00:00 2001 From: nkasimova Date: Fri, 9 Jul 2021 15:38:14 +0300 Subject: [PATCH 11/68] Fixed bug in setting 'time in force' in order update --- bfxapi/websockets/order_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bfxapi/websockets/order_manager.py b/bfxapi/websockets/order_manager.py index 2f9cbbf..91ab04b 100644 --- a/bfxapi/websockets/order_manager.py +++ b/bfxapi/websockets/order_manager.py @@ -202,7 +202,7 @@ class OrderManager: if price_trailing != None: payload['price_trailing'] = str(price_trailing) if time_in_force != None: - payload['time_in_force'] = str(time_in_force) + payload['tif'] = str(time_in_force) if leverage != None: payload['lev'] = str(leverage) flags = calculate_order_flags( From 4f07ba0e3951d58bd97d515e45357943d9beddc2 Mon Sep 17 00:00:00 2001 From: nkasimova Date: Thu, 15 Jul 2021 11:19:38 +0300 Subject: [PATCH 12/68] Fixed bug in setting 'time in force' in order update (added changes to rest) --- bfxapi/rest/bfx_rest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bfxapi/rest/bfx_rest.py b/bfxapi/rest/bfx_rest.py index fba9b60..7100464 100644 --- a/bfxapi/rest/bfx_rest.py +++ b/bfxapi/rest/bfx_rest.py @@ -869,7 +869,7 @@ class BfxRest: if price_trailing != None: payload['price_trailing'] = str(price_trailing) if time_in_force != None: - payload['time_in_force'] = str(time_in_force) + payload['tif'] = str(time_in_force) if leverage != None: payload["lev"] = str(leverage) flags = calculate_order_flags( From 4e86eede4125cb0a997df23acc9f9686a364e7f5 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Fri, 6 Aug 2021 13:45:28 +0200 Subject: [PATCH 13/68] if max_retries == 0 continue forever --- CHANGELOG | 1 + bfxapi/websockets/generic_websocket.py | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 836ffc8..b1a45f8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ 1.1.16 -) Implemented Margin Info (rest) -) Implemented claim position (rest) +-) When max_retries == 0 continue forever to retry (websocket) 1.1.15 -) Added 'ids' parameter to get_order_history() diff --git a/bfxapi/websockets/generic_websocket.py b/bfxapi/websockets/generic_websocket.py index afec9cc..80d19c3 100644 --- a/bfxapi/websockets/generic_websocket.py +++ b/bfxapi/websockets/generic_websocket.py @@ -128,7 +128,7 @@ class GenericWebsocket: s = Socket(sId) self.sockets[sId] = s loop = asyncio.get_event_loop() - while retries < self.max_retries and self.attempt_retry: + while self.max_retries == 0 or (retries < self.max_retries and self.attempt_retry): try: async with websockets.connect(self.host) as websocket: self.sockets[sId].set_websocket(websocket) diff --git a/setup.py b/setup.py index c638f7a..8bc5e46 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from os import path here = path.abspath(path.dirname(__file__)) setup( name='bitfinex-api-py', - version='1.1.15', + version='1.1.16', description='Official Bitfinex Python API', long_description='A Python reference implementation of the Bitfinex API for both REST and websocket interaction', long_description_content_type='text/markdown', From 110b4b46e1cc95ffdedf62a1ba54ef8d06ab0817 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Mon, 9 Aug 2021 12:15:56 +0200 Subject: [PATCH 14/68] added full_orderbook.py example --- bfxapi/examples/ws/full_orderbook.py | 80 ++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 bfxapi/examples/ws/full_orderbook.py diff --git a/bfxapi/examples/ws/full_orderbook.py b/bfxapi/examples/ws/full_orderbook.py new file mode 100644 index 0000000..d8678e3 --- /dev/null +++ b/bfxapi/examples/ws/full_orderbook.py @@ -0,0 +1,80 @@ +import os +import sys +import time +from collections import OrderedDict +sys.path.append('../../../') + +from bfxapi import Client + +bfx = Client( + manageOrderBooks=True +) + +class OrderBook: + def __init__(self, snapshot): + self.bids = OrderedDict() + self.asks = OrderedDict() + self.load(snapshot) + + def load(self, snapshot): + for record in snapshot: + if record[2] >= 0: + self.bids[record[0]] = { + 'count': record[1], + 'amount': record[2] + } + else: + self.asks[record[0]] = { + 'count': record[1], + 'amount': record[2] + } + + def update(self, record): + # count is 0 + if record[1] == 0: + if record[2] == 1: + # remove from bids + del self.bids[record[0]] + elif record[2] == -1: + # remove from asks + del self.asks[record[0]] + elif record[1] > 0: + if record[2] > 0: + # update bids + if record[0] not in self.bids: + self.bids[record[0]] = {} + self.bids[record[0]]['count'] = record[1] + self.bids[record[0]]['amount'] = record[2] + elif record[2] < 0: + # update asks + if record[0] not in self.asks: + self.asks[record[0]] = {} + self.asks[record[0]]['count'] = record[1] + self.asks[record[0]]['amount'] = record[2] + +obs = {} + +@bfx.ws.on('error') +def log_error(err): + print ("Error: {}".format(err)) + +@bfx.ws.on('order_book_update') +def log_update(data): + obs[data['symbol']].update(data['data']) + +@bfx.ws.on('order_book_snapshot') +def log_snapshot(data): + obs[data['symbol']] = OrderBook(data['data']) + +async def start(): + await bfx.ws.subscribe('book', 'tBTCUSD') + +bfx.ws.on('connected', start) +bfx.ws.run() + +for n in range(0, 10): + time.sleep(2) + for key in obs: + print(f"Printing {key} orderbook...") + print(f"{obs[key].bids}\n") + print(f"{obs[key].asks}\n") From 8e87cf17cea7fbb620e9bcad251a8d90f46b903f Mon Sep 17 00:00:00 2001 From: itsdeka Date: Mon, 9 Aug 2021 12:18:13 +0200 Subject: [PATCH 15/68] added full_orderbook.py example --- CHANGELOG | 3 +++ bfxapi/version.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 836ffc8..f04969b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +1.1.18 +-) Added orderbook implementation example (ws) + 1.1.16 -) Implemented Margin Info (rest) -) Implemented claim position (rest) diff --git a/bfxapi/version.py b/bfxapi/version.py index 3bf093c..370ebdd 100644 --- a/bfxapi/version.py +++ b/bfxapi/version.py @@ -2,4 +2,4 @@ This module contains the current version of the bfxapi lib """ -__version__ = '1.1.16' +__version__ = '1.1.18' From df0bc58305e72ff07828945a53391236a88b325f Mon Sep 17 00:00:00 2001 From: itsdeka Date: Mon, 9 Aug 2021 12:30:41 +0200 Subject: [PATCH 16/68] ver --- CHANGELOG | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index f04969b..1c47b15 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,4 @@ -1.1.18 +1.1.17 -) Added orderbook implementation example (ws) 1.1.16 diff --git a/setup.py b/setup.py index c638f7a..d0c6278 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from os import path here = path.abspath(path.dirname(__file__)) setup( name='bitfinex-api-py', - version='1.1.15', + version='1.1.17', description='Official Bitfinex Python API', long_description='A Python reference implementation of the Bitfinex API for both REST and websocket interaction', long_description_content_type='text/markdown', From 48e8455180f1d69c41feff11e8d0f055f11dddf4 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Mon, 9 Aug 2021 12:31:16 +0200 Subject: [PATCH 17/68] ver --- bfxapi/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bfxapi/version.py b/bfxapi/version.py index 370ebdd..374b348 100644 --- a/bfxapi/version.py +++ b/bfxapi/version.py @@ -2,4 +2,4 @@ This module contains the current version of the bfxapi lib """ -__version__ = '1.1.18' +__version__ = '1.1.17' From 30b46b1ebe75cd3ca73091bb321881d5849df6f6 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Thu, 12 Aug 2021 16:31:54 +0200 Subject: [PATCH 18/68] semver adjustment --- CHANGELOG | 2 +- bfxapi/version.py | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b1a45f8..74e320e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,4 @@ -1.1.16 +1.2.0 -) Implemented Margin Info (rest) -) Implemented claim position (rest) -) When max_retries == 0 continue forever to retry (websocket) diff --git a/bfxapi/version.py b/bfxapi/version.py index 3bf093c..f46ec8e 100644 --- a/bfxapi/version.py +++ b/bfxapi/version.py @@ -2,4 +2,4 @@ This module contains the current version of the bfxapi lib """ -__version__ = '1.1.16' +__version__ = '1.2.0' diff --git a/setup.py b/setup.py index 8bc5e46..4a50b95 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from os import path here = path.abspath(path.dirname(__file__)) setup( name='bitfinex-api-py', - version='1.1.16', + version='1.2.0', description='Official Bitfinex Python API', long_description='A Python reference implementation of the Bitfinex API for both REST and websocket interaction', long_description_content_type='text/markdown', From e1e8eb57535dea28cf1b8d99bafb6f588fed4c29 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Thu, 12 Aug 2021 16:33:04 +0200 Subject: [PATCH 19/68] semver adjustment --- CHANGELOG | 2 +- bfxapi/version.py | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 1c47b15..6949d64 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,4 @@ -1.1.17 +1.2.1 -) Added orderbook implementation example (ws) 1.1.16 diff --git a/bfxapi/version.py b/bfxapi/version.py index 374b348..f00d03f 100644 --- a/bfxapi/version.py +++ b/bfxapi/version.py @@ -2,4 +2,4 @@ This module contains the current version of the bfxapi lib """ -__version__ = '1.1.17' +__version__ = '1.2.1' diff --git a/setup.py b/setup.py index d0c6278..b008d75 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from os import path here = path.abspath(path.dirname(__file__)) setup( name='bitfinex-api-py', - version='1.1.17', + version='1.2.1', description='Official Bitfinex Python API', long_description='A Python reference implementation of the Bitfinex API for both REST and websocket interaction', long_description_content_type='text/markdown', From b4dfa65597e8d0ac6fc8fe142793688c4ad3a13b Mon Sep 17 00:00:00 2001 From: itsdeka Date: Mon, 4 Oct 2021 11:33:32 +0200 Subject: [PATCH 20/68] Handle InvalidStatuCode exception --- bfxapi/websockets/generic_websocket.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bfxapi/websockets/generic_websocket.py b/bfxapi/websockets/generic_websocket.py index 80d19c3..a92ebd9 100644 --- a/bfxapi/websockets/generic_websocket.py +++ b/bfxapi/websockets/generic_websocket.py @@ -13,7 +13,7 @@ from pyee import AsyncIOEventEmitter from ..utils.custom_logger import CustomLogger # websocket exceptions -from websockets.exceptions import ConnectionClosed +from websockets.exceptions import ConnectionClosed, InvalidStatusCode class AuthError(Exception): """ @@ -141,7 +141,7 @@ class GenericWebsocket: await asyncio.sleep(0) message = await websocket.recv() await self.on_message(sId, message) - except (ConnectionClosed, socket.error) as e: + except (ConnectionClosed, socket.error, InvalidStatusCode) as e: self.sockets[sId].set_disconnected() if self.sockets[sId].isAuthenticated: self.sockets[sId].set_unauthenticated() From 64917d9e94051133364bcb3408ddc4d6ea90b857 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Mon, 4 Oct 2021 11:34:23 +0200 Subject: [PATCH 21/68] updated version --- CHANGELOG | 3 +++ bfxapi/version.py | 2 +- setup.py | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 90b8884..86e89ef 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +1.2.2 +-) WS bugfix (exception InvalidStatuCode not handled) + 1.2.1 -) Added orderbook implementation example (ws) diff --git a/bfxapi/version.py b/bfxapi/version.py index f00d03f..9de1127 100644 --- a/bfxapi/version.py +++ b/bfxapi/version.py @@ -2,4 +2,4 @@ This module contains the current version of the bfxapi lib """ -__version__ = '1.2.1' +__version__ = '1.2.2' diff --git a/setup.py b/setup.py index b008d75..a4dff33 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from os import path here = path.abspath(path.dirname(__file__)) setup( name='bitfinex-api-py', - version='1.2.1', + version='1.2.2', description='Official Bitfinex Python API', long_description='A Python reference implementation of the Bitfinex API for both REST and websocket interaction', long_description_content_type='text/markdown', From 1af7badf6512d58ae2c92101a02ee45fa0b7f681 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Mon, 4 Oct 2021 11:34:54 +0200 Subject: [PATCH 22/68] adjusted CHANGELOG --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 86e89ef..58ebbca 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,5 @@ 1.2.2 --) WS bugfix (exception InvalidStatuCode not handled) +-) WS bugfix (exception InvalidStatusCode not handled) 1.2.1 -) Added orderbook implementation example (ws) From b149f59d056a079b03b5d02b89bfe1eb2d3861ca Mon Sep 17 00:00:00 2001 From: Vigan Abdurrahmani Date: Mon, 11 Oct 2021 11:33:51 +0200 Subject: [PATCH 23/68] Create python-app.yml --- .github/workflows/python-app.yml | 36 ++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/python-app.yml diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml new file mode 100644 index 0000000..70bba55 --- /dev/null +++ b/.github/workflows/python-app.yml @@ -0,0 +1,36 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Python application + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.9 + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + pytest From 01a2a302b59ad5882705804b738e69acb6751ae9 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Mon, 11 Oct 2021 12:57:55 +0200 Subject: [PATCH 24/68] semver adjustment --- CHANGELOG | 3 +++ bfxapi/tests/test_rest_get_public_trades.py | 2 +- bfxapi/version.py | 2 +- requirements.txt | 2 +- setup.py | 2 +- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 58ebbca..9e962e8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +1.2.3 +-) Tests adjusted + 1.2.2 -) WS bugfix (exception InvalidStatusCode not handled) diff --git a/bfxapi/tests/test_rest_get_public_trades.py b/bfxapi/tests/test_rest_get_public_trades.py index 7de22d0..4f7a0d4 100644 --- a/bfxapi/tests/test_rest_get_public_trades.py +++ b/bfxapi/tests/test_rest_get_public_trades.py @@ -16,7 +16,7 @@ async def run(): print(trades) for trade in trades: orders_ids.append(trade[0]) - assert orders_ids == [657815316, 657815314, 657815312, 657815311, 657815309] + assert orders_ids == [657815316, 657815314, 657815312, 657815308, 657815304] # check that strictly decreasing order id condition is always respected # check that not increasing timestamp condition is always respected diff --git a/bfxapi/version.py b/bfxapi/version.py index 9de1127..fb61ac5 100644 --- a/bfxapi/version.py +++ b/bfxapi/version.py @@ -2,4 +2,4 @@ This module contains the current version of the bfxapi lib """ -__version__ = '1.2.2' +__version__ = '1.2.3' diff --git a/requirements.txt b/requirements.txt index 346acb6..6f06c18 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ asyncio==3.4.3 websockets==8.1 pylint==2.3.0 -pytest-asyncio==0.10.0 +pytest-asyncio==0.15.1 six==1.12.0 pyee==8.0.1 aiohttp==3.4.4 diff --git a/setup.py b/setup.py index a4dff33..a20e97b 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from os import path here = path.abspath(path.dirname(__file__)) setup( name='bitfinex-api-py', - version='1.2.2', + version='1.2.3', description='Official Bitfinex Python API', long_description='A Python reference implementation of the Bitfinex API for both REST and websocket interaction', long_description_content_type='text/markdown', From 83b0a5ecb659ec86a3867cd23fb4a9d297fe7a82 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Thu, 11 Nov 2021 12:38:47 +0100 Subject: [PATCH 25/68] Added example of MARKET order with price=None --- CHANGELOG | 3 +++ bfxapi/examples/rest/create_order.py | 3 ++- bfxapi/examples/ws/send_order.py | 2 +- bfxapi/version.py | 2 +- setup.py | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 9e962e8..708b445 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +1.2.4 +-) Added example of MARKET order with price=None + 1.2.3 -) Tests adjusted diff --git a/bfxapi/examples/rest/create_order.py b/bfxapi/examples/rest/create_order.py index b987cff..9c69a0c 100644 --- a/bfxapi/examples/rest/create_order.py +++ b/bfxapi/examples/rest/create_order.py @@ -4,6 +4,7 @@ import asyncio import time sys.path.append('../../../') from bfxapi import Client +from bfxapi.models import OrderType API_KEY=os.getenv("BFX_KEY") API_SECRET=os.getenv("BFX_SECRET") @@ -15,7 +16,7 @@ bfx = Client( ) async def create_order(): - response = await bfx.rest.submit_order("tBTCUSD", 10, 0.1) + response = await bfx.rest.submit_order(symbol="tBTCUSD", amount=10, price=None, market_type=OrderType.MARKET) # response is in the form of a Notification object for o in response.notify_info: # each item is in the form of an Order object diff --git a/bfxapi/examples/ws/send_order.py b/bfxapi/examples/ws/send_order.py index 43a8b2d..6c30fd7 100644 --- a/bfxapi/examples/ws/send_order.py +++ b/bfxapi/examples/ws/send_order.py @@ -34,7 +34,7 @@ def log_error(msg): @bfx.ws.on('authenticated') async def submit_order(auth_message): - await bfx.ws.submit_order('tBTCUSD', 19000, 0.01, Order.Type.EXCHANGE_MARKET) + await bfx.ws.submit_order(symbol='tBTCUSD', price=None, amount=0.01, market_type=Order.Type.EXCHANGE_MARKET) # If you dont want to use a decorator # ws.on('authenticated', submit_order) diff --git a/bfxapi/version.py b/bfxapi/version.py index fb61ac5..747cce9 100644 --- a/bfxapi/version.py +++ b/bfxapi/version.py @@ -2,4 +2,4 @@ This module contains the current version of the bfxapi lib """ -__version__ = '1.2.3' +__version__ = '1.2.4' diff --git a/setup.py b/setup.py index a20e97b..c41ebd9 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from os import path here = path.abspath(path.dirname(__file__)) setup( name='bitfinex-api-py', - version='1.2.3', + version='1.2.4', description='Official Bitfinex Python API', long_description='A Python reference implementation of the Bitfinex API for both REST and websocket interaction', long_description_content_type='text/markdown', From bf3c5f57abd981956480178f63d7c641548ed807 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Mon, 22 Nov 2021 16:09:59 +0100 Subject: [PATCH 26/68] adjusted get_order_history() rest endpoint --- CHANGELOG | 3 +++ bfxapi/rest/bfx_rest.py | 15 +++++++++++---- bfxapi/version.py | 2 +- setup.py | 2 +- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 708b445..254b884 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +1.2.5 +-) Adjusted get_order_history() rest endpoint + 1.2.4 -) Added example of MARKET order with price=None diff --git a/bfxapi/rest/bfx_rest.py b/bfxapi/rest/bfx_rest.py index 7100464..e305666 100644 --- a/bfxapi/rest/bfx_rest.py +++ b/bfxapi/rest/bfx_rest.py @@ -427,11 +427,18 @@ class BfxRest: @return Array """ endpoint = "auth/r/orders/{}/hist".format(symbol) - params = "?start={}&end={}&limit={}&sort={}".format( - start, end, limit, sort) + payload = {} + if start: + payload['start'] = start + if end: + payload['end'] = end + if limit: + payload['limit'] = limit + if sort: + payload['sort'] = sort if ids: - params += "&id=" + ",".join(str(id) for id in ids) - raw_orders = await self.post(endpoint, params=params) + payload['id'] = ids + raw_orders = await self.post(endpoint, payload) return [Order.from_raw_order(ro) for ro in raw_orders] async def get_active_position(self): diff --git a/bfxapi/version.py b/bfxapi/version.py index 747cce9..946288f 100644 --- a/bfxapi/version.py +++ b/bfxapi/version.py @@ -2,4 +2,4 @@ This module contains the current version of the bfxapi lib """ -__version__ = '1.2.4' +__version__ = '1.2.5' diff --git a/setup.py b/setup.py index c41ebd9..8fba16c 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from os import path here = path.abspath(path.dirname(__file__)) setup( name='bitfinex-api-py', - version='1.2.4', + version='1.2.5', description='Official Bitfinex Python API', long_description='A Python reference implementation of the Bitfinex API for both REST and websocket interaction', long_description_content_type='text/markdown', From f78926b2f822b9e8f00db9cbd74917d9249427b8 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Tue, 23 Nov 2021 14:38:26 +0100 Subject: [PATCH 27/68] updated version of websockets --- CHANGELOG | 3 +++ bfxapi/version.py | 2 +- requirements.txt | 2 +- setup.py | 4 ++-- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 254b884..dcd341b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +1.2.6 +-) Updated websockets to 9.1 + 1.2.5 -) Adjusted get_order_history() rest endpoint diff --git a/bfxapi/version.py b/bfxapi/version.py index 946288f..a663868 100644 --- a/bfxapi/version.py +++ b/bfxapi/version.py @@ -2,4 +2,4 @@ This module contains the current version of the bfxapi lib """ -__version__ = '1.2.5' +__version__ = '1.2.6' diff --git a/requirements.txt b/requirements.txt index 6f06c18..b579314 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ asyncio==3.4.3 -websockets==8.1 +websockets==9.1 pylint==2.3.0 pytest-asyncio==0.15.1 six==1.12.0 diff --git a/setup.py b/setup.py index 8fba16c..69f5e2e 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from os import path here = path.abspath(path.dirname(__file__)) setup( name='bitfinex-api-py', - version='1.2.5', + version='1.2.6', description='Official Bitfinex Python API', long_description='A Python reference implementation of the Bitfinex API for both REST and websocket interaction', long_description_content_type='text/markdown', @@ -49,7 +49,7 @@ setup( # deps installed by pip install_requires=[ 'asyncio~=3.0', - 'websockets~=8.0', + 'websockets>=8,<10', 'aiohttp~=3.0', 'pyee~=8.0' ], From abfafbf8cf0cb304feba4c81a9cb303eba18bef8 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Wed, 24 Nov 2021 14:34:33 +0100 Subject: [PATCH 28/68] added support per py 3.9 and 3.10 --- CHANGELOG | 3 +++ bfxapi/version.py | 2 +- bfxapi/websockets/generic_websocket.py | 9 +++------ requirements.txt | 1 + setup.py | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index dcd341b..b7426db 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +1.2.7 +-) Added ws support for Python 3.9 and 3.10 + 1.2.6 -) Updated websockets to 9.1 diff --git a/bfxapi/version.py b/bfxapi/version.py index a663868..64e75f8 100644 --- a/bfxapi/version.py +++ b/bfxapi/version.py @@ -2,4 +2,4 @@ This module contains the current version of the bfxapi lib """ -__version__ = '1.2.6' +__version__ = '1.2.7' diff --git a/bfxapi/websockets/generic_websocket.py b/bfxapi/websockets/generic_websocket.py index a92ebd9..4f7d482 100644 --- a/bfxapi/websockets/generic_websocket.py +++ b/bfxapi/websockets/generic_websocket.py @@ -7,6 +7,7 @@ import websockets import socket import json import time +import nest_asyncio from threading import Thread, Lock from pyee import AsyncIOEventEmitter @@ -14,6 +15,7 @@ from ..utils.custom_logger import CustomLogger # websocket exceptions from websockets.exceptions import ConnectionClosed, InvalidStatusCode +nest_asyncio.apply() class AuthError(Exception): """ @@ -94,12 +96,7 @@ class GenericWebsocket: def _start_new_socket(self, socketId=None): if not socketId: socketId = len(self.sockets) - def start_loop(loop): - asyncio.set_event_loop(loop) - loop.run_until_complete(self._run_socket()) - worker_loop = asyncio.new_event_loop() - worker = Thread(target=start_loop, args=(worker_loop,)) - worker.start() + asyncio.run(self._run_socket()) return socketId def _wait_for_socket(self, socket_id): diff --git a/requirements.txt b/requirements.txt index b579314..9f4cfff 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,4 @@ six==1.12.0 pyee==8.0.1 aiohttp==3.4.4 isort==4.3.21 +nest_asyncio==1.5.1 \ No newline at end of file diff --git a/setup.py b/setup.py index 69f5e2e..cec0c1f 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from os import path here = path.abspath(path.dirname(__file__)) setup( name='bitfinex-api-py', - version='1.2.6', + version='1.2.7', description='Official Bitfinex Python API', long_description='A Python reference implementation of the Bitfinex API for both REST and websocket interaction', long_description_content_type='text/markdown', From faa6075eae4ec428f2db455b17e1f8e8acb7689b Mon Sep 17 00:00:00 2001 From: itsdeka Date: Thu, 25 Nov 2021 12:19:31 +0100 Subject: [PATCH 29/68] new approach --- bfxapi/websockets/generic_websocket.py | 10 +++++++--- requirements.txt | 3 +-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/bfxapi/websockets/generic_websocket.py b/bfxapi/websockets/generic_websocket.py index 4f7d482..f5b6269 100644 --- a/bfxapi/websockets/generic_websocket.py +++ b/bfxapi/websockets/generic_websocket.py @@ -7,7 +7,6 @@ import websockets import socket import json import time -import nest_asyncio from threading import Thread, Lock from pyee import AsyncIOEventEmitter @@ -15,7 +14,6 @@ from ..utils.custom_logger import CustomLogger # websocket exceptions from websockets.exceptions import ConnectionClosed, InvalidStatusCode -nest_asyncio.apply() class AuthError(Exception): """ @@ -86,6 +84,8 @@ class GenericWebsocket: thread and connection. """ self._start_new_socket() + while True: + time.sleep(1) def get_task_executable(self): """ @@ -93,10 +93,14 @@ class GenericWebsocket: """ return self._run_socket() + def _start_new_async_socket(self): + asyncio.run(self._run_socket()) + def _start_new_socket(self, socketId=None): if not socketId: socketId = len(self.sockets) - asyncio.run(self._run_socket()) + worker = Thread(target=self._start_new_async_socket) + worker.start() return socketId def _wait_for_socket(self, socket_id): diff --git a/requirements.txt b/requirements.txt index 9f4cfff..5bb3aee 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,5 +5,4 @@ pytest-asyncio==0.15.1 six==1.12.0 pyee==8.0.1 aiohttp==3.4.4 -isort==4.3.21 -nest_asyncio==1.5.1 \ No newline at end of file +isort==4.3.21 \ No newline at end of file From f74c1c0fde4aa732555106ec93a8e0589a217ca1 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Thu, 25 Nov 2021 12:21:19 +0100 Subject: [PATCH 30/68] fix --- requirements.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 5bb3aee..91f7936 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,5 +4,4 @@ pylint==2.3.0 pytest-asyncio==0.15.1 six==1.12.0 pyee==8.0.1 -aiohttp==3.4.4 -isort==4.3.21 \ No newline at end of file +aiohttp==3.4.4 \ No newline at end of file From bf67841ada7e6a94b57af5ad189a332a9102f797 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Thu, 25 Nov 2021 12:22:08 +0100 Subject: [PATCH 31/68] fix --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 91f7936..5bb3aee 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,5 @@ pylint==2.3.0 pytest-asyncio==0.15.1 six==1.12.0 pyee==8.0.1 -aiohttp==3.4.4 \ No newline at end of file +aiohttp==3.4.4 +isort==4.3.21 \ No newline at end of file From d0246296e616b24fd65c20cb39fa69f7a8908507 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Thu, 25 Nov 2021 12:27:15 +0100 Subject: [PATCH 32/68] Support for Python <= 3.8 --- bfxapi/websockets/generic_websocket.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bfxapi/websockets/generic_websocket.py b/bfxapi/websockets/generic_websocket.py index f5b6269..f975b1e 100644 --- a/bfxapi/websockets/generic_websocket.py +++ b/bfxapi/websockets/generic_websocket.py @@ -94,7 +94,8 @@ class GenericWebsocket: return self._run_socket() def _start_new_async_socket(self): - asyncio.run(self._run_socket()) + loop = asyncio.new_event_loop() + loop.run_until_complete(self._run_socket()) def _start_new_socket(self, socketId=None): if not socketId: From 765532185ef331afb1b46e77d6633aa7c273ab03 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Mon, 29 Nov 2021 09:38:42 +0100 Subject: [PATCH 33/68] fixed pyee 'error must derive from BaseException' issue --- bfxapi/websockets/bfx_websocket.py | 56 +++++++++++++------------- bfxapi/websockets/generic_websocket.py | 8 ++-- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/bfxapi/websockets/bfx_websocket.py b/bfxapi/websockets/bfx_websocket.py index 0c3ff80..07771bd 100644 --- a/bfxapi/websockets/bfx_websocket.py +++ b/bfxapi/websockets/bfx_websocket.py @@ -266,13 +266,13 @@ class BfxWebsocket(GenericWebsocket): socketId, ERRORS[data.get('code', 10000)], data.get("msg", "")) - self._emit('error', err_string) + await self._emit(Exception(err_string)) async def _system_auth_handler(self, socketId, data): if data.get('status') == 'FAILED': raise AuthError(ERRORS[data.get('code')]) else: - self._emit('authenticated', data) + await self._emit('authenticated', data) self.logger.info("Authentication successful.") async def _trade_update_handler(self, data): @@ -281,7 +281,7 @@ class BfxWebsocket(GenericWebsocket): if self.subscriptionManager.is_subscribed(data[0]): symbol = self.subscriptionManager.get(data[0]).symbol tradeObj = _parse_trade(tData, symbol) - self._emit('trade_update', tradeObj) + await self._emit('trade_update', tradeObj) async def _trade_executed_handler(self, data): tData = data[2] @@ -289,28 +289,28 @@ class BfxWebsocket(GenericWebsocket): if self.subscriptionManager.is_subscribed(data[0]): symbol = self.subscriptionManager.get(data[0]).symbol tradeObj = _parse_trade(tData, symbol) - self._emit('new_trade', tradeObj) + await self._emit('new_trade', tradeObj) async def _wallet_update_handler(self, data): # [0,"wu",["exchange","USD",89134.66933283,0]] uw = self.wallets._update_from_event(data) - self._emit('wallet_update', uw) + await self._emit('wallet_update', uw) self.logger.info("Wallet update: {}".format(uw)) async def _heart_beat_handler(self, data): self.logger.debug("Heartbeat - {}".format(self.host)) async def _margin_info_update_handler(self, data): - self._emit('margin_info_update', data) + await self._emit('margin_info_update', data) self.logger.info("Margin info update: {}".format(data)) async def _funding_info_update_handler(self, data): - self._emit('funding_info_update', data) + await self._emit('funding_info_update', data) self.logger.info("Funding info update: {}".format(data)) async def _notification_handler(self, data): nInfo = data[2] - self._emit('notification', nInfo) + await self._emit('notification', nInfo) notificationType = nInfo[6] notificationText = nInfo[7] if notificationType == 'ERROR': @@ -324,7 +324,7 @@ class BfxWebsocket(GenericWebsocket): async def _balance_update_handler(self, data): self.logger.info('Balance update: {}'.format(data[2])) - self._emit('balance_update', data[2]) + await self._emit('balance_update', data[2]) async def _order_closed_handler(self, data): await self.orderManager.confirm_order_closed(data) @@ -343,34 +343,34 @@ class BfxWebsocket(GenericWebsocket): async def _wallet_snapshot_handler(self, data): wallets = self.wallets._update_from_snapshot(data) - self._emit('wallet_snapshot', wallets) + await self._emit('wallet_snapshot', wallets) async def _position_snapshot_handler(self, data): - self._emit('position_snapshot', data) + await self._emit('position_snapshot', data) self.logger.info("Position snapshot: {}".format(data)) async def _position_update_handler(self, data): - self._emit('position_update', data) + await self._emit('position_update', data) self.logger.info("Position update: {}".format(data)) async def _position_close_handler(self, data): - self._emit('position_close', data) + await self._emit('position_close', data) self.logger.info("Position close: {}".format(data)) async def _position_new_handler(self, data): - self._emit('position_new', data) + await self._emit('position_new', data) self.logger.info("Position new: {}".format(data)) async def _funding_offer_snapshot_handler(self, data): - self._emit('funding_offer_snapshot', data) + await self._emit('funding_offer_snapshot', data) self.logger.info("Funding offer snapshot: {}".format(data)) async def _funding_load_snapshot_handler(self, data): - self._emit('funding_loan_snapshot', data[2]) + await self._emit('funding_loan_snapshot', data[2]) self.logger.info("Funding loan snapshot: {}".format(data)) async def _funding_credit_snapshot_handler(self, data): - self._emit('funding_credit_snapshot', data[2]) + await self._emit('funding_credit_snapshot', data[2]) self.logger.info("Funding credit snapshot: {}".format(data)) async def _status_handler(self, data): @@ -381,7 +381,7 @@ class BfxWebsocket(GenericWebsocket): if status_type == "deriv": status = _parse_deriv_status_update(rstatus, symbol) if status: - self._emit('status_update', status) + await self._emit('status_update', status) else: self.logger.warn('Unknown status data type: {}'.format(data)) @@ -392,13 +392,13 @@ class BfxWebsocket(GenericWebsocket): t = None if symbol[0] == 't': t = Ticker.from_raw_ticker(raw_ticker, symbol) - self._emit('new_trading_ticker', t) + await self._emit('new_trading_ticker', t) elif symbol[0] == 'f': t = FundingTicker.from_raw_ticker(raw_ticker, symbol) - self._emit('new_funding_ticker', t) + await self._emit('new_funding_ticker', t) else: self.logger.warn('Unknown ticker type: {}'.format(raw_ticker)) - self._emit('new_ticker', t) + await self._emit('new_ticker', t) async def _trade_handler(self, data): symbol = self.subscriptionManager.get(data[0]).symbol @@ -414,7 +414,7 @@ class BfxWebsocket(GenericWebsocket): 'price': t[3], 'symbol': symbol } - self._emit('seed_trade', trade) + await self._emit('seed_trade', trade) async def _candle_handler(self, data): subscription = self.subscriptionManager.get(data[0]) @@ -429,11 +429,11 @@ class BfxWebsocket(GenericWebsocket): for c in candlesSnapshot: candle = _parse_candle( c, subscription.symbol, subscription.timeframe) - self._emit('seed_candle', candle) + await self._emit('seed_candle', candle) else: candle = _parse_candle( data[1], subscription.symbol, subscription.timeframe) - self._emit('new_candle', candle) + await self._emit('new_candle', candle) async def _order_book_handler(self, data, orig_raw_message): obInfo = data[1] @@ -461,17 +461,17 @@ class BfxWebsocket(GenericWebsocket): if isSnapshot: self.orderBooks[symbol] = OrderBook() self.orderBooks[symbol].update_from_snapshot(obInfo, orig_raw_message) - self._emit('order_book_snapshot', { + await self._emit('order_book_snapshot', { 'symbol': symbol, 'data': obInfo}) else: self.orderBooks[symbol].update_with(obInfo, orig_raw_message) - self._emit('order_book_update', {'symbol': symbol, 'data': obInfo}) + await self._emit('order_book_update', {'symbol': symbol, 'data': obInfo}) async def on_message(self, socketId, message): self.logger.debug(message) # convert float values to decimal msg = json.loads(message, parse_float=self.parse_float) - self._emit('all', msg) + await self._emit('all', msg) if type(msg) is dict: # System messages are received as json await self._ws_system_handler(socketId, msg) @@ -495,7 +495,7 @@ class BfxWebsocket(GenericWebsocket): self.logger.info("Websocket opened.") if len(self.sockets) == 1: ## only call on first connection - self._emit('connected') + await self._emit('connected') # Orders are simulated in backtest mode if self.API_KEY and self.API_SECRET and self.get_authenticated_socket() == None: await self._ws_authenticate_socket(socket_id) diff --git a/bfxapi/websockets/generic_websocket.py b/bfxapi/websockets/generic_websocket.py index f975b1e..91a9f0a 100644 --- a/bfxapi/websockets/generic_websocket.py +++ b/bfxapi/websockets/generic_websocket.py @@ -44,7 +44,7 @@ class Socket(): def set_authenticated(self): self.isAuthenticated = True - + def set_unauthenticated(self): self.isAuthenticated = False @@ -84,8 +84,6 @@ class GenericWebsocket: thread and connection. """ self._start_new_socket() - while True: - time.sleep(1) def get_task_executable(self): """ @@ -191,7 +189,9 @@ class GenericWebsocket: return self.events.once(event) self.events.once(event, func) - def _emit(self, event, *args, **kwargs): + async def _emit(self, event, *args, **kwargs): + if type(event) == Exception: + await self.on_error(event) self.events.emit(event, *args, **kwargs) async def on_error(self, error): From b0f07814e7b50124a078e3f6a894ee1c760b8345 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Mon, 29 Nov 2021 09:42:55 +0100 Subject: [PATCH 34/68] updated tests --- bfxapi/tests/test_ws_orders.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bfxapi/tests/test_ws_orders.py b/bfxapi/tests/test_ws_orders.py index b28a841..be4a400 100644 --- a/bfxapi/tests/test_ws_orders.py +++ b/bfxapi/tests/test_ws_orders.py @@ -119,7 +119,7 @@ async def test_closed_callback_on_submit_order_closed(): ## send auth accepted await ws_publish_auth_accepted(client.ws) async def c(order): - client.ws._emit('c1', order) + await client.ws._emit('c1', order) callback_wait = EventWatcher.watch(client.ws, 'c1') # override cid generation client.ws.orderManager._gen_unique_cid = lambda: 123 @@ -136,7 +136,7 @@ async def test_confirmed_callback_on_submit_order_closed(): ## send auth accepted await ws_publish_auth_accepted(client.ws) async def c(order): - client.ws._emit('c1', order) + await client.ws._emit('c1', order) callback_wait = EventWatcher.watch(client.ws, 'c1') # override cid generation client.ws.orderManager._gen_unique_cid = lambda: 123 @@ -152,7 +152,7 @@ async def test_confirmed_callback_on_submit_new_order(): ## send auth accepted await ws_publish_auth_accepted(client.ws) async def c(order): - client.ws._emit('c1', order) + await client.ws._emit('c1', order) callback_wait = EventWatcher.watch(client.ws, 'c1') # override cid generation client.ws.orderManager._gen_unique_cid = lambda: 123 @@ -168,7 +168,7 @@ async def test_confirmed_callback_on_submit_order_update(): ## send auth accepted await ws_publish_auth_accepted(client.ws) async def c(order): - client.ws._emit('c1', order) + await client.ws._emit('c1', order) callback_wait = EventWatcher.watch(client.ws, 'c1') # override cid generation client.ws.orderManager._gen_unique_cid = lambda: 123 @@ -184,7 +184,7 @@ async def test_confirmed_callback_on_submit_cancel_order(): ## send auth accepted await ws_publish_auth_accepted(client.ws) async def c(order): - client.ws._emit('c1', order) + await client.ws._emit('c1', order) callback_wait = EventWatcher.watch(client.ws, 'c1') # override cid generation client.ws.orderManager._gen_unique_cid = lambda: 123 @@ -200,7 +200,7 @@ async def test_confirmed_callback_on_submit_cancel_group_order(): ## send auth accepted await ws_publish_auth_accepted(client.ws) async def c(order): - client.ws._emit('c1', order) + await client.ws._emit('c1', order) callback_wait = EventWatcher.watch(client.ws, 'c1') # override cid generation client.ws.orderManager._gen_unique_cid = lambda: 123 From e05e1522c4d25a08a4edfe4cc32b3a14b176b5d7 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Mon, 29 Nov 2021 09:46:49 +0100 Subject: [PATCH 35/68] removed useless import --- bfxapi/tests/test_ws_subscriptions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bfxapi/tests/test_ws_subscriptions.py b/bfxapi/tests/test_ws_subscriptions.py index 2928215..7e1866a 100644 --- a/bfxapi/tests/test_ws_subscriptions.py +++ b/bfxapi/tests/test_ws_subscriptions.py @@ -1,6 +1,5 @@ import pytest import json -import asyncio from .helpers import (create_stubbed_client, ws_publish_connection_init, EventWatcher) @pytest.mark.asyncio From d1308dad7b0d44c9daae6e3bc8d8690008bbf0c2 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Mon, 29 Nov 2021 09:49:33 +0100 Subject: [PATCH 36/68] refactoring --- bfxapi/websockets/bfx_websocket.py | 56 +++++++++++++------------- bfxapi/websockets/generic_websocket.py | 4 +- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/bfxapi/websockets/bfx_websocket.py b/bfxapi/websockets/bfx_websocket.py index 07771bd..49febc5 100644 --- a/bfxapi/websockets/bfx_websocket.py +++ b/bfxapi/websockets/bfx_websocket.py @@ -266,13 +266,13 @@ class BfxWebsocket(GenericWebsocket): socketId, ERRORS[data.get('code', 10000)], data.get("msg", "")) - await self._emit(Exception(err_string)) + self._emit(Exception(err_string)) async def _system_auth_handler(self, socketId, data): if data.get('status') == 'FAILED': raise AuthError(ERRORS[data.get('code')]) else: - await self._emit('authenticated', data) + self._emit('authenticated', data) self.logger.info("Authentication successful.") async def _trade_update_handler(self, data): @@ -281,7 +281,7 @@ class BfxWebsocket(GenericWebsocket): if self.subscriptionManager.is_subscribed(data[0]): symbol = self.subscriptionManager.get(data[0]).symbol tradeObj = _parse_trade(tData, symbol) - await self._emit('trade_update', tradeObj) + self._emit('trade_update', tradeObj) async def _trade_executed_handler(self, data): tData = data[2] @@ -289,28 +289,28 @@ class BfxWebsocket(GenericWebsocket): if self.subscriptionManager.is_subscribed(data[0]): symbol = self.subscriptionManager.get(data[0]).symbol tradeObj = _parse_trade(tData, symbol) - await self._emit('new_trade', tradeObj) + self._emit('new_trade', tradeObj) async def _wallet_update_handler(self, data): # [0,"wu",["exchange","USD",89134.66933283,0]] uw = self.wallets._update_from_event(data) - await self._emit('wallet_update', uw) + self._emit('wallet_update', uw) self.logger.info("Wallet update: {}".format(uw)) async def _heart_beat_handler(self, data): self.logger.debug("Heartbeat - {}".format(self.host)) async def _margin_info_update_handler(self, data): - await self._emit('margin_info_update', data) + self._emit('margin_info_update', data) self.logger.info("Margin info update: {}".format(data)) async def _funding_info_update_handler(self, data): - await self._emit('funding_info_update', data) + self._emit('funding_info_update', data) self.logger.info("Funding info update: {}".format(data)) async def _notification_handler(self, data): nInfo = data[2] - await self._emit('notification', nInfo) + self._emit('notification', nInfo) notificationType = nInfo[6] notificationText = nInfo[7] if notificationType == 'ERROR': @@ -324,7 +324,7 @@ class BfxWebsocket(GenericWebsocket): async def _balance_update_handler(self, data): self.logger.info('Balance update: {}'.format(data[2])) - await self._emit('balance_update', data[2]) + self._emit('balance_update', data[2]) async def _order_closed_handler(self, data): await self.orderManager.confirm_order_closed(data) @@ -343,34 +343,34 @@ class BfxWebsocket(GenericWebsocket): async def _wallet_snapshot_handler(self, data): wallets = self.wallets._update_from_snapshot(data) - await self._emit('wallet_snapshot', wallets) + self._emit('wallet_snapshot', wallets) async def _position_snapshot_handler(self, data): - await self._emit('position_snapshot', data) + self._emit('position_snapshot', data) self.logger.info("Position snapshot: {}".format(data)) async def _position_update_handler(self, data): - await self._emit('position_update', data) + self._emit('position_update', data) self.logger.info("Position update: {}".format(data)) async def _position_close_handler(self, data): - await self._emit('position_close', data) + self._emit('position_close', data) self.logger.info("Position close: {}".format(data)) async def _position_new_handler(self, data): - await self._emit('position_new', data) + self._emit('position_new', data) self.logger.info("Position new: {}".format(data)) async def _funding_offer_snapshot_handler(self, data): - await self._emit('funding_offer_snapshot', data) + self._emit('funding_offer_snapshot', data) self.logger.info("Funding offer snapshot: {}".format(data)) async def _funding_load_snapshot_handler(self, data): - await self._emit('funding_loan_snapshot', data[2]) + self._emit('funding_loan_snapshot', data[2]) self.logger.info("Funding loan snapshot: {}".format(data)) async def _funding_credit_snapshot_handler(self, data): - await self._emit('funding_credit_snapshot', data[2]) + self._emit('funding_credit_snapshot', data[2]) self.logger.info("Funding credit snapshot: {}".format(data)) async def _status_handler(self, data): @@ -381,7 +381,7 @@ class BfxWebsocket(GenericWebsocket): if status_type == "deriv": status = _parse_deriv_status_update(rstatus, symbol) if status: - await self._emit('status_update', status) + self._emit('status_update', status) else: self.logger.warn('Unknown status data type: {}'.format(data)) @@ -392,13 +392,13 @@ class BfxWebsocket(GenericWebsocket): t = None if symbol[0] == 't': t = Ticker.from_raw_ticker(raw_ticker, symbol) - await self._emit('new_trading_ticker', t) + self._emit('new_trading_ticker', t) elif symbol[0] == 'f': t = FundingTicker.from_raw_ticker(raw_ticker, symbol) - await self._emit('new_funding_ticker', t) + self._emit('new_funding_ticker', t) else: self.logger.warn('Unknown ticker type: {}'.format(raw_ticker)) - await self._emit('new_ticker', t) + self._emit('new_ticker', t) async def _trade_handler(self, data): symbol = self.subscriptionManager.get(data[0]).symbol @@ -414,7 +414,7 @@ class BfxWebsocket(GenericWebsocket): 'price': t[3], 'symbol': symbol } - await self._emit('seed_trade', trade) + self._emit('seed_trade', trade) async def _candle_handler(self, data): subscription = self.subscriptionManager.get(data[0]) @@ -429,11 +429,11 @@ class BfxWebsocket(GenericWebsocket): for c in candlesSnapshot: candle = _parse_candle( c, subscription.symbol, subscription.timeframe) - await self._emit('seed_candle', candle) + self._emit('seed_candle', candle) else: candle = _parse_candle( data[1], subscription.symbol, subscription.timeframe) - await self._emit('new_candle', candle) + self._emit('new_candle', candle) async def _order_book_handler(self, data, orig_raw_message): obInfo = data[1] @@ -461,17 +461,17 @@ class BfxWebsocket(GenericWebsocket): if isSnapshot: self.orderBooks[symbol] = OrderBook() self.orderBooks[symbol].update_from_snapshot(obInfo, orig_raw_message) - await self._emit('order_book_snapshot', { + self._emit('order_book_snapshot', { 'symbol': symbol, 'data': obInfo}) else: self.orderBooks[symbol].update_with(obInfo, orig_raw_message) - await self._emit('order_book_update', {'symbol': symbol, 'data': obInfo}) + self._emit('order_book_update', {'symbol': symbol, 'data': obInfo}) async def on_message(self, socketId, message): self.logger.debug(message) # convert float values to decimal msg = json.loads(message, parse_float=self.parse_float) - await self._emit('all', msg) + self._emit('all', msg) if type(msg) is dict: # System messages are received as json await self._ws_system_handler(socketId, msg) @@ -495,7 +495,7 @@ class BfxWebsocket(GenericWebsocket): self.logger.info("Websocket opened.") if len(self.sockets) == 1: ## only call on first connection - await self._emit('connected') + self._emit('connected') # Orders are simulated in backtest mode if self.API_KEY and self.API_SECRET and self.get_authenticated_socket() == None: await self._ws_authenticate_socket(socket_id) diff --git a/bfxapi/websockets/generic_websocket.py b/bfxapi/websockets/generic_websocket.py index 91a9f0a..f155278 100644 --- a/bfxapi/websockets/generic_websocket.py +++ b/bfxapi/websockets/generic_websocket.py @@ -189,9 +189,9 @@ class GenericWebsocket: return self.events.once(event) self.events.once(event, func) - async def _emit(self, event, *args, **kwargs): + def _emit(self, event, *args, **kwargs): if type(event) == Exception: - await self.on_error(event) + self.logger.error(event) self.events.emit(event, *args, **kwargs) async def on_error(self, error): From 6c065e6fad8a5c75394d2230b917a83aa1931d59 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Mon, 29 Nov 2021 10:28:17 +0100 Subject: [PATCH 37/68] fixs --- bfxapi/tests/test_ws_capacity.py | 6 ++-- bfxapi/tests/test_ws_orderbook.py | 24 ++++++------- bfxapi/tests/test_ws_orders.py | 52 +++++++++++++-------------- bfxapi/tests/test_ws_subscriptions.py | 38 ++++++++++---------- 4 files changed, 60 insertions(+), 60 deletions(-) diff --git a/bfxapi/tests/test_ws_capacity.py b/bfxapi/tests/test_ws_capacity.py index 48cdbf9..77534a1 100644 --- a/bfxapi/tests/test_ws_capacity.py +++ b/bfxapi/tests/test_ws_capacity.py @@ -12,12 +12,12 @@ async def test_ws_creates_new_socket(): await ws_publish_connection_init(client.ws) # create a bunch of websocket subscriptions for symbol in ['tXRPBTC', 'tLTCUSD']: - await client.ws.subscribe('candles', symbol, timeframe='1m') + client.ws.subscribe('candles', symbol, timeframe='1m') assert len(client.ws.sockets) == 1 assert client.ws.get_total_available_capcity() == 3 # subscribe to a few more to force the lib to create a new ws conenction for symbol in ['tETHBTC', 'tBTCUSD', 'tETHUSD', 'tLTCBTC']: - await client.ws.subscribe('candles', symbol, timeframe='1m') + client.ws.subscribe('candles', symbol, timeframe='1m') assert len(client.ws.sockets) == 2 assert client.ws.get_total_available_capcity() == 4 @@ -29,7 +29,7 @@ async def test_ws_uses_authenticated_socket(): await ws_publish_connection_init(client.ws) # create a bunch of websocket subscriptions for symbol in ['tXRPBTC', 'tLTCUSD', 'tETHBTC', 'tBTCUSD', 'tETHUSD', 'tLTCBTC']: - await client.ws.subscribe('candles', symbol, timeframe='1m') + client.ws.subscribe('candles', symbol, timeframe='1m') # publish connection created message on socket (0 by default) await ws_publish_connection_init(client.ws) # send auth accepted (on socket by default) diff --git a/bfxapi/tests/test_ws_orderbook.py b/bfxapi/tests/test_ws_orderbook.py index ebbee52..7b4d2d7 100644 --- a/bfxapi/tests/test_ws_orderbook.py +++ b/bfxapi/tests/test_ws_orderbook.py @@ -10,12 +10,12 @@ async def test_checksum_generation(): # publish checksum flag accepted await ws_publish_conf_accepted(client.ws, 131072) # subscribe to order book - await client.ws.subscribe('book', symbol) + client.ws.subscribe('book', symbol) ## send subscription accepted chanId = 123 - await client.ws.publish({"event":"subscribed","channel":"book","chanId": chanId,"symbol": symbol,"prec":"P0","freq":"F0","len":"25","pair": symbol}) + client.ws.publish({"event":"subscribed","channel":"book","chanId": chanId,"symbol": symbol,"prec":"P0","freq":"F0","len":"25","pair": symbol}) ## send orderbook snapshot - await client.ws.publish("""[123, [[0.0000886,1,1060.55466114],[0.00008859,1,1000],[0.00008858,1,2713.47159343],[0.00008857,1,4276.92870916],[0.00008856,2,6764.75562319], + client.ws.publish("""[123, [[0.0000886,1,1060.55466114],[0.00008859,1,1000],[0.00008858,1,2713.47159343],[0.00008857,1,4276.92870916],[0.00008856,2,6764.75562319], [0.00008854,1,5641.48532401],[0.00008853,1,2255.92632223],[0.0000885,1,2256.69584601],[0.00008848,2,3630.3],[0.00008845,1,28195.70625766], [0.00008844,1,15571.7],[0.00008843,1,2500],[0.00008841,1,64196.16117814],[0.00008838,1,7500],[0.00008837,2,2764.12999012],[0.00008834,2,10886.476298], [0.00008831,1,20000],[0.0000883,1,1000],[0.00008829,2,2517.22175358],[0.00008828,1,450.45],[0.00008827,1,13000],[0.00008824,1,1500],[0.0000882,1,300], @@ -25,9 +25,9 @@ async def test_checksum_generation(): [0.00008894,1,-775.08564697],[0.00008896,1,-150],[0.00008899,3,-11628.02590049],[0.000089,2,-1299.7],[0.00008902,2,-4841.8],[0.00008904,3,-25320.46250083], [0.00008909,1,-14000],[0.00008913,1,-123947.999],[0.00008915,2,-28019.6]]]""", is_json=False) ## send some more price updates - await client.ws.publish("[{},[0.00008915,0,-1]]".format(chanId), is_json=False) - await client.ws.publish("[{},[0.00008837,1,56.54876269]]".format(chanId), is_json=False) - await client.ws.publish("[{},[0.00008873,1,-15699.9]]".format(chanId), is_json=False) + client.ws.publish("[{},[0.00008915,0,-1]]".format(chanId), is_json=False) + client.ws.publish("[{},[0.00008837,1,56.54876269]]".format(chanId), is_json=False) + client.ws.publish("[{},[0.00008873,1,-15699.9]]".format(chanId), is_json=False) ## check checksum is the same as expected expected_checksum = 30026640 actual_checksum = client.ws.orderBooks[symbol].checksum() @@ -42,12 +42,12 @@ async def test_checksum_really_samll_numbers_generation(): # publish checksum flag accepted await ws_publish_conf_accepted(client.ws, 131072) # subscribe to order book - await client.ws.subscribe('book', symbol) + client.ws.subscribe('book', symbol) ## send subscription accepted chanId = 123 - await client.ws.publish({"event":"subscribed","channel":"book","chanId": chanId,"symbol": symbol,"prec":"P0","freq":"F0","len":"25","pair": symbol}) + client.ws.publish({"event":"subscribed","channel":"book","chanId": chanId,"symbol": symbol,"prec":"P0","freq":"F0","len":"25","pair": symbol}) ## send orderbook snapshot - await client.ws.publish("""[123, [[0.00000121,5,249013.0209708],[0.0000012,6,518315.33310128],[0.00000119,4,566200.89],[0.00000118,2,260000],[0.00000117,1,100000], + client.ws.publish("""[123, [[0.00000121,5,249013.0209708],[0.0000012,6,518315.33310128],[0.00000119,4,566200.89],[0.00000118,2,260000],[0.00000117,1,100000], [0.00000116,2,160000],[0.00000114,1,60000],[0.00000113,2,198500],[0.00000112,1,60000],[0.0000011,1,60000],[0.00000106,2,113868.87735849],[0.00000105,2,105000], [0.00000103,1,3000],[0.00000102,2,105000],[0.00000101,2,202970],[0.000001,2,21000],[7e-7,1,10000],[6.6e-7,1,10000],[6e-7,1,100000],[4.9e-7,1,10000],[2.5e-7,1,2000], [6e-8,1,100000],[5e-8,1,200000],[1e-8,4,640000],[0.00000122,7,-312043.19],[0.00000123,6,-415094.8939744],[0.00000124,5,-348181.23],[0.00000125,1,-12000], @@ -56,9 +56,9 @@ async def test_checksum_really_samll_numbers_generation(): [0.00000164,1,-4000],[0.00000166,1,-3831.46784605],[0.00000171,1,-14575.17730379],[0.00000174,1,-3124.81815395],[0.0000018,1,-18000],[0.00000182,1,-16000], [0.00000186,1,-4000],[0.00000189,1,-10000.686624],[0.00000191,1,-14500]]]""", is_json=False) ## send some more price updates - await client.ws.publish("[{},[0.00000121,4,228442.6609708]]".format(chanId), is_json=False) - await client.ws.publish("[{},[0.00000121,6,304023.8109708]]".format(chanId), is_json=False) - # await client.ws.publish("[{},[0.00008873,1,-15699.9]]".format(chanId), is_json=False) + client.ws.publish("[{},[0.00000121,4,228442.6609708]]".format(chanId), is_json=False) + client.ws.publish("[{},[0.00000121,6,304023.8109708]]".format(chanId), is_json=False) + # client.ws.publish("[{},[0.00008873,1,-15699.9]]".format(chanId), is_json=False) ## check checksum is the same as expected expected_checksum = 1770440002 actual_checksum = client.ws.orderBooks[symbol].checksum() diff --git a/bfxapi/tests/test_ws_orders.py b/bfxapi/tests/test_ws_orders.py index be4a400..60e0b6e 100644 --- a/bfxapi/tests/test_ws_orders.py +++ b/bfxapi/tests/test_ws_orders.py @@ -12,7 +12,7 @@ async def test_submit_order(): ## send auth accepted await ws_publish_auth_accepted(client.ws) ## send new order - await client.ws.submit_order('tBTCUSD', 19000, 0.01, 'EXCHANGE MARKET') + client.ws.submit_order('tBTCUSD', 19000, 0.01, 'EXCHANGE MARKET') last_sent = client.ws.get_last_sent_item() sent_order_array = json.loads(last_sent['data']) assert sent_order_array[1] == "on" @@ -30,7 +30,7 @@ async def test_submit_update_order(): ## send auth accepted await ws_publish_auth_accepted(client.ws) ## send new order - await client.ws.update_order(123, price=100, amount=0.01, hidden=True) + client.ws.update_order(123, price=100, amount=0.01, hidden=True) last_sent = client.ws.get_last_sent_item() sent_order_array = json.loads(last_sent['data']) assert sent_order_array[1] == "ou" @@ -49,7 +49,7 @@ async def test_submit_cancel_order(): ## send auth accepted await ws_publish_auth_accepted(client.ws) ## send new order - await client.ws.cancel_order(123) + client.ws.cancel_order(123) last_sent = client.ws.get_last_sent_item() sent_order_array = json.loads(last_sent['data']) assert sent_order_array[1] == "oc" @@ -66,7 +66,7 @@ async def test_events_on_new_order(): ## look for new order confirmation o_new = EventWatcher.watch(client.ws, 'order_new') - await client.ws.publish([0,"on",[1151718504,None,1548262833910,"tBTCUSD",1548262833379,1548262833410,-1,-1,"EXCHANGE LIMIT",None,None,None,0,"ACTIVE",None,None,15980,0,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + client.ws.publish([0,"on",[1151718504,None,1548262833910,"tBTCUSD",1548262833379,1548262833410,-1,-1,"EXCHANGE LIMIT",None,None,None,0,"ACTIVE",None,None,15980,0,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) new_res = o_new.wait_until_complete() assert new_res.amount_orig == -1 assert new_res.amount_filled == 0 @@ -75,7 +75,7 @@ async def test_events_on_new_order(): ## look for order update confirmation o_update = EventWatcher.watch(client.ws, 'order_update') - await client.ws.publish([0,"ou",[1151718504,None,1548262833910,"tBTCUSD",1548262833379,1548262846964,-0.5,-1,"EXCHANGE LIMIT",None,None,None,0,"PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + client.ws.publish([0,"ou",[1151718504,None,1548262833910,"tBTCUSD",1548262833379,1548262846964,-0.5,-1,"EXCHANGE LIMIT",None,None,None,0,"PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) update_res = o_update.wait_until_complete() assert update_res.amount_orig == -1 assert float(update_res.amount_filled) == -0.5 @@ -84,7 +84,7 @@ async def test_events_on_new_order(): ## look for closed notification o_closed = EventWatcher.watch(client.ws, 'order_closed') - await client.ws.publish([0,"oc",[1151718504,None,1548262833910,"tBTCUSD",1548262833379,1548262888016,0,-1,"EXCHANGE LIMIT",None,None,None,0,"EXECUTED @ 15980.0(-0.5): was PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + client.ws.publish([0,"oc",[1151718504,None,1548262833910,"tBTCUSD",1548262833379,1548262888016,0,-1,"EXCHANGE LIMIT",None,None,None,0,"EXECUTED @ 15980.0(-0.5): was PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) closed_res = o_closed.wait_until_complete() assert new_res.amount_orig == -1 assert new_res.amount_filled == 0 @@ -100,11 +100,11 @@ async def test_events_on_cancel_order(): await ws_publish_auth_accepted(client.ws) ## Create new order - await client.ws.publish([0,"on",[1151718565,None,1548325124885,"tBTCUSD",1548325123435,1548325123460,1,1,"EXCHANGE LIMIT",None,None,None,0,"ACTIVE",None,None,10,0,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + client.ws.publish([0,"on",[1151718565,None,1548325124885,"tBTCUSD",1548325123435,1548325123460,1,1,"EXCHANGE LIMIT",None,None,None,0,"ACTIVE",None,None,10,0,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) ## look for order closed confirmation o_close = EventWatcher.watch(client.ws, 'order_closed') - await client.ws.publish([0,"oc",[1151718565,None,1548325124885,"tBTCUSD",1548325123435,1548325123548,1,1,"EXCHANGE LIMIT",None,None,None,0,"CANCELED",None,None,10,0,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + client.ws.publish([0,"oc",[1151718565,None,1548325124885,"tBTCUSD",1548325123435,1548325123548,1,1,"EXCHANGE LIMIT",None,None,None,0,"CANCELED",None,None,10,0,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) close_res = o_close.wait_until_complete() assert close_res.amount_orig == 1 assert float(close_res.amount_filled) == 0 @@ -119,12 +119,12 @@ async def test_closed_callback_on_submit_order_closed(): ## send auth accepted await ws_publish_auth_accepted(client.ws) async def c(order): - await client.ws._emit('c1', order) + client.ws._emit('c1', order) callback_wait = EventWatcher.watch(client.ws, 'c1') # override cid generation client.ws.orderManager._gen_unique_cid = lambda: 123 - await client.ws.submit_order('tBTCUSD', 19000, 0.01, 'EXCHANGE MARKET', onClose=c) - await client.ws.publish([0,"oc",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262888016,0,-1,"EXCHANGE LIMIT",None,None,None,0,"EXECUTED @ 15980.0(-0.5): was PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + client.ws.submit_order('tBTCUSD', 19000, 0.01, 'EXCHANGE MARKET', onClose=c) + client.ws.publish([0,"oc",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262888016,0,-1,"EXCHANGE LIMIT",None,None,None,0,"EXECUTED @ 15980.0(-0.5): was PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) callback_wait.wait_until_complete() @@ -136,12 +136,12 @@ async def test_confirmed_callback_on_submit_order_closed(): ## send auth accepted await ws_publish_auth_accepted(client.ws) async def c(order): - await client.ws._emit('c1', order) + client.ws._emit('c1', order) callback_wait = EventWatcher.watch(client.ws, 'c1') # override cid generation client.ws.orderManager._gen_unique_cid = lambda: 123 - await client.ws.submit_order('tBTCUSD', 19000, 0.01, 'EXCHANGE MARKET', onConfirm=c) - await client.ws.publish([0,"oc",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262888016,0,-1,"EXCHANGE LIMIT",None,None,None,0,"EXECUTED @ 15980.0(-0.5): was PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + client.ws.submit_order('tBTCUSD', 19000, 0.01, 'EXCHANGE MARKET', onConfirm=c) + client.ws.publish([0,"oc",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262888016,0,-1,"EXCHANGE LIMIT",None,None,None,0,"EXECUTED @ 15980.0(-0.5): was PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) callback_wait.wait_until_complete() @pytest.mark.asyncio @@ -152,12 +152,12 @@ async def test_confirmed_callback_on_submit_new_order(): ## send auth accepted await ws_publish_auth_accepted(client.ws) async def c(order): - await client.ws._emit('c1', order) + client.ws._emit('c1', order) callback_wait = EventWatcher.watch(client.ws, 'c1') # override cid generation client.ws.orderManager._gen_unique_cid = lambda: 123 - await client.ws.submit_order('tBTCUSD', 19000, 0.01, 'EXCHANGE MARKET', onConfirm=c) - await client.ws.publish([0,"on",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262833410,-1,-1,"EXCHANGE LIMIT",None,None,None,0,"ACTIVE",None,None,15980,0,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + client.ws.submit_order('tBTCUSD', 19000, 0.01, 'EXCHANGE MARKET', onConfirm=c) + client.ws.publish([0,"on",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262833410,-1,-1,"EXCHANGE LIMIT",None,None,None,0,"ACTIVE",None,None,15980,0,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) callback_wait.wait_until_complete() @pytest.mark.asyncio @@ -168,12 +168,12 @@ async def test_confirmed_callback_on_submit_order_update(): ## send auth accepted await ws_publish_auth_accepted(client.ws) async def c(order): - await client.ws._emit('c1', order) + client.ws._emit('c1', order) callback_wait = EventWatcher.watch(client.ws, 'c1') # override cid generation client.ws.orderManager._gen_unique_cid = lambda: 123 - await client.ws.update_order(123, price=100, onConfirm=c) - await client.ws.publish([0,"ou",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262846964,-0.5,-1,"EXCHANGE LIMIT",None,None,None,0,"PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + client.ws.update_order(123, price=100, onConfirm=c) + client.ws.publish([0,"ou",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262846964,-0.5,-1,"EXCHANGE LIMIT",None,None,None,0,"PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) callback_wait.wait_until_complete() @pytest.mark.asyncio @@ -184,12 +184,12 @@ async def test_confirmed_callback_on_submit_cancel_order(): ## send auth accepted await ws_publish_auth_accepted(client.ws) async def c(order): - await client.ws._emit('c1', order) + client.ws._emit('c1', order) callback_wait = EventWatcher.watch(client.ws, 'c1') # override cid generation client.ws.orderManager._gen_unique_cid = lambda: 123 - await client.ws.cancel_order(123, onConfirm=c) - await client.ws.publish([0,"oc",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262888016,0,-1,"EXCHANGE LIMIT",None,None,None,0,"EXECUTED @ 15980.0(-0.5): was PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + client.ws.cancel_order(123, onConfirm=c) + client.ws.publish([0,"oc",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262888016,0,-1,"EXCHANGE LIMIT",None,None,None,0,"EXECUTED @ 15980.0(-0.5): was PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) callback_wait.wait_until_complete() @pytest.mark.asyncio @@ -200,10 +200,10 @@ async def test_confirmed_callback_on_submit_cancel_group_order(): ## send auth accepted await ws_publish_auth_accepted(client.ws) async def c(order): - await client.ws._emit('c1', order) + client.ws._emit('c1', order) callback_wait = EventWatcher.watch(client.ws, 'c1') # override cid generation client.ws.orderManager._gen_unique_cid = lambda: 123 - await client.ws.cancel_order_group(123, onConfirm=c) - await client.ws.publish([0,"oc",[1548262833910,123,1548262833910,"tBTCUSD",1548262833379,1548262888016,0,-1,"EXCHANGE LIMIT",None,None,None,0,"EXECUTED @ 15980.0(-0.5): was PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + client.ws.cancel_order_group(123, onConfirm=c) + client.ws.publish([0,"oc",[1548262833910,123,1548262833910,"tBTCUSD",1548262833379,1548262888016,0,-1,"EXCHANGE LIMIT",None,None,None,0,"EXECUTED @ 15980.0(-0.5): was PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) callback_wait.wait_until_complete() diff --git a/bfxapi/tests/test_ws_subscriptions.py b/bfxapi/tests/test_ws_subscriptions.py index 7e1866a..06d69b9 100644 --- a/bfxapi/tests/test_ws_subscriptions.py +++ b/bfxapi/tests/test_ws_subscriptions.py @@ -10,7 +10,7 @@ async def test_submit_subscribe(): await ws_publish_connection_init(client.ws) # Create new subscription to orderbook - await client.ws.subscribe('book', symb) + client.ws.subscribe('book', symb) last_sent = client.ws.get_last_sent_item() sent_sub = json.loads(last_sent['data']) # {'time': 1548327054030, 'data': '{"event": "subscribe", "channel": "book", "symbol": "tXRPBTC"}'} @@ -19,7 +19,7 @@ async def test_submit_subscribe(): assert sent_sub['symbol'] == symb # create new subscription to trades - await client.ws.subscribe('trades', symb) + client.ws.subscribe('trades', symb) last_sent = client.ws.get_last_sent_item() sent_sub = json.loads(last_sent['data']) # {'event': 'subscribe', 'channel': 'trades', 'symbol': 'tBTCUSD'} @@ -28,7 +28,7 @@ async def test_submit_subscribe(): assert sent_sub['symbol'] == symb # create new subscription to candles - await client.ws.subscribe('candles', symb, timeframe='1m') + client.ws.subscribe('candles', symb, timeframe='1m') last_sent = client.ws.get_last_sent_item() sent_sub = json.loads(last_sent['data']) #{'event': 'subscribe', 'channel': 'candles', 'symbol': 'tBTCUSD', 'key': 'trade:1m:tBTCUSD'} @@ -44,10 +44,10 @@ async def test_event_subscribe(): # publish connection created message await ws_publish_connection_init(client.ws) # create a new subscription - await client.ws.subscribe('trades', symb) + client.ws.subscribe('trades', symb) # announce subscription was successful sub_watch = EventWatcher.watch(client.ws, 'subscribed') - await client.ws.publish({"event":"subscribed","channel":"trades","chanId":2,"symbol":symb,"pair":pair}) + client.ws.publish({"event":"subscribed","channel":"trades","chanId":2,"symbol":symb,"pair":pair}) s_res = sub_watch.wait_until_complete() assert s_res.channel_name == 'trades' assert s_res.symbol == symb @@ -62,10 +62,10 @@ async def test_submit_unsubscribe(): # publish connection created message await ws_publish_connection_init(client.ws) # create new subscription to trades - await client.ws.subscribe('trades', symb) + client.ws.subscribe('trades', symb) # announce subscription was successful sub_watch = EventWatcher.watch(client.ws, 'subscribed') - await client.ws.publish({"event":"subscribed","channel":"trades","chanId":2,"symbol":symb,"pair":pair}) + client.ws.publish({"event":"subscribed","channel":"trades","chanId":2,"symbol":symb,"pair":pair}) s_res = sub_watch.wait_until_complete() # unsubscribe from channel await s_res.unsubscribe() @@ -83,10 +83,10 @@ async def test_event_unsubscribe(): # publish connection created message await ws_publish_connection_init(client.ws) # create new subscription to trades - await client.ws.subscribe('trades', symb) + client.ws.subscribe('trades', symb) # announce subscription was successful sub_watch = EventWatcher.watch(client.ws, 'subscribed') - await client.ws.publish({"event":"subscribed","channel":"trades","chanId":2,"symbol":symb,"pair":pair}) + client.ws.publish({"event":"subscribed","channel":"trades","chanId":2,"symbol":symb,"pair":pair}) s_res = sub_watch.wait_until_complete() # unsubscribe from channel await s_res.unsubscribe() @@ -95,7 +95,7 @@ async def test_event_unsubscribe(): # publish confirmation of unsubscribe unsub_watch = EventWatcher.watch(client.ws, 'unsubscribed') - await client.ws.publish({"event":"unsubscribed","status":"OK","chanId":2}) + client.ws.publish({"event":"unsubscribed","status":"OK","chanId":2}) unsub_res = unsub_watch.wait_until_complete() assert s_res.channel_name == 'trades' assert s_res.symbol == symb @@ -110,13 +110,13 @@ async def test_submit_resubscribe(): # publish connection created message await ws_publish_connection_init(client.ws) # request two new subscriptions - await client.ws.subscribe('book', symb) - await client.ws.subscribe('trades', symb) + client.ws.subscribe('book', symb) + client.ws.subscribe('trades', symb) # confirm subscriptions - await client.ws.publish({"event":"subscribed","channel":"trades","chanId":2,"symbol":symb,"pair":pair}) - await client.ws.publish({"event":"subscribed","channel":"book","chanId":3,"symbol":symb,"prec":"P0","freq":"F0","len":"25","pair":pair}) + client.ws.publish({"event":"subscribed","channel":"trades","chanId":2,"symbol":symb,"pair":pair}) + client.ws.publish({"event":"subscribed","channel":"book","chanId":3,"symbol":symb,"prec":"P0","freq":"F0","len":"25","pair":pair}) # call resubscribe all - await client.ws.resubscribe_all() + client.ws.resubscribe_all() ## assert that 2 unsubscribe requests were sent last_sent = client.ws.get_sent_items()[-2:] for i in last_sent: @@ -124,12 +124,12 @@ async def test_submit_resubscribe(): assert data['event'] == 'unsubscribe' assert (data['chanId'] == 2 or data['chanId'] == 3) ## confirm unsubscriptions - await client.ws.publish({"event":"unsubscribed","status":"OK","chanId":2}) - await client.ws.publish({"event":"unsubscribed","status":"OK","chanId":3}) + client.ws.publish({"event":"unsubscribed","status":"OK","chanId":2}) + client.ws.publish({"event":"unsubscribed","status":"OK","chanId":3}) ## confirm subscriptions - # await client.ws.publish({"event":"subscribed","channel":"trades","chanId":2,"symbol":symb,"pair":pair}) - # await client.ws.publish({"event":"subscribed","channel":"book","chanId":3,"symbol":symb,"prec":"P0","freq":"F0","len":"25","pair":pair}) + # client.ws.publish({"event":"subscribed","channel":"trades","chanId":2,"symbol":symb,"pair":pair}) + # client.ws.publish({"event":"subscribed","channel":"book","chanId":3,"symbol":symb,"prec":"P0","freq":"F0","len":"25","pair":pair}) # wait for emit of event n_last_sent = client.ws.get_sent_items()[-2:] for i in n_last_sent: From 25d748042a2410d4c89f2aa416485d0a945658a7 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Mon, 29 Nov 2021 10:31:45 +0100 Subject: [PATCH 38/68] fixs --- bfxapi/tests/test_ws_capacity.py | 6 ++-- bfxapi/tests/test_ws_orderbook.py | 24 ++++++++-------- bfxapi/tests/test_ws_orders.py | 40 +++++++++++++-------------- bfxapi/tests/test_ws_subscriptions.py | 38 ++++++++++++------------- 4 files changed, 54 insertions(+), 54 deletions(-) diff --git a/bfxapi/tests/test_ws_capacity.py b/bfxapi/tests/test_ws_capacity.py index 77534a1..48cdbf9 100644 --- a/bfxapi/tests/test_ws_capacity.py +++ b/bfxapi/tests/test_ws_capacity.py @@ -12,12 +12,12 @@ async def test_ws_creates_new_socket(): await ws_publish_connection_init(client.ws) # create a bunch of websocket subscriptions for symbol in ['tXRPBTC', 'tLTCUSD']: - client.ws.subscribe('candles', symbol, timeframe='1m') + await client.ws.subscribe('candles', symbol, timeframe='1m') assert len(client.ws.sockets) == 1 assert client.ws.get_total_available_capcity() == 3 # subscribe to a few more to force the lib to create a new ws conenction for symbol in ['tETHBTC', 'tBTCUSD', 'tETHUSD', 'tLTCBTC']: - client.ws.subscribe('candles', symbol, timeframe='1m') + await client.ws.subscribe('candles', symbol, timeframe='1m') assert len(client.ws.sockets) == 2 assert client.ws.get_total_available_capcity() == 4 @@ -29,7 +29,7 @@ async def test_ws_uses_authenticated_socket(): await ws_publish_connection_init(client.ws) # create a bunch of websocket subscriptions for symbol in ['tXRPBTC', 'tLTCUSD', 'tETHBTC', 'tBTCUSD', 'tETHUSD', 'tLTCBTC']: - client.ws.subscribe('candles', symbol, timeframe='1m') + await client.ws.subscribe('candles', symbol, timeframe='1m') # publish connection created message on socket (0 by default) await ws_publish_connection_init(client.ws) # send auth accepted (on socket by default) diff --git a/bfxapi/tests/test_ws_orderbook.py b/bfxapi/tests/test_ws_orderbook.py index 7b4d2d7..ebbee52 100644 --- a/bfxapi/tests/test_ws_orderbook.py +++ b/bfxapi/tests/test_ws_orderbook.py @@ -10,12 +10,12 @@ async def test_checksum_generation(): # publish checksum flag accepted await ws_publish_conf_accepted(client.ws, 131072) # subscribe to order book - client.ws.subscribe('book', symbol) + await client.ws.subscribe('book', symbol) ## send subscription accepted chanId = 123 - client.ws.publish({"event":"subscribed","channel":"book","chanId": chanId,"symbol": symbol,"prec":"P0","freq":"F0","len":"25","pair": symbol}) + await client.ws.publish({"event":"subscribed","channel":"book","chanId": chanId,"symbol": symbol,"prec":"P0","freq":"F0","len":"25","pair": symbol}) ## send orderbook snapshot - client.ws.publish("""[123, [[0.0000886,1,1060.55466114],[0.00008859,1,1000],[0.00008858,1,2713.47159343],[0.00008857,1,4276.92870916],[0.00008856,2,6764.75562319], + await client.ws.publish("""[123, [[0.0000886,1,1060.55466114],[0.00008859,1,1000],[0.00008858,1,2713.47159343],[0.00008857,1,4276.92870916],[0.00008856,2,6764.75562319], [0.00008854,1,5641.48532401],[0.00008853,1,2255.92632223],[0.0000885,1,2256.69584601],[0.00008848,2,3630.3],[0.00008845,1,28195.70625766], [0.00008844,1,15571.7],[0.00008843,1,2500],[0.00008841,1,64196.16117814],[0.00008838,1,7500],[0.00008837,2,2764.12999012],[0.00008834,2,10886.476298], [0.00008831,1,20000],[0.0000883,1,1000],[0.00008829,2,2517.22175358],[0.00008828,1,450.45],[0.00008827,1,13000],[0.00008824,1,1500],[0.0000882,1,300], @@ -25,9 +25,9 @@ async def test_checksum_generation(): [0.00008894,1,-775.08564697],[0.00008896,1,-150],[0.00008899,3,-11628.02590049],[0.000089,2,-1299.7],[0.00008902,2,-4841.8],[0.00008904,3,-25320.46250083], [0.00008909,1,-14000],[0.00008913,1,-123947.999],[0.00008915,2,-28019.6]]]""", is_json=False) ## send some more price updates - client.ws.publish("[{},[0.00008915,0,-1]]".format(chanId), is_json=False) - client.ws.publish("[{},[0.00008837,1,56.54876269]]".format(chanId), is_json=False) - client.ws.publish("[{},[0.00008873,1,-15699.9]]".format(chanId), is_json=False) + await client.ws.publish("[{},[0.00008915,0,-1]]".format(chanId), is_json=False) + await client.ws.publish("[{},[0.00008837,1,56.54876269]]".format(chanId), is_json=False) + await client.ws.publish("[{},[0.00008873,1,-15699.9]]".format(chanId), is_json=False) ## check checksum is the same as expected expected_checksum = 30026640 actual_checksum = client.ws.orderBooks[symbol].checksum() @@ -42,12 +42,12 @@ async def test_checksum_really_samll_numbers_generation(): # publish checksum flag accepted await ws_publish_conf_accepted(client.ws, 131072) # subscribe to order book - client.ws.subscribe('book', symbol) + await client.ws.subscribe('book', symbol) ## send subscription accepted chanId = 123 - client.ws.publish({"event":"subscribed","channel":"book","chanId": chanId,"symbol": symbol,"prec":"P0","freq":"F0","len":"25","pair": symbol}) + await client.ws.publish({"event":"subscribed","channel":"book","chanId": chanId,"symbol": symbol,"prec":"P0","freq":"F0","len":"25","pair": symbol}) ## send orderbook snapshot - client.ws.publish("""[123, [[0.00000121,5,249013.0209708],[0.0000012,6,518315.33310128],[0.00000119,4,566200.89],[0.00000118,2,260000],[0.00000117,1,100000], + await client.ws.publish("""[123, [[0.00000121,5,249013.0209708],[0.0000012,6,518315.33310128],[0.00000119,4,566200.89],[0.00000118,2,260000],[0.00000117,1,100000], [0.00000116,2,160000],[0.00000114,1,60000],[0.00000113,2,198500],[0.00000112,1,60000],[0.0000011,1,60000],[0.00000106,2,113868.87735849],[0.00000105,2,105000], [0.00000103,1,3000],[0.00000102,2,105000],[0.00000101,2,202970],[0.000001,2,21000],[7e-7,1,10000],[6.6e-7,1,10000],[6e-7,1,100000],[4.9e-7,1,10000],[2.5e-7,1,2000], [6e-8,1,100000],[5e-8,1,200000],[1e-8,4,640000],[0.00000122,7,-312043.19],[0.00000123,6,-415094.8939744],[0.00000124,5,-348181.23],[0.00000125,1,-12000], @@ -56,9 +56,9 @@ async def test_checksum_really_samll_numbers_generation(): [0.00000164,1,-4000],[0.00000166,1,-3831.46784605],[0.00000171,1,-14575.17730379],[0.00000174,1,-3124.81815395],[0.0000018,1,-18000],[0.00000182,1,-16000], [0.00000186,1,-4000],[0.00000189,1,-10000.686624],[0.00000191,1,-14500]]]""", is_json=False) ## send some more price updates - client.ws.publish("[{},[0.00000121,4,228442.6609708]]".format(chanId), is_json=False) - client.ws.publish("[{},[0.00000121,6,304023.8109708]]".format(chanId), is_json=False) - # client.ws.publish("[{},[0.00008873,1,-15699.9]]".format(chanId), is_json=False) + await client.ws.publish("[{},[0.00000121,4,228442.6609708]]".format(chanId), is_json=False) + await client.ws.publish("[{},[0.00000121,6,304023.8109708]]".format(chanId), is_json=False) + # await client.ws.publish("[{},[0.00008873,1,-15699.9]]".format(chanId), is_json=False) ## check checksum is the same as expected expected_checksum = 1770440002 actual_checksum = client.ws.orderBooks[symbol].checksum() diff --git a/bfxapi/tests/test_ws_orders.py b/bfxapi/tests/test_ws_orders.py index 60e0b6e..b28a841 100644 --- a/bfxapi/tests/test_ws_orders.py +++ b/bfxapi/tests/test_ws_orders.py @@ -12,7 +12,7 @@ async def test_submit_order(): ## send auth accepted await ws_publish_auth_accepted(client.ws) ## send new order - client.ws.submit_order('tBTCUSD', 19000, 0.01, 'EXCHANGE MARKET') + await client.ws.submit_order('tBTCUSD', 19000, 0.01, 'EXCHANGE MARKET') last_sent = client.ws.get_last_sent_item() sent_order_array = json.loads(last_sent['data']) assert sent_order_array[1] == "on" @@ -30,7 +30,7 @@ async def test_submit_update_order(): ## send auth accepted await ws_publish_auth_accepted(client.ws) ## send new order - client.ws.update_order(123, price=100, amount=0.01, hidden=True) + await client.ws.update_order(123, price=100, amount=0.01, hidden=True) last_sent = client.ws.get_last_sent_item() sent_order_array = json.loads(last_sent['data']) assert sent_order_array[1] == "ou" @@ -49,7 +49,7 @@ async def test_submit_cancel_order(): ## send auth accepted await ws_publish_auth_accepted(client.ws) ## send new order - client.ws.cancel_order(123) + await client.ws.cancel_order(123) last_sent = client.ws.get_last_sent_item() sent_order_array = json.loads(last_sent['data']) assert sent_order_array[1] == "oc" @@ -66,7 +66,7 @@ async def test_events_on_new_order(): ## look for new order confirmation o_new = EventWatcher.watch(client.ws, 'order_new') - client.ws.publish([0,"on",[1151718504,None,1548262833910,"tBTCUSD",1548262833379,1548262833410,-1,-1,"EXCHANGE LIMIT",None,None,None,0,"ACTIVE",None,None,15980,0,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + await client.ws.publish([0,"on",[1151718504,None,1548262833910,"tBTCUSD",1548262833379,1548262833410,-1,-1,"EXCHANGE LIMIT",None,None,None,0,"ACTIVE",None,None,15980,0,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) new_res = o_new.wait_until_complete() assert new_res.amount_orig == -1 assert new_res.amount_filled == 0 @@ -75,7 +75,7 @@ async def test_events_on_new_order(): ## look for order update confirmation o_update = EventWatcher.watch(client.ws, 'order_update') - client.ws.publish([0,"ou",[1151718504,None,1548262833910,"tBTCUSD",1548262833379,1548262846964,-0.5,-1,"EXCHANGE LIMIT",None,None,None,0,"PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + await client.ws.publish([0,"ou",[1151718504,None,1548262833910,"tBTCUSD",1548262833379,1548262846964,-0.5,-1,"EXCHANGE LIMIT",None,None,None,0,"PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) update_res = o_update.wait_until_complete() assert update_res.amount_orig == -1 assert float(update_res.amount_filled) == -0.5 @@ -84,7 +84,7 @@ async def test_events_on_new_order(): ## look for closed notification o_closed = EventWatcher.watch(client.ws, 'order_closed') - client.ws.publish([0,"oc",[1151718504,None,1548262833910,"tBTCUSD",1548262833379,1548262888016,0,-1,"EXCHANGE LIMIT",None,None,None,0,"EXECUTED @ 15980.0(-0.5): was PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + await client.ws.publish([0,"oc",[1151718504,None,1548262833910,"tBTCUSD",1548262833379,1548262888016,0,-1,"EXCHANGE LIMIT",None,None,None,0,"EXECUTED @ 15980.0(-0.5): was PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) closed_res = o_closed.wait_until_complete() assert new_res.amount_orig == -1 assert new_res.amount_filled == 0 @@ -100,11 +100,11 @@ async def test_events_on_cancel_order(): await ws_publish_auth_accepted(client.ws) ## Create new order - client.ws.publish([0,"on",[1151718565,None,1548325124885,"tBTCUSD",1548325123435,1548325123460,1,1,"EXCHANGE LIMIT",None,None,None,0,"ACTIVE",None,None,10,0,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + await client.ws.publish([0,"on",[1151718565,None,1548325124885,"tBTCUSD",1548325123435,1548325123460,1,1,"EXCHANGE LIMIT",None,None,None,0,"ACTIVE",None,None,10,0,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) ## look for order closed confirmation o_close = EventWatcher.watch(client.ws, 'order_closed') - client.ws.publish([0,"oc",[1151718565,None,1548325124885,"tBTCUSD",1548325123435,1548325123548,1,1,"EXCHANGE LIMIT",None,None,None,0,"CANCELED",None,None,10,0,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + await client.ws.publish([0,"oc",[1151718565,None,1548325124885,"tBTCUSD",1548325123435,1548325123548,1,1,"EXCHANGE LIMIT",None,None,None,0,"CANCELED",None,None,10,0,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) close_res = o_close.wait_until_complete() assert close_res.amount_orig == 1 assert float(close_res.amount_filled) == 0 @@ -123,8 +123,8 @@ async def test_closed_callback_on_submit_order_closed(): callback_wait = EventWatcher.watch(client.ws, 'c1') # override cid generation client.ws.orderManager._gen_unique_cid = lambda: 123 - client.ws.submit_order('tBTCUSD', 19000, 0.01, 'EXCHANGE MARKET', onClose=c) - client.ws.publish([0,"oc",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262888016,0,-1,"EXCHANGE LIMIT",None,None,None,0,"EXECUTED @ 15980.0(-0.5): was PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + await client.ws.submit_order('tBTCUSD', 19000, 0.01, 'EXCHANGE MARKET', onClose=c) + await client.ws.publish([0,"oc",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262888016,0,-1,"EXCHANGE LIMIT",None,None,None,0,"EXECUTED @ 15980.0(-0.5): was PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) callback_wait.wait_until_complete() @@ -140,8 +140,8 @@ async def test_confirmed_callback_on_submit_order_closed(): callback_wait = EventWatcher.watch(client.ws, 'c1') # override cid generation client.ws.orderManager._gen_unique_cid = lambda: 123 - client.ws.submit_order('tBTCUSD', 19000, 0.01, 'EXCHANGE MARKET', onConfirm=c) - client.ws.publish([0,"oc",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262888016,0,-1,"EXCHANGE LIMIT",None,None,None,0,"EXECUTED @ 15980.0(-0.5): was PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + await client.ws.submit_order('tBTCUSD', 19000, 0.01, 'EXCHANGE MARKET', onConfirm=c) + await client.ws.publish([0,"oc",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262888016,0,-1,"EXCHANGE LIMIT",None,None,None,0,"EXECUTED @ 15980.0(-0.5): was PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) callback_wait.wait_until_complete() @pytest.mark.asyncio @@ -156,8 +156,8 @@ async def test_confirmed_callback_on_submit_new_order(): callback_wait = EventWatcher.watch(client.ws, 'c1') # override cid generation client.ws.orderManager._gen_unique_cid = lambda: 123 - client.ws.submit_order('tBTCUSD', 19000, 0.01, 'EXCHANGE MARKET', onConfirm=c) - client.ws.publish([0,"on",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262833410,-1,-1,"EXCHANGE LIMIT",None,None,None,0,"ACTIVE",None,None,15980,0,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + await client.ws.submit_order('tBTCUSD', 19000, 0.01, 'EXCHANGE MARKET', onConfirm=c) + await client.ws.publish([0,"on",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262833410,-1,-1,"EXCHANGE LIMIT",None,None,None,0,"ACTIVE",None,None,15980,0,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) callback_wait.wait_until_complete() @pytest.mark.asyncio @@ -172,8 +172,8 @@ async def test_confirmed_callback_on_submit_order_update(): callback_wait = EventWatcher.watch(client.ws, 'c1') # override cid generation client.ws.orderManager._gen_unique_cid = lambda: 123 - client.ws.update_order(123, price=100, onConfirm=c) - client.ws.publish([0,"ou",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262846964,-0.5,-1,"EXCHANGE LIMIT",None,None,None,0,"PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + await client.ws.update_order(123, price=100, onConfirm=c) + await client.ws.publish([0,"ou",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262846964,-0.5,-1,"EXCHANGE LIMIT",None,None,None,0,"PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) callback_wait.wait_until_complete() @pytest.mark.asyncio @@ -188,8 +188,8 @@ async def test_confirmed_callback_on_submit_cancel_order(): callback_wait = EventWatcher.watch(client.ws, 'c1') # override cid generation client.ws.orderManager._gen_unique_cid = lambda: 123 - client.ws.cancel_order(123, onConfirm=c) - client.ws.publish([0,"oc",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262888016,0,-1,"EXCHANGE LIMIT",None,None,None,0,"EXECUTED @ 15980.0(-0.5): was PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + await client.ws.cancel_order(123, onConfirm=c) + await client.ws.publish([0,"oc",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262888016,0,-1,"EXCHANGE LIMIT",None,None,None,0,"EXECUTED @ 15980.0(-0.5): was PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) callback_wait.wait_until_complete() @pytest.mark.asyncio @@ -204,6 +204,6 @@ async def test_confirmed_callback_on_submit_cancel_group_order(): callback_wait = EventWatcher.watch(client.ws, 'c1') # override cid generation client.ws.orderManager._gen_unique_cid = lambda: 123 - client.ws.cancel_order_group(123, onConfirm=c) - client.ws.publish([0,"oc",[1548262833910,123,1548262833910,"tBTCUSD",1548262833379,1548262888016,0,-1,"EXCHANGE LIMIT",None,None,None,0,"EXECUTED @ 15980.0(-0.5): was PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + await client.ws.cancel_order_group(123, onConfirm=c) + await client.ws.publish([0,"oc",[1548262833910,123,1548262833910,"tBTCUSD",1548262833379,1548262888016,0,-1,"EXCHANGE LIMIT",None,None,None,0,"EXECUTED @ 15980.0(-0.5): was PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) callback_wait.wait_until_complete() diff --git a/bfxapi/tests/test_ws_subscriptions.py b/bfxapi/tests/test_ws_subscriptions.py index 06d69b9..7e1866a 100644 --- a/bfxapi/tests/test_ws_subscriptions.py +++ b/bfxapi/tests/test_ws_subscriptions.py @@ -10,7 +10,7 @@ async def test_submit_subscribe(): await ws_publish_connection_init(client.ws) # Create new subscription to orderbook - client.ws.subscribe('book', symb) + await client.ws.subscribe('book', symb) last_sent = client.ws.get_last_sent_item() sent_sub = json.loads(last_sent['data']) # {'time': 1548327054030, 'data': '{"event": "subscribe", "channel": "book", "symbol": "tXRPBTC"}'} @@ -19,7 +19,7 @@ async def test_submit_subscribe(): assert sent_sub['symbol'] == symb # create new subscription to trades - client.ws.subscribe('trades', symb) + await client.ws.subscribe('trades', symb) last_sent = client.ws.get_last_sent_item() sent_sub = json.loads(last_sent['data']) # {'event': 'subscribe', 'channel': 'trades', 'symbol': 'tBTCUSD'} @@ -28,7 +28,7 @@ async def test_submit_subscribe(): assert sent_sub['symbol'] == symb # create new subscription to candles - client.ws.subscribe('candles', symb, timeframe='1m') + await client.ws.subscribe('candles', symb, timeframe='1m') last_sent = client.ws.get_last_sent_item() sent_sub = json.loads(last_sent['data']) #{'event': 'subscribe', 'channel': 'candles', 'symbol': 'tBTCUSD', 'key': 'trade:1m:tBTCUSD'} @@ -44,10 +44,10 @@ async def test_event_subscribe(): # publish connection created message await ws_publish_connection_init(client.ws) # create a new subscription - client.ws.subscribe('trades', symb) + await client.ws.subscribe('trades', symb) # announce subscription was successful sub_watch = EventWatcher.watch(client.ws, 'subscribed') - client.ws.publish({"event":"subscribed","channel":"trades","chanId":2,"symbol":symb,"pair":pair}) + await client.ws.publish({"event":"subscribed","channel":"trades","chanId":2,"symbol":symb,"pair":pair}) s_res = sub_watch.wait_until_complete() assert s_res.channel_name == 'trades' assert s_res.symbol == symb @@ -62,10 +62,10 @@ async def test_submit_unsubscribe(): # publish connection created message await ws_publish_connection_init(client.ws) # create new subscription to trades - client.ws.subscribe('trades', symb) + await client.ws.subscribe('trades', symb) # announce subscription was successful sub_watch = EventWatcher.watch(client.ws, 'subscribed') - client.ws.publish({"event":"subscribed","channel":"trades","chanId":2,"symbol":symb,"pair":pair}) + await client.ws.publish({"event":"subscribed","channel":"trades","chanId":2,"symbol":symb,"pair":pair}) s_res = sub_watch.wait_until_complete() # unsubscribe from channel await s_res.unsubscribe() @@ -83,10 +83,10 @@ async def test_event_unsubscribe(): # publish connection created message await ws_publish_connection_init(client.ws) # create new subscription to trades - client.ws.subscribe('trades', symb) + await client.ws.subscribe('trades', symb) # announce subscription was successful sub_watch = EventWatcher.watch(client.ws, 'subscribed') - client.ws.publish({"event":"subscribed","channel":"trades","chanId":2,"symbol":symb,"pair":pair}) + await client.ws.publish({"event":"subscribed","channel":"trades","chanId":2,"symbol":symb,"pair":pair}) s_res = sub_watch.wait_until_complete() # unsubscribe from channel await s_res.unsubscribe() @@ -95,7 +95,7 @@ async def test_event_unsubscribe(): # publish confirmation of unsubscribe unsub_watch = EventWatcher.watch(client.ws, 'unsubscribed') - client.ws.publish({"event":"unsubscribed","status":"OK","chanId":2}) + await client.ws.publish({"event":"unsubscribed","status":"OK","chanId":2}) unsub_res = unsub_watch.wait_until_complete() assert s_res.channel_name == 'trades' assert s_res.symbol == symb @@ -110,13 +110,13 @@ async def test_submit_resubscribe(): # publish connection created message await ws_publish_connection_init(client.ws) # request two new subscriptions - client.ws.subscribe('book', symb) - client.ws.subscribe('trades', symb) + await client.ws.subscribe('book', symb) + await client.ws.subscribe('trades', symb) # confirm subscriptions - client.ws.publish({"event":"subscribed","channel":"trades","chanId":2,"symbol":symb,"pair":pair}) - client.ws.publish({"event":"subscribed","channel":"book","chanId":3,"symbol":symb,"prec":"P0","freq":"F0","len":"25","pair":pair}) + await client.ws.publish({"event":"subscribed","channel":"trades","chanId":2,"symbol":symb,"pair":pair}) + await client.ws.publish({"event":"subscribed","channel":"book","chanId":3,"symbol":symb,"prec":"P0","freq":"F0","len":"25","pair":pair}) # call resubscribe all - client.ws.resubscribe_all() + await client.ws.resubscribe_all() ## assert that 2 unsubscribe requests were sent last_sent = client.ws.get_sent_items()[-2:] for i in last_sent: @@ -124,12 +124,12 @@ async def test_submit_resubscribe(): assert data['event'] == 'unsubscribe' assert (data['chanId'] == 2 or data['chanId'] == 3) ## confirm unsubscriptions - client.ws.publish({"event":"unsubscribed","status":"OK","chanId":2}) - client.ws.publish({"event":"unsubscribed","status":"OK","chanId":3}) + await client.ws.publish({"event":"unsubscribed","status":"OK","chanId":2}) + await client.ws.publish({"event":"unsubscribed","status":"OK","chanId":3}) ## confirm subscriptions - # client.ws.publish({"event":"subscribed","channel":"trades","chanId":2,"symbol":symb,"pair":pair}) - # client.ws.publish({"event":"subscribed","channel":"book","chanId":3,"symbol":symb,"prec":"P0","freq":"F0","len":"25","pair":pair}) + # await client.ws.publish({"event":"subscribed","channel":"trades","chanId":2,"symbol":symb,"pair":pair}) + # await client.ws.publish({"event":"subscribed","channel":"book","chanId":3,"symbol":symb,"prec":"P0","freq":"F0","len":"25","pair":pair}) # wait for emit of event n_last_sent = client.ws.get_sent_items()[-2:] for i in n_last_sent: From 603f546037191dda39d4bf92d50d0a3beddb7e07 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Mon, 29 Nov 2021 18:37:01 +0100 Subject: [PATCH 39/68] fix run() --- bfxapi/websockets/generic_websocket.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bfxapi/websockets/generic_websocket.py b/bfxapi/websockets/generic_websocket.py index f155278..20ff37a 100644 --- a/bfxapi/websockets/generic_websocket.py +++ b/bfxapi/websockets/generic_websocket.py @@ -84,6 +84,8 @@ class GenericWebsocket: thread and connection. """ self._start_new_socket() + while True: + time.sleep(1) def get_task_executable(self): """ From b5f3d6d8cb3c7cdd45385f6a99195b7da472c57b Mon Sep 17 00:00:00 2001 From: itsdeka Date: Fri, 10 Dec 2021 10:40:17 +0100 Subject: [PATCH 40/68] Bugfix - It is now possible to call bfx.ws.run() from an already running event loop --- CHANGELOG | 3 +++ bfxapi/version.py | 2 +- bfxapi/websockets/generic_websocket.py | 6 ++++-- setup.py | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b7426db..f972440 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +1.2.8 +-) Bugfix - It is possible to call bfx.ws.run() from an already running event loop + 1.2.7 -) Added ws support for Python 3.9 and 3.10 diff --git a/bfxapi/version.py b/bfxapi/version.py index 64e75f8..e9e4434 100644 --- a/bfxapi/version.py +++ b/bfxapi/version.py @@ -2,4 +2,4 @@ This module contains the current version of the bfxapi lib """ -__version__ = '1.2.7' +__version__ = '1.2.8' diff --git a/bfxapi/websockets/generic_websocket.py b/bfxapi/websockets/generic_websocket.py index 20ff37a..dcb39a8 100644 --- a/bfxapi/websockets/generic_websocket.py +++ b/bfxapi/websockets/generic_websocket.py @@ -84,8 +84,10 @@ class GenericWebsocket: thread and connection. """ self._start_new_socket() - while True: - time.sleep(1) + event_loop = asyncio.get_event_loop() + if not event_loop or not event_loop.is_running(): + while True: + time.sleep(1) def get_task_executable(self): """ diff --git a/setup.py b/setup.py index cec0c1f..ffda49f 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from os import path here = path.abspath(path.dirname(__file__)) setup( name='bitfinex-api-py', - version='1.2.7', + version='1.2.8', description='Official Bitfinex Python API', long_description='A Python reference implementation of the Bitfinex API for both REST and websocket interaction', long_description_content_type='text/markdown', From ddf0c14cec836fd8408e9e5821517890cb42de55 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Mon, 13 Dec 2021 13:15:56 +0100 Subject: [PATCH 41/68] Adjusted get_trades() to allow symbol to be None and get trades for all symbols --- CHANGELOG | 3 +++ bfxapi/examples/rest/get_authenticated_data.py | 2 +- bfxapi/rest/bfx_rest.py | 4 ++-- bfxapi/version.py | 2 +- setup.py | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index f972440..351bdaa 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +1.3.0 +-) Adjusted get_trades() to allow symbol to be None and get trades for all symbols + 1.2.8 -) Bugfix - It is possible to call bfx.ws.run() from an already running event loop diff --git a/bfxapi/examples/rest/get_authenticated_data.py b/bfxapi/examples/rest/get_authenticated_data.py index 9cf2adc..0efd74f 100644 --- a/bfxapi/examples/rest/get_authenticated_data.py +++ b/bfxapi/examples/rest/get_authenticated_data.py @@ -39,7 +39,7 @@ async def log_active_positions(): [ print (p) for p in positions ] async def log_trades(): - trades = await bfx.rest.get_trades('tBTCUSD', 0, then) + trades = await bfx.rest.get_trades(symbol='tBTCUSD', start=0, end=then) print ("Trades:") [ print (t) for t in trades] diff --git a/bfxapi/rest/bfx_rest.py b/bfxapi/rest/bfx_rest.py index e305666..81fff4a 100644 --- a/bfxapi/rest/bfx_rest.py +++ b/bfxapi/rest/bfx_rest.py @@ -465,7 +465,7 @@ class BfxRest: raw_trades = await self.post(endpoint) return [Trade.from_raw_rest_trade(rt) for rt in raw_trades] - async def get_trades(self, symbol, start, end, limit=25): + async def get_trades(self, start, end, symbol=None, limit=25): """ Get all of the trades between the start and end period associated with API_KEY - Requires authentication. @@ -477,7 +477,7 @@ class BfxRest: @param limit int: max number of items in response @return Array """ - endpoint = "auth/r/trades/{}/hist".format(symbol) + endpoint = "auth/r/trades/{}/hist".format(symbol) if symbol else "auth/r/trades/hist" params = "?start={}&end={}&limit={}".format(start, end, limit) raw_trades = await self.post(endpoint, params=params) return [Trade.from_raw_rest_trade(rt) for rt in raw_trades] diff --git a/bfxapi/version.py b/bfxapi/version.py index e9e4434..e4840b6 100644 --- a/bfxapi/version.py +++ b/bfxapi/version.py @@ -2,4 +2,4 @@ This module contains the current version of the bfxapi lib """ -__version__ = '1.2.8' +__version__ = '1.3.0' diff --git a/setup.py b/setup.py index ffda49f..a5227e7 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from os import path here = path.abspath(path.dirname(__file__)) setup( name='bitfinex-api-py', - version='1.2.8', + version='1.3.0', description='Official Bitfinex Python API', long_description='A Python reference implementation of the Bitfinex API for both REST and websocket interaction', long_description_content_type='text/markdown', From 65a3bec654795025fd1e755106eef75273d695c4 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Tue, 14 Dec 2021 12:05:46 +0100 Subject: [PATCH 42/68] spacing --- CHANGELOG | 3 +++ bfxapi/version.py | 2 +- bfxapi/websockets/generic_websocket.py | 5 ++++- setup.py | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 351bdaa..748b42a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +1.3.1 +-) Handle exception of asyncio.get_event_loop() | Related to v1.2.8 + 1.3.0 -) Adjusted get_trades() to allow symbol to be None and get trades for all symbols diff --git a/bfxapi/version.py b/bfxapi/version.py index e4840b6..880ed5d 100644 --- a/bfxapi/version.py +++ b/bfxapi/version.py @@ -2,4 +2,4 @@ This module contains the current version of the bfxapi lib """ -__version__ = '1.3.0' +__version__ = '1.3.1' diff --git a/bfxapi/websockets/generic_websocket.py b/bfxapi/websockets/generic_websocket.py index dcb39a8..d615754 100644 --- a/bfxapi/websockets/generic_websocket.py +++ b/bfxapi/websockets/generic_websocket.py @@ -84,7 +84,10 @@ class GenericWebsocket: thread and connection. """ self._start_new_socket() - event_loop = asyncio.get_event_loop() + try: + event_loop = asyncio.get_event_loop() + except Exception: + event_loop = None if not event_loop or not event_loop.is_running(): while True: time.sleep(1) diff --git a/setup.py b/setup.py index a5227e7..2098d8e 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from os import path here = path.abspath(path.dirname(__file__)) setup( name='bitfinex-api-py', - version='1.3.0', + version='1.3.1', description='Official Bitfinex Python API', long_description='A Python reference implementation of the Bitfinex API for both REST and websocket interaction', long_description_content_type='text/markdown', From 0a6cc34f8ae602be9469e2571a2c4de1381152a9 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Tue, 14 Dec 2021 12:10:45 +0100 Subject: [PATCH 43/68] Revert "spacing" This reverts commit 65a3bec654795025fd1e755106eef75273d695c4. --- CHANGELOG | 3 --- bfxapi/version.py | 2 +- bfxapi/websockets/generic_websocket.py | 5 +---- setup.py | 2 +- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 748b42a..351bdaa 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,3 @@ -1.3.1 --) Handle exception of asyncio.get_event_loop() | Related to v1.2.8 - 1.3.0 -) Adjusted get_trades() to allow symbol to be None and get trades for all symbols diff --git a/bfxapi/version.py b/bfxapi/version.py index 880ed5d..e4840b6 100644 --- a/bfxapi/version.py +++ b/bfxapi/version.py @@ -2,4 +2,4 @@ This module contains the current version of the bfxapi lib """ -__version__ = '1.3.1' +__version__ = '1.3.0' diff --git a/bfxapi/websockets/generic_websocket.py b/bfxapi/websockets/generic_websocket.py index d615754..dcb39a8 100644 --- a/bfxapi/websockets/generic_websocket.py +++ b/bfxapi/websockets/generic_websocket.py @@ -84,10 +84,7 @@ class GenericWebsocket: thread and connection. """ self._start_new_socket() - try: - event_loop = asyncio.get_event_loop() - except Exception: - event_loop = None + event_loop = asyncio.get_event_loop() if not event_loop or not event_loop.is_running(): while True: time.sleep(1) diff --git a/setup.py b/setup.py index 2098d8e..a5227e7 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from os import path here = path.abspath(path.dirname(__file__)) setup( name='bitfinex-api-py', - version='1.3.1', + version='1.3.0', description='Official Bitfinex Python API', long_description='A Python reference implementation of the Bitfinex API for both REST and websocket interaction', long_description_content_type='text/markdown', From aa5566208b65e025c5af5a822dbc236e77391524 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Tue, 14 Dec 2021 23:11:56 +0100 Subject: [PATCH 44/68] Added bitfinex-pay merchants endpoints --- CHANGELOG | 3 + bfxapi/rest/bfx_rest.py | 130 ++++++++++++++++++++++++++++++++++++++++ bfxapi/version.py | 2 +- setup.py | 2 +- 4 files changed, 135 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 748b42a..d983ec4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +1.3.2 +-) Implemented Merchants endpoints (REST) + 1.3.1 -) Handle exception of asyncio.get_event_loop() | Related to v1.2.8 diff --git a/bfxapi/rest/bfx_rest.py b/bfxapi/rest/bfx_rest.py index 81fff4a..430d72d 100644 --- a/bfxapi/rest/bfx_rest.py +++ b/bfxapi/rest/bfx_rest.py @@ -1109,3 +1109,133 @@ class BfxRest: payload['symbol'] = symbol payload['collateral'] = collateral return await self.post(endpoint, data=payload) + + ################################################## + # Merchants # + ################################################## + + async def submit_invoice(self, amount, currency, pay_currencies, order_id, webhook, redirect_url, customer_info_nationality, + customer_info_resid_country, customer_info_resid_city, customer_info_resid_zip_code, + customer_info_resid_street, customer_info_full_name, customer_info_email, + customer_info_resid_state=None, customer_info_resid_building_no=None, duration=None): + """ + Submit an invoice for payment + + # Attributes + @param amount str: Invoice amount in currency (From 0.1 USD to 1000 USD) + @param currency str: Invoice currency, currently supported: USD + @param pay_currencies list of str: Currencies in which invoice accepts the payments, supported values are BTC, ETH, UST-ETH, UST-TRX, UST-LBT, LNX, LBT + @param order_id str: Reference order identifier in merchant's platform + @param webhook str: The endpoint that will be called once the payment is completed/expired + @param redirect_url str: Merchant redirect URL, this one is used in UI to redirect customer to merchant's site once the payment is completed/expired + @param customer_info_nationality str: Customer's nationality, alpha2 code or full country name (alpha2 preffered) + @param customer_info_resid_country str: Customer's residential country, alpha2 code or full country name (alpha2 preffered) + @param customer_info_resid_city str: Customer's residential city/town + @param customer_info_resid_zip_code str: Customer's residential zip code/postal code + @param customer_info_resid_street str: Customer's residential street address + @param customer_info_full_name str: Customer's full name + @param customer_info_email str: Customer's email address + @param customer_info_resid_state str: Optional, customer's residential state/province + @param customer_info_resid_building_no str: Optional, customer's residential building number/name + @param duration int: Optional, invoice expire time in seconds, minimal duration is 5 mins (300) and maximal duration is 24 hours (86400). Default value is 15 minutes + """ + endpoint = '/auth/w/ext/pay/invoice/create' + payload = { + 'amount': amount, + 'currency': currency, + 'payCurrencies': pay_currencies, + 'orderId': order_id, + 'webhook': webhook, + 'redirectUrl': redirect_url, + 'customerInfo': { + 'nationality': customer_info_nationality, + 'residCountry': customer_info_resid_country, + 'residCity': customer_info_resid_city, + 'residZipCode': customer_info_resid_zip_code, + 'residStreet': customer_info_resid_street, + 'fullName': customer_info_full_name, + 'email': customer_info_email + }, + 'duration': duration + } + + if customer_info_resid_state: + payload['customerInfo']['residState'] = customer_info_resid_state + + if customer_info_resid_building_no: + payload['customerInfo']['residBuildingNo'] = customer_info_resid_building_no + + return await self.post(endpoint, data=payload) + + async def get_invoices(self, id=None, start=None, end=None, limit=10): + """ + List submitted invoices + + # Attributes + @param id str: Unique invoice identifier + @param start int: Millisecond start time + @param end int: Millisecond end time + @param limit int: Millisecond start time + """ + endpoint = '/auth/w/ext/pay/invoices' + payload = {} + + if id: + payload['id'] = id + + if start: + payload['start'] = start + + if end: + payload['end'] = end + + if limit: + payload['limit'] = limit + + return await self.post(endpoint, data=payload) + + async def complete_invoice(self, id, pay_ccy, deposit_id=None, ledger_id=None): + """ + Manually complete an invoice + + # Attributes + @param id str: Unique invoice identifier + @param pay_ccy str: Paid invoice currency, should be one of values under payCurrencies field on invoice + @param deposit_id int: Movement/Deposit Id linked to invoice as payment + @param ledger_id int: Ledger entry Id linked to invoice as payment, use either depositId or ledgerId + """ + endpoint = '/auth/w/ext/pay/invoice/complete' + payload = { + 'id': id, + 'payCcy': pay_ccy + } + + if deposit_id: + payload['depositId'] = deposit_id + + if ledger_id: + payload['ledgerId'] = ledger_id + + return await self.post(endpoint, data=payload) + + async def get_unlinked_deposits(self, ccy, start=None, end=None): + """ + Retrieve deposits that possibly could be linked to bitfinex pay invoices + + # Attributes + @param ccy str: Pay currency to search deposits for, supported values are: BTC, ETH, UST-ETH, UST-TRX, UST-LBT, LNX, LBT + @param start int: Millisecond start time + @param end int: Millisecond end time + """ + endpoint = '/auth/w/ext/pay/deposits/unlinked' + payload = { + 'ccy': ccy + } + + if start: + payload['start'] = start + + if end: + payload['end'] = end + + return await self.post(endpoint, data=payload) diff --git a/bfxapi/version.py b/bfxapi/version.py index 880ed5d..51534a7 100644 --- a/bfxapi/version.py +++ b/bfxapi/version.py @@ -2,4 +2,4 @@ This module contains the current version of the bfxapi lib """ -__version__ = '1.3.1' +__version__ = '1.3.2' diff --git a/setup.py b/setup.py index 2098d8e..3444a25 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from os import path here = path.abspath(path.dirname(__file__)) setup( name='bitfinex-api-py', - version='1.3.1', + version='1.3.2', description='Official Bitfinex Python API', long_description='A Python reference implementation of the Bitfinex API for both REST and websocket interaction', long_description_content_type='text/markdown', From 35ee037eccc94c522960627cc947421240a790d9 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Wed, 15 Dec 2021 13:47:35 +0100 Subject: [PATCH 45/68] added merchant example | fixed endpoints --- bfxapi/examples/rest/merchant.py | 33 ++++++++++++++++++++++++++++++++ bfxapi/rest/bfx_rest.py | 8 ++++---- 2 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 bfxapi/examples/rest/merchant.py diff --git a/bfxapi/examples/rest/merchant.py b/bfxapi/examples/rest/merchant.py new file mode 100644 index 0000000..91521fa --- /dev/null +++ b/bfxapi/examples/rest/merchant.py @@ -0,0 +1,33 @@ +import os +import sys +import asyncio +sys.path.append('../../../') +from bfxapi import Client + +API_KEY=os.getenv("BFX_KEY") +API_SECRET=os.getenv("BFX_SECRET") + +bfx = Client( + API_KEY=API_KEY, + API_SECRET=API_SECRET, + logLevel='DEBUG' +) + +async def run(): + await bfx.rest.submit_invoice(amount='2.0', currency='USD', pay_currencies=['BTC', 'ETH'], order_id='order123', webhook='https://example.com/api/v3/order/order123', + redirect_url='https://example.com/api/v3/order/order123', customer_info_nationality='DE', + customer_info_resid_country='GB', customer_info_resid_city='London', customer_info_resid_zip_code='WC2H 7NA', + customer_info_resid_street='5-6 Leicester Square', customer_info_resid_building_no='23 A', + customer_info_full_name='John Doe', customer_info_email='john@example.com', duration=86339) + + invoices = await bfx.rest.get_invoices() + print(invoices) + + # await bfx.rest.complete_invoice(id=invoices[0]['id'], pay_ccy='BTC', deposit_id=1357996) + + unlinked_deposits = await bfx.rest.get_unlinked_deposits(ccy='BTC') + print(unlinked_deposits) + + +t = asyncio.ensure_future(run()) +asyncio.get_event_loop().run_until_complete(t) diff --git a/bfxapi/rest/bfx_rest.py b/bfxapi/rest/bfx_rest.py index 430d72d..a2a104e 100644 --- a/bfxapi/rest/bfx_rest.py +++ b/bfxapi/rest/bfx_rest.py @@ -1139,7 +1139,7 @@ class BfxRest: @param customer_info_resid_building_no str: Optional, customer's residential building number/name @param duration int: Optional, invoice expire time in seconds, minimal duration is 5 mins (300) and maximal duration is 24 hours (86400). Default value is 15 minutes """ - endpoint = '/auth/w/ext/pay/invoice/create' + endpoint = 'auth/w/ext/pay/invoice/create' payload = { 'amount': amount, 'currency': currency, @@ -1177,7 +1177,7 @@ class BfxRest: @param end int: Millisecond end time @param limit int: Millisecond start time """ - endpoint = '/auth/w/ext/pay/invoices' + endpoint = 'auth/r/ext/pay/invoices' payload = {} if id: @@ -1204,7 +1204,7 @@ class BfxRest: @param deposit_id int: Movement/Deposit Id linked to invoice as payment @param ledger_id int: Ledger entry Id linked to invoice as payment, use either depositId or ledgerId """ - endpoint = '/auth/w/ext/pay/invoice/complete' + endpoint = 'auth/w/ext/pay/invoice/complete' payload = { 'id': id, 'payCcy': pay_ccy @@ -1227,7 +1227,7 @@ class BfxRest: @param start int: Millisecond start time @param end int: Millisecond end time """ - endpoint = '/auth/w/ext/pay/deposits/unlinked' + endpoint = 'auth/r/ext/pay/deposits/unlinked' payload = { 'ccy': ccy } From 7f47405fa36b092cb5be040ed0f4cce4c6c8b57d Mon Sep 17 00:00:00 2001 From: itsdeka Date: Mon, 20 Dec 2021 12:17:27 +0100 Subject: [PATCH 46/68] Fixed socket.send() issue (IndexError: deque index out of range) --- CHANGELOG | 3 +++ bfxapi/utils/decorators.py | 12 ++++++++++++ bfxapi/version.py | 2 +- bfxapi/websockets/bfx_websocket.py | 4 ++++ setup.py | 2 +- 5 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 bfxapi/utils/decorators.py diff --git a/CHANGELOG b/CHANGELOG index d983ec4..5f5e657 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +1.3.3 +-) Fixed socket.send() issue (IndexError: deque index out of range) + 1.3.2 -) Implemented Merchants endpoints (REST) diff --git a/bfxapi/utils/decorators.py b/bfxapi/utils/decorators.py new file mode 100644 index 0000000..b226edc --- /dev/null +++ b/bfxapi/utils/decorators.py @@ -0,0 +1,12 @@ +from ..utils.custom_logger import CustomLogger + + +def handle_failure(func): + def inner_function(*args, **kwargs): + logger = CustomLogger('BfxWebsocket', logLevel="DEBUG") + try: + func(*args, **kwargs) + except Exception as exception_message: + logger.error(exception_message) + + return inner_function diff --git a/bfxapi/version.py b/bfxapi/version.py index 51534a7..85f1fa2 100644 --- a/bfxapi/version.py +++ b/bfxapi/version.py @@ -2,4 +2,4 @@ This module contains the current version of the bfxapi lib """ -__version__ = '1.3.2' +__version__ = '1.3.3' diff --git a/bfxapi/websockets/bfx_websocket.py b/bfxapi/websockets/bfx_websocket.py index 49febc5..ce5660e 100644 --- a/bfxapi/websockets/bfx_websocket.py +++ b/bfxapi/websockets/bfx_websocket.py @@ -12,6 +12,7 @@ from .subscription_manager import SubscriptionManager from .wallet_manager import WalletManager from .order_manager import OrderManager from ..utils.auth import generate_auth_payload +from ..utils.decorators import handle_failure from ..models import Order, Trade, OrderBook, Ticker, FundingTicker @@ -481,6 +482,7 @@ class BfxWebsocket(GenericWebsocket): else: self.logger.warn('Unknown (socketId={}) websocket response: {}'.format(socketId, msg)) + @handle_failure async def _ws_authenticate_socket(self, socketId): socket = self.sockets[socketId] socket.set_authenticated() @@ -507,6 +509,7 @@ class BfxWebsocket(GenericWebsocket): # re-subscribe to existing channels await self.subscriptionManager.resubscribe_by_socket(socket_id) + @handle_failure async def _send_auth_command(self, channel_name, data): payload = [0, channel_name, None, data] socket = self.get_authenticated_socket() @@ -538,6 +541,7 @@ class BfxWebsocket(GenericWebsocket): total += self.get_socket_capacity(socketId) return total + @handle_failure async def enable_flag(self, flag): """ Enable flag on websocket connection diff --git a/setup.py b/setup.py index 3444a25..32fad42 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from os import path here = path.abspath(path.dirname(__file__)) setup( name='bitfinex-api-py', - version='1.3.2', + version='1.3.3', description='Official Bitfinex Python API', long_description='A Python reference implementation of the Bitfinex API for both REST and websocket interaction', long_description_content_type='text/markdown', From ae234b60662615abe12ec61421c43020349ba767 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Mon, 20 Dec 2021 12:41:33 +0100 Subject: [PATCH 47/68] async fix --- bfxapi/utils/decorators.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bfxapi/utils/decorators.py b/bfxapi/utils/decorators.py index b226edc..3a4fe9a 100644 --- a/bfxapi/utils/decorators.py +++ b/bfxapi/utils/decorators.py @@ -2,10 +2,10 @@ from ..utils.custom_logger import CustomLogger def handle_failure(func): - def inner_function(*args, **kwargs): + async def inner_function(*args, **kwargs): logger = CustomLogger('BfxWebsocket', logLevel="DEBUG") try: - func(*args, **kwargs) + await func(*args, **kwargs) except Exception as exception_message: logger.error(exception_message) From a6670caf5ee04800d0d2728e4ea26a1ac390ea4d Mon Sep 17 00:00:00 2001 From: itsdeka Date: Tue, 4 Jan 2022 17:30:32 +0100 Subject: [PATCH 48/68] Avoid p_sub not initialized issue Thanks to @nkasimova (https://github.com/bitfinexcom/bitfinex-api-py/pull/170) --- bfxapi/websockets/subscription_manager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bfxapi/websockets/subscription_manager.py b/bfxapi/websockets/subscription_manager.py index c90c073..b516867 100644 --- a/bfxapi/websockets/subscription_manager.py +++ b/bfxapi/websockets/subscription_manager.py @@ -62,6 +62,7 @@ class SubscriptionManager: channel = raw_ws_data.get("channel") chan_id = raw_ws_data.get("chanId") key = raw_ws_data.get("key", None) + p_sub = None get_key = "{}_{}".format(channel, key or symbol) if chan_id in self.subscriptions_chanid: # subscription has already existed in the past From 64d203d8a82f41f879c843a2471d43d5ab69e2c7 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Tue, 4 Jan 2022 17:42:15 +0100 Subject: [PATCH 49/68] Add submit cancel all funding offer Thanks to @charlychiu (https://github.com/bitfinexcom/bitfinex-api-py/pull/169) --- CHANGELOG | 4 ++++ bfxapi/rest/bfx_rest.py | 11 +++++++++++ bfxapi/version.py | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 5f5e657..5cfd0ec 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +1.3.4 +-) Fixed undefined p_sub issue in subscription_manager.py +-) Added submit cancel all funding orders endpoint (REST) + 1.3.3 -) Fixed socket.send() issue (IndexError: deque index out of range) diff --git a/bfxapi/rest/bfx_rest.py b/bfxapi/rest/bfx_rest.py index a2a104e..c29707f 100644 --- a/bfxapi/rest/bfx_rest.py +++ b/bfxapi/rest/bfx_rest.py @@ -636,6 +636,17 @@ class BfxRest: raw_notification = await self.post(endpoint, {'id': fundingId}) return Notification.from_raw_notification(raw_notification) + async def submit_cancel_all_funding_offer(self, currency): + """ + Cancel all funding offers at once + + # Attributes + @param currency str: currency for which to cancel all offers (USD, BTC, UST ...) + """ + endpoint = "auth/w/funding/offer/cancel/all" + raw_notification = await self.post(endpoint, {'currency': currency}) + return Notification.from_raw_notification(raw_notification) + async def keep_funding(self, type, id): """ Toggle to keep funding taken. Specify loan for unused funding and credit for used funding. diff --git a/bfxapi/version.py b/bfxapi/version.py index 85f1fa2..bceabe3 100644 --- a/bfxapi/version.py +++ b/bfxapi/version.py @@ -2,4 +2,4 @@ This module contains the current version of the bfxapi lib """ -__version__ = '1.3.3' +__version__ = '1.3.4' From 7fff1038af6c06e8ca80045e613fc496066237f2 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Tue, 4 Jan 2022 17:58:10 +0100 Subject: [PATCH 50/68] Added get all exchange pairs endpoint (REST) --- CHANGELOG | 1 + bfxapi/rest/bfx_rest.py | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 5cfd0ec..3c1f98a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ 1.3.4 -) Fixed undefined p_sub issue in subscription_manager.py -) Added submit cancel all funding orders endpoint (REST) +-) Added get all exchange pairs endpoint (REST) 1.3.3 -) Fixed socket.send() issue (IndexError: deque index out of range) diff --git a/bfxapi/rest/bfx_rest.py b/bfxapi/rest/bfx_rest.py index c29707f..5cfc97a 100644 --- a/bfxapi/rest/bfx_rest.py +++ b/bfxapi/rest/bfx_rest.py @@ -370,6 +370,15 @@ class BfxRest: stats = await self.fetch(endpoint) return stats + async def get_conf_list_pair_exchange(self): + """ + Get list of available exchange pairs + # Attributes + @return Array [ SYMBOL ] + """ + endpoint = "conf/pub:list:pair:exchange" + pairs = await self.fetch(endpoint) + return pairs ################################################## # Authenticated Data # From bfb9ccb7fb7d7e6b1de355698e2e542eb1add0ee Mon Sep 17 00:00:00 2001 From: itsdeka Date: Tue, 4 Jan 2022 20:48:37 +0100 Subject: [PATCH 51/68] updated version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 32fad42..1d2cfce 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from os import path here = path.abspath(path.dirname(__file__)) setup( name='bitfinex-api-py', - version='1.3.3', + version='1.3.4', description='Official Bitfinex Python API', long_description='A Python reference implementation of the Bitfinex API for both REST and websocket interaction', long_description_content_type='text/markdown', From a79cd6a5a6078cebe2116c45b70d33ef61667391 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Sun, 16 Jan 2022 22:15:13 +0100 Subject: [PATCH 52/68] Implemented Movement endpoints (REST) Thanks to @ph4z --- CHANGELOG | 3 +++ bfxapi/__init__.py | 3 ++- bfxapi/models/__init__.py | 1 + bfxapi/rest/bfx_rest.py | 16 ++++++++++++++++ bfxapi/version.py | 2 +- setup.py | 2 +- 6 files changed, 24 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 3c1f98a..3119820 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +1.3.5 +-) Implemented Movement endpoints (REST) + 1.3.4 -) Fixed undefined p_sub issue in subscription_manager.py -) Added submit cancel all funding orders endpoint (REST) diff --git a/bfxapi/__init__.py b/bfxapi/__init__.py index f5fbc80..4ad4f80 100644 --- a/bfxapi/__init__.py +++ b/bfxapi/__init__.py @@ -5,7 +5,8 @@ This module is used to interact with the bitfinex api from .version import __version__ from .client import Client from .models import (Order, Trade, OrderBook, Subscription, Wallet, - Position, FundingLoan, FundingOffer, FundingCredit) + Position, FundingLoan, FundingOffer, FundingCredit, + Movement) from .websockets.generic_websocket import GenericWebsocket, Socket from .websockets.bfx_websocket import BfxWebsocket from .utils.decimal import Decimal diff --git a/bfxapi/models/__init__.py b/bfxapi/models/__init__.py index b9b4b17..c7bf7b2 100644 --- a/bfxapi/models/__init__.py +++ b/bfxapi/models/__init__.py @@ -22,5 +22,6 @@ from .ledger import Ledger from .funding_trade import FundingTrade from .margin_info import MarginInfo from .margin_info_base import MarginInfoBase +from .movement import Movement NAME = "models" diff --git a/bfxapi/rest/bfx_rest.py b/bfxapi/rest/bfx_rest.py index 5cfc97a..b1332b1 100644 --- a/bfxapi/rest/bfx_rest.py +++ b/bfxapi/rest/bfx_rest.py @@ -609,6 +609,22 @@ class BfxRest: raw_ledgers = await self.post(endpoint, params=params) return [Ledger.from_raw_ledger(rl) for rl in raw_ledgers] + async def get_movement_history(self, currency, start="", end="", limit=25): + """ + Get all of the deposits and withdraws between the start and end period associated with API_KEY + - Requires authentication. + # Attributes + @param currency string: pair symbol i.e BTC + @param start int: millisecond start time + @param end int: millisecond end time + @param limit int: max number of items in response + @return Array + """ + endpoint = "auth/r/movements/{}/hist".format(currency) + params = "?start={}&end={}&limit={}".format(start, end, limit) + raw_movements = await self.post(endpoint, params=params) + return [Movement.from_raw_movement(rm) for rm in raw_movements] + async def submit_funding_offer(self, symbol, amount, rate, period, funding_type=FundingOffer.Type.LIMIT, hidden=False): """ diff --git a/bfxapi/version.py b/bfxapi/version.py index bceabe3..62a74a7 100644 --- a/bfxapi/version.py +++ b/bfxapi/version.py @@ -2,4 +2,4 @@ This module contains the current version of the bfxapi lib """ -__version__ = '1.3.4' +__version__ = '1.3.5' diff --git a/setup.py b/setup.py index 1d2cfce..dfcaf4a 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from os import path here = path.abspath(path.dirname(__file__)) setup( name='bitfinex-api-py', - version='1.3.4', + version='1.3.5', description='Official Bitfinex Python API', long_description='A Python reference implementation of the Bitfinex API for both REST and websocket interaction', long_description_content_type='text/markdown', From a589fe9bd1ab86d8b66320ba909509107102c9b7 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Sun, 16 Jan 2022 22:18:03 +0100 Subject: [PATCH 53/68] Implemented Movement endpoints (REST) Thanks to @ph4z --- bfxapi/models/movement.py | 76 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 bfxapi/models/movement.py diff --git a/bfxapi/models/movement.py b/bfxapi/models/movement.py new file mode 100644 index 0000000..d212e72 --- /dev/null +++ b/bfxapi/models/movement.py @@ -0,0 +1,76 @@ +""" +Module used to describe movement data types +""" + +import time +import datetime + +class MovementModel: + """ + Enum used index the different values in a raw movement array + """ + + ID = 0 + CURRENCY = 1 + CURRENCY_NAME = 2 + MTS_STARTED = 5 + MTS_UPDATED = 6 + STATUS = 9 + AMOUNT = 12 + FEES = 13 + DESTINATION_ADDRESS = 16 + TRANSACTION_ID = 20 + +class Movement: + + """ + ID String Movement identifier + CURRENCY String The symbol of the currency (ex. "BTC") + CURRENCY_NAME String The extended name of the currency (ex. "BITCOIN") + MTS_STARTED Date Movement started at + MTS_UPDATED Date Movement last updated at + STATUS String Current status + AMOUNT String Amount of funds moved + FEES String Tx Fees applied + DESTINATION_ADDRESS String Destination address + TRANSACTION_ID String Transaction identifier + """ + + def __init__(self, mid, currency, mts_started, mts_updated, status, amount, fees, dst_address, tx_id): + self.id = mid + self.currency = currency + self.mts_started = mts_started + self.mts_updated = mts_updated + self.status = status + self.amount = amount + self.fees = fees + self.dst_address = dst_address + self.tx_id = tx_id + + self.date = datetime.datetime.fromtimestamp(mts_started/1000.0) + + + @staticmethod + def from_raw_movement(raw_movement): + """ + Parse a raw movement object into a Movement object + @return Movement + """ + + mid = raw_movement[MovementModel.ID] + currency = raw_movement[MovementModel.CURRENCY] + mts_started = raw_movement[MovementModel.MTS_STARTED] + mts_updated = raw_movement[MovementModel.MTS_UPDATED] + status = raw_movement[MovementModel.STATUS] + amount = raw_movement[MovementModel.AMOUNT] + fees = raw_movement[MovementModel.FEES] + dst_address = raw_movement[MovementModel.DESTINATION_ADDRESS] + tx_id = raw_movement[MovementModel.TRANSACTION_ID] + + return Movement(mid, currency, mts_started, mts_updated, status, amount, fees, dst_address, tx_id) + + def __str__(self): + ''' Allow us to print the Movement object in a pretty format ''' + text = "Movement <'{}' amount={} fees={} mts_created={} mts_updated={} status='{}' destination_address={} transaction_id={}>" + return text.format(self.currency, self.amount, self.fees, + self.mts_started, self.mts_updated, self.status, self.dst_address, self.tx_id) \ No newline at end of file From 767735bf621b8745f8b09a88ca98e91fe18f02e1 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Sun, 16 Jan 2022 22:20:27 +0100 Subject: [PATCH 54/68] added missing imported model Movement --- bfxapi/rest/bfx_rest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bfxapi/rest/bfx_rest.py b/bfxapi/rest/bfx_rest.py index b1332b1..e2b0c90 100644 --- a/bfxapi/rest/bfx_rest.py +++ b/bfxapi/rest/bfx_rest.py @@ -11,7 +11,7 @@ import datetime from ..utils.custom_logger import CustomLogger from ..utils.auth import generate_auth_headers, calculate_order_flags, gen_unique_cid from ..models import Wallet, Order, Position, Trade, FundingLoan, FundingOffer, FundingTrade, MarginInfoBase, MarginInfo -from ..models import FundingCredit, Notification, Ledger +from ..models import FundingCredit, Notification, Ledger, Movement class BfxRest: From f2d83fefbc32821f5f9eae094f516e2f97714edb Mon Sep 17 00:00:00 2001 From: itsdeka Date: Thu, 20 Jan 2022 12:41:37 +0100 Subject: [PATCH 55/68] stop wasn't awaited (now it is) thanks to @mgfreixa --- bfxapi/websockets/generic_websocket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bfxapi/websockets/generic_websocket.py b/bfxapi/websockets/generic_websocket.py index dcb39a8..3530ff4 100644 --- a/bfxapi/websockets/generic_websocket.py +++ b/bfxapi/websockets/generic_websocket.py @@ -208,7 +208,7 @@ class GenericWebsocket: """ This is used by the HF data server. """ - self.stop() + await self.stop() async def on_open(self): """ From 793fda9d6716ebc06eedb88c378e32802abc3bd7 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Thu, 20 Jan 2022 13:35:36 +0100 Subject: [PATCH 56/68] updated version changed account's trade execution (te) and trade update (tu) handling --- CHANGELOG | 5 +++ bfxapi/version.py | 2 +- bfxapi/websockets/bfx_websocket.py | 54 +++++++++++++++++++++--------- setup.py | 2 +- 4 files changed, 45 insertions(+), 18 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 3119820..aab6ecd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +2.0.0 +-) Implemented Movement endpoints (REST) +-) Fixed unawaited stop +-) Changed account's trade execution (te) and trade update (tu) handling + 1.3.5 -) Implemented Movement endpoints (REST) diff --git a/bfxapi/version.py b/bfxapi/version.py index 62a74a7..b16d0cf 100644 --- a/bfxapi/version.py +++ b/bfxapi/version.py @@ -2,4 +2,4 @@ This module contains the current version of the bfxapi lib """ -__version__ = '1.3.5' +__version__ = '2.0.0' diff --git a/bfxapi/websockets/bfx_websocket.py b/bfxapi/websockets/bfx_websocket.py index ce5660e..41aff69 100644 --- a/bfxapi/websockets/bfx_websocket.py +++ b/bfxapi/websockets/bfx_websocket.py @@ -66,6 +66,37 @@ def _parse_trade(tData, symbol): 'symbol': symbol } +def _parse_account_trade(tData): + return { + 'id': tData[0], + 'symbol': tData[1], + 'mts_create': tData[2], + 'order_id': tData[3], + 'exec_amount': tData[4], + 'exec_price': tData[5], + 'order_type': tData[6], + 'order_price': tData[7], + 'maker': tData[8], + 'cid': tData[11], + } + + +def _parse_account_trade_update(tData): + return { + 'id': tData[0], + 'symbol': tData[1], + 'mts_create': tData[2], + 'order_id': tData[3], + 'exec_amount': tData[4], + 'exec_price': tData[5], + 'order_type': tData[6], + 'order_price': tData[7], + 'maker': tData[8], + 'fee': tData[9], + 'fee_currency': tData[10], + 'cid': tData[11], + } + def _parse_deriv_status_update(sData, symbol): return { 'symbol': symbol, @@ -278,19 +309,15 @@ class BfxWebsocket(GenericWebsocket): async def _trade_update_handler(self, data): tData = data[2] - # [209, 'tu', [312372989, 1542303108930, 0.35, 5688.61834032]] - if self.subscriptionManager.is_subscribed(data[0]): - symbol = self.subscriptionManager.get(data[0]).symbol - tradeObj = _parse_trade(tData, symbol) - self._emit('trade_update', tradeObj) + # [0,"tu",[738045455,"tTESTBTC:TESTUSD",1622169615771,66635385225,0.001,38175,"EXCHANGE LIMIT",39000,-1,-0.000002,"TESTBTC",1622169615685]] + tradeObj = _parse_account_trade_update(tData) + self._emit('trade_update', tradeObj) async def _trade_executed_handler(self, data): tData = data[2] - # [209, 'te', [312372989, 1542303108930, 0.35, 5688.61834032]] - if self.subscriptionManager.is_subscribed(data[0]): - symbol = self.subscriptionManager.get(data[0]).symbol - tradeObj = _parse_trade(tData, symbol) - self._emit('new_trade', tradeObj) + # [0,"te",[738045455,"tTESTBTC:TESTUSD",1622169615771,66635385225,0.001,38175,"EXCHANGE LIMIT",39000,-1,null,null,1622169615685]] + tradeObj = _parse_account_trade(tData) + self._emit('new_trade', tradeObj) async def _wallet_update_handler(self, data): # [0,"wu",["exchange","USD",89134.66933283,0]] @@ -409,12 +436,7 @@ class BfxWebsocket(GenericWebsocket): # connection data.reverse() for t in data: - trade = { - 'mts': t[1], - 'amount': t[2], - 'price': t[3], - 'symbol': symbol - } + trade = _parse_trade(t, symbol) self._emit('seed_trade', trade) async def _candle_handler(self, data): diff --git a/setup.py b/setup.py index dfcaf4a..1223b29 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from os import path here = path.abspath(path.dirname(__file__)) setup( name='bitfinex-api-py', - version='1.3.5', + version='2.0.0', description='Official Bitfinex Python API', long_description='A Python reference implementation of the Bitfinex API for both REST and websocket interaction', long_description_content_type='text/markdown', From 04ef7525232ad391c563619f7087b0a3ce9db2c8 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Thu, 20 Jan 2022 13:36:26 +0100 Subject: [PATCH 57/68] adjusted CHANGELOG --- CHANGELOG | 3 --- 1 file changed, 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index aab6ecd..0ce9902 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,9 +3,6 @@ -) Fixed unawaited stop -) Changed account's trade execution (te) and trade update (tu) handling -1.3.5 --) Implemented Movement endpoints (REST) - 1.3.4 -) Fixed undefined p_sub issue in subscription_manager.py -) Added submit cancel all funding orders endpoint (REST) From 52a2d763791aea7a282fbbc53f1a443caa870a16 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Sun, 23 Jan 2022 22:33:10 +0100 Subject: [PATCH 58/68] -) Added User Settings Write/Read/Delete endpoints (REST) -) Added Balance Available for Orders/Offers endpoint (REST) --- CHANGELOG | 4 +++ bfxapi/rest/bfx_rest.py | 72 +++++++++++++++++++++++++++++++++++++++++ bfxapi/version.py | 2 +- setup.py | 2 +- 4 files changed, 78 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 0ce9902..dc27799 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +2.0.1 +-) Added User Settings Write/Read/Delete endpoints (REST) +-) Added Balance Available for Orders/Offers endpoint (REST) + 2.0.0 -) Implemented Movement endpoints (REST) -) Fixed unawaited stop diff --git a/bfxapi/rest/bfx_rest.py b/bfxapi/rest/bfx_rest.py index e2b0c90..1339db1 100644 --- a/bfxapi/rest/bfx_rest.py +++ b/bfxapi/rest/bfx_rest.py @@ -1005,6 +1005,78 @@ class BfxRest: message = await self.post(endpoint, payload) return message + async def calc_order_avail(self, symbol, type, lev, dir=None, rate=None): + """ + Calculate the balance available for orders/offers + + # Attributes + @param symbol str: Symbol (tBTCUSD, tBTCUST, fUSD, .... ) + @param dir int: Direction of the order (1 for by, -1 for sell) (Mandator for EXCHANGE and MARGIN type, not used for FUNDING) + @param rate str: Order price (Mandator for EXCHANGE and MARGIN type, not used for FUNDING) + @param type str: Type of the order/offer EXCHANGE, MARGIN, DERIV, or FUNDING + @param lev str: Leverage that you want to use in calculating the max order amount (DERIV only) + """ + endpoint = f"auth/calc/order/avail" + payload = { + "symbol": symbol, + "type": type, + "lev": lev + } + + if dir: + payload["dir"] = dir + + if rate: + payload["rate"] = rate + + message = await self.post(endpoint, payload) + return message + + async def write_user_settings(self, settings): + """ + Allows you to create custom settings by creating key: value pairs + + # Attributes + @param Settings object: object of keys and values to be set. Must follow regex pattern /^api:[A-Za-z0-9_-]*$/ + """ + endpoint = f"auth/w/settings/set" + payload = { + "Settings": settings + } + + message = await self.post(endpoint, payload) + return message + + async def read_user_settings(self, keys): + """ + Allows you to read custom settings by providing a key + + # Attributes + @param Keys array: the keys for which you wish to retrieve the values + """ + endpoint = f"auth/w/settings" + payload = { + "Keys": keys + } + + message = await self.post(endpoint, payload) + return message + + async def delete_user_settings(self, settings): + """ + Allows you to delete custom settings + + # Attributes + @param settings object: object of keys to be deleted followed by value 1. Must follow regex pattern /^api:[A-Za-z0-9_-]*$/ + """ + endpoint = f"auth/w/settings/del" + payload = { + "Settings": settings + } + + message = await self.post(endpoint, payload) + return message + async def get_auth_pulse_hist(self, is_public=None): """ Allows you to retrieve your private pulse history or the public pulse history with an additional UID_LIKED field. diff --git a/bfxapi/version.py b/bfxapi/version.py index b16d0cf..f5a20c1 100644 --- a/bfxapi/version.py +++ b/bfxapi/version.py @@ -2,4 +2,4 @@ This module contains the current version of the bfxapi lib """ -__version__ = '2.0.0' +__version__ = '2.0.1' diff --git a/setup.py b/setup.py index 1223b29..af19bde 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from os import path here = path.abspath(path.dirname(__file__)) setup( name='bitfinex-api-py', - version='2.0.0', + version='2.0.1', description='Official Bitfinex Python API', long_description='A Python reference implementation of the Bitfinex API for both REST and websocket interaction', long_description_content_type='text/markdown', From 8ed2e51228e9fc7fb549c58953d98d12273a12f8 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Tue, 25 Jan 2022 14:42:34 +0100 Subject: [PATCH 59/68] added alerts endpoints --- CHANGELOG | 1 + bfxapi/rest/bfx_rest.py | 42 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index dc27799..f84b02e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ 2.0.1 -) Added User Settings Write/Read/Delete endpoints (REST) -) Added Balance Available for Orders/Offers endpoint (REST) +-) Added Alerts endpoints (REST) 2.0.0 -) Implemented Movement endpoints (REST) diff --git a/bfxapi/rest/bfx_rest.py b/bfxapi/rest/bfx_rest.py index 1339db1..4477871 100644 --- a/bfxapi/rest/bfx_rest.py +++ b/bfxapi/rest/bfx_rest.py @@ -1005,6 +1005,48 @@ class BfxRest: message = await self.post(endpoint, payload) return message + async def get_alerts(self): + """ + Retrieve a list of active price alerts + """ + endpoint = f"auth/r/alerts" + + message = await self.post(endpoint, {}) + return message + + async def set_alert(self, type, symbol, price): + """ + Sets up a price alert at the given value + + # Attributes + @param type string + @param symbol string + @param price float + """ + endpoint = f"auth/w/alert/set" + payload = { + "Settings": settings + } + + message = await self.post(endpoint, payload) + return message + + async def delete_alert(self, symbol, price): + """ + Delete an active alert + + # Attributes + @param symbol string + @param price float + """ + endpoint = f"auth/w/alert/price:{symbol}:{price}/del" + payload = { + "Settings": settings + } + + message = await self.post(endpoint, payload) + return message + async def calc_order_avail(self, symbol, type, lev, dir=None, rate=None): """ Calculate the balance available for orders/offers From 87cc56440ab23005100ac9577f630b8d0d7cb9df Mon Sep 17 00:00:00 2001 From: itsdeka Date: Tue, 25 Jan 2022 14:45:22 +0100 Subject: [PATCH 60/68] fixed payloads --- bfxapi/rest/bfx_rest.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bfxapi/rest/bfx_rest.py b/bfxapi/rest/bfx_rest.py index 4477871..b276f13 100644 --- a/bfxapi/rest/bfx_rest.py +++ b/bfxapi/rest/bfx_rest.py @@ -1025,7 +1025,9 @@ class BfxRest: """ endpoint = f"auth/w/alert/set" payload = { - "Settings": settings + "type": type, + "symbol": symbol, + "price": price } message = await self.post(endpoint, payload) @@ -1041,7 +1043,8 @@ class BfxRest: """ endpoint = f"auth/w/alert/price:{symbol}:{price}/del" payload = { - "Settings": settings + "symbol": symbol, + "price": price } message = await self.post(endpoint, payload) From d3c105a710f4c4058a36a6ac68d7122d39f86531 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Sat, 29 Jan 2022 22:32:17 +0100 Subject: [PATCH 61/68] fixed trades handling error --- CHANGELOG | 1 + bfxapi/websockets/bfx_websocket.py | 53 +++++++++--------------------- 2 files changed, 17 insertions(+), 37 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index f84b02e..69a4b97 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,7 @@ -) Added User Settings Write/Read/Delete endpoints (REST) -) Added Balance Available for Orders/Offers endpoint (REST) -) Added Alerts endpoints (REST) +-) Fixed trades handling error 2.0.0 -) Implemented Movement endpoints (REST) diff --git a/bfxapi/websockets/bfx_websocket.py b/bfxapi/websockets/bfx_websocket.py index 41aff69..6df4487 100644 --- a/bfxapi/websockets/bfx_websocket.py +++ b/bfxapi/websockets/bfx_websocket.py @@ -66,36 +66,6 @@ def _parse_trade(tData, symbol): 'symbol': symbol } -def _parse_account_trade(tData): - return { - 'id': tData[0], - 'symbol': tData[1], - 'mts_create': tData[2], - 'order_id': tData[3], - 'exec_amount': tData[4], - 'exec_price': tData[5], - 'order_type': tData[6], - 'order_price': tData[7], - 'maker': tData[8], - 'cid': tData[11], - } - - -def _parse_account_trade_update(tData): - return { - 'id': tData[0], - 'symbol': tData[1], - 'mts_create': tData[2], - 'order_id': tData[3], - 'exec_amount': tData[4], - 'exec_price': tData[5], - 'order_type': tData[6], - 'order_price': tData[7], - 'maker': tData[8], - 'fee': tData[9], - 'fee_currency': tData[10], - 'cid': tData[11], - } def _parse_deriv_status_update(sData, symbol): return { @@ -309,15 +279,19 @@ class BfxWebsocket(GenericWebsocket): async def _trade_update_handler(self, data): tData = data[2] - # [0,"tu",[738045455,"tTESTBTC:TESTUSD",1622169615771,66635385225,0.001,38175,"EXCHANGE LIMIT",39000,-1,-0.000002,"TESTBTC",1622169615685]] - tradeObj = _parse_account_trade_update(tData) - self._emit('trade_update', tradeObj) + # [209, 'tu', [312372989, 1542303108930, 0.35, 5688.61834032]] + if self.subscriptionManager.is_subscribed(data[0]): + symbol = self.subscriptionManager.get(data[0]).symbol + tradeObj = _parse_trade(tData, symbol) + self._emit('trade_update', tradeObj) async def _trade_executed_handler(self, data): tData = data[2] - # [0,"te",[738045455,"tTESTBTC:TESTUSD",1622169615771,66635385225,0.001,38175,"EXCHANGE LIMIT",39000,-1,null,null,1622169615685]] - tradeObj = _parse_account_trade(tData) - self._emit('new_trade', tradeObj) + # [209, 'te', [312372989, 1542303108930, 0.35, 5688.61834032]] + if self.subscriptionManager.is_subscribed(data[0]): + symbol = self.subscriptionManager.get(data[0]).symbol + tradeObj = _parse_trade(tData, symbol) + self._emit('new_trade', tradeObj) async def _wallet_update_handler(self, data): # [0,"wu",["exchange","USD",89134.66933283,0]] @@ -436,7 +410,12 @@ class BfxWebsocket(GenericWebsocket): # connection data.reverse() for t in data: - trade = _parse_trade(t, symbol) + trade = { + 'mts': t[1], + 'amount': t[2], + 'price': t[3], + 'symbol': symbol + } self._emit('seed_trade', trade) async def _candle_handler(self, data): From 3650bc7944f0381441be2430c0b1bb44214f8d76 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Sun, 30 Jan 2022 12:54:57 +0100 Subject: [PATCH 62/68] fixed trades handling error added new_user_trade use to examples updated docs --- .../examples/ws/subscribe_trades_candles.py | 4 ++ bfxapi/websockets/bfx_websocket.py | 43 +++++++++++++++++++ docs/ws_v2.md | 2 + 3 files changed, 49 insertions(+) diff --git a/bfxapi/examples/ws/subscribe_trades_candles.py b/bfxapi/examples/ws/subscribe_trades_candles.py index 2e17fe3..d7135e6 100644 --- a/bfxapi/examples/ws/subscribe_trades_candles.py +++ b/bfxapi/examples/ws/subscribe_trades_candles.py @@ -20,6 +20,10 @@ def log_candle(candle): def log_trade(trade): print ("New trade: {}".format(trade)) +@bfx.ws.on('new_user_trade') +def log_user_trade(trade): + print ("New user trade: {}".format(trade)) + async def start(): await bfx.ws.subscribe('candles', 'tBTCUSD', timeframe='1m') await bfx.ws.subscribe('trades', 'tBTCUSD') diff --git a/bfxapi/websockets/bfx_websocket.py b/bfxapi/websockets/bfx_websocket.py index 6df4487..74c2371 100644 --- a/bfxapi/websockets/bfx_websocket.py +++ b/bfxapi/websockets/bfx_websocket.py @@ -67,6 +67,38 @@ def _parse_trade(tData, symbol): } +def _parse_user_trade(tData): + return { + 'id': tData[0], + 'symbol': tData[1], + 'mts_create': tData[2], + 'order_id': tData[3], + 'exec_amount': tData[4], + 'exec_price': tData[5], + 'order_type': tData[6], + 'order_price': tData[7], + 'maker': tData[8], + 'cid': tData[11], + } + + +def _parse_user_trade_update(tData): + return { + 'id': tData[0], + 'symbol': tData[1], + 'mts_create': tData[2], + 'order_id': tData[3], + 'exec_amount': tData[4], + 'exec_price': tData[5], + 'order_type': tData[6], + 'order_price': tData[7], + 'maker': tData[8], + 'fee': tData[9], + 'fee_currency': tData[10], + 'cid': tData[11], + } + + def _parse_deriv_status_update(sData, symbol): return { 'symbol': symbol, @@ -141,6 +173,7 @@ class BfxWebsocket(GenericWebsocket): - `funding_credit_snapshot` (array): Opening funding credit balances - `balance_update` (array): When the state of a balance is changed - `new_trade` (array): A new trade on the market has been executed + - `new_user_trade` (array): A new - your - trade has been executed - `new_ticker` (Ticker|FundingTicker): A new ticker update has been published - `new_funding_ticker` (FundingTicker): A new funding ticker update has been published - `new_trading_ticker` (Ticker): A new trading ticker update has been published @@ -284,6 +317,11 @@ class BfxWebsocket(GenericWebsocket): symbol = self.subscriptionManager.get(data[0]).symbol tradeObj = _parse_trade(tData, symbol) self._emit('trade_update', tradeObj) + else: + # user trade + # [0,"tu",[738045455,"tTESTBTC:TESTUSD",1622169615771,66635385225,0.001,38175,"EXCHANGE LIMIT",39000,-1,-0.000002,"TESTBTC",1622169615685]] + tradeObj = _parse_user_trade_update(tData) + self._emit('user_trade_update', tradeObj) async def _trade_executed_handler(self, data): tData = data[2] @@ -292,6 +330,11 @@ class BfxWebsocket(GenericWebsocket): symbol = self.subscriptionManager.get(data[0]).symbol tradeObj = _parse_trade(tData, symbol) self._emit('new_trade', tradeObj) + else: + # user trade + # [0, 'te', [37558151, 'tBTCUSD', 1643542688513, 1512164914, 0.0001, 30363, 'EXCHANGE MARKET', 100000, -1, None, None, 1643542688390]] + tradeObj = _parse_user_trade(tData) + self._emit('new_user_trade', tradeObj) async def _wallet_update_handler(self, data): # [0,"wu",["exchange","USD",89134.66933283,0]] diff --git a/docs/ws_v2.md b/docs/ws_v2.md index 783e74f..f0cc3bb 100644 --- a/docs/ws_v2.md +++ b/docs/ws_v2.md @@ -76,10 +76,12 @@ https://github.com/Crypto-toolbox/btfxwss - `funding_credit_snapshot` (array): Opening funding credit balances - `balance_update` (array): When the state of a balance is changed - `new_trade` (array): A new trade on the market has been executed + - `new_user_trade` (array): A new - your - trade has been executed - `new_ticker` (Ticker|FundingTicker): A new ticker update has been published - `new_funding_ticker` (FundingTicker): A new funding ticker update has been published - `new_trading_ticker` (Ticker): A new trading ticker update has been published - `trade_update` (array): A trade on the market has been updated + - `user_trade_update` (array): A - your - trade has been updated - `new_candle` (array): A new candle has been produced - `margin_info_updates` (array): New margin information has been broadcasted - `funding_info_updates` (array): New funding information has been broadcasted From 87e7f9820edb71024a211d696037ba48465808cb Mon Sep 17 00:00:00 2001 From: itsdeka Date: Mon, 22 Aug 2022 19:31:16 +0200 Subject: [PATCH 63/68] -) Use private host for auth-based requests -) Updated examples --- CHANGELOG | 3 +++ bfxapi/__init__.py | 2 +- bfxapi/client.py | 8 ++++---- bfxapi/examples/rest/create_funding.py | 8 +++++--- bfxapi/examples/rest/create_order.py | 7 +++++-- bfxapi/examples/rest/get_authenticated_data.py | 7 +++++-- bfxapi/examples/rest/get_public_data.py | 5 ++++- bfxapi/examples/rest/get_seed_trades.py | 7 +++++-- bfxapi/examples/rest/merchant.py | 7 +++++-- bfxapi/examples/rest/transfer_wallet.py | 8 +++++--- bfxapi/examples/ws/cancel_order.py | 7 +++++-- bfxapi/examples/ws/connect.py | 6 ++++-- bfxapi/examples/ws/connect_auth.py | 4 +++- bfxapi/examples/ws/full_orderbook.py | 8 +++++--- bfxapi/examples/ws/multiple_instances.py | 6 ++---- bfxapi/examples/ws/resubscribe_orderbook.py | 8 +++++--- bfxapi/examples/ws/send_order.py | 7 +++++-- bfxapi/examples/ws/start_stop_connection.py | 5 +++-- bfxapi/examples/ws/subscribe_derivative_status.py | 8 +++++--- bfxapi/examples/ws/subscribe_orderbook.py | 5 ++++- bfxapi/examples/ws/subscribe_tickers.py | 8 +++++--- bfxapi/examples/ws/subscribe_trades_candles.py | 8 +++++--- bfxapi/examples/ws/update_order.py | 7 +++++-- bfxapi/examples/ws/wallet_balance.py | 7 +++++-- bfxapi/version.py | 2 +- bfxapi/websockets/bfx_websocket.py | 2 +- 26 files changed, 105 insertions(+), 55 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 69a4b97..8563409 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +2.0.2 +-) Use private host for auth-based requests + 2.0.1 -) Added User Settings Write/Read/Delete endpoints (REST) -) Added Balance Available for Orders/Offers endpoint (REST) diff --git a/bfxapi/__init__.py b/bfxapi/__init__.py index 4ad4f80..5b6918e 100644 --- a/bfxapi/__init__.py +++ b/bfxapi/__init__.py @@ -3,7 +3,7 @@ This module is used to interact with the bitfinex api """ from .version import __version__ -from .client import Client +from .client import Client, PUB_REST_HOST, PUB_WS_HOST, REST_HOST, WS_HOST from .models import (Order, Trade, OrderBook, Subscription, Wallet, Position, FundingLoan, FundingOffer, FundingCredit, Movement) diff --git a/bfxapi/client.py b/bfxapi/client.py index fff114c..1700f6c 100644 --- a/bfxapi/client.py +++ b/bfxapi/client.py @@ -5,13 +5,13 @@ a websocket client and a rest interface client # pylint: disable-all -import asyncio - from .websockets.bfx_websocket import BfxWebsocket from .rest.bfx_rest import BfxRest -REST_HOST = 'https://api-pub.bitfinex.com/v2' -WS_HOST = 'wss://api-pub.bitfinex.com/ws/2' +REST_HOST = 'https://api.bitfinex.com/v2' +WS_HOST = 'wss://api.bitfinex.com/ws/2' +PUB_REST_HOST = 'https://api-pub.bitfinex.com/v2' +PUB_WS_HOST = 'wss://api-pub.bitfinex.com/ws/2' class Client: """ diff --git a/bfxapi/examples/rest/create_funding.py b/bfxapi/examples/rest/create_funding.py index c6213bb..8e4eeae 100644 --- a/bfxapi/examples/rest/create_funding.py +++ b/bfxapi/examples/rest/create_funding.py @@ -1,18 +1,20 @@ import os import sys import asyncio -import time sys.path.append('../../../') -from bfxapi import Client +from bfxapi import Client, WS_HOST, REST_HOST API_KEY=os.getenv("BFX_KEY") API_SECRET=os.getenv("BFX_SECRET") +# Create funding requires private hosts bfx = Client( API_KEY=API_KEY, API_SECRET=API_SECRET, - logLevel='DEBUG' + logLevel='DEBUG', + ws_host=WS_HOST, + rest_host=REST_HOST ) async def create_funding(): diff --git a/bfxapi/examples/rest/create_order.py b/bfxapi/examples/rest/create_order.py index 9c69a0c..ad5bc23 100644 --- a/bfxapi/examples/rest/create_order.py +++ b/bfxapi/examples/rest/create_order.py @@ -3,16 +3,19 @@ import sys import asyncio import time sys.path.append('../../../') -from bfxapi import Client +from bfxapi import Client, WS_HOST, REST_HOST from bfxapi.models import OrderType API_KEY=os.getenv("BFX_KEY") API_SECRET=os.getenv("BFX_SECRET") +# Create order requires private hosts bfx = Client( API_KEY=API_KEY, API_SECRET=API_SECRET, - logLevel='DEBUG' + logLevel='DEBUG', + ws_host=WS_HOST, + rest_host=REST_HOST ) async def create_order(): diff --git a/bfxapi/examples/rest/get_authenticated_data.py b/bfxapi/examples/rest/get_authenticated_data.py index 0efd74f..c5776a1 100644 --- a/bfxapi/examples/rest/get_authenticated_data.py +++ b/bfxapi/examples/rest/get_authenticated_data.py @@ -4,15 +4,18 @@ import asyncio import time sys.path.append('../../../') -from bfxapi import Client +from bfxapi import Client, WS_HOST, REST_HOST API_KEY=os.getenv("BFX_KEY") API_SECRET=os.getenv("BFX_SECRET") +# Retrieving authenticated data requires private hosts bfx = Client( API_KEY=API_KEY, API_SECRET=API_SECRET, - logLevel='DEBUG' + logLevel='DEBUG', + ws_host=WS_HOST, + rest_host=REST_HOST ) now = int(round(time.time() * 1000)) diff --git a/bfxapi/examples/rest/get_public_data.py b/bfxapi/examples/rest/get_public_data.py index eae9f94..151ca0d 100644 --- a/bfxapi/examples/rest/get_public_data.py +++ b/bfxapi/examples/rest/get_public_data.py @@ -4,10 +4,13 @@ import asyncio import time sys.path.append('../../../') -from bfxapi import Client +from bfxapi import Client, PUB_WS_HOST, PUB_REST_HOST +# Retrieving public data requires public hosts bfx = Client( logLevel='DEBUG', + ws_host=PUB_WS_HOST, + rest_host=PUB_REST_HOST ) now = int(round(time.time() * 1000)) diff --git a/bfxapi/examples/rest/get_seed_trades.py b/bfxapi/examples/rest/get_seed_trades.py index fb34dab..caffb19 100644 --- a/bfxapi/examples/rest/get_seed_trades.py +++ b/bfxapi/examples/rest/get_seed_trades.py @@ -3,10 +3,13 @@ import sys import asyncio sys.path.append('../../../') -from bfxapi import Client +from bfxapi import Client, PUB_WS_HOST, PUB_REST_HOST +# Retrieving seed trades requires public hosts bfx = Client( - logLevel='INFO' + logLevel='INFO', + ws_host=PUB_WS_HOST, + rest_host=PUB_REST_HOST ) async def get_seeds(): diff --git a/bfxapi/examples/rest/merchant.py b/bfxapi/examples/rest/merchant.py index 91521fa..81c8fae 100644 --- a/bfxapi/examples/rest/merchant.py +++ b/bfxapi/examples/rest/merchant.py @@ -2,15 +2,18 @@ import os import sys import asyncio sys.path.append('../../../') -from bfxapi import Client +from bfxapi import Client, WS_HOST, REST_HOST API_KEY=os.getenv("BFX_KEY") API_SECRET=os.getenv("BFX_SECRET") +# Submitting invoices requires private hosts bfx = Client( API_KEY=API_KEY, API_SECRET=API_SECRET, - logLevel='DEBUG' + logLevel='DEBUG', + ws_host=WS_HOST, + rest_host=REST_HOST ) async def run(): diff --git a/bfxapi/examples/rest/transfer_wallet.py b/bfxapi/examples/rest/transfer_wallet.py index 631ea3b..84e5616 100644 --- a/bfxapi/examples/rest/transfer_wallet.py +++ b/bfxapi/examples/rest/transfer_wallet.py @@ -1,18 +1,20 @@ import os import sys import asyncio -import time sys.path.append('../../../') -from bfxapi import Client +from bfxapi import Client, WS_HOST, REST_HOST API_KEY=os.getenv("BFX_KEY") API_SECRET=os.getenv("BFX_SECRET") +# Transfer wallet requires private hosts bfx = Client( API_KEY=API_KEY, API_SECRET=API_SECRET, - logLevel='DEBUG' + logLevel='DEBUG', + ws_host=WS_HOST, + rest_host=REST_HOST ) async def transfer_wallet(): diff --git a/bfxapi/examples/ws/cancel_order.py b/bfxapi/examples/ws/cancel_order.py index 70511db..7d105f5 100644 --- a/bfxapi/examples/ws/cancel_order.py +++ b/bfxapi/examples/ws/cancel_order.py @@ -2,15 +2,18 @@ import os import sys sys.path.append('../../../') -from bfxapi import Client, Order +from bfxapi import Client, WS_HOST, REST_HOST API_KEY=os.getenv("BFX_KEY") API_SECRET=os.getenv("BFX_SECRET") +# Canceling orders requires private hosts bfx = Client( API_KEY=API_KEY, API_SECRET=API_SECRET, - logLevel='DEBUG' + logLevel='DEBUG', + ws_host=WS_HOST, + rest_host=REST_HOST ) @bfx.ws.on('order_closed') diff --git a/bfxapi/examples/ws/connect.py b/bfxapi/examples/ws/connect.py index 4cb21bb..ffb28ac 100644 --- a/bfxapi/examples/ws/connect.py +++ b/bfxapi/examples/ws/connect.py @@ -2,10 +2,12 @@ import os import sys sys.path.append('../../../') -from bfxapi import Client +from bfxapi import Client, PUB_WS_HOST, PUB_REST_HOST bfx = Client( - logLevel='DEBUG' + logLevel='DEBUG', + ws_host=PUB_WS_HOST, + rest_host=PUB_REST_HOST ) @bfx.ws.on('error') diff --git a/bfxapi/examples/ws/connect_auth.py b/bfxapi/examples/ws/connect_auth.py index fac67e3..4e5db58 100644 --- a/bfxapi/examples/ws/connect_auth.py +++ b/bfxapi/examples/ws/connect_auth.py @@ -2,7 +2,7 @@ import os import sys sys.path.append('../../../') -from bfxapi import Client, Order +from bfxapi import Client, WS_HOST, REST_HOST API_KEY=os.getenv("BFX_KEY") API_SECRET=os.getenv("BFX_SECRET") @@ -11,6 +11,8 @@ bfx = Client( API_KEY=API_KEY, API_SECRET=API_SECRET, logLevel='DEBUG', + ws_host=WS_HOST, + rest_host=REST_HOST, dead_man_switch=True, # <-- kill all orders if this connection drops channel_filter=['wallet'] # <-- only receive wallet updates ) diff --git a/bfxapi/examples/ws/full_orderbook.py b/bfxapi/examples/ws/full_orderbook.py index d8678e3..49639b4 100644 --- a/bfxapi/examples/ws/full_orderbook.py +++ b/bfxapi/examples/ws/full_orderbook.py @@ -1,13 +1,15 @@ -import os import sys import time from collections import OrderedDict sys.path.append('../../../') -from bfxapi import Client +from bfxapi import Client, PUB_WS_HOST, PUB_REST_HOST +# Retrieving orderbook requires public hosts bfx = Client( - manageOrderBooks=True + manageOrderBooks=True, + ws_host=PUB_WS_HOST, + rest_host=PUB_REST_HOST ) class OrderBook: diff --git a/bfxapi/examples/ws/multiple_instances.py b/bfxapi/examples/ws/multiple_instances.py index 89fa677..9cb89ed 100644 --- a/bfxapi/examples/ws/multiple_instances.py +++ b/bfxapi/examples/ws/multiple_instances.py @@ -8,11 +8,9 @@ bfx ws instances to comply with the open subscriptions number constraint (max. 2 import sys sys.path.append('../../../') import asyncio -import json -from datetime import datetime from functools import partial import websockets as ws -from bfxapi import Client +from bfxapi import Client, PUB_WS_HOST, PUB_REST_HOST import math import random @@ -27,7 +25,7 @@ def get_random_list_of_tickers(): class Instance: def __init__(self, _id): self.id = _id - self.bfx = Client(logLevel='INFO') + self.bfx = Client(logLevel='INFO', ws_host=PUB_WS_HOST, rest_host=PUB_REST_HOST) self.subscriptions = {'trades': {}, 'ticker': {}} self.is_ready = False diff --git a/bfxapi/examples/ws/resubscribe_orderbook.py b/bfxapi/examples/ws/resubscribe_orderbook.py index ede112d..7bbfb20 100644 --- a/bfxapi/examples/ws/resubscribe_orderbook.py +++ b/bfxapi/examples/ws/resubscribe_orderbook.py @@ -1,11 +1,13 @@ -import os import sys sys.path.append('../../../') -from bfxapi import Client +from bfxapi import Client, PUB_WS_HOST, PUB_REST_HOST +# Retrieving orderbook requires public hosts bfx = Client( - logLevel='INFO' + manageOrderBooks=True, + ws_host=PUB_WS_HOST, + rest_host=PUB_REST_HOST ) @bfx.ws.on('error') diff --git a/bfxapi/examples/ws/send_order.py b/bfxapi/examples/ws/send_order.py index 6c30fd7..f26d670 100644 --- a/bfxapi/examples/ws/send_order.py +++ b/bfxapi/examples/ws/send_order.py @@ -2,15 +2,18 @@ import os import sys sys.path.append('../../../') -from bfxapi import Client, Order +from bfxapi import Client, Order, WS_HOST, REST_HOST API_KEY=os.getenv("BFX_KEY") API_SECRET=os.getenv("BFX_SECRET") +# Sending order requires private hosts bfx = Client( API_KEY=API_KEY, API_SECRET=API_SECRET, - logLevel='DEBUG' + logLevel='DEBUG', + ws_host=WS_HOST, + rest_host=REST_HOST ) @bfx.ws.on('order_snapshot') diff --git a/bfxapi/examples/ws/start_stop_connection.py b/bfxapi/examples/ws/start_stop_connection.py index 4e23469..59de479 100644 --- a/bfxapi/examples/ws/start_stop_connection.py +++ b/bfxapi/examples/ws/start_stop_connection.py @@ -1,11 +1,12 @@ -import os import sys sys.path.append('../../../') -from bfxapi import Client +from bfxapi import Client, PUB_WS_HOST, PUB_REST_HOST bfx = Client( logLevel='DEBUG', + ws_host=PUB_WS_HOST, + rest_host=PUB_REST_HOST ) @bfx.ws.on('order_book_snapshot') diff --git a/bfxapi/examples/ws/subscribe_derivative_status.py b/bfxapi/examples/ws/subscribe_derivative_status.py index 3ddb7e5..52ca79d 100644 --- a/bfxapi/examples/ws/subscribe_derivative_status.py +++ b/bfxapi/examples/ws/subscribe_derivative_status.py @@ -1,11 +1,13 @@ -import os import sys sys.path.append('../../../') -from bfxapi import Client +from bfxapi import Client, PUB_WS_HOST, PUB_REST_HOST +# Retrieving derivative status requires public hosts bfx = Client( - logLevel='INFO' + logLevel='DEBUG', + ws_host=PUB_WS_HOST, + rest_host=PUB_REST_HOST ) @bfx.ws.on('error') diff --git a/bfxapi/examples/ws/subscribe_orderbook.py b/bfxapi/examples/ws/subscribe_orderbook.py index 7febcf3..bc06945 100644 --- a/bfxapi/examples/ws/subscribe_orderbook.py +++ b/bfxapi/examples/ws/subscribe_orderbook.py @@ -2,10 +2,13 @@ import os import sys sys.path.append('../../../') -from bfxapi import Client +from bfxapi import Client, PUB_WS_HOST, PUB_REST_HOST +# Retrieving trades/candles requires public hosts bfx = Client( logLevel='DEBUG', + ws_host=PUB_WS_HOST, + rest_host=PUB_REST_HOST, # Verifies that the local orderbook is up to date # with the bitfinex servers manageOrderBooks=True diff --git a/bfxapi/examples/ws/subscribe_tickers.py b/bfxapi/examples/ws/subscribe_tickers.py index 984fa47..2eb744f 100644 --- a/bfxapi/examples/ws/subscribe_tickers.py +++ b/bfxapi/examples/ws/subscribe_tickers.py @@ -1,11 +1,13 @@ -import os import sys sys.path.append('../../../') -from bfxapi import Client +from bfxapi import Client, PUB_WS_HOST, PUB_REST_HOST +# Retrieving tickers requires public hosts bfx = Client( - logLevel='DEBUG' + logLevel='DEBUG', + ws_host=PUB_WS_HOST, + rest_host=PUB_REST_HOST ) @bfx.ws.on('error') diff --git a/bfxapi/examples/ws/subscribe_trades_candles.py b/bfxapi/examples/ws/subscribe_trades_candles.py index d7135e6..1248dea 100644 --- a/bfxapi/examples/ws/subscribe_trades_candles.py +++ b/bfxapi/examples/ws/subscribe_trades_candles.py @@ -1,11 +1,13 @@ -import os import sys sys.path.append('../../../') -from bfxapi import Client +from bfxapi import Client, PUB_WS_HOST, PUB_REST_HOST +# Retrieving trades/candles requires public hosts bfx = Client( - logLevel='DEBUG' + logLevel='DEBUG', + ws_host=PUB_WS_HOST, + rest_host=PUB_REST_HOST ) @bfx.ws.on('error') diff --git a/bfxapi/examples/ws/update_order.py b/bfxapi/examples/ws/update_order.py index 68df31e..936d1b6 100644 --- a/bfxapi/examples/ws/update_order.py +++ b/bfxapi/examples/ws/update_order.py @@ -2,15 +2,18 @@ import os import sys sys.path.append('../../../') -from bfxapi import Client, Order +from bfxapi import Client, Order, WS_HOST, REST_HOST API_KEY=os.getenv("BFX_KEY") API_SECRET=os.getenv("BFX_SECRET") +# Update order requires private hosts bfx = Client( API_KEY=API_KEY, API_SECRET=API_SECRET, - logLevel='DEBUG' + logLevel='INFO', + ws_host=WS_HOST, + rest_host=REST_HOST ) @bfx.ws.on('order_update') diff --git a/bfxapi/examples/ws/wallet_balance.py b/bfxapi/examples/ws/wallet_balance.py index c46fa1e..731faa6 100644 --- a/bfxapi/examples/ws/wallet_balance.py +++ b/bfxapi/examples/ws/wallet_balance.py @@ -1,15 +1,18 @@ import os import sys sys.path.append('../../../') -from bfxapi import Client +from bfxapi import Client, WS_HOST, REST_HOST API_KEY=os.getenv("BFX_KEY") API_SECRET=os.getenv("BFX_SECRET") +# Checking wallet balances requires private hosts bfx = Client( API_KEY=API_KEY, API_SECRET=API_SECRET, - logLevel='INFO' + logLevel='INFO', + ws_host=WS_HOST, + rest_host=REST_HOST ) @bfx.ws.on('wallet_snapshot') diff --git a/bfxapi/version.py b/bfxapi/version.py index f5a20c1..2b350b7 100644 --- a/bfxapi/version.py +++ b/bfxapi/version.py @@ -2,4 +2,4 @@ This module contains the current version of the bfxapi lib """ -__version__ = '2.0.1' +__version__ = '2.0.2' diff --git a/bfxapi/websockets/bfx_websocket.py b/bfxapi/websockets/bfx_websocket.py index 74c2371..0afa60b 100644 --- a/bfxapi/websockets/bfx_websocket.py +++ b/bfxapi/websockets/bfx_websocket.py @@ -187,7 +187,7 @@ class BfxWebsocket(GenericWebsocket): - `unsubscribed` (Subscription): A channel has been un-subscribed """ - def __init__(self, API_KEY=None, API_SECRET=None, host='wss://api-pub.bitfinex.com/ws/2', + def __init__(self, host, API_KEY=None, API_SECRET=None, manageOrderBooks=False, dead_man_switch=False, ws_capacity=25, logLevel='INFO', parse_float=float, channel_filter=[], *args, **kwargs): self.API_KEY = API_KEY From 26a5f509676ba11b6f5f5da877ff6aa08f20a777 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Mon, 22 Aug 2022 19:36:09 +0200 Subject: [PATCH 64/68] fix cancel order updated version --- bfxapi/examples/ws/cancel_order.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bfxapi/examples/ws/cancel_order.py b/bfxapi/examples/ws/cancel_order.py index 7d105f5..6804c30 100644 --- a/bfxapi/examples/ws/cancel_order.py +++ b/bfxapi/examples/ws/cancel_order.py @@ -2,7 +2,7 @@ import os import sys sys.path.append('../../../') -from bfxapi import Client, WS_HOST, REST_HOST +from bfxapi import Client, Order, WS_HOST, REST_HOST API_KEY=os.getenv("BFX_KEY") API_SECRET=os.getenv("BFX_SECRET") diff --git a/setup.py b/setup.py index af19bde..a5c1f42 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from os import path here = path.abspath(path.dirname(__file__)) setup( name='bitfinex-api-py', - version='2.0.1', + version='2.0.2', description='Official Bitfinex Python API', long_description='A Python reference implementation of the Bitfinex API for both REST and websocket interaction', long_description_content_type='text/markdown', From b2460450c64b6257fc7853039f42f20ec10e97ef Mon Sep 17 00:00:00 2001 From: itsdeka Date: Tue, 23 Aug 2022 13:30:05 +0200 Subject: [PATCH 65/68] revert change --- bfxapi/websockets/bfx_websocket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bfxapi/websockets/bfx_websocket.py b/bfxapi/websockets/bfx_websocket.py index 0afa60b..74c2371 100644 --- a/bfxapi/websockets/bfx_websocket.py +++ b/bfxapi/websockets/bfx_websocket.py @@ -187,7 +187,7 @@ class BfxWebsocket(GenericWebsocket): - `unsubscribed` (Subscription): A channel has been un-subscribed """ - def __init__(self, host, API_KEY=None, API_SECRET=None, + def __init__(self, API_KEY=None, API_SECRET=None, host='wss://api-pub.bitfinex.com/ws/2', manageOrderBooks=False, dead_man_switch=False, ws_capacity=25, logLevel='INFO', parse_float=float, channel_filter=[], *args, **kwargs): self.API_KEY = API_KEY From 17646f9980239aa9e3857cb6cf8c825cc9aedb59 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Sat, 27 Aug 2022 17:09:00 +0200 Subject: [PATCH 66/68] refactoring - moved hosts to constants.py --- bfxapi/client.py | 6 +----- bfxapi/constants.py | 4 ++++ bfxapi/websockets/bfx_websocket.py | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 bfxapi/constants.py diff --git a/bfxapi/client.py b/bfxapi/client.py index 1700f6c..1d2d57c 100644 --- a/bfxapi/client.py +++ b/bfxapi/client.py @@ -7,11 +7,7 @@ a websocket client and a rest interface client from .websockets.bfx_websocket import BfxWebsocket from .rest.bfx_rest import BfxRest - -REST_HOST = 'https://api.bitfinex.com/v2' -WS_HOST = 'wss://api.bitfinex.com/ws/2' -PUB_REST_HOST = 'https://api-pub.bitfinex.com/v2' -PUB_WS_HOST = 'wss://api-pub.bitfinex.com/ws/2' +from .constants import * class Client: """ diff --git a/bfxapi/constants.py b/bfxapi/constants.py new file mode 100644 index 0000000..a0c6525 --- /dev/null +++ b/bfxapi/constants.py @@ -0,0 +1,4 @@ +REST_HOST = 'https://api.bitfinex.com/v2' +WS_HOST = 'wss://api.bitfinex.com/ws/2' +PUB_REST_HOST = 'https://api-pub.bitfinex.com/v2' +PUB_WS_HOST = 'wss://api-pub.bitfinex.com/ws/2' \ No newline at end of file diff --git a/bfxapi/websockets/bfx_websocket.py b/bfxapi/websockets/bfx_websocket.py index 74c2371..451d975 100644 --- a/bfxapi/websockets/bfx_websocket.py +++ b/bfxapi/websockets/bfx_websocket.py @@ -14,6 +14,7 @@ from .order_manager import OrderManager from ..utils.auth import generate_auth_payload from ..utils.decorators import handle_failure from ..models import Order, Trade, OrderBook, Ticker, FundingTicker +from ..constants import PUB_WS_HOST class Flags: @@ -187,7 +188,7 @@ class BfxWebsocket(GenericWebsocket): - `unsubscribed` (Subscription): A channel has been un-subscribed """ - def __init__(self, API_KEY=None, API_SECRET=None, host='wss://api-pub.bitfinex.com/ws/2', + def __init__(self, API_KEY=None, API_SECRET=None, host=PUB_WS_HOST, manageOrderBooks=False, dead_man_switch=False, ws_capacity=25, logLevel='INFO', parse_float=float, channel_filter=[], *args, **kwargs): self.API_KEY = API_KEY From 23c00e77a2d47342961aebadcb083a07bc8a03a7 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Sat, 27 Aug 2022 17:17:13 +0200 Subject: [PATCH 67/68] refactoring - updated tests --- bfxapi/examples/rest/create_funding.py | 3 ++- bfxapi/examples/rest/create_order.py | 3 ++- bfxapi/examples/rest/get_authenticated_data.py | 3 ++- bfxapi/examples/rest/get_public_data.py | 3 ++- bfxapi/examples/rest/get_seed_trades.py | 3 ++- bfxapi/examples/rest/merchant.py | 3 ++- bfxapi/examples/rest/transfer_wallet.py | 3 ++- bfxapi/examples/ws/cancel_order.py | 3 ++- bfxapi/examples/ws/connect.py | 3 ++- bfxapi/examples/ws/connect_auth.py | 3 ++- bfxapi/examples/ws/full_orderbook.py | 3 ++- bfxapi/examples/ws/multiple_instances.py | 3 ++- bfxapi/examples/ws/resubscribe_orderbook.py | 3 ++- bfxapi/examples/ws/send_order.py | 3 ++- bfxapi/examples/ws/start_stop_connection.py | 3 ++- bfxapi/examples/ws/subscribe_derivative_status.py | 3 ++- bfxapi/examples/ws/subscribe_orderbook.py | 3 ++- bfxapi/examples/ws/subscribe_tickers.py | 3 ++- bfxapi/examples/ws/subscribe_trades_candles.py | 3 ++- bfxapi/examples/ws/update_order.py | 3 ++- bfxapi/examples/ws/wallet_balance.py | 3 ++- 21 files changed, 42 insertions(+), 21 deletions(-) diff --git a/bfxapi/examples/rest/create_funding.py b/bfxapi/examples/rest/create_funding.py index 8e4eeae..db16826 100644 --- a/bfxapi/examples/rest/create_funding.py +++ b/bfxapi/examples/rest/create_funding.py @@ -3,7 +3,8 @@ import sys import asyncio sys.path.append('../../../') -from bfxapi import Client, WS_HOST, REST_HOST +from bfxapi import Client +from bfxapi.constants import WS_HOST, REST_HOST API_KEY=os.getenv("BFX_KEY") API_SECRET=os.getenv("BFX_SECRET") diff --git a/bfxapi/examples/rest/create_order.py b/bfxapi/examples/rest/create_order.py index ad5bc23..94e9640 100644 --- a/bfxapi/examples/rest/create_order.py +++ b/bfxapi/examples/rest/create_order.py @@ -3,7 +3,8 @@ import sys import asyncio import time sys.path.append('../../../') -from bfxapi import Client, WS_HOST, REST_HOST +from bfxapi import Client +from bfxapi.constants import WS_HOST, REST_HOST from bfxapi.models import OrderType API_KEY=os.getenv("BFX_KEY") diff --git a/bfxapi/examples/rest/get_authenticated_data.py b/bfxapi/examples/rest/get_authenticated_data.py index c5776a1..a557de2 100644 --- a/bfxapi/examples/rest/get_authenticated_data.py +++ b/bfxapi/examples/rest/get_authenticated_data.py @@ -4,7 +4,8 @@ import asyncio import time sys.path.append('../../../') -from bfxapi import Client, WS_HOST, REST_HOST +from bfxapi import Client +from bfxapi.constants import WS_HOST, REST_HOST API_KEY=os.getenv("BFX_KEY") API_SECRET=os.getenv("BFX_SECRET") diff --git a/bfxapi/examples/rest/get_public_data.py b/bfxapi/examples/rest/get_public_data.py index 151ca0d..6d1e246 100644 --- a/bfxapi/examples/rest/get_public_data.py +++ b/bfxapi/examples/rest/get_public_data.py @@ -4,7 +4,8 @@ import asyncio import time sys.path.append('../../../') -from bfxapi import Client, PUB_WS_HOST, PUB_REST_HOST +from bfxapi import Client +from bfxapi.constants import PUB_WS_HOST, PUB_REST_HOST # Retrieving public data requires public hosts bfx = Client( diff --git a/bfxapi/examples/rest/get_seed_trades.py b/bfxapi/examples/rest/get_seed_trades.py index caffb19..6f9cbf8 100644 --- a/bfxapi/examples/rest/get_seed_trades.py +++ b/bfxapi/examples/rest/get_seed_trades.py @@ -3,7 +3,8 @@ import sys import asyncio sys.path.append('../../../') -from bfxapi import Client, PUB_WS_HOST, PUB_REST_HOST +from bfxapi import Client +from bfxapi.constants import PUB_WS_HOST, PUB_REST_HOST # Retrieving seed trades requires public hosts bfx = Client( diff --git a/bfxapi/examples/rest/merchant.py b/bfxapi/examples/rest/merchant.py index 81c8fae..5477588 100644 --- a/bfxapi/examples/rest/merchant.py +++ b/bfxapi/examples/rest/merchant.py @@ -2,7 +2,8 @@ import os import sys import asyncio sys.path.append('../../../') -from bfxapi import Client, WS_HOST, REST_HOST +from bfxapi import Client +from bfxapi.constants import WS_HOST, REST_HOST API_KEY=os.getenv("BFX_KEY") API_SECRET=os.getenv("BFX_SECRET") diff --git a/bfxapi/examples/rest/transfer_wallet.py b/bfxapi/examples/rest/transfer_wallet.py index 84e5616..a0c59ce 100644 --- a/bfxapi/examples/rest/transfer_wallet.py +++ b/bfxapi/examples/rest/transfer_wallet.py @@ -3,7 +3,8 @@ import sys import asyncio sys.path.append('../../../') -from bfxapi import Client, WS_HOST, REST_HOST +from bfxapi import Client +from bfxapi.constants import WS_HOST, REST_HOST API_KEY=os.getenv("BFX_KEY") API_SECRET=os.getenv("BFX_SECRET") diff --git a/bfxapi/examples/ws/cancel_order.py b/bfxapi/examples/ws/cancel_order.py index 6804c30..e37ad00 100644 --- a/bfxapi/examples/ws/cancel_order.py +++ b/bfxapi/examples/ws/cancel_order.py @@ -2,7 +2,8 @@ import os import sys sys.path.append('../../../') -from bfxapi import Client, Order, WS_HOST, REST_HOST +from bfxapi import Client, Order +from bfxapi.constants import WS_HOST, REST_HOST API_KEY=os.getenv("BFX_KEY") API_SECRET=os.getenv("BFX_SECRET") diff --git a/bfxapi/examples/ws/connect.py b/bfxapi/examples/ws/connect.py index ffb28ac..b63cd7d 100644 --- a/bfxapi/examples/ws/connect.py +++ b/bfxapi/examples/ws/connect.py @@ -2,7 +2,8 @@ import os import sys sys.path.append('../../../') -from bfxapi import Client, PUB_WS_HOST, PUB_REST_HOST +from bfxapi import Client +from bfxapi.constants import PUB_WS_HOST, PUB_REST_HOST bfx = Client( logLevel='DEBUG', diff --git a/bfxapi/examples/ws/connect_auth.py b/bfxapi/examples/ws/connect_auth.py index 4e5db58..a01c6d3 100644 --- a/bfxapi/examples/ws/connect_auth.py +++ b/bfxapi/examples/ws/connect_auth.py @@ -2,7 +2,8 @@ import os import sys sys.path.append('../../../') -from bfxapi import Client, WS_HOST, REST_HOST +from bfxapi import Client +from bfxapi.constants import WS_HOST, REST_HOST API_KEY=os.getenv("BFX_KEY") API_SECRET=os.getenv("BFX_SECRET") diff --git a/bfxapi/examples/ws/full_orderbook.py b/bfxapi/examples/ws/full_orderbook.py index 49639b4..baac656 100644 --- a/bfxapi/examples/ws/full_orderbook.py +++ b/bfxapi/examples/ws/full_orderbook.py @@ -3,7 +3,8 @@ import time from collections import OrderedDict sys.path.append('../../../') -from bfxapi import Client, PUB_WS_HOST, PUB_REST_HOST +from bfxapi import Client +from bfxapi.constants import PUB_WS_HOST, PUB_REST_HOST # Retrieving orderbook requires public hosts bfx = Client( diff --git a/bfxapi/examples/ws/multiple_instances.py b/bfxapi/examples/ws/multiple_instances.py index 9cb89ed..cf1dc04 100644 --- a/bfxapi/examples/ws/multiple_instances.py +++ b/bfxapi/examples/ws/multiple_instances.py @@ -10,7 +10,8 @@ sys.path.append('../../../') import asyncio from functools import partial import websockets as ws -from bfxapi import Client, PUB_WS_HOST, PUB_REST_HOST +from bfxapi import Client +from bfxapi.constants import PUB_WS_HOST, PUB_REST_HOST import math import random diff --git a/bfxapi/examples/ws/resubscribe_orderbook.py b/bfxapi/examples/ws/resubscribe_orderbook.py index 7bbfb20..5e10277 100644 --- a/bfxapi/examples/ws/resubscribe_orderbook.py +++ b/bfxapi/examples/ws/resubscribe_orderbook.py @@ -1,7 +1,8 @@ import sys sys.path.append('../../../') -from bfxapi import Client, PUB_WS_HOST, PUB_REST_HOST +from bfxapi import Client +from bfxapi.constants import PUB_WS_HOST, PUB_REST_HOST # Retrieving orderbook requires public hosts bfx = Client( diff --git a/bfxapi/examples/ws/send_order.py b/bfxapi/examples/ws/send_order.py index f26d670..743934d 100644 --- a/bfxapi/examples/ws/send_order.py +++ b/bfxapi/examples/ws/send_order.py @@ -2,7 +2,8 @@ import os import sys sys.path.append('../../../') -from bfxapi import Client, Order, WS_HOST, REST_HOST +from bfxapi import Client, Order +from bfxapi.constants import WS_HOST, REST_HOST API_KEY=os.getenv("BFX_KEY") API_SECRET=os.getenv("BFX_SECRET") diff --git a/bfxapi/examples/ws/start_stop_connection.py b/bfxapi/examples/ws/start_stop_connection.py index 59de479..4ddafbf 100644 --- a/bfxapi/examples/ws/start_stop_connection.py +++ b/bfxapi/examples/ws/start_stop_connection.py @@ -1,7 +1,8 @@ import sys sys.path.append('../../../') -from bfxapi import Client, PUB_WS_HOST, PUB_REST_HOST +from bfxapi import Client +from bfxapi.constants import PUB_WS_HOST, PUB_REST_HOST bfx = Client( logLevel='DEBUG', diff --git a/bfxapi/examples/ws/subscribe_derivative_status.py b/bfxapi/examples/ws/subscribe_derivative_status.py index 52ca79d..3c5a995 100644 --- a/bfxapi/examples/ws/subscribe_derivative_status.py +++ b/bfxapi/examples/ws/subscribe_derivative_status.py @@ -1,7 +1,8 @@ import sys sys.path.append('../../../') -from bfxapi import Client, PUB_WS_HOST, PUB_REST_HOST +from bfxapi import Client +from bfxapi.constants import PUB_WS_HOST, PUB_REST_HOST # Retrieving derivative status requires public hosts bfx = Client( diff --git a/bfxapi/examples/ws/subscribe_orderbook.py b/bfxapi/examples/ws/subscribe_orderbook.py index bc06945..a9c44ab 100644 --- a/bfxapi/examples/ws/subscribe_orderbook.py +++ b/bfxapi/examples/ws/subscribe_orderbook.py @@ -2,7 +2,8 @@ import os import sys sys.path.append('../../../') -from bfxapi import Client, PUB_WS_HOST, PUB_REST_HOST +from bfxapi import Client +from bfxapi.constants import PUB_WS_HOST, PUB_REST_HOST # Retrieving trades/candles requires public hosts bfx = Client( diff --git a/bfxapi/examples/ws/subscribe_tickers.py b/bfxapi/examples/ws/subscribe_tickers.py index 2eb744f..616571e 100644 --- a/bfxapi/examples/ws/subscribe_tickers.py +++ b/bfxapi/examples/ws/subscribe_tickers.py @@ -1,7 +1,8 @@ import sys sys.path.append('../../../') -from bfxapi import Client, PUB_WS_HOST, PUB_REST_HOST +from bfxapi import Client +from bfxapi.constants import PUB_WS_HOST, PUB_REST_HOST # Retrieving tickers requires public hosts bfx = Client( diff --git a/bfxapi/examples/ws/subscribe_trades_candles.py b/bfxapi/examples/ws/subscribe_trades_candles.py index 1248dea..024eb58 100644 --- a/bfxapi/examples/ws/subscribe_trades_candles.py +++ b/bfxapi/examples/ws/subscribe_trades_candles.py @@ -1,7 +1,8 @@ import sys sys.path.append('../../../') -from bfxapi import Client, PUB_WS_HOST, PUB_REST_HOST +from bfxapi import Client +from bfxapi.constants import PUB_WS_HOST, PUB_REST_HOST # Retrieving trades/candles requires public hosts bfx = Client( diff --git a/bfxapi/examples/ws/update_order.py b/bfxapi/examples/ws/update_order.py index 936d1b6..8eb26ae 100644 --- a/bfxapi/examples/ws/update_order.py +++ b/bfxapi/examples/ws/update_order.py @@ -2,7 +2,8 @@ import os import sys sys.path.append('../../../') -from bfxapi import Client, Order, WS_HOST, REST_HOST +from bfxapi import Client, Order +from bfxapi.constants import WS_HOST, REST_HOST API_KEY=os.getenv("BFX_KEY") API_SECRET=os.getenv("BFX_SECRET") diff --git a/bfxapi/examples/ws/wallet_balance.py b/bfxapi/examples/ws/wallet_balance.py index 731faa6..f4af163 100644 --- a/bfxapi/examples/ws/wallet_balance.py +++ b/bfxapi/examples/ws/wallet_balance.py @@ -1,7 +1,8 @@ import os import sys sys.path.append('../../../') -from bfxapi import Client, WS_HOST, REST_HOST +from bfxapi import Client +from bfxapi.constants import WS_HOST, REST_HOST API_KEY=os.getenv("BFX_KEY") API_SECRET=os.getenv("BFX_SECRET") From fa7d3e26860e1ea5e6eb16094a889d9332d7c319 Mon Sep 17 00:00:00 2001 From: itsdeka Date: Tue, 4 Oct 2022 12:41:31 +0200 Subject: [PATCH 68/68] implemented liquidations endpoint (rest) --- CHANGELOG | 3 +++ bfxapi/examples/rest/get_liquidations.py | 21 +++++++++++++++++++++ bfxapi/rest/bfx_rest.py | 18 ++++++++++++++++++ bfxapi/version.py | 2 +- setup.py | 2 +- 5 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 bfxapi/examples/rest/get_liquidations.py diff --git a/CHANGELOG b/CHANGELOG index 8563409..c874132 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +2.0.3 +-) Implemented Liquidations endpoint (REST) + 2.0.2 -) Use private host for auth-based requests diff --git a/bfxapi/examples/rest/get_liquidations.py b/bfxapi/examples/rest/get_liquidations.py new file mode 100644 index 0000000..f7dd896 --- /dev/null +++ b/bfxapi/examples/rest/get_liquidations.py @@ -0,0 +1,21 @@ +import os +import sys +import asyncio +import time +sys.path.append('../../../') + +from bfxapi import Client, PUB_REST_HOST + +bfx = Client( + logLevel='INFO', + rest_host=PUB_REST_HOST +) + +now = int(round(time.time() * 1000)) +then = now - (1000 * 60 * 60 * 24 * 10) # 10 days ago + +async def get_liquidations(): + liquidations = await bfx.rest.get_liquidations(start=then, end=now) + print(liquidations) + +asyncio.get_event_loop().run_until_complete(get_liquidations()) diff --git a/bfxapi/rest/bfx_rest.py b/bfxapi/rest/bfx_rest.py index b276f13..fabd4f9 100644 --- a/bfxapi/rest/bfx_rest.py +++ b/bfxapi/rest/bfx_rest.py @@ -219,6 +219,24 @@ class BfxRest: status = await self.fetch(endpoint) return status + async def get_liquidations(self, start, end, limit=100, sort=-1): + """ + Endpoint to retrieve liquidations. By default it will retrieve the most recent liquidations, + but time-specific data can be retrieved using timestamps. + + # Attributes + @param start int: millisecond start time + @param end int: millisecond end time + @param limit int: max number of items in response (max. 500) + @param sort int: if = 1 it sorts results returned with old > new + @return Array [ POS_ID, MTS, SYMBOL, AMOUNT, BASE_PRICE, IS_MATCH, IS_MARKET_SOLD, PRICE_ACQUIRED ] + """ + endpoint = "liquidations/hist" + params = "?start={}&end={}&limit={}&sort={}".format( + start, end, limit, sort) + liquidations = await self.fetch(endpoint, params=params) + return liquidations + async def get_public_pulse_hist(self, end=None, limit=25): """ View the latest pulse messages. You can specify an end timestamp to view older messages. diff --git a/bfxapi/version.py b/bfxapi/version.py index 2b350b7..42f2fa1 100644 --- a/bfxapi/version.py +++ b/bfxapi/version.py @@ -2,4 +2,4 @@ This module contains the current version of the bfxapi lib """ -__version__ = '2.0.2' +__version__ = '2.0.3' diff --git a/setup.py b/setup.py index a5c1f42..126d5c1 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from os import path here = path.abspath(path.dirname(__file__)) setup( name='bitfinex-api-py', - version='2.0.2', + version='2.0.3', description='Official Bitfinex Python API', long_description='A Python reference implementation of the Bitfinex API for both REST and websocket interaction', long_description_content_type='text/markdown',