From 7f3eea9a3f013984283ae7bbedb36eeb1cb219d0 Mon Sep 17 00:00:00 2001 From: Bob Wang Date: Wed, 7 Jul 2021 10:09:38 +0800 Subject: [PATCH] Auto adjust BNB balance --- .user.cfg.example | 4 +- README.md | 4 ++ binance_trade_bot/binance_api_manager.py | 56 +++++++++++++++++++++++- binance_trade_bot/config.py | 11 ++++- 4 files changed, 71 insertions(+), 4 deletions(-) diff --git a/.user.cfg.example b/.user.cfg.example index a70932351..0a6c115ff 100755 --- a/.user.cfg.example +++ b/.user.cfg.example @@ -19,4 +19,6 @@ trade_fee=auto price_type=orderbook accept_losses=false max_idle_hours=72 -ratio_adjust_weight=500 \ No newline at end of file +ratio_adjust_weight=500 +auto_adjust_bnb_balance=false +auto_adjust_bnb_balance_rate=3 diff --git a/README.md b/README.md index 50ede5e21..d946843c8 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,8 @@ Create a .cfg file named `user.cfg` based off `.user.cfg.example`, then add your - **accept_losses** - Needs to be set to true for highly risky and gamling strategies. Otherwise the bot wont start. - **max_idle_hours** - Controls the amount of hours for reseting the ratios when the bot has not traded (only used in db_reset strategy) - **ratio_adjust_weight** - Controls the weight of the cumulative moving ratio avarage in the ratio_adjust strategy (only used in ratio_adjust strategy) +- **auto_adjust_bnb_balance** - Controls the bot to auto buy BNB while there is no enough BNB balance in your account, to get the benifits of using BNB to pay the commisions. Default is false. Effective if you have enabled to [use BNB to pay for any fees on the Binance platform](https://www.binance.com/en/support/faq/115000583311-Using-BNB-to-Pay-for-Fees), reade more information [here](#paying-fees-with-bnb). +- **auto_adjust_bnb_balance_rate** - The multiplying power of buying quantity of BNB compares to evaluated comission of the coming order, effective only if auto_adjust_bnb_balance is true. Default value is 3. #### Environment Variables @@ -91,6 +93,8 @@ BUY_TIMEOUT: 0 SELL_TIMEOUT: 0 BUY_ORDER_TYPE: limit SELL_ORDER_TYPE: market +AUTO_ADJUST_BNB_BALANCE: false +AUTO_ADJUST_BNB_BALANCE_RATE: 3 ``` ### Paying Fees with BNB diff --git a/binance_trade_bot/binance_api_manager.py b/binance_trade_bot/binance_api_manager.py index 4d891ab46..a8c7225f3 100755 --- a/binance_trade_bot/binance_api_manager.py +++ b/binance_trade_bot/binance_api_manager.py @@ -410,6 +410,10 @@ def get_alt_tick(self, origin_symbol: str, target_symbol: str): def get_min_notional(self, origin_symbol: str, target_symbol: str): return float(self.get_symbol_filter(origin_symbol, target_symbol, "MIN_NOTIONAL")["minNotional"]) + @cached(cache=TTLCache(maxsize=2000, ttl=43200)) + def get_min_qty(self, origin_symbol: str, target_symbol: str): + return float(self.get_symbol_filter(origin_symbol, target_symbol, "LOT_SIZE")["minQty"]) + def _wait_for_order( self, order_id, origin_symbol: str, target_symbol: str ) -> Optional[BinanceOrder]: # pylint: disable=unsubscriptable-object @@ -494,6 +498,48 @@ def _should_cancel_order(self, order_status): return False + def _adjust_bnb_balance(self, origin_coin: Coin, target_coin: Coin): + if not self.get_using_bnb_for_fees(): + # No need to adjust bnb balance if not using bnb for fees + return + + base_fee = self.get_trade_fees()[origin_coin + target_coin] + + # The discount is only applied if we have enough BNB to cover the fee + amount_trading = self._buy_quantity(origin_coin.symbol, target_coin.symbol) + + fee_amount = amount_trading * base_fee * 0.75 + if origin_coin.symbol == "BNB": + fee_amount_bnb = fee_amount + else: + origin_price = self.get_ticker_price(origin_coin.symbol + "BNB") + if origin_price is None: + return + fee_amount_bnb = fee_amount * origin_price + + bnb_balance = self.get_currency_balance("BNB") + + if bnb_balance >= fee_amount_bnb: + # No need to buy more bnb + return + + min_qty = self.get_min_qty("BNB", target_coin.symbol) + alt_tick = self.get_alt_tick("BNB", target_coin.symbol) + # Default value of AUTO_ADJUST_BNB_BALANCE_RATE is 3, means trying to buy 3x BNB compare to the commision needed by the coming order. + # Put "3x" as default since: 1. buy commision, 2. sell commision, 3. buffer, since selling price may rise and then needs more comission. + fee_amount_bnb_ceil = math.ceil((fee_amount_bnb * self.config.AUTO_ADJUST_BNB_BALANCE_RATE - bnb_balance) * 10 ** alt_tick) / float(10 ** alt_tick) + + min_notional = self.get_min_notional("BNB", target_coin.symbol) + bnb_price = self.get_ticker_price("BNB" + target_coin.symbol) + # multiply 1.01 considering that market price is changing + min_qty_for_min_notinal = math.ceil((min_notional / bnb_price) * 1.01 * 10 ** alt_tick) / float(10 ** alt_tick) + + buy_quantity = max(min_qty, fee_amount_bnb_ceil, min_qty_for_min_notinal) + + self.logger.info(f"Needed/available BNB balance: {fee_amount_bnb}/{bnb_balance}, buy quantity: {buy_quantity}...") + + self.retry(self._buy_alt, Coin("BNB"), target_coin, bnb_price, buy_quantity) + def buy_alt(self, origin_coin: Coin, target_coin: Coin, buy_price: float) -> BinanceOrder: return self.retry(self._buy_alt, origin_coin, target_coin, buy_price) @@ -510,10 +556,13 @@ def _buy_quantity( def float_as_decimal_str(num: float): return f"{num:0.08f}".rstrip("0").rstrip(".") # remove trailing zeroes too - def _buy_alt(self, origin_coin: Coin, target_coin: Coin, buy_price: float): # pylint: disable=too-many-locals + def _buy_alt(self, origin_coin: Coin, target_coin: Coin, buy_price: float, buy_quantity: float=None): # pylint: disable=too-many-locals """ Buy altcoin """ + if self.config.AUTO_ADJUST_BNB_BALANCE and origin_coin.symbol != "BNB": + self._adjust_bnb_balance(origin_coin, target_coin) + origin_symbol = origin_coin.symbol target_symbol = target_coin.symbol @@ -531,7 +580,10 @@ def _buy_alt(self, origin_coin: Coin, target_coin: Coin, buy_price: float): # p #from_coin_price = min(buy_price, from_coin_price) trade_log = self.db.start_trade_log(origin_coin, target_coin, False) - order_quantity = self._buy_quantity(origin_symbol, target_symbol, target_balance, from_coin_price) + if buy_quantity is None: + order_quantity = self._buy_quantity(origin_symbol, target_symbol, target_balance, from_coin_price) + else: + order_quantity = buy_quantity self.logger.info(f"BUY QTY {order_quantity} of <{origin_symbol}>") # Try to buy until successful diff --git a/binance_trade_bot/config.py b/binance_trade_bot/config.py index 4054b06e3..6575e68b3 100644 --- a/binance_trade_bot/config.py +++ b/binance_trade_bot/config.py @@ -37,7 +37,9 @@ def __init__(self): "price_type": self.PRICE_TYPE_ORDERBOOK, "accept_losses": "false", "max_idle_hours": "3", - "ratio_adjust_weight":"100" + "ratio_adjust_weight":"100", + "auto_adjust_bnb_balance": "false", + "auto_adjust_bnb_balance_rate": "3", } if not os.path.exists(CFG_FL_NAME): @@ -148,3 +150,10 @@ def __init__(self): self.ACCEPT_LOSSES = accept_losses_str == 'true' or accept_losses_str == 'True' self.MAX_IDLE_HOURS = os.environ.get("MAX_IDLE_HOURS") or config.get(USER_CFG_SECTION, "max_idle_hours") + + auto_adjust_bnb_balance_str = os.environ.get("AUTO_ADJUST_BNB_BALANCE") or config.get(USER_CFG_SECTION, "auto_adjust_bnb_balance") + self.AUTO_ADJUST_BNB_BALANCE = str(auto_adjust_bnb_balance_str).lower() == "true" + + self.AUTO_ADJUST_BNB_BALANCE_RATE = float( + os.environ.get("AUTO_ADJUST_BNB_BALANCE_RATE") or config.get(USER_CFG_SECTION, "auto_adjust_bnb_balance_rate") + )