Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Exchanges] add error_describer for proxy erros #1427

Merged
merged 1 commit into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions Trading/Exchange/binance/binance_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,15 @@ def get_adapter_class(self):

async def get_account_id(self, **kwargs: dict) -> str:
try:
if self.exchange_manager.is_future:
raw_binance_balance = await self.connector.client.fapiPrivateV3GetBalance()
# accountAlias = unique account code
# from https://binance-docs.github.io/apidocs/futures/en/#futures-account-balance-v3-user_data
return raw_binance_balance[0]["accountAlias"]
else:
raw_balance = await self.connector.client.fetch_balance()
return raw_balance[ccxt_constants.CCXT_INFO]["uid"]
with self.connector.error_describer():
if self.exchange_manager.is_future:
raw_binance_balance = await self.connector.client.fapiPrivateV3GetBalance()
# accountAlias = unique account code
# from https://binance-docs.github.io/apidocs/futures/en/#futures-account-balance-v3-user_data
return raw_binance_balance[0]["accountAlias"]
else:
raw_balance = await self.connector.client.fetch_balance()
return raw_balance[ccxt_constants.CCXT_INFO]["uid"]
except (KeyError, IndexError):
# should not happen
raise
Expand Down
5 changes: 3 additions & 2 deletions Trading/Exchange/bingx/bingx_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,9 @@ def get_name(cls) -> str:
return 'bingx'

async def get_account_id(self, **kwargs: dict) -> str:
resp = await self.connector.client.accountV1PrivateGetUid()
return resp["data"]["uid"]
with self.connector.error_describer():
resp = await self.connector.client.accountV1PrivateGetUid()
return resp["data"]["uid"]

async def get_my_recent_trades(self, symbol=None, since=None, limit=None, **kwargs):
# On SPOT Bingx, account recent trades is available under fetch_closed_orders
Expand Down
62 changes: 32 additions & 30 deletions Trading/Exchange/coinbase/coinbase_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,37 +172,39 @@ def get_adapter_class(self):

