Пример #1
0
async def main():
    chdir_to_data_directory()

    await create_yml_files()

    # This init_logging() call is important, to skip over the missing config warnings.
    init_logging("hummingbot_logs.yml")
    hb = HummingbotApplication.main_application()
    setHummingInstance(hb)

    read_configs_from_yml()
    ExchangeRateConversion.get_instance().start()

    with patch_stdout(log_field=hb.app.log_field):
        dev_mode = check_dev_mode()
        if dev_mode:
            hb.app.log(
                "Running from dev branches. Full remote logging will be enabled."
            )
        init_logging(
            "hummingbot_logs.yml",
            override_log_level=global_config_map.get("log_level").value,
            dev_mode=dev_mode)
        tasks: List[Coroutine] = [hb.run(), sio.connect('ws://localhost:5000')]
        await safe_gather(*tasks)
Пример #2
0
 def setUpClass(cls):
     ExchangeRateConversion.set_global_exchange_rate_config({
         "global_config": {
             "WETH": {
                 "default": 1.0,
                 "source": "None"
             },
             "ETH": {
                 "default": 1.0,
                 "source": "None"
             },
             "QETH": {
                 "default": 0.95,
                 "source": "None"
             },
         },
         "conversion_required": {
             "WETH": {
                 "default": 1.0,
                 "source": "None"
             },
             "QETH": {
                 "default": 0.95,
                 "source": "None"
             },
         },
     })
Пример #3
0
    def start(
            self,  # type: HummingbotApplication
            log_level: Optional[str] = None):
        if threading.current_thread() != threading.main_thread():
            self.ev_loop.call_soon_threadsafe(self.start, log_level)
            return

        is_valid = self.status()
        if not is_valid:
            return

        if log_level is not None:
            init_logging("hummingbot_logs.yml",
                         override_log_level=log_level.upper())

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

        # TODO add option to select data feed
        self.data_feed: DataFeedBase = CoinCapDataFeed.get_instance()

        self._initialize_notifiers()

        ExchangeRateConversion.get_instance().start()
        strategy_name = in_memory_config_map.get("strategy").value
        self.init_reporting_module()
        self._notify(
            f"\n  Status check complete. Starting '{strategy_name}' strategy..."
        )
        asyncio.ensure_future(self.start_market_making(strategy_name),
                              loop=self.ev_loop)
Пример #4
0
    async def exit_loop(
            self,  # type: HummingbotApplication
            force: bool = False):
        if self.strategy_task is not None and not self.strategy_task.cancelled(
        ):
            self.strategy_task.cancel()
        if force is False and self._trading_required:
            success = await self._cancel_outstanding_orders()
            if not success:
                self._notify(
                    'Wind down process terminated: Failed to cancel all outstanding orders. '
                    '\nYou may need to manually cancel remaining orders by logging into your chosen exchanges'
                    '\n\nTo force exit the app, enter "exit -f"')
                return
            # Freeze screen 1 second for better UI
            await asyncio.sleep(1)
        ExchangeRateConversion.get_instance().stop()

        if force is False and self.liquidity_bounty is not None:
            self._notify("Winding down liquidity bounty submission...")
            await self.liquidity_bounty.stop_network()

        self._notify("Winding down notifiers...")
        for notifier in self.notifiers:
            notifier.stop()

        self.app.exit()
Пример #5
0
async def main():
    await create_yml_files()

    # This init_logging() call is important, to skip over the missing config warnings.
    init_logging("hummingbot_logs.yml")

    read_system_configs_from_yml()
    ExchangeRateConversion.get_instance().start()

    hb = HummingbotApplication.main_application()

    with patch_stdout(log_field=hb.app.log_field):
        dev_mode = check_dev_mode()
        if dev_mode:
            hb.app.log("Running from dev branches. Full remote logging will be enabled.")
        init_logging("hummingbot_logs.yml",
                     override_log_level=global_config_map.get("log_level").value,
                     dev_mode=dev_mode)
        tasks: List[Coroutine] = [hb.run()]
        if global_config_map.get("debug_console").value:
            if not hasattr(__builtins__, "help"):
                import _sitebuiltins
                __builtins__.help = _sitebuiltins._Helper()

            from hummingbot.core.management.console import start_management_console
            management_port: int = detect_available_port(8211)
            tasks.append(start_management_console(locals(), host="localhost", port=management_port))
        await safe_gather(*tasks)
 def setUp(self) -> None:
     self._weth_price = 1.0
     self._eth_price = 1.0
     self._dai_price = 0.95
     self._usdc_price = 1.05
     self._price = 50
     ExchangeRateConversion.set_global_exchange_rate_config({
         "conversion_required": {
             "WETH": {
                 "default": self._weth_price,
                 "source": "None"
             },
             "ETH": {
                 "default": self._eth_price,
                 "source": "None"
             },
             "DAI": {
                 "default": self._dai_price,
                 "source": "None"
             },
             "USDC": {
                 "default": self._usdc_price,
                 "source": "None"
             },
             "USD": {
                 "default": 1.0,
                 "source": "None"
             }
         }
     })
