Beispiel #1
0
    def test_estimate_fee(self):
        """
        test the estimate_fee function
        """

        # test against centralized exchanges
        self.assertEqual(
            estimate_fee("kucoin", True),
            AddedToCostTradeFee(percent=Decimal('0.001'), flat_fees=[]))
        self.assertEqual(
            estimate_fee("kucoin", False),
            AddedToCostTradeFee(percent=Decimal('0.001'), flat_fees=[]))
        self.assertEqual(
            estimate_fee("binance", True),
            DeductedFromReturnsTradeFee(percent=Decimal('0.001'),
                                        flat_fees=[]))
        self.assertEqual(
            estimate_fee("binance", False),
            DeductedFromReturnsTradeFee(percent=Decimal('0.001'),
                                        flat_fees=[]))

        # test against decentralized exchanges
        self.assertEqual(
            estimate_fee("beaxy", True),
            AddedToCostTradeFee(percent=Decimal('0.0015'), flat_fees=[]))
        self.assertEqual(
            estimate_fee("beaxy", False),
            AddedToCostTradeFee(percent=Decimal('0.0025'), flat_fees=[]))

        # test against exchanges that do not exist in hummingbot.client.settings.CONNECTOR_SETTINGS
        self.assertRaisesRegex(Exception, "^Invalid connector", estimate_fee,
                               "does_not_exist", True)
        self.assertRaisesRegex(Exception, "Invalid connector", estimate_fee,
                               "does_not_exist", False)
    def simulate_limit_order_fill(market: MockPaperExchange, limit_order: LimitOrder, timestamp: float = 0):
        quote_currency_traded: Decimal = limit_order.price * limit_order.quantity
        base_currency_traded: Decimal = limit_order.quantity
        quote_currency: str = limit_order.quote_currency
        base_currency: str = limit_order.base_currency

        trade_event: OrderBookTradeEvent = OrderBookTradeEvent(
            trading_pair=limit_order.trading_pair,
            timestamp=timestamp,
            type=TradeType.BUY if limit_order.is_buy else TradeType.SELL,
            price=limit_order.price,
            amount=limit_order.quantity
        )

        market.get_order_book(limit_order.trading_pair).apply_trade(trade_event)

        if limit_order.is_buy:
            market.set_balance(quote_currency, market.get_balance(quote_currency) - quote_currency_traded)
            market.set_balance(base_currency, market.get_balance(base_currency) + base_currency_traded)
            market.trigger_event(MarketEvent.OrderFilled, OrderFilledEvent(
                market.current_timestamp,
                limit_order.client_order_id,
                limit_order.trading_pair,
                TradeType.BUY,
                OrderType.LIMIT,
                limit_order.price,
                limit_order.quantity,
                AddedToCostTradeFee(Decimal(0.0))
            ))
            market.trigger_event(MarketEvent.BuyOrderCompleted, BuyOrderCompletedEvent(
                market.current_timestamp,
                limit_order.client_order_id,
                base_currency,
                quote_currency,
                base_currency_traded,
                quote_currency_traded,
                OrderType.LIMIT
            ))
        else:
            market.set_balance(quote_currency, market.get_balance(quote_currency) + quote_currency_traded)
            market.set_balance(base_currency, market.get_balance(base_currency) - base_currency_traded)
            market.trigger_event(MarketEvent.OrderFilled, OrderFilledEvent(
                market.current_timestamp,
                limit_order.client_order_id,
                limit_order.trading_pair,
                TradeType.SELL,
                OrderType.LIMIT,
                limit_order.price,
                limit_order.quantity,
                AddedToCostTradeFee(Decimal(0.0))
            ))
            market.trigger_event(MarketEvent.SellOrderCompleted, SellOrderCompletedEvent(
                market.current_timestamp,
                limit_order.client_order_id,
                base_currency,
                quote_currency,
                base_currency_traded,
                quote_currency_traded,
                OrderType.LIMIT
            ))
    def simulate_limit_order_fill(market: MockPaperExchange, limit_order: LimitOrder):
        quote_currency_traded: Decimal = limit_order.price * limit_order.quantity
        base_currency_traded: Decimal = limit_order.quantity
        quote_currency: str = limit_order.quote_currency
        base_currency: str = limit_order.base_currency

        if limit_order.is_buy:
            market.set_balance(quote_currency, market.get_balance(quote_currency) - quote_currency_traded)
            market.set_balance(base_currency, market.get_balance(base_currency) + base_currency_traded)
            market.trigger_event(MarketEvent.OrderFilled, OrderFilledEvent(
                market.current_timestamp,
                limit_order.client_order_id,
                limit_order.trading_pair,
                TradeType.BUY,
                OrderType.LIMIT,
                limit_order.price,
                limit_order.quantity,
                AddedToCostTradeFee(Decimal("0"))
            ))
            market.trigger_event(MarketEvent.BuyOrderCompleted, BuyOrderCompletedEvent(
                market.current_timestamp,
                limit_order.client_order_id,
                base_currency,
                quote_currency,
                quote_currency,
                base_currency_traded,
                quote_currency_traded,
                Decimal("0"),
                OrderType.LIMIT
            ))
        else:
            market.set_balance(quote_currency, market.get_balance(quote_currency) + quote_currency_traded)
            market.set_balance(base_currency, market.get_balance(base_currency) - base_currency_traded)
            market.trigger_event(MarketEvent.OrderFilled, OrderFilledEvent(
                market.current_timestamp,
                limit_order.client_order_id,
                limit_order.trading_pair,
                TradeType.SELL,
                OrderType.LIMIT,
                limit_order.price,
                limit_order.quantity,
                AddedToCostTradeFee(Decimal("0"))
            ))
            market.trigger_event(MarketEvent.SellOrderCompleted, SellOrderCompletedEvent(
                market.current_timestamp,
                limit_order.client_order_id,
                base_currency,
                quote_currency,
                quote_currency,
                base_currency_traded,
                quote_currency_traded,
                Decimal("0"),
                OrderType.LIMIT
            ))
