def test_renew_hanging_orders_past_max_order_age(self): current_time_mock = 1234567891 # Order just executed self.tracker.add_order(LimitOrder("Order-1234567890000000", "BTC-USDT", True, "BTC", "USDT", Decimal(101), Decimal(1))) # Order executed 1900 seconds ago self.tracker.add_order(LimitOrder("Order-1234565991000000", "BTC-USDT", True, "BTC", "USDT", Decimal(105), Decimal(1))) with patch('time.time', return_value=current_time_mock): self.tracker.update_strategy_orders_with_equivalent_orders() self.tracker.renew_hanging_orders_past_max_order_age() self.assertEqual(self.tracker.orders_to_be_created, {HangingOrder(None, "BTC-USDT", True, Decimal(105), Decimal(1))})
def test_last_own_trade_price_order_update(self): self.clock.add_iterator(self.buy_last_own_trade_price_strategy) # Simulate own order filled last_own_traded_price: Decimal = Decimal("102.0") filled_order: LimitOrder = LimitOrder(client_order_id="myTrade", trading_pair=self.trading_pair, is_buy=True, base_currency=self.base_asset, quote_currency=self.quote_asset, price=last_own_traded_price, quantity=Decimal("10")) self.buy_last_own_trade_price_strategy._tracked_order_ids.add( "myTrade") self.simulate_limit_order_fill(self.market, filled_order, self.start_timestamp) market_filled_order: LimitOrder = LimitOrder( client_order_id="notMyTrade", trading_pair=self.trading_pair, is_buy=True, base_currency=self.base_asset, quote_currency=self.quote_asset, price=Decimal("100"), quantity=Decimal("10")) self.simulate_limit_order_fill(self.market, market_filled_order, self.start_timestamp) expected_new_price: Decimal = last_own_traded_price * ( Decimal('1') - (self.spread / Decimal("100"))) new_price: Decimal = self.buy_last_own_trade_price_strategy._recalculate_price_parameter( ) self.assertEqual(new_price, expected_new_price)
def test_symmetrical_volume_age_weighted(self): # Symmetrical in distance to mid-price and amounts, BUT different ages current_time_mock = 1234567891 self.tracker.add_order(LimitOrder(f"Order-{current_time_mock - 300}000000", "BTC-USDT", True, "BTC", "USDT", Decimal(99), Decimal(6))) self.tracker.add_order(LimitOrder(f"Order-{current_time_mock - 600}000000", "BTC-USDT", True, "BTC", "USDT", Decimal(91), Decimal(5))) self.tracker.add_order(LimitOrder(f"Order-{current_time_mock - 800}000000", "BTC-USDT", False, "BTC", "USDT", Decimal(101), Decimal(6))) self.tracker.add_order(LimitOrder(f"Order-{current_time_mock - 1200}000000", "BTC-USDT", False, "BTC", "USDT", Decimal(109), Decimal(5))) with patch('time.time', return_value=current_time_mock): self.tracker.set_aggregation_method(HangingOrdersAggregationType.VOLUME_TIME_WEIGHTED) self.tracker.update_strategy_orders_with_equivalent_orders() self.assertEqual(self.tracker.strategy_current_hanging_orders, {HangingOrder(order_id=None, trading_pair='BTC-USDT', is_buy=True, price=Decimal('95.69098'), amount=Decimal('11.00000')), HangingOrder(order_id=None, trading_pair='BTC-USDT', is_buy=False, price=Decimal('104.20177'), amount=Decimal('11.00000'))})
def test_remove_orders_far_from_price(self): # hanging_orders_cancel_pct = 10% so will add one closer and one further # Current price = 100.0 self.tracker.add_order(LimitOrder("Order-number-1", "BTC-USDT", False, "BTC", "USDT", Decimal(101), Decimal(1))) self.tracker.add_order(LimitOrder("Order-number-2", "BTC-USDT", False, "BTC", "USDT", Decimal(120), Decimal(1))) self.assertEqual(len(self.tracker.original_orders), 2) self.tracker.remove_orders_far_from_price() self.assertEqual(len(self.tracker.original_orders), 1)
def test_renew_hanging_orders_past_max_order_age(self): cancelled_orders_ids = [] strategy_active_orders = [] type(self.strategy).current_timestamp = PropertyMock( return_value=1234967891) type(self.strategy).active_orders = PropertyMock( return_value=strategy_active_orders) self.strategy.cancel_order.side_effect = lambda order_id: cancelled_orders_ids.append( order_id) self.strategy.buy_with_specific_market.return_value = "Order-1234569990000000" # Order just executed new_order = LimitOrder("Order-1234567890000000", "BTC-USDT", True, "BTC", "USDT", Decimal(101), Decimal(1)) # Order executed 1900 seconds ago old_order = LimitOrder("Order-1234565991000000", "BTC-USDT", True, "BTC", "USDT", Decimal(105), Decimal(1)) self.tracker.add_order(new_order) strategy_active_orders.append(new_order) self.tracker.add_order(old_order) strategy_active_orders.append(old_order) self.tracker.update_strategy_orders_with_equivalent_orders() self.assertTrue( any(order.trading_pair == "BTC-USDT" and order.price == Decimal(105) for order in self.tracker.strategy_current_hanging_orders)) # When calling the renew logic, the old order should start the renew process (it should be canceled) # but it will only stop being a current hanging order once the cancel confirmation arrives self.tracker.process_tick() self.assertTrue(old_order.client_order_id in cancelled_orders_ids) self.assertTrue( any(order.trading_pair == "BTC-USDT" and order.price == Decimal(105) for order in self.tracker.strategy_current_hanging_orders)) # When the cancel is confirmed the order should no longer be considered a hanging order strategy_active_orders.remove(old_order) self.tracker._did_cancel_order( MarketEvent.OrderCancelled, self, OrderCancelledEvent(old_order.client_order_id, old_order.client_order_id)) self.assertTrue( self._is_logged( "INFO", f"(BTC-USDT) Hanging order {old_order.client_order_id} " f"has been cancelled as part of the renew process. " f"Now the replacing order will be created.")) self.assertFalse( any(order.order_id == old_order.client_order_id for order in self.tracker.strategy_current_hanging_orders)) self.assertTrue( any(order.order_id == "Order-1234569990000000" for order in self.tracker.strategy_current_hanging_orders))
def test_limit_order_added_to_non_grouping_tracker_is_potential_hanging_order( self): strategy_active_orders = [] newly_created_buy_orders_ids = [ "Order-1234570000000000", "Order-1234570020000000", "Order-1234570040000000", "Order-1234570060000000" ] newly_created_sell_orders_ids = [ "Order-1234570010000000", "Order-1234570030000000", "Order-1234570050000000", "Order-1234570070000000" ] type(self.strategy).active_orders = PropertyMock( return_value=strategy_active_orders) self.strategy.buy_with_specific_market.side_effect = newly_created_buy_orders_ids self.strategy.sell_with_specific_market.side_effect = newly_created_sell_orders_ids buy_order_1 = LimitOrder("Order-1234569960000000", "BTC-USDT", True, "BTC", "USDT", Decimal(101), Decimal(1), creation_timestamp=1234569960000000) buy_order_2 = LimitOrder("Order-1234569980000000", "BTC-USDT", True, "BTC", "USDT", Decimal(105), Decimal(1), creation_timestamp=1234569980000000) sell_order_1 = LimitOrder("Order-1234569970000000", "BTC-USDT", False, "BTC", "USDT", Decimal(110), Decimal(1), creation_timestamp=1234569970000000) self.tracker.add_order(buy_order_1) strategy_active_orders.append(buy_order_1) self.tracker.add_order(buy_order_2) strategy_active_orders.append(buy_order_2) self.tracker.add_order(sell_order_1) strategy_active_orders.append(sell_order_1) self.tracker.update_strategy_orders_with_equivalent_orders() self.assertTrue(self.tracker.is_potential_hanging_order(buy_order_1)) self.assertTrue(self.tracker.is_potential_hanging_order(buy_order_2)) self.assertTrue(self.tracker.is_potential_hanging_order(sell_order_1))
def test_add_remove_limit_order(self): order_to_add = LimitOrder("Order-number-1", "BTC-USDT", True, "BTC", "USDT", Decimal(100), Decimal(1)) self.tracker.add_order(order_to_add) self.assertEqual(len(self.tracker.original_orders), 1) order_that_doesnt_belong = LimitOrder("Order-number-2", "BTC-USDT", True, "BTC", "USDT", Decimal(100), Decimal(1)) self.tracker.remove_order(order_that_doesnt_belong) self.assertEqual(len(self.tracker.original_orders), 1) self.tracker.remove_order(order_to_add) self.assertEqual(len(self.tracker.original_orders), 0) self.tracker.add_order(LimitOrder("Order-number-3", "BTC-USDT", True, "BTC", "USDT", Decimal(100), Decimal(1))) self.tracker.add_order(LimitOrder("Order-number-4", "BTC-USDT", True, "BTC", "USDT", Decimal(100), Decimal(1))) self.tracker.remove_all_orders() self.assertEqual(len(self.tracker.original_orders), 0)
def test_order_being_renewed_is_canceled_only_one_time(self): cancelled_orders_ids = [] strategy_active_orders = [] type(self.strategy).current_timestamp = PropertyMock( return_value=1234967891) type(self.strategy).active_orders = PropertyMock( return_value=strategy_active_orders) self.strategy.cancel_order.side_effect = lambda order_id: cancelled_orders_ids.append( order_id) self.strategy.buy_with_specific_market.return_value = "Order-1234569990000000" # Order just executed new_order = LimitOrder("Order-1234567890000000", "BTC-USDT", True, "BTC", "USDT", Decimal(101), Decimal(1), creation_timestamp=1234567890000000) # Order executed 1900 seconds ago old_order = LimitOrder("Order-1234565991000000", "BTC-USDT", True, "BTC", "USDT", Decimal(105), Decimal(1), creation_timestamp=1234565991000000) self.tracker.add_order(new_order) strategy_active_orders.append(new_order) self.tracker.add_order(old_order) strategy_active_orders.append(old_order) self.tracker.update_strategy_orders_with_equivalent_orders() self.assertTrue( any(order.trading_pair == "BTC-USDT" and order.price == Decimal(105) for order in self.tracker.strategy_current_hanging_orders)) # When calling the renew logic, the old order should start the renew process (it should be canceled) # but it will only stop being a current hanging order once the cancel confirmation arrives self.tracker.process_tick() self.assertTrue(old_order.client_order_id in cancelled_orders_ids) # Now we suppose that a new tick happens before the cancellation confirmation arrives self.tracker.process_tick() # The cancel request should not have been sent a second time self.assertEqual(1, cancelled_orders_ids.count(old_order.client_order_id))
def setUpClass(cls): cls.ev_loop = asyncio.get_event_loop() cls.trading_pair = "COINALPHA-HBOT" cls.limit_orders: List[LimitOrder] = [ LimitOrder(client_order_id=f"LIMIT//-{i}-{int(time.time()*1e3)}", trading_pair=cls.trading_pair, is_buy=True if i % 2 == 0 else False, base_currency=cls.trading_pair.split("-")[0], quote_currency=cls.trading_pair.split("-")[1], price=Decimal(f"{100 - i}") if i % 2 == 0 else Decimal(f"{100 + i}"), quantity=Decimal(f"{10 * (i + 1)}") ) for i in range(20) ] cls.market_orders: List[MarketOrder] = [ MarketOrder(order_id=f"MARKET//-{i}-{int(time.time()*1e3)}", trading_pair=cls.trading_pair, is_buy=True if i % 2 == 0 else False, base_asset=cls.trading_pair.split("-")[0], quote_asset=cls.trading_pair.split("-")[1], amount=float(f"{10 * (i + 1)}"), timestamp=time.time() ) for i in range(20) ] cls.market: BacktestMarket = BacktestMarket() cls.market_info: MarketTradingPairTuple = MarketTradingPairTuple( cls.market, cls.trading_pair, *cls.trading_pair.split("-") )
def test_get_price_by_type(self): # Check PriceType.BestAsk expected_best_ask: Decimal = max([entry.price for entry in self.market.order_book_bid_entries(self.trading_pair)]) self.assertEqual(expected_best_ask, self.market_info.get_price_by_type(PriceType.BestBid)) # Check PriceType.BestAsk expected_best_ask: Decimal = min([entry.price for entry in self.market.order_book_ask_entries(self.trading_pair)]) self.assertEqual(expected_best_ask, self.market_info.get_price_by_type(PriceType.BestAsk)) # Check PriceType.MidPrice expected_mid_price: Decimal = Decimal(self.initial_mid_price) self.assertEqual(expected_mid_price, self.market_info.get_price_by_type(PriceType.MidPrice)) # Check initial PriceType.LastTrade self.assertTrue(math.isnan(self.market_info.get_price_by_type(PriceType.LastTrade))) # Simulate fill buy order expected_trade_price = Decimal("101.0") fill_order: LimitOrder = LimitOrder(client_order_id="test", trading_pair=self.trading_pair, is_buy=True, base_currency=self.base_asset, quote_currency=self.quote_asset, price=expected_trade_price, quantity=Decimal("10")) self.simulate_limit_order_fill(self.market_info.market, fill_order) # Check for updated trade price self.assertEqual(expected_trade_price, self.market_info.get_price_by_type(PriceType.LastTrade))
def format_status(self) -> str: lines = [] warning_lines = [] for market_info in self._market_infos.values(): active_orders = self.market_info_to_active_orders.get( market_info, []) warning_lines.extend(self.network_warning([market_info])) markets_df = self.market_status_data_frame([market_info]) lines.extend( ["", " Markets:"] + [" " + line for line in str(markets_df).split("\n")]) assets_df = self.wallet_balance_data_frame([market_info]) lines.extend( ["", " Assets:"] + [" " + line for line in str(assets_df).split("\n")]) # See if there're any open orders. if active_orders: df = LimitOrder.to_pandas(active_orders) df_lines = str(df).split("\n") lines.extend(["", " Active orders:"] + [" " + line for line in df_lines]) else: lines.extend(["", " No active maker orders."]) warning_lines.extend(self.balance_warning([market_info])) if warning_lines: lines.extend(["", "*** WARNINGS ***"] + warning_lines) return "\n".join(lines)
def test_sell_last_price_place_order(self): self.clock.add_iterator(self.sell_last_price_strategy) filled_order: LimitOrder = LimitOrder(client_order_id="test", trading_pair=self.trading_pair, is_buy=True, base_currency=self.base_asset, quote_currency=self.quote_asset, price=Decimal("101.0"), quantity=Decimal("10")) self.simulate_limit_order_fill(self.market, filled_order, self.start_timestamp) self.clock.backtest_til(self.start_timestamp + self.clock_tick_size + self.time_delay) last_trade_price = filled_order.price expected_order_price: Decimal = last_trade_price * ( Decimal('1') + (self.spread / Decimal("100"))) ask_orders: List[LimitOrder] = [ o for o in self.sell_last_price_strategy.active_orders if not o.is_buy ] self.assertEqual(1, len(ask_orders)) ask_order: LimitOrder = ask_orders[0] self.assertEqual(expected_order_price, ask_order.price) self.assertEqual(1, ask_order.quantity)
def test_hanging_order_removed_when_cancelled(self): strategy_active_orders = [] strategy_logs = [] app_notifications = [] type(self.strategy).active_orders = PropertyMock(return_value=strategy_active_orders) self.strategy.log_with_clock.side_effect = lambda log_type, message: strategy_logs.append((log_type, message)) self.strategy.notify_hb_app.side_effect = lambda message: app_notifications.append(message) new_order = LimitOrder("Order-1234567890000000", "BTC-USDT", True, "BTC", "USDT", Decimal(101), Decimal(1)) self.tracker.add_order(new_order) strategy_active_orders.append(new_order) self.tracker.update_strategy_orders_with_equivalent_orders() # Now we simulate the order is cancelled self.tracker._did_cancel_order(MarketEvent.OrderCancelled, self, OrderCancelledEvent(new_order.client_order_id, new_order.client_order_id)) self.assertIn((logging.INFO, "(BTC-USDT) Hanging order Order-1234567890000000 cancelled."), strategy_logs) self.assertIn("(BTC-USDT) Hanging order Order-1234567890000000 cancelled.", app_notifications) self.assertTrue(len(self.tracker.strategy_current_hanging_orders) == 0) self.assertNotIn(new_order, self.tracker.original_orders)
def test_stop_tracking_limit_order(self): self.assertEqual(0, len(self.strategy.order_tracker.tracked_limit_orders)) limit_order: LimitOrder = LimitOrder( client_order_id="limit_test", trading_pair=self.trading_pair, is_buy=True, base_currency=self.trading_pair.split("-")[0], quote_currency=self.trading_pair.split("-")[1], price=Decimal("100"), quantity=Decimal("50")) limit_order_id: str = self.strategy.buy_with_specific_market( market_trading_pair_tuple=self.market_info, order_type=OrderType.LIMIT, price=limit_order.price, amount=limit_order.quantity, ) self.assertEqual(1, len(self.strategy.order_tracker.tracked_limit_orders)) self.strategy.cancel_order(self.market_info, limit_order_id) self.assertEqual(0, len(self.strategy.order_tracker.tracked_limit_orders))
def test_sell_last_own_trade_price_place_order(self): self.clock.add_iterator(self.sell_last_own_trade_price_strategy) # Simulate a order being filled in the orderbook own_filled_order: LimitOrder = LimitOrder( client_order_id="myTrade", trading_pair=self.trading_pair, is_buy=True, base_currency=self.base_asset, quote_currency=self.quote_asset, price=Decimal("102.0"), quantity=Decimal("10")) self.sell_last_own_trade_price_strategy._tracked_order_ids.add( "myTrade") self.simulate_limit_order_fill(self.market, own_filled_order, self.start_timestamp + 60) self.clock.backtest_til(self.start_timestamp + 120) last_own_traded_price = own_filled_order.price expected_order_price: Decimal = last_own_traded_price * ( Decimal('1') + (self.spread / Decimal("100"))) ask_orders: List[LimitOrder] = [ o for o in self.sell_last_own_trade_price_strategy.active_orders if not o.is_buy ] self.assertEqual(1, len(ask_orders)) ask_order: LimitOrder = ask_orders[0] self.assertEqual(expected_order_price, ask_order.price) self.assertEqual(1, ask_order.quantity)
def test_hanging_order_removed_when_cancelled(self): strategy_active_orders = [] type(self.strategy).active_orders = PropertyMock( return_value=strategy_active_orders) new_order = LimitOrder("Order-1234567890000000", "BTC-USDT", True, "BTC", "USDT", Decimal(101), Decimal(1), creation_timestamp=1234567890000000) self.tracker.add_order(new_order) strategy_active_orders.append(new_order) self.tracker.update_strategy_orders_with_equivalent_orders() # Now we simulate the order is cancelled self.tracker._did_cancel_order( MarketEvent.OrderCancelled.value, self, OrderCancelledEvent(datetime.now().timestamp(), new_order.client_order_id, new_order.client_order_id)) self.assertTrue( self._is_logged( "INFO", "(BTC-USDT) Hanging order Order-1234567890000000 canceled.")) self.assertTrue(len(self.tracker.strategy_current_hanging_orders) == 0) self.assertNotIn(new_order, self.tracker.original_orders)
def test_to_limit_order(self): order: InFlightOrder = InFlightOrder( client_order_id=self.client_order_id, trading_pair=self.trading_pair, order_type=OrderType.LIMIT, trade_type=TradeType.BUY, amount=Decimal("1000.0"), price=Decimal("1.0"), ) expected_limit_order: LimitOrder = LimitOrder( client_order_id=order.client_order_id, trading_pair=order.trading_pair, is_buy=True, base_currency=self.base_asset, quote_currency=self.quote_asset, price=Decimal("1.0"), quantity=Decimal("1000.0"), filled_quantity=Decimal("0"), ) limit_order = order.to_limit_order() self.assertIsInstance(limit_order, LimitOrder) self.assertEqual(limit_order.client_order_id, expected_limit_order.client_order_id) self.assertEqual(limit_order.trading_pair, expected_limit_order.trading_pair) self.assertEqual(limit_order.is_buy, expected_limit_order.is_buy) self.assertEqual(limit_order.base_currency, expected_limit_order.base_currency) self.assertEqual(limit_order.quote_currency, expected_limit_order.quote_currency) self.assertEqual(limit_order.price, expected_limit_order.price) self.assertEqual(limit_order.quantity, expected_limit_order.quantity) self.assertEqual(limit_order.filled_quantity, expected_limit_order.filled_quantity) self.assertEqual(limit_order.creation_timestamp, expected_limit_order.creation_timestamp) self.assertEqual(limit_order.status, expected_limit_order.status)
def test_add_order_as_hanging_order(self): order = LimitOrder("Order-number-1", "BTC-USDT", True, "BTC", "USDT", Decimal(100), Decimal(1)) self.tracker.add_as_hanging_order(order) self.assertIn(order, self.tracker.original_orders) self.assertEqual(1, len(self.tracker.strategy_current_hanging_orders)) hanging_order = next((hanging_order for hanging_order in self.tracker.strategy_current_hanging_orders)) self.assertEqual(order.client_order_id, hanging_order.order_id)
def test_sell_with_specific_market(self): limit_order: LimitOrder = LimitOrder( client_order_id="limit_test", trading_pair=self.trading_pair, is_buy=False, base_currency=self.trading_pair.split("-")[0], quote_currency=self.trading_pair.split("-")[1], price=Decimal("100"), quantity=Decimal("50")) limit_order_id: str = self.strategy.sell_with_specific_market( market_trading_pair_tuple=self.market_info, order_type=OrderType.LIMIT, price=limit_order.price, amount=limit_order.quantity, ) tracked_limit_order: LimitOrder = self.strategy.order_tracker.get_limit_order( self.market_info, limit_order_id) # Note: order_id generate here is random self.assertIsNotNone(limit_order_id) self.assertEqual(limit_order.is_buy, tracked_limit_order.is_buy) self.assertEqual(limit_order.trading_pair, tracked_limit_order.trading_pair) self.assertEqual(limit_order.price, tracked_limit_order.price) self.assertEqual(limit_order.quantity, tracked_limit_order.quantity) market_order: MarketOrder = MarketOrder( order_id="market_test", trading_pair=self.trading_pair, is_buy=False, base_asset=self.trading_pair.split("-")[0], quote_asset=self.trading_pair.split("-")[1], amount=Decimal("100"), timestamp=int(time.time() * 1e3)) # Note: order_id generate here is random market_order_id: str = self.strategy.sell_with_specific_market( market_trading_pair_tuple=self.market_info, order_type=OrderType.MARKET, amount=market_order.amount) tracked_market_order: MarketOrder = self.strategy.order_tracker.get_market_order( self.market_info, market_order_id) # Note: order_id generate here is random self.assertIsNotNone(market_order_id) self.assertEqual(market_order.is_buy, tracked_market_order.is_buy) self.assertEqual(market_order.trading_pair, tracked_market_order.trading_pair) self.assertEqual(market_order.amount, tracked_market_order.amount)
def test_order_creation_with_default_values(self): order = LimitOrder(client_order_id="HBOT_1", trading_pair="HBOT-USDT", is_buy=False, base_currency="HBOT", quote_currency="USDT", price=Decimal("100"), quantity=Decimal("1.5") ) self.assertEqual("HBOT_1", order.client_order_id) self.assertEqual("HBOT-USDT", order.trading_pair) self.assertEqual(False, order.is_buy) self.assertEqual("HBOT", order.base_currency) self.assertEqual("USDT", order.quote_currency) self.assertEqual(Decimal("100"), order.price) self.assertEqual(Decimal("1.5"), order.quantity) self.assertTrue(Decimal.is_nan(order.filled_quantity)) self.assertEqual(0, order.creation_timestamp) self.assertEqual(LimitOrderStatus.UNKNOWN, order.status) self.assertEqual(-1, order.age())
def test_symmetrical_volume_distance_weighted(self): # Symmetrical in distance to mid-price and amounts, BUT with distance affecting the weight exponentially self.tracker.add_order(LimitOrder("Order-number-1", "BTC-USDT", True, "BTC", "USDT", Decimal(99), Decimal(6))) self.tracker.add_order(LimitOrder("Order-number-2", "BTC-USDT", True, "BTC", "USDT", Decimal(91), Decimal(5))) self.tracker.add_order(LimitOrder("Order-number-3", "BTC-USDT", False, "BTC", "USDT", Decimal(101), Decimal(6))) self.tracker.add_order(LimitOrder("Order-number-4", "BTC-USDT", False, "BTC", "USDT", Decimal(109), Decimal(5))) self.tracker.set_aggregation_method(HangingOrdersAggregationType.VOLUME_DISTANCE_WEIGHTED) self.tracker.update_strategy_orders_with_equivalent_orders() self.assertEqual(self.tracker.strategy_current_hanging_orders, {HangingOrder(order_id=None, trading_pair='BTC-USDT', is_buy=True, price=Decimal('96.82055'), amount=Decimal('11.00000')), HangingOrder(order_id=None, trading_pair='BTC-USDT', is_buy=False, price=Decimal('103.17945'), amount=Decimal('11.00000'))})
def test_asymmetrical_volume_weighted(self): # Asymmetrical in distance to mid-price and amounts self.tracker.add_order(LimitOrder("Order-number-1", "BTC-USDT", True, "BTC", "USDT", Decimal(99), Decimal(2))) self.tracker.add_order(LimitOrder("Order-number-2", "BTC-USDT", True, "BTC", "USDT", Decimal(95), Decimal(1))) self.tracker.add_order(LimitOrder("Order-number-3", "BTC-USDT", False, "BTC", "USDT", Decimal(94), Decimal(6))) self.tracker.add_order(LimitOrder("Order-number-4", "BTC-USDT", False, "BTC", "USDT", Decimal(109), Decimal(5))) self.tracker.set_aggregation_method(HangingOrdersAggregationType.VOLUME_WEIGHTED) self.tracker.update_strategy_orders_with_equivalent_orders() self.assertEqual(self.tracker.strategy_current_hanging_orders, {HangingOrder(order_id=None, trading_pair='BTC-USDT', is_buy=True, price=Decimal('97.66667'), amount=Decimal('3.00000')), HangingOrder(order_id=None, trading_pair='BTC-USDT', is_buy=False, price=Decimal('107.36364'), amount=Decimal('11.00000'))})
def test_order_creation_with_all_values(self): created = int((time.time() - 100.) * 1e6) order = LimitOrder(client_order_id="HBOT_1", trading_pair="HBOT-USDT", is_buy=False, base_currency="HBOT", quote_currency="USDT", price=Decimal("100"), quantity=Decimal("1.5"), filled_quantity=Decimal("0.5"), creation_timestamp=created, status=LimitOrderStatus.OPEN ) self.assertEqual(Decimal("0.5"), order.filled_quantity) self.assertEqual(created, order.creation_timestamp) self.assertEqual(LimitOrderStatus.OPEN, order.status) self.assertEqual(100, order.age()) end_time = created + (50 * 1e6) self.assertEqual(50, order.age_til(end_time)) end_time = created - (50 * 1e6) self.assertEqual(-1, order.age_til(end_time))
def format_status(self) -> str: lines: list = [] warning_lines: list = [] lines.extend(self.configuration_status_lines()) for market_info in self._market_infos.values(): active_orders = self.market_info_to_active_orders.get( market_info, []) warning_lines.extend(self.network_warning([market_info])) markets_df = self.market_status_data_frame([market_info]) lines.extend( ["", " Markets:"] + [" " + line for line in str(markets_df).split("\n")]) assets_df = self.wallet_balance_data_frame([market_info]) lines.extend( ["", " Assets:"] + [" " + line for line in str(assets_df).split("\n")]) # See if there're any open orders. if len(active_orders) > 0: df = LimitOrder.to_pandas(active_orders) df_lines = str(df).split("\n") lines.extend(["", " Active orders:"] + [" " + line for line in df_lines]) else: lines.extend(["", " No active maker orders."]) filled_trades = self.filled_trades() average_price = (statistics.mean( [trade.price for trade in filled_trades]) if filled_trades else Decimal(0)) lines.extend([ "", f" Average filled orders price: " f"{PerformanceMetrics.smart_round(average_price)} " f"{market_info.quote_asset}" ]) lines.extend([ f" Pending amount: {PerformanceMetrics.smart_round(self._quantity_remaining)} " f"{market_info.base_asset}" ]) warning_lines.extend(self.balance_warning([market_info])) if warning_lines: lines.extend(["", "*** WARNINGS ***"] + warning_lines) return "\n".join(lines)
def test_did_create_buy_order(self): limit_order: limit_order = LimitOrder(client_order_id="test", trading_pair=self.trading_pair, is_buy=True, base_currency=self.trading_pair.split("-")[0], quote_currency=self.trading_pair.split("-")[1], price=Decimal("100"), quantity=Decimal("50")) self.simulate_order_created(self.market_info, limit_order) event = self.strategy.events_queue.popleft() self.assertIsInstance(event, BuyOrderCreatedEvent)
def to_limit_order(self) -> LimitOrder: """ Returns this InFlightOrder as a LimitOrder object. :return: LimitOrder object. """ return LimitOrder(client_order_id=self.client_order_id, trading_pair=self.trading_pair, is_buy=self.trade_type is TradeType.BUY, base_currency=self.base_asset, quote_currency=self.quote_asset, price=self.price, quantity=self.amount, filled_quantity=self.executed_amount_base)
def test_order_age(self, time_mock): time_mock.return_value = 1640001112.223 order = LimitOrder(client_order_id="OID1", trading_pair="COINALPHA-HBOT", is_buy=True, base_currency="COINALPHA", quote_currency="HBOT", price=Decimal(1000), quantity=Decimal(1), creation_timestamp=1640001110000000) age = order_age(order) self.assertEqual(int(time_mock.return_value - 1640001110), age)
def test_trades(self): self.assertEqual(0, len(self.strategy.trades)) # Simulate order being placed and filled limit_order = LimitOrder( client_order_id="test", trading_pair=self.trading_pair, is_buy=False, base_currency=self.trading_pair.split("-")[0], quote_currency=self.trading_pair.split("-")[1], price=Decimal("100"), quantity=Decimal("50")) self.simulate_order_filled(self.market_info, limit_order) self.assertEqual(1, len(self.strategy.trades))
def test_asymmetrical_volume_age_weighted(self): current_time_mock = 1234567891 self.tracker.add_order(LimitOrder(f"Order-{current_time_mock - 500}000000", "BTC-USDT", True, "BTC", "USDT", Decimal(99), Decimal(2))) self.tracker.add_order(LimitOrder(f"Order-{current_time_mock - 600}000000", "BTC-USDT", True, "BTC", "USDT", Decimal(95), Decimal(1))) self.tracker.add_order(LimitOrder(f"Order-{current_time_mock - 700}000000", "BTC-USDT", True, "BTC", "USDT", Decimal(94), Decimal(6))) self.tracker.add_order(LimitOrder(f"Order-{current_time_mock - 800}000000", "BTC-USDT", False, "BTC", "USDT", Decimal(109), Decimal(5))) with patch('time.time', return_value=current_time_mock): self.tracker.set_aggregation_method(HangingOrdersAggregationType.VOLUME_TIME_WEIGHTED) self.tracker.update_strategy_orders_with_equivalent_orders() self.assertEqual(self.tracker.strategy_current_hanging_orders, {HangingOrder(order_id=None, trading_pair='BTC-USDT', is_buy=True, price=Decimal('95.31641'), amount=Decimal('9.00000')), HangingOrder(order_id=None, trading_pair='BTC-USDT', is_buy=False, price=Decimal('109.00000'), amount=Decimal('5.00000'))})
def test_to_pandas(self): # Fix the timestamp here so that we can test order age accurately created = 1625835199511442 now_ts = created + 100 * 1e6 self.maxDiff = None orders = [ LimitOrder("HBOT_1", "A-B", True, "A", "B", Decimal("1"), Decimal("1.5")), LimitOrder(f"HBOT_{str(created)}", "C-D", True, "C", "D", Decimal("1"), Decimal("1")), LimitOrder("HBOT_2", "A-B ", False, "A", "B", Decimal("2.5"), Decimal("1"), Decimal("0"), created, LimitOrderStatus.OPEN), LimitOrder(f"HBOT_{str(created)}", "A-B ", False, "A", "B", Decimal("2"), Decimal("1"), Decimal(0), created, LimitOrderStatus.CANCELED), ] df = LimitOrder.to_pandas(orders, 1.5, end_time_order_age=now_ts) # Except df output is as below # Order ID Type Price Spread Amount Age Hang # HBOT_2 sell 2.5 66.67% 1.0 00:01:40 n/a # ...1442 sell 2.0 33.33% 1.0 00:01:40 n/a # HBOT_1 buy 1.0 33.33% 1.5 n/a n/a # ...1442 buy 1.0 33.33% 1.0 00:01:40 n/a # we can't compare the text output directly as for some weird reason the test file passes when run individually # but will fail under coverage run -m nose test.hummingbot # self.assertEqual(expect_txt, df.to_string(index=False, max_colwidth=50)) self.assertEqual("HBOT_2", df["Order ID"][0]) self.assertEqual("sell", df["Type"][0]) self.assertAlmostEqual(2.5, df["Price"][0]) self.assertEqual("66.67%", df["Spread"][0]) self.assertAlmostEqual(1., df["Amount"][0]) self.assertEqual("00:01:40", df["Age"][0]) self.assertEqual("n/a", df["Hang"][0]) # Test to see if hanging orders are displayed correctly df = LimitOrder.to_pandas(orders, 1.5, ["HBOT_1", "HBOT_2"], end_time_order_age=now_ts) # Except df output is as below # Order ID Type Price Spread Amount Age Hang # HBOT_2 sell 2.5 66.67% 1.0 00:01:40 yes # ...1442 sell 2.0 33.33% 1.0 00:01:40 no # HBOT_1 buy 1.0 33.33% 1.5 n/a yes # ...1442 buy 1.0 33.33% 1.0 00:01:40 no self.assertEqual("HBOT_2", df["Order ID"][0]) self.assertEqual("sell", df["Type"][0]) self.assertAlmostEqual(2.5, df["Price"][0]) self.assertEqual("66.67%", df["Spread"][0]) self.assertAlmostEqual(1., df["Amount"][0]) self.assertEqual("00:01:40", df["Age"][0]) self.assertEqual("yes", df["Hang"][0]) # Test to see if df is created and order age is calculated df = LimitOrder.to_pandas(orders, 1.5, []) self.assertAlmostEqual(2.5, df["Price"][0]) self.assertTrue(":" in df["Age"][0])