示例#1
0
    def test_get_binance_prices(self, mock_api):
        url = RateOracle.binance_price_url
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))
        mock_api.get(regex_url, body=json.dumps(Fixture.Binance), repeat=True)

        url = RateOracle.binance_us_price_url
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))
        mock_response: Fixture.Binance
        mock_api.get(regex_url,
                     body=json.dumps(Fixture.BinanceUS),
                     repeat=True)

        com_prices = self.async_run_with_timeout(
            RateOracle.get_binance_prices_by_domain(
                RateOracle.binance_price_url))
        self._assert_rate_dict(com_prices)

        us_prices = self.async_run_with_timeout(
            RateOracle.get_binance_prices_by_domain(
                RateOracle.binance_us_price_url, "USD", domain="us"))
        self._assert_rate_dict(us_prices)
        self.assertGreater(len(us_prices), 1)

        quotes = {p.split("-")[1] for p in us_prices}
        self.assertEqual(len(quotes), 1)
        self.assertEqual(list(quotes)[0], "USD")
        combined_prices = self.async_run_with_timeout(
            RateOracle.get_binance_prices())
        self._assert_rate_dict(combined_prices)
        self.assertGreater(len(combined_prices), len(com_prices))
示例#2
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)
示例#3
0
    def test_get_rate_coingecko(self, mock_api):
        url = RateOracle.coingecko_usd_price_url.format(1, "USD")
        regex_url = re.compile(f"^{url}".replace(".", r"\.").replace("?", r"\?"))
        mock_api.get(regex_url, body=json.dumps(Fixture.CoinGeckoPage1), repeat=True)

        url = RateOracle.coingecko_usd_price_url.format(2, "USD")
        regex_url = re.compile(f"^{url}".replace(".", r"\.").replace("?", r"\?"))
        mock_api.get(regex_url, body=json.dumps(Fixture.CoinGeckoPage2), repeat=True)

        url = RateOracle.coingecko_usd_price_url.format(3, "USD")
        regex_url = re.compile(f"^{url}".replace(".", r"\.").replace("?", r"\?"))
        mock_api.get(regex_url, body=json.dumps(Fixture.CoinGeckoPage3), repeat=True)

        url = RateOracle.coingecko_usd_price_url.format(4, "USD")
        regex_url = re.compile(f"^{url}".replace(".", r"\.").replace("?", r"\?"))
        mock_api.get(regex_url, body=json.dumps(Fixture.CoinGeckoPage4), repeat=True)

        url = RateOracle.coingecko_supported_vs_tokens_url
        regex_url = re.compile(f"^{url}".replace(".", r"\.").replace("?", r"\?"))
        mock_api.get(regex_url, body=json.dumps(Fixture.CoinGeckoVSCurrencies), repeat=True)

        rates = self.async_run_with_timeout(RateOracle.get_coingecko_prices_by_page("USD", 1))
        self._assert_rate_dict(rates)
        rates = self.async_run_with_timeout(RateOracle.get_coingecko_prices("USD"))
        self._assert_rate_dict(rates)
    def get_taker_to_maker_conversion_rate(
        self, market_pair: CrossExchangeMarketPair
    ) -> Tuple[str, str, Decimal, str, str, Decimal]:
        """
        Find conversion rates from taker market to maker market
        :param market_pair: maker and taker trading pairs for which to do conversion
        :return: A tuple of quote pair symbol, quote conversion rate source, quote conversion rate,
        base pair symbol, base conversion rate source, base conversion rate
        """
        quote_pair = f"{market_pair.taker.quote_asset}-{market_pair.maker.quote_asset}"
        if market_pair.taker.quote_asset != market_pair.maker.quote_asset:
            quote_rate_source = RateOracle.source.name
            quote_rate = RateOracle.get_instance().rate(quote_pair)
        else:
            quote_rate_source = "fixed"
            quote_rate = Decimal("1")

        base_pair = f"{market_pair.taker.base_asset}-{market_pair.maker.base_asset}"
        if market_pair.taker.base_asset != market_pair.maker.base_asset:
            base_rate_source = RateOracle.source.name
            base_rate = RateOracle.get_instance().rate(base_pair)
        else:
            base_rate_source = "fixed"
            base_rate = Decimal("1")

        return quote_pair, quote_rate_source, quote_rate, base_pair, base_rate_source, base_rate