Beispiel #4
0
    def _process_trade_message(self, order_msg: Dict[str, Any]):
        """
        Updates in-flight order and trigger order filled event for trade message received. Triggers order completed
        event if the total executed amount equals to the specified order amount.
        """
        # Only process trade when trade fees have been accounted for; when trade status is "settled".
        if order_msg["status"] != "settled":
            return

        ex_order_id = order_msg["order_id"]

        client_order_id = None
        for track_order in self.in_flight_orders.values():
            if track_order.exchange_order_id == ex_order_id:
                client_order_id = track_order.client_order_id
                break

        if client_order_id is None:
            return

        tracked_order = self.in_flight_orders[client_order_id]
        updated = tracked_order.update_with_trade_update(order_msg)
        if not updated:
            return

        self.trigger_event(
            MarketEvent.OrderFilled,
            OrderFilledEvent(
                self.current_timestamp,
                tracked_order.client_order_id,
                tracked_order.trading_pair,
                tracked_order.trade_type,
                tracked_order.order_type,
                Decimal(str(order_msg["price"])),
                Decimal(str(order_msg["quantity"])),
                AddedToCostTradeFee(flat_fees=[
                    TokenAmount(order_msg["fee_currency_id"],
                                Decimal(str(order_msg["fee_amount"])))
                ]),
                exchange_trade_id=order_msg["id"]))
        if math.isclose(tracked_order.executed_amount_base, tracked_order.amount) or \
                tracked_order.executed_amount_base >= tracked_order.amount:
            tracked_order.last_state = "filled"
            self.logger().info(
                f"The {tracked_order.trade_type.name} order "
                f"{tracked_order.client_order_id} has completed "
                f"according to order status API.")
            event_tag = MarketEvent.BuyOrderCompleted if tracked_order.trade_type is TradeType.BUY \
                else MarketEvent.SellOrderCompleted
            event_class = BuyOrderCompletedEvent if tracked_order.trade_type is TradeType.BUY \
                else SellOrderCompletedEvent
            self.trigger_event(
                event_tag,
                event_class(
                    self.current_timestamp, tracked_order.client_order_id,
                    tracked_order.base_asset, tracked_order.quote_asset,
                    tracked_order.executed_amount_base,
                    tracked_order.executed_amount_quote,
                    tracked_order.order_type, tracked_order.exchange_order_id))
            self.stop_tracking_order(tracked_order.client_order_id)
Beispiel #5
0
    def test_tolerance_level(self, estimate_fee_mock):
        """
        Test tolerance level
        """
        estimate_fee_mock.return_value = AddedToCostTradeFee(
            percent=0, flat_fees=[TokenAmount('ETH', Decimal(0.00005))])

        # initiate strategy and add active orders
        self.clock.add_iterator(self.default_strategy)
        self.clock.backtest_til(self.start_timestamp + 9)

        # the order tolerance is 1%
        # set the orders to the same values
        proposal = Proposal("ETH-USDT", PriceSize(100, 1), PriceSize(100, 1))
        self.assertTrue(
            self.default_strategy.is_within_tolerance(
                self.default_strategy.active_orders, proposal))

        # update orders to withint the tolerance
        proposal = Proposal("ETH-USDT", PriceSize(109, 1), PriceSize(91, 1))
        self.assertTrue(
            self.default_strategy.is_within_tolerance(
                self.default_strategy.active_orders, proposal))

        # push the orders beyond the tolerance, this proposal should return False
        proposal = Proposal("ETH-USDT", PriceSize(150, 1), PriceSize(50, 1))
        self.assertFalse(
            self.default_strategy.is_within_tolerance(
                self.default_strategy.active_orders, proposal))
Beispiel #6
0
    def test_calculate_fees_in_quote_for_one_trade_with_fees_different_tokens(
            self):
        rate_oracle = RateOracle()
        rate_oracle._prices["DAI-COINALPHA"] = Decimal("2")
        rate_oracle._prices["USDT-DAI"] = Decimal("0.9")
        RateOracle._shared_instance = rate_oracle

        performance_metric = PerformanceMetrics()
        flat_fees = [
            TokenAmount(token="USDT", amount=Decimal("10")),
            TokenAmount(token="DAI", amount=Decimal("5")),
        ]
        trade = Trade(trading_pair="HBOT-COINALPHA",
                      side=TradeType.BUY,
                      price=Decimal("1000"),
                      amount=Decimal("1"),
                      order_type=OrderType.LIMIT,
                      market="binance",
                      timestamp=1640001112.223,
                      trade_fee=AddedToCostTradeFee(percent=Decimal("0.1"),
                                                    percent_token="COINALPHA",
                                                    flat_fees=flat_fees))

        self.async_run_with_timeout(
            performance_metric._calculate_fees(quote="COINALPHA",
                                               trades=[trade]))

        expected_fee_amount = trade.amount * trade.price * trade.trade_fee.percent
        expected_fee_amount += flat_fees[0].amount * Decimal("0.9") * Decimal(
            "2")
        expected_fee_amount += flat_fees[1].amount * Decimal("2")
        self.assertEqual(expected_fee_amount, performance_metric.fee_in_quote)
