From e37bf67fded277b5bf7a6949715343892327f3cb Mon Sep 17 00:00:00 2001 From: Guillaume De Saint Martin Date: Sun, 26 Jan 2025 22:16:04 +0100 Subject: [PATCH] [GridTrading] add test and log --- .../Mode/grid_trading_mode/grid_trading.py | 13 +++- .../tests/test_grid_trading_mode.py | 73 +++++++++++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/Trading/Mode/grid_trading_mode/grid_trading.py b/Trading/Mode/grid_trading_mode/grid_trading.py index 8d4bbb2e2..e0d1a5b0f 100644 --- a/Trading/Mode/grid_trading_mode/grid_trading.py +++ b/Trading/Mode/grid_trading_mode/grid_trading.py @@ -440,7 +440,7 @@ async def _generate_staggered_orders(self, current_price, ignore_available_funds highest_buy = current_price lowest_sell = current_price origin_created_buy_orders_count, origin_created_sell_orders_count = self._get_origin_orders_count( - sorted_orders, recently_closed_trades + recently_closed_trades, sorted_orders ) min_max_total_order_price_delta = ( @@ -485,6 +485,12 @@ async def _generate_staggered_orders(self, current_price, ignore_available_funds self.logger.info( f"{len(missing_orders)} missing {self.symbol} orders on {self.exchange_name}: {missing_orders}" ) + else: + self.logger.info( + f"All {len(sorted_orders)} out of {self.buy_orders_count + self.sell_orders_count} {self.symbol} " + f"target orders are in place on {self.exchange_name}" + ) + await self._handle_missed_mirror_orders_fills(recently_closed_trades, missing_orders, current_price) try: # apply state and (re)create missing orders @@ -515,10 +521,13 @@ def _get_origin_orders_count(self, recent_trades, open_orders): origin_created_buy_orders_count = self.buy_orders_count origin_created_sell_orders_count = self.sell_orders_count if recent_trades: + # in case all initial orders didn't get created, try to infer the original value from open orders and trades buy_orders_count = len([order for order in open_orders if order.side is trading_enums.TradeOrderSide.BUY]) buy_trades_count = len([trade for trade in recent_trades if trade.side is trading_enums.TradeOrderSide.BUY]) origin_created_buy_orders_count = buy_orders_count + buy_trades_count - origin_created_sell_orders_count = len(open_orders) + len(recent_trades) - origin_created_buy_orders_count + origin_created_sell_orders_count = ( + len(open_orders) + len(recent_trades) - origin_created_buy_orders_count + ) return origin_created_buy_orders_count, origin_created_sell_orders_count def _get_grid_trades_or_orders(self, trades_or_orders): diff --git a/Trading/Mode/grid_trading_mode/tests/test_grid_trading_mode.py b/Trading/Mode/grid_trading_mode/tests/test_grid_trading_mode.py index dd625563c..d4dd3bba6 100644 --- a/Trading/Mode/grid_trading_mode/tests/test_grid_trading_mode.py +++ b/Trading/Mode/grid_trading_mode/tests/test_grid_trading_mode.py @@ -911,6 +911,79 @@ async def test_start_after_offline_x_filled_and_price_back_should_buy_to_recreat _check_created_orders(producer, trading_api.get_open_orders(exchange_manager), 200) +async def test_start_after_offline_x_filled_and_missing_should_recreate_sell(): + symbol = "BTC/USDT" + async with _get_tools(symbol) as (producer, _, exchange_manager): + # forced config + producer.buy_funds = producer.sell_funds = 0 + producer.allow_order_funds_redispatch = True + producer.buy_orders_count = producer.sell_orders_count = 5 + producer.compensate_for_missed_mirror_order = True + producer.enable_trailing_down = False + producer.enable_trailing_up = True + producer.flat_increment = decimal.Decimal(100) + producer.flat_spread = decimal.Decimal(300) + producer.reinvest_profits = False + producer.sell_volume_per_order = producer.buy_volume_per_order = False + producer.starting_price = 0 + producer.use_existing_orders_only = False + producer.use_fixed_volume_for_mirror_orders = False + + orders_count = producer.buy_orders_count + producer.sell_orders_count + + + initial_price = decimal.Decimal("105278.1") + trading_api.force_set_mark_price(exchange_manager, producer.symbol, initial_price) + btc_pf = trading_api.get_portfolio_currency(exchange_manager, "BTC") + usdt_pf = trading_api.get_portfolio_currency(exchange_manager, "USDT") + btc_pf.available = decimal.Decimal("0.00141858") + btc_pf.total = decimal.Decimal("0.00141858") + usdt_pf.available = decimal.Decimal("150.505098") + usdt_pf.total = decimal.Decimal("150.505098") + + await producer._ensure_staggered_orders() + await asyncio.create_task(_check_open_orders_count(exchange_manager, orders_count)) + original_orders = copy.copy(trading_api.get_open_orders(exchange_manager)) + assert len(original_orders) == orders_count + assert sorted([ + order.origin_price for order in original_orders + ]) == [ + # buy orders + decimal.Decimal('104728.1'), decimal.Decimal('104828.1'), decimal.Decimal('104928.1'), + decimal.Decimal('105028.1'), decimal.Decimal('105128.1'), + # sell orders + decimal.Decimal('105428.1'), decimal.Decimal('105528.1'), decimal.Decimal('105628.1'), + decimal.Decimal('105728.1'), decimal.Decimal('105828.1') + ] + + # price goes down to 105120, 105128.1 order gets filled + price = decimal.Decimal("105120") + # offline simulation: price goes down to 105120, 105128.1 order gets filled + offline_filled = [order for order in original_orders if order.origin_price == decimal.Decimal('105128.1')] + assert len(offline_filled) == 1 + assert offline_filled[0].side == trading_enums.TradeOrderSide.BUY + for order in offline_filled: + await _fill_order(order, exchange_manager, trigger_update_callback=False, producer=producer) + assert len(trading_api.get_open_orders(exchange_manager)) == orders_count - len(offline_filled) + assert btc_pf.available == decimal.Decimal('0.00028861409') + assert btc_pf.total == decimal.Decimal('0.00170420409') + assert usdt_pf.available == decimal.Decimal('0.247225519') + assert usdt_pf.total == decimal.Decimal('120.447922929') + + # back online: restore orders according to current price + trading_api.force_set_mark_price(exchange_manager, producer.symbol, price) + with _assert_missing_orders_count(producer, len(offline_filled)): + await producer._ensure_staggered_orders() + # restored orders + await asyncio.create_task(_check_open_orders_count(exchange_manager, orders_count)) + open_orders = trading_api.get_open_orders(exchange_manager) + # there is now 6 sell orders + assert len([order for order in open_orders if order.side is trading_enums.TradeOrderSide.SELL]) == 6 + # there is now 4 buy orders + assert len([order for order in open_orders if order.side is trading_enums.TradeOrderSide.BUY]) == 4 + _check_created_orders(producer, trading_api.get_open_orders(exchange_manager), initial_price) + + async def test_start_after_offline_1_filled_should_create_buy(): symbol = "BTC/USDT" async with _get_tools(symbol) as (producer, _, exchange_manager):