From 0258b68e65d05278442cad8ab709683f3a63f95d Mon Sep 17 00:00:00 2001 From: DogsTailFarmer Date: Sat, 13 Jul 2024 21:28:26 +0300 Subject: [PATCH] 3.0.11 --- CHANGELOG.md | 8 +- README.md | 4 +- martin_binance/__init__.py | 2 +- martin_binance/executor.py | 21 +++- martin_binance/params.py | 25 +++-- martin_binance/strategy_base.py | 103 +++++++++++++++--- martin_binance/templates/cli_0_BTCUSDT.py | 11 +- martin_binance/templates/cli_1_BTCUSDT.py | 15 ++- .../templates/cli_2_TESTBTCTESTUSDT.py | 11 +- martin_binance/templates/cli_3_BTCUSDT.py | 11 +- pyproject.toml | 2 +- requirements.txt | 2 +- 12 files changed, 176 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78231bd..0830a07 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,19 @@ +## 3.0.11 - 2024-07-13 +### Added new features +* [Maintaining a supply of BNB in sub-account to pay fees](https://github.com/DogsTailFarmer/martin-binance/wiki/How-it's-work#maintaining-a-supply-of-bnb-in-a-sub-account-to-pay-fees) + ## 3.0.10 - 2024-07-07 ### Fix * `on_balance_update_ex`: excessive recalculation of income when withdrawing funds ### Update * Processing account replenishment in all states with increasing turnover -* Dependency !!!!!!! +* Dependency ## 3.0.9 - 2024-07-07 ### Added new features * Updated `APY profit rating` Grafana report, used data from selected time range for APY calculation -* Processing account replenishment in all states with increasing turnover +* Processing [account replenishment](https://github.com/DogsTailFarmer/martin-binance/wiki/How-it's-work#deposit-and-withdraw-assets-on-active-strategy) in all states with increasing turnover ## 3.0.8 - 2024-06-26 ### Fix diff --git a/README.md b/README.md index 0f70c0d..54907ee 100755 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ All risks and possible losses associated with use of this strategy lie with you. Strongly recommended that you test the strategy in the demo mode before using real bidding. ## Important notices -* After update to `3.0.1`, the configuration files `cli_XX_AAABBB.py` for all running trading pairs +* After update to `3.0.11`, the configuration files `cli_XX_AAABBB.py` for `Binance` trading pairs should be updated. Use templates for reference. * You cannot run multiple pairs with overlapping currencies on the same account! @@ -37,7 +37,7 @@ should be updated. Use templates for reference. > >As a result of the mutual impact on the operating balance sheet, the liquidity control system will block the work. -* Due to a limitation in the implementation of asyncio under Windows, this program cannot be executed. [Use with Docker on Windows instead.](https://github.com/DogsTailFarmer/martin-binance/wiki/Quick-start#docker) +* Due to a limitation in the implementation of asyncio under **Windows**, this program cannot be executed. [Use with Docker on Windows instead.](https://github.com/DogsTailFarmer/martin-binance/wiki/Quick-start#docker) ## References * Detailed information about use this strategy placed to [wiki](https://github.com/DogsTailFarmer/martin-binance/wiki) diff --git a/martin_binance/__init__.py b/martin_binance/__init__.py index 094701d..ddf29f3 100755 --- a/martin_binance/__init__.py +++ b/martin_binance/__init__.py @@ -6,7 +6,7 @@ __author__ = "Jerry Fedorenko" __copyright__ = "Copyright © 2021 Jerry Fedorenko aka VM" __license__ = "MIT" -__version__ = "3.0.10" +__version__ = "3.0.11" __maintainer__ = "Jerry Fedorenko" __contact__ = "https://github.com/DogsTailFarmer" diff --git a/martin_binance/executor.py b/martin_binance/executor.py index 931c4e3..f125a4e 100644 --- a/martin_binance/executor.py +++ b/martin_binance/executor.py @@ -4,7 +4,7 @@ __author__ = "Jerry Fedorenko" __copyright__ = "Copyright © 2021 Jerry Fedorenko aka VM" __license__ = "MIT" -__version__ = "3.0.10" +__version__ = "3.0.11" __maintainer__ = "Jerry Fedorenko" __contact__ = 'https://github.com/DogsTailFarmer' ################################################################## @@ -310,7 +310,20 @@ def event_get_command_tlg(self): row = None print(f"SELECT from t_control: {err}") if row and row[0]: - self.command = row[1] + if 'BNB_request' in row[1]: + params = json.loads(row[1])[1] + self.transfer_to( + 'BNB', + any2str( + max( + Decimal(params['tranche_volume']), + self.get_trading_capability_manager().min_notional + ) / self.get_buffered_ticker().last_price + ), + params['email'] + ) + else: + self.command = row[1] # Remove applied command from .db try: self.connection_analytic.execute( @@ -981,12 +994,12 @@ def collect_assets(self) -> (): tcm = self.get_trading_capability_manager() if ff >= f2d(tcm.min_qty): self.message_log(f"Sending {ff} {self.f_currency} to main account", color=Style.UNDERLINE) - self.transfer_to_master(self.f_currency, any2str(ff)) + self.transfer_to(self.f_currency, any2str(ff)) else: ff = O_DEC if fs >= f2d(tcm.min_notional): self.message_log(f"Sending {fs} {self.s_currency} to main account", color=Style.UNDERLINE) - self.transfer_to_master(self.s_currency, any2str(fs)) + self.transfer_to(self.s_currency, any2str(fs)) else: fs = O_DEC return ff, fs diff --git a/martin_binance/params.py b/martin_binance/params.py index 9a7c583..cca93e9 100644 --- a/martin_binance/params.py +++ b/martin_binance/params.py @@ -4,7 +4,7 @@ __author__ = "Jerry Fedorenko" __copyright__ = "Copyright © 2021 Jerry Fedorenko aka VM" __license__ = "MIT" -__version__ = "3.0.5" +__version__ = "3.0.11" __maintainer__ = "Jerry Fedorenko" __contact__ = "https://github.com/DogsTailFarmer" @@ -13,14 +13,14 @@ from pathlib import Path __all__ = [ - 'SYMBOL', 'EXCHANGE', 'ID_EXCHANGE', 'FEE_MAKER', 'FEE_TAKER', 'FEE_FIRST', 'FEE_SECOND', 'GRID_MAX_COUNT', - 'START_ON_BUY', 'AMOUNT_FIRST', 'USE_ALL_FUND', 'AMOUNT_SECOND', 'PRICE_SHIFT', 'PRICE_LIMIT_RULES', - 'ROUND_BASE', 'ROUND_QUOTE', 'PROFIT', 'PROFIT_MAX', 'OVER_PRICE', 'ORDER_Q', 'MARTIN', 'SHIFT_GRID_DELAY', - 'GRID_UPDATE_INTERVAL', 'STATUS_DELAY', 'GRID_ONLY', 'LOG_LEVEL', 'HOLD_TP_ORDER_TIMEOUT', 'COLLECT_ASSETS', - 'GRID_ONLY_DELAY', 'ADAPTIVE_TRADE_CONDITION', 'BB_CANDLE_SIZE_IN_MINUTES', 'BB_NUMBER_OF_CANDLES', 'KBB', - 'LINEAR_GRID_K', 'ADX_CANDLE_SIZE_IN_MINUTES', 'ADX_NUMBER_OF_CANDLES', 'ADX_PERIOD', 'ADX_THRESHOLD', - 'ADX_PRICE_THRESHOLD', 'REVERSE', 'REVERSE_TARGET_AMOUNT', 'REVERSE_INIT_AMOUNT', 'REVERSE_STOP', - 'HEAD_VERSION', 'LOAD_LAST_STATE', 'LAST_STATE_FILE', 'VPS_NAME', 'PARAMS', 'TELEGRAM_URL', 'TOKEN', + 'SYMBOL', 'EXCHANGE', 'ID_EXCHANGE', 'FEE_MAKER', 'FEE_TAKER', 'FEE_FIRST', 'FEE_SECOND', 'FEE_BNB', + 'GRID_MAX_COUNT', 'START_ON_BUY', 'AMOUNT_FIRST', 'USE_ALL_FUND', 'AMOUNT_SECOND', 'PRICE_SHIFT', + 'PRICE_LIMIT_RULES', 'ROUND_BASE', 'ROUND_QUOTE', 'PROFIT', 'PROFIT_MAX', 'OVER_PRICE', 'ORDER_Q', 'MARTIN', + 'SHIFT_GRID_DELAY', 'GRID_UPDATE_INTERVAL', 'STATUS_DELAY', 'GRID_ONLY', 'LOG_LEVEL', 'HOLD_TP_ORDER_TIMEOUT', + 'COLLECT_ASSETS', 'GRID_ONLY_DELAY', 'ADAPTIVE_TRADE_CONDITION', 'BB_CANDLE_SIZE_IN_MINUTES', + 'BB_NUMBER_OF_CANDLES', 'KBB', 'LINEAR_GRID_K', 'ADX_CANDLE_SIZE_IN_MINUTES', 'ADX_NUMBER_OF_CANDLES', + 'ADX_PERIOD', 'ADX_THRESHOLD', 'ADX_PRICE_THRESHOLD', 'REVERSE', 'REVERSE_TARGET_AMOUNT', 'REVERSE_INIT_AMOUNT', + 'REVERSE_STOP', 'HEAD_VERSION', 'LOAD_LAST_STATE', 'LAST_STATE_FILE', 'VPS_NAME', 'PARAMS', 'TELEGRAM_URL', 'TOKEN', 'CHANNEL_ID', 'STOP_TLG', 'INLINE_BOT', 'MODE', 'XTIME', 'SAVE_DS', 'SAVE_PERIOD', 'LOGGING', 'SELF_OPTIMIZATION', 'N_TRIALS', 'SESSION_RESULT' ] @@ -33,6 +33,13 @@ FEE_TAKER = Decimal() FEE_FIRST = False FEE_SECOND = False +FEE_BNB = { + 'id_exchange': 0, + 'symbol': 'BNB/USDT', + 'email': str(), + 'target_amount': '0', + 'tranche_volume': '0' +} GRID_MAX_COUNT = int() # Trade parameter START_ON_BUY = bool() diff --git a/martin_binance/strategy_base.py b/martin_binance/strategy_base.py index da26662..e67e901 100644 --- a/martin_binance/strategy_base.py +++ b/martin_binance/strategy_base.py @@ -4,7 +4,7 @@ __author__ = "Jerry Fedorenko" __copyright__ = "Copyright © 2021 Jerry Fedorenko aka VM" __license__ = "MIT" -__version__ = "3.0.7" +__version__ = "3.0.11" __maintainer__ = "Jerry Fedorenko" __contact__ = "https://github.com/DogsTailFarmer" @@ -239,9 +239,12 @@ def get_time(self) -> float: last = current_time return last - def transfer_to_master(self, symbol: str, amount: str): + def transfer_to(self, symbol: str, amount: str, email=None): if prm.MODE in ('T', 'TC'): - self.tasks_manage(self.transfer2master(symbol, amount)) + if email: + self.tasks_manage(self.transfer2sub(email, symbol, amount)) + else: + self.tasks_manage(self.transfer2master(symbol, amount)) def place_limit_order(self, buy: bool, amount: Decimal, price: Decimal) -> int: self.order_id += 1 @@ -599,17 +602,28 @@ async def save_asset(self): if res: balances = list(map(json.loads, res.items)) # Refresh actual balance - try: - balance_f = next(item for item in balances if item["asset"] == self.base_asset) - except StopIteration: - balance_f = {'asset': self.base_asset, 'free': '0.0', 'locked': '0.0'} - try: - balance_s = next(item for item in balances if item["asset"] == self.quote_asset) - except StopIteration: - balance_s = {'asset': self.base_asset, 'free': '0.0', 'locked': '0.0'} + default_balance = {'free': '0.0', 'locked': '0.0'} + + if self.exchange == 'binance' and \ + not (prm.FEE_FIRST and prm.FEE_SECOND) and (prm.FEE_MAKER or prm.FEE_TAKER): + + await self.fee_generate_bnb_request(balances, connection_analytic, default_balance) + + balance_f = next( + (item for item in balances if item["asset"] == self.base_asset), + default_balance + ).copy() + balance_f.pop('asset', None) + + balance_s = next( + (item for item in balances if item["asset"] == self.quote_asset), + default_balance + ).copy() + balance_s.pop('asset', None) + self.funds = { - self.base_asset: {'free': balance_f['free'], 'locked': balance_f['locked']}, - self.quote_asset: {'free': balance_s['free'], 'locked': balance_s['locked']} + self.base_asset: balance_f, + self.quote_asset: balance_s } # Get asset balances from Funding Wallet funding_wallet = [] @@ -662,6 +676,46 @@ async def save_asset(self): self.message_log(f"Refresh t_asset: {err}", log_level=logging.WARNING) await asyncio.sleep(delay) + async def fee_generate_bnb_request(self, balances, connection_analytic, default_balance): + bnb = next((item for item in balances if item["asset"] == 'BNB'), default_balance)['free'] + _price = await self.send_request( + self.stub.fetch_symbol_price_ticker, + mr.MarketRequest, + symbol=prm.FEE_BNB['symbol'].replace('/', '') + ) + price = _price.to_pydict()['price'] + if (Decimal(bnb) * Decimal(price) <= + max(self.tcm.min_notional, Decimal(prm.FEE_BNB['target_amount']))): + bot_id = f"{prm.EXCHANGE[prm.FEE_BNB['id_exchange']]}, {prm.FEE_BNB['symbol']}" + cursor = connection_analytic.cursor() + try: + cursor.execute( + 'SELECT max(message_id), text_in\ + FROM t_control \ + WHERE bot_id=:bot_id', + {'bot_id': bot_id} + ) + row = cursor.fetchone() + cursor.close() + except sqlite3.Error as err: + cursor.close() + row = None + print(f"SELECT from t_control: {err}") + + if row and (row[0] is None or prm.FEE_BNB['email'] not in row[1]): + msg = json.dumps(['BNB_request', prm.FEE_BNB]) + try: + connection_analytic.execute( + 'insert into t_control values(?,?,?,?)', + ((row[0] or 0) + 1, msg, bot_id, None) + ) + connection_analytic.commit() + except sqlite3.Error as err: + logger.error(f"INSERT into t_control: {err}") + else: + self.message_log(f"BNB request was generated from {bot_id} to {prm.FEE_BNB['email']}", + color=Style.BLUE) + async def ask_exit(self): self.message_log("Got signal for exit", color=Style.MAGENTA) self.operational_status = False @@ -901,6 +955,29 @@ async def transfer2master(self, symbol: str, amount: str): self.message_log(f"Not sent {amount} {symbol} to main account\n,{res.result}", log_level=logging.WARNING) + async def transfer2sub(self, email: str, symbol: str, amount: str): + try: + res = await self.send_request( + self.stub.transfer_to_sub, + mr.MarketRequest, + symbol=symbol, + amount=amount, + data=email + ) + except asyncio.CancelledError: + pass # Task cancellation should not be logged as an error + except GRPCError as ex: + status_code = ex.status + self.message_log(f"Exception transfer {symbol} to subaccount: {status_code.name}, {ex.message}") + except Exception as _ex: + self.message_log(f"Exception transfer {symbol} to subaccount: {_ex}") + else: + if res.success: + self.message_log(f"Sent {amount} {symbol} to subaccount {email}", log_level=logging.INFO) + else: + self.message_log(f"Not sent {amount} {symbol} to subaccount {email}\n,{res.result}", + log_level=logging.WARNING) + async def buffered_funds(self, print_info: bool = True): try: if prm.MODE in ('T', 'TC'): diff --git a/martin_binance/templates/cli_0_BTCUSDT.py b/martin_binance/templates/cli_0_BTCUSDT.py index 94d89a9..6b4acb0 100644 --- a/martin_binance/templates/cli_0_BTCUSDT.py +++ b/martin_binance/templates/cli_0_BTCUSDT.py @@ -7,7 +7,7 @@ __author__ = "Jerry Fedorenko" __copyright__ = "Copyright © 2021 Jerry Fedorenko aka VM" __license__ = "MIT" -__version__ = "3.0.3" +__version__ = "3.0.11" __maintainer__ = "Jerry Fedorenko" __contact__ = "https://github.com/DogsTailFarmer" """ @@ -41,6 +41,15 @@ ex.FEE_TAKER = Decimal('0.1') # standard exchange Fee for taker ex.FEE_FIRST = False # For example fee in BNB and BNB in pair, and it is base asset ex.FEE_SECOND = False # For example fee in BNB and BNB in pair, and it is quote asset +# Setting for auto deposit BNB on subaccount for fee payment. For Binance subaccount only. +# See also https://github.com/DogsTailFarmer/martin-binance/wiki/How-it's-work#keeping-level-of-first-asset +ex.FEE_BNB = { + 'id_exchange': 0, # Where collected assets and keeping BNB volume + 'symbol': 'BNB/USDT', # Specified on the source strategy (id_exchange above) + 'email': 'sub-account@email.com', # Email registered on this subaccount + 'target_amount': '0', # BNB in USD equivalent, no less than min_notional + 'tranche_volume': '0' # BNB in USD equivalent, no less than min_notional +} ex.GRID_MAX_COUNT = 5 # Maximum counts for placed grid orders # Trade parameter ex.START_ON_BUY = True # First cycle direction diff --git a/martin_binance/templates/cli_1_BTCUSDT.py b/martin_binance/templates/cli_1_BTCUSDT.py index f0ba03c..6cc9744 100644 --- a/martin_binance/templates/cli_1_BTCUSDT.py +++ b/martin_binance/templates/cli_1_BTCUSDT.py @@ -7,7 +7,7 @@ __author__ = "Jerry Fedorenko" __copyright__ = "Copyright © 2021 Jerry Fedorenko aka VM" __license__ = "MIT" -__version__ = "3.0.3" +__version__ = "3.0.11" __maintainer__ = "Jerry Fedorenko" __contact__ = "https://github.com/DogsTailFarmer" """ @@ -41,7 +41,16 @@ ex.FEE_TAKER = Decimal('0.1') # standard exchange Fee for taker ex.FEE_FIRST = False # For example fee in BNB and BNB in pair, and it is base asset ex.FEE_SECOND = False # For example fee in BNB and BNB in pair, and it is quote asset -ex.GRID_MAX_COUNT = 3 # Maximum counts for placed grid orders +# Setting for auto deposit BNB on subaccount for fee payment. For Binance subaccount only. +# See also https://github.com/DogsTailFarmer/martin-binance/wiki/How-it's-work#keeping-level-of-first-asset +ex.FEE_BNB = { + 'id_exchange': 0, # Where collected assets and keeping BNB volume + 'symbol': 'BNB/USDT', # Specified on the source strategy (id_exchange above) + 'email': 'sub-account@email.com', # Email registered on this subaccount + 'target_amount': '0', # BNB in USD equivalent, no less than min_notional + 'tranche_volume': '0' # BNB in USD equivalent, no less than min_notional +} +ex.GRID_MAX_COUNT = 5 # Maximum counts for placed grid orders # Trade parameter ex.START_ON_BUY = True # First cycle direction ex.AMOUNT_FIRST = Decimal('0.05') # Deposit for Sale cycle in first currency @@ -137,7 +146,7 @@ def trade(strategy=None): loop.create_task(strategy.main(ex.SYMBOL)) loop.run_forever() except KeyboardInterrupt: - pass # user interrupt + pass # user interrupt finally: try: loop.run_until_complete(strategy.ask_exit()) diff --git a/martin_binance/templates/cli_2_TESTBTCTESTUSDT.py b/martin_binance/templates/cli_2_TESTBTCTESTUSDT.py index b7677af..afa3317 100644 --- a/martin_binance/templates/cli_2_TESTBTCTESTUSDT.py +++ b/martin_binance/templates/cli_2_TESTBTCTESTUSDT.py @@ -7,7 +7,7 @@ __author__ = "Jerry Fedorenko" __copyright__ = "Copyright © 2021 Jerry Fedorenko aka VM" __license__ = "MIT" -__version__ = "3.0.3" +__version__ = "3.0.11" __maintainer__ = "Jerry Fedorenko" __contact__ = "https://github.com/DogsTailFarmer" """ @@ -41,6 +41,15 @@ ex.FEE_TAKER = Decimal('0.17') # standard exchange Fee for taker ex.FEE_FIRST = False # For example fee in BNB and BNB in pair, and it is base asset ex.FEE_SECOND = False # For example fee in BNB and BNB in pair, and it is quote asset +# Setting for auto deposit BNB on subaccount for fee payment. For Binance subaccount only. +# See also https://github.com/DogsTailFarmer/martin-binance/wiki/How-it's-work#keeping-level-of-first-asset +ex.FEE_BNB = { + 'id_exchange': 0, # Where collected assets and keeping BNB volume + 'symbol': 'BNB/USDT', # Specified on the source strategy (id_exchange above) + 'email': 'sub-account@email.com', # Email registered on this subaccount + 'target_amount': '0', # BNB in USD equivalent, no less than min_notional + 'tranche_volume': '0' # BNB in USD equivalent, no less than min_notional +} ex.GRID_MAX_COUNT = 5 # Maximum counts for placed grid orders # Trade parameter ex.START_ON_BUY = True # First cycle direction diff --git a/martin_binance/templates/cli_3_BTCUSDT.py b/martin_binance/templates/cli_3_BTCUSDT.py index 2736347..a4b9c43 100755 --- a/martin_binance/templates/cli_3_BTCUSDT.py +++ b/martin_binance/templates/cli_3_BTCUSDT.py @@ -7,7 +7,7 @@ __author__ = "Jerry Fedorenko" __copyright__ = "Copyright © 2021 Jerry Fedorenko aka VM" __license__ = "MIT" -__version__ = "3.0.3" +__version__ = "3.0.11" __maintainer__ = "Jerry Fedorenko" __contact__ = "https://github.com/DogsTailFarmer" """ @@ -41,6 +41,15 @@ ex.FEE_TAKER = Decimal('0.15') # standard exchange Fee for taker ex.FEE_FIRST = False # For example fee in BNB and BNB in pair, and it is base asset ex.FEE_SECOND = False # For example fee in BNB and BNB in pair, and it is quote asset +# Setting for auto deposit BNB on subaccount for fee payment. For Binance subaccount only. +# See also https://github.com/DogsTailFarmer/martin-binance/wiki/How-it's-work#keeping-level-of-first-asset +ex.FEE_BNB = { + 'id_exchange': 0, # Where collected assets and keeping BNB volume + 'symbol': 'BNB/USDT', # Specified on the source strategy (id_exchange above) + 'email': 'sub-account@email.com', # Email registered on this subaccount + 'target_amount': '0', # BNB in USD equivalent, no less than min_notional + 'tranche_volume': '0' # BNB in USD equivalent, no less than min_notional +} ex.GRID_MAX_COUNT = 5 # Maximum counts for placed grid orders # Trade parameter ex.START_ON_BUY = True # First cycle direction diff --git a/pyproject.toml b/pyproject.toml index a5d1962..e7f2ed5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,7 @@ dynamic = ["version", "description"] requires-python = ">=3.9" dependencies = [ - "exchanges-wrapper==2.1.13", + "exchanges-wrapper==2.1.15", "jsonpickle==3.0.2", "psutil==5.9.6", "requests==2.32.0", diff --git a/requirements.txt b/requirements.txt index 54f467d..693b386 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -exchanges-wrapper==2.1.13 +exchanges-wrapper==2.1.15 jsonpickle==3.0.2 psutil==5.9.6 requests==2.32.0