示例#5
0
    async def start_check(
            self,  # type: HummingbotApplication
            log_level: Optional[str] = None,
            restore: Optional[bool] = False):
        if self.strategy_task is not None and not self.strategy_task.done():
            self._notify(
                'The bot is already running - please run "stop" first')
            return

        if settings.required_rate_oracle:
            if not (await self.confirm_oracle_conversion_rate()):
                self._notify("The strategy failed to start.")
                return
            else:
                RateOracle.get_instance().start()
        is_valid = await self.status_check_all(notify_success=False)
        if not is_valid:
            self._notify("Status checks failed. Start aborted.")
            return
        if self._last_started_strategy_file != self.strategy_file_name:
            init_logging(
                "hummingbot_logs.yml",
                override_log_level=log_level.upper() if log_level else None,
                strategy_file_path=self.strategy_file_name)
            self._last_started_strategy_file = self.strategy_file_name

        # If macOS, disable App Nap.
        if platform.system() == "Darwin":
            import appnope
            appnope.nope()

        self._initialize_notifiers()

        self._notify(
            f"\nStatus check complete. Starting '{self.strategy_name}' strategy..."
        )
        if global_config_map.get("paper_trade_enabled").value:
            self._notify(
                "\nPaper Trading ON: All orders are simulated, and no real orders are placed."
            )

        for exchange in settings.required_exchanges:
            connector = str(exchange)
            status = get_connector_status(connector)

            # Display custom warning message for specific connectors
            warning_msg = warning_messages.get(connector, None)
            if warning_msg is not None:
                self._notify(f"\nConnector status: {status}\n"
                             f"{warning_msg}")

            # Display warning message if the exchange connector has outstanding issues or not working
            elif status != "GREEN":
                self._notify(
                    f"\nConnector status: {status}. This connector has one or more issues.\n"
                    "Refer to our Github page for more info: https://github.com/coinalpha/hummingbot"
                )

        await self.start_market_making(self.strategy_name, restore)
示例#6
0
    def test_performance_metrics(self):
        rate_oracle = RateOracle()
        rate_oracle._prices["USDT-HBOT"] = Decimal("5")
        RateOracle._shared_instance = rate_oracle

        trade_fee = AddedToCostTradeFee(
            flat_fees=[TokenAmount(quote, Decimal("0"))])
        trades = [
            TradeFill(
                config_file_path="some-strategy.yml",
                strategy="pure_market_making",
                market="binance",
                symbol=trading_pair,
                base_asset=base,
                quote_asset=quote,
                timestamp=int(time.time()),
                order_id="someId0",
                trade_type="BUY",
                order_type="LIMIT",
                price=100,
                amount=10,
                trade_fee=trade_fee.to_json(),
                exchange_trade_id="someExchangeId0",
                position=PositionAction.NIL.value,
            ),
            TradeFill(
                config_file_path="some-strategy.yml",
                strategy="pure_market_making",
                market="binance",
                symbol=trading_pair,
                base_asset=base,
                quote_asset=quote,
                timestamp=int(time.time()),
                order_id="someId1",
                trade_type="SELL",
                order_type="LIMIT",
                price=120,
                amount=15,
                trade_fee=trade_fee.to_json(),
                exchange_trade_id="someExchangeId1",
                position=PositionAction.NIL.value,
            )
        ]
        cur_bals = {base: 100, quote: 10000}
        metrics = asyncio.get_event_loop().run_until_complete(
            PerformanceMetrics.create(trading_pair, trades, cur_bals))
        self.assertEqual(Decimal("799"), metrics.trade_pnl)
        print(metrics)
示例#7
0
 def calculate_profitability(self, position: UniswapV3InFlightPosition):
     """
     Does simple computation and returns a dictionary containing data required by other functions.
     :param position: an instance of UniswapV3InFlightPosition
     :return {}: dictionaty containing "base_change", "quote_change", "base_fee", "quote_fee"
                 "tx_fee", "profitability".
     """
     base_tkn, quote_tkn = position.trading_pair.split("-")
     init_base = Decimal(str(position.base_amount))
     init_quote = Decimal(str(position.quote_amount))
     base_change = Decimal(str(position.current_base_amount)) - Decimal(
         str(position.base_amount))
     quote_change = Decimal(str(position.current_quote_amount)) - Decimal(
         str(position.quote_amount))
     base_fee = Decimal(str(position.unclaimed_base_amount))
     quote_fee = Decimal(str(position.unclaimed_quote_amount))
     if base_tkn != "WETH":
         fee_rate = RateOracle.get_instance().rate(f"ETH-{quote_tkn}")
         if fee_rate:
             tx_fee = Decimal(str(position.gas_price)) * 2 * fee_rate
         else:  # cases like this would be rare
             tx_fee = Decimal(str(position.gas_price)) * 2
     else:
         tx_fee = Decimal(str(position.gas_price)) * 2
     init_value = (init_base * self._last_price) + init_quote
     profitability = s_decimal_0 if init_value == s_decimal_0 else \
         ((((base_change + base_fee) * self._last_price) + quote_change + quote_fee - tx_fee) / init_value)
     return {
         "base_change": base_change,
         "quote_change": quote_change,
         "base_fee": base_fee,
         "quote_fee": quote_fee,
         "tx_fee": tx_fee,
         "profitability": profitability
     }
示例#8
0
    def test_get_ascend_ex_prices(self, mock_api):
        url = RateOracle.ascend_ex_price_url
        regex_url = re.compile(f"^{url}".replace(".", r"\.").replace("?", r"\?"))
        mock_api.get(regex_url, body=json.dumps(Fixture.AscendEx), repeat=True)

        prices = self.async_run_with_timeout(RateOracle.get_ascend_ex_prices())
        self._assert_rate_dict(prices)