Пример #7
0
    async def stop_loop(
            self,  # type: HummingbotApplication
            skip_order_cancellation: bool = False):
        self._notify("\nWinding down...")

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

        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()
            if success:
                # Only erase markets when cancellation has been successful
                self.markets = {}
        if self.reporting_module:
            self.reporting_module.stop()
        if self.strategy_task is not None and not self.strategy_task.cancelled(
        ):
            self.strategy_task.cancel()
        ExchangeRateConversion.get_instance().stop()
        self.markets_recorder.stop()
        if self.kill_switch is not None:
            self.kill_switch.stop()
        self.wallet = None
        self.strategy_task = None
        self.strategy = None
        self.market_pair = None
        self.clock = None
        self.markets_recorder = None
async def quick_start():
    try:
        args = CmdlineParser().parse_args()

        strategy = args.strategy
        config_file_name = args.config_file_name
        wallet = args.wallet
        password = args.config_password

        await create_yml_files()
        init_logging("hummingbot_logs.yml")
        read_configs_from_yml()
        ExchangeRateConversion.get_instance().start()
        await ExchangeRateConversion.get_instance().wait_till_ready()
        hb = HummingbotApplication.main_application()

        in_memory_config_map.get("password").value = password
        in_memory_config_map.get("strategy").value = strategy
        in_memory_config_map.get("strategy").validate(strategy)
        in_memory_config_map.get("strategy_file_path").value = config_file_name
        in_memory_config_map.get("strategy_file_path").validate(config_file_name)

        # To ensure quickstart runs with the default value of False for kill_switch_enabled if not present
        if not global_config_map.get("kill_switch_enabled"):
            global_config_map.get("kill_switch_enabled").value = False

        if wallet and password:
            global_config_map.get("wallet").value = wallet
            hb.acct = unlock_wallet(public_key=wallet, password=password)

        if not hb.config_complete:
            config_map = load_required_configs()
            empty_configs = [key for key, config in config_map.items() if config.value is None and config.required]
            empty_config_description: str = "\n- ".join([""] + empty_configs)
            raise ValueError(f"Missing empty configs: {empty_config_description}\n")

        with patch_stdout(log_field=hb.app.log_field):
            dev_mode = check_dev_mode()
            if dev_mode:
                hb.app.log("Running from dev branches. Full remote logging will be enabled.")

            log_level = global_config_map.get("log_level").value
            init_logging("hummingbot_logs.yml",
                         override_log_level=log_level,
                         dev_mode=dev_mode,
                         strategy_file_path=config_file_name)
            await write_config_to_yml()
            hb.start(log_level)

            tasks: List[Coroutine] = [hb.run()]
            if global_config_map.get("debug_console").value:
                management_port: int = detect_available_port(8211)
                tasks.append(start_management_console(locals(), host="localhost", port=management_port))
            await safe_gather(*tasks)

    except Exception as e:
        # In case of quick start failure, start the bot normally to allow further configuration
        logging.getLogger().warning(f"Bot config incomplete: {str(e)}. Starting normally...")
        await normal_start()
    def test_convert_token_value(self):
        coin_alpha_to_cat = ExchangeRateConversion.get_instance(
        ).convert_token_value(10,
                              from_currency="coin_alpha",
                              to_currency="cat")
        self.assertEqual(coin_alpha_to_cat, 2.0)

        coin_alpha_to_cat = ExchangeRateConversion.get_instance(
        ).convert_token_value(1, from_currency="coin_alpha", to_currency="cat")
        self.assertEqual(coin_alpha_to_cat, 0.2)
 def setUpClass(cls):
     ExchangeRateConversion.set_global_exchange_rate_config({
         "global_config": {
             "cat": {
                 "default": 1,
                 "source": "cat"
             },
             "coin_alpha": {
                 "default": 0,
                 "source": "coin_alpha_feed"
             }
         },
         "conversion_required": {
             "cat": {
                 "default": 100,
                 "source": "cat"
             }
         },
         "default_data_feed":
         "cat"
     })
     ExchangeRateConversion.set_data_feeds(
         [MockDataFeed1.get_instance(),
          MockDataFeed2.get_instance()])
     ExchangeRateConversion.set_update_interval(0.1)
     ExchangeRateConversion.get_instance().start()
     time.sleep(1)