Beispiel #7
0
    def test_process_tick_does_not_collect_metrics_if_activation_interval_not_reached(
            self):
        event = OrderFilledEvent(
            timestamp=1000,
            order_id="OID1",
            trading_pair="COINALPHA-HBOT",
            trade_type=TradeType.BUY,
            order_type=OrderType.LIMIT,
            price=Decimal(1000),
            amount=Decimal(1),
            trade_fee=AddedToCostTradeFee(),
        )
        self.metrics_collector._register_fill_event(event)

        last_process_tick_timestamp_copy = self.metrics_collector._last_process_tick_timestamp
        last_executed_collection_process = self.metrics_collector._last_executed_collection_process

        self.metrics_collector.process_tick(timestamp=5)

        self.assertEqual(last_process_tick_timestamp_copy,
                         self.metrics_collector._last_process_tick_timestamp)
        self.assertEqual(
            last_executed_collection_process,
            self.metrics_collector._last_executed_collection_process)
        self.assertIn(event, self.metrics_collector._collected_events)
Beispiel #8
0
    def test_update_with_trade_update_duplicate_trade_update(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"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
        )

        trade_update: TradeUpdate = TradeUpdate(
            trade_id="someTradeId",
            client_order_id=self.client_order_id,
            exchange_order_id=self.exchange_order_id,
            trading_pair=self.trading_pair,
            fill_price=Decimal("1.0"),
            fill_base_amount=Decimal("500.0"),
            fill_quote_amount=Decimal("500.0"),
            fee=AddedToCostTradeFee(
                flat_fees=[TokenAmount(token=self.quote_asset, amount=self.trade_fee_percent * Decimal("500.0"))]),
            fill_timestamp=1,
        )

        self.assertTrue(order.update_with_trade_update(trade_update))
        self.assertEqual(order.executed_amount_base, trade_update.fill_base_amount)
        self.assertEqual(order.executed_amount_quote, trade_update.fill_quote_amount)
        self.assertEqual(order.last_update_timestamp, trade_update.fill_timestamp)
        self.assertEqual(1, len(order.order_fills))
        self.assertIn(trade_update.trade_id, order.order_fills)

        # Ignores duplicate trade update
        self.assertFalse(order.update_with_trade_update(trade_update))
    def test_simulate_maker_market_trade(self, estimate_fee_mock):
        """
        Test that we can set up a liquidity mining strategy, and a trade
        """
        estimate_fee_mock.return_value = AddedToCostTradeFee(
            percent=0, flat_fees=[TokenAmount('ETH', Decimal(0.00005))]
        )

        # initiate
        self.clock.add_iterator(self.default_strategy)
        self.clock.backtest_til(self.start_timestamp)

        # assert that there are no active trades on initialization and before clock has moved forward
        self.assertEqual(0, len(self.default_strategy.active_orders))

        # advance by one tick, the strategy will initiate two orders per pair
        self.clock.backtest_til(self.start_timestamp + self.clock_tick_size)
        self.assertEqual(4, len(self.default_strategy.active_orders))

        # assert that a buy and sell order is made for each pair
        self.assertTrue(self.has_limit_order(self.default_strategy.active_orders, 'ETH-USDT', True, Decimal(99.95), Decimal(2.0)))
        self.assertTrue(self.has_limit_order(self.default_strategy.active_orders, 'ETH-USDT', False, Decimal(100.05), Decimal(2.0)))
        self.assertTrue(self.has_limit_order(self.default_strategy.active_orders, 'ETH-BTC', True, Decimal(99.95), Decimal(1.0005)))
        self.assertTrue(self.has_limit_order(self.default_strategy.active_orders, 'ETH-BTC', False, Decimal(100.05), Decimal(2)))

        # Simulate buy order fill
        self.clock.backtest_til(self.start_timestamp + 8)
        self.simulate_maker_market_trade(False, Decimal("50"), Decimal("1"), "ETH-USDT")
        self.assertEqual(3, len(self.default_strategy.active_orders))