示例#9
0
    def setUp(self) -> None:
        super().setUp()

        self.metrics_collector_url = "localhost"
        self.connector_name = "test_connector"
        self.instance_id = "test_instance_id"
        self.client_version = "0.1"
        self.rate_oracle = RateOracle()
        self.connector_mock = MagicMock()
        type(self.connector_mock).name = PropertyMock(
            return_value=self.connector_name)
        self.dispatcher_mock = MagicMock()
        type(self.dispatcher_mock).log_server_url = PropertyMock(
            return_value=self.metrics_collector_url)

        self.original_client_version = hummingbot.connector.connector_metrics_collector.CLIENT_VERSION
        hummingbot.connector.connector_metrics_collector.CLIENT_VERSION = self.client_version

        self.metrics_collector = TradeVolumeMetricCollector(
            connector=self.connector_mock,
            activation_interval=Decimal(10),
            rate_provider=self.rate_oracle,
            instance_id=self.instance_id)

        self.metrics_collector._dispatcher = self.dispatcher_mock
示例#10
0
    async def stop_loop(
            self,  # type: HummingbotApplication
            skip_order_cancellation: bool = False):
        self.logger().info("stop command initiated.")
        self._notify("\nWinding down...")

        # Restore App Nap on macOS.
        if platform.system() == "Darwin":
            import appnope
            appnope.nap()

        if self._script_iterator is not None:
            self._script_iterator.stop(self.clock)

        if self._trading_required and not skip_order_cancellation:
            # Remove the strategy from clock before cancelling orders, to
            # prevent race condition where the strategy tries to create more
            # orders during cancellation.
            if self.clock:
                self.clock.remove_iterator(self.strategy)
            success = await self._cancel_outstanding_orders()
            # Give some time for cancellation events to trigger
            await asyncio.sleep(0.5)
            if success:
                # Only erase markets when cancellation has been successful
                self.markets = {}

        if self.strategy_task is not None and not self.strategy_task.cancelled(
        ):
            self.strategy_task.cancel()

        if RateOracle.get_instance().started:
            RateOracle.get_instance().stop()

        if self.markets_recorder is not None:
            self.markets_recorder.stop()

        if self.kill_switch is not None:
            self.kill_switch.stop()

        self.strategy_task = None
        self.strategy = None
        self.market_pair = None
        self.clock = None
        self.markets_recorder = None
        self.market_trading_pairs_map.clear()
示例#11
0
    def _get_exchange_rate(trading_pair: str, exchange: Optional["ExchangeBase"] = None) -> Decimal:
        from hummingbot.core.rate_oracle.rate_oracle import RateOracle

        if exchange is not None:
            rate = exchange.get_price(trading_pair, is_buy=True)
        else:
            rate = RateOracle.get_instance().rate(trading_pair)
        return rate
    def test_get_kucoin_prices(self, mock_api, connector_creator_mock):
        connector_creator_mock.side_effect = [self._kucoin_connector]
        url = RateOracle.kucoin_price_url
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))
        mock_api.get(regex_url, body=json.dumps(Fixture.Kucoin), repeat=True)

        prices = self.async_run_with_timeout(RateOracle.get_kucoin_prices())
        self._assert_rate_dict(prices)
示例#13
0
    def init_params(
        self,
        market_info_1: MarketTradingPairTuple,
        market_info_2: MarketTradingPairTuple,
        min_profitability: Decimal,
        order_amount: Decimal,
        market_1_slippage_buffer: Decimal = Decimal("0"),
        market_2_slippage_buffer: Decimal = Decimal("0"),
        concurrent_orders_submission: bool = True,
        status_report_interval: float = 900,
        gateway_transaction_cancel_interval: int = 600,
    ):
        """
        Assigns strategy parameters, this function must be called directly after init.
        The reason for this is to make the parameters discoverable on introspect (it is not possible on init of
        a Cython class).
        :param market_info_1: The first market
        :param market_info_2: The second market
        :param min_profitability: The minimum profitability for execute trades (e.g. 0.0003 for 0.3%)
        :param order_amount: The order amount
        :param market_1_slippage_buffer: The buffer for which to adjust order price for higher chance of
        the order getting filled. This is quite important for AMM which transaction takes a long time where a slippage
        is acceptable rather having the transaction get rejected. The submitted order price will be adjust higher
        for buy order and lower for sell order.
        :param market_2_slippage_buffer: The slipper buffer for market_2
        :param concurrent_orders_submission: whether to submit both arbitrage taker orders (buy and sell) simultaneously
        If false, the bot will wait for first exchange order filled before submitting the other order.
        :param status_report_interval: Amount of seconds to wait to refresh the status report
        :param gateway_transaction_cancel_interval: Amount of seconds to wait before trying to cancel orders that are
        blockchain transactions that have not been included in a block (they are still in the mempool).
        """
        self._market_info_1 = market_info_1
        self._market_info_2 = market_info_2
        self._min_profitability = min_profitability
        self._order_amount = order_amount
        self._market_1_slippage_buffer = market_1_slippage_buffer
        self._market_2_slippage_buffer = market_2_slippage_buffer
        self._concurrent_orders_submission = concurrent_orders_submission
        self._last_no_arb_reported = 0
        self._all_arb_proposals = None
        self._all_markets_ready = False

        self._ev_loop = asyncio.get_event_loop()
        self._main_task = None

        self._last_timestamp = 0
        self._status_report_interval = status_report_interval
        self.add_markets([market_info_1.market, market_info_2.market])
        self._quote_eth_rate_fetch_loop_task = None

        self._rate_source = RateOracle.get_instance()

        self._cancel_outdated_orders_task = None
        self._gateway_transaction_cancel_interval = gateway_transaction_cancel_interval

        self._order_id_side_map: Dict[str, ArbProposalSide] = {}
