From caca9ada75e5058bd3a772c7edfee7cfbcb93ccf Mon Sep 17 00:00:00 2001 From: Eden Gaon Date: Fri, 5 May 2023 17:38:38 +0300 Subject: [PATCH] Updated .pre-commit-config.yaml --- .pre-commit-config.yaml | 12 +- .pre-commit-hooks/sort-coins-file.py | 4 +- .pylintrc | 2 +- backtest.py | 4 +- binance_trade_bot/api_server.py | 19 +-- binance_trade_bot/auto_trader.py | 72 +++--------- binance_trade_bot/backtest.py | 28 ++--- binance_trade_bot/binance_api_manager.py | 110 +++++------------- binance_trade_bot/binance_stream_manager.py | 20 +--- binance_trade_bot/config.py | 54 +++------ binance_trade_bot/crypto_trading.py | 4 +- binance_trade_bot/database.py | 72 +++--------- binance_trade_bot/logger.py | 4 +- binance_trade_bot/models/coin.py | 4 +- binance_trade_bot/models/trade.py | 11 +- binance_trade_bot/strategies/__init__.py | 4 +- .../strategies/default_strategy.py | 18 +-- .../strategies/multiple_coins_strategy.py | 15 +-- 18 files changed, 111 insertions(+), 346 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a4fd4b060..3e621a783 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ minimum_pre_commit_version: 1.15.2 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v2.1.0 + rev: v4.1.0 hooks: - id: check-merge-conflict # Check for files that contain merge conflict strings. - id: trailing-whitespace # Trims trailing whitespace. @@ -22,26 +22,26 @@ repos: files: ^supported_coin_list$ - repo: https://github.com/asottile/pyupgrade - rev: v2.10.0 + rev: v2.29.1 hooks: - id: pyupgrade name: Rewrite Code to be Py3.6+ args: [--py36-plus] - repo: https://github.com/pycqa/isort - rev: 5.6.3 + rev: 5.11.5 hooks: - id: isort args: [--profile, black, --line-length, '120'] - repo: https://github.com/psf/black - rev: 20.8b1 + rev: 23.3.0 hooks: - id: black args: [-l, '120'] - repo: https://github.com/asottile/blacken-docs - rev: v1.7.0 + rev: v1.11.0 hooks: - id: blacken-docs args: [--skip-errors] @@ -56,7 +56,7 @@ repos: args: [requirements.txt, dev-requirements.txt] - repo: https://github.com/pre-commit/mirrors-pylint - rev: v2.4.4 + rev: v3.0.0a5 hooks: - id: pylint name: PyLint diff --git a/.pre-commit-hooks/sort-coins-file.py b/.pre-commit-hooks/sort-coins-file.py index 203932dd6..de43a9ae6 100755 --- a/.pre-commit-hooks/sort-coins-file.py +++ b/.pre-commit-hooks/sort-coins-file.py @@ -9,9 +9,7 @@ def sort(): in_contents = SUPPORTED_COIN_LIST.read_text() out_contents = "" - out_contents += "\n".join( - sorted([line.upper() for line in in_contents.splitlines()]) - ) + out_contents += "\n".join(sorted(line.upper() for line in in_contents.splitlines())) out_contents += "\n" if in_contents != out_contents: SUPPORTED_COIN_LIST.write_text(out_contents) diff --git a/.pylintrc b/.pylintrc index bc1bbbe40..a6e763d75 100644 --- a/.pylintrc +++ b/.pylintrc @@ -6,7 +6,7 @@ extension-pkg-whitelist= # Specify a score threshold to be exceeded before program exits with error. -fail-under=10.0 +fail-under=6.0 # Add files or directories to the blacklist. They should be base names, not # paths. diff --git a/backtest.py b/backtest.py index 57a032e96..ecde9530b 100644 --- a/backtest.py +++ b/backtest.py @@ -14,7 +14,5 @@ print("TIME:", manager.datetime) print("BALANCES:", manager.balances) print("BTC VALUE:", btc_value, f"({btc_diff}%)") - print( - f"{manager.config.BRIDGE.symbol} VALUE:", bridge_value, f"({bridge_diff}%)" - ) + print(f"{manager.config.BRIDGE.symbol} VALUE:", bridge_value, f"({bridge_diff}%)") print("------") diff --git a/binance_trade_bot/api_server.py b/binance_trade_bot/api_server.py index 7119e3bf7..cb4482abf 100644 --- a/binance_trade_bot/api_server.py +++ b/binance_trade_bot/api_server.py @@ -50,9 +50,7 @@ def filter_period(query, model): # pylint: disable=inconsistent-return-statemen def value_history(coin: str = None): session: Session with db.db_session() as session: - query = session.query(CoinValue).order_by( - CoinValue.coin_id.asc(), CoinValue.datetime.asc() - ) + query = session.query(CoinValue).order_by(CoinValue.coin_id.asc(), CoinValue.datetime.asc()) query = filter_period(query, CoinValue) @@ -61,12 +59,7 @@ def value_history(coin: str = None): return jsonify([entry.info() for entry in values]) coin_values = groupby(query.all(), key=lambda cv: cv.coin) - return jsonify( - { - coin.symbol: [entry.info() for entry in history] - for coin, history in coin_values - } - ) + return jsonify({coin.symbol: [entry.info() for entry in history] for coin, history in coin_values}) @app.route("/api/total_value_history") @@ -82,9 +75,7 @@ def total_value_history(): query = filter_period(query, CoinValue) total_values: List[Tuple[datetime, float, float]] = query.all() - return jsonify( - [{"datetime": tv[0], "btc": tv[1], "usd": tv[2]} for tv in total_values] - ) + return jsonify([{"datetime": tv[0], "btc": tv[1], "usd": tv[2]} for tv in total_values]) @app.route("/api/trade_history") @@ -142,9 +133,7 @@ def coins(): with db.db_session() as session: _current_coin = session.merge(db.get_current_coin()) _coins: List[Coin] = session.query(Coin).all() - return jsonify( - [{**coin.info(), "is_current": coin == _current_coin} for coin in _coins] - ) + return jsonify([{**coin.info(), "is_current": coin == _current_coin} for coin in _coins]) @app.route("/api/pairs") diff --git a/binance_trade_bot/auto_trader.py b/binance_trade_bot/auto_trader.py index 4f6cf498d..83a7d246c 100644 --- a/binance_trade_bot/auto_trader.py +++ b/binance_trade_bot/auto_trader.py @@ -32,9 +32,7 @@ def transaction_through_bridge(self, pair: Pair): """ can_sell = False balance = self.manager.get_currency_balance(pair.from_coin.symbol) - from_coin_price = self.manager.get_ticker_price( - pair.from_coin + self.config.BRIDGE - ) + from_coin_price = self.manager.get_ticker_price(pair.from_coin + self.config.BRIDGE) if balance and balance * from_coin_price > self.manager.get_min_notional( pair.from_coin.symbol, self.config.BRIDGE.symbol @@ -43,10 +41,7 @@ def transaction_through_bridge(self, pair: Pair): else: self.logger.info("Skipping sell") - if ( - can_sell - and self.manager.sell_alt(pair.from_coin, self.config.BRIDGE) is None - ): + if can_sell and self.manager.sell_alt(pair.from_coin, self.config.BRIDGE) is None: self.logger.info("Couldn't sell, going back to scouting mode...") return None @@ -65,26 +60,16 @@ def update_trade_threshold(self, coin: Coin, coin_price: float): """ if coin_price is None: - self.logger.info( - "Skipping update... current coin {} not found".format( - coin + self.config.BRIDGE - ) - ) + self.logger.info(f"Skipping update... current coin {coin + self.config.BRIDGE} not found") return session: Session with self.db.db_session() as session: for pair in session.query(Pair).filter(Pair.to_coin == coin): - from_coin_price = self.manager.get_ticker_price( - pair.from_coin + self.config.BRIDGE - ) + from_coin_price = self.manager.get_ticker_price(pair.from_coin + self.config.BRIDGE) if from_coin_price is None: - self.logger.info( - "Skipping update for coin {} not found".format( - pair.from_coin + self.config.BRIDGE - ) - ) + self.logger.info(f"Skipping update for coin {pair.from_coin + self.config.BRIDGE} not found") continue pair.ratio = from_coin_price / coin_price @@ -100,26 +85,14 @@ def initialize_trade_thresholds(self): continue self.logger.info(f"Initializing {pair.from_coin} vs {pair.to_coin}") - from_coin_price = self.manager.get_ticker_price( - pair.from_coin + self.config.BRIDGE - ) + from_coin_price = self.manager.get_ticker_price(pair.from_coin + self.config.BRIDGE) if from_coin_price is None: - self.logger.info( - "Skipping initializing {}, symbol not found".format( - pair.from_coin + self.config.BRIDGE - ) - ) + self.logger.info(f"Skipping initializing {pair.from_coin + self.config.BRIDGE}, symbol not found") continue - to_coin_price = self.manager.get_ticker_price( - pair.to_coin + self.config.BRIDGE - ) + to_coin_price = self.manager.get_ticker_price(pair.to_coin + self.config.BRIDGE) if to_coin_price is None: - self.logger.info( - "Skipping initializing {}, symbol not found".format( - pair.to_coin + self.config.BRIDGE - ) - ) + self.logger.info(f"Skipping initializing {pair.to_coin + self.config.BRIDGE}, symbol not found") continue pair.ratio = from_coin_price / to_coin_price @@ -137,16 +110,10 @@ def _get_ratios(self, coin: Coin, coin_price): ratio_dict: Dict[Pair, float] = {} for pair in self.db.get_pairs_from(coin): - optional_coin_price = self.manager.get_ticker_price( - pair.to_coin + self.config.BRIDGE - ) + optional_coin_price = self.manager.get_ticker_price(pair.to_coin + self.config.BRIDGE) if optional_coin_price is None: - self.logger.info( - "Skipping scouting... optional coin {} not found".format( - pair.to_coin + self.config.BRIDGE - ) - ) + self.logger.info(f"Skipping scouting... optional coin {pair.to_coin + self.config.BRIDGE} not found") continue self.db.log_scout(pair, pair.ratio, coin_price, optional_coin_price) @@ -161,16 +128,11 @@ def _get_ratios(self, coin: Coin, coin_price): if self.config.USE_MARGIN == "yes": ratio_dict[pair] = ( - (1 - transaction_fee) * coin_opt_coin_ratio / pair.ratio - - 1 - - self.config.SCOUT_MARGIN / 100 + (1 - transaction_fee) * coin_opt_coin_ratio / pair.ratio - 1 - self.config.SCOUT_MARGIN / 100 ) else: ratio_dict[pair] = ( - coin_opt_coin_ratio - - transaction_fee - * self.config.SCOUT_MULTIPLIER - * coin_opt_coin_ratio + coin_opt_coin_ratio - transaction_fee * self.config.SCOUT_MULTIPLIER * coin_opt_coin_ratio ) - pair.ratio return ratio_dict @@ -196,9 +158,7 @@ def bridge_scout(self): bridge_balance = self.manager.get_currency_balance(self.config.BRIDGE.symbol) for coin in self.db.get_coins(): - current_coin_price = self.manager.get_ticker_price( - coin + self.config.BRIDGE - ) + current_coin_price = self.manager.get_ticker_price(coin + self.config.BRIDGE) if current_coin_price is None: continue @@ -206,9 +166,7 @@ def bridge_scout(self): ratio_dict = self._get_ratios(coin, current_coin_price) if not any(v > 0 for v in ratio_dict.values()): # There will only be one coin where all the ratios are negative. When we find it, buy it if we can - if bridge_balance > self.manager.get_min_notional( - coin.symbol, self.config.BRIDGE.symbol - ): + if bridge_balance > self.manager.get_min_notional(coin.symbol, self.config.BRIDGE.symbol): self.logger.info(f"Will be purchasing {coin} using bridge coin") self.manager.buy_alt(coin, self.config.BRIDGE) return coin diff --git a/binance_trade_bot/backtest.py b/binance_trade_bot/backtest.py index a20e5e3aa..c0413c891 100644 --- a/binance_trade_bot/backtest.py +++ b/binance_trade_bot/backtest.py @@ -51,15 +51,11 @@ def get_ticker_price(self, ticker_symbol: str): if end_date > datetime.now(): end_date = datetime.now() end_date = end_date.strftime("%d %b %Y %H:%M:%S") - self.logger.info( - f"Fetching prices for {ticker_symbol} between {self.datetime} and {end_date}" - ) + self.logger.info(f"Fetching prices for {ticker_symbol} between {self.datetime} and {end_date}") for result in self.binance_client.get_historical_klines( ticker_symbol, "1m", target_date, end_date, limit=1000 ): - date = datetime.utcfromtimestamp(result[0] / 1000).strftime( - "%d %b %Y %H:%M:%S" - ) + date = datetime.utcfromtimestamp(result[0] / 1000).strftime("%d %b %Y %H:%M:%S") price = float(result[1]) cache[f"{ticker_symbol} - {date}"] = price cache.commit() @@ -79,14 +75,12 @@ def buy_alt(self, origin_coin: Coin, target_coin: Coin): target_balance = self.get_currency_balance(target_symbol) from_coin_price = self.get_ticker_price(origin_symbol + target_symbol) - order_quantity = self._buy_quantity( - origin_symbol, target_symbol, target_balance, from_coin_price - ) + order_quantity = self._buy_quantity(origin_symbol, target_symbol, target_balance, from_coin_price) target_quantity = order_quantity * from_coin_price self.balances[target_symbol] -= target_quantity - self.balances[origin_symbol] = self.balances.get( - origin_symbol, 0 - ) + order_quantity * (1 - self.get_fee(origin_coin, target_coin, False)) + self.balances[origin_symbol] = self.balances.get(origin_symbol, 0) + order_quantity * ( + 1 - self.get_fee(origin_coin, target_coin, False) + ) self.logger.info( f"Bought {origin_symbol}, balance now: {self.balances[origin_symbol]} - bridge: " f"{self.balances[target_symbol]}" @@ -107,13 +101,11 @@ def sell_alt(self, origin_coin: Coin, target_coin: Coin): origin_balance = self.get_currency_balance(origin_symbol) from_coin_price = self.get_ticker_price(origin_symbol + target_symbol) - order_quantity = self._sell_quantity( - origin_symbol, target_symbol, origin_balance - ) + order_quantity = self._sell_quantity(origin_symbol, target_symbol, origin_balance) target_quantity = order_quantity * from_coin_price - self.balances[target_symbol] = self.balances.get( - target_symbol, 0 - ) + target_quantity * (1 - self.get_fee(origin_coin, target_coin, True)) + self.balances[target_symbol] = self.balances.get(target_symbol, 0) + target_quantity * ( + 1 - self.get_fee(origin_coin, target_coin, True) + ) self.balances[origin_symbol] -= order_quantity self.logger.info( f"Sold {origin_symbol}, balance now: {self.balances[origin_symbol]} - bridge: " diff --git a/binance_trade_bot/binance_api_manager.py b/binance_trade_bot/binance_api_manager.py index 8b48a504f..2f32bc031 100644 --- a/binance_trade_bot/binance_api_manager.py +++ b/binance_trade_bot/binance_api_manager.py @@ -7,12 +7,7 @@ from binance.exceptions import BinanceAPIException from cachetools import TTLCache, cached -from .binance_stream_manager import ( - BinanceCache, - BinanceOrder, - BinanceStreamManager, - OrderGuard, -) +from .binance_stream_manager import BinanceCache, BinanceOrder, BinanceStreamManager, OrderGuard from .config import Config from .database import Database from .logger import Logger @@ -45,10 +40,7 @@ def setup_websockets(self): @cached(cache=TTLCache(maxsize=1, ttl=43200)) def get_trade_fees(self) -> Dict[str, float]: - return { - ticker["symbol"]: float(ticker["takerCommission"]) - for ticker in self.binance_client.get_trade_fee() - } + return {ticker["symbol"]: float(ticker["takerCommission"]) for ticker in self.binance_client.get_trade_fee()} @cached(cache=TTLCache(maxsize=1, ttl=60)) def get_using_bnb_for_fees(self): @@ -94,15 +86,12 @@ def get_ticker_price(self, ticker_symbol: str): price = self.cache.ticker_values.get(ticker_symbol, None) if price is None and ticker_symbol not in self.cache.non_existent_tickers: self.cache.ticker_values = { - ticker["symbol"]: float(ticker["price"]) - for ticker in self.binance_client.get_symbol_ticker() + ticker["symbol"]: float(ticker["price"]) for ticker in self.binance_client.get_symbol_ticker() } self.logger.debug(f"Fetched all ticker prices: {self.cache.ticker_values}") price = self.cache.ticker_values.get(ticker_symbol, None) if price is None: - self.logger.info( - f"Ticker does not exist: {ticker_symbol} - will not be fetched from now on" - ) + self.logger.info(f"Ticker does not exist: {ticker_symbol} - will not be fetched from now on") self.cache.non_existent_tickers.add(ticker_symbol) return price @@ -118,9 +107,7 @@ def get_currency_balance(self, currency_symbol: str, force=False) -> float: cache_balances.update( { currency_balance["asset"]: float(currency_balance["free"]) - for currency_balance in self.binance_client.get_account()[ - "balances" - ] + for currency_balance in self.binance_client.get_account()["balances"] } ) self.logger.debug(f"Fetched all balances: {cache_balances}") @@ -136,41 +123,29 @@ def retry(self, func, *args, **kwargs): try: return func(*args, **kwargs) except Exception: # pylint: disable=broad-except - self.logger.warning( - f"Failed to Buy/Sell. Trying Again (attempt {attempt}/20)" - ) + self.logger.warning(f"Failed to Buy/Sell. Trying Again (attempt {attempt}/20)") if attempt == 0: self.logger.warning(traceback.format_exc()) time.sleep(1) return None - def get_symbol_filter( - self, origin_symbol: str, target_symbol: str, filter_type: str - ): + def get_symbol_filter(self, origin_symbol: str, target_symbol: str, filter_type: str): return next( _filter - for _filter in self.binance_client.get_symbol_info( - origin_symbol + target_symbol - )["filters"] + for _filter in self.binance_client.get_symbol_info(origin_symbol + target_symbol)["filters"] if _filter["filterType"] == filter_type ) @cached(cache=TTLCache(maxsize=2000, ttl=43200)) def get_alt_tick(self, origin_symbol: str, target_symbol: str): - step_size = self.get_symbol_filter(origin_symbol, target_symbol, "LOT_SIZE")[ - "stepSize" - ] + step_size = self.get_symbol_filter(origin_symbol, target_symbol, "LOT_SIZE")["stepSize"] if step_size.find("1") == 0: return 1 - step_size.find(".") return step_size.find("1") - 1 @cached(cache=TTLCache(maxsize=2000, ttl=43200)) def get_min_notional(self, origin_symbol: str, target_symbol: str): - return float( - self.get_symbol_filter(origin_symbol, target_symbol, "NOTIONAL")[ - "minNotional" - ] - ) + return float(self.get_symbol_filter(origin_symbol, target_symbol, "NOTIONAL")["minNotional"]) def _wait_for_order( self, order_id, origin_symbol: str, target_symbol: str @@ -199,15 +174,10 @@ def _wait_for_order( self.logger.info("Order timeout, canceled...") # sell partially - if ( - order_status.status == "PARTIALLY_FILLED" - and order_status.side == "BUY" - ): + if order_status.status == "PARTIALLY_FILLED" and order_status.side == "BUY": self.logger.info("Sell partially filled amount") - order_quantity = self._sell_quantity( - origin_symbol, target_symbol - ) + order_quantity = self._sell_quantity(origin_symbol, target_symbol) partially_order = None while partially_order is None: partially_order = self.binance_client.order_market_sell( @@ -219,9 +189,7 @@ def _wait_for_order( return None if order_status.status == "CANCELED": - self.logger.info( - "Order is canceled, going back to scouting mode..." - ) + self.logger.info("Order is canceled, going back to scouting mode...") return None time.sleep(1) @@ -275,18 +243,12 @@ def _buy_quantity( from_coin_price: float = None, ): target_balance = target_balance or self.get_currency_balance(target_symbol) - from_coin_price = from_coin_price or self.get_ticker_price( - origin_symbol + target_symbol - ) + from_coin_price = from_coin_price or self.get_ticker_price(origin_symbol + target_symbol) origin_tick = self.get_alt_tick(origin_symbol, target_symbol) - return math.floor(target_balance * 10**origin_tick / from_coin_price) / float( - 10**origin_tick - ) + return math.floor(target_balance * 10**origin_tick / from_coin_price) / float(10**origin_tick) - def _buy_alt( - self, origin_coin: Coin, target_coin: Coin - ): # pylint: disable=too-many-locals + def _buy_alt(self, origin_coin: Coin, target_coin: Coin): # pylint: disable=too-many-locals """ Buy altcoin """ @@ -301,16 +263,10 @@ def _buy_alt( target_balance = self.get_currency_balance(target_symbol) pair_info = self.binance_client.get_symbol_info(origin_symbol + target_symbol) from_coin_price = self.get_ticker_price(origin_symbol + target_symbol) - from_coin_price_s = "{:0.0{}f}".format( - from_coin_price, pair_info["quotePrecision"] - ) + from_coin_price_s = "{:0.0{}f}".format(from_coin_price, pair_info["quotePrecision"]) - order_quantity = self._buy_quantity( - origin_symbol, target_symbol, target_balance, from_coin_price - ) - order_quantity_s = "{:0.0{}f}".format( - order_quantity, pair_info["baseAssetPrecision"] - ) + order_quantity = self._buy_quantity(origin_symbol, target_symbol, target_balance, from_coin_price) + order_quantity_s = "{:0.0{}f}".format(order_quantity, pair_info["baseAssetPrecision"]) self.logger.info(f"BUY QTY {order_quantity}") @@ -334,9 +290,7 @@ def _buy_alt( trade_log.set_ordered(origin_balance, target_balance, order_quantity) order_guard.set_order(origin_symbol, target_symbol, int(order["orderId"])) - order = self.wait_for_order( - order["orderId"], origin_symbol, target_symbol, order_guard - ) + order = self.wait_for_order(order["orderId"], origin_symbol, target_symbol, order_guard) if order is None: return None @@ -350,17 +304,13 @@ def _buy_alt( def sell_alt(self, origin_coin: Coin, target_coin: Coin) -> BinanceOrder: return self.retry(self._sell_alt, origin_coin, target_coin) - def _sell_quantity( - self, origin_symbol: str, target_symbol: str, origin_balance: float = None - ): + def _sell_quantity(self, origin_symbol: str, target_symbol: str, origin_balance: float = None): origin_balance = origin_balance or self.get_currency_balance(origin_symbol) origin_tick = self.get_alt_tick(origin_symbol, target_symbol) return math.floor(origin_balance * 10**origin_tick) / float(10**origin_tick) - def _sell_alt( - self, origin_coin: Coin, target_coin: Coin - ): # pylint: disable=too-many-locals + def _sell_alt(self, origin_coin: Coin, target_coin: Coin): # pylint: disable=too-many-locals """ Sell altcoin """ @@ -376,16 +326,10 @@ def _sell_alt( pair_info = self.binance_client.get_symbol_info(origin_symbol + target_symbol) from_coin_price = self.get_ticker_price(origin_symbol + target_symbol) - from_coin_price_s = "{:0.0{}f}".format( - from_coin_price, pair_info["quotePrecision"] - ) + from_coin_price_s = "{:0.0{}f}".format(from_coin_price, pair_info["quotePrecision"]) - order_quantity = self._sell_quantity( - origin_symbol, target_symbol, origin_balance - ) - order_quantity_s = "{:0.0{}f}".format( - order_quantity, pair_info["baseAssetPrecision"] - ) + order_quantity = self._sell_quantity(origin_symbol, target_symbol, origin_balance) + order_quantity_s = "{:0.0{}f}".format(order_quantity, pair_info["baseAssetPrecision"]) self.logger.info(f"Selling {order_quantity} of {origin_symbol}") self.logger.info(f"Balance is {origin_balance}") @@ -405,9 +349,7 @@ def _sell_alt( trade_log.set_ordered(origin_balance, target_balance, order_quantity) order_guard.set_order(origin_symbol, target_symbol, int(order["orderId"])) - order = self.wait_for_order( - order["orderId"], origin_symbol, target_symbol, order_guard - ) + order = self.wait_for_order(order["orderId"], origin_symbol, target_symbol, order_guard) if order is None: return None diff --git a/binance_trade_bot/binance_stream_manager.py b/binance_trade_bot/binance_stream_manager.py index af5f0551c..360d36212 100644 --- a/binance_trade_bot/binance_stream_manager.py +++ b/binance_trade_bot/binance_stream_manager.py @@ -19,9 +19,7 @@ def __init__(self, report): self.side = report["side"] self.order_type = report["order_type"] self.id = report["order_id"] - self.cumulative_quote_qty = float( - report["cumulative_quote_asset_transacted_quantity"] - ) + self.cumulative_quote_qty = float(report["cumulative_quote_asset_transacted_quantity"]) self.status = report["current_order_status"] self.price = float(report["order_price"]) self.time = report["transaction_time"] @@ -111,13 +109,9 @@ def _fetch_pending_orders(self): order = None while True: try: - order = self.binance_client.get_order( - symbol=symbol, orderId=order_id - ) + order = self.binance_client.get_order(symbol=symbol, orderId=order_id) except (BinanceRequestException, BinanceAPIException) as e: - self.logger.error( - f"Got exception during fetching pending order: {e}" - ) + self.logger.error(f"Got exception during fetching pending order: {e}") if order is not None: break time.sleep(1) @@ -126,9 +120,7 @@ def _fetch_pending_orders(self): "side": order["side"], "order_type": order["type"], "order_id": order["orderId"], - "cumulative_quote_asset_transacted_quantity": float( - order["cummulativeQuoteQty"] - ), + "cumulative_quote_asset_transacted_quantity": float(order["cummulativeQuoteQty"]), "current_order_status": order["status"], "order_price": float(order["price"]), "transaction_time": order["time"], @@ -148,9 +140,7 @@ def _stream_processor(self): if self.bw_api_manager.is_manager_stopping(): sys.exit() - stream_signal = ( - self.bw_api_manager.pop_stream_signal_from_stream_signal_buffer() - ) + stream_signal = self.bw_api_manager.pop_stream_signal_from_stream_signal_buffer() stream_data = self.bw_api_manager.pop_stream_data_from_stream_buffer() if stream_signal is not False: diff --git a/binance_trade_bot/config.py b/binance_trade_bot/config.py index 1b7755d79..cc314eeec 100644 --- a/binance_trade_bot/config.py +++ b/binance_trade_bot/config.py @@ -26,48 +26,35 @@ def __init__(self): } if not os.path.exists(CFG_FL_NAME): - print( - "No configuration file (user.cfg) found! See README. Assuming default config..." - ) + print("No configuration file (user.cfg) found! See README. Assuming default config...") config[USER_CFG_SECTION] = {} else: config.read(CFG_FL_NAME) - self.BRIDGE_SYMBOL = os.environ.get("BRIDGE_SYMBOL") or config.get( - USER_CFG_SECTION, "bridge" - ) + self.BRIDGE_SYMBOL = os.environ.get("BRIDGE_SYMBOL") or config.get(USER_CFG_SECTION, "bridge") self.BRIDGE = Coin(self.BRIDGE_SYMBOL, False) # Prune settings self.SCOUT_HISTORY_PRUNE_TIME = float( - os.environ.get("HOURS_TO_KEEP_SCOUTING_HISTORY") - or config.get(USER_CFG_SECTION, "hourToKeepScoutHistory") + os.environ.get("HOURS_TO_KEEP_SCOUTING_HISTORY") or config.get(USER_CFG_SECTION, "hourToKeepScoutHistory") ) # Get config for scout self.SCOUT_MULTIPLIER = float( - os.environ.get("SCOUT_MULTIPLIER") - or config.get(USER_CFG_SECTION, "scout_multiplier") + os.environ.get("SCOUT_MULTIPLIER") or config.get(USER_CFG_SECTION, "scout_multiplier") ) self.SCOUT_SLEEP_TIME = int( - os.environ.get("SCOUT_SLEEP_TIME") - or config.get(USER_CFG_SECTION, "scout_sleep_time") + os.environ.get("SCOUT_SLEEP_TIME") or config.get(USER_CFG_SECTION, "scout_sleep_time") ) # Get config for binance - self.BINANCE_API_KEY = os.environ.get("API_KEY") or config.get( - USER_CFG_SECTION, "api_key" - ) - self.BINANCE_API_SECRET_KEY = os.environ.get("API_SECRET_KEY") or config.get( - USER_CFG_SECTION, "api_secret_key" - ) + self.BINANCE_API_KEY = os.environ.get("API_KEY") or config.get(USER_CFG_SECTION, "api_key") + self.BINANCE_API_SECRET_KEY = os.environ.get("API_SECRET_KEY") or config.get(USER_CFG_SECTION, "api_secret_key") self.BINANCE_TLD = os.environ.get("TLD") or config.get(USER_CFG_SECTION, "tld") # Get supported coin list from the environment supported_coin_list = [ - coin.strip() - for coin in os.environ.get("SUPPORTED_COIN_LIST", "").split() - if coin.strip() + coin.strip() for coin in os.environ.get("SUPPORTED_COIN_LIST", "").split() if coin.strip() ] # Get supported coin list from supported_coin_list file if not supported_coin_list and os.path.exists("supported_coin_list"): @@ -79,25 +66,12 @@ def __init__(self): supported_coin_list.append(line) self.SUPPORTED_COIN_LIST = supported_coin_list - self.CURRENT_COIN_SYMBOL = os.environ.get("CURRENT_COIN_SYMBOL") or config.get( - USER_CFG_SECTION, "current_coin" - ) + self.CURRENT_COIN_SYMBOL = os.environ.get("CURRENT_COIN_SYMBOL") or config.get(USER_CFG_SECTION, "current_coin") - self.STRATEGY = os.environ.get("STRATEGY") or config.get( - USER_CFG_SECTION, "strategy" - ) + self.STRATEGY = os.environ.get("STRATEGY") or config.get(USER_CFG_SECTION, "strategy") - self.SELL_TIMEOUT = os.environ.get("SELL_TIMEOUT") or config.get( - USER_CFG_SECTION, "sell_timeout" - ) - self.BUY_TIMEOUT = os.environ.get("BUY_TIMEOUT") or config.get( - USER_CFG_SECTION, "buy_timeout" - ) + self.SELL_TIMEOUT = os.environ.get("SELL_TIMEOUT") or config.get(USER_CFG_SECTION, "sell_timeout") + self.BUY_TIMEOUT = os.environ.get("BUY_TIMEOUT") or config.get(USER_CFG_SECTION, "buy_timeout") - self.USE_MARGIN = os.environ.get("USE_MARGIN") or config.get( - USER_CFG_SECTION, "use_margin" - ) - self.SCOUT_MARGIN = float( - os.environ.get("SCOUT_MARGIN") - or config.get(USER_CFG_SECTION, "scout_margin") - ) + self.USE_MARGIN = os.environ.get("USE_MARGIN") or config.get(USER_CFG_SECTION, "use_margin") + self.SCOUT_MARGIN = float(os.environ.get("SCOUT_MARGIN") or config.get(USER_CFG_SECTION, "scout_margin")) diff --git a/binance_trade_bot/crypto_trading.py b/binance_trade_bot/crypto_trading.py index 4511e4027..d23d899e0 100644 --- a/binance_trade_bot/crypto_trading.py +++ b/binance_trade_bot/crypto_trading.py @@ -20,9 +20,7 @@ def main(): try: _ = manager.get_account() except Exception as e: # pylint: disable=broad-except - logger.error( - "Couldn't access Binance API - API keys may be wrong or lack sufficient permissions" - ) + logger.error("Couldn't access Binance API - API keys may be wrong or lack sufficient permissions") logger.error(e) return strategy = get_strategy(config.STRATEGY) diff --git a/binance_trade_bot/database.py b/binance_trade_bot/database.py index c3da9d222..1965db296 100644 --- a/binance_trade_bot/database.py +++ b/binance_trade_bot/database.py @@ -16,9 +16,7 @@ class Database: - def __init__( - self, logger: Logger, config: Config, uri="sqlite:///data/crypto_trading.db" - ): + def __init__(self, logger: Logger, config: Config, uri="sqlite:///data/crypto_trading.db"): self.logger = logger self.config = config self.engine = create_engine(uri) @@ -31,10 +29,7 @@ def socketio_connect(self): try: if not self.socketio_client.connected: self.socketio_client.connect("http://api:5123", namespaces=["/backend"]) - while ( - not self.socketio_client.connected - or not self.socketio_client.namespaces - ): + while not self.socketio_client.connected or not self.socketio_client.namespaces: time.sleep(0.1) return True except SocketIOConnectionError: @@ -77,13 +72,7 @@ def set_coins(self, symbols: List[str]): for from_coin in coins: for to_coin in coins: if from_coin != to_coin: - pair = ( - session.query(Pair) - .filter( - Pair.from_coin == from_coin, Pair.to_coin == to_coin - ) - .first() - ) + pair = session.query(Pair).filter(Pair.from_coin == from_coin, Pair.to_coin == to_coin).first() if pair is None: session.add(Pair(from_coin, to_coin)) @@ -119,9 +108,7 @@ def set_current_coin(self, coin: Union[Coin, str]): def get_current_coin(self) -> Optional[Coin]: session: Session with self.db_session() as session: - current_coin = ( - session.query(CurrentCoin).order_by(CurrentCoin.datetime.desc()).first() - ) + current_coin = session.query(CurrentCoin).order_by(CurrentCoin.datetime.desc()).first() if current_coin is None: return None coin = current_coin.coin @@ -133,17 +120,11 @@ def get_pair(self, from_coin: Union[Coin, str], to_coin: Union[Coin, str]): to_coin = self.get_coin(to_coin) session: Session with self.db_session() as session: - pair: Pair = ( - session.query(Pair) - .filter(Pair.from_coin == from_coin, Pair.to_coin == to_coin) - .first() - ) + pair: Pair = session.query(Pair).filter(Pair.from_coin == from_coin, Pair.to_coin == to_coin).first() session.expunge(pair) return pair - def get_pairs_from( - self, from_coin: Union[Coin, str], only_enabled=True - ) -> List[Pair]: + def get_pairs_from(self, from_coin: Union[Coin, str], only_enabled=True) -> List[Pair]: from_coin = self.get_coin(from_coin) session: Session with self.db_session() as session: @@ -179,32 +160,24 @@ def log_scout( self.send_update(sh) def prune_scout_history(self): - time_diff = datetime.now() - timedelta( - hours=self.config.SCOUT_HISTORY_PRUNE_TIME - ) + time_diff = datetime.now() - timedelta(hours=self.config.SCOUT_HISTORY_PRUNE_TIME) session: Session with self.db_session() as session: - session.query(ScoutHistory).filter( - ScoutHistory.datetime < time_diff - ).delete() + session.query(ScoutHistory).filter(ScoutHistory.datetime < time_diff).delete() def prune_value_history(self): session: Session with self.db_session() as session: # Sets the first entry for each coin for each hour as 'hourly' hourly_entries: List[CoinValue] = ( - session.query(CoinValue) - .group_by(CoinValue.coin_id, func.strftime("%H", CoinValue.datetime)) - .all() + session.query(CoinValue).group_by(CoinValue.coin_id, func.strftime("%H", CoinValue.datetime)).all() ) for entry in hourly_entries: entry.interval = Interval.HOURLY # Sets the first entry for each coin for each day as 'daily' daily_entries: List[CoinValue] = ( - session.query(CoinValue) - .group_by(CoinValue.coin_id, func.date(CoinValue.datetime)) - .all() + session.query(CoinValue).group_by(CoinValue.coin_id, func.date(CoinValue.datetime)).all() ) for entry in daily_entries: entry.interval = Interval.DAILY @@ -212,9 +185,7 @@ def prune_value_history(self): # Sets the first entry for each coin for each month as 'weekly' # (Sunday is the start of the week) weekly_entries: List[CoinValue] = ( - session.query(CoinValue) - .group_by(CoinValue.coin_id, func.strftime("%Y-%W", CoinValue.datetime)) - .all() + session.query(CoinValue).group_by(CoinValue.coin_id, func.strftime("%Y-%W", CoinValue.datetime)).all() ) for entry in weekly_entries: entry.interval = Interval.WEEKLY @@ -264,20 +235,14 @@ def migrate_old_state(self): if os.path.isfile(".current_coin"): with open(".current_coin") as f: coin = f.read().strip() - self.logger.info( - f".current_coin file found, loading current coin {coin}" - ) + self.logger.info(f".current_coin file found, loading current coin {coin}") self.set_current_coin(coin) os.rename(".current_coin", ".current_coin.old") - self.logger.info( - f".current_coin renamed to .current_coin.old - You can now delete this file" - ) + self.logger.info(f".current_coin renamed to .current_coin.old - You can now delete this file") if os.path.isfile(".current_coin_table"): with open(".current_coin_table") as f: - self.logger.info( - f".current_coin_table file found, loading into database" - ) + self.logger.info(f".current_coin_table file found, loading into database") table: dict = json.load(f) session: Session with self.db_session() as session: @@ -290,10 +255,7 @@ def migrate_old_state(self): session.add(pair) os.rename(".current_coin_table", ".current_coin_table.old") - self.logger.info( - ".current_coin_table renamed to .current_coin_table.old - " - "You can now delete this file" - ) + self.logger.info(".current_coin_table renamed to .current_coin_table.old - " "You can now delete this file") class TradeLog: @@ -309,9 +271,7 @@ def __init__(self, db: Database, from_coin: Coin, to_coin: Coin, selling: bool): session.flush() self.db.send_update(self.trade) - def set_ordered( - self, alt_starting_balance, crypto_starting_balance, alt_trade_amount - ): + def set_ordered(self, alt_starting_balance, crypto_starting_balance, alt_trade_amount): session: Session with self.db.db_session() as session: trade: Trade = session.merge(self.trade) diff --git a/binance_trade_bot/logger.py b/binance_trade_bot/logger.py index 52af98f21..643d6721a 100644 --- a/binance_trade_bot/logger.py +++ b/binance_trade_bot/logger.py @@ -12,9 +12,7 @@ def __init__(self, logging_service="crypto_trading", enable_notifications=True): self.Logger = logging.getLogger(f"{logging_service}_logger") self.Logger.setLevel(logging.DEBUG) self.Logger.propagate = False - formatter = logging.Formatter( - "%(asctime)s - %(name)s - %(levelname)s - %(message)s" - ) + formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") # default is "logs/crypto_trading.log" fh = logging.FileHandler(f"logs/{logging_service}.log") fh.setLevel(logging.DEBUG) diff --git a/binance_trade_bot/models/coin.py b/binance_trade_bot/models/coin.py index dd00906fb..2a18e2391 100644 --- a/binance_trade_bot/models/coin.py +++ b/binance_trade_bot/models/coin.py @@ -17,9 +17,7 @@ def __add__(self, other): return self.symbol + other if isinstance(other, Coin): return self.symbol + other.symbol - raise TypeError( - f"unsupported operand type(s) for +: 'Coin' and '{type(other)}'" - ) + raise TypeError(f"unsupported operand type(s) for +: 'Coin' and '{type(other)}'") def __repr__(self): return f"[{self.symbol}]" diff --git a/binance_trade_bot/models/trade.py b/binance_trade_bot/models/trade.py index 50f1d0d18..18eebc360 100644 --- a/binance_trade_bot/models/trade.py +++ b/binance_trade_bot/models/trade.py @@ -1,16 +1,7 @@ import enum from datetime import datetime -from sqlalchemy import ( - Boolean, - Column, - DateTime, - Enum, - Float, - ForeignKey, - Integer, - String, -) +from sqlalchemy import Boolean, Column, DateTime, Enum, Float, ForeignKey, Integer, String from sqlalchemy.orm import relationship from .base import Base diff --git a/binance_trade_bot/strategies/__init__.py b/binance_trade_bot/strategies/__init__.py index c8edf9caf..250db4314 100644 --- a/binance_trade_bot/strategies/__init__.py +++ b/binance_trade_bot/strategies/__init__.py @@ -8,9 +8,7 @@ def get_strategy(name): for filename in filenames: if filename.endswith("_strategy.py"): if filename.replace("_strategy.py", "") == name: - spec = importlib.util.spec_from_file_location( - name, os.path.join(dirpath, filename) - ) + spec = importlib.util.spec_from_file_location(name, os.path.join(dirpath, filename)) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module.Strategy diff --git a/binance_trade_bot/strategies/default_strategy.py b/binance_trade_bot/strategies/default_strategy.py index c4d259164..e599cba7c 100644 --- a/binance_trade_bot/strategies/default_strategy.py +++ b/binance_trade_bot/strategies/default_strategy.py @@ -23,25 +23,17 @@ def scout(self): end="\r", ) - current_coin_price = self.manager.get_ticker_price( - current_coin + self.config.BRIDGE - ) + current_coin_price = self.manager.get_ticker_price(current_coin + self.config.BRIDGE) if current_coin_price is None: - self.logger.info( - "Skipping scouting... current coin {} not found".format( - current_coin + self.config.BRIDGE - ) - ) + self.logger.info(f"Skipping scouting... current coin {current_coin + self.config.BRIDGE} not found") return self._jump_to_best_coin(current_coin, current_coin_price) def bridge_scout(self): current_coin = self.db.get_current_coin() - if self.manager.get_currency_balance( - current_coin.symbol - ) > self.manager.get_min_notional( + if self.manager.get_currency_balance(current_coin.symbol) > self.manager.get_min_notional( current_coin.symbol, self.config.BRIDGE.symbol ): # Only scout if we don't have enough of the current coin @@ -62,9 +54,7 @@ def initialize_current_coin(self): self.logger.info(f"Setting initial coin to {current_coin_symbol}") if current_coin_symbol not in self.config.SUPPORTED_COIN_LIST: - sys.exit( - "***\nERROR!\nSince there is no backup file, a proper coin name must be provided at init\n***" - ) + sys.exit("***\nERROR!\nSince there is no backup file, a proper coin name must be provided at init\n***") self.db.set_current_coin(current_coin_symbol) # if we don't have a configuration, we selected a coin at random... Buy it so we can start trading. diff --git a/binance_trade_bot/strategies/multiple_coins_strategy.py b/binance_trade_bot/strategies/multiple_coins_strategy.py index 25f564a7f..55e031e63 100644 --- a/binance_trade_bot/strategies/multiple_coins_strategy.py +++ b/binance_trade_bot/strategies/multiple_coins_strategy.py @@ -22,21 +22,12 @@ def scout(self): coin_price = self.manager.get_ticker_price(coin + self.config.BRIDGE) if coin_price is None: - self.logger.info( - "Skipping scouting... current coin {} not found".format( - coin + self.config.BRIDGE - ) - ) + self.logger.info(f"Skipping scouting... current coin {coin + self.config.BRIDGE} not found") continue - min_notional = self.manager.get_min_notional( - coin.symbol, self.config.BRIDGE.symbol - ) + min_notional = self.manager.get_min_notional(coin.symbol, self.config.BRIDGE.symbol) - if ( - coin.symbol != current_coin_symbol - and coin_price * current_coin_balance < min_notional - ): + if coin.symbol != current_coin_symbol and coin_price * current_coin_balance < min_notional: continue have_coin = True