Merge pull request #584 from gcarq/feature/fix-loglevel
Drop Logger class and ensure parent logger detection
This commit is contained in:
		| @@ -1,6 +1,7 @@ | ||||
| """ | ||||
| Functions to analyze ticker data with indicators and produce buy and sell signals | ||||
| """ | ||||
| import logging | ||||
| from datetime import datetime, timedelta | ||||
| from enum import Enum | ||||
| from typing import Dict, List, Tuple | ||||
| @@ -9,11 +10,13 @@ import arrow | ||||
| from pandas import DataFrame, to_datetime | ||||
|  | ||||
| from freqtrade.exchange import get_ticker_history | ||||
| from freqtrade.logger import Logger | ||||
| from freqtrade.persistence import Trade | ||||
| from freqtrade.strategy.strategy import Strategy | ||||
|  | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class SignalType(Enum): | ||||
|     """ | ||||
|     Enum to distinguish between buy and sell signals | ||||
| @@ -32,8 +35,6 @@ class Analyze(object): | ||||
|         Init Analyze | ||||
|         :param config: Bot configuration (use the one from Configuration()) | ||||
|         """ | ||||
|         self.logger = Logger(name=__name__, level=config.get('loglevel')).get_logger() | ||||
|  | ||||
|         self.config = config | ||||
|         self.strategy = Strategy(self.config) | ||||
|  | ||||
| @@ -107,20 +108,20 @@ class Analyze(object): | ||||
|         """ | ||||
|         ticker_hist = get_ticker_history(pair, interval) | ||||
|         if not ticker_hist: | ||||
|             self.logger.warning('Empty ticker history for pair %s', pair) | ||||
|             logger.warning('Empty ticker history for pair %s', pair) | ||||
|             return False, False | ||||
|  | ||||
|         try: | ||||
|             dataframe = self.analyze_ticker(ticker_hist) | ||||
|         except ValueError as error: | ||||
|             self.logger.warning( | ||||
|             logger.warning( | ||||
|                 'Unable to analyze ticker for pair %s: %s', | ||||
|                 pair, | ||||
|                 str(error) | ||||
|             ) | ||||
|             return False, False | ||||
|         except Exception as error: | ||||
|             self.logger.exception( | ||||
|             logger.exception( | ||||
|                 'Unexpected error when analyzing ticker for pair %s: %s', | ||||
|                 pair, | ||||
|                 str(error) | ||||
| @@ -128,7 +129,7 @@ class Analyze(object): | ||||
|             return False, False | ||||
|  | ||||
|         if dataframe.empty: | ||||
|             self.logger.warning('Empty dataframe for pair %s', pair) | ||||
|             logger.warning('Empty dataframe for pair %s', pair) | ||||
|             return False, False | ||||
|  | ||||
|         latest = dataframe.iloc[-1] | ||||
| @@ -136,7 +137,7 @@ class Analyze(object): | ||||
|         # Check if dataframe is out of date | ||||
|         signal_date = arrow.get(latest['date']) | ||||
|         if signal_date < arrow.utcnow() - timedelta(minutes=(interval + 5)): | ||||
|             self.logger.warning( | ||||
|             logger.warning( | ||||
|                 'Outdated history for pair %s. Last tick is %s minutes old', | ||||
|                 pair, | ||||
|                 (arrow.utcnow() - signal_date).seconds // 60 | ||||
| @@ -144,7 +145,7 @@ class Analyze(object): | ||||
|             return False, False | ||||
|  | ||||
|         (buy, sell) = latest[SignalType.BUY.value] == 1, latest[SignalType.SELL.value] == 1 | ||||
|         self.logger.debug( | ||||
|         logger.debug( | ||||
|             'trigger: %s (pair=%s) buy=%s sell=%s', | ||||
|             latest['date'], | ||||
|             pair, | ||||
| @@ -161,17 +162,17 @@ class Analyze(object): | ||||
|         """ | ||||
|         # Check if minimal roi has been reached and no longer in buy conditions (avoiding a fee) | ||||
|         if self.min_roi_reached(trade=trade, current_rate=rate, current_time=date): | ||||
|             self.logger.debug('Required profit reached. Selling..') | ||||
|             logger.debug('Required profit reached. Selling..') | ||||
|             return True | ||||
|  | ||||
|         # Experimental: Check if the trade is profitable before selling it (avoid selling at loss) | ||||
|         if self.config.get('experimental', {}).get('sell_profit_only', False): | ||||
|             self.logger.debug('Checking if trade is profitable..') | ||||
|             logger.debug('Checking if trade is profitable..') | ||||
|             if trade.calc_profit(rate=rate) <= 0: | ||||
|                 return False | ||||
|  | ||||
|         if sell and not buy and self.config.get('experimental', {}).get('use_sell_signal', False): | ||||
|             self.logger.debug('Sell signal received. Selling..') | ||||
|             logger.debug('Sell signal received. Selling..') | ||||
|             return True | ||||
|  | ||||
|         return False | ||||
| @@ -184,7 +185,7 @@ class Analyze(object): | ||||
|         """ | ||||
|         current_profit = trade.calc_profit_percent(current_rate) | ||||
|         if self.strategy.stoploss is not None and current_profit < self.strategy.stoploss: | ||||
|             self.logger.debug('Stop loss hit.') | ||||
|             logger.debug('Stop loss hit.') | ||||
|             return True | ||||
|  | ||||
|         # Check if time matches and current rate is above threshold | ||||
|   | ||||
| @@ -3,6 +3,7 @@ This module contains the configuration class | ||||
| """ | ||||
|  | ||||
| import json | ||||
| import logging | ||||
| from argparse import Namespace | ||||
| from typing import Dict, Any | ||||
|  | ||||
| @@ -10,7 +11,9 @@ from jsonschema import Draft4Validator, validate | ||||
| from jsonschema.exceptions import ValidationError, best_match | ||||
|  | ||||
| from freqtrade.constants import Constants | ||||
| from freqtrade.logger import Logger | ||||
|  | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class Configuration(object): | ||||
| @@ -20,8 +23,6 @@ class Configuration(object): | ||||
|     """ | ||||
|     def __init__(self, args: Namespace) -> None: | ||||
|         self.args = args | ||||
|         self.logging = Logger(name=__name__) | ||||
|         self.logger = self.logging.get_logger() | ||||
|         self.config = None | ||||
|  | ||||
|     def load_config(self) -> Dict[str, Any]: | ||||
| @@ -29,7 +30,7 @@ class Configuration(object): | ||||
|         Extract information for sys.argv and load the bot configuration | ||||
|         :return: Configuration dictionary | ||||
|         """ | ||||
|         self.logger.info('Using config: %s ...', self.args.config) | ||||
|         logger.info('Using config: %s ...', self.args.config) | ||||
|         config = self._load_config_file(self.args.config) | ||||
|  | ||||
|         # Add the strategy file to use | ||||
| @@ -56,7 +57,7 @@ class Configuration(object): | ||||
|             with open(path) as file: | ||||
|                 conf = json.load(file) | ||||
|         except FileNotFoundError: | ||||
|             self.logger.critical( | ||||
|             logger.critical( | ||||
|                 'Config file "%s" not found. Please create your config file', | ||||
|                 path | ||||
|             ) | ||||
| @@ -64,7 +65,7 @@ class Configuration(object): | ||||
|  | ||||
|         if 'internals' not in conf: | ||||
|             conf['internals'] = {} | ||||
|         self.logger.info('Validating configuration ...') | ||||
|         logger.info('Validating configuration ...') | ||||
|  | ||||
|         return self._validate_config(conf) | ||||
|  | ||||
| @@ -77,13 +78,16 @@ class Configuration(object): | ||||
|         # Log level | ||||
|         if 'loglevel' in self.args and self.args.loglevel: | ||||
|             config.update({'loglevel': self.args.loglevel}) | ||||
|             self.logging.set_level(self.args.loglevel) | ||||
|             self.logger.info('Log level set at %s', config['loglevel']) | ||||
|             logging.basicConfig( | ||||
|                 level=config['loglevel'], | ||||
|                 format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', | ||||
|             ) | ||||
|             logger.info('Log level set to %s', logging.getLevelName(config['loglevel'])) | ||||
|  | ||||
|         # Add dynamic_whitelist if found | ||||
|         if 'dynamic_whitelist' in self.args and self.args.dynamic_whitelist: | ||||
|             config.update({'dynamic_whitelist': self.args.dynamic_whitelist}) | ||||
|             self.logger.info( | ||||
|             logger.info( | ||||
|                 'Parameter --dynamic-whitelist detected. ' | ||||
|                 'Using dynamically generated whitelist. ' | ||||
|                 '(not applicable with Backtesting and Hyperopt)' | ||||
| @@ -92,13 +96,13 @@ class Configuration(object): | ||||
|         # Add dry_run_db if found and the bot in dry run | ||||
|         if self.args.dry_run_db and config.get('dry_run', False): | ||||
|             config.update({'dry_run_db': True}) | ||||
|             self.logger.info('Parameter --dry-run-db detected ...') | ||||
|             logger.info('Parameter --dry-run-db detected ...') | ||||
|  | ||||
|         if config.get('dry_run_db', False): | ||||
|             if config.get('dry_run', False): | ||||
|                 self.logger.info('Dry_run will use the DB file: "tradesv3.dry_run.sqlite"') | ||||
|                 logger.info('Dry_run will use the DB file: "tradesv3.dry_run.sqlite"') | ||||
|             else: | ||||
|                 self.logger.info('Dry run is disabled. (--dry_run_db ignored)') | ||||
|                 logger.info('Dry run is disabled. (--dry_run_db ignored)') | ||||
|  | ||||
|         return config | ||||
|  | ||||
| @@ -112,39 +116,39 @@ class Configuration(object): | ||||
|         # (that will override the strategy configuration) | ||||
|         if 'ticker_interval' in self.args and self.args.ticker_interval: | ||||
|             config.update({'ticker_interval': self.args.ticker_interval}) | ||||
|             self.logger.info('Parameter -i/--ticker-interval detected ...') | ||||
|             self.logger.info('Using ticker_interval: %d ...', config.get('ticker_interval')) | ||||
|             logger.info('Parameter -i/--ticker-interval detected ...') | ||||
|             logger.info('Using ticker_interval: %d ...', config.get('ticker_interval')) | ||||
|  | ||||
|         # If -l/--live is used we add it to the configuration | ||||
|         if 'live' in self.args and self.args.live: | ||||
|             config.update({'live': True}) | ||||
|             self.logger.info('Parameter -l/--live detected ...') | ||||
|             logger.info('Parameter -l/--live detected ...') | ||||
|  | ||||
|         # If --realistic-simulation is used we add it to the configuration | ||||
|         if 'realistic_simulation' in self.args and self.args.realistic_simulation: | ||||
|             config.update({'realistic_simulation': True}) | ||||
|             self.logger.info('Parameter --realistic-simulation detected ...') | ||||
|         self.logger.info('Using max_open_trades: %s ...', config.get('max_open_trades')) | ||||
|             logger.info('Parameter --realistic-simulation detected ...') | ||||
|         logger.info('Using max_open_trades: %s ...', config.get('max_open_trades')) | ||||
|  | ||||
|         # If --timerange is used we add it to the configuration | ||||
|         if 'timerange' in self.args and self.args.timerange: | ||||
|             config.update({'timerange': self.args.timerange}) | ||||
|             self.logger.info('Parameter --timerange detected: %s ...', self.args.timerange) | ||||
|             logger.info('Parameter --timerange detected: %s ...', self.args.timerange) | ||||
|  | ||||
|         # If --datadir is used we add it to the configuration | ||||
|         if 'datadir' in self.args and self.args.datadir: | ||||
|             config.update({'datadir': self.args.datadir}) | ||||
|             self.logger.info('Parameter --datadir detected: %s ...', self.args.datadir) | ||||
|             logger.info('Parameter --datadir detected: %s ...', self.args.datadir) | ||||
|  | ||||
|         # If -r/--refresh-pairs-cached is used we add it to the configuration | ||||
|         if 'refresh_pairs' in self.args and self.args.refresh_pairs: | ||||
|             config.update({'refresh_pairs': True}) | ||||
|             self.logger.info('Parameter -r/--refresh-pairs-cached detected ...') | ||||
|             logger.info('Parameter -r/--refresh-pairs-cached detected ...') | ||||
|  | ||||
|         # If --export is used we add it to the configuration | ||||
|         if 'export' in self.args and self.args.export: | ||||
|             config.update({'export': self.args.export}) | ||||
|             self.logger.info('Parameter --export detected: %s ...', self.args.export) | ||||
|             logger.info('Parameter --export detected: %s ...', self.args.export) | ||||
|  | ||||
|         return config | ||||
|  | ||||
| @@ -156,18 +160,18 @@ class Configuration(object): | ||||
|         # If --realistic-simulation is used we add it to the configuration | ||||
|         if 'epochs' in self.args and self.args.epochs: | ||||
|             config.update({'epochs': self.args.epochs}) | ||||
|             self.logger.info('Parameter --epochs detected ...') | ||||
|             self.logger.info('Will run Hyperopt with for %s epochs ...', config.get('epochs')) | ||||
|             logger.info('Parameter --epochs detected ...') | ||||
|             logger.info('Will run Hyperopt with for %s epochs ...', config.get('epochs')) | ||||
|  | ||||
|         # If --mongodb is used we add it to the configuration | ||||
|         if 'mongodb' in self.args and self.args.mongodb: | ||||
|             config.update({'mongodb': self.args.mongodb}) | ||||
|             self.logger.info('Parameter --use-mongodb detected ...') | ||||
|             logger.info('Parameter --use-mongodb detected ...') | ||||
|  | ||||
|         # If --spaces is used we add it to the configuration | ||||
|         if 'spaces' in self.args and self.args.spaces: | ||||
|             config.update({'spaces': self.args.spaces}) | ||||
|             self.logger.info('Parameter -s/--spaces detected: %s', config.get('spaces')) | ||||
|             logger.info('Parameter -s/--spaces detected: %s', config.get('spaces')) | ||||
|  | ||||
|         return config | ||||
|  | ||||
| @@ -181,7 +185,7 @@ class Configuration(object): | ||||
|             validate(conf, Constants.CONF_SCHEMA) | ||||
|             return conf | ||||
|         except ValidationError as exception: | ||||
|             self.logger.fatal( | ||||
|             logger.fatal( | ||||
|                 'Invalid configuration. See config.json.example. Reason: %s', | ||||
|                 exception | ||||
|             ) | ||||
|   | ||||
| @@ -4,6 +4,7 @@ Freqtrade is the main module of this bot. It contains the class Freqtrade() | ||||
|  | ||||
| import copy | ||||
| import json | ||||
| import logging | ||||
| import time | ||||
| import traceback | ||||
| from datetime import datetime | ||||
| @@ -13,16 +14,20 @@ import arrow | ||||
| import requests | ||||
| from cachetools import cached, TTLCache | ||||
|  | ||||
| from freqtrade import (DependencyException, OperationalException, exchange, persistence) | ||||
| from freqtrade import ( | ||||
|     DependencyException, OperationalException, exchange, persistence, __version__ | ||||
| ) | ||||
| from freqtrade.analyze import Analyze | ||||
| from freqtrade.constants import Constants | ||||
| from freqtrade.fiat_convert import CryptoToFiatConverter | ||||
| from freqtrade.logger import Logger | ||||
| from freqtrade.persistence import Trade | ||||
| from freqtrade.rpc.rpc_manager import RPCManager | ||||
| from freqtrade.state import State | ||||
|  | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class FreqtradeBot(object): | ||||
|     """ | ||||
|     Freqtrade is the main class of the bot. | ||||
| @@ -37,8 +42,10 @@ class FreqtradeBot(object): | ||||
|         :param db_url: database connector string for sqlalchemy (Optional) | ||||
|         """ | ||||
|  | ||||
|         # Init the logger | ||||
|         self.logger = Logger(name=__name__, level=config.get('loglevel')).get_logger() | ||||
|         logger.info( | ||||
|             'Starting freqtrade %s', | ||||
|             __version__, | ||||
|         ) | ||||
|  | ||||
|         # Init bot states | ||||
|         self.state = State.STOPPED | ||||
| @@ -81,7 +88,7 @@ class FreqtradeBot(object): | ||||
|         :return: None | ||||
|         """ | ||||
|         self.rpc.send_msg('*Status:* `Stopping trader...`') | ||||
|         self.logger.info('Stopping trader and cleaning up modules...') | ||||
|         logger.info('Stopping trader and cleaning up modules...') | ||||
|         self.state = State.STOPPED | ||||
|         self.rpc.cleanup() | ||||
|         persistence.cleanup() | ||||
| @@ -97,7 +104,7 @@ class FreqtradeBot(object): | ||||
|         state = self.state | ||||
|         if state != old_state: | ||||
|             self.rpc.send_msg('*Status:* `{}`'.format(state.name.lower())) | ||||
|             self.logger.info('Changing state to: %s', state.name) | ||||
|             logger.info('Changing state to: %s', state.name) | ||||
|  | ||||
|         if state == State.STOPPED: | ||||
|             time.sleep(1) | ||||
| @@ -129,7 +136,7 @@ class FreqtradeBot(object): | ||||
|         result = func(*args, **kwargs) | ||||
|         end = time.time() | ||||
|         duration = max(min_secs - (end - start), 0.0) | ||||
|         self.logger.debug('Throttling %s for %.2f seconds', func.__name__, duration) | ||||
|         logger.debug('Throttling %s for %.2f seconds', func.__name__, duration) | ||||
|         time.sleep(duration) | ||||
|         return result | ||||
|  | ||||
| @@ -170,7 +177,7 @@ class FreqtradeBot(object): | ||||
|                 Trade.session.flush() | ||||
|  | ||||
|         except (requests.exceptions.RequestException, json.JSONDecodeError) as error: | ||||
|             self.logger.warning('%s, retrying in 30 seconds...', error) | ||||
|             logger.warning('%s, retrying in 30 seconds...', error) | ||||
|             time.sleep(Constants.RETRY_TIMEOUT) | ||||
|         except OperationalException: | ||||
|             self.rpc.send_msg( | ||||
| @@ -180,7 +187,7 @@ class FreqtradeBot(object): | ||||
|                     hint='Issue `/start` if you think it is safe to restart.' | ||||
|                 ) | ||||
|             ) | ||||
|             self.logger.exception('OperationalException. Stopping trader ...') | ||||
|             logger.exception('OperationalException. Stopping trader ...') | ||||
|             self.state = State.STOPPED | ||||
|         return state_changed | ||||
|  | ||||
| @@ -222,7 +229,7 @@ class FreqtradeBot(object): | ||||
|             # Market is not active | ||||
|             if not status['IsActive']: | ||||
|                 sanitized_whitelist.remove(pair) | ||||
|                 self.logger.info( | ||||
|                 logger.info( | ||||
|                     'Ignoring %s from whitelist (reason: %s).', | ||||
|                     pair, status.get('Notice') or 'wallet is not active' | ||||
|                 ) | ||||
| @@ -253,7 +260,7 @@ class FreqtradeBot(object): | ||||
|         stake_amount = self.config['stake_amount'] | ||||
|         interval = self.analyze.get_ticker_interval() | ||||
|  | ||||
|         self.logger.info( | ||||
|         logger.info( | ||||
|             'Checking buy signals to create a new trade with stake_amount: %f ...', | ||||
|             stake_amount | ||||
|         ) | ||||
| @@ -268,7 +275,7 @@ class FreqtradeBot(object): | ||||
|         for trade in Trade.query.filter(Trade.is_open.is_(True)).all(): | ||||
|             if trade.pair in whitelist: | ||||
|                 whitelist.remove(trade.pair) | ||||
|                 self.logger.debug('Ignoring %s in pair whitelist', trade.pair) | ||||
|                 logger.debug('Ignoring %s in pair whitelist', trade.pair) | ||||
|  | ||||
|         if not whitelist: | ||||
|             raise DependencyException('No currency pairs in whitelist') | ||||
| @@ -333,10 +340,10 @@ class FreqtradeBot(object): | ||||
|             if self.create_trade(): | ||||
|                 return True | ||||
|  | ||||
|             self.logger.info('Found no buy signals for whitelisted currencies. Trying again..') | ||||
|             logger.info('Found no buy signals for whitelisted currencies. Trying again..') | ||||
|             return False | ||||
|         except DependencyException as exception: | ||||
|             self.logger.warning('Unable to create trade: %s', exception) | ||||
|             logger.warning('Unable to create trade: %s', exception) | ||||
|             return False | ||||
|  | ||||
|     def process_maybe_execute_sell(self, trade: Trade) -> bool: | ||||
| @@ -347,7 +354,7 @@ class FreqtradeBot(object): | ||||
|         # Get order details for actual price per unit | ||||
|         if trade.open_order_id: | ||||
|             # Update trade with order values | ||||
|             self.logger.info('Found open order for %s', trade) | ||||
|             logger.info('Found open order for %s', trade) | ||||
|             trade.update(exchange.get_order(trade.open_order_id)) | ||||
|  | ||||
|         if trade.is_open and trade.open_order_id is None: | ||||
| @@ -363,7 +370,7 @@ class FreqtradeBot(object): | ||||
|         if not trade.is_open: | ||||
|             raise ValueError('attempt to handle closed trade: {}'.format(trade)) | ||||
|  | ||||
|         self.logger.debug('Handling %s ...', trade) | ||||
|         logger.debug('Handling %s ...', trade) | ||||
|         current_rate = exchange.get_ticker(trade.pair)['bid'] | ||||
|  | ||||
|         (buy, sell) = (False, False) | ||||
| @@ -389,7 +396,7 @@ class FreqtradeBot(object): | ||||
|             try: | ||||
|                 order = exchange.get_order(trade.open_order_id) | ||||
|             except requests.exceptions.RequestException: | ||||
|                 self.logger.info( | ||||
|                 logger.info( | ||||
|                     'Cannot query order for %s due to %s', | ||||
|                     trade, | ||||
|                     traceback.format_exc()) | ||||
| @@ -419,7 +426,7 @@ class FreqtradeBot(object): | ||||
|             # FIX? do we really need to flush, caller of | ||||
|             #      check_handle_timedout will flush afterwards | ||||
|             Trade.session.flush() | ||||
|             self.logger.info('Buy order timeout for %s.', trade) | ||||
|             logger.info('Buy order timeout for %s.', trade) | ||||
|             self.rpc.send_msg('*Timeout:* Unfilled buy order for {} cancelled'.format( | ||||
|                 trade.pair.replace('_', '/'))) | ||||
|             return True | ||||
| @@ -429,7 +436,7 @@ class FreqtradeBot(object): | ||||
|         trade.amount = order['amount'] - order['remaining'] | ||||
|         trade.stake_amount = trade.amount * trade.open_rate | ||||
|         trade.open_order_id = None | ||||
|         self.logger.info('Partial buy order timeout for %s.', trade) | ||||
|         logger.info('Partial buy order timeout for %s.', trade) | ||||
|         self.rpc.send_msg('*Timeout:* Remaining buy order for {} cancelled'.format( | ||||
|             trade.pair.replace('_', '/'))) | ||||
|         return False | ||||
| @@ -450,7 +457,7 @@ class FreqtradeBot(object): | ||||
|             trade.open_order_id = None | ||||
|             self.rpc.send_msg('*Timeout:* Unfilled sell order for {} cancelled'.format( | ||||
|                 trade.pair.replace('_', '/'))) | ||||
|             self.logger.info('Sell order timeout for %s.', trade) | ||||
|             logger.info('Sell order timeout for %s.', trade) | ||||
|             return True | ||||
|  | ||||
|         # TODO: figure out how to handle partially complete sell orders | ||||
|   | ||||
| @@ -1,83 +0,0 @@ | ||||
| # pragma pylint: disable=too-few-public-methods | ||||
|  | ||||
| """ | ||||
| This module contains the class for logger and logging messages | ||||
| """ | ||||
|  | ||||
| import logging | ||||
|  | ||||
|  | ||||
| class Logger(object): | ||||
|     """ | ||||
|     Logging class | ||||
|     """ | ||||
|     def __init__(self, name='', level=logging.INFO) -> None: | ||||
|         """ | ||||
|         Init the logger class | ||||
|         :param name: Name of the Logger scope | ||||
|         :param level: Logger level that should be used | ||||
|         :return: None | ||||
|         """ | ||||
|         self.name = name | ||||
|         self.logger = None | ||||
|  | ||||
|         if level is None: | ||||
|             level = logging.INFO | ||||
|         self.level = level | ||||
|  | ||||
|         self._init_logger() | ||||
|  | ||||
|     def _init_logger(self) -> None: | ||||
|         """ | ||||
|         Setup the bot logger configuration | ||||
|         :return: None | ||||
|         """ | ||||
|         logging.basicConfig( | ||||
|             level=self.level, | ||||
|             format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', | ||||
|         ) | ||||
|  | ||||
|         self.logger = self.get_logger() | ||||
|         self.set_level(self.level) | ||||
|  | ||||
|     def get_logger(self) -> logging.RootLogger: | ||||
|         """ | ||||
|         Return the logger instance to use for sending message | ||||
|         :return: the logger instance | ||||
|         """ | ||||
|         return logging.getLogger(self.name) | ||||
|  | ||||
|     def set_name(self, name: str) -> logging.RootLogger: | ||||
|         """ | ||||
|         Set the name of the logger | ||||
|         :param name: Name of the logger | ||||
|         :return: None | ||||
|         """ | ||||
|         self.name = name | ||||
|         self.logger = self.get_logger() | ||||
|         return self.logger | ||||
|  | ||||
|     def set_level(self, level) -> None: | ||||
|         """ | ||||
|         Set the level of the logger | ||||
|         :param level: | ||||
|         :return: None | ||||
|         """ | ||||
|         self.level = level | ||||
|         self.logger.setLevel(self.level) | ||||
|  | ||||
|     def set_format(self, log_format: str, propagate: bool = False) -> None: | ||||
|         """ | ||||
|         Set a new logging format | ||||
|         :return: None | ||||
|         """ | ||||
|         handler = logging.StreamHandler() | ||||
|  | ||||
|         len_handlers = len(self.logger.handlers) | ||||
|         if len_handlers: | ||||
|             self.logger.removeHandler(handler) | ||||
|  | ||||
|         handler.setFormatter(logging.Formatter(log_format)) | ||||
|         self.logger.addHandler(handler) | ||||
|  | ||||
|         self.logger.propagate = propagate | ||||
| @@ -8,13 +8,11 @@ import logging | ||||
| import sys | ||||
| from typing import List | ||||
|  | ||||
| from freqtrade import (__version__) | ||||
| from freqtrade.arguments import Arguments | ||||
| from freqtrade.configuration import Configuration | ||||
| from freqtrade.freqtradebot import FreqtradeBot | ||||
| from freqtrade.logger import Logger | ||||
|  | ||||
| logger = Logger(name='freqtrade').get_logger() | ||||
| logger = logging.getLogger('freqtrade') | ||||
|  | ||||
|  | ||||
| def main(sysargv: List[str]) -> None: | ||||
| @@ -34,20 +32,14 @@ def main(sysargv: List[str]) -> None: | ||||
|         args.func(args) | ||||
|         return | ||||
|  | ||||
|     logger.info( | ||||
|         'Starting freqtrade %s (loglevel=%s)', | ||||
|         __version__, | ||||
|         logging.getLevelName(args.loglevel) | ||||
|     ) | ||||
|  | ||||
|     freqtrade = None | ||||
|     return_code = 1 | ||||
|     try: | ||||
|         # Load and validate configuration | ||||
|         configuration = Configuration(args) | ||||
|         config = Configuration(args).get_config() | ||||
|  | ||||
|         # Init the bot | ||||
|         freqtrade = FreqtradeBot(configuration.get_config()) | ||||
|         freqtrade = FreqtradeBot(config) | ||||
|  | ||||
|         state = None | ||||
|         while 1: | ||||
|   | ||||
| @@ -2,15 +2,15 @@ | ||||
|  | ||||
| import gzip | ||||
| import json | ||||
| import logging | ||||
| import os | ||||
| from typing import Optional, List, Dict, Tuple | ||||
|  | ||||
| from freqtrade import misc | ||||
| from freqtrade.exchange import get_ticker_history | ||||
| from freqtrade.logger import Logger | ||||
| from user_data.hyperopt_conf import hyperopt_optimize_conf | ||||
|  | ||||
| logger = Logger(name=__name__).get_logger() | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| def trim_tickerlist(tickerlist: List[Dict], timerange: Tuple[Tuple, int, int]) -> List[Dict]: | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
| """ | ||||
| This module contains the backtesting logic | ||||
| """ | ||||
| import logging | ||||
| from argparse import Namespace | ||||
| from typing import Dict, Tuple, Any, List, Optional | ||||
|  | ||||
| @@ -16,11 +17,13 @@ from freqtrade.analyze import Analyze | ||||
| from freqtrade.arguments import Arguments | ||||
| from freqtrade.configuration import Configuration | ||||
| from freqtrade.exchange import Bittrex | ||||
| from freqtrade.logger import Logger | ||||
| from freqtrade.misc import file_dump_json | ||||
| from freqtrade.persistence import Trade | ||||
|  | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class Backtesting(object): | ||||
|     """ | ||||
|     Backtesting class, this class contains all the logic to run a backtest | ||||
| @@ -30,10 +33,6 @@ class Backtesting(object): | ||||
|     backtesting.start() | ||||
|     """ | ||||
|     def __init__(self, config: Dict[str, Any]) -> None: | ||||
|  | ||||
|         # Init the logger | ||||
|         self.logging = Logger(name=__name__, level=config['loglevel']) | ||||
|         self.logger = self.logging.get_logger() | ||||
|         self.config = config | ||||
|         self.analyze = None | ||||
|         self.ticker_interval = None | ||||
| @@ -200,7 +199,7 @@ class Backtesting(object): | ||||
|         # For now export inside backtest(), maybe change so that backtest() | ||||
|         # returns a tuple like: (dataframe, records, logs, etc) | ||||
|         if record and record.find('trades') >= 0: | ||||
|             self.logger.info('Dumping backtest results') | ||||
|             logger.info('Dumping backtest results') | ||||
|             file_dump_json('backtest-result.json', records) | ||||
|         labels = ['currency', 'profit_percent', 'profit_BTC', 'duration'] | ||||
|         return DataFrame.from_records(trades, columns=labels) | ||||
| @@ -212,15 +211,15 @@ class Backtesting(object): | ||||
|         """ | ||||
|         data = {} | ||||
|         pairs = self.config['exchange']['pair_whitelist'] | ||||
|         self.logger.info('Using stake_currency: %s ...', self.config['stake_currency']) | ||||
|         self.logger.info('Using stake_amount: %s ...', self.config['stake_amount']) | ||||
|         logger.info('Using stake_currency: %s ...', self.config['stake_currency']) | ||||
|         logger.info('Using stake_amount: %s ...', self.config['stake_amount']) | ||||
|  | ||||
|         if self.config.get('live'): | ||||
|             self.logger.info('Downloading data for all pairs in whitelist ...') | ||||
|             logger.info('Downloading data for all pairs in whitelist ...') | ||||
|             for pair in pairs: | ||||
|                 data[pair] = exchange.get_ticker_history(pair, self.ticker_interval) | ||||
|         else: | ||||
|             self.logger.info('Using local backtesting data (using whitelist in given config) ...') | ||||
|             logger.info('Using local backtesting data (using whitelist in given config) ...') | ||||
|  | ||||
|             timerange = Arguments.parse_timerange(self.config.get('timerange')) | ||||
|             data = optimize.load_data( | ||||
| @@ -235,14 +234,14 @@ class Backtesting(object): | ||||
|         if self.config.get('realistic_simulation', False): | ||||
|             max_open_trades = self.config['max_open_trades'] | ||||
|         else: | ||||
|             self.logger.info('Ignoring max_open_trades (realistic_simulation not set) ...') | ||||
|             logger.info('Ignoring max_open_trades (realistic_simulation not set) ...') | ||||
|             max_open_trades = 0 | ||||
|  | ||||
|         preprocessed = self.tickerdata_to_dataframe(data) | ||||
|  | ||||
|         # Print timeframe | ||||
|         min_date, max_date = self.get_timeframe(preprocessed) | ||||
|         self.logger.info( | ||||
|         logger.info( | ||||
|             'Measuring data from %s up to %s (%s days)..', | ||||
|             min_date.isoformat(), | ||||
|             max_date.isoformat(), | ||||
| @@ -263,9 +262,7 @@ class Backtesting(object): | ||||
|                 'record': self.config.get('export') | ||||
|             } | ||||
|         ) | ||||
|  | ||||
|         self.logging.set_format('%(message)s') | ||||
|         self.logger.info( | ||||
|         logger.info( | ||||
|             '\n==================================== ' | ||||
|             'BACKTESTING REPORT' | ||||
|             ' ====================================\n' | ||||
| @@ -299,13 +296,9 @@ def start(args: Namespace) -> None: | ||||
|     :param args: Cli args from Arguments() | ||||
|     :return: None | ||||
|     """ | ||||
|  | ||||
|     # Initialize logger | ||||
|     logger = Logger(name=__name__).get_logger() | ||||
|     logger.info('Starting freqtrade in Backtesting mode') | ||||
|  | ||||
|     # Initialize configuration | ||||
|     config = setup_configuration(args) | ||||
|     logger.info('Starting freqtrade in Backtesting mode') | ||||
|  | ||||
|     # Initialize backtesting object | ||||
|     backtesting = Backtesting(config) | ||||
|   | ||||
| @@ -25,12 +25,14 @@ from pandas import DataFrame | ||||
| import freqtrade.vendor.qtpylib.indicators as qtpylib | ||||
| from freqtrade.arguments import Arguments | ||||
| from freqtrade.configuration import Configuration | ||||
| from freqtrade.logger import Logger | ||||
| from freqtrade.optimize import load_data | ||||
| from freqtrade.optimize.backtesting import Backtesting | ||||
| from user_data.hyperopt_conf import hyperopt_optimize_conf | ||||
|  | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class Hyperopt(Backtesting): | ||||
|     """ | ||||
|     Hyperopt class, this class contains all the logic to run a hyperopt simulation | ||||
| @@ -42,11 +44,6 @@ class Hyperopt(Backtesting): | ||||
|     def __init__(self, config: Dict[str, Any]) -> None: | ||||
|  | ||||
|         super().__init__(config) | ||||
|  | ||||
|         # Rename the logging to display Hyperopt file instead of Backtesting | ||||
|         self.logging = Logger(name=__name__, level=config['loglevel']) | ||||
|         self.logger = self.logging.get_logger() | ||||
|  | ||||
|         # set TARGET_TRADES to suit your number concurrent trades so its realistic | ||||
|         # to the number of days | ||||
|         self.target_trades = 600 | ||||
| @@ -194,14 +191,14 @@ class Hyperopt(Backtesting): | ||||
|         """ | ||||
|         Save hyperopt trials to file | ||||
|         """ | ||||
|         self.logger.info('Saving Trials to \'%s\'', self.trials_file) | ||||
|         logger.info('Saving Trials to \'%s\'', self.trials_file) | ||||
|         pickle.dump(self.trials, open(self.trials_file, 'wb')) | ||||
|  | ||||
|     def read_trials(self) -> Trials: | ||||
|         """ | ||||
|         Read hyperopt trials file | ||||
|         """ | ||||
|         self.logger.info('Reading Trials from \'%s\'', self.trials_file) | ||||
|         logger.info('Reading Trials from \'%s\'', self.trials_file) | ||||
|         trials = pickle.load(open(self.trials_file, 'rb')) | ||||
|         os.remove(self.trials_file) | ||||
|         return trials | ||||
| @@ -212,7 +209,7 @@ class Hyperopt(Backtesting): | ||||
|         """ | ||||
|         vals = json.dumps(self.trials.best_trial['misc']['vals'], indent=4) | ||||
|         results = self.trials.best_trial['result']['result'] | ||||
|         self.logger.info('Best result:\n%s\nwith values:\n%s', results, vals) | ||||
|         logger.info('Best result:\n%s\nwith values:\n%s', results, vals) | ||||
|  | ||||
|     def log_results(self, results) -> None: | ||||
|         """ | ||||
| @@ -220,13 +217,13 @@ class Hyperopt(Backtesting): | ||||
|         """ | ||||
|         if results['loss'] < self.current_best_loss: | ||||
|             self.current_best_loss = results['loss'] | ||||
|             log_msg = '{:5d}/{}: {}. Loss {:.5f}'.format( | ||||
|             log_msg = '\n{:5d}/{}: {}. Loss {:.5f}'.format( | ||||
|                 results['current_tries'], | ||||
|                 results['total_tries'], | ||||
|                 results['result'], | ||||
|                 results['loss'] | ||||
|             ) | ||||
|             self.logger.info(log_msg) | ||||
|             print(log_msg) | ||||
|         else: | ||||
|             print('.', end='') | ||||
|             sys.stdout.flush() | ||||
| @@ -511,8 +508,8 @@ class Hyperopt(Backtesting): | ||||
|         self.processed = self.tickerdata_to_dataframe(data) | ||||
|  | ||||
|         if self.config.get('mongodb'): | ||||
|             self.logger.info('Using mongodb ...') | ||||
|             self.logger.info( | ||||
|             logger.info('Using mongodb ...') | ||||
|             logger.info( | ||||
|                 'Start scripts/start-mongodb.sh and start-hyperopt-worker.sh manually!' | ||||
|             ) | ||||
|  | ||||
| @@ -522,7 +519,7 @@ class Hyperopt(Backtesting): | ||||
|                 exp_key='exp1' | ||||
|             ) | ||||
|         else: | ||||
|             self.logger.info('Preparing Trials..') | ||||
|             logger.info('Preparing Trials..') | ||||
|             signal.signal(signal.SIGINT, self.signal_handler) | ||||
|             # read trials file if we have one | ||||
|             if os.path.exists(self.trials_file) and os.path.getsize(self.trials_file) > 0: | ||||
| @@ -530,16 +527,13 @@ class Hyperopt(Backtesting): | ||||
|  | ||||
|                 self.current_tries = len(self.trials.results) | ||||
|                 self.total_tries += self.current_tries | ||||
|                 self.logger.info( | ||||
|                 logger.info( | ||||
|                     'Continuing with trials. Current: %d, Total: %d', | ||||
|                     self.current_tries, | ||||
|                     self.total_tries | ||||
|                 ) | ||||
|  | ||||
|         try: | ||||
|             # change the Logging format | ||||
|             self.logging.set_format('\n%(message)s') | ||||
|  | ||||
|             best_parameters = fmin( | ||||
|                 fn=self.generate_optimizer, | ||||
|                 space=self.hyperopt_space(), | ||||
| @@ -563,11 +557,11 @@ class Hyperopt(Backtesting): | ||||
|                 best_parameters | ||||
|             ) | ||||
|  | ||||
|         self.logger.info('Best parameters:\n%s', json.dumps(best_parameters, indent=4)) | ||||
|         logger.info('Best parameters:\n%s', json.dumps(best_parameters, indent=4)) | ||||
|         if 'roi_t1' in best_parameters: | ||||
|             self.logger.info('ROI table:\n%s', self.generate_roi_table(best_parameters)) | ||||
|             logger.info('ROI table:\n%s', self.generate_roi_table(best_parameters)) | ||||
|  | ||||
|         self.logger.info('Best Result:\n%s', best_result) | ||||
|         logger.info('Best Result:\n%s', best_result) | ||||
|  | ||||
|         # Store trials result to file to resume next time | ||||
|         self.save_trials() | ||||
| @@ -576,7 +570,7 @@ class Hyperopt(Backtesting): | ||||
|         """ | ||||
|         Hyperopt SIGINT handler | ||||
|         """ | ||||
|         self.logger.info( | ||||
|         logger.info( | ||||
|             'Hyperopt received %s', | ||||
|             signal.Signals(sig).name | ||||
|         ) | ||||
| @@ -597,13 +591,11 @@ def start(args: Namespace) -> None: | ||||
|     logging.getLogger('hyperopt.mongoexp').setLevel(logging.WARNING) | ||||
|     logging.getLogger('hyperopt.tpe').setLevel(logging.WARNING) | ||||
|  | ||||
|     # Initialize logger | ||||
|     logger = Logger(name=__name__).get_logger() | ||||
|     logger.info('Starting freqtrade in Hyperopt mode') | ||||
|  | ||||
|     # Initialize configuration | ||||
|     # Monkey patch the configuration with hyperopt_conf.py | ||||
|     configuration = Configuration(args) | ||||
|     logger.info('Starting freqtrade in Hyperopt mode') | ||||
|  | ||||
|     optimize_config = hyperopt_optimize_conf() | ||||
|     config = configuration._load_common_config(optimize_config) | ||||
|     config = configuration._load_backtesting_config(config) | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| """ | ||||
| This module contains class to define a RPC communications | ||||
| """ | ||||
|  | ||||
| import logging | ||||
| from datetime import datetime, timedelta | ||||
| from decimal import Decimal | ||||
| from typing import Tuple, Any | ||||
| @@ -11,12 +11,14 @@ import sqlalchemy as sql | ||||
| from pandas import DataFrame | ||||
|  | ||||
| from freqtrade import exchange | ||||
| from freqtrade.logger import Logger | ||||
| from freqtrade.misc import shorten_date | ||||
| from freqtrade.persistence import Trade | ||||
| from freqtrade.state import State | ||||
|  | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class RPC(object): | ||||
|     """ | ||||
|     RPC class can be used to have extra feature, like bot data, and access to DB data | ||||
| @@ -28,10 +30,6 @@ class RPC(object): | ||||
|         :return: None | ||||
|         """ | ||||
|         self.freqtrade = freqtrade | ||||
|         self.logger = Logger( | ||||
|             name=__name__, | ||||
|             level=self.freqtrade.config.get('loglevel') | ||||
|         ).get_logger() | ||||
|  | ||||
|     def rpc_trade_status(self) -> Tuple[bool, Any]: | ||||
|         """ | ||||
| @@ -346,7 +344,7 @@ class RPC(object): | ||||
|             ) | ||||
|         ).first() | ||||
|         if not trade: | ||||
|             self.logger.warning('forcesell: Invalid argument received') | ||||
|             logger.warning('forcesell: Invalid argument received') | ||||
|             return True, 'Invalid argument.' | ||||
|  | ||||
|         _exec_forcesell(trade) | ||||
|   | ||||
| @@ -1,11 +1,14 @@ | ||||
| """ | ||||
| This module contains class to manage RPC communications (Telegram, Slack, ...) | ||||
| """ | ||||
| import logging | ||||
|  | ||||
| from freqtrade.logger import Logger | ||||
| from freqtrade.rpc.telegram import Telegram | ||||
|  | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class RPCManager(object): | ||||
|     """ | ||||
|     Class to manage RPC objects (Telegram, Slack, ...) | ||||
| @@ -18,12 +21,6 @@ class RPCManager(object): | ||||
|         """ | ||||
|         self.freqtrade = freqtrade | ||||
|  | ||||
|         # Init the logger | ||||
|         self.logger = Logger( | ||||
|             name=__name__, | ||||
|             level=self.freqtrade.config.get('loglevel') | ||||
|         ).get_logger() | ||||
|  | ||||
|         self.registered_modules = [] | ||||
|         self.telegram = None | ||||
|         self._init() | ||||
| @@ -34,7 +31,7 @@ class RPCManager(object): | ||||
|         :return: | ||||
|         """ | ||||
|         if self.freqtrade.config['telegram'].get('enabled', False): | ||||
|             self.logger.info('Enabling rpc.telegram ...') | ||||
|             logger.info('Enabling rpc.telegram ...') | ||||
|             self.registered_modules.append('telegram') | ||||
|             self.telegram = Telegram(self.freqtrade) | ||||
|  | ||||
| @@ -44,7 +41,7 @@ class RPCManager(object): | ||||
|         :return: None | ||||
|         """ | ||||
|         if 'telegram' in self.registered_modules: | ||||
|             self.logger.info('Cleaning up rpc.telegram ...') | ||||
|             logger.info('Cleaning up rpc.telegram ...') | ||||
|             self.registered_modules.remove('telegram') | ||||
|             self.telegram.cleanup() | ||||
|  | ||||
| @@ -54,6 +51,6 @@ class RPCManager(object): | ||||
|         :param msg: message | ||||
|         :return: None | ||||
|         """ | ||||
|         self.logger.info(msg) | ||||
|         logger.info(msg) | ||||
|         if 'telegram' in self.registered_modules: | ||||
|             self.telegram.send_msg(msg) | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
| """ | ||||
| This module manage Telegram communication | ||||
| """ | ||||
|  | ||||
| import logging | ||||
| from typing import Any, Callable | ||||
|  | ||||
| from tabulate import tabulate | ||||
| @@ -15,6 +15,9 @@ from freqtrade.__init__ import __version__ | ||||
| from freqtrade.rpc.rpc import RPC | ||||
|  | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| def authorized_only(command_handler: Callable[[Bot, Update], None]) -> Callable[..., Any]: | ||||
|     """ | ||||
|     Decorator to check if the message comes from the correct chat_id | ||||
| @@ -31,13 +34,13 @@ def authorized_only(command_handler: Callable[[Bot, Update], None]) -> Callable[ | ||||
|         chat_id = int(self._config['telegram']['chat_id']) | ||||
|  | ||||
|         if int(update.message.chat_id) != chat_id: | ||||
|             self.logger.info( | ||||
|             logger.info( | ||||
|                 'Rejected unauthorized message from: %s', | ||||
|                 update.message.chat_id | ||||
|             ) | ||||
|             return wrapper | ||||
|  | ||||
|         self.logger.info( | ||||
|         logger.info( | ||||
|             'Executing handler: %s for chat_id: %s', | ||||
|             command_handler.__name__, | ||||
|             chat_id | ||||
| @@ -45,7 +48,7 @@ def authorized_only(command_handler: Callable[[Bot, Update], None]) -> Callable[ | ||||
|         try: | ||||
|             return command_handler(self, *args, **kwargs) | ||||
|         except BaseException: | ||||
|             self.logger.exception('Exception occurred within Telegram module') | ||||
|             logger.exception('Exception occurred within Telegram module') | ||||
|  | ||||
|     return wrapper | ||||
|  | ||||
| @@ -101,7 +104,7 @@ class Telegram(RPC): | ||||
|             timeout=30, | ||||
|             read_latency=60, | ||||
|         ) | ||||
|         self.logger.info( | ||||
|         logger.info( | ||||
|             'rpc.telegram is listening for following commands: %s', | ||||
|             [h.command for h in handles] | ||||
|         ) | ||||
| @@ -357,7 +360,7 @@ class Telegram(RPC): | ||||
|             'max': [self._config['max_open_trades']] | ||||
|         }, headers=['current', 'max'], tablefmt='simple') | ||||
|         message = "<pre>{}</pre>".format(message) | ||||
|         self.logger.debug(message) | ||||
|         logger.debug(message) | ||||
|         self.send_msg(message, parse_mode=ParseMode.HTML) | ||||
|  | ||||
|     @authorized_only | ||||
| @@ -428,7 +431,7 @@ class Telegram(RPC): | ||||
|             except NetworkError as network_err: | ||||
|                 # Sometimes the telegram server resets the current connection, | ||||
|                 # if this is the case we send the message again. | ||||
|                 self.logger.warning( | ||||
|                 logger.warning( | ||||
|                     'Telegram NetworkError: %s! Trying one more time.', | ||||
|                     network_err.message | ||||
|                 ) | ||||
| @@ -439,7 +442,7 @@ class Telegram(RPC): | ||||
|                     reply_markup=reply_markup | ||||
|                 ) | ||||
|         except TelegramError as telegram_err: | ||||
|             self.logger.warning( | ||||
|             logger.warning( | ||||
|                 'TelegramError: %s! Giving up on that message.', | ||||
|                 telegram_err.message | ||||
|             ) | ||||
|   | ||||
| @@ -4,6 +4,7 @@ | ||||
| This module load custom strategies | ||||
| """ | ||||
| import importlib | ||||
| import logging | ||||
| import os | ||||
| import sys | ||||
| from collections import OrderedDict | ||||
| @@ -11,12 +12,14 @@ from collections import OrderedDict | ||||
| from pandas import DataFrame | ||||
|  | ||||
| from freqtrade.constants import Constants | ||||
| from freqtrade.logger import Logger | ||||
| from freqtrade.strategy.interface import IStrategy | ||||
|  | ||||
| sys.path.insert(0, r'../../user_data/strategies') | ||||
|  | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class Strategy(object): | ||||
|     """ | ||||
|     This class contains all the logic to load custom strategy class | ||||
| @@ -27,8 +30,6 @@ class Strategy(object): | ||||
|         :param config: | ||||
|         :return: | ||||
|         """ | ||||
|         self.logger = Logger(name=__name__).get_logger() | ||||
|  | ||||
|         # Verify the strategy is in the configuration, otherwise fallback to the default strategy | ||||
|         if 'strategy' in config: | ||||
|             strategy = config['strategy'] | ||||
| @@ -42,17 +43,17 @@ class Strategy(object): | ||||
|         # Check if we need to override configuration | ||||
|         if 'minimal_roi' in config: | ||||
|             self.custom_strategy.minimal_roi = config['minimal_roi'] | ||||
|             self.logger.info("Override strategy \'minimal_roi\' with value in config file.") | ||||
|             logger.info("Override strategy \'minimal_roi\' with value in config file.") | ||||
|  | ||||
|         if 'stoploss' in config: | ||||
|             self.custom_strategy.stoploss = config['stoploss'] | ||||
|             self.logger.info( | ||||
|             logger.info( | ||||
|                 "Override strategy \'stoploss\' with value in config file: %s.", config['stoploss'] | ||||
|             ) | ||||
|  | ||||
|         if 'ticker_interval' in config: | ||||
|             self.custom_strategy.ticker_interval = config['ticker_interval'] | ||||
|             self.logger.info( | ||||
|             logger.info( | ||||
|                 "Override strategy \'ticker_interval\' with value in config file: %s.", | ||||
|                 config['ticker_interval'] | ||||
|             ) | ||||
| @@ -87,12 +88,12 @@ class Strategy(object): | ||||
|  | ||||
|         # Fallback to the default strategy | ||||
|         except (ImportError, TypeError) as error: | ||||
|             self.logger.error( | ||||
|             logger.error( | ||||
|                 "Impossible to load Strategy 'user_data/strategies/%s.py'. This file does not exist" | ||||
|                 " or contains Python code errors", | ||||
|                 strategy_name | ||||
|             ) | ||||
|             self.logger.error( | ||||
|             logger.error( | ||||
|                 "The error is:\n%s.", | ||||
|                 error | ||||
|             ) | ||||
| @@ -106,7 +107,7 @@ class Strategy(object): | ||||
|         module = importlib.import_module(filename, __package__) | ||||
|         custom_strategy = getattr(module, module.class_name) | ||||
|  | ||||
|         self.logger.info("Load strategy class: %s (%s.py)", module.class_name, filename) | ||||
|         logger.info("Load strategy class: %s (%s.py)", module.class_name, filename) | ||||
|         return custom_strategy() | ||||
|  | ||||
|     @staticmethod | ||||
|   | ||||
| @@ -50,7 +50,6 @@ def test_start(mocker, default_conf, caplog) -> None: | ||||
|     Test start() function | ||||
|     """ | ||||
|     start_mock = MagicMock() | ||||
|     mocker.patch('freqtrade.logger.Logger.set_format', MagicMock()) | ||||
|     mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock) | ||||
|     mocker.patch('freqtrade.configuration.open', mocker.mock_open( | ||||
|         read_data=json.dumps(default_conf) | ||||
| @@ -110,7 +109,7 @@ def test_loss_calculation_has_limited_profit() -> None: | ||||
|     assert under > correct | ||||
|  | ||||
|  | ||||
| def test_log_results_if_loss_improves(caplog) -> None: | ||||
| def test_log_results_if_loss_improves(capsys) -> None: | ||||
|     hyperopt = _HYPEROPT | ||||
|     hyperopt.current_best_loss = 2 | ||||
|     hyperopt.log_results( | ||||
| @@ -121,7 +120,8 @@ def test_log_results_if_loss_improves(caplog) -> None: | ||||
|             'result': 'foo' | ||||
|         } | ||||
|     ) | ||||
|     assert log_has('    1/2: foo. Loss 1.00000', caplog.record_tuples) | ||||
|     out, err = capsys.readouterr() | ||||
|     assert '    1/2: foo. Loss 1.00000'in out | ||||
|  | ||||
|  | ||||
| def test_no_log_if_loss_does_not_improve(caplog) -> None: | ||||
| @@ -169,7 +169,6 @@ def test_fmin_best_results(mocker, default_conf, caplog) -> None: | ||||
|     mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock()) | ||||
|     mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value=fmin_result) | ||||
|     mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf) | ||||
|     mocker.patch('freqtrade.logger.Logger.set_format', MagicMock()) | ||||
|  | ||||
|     Strategy({'strategy': 'default_strategy'}) | ||||
|     hyperopt = Hyperopt(conf) | ||||
| @@ -214,7 +213,6 @@ def test_fmin_throw_value_error(mocker, default_conf, caplog) -> None: | ||||
|     conf.update({'timerange': None}) | ||||
|     conf.update({'spaces': 'all'}) | ||||
|     mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf) | ||||
|     mocker.patch('freqtrade.logger.Logger.set_format', MagicMock()) | ||||
|     Strategy({'strategy': 'default_strategy'}) | ||||
|     hyperopt = Hyperopt(conf) | ||||
|     hyperopt.trials = create_trials(mocker) | ||||
| @@ -256,7 +254,6 @@ def test_resuming_previous_hyperopt_results_succeeds(mocker, default_conf) -> No | ||||
|     mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock()) | ||||
|     mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value={}) | ||||
|     mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf) | ||||
|     mocker.patch('freqtrade.logger.Logger.set_format', MagicMock()) | ||||
|  | ||||
|     Strategy({'strategy': 'default_strategy'}) | ||||
|     hyperopt = Hyperopt(conf) | ||||
|   | ||||
| @@ -29,7 +29,6 @@ def test_strategy_structure(): | ||||
|  | ||||
| def test_load_strategy(result): | ||||
|     strategy = Strategy() | ||||
|     strategy.logger = logging.getLogger(__name__) | ||||
|  | ||||
|     assert not hasattr(Strategy, 'custom_strategy') | ||||
|     strategy._load_strategy('test_strategy') | ||||
| @@ -42,14 +41,13 @@ def test_load_strategy(result): | ||||
|  | ||||
| def test_load_not_found_strategy(caplog): | ||||
|     strategy = Strategy() | ||||
|     strategy.logger = logging.getLogger(__name__) | ||||
|  | ||||
|     assert not hasattr(Strategy, 'custom_strategy') | ||||
|     strategy._load_strategy('NotFoundStrategy') | ||||
|  | ||||
|     error_msg = "Impossible to load Strategy 'user_data/strategies/{}.py'. This file does not " \ | ||||
|                 "exist or contains Python code errors".format('NotFoundStrategy') | ||||
|     assert ('test_strategy', logging.ERROR, error_msg) in caplog.record_tuples | ||||
|     assert ('freqtrade.strategy.strategy', logging.ERROR, error_msg) in caplog.record_tuples | ||||
|  | ||||
|  | ||||
| def test_strategy(result): | ||||
|   | ||||
| @@ -1,97 +0,0 @@ | ||||
| """ | ||||
| Unit test file for logger.py | ||||
| """ | ||||
|  | ||||
| import logging | ||||
|  | ||||
| from freqtrade.logger import Logger | ||||
|  | ||||
|  | ||||
| def test_logger_object() -> None: | ||||
|     """ | ||||
|     Test the Constants object has the mandatory Constants | ||||
|     :return: None | ||||
|     """ | ||||
|     logger = Logger() | ||||
|     assert logger.name == '' | ||||
|     assert logger.level == 20 | ||||
|     assert logger.level is logging.INFO | ||||
|     assert hasattr(logger, 'get_logger') | ||||
|  | ||||
|     logger = Logger(name='Foo', level=logging.WARNING) | ||||
|     assert logger.name == 'Foo' | ||||
|     assert logger.name != '' | ||||
|     assert logger.level == 30 | ||||
|     assert logger.level is logging.WARNING | ||||
|  | ||||
|  | ||||
| def test_get_logger() -> None: | ||||
|     """ | ||||
|     Test Logger.get_logger() and Logger._init_logger() | ||||
|     :return: None | ||||
|     """ | ||||
|     logger = Logger(name='get_logger', level=logging.WARNING) | ||||
|     get_logger = logger.get_logger() | ||||
|     assert logger.logger is get_logger | ||||
|     assert get_logger is not None | ||||
|     assert hasattr(get_logger, 'debug') | ||||
|     assert hasattr(get_logger, 'info') | ||||
|     assert hasattr(get_logger, 'warning') | ||||
|     assert hasattr(get_logger, 'critical') | ||||
|     assert hasattr(get_logger, 'exception') | ||||
|  | ||||
|  | ||||
| def test_set_name() -> None: | ||||
|     """ | ||||
|     Test Logger.set_name() | ||||
|     :return: None | ||||
|     """ | ||||
|     logger = Logger(name='set_name') | ||||
|     assert logger.name == 'set_name' | ||||
|  | ||||
|     logger.set_name('set_name_new') | ||||
|     assert logger.name == 'set_name_new' | ||||
|  | ||||
|  | ||||
| def test_set_level() -> None: | ||||
|     """ | ||||
|     Test Logger.set_name() | ||||
|     :return: None | ||||
|     """ | ||||
|     logger = Logger(name='Foo', level=logging.WARNING) | ||||
|     assert logger.level == logging.WARNING | ||||
|     assert logger.get_logger().level == logging.WARNING | ||||
|  | ||||
|     logger.set_level(logging.INFO) | ||||
|     assert logger.level == logging.INFO | ||||
|     assert logger.get_logger().level == logging.INFO | ||||
|  | ||||
|  | ||||
| def test_sending_msg(caplog) -> None: | ||||
|     """ | ||||
|     Test send a logging message | ||||
|     :return: None | ||||
|     """ | ||||
|     logger = Logger(name='sending_msg', level=logging.WARNING).get_logger() | ||||
|  | ||||
|     logger.info('I am an INFO message') | ||||
|     assert('sending_msg', logging.INFO, 'I am an INFO message') not in caplog.record_tuples | ||||
|  | ||||
|     logger.warning('I am an WARNING message') | ||||
|     assert ('sending_msg', logging.WARNING, 'I am an WARNING message') in caplog.record_tuples | ||||
|  | ||||
|  | ||||
| def test_set_format(caplog) -> None: | ||||
|     """ | ||||
|     Test Logger.set_format() | ||||
|     :return: None | ||||
|     """ | ||||
|     log = Logger(name='set_format') | ||||
|     logger = log.get_logger() | ||||
|  | ||||
|     logger.info('I am the first message') | ||||
|     assert ('set_format', logging.INFO, 'I am the first message') in caplog.record_tuples | ||||
|  | ||||
|     log.set_format(log_format='%(message)s', propagate=True) | ||||
|     logger.info('I am the second message') | ||||
|     assert ('set_format', logging.INFO, 'I am the second message') in caplog.record_tuples | ||||
| @@ -11,7 +11,7 @@ Optional Cli parameters | ||||
| --timerange: specify what timerange of data to use. | ||||
| -l / --live: Live, to download the latest ticker for the pair | ||||
| """ | ||||
|  | ||||
| import logging | ||||
| import sys | ||||
| from argparse import Namespace | ||||
|  | ||||
| @@ -24,11 +24,10 @@ import plotly.graph_objs as go | ||||
| from freqtrade.arguments import Arguments | ||||
| from freqtrade.analyze import Analyze | ||||
| from freqtrade import exchange | ||||
| from freqtrade.logger import Logger | ||||
| import freqtrade.optimize as optimize | ||||
|  | ||||
|  | ||||
| logger = Logger(name="Graph dataframe").get_logger() | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| def plot_analyzed_dataframe(args: Namespace) -> None: | ||||
|   | ||||
| @@ -10,7 +10,7 @@ Optional Cli parameters | ||||
| -s / --strategy: strategy to use | ||||
| --timerange: specify what timerange of data to use. | ||||
| """ | ||||
|  | ||||
| import logging | ||||
| import sys | ||||
| import json | ||||
| from argparse import Namespace | ||||
| @@ -24,13 +24,12 @@ import plotly.graph_objs as go | ||||
| from freqtrade.arguments import Arguments | ||||
| from freqtrade.configuration import Configuration | ||||
| from freqtrade.analyze import Analyze | ||||
| from freqtrade.logger import Logger | ||||
|  | ||||
| import freqtrade.optimize as optimize | ||||
| import freqtrade.misc as misc | ||||
|  | ||||
|  | ||||
| logger = Logger(name="Graph profits").get_logger() | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| # data:: [ pair,      profit-%,  enter,         exit,        time, duration] | ||||
|   | ||||
		Reference in New Issue
	
	Block a user