Пример #11
0
 async def balances_df(self  # type: HummingbotApplication
                       ):
     all_ex_bals = await UserBalances.instance().all_balances_all_exchanges(
     )
     ex_columns = [
         ex for ex, bals in all_ex_bals.items()
         if any(bal > 0 for bal in bals.values())
     ]
     rows = []
     for exchange, bals in all_ex_bals.items():
         for token, bal in bals.items():
             if bal == 0:
                 continue
             token = token.upper()
             if not any(r.get("Symbol") == token for r in rows):
                 rows.append({"Symbol": token})
             row = [r for r in rows if r["Symbol"] == token][0]
             row[exchange] = round(bal, 4)
     for row in rows:
         ex_total = 0
         for ex, amount in row.items():
             try:
                 if ex != "Symbol":
                     ex_total += ERC.get_instance(
                     ).convert_token_value_decimal(amount, row["Symbol"],
                                                   "USD")
             except Exception:
                 continue
         row["Total(USD)"] = round(ex_total, 2)
     last_row = {"Symbol": "Total(USD)"}
     for ex in ex_columns:
         token_total = 0
         for row in rows:
             try:
                 token_total += ERC.get_instance(
                 ).convert_token_value_decimal(row[ex], row["Symbol"],
                                               "USD")
             except Exception:
                 continue
         last_row[ex] = round(token_total, 2)
     last_row["Total(USD)"] = round(
         sum(amount for ex, amount in last_row.items() if ex in ex_columns),
         2)
     ex_columns.sort(key=lambda ex: last_row[ex], reverse=True)
     columns = ["Symbol"] + ex_columns + ["Total(USD)"]
     df = pd.DataFrame(data=rows, columns=columns)
     df = df.replace(NaN, 0)
     df.sort_values(by=["Total(USD)"], inplace=True, ascending=False)
     df = df.append(last_row, ignore_index=True, sort=False)
     return df
    def add_balances(self, asset_name: str, amount: float, is_base: bool,
                     is_starting: bool):
        """ Adds the balance of either the base or the quote in the given market trading pair token to the corresponding
        CurrencyAmount object.

        NOTE: This is not to say that base / quote pairs between different markets are equivalent because that is NOT
        the case. Instead, this method will determine the current conversion rate between two stable coins before
        adding the balance to the corresponding CurrencyAmount object. Additionally, since it is possible that the
        exchange rate varies from the starting time of the bot to the current time, this conversion will always be
        performed using the SAME conversion rate - that is, the current conversion rate.

        So for example, let's say we are trading WETH/DAI and ETH/USD. Let's also assume that in  the
        hummingbot_application class, the first MarketTradingPairTuple in the market_trading_pair_tuple list is WETH/DAI. This means
        that in theory, the base and quote balances will be computed in terms of WETH and DAI, respectively. When the
        ETH and USD balances are added to those of WETH and DAI, the token conversion method - see
        erc.convert_token_value() will be called to convert the currencies using the CURRENT conversion rate. The
        current WETH/ETH conversion rate as well as the current DAI/USD conversion rates will be used for BOTH the
        starting and the current balance to ensure that any changes in the conversion rates while the bot was running
        do not affect the performance analysis feature."""
        currency_amount = self._get_currency_amount_pair(is_base, is_starting)
        if currency_amount.token is None:
            currency_amount.token = asset_name
            currency_amount.amount = amount
        else:
            if currency_amount.token == asset_name:
                currency_amount.amount += amount
            else:
                erc = ExchangeRateConversion.get_instance()
                temp_amount = erc.convert_token_value(amount,
                                                      asset_name,
                                                      currency_amount.token,
                                                      source="any")
                currency_amount.amount += temp_amount
