diff --git a/additional_tests/exchanges_tests/abstract_authenticated_exchange_tester.py b/additional_tests/exchanges_tests/abstract_authenticated_exchange_tester.py index d318ad943..8c8576edf 100644 --- a/additional_tests/exchanges_tests/abstract_authenticated_exchange_tester.py +++ b/additional_tests/exchanges_tests/abstract_authenticated_exchange_tester.py @@ -29,6 +29,7 @@ import octobot_trading.enums as trading_enums import octobot_trading.constants as trading_constants import octobot_trading.exchanges as trading_exchanges +import octobot_trading.exchanges.connectors.ccxt.constants as ccxt_constants import octobot_trading.personal_data as personal_data import octobot_trading.personal_data.orders as personal_data_orders import octobot_trading.util.test_tools.exchanges_test_tools as exchanges_test_tools @@ -52,6 +53,18 @@ class AbstractAuthenticatedExchangeTester: SETTLEMENT_CURRENCY = "USDT" SYMBOL = f"{ORDER_CURRENCY}/{SETTLEMENT_CURRENCY}" VALID_ORDER_ID = "8bb80a81-27f7-4415-aa50-911ea46d841c" + SPECIAL_ORDER_TYPES_BY_EXCHANGE_ID: dict[ + str, ( + str, # symbol + str, # order type key in 'info' dict + str, # order type found in 'info' dict + str, # parsed trading_enums.TradeOrderType + str, # parsed trading_enums.TradeOrderSide + bool, # trigger above (on higher price than order price) + ) + ] = {} # stop loss / take profit and other special order types to be successfully parsed + # details of an order that exists but can"t be cancelled + UNCANCELLABLE_ORDER_ID_SYMBOL_TYPE: tuple[str, str, trading_enums.TraderOrderType] = None ORDER_SIZE = 10 # % of portfolio to include in test orders PORTFOLIO_TYPE_FOR_SIZE = trading_constants.CONFIG_PORTFOLIO_FREE CONVERTS_ORDER_SIZE_BEFORE_PUSHING_TO_EXCHANGES = False @@ -327,8 +340,53 @@ async def inner_test_is_valid_account(self): assert isinstance(error, str) assert len(error) > 0 + + async def test_get_special_orders(self): + if self.SPECIAL_ORDER_TYPES_BY_EXCHANGE_ID: + async with self.local_exchange_manager(): + await self.inner_test_get_special_orders() + + async def inner_test_get_special_orders(self): + # open_orders = await self.get_open_orders(_symbols=["TAO/USDT"]) + # print(open_orders) # to print special orders when they are open + # return + for exchange_id, order_details in self.SPECIAL_ORDER_TYPES_BY_EXCHANGE_ID.items(): + symbol, info_key, info_type, expected_type, expected_side, expected_trigger_above = order_details + fetched_order = await self.exchange_manager.exchange.get_order(exchange_id, symbol=symbol) + assert fetched_order is not None + self._check_fetched_order_dicts([fetched_order]) + # ensure parsing order doesn't crash + parsed = personal_data.create_order_instance_from_raw(self.exchange_manager.trader, fetched_order) + assert isinstance(parsed, personal_data.Order) + + assert fetched_order[trading_enums.ExchangeConstantsOrderColumns.SYMBOL.value] == symbol + found_type = fetched_order[ccxt_constants.CCXT_INFO][info_key] + assert found_type == info_type, f"[{exchange_id}]: {found_type} != {info_type}" + parsed_type = fetched_order[trading_enums.ExchangeConstantsOrderColumns.TYPE.value] + assert parsed_type == expected_type, f"[{exchange_id}]: {parsed_type} != {expected_type}" + assert fetched_order[trading_enums.ExchangeConstantsOrderColumns.SIDE.value] == expected_side + if expected_trigger_above is None: + assert trading_enums.ExchangeConstantsOrderColumns.TRIGGER_ABOVE.value not in fetched_order + else: + parsed_trigger_above = fetched_order[trading_enums.ExchangeConstantsOrderColumns.TRIGGER_ABOVE.value] + assert parsed_trigger_above == expected_trigger_above, ( + f"[{exchange_id}]: {parsed_trigger_above} != {expected_trigger_above}" + ) + if isinstance(parsed, personal_data.LimitOrder): + assert parsed.trigger_above == parsed_trigger_above + if expected_type == trading_enums.TradeOrderType.LIMIT.value: + assert fetched_order[trading_enums.ExchangeConstantsOrderColumns.PRICE.value] > 0 + elif expected_type == trading_enums.TradeOrderType.STOP_LOSS.value: + assert fetched_order[trading_enums.ExchangeConstantsOrderColumns.STOP_PRICE.value] > 0 + elif expected_type == trading_enums.TradeOrderType.UNSUPPORTED.value: + assert isinstance(parsed, personal_data.UnsupportedOrder) + else: + # ensure all cases are covered, otherwise there is a problem in order type parsing + assert expected_type == trading_enums.TradeOrderType.MARKET + async def test_create_and_cancel_limit_orders(self): async with self.local_exchange_manager(): + await self.inner_test_cancel_uncancellable_order() await self.inner_test_create_and_cancel_limit_orders() async def inner_test_create_and_cancel_limit_orders(self, symbol=None, settlement_currency=None, margin_type=None): @@ -396,6 +454,12 @@ async def inner_test_create_and_cancel_limit_orders(self, symbol=None, settlemen assert await self.order_not_in_open_orders(open_orders, buy_limit, symbol=symbol) assert await self.order_in_cancelled_orders(cancelled_orders, buy_limit, symbol=symbol) + async def inner_test_cancel_uncancellable_order(self): + if self.UNCANCELLABLE_ORDER_ID_SYMBOL_TYPE: + order_id, symbol, order_type = self.UNCANCELLABLE_ORDER_ID_SYMBOL_TYPE + with pytest.raises(trading_errors.ExchangeOrderCancelError): + await self.exchange_manager.exchange.cancel_order(order_id, symbol, order_type) + async def test_create_and_fill_market_orders(self): async with self.local_exchange_manager(): await self.inner_test_create_and_fill_market_orders() @@ -747,12 +811,12 @@ async def get_my_recent_trades(self, exchange_data=None): async def get_closed_orders(self, symbol=None): return await self.exchange_manager.exchange.get_closed_orders(symbol or self.SYMBOL) - async def get_cancelled_orders(self, exchange_data=None, force_fetch=False): + async def get_cancelled_orders(self, exchange_data=None, force_fetch=False, _symbols=None): if not force_fetch and not self.exchange_manager.exchange.SUPPORT_FETCHING_CANCELLED_ORDERS: # skipped return [] exchange_data = exchange_data or self.get_exchange_data() - return await exchanges_test_tools.get_cancelled_orders(self.exchange_manager, exchange_data) + return await exchanges_test_tools.get_cancelled_orders(self.exchange_manager, exchange_data, symbols=_symbols) async def check_require_closed_orders_from_recent_trades(self, symbol=None): if self.exchange_manager.exchange.REQUIRE_CLOSED_ORDERS_FROM_RECENT_TRADES: @@ -1088,9 +1152,9 @@ def get_order_price(self, price, is_above_price, symbol=None, price_diff=None): price * (decimal.Decimal(str(multiplier))) ) - async def get_open_orders(self, exchange_data=None): + async def get_open_orders(self, exchange_data=None, _symbols=None): exchange_data = exchange_data or self.get_exchange_data() - orders = await exchanges_test_tools.get_open_orders(self.exchange_manager, exchange_data) + orders = await exchanges_test_tools.get_open_orders(self.exchange_manager, exchange_data, symbols=_symbols) self.check_duplicate(orders) self._check_fetched_order_dicts(orders) return orders diff --git a/additional_tests/exchanges_tests/test_ascendex.py b/additional_tests/exchanges_tests/test_ascendex.py index f43b2800d..10d90d2fd 100644 --- a/additional_tests/exchanges_tests/test_ascendex.py +++ b/additional_tests/exchanges_tests/test_ascendex.py @@ -63,6 +63,9 @@ async def test_get_not_found_order(self): async def test_is_valid_account(self): await super().test_is_valid_account() + async def test_get_special_orders(self): + await super().test_get_special_orders() + async def test_create_and_cancel_limit_orders(self): await super().test_create_and_cancel_limit_orders() diff --git a/additional_tests/exchanges_tests/test_binance.py b/additional_tests/exchanges_tests/test_binance.py index 7755dcc6a..afef1004d 100644 --- a/additional_tests/exchanges_tests/test_binance.py +++ b/additional_tests/exchanges_tests/test_binance.py @@ -14,6 +14,7 @@ # You should have received a copy of the GNU General Public # License along with OctoBot. If not, see . import pytest +import octobot_trading.enums from additional_tests.exchanges_tests import abstract_authenticated_exchange_tester @@ -41,6 +42,38 @@ class TestBinanceAuthenticatedExchange( IS_BROKER_ENABLED_ACCOUNT = False IS_AUTHENTICATED_REQUEST_CHECK_AVAILABLE = True # set True when is_authenticated_request is implemented + SPECIAL_ORDER_TYPES_BY_EXCHANGE_ID: dict[ + str, ( + str, # symbol + str, # order type key in 'info' dict + str, # order type found in 'info' dict + str, # parsed trading_enums.TradeOrderType + str, # parsed trading_enums.TradeOrderSide + bool, # trigger above (on higher price than order price) + ) + ] = { + "6799804660": ( + "BNB/USDT", "type", "TAKE_PROFIT", + octobot_trading.enums.TradeOrderType.LIMIT.value, octobot_trading.enums.TradeOrderSide.SELL.value, True + ), + '6799810041': ( + "BNB/USDT", "type", "STOP_LOSS", + octobot_trading.enums.TradeOrderType.STOP_LOSS.value, octobot_trading.enums.TradeOrderSide.SELL.value, False + ), + '6799798838': ( + "BNB/USDT", "type", "TAKE_PROFIT_LIMIT", + octobot_trading.enums.TradeOrderType.LIMIT.value, octobot_trading.enums.TradeOrderSide.SELL.value, True + ), + '6799795001': ( + "BNB/USDT", "type", "STOP_LOSS_LIMIT", + octobot_trading.enums.TradeOrderType.STOP_LOSS.value, octobot_trading.enums.TradeOrderSide.SELL.value, False + ), + } # stop loss / take profit and other special order types to be successfully parsed + # details of an order that exists but can"t be cancelled + UNCANCELLABLE_ORDER_ID_SYMBOL_TYPE: tuple[str, str, octobot_trading.enums.TraderOrderType] = ( + "6799798838", "BNB/USDT", octobot_trading.enums.TraderOrderType.BUY_LIMIT.value + ) + async def test_get_portfolio(self): await super().test_get_portfolio() @@ -69,6 +102,9 @@ async def test_get_not_found_order(self): async def test_is_valid_account(self): await super().test_is_valid_account() + async def test_get_special_orders(self): + await super().test_get_special_orders() + async def test_create_and_cancel_limit_orders(self): await super().test_create_and_cancel_limit_orders() diff --git a/additional_tests/exchanges_tests/test_binance_futures.py b/additional_tests/exchanges_tests/test_binance_futures.py index 57cc09cde..c7a2580c6 100644 --- a/additional_tests/exchanges_tests/test_binance_futures.py +++ b/additional_tests/exchanges_tests/test_binance_futures.py @@ -15,6 +15,7 @@ # License along with OctoBot. If not, see . import pytest +import octobot_trading.enums from additional_tests.exchanges_tests import abstract_authenticated_future_exchange_tester, \ abstract_authenticated_exchange_tester @@ -38,6 +39,38 @@ class TestBinanceFuturesAuthenticatedExchange( EXPECTED_QUOTE_MIN_ORDER_SIZE = 200 # min quote value of orders to create (used to check market status parsing) IS_AUTHENTICATED_REQUEST_CHECK_AVAILABLE = True # set True when is_authenticated_request is implemented + SPECIAL_ORDER_TYPES_BY_EXCHANGE_ID: dict[ + str, ( + str, # symbol + str, # order type key in 'info' dict + str, # order type found in 'info' dict + str, # parsed trading_enums.TradeOrderType + str, # parsed trading_enums.TradeOrderSide + bool, # trigger above (on higher price than order price) + ) + ] = { + "4075521283": ( + "BTC/USDT:USDT", "type", "TAKE_PROFIT_MARKET", + octobot_trading.enums.TradeOrderType.LIMIT.value, octobot_trading.enums.TradeOrderSide.SELL.value, True + ), + '622529': ( + "BTC/USDC:USDC", "type", "STOP_MARKET", + octobot_trading.enums.TradeOrderType.STOP_LOSS.value, octobot_trading.enums.TradeOrderSide.SELL.value, False + ), + '4076521927': ( + "BTC/USDT:USDT", "type", "TAKE_PROFIT", + octobot_trading.enums.TradeOrderType.LIMIT.value, octobot_trading.enums.TradeOrderSide.BUY.value, False + ), + '4076521976': ( + "BTC/USDT:USDT", "type", "STOP", + octobot_trading.enums.TradeOrderType.STOP_LOSS.value, octobot_trading.enums.TradeOrderSide.SELL.value, False + ), + } # stop loss / take profit and other special order types to be successfully parsed + # details of an order that exists but can"t be cancelled + UNCANCELLABLE_ORDER_ID_SYMBOL_TYPE: tuple[str, str, octobot_trading.enums.TraderOrderType] = ( + "4076521927", "BTC/USDT:USDT", octobot_trading.enums.TraderOrderType.BUY_LIMIT.value + ) + async def _set_account_types(self, account_types): # todo remove this and use both types when exchange-side multi portfolio is enabled self.exchange_manager.exchange._futures_account_types = account_types @@ -86,6 +119,9 @@ async def test_get_and_set_leverage(self): async def test_is_valid_account(self): await super().test_is_valid_account() + async def test_get_special_orders(self): + await super().test_get_special_orders() + async def test_create_and_cancel_limit_orders(self): await super().test_create_and_cancel_limit_orders() diff --git a/additional_tests/exchanges_tests/test_bingx.py b/additional_tests/exchanges_tests/test_bingx.py index 8d402a4d1..e40a9b160 100644 --- a/additional_tests/exchanges_tests/test_bingx.py +++ b/additional_tests/exchanges_tests/test_bingx.py @@ -14,6 +14,7 @@ # You should have received a copy of the GNU General Public # License along with OctoBot. If not, see . import pytest +import octobot_trading.enums from additional_tests.exchanges_tests import abstract_authenticated_exchange_tester @@ -38,6 +39,38 @@ class TestBingxAuthenticatedExchange( VALID_ORDER_ID = "1812980957928929280" + SPECIAL_ORDER_TYPES_BY_EXCHANGE_ID: dict[ + str, ( + str, # symbol + str, # order type key in 'info' dict + str, # order type found in 'info' dict + str, # parsed trading_enums.TradeOrderType + str, # parsed trading_enums.TradeOrderSide + bool, # trigger above (on higher price than order price) + ) + ] = { + "1877004154170146816": ( + "TAO/USDT", "type", "TAKE_STOP_MARKET", + octobot_trading.enums.TradeOrderType.STOP_LOSS.value, octobot_trading.enums.TradeOrderSide.SELL.value, False + ), + '1877004191864356864': ( + "TAO/USDT", "type", "TAKE_STOP_MARKET", + octobot_trading.enums.TradeOrderType.LIMIT.value, octobot_trading.enums.TradeOrderSide.SELL.value, True + ), + '1877004220704391168': ( + "TAO/USDT", "type", "TAKE_STOP_LIMIT", + octobot_trading.enums.TradeOrderType.UNSUPPORTED.value, octobot_trading.enums.TradeOrderSide.SELL.value, None + ), + '1877004292053696512': ( + "TAO/USDT", "type", "TAKE_STOP_LIMIT", + octobot_trading.enums.TradeOrderType.UNSUPPORTED.value, octobot_trading.enums.TradeOrderSide.SELL.value, None + ), + } # stop loss / take profit and other special order types to be successfully parsed + # details of an order that exists but can"t be cancelled + UNCANCELLABLE_ORDER_ID_SYMBOL_TYPE: tuple[str, str, octobot_trading.enums.TraderOrderType] = ( + "1877004292053696512", "TAO/USDT", octobot_trading.enums.TraderOrderType.SELL_LIMIT.value + ) + async def test_get_portfolio(self): await super().test_get_portfolio() @@ -65,6 +98,9 @@ async def test_get_not_found_order(self): async def test_is_valid_account(self): await super().test_is_valid_account() + async def test_get_special_orders(self): + await super().test_get_special_orders() + async def test_create_and_cancel_limit_orders(self): await super().test_create_and_cancel_limit_orders() diff --git a/additional_tests/exchanges_tests/test_bitget.py b/additional_tests/exchanges_tests/test_bitget.py index 33c03e91d..e62de2920 100644 --- a/additional_tests/exchanges_tests/test_bitget.py +++ b/additional_tests/exchanges_tests/test_bitget.py @@ -65,6 +65,9 @@ async def test_get_not_found_order(self): async def test_is_valid_account(self): await super().test_is_valid_account() + async def test_get_special_orders(self): + await super().test_get_special_orders() + async def test_create_and_cancel_limit_orders(self): await super().test_create_and_cancel_limit_orders() diff --git a/additional_tests/exchanges_tests/test_bitmart.py b/additional_tests/exchanges_tests/test_bitmart.py index f1eacab34..58146e68a 100644 --- a/additional_tests/exchanges_tests/test_bitmart.py +++ b/additional_tests/exchanges_tests/test_bitmart.py @@ -63,6 +63,9 @@ async def test_get_not_found_order(self): async def test_is_valid_account(self): await super().test_is_valid_account() + async def test_get_special_orders(self): + await super().test_get_special_orders() + async def test_create_and_cancel_limit_orders(self): await super().test_create_and_cancel_limit_orders() diff --git a/additional_tests/exchanges_tests/test_bybit.py b/additional_tests/exchanges_tests/test_bybit.py index 1eb4dde3c..6bce71e67 100644 --- a/additional_tests/exchanges_tests/test_bybit.py +++ b/additional_tests/exchanges_tests/test_bybit.py @@ -67,6 +67,9 @@ async def test_get_not_found_order(self): async def test_is_valid_account(self): await super().test_is_valid_account() + async def test_get_special_orders(self): + await super().test_get_special_orders() + async def test_create_and_cancel_limit_orders(self): await super().test_create_and_cancel_limit_orders() diff --git a/additional_tests/exchanges_tests/test_bybit_futures.py b/additional_tests/exchanges_tests/test_bybit_futures.py index 950b07c0b..98678b74c 100644 --- a/additional_tests/exchanges_tests/test_bybit_futures.py +++ b/additional_tests/exchanges_tests/test_bybit_futures.py @@ -79,6 +79,9 @@ async def test_get_and_set_leverage(self): async def test_is_valid_account(self): await super().test_is_valid_account() + async def test_get_special_orders(self): + await super().test_get_special_orders() + async def test_create_and_cancel_limit_orders(self): await super().test_create_and_cancel_limit_orders() diff --git a/additional_tests/exchanges_tests/test_coinbase.py b/additional_tests/exchanges_tests/test_coinbase.py index 7b3790f06..6176f14f8 100644 --- a/additional_tests/exchanges_tests/test_coinbase.py +++ b/additional_tests/exchanges_tests/test_coinbase.py @@ -15,6 +15,7 @@ # License along with OctoBot. If not, see . import pytest +import octobot_trading.enums from additional_tests.exchanges_tests import abstract_authenticated_exchange_tester # All test coroutines will be treated as marked. @@ -29,7 +30,7 @@ class TestCoinbaseAuthenticatedExchange( ORDER_CURRENCY = "ADA" SETTLEMENT_CURRENCY = "BTC" SYMBOL = f"{ORDER_CURRENCY}/{SETTLEMENT_CURRENCY}" - ORDER_SIZE = 5 # % of portfolio to include in test orders + ORDER_SIZE = 25 # % of portfolio to include in test orders CONVERTS_ORDER_SIZE_BEFORE_PUSHING_TO_EXCHANGES = True VALID_ORDER_ID = "8bb80a81-27f7-4415-aa50-911ea46d841c" USE_ORDER_OPERATION_TO_CHECK_API_KEY_RIGHTS = True # set True when api key rights can't be checked using a @@ -37,6 +38,34 @@ class TestCoinbaseAuthenticatedExchange( IS_BROKER_ENABLED_ACCOUNT = False IS_AUTHENTICATED_REQUEST_CHECK_AVAILABLE = True # set True when is_authenticated_request is implemented + SPECIAL_ORDER_TYPES_BY_EXCHANGE_ID: dict[ + str, ( + str, # symbol + str, # order type key in 'info' dict + str, # order type found in 'info' dict + str, # parsed trading_enums.TradeOrderType + str, # parsed trading_enums.TradeOrderSide + bool, # trigger above (on higher price than order price) + ) + ] = { + '7e03c745-7340-49ef-8af1-b8f7fe431c8a': ( + "BTC/EUR", "order_type", "STOP_LIMIT", # sell at a lower price + octobot_trading.enums.TradeOrderType.STOP_LOSS.value, octobot_trading.enums.TradeOrderSide.SELL.value, False + ), + '1e2f0918-5728-4c68-b8f4-6fd804396248': ( + "ETH/BTC", "order_type", "STOP_LIMIT", # buy at a higher price + octobot_trading.enums.TradeOrderType.STOP_LOSS.value, octobot_trading.enums.TradeOrderSide.BUY.value, True + ), + 'f4016e50-1f0b-4caa-abe5-1ec00af18be9': ( + "ETH/BTC", "order_type", "STOP_LIMIT", # buy at a lower price + octobot_trading.enums.TradeOrderType.LIMIT.value, octobot_trading.enums.TradeOrderSide.BUY.value, False + ), + } # stop loss / take profit and other special order types to be successfully parsed + # details of an order that exists but can"t be cancelled + UNCANCELLABLE_ORDER_ID_SYMBOL_TYPE: tuple[str, str, octobot_trading.enums.TraderOrderType] = ( + "f4016e50-1f0b-4caa-abe5-1ec00af18be9", "ETH/BTC", octobot_trading.enums.TraderOrderType.BUY_LIMIT.value + ) + async def test_get_portfolio(self): await super().test_get_portfolio() @@ -64,6 +93,9 @@ async def test_get_not_found_order(self): async def test_is_valid_account(self): await super().test_is_valid_account() + async def test_get_special_orders(self): + await super().test_get_special_orders() + async def test_create_and_cancel_limit_orders(self): await super().test_create_and_cancel_limit_orders() diff --git a/additional_tests/exchanges_tests/test_coinex.py b/additional_tests/exchanges_tests/test_coinex.py index d4fa0a9c1..3df57e583 100644 --- a/additional_tests/exchanges_tests/test_coinex.py +++ b/additional_tests/exchanges_tests/test_coinex.py @@ -62,6 +62,9 @@ async def test_get_not_found_order(self): async def test_is_valid_account(self): await super().test_is_valid_account() + async def test_get_special_orders(self): + await super().test_get_special_orders() + async def test_create_and_cancel_limit_orders(self): await super().test_create_and_cancel_limit_orders() diff --git a/additional_tests/exchanges_tests/test_cryptocom.py b/additional_tests/exchanges_tests/test_cryptocom.py index 7963937b9..efbbe4994 100644 --- a/additional_tests/exchanges_tests/test_cryptocom.py +++ b/additional_tests/exchanges_tests/test_cryptocom.py @@ -63,6 +63,9 @@ async def test_get_not_found_order(self): async def test_is_valid_account(self): await super().test_is_valid_account() + async def test_get_special_orders(self): + await super().test_get_special_orders() + async def test_create_and_cancel_limit_orders(self): await super().test_create_and_cancel_limit_orders() diff --git a/additional_tests/exchanges_tests/test_gateio.py b/additional_tests/exchanges_tests/test_gateio.py index d5cf41b7e..c9abe4f3b 100644 --- a/additional_tests/exchanges_tests/test_gateio.py +++ b/additional_tests/exchanges_tests/test_gateio.py @@ -65,6 +65,9 @@ async def test_get_not_found_order(self): async def test_is_valid_account(self): await super().test_is_valid_account() + async def test_get_special_orders(self): + await super().test_get_special_orders() + async def test_create_and_cancel_limit_orders(self): await super().test_create_and_cancel_limit_orders() diff --git a/additional_tests/exchanges_tests/test_hollaex.py b/additional_tests/exchanges_tests/test_hollaex.py index be63620b9..c2e9c3027 100644 --- a/additional_tests/exchanges_tests/test_hollaex.py +++ b/additional_tests/exchanges_tests/test_hollaex.py @@ -65,6 +65,9 @@ async def test_get_not_found_order(self): async def test_is_valid_account(self): await super().test_is_valid_account() + async def test_get_special_orders(self): + await super().test_get_special_orders() + async def test_create_and_cancel_limit_orders(self): await super().test_create_and_cancel_limit_orders() diff --git a/additional_tests/exchanges_tests/test_htx.py b/additional_tests/exchanges_tests/test_htx.py index a22c17725..ab2f68b44 100644 --- a/additional_tests/exchanges_tests/test_htx.py +++ b/additional_tests/exchanges_tests/test_htx.py @@ -64,6 +64,9 @@ async def test_get_not_found_order(self): async def test_is_valid_account(self): await super().test_is_valid_account() + async def test_get_special_orders(self): + await super().test_get_special_orders() + async def test_create_and_cancel_limit_orders(self): await super().test_create_and_cancel_limit_orders() diff --git a/additional_tests/exchanges_tests/test_kucoin.py b/additional_tests/exchanges_tests/test_kucoin.py index ef48cfbe7..a42eb3f55 100644 --- a/additional_tests/exchanges_tests/test_kucoin.py +++ b/additional_tests/exchanges_tests/test_kucoin.py @@ -14,6 +14,7 @@ # You should have received a copy of the GNU General Public # License along with OctoBot. If not, see . import pytest +import octobot_trading.enums from additional_tests.exchanges_tests import abstract_authenticated_exchange_tester @@ -36,6 +37,38 @@ class TestKucoinAuthenticatedExchange( VALID_ORDER_ID = "6617e84c5c1e0000083c71f7" IS_AUTHENTICATED_REQUEST_CHECK_AVAILABLE = True # set True when is_authenticated_request is implemented + SPECIAL_ORDER_TYPES_BY_EXCHANGE_ID: dict[ + str, ( + str, # symbol + str, # order type key in 'info' dict + str, # order type found in 'info' dict + str, # parsed trading_enums.TradeOrderType + str, # parsed trading_enums.TradeOrderSide + bool, # trigger above (on higher price than order price) + ) + ] = { + "vs93gpruc6ikekiv003o48ci": ( + "BTC/USDT", "type", "market", + octobot_trading.enums.TradeOrderType.LIMIT.value, octobot_trading.enums.TradeOrderSide.BUY.value, False + ), + 'vs93gpruc6n45taf003lr546': ( + "BTC/USDT", "type", "market", + octobot_trading.enums.TradeOrderType.STOP_LOSS.value, octobot_trading.enums.TradeOrderSide.BUY.value, True + ), + 'vs93gpruc69s00cs003tat0g': ( + "BTC/USDT", "type", "limit", + octobot_trading.enums.TradeOrderType.LIMIT.value, octobot_trading.enums.TradeOrderSide.BUY.value, False + ), + 'vs93gpruc5q45taf003lr545': ( + "BTC/USDT", "type", "limit", + octobot_trading.enums.TradeOrderType.STOP_LOSS.value, octobot_trading.enums.TradeOrderSide.BUY.value, True + ), + } # stop loss / take profit and other special order types to be successfully parsed + # details of an order that exists but can"t be cancelled + UNCANCELLABLE_ORDER_ID_SYMBOL_TYPE: tuple[str, str, octobot_trading.enums.TraderOrderType] = ( + "vs93gpruc69s00cs003tat0g", "BTC/USDT", octobot_trading.enums.TraderOrderType.BUY_LIMIT.value + ) + async def test_get_portfolio(self): await super().test_get_portfolio() @@ -45,6 +78,9 @@ async def test_get_portfolio_with_market_filter(self): async def test_is_valid_account(self): await super().test_is_valid_account() + async def test_get_special_orders(self): + await super().test_get_special_orders() + async def test_create_and_cancel_limit_orders(self): await super().test_create_and_cancel_limit_orders() diff --git a/additional_tests/exchanges_tests/test_kucoin_futures.py b/additional_tests/exchanges_tests/test_kucoin_futures.py index ef6ed0e18..d6a9f5a65 100644 --- a/additional_tests/exchanges_tests/test_kucoin_futures.py +++ b/additional_tests/exchanges_tests/test_kucoin_futures.py @@ -15,6 +15,7 @@ # License along with OctoBot. If not, see . import pytest +import octobot_trading.enums from additional_tests.exchanges_tests import abstract_authenticated_future_exchange_tester # All test coroutines will be treated as marked. @@ -40,6 +41,38 @@ class TestKucoinFuturesAuthenticatedExchange( EXPECTED_QUOTE_MIN_ORDER_SIZE = 40 EXPECT_BALANCE_FILTER_BY_MARKET_STATUS = True + SPECIAL_ORDER_TYPES_BY_EXCHANGE_ID: dict[ + str, ( + str, # symbol + str, # order type key in 'info' dict + str, # order type found in 'info' dict + str, # parsed trading_enums.TradeOrderType + str, # parsed trading_enums.TradeOrderSide + bool, # trigger above (on higher price than order price) + ) + ] = { + "266424660906831872": ( + "ETH/USDT:USDT", "type", "market", + octobot_trading.enums.TradeOrderType.LIMIT.value, octobot_trading.enums.TradeOrderSide.BUY.value, False + ), + '266424746172764160': ( + "ETH/USDT:USDT", "type", "market", + octobot_trading.enums.TradeOrderType.STOP_LOSS.value, octobot_trading.enums.TradeOrderSide.SELL.value, False + ), + '266424798085668865': ( + "ETH/USDT:USDT", "type", "limit", + octobot_trading.enums.TradeOrderType.LIMIT.value, octobot_trading.enums.TradeOrderSide.BUY.value, False + ), + '266424826044899328': ( + "ETH/USDT:USDT", "type", "limit", + octobot_trading.enums.TradeOrderType.STOP_LOSS.value, octobot_trading.enums.TradeOrderSide.SELL.value, False + ), + } # stop loss / take profit and other special order types to be successfully parsed + # details of an order that exists but can"t be cancelled + UNCANCELLABLE_ORDER_ID_SYMBOL_TYPE: tuple[str, str, octobot_trading.enums.TraderOrderType] = ( + "266424798085668865", "ETH/USDT:USDT", octobot_trading.enums.TraderOrderType.BUY_LIMIT.value + ) + async def test_get_portfolio(self): await super().test_get_portfolio() @@ -76,6 +109,9 @@ async def test_get_and_set_leverage(self): async def test_is_valid_account(self): await super().test_is_valid_account() + async def test_get_special_orders(self): + await super().test_get_special_orders() + async def test_create_and_cancel_limit_orders(self): # todo test cross position order creation (kucoin param) at next ccxt update (will support set margin type) await super().test_create_and_cancel_limit_orders() diff --git a/additional_tests/exchanges_tests/test_mexc.py b/additional_tests/exchanges_tests/test_mexc.py index a47659703..e14b77102 100644 --- a/additional_tests/exchanges_tests/test_mexc.py +++ b/additional_tests/exchanges_tests/test_mexc.py @@ -64,6 +64,9 @@ async def test_get_not_found_order(self): async def test_is_valid_account(self): await super().test_is_valid_account() + async def test_get_special_orders(self): + await super().test_get_special_orders() + async def test_create_and_cancel_limit_orders(self): await super().test_create_and_cancel_limit_orders() diff --git a/additional_tests/exchanges_tests/test_okx.py b/additional_tests/exchanges_tests/test_okx.py index 632cceaf2..0737b6fa1 100644 --- a/additional_tests/exchanges_tests/test_okx.py +++ b/additional_tests/exchanges_tests/test_okx.py @@ -45,6 +45,9 @@ async def test_get_portfolio_with_market_filter(self): async def test_is_valid_account(self): await super().test_is_valid_account() + async def test_get_special_orders(self): + await super().test_get_special_orders() + async def test_create_and_cancel_limit_orders(self): await super().test_create_and_cancel_limit_orders() diff --git a/additional_tests/exchanges_tests/test_okx_futures.py b/additional_tests/exchanges_tests/test_okx_futures.py index 656efb7a9..104969d42 100644 --- a/additional_tests/exchanges_tests/test_okx_futures.py +++ b/additional_tests/exchanges_tests/test_okx_futures.py @@ -73,6 +73,9 @@ async def test_get_and_set_leverage(self): async def test_is_valid_account(self): await super().test_is_valid_account() + async def test_get_special_orders(self): + await super().test_get_special_orders() + async def test_create_and_cancel_limit_orders(self): await super().test_create_and_cancel_limit_orders() diff --git a/additional_tests/exchanges_tests/test_phemex.py b/additional_tests/exchanges_tests/test_phemex.py index b570e3bd8..83fbce10c 100644 --- a/additional_tests/exchanges_tests/test_phemex.py +++ b/additional_tests/exchanges_tests/test_phemex.py @@ -62,6 +62,9 @@ async def test_get_not_found_order(self): async def test_is_valid_account(self): await super().test_is_valid_account() + async def test_get_special_orders(self): + await super().test_get_special_orders() + async def test_create_and_cancel_limit_orders(self): await super().test_create_and_cancel_limit_orders() diff --git a/octobot/community/models/formatters.py b/octobot/community/models/formatters.py index e7bff3f88..092029c14 100644 --- a/octobot/community/models/formatters.py +++ b/octobot/community/models/formatters.py @@ -123,6 +123,9 @@ def format_orders(orders: list, exchange_name: str) -> list: trading_enums.ExchangeConstantsOrderColumns.AMOUNT.value], backend_enums.OrderKeys.SIDE.value: storage_order[trading_constants.STORAGE_ORIGIN_VALUE][ trading_enums.ExchangeConstantsOrderColumns.SIDE.value], + backend_enums.OrderKeys.TRIGGER_ABOVE.value: storage_order[trading_constants.STORAGE_ORIGIN_VALUE].get( + trading_enums.ExchangeConstantsOrderColumns.TRIGGER_ABOVE.value + ), backend_enums.OrderKeys.EXCHANGE_ID.value: storage_order[trading_constants.STORAGE_ORIGIN_VALUE][ trading_enums.ExchangeConstantsOrderColumns.EXCHANGE_ID.value], backend_enums.OrderKeys.CHAINED.value: format_orders( diff --git a/octobot/community/supabase_backend/enums.py b/octobot/community/supabase_backend/enums.py index 2c7dcfa84..c421db22c 100644 --- a/octobot/community/supabase_backend/enums.py +++ b/octobot/community/supabase_backend/enums.py @@ -173,6 +173,7 @@ class OrderKeys(enum.Enum): TYPE = "type" CHAINED = "chained" SIDE = "side" + TRIGGER_ABOVE = "trigger_above" class PositionKeys(enum.Enum): diff --git a/requirements.txt b/requirements.txt index eca2c3d86..476a90d05 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ # Drakkar-Software requirements OctoBot-Commons==1.9.70 -OctoBot-Trading==2.4.142 +OctoBot-Trading==2.4.143 OctoBot-Evaluators==1.9.7 OctoBot-Tentacles-Manager==2.9.16 OctoBot-Services==1.6.21