示例#14
0
 def test_rate_oracle_network(self):
     oracle = RateOracle.get_instance()
     oracle.start()
     asyncio.get_event_loop().run_until_complete(oracle.get_ready())
     self.assertGreater(len(oracle.prices), 0)
     rate = oracle.rate("SCRT-USDT")
     self.assertGreater(rate, 0)
     rate1 = oracle.rate("BTC-USDT")
     self.assertGreater(rate1, 100)
     oracle.stop()
示例#15
0
    def test_calculate_fees_in_quote_for_one_trade_fill_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 = TradeFill(
            config_file_path="some-strategy.yml",
            strategy="pure_market_making",
            market="binance",
            symbol="HBOT-COINALPHA",
            base_asset="HBOT",
            quote_asset="COINALPHA",
            timestamp=int(time.time()),
            order_id="someId0",
            trade_type="BUY",
            order_type="LIMIT",
            price=1000,
            amount=1,
            trade_fee=AddedToCostTradeFee(percent=Decimal("0.1"),
                                          percent_token="COINALPHA",
                                          flat_fees=flat_fees).to_json(),
            exchange_trade_id="someExchangeId0",
            position=PositionAction.NIL.value,
        )

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

        expected_fee_amount = Decimal(str(trade.amount)) * Decimal(
            str(trade.price)) * Decimal("0.1")
        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)
 def setUpClass(cls):
     super().setUpClass()
     cls.ev_loop = asyncio.get_event_loop()
     cls.client_config_map = ClientConfigAdapter(ClientConfigMap())
     cls._binance_connector = RateOracle._binance_connector_without_private_keys(
         domain="com")
     cls._binance_connector._set_trading_pair_symbol_map(
         bidict({
             "ETHBTC": "ETH-BTC",
             "LTCBTC": "LTC-BTC",
             "BTCUSDT": "BTC-USDT",
             "SCRTBTC": "SCRT-BTC"
         }))
     cls._binance_connector_us = RateOracle._binance_connector_without_private_keys(
         domain="us")
     cls._binance_connector_us._set_trading_pair_symbol_map(
         bidict({
             "BTCUSD": "BTC-USD",
             "ETHUSD": "ETH-USD"
         }))
     AscendExAPIOrderBookDataSource._trading_pair_symbol_map = bidict({
         "ETH/BTC":
         "ETH-BTC",
         "LTC/BTC":
         "LTC-BTC",
         "BTC/USDT":
         "BTC-USDT",
         "SCRT/BTC":
         "SCRT-BTC",
         "MAPS/USDT":
         "MAPS-USDT",
         "QTUM/BTC":
         "QTUM-BTC"
     })
     cls._kucoin_connector = RateOracle._kucoin_connector_without_private_keys(
     )
     cls._kucoin_connector._set_trading_pair_symbol_map(
         bidict({
             "SHA-USDT": "SHA-USDT",
             "LOOM-BTC": "LOOM-BTC",
         }))
示例#17
0
    def test_rate_oracle_network(self, mock_api):
        url = RateOracle.binance_price_url
        regex_url = re.compile(f"^{url}".replace(".", r"\.").replace("?", r"\?"))
        mock_api.get(regex_url, body=json.dumps(Fixture.Binance))

        url = RateOracle.binance_us_price_url
        regex_url = re.compile(f"^{url}".replace(".", r"\.").replace("?", r"\?"))
        mock_response: Fixture.Binance
        mock_api.get(regex_url, body=json.dumps(Fixture.BinanceUS))

        oracle = RateOracle()
        oracle.start()
        self.async_run_with_timeout(oracle.get_ready())
        self.assertGreater(len(oracle.prices), 0)
        rate = oracle.rate("SCRT-USDT")
        self.assertGreater(rate, 0)
        rate1 = oracle.rate("BTC-USDT")
        self.assertGreater(rate1, 100)
        oracle.stop()