Пример #13
0
 def base_amount_ratio(trading_pair, balances):
     base, quote = trading_pair.split("-")
     base_amount = balances.get(base, 0)
     quote_amount = balances.get(quote, 0)
     rate = ExchangeRateConversion.get_instance().convert_token_value_decimal(1, quote, base)
     total_value = base_amount + (quote_amount * rate)
     return None if total_value <= 0 else base_amount / total_value
 def calculate_trade_asset_delta_with_fees(
         trade: TradeFill) -> Tuple[Decimal, Decimal]:
     trade_fee: Dict[str, any] = trade.trade_fee
     total_flat_fees: Decimal = s_decimal_0
     amount: Decimal = Decimal(trade.amount)
     price: Decimal = Decimal(trade.price)
     for flat_fee_currency, flat_fee_amount in trade_fee["flat_fees"]:
         if flat_fee_currency == trade.quote_asset:
             total_flat_fees += Decimal(flat_fee_amount)
         else:
             # if the flat fee asset does not match quote asset, convert to quote asset value
             total_flat_fees += ExchangeRateConversion.get_instance(
             ).convert_token_value_decimal(amount=Decimal(flat_fee_amount),
                                           from_currency=flat_fee_currency,
                                           to_currency=trade.quote_asset,
                                           source="default")
     if trade.trade_type == TradeType.SELL.name:
         net_base_delta: Decimal = amount
         net_quote_delta: Decimal = amount * price * (
             Decimal("1") - Decimal(trade_fee["percent"])) - total_flat_fees
     elif trade.trade_type == TradeType.BUY.name:
         net_base_delta: Decimal = amount * (
             Decimal("1") - Decimal(trade_fee["percent"])) - total_flat_fees
         net_quote_delta: Decimal = amount * price
     else:
         raise Exception(f"Unsupported trade type {trade.trade_type}")
     return net_base_delta, net_quote_delta
    async def get_active_exchange_markets(cls) -> pd.DataFrame:
        """
        Returned data frame should have symbol as index and include usd volume, baseAsset and quoteAsset
        """
        client: aiohttp.ClientSession = cls.http_client()
        async with client.get(f"{MARKETS_URL}?include=ticker,stats") as response:
            response: aiohttp.ClientResponse = response
            if response.status != 200:
                raise IOError(f"Error fetching active Radar Relay markets. HTTP status is {response.status}.")
            data = await response.json()
            data: List[Dict[str, any]] = [
                {**item, **{"baseAsset": item["id"].split("-")[0], "quoteAsset": item["id"].split("-")[1]}}
                for item in data
            ]
            all_markets: pd.DataFrame = pd.DataFrame.from_records(data=data, index="id")

            weth_dai_price: float = float(all_markets.loc["WETH-DAI"]["ticker"]["price"])
            dai_usd_price: float = ExchangeRateConversion.get_instance().adjust_token_rate("DAI", weth_dai_price)
            usd_volume: List[float] = []
            quote_volume: List[float] = []
            for row in all_markets.itertuples():
                product_name: str = row.Index
                base_volume: float = float(row.stats["volume24Hour"])
                quote_volume.append(base_volume)
                if product_name.endswith("WETH"):
                    usd_volume.append(dai_usd_price * base_volume)
                else:
                    usd_volume.append(base_volume)

            all_markets.loc[:, "USDVolume"] = usd_volume
            all_markets.loc[:, "volume"] = quote_volume
            return all_markets.sort_values("USDVolume", ascending=False)
