diff --git a/packages/trading/octobot_trading/exchanges/__init__.py b/packages/trading/octobot_trading/exchanges/__init__.py index 29e0a800f2..420e05cdee 100644 --- a/packages/trading/octobot_trading/exchanges/__init__.py +++ b/packages/trading/octobot_trading/exchanges/__init__.py @@ -117,6 +117,7 @@ BacktestingExchangeConfig, ExchangeProxyConfig, ExchangeCredentialsData, + ExchangeTradingProvider, ) from octobot_trading.exchanges import traders from octobot_trading.exchanges.traders import ( @@ -159,6 +160,7 @@ "BacktestingExchangeConfig", "ExchangeProxyConfig", "ExchangeCredentialsData", + "ExchangeTradingProvider", "ExchangeManager", "ExchangeBuilder", "create_exchange_builder_instance", diff --git a/packages/trading/octobot_trading/exchanges/adapters/abstract_adapter.py b/packages/trading/octobot_trading/exchanges/adapters/abstract_adapter.py index be1e5c2a33..16a25a8d21 100644 --- a/packages/trading/octobot_trading/exchanges/adapters/abstract_adapter.py +++ b/packages/trading/octobot_trading/exchanges/adapters/abstract_adapter.py @@ -155,6 +155,11 @@ def adapt_deposit_address(self, raw, **kwargs) -> dict: fixed = self.fix_deposit_address(raw, **kwargs) return self.parse_deposit_address(fixed, **kwargs) + @_adapter + def adapt_exchange_trading_providers(self, raw, **kwargs) -> list: + fixed = self.fix_exchange_trading_providers(raw, **kwargs) + return self.parse_exchange_trading_providers(fixed, **kwargs) + def get_uniformized_timestamp(self, timestamp) -> float: # override if the exchange time is not a second timestamp or millisecond if timestamp is not None and timestamp > 16728292300: # Friday 5 February 2500 11:51:40 @@ -309,3 +314,10 @@ def fix_deposit_address(self, raw, **kwargs) -> dict: def parse_deposit_address(self, fixed, **kwargs) -> dict: raise NotImplementedError("parse_deposit_address is not implemented") + + def fix_exchange_trading_providers(self, raw, **kwargs): + # add generic logic if necessary + return raw + + def parse_exchange_trading_providers(self, fixed, **kwargs) -> list: + raise NotImplementedError("parse_exchange_trading_providers is not implemented") diff --git a/packages/trading/octobot_trading/exchanges/config/__init__.py b/packages/trading/octobot_trading/exchanges/config/__init__.py index 2f6e5d1423..3075a32ae5 100644 --- a/packages/trading/octobot_trading/exchanges/config/__init__.py +++ b/packages/trading/octobot_trading/exchanges/config/__init__.py @@ -30,10 +30,14 @@ from octobot_trading.exchanges.config.exchange_credentials_data import ( ExchangeCredentialsData, ) +from octobot_trading.exchanges.config.exchange_trading_provider import ( + ExchangeTradingProvider, +) __all__ = [ "ExchangeConfig", "BacktestingExchangeConfig", "ExchangeProxyConfig", "ExchangeCredentialsData", + "ExchangeTradingProvider", ] diff --git a/packages/trading/octobot_trading/exchanges/config/exchange_trading_provider.py b/packages/trading/octobot_trading/exchanges/config/exchange_trading_provider.py new file mode 100644 index 0000000000..460e4eee77 --- /dev/null +++ b/packages/trading/octobot_trading/exchanges/config/exchange_trading_provider.py @@ -0,0 +1,26 @@ +# Drakkar-Software OctoBot-Trading +# Copyright (c) Drakkar-Software, All rights reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 3.0 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. +import dataclasses +import typing + + +@dataclasses.dataclass +class ExchangeTradingProvider: + """ + Sometimes used by exchanges to identify the provider of a trade, when not the exchange itself. + """ + name: str + rating: typing.Optional[str] = None diff --git a/packages/trading/octobot_trading/exchanges/connectors/ccxt/ccxt_connector.py b/packages/trading/octobot_trading/exchanges/connectors/ccxt/ccxt_connector.py index 529c555ccb..fdd34e10c3 100644 --- a/packages/trading/octobot_trading/exchanges/connectors/ccxt/ccxt_connector.py +++ b/packages/trading/octobot_trading/exchanges/connectors/ccxt/ccxt_connector.py @@ -566,6 +566,15 @@ async def get_dex_pairs(self, symbols: list[str], **kwargs: dict) -> list[dict]: await self.client.ob_fetch_dex_pairs(symbols, **kwargs) ) + async def get_exchange_trading_providers(self, **kwargs: dict) -> list: + if not self.client.has.get('obGetExchangeTradingProviders'): + raise octobot_trading.errors.NotSupported( + "This exchange doesn't support obGetExchangeTradingProviders" + ) + return self.adapter.adapt_exchange_trading_providers( + await self.client.ob_get_exchange_trading_providers(**kwargs) + ) + @ccxt_client_util.converted_ccxt_common_errors async def get_account_id(self, **kwargs: dict) -> str: if not self.client.has.get('fetchAccountId'): diff --git a/packages/trading/octobot_trading/exchanges/types/rest_exchange.py b/packages/trading/octobot_trading/exchanges/types/rest_exchange.py index 3f4730c8e8..75f93a983d 100644 --- a/packages/trading/octobot_trading/exchanges/types/rest_exchange.py +++ b/packages/trading/octobot_trading/exchanges/types/rest_exchange.py @@ -616,6 +616,9 @@ def get_market_status(self, symbol, price_example=None, with_fixer=True): async def get_dex_pairs(self, symbols: list[str], **kwargs: dict) -> list[dict]: return await self.connector.get_dex_pairs(symbols, **kwargs) + async def get_exchange_trading_providers(self, **kwargs: dict) -> list: + return await self.connector.get_exchange_trading_providers(**kwargs) + def uses_demo_trading_instead_of_sandbox(self, exchange_type: enums.ExchangeTypes) -> bool: return self.connector.uses_demo_trading_instead_of_sandbox(exchange_type) diff --git a/packages/trading/tests_additional/real_exchanges/real_exchange_tester.py b/packages/trading/tests_additional/real_exchanges/real_exchange_tester.py index 502ac898ca..eb99f9ef51 100644 --- a/packages/trading/tests_additional/real_exchanges/real_exchange_tester.py +++ b/packages/trading/tests_additional/real_exchanges/real_exchange_tester.py @@ -1030,9 +1030,9 @@ async def assert_get_recent_trades(self, limit=50): ) self.ensure_elements_order(recent_trades, trading_enums.ExchangeConstantsTickersColumns.TIMESTAMP.value) - async def get_price_ticker(self, symbol: typing.Optional[str] = None): + async def get_price_ticker(self, symbol: typing.Optional[str] = None, **kwargs: dict): async with self.get_exchange_manager() as exchange_manager: - return await exchange_manager.exchange.get_price_ticker(symbol or self.SYMBOL) + return await exchange_manager.exchange.get_price_ticker(symbol or self.SYMBOL, **kwargs) async def assert_get_price_ticker( self, @@ -1040,13 +1040,14 @@ async def assert_get_price_ticker( *, symbol: typing.Optional[str] = None, ticker_expectations: typing.Optional[TickerRequiredExpectations] = None, + **kwargs: dict, ): symbol = symbol or self.SYMBOL async with self.get_exchange_manager() as exchange_manager: no_volume_in_ticker = exchange_manager.exchange.get_option_value( trading_enums.ExchangeClientOptions.NO_VOLUME_IN_TICKER ) - ticker = await exchange_manager.exchange.get_price_ticker(symbol) + ticker = await exchange_manager.exchange.get_price_ticker(symbol, **kwargs) self._check_ticker( ticker, symbol, extra_checks=extra_checks, no_volume_in_ticker=no_volume_in_ticker, )