Beispiel #10
0
    def test_trade_update_does_not_change_exchange_order_id(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"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
        )

        trade_update: TradeUpdate = TradeUpdate(
            trade_id="someTradeId",
            client_order_id=self.client_order_id,
            exchange_order_id=self.exchange_order_id,
            trading_pair=self.trading_pair,
            fill_price=Decimal("1.0"),
            fill_base_amount=Decimal("500.0"),
            fill_quote_amount=Decimal("500.0"),
            fee=AddedToCostTradeFee(
                flat_fees=[TokenAmount(token=self.quote_asset, amount=self.trade_fee_percent * Decimal("500.0"))]),
            fill_timestamp=1,
        )

        self.assertTrue(order.update_with_trade_update(trade_update))
        self.assertIsNone(order.exchange_order_id)
        self.assertFalse(order.exchange_order_id_update_event.is_set())
    def test_inventory_skew(self, estimate_fee_mock):
        """
        When inventory_skew_enabled is true, the strategy will try to balance the amounts of base to match it
        """
        estimate_fee_mock.return_value = AddedToCostTradeFee(
            percent=0, flat_fees=[TokenAmount('ETH', Decimal(0.00005))]
        )

        # initiate with similar balances so the skew is obvious
        usdt_balance = 1000
        busd_balance = 1000
        eth_balance = 1000
        btc_balance = 1000

        trading_pairs = list(map(lambda quote_asset: "ETH-" + quote_asset, ["USDT", "BUSD", "BTC"]))
        market, market_infos = self.create_market(trading_pairs, 100, {"USDT": usdt_balance, "BUSD": busd_balance, "ETH": eth_balance, "BTC": btc_balance})

        skewed_base_strategy = LiquidityMiningStrategy()
        skewed_base_strategy.init_params(
            exchange=market,
            market_infos=market_infos,
            token="ETH",
            order_amount=Decimal(2),
            spread=Decimal(0.0005),
            inventory_skew_enabled=True,
            target_base_pct=Decimal(0.1),  # less base, more quote
            order_refresh_time=5,
            order_refresh_tolerance_pct=Decimal(0.1),  # tolerance of 10 % change
        )

        unskewed_strategy = LiquidityMiningStrategy()
        unskewed_strategy.init_params(
            exchange=market,
            market_infos=market_infos,
            token="ETH",
            order_amount=Decimal(2),
            spread=Decimal(0.0005),
            inventory_skew_enabled=False,
            order_refresh_time=5,
            target_base_pct=Decimal(0.1),  # this does nothing when inventory_skew_enabled is False
            order_refresh_tolerance_pct=Decimal(0.1),  # tolerance of 10 % change
        )

        self.clock.add_iterator(skewed_base_strategy)
        self.clock.backtest_til(self.start_timestamp + 10)
        self.clock.add_iterator(unskewed_strategy)
        self.clock.backtest_til(self.start_timestamp + 20)

        # iterate through pairs in skewed and unskewed strategy
        for unskewed_order in unskewed_strategy.active_orders:
            for skewed_base_order in skewed_base_strategy.active_orders:
                # if the trading_pair and trade type are the same, compare them
                if skewed_base_order.trading_pair == unskewed_order.trading_pair and \
                        skewed_base_order.is_buy == unskewed_order.is_buy:
                    if skewed_base_order.is_buy:
                        # the skewed strategy tries to buy more quote thant the unskewed one
                        self.assertGreater(skewed_base_order.price, unskewed_order.price)
                    else:
                        # trying to keep less base
                        self.assertLessEqual(skewed_base_order.price, unskewed_order.price)
Beispiel #12
0
    def _get_fee(self,
                 base_currency: str,
                 quote_currency: str,
                 order_type: OrderType,
                 order_side: TradeType,
                 amount: Decimal,
                 price: Decimal = s_decimal_NaN,
                 is_maker: Optional[bool] = None) -> AddedToCostTradeFee:

        is_maker = is_maker or (order_type is OrderType.LIMIT_MAKER)
        trading_pair = combine_to_hb_trading_pair(base=base_currency,
                                                  quote=quote_currency)
        if trading_pair in self._trading_fees:
            fees_data = self._trading_fees[trading_pair]
            fee_value = Decimal(
                fees_data["makerFeeRate"]) if is_maker else Decimal(
                    fees_data["takerFeeRate"])
            fee = AddedToCostTradeFee(percent=fee_value)
        else:
            fee = build_trade_fee(
                self.name,
                is_maker,
                base_currency=base_currency,
                quote_currency=quote_currency,
                order_type=order_type,
                order_side=order_side,
                amount=amount,
                price=price,
            )
        return fee
Beispiel #13
0
    def test_added_to_cost_json_deserialization(self):
        token_amount = TokenAmount(token="COINALPHA", amount=Decimal("20.6"))
        fee = AddedToCostTradeFee(percent=Decimal("0.5"),
                                  percent_token="COINALPHA",
                                  flat_fees=[token_amount])

        self.assertEqual(fee, TradeFeeBase.from_json(fee.to_json()))
Beispiel #14
0
    def test_process_order_fill_event_buy(self):
        amount = Decimal("1")
        price = Decimal("9000")
        event = OrderFilledEvent(
            timestamp=1,
            order_id="order1",
            trading_pair=self.trading_pair,
            trade_type=TradeType.BUY,
            order_type=OrderType.LIMIT,
            price=price,
            amount=amount,
            trade_fee=AddedToCostTradeFee(percent=Decimal("0"), flat_fees=[]),
        )
        # first event creates DB record
        self.delegate.process_order_fill_event(event)
        with self.trade_fill_sql.get_new_session() as session:
            count = session.query(InventoryCost).count()
            self.assertEqual(count, 1)

            # second event causes update to existing record
            self.delegate.process_order_fill_event(event)
            record = InventoryCost.get_record(session, self.base_asset,
                                              self.quote_asset)
            self.assertEqual(record.base_volume, amount * 2)
            self.assertEqual(record.quote_volume, price * 2)