Пример #16
0
    async def get_active_exchange_markets(
            cls,
            api_endpoint: str = "https://rest.bamboorelay.com/",
            api_prefix: str = "main/0x") -> pd.DataFrame:
        """
        Returned data frame should have trading_pair as index and include usd volume, baseAsset and quoteAsset
        """
        client: aiohttp.ClientSession = cls.http_client()
        async with client.get(
                f"{api_endpoint}{api_prefix}/markets?perPage=1000&include=ticker,stats"
        ) as response:
            response: aiohttp.ClientResponse = response
            if response.status != 200:
                raise IOError(
                    f"Error fetching active Bamboo Relay markets. HTTP status is {response.status}."
                )
            data = await response.json()
            data: List[Dict[str, any]] = [{
                **item,
                **{
                    "baseAsset": item["id"].split("-")[0],
                    "quoteAsset": item["id"].split("-")[1]
                }
            } for item in data]
            all_markets: pd.DataFrame = pd.DataFrame.from_records(data=data,
                                                                  index="id")

            weth_dai_price: Decimal = Decimal(
                ExchangeRateConversion.get_instance().convert_token_value(
                    1.0, from_currency="WETH", to_currency="DAI"))
            dai_usd_price: float = float(
                ExchangeRateConversion.get_instance().adjust_token_rate(
                    "DAI", weth_dai_price))
            usd_volume: List[float] = []
            quote_volume: List[float] = []
            for row in all_markets.itertuples():
                product_name: str = row.Index
                base_volume: float = float(row.stats["volume24Hour"])
                quote_volume.append(base_volume)
                if product_name.endswith("WETH"):
                    usd_volume.append(dai_usd_price * base_volume)
                else:
                    usd_volume.append(base_volume)

            all_markets.loc[:, "USDVolume"] = usd_volume
            all_markets.loc[:, "volume"] = quote_volume
            return all_markets.sort_values("USDVolume", ascending=False)
Пример #17
0
 async def exit(self, force: bool = False):
     if self.strategy_task is not None and not self.strategy_task.cancelled(
     ):
         self.strategy_task.cancel()
     if self.strategy:
         self.strategy.stop()
     if force is False and self._trading_required:
         success = await self._cancel_outstanding_orders()
         if not success:
             self.app.log(
                 'Wind down process terminated: Failed to cancel all outstanding orders. '
                 '\nYou may need to manually cancel remaining orders by logging into your chosen exchanges'
                 '\n\nTo force exit the app, enter "exit -f"')
             return
         # Freeze screen 1 second for better UI
         await asyncio.sleep(1)
     ExchangeRateConversion.get_instance().stop()
     self.app.exit()
Пример #18
0
 async def set_up_class(cls):
     add_files_extension(settings.CONF_FILE_PATH, [".yml", ".json"],
                         ".temp")
     asyncio.ensure_future(hb_main())
     cls.hb = HummingbotApplication.main_application()
     await wait_til(
         lambda: ExchangeRateConversion.get_instance()._ready_notifier.
         is_set(), 20)
     await wait_til(lambda: 'Enter "config" to create a bot' in cls.hb.app.
                    output_field.document.text)
def minimum_order_amount(trading_pair):
    try:
        base_asset, quote_asset = trading_pair.split("-")
        default_quote_asset, default_amount = default_min_quote(quote_asset)
        quote_amount = ExchangeRateConversion.get_instance(
        ).convert_token_value_decimal(default_amount, default_quote_asset,
                                      base_asset)
    except Exception:
        quote_amount = Decimal('0')
    return round(quote_amount, 4)
Пример #20
0
    def __init__(self, hummingbot_app: "HummingbotApplication",
                 report_aggregation_interval: float = 60.0,
                 log_report_interval: float = 60.0):

        self.log_report_interval: float = log_report_interval
        self.report_aggregation_interval: float = report_aggregation_interval
        self.stats: dict = defaultdict(list)
        self.hummingbot_app: "HummingbotApplication" = hummingbot_app
        self.get_open_order_stats_task: Optional[asyncio.Task] = None
        self.get_event_task: Optional[asyncio.Task] = None
        self.log_report_task: Optional[asyncio.Task] = None
        self.exchange_converter: ExchangeRateConversion = ExchangeRateConversion().get_instance()
Пример #21
0
    def start(self, log_level: Optional[str] = None):
        is_valid = self.status()
        if not is_valid:
            return

        if log_level is not None:
            init_logging("hummingbot_logs.yml",
                         override_log_level=log_level.upper())

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

        # TODO add option to select data feed
        self.data_feed: DataFeedBase = CoinCapDataFeed.get_instance()

        ExchangeRateConversion.get_instance().start()
        strategy_name = in_memory_config_map.get("strategy").value
        self.init_reporting_module()
        self.app.log(
            f"\n  Status check complete. Starting '{strategy_name}' strategy..."
        )
        asyncio.ensure_future(self.start_market_making(strategy_name))