示例#18
0
    def test_find_rate_from_source(self, mock_api):
        url = RateOracle.binance_price_url
        regex_url = re.compile(f"^{url}".replace(".", r"\.").replace("?", r"\?"))
        mock_api.get(regex_url, body=json.dumps(Fixture.Binance), repeat=True)

        url = RateOracle.binance_us_price_url
        regex_url = re.compile(f"^{url}".replace(".", r"\.").replace("?", r"\?"))
        mock_response: Fixture.Binance
        mock_api.get(regex_url, body=json.dumps(Fixture.BinanceUS), repeat=True)

        expected_rate = (Decimal("33327.43000000") + Decimal("33327.44000000")) / Decimal(2)

        rate = self.async_run_with_timeout(RateOracle.rate_async("BTC-USDT"))
        self.assertEqual(expected_rate, rate)
示例#19
0
    def _get_exchange_rate(trading_pair: str,
                           exchange: Optional["ExchangeBase"] = None,
                           rate_source: Optional[Any] = None) -> Decimal:
        from hummingbot.core.rate_oracle.rate_oracle import RateOracle

        if exchange is not None and trading_pair in exchange.order_books:
            rate = exchange.get_price_by_type(trading_pair, PriceType.MidPrice)
        else:
            local_rate_source = rate_source or RateOracle.get_instance()
            rate = local_rate_source.rate(trading_pair)
            if rate is None:
                raise ValueError(
                    f"Could not find the exchange rate for {trading_pair} using the rate source "
                    f"{local_rate_source} (please verify it has been correctly configured)"
                )
        return rate
示例#20
0
 def create_proposal(self) -> List[OrderCandidate]:
     """
     Creates and returns a proposal (a list of order candidate), in this strategy the list has 1 element at most.
     """
     daily_closes = self._get_daily_close_list(self.trading_pair)
     start_index = (-1 * self.moving_avg_period) - 1
     # Calculate the average of the 50 element prior to the last element
     avg_close = mean(daily_closes[start_index:-1])
     proposal = []
     # If the current price (the last close) is below the dip, add a new order candidate to the proposal
     if daily_closes[-1] < avg_close * (Decimal("1") - self.dip_percentage):
         order_price = self.connector.get_price(self.trading_pair, False) * Decimal("0.9")
         usd_conversion_rate = RateOracle.get_instance().rate(self.conversion_pair)
         amount = (self.buy_usd_amount / usd_conversion_rate) / order_price
         proposal.append(OrderCandidate(self.trading_pair, False, OrderType.LIMIT, TradeType.BUY, amount,
                                        order_price))
     return proposal
 def test_rate_oracle_network(self):
     oracle = RateOracle.get_instance()
     oracle.start()
     asyncio.get_event_loop().run_until_complete(oracle.get_ready())
     print(oracle.prices)
     self.assertGreater(len(oracle.prices), 0)
     rate = oracle.rate("SCRT-USDT")
     print(f"rate SCRT-USDT: {rate}")
     self.assertGreater(rate, 0)
     rate1 = oracle.rate("BTC-USDT")
     print(f"rate BTC-USDT: {rate1}")
     self.assertGreater(rate1, 100)
     # wait for 5 s to check rate again
     asyncio.get_event_loop().run_until_complete(asyncio.sleep(5))
     rate2 = oracle.rate("BTC-USDT")
     print(f"rate BTC-USDT: {rate2}")
     self.assertNotEqual(0, rate2)
     oracle.stop()
示例#22
0
 async def calculate_profitability(self,
                                   position: UniswapV3InFlightPosition):
     """
     Does simple computation and returns a dictionary containing data required by other functions.
     :param position: an instance of UniswapV3InFlightPosition
     :return {}: dictionaty containing "base_change", "quote_change", "base_fee", "quote_fee"
                 "tx_fee", "profitability".
     """
     base_tkn, quote_tkn = position.trading_pair.split("-")
     init_base = position.base_amount
     init_quote = position.quote_amount
     base_change = position.current_base_amount - position.base_amount
     quote_change = position.current_quote_amount - position.quote_amount
     base_fee = position.unclaimed_base_amount
     quote_fee = position.unclaimed_quote_amount
     if len(position.tx_fees) < 2 or position.tx_fees[-1] == s_decimal_0:
         remove_lp_fee = await self._market_info.market._remove_position(
             position.hb_id, position.token_id, Decimal("100.0"), True)
         remove_lp_fee = remove_lp_fee if remove_lp_fee is not None else s_decimal_0
         position.tx_fees.append(remove_lp_fee)
     if quote_tkn != "WETH":
         fee_rate = RateOracle.get_instance().rate(f"ETH-{quote_tkn}")
         if fee_rate:
             tx_fee = sum(position.tx_fees) * fee_rate
         else:  # cases like this would be rare
             tx_fee = sum(position.tx_fees)
     else:
         tx_fee = sum(position.tx_fees)
     init_value = (init_base * self._last_price) + init_quote
     profitability = s_decimal_0 if init_value == s_decimal_0 else \
         ((((base_change + base_fee) * self._last_price) + quote_change + quote_fee - tx_fee) / init_value)
     return {
         "base_change": base_change,
         "quote_change": quote_change,
         "base_fee": base_fee,
         "quote_fee": quote_fee,
         "tx_fee": tx_fee,
         "profitability": profitability
     }