Beispiel #15
0
    def test_get_trade_for_config(self):
        recorder = MarketsRecorder(sql=self.manager,
                                   markets=[self],
                                   config_file_path=self.config_file_path,
                                   strategy_name=self.strategy_name)

        with self.manager.get_new_session() as session:
            with session.begin():
                trade_fill_record = TradeFill(
                    config_file_path=self.config_file_path,
                    strategy=self.strategy_name,
                    market=self.display_name,
                    symbol=self.symbol,
                    base_asset=self.base,
                    quote_asset=self.quote,
                    timestamp=int(time.time()),
                    order_id="OID1",
                    trade_type=TradeType.BUY.name,
                    order_type=OrderType.LIMIT.name,
                    price=Decimal(1000),
                    amount=Decimal(1),
                    leverage=1,
                    trade_fee=AddedToCostTradeFee().to_json(),
                    exchange_trade_id="EOID1",
                    position=PositionAction.NIL.value)
                session.add(trade_fill_record)

            fill_id = trade_fill_record.exchange_trade_id

        trades = recorder.get_trades_for_config("test_config")
        self.assertEqual(1, len(trades))
        self.assertEqual(fill_id, trades[0].exchange_trade_id)
Beispiel #16
0
    def test_metrics_not_collected_when_convertion_rate_to_volume_token_not_found(
            self):
        mock_rate_oracle = MagicMock()
        mock_rate_oracle.stored_or_live_rate = AsyncMock(return_value=None)

        local_collector = TradeVolumeMetricCollector(
            connector=self.connector_mock,
            activation_interval=10,
            rate_provider=mock_rate_oracle,
            instance_id=self.instance_id)
        local_collector._dispatcher = self.dispatcher_mock

        event = OrderFilledEvent(
            timestamp=1000,
            order_id="OID1",
            trading_pair="COINALPHA-HBOT",
            trade_type=TradeType.BUY,
            order_type=OrderType.LIMIT,
            price=Decimal(1000),
            amount=Decimal(1),
            trade_fee=AddedToCostTradeFee(),
        )
        self.async_run_with_timeout(local_collector.collect_metrics([event]))

        self.dispatcher_mock.request.assert_not_called()
Beispiel #17
0
 def get_fee(self,
             base_currency: str,
             quote_currency: str,
             order_type: OrderType,
             order_side: TradeType,
             amount: Decimal,
             price: Decimal = s_decimal_NaN,
             is_maker: Optional[bool] = None) -> AddedToCostTradeFee:
     is_maker = order_type is OrderType.LIMIT_MAKER
     return AddedToCostTradeFee(percent=self.estimate_fee_pct(is_maker))
Beispiel #18
0
 def setUpClass(cls):
     cls.ev_loop = asyncio.get_event_loop()
     cls.clock: Clock = Clock(ClockMode.REALTIME)
     cls.stack: contextlib.ExitStack = contextlib.ExitStack()
     cls._clock = cls.stack.enter_context(cls.clock)
     cls._patcher = unittest.mock.patch(
         "hummingbot.strategy.amm_arb.data_types.estimate_fee")
     cls._url_mock = cls._patcher.start()
     cls._url_mock.return_value = AddedToCostTradeFee(percent=0,
                                                      flat_fees=[])
Beispiel #19
0
    def test_budget_allocation(self, estimate_fee_mock):
        """
        Liquidity mining strategy budget allocation is different from pmm, it depends on the token base and it splits
        its budget between the quote tokens.
        """
        estimate_fee_mock.return_value = AddedToCostTradeFee(
            percent=0, flat_fees=[TokenAmount('ETH', Decimal(0.00005))])

        # initiate
        usdt_balance = 1000
        busd_balance = 900
        eth_balance = 100
        btc_balance = 10

        trading_pairs = list(
            map(lambda quote_asset: "ETH-" + quote_asset,
                ["USDT", "BUSD", "BTC"]))
        market, market_infos = self.create_market(
            trading_pairs, 100, {
                "USDT": usdt_balance,
                "BUSD": busd_balance,
                "ETH": eth_balance,
                "BTC": btc_balance
            })

        strategy = LiquidityMiningStrategy()
        strategy.init_params(
            exchange=market,
            market_infos=market_infos,
            token="ETH",
            order_amount=Decimal(2),
            spread=Decimal(0.0005),
            inventory_skew_enabled=False,
            target_base_pct=Decimal(0.5),
            order_refresh_time=5,
            order_refresh_tolerance_pct=Decimal(
                0.1),  # tolerance of 10 % change
        )

        self.clock.add_iterator(strategy)
        self.clock.backtest_til(self.start_timestamp + 10)

        # there should be a buy and sell budget for each pair
        self.assertEqual(len(strategy.sell_budgets), 3)
        self.assertEqual(len(strategy.buy_budgets), 3)

        # the buy budgets use all of the available balance for the quote tokens
        self.assertEqual(strategy.buy_budgets["ETH-USDT"], usdt_balance)
        self.assertEqual(strategy.buy_budgets["ETH-BTC"], btc_balance)
        self.assertEqual(strategy.buy_budgets["ETH-BUSD"], busd_balance)

        # the sell budget tries to evenly split the base token between the quote tokens
        self.assertLess(strategy.sell_budgets["ETH-USDT"], eth_balance * 0.4)
        self.assertLess(strategy.sell_budgets["ETH-BTC"], eth_balance * 0.4)
        self.assertLess(strategy.sell_budgets["ETH-BUSD"], eth_balance * 0.4)