Пример #22
0
 def setUpClass(cls):
     cls.ev_loop: asyncio.BaseEventLoop = asyncio.get_event_loop()
     ExchangeRateConversion.get_instance().set_data_feeds(
         [MockDataFeed1.get_instance()])
     cls._weth_price = 1.0
     cls._eth_price = 1.0
     cls._dai_price = 0.95
     cls._usdc_price = 1.05
     cls._price = 50
     ExchangeRateConversion.set_global_exchange_rate_config(
         {"default_data_feed": "coin_alpha_feed"})
     ExchangeRateConversion.get_instance().start()
     cls.ev_loop.run_until_complete(
         cls.run_parallel_async(
             ExchangeRateConversion.get_instance().wait_till_ready()))
    def setUpClass(cls):
        # XXX(martin_kou): ExchangeRatioConversion is a f*****g mess now. Need to manually reset it.
        # See: https://app.clubhouse.io/coinalpha/story/8346/clean-up-exchangerateconversion
        if ExchangeRateConversion._erc_shared_instance is not None:
            ExchangeRateConversion._erc_shared_instance.stop()
            ExchangeRateConversion._erc_shared_instance = None

        ExchangeRateConversion._exchange_rate = {}
        ExchangeRateConversion._all_data_feed_exchange_rate = {}
        ExchangeRateConversion._ready_notifier = asyncio.Event()

        ExchangeRateConversion.set_global_exchange_rate_config({
            "global_config": {
                "WETH": {
                    "default": 1.0,
                    "source": "None"
                },
                "ETH": {
                    "default": 1.0,
                    "source": "None"
                },
                "QETH": {
                    "default": 0.95,
                    "source": "None"
                },
                "DAI": {
                    "default": 1.0,
                    "source": "None"
                },
            },
            "conversion_required": {
                "WETH": {
                    "default": 1.0,
                    "source": "None"
                },
                "QETH": {
                    "default": 0.95,
                    "source": "None"
                },
            },
        })
        ExchangeRateConversion.set_data_feeds([])
        ExchangeRateConversion.get_instance().start()

        asyncio.get_event_loop().run_until_complete(
            ExchangeRateConversion.get_instance().wait_till_ready())
    def setUpClass(cls):
        cls.maxDiff = None
        cls.trade_fill_sql = SQLConnectionManager(SQLConnectionType.TRADE_FILLS, db_path="")
        cls.ev_loop: asyncio.BaseEventLoop = asyncio.get_event_loop()
        ExchangeRateConversion.get_instance().set_data_feeds([MockDataFeed1.get_instance()])
        cls._weth_price = 1.0
        cls._eth_price = 1.0
        cls._dai_price = 0.95
        cls._usdc_price = 1.05

        cls.trading_pair_tuple_1 = MarketTradingPairTuple(MockMarket1(), "WETHDAI", "WETH", "DAI")
        cls.trading_pair_tuple_2 = MarketTradingPairTuple(MockMarket2(), "ETHUSDC", "ETH", "USDC")
        cls.strategy_1 = "strategy_1"
        ExchangeRateConversion.set_global_exchange_rate_config({
            "default_data_feed": "coin_alpha_feed"
        })
        ExchangeRateConversion.get_instance().start()
        cls.ev_loop.run_until_complete(cls.run_parallel_async(ExchangeRateConversion.get_instance().wait_till_ready()))
 def test_adjust_token_rate(self):
     adjusted_cat = ExchangeRateConversion.get_instance().adjust_token_rate(
         "cat", Decimal(10))
     self.assertEqual(adjusted_cat, Decimal(50))
 def setUp(self):
     async_run(ExchangeRateConversion.get_instance().
               update_exchange_rates_from_data_feeds())
    def calculate_trade_performance(self,
                                    current_strategy_name: str,
                                    market_trading_pair_tuples: List[MarketTradingPairTuple],
                                    raw_queried_trades: List[TradeFill],
                                    starting_balances: Dict[str, Dict[str, Decimal]]) \
            -> Tuple[Dict, Dict]:
        """
        Calculate total spent and acquired amount for the whole portfolio in quote value.

        :param current_strategy_name: Name of the currently configured strategy
        :param market_trading_pair_tuples: Current MarketTradingPairTuple
        :param raw_queried_trades: List of queried trades
        :param starting_balances: Dictionary of starting asset balance for each market, as balance_snapshot on
        history command.
        :return: Dictionary consisting of total spent and acquired across whole portfolio in quote value,
                 as well as individual assets
        """
        ERC = ExchangeRateConversion.get_instance()
        trade_performance_stats: Dict[str, Decimal] = {}
        # The final stats will be in primary quote unit
        primary_quote_asset: str = market_trading_pair_tuples[0].quote_asset
        market_trading_pair_stats: Dict[str, Dict[
            str, Decimal]] = self.calculate_asset_delta_from_trades(
                current_strategy_name, market_trading_pair_tuples,
                raw_queried_trades)

        # Calculate total spent and acquired amount for each trading pair in primary quote value
        for market_trading_pair_tuple, trading_pair_stats in market_trading_pair_stats.items(
        ):
            market_trading_pair_tuple: MarketTradingPairTuple
            base_asset: str = market_trading_pair_tuple.base_asset.upper()
            quote_asset: str = market_trading_pair_tuple.quote_asset.upper()
            quote_rate: Decimal = market_trading_pair_tuple.get_mid_price()
            trading_pair_stats["end_quote_rate"] = quote_rate
            asset_stats: Dict[str, Decimal] = trading_pair_stats["asset"]

            # Calculate delta amount and delta percentage for each asset based on spent and acquired amount
            for asset, stats in asset_stats.items():
                stats["delta"] = stats["acquired"] - stats["spent"]

                if stats["spent"] == s_decimal_0 and stats[
                        "acquired"] > s_decimal_0:
                    stats["delta_percentage"] = Decimal("100")
                elif stats["spent"] == s_decimal_0 and stats[
                        "acquired"] == s_decimal_0:
                    stats["delta_percentage"] = s_decimal_0
                else:
                    stats["delta_percentage"] = (
                        (stats["acquired"] / stats["spent"]) -
                        Decimal("1")) * Decimal("100")
            # Convert spent and acquired amount for base asset to quote asset value
            spent_base_quote_value: Decimal = asset_stats[base_asset][
                "spent"] * quote_rate
            acquired_base_quote_value: Decimal = asset_stats[base_asset][
                "acquired"] * quote_rate

            # Calculate total spent and acquired of a trading pair
            combined_spent: Decimal = spent_base_quote_value + asset_stats[
                quote_asset]["spent"]
            combined_acquired: Decimal = acquired_base_quote_value + asset_stats[
                quote_asset]["acquired"]

            market_name = market_trading_pair_tuple.market.name
            if base_asset in starting_balances and market_name in starting_balances[
                    base_asset]:
                starting_base = Decimal(
                    starting_balances[base_asset][market_name])
            else:
                starting_base = Decimal("0")
            if quote_asset in starting_balances and market_name in starting_balances[
                    quote_asset]:
                starting_quote = Decimal(
                    starting_balances[quote_asset][market_name])
            else:
                starting_quote = Decimal("0")
            if starting_base + starting_quote == 0:
                raise ValueError("Starting balances must be supplied.")
            starting_total = starting_quote + (starting_base * quote_rate)
            # Convert trading pair's spent and acquired amount into primary quote asset value
            # (primary quote asset is the quote asset of the first trading pair)
            trading_pair_stats[
                "acquired_quote_value"] = ERC.convert_token_value_decimal(
                    combined_acquired,
                    quote_asset,
                    primary_quote_asset,
                    source="default")
            trading_pair_stats[
                "spent_quote_value"] = ERC.convert_token_value_decimal(
                    combined_spent,
                    quote_asset,
                    primary_quote_asset,
                    source="default")
            trading_pair_stats[
                "starting_quote_value"] = ERC.convert_token_value_decimal(
                    starting_total,
                    quote_asset,
                    primary_quote_asset,
                    source="default")

            trading_pair_stats[
                "trading_pair_delta"] = combined_acquired - combined_spent

            if combined_acquired == s_decimal_0 or combined_spent == s_decimal_0:
                trading_pair_stats[
                    "trading_pair_delta_percentage"] = s_decimal_0
                continue
            trading_pair_stats["trading_pair_delta_percentage"] = \
                ((combined_acquired - combined_spent) / starting_total) * Decimal("100")

        portfolio_acquired_quote_value: Decimal = sum(
            s["acquired_quote_value"]
            for s in market_trading_pair_stats.values())
        portfolio_spent_quote_value: Decimal = sum(
            s["spent_quote_value"] for s in market_trading_pair_stats.values())
        portfolio_starting_quote_value: Decimal = sum(
            s["starting_quote_value"]
            for s in market_trading_pair_stats.values())

        if portfolio_acquired_quote_value == s_decimal_0 or portfolio_spent_quote_value == s_decimal_0:
            portfolio_delta_percentage: Decimal = s_decimal_0
        else:
            portfolio_delta_percentage: Decimal = (
                (portfolio_acquired_quote_value - portfolio_spent_quote_value)
                / portfolio_starting_quote_value) * Decimal("100")

        trade_performance_stats[
            "portfolio_acquired_quote_value"] = portfolio_acquired_quote_value
        trade_performance_stats[
            "portfolio_spent_quote_value"] = portfolio_spent_quote_value
        trade_performance_stats[
            "portfolio_delta"] = portfolio_acquired_quote_value - portfolio_spent_quote_value
        trade_performance_stats[
            "portfolio_delta_percentage"] = portfolio_delta_percentage

        return trade_performance_stats, market_trading_pair_stats
 def test_get_multiple_data_feed(self):
     exchaneg_rate = ExchangeRateConversion.get_instance().exchange_rate
     self.assertEqual(exchaneg_rate, {'CAT': 5, 'COIN_ALPHA': 1})
 def test_get_multiple_data_feed(self):
     exchaneg_rate = ExchangeRateConversion.get_instance().exchange_rate
     self.assertEqual(exchaneg_rate, {'cat': 5, 'coin_alpha': 1})