示例#23
0
    def test_performance_metrics_for_derivatives(self, is_trade_fill_mock):
        rate_oracle = RateOracle()
        rate_oracle._prices["USDT-HBOT"] = Decimal("5")
        RateOracle._shared_instance = rate_oracle

        is_trade_fill_mock.return_value = True
        trades = []
        trades.append(
            self.mock_trade(id="order1",
                            amount=Decimal("100"),
                            price=Decimal("10"),
                            position="OPEN",
                            type="BUY",
                            fee=AddedToCostTradeFee(
                                flat_fees=[TokenAmount(quote, Decimal("0"))])))
        trades.append(
            self.mock_trade(id="order2",
                            amount=Decimal("100"),
                            price=Decimal("15"),
                            position="CLOSE",
                            type="SELL",
                            fee=AddedToCostTradeFee(
                                flat_fees=[TokenAmount(quote, Decimal("0"))])))
        trades.append(
            self.mock_trade(id="order3",
                            amount=Decimal("100"),
                            price=Decimal("20"),
                            position="OPEN",
                            type="SELL",
                            fee=AddedToCostTradeFee(
                                Decimal("0.1"),
                                flat_fees=[TokenAmount("USD", Decimal("0"))])))
        trades.append(
            self.mock_trade(id="order4",
                            amount=Decimal("100"),
                            price=Decimal("15"),
                            position="CLOSE",
                            type="BUY",
                            fee=AddedToCostTradeFee(
                                Decimal("0.1"),
                                flat_fees=[TokenAmount("USD", Decimal("0"))])))

        cur_bals = {base: 100, quote: 10000}
        metrics = asyncio.get_event_loop().run_until_complete(
            PerformanceMetrics.create(trading_pair, trades, cur_bals))
        self.assertEqual(metrics.num_buys, 2)
        self.assertEqual(metrics.num_sells, 2)
        self.assertEqual(metrics.num_trades, 4)
        self.assertEqual(metrics.b_vol_base, Decimal("200"))
        self.assertEqual(metrics.s_vol_base, Decimal("-200"))
        self.assertEqual(metrics.tot_vol_base, Decimal("0"))
        self.assertEqual(metrics.b_vol_quote, Decimal("-2500"))
        self.assertEqual(metrics.s_vol_quote, Decimal("3500"))
        self.assertEqual(metrics.tot_vol_quote, Decimal("1000"))
        self.assertEqual(metrics.avg_b_price, Decimal("12.5"))
        self.assertEqual(metrics.avg_s_price, Decimal("17.5"))
        self.assertEqual(metrics.avg_tot_price, Decimal("15"))
        self.assertEqual(metrics.start_base_bal, Decimal("100"))
        self.assertEqual(metrics.start_quote_bal, Decimal("9000"))
        self.assertEqual(metrics.cur_base_bal, 100)
        self.assertEqual(metrics.cur_quote_bal, 10000),
        self.assertEqual(metrics.start_price, Decimal("10")),
        self.assertEqual(metrics.cur_price, Decimal("0.2"))
        self.assertEqual(metrics.trade_pnl, Decimal("1000"))
        self.assertEqual(metrics.total_pnl, Decimal("650"))