Beispiel #20
0
    async def _process_trade_message(self, trade_msg: Dict[str, Any]):
        """
        Updates in-flight order and trigger order filled event for trade message received. Triggers order completed
        event if the total executed amount equals to the specified order amount.
        """
        for order in self._in_flight_orders.values():
            await order.get_exchange_order_id()
        track_order = [
            o for o in self._in_flight_orders.values()
            if trade_msg["order_id"] == o.exchange_order_id
        ]
        if not track_order:
            return
        tracked_order = track_order[0]

        updated = tracked_order.update_with_trade_update(trade_msg)
        if not updated:
            return
        self.logger().info("_process_trade_message")
        self.logger().info(trade_msg)
        self.trigger_event(
            MarketEvent.OrderFilled,
            OrderFilledEvent(self.current_timestamp,
                             tracked_order.client_order_id,
                             tracked_order.trading_pair,
                             tracked_order.trade_type,
                             tracked_order.order_type,
                             Decimal(str(trade_msg["traded_price"])),
                             Decimal(str(trade_msg["traded_quantity"])),
                             AddedToCostTradeFee(flat_fees=[
                                 TokenAmount(trade_msg["fee_currency"],
                                             Decimal(str(trade_msg["fee"])))
                             ]),
                             exchange_trade_id=trade_msg["trade_id"]))
        if math.isclose(tracked_order.executed_amount_base, tracked_order.amount) or \
                tracked_order.executed_amount_base >= tracked_order.amount:
            tracked_order.last_state = "FILLED"
            self.logger().info(
                f"The {tracked_order.trade_type.name} order "
                f"{tracked_order.client_order_id} has completed "
                f"according to order status API.")
            event_tag = MarketEvent.BuyOrderCompleted if tracked_order.trade_type is TradeType.BUY \
                else MarketEvent.SellOrderCompleted
            event_class = BuyOrderCompletedEvent if tracked_order.trade_type is TradeType.BUY \
                else SellOrderCompletedEvent
            self.trigger_event(
                event_tag,
                event_class(self.current_timestamp,
                            tracked_order.client_order_id,
                            tracked_order.base_asset,
                            tracked_order.quote_asset, tracked_order.fee_asset,
                            tracked_order.executed_amount_base,
                            tracked_order.executed_amount_quote,
                            tracked_order.fee_paid, tracked_order.order_type))
            self.stop_tracking_order(tracked_order.client_order_id)