Пример #30
0
async def quick_start(args):
    strategy = args.strategy
    config_file_name = args.config_file_name
    wallet = args.wallet
    password = args.config_password

    if password is not None and not Security.login(password):
        logging.getLogger().error(f"Invalid password.")
        return

    await Security.wait_til_decryption_done()
    await create_yml_files()
    init_logging("hummingbot_logs.yml")
    read_system_configs_from_yml()

    ExchangeRateConversion.get_instance().start()
    await ExchangeRateConversion.get_instance().wait_till_ready()
    hb = HummingbotApplication.main_application()
    # Todo: validate strategy and config_file_name before assinging

    if config_file_name is not None and strategy is not None:
        hb.strategy_name = strategy
        hb.strategy_file_name = config_file_name
        update_strategy_config_map_from_file(
            os.path.join(CONF_FILE_PATH, config_file_name))

    # To ensure quickstart runs with the default value of False for kill_switch_enabled if not present
    if not global_config_map.get("kill_switch_enabled"):
        global_config_map.get("kill_switch_enabled").value = False

    if wallet and password:
        global_config_map.get("ethereum_wallet").value = wallet

    if hb.strategy_name and hb.strategy_file_name:
        if not all_configs_complete(hb.strategy_name):
            hb.status()

    with patch_stdout(log_field=hb.app.log_field):
        dev_mode = check_dev_mode()
        if dev_mode:
            hb.app.log(
                "Running from dev branches. Full remote logging will be enabled."
            )

        log_level = global_config_map.get("log_level").value
        init_logging("hummingbot_logs.yml",
                     override_log_level=log_level,
                     dev_mode=dev_mode)

        if hb.strategy_file_name is not None and hb.strategy_name is not None:
            await write_config_to_yml(hb.strategy_name, hb.strategy_file_name)
            hb.start(log_level)

        tasks: List[Coroutine] = [hb.run()]
        if global_config_map.get("debug_console").value:
            management_port: int = detect_available_port(8211)
            tasks.append(
                start_management_console(locals(),
                                         host="localhost",
                                         port=management_port))
        await safe_gather(*tasks)