diff --git a/freqtrade/analyze.py b/freqtrade/analyze.py index b62d8240e..076d3d265 100644 --- a/freqtrade/analyze.py +++ b/freqtrade/analyze.py @@ -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__, 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 diff --git a/freqtrade/configuration.py b/freqtrade/configuration.py index d4f73a086..1314f624e 100644 --- a/freqtrade/configuration.py +++ b/freqtrade/configuration.py @@ -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__) - 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 ) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 48212b865..3972a4382 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -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__, 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 diff --git a/freqtrade/main.py b/freqtrade/main.py index 511270a60..d62c92b9f 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -8,11 +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 = logging.getLogger('freqtrade') def main(sysargv: List[str]) -> None: @@ -26,28 +26,20 @@ def main(sysargv: List[str]) -> None: ) args = arguments.get_parsed_arg() - logger = Logger(__name__, level=args.loglevel).get_logger() - # A subcommand has been issued. # Means if Backtesting or Hyperopt have been called we exit the bot if hasattr(args, 'func'): 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: diff --git a/freqtrade/optimize/__init__.py b/freqtrade/optimize/__init__.py index 23e8f19ad..fff4b72d9 100644 --- a/freqtrade/optimize/__init__.py +++ b/freqtrade/optimize/__init__.py @@ -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__).get_logger() +logger = logging.getLogger(__name__) def trim_tickerlist(tickerlist: List[Dict], timerange: Tuple[Tuple, int, int]) -> List[Dict]: diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 5cedb093c..1d1c33ff9 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -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__, 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' @@ -301,7 +298,6 @@ def start(args: Namespace) -> None: """ # Initialize logger - logger = Logger(__name__).get_logger() logger.info('Starting freqtrade in Backtesting mode') # Initialize configuration diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 551c3e860..3e7b14610 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -31,6 +31,9 @@ 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 +45,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__, 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 +192,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 +210,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: """ @@ -226,7 +224,7 @@ class Hyperopt(Backtesting): results['result'], results['loss'] ) - self.logger.info(log_msg) + logger.info(log_msg) else: print('.', end='') sys.stdout.flush() @@ -511,8 +509,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 +520,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 +528,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 +558,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 +571,7 @@ class Hyperopt(Backtesting): """ Hyperopt SIGINT handler """ - self.logger.info( + logger.info( 'Hyperopt received %s', signal.Signals(sig).name ) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index b052f6775..860d3ba43 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -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__, - 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) diff --git a/freqtrade/rpc/rpc_manager.py b/freqtrade/rpc/rpc_manager.py index 6bd1f69d0..0299c793a 100644 --- a/freqtrade/rpc/rpc_manager.py +++ b/freqtrade/rpc/rpc_manager.py @@ -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__, - 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) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index fce7a81f9..81749d778 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -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 = "
{}
".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 ) diff --git a/freqtrade/strategy/strategy.py b/freqtrade/strategy/strategy.py index c6e6e49f7..7840765bb 100644 --- a/freqtrade/strategy/strategy.py +++ b/freqtrade/strategy/strategy.py @@ -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__).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 diff --git a/freqtrade/tests/strategy/test_strategy.py b/freqtrade/tests/strategy/test_strategy.py index 7ce9ae0ef..a364a59fe 100644 --- a/freqtrade/tests/strategy/test_strategy.py +++ b/freqtrade/tests/strategy/test_strategy.py @@ -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): diff --git a/scripts/plot_dataframe.py b/scripts/plot_dataframe.py index 1bb944c7b..aa61fc172 100755 --- a/scripts/plot_dataframe.py +++ b/scripts/plot_dataframe.py @@ -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__).get_logger() +logger = logging.getLogger(__name__) def plot_analyzed_dataframe(args: Namespace) -> None: diff --git a/scripts/plot_profit.py b/scripts/plot_profit.py index 1c025ce1d..4bf220dd9 100755 --- a/scripts/plot_profit.py +++ b/scripts/plot_profit.py @@ -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__).get_logger() +logger = logging.getLogger(__name__) # data:: [ pair, profit-%, enter, exit, time, duration]