Beispiel #21
0
    def test_from_json(self):
        fee = AddedToCostTradeFee(
            percent=Decimal("0.5"),
            percent_token=self.quote_asset
        )
        trade_update = TradeUpdate(
            trade_id="12345",
            client_order_id=self.client_order_id,
            exchange_order_id="EOID1",
            trading_pair=self.trading_pair,
            fill_timestamp=1640001112,
            fill_price=Decimal("1000.11"),
            fill_base_amount=Decimal("2"),
            fill_quote_amount=Decimal("2000.22"),
            fee=fee,
        )

        order_json = {
            "client_order_id": self.client_order_id,
            "exchange_order_id": self.exchange_order_id,
            "trading_pair": self.trading_pair,
            "order_type": OrderType.LIMIT.name,
            "trade_type": TradeType.BUY.name,
            "price": "1.0",
            "amount": "1000.0",
            "executed_amount_base": "0",
            "executed_amount_quote": "0",
            "fee_asset": None,
            "fee_paid": "0",
            "last_state": "0",
            "leverage": "1",
            "position": "NIL",
            "creation_timestamp": 1640001112.0,
            "order_fills": {"1": trade_update.to_json()}
        }

        expected_order: InFlightOrder = InFlightOrder(
            client_order_id=self.client_order_id,
            exchange_order_id=self.exchange_order_id,
            trading_pair=self.trading_pair,
            order_type=OrderType.LIMIT,
            trade_type=TradeType.BUY,
            amount=Decimal("1000.0"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
        )

        order_from_json = InFlightOrder.from_json(order_json)
        self.assertEqual(expected_order, order_from_json)
        self.assertFalse(order_from_json.completely_filled_event.is_set())

        self.assertIn("1", order_from_json.order_fills)
        self.assertEqual(trade_update, order_from_json.order_fills["1"])
 async def update_swap_order(self, update_result: Dict[str, any],
                             tracked_order: UniswapInFlightOrder):
     if update_result.get("confirmed", False):
         if update_result["receipt"].get("status", 0) == 1:
             order_id = await tracked_order.get_exchange_order_id()
             gas_used = update_result["receipt"]["gasUsed"]
             gas_price = tracked_order.gas_price
             fee = Decimal(str(gas_used)) * Decimal(
                 str(gas_price)) / Decimal(str(1e9))
             self.trigger_event(
                 MarketEvent.OrderFilled,
                 OrderFilledEvent(self.current_timestamp,
                                  tracked_order.client_order_id,
                                  tracked_order.trading_pair,
                                  tracked_order.trade_type,
                                  tracked_order.order_type,
                                  Decimal(str(tracked_order.price)),
                                  Decimal(str(tracked_order.amount)),
                                  AddedToCostTradeFee(flat_fees=[
                                      TokenAmount(tracked_order.fee_asset,
                                                  Decimal(str(fee)))
                                  ]),
                                  exchange_trade_id=order_id))
             tracked_order.last_state = "FILLED"
             self.logger().info(
                 f"The {tracked_order.trade_type.name} order "
                 f"{tracked_order.client_order_id} has completed "
                 f"according to order status API.")
             event_tag = MarketEvent.BuyOrderCompleted if tracked_order.trade_type is TradeType.BUY \
                 else MarketEvent.SellOrderCompleted
             event_class = BuyOrderCompletedEvent if tracked_order.trade_type is TradeType.BUY \
                 else SellOrderCompletedEvent
             self.trigger_event(
                 event_tag,
                 event_class(self.current_timestamp,
                             tracked_order.client_order_id,
                             tracked_order.base_asset,
                             tracked_order.quote_asset,
                             tracked_order.fee_asset,
                             tracked_order.executed_amount_base,
                             tracked_order.executed_amount_quote,
                             float(fee), tracked_order.order_type))
             self.stop_tracking_order(tracked_order.client_order_id)
         else:
             self.logger().info(
                 f"The {tracked_order.type} order {tracked_order.client_order_id} has failed according to order status API. "
             )
             self.trigger_event(
                 MarketEvent.OrderFailure,
                 MarketOrderFailureEvent(self.current_timestamp,
                                         tracked_order.client_order_id,
                                         tracked_order.order_type))
             self.stop_tracking_order(tracked_order.client_order_id)
Beispiel #23
0
    def test_added_to_cost_fee_amount_in_token_does_not_look_for_convertion_rate_when_percentage_zero(
            self):
        # Configure fee to use a percent token different from the token used to request the fee value
        # That forces the logic to need the convertion rate if the fee amount is calculated
        fee = AddedToCostTradeFee(percent=Decimal("0"),
                                  percent_token="COINALPHA")

        fee_amount = fee.fee_amount_in_token(trading_pair="HBOT-COINALPHA",
                                             price=Decimal("1000"),
                                             order_amount=Decimal("1"),
                                             token="BNB")

        self.assertEqual(Decimal("0"), fee_amount)
Beispiel #24
0
    def test_volatility(self, estimate_fee_mock, get_mid_price_mock):
        """
        Assert that volatility information is updated after the expected number of intervals
        """
        estimate_fee_mock.return_value = AddedToCostTradeFee(
            percent=0, flat_fees=[TokenAmount('ETH', Decimal(0.00005))])

        # initiate with similar balances so the skew is obvious
        usdt_balance = 1000
        eth_balance = 1000

        trading_pairs = list(
            map(lambda quote_asset: "ETH-" + quote_asset, ["USDT"]))
        market, market_infos = self.create_market(trading_pairs, 100, {
            "USDT": usdt_balance,
            "ETH": eth_balance
        })

        strategy = LiquidityMiningStrategy()
        strategy.init_params(
            exchange=market,
            market_infos=market_infos,
            token="ETH",
            order_amount=Decimal(2),
            spread=Decimal(0.0005),
            inventory_skew_enabled=False,
            target_base_pct=Decimal(0.5),  # less base, more quote
            order_refresh_time=1,
            order_refresh_tolerance_pct=Decimal(
                0.1),  # tolerance of 10 % change
            # volatility_interval=2,
            # avg_volatility_period=2,
            # volatility_to_spread_multiplier=2,
        )

        get_mid_price_mock.return_value = Decimal(100.0)
        self.clock.add_iterator(strategy)
        self.clock.backtest_til(self.start_timestamp + 1)

        # update prices to create volatility after 2 intervals
        get_mid_price_mock.return_value = Decimal(105.0)
        self.clock.backtest_til(self.start_timestamp + 2)

        get_mid_price_mock.return_value = Decimal(110)
        self.clock.backtest_til(self.start_timestamp + 3)

        # assert that volatility is none zero
        self.assertAlmostEqual(float(
            strategy.market_status_df().loc[0, 'Volatility'].strip('%')),
                               10.00,
                               delta=0.1)
Beispiel #25
0
    def test_create_order_and_process_fill(self):
        recorder = MarketsRecorder(sql=self.manager,
                                   markets=[self],
                                   config_file_path=self.config_file_path,
                                   strategy_name=self.strategy_name)

        create_event = BuyOrderCreatedEvent(
            timestamp=1642010000,
            type=OrderType.LIMIT,
            trading_pair=self.trading_pair,
            amount=Decimal(1),
            price=Decimal(1000),
            order_id="OID1-1642010000000000",
            creation_timestamp=1640001112.223,
            exchange_order_id="EOID1",
        )

        recorder._did_create_order(MarketEvent.BuyOrderCreated.value, self,
                                   create_event)

        fill_event = OrderFilledEvent(timestamp=1642020000,
                                      order_id=create_event.order_id,
                                      trading_pair=create_event.trading_pair,
                                      trade_type=TradeType.BUY,
                                      order_type=create_event.type,
                                      price=Decimal(1010),
                                      amount=create_event.amount,
                                      trade_fee=AddedToCostTradeFee(),
                                      exchange_trade_id="TradeId1")

        recorder._did_fill_order(MarketEvent.OrderFilled.value, self,
                                 fill_event)

        with self.manager.get_new_session() as session:
            query = session.query(Order)
            orders = query.all()
            order = orders[0]
            order_status = order.status
            trade_fills = order.trade_fills

        self.assertEqual(1, len(orders))
        self.assertEqual(self.config_file_path, orders[0].config_file_path)
        self.assertEqual(create_event.order_id, orders[0].id)
        self.assertEqual(2, len(order_status))
        self.assertEqual(MarketEvent.BuyOrderCreated.name,
                         order_status[0].status)
        self.assertEqual(MarketEvent.OrderFilled.name, order_status[1].status)
        self.assertEqual(1, len(trade_fills))
        self.assertEqual(self.config_file_path,
                         trade_fills[0].config_file_path)
        self.assertEqual(fill_event.order_id, trade_fills[0].order_id)
Beispiel #26
0
    def test_to_json(self):
        fee = AddedToCostTradeFee(percent=Decimal("0.5"),
                                  percent_token=self.quote_asset)
        trade_update = TradeUpdate(
            trade_id="12345",
            client_order_id=self.client_order_id,
            exchange_order_id="EOID1",
            trading_pair=self.trading_pair,
            fill_timestamp=1640001112,
            fill_price=Decimal("1000.11"),
            fill_base_amount=Decimal("2"),
            fill_quote_amount=Decimal("2000.22"),
            fee=fee,
        )

        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"),
            creation_timestamp=1640001112.0,
            price=Decimal("1.0"),
        )
        order.order_fills["1"] = trade_update

        order_json = order.to_json()

        self.assertIsInstance(order_json, dict)

        self.assertEqual(order_json["client_order_id"], order.client_order_id)
        self.assertEqual(order_json["exchange_order_id"],
                         order.exchange_order_id)
        self.assertEqual(order_json["trading_pair"], order.trading_pair)
        self.assertEqual(order_json["order_type"], order.order_type.name)
        self.assertEqual(order_json["trade_type"], order.trade_type.name)
        self.assertEqual(order_json["price"], str(order.price))
        self.assertEqual(order_json["amount"], str(order.amount))
        self.assertEqual(order_json["executed_amount_base"],
                         str(order.executed_amount_base))
        self.assertEqual(order_json["executed_amount_quote"],
                         str(order.executed_amount_quote))
        self.assertEqual(order_json["last_state"],
                         str(order.current_state.value))
        self.assertEqual(order_json["leverage"], str(order.leverage))
        self.assertEqual(order_json["position"], order.position.value)
        self.assertEqual(order_json["creation_timestamp"],
                         order.creation_timestamp)
        self.assertEqual(order_json["order_fills"],
                         {"1": trade_update.to_json()})