示例#24
0
    async def start_check(self,  # type: HummingbotApplication
                          log_level: Optional[str] = None,
                          restore: Optional[bool] = False,
                          strategy_file_name: Optional[str] = None):
        if self.strategy_task is not None and not self.strategy_task.done():
            self.notify('The bot is already running - please run "stop" first')
            return

        if settings.required_rate_oracle:
            # If the strategy to run requires using the rate oracle to find FX rates, validate there is a rate for
            # each configured token pair
            if not (await self.confirm_oracle_conversion_rate()):
                self.notify("The strategy failed to start.")
                return

        if strategy_file_name:
            file_name = strategy_file_name.split(".")[0]
            self.strategy_file_name = file_name
            self.strategy_name = file_name
        elif not await self.status_check_all(notify_success=False):
            self.notify("Status checks failed. Start aborted.")
            return
        if self._last_started_strategy_file != self.strategy_file_name:
            init_logging("hummingbot_logs.yml",
                         override_log_level=log_level.upper() if log_level else None,
                         strategy_file_path=self.strategy_file_name)
            self._last_started_strategy_file = self.strategy_file_name

        # If macOS, disable App Nap.
        if platform.system() == "Darwin":
            import appnope
            appnope.nope()

        self._initialize_notifiers()
        try:
            self._initialize_strategy(self.strategy_name)
        except NotImplementedError:
            self.strategy_name = None
            self.strategy_file_name = None
            self.notify("Invalid strategy. Start aborted.")
            raise

        if any([str(exchange).endswith("paper_trade") for exchange in settings.required_exchanges]):
            self.notify("\nPaper Trading Active: All orders are simulated and no real orders are placed.")

        for exchange in settings.required_exchanges:
            connector: str = str(exchange)
            status: str = get_connector_status(connector)
            warning_msg: Optional[str] = warning_messages.get(connector, None)

            # confirm gateway connection
            conn_setting: settings.ConnectorSetting = settings.AllConnectorSettings.get_connector_settings()[connector]
            if conn_setting.uses_gateway_generic_connector():
                connector_details: Dict[str, Any] = conn_setting.conn_init_parameters()
                if connector_details:
                    data: List[List[str]] = [
                        ["chain", connector_details['chain']],
                        ["network", connector_details['network']],
                        ["wallet_address", connector_details['wallet_address']]
                    ]
                    await UserBalances.instance().update_exchange_balance(connector)
                    balances: List[str] = [
                        f"{str(PerformanceMetrics.smart_round(v, 8))} {k}"
                        for k, v in UserBalances.instance().all_balances(connector).items()
                    ]
                    data.append(["balances", ""])
                    for bal in balances:
                        data.append(["", bal])
                    wallet_df: pd.DataFrame = pd.DataFrame(data=data, columns=["", f"{connector} configuration"])
                    self.notify(wallet_df.to_string(index=False))

                    self.app.clear_input()
                    self.placeholder_mode = True
                    use_configuration = await self.app.prompt(prompt="Do you want to continue? (Yes/No) >>> ")
                    self.placeholder_mode = False
                    self.app.change_prompt(prompt=">>> ")

                    if use_configuration in ["N", "n", "No", "no"]:
                        return

                    if use_configuration not in ["Y", "y", "Yes", "yes"]:
                        self.notify("Invalid input. Please execute the `start` command again.")
                        return

            # Display custom warning message for specific connectors
            elif warning_msg is not None:
                self.notify(f"\nConnector status: {status}\n"
                            f"{warning_msg}")

            # Display warning message if the exchange connector has outstanding issues or not working
            elif not status.endswith("GREEN"):
                self.notify(f"\nConnector status: {status}. This connector has one or more issues.\n"
                            "Refer to our Github page for more info: https://github.com/coinalpha/hummingbot")

        self.notify(f"\nStatus check complete. Starting '{self.strategy_name}' strategy...")
        await self.start_market_making(restore)
        # We always start the RateOracle. It is required for PNL calculation.
        RateOracle.get_instance().start()
    def test_get_rate_coingecko(self, mock_api):
        url = RateOracle.coingecko_usd_price_url.format(
            "cryptocurrency", 1, "USD")
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))
        mock_api.get(regex_url,
                     body=json.dumps(Fixture.CoinGeckoCryptocurrencyPage1),
                     repeat=True)

        url = RateOracle.coingecko_usd_price_url.format(
            "cryptocurrency", 2, "USD")
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))
        mock_api.get(regex_url,
                     body=json.dumps(Fixture.CoinGeckoCryptocurrencyPage2),
                     repeat=True)

        url = RateOracle.coingecko_usd_price_url.format(
            "decentralized-exchange", 1, "USD")
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))
        mock_api.get(regex_url,
                     body=json.dumps(Fixture.CoinGeckoDEXPage1),
                     repeat=True)

        url = RateOracle.coingecko_usd_price_url.format(
            "decentralized-exchange", 2, "USD")
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))
        mock_api.get(regex_url,
                     body=json.dumps(Fixture.CoinGeckoDEXPage2),
                     repeat=True)

        url = RateOracle.coingecko_usd_price_url.format(
            "decentralized-finance-defi", 1, "USD")
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))
        mock_api.get(regex_url,
                     body=json.dumps(Fixture.CoinGeckoDEFIPage1),
                     repeat=True)

        url = RateOracle.coingecko_usd_price_url.format(
            "decentralized-finance-defi", 2, "USD")
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))
        mock_api.get(regex_url,
                     body=json.dumps(Fixture.CoinGeckoDEFIPage2),
                     repeat=True)

        url = RateOracle.coingecko_usd_price_url.format(
            "smart-contract-platform", 1, "USD")
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))
        mock_api.get(regex_url,
                     body=json.dumps(Fixture.CoinGeckoSmartContractPage1),
                     repeat=True)

        url = RateOracle.coingecko_usd_price_url.format(
            "smart-contract-platform", 2, "USD")
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))
        mock_api.get(regex_url,
                     body=json.dumps(Fixture.CoinGeckoSmartContractPage2),
                     repeat=True)

        url = RateOracle.coingecko_usd_price_url.format(
            "stablecoins", 1, "USD")
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))
        mock_api.get(regex_url,
                     body=json.dumps(Fixture.CoinGeckoStableCoinsPage1),
                     repeat=True)

        url = RateOracle.coingecko_usd_price_url.format(
            "stablecoins", 2, "USD")
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))
        mock_api.get(regex_url,
                     body=json.dumps(Fixture.CoinGeckoStableCoinsPage2),
                     repeat=True)

        url = RateOracle.coingecko_usd_price_url.format(
            "wrapped-tokens", 1, "USD")
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))
        mock_api.get(regex_url,
                     body=json.dumps(Fixture.CoinGeckoWrappedTokensPage1),
                     repeat=True)

        url = RateOracle.coingecko_usd_price_url.format(
            "wrapped-tokens", 2, "USD")
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))
        mock_api.get(regex_url,
                     body=json.dumps(Fixture.CoinGeckoWrappedTokensPage2),
                     repeat=True)

        url = RateOracle.coingecko_supported_vs_tokens_url
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))
        mock_api.get(regex_url,
                     body=json.dumps(Fixture.CoinGeckoVSCurrencies),
                     repeat=True)

        rates = self.async_run_with_timeout(
            RateOracle.get_coingecko_prices_by_page("USD", 1,
                                                    "cryptocurrency"))
        self._assert_rate_dict(rates)
        rates = self.async_run_with_timeout(
            RateOracle.get_coingecko_prices("USD"))
        self._assert_rate_dict(rates)