async def get_account_id(self, **kwargs: dict) -> str:
try:
# warning might become deprecated
# https://docs.cloud.coinbase.com/sign-in-with-coinbase/docs/api-users
portfolio_id = None
accounts = await self.connector.client.fetch_accounts()
# use portfolio id when possible to enable "coinbase subaccounts" which are called "portfolios"
# note: oldest portfolio portfolio id == user id (from previous v2PrivateGetUser) when using master account
portfolio_ids = set(account[ccxt_constants.CCXT_INFO]['retail_portfolio_id'] for account in accounts)
if len(portfolio_ids) != 1:
is_up_to_date_key = self._is_up_to_date_api_key()
if is_up_to_date_key:
self.logger.error(
f"Unexpected: failed to identify Coinbase portfolio id on up to date API keys: "
f"{portfolio_ids=}"
with self.connector.error_describer():
# warning might become deprecated
# https://docs.cloud.coinbase.com/sign-in-with-coinbase/docs/api-users
portfolio_id = None
accounts = await self.connector.client.fetch_accounts()
# use portfolio id when possible to enable "coinbase subaccounts" which are called "portfolios"
# note: oldest portfolio portfolio id == user id (from previous v2PrivateGetUser) when
# using master account
portfolio_ids = set(account[ccxt_constants.CCXT_INFO]['retail_portfolio_id'] for account in accounts)
if len(portfolio_ids) != 1:
is_up_to_date_key = self._is_up_to_date_api_key()
if is_up_to_date_key:
self.logger.error(
f"Unexpected: failed to identify Coinbase portfolio id on up to date API keys: "
f"{portfolio_ids=}"
)
sorted_portfolios = sorted(
[
account[ccxt_constants.CCXT_INFO]
for account in accounts
],
key=lambda account: account["created_at"],
)
sorted_portfolios = sorted(
[
account[ccxt_constants.CCXT_INFO]
for account in accounts
],
key=lambda account: account["created_at"],
)
portfolio_id = sorted_portfolios[0]['retail_portfolio_id']
self.logger.info(
f"{len(portfolio_ids)} portfolio found on Coinbase account. "
f"This can happen with non up-to-date API keys ({is_up_to_date_key=}). "
f"Using the oldest portfolio id to bind to main account: {portfolio_id=}."
)
else:
portfolio_id = next(iter(portfolio_ids))
return portfolio_id
except ccxt.BaseError as err:
portfolio_id = sorted_portfolios[0]['retail_portfolio_id']
self.logger.info(
f"{len(portfolio_ids)} portfolio found on Coinbase account. "
f"This can happen with non up-to-date API keys ({is_up_to_date_key=}). "
f"Using the oldest portfolio id to bind to main account: {portfolio_id=}."
)
else:
portfolio_id = next(iter(portfolio_ids))
return portfolio_id
except (ccxt.BaseError, octobot_trading.errors.OctoBotExchangeError) as err:
self.logger.exception(
err, True,
f"Error when fetching {self.get_name()} account id: {err} ({err.__class__.__name__}). "
Expand Down
5 changes: 3 additions & 2 deletions Trading/Exchange/hollaex/hollaex_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,9 @@ def is_configurable(cls):
return True

async def get_account_id(self, **kwargs: dict) -> str:
user_info = await self.connector.client.private_get_user()
return user_info["id"]
with self.connector.error_describer():
user_info = await self.connector.client.private_get_user()
return user_info["id"]

async def get_symbol_prices(self, symbol, time_frame, limit: int = None, **kwargs: dict):
# ohlcv without limit is not supported, replaced by a default max limit
Expand Down
93 changes: 46 additions & 47 deletions Trading/Exchange/kucoin/kucoin_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import octobot_trading.exchanges.connectors.ccxt.ccxt_connector as ccxt_connector
import octobot_trading.exchanges.connectors.ccxt.enums as ccxt_enums
import octobot_trading.exchanges.connectors.ccxt.constants as ccxt_constants
import octobot_trading.exchanges.connectors.ccxt.ccxt_client_util as ccxt_client_util
import octobot_commons.constants as commons_constants
import octobot_trading.constants as constants
import octobot_trading.enums as trading_enums
Expand Down Expand Up @@ -175,56 +176,54 @@ async def get_account_id(self, **kwargs: dict) -> str:
# It is currently impossible to fetch subaccounts account id, use a constant value to identify it.
# updated: 21/05/2024
try:
account_id = None
subaccount_id = None
sub_accounts = await self.connector.client.private_get_sub_accounts()
accounts = sub_accounts.get("data", {}).get("items", {})
has_subaccounts = bool(accounts)
if has_subaccounts:
if len(accounts) == 1:
# only 1 account: use its id or name
account = accounts[0]
# try using subUserId if available
# 'ex subUserId: 65d41ea409407d000160cc17 subName: octobot1'
account_id = account.get("subUserId") or account["subName"]
else:
# more than 1 account: consider other accounts
for account in accounts:
if account["subUserId"]:
subaccount_id = account["subName"]
else:
# only subaccounts have a subUserId: if this condition is True, we are on the main account
account_id = account["subName"]
if account_id and self.exchange_manager.is_future:
account_id = octobot.community.to_community_exchange_internal_name(
account_id, commons_constants.CONFIG_EXCHANGE_FUTURE
with self.connector.error_describer():
account_id = None
subaccount_id = None
sub_accounts = await self.connector.client.private_get_sub_accounts()
accounts = sub_accounts.get("data", {}).get("items", {})
has_subaccounts = bool(accounts)
if has_subaccounts:
if len(accounts) == 1:
# only 1 account: use its id or name
account = accounts[0]
# try using subUserId if available
# 'ex subUserId: 65d41ea409407d000160cc17 subName: octobot1'
account_id = account.get("subUserId") or account["subName"]
else:
# more than 1 account: consider other accounts
for account in accounts:
if account["subUserId"]:
subaccount_id = account["subName"]
else:
# only subaccounts have a subUserId: if this condition is True, we are on the main account
account_id = account["subName"]
if account_id and self.exchange_manager.is_future:
account_id = octobot.community.to_community_exchange_internal_name(
account_id, commons_constants.CONFIG_EXCHANGE_FUTURE
)
if subaccount_id:
# there is at least a subaccount: ensure the current account is the main account as there is no way
# to know the id of the current account (only a list of existing accounts)
subaccount_api_key_details = await self.connector.client.private_get_sub_api_key(
{"subName": subaccount_id}
)
if subaccount_id:
# there is at least a subaccount: ensure the current account is the main account as there is no way
# to know the id of the current account (only a list of existing accounts)
subaccount_api_key_details = await self.connector.client.private_get_sub_api_key(
{"subName": subaccount_id}
)
if "data" not in subaccount_api_key_details or "msg" in subaccount_api_key_details:
# subaccounts can't fetch other accounts data, if this is False, we are on a subaccount
if "data" not in subaccount_api_key_details or "msg" in subaccount_api_key_details:
# subaccounts can't fetch other accounts data, if this is False, we are on a subaccount
self.logger.error(
f"kucoin api changed: it is now possible to call private_get_sub_accounts on subaccounts. "
f"kucoin get_account_id has to be updated. "
f"sub_accounts={sub_accounts} subaccount_api_key_details={subaccount_api_key_details}"
)
return constants.DEFAULT_ACCOUNT_ID
if has_subaccounts and account_id is None:
self.logger.error(
f"kucoin api changed: it is now possible to call private_get_sub_accounts on subaccounts. "
f"kucoin get_account_id has to be updated. "
f"sub_accounts={sub_accounts} subaccount_api_key_details={subaccount_api_key_details}"
f"kucoin api changed: can't fetch master account account_id. "
f"kucoin get_account_id has to be updated."
f"sub_accounts={sub_accounts}"
)
return constants.DEFAULT_ACCOUNT_ID
if has_subaccounts and account_id is None:
self.logger.error(
f"kucoin api changed: can't fetch master account account_id. "
f"kucoin get_account_id has to be updated."
f"sub_accounts={sub_accounts}"
)
account_id = constants.DEFAULT_ACCOUNT_ID
# we are on the master account
return account_id or constants.DEFAULT_ACCOUNT_ID
except ccxt.AuthenticationError:
# when api key is wrong
raise
account_id = constants.DEFAULT_ACCOUNT_ID
# we are on the master account
return account_id or constants.DEFAULT_ACCOUNT_ID
except ccxt.ExchangeError as err:
# ExchangeError('kucoin This user is not a master user')
if "not a master user" not in str(err):
Expand Down
3 changes: 2 additions & 1 deletion Trading/Exchange/okx/okx_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,8 @@ def _fix_limit(self, limit: int) -> int:
async def get_account_id(self, **kwargs: dict) -> str:
accounts = await self.connector.client.fetch_accounts()
try:
return accounts[0]["id"]
with self.connector.error_describer():
return accounts[0]["id"]
except IndexError:
# should never happen as at least one account should be available
return None
Expand Down
Loading