Beispiel #27
0
    def test_added_to_cost_json_serialization(self):
        token_amount = TokenAmount(token="COINALPHA", amount=Decimal("20.6"))
        fee = AddedToCostTradeFee(percent=Decimal("0.5"),
                                  percent_token="COINALPHA",
                                  flat_fees=[token_amount])

        expected_json = {
            "fee_type": AddedToCostTradeFee.type_descriptor_for_json(),
            "percent": "0.5",
            "percent_token": "COINALPHA",
            "flat_fees": [token_amount.to_json()]
        }

        self.assertEqual(expected_json, fee.to_json())
Beispiel #28
0
    def test_collect_metrics_for_single_event(self):
        self.rate_oracle._prices = {"HBOT-USDT": Decimal("100")}

        event = OrderFilledEvent(
            timestamp=1000,
            order_id="OID1",
            trading_pair="COINALPHA-HBOT",
            trade_type=TradeType.BUY,
            order_type=OrderType.LIMIT,
            price=Decimal(1000),
            amount=Decimal(1),
            trade_fee=AddedToCostTradeFee(),
        )
        self.async_run_with_timeout(
            self.metrics_collector.collect_metrics([event]))

        expected_dispatch_request = {
            "url": f"{self.metrics_collector_url}/client_metrics",
            "method": "POST",
            "request_obj": {
                "headers": {
                    'Content-Type': "application/json"
                },
                "data":
                json.dumps({
                    "source": "hummingbot",
                    "name": TradeVolumeMetricCollector.METRIC_NAME,
                    "instance_id": self.instance_id,
                    "exchange": self.connector_name,
                    "version": self.client_version,
                    "system":
                    f"{platform.system()} {platform.release()}({platform.platform()})",
                    "value": str(event.amount * event.price * 100)
                }),
                "params": {
                    "ddtags":
                    f"instance_id:{self.instance_id},"
                    f"client_version:{self.client_version},"
                    f"type:metrics",
                    "ddsource":
                    "hummingbot-client"
                }
            }
        }

        self.dispatcher_mock.request.assert_called()
        dispatched_metric = self.dispatcher_mock.request.call_args[0][0]

        self.assertEqual(expected_dispatch_request, dispatched_metric)
Beispiel #29
0
 def get_fee(self,
             base_currency: str,
             quote_currency: str,
             order_type: OrderType,
             order_side: TradeType,
             amount: Decimal,
             price: Decimal = s_decimal_NaN,
             is_maker: Optional[bool] = None) -> AddedToCostTradeFee:
     """
     To get trading fee, this function is simplified by using fee override configuration. Most parameters to this
     function are ignore except order_type. Use OrderType.LIMIT_MAKER to specify you want trading fee for
     maker order.
     """
     is_maker = order_type is OrderType.LIMIT_MAKER
     return AddedToCostTradeFee(percent=self.estimate_fee_pct(is_maker))
Beispiel #30
0
 def test_process_order_fill_event_sell_no_initial_cost_set(self):
     amount = Decimal("1")
     price = Decimal("9000")
     event = OrderFilledEvent(
         timestamp=1,
         order_id="order1",
         trading_pair=self.trading_pair,
         trade_type=TradeType.SELL,
         order_type=OrderType.LIMIT,
         price=price,
         amount=amount,
         trade_fee=AddedToCostTradeFee(percent=Decimal("0"), flat_fees=[]),
     )
     with self.assertRaises(RuntimeError):
         self.delegate.process_order_fill_event(event)