示例#26
0
    def profit_pct(
            self,
            rate_source: Optional[RateOracle] = None,
            account_for_fee: bool = False,
    ) -> Decimal:
        """
        Returns a profit in percentage value (e.g. 0.01 for 1% profitability)
        Assumes the base token is the same in both arbitrage sides
        """
        if not rate_source:
            rate_source = RateOracle.get_instance()

        buy_side: ArbProposalSide = self.first_side if self.first_side.is_buy else self.second_side
        sell_side: ArbProposalSide = self.first_side if not self.first_side.is_buy else self.second_side
        base_conversion_pair: str = f"{sell_side.market_info.base_asset}-{buy_side.market_info.base_asset}"
        quote_conversion_pair: str = f"{sell_side.market_info.quote_asset}-{buy_side.market_info.quote_asset}"

        sell_base_to_buy_base_rate: Decimal = Decimal(1)
        sell_quote_to_buy_quote_rate: Decimal = rate_source.rate(quote_conversion_pair)

        buy_fee_amount: Decimal = s_decimal_0
        sell_fee_amount: Decimal = s_decimal_0
        result: Decimal = s_decimal_0

        if sell_quote_to_buy_quote_rate and sell_base_to_buy_base_rate:
            if account_for_fee:
                buy_trade_fee: TradeFeeBase = build_trade_fee(
                    exchange=buy_side.market_info.market.name,
                    is_maker=False,
                    base_currency=buy_side.market_info.base_asset,
                    quote_currency=buy_side.market_info.quote_asset,
                    order_type=OrderType.MARKET,
                    order_side=TradeType.BUY,
                    amount=buy_side.amount,
                    price=buy_side.order_price,
                    extra_flat_fees=buy_side.extra_flat_fees
                )
                sell_trade_fee: TradeFeeBase = build_trade_fee(
                    exchange=sell_side.market_info.market.name,
                    is_maker=False,
                    base_currency=sell_side.market_info.base_asset,
                    quote_currency=sell_side.market_info.quote_asset,
                    order_type=OrderType.MARKET,
                    order_side=TradeType.SELL,
                    amount=sell_side.amount,
                    price=sell_side.order_price,
                    extra_flat_fees=sell_side.extra_flat_fees
                )
                buy_fee_amount: Decimal = buy_trade_fee.fee_amount_in_token(
                    trading_pair=buy_side.market_info.trading_pair,
                    price=buy_side.quote_price,
                    order_amount=buy_side.amount,
                    token=buy_side.market_info.quote_asset,
                    rate_source=rate_source
                )
                sell_fee_amount: Decimal = sell_trade_fee.fee_amount_in_token(
                    trading_pair=sell_side.market_info.trading_pair,
                    price=sell_side.quote_price,
                    order_amount=sell_side.amount,
                    token=sell_side.market_info.quote_asset,
                    rate_source=rate_source
                )

            buy_spent_net: Decimal = (buy_side.amount * buy_side.quote_price) + buy_fee_amount
            sell_gained_net: Decimal = (sell_side.amount * sell_side.quote_price) - sell_fee_amount
            sell_gained_net_in_buy_quote_currency: Decimal = (
                sell_gained_net * sell_quote_to_buy_quote_rate / sell_base_to_buy_base_rate
            )

            result: Decimal = (
                ((sell_gained_net_in_buy_quote_currency - buy_spent_net) / buy_spent_net)
                if buy_spent_net != s_decimal_0
                else s_decimal_0
            )
        else:
            self.logger().warning("The arbitrage proposal profitability could not be calculated due to a missing rate"
                                  f" ({base_conversion_pair}={sell_base_to_buy_base_rate},"
                                  f" {quote_conversion_pair}={sell_quote_to_buy_quote_rate})")
        return result