From 60afba559274a13e33408c9f7fb0db24763b9594 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Fri, 22 Mar 2019 20:16:54 +0300 Subject: [PATCH 01/19] move worker stuff to main.py --- freqtrade/freqtradebot.py | 113 +++-------------------- freqtrade/main.py | 190 +++++++++++++++++++++++++++++++------- 2 files changed, 169 insertions(+), 134 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 6f1fb2c99..ef00ba21d 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -7,11 +7,10 @@ import logging import time import traceback from datetime import datetime -from typing import Any, Callable, Dict, List, Optional, Tuple +from typing import Any, Dict, List, Optional, Tuple import arrow from requests.exceptions import RequestException -import sdnotify from freqtrade import (DependencyException, OperationalException, TemporaryError, __version__, constants, persistence) @@ -24,6 +23,7 @@ from freqtrade.resolvers import ExchangeResolver, StrategyResolver, PairListReso from freqtrade.state import State from freqtrade.strategy.interface import SellType, IStrategy from freqtrade.wallets import Wallets +from freqtrade.main import Worker logger = logging.getLogger(__name__) @@ -35,26 +35,18 @@ class FreqtradeBot(object): This is from here the bot start its logic. """ - def __init__(self, config: Dict[str, Any]) -> None: + def __init__(self, config: Dict[str, Any], worker: Worker) -> None: """ Init all variables and objects the bot needs to work :param config: configuration dict, you can use Configuration.get_config() to get the config dict. """ - logger.info( - 'Starting freqtrade %s', - __version__, - ) - - # Init bot states - self.state = State.STOPPED + logger.info('Starting freqtrade %s', __version__) # Init objects self.config = config - - self._sd_notify = sdnotify.SystemdNotifier() if \ - self.config.get('internals', {}).get('sd_notify', False) else None + self._worker: Worker = worker self.strategy: IStrategy = StrategyResolver(self.config).strategy @@ -79,29 +71,16 @@ class FreqtradeBot(object): self.config.get('edge', {}).get('enabled', False) else None self.active_pair_whitelist: List[str] = self.config['exchange']['pair_whitelist'] - self._init_modules() - - # Tell the systemd that we completed initialization phase - if self._sd_notify: - logger.debug("sd_notify: READY=1") - self._sd_notify.notify("READY=1") - - def _init_modules(self) -> None: - """ - Initializes all modules and updates the config - :return: None - """ - # Initialize all modules persistence.init(self.config) - # Set initial application state - initial_state = self.config.get('initial_state') + @property + def state(self) -> State: + return self._worker.state - if initial_state: - self.state = State[initial_state.upper()] - else: - self.state = State.STOPPED + @state.setter + def state(self, value: State): + self._worker.state = value def cleanup(self) -> None: """ @@ -113,75 +92,7 @@ class FreqtradeBot(object): self.rpc.cleanup() persistence.cleanup() - def stopping(self) -> None: - # Tell systemd that we are exiting now - if self._sd_notify: - logger.debug("sd_notify: STOPPING=1") - self._sd_notify.notify("STOPPING=1") - - def reconfigure(self) -> None: - # Tell systemd that we initiated reconfiguring - if self._sd_notify: - logger.debug("sd_notify: RELOADING=1") - self._sd_notify.notify("RELOADING=1") - - def worker(self, old_state: State = None) -> State: - """ - Trading routine that must be run at each loop - :param old_state: the previous service state from the previous call - :return: current service state - """ - # Log state transition - state = self.state - if state != old_state: - self.rpc.send_msg({ - 'type': RPCMessageType.STATUS_NOTIFICATION, - 'status': f'{state.name.lower()}' - }) - logger.info('Changing state to: %s', state.name) - if state == State.RUNNING: - self.rpc.startup_messages(self.config, self.pairlists) - - throttle_secs = self.config.get('internals', {}).get( - 'process_throttle_secs', - constants.PROCESS_THROTTLE_SECS - ) - - if state == State.STOPPED: - # Ping systemd watchdog before sleeping in the stopped state - if self._sd_notify: - logger.debug("sd_notify: WATCHDOG=1\\nSTATUS=State: STOPPED.") - self._sd_notify.notify("WATCHDOG=1\nSTATUS=State: STOPPED.") - - time.sleep(throttle_secs) - - elif state == State.RUNNING: - # Ping systemd watchdog before throttling - if self._sd_notify: - logger.debug("sd_notify: WATCHDOG=1\\nSTATUS=State: RUNNING.") - self._sd_notify.notify("WATCHDOG=1\nSTATUS=State: RUNNING.") - - self._throttle(func=self._process, min_secs=throttle_secs) - - return state - - def _throttle(self, func: Callable[..., Any], min_secs: float, *args, **kwargs) -> Any: - """ - Throttles the given callable that it - takes at least `min_secs` to finish execution. - :param func: Any callable - :param min_secs: minimum execution time in seconds - :return: Any - """ - start = time.time() - result = func(*args, **kwargs) - end = time.time() - duration = max(min_secs - (end - start), 0.0) - logger.debug('Throttling %s for %.2f seconds', func.__name__, duration) - time.sleep(duration) - return result - - def _process(self) -> bool: + def process(self) -> bool: """ Queries the persistence layer for open trades and handles them, otherwise a new trade is created. diff --git a/freqtrade/main.py b/freqtrade/main.py index c41d54f0e..48ae17f9d 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -5,10 +5,12 @@ Read the documentation to know what cli arguments you need. """ import logging import sys +import time from argparse import Namespace -from typing import List +from typing import Any, Callable, List +import sdnotify -from freqtrade import OperationalException +from freqtrade import (constants, OperationalException, __version__) from freqtrade.arguments import Arguments from freqtrade.configuration import Configuration, set_loggers from freqtrade.freqtradebot import FreqtradeBot @@ -35,20 +37,11 @@ def main(sysargv: List[str]) -> None: args.func(args) return - freqtrade = None return_code = 1 try: - # Load and validate configuration - config = Configuration(args, None).get_config() - - # Init the bot - freqtrade = FreqtradeBot(config) - - state = None - while True: - state = freqtrade.worker(old_state=state) - if state == State.RELOAD_CONF: - freqtrade = reconfigure(freqtrade, args) + # Load and run worker + worker = Worker(args) + worker.run() except KeyboardInterrupt: logger.info('SIGINT received, aborting ...') @@ -59,32 +52,163 @@ def main(sysargv: List[str]) -> None: except BaseException: logger.exception('Fatal exception!') finally: - if freqtrade: - freqtrade.stopping() - freqtrade.rpc.send_msg({ - 'type': RPCMessageType.STATUS_NOTIFICATION, - 'status': 'process died' - }) - freqtrade.cleanup() + if worker is not None: + worker.exit() sys.exit(return_code) -def reconfigure(freqtrade: FreqtradeBot, args: Namespace) -> FreqtradeBot: +class Worker(object): """ - Cleans up current instance, reloads the configuration and returns the new instance + Freqtradebot worker class """ - freqtrade.reconfigure() - # Clean up current modules - freqtrade.cleanup() + def __init__(self, args: Namespace) -> None: + """ + Init all variables and objects the bot needs to work + """ + logger.info('Starting worker %s', __version__) - # Create new instance - freqtrade = FreqtradeBot(Configuration(args, None).get_config()) - freqtrade.rpc.send_msg({ - 'type': RPCMessageType.STATUS_NOTIFICATION, - 'status': 'config reloaded' - }) - return freqtrade + self._args = args + self._init() + + # Tell systemd that we completed initialization phase + if self._sd_notify: + logger.debug("sd_notify: READY=1") + self._sd_notify.notify("READY=1") + + def _init(self): + """ + Also called from the _reconfigure() method. + """ + # Load configuration + self._config = Configuration(self._args, None).get_config() + + # Init the instance of the bot + self.freqtrade = FreqtradeBot(self._config, self) + + # Set initial bot state + initial_state = self._config.get('initial_state') + if initial_state: + self._state = State[initial_state.upper()] + else: + self._state = State.STOPPED + + self._throttle_secs = self._config.get('internals', {}).get( + 'process_throttle_secs', + constants.PROCESS_THROTTLE_SECS + ) + + self._sd_notify = sdnotify.SystemdNotifier() if \ + self._config.get('internals', {}).get('sd_notify', False) else None + + @property + def state(self) -> State: + return self._state + + @state.setter + def state(self, value: State): + self._state = value + + def run(self): + state = None + while True: + state = self._worker(old_state=state, throttle_secs=self._throttle_secs) + if state == State.RELOAD_CONF: + self.freqtrade = self._reconfigure() + + def _worker(self, old_state: State, throttle_secs: float) -> State: + """ + Trading routine that must be run at each loop + :param old_state: the previous service state from the previous call + :return: current service state + """ + state = self._state + + # Log state transition + if state != old_state: + self.freqtrade.rpc.send_msg({ + 'type': RPCMessageType.STATUS_NOTIFICATION, + 'status': f'{state.name.lower()}' + }) + logger.info('Changing state to: %s', state.name) + if state == State.RUNNING: + self.freqtrade.rpc.startup_messages(self._config, self.freqtrade.pairlists) + + if state == State.STOPPED: + # Ping systemd watchdog before sleeping in the stopped state + if self._sd_notify: + logger.debug("sd_notify: WATCHDOG=1\\nSTATUS=State: STOPPED.") + self._sd_notify.notify("WATCHDOG=1\nSTATUS=State: STOPPED.") + + time.sleep(throttle_secs) + + elif state == State.RUNNING: + # Ping systemd watchdog before throttling + if self._sd_notify: + logger.debug("sd_notify: WATCHDOG=1\\nSTATUS=State: RUNNING.") + self._sd_notify.notify("WATCHDOG=1\nSTATUS=State: RUNNING.") + + self._throttle(func=self._process, min_secs=throttle_secs) + + return state + + def _throttle(self, func: Callable[..., Any], min_secs: float, *args, **kwargs) -> Any: + """ + Throttles the given callable that it + takes at least `min_secs` to finish execution. + :param func: Any callable + :param min_secs: minimum execution time in seconds + :return: Any + """ + start = time.time() + result = func(*args, **kwargs) + end = time.time() + duration = max(min_secs - (end - start), 0.0) + logger.debug('Throttling %s for %.2f seconds', func.__name__, duration) + time.sleep(duration) + return result + + def _process(self) -> bool: + return self.freqtrade.process() + + def _reconfigure(self): + """ + Cleans up current freqtradebot instance, reloads the configuration and + returns the new instance + """ + # Tell systemd that we initiated reconfiguration + if self._sd_notify: + logger.debug("sd_notify: RELOADING=1") + self._sd_notify.notify("RELOADING=1") + + # Clean up current freqtrade modules + self.freqtrade.cleanup() + + # Load and validate config and create new instance of the bot + self._init() + + self.freqtrade.rpc.send_msg({ + 'type': RPCMessageType.STATUS_NOTIFICATION, + 'status': 'config reloaded' + }) + + # Tell systemd that we completed reconfiguration + if self._sd_notify: + logger.debug("sd_notify: READY=1") + self._sd_notify.notify("READY=1") + + def exit(self): + # Tell systemd that we are exiting now + if self._sd_notify: + logger.debug("sd_notify: STOPPING=1") + self._sd_notify.notify("STOPPING=1") + + if self.freqtrade: + self.freqtrade.rpc.send_msg({ + 'type': RPCMessageType.STATUS_NOTIFICATION, + 'status': 'process died' + }) + self.freqtrade.cleanup() if __name__ == '__main__': From be6836b0ef912541c336e8c7ccdd29de36dce908 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Fri, 22 Mar 2019 21:49:19 +0300 Subject: [PATCH 02/19] resolve python module circular dependency --- freqtrade/main.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/freqtrade/main.py b/freqtrade/main.py index 48ae17f9d..e1b4d80fa 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -13,7 +13,6 @@ import sdnotify from freqtrade import (constants, OperationalException, __version__) from freqtrade.arguments import Arguments from freqtrade.configuration import Configuration, set_loggers -from freqtrade.freqtradebot import FreqtradeBot from freqtrade.state import State from freqtrade.rpc import RPCMessageType @@ -83,6 +82,10 @@ class Worker(object): # Load configuration self._config = Configuration(self._args, None).get_config() + # Import freqtradebot here in order to avoid python circular + # dependency error, damn! + from freqtrade.freqtradebot import FreqtradeBot + # Init the instance of the bot self.freqtrade = FreqtradeBot(self._config, self) From b4488902100408a90beb33acd79bc43fe272235b Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Fri, 22 Mar 2019 22:03:15 +0300 Subject: [PATCH 03/19] test_main.py adjusted (only beginning) --- freqtrade/tests/test_main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/tests/test_main.py b/freqtrade/tests/test_main.py index 51c95a4a9..188b9b9bd 100644 --- a/freqtrade/tests/test_main.py +++ b/freqtrade/tests/test_main.py @@ -8,7 +8,7 @@ import pytest from freqtrade import OperationalException from freqtrade.arguments import Arguments from freqtrade.freqtradebot import FreqtradeBot -from freqtrade.main import main, reconfigure +from freqtrade.main import main, Worker from freqtrade.state import State from freqtrade.tests.conftest import log_has, patch_exchange @@ -126,7 +126,7 @@ def test_main_reload_conf(mocker, default_conf, caplog) -> None: # Raise exception as side effect to avoid endless loop reconfigure_mock = mocker.patch( - 'freqtrade.main.reconfigure', MagicMock(side_effect=Exception) + 'freqtrade.main.Worker._reconfigure', MagicMock(side_effect=Exception) ) with pytest.raises(SystemExit): From e35daf95c04571a4297769e1ac6d1c2f5965b2c3 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Fri, 22 Mar 2019 23:41:48 +0300 Subject: [PATCH 04/19] minor cleanup --- freqtrade/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/main.py b/freqtrade/main.py index e1b4d80fa..923230b7f 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -36,6 +36,7 @@ def main(sysargv: List[str]) -> None: args.func(args) return + worker = None return_code = 1 try: # Load and run worker @@ -51,7 +52,7 @@ def main(sysargv: List[str]) -> None: except BaseException: logger.exception('Fatal exception!') finally: - if worker is not None: + if worker: worker.exit() sys.exit(return_code) From 158cb307f6268c37b711b1bc1ac5d2196bca3647 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sat, 23 Mar 2019 00:20:20 +0300 Subject: [PATCH 05/19] further refactoring of FreqtradeBot.process() --- freqtrade/freqtradebot.py | 71 ++++++++++++++++----------------------- freqtrade/main.py | 22 ++++++++++-- 2 files changed, 49 insertions(+), 44 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index ef00ba21d..784c0b938 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -4,7 +4,6 @@ Freqtrade is the main module of this bot. It contains the class Freqtrade() import copy import logging -import time import traceback from datetime import datetime from typing import Any, Dict, List, Optional, Tuple @@ -13,7 +12,7 @@ import arrow from requests.exceptions import RequestException from freqtrade import (DependencyException, OperationalException, - TemporaryError, __version__, constants, persistence) + __version__, constants, persistence) from freqtrade.data.converter import order_book_to_dataframe from freqtrade.data.dataprovider import DataProvider from freqtrade.edge import Edge @@ -99,55 +98,43 @@ class FreqtradeBot(object): :return: True if one or more trades has been created or closed, False otherwise """ state_changed = False - try: - # Check whether markets have to be reloaded - self.exchange._reload_markets() - # Refresh whitelist - self.pairlists.refresh_pairlist() - self.active_pair_whitelist = self.pairlists.whitelist + # Check whether markets have to be reloaded + self.exchange._reload_markets() - # Calculating Edge positioning - if self.edge: - self.edge.calculate() - self.active_pair_whitelist = self.edge.adjust(self.active_pair_whitelist) + # Refresh whitelist + self.pairlists.refresh_pairlist() + self.active_pair_whitelist = self.pairlists.whitelist - # Query trades from persistence layer - trades = Trade.get_open_trades() + # Calculating Edge positioning + if self.edge: + self.edge.calculate() + self.active_pair_whitelist = self.edge.adjust(self.active_pair_whitelist) - # Extend active-pair whitelist with pairs from open trades - # It ensures that tickers are downloaded for open trades - self._extend_whitelist_with_trades(self.active_pair_whitelist, trades) + # Query trades from persistence layer + trades = Trade.get_open_trades() - # Refreshing candles - self.dataprovider.refresh(self._create_pair_whitelist(self.active_pair_whitelist), - self.strategy.informative_pairs()) + # Extend active-pair whitelist with pairs from open trades + # It ensures that tickers are downloaded for open trades + self._extend_whitelist_with_trades(self.active_pair_whitelist, trades) - # First process current opened trades - for trade in trades: - state_changed |= self.process_maybe_execute_sell(trade) + # Refreshing candles + self.dataprovider.refresh(self._create_pair_whitelist(self.active_pair_whitelist), + self.strategy.informative_pairs()) - # Then looking for buy opportunities - if len(trades) < self.config['max_open_trades']: - state_changed = self.process_maybe_execute_buy() + # First process current opened trades + for trade in trades: + state_changed |= self.process_maybe_execute_sell(trade) - if 'unfilledtimeout' in self.config: - # Check and handle any timed out open orders - self.check_handle_timedout() - Trade.session.flush() + # Then looking for buy opportunities + if len(trades) < self.config['max_open_trades']: + state_changed = self.process_maybe_execute_buy() + + if 'unfilledtimeout' in self.config: + # Check and handle any timed out open orders + self.check_handle_timedout() + Trade.session.flush() - except TemporaryError as error: - logger.warning(f"Error: {error}, retrying in {constants.RETRY_TIMEOUT} seconds...") - time.sleep(constants.RETRY_TIMEOUT) - except OperationalException: - tb = traceback.format_exc() - hint = 'Issue `/start` if you think it is safe to restart.' - self.rpc.send_msg({ - 'type': RPCMessageType.STATUS_NOTIFICATION, - 'status': f'OperationalException:\n```\n{tb}```{hint}' - }) - logger.exception('OperationalException. Stopping trader ...') - self.state = State.STOPPED return state_changed def _extend_whitelist_with_trades(self, whitelist: List[str], trades: List[Any]): diff --git a/freqtrade/main.py b/freqtrade/main.py index 923230b7f..9331206fc 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -6,11 +6,13 @@ Read the documentation to know what cli arguments you need. import logging import sys import time +import traceback from argparse import Namespace from typing import Any, Callable, List import sdnotify -from freqtrade import (constants, OperationalException, __version__) +from freqtrade import (constants, OperationalException, TemporaryError, + __version__) from freqtrade.arguments import Arguments from freqtrade.configuration import Configuration, set_loggers from freqtrade.state import State @@ -173,7 +175,23 @@ class Worker(object): return result def _process(self) -> bool: - return self.freqtrade.process() + state_changed = False + try: + state_changed = self.freqtrade.process() + + except TemporaryError as error: + logger.warning(f"Error: {error}, retrying in {constants.RETRY_TIMEOUT} seconds...") + time.sleep(constants.RETRY_TIMEOUT) + except OperationalException: + tb = traceback.format_exc() + hint = 'Issue `/start` if you think it is safe to restart.' + self.freqtrade.rpc.send_msg({ + 'type': RPCMessageType.STATUS_NOTIFICATION, + 'status': f'OperationalException:\n```\n{tb}```{hint}' + }) + logger.exception('OperationalException. Stopping trader ...') + self.state = State.STOPPED + return state_changed def _reconfigure(self): """ From c8b0c9af0a5e28b18d7a8f604a5f3d3db836e992 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Mon, 25 Mar 2019 17:45:03 +0300 Subject: [PATCH 06/19] Worker moved to new worker.py --- freqtrade/freqtradebot.py | 2 +- freqtrade/main.py | 190 ++----------------------------------- freqtrade/worker.py | 192 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 199 insertions(+), 185 deletions(-) create mode 100755 freqtrade/worker.py diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 784c0b938..94c1bca8a 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -22,7 +22,7 @@ from freqtrade.resolvers import ExchangeResolver, StrategyResolver, PairListReso from freqtrade.state import State from freqtrade.strategy.interface import SellType, IStrategy from freqtrade.wallets import Wallets -from freqtrade.main import Worker +from freqtrade.worker import Worker logger = logging.getLogger(__name__) diff --git a/freqtrade/main.py b/freqtrade/main.py index 9331206fc..877e2921d 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -5,18 +5,14 @@ Read the documentation to know what cli arguments you need. """ import logging import sys -import time -import traceback from argparse import Namespace -from typing import Any, Callable, List -import sdnotify +from typing import List -from freqtrade import (constants, OperationalException, TemporaryError, - __version__) +from freqtrade import OperationalException from freqtrade.arguments import Arguments -from freqtrade.configuration import Configuration, set_loggers -from freqtrade.state import State -from freqtrade.rpc import RPCMessageType +from freqtrade.configuration import set_loggers +from freqtrade.worker import Worker + logger = logging.getLogger('freqtrade') @@ -30,7 +26,7 @@ def main(sysargv: List[str]) -> None: sysargv, 'Free, open source crypto trading bot' ) - args = arguments.get_parsed_arg() + args: Namespace = arguments.get_parsed_arg() # A subcommand has been issued. # Means if Backtesting or Hyperopt have been called we exit the bot @@ -59,180 +55,6 @@ def main(sysargv: List[str]) -> None: sys.exit(return_code) -class Worker(object): - """ - Freqtradebot worker class - """ - - def __init__(self, args: Namespace) -> None: - """ - Init all variables and objects the bot needs to work - """ - logger.info('Starting worker %s', __version__) - - self._args = args - self._init() - - # Tell systemd that we completed initialization phase - if self._sd_notify: - logger.debug("sd_notify: READY=1") - self._sd_notify.notify("READY=1") - - def _init(self): - """ - Also called from the _reconfigure() method. - """ - # Load configuration - self._config = Configuration(self._args, None).get_config() - - # Import freqtradebot here in order to avoid python circular - # dependency error, damn! - from freqtrade.freqtradebot import FreqtradeBot - - # Init the instance of the bot - self.freqtrade = FreqtradeBot(self._config, self) - - # Set initial bot state - initial_state = self._config.get('initial_state') - if initial_state: - self._state = State[initial_state.upper()] - else: - self._state = State.STOPPED - - self._throttle_secs = self._config.get('internals', {}).get( - 'process_throttle_secs', - constants.PROCESS_THROTTLE_SECS - ) - - self._sd_notify = sdnotify.SystemdNotifier() if \ - self._config.get('internals', {}).get('sd_notify', False) else None - - @property - def state(self) -> State: - return self._state - - @state.setter - def state(self, value: State): - self._state = value - - def run(self): - state = None - while True: - state = self._worker(old_state=state, throttle_secs=self._throttle_secs) - if state == State.RELOAD_CONF: - self.freqtrade = self._reconfigure() - - def _worker(self, old_state: State, throttle_secs: float) -> State: - """ - Trading routine that must be run at each loop - :param old_state: the previous service state from the previous call - :return: current service state - """ - state = self._state - - # Log state transition - if state != old_state: - self.freqtrade.rpc.send_msg({ - 'type': RPCMessageType.STATUS_NOTIFICATION, - 'status': f'{state.name.lower()}' - }) - logger.info('Changing state to: %s', state.name) - if state == State.RUNNING: - self.freqtrade.rpc.startup_messages(self._config, self.freqtrade.pairlists) - - if state == State.STOPPED: - # Ping systemd watchdog before sleeping in the stopped state - if self._sd_notify: - logger.debug("sd_notify: WATCHDOG=1\\nSTATUS=State: STOPPED.") - self._sd_notify.notify("WATCHDOG=1\nSTATUS=State: STOPPED.") - - time.sleep(throttle_secs) - - elif state == State.RUNNING: - # Ping systemd watchdog before throttling - if self._sd_notify: - logger.debug("sd_notify: WATCHDOG=1\\nSTATUS=State: RUNNING.") - self._sd_notify.notify("WATCHDOG=1\nSTATUS=State: RUNNING.") - - self._throttle(func=self._process, min_secs=throttle_secs) - - return state - - def _throttle(self, func: Callable[..., Any], min_secs: float, *args, **kwargs) -> Any: - """ - Throttles the given callable that it - takes at least `min_secs` to finish execution. - :param func: Any callable - :param min_secs: minimum execution time in seconds - :return: Any - """ - start = time.time() - result = func(*args, **kwargs) - end = time.time() - duration = max(min_secs - (end - start), 0.0) - logger.debug('Throttling %s for %.2f seconds', func.__name__, duration) - time.sleep(duration) - return result - - def _process(self) -> bool: - state_changed = False - try: - state_changed = self.freqtrade.process() - - except TemporaryError as error: - logger.warning(f"Error: {error}, retrying in {constants.RETRY_TIMEOUT} seconds...") - time.sleep(constants.RETRY_TIMEOUT) - except OperationalException: - tb = traceback.format_exc() - hint = 'Issue `/start` if you think it is safe to restart.' - self.freqtrade.rpc.send_msg({ - 'type': RPCMessageType.STATUS_NOTIFICATION, - 'status': f'OperationalException:\n```\n{tb}```{hint}' - }) - logger.exception('OperationalException. Stopping trader ...') - self.state = State.STOPPED - return state_changed - - def _reconfigure(self): - """ - Cleans up current freqtradebot instance, reloads the configuration and - returns the new instance - """ - # Tell systemd that we initiated reconfiguration - if self._sd_notify: - logger.debug("sd_notify: RELOADING=1") - self._sd_notify.notify("RELOADING=1") - - # Clean up current freqtrade modules - self.freqtrade.cleanup() - - # Load and validate config and create new instance of the bot - self._init() - - self.freqtrade.rpc.send_msg({ - 'type': RPCMessageType.STATUS_NOTIFICATION, - 'status': 'config reloaded' - }) - - # Tell systemd that we completed reconfiguration - if self._sd_notify: - logger.debug("sd_notify: READY=1") - self._sd_notify.notify("READY=1") - - def exit(self): - # Tell systemd that we are exiting now - if self._sd_notify: - logger.debug("sd_notify: STOPPING=1") - self._sd_notify.notify("STOPPING=1") - - if self.freqtrade: - self.freqtrade.rpc.send_msg({ - 'type': RPCMessageType.STATUS_NOTIFICATION, - 'status': 'process died' - }) - self.freqtrade.cleanup() - - if __name__ == '__main__': set_loggers() main(sys.argv[1:]) diff --git a/freqtrade/worker.py b/freqtrade/worker.py new file mode 100755 index 000000000..9a7e67424 --- /dev/null +++ b/freqtrade/worker.py @@ -0,0 +1,192 @@ +""" +Main Freqtrade worker class. +""" +import logging +import time +import traceback +from argparse import Namespace +from typing import Any, Callable +import sdnotify + +from freqtrade import (constants, OperationalException, TemporaryError, + __version__) +from freqtrade.configuration import Configuration +from freqtrade.state import State +from freqtrade.rpc import RPCMessageType + + +logger = logging.getLogger(__name__) + + +class Worker(object): + """ + Freqtradebot worker class + """ + + def __init__(self, args: Namespace) -> None: + """ + Init all variables and objects the bot needs to work + """ + logger.info('Starting worker %s', __version__) + + self._args = args + self._init() + + # Tell systemd that we completed initialization phase + if self._sd_notify: + logger.debug("sd_notify: READY=1") + self._sd_notify.notify("READY=1") + + def _init(self): + """ + Also called from the _reconfigure() method. + """ + # Load configuration + self._config = Configuration(self._args, None).get_config() + + # Import freqtradebot here in order to avoid python circular + # dependency error, damn! + from freqtrade.freqtradebot import FreqtradeBot + + # Init the instance of the bot + self.freqtrade = FreqtradeBot(self._config, self) + + # Set initial bot state + initial_state = self._config.get('initial_state') + if initial_state: + self._state = State[initial_state.upper()] + else: + self._state = State.STOPPED + + self._throttle_secs = self._config.get('internals', {}).get( + 'process_throttle_secs', + constants.PROCESS_THROTTLE_SECS + ) + + self._sd_notify = sdnotify.SystemdNotifier() if \ + self._config.get('internals', {}).get('sd_notify', False) else None + + @property + def state(self) -> State: + return self._state + + @state.setter + def state(self, value: State): + self._state = value + + def run(self): + state = None + while True: + state = self._worker(old_state=state, throttle_secs=self._throttle_secs) + if state == State.RELOAD_CONF: + self.freqtrade = self._reconfigure() + + def _worker(self, old_state: State, throttle_secs: float) -> State: + """ + Trading routine that must be run at each loop + :param old_state: the previous service state from the previous call + :return: current service state + """ + state = self._state + + # Log state transition + if state != old_state: + self.freqtrade.rpc.send_msg({ + 'type': RPCMessageType.STATUS_NOTIFICATION, + 'status': f'{state.name.lower()}' + }) + logger.info('Changing state to: %s', state.name) + if state == State.RUNNING: + self.freqtrade.rpc.startup_messages(self._config, self.freqtrade.pairlists) + + if state == State.STOPPED: + # Ping systemd watchdog before sleeping in the stopped state + if self._sd_notify: + logger.debug("sd_notify: WATCHDOG=1\\nSTATUS=State: STOPPED.") + self._sd_notify.notify("WATCHDOG=1\nSTATUS=State: STOPPED.") + + time.sleep(throttle_secs) + + elif state == State.RUNNING: + # Ping systemd watchdog before throttling + if self._sd_notify: + logger.debug("sd_notify: WATCHDOG=1\\nSTATUS=State: RUNNING.") + self._sd_notify.notify("WATCHDOG=1\nSTATUS=State: RUNNING.") + + self._throttle(func=self._process, min_secs=throttle_secs) + + return state + + def _throttle(self, func: Callable[..., Any], min_secs: float, *args, **kwargs) -> Any: + """ + Throttles the given callable that it + takes at least `min_secs` to finish execution. + :param func: Any callable + :param min_secs: minimum execution time in seconds + :return: Any + """ + start = time.time() + result = func(*args, **kwargs) + end = time.time() + duration = max(min_secs - (end - start), 0.0) + logger.debug('Throttling %s for %.2f seconds', func.__name__, duration) + time.sleep(duration) + return result + + def _process(self) -> bool: + state_changed = False + try: + state_changed = self.freqtrade.process() + + except TemporaryError as error: + logger.warning(f"Error: {error}, retrying in {constants.RETRY_TIMEOUT} seconds...") + time.sleep(constants.RETRY_TIMEOUT) + except OperationalException: + tb = traceback.format_exc() + hint = 'Issue `/start` if you think it is safe to restart.' + self.freqtrade.rpc.send_msg({ + 'type': RPCMessageType.STATUS_NOTIFICATION, + 'status': f'OperationalException:\n```\n{tb}```{hint}' + }) + logger.exception('OperationalException. Stopping trader ...') + self.state = State.STOPPED + return state_changed + + def _reconfigure(self): + """ + Cleans up current freqtradebot instance, reloads the configuration and + returns the new instance + """ + # Tell systemd that we initiated reconfiguration + if self._sd_notify: + logger.debug("sd_notify: RELOADING=1") + self._sd_notify.notify("RELOADING=1") + + # Clean up current freqtrade modules + self.freqtrade.cleanup() + + # Load and validate config and create new instance of the bot + self._init() + + self.freqtrade.rpc.send_msg({ + 'type': RPCMessageType.STATUS_NOTIFICATION, + 'status': 'config reloaded' + }) + + # Tell systemd that we completed reconfiguration + if self._sd_notify: + logger.debug("sd_notify: READY=1") + self._sd_notify.notify("READY=1") + + def exit(self): + # Tell systemd that we are exiting now + if self._sd_notify: + logger.debug("sd_notify: STOPPING=1") + self._sd_notify.notify("STOPPING=1") + + if self.freqtrade: + self.freqtrade.rpc.send_msg({ + 'type': RPCMessageType.STATUS_NOTIFICATION, + 'status': 'process died' + }) + self.freqtrade.cleanup() From 5161e1abb3fd2fa5d5ea69751447d14cd3c58007 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 26 Mar 2019 11:07:02 +0300 Subject: [PATCH 07/19] Allow to pass config into worker, as it's used in the tests --- freqtrade/freqtradebot.py | 8 ++++++-- freqtrade/worker.py | 24 ++++++++++++++---------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 94c1bca8a..7c92ac29a 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -34,7 +34,7 @@ class FreqtradeBot(object): This is from here the bot start its logic. """ - def __init__(self, config: Dict[str, Any], worker: Worker) -> None: + def __init__(self, config: Dict[str, Any], worker: Optional[Worker] = None) -> None: """ Init all variables and objects the bot needs to work :param config: configuration dict, you can use Configuration.get_config() @@ -45,7 +45,7 @@ class FreqtradeBot(object): # Init objects self.config = config - self._worker: Worker = worker + self._worker = worker self.strategy: IStrategy = StrategyResolver(self.config).strategy @@ -75,10 +75,14 @@ class FreqtradeBot(object): @property def state(self) -> State: + if self._worker is None: + raise DependencyException("No Worker is available") return self._worker.state @state.setter def state(self, value: State): + if self._worker is None: + raise DependencyException("No Worker is available") self._worker.state = value def cleanup(self) -> None: diff --git a/freqtrade/worker.py b/freqtrade/worker.py index 9a7e67424..a6fba1460 100755 --- a/freqtrade/worker.py +++ b/freqtrade/worker.py @@ -5,7 +5,7 @@ import logging import time import traceback from argparse import Namespace -from typing import Any, Callable +from typing import Any, Callable, Optional import sdnotify from freqtrade import (constants, OperationalException, TemporaryError, @@ -23,26 +23,28 @@ class Worker(object): Freqtradebot worker class """ - def __init__(self, args: Namespace) -> None: + def __init__(self, args: Optional[Namespace] = None, config = None) -> None: """ Init all variables and objects the bot needs to work """ logger.info('Starting worker %s', __version__) self._args = args - self._init() + self._config = config + self._init(False) # Tell systemd that we completed initialization phase if self._sd_notify: logger.debug("sd_notify: READY=1") self._sd_notify.notify("READY=1") - def _init(self): + def _init(self, reconfig: bool): """ - Also called from the _reconfigure() method. + Also called from the _reconfigure() method (with reconfig=True). """ - # Load configuration - self._config = Configuration(self._args, None).get_config() + if reconfig or self._config is None: + # Load configuration + self._config = Configuration(self._args, None).get_config() # Import freqtradebot here in order to avoid python circular # dependency error, damn! @@ -77,17 +79,19 @@ class Worker(object): def run(self): state = None while True: - state = self._worker(old_state=state, throttle_secs=self._throttle_secs) + state = self._worker(old_state=state) if state == State.RELOAD_CONF: self.freqtrade = self._reconfigure() - def _worker(self, old_state: State, throttle_secs: float) -> State: + def _worker(self, old_state: State, throttle_secs: Optional[float] = None) -> State: """ Trading routine that must be run at each loop :param old_state: the previous service state from the previous call :return: current service state """ state = self._state + if throttle_secs is None: + throttle_secs = self._throttle_secs # Log state transition if state != old_state: @@ -166,7 +170,7 @@ class Worker(object): self.freqtrade.cleanup() # Load and validate config and create new instance of the bot - self._init() + self._init(True) self.freqtrade.rpc.send_msg({ 'type': RPCMessageType.STATUS_NOTIFICATION, From 5ccd618189f2bc3a303148a487f114d4b5df66be Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 26 Mar 2019 11:07:24 +0300 Subject: [PATCH 08/19] tests adjusted --- freqtrade/tests/conftest.py | 11 +- freqtrade/tests/rpc/test_rpc.py | 74 ++++++++++--- freqtrade/tests/rpc/test_rpc_telegram.py | 134 +++++++++++++++-------- freqtrade/tests/test_freqtradebot.py | 93 ++++++++++------ freqtrade/tests/test_main.py | 45 +++----- 5 files changed, 228 insertions(+), 129 deletions(-) diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py index 26262cb4b..772602f6d 100644 --- a/freqtrade/tests/conftest.py +++ b/freqtrade/tests/conftest.py @@ -15,6 +15,7 @@ from freqtrade import constants from freqtrade.data.converter import parse_ticker_dataframe from freqtrade.exchange import Exchange from freqtrade.edge import Edge, PairInfo +from freqtrade.worker import Worker from freqtrade.freqtradebot import FreqtradeBot from freqtrade.resolvers import ExchangeResolver @@ -88,7 +89,7 @@ def get_patched_edge(mocker, config) -> Edge: # Functions for recurrent object patching -def get_patched_freqtradebot(mocker, config) -> FreqtradeBot: +def patch_freqtradebot(mocker, config) -> None: """ This function patch _init_modules() to not call dependencies :param mocker: a Mocker object to apply patches @@ -102,9 +103,17 @@ def get_patched_freqtradebot(mocker, config) -> FreqtradeBot: mocker.patch('freqtrade.freqtradebot.RPCManager._init', MagicMock()) mocker.patch('freqtrade.freqtradebot.RPCManager.send_msg', MagicMock()) + +def get_patched_freqtradebot(mocker, config) -> FreqtradeBot: + patch_freqtradebot(mocker, config) return FreqtradeBot(config) +def get_patched_worker(mocker, config) -> Worker: + patch_freqtradebot(mocker, config) + return Worker(args=None, config=config) + + def patch_coinmarketcap(mocker, value: Optional[Dict[str, float]] = None) -> None: """ Mocker to coinmarketcap to speed up tests diff --git a/freqtrade/tests/rpc/test_rpc.py b/freqtrade/tests/rpc/test_rpc.py index baddc0685..69b428693 100644 --- a/freqtrade/tests/rpc/test_rpc.py +++ b/freqtrade/tests/rpc/test_rpc.py @@ -8,6 +8,7 @@ import pytest from numpy import isnan from freqtrade import TemporaryError, DependencyException +from freqtrade.worker import Worker from freqtrade.freqtradebot import FreqtradeBot from freqtrade.persistence import Trade from freqtrade.rpc import RPC, RPCException @@ -37,7 +38,9 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None: markets=PropertyMock(return_value=markets) ) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -93,7 +96,9 @@ def test_rpc_status_table(default_conf, ticker, fee, markets, mocker) -> None: markets=PropertyMock(return_value=markets) ) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -129,7 +134,9 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee, markets=PropertyMock(return_value=markets) ) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] @@ -183,7 +190,9 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, markets=PropertyMock(return_value=markets) ) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] @@ -271,7 +280,9 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, markets, markets=PropertyMock(return_value=markets) ) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] @@ -340,7 +351,9 @@ def test_rpc_balance_handle(default_conf, mocker): get_ticker=MagicMock(side_effect=TemporaryError('Could not load ticker due to xxx')) ) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter() @@ -368,7 +381,9 @@ def test_rpc_start(mocker, default_conf) -> None: get_ticker=MagicMock() ) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) freqtradebot.state = State.STOPPED @@ -391,7 +406,9 @@ def test_rpc_stop(mocker, default_conf) -> None: get_ticker=MagicMock() ) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -415,7 +432,9 @@ def test_rpc_stopbuy(mocker, default_conf) -> None: get_ticker=MagicMock() ) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -447,7 +466,9 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker, markets) -> None: markets=PropertyMock(return_value=markets) ) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -539,7 +560,9 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee, markets=PropertyMock(return_value=markets) ) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -575,7 +598,9 @@ def test_rpc_count(mocker, default_conf, ticker, fee, markets) -> None: markets=PropertyMock(return_value=markets) ) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -605,7 +630,9 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, markets, limit_buy_order buy=buy_mm ) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) pair = 'ETH/BTC' @@ -630,7 +657,10 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, markets, limit_buy_order # Test not buying default_conf['stake_amount'] = 0.0000001 - freqtradebot = FreqtradeBot(default_conf) + + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) pair = 'TKN/BTC' @@ -645,7 +675,9 @@ def test_rpcforcebuy_stopped(mocker, default_conf) -> None: patch_exchange(mocker) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) pair = 'ETH/BTC' @@ -658,7 +690,9 @@ def test_rpcforcebuy_disabled(mocker, default_conf) -> None: patch_exchange(mocker) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) pair = 'ETH/BTC' @@ -671,7 +705,9 @@ def test_rpc_whitelist(mocker, default_conf) -> None: patch_exchange(mocker) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + rpc = RPC(freqtradebot) ret = rpc._rpc_whitelist() assert ret['method'] == 'StaticPairList' @@ -687,7 +723,9 @@ def test_rpc_whitelist_dynamic(mocker, default_conf) -> None: mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + rpc = RPC(freqtradebot) ret = rpc._rpc_whitelist() assert ret['method'] == 'VolumePairList' diff --git a/freqtrade/tests/rpc/test_rpc_telegram.py b/freqtrade/tests/rpc/test_rpc_telegram.py index 8e8d1f1bb..1452267ea 100644 --- a/freqtrade/tests/rpc/test_rpc_telegram.py +++ b/freqtrade/tests/rpc/test_rpc_telegram.py @@ -13,13 +13,14 @@ from telegram import Chat, Message, Update from telegram.error import NetworkError from freqtrade import __version__ +from freqtrade.worker import Worker from freqtrade.freqtradebot import FreqtradeBot from freqtrade.persistence import Trade from freqtrade.rpc import RPCMessageType from freqtrade.rpc.telegram import Telegram, authorized_only from freqtrade.strategy.interface import SellType from freqtrade.state import State -from freqtrade.tests.conftest import (get_patched_freqtradebot, log_has, +from freqtrade.tests.conftest import (get_patched_freqtradebot, get_patched_worker, log_has, patch_exchange) from freqtrade.tests.test_freqtradebot import patch_get_signal from freqtrade.tests.conftest import patch_coinmarketcap @@ -98,7 +99,10 @@ def test_authorized_only(default_conf, mocker, caplog) -> None: update.message = Message(randint(1, 100), 0, datetime.utcnow(), chat) default_conf['telegram']['enabled'] = False - bot = FreqtradeBot(default_conf) + + worker = Worker(args=None, config=default_conf) + bot = worker.freqtrade + patch_get_signal(bot, (True, False)) dummy = DummyCls(bot) dummy.dummy_handler(bot=MagicMock(), update=update) @@ -125,7 +129,10 @@ def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None: update.message = Message(randint(1, 100), 0, datetime.utcnow(), chat) default_conf['telegram']['enabled'] = False - bot = FreqtradeBot(default_conf) + + worker = Worker(args=None, config=default_conf) + bot = worker.freqtrade + patch_get_signal(bot, (True, False)) dummy = DummyCls(bot) dummy.dummy_handler(bot=MagicMock(), update=update) @@ -153,7 +160,9 @@ def test_authorized_only_exception(default_conf, mocker, caplog) -> None: default_conf['telegram']['enabled'] = False - bot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + bot = worker.freqtrade + patch_get_signal(bot, (True, False)) dummy = DummyCls(bot) @@ -208,7 +217,9 @@ def test_status(default_conf, update, mocker, fee, ticker, markets) -> None: ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -244,19 +255,21 @@ def test_status_handle(default_conf, update, ticker, fee, markets, mocker) -> No ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) - freqtradebot.state = State.STOPPED + worker.state = State.STOPPED # Status is also enabled when stopped telegram._status(bot=MagicMock(), update=update) assert msg_mock.call_count == 1 assert 'no active trade' in msg_mock.call_args_list[0][0][0] msg_mock.reset_mock() - freqtradebot.state = State.RUNNING + worker.state = State.RUNNING telegram._status(bot=MagicMock(), update=update) assert msg_mock.call_count == 1 assert 'no active trade' in msg_mock.call_args_list[0][0][0] @@ -290,19 +303,22 @@ def test_status_table_handle(default_conf, update, ticker, fee, markets, mocker) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) default_conf['stake_amount'] = 15.0 - freqtradebot = FreqtradeBot(default_conf) + + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) - freqtradebot.state = State.STOPPED + worker.state = State.STOPPED # Status table is also enabled when stopped telegram._status_table(bot=MagicMock(), update=update) assert msg_mock.call_count == 1 assert 'no active order' in msg_mock.call_args_list[0][0][0] msg_mock.reset_mock() - freqtradebot.state = State.RUNNING + worker.state = State.RUNNING telegram._status_table(bot=MagicMock(), update=update) assert msg_mock.call_count == 1 assert 'no active order' in msg_mock.call_args_list[0][0][0] @@ -344,7 +360,9 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -409,13 +427,15 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None: ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) # Try invalid data msg_mock.reset_mock() - freqtradebot.state = State.RUNNING + worker.state = State.RUNNING update.message.text = '/daily -2' telegram._daily(bot=MagicMock(), update=update) assert msg_mock.call_count == 1 @@ -423,7 +443,7 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None: # Try invalid data msg_mock.reset_mock() - freqtradebot.state = State.RUNNING + worker.state = State.RUNNING update.message.text = '/daily today' telegram._daily(bot=MagicMock(), update=update) assert str('Daily Profit over the last 7 days') in msg_mock.call_args_list[0][0][0] @@ -448,7 +468,9 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -593,13 +615,14 @@ def test_start_handle(default_conf, update, mocker) -> None: _send_msg=msg_mock ) - freqtradebot = get_patched_freqtradebot(mocker, default_conf) + worker = get_patched_worker(mocker, default_conf) + freqtradebot = worker.freqtrade telegram = Telegram(freqtradebot) - freqtradebot.state = State.STOPPED - assert freqtradebot.state == State.STOPPED + worker.state = State.STOPPED + assert worker.state == State.STOPPED telegram._start(bot=MagicMock(), update=update) - assert freqtradebot.state == State.RUNNING + assert worker.state == State.RUNNING assert msg_mock.call_count == 1 @@ -611,13 +634,14 @@ def test_start_handle_already_running(default_conf, update, mocker) -> None: _send_msg=msg_mock ) - freqtradebot = get_patched_freqtradebot(mocker, default_conf) + worker = get_patched_worker(mocker, default_conf) + freqtradebot = worker.freqtrade telegram = Telegram(freqtradebot) - freqtradebot.state = State.RUNNING - assert freqtradebot.state == State.RUNNING + worker.state = State.RUNNING + assert worker.state == State.RUNNING telegram._start(bot=MagicMock(), update=update) - assert freqtradebot.state == State.RUNNING + assert worker.state == State.RUNNING assert msg_mock.call_count == 1 assert 'already running' in msg_mock.call_args_list[0][0][0] @@ -631,13 +655,14 @@ def test_stop_handle(default_conf, update, mocker) -> None: _send_msg=msg_mock ) - freqtradebot = get_patched_freqtradebot(mocker, default_conf) + worker = get_patched_worker(mocker, default_conf) + freqtradebot = worker.freqtrade telegram = Telegram(freqtradebot) - freqtradebot.state = State.RUNNING - assert freqtradebot.state == State.RUNNING + worker.state = State.RUNNING + assert worker.state == State.RUNNING telegram._stop(bot=MagicMock(), update=update) - assert freqtradebot.state == State.STOPPED + assert worker.state == State.STOPPED assert msg_mock.call_count == 1 assert 'stopping trader' in msg_mock.call_args_list[0][0][0] @@ -651,13 +676,14 @@ def test_stop_handle_already_stopped(default_conf, update, mocker) -> None: _send_msg=msg_mock ) - freqtradebot = get_patched_freqtradebot(mocker, default_conf) + worker = get_patched_worker(mocker, default_conf) + freqtradebot = worker.freqtrade telegram = Telegram(freqtradebot) - freqtradebot.state = State.STOPPED - assert freqtradebot.state == State.STOPPED + worker.state = State.STOPPED + assert worker.state == State.STOPPED telegram._stop(bot=MagicMock(), update=update) - assert freqtradebot.state == State.STOPPED + assert worker.state == State.STOPPED assert msg_mock.call_count == 1 assert 'already stopped' in msg_mock.call_args_list[0][0][0] @@ -691,13 +717,14 @@ def test_reload_conf_handle(default_conf, update, mocker) -> None: _send_msg=msg_mock ) - freqtradebot = get_patched_freqtradebot(mocker, default_conf) + worker = get_patched_worker(mocker, default_conf) + freqtradebot = worker.freqtrade telegram = Telegram(freqtradebot) - freqtradebot.state = State.RUNNING - assert freqtradebot.state == State.RUNNING + worker.state = State.RUNNING + assert worker.state == State.RUNNING telegram._reload_conf(bot=MagicMock(), update=update) - assert freqtradebot.state == State.RELOAD_CONF + assert worker.state == State.RELOAD_CONF assert msg_mock.call_count == 1 assert 'reloading config' in msg_mock.call_args_list[0][0][0] @@ -717,7 +744,9 @@ def test_forcesell_handle(default_conf, update, ticker, fee, validate_pairs=MagicMock(return_value={}) ) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -768,7 +797,9 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee, validate_pairs=MagicMock(return_value={}) ) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -822,7 +853,9 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, markets, mocker validate_pairs=MagicMock(return_value={}) ) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -865,7 +898,9 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None: ) patch_exchange(mocker) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -907,7 +942,9 @@ def test_forcebuy_handle(default_conf, update, markets, mocker) -> None: fbuy_mock = MagicMock(return_value=None) mocker.patch('freqtrade.rpc.RPC._rpc_forcebuy', fbuy_mock) - freqtradebot = FreqtradeBot(default_conf) + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -941,7 +978,10 @@ def test_forcebuy_handle_exception(default_conf, update, markets, mocker) -> Non markets=PropertyMock(markets), validate_pairs=MagicMock(return_value={}) ) - freqtradebot = FreqtradeBot(default_conf) + + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -970,7 +1010,10 @@ def test_performance_handle(default_conf, update, ticker, fee, validate_pairs=MagicMock(return_value={}) ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - freqtradebot = FreqtradeBot(default_conf) + + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -1009,7 +1052,10 @@ def test_count_handle(default_conf, update, ticker, fee, markets, mocker) -> Non markets=PropertyMock(markets) ) mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) - freqtradebot = FreqtradeBot(default_conf) + + worker = Worker(args=None, config=default_conf) + freqtradebot = worker.freqtrade + patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index e4f0415f7..276cdc0b9 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -13,6 +13,7 @@ import requests from freqtrade import (DependencyException, OperationalException, TemporaryError, constants) +from freqtrade.worker import Worker from freqtrade.freqtradebot import FreqtradeBot from freqtrade.persistence import Trade from freqtrade.rpc import RPCMessageType @@ -22,9 +23,9 @@ from freqtrade.tests.conftest import log_has, log_has_re, patch_exchange, patch_ # Functions for recurrent object patching -def get_patched_freqtradebot(mocker, config) -> FreqtradeBot: +def patch_freqtradebot(mocker, config) -> None: """ - This function patch _init_modules() to not call dependencies + This function patches _init_modules() to not call dependencies :param mocker: a Mocker object to apply patches :param config: Config to pass to the bot :return: None @@ -33,9 +34,29 @@ def get_patched_freqtradebot(mocker, config) -> FreqtradeBot: mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock()) patch_exchange(mocker) + +def get_patched_freqtradebot(mocker, config) -> FreqtradeBot: + """ + This function patches _init_modules() to not call dependencies + :param mocker: a Mocker object to apply patches + :param config: Config to pass to the bot + :return: FreqtradeBot + """ + patch_freqtradebot(mocker, config) return FreqtradeBot(config) +def get_patched_worker(mocker, config) -> Worker: + """ + This function patches _init_modules() to not call dependencies + :param mocker: a Mocker object to apply patches + :param config: Config to pass to the bot + :return: FreqtradeBot + """ + patch_freqtradebot(mocker, config) + return Worker(args=None, config=config) + + def patch_get_signal(freqtrade: FreqtradeBot, value=(True, False)) -> None: """ :param mocker: mocker to patch IStrategy class @@ -61,12 +82,12 @@ def patch_RPCManager(mocker) -> MagicMock: def test_freqtradebot(mocker, default_conf, markets) -> None: mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets)) - freqtrade = get_patched_freqtradebot(mocker, default_conf) - assert freqtrade.state is State.RUNNING + worker = get_patched_worker(mocker, default_conf) + assert worker.state is State.RUNNING default_conf.pop('initial_state') - freqtrade = FreqtradeBot(default_conf) - assert freqtrade.state is State.STOPPED + worker = Worker(args=None, config=default_conf) + assert worker.state is State.STOPPED def test_cleanup(mocker, default_conf, caplog) -> None: @@ -80,11 +101,11 @@ def test_cleanup(mocker, default_conf, caplog) -> None: def test_worker_running(mocker, default_conf, caplog) -> None: mock_throttle = MagicMock() - mocker.patch('freqtrade.freqtradebot.FreqtradeBot._throttle', mock_throttle) + mocker.patch('freqtrade.worker.Worker._throttle', mock_throttle) - freqtrade = get_patched_freqtradebot(mocker, default_conf) + worker = get_patched_worker(mocker, default_conf) - state = freqtrade.worker(old_state=None) + state = worker._worker(old_state=None) assert state is State.RUNNING assert log_has('Changing state to: RUNNING', caplog.record_tuples) assert mock_throttle.call_count == 1 @@ -92,12 +113,12 @@ def test_worker_running(mocker, default_conf, caplog) -> None: def test_worker_stopped(mocker, default_conf, caplog) -> None: mock_throttle = MagicMock() - mocker.patch('freqtrade.freqtradebot.FreqtradeBot._throttle', mock_throttle) + mocker.patch('freqtrade.worker.Worker._throttle', mock_throttle) mock_sleep = mocker.patch('time.sleep', return_value=None) - freqtrade = get_patched_freqtradebot(mocker, default_conf) - freqtrade.state = State.STOPPED - state = freqtrade.worker(old_state=State.RUNNING) + worker = get_patched_worker(mocker, default_conf) + worker.state = State.STOPPED + state = worker._worker(old_state=State.RUNNING) assert state is State.STOPPED assert log_has('Changing state to: STOPPED', caplog.record_tuples) assert mock_throttle.call_count == 0 @@ -109,17 +130,17 @@ def test_throttle(mocker, default_conf, caplog) -> None: return 42 caplog.set_level(logging.DEBUG) - freqtrade = get_patched_freqtradebot(mocker, default_conf) + worker = get_patched_worker(mocker, default_conf) start = time.time() - result = freqtrade._throttle(throttled_func, min_secs=0.1) + result = worker._throttle(throttled_func, min_secs=0.1) end = time.time() assert result == 42 assert end - start > 0.1 assert log_has('Throttling throttled_func for 0.10 seconds', caplog.record_tuples) - result = freqtrade._throttle(throttled_func, min_secs=-1) + result = worker._throttle(throttled_func, min_secs=-1) assert result == 42 @@ -127,12 +148,12 @@ def test_throttle_with_assets(mocker, default_conf) -> None: def throttled_func(nb_assets=-1): return nb_assets - freqtrade = get_patched_freqtradebot(mocker, default_conf) + worker = get_patched_worker(mocker, default_conf) - result = freqtrade._throttle(throttled_func, min_secs=0.1, nb_assets=666) + result = worker._throttle(throttled_func, min_secs=0.1, nb_assets=666) assert result == 666 - result = freqtrade._throttle(throttled_func, min_secs=0.1) + result = worker._throttle(throttled_func, min_secs=0.1) assert result == -1 @@ -218,7 +239,7 @@ def test_edge_called_in_process(mocker, edge_conf) -> None: freqtrade = FreqtradeBot(edge_conf) freqtrade.pairlists._validate_whitelist = _refresh_whitelist patch_get_signal(freqtrade) - freqtrade._process() + freqtrade.process() assert freqtrade.active_pair_whitelist == ['NEO/BTC', 'LTC/BTC'] @@ -652,7 +673,7 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order, trades = Trade.query.filter(Trade.is_open.is_(True)).all() assert not trades - result = freqtrade._process() + result = freqtrade.process() assert result is True trades = Trade.query.filter(Trade.is_open.is_(True)).all() @@ -683,10 +704,10 @@ def test_process_exchange_failures(default_conf, ticker, markets, mocker) -> Non ) sleep_mock = mocker.patch('time.sleep', side_effect=lambda _: None) - freqtrade = FreqtradeBot(default_conf) - patch_get_signal(freqtrade) + worker = get_patched_worker(mocker, default_conf) + patch_get_signal(worker.freqtrade) - result = freqtrade._process() + result = worker.freqtrade.process() assert result is False assert sleep_mock.has_calls() @@ -700,14 +721,14 @@ def test_process_operational_exception(default_conf, ticker, markets, mocker) -> markets=PropertyMock(return_value=markets), buy=MagicMock(side_effect=OperationalException) ) - freqtrade = FreqtradeBot(default_conf) - patch_get_signal(freqtrade) + worker = get_patched_worker(mocker, default_conf) + patch_get_signal(worker.freqtrade) - assert freqtrade.state == State.RUNNING + assert worker.state == State.RUNNING - result = freqtrade._process() + result = worker.freqtrade.process() assert result is False - assert freqtrade.state == State.STOPPED + assert worker.state == State.STOPPED assert 'OperationalException' in msg_mock.call_args_list[-1][0][0]['status'] @@ -728,18 +749,18 @@ def test_process_trade_handling( trades = Trade.query.filter(Trade.is_open.is_(True)).all() assert not trades - result = freqtrade._process() + result = freqtrade.process() assert result is True trades = Trade.query.filter(Trade.is_open.is_(True)).all() assert len(trades) == 1 - result = freqtrade._process() + result = freqtrade.process() assert result is False def test_process_trade_no_whitelist_pair( default_conf, ticker, limit_buy_order, markets, fee, mocker) -> None: - """ Test _process with trade not in pair list """ + """ Test process with trade not in pair list """ patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -776,7 +797,7 @@ def test_process_trade_no_whitelist_pair( )) assert pair not in freqtrade.active_pair_whitelist - result = freqtrade._process() + result = freqtrade.process() assert pair in freqtrade.active_pair_whitelist # Make sure each pair is only in the list once assert len(freqtrade.active_pair_whitelist) == len(set(freqtrade.active_pair_whitelist)) @@ -806,7 +827,7 @@ def test_process_informative_pairs_added(default_conf, ticker, markets, mocker) freqtrade.strategy.informative_pairs = inf_pairs # patch_get_signal(freqtrade) - freqtrade._process() + freqtrade.process() assert inf_pairs.call_count == 1 assert refresh_mock.call_count == 1 assert ("BTC/ETH", "1m") in refresh_mock.call_args[0][0] @@ -2992,5 +3013,5 @@ def test_startup_messages(default_conf, mocker): 'config': {'number_assets': 20} } mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) - freqtrade = get_patched_freqtradebot(mocker, default_conf) - assert freqtrade.state is State.RUNNING + worker = get_patched_worker(mocker, default_conf) + assert worker.state is State.RUNNING diff --git a/freqtrade/tests/test_main.py b/freqtrade/tests/test_main.py index 188b9b9bd..cce02bf8b 100644 --- a/freqtrade/tests/test_main.py +++ b/freqtrade/tests/test_main.py @@ -43,17 +43,14 @@ def test_main_start_hyperopt(mocker) -> None: def test_main_fatal_exception(mocker, default_conf, caplog) -> None: patch_exchange(mocker) - mocker.patch.multiple( - 'freqtrade.freqtradebot.FreqtradeBot', - _init_modules=MagicMock(), - worker=MagicMock(side_effect=Exception), - cleanup=MagicMock(), - ) + mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock()) + mocker.patch('freqtrade.worker.Worker._worker', MagicMock(side_effect=Exception)) mocker.patch( 'freqtrade.configuration.Configuration._load_config_file', lambda *args, **kwargs: default_conf ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) + mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock()) args = ['-c', 'config.json.example'] @@ -66,17 +63,14 @@ def test_main_fatal_exception(mocker, default_conf, caplog) -> None: def test_main_keyboard_interrupt(mocker, default_conf, caplog) -> None: patch_exchange(mocker) - mocker.patch.multiple( - 'freqtrade.freqtradebot.FreqtradeBot', - _init_modules=MagicMock(), - worker=MagicMock(side_effect=KeyboardInterrupt), - cleanup=MagicMock(), - ) + mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock()) + mocker.patch('freqtrade.worker.Worker._worker', MagicMock(side_effect=KeyboardInterrupt)) mocker.patch( 'freqtrade.configuration.Configuration._load_config_file', lambda *args, **kwargs: default_conf ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) + mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock()) args = ['-c', 'config.json.example'] @@ -89,17 +83,14 @@ def test_main_keyboard_interrupt(mocker, default_conf, caplog) -> None: def test_main_operational_exception(mocker, default_conf, caplog) -> None: patch_exchange(mocker) - mocker.patch.multiple( - 'freqtrade.freqtradebot.FreqtradeBot', - _init_modules=MagicMock(), - worker=MagicMock(side_effect=OperationalException('Oh snap!')), - cleanup=MagicMock(), - ) + mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock()) + mocker.patch('freqtrade.worker.Worker._worker', MagicMock(side_effect=OperationalException('Oh snap!'))) mocker.patch( 'freqtrade.configuration.Configuration._load_config_file', lambda *args, **kwargs: default_conf ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) + mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock()) args = ['-c', 'config.json.example'] @@ -112,17 +103,14 @@ def test_main_operational_exception(mocker, default_conf, caplog) -> None: def test_main_reload_conf(mocker, default_conf, caplog) -> None: patch_exchange(mocker) - mocker.patch.multiple( - 'freqtrade.freqtradebot.FreqtradeBot', - _init_modules=MagicMock(), - worker=MagicMock(return_value=State.RELOAD_CONF), - cleanup=MagicMock(), - ) + mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock()) + mocker.patch('freqtrade.worker.Worker._worker', MagicMock(return_value=State.RELOAD_CONF)) mocker.patch( 'freqtrade.configuration.Configuration._load_config_file', lambda *args, **kwargs: default_conf ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) + mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock()) # Raise exception as side effect to avoid endless loop reconfigure_mock = mocker.patch( @@ -138,17 +126,14 @@ def test_main_reload_conf(mocker, default_conf, caplog) -> None: def test_reconfigure(mocker, default_conf) -> None: patch_exchange(mocker) - mocker.patch.multiple( - 'freqtrade.freqtradebot.FreqtradeBot', - _init_modules=MagicMock(), - worker=MagicMock(side_effect=OperationalException('Oh snap!')), - cleanup=MagicMock(), - ) + mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock()) + mocker.patch('freqtrade.worker.Worker._worker', MagicMock(side_effect=OperationalException('Oh snap!'))) mocker.patch( 'freqtrade.configuration.Configuration._load_config_file', lambda *args, **kwargs: default_conf ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) + mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock()) freqtrade = FreqtradeBot(default_conf) From 8aee009a0acce1d86cd4be84f3696cdb6e654d19 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 26 Mar 2019 12:42:19 +0300 Subject: [PATCH 09/19] test _reconfigure() adjusted --- freqtrade/tests/test_main.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/freqtrade/tests/test_main.py b/freqtrade/tests/test_main.py index cce02bf8b..96cf2834d 100644 --- a/freqtrade/tests/test_main.py +++ b/freqtrade/tests/test_main.py @@ -7,8 +7,9 @@ import pytest from freqtrade import OperationalException from freqtrade.arguments import Arguments +from freqtrade.worker import Worker from freqtrade.freqtradebot import FreqtradeBot -from freqtrade.main import main, Worker +from freqtrade.main import main from freqtrade.state import State from freqtrade.tests.conftest import log_has, patch_exchange @@ -135,7 +136,9 @@ def test_reconfigure(mocker, default_conf) -> None: mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock()) - freqtrade = FreqtradeBot(default_conf) + args = Arguments(['-c', 'config.json.example'], '').get_parsed_arg() + worker = Worker(args=args, config=default_conf) + freqtrade = worker.freqtrade # Renew mock to return modified data conf = deepcopy(default_conf) @@ -145,11 +148,10 @@ def test_reconfigure(mocker, default_conf) -> None: lambda *args, **kwargs: conf ) + worker._config = conf # reconfigure should return a new instance - freqtrade2 = reconfigure( - freqtrade, - Arguments(['-c', 'config.json.example'], '').get_parsed_arg() - ) + worker._reconfigure() + freqtrade2 = worker.freqtrade # Verify we have a new instance with the new config assert freqtrade is not freqtrade2 From c6d2c1e52095dc3d4fbfaed5daa067b63131ede7 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 26 Mar 2019 12:45:19 +0300 Subject: [PATCH 10/19] rest of telegram tests adjusted --- freqtrade/tests/rpc/test_rpc_telegram.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/freqtrade/tests/rpc/test_rpc_telegram.py b/freqtrade/tests/rpc/test_rpc_telegram.py index 1452267ea..578e45e24 100644 --- a/freqtrade/tests/rpc/test_rpc_telegram.py +++ b/freqtrade/tests/rpc/test_rpc_telegram.py @@ -697,7 +697,9 @@ def test_stopbuy_handle(default_conf, update, mocker) -> None: _send_msg=msg_mock ) - freqtradebot = get_patched_freqtradebot(mocker, default_conf) + worker = get_patched_worker(mocker, default_conf) + freqtradebot = worker.freqtrade + telegram = Telegram(freqtradebot) assert freqtradebot.config['max_open_trades'] != 0 From f5744cc9bf3d7b0febc35dade539ba1f7b546bab Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 26 Mar 2019 18:34:50 +0300 Subject: [PATCH 11/19] fix in the tests --- freqtrade/tests/test_freqtradebot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 276cdc0b9..ece07f6fd 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -51,7 +51,7 @@ def get_patched_worker(mocker, config) -> Worker: This function patches _init_modules() to not call dependencies :param mocker: a Mocker object to apply patches :param config: Config to pass to the bot - :return: FreqtradeBot + :return: Worker """ patch_freqtradebot(mocker, config) return Worker(args=None, config=config) @@ -707,7 +707,7 @@ def test_process_exchange_failures(default_conf, ticker, markets, mocker) -> Non worker = get_patched_worker(mocker, default_conf) patch_get_signal(worker.freqtrade) - result = worker.freqtrade.process() + result = worker._process() assert result is False assert sleep_mock.has_calls() @@ -726,7 +726,7 @@ def test_process_operational_exception(default_conf, ticker, markets, mocker) -> assert worker.state == State.RUNNING - result = worker.freqtrade.process() + result = worker._process() assert result is False assert worker.state == State.STOPPED assert 'OperationalException' in msg_mock.call_args_list[-1][0][0]['status'] From a87fc5f8634d656faa0d9de020e5f0beb0bda40c Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 28 Mar 2019 19:37:50 +0100 Subject: [PATCH 12/19] Fix tests - freqtrade should not be patched in this case --- freqtrade/tests/test_freqtradebot.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index ceeae6865..8bc071071 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -112,9 +112,9 @@ def test_worker_running(mocker, default_conf, caplog) -> None: assert log_has('Changing state to: RUNNING', caplog.record_tuples) assert mock_throttle.call_count == 1 # Check strategy is loaded, and received a dataprovider object - assert freqtrade.strategy - assert freqtrade.strategy.dp - assert isinstance(freqtrade.strategy.dp, DataProvider) + assert worker.freqtrade.strategy + assert worker.freqtrade.strategy.dp + assert isinstance(worker.freqtrade.strategy.dp, DataProvider) def test_worker_stopped(mocker, default_conf, caplog) -> None: @@ -710,7 +710,7 @@ def test_process_exchange_failures(default_conf, ticker, markets, mocker) -> Non ) sleep_mock = mocker.patch('time.sleep', side_effect=lambda _: None) - worker = get_patched_worker(mocker, default_conf) + worker = Worker(args=None, config=default_conf) patch_get_signal(worker.freqtrade) result = worker._process() @@ -727,7 +727,7 @@ def test_process_operational_exception(default_conf, ticker, markets, mocker) -> markets=PropertyMock(return_value=markets), buy=MagicMock(side_effect=OperationalException) ) - worker = get_patched_worker(mocker, default_conf) + worker = Worker(args=None, config=default_conf) patch_get_signal(worker.freqtrade) assert worker.state == State.RUNNING From 208832e8471faed4427155c5b6817a6f8295aab8 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sat, 30 Mar 2019 02:19:43 +0300 Subject: [PATCH 13/19] flake8, mypy resolved --- freqtrade/tests/rpc/test_rpc_telegram.py | 1 - freqtrade/tests/test_main.py | 11 ++++++++--- freqtrade/worker.py | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/freqtrade/tests/rpc/test_rpc_telegram.py b/freqtrade/tests/rpc/test_rpc_telegram.py index b44c19688..384cdcfa0 100644 --- a/freqtrade/tests/rpc/test_rpc_telegram.py +++ b/freqtrade/tests/rpc/test_rpc_telegram.py @@ -14,7 +14,6 @@ from telegram.error import NetworkError from freqtrade import __version__ from freqtrade.edge import PairInfo -from freqtrade.freqtradebot import FreqtradeBot from freqtrade.persistence import Trade from freqtrade.rpc import RPCMessageType from freqtrade.rpc.telegram import Telegram, authorized_only diff --git a/freqtrade/tests/test_main.py b/freqtrade/tests/test_main.py index 96cf2834d..fc5d2e378 100644 --- a/freqtrade/tests/test_main.py +++ b/freqtrade/tests/test_main.py @@ -8,7 +8,6 @@ import pytest from freqtrade import OperationalException from freqtrade.arguments import Arguments from freqtrade.worker import Worker -from freqtrade.freqtradebot import FreqtradeBot from freqtrade.main import main from freqtrade.state import State from freqtrade.tests.conftest import log_has, patch_exchange @@ -85,7 +84,10 @@ def test_main_keyboard_interrupt(mocker, default_conf, caplog) -> None: def test_main_operational_exception(mocker, default_conf, caplog) -> None: patch_exchange(mocker) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock()) - mocker.patch('freqtrade.worker.Worker._worker', MagicMock(side_effect=OperationalException('Oh snap!'))) + mocker.patch( + 'freqtrade.worker.Worker._worker', + MagicMock(side_effect=OperationalException('Oh snap!')) + ) mocker.patch( 'freqtrade.configuration.Configuration._load_config_file', lambda *args, **kwargs: default_conf @@ -128,7 +130,10 @@ def test_main_reload_conf(mocker, default_conf, caplog) -> None: def test_reconfigure(mocker, default_conf) -> None: patch_exchange(mocker) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock()) - mocker.patch('freqtrade.worker.Worker._worker', MagicMock(side_effect=OperationalException('Oh snap!'))) + mocker.patch( + 'freqtrade.worker.Worker._worker', + MagicMock(side_effect=OperationalException('Oh snap!')) + ) mocker.patch( 'freqtrade.configuration.Configuration._load_config_file', lambda *args, **kwargs: default_conf diff --git a/freqtrade/worker.py b/freqtrade/worker.py index a6fba1460..f32e6ff49 100755 --- a/freqtrade/worker.py +++ b/freqtrade/worker.py @@ -23,7 +23,7 @@ class Worker(object): Freqtradebot worker class """ - def __init__(self, args: Optional[Namespace] = None, config = None) -> None: + def __init__(self, args: Namespace, config=None) -> None: """ Init all variables and objects the bot needs to work """ From 06144a1fc4ea15f107ecbe21bca9382d1e8193c6 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sat, 30 Mar 2019 23:33:52 +0300 Subject: [PATCH 14/19] Wording in a comment --- freqtrade/worker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/worker.py b/freqtrade/worker.py index f32e6ff49..e5ca36035 100755 --- a/freqtrade/worker.py +++ b/freqtrade/worker.py @@ -159,7 +159,7 @@ class Worker(object): def _reconfigure(self): """ Cleans up current freqtradebot instance, reloads the configuration and - returns the new instance + replaces it with the new instance """ # Tell systemd that we initiated reconfiguration if self._sd_notify: From 7251e5bd625817e823dc4d90f14ecd04f84a4461 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sun, 31 Mar 2019 23:39:55 +0300 Subject: [PATCH 15/19] bot state moved back to freqtradebot from worker --- freqtrade/freqtradebot.py | 21 +++++++-------------- freqtrade/worker.py | 24 +++++++----------------- 2 files changed, 14 insertions(+), 31 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 7c92ac29a..6345dae56 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -22,7 +22,6 @@ from freqtrade.resolvers import ExchangeResolver, StrategyResolver, PairListReso from freqtrade.state import State from freqtrade.strategy.interface import SellType, IStrategy from freqtrade.wallets import Wallets -from freqtrade.worker import Worker logger = logging.getLogger(__name__) @@ -34,7 +33,7 @@ class FreqtradeBot(object): This is from here the bot start its logic. """ - def __init__(self, config: Dict[str, Any], worker: Optional[Worker] = None) -> None: + def __init__(self, config: Dict[str, Any]) -> None: """ Init all variables and objects the bot needs to work :param config: configuration dict, you can use Configuration.get_config() @@ -43,9 +42,11 @@ class FreqtradeBot(object): logger.info('Starting freqtrade %s', __version__) + # Init bot state + self.state = State.STOPPED + # Init objects self.config = config - self._worker = worker self.strategy: IStrategy = StrategyResolver(self.config).strategy @@ -73,17 +74,9 @@ class FreqtradeBot(object): persistence.init(self.config) - @property - def state(self) -> State: - if self._worker is None: - raise DependencyException("No Worker is available") - return self._worker.state - - @state.setter - def state(self, value: State): - if self._worker is None: - raise DependencyException("No Worker is available") - self._worker.state = value + # Set initial bot state from config + initial_state = self.config.get('initial_state') + self.state = State[initial_state.upper()] if initial_state else State.STOPPED def cleanup(self) -> None: """ diff --git a/freqtrade/worker.py b/freqtrade/worker.py index e5ca36035..2440e7320 100755 --- a/freqtrade/worker.py +++ b/freqtrade/worker.py @@ -11,6 +11,7 @@ import sdnotify from freqtrade import (constants, OperationalException, TemporaryError, __version__) from freqtrade.configuration import Configuration +from freqtrade.freqtradebot import FreqtradeBot from freqtrade.state import State from freqtrade.rpc import RPCMessageType @@ -46,19 +47,8 @@ class Worker(object): # Load configuration self._config = Configuration(self._args, None).get_config() - # Import freqtradebot here in order to avoid python circular - # dependency error, damn! - from freqtrade.freqtradebot import FreqtradeBot - # Init the instance of the bot - self.freqtrade = FreqtradeBot(self._config, self) - - # Set initial bot state - initial_state = self._config.get('initial_state') - if initial_state: - self._state = State[initial_state.upper()] - else: - self._state = State.STOPPED + self.freqtrade = FreqtradeBot(self._config) self._throttle_secs = self._config.get('internals', {}).get( 'process_throttle_secs', @@ -70,11 +60,11 @@ class Worker(object): @property def state(self) -> State: - return self._state + return self.freqtrade.state @state.setter def state(self, value: State): - self._state = value + self.freqtrade.state = value def run(self): state = None @@ -89,7 +79,7 @@ class Worker(object): :param old_state: the previous service state from the previous call :return: current service state """ - state = self._state + state = self.freqtrade.state if throttle_secs is None: throttle_secs = self._throttle_secs @@ -141,7 +131,6 @@ class Worker(object): state_changed = False try: state_changed = self.freqtrade.process() - except TemporaryError as error: logger.warning(f"Error: {error}, retrying in {constants.RETRY_TIMEOUT} seconds...") time.sleep(constants.RETRY_TIMEOUT) @@ -153,7 +142,8 @@ class Worker(object): 'status': f'OperationalException:\n```\n{tb}```{hint}' }) logger.exception('OperationalException. Stopping trader ...') - self.state = State.STOPPED + self.freqtrade.state = State.STOPPED +### state_changed = True return state_changed def _reconfigure(self): From 62141d3d2790c55d779b296528e224754d1fae7f Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 2 Apr 2019 21:57:52 +0300 Subject: [PATCH 16/19] test cloned, separate tests for worker and freqtrade states --- freqtrade/tests/test_freqtradebot.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 8bc071071..250e43c3b 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -82,7 +82,17 @@ def patch_RPCManager(mocker) -> MagicMock: # Unit tests -def test_freqtradebot(mocker, default_conf, markets) -> None: +def test_freqtradebot_state(mocker, default_conf, markets) -> None: + mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets)) + freqtrade = get_patched_freqtradebot(mocker, default_conf) + assert freqtrade.state is State.RUNNING + + default_conf.pop('initial_state') + freqtrade = FreqtradeBot(config=default_conf) + assert freqtrade.state is State.STOPPED + + +def test_worker_state(mocker, default_conf, markets) -> None: mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets)) worker = get_patched_worker(mocker, default_conf) assert worker.state is State.RUNNING From b0ddb33acc5fb2eb3d5d30068853456e44af6c81 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 2 Apr 2019 22:36:30 +0300 Subject: [PATCH 17/19] tests cleanup: Worker --> FreqtradeBot where the Worker object is not really needed --- freqtrade/tests/rpc/test_rpc.py | 55 ++++------- freqtrade/tests/rpc/test_rpc_telegram.py | 116 +++++++++-------------- 2 files changed, 64 insertions(+), 107 deletions(-) diff --git a/freqtrade/tests/rpc/test_rpc.py b/freqtrade/tests/rpc/test_rpc.py index 46033ec23..b7b041695 100644 --- a/freqtrade/tests/rpc/test_rpc.py +++ b/freqtrade/tests/rpc/test_rpc.py @@ -16,7 +16,6 @@ from freqtrade.rpc.fiat_convert import CryptoToFiatConverter from freqtrade.state import State from freqtrade.tests.conftest import patch_coinmarketcap, patch_exchange from freqtrade.tests.test_freqtradebot import patch_get_signal -from freqtrade.worker import Worker # Functions for recurrent object patching @@ -39,8 +38,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None: markets=PropertyMock(return_value=markets) ) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -97,8 +95,7 @@ def test_rpc_status_table(default_conf, ticker, fee, markets, mocker) -> None: markets=PropertyMock(return_value=markets) ) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -135,8 +132,7 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee, markets=PropertyMock(return_value=markets) ) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) stake_currency = default_conf['stake_currency'] @@ -191,8 +187,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, markets=PropertyMock(return_value=markets) ) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) stake_currency = default_conf['stake_currency'] @@ -281,8 +276,7 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, markets, markets=PropertyMock(return_value=markets) ) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) stake_currency = default_conf['stake_currency'] @@ -352,8 +346,7 @@ def test_rpc_balance_handle(default_conf, mocker): get_ticker=MagicMock(side_effect=TemporaryError('Could not load ticker due to xxx')) ) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -382,8 +375,7 @@ def test_rpc_start(mocker, default_conf) -> None: get_ticker=MagicMock() ) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -407,8 +399,7 @@ def test_rpc_stop(mocker, default_conf) -> None: get_ticker=MagicMock() ) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -433,8 +424,7 @@ def test_rpc_stopbuy(mocker, default_conf) -> None: get_ticker=MagicMock() ) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -467,8 +457,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker, markets) -> None: markets=PropertyMock(return_value=markets) ) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -561,8 +550,7 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee, markets=PropertyMock(return_value=markets) ) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -599,8 +587,7 @@ def test_rpc_count(mocker, default_conf, ticker, fee, markets) -> None: markets=PropertyMock(return_value=markets) ) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -631,8 +618,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, markets, limit_buy_order buy=buy_mm ) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -659,8 +645,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, markets, limit_buy_order # Test not buying default_conf['stake_amount'] = 0.0000001 - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -676,8 +661,7 @@ def test_rpcforcebuy_stopped(mocker, default_conf) -> None: patch_exchange(mocker) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -691,8 +675,7 @@ def test_rpcforcebuy_disabled(mocker, default_conf) -> None: patch_exchange(mocker) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -706,8 +689,7 @@ def test_rpc_whitelist(mocker, default_conf) -> None: patch_exchange(mocker) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) rpc = RPC(freqtradebot) ret = rpc._rpc_whitelist() @@ -724,8 +706,7 @@ def test_rpc_whitelist_dynamic(mocker, default_conf) -> None: mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) rpc = RPC(freqtradebot) ret = rpc._rpc_whitelist() diff --git a/freqtrade/tests/rpc/test_rpc_telegram.py b/freqtrade/tests/rpc/test_rpc_telegram.py index 384cdcfa0..c0b77076f 100644 --- a/freqtrade/tests/rpc/test_rpc_telegram.py +++ b/freqtrade/tests/rpc/test_rpc_telegram.py @@ -14,15 +14,15 @@ from telegram.error import NetworkError from freqtrade import __version__ from freqtrade.edge import PairInfo +from freqtrade.freqtradebot import FreqtradeBot from freqtrade.persistence import Trade from freqtrade.rpc import RPCMessageType from freqtrade.rpc.telegram import Telegram, authorized_only from freqtrade.state import State from freqtrade.strategy.interface import SellType -from freqtrade.tests.conftest import (get_patched_freqtradebot, get_patched_worker, +from freqtrade.tests.conftest import (get_patched_freqtradebot, log_has, patch_coinmarketcap, patch_exchange) from freqtrade.tests.test_freqtradebot import patch_get_signal -from freqtrade.worker import Worker class DummyCls(Telegram): @@ -99,8 +99,7 @@ def test_authorized_only(default_conf, mocker, caplog) -> None: default_conf['telegram']['enabled'] = False - worker = Worker(args=None, config=default_conf) - bot = worker.freqtrade + bot = FreqtradeBot(config=default_conf) patch_get_signal(bot, (True, False)) dummy = DummyCls(bot) @@ -129,8 +128,7 @@ def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None: default_conf['telegram']['enabled'] = False - worker = Worker(args=None, config=default_conf) - bot = worker.freqtrade + bot = FreqtradeBot(config=default_conf) patch_get_signal(bot, (True, False)) dummy = DummyCls(bot) @@ -159,8 +157,7 @@ def test_authorized_only_exception(default_conf, mocker, caplog) -> None: default_conf['telegram']['enabled'] = False - worker = Worker(args=None, config=default_conf) - bot = worker.freqtrade + bot = FreqtradeBot(config=default_conf) patch_get_signal(bot, (True, False)) dummy = DummyCls(bot) @@ -216,8 +213,7 @@ def test_status(default_conf, update, mocker, fee, ticker, markets) -> None: ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -254,21 +250,20 @@ def test_status_handle(default_conf, update, ticker, fee, markets, mocker) -> No ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) - worker.state = State.STOPPED + freqtradebot.state = State.STOPPED # Status is also enabled when stopped telegram._status(bot=MagicMock(), update=update) assert msg_mock.call_count == 1 assert 'no active trade' in msg_mock.call_args_list[0][0][0] msg_mock.reset_mock() - worker.state = State.RUNNING + freqtradebot.state = State.RUNNING telegram._status(bot=MagicMock(), update=update) assert msg_mock.call_count == 1 assert 'no active trade' in msg_mock.call_args_list[0][0][0] @@ -303,21 +298,20 @@ def test_status_table_handle(default_conf, update, ticker, fee, markets, mocker) default_conf['stake_amount'] = 15.0 - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) - worker.state = State.STOPPED + freqtradebot.state = State.STOPPED # Status table is also enabled when stopped telegram._status_table(bot=MagicMock(), update=update) assert msg_mock.call_count == 1 assert 'no active order' in msg_mock.call_args_list[0][0][0] msg_mock.reset_mock() - worker.state = State.RUNNING + freqtradebot.state = State.RUNNING telegram._status_table(bot=MagicMock(), update=update) assert msg_mock.call_count == 1 assert 'no active order' in msg_mock.call_args_list[0][0][0] @@ -359,8 +353,7 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -426,15 +419,14 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None: ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) # Try invalid data msg_mock.reset_mock() - worker.state = State.RUNNING + freqtradebot.state = State.RUNNING update.message.text = '/daily -2' telegram._daily(bot=MagicMock(), update=update) assert msg_mock.call_count == 1 @@ -442,7 +434,7 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None: # Try invalid data msg_mock.reset_mock() - worker.state = State.RUNNING + freqtradebot.state = State.RUNNING update.message.text = '/daily today' telegram._daily(bot=MagicMock(), update=update) assert str('Daily Profit over the last 7 days') in msg_mock.call_args_list[0][0][0] @@ -467,8 +459,7 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -614,14 +605,13 @@ def test_start_handle(default_conf, update, mocker) -> None: _send_msg=msg_mock ) - worker = get_patched_worker(mocker, default_conf) - freqtradebot = worker.freqtrade + freqtradebot = get_patched_freqtradebot(mocker, default_conf) telegram = Telegram(freqtradebot) - worker.state = State.STOPPED - assert worker.state == State.STOPPED + freqtradebot.state = State.STOPPED + assert freqtradebot.state == State.STOPPED telegram._start(bot=MagicMock(), update=update) - assert worker.state == State.RUNNING + assert freqtradebot.state == State.RUNNING assert msg_mock.call_count == 1 @@ -633,14 +623,13 @@ def test_start_handle_already_running(default_conf, update, mocker) -> None: _send_msg=msg_mock ) - worker = get_patched_worker(mocker, default_conf) - freqtradebot = worker.freqtrade + freqtradebot = get_patched_freqtradebot(mocker, default_conf) telegram = Telegram(freqtradebot) - worker.state = State.RUNNING - assert worker.state == State.RUNNING + freqtradebot.state = State.RUNNING + assert freqtradebot.state == State.RUNNING telegram._start(bot=MagicMock(), update=update) - assert worker.state == State.RUNNING + assert freqtradebot.state == State.RUNNING assert msg_mock.call_count == 1 assert 'already running' in msg_mock.call_args_list[0][0][0] @@ -654,14 +643,13 @@ def test_stop_handle(default_conf, update, mocker) -> None: _send_msg=msg_mock ) - worker = get_patched_worker(mocker, default_conf) - freqtradebot = worker.freqtrade + freqtradebot = get_patched_freqtradebot(mocker, default_conf) telegram = Telegram(freqtradebot) - worker.state = State.RUNNING - assert worker.state == State.RUNNING + freqtradebot.state = State.RUNNING + assert freqtradebot.state == State.RUNNING telegram._stop(bot=MagicMock(), update=update) - assert worker.state == State.STOPPED + assert freqtradebot.state == State.STOPPED assert msg_mock.call_count == 1 assert 'stopping trader' in msg_mock.call_args_list[0][0][0] @@ -675,14 +663,13 @@ def test_stop_handle_already_stopped(default_conf, update, mocker) -> None: _send_msg=msg_mock ) - worker = get_patched_worker(mocker, default_conf) - freqtradebot = worker.freqtrade + freqtradebot = get_patched_freqtradebot(mocker, default_conf) telegram = Telegram(freqtradebot) - worker.state = State.STOPPED - assert worker.state == State.STOPPED + freqtradebot.state = State.STOPPED + assert freqtradebot.state == State.STOPPED telegram._stop(bot=MagicMock(), update=update) - assert worker.state == State.STOPPED + assert freqtradebot.state == State.STOPPED assert msg_mock.call_count == 1 assert 'already stopped' in msg_mock.call_args_list[0][0][0] @@ -696,9 +683,7 @@ def test_stopbuy_handle(default_conf, update, mocker) -> None: _send_msg=msg_mock ) - worker = get_patched_worker(mocker, default_conf) - freqtradebot = worker.freqtrade - + freqtradebot = get_patched_freqtradebot(mocker, default_conf) telegram = Telegram(freqtradebot) assert freqtradebot.config['max_open_trades'] != 0 @@ -718,14 +703,13 @@ def test_reload_conf_handle(default_conf, update, mocker) -> None: _send_msg=msg_mock ) - worker = get_patched_worker(mocker, default_conf) - freqtradebot = worker.freqtrade + freqtradebot = get_patched_freqtradebot(mocker, default_conf) telegram = Telegram(freqtradebot) - worker.state = State.RUNNING - assert worker.state == State.RUNNING + freqtradebot.state = State.RUNNING + assert freqtradebot.state == State.RUNNING telegram._reload_conf(bot=MagicMock(), update=update) - assert worker.state == State.RELOAD_CONF + assert freqtradebot.state == State.RELOAD_CONF assert msg_mock.call_count == 1 assert 'reloading config' in msg_mock.call_args_list[0][0][0] @@ -745,8 +729,7 @@ def test_forcesell_handle(default_conf, update, ticker, fee, validate_pairs=MagicMock(return_value={}) ) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -798,8 +781,7 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee, validate_pairs=MagicMock(return_value={}) ) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -854,8 +836,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, markets, mocker validate_pairs=MagicMock(return_value={}) ) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -899,8 +880,7 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None: ) patch_exchange(mocker) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -943,8 +923,7 @@ def test_forcebuy_handle(default_conf, update, markets, mocker) -> None: fbuy_mock = MagicMock(return_value=None) mocker.patch('freqtrade.rpc.RPC._rpc_forcebuy', fbuy_mock) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -980,8 +959,7 @@ def test_forcebuy_handle_exception(default_conf, update, markets, mocker) -> Non validate_pairs=MagicMock(return_value={}) ) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -1012,8 +990,7 @@ def test_performance_handle(default_conf, update, ticker, fee, ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -1054,8 +1031,7 @@ def test_count_handle(default_conf, update, ticker, fee, markets, mocker) -> Non ) mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) - worker = Worker(args=None, config=default_conf) - freqtradebot = worker.freqtrade + freqtradebot = FreqtradeBot(config=default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) From d54acca53abdc2d8cc6752a687f84c53c9390c73 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Wed, 3 Apr 2019 00:55:59 +0300 Subject: [PATCH 18/19] move tests back to original codebase to minimize changes --- freqtrade/tests/rpc/test_rpc.py | 55 ++++++++--------------- freqtrade/tests/rpc/test_rpc_telegram.py | 57 +++++++----------------- freqtrade/tests/test_freqtradebot.py | 2 +- 3 files changed, 36 insertions(+), 78 deletions(-) diff --git a/freqtrade/tests/rpc/test_rpc.py b/freqtrade/tests/rpc/test_rpc.py index 4a3cafbd2..b454f9cd8 100644 --- a/freqtrade/tests/rpc/test_rpc.py +++ b/freqtrade/tests/rpc/test_rpc.py @@ -37,8 +37,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None: markets=PropertyMock(return_value=markets) ) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -101,8 +100,7 @@ def test_rpc_status_table(default_conf, ticker, fee, markets, mocker) -> None: markets=PropertyMock(return_value=markets) ) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -137,8 +135,7 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee, markets=PropertyMock(return_value=markets) ) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] @@ -191,8 +188,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, markets=PropertyMock(return_value=markets) ) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] @@ -280,8 +276,7 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, markets, markets=PropertyMock(return_value=markets) ) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] @@ -349,8 +344,7 @@ def test_rpc_balance_handle(default_conf, mocker): get_ticker=MagicMock(side_effect=TemporaryError('Could not load ticker due to xxx')) ) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter() @@ -377,8 +371,7 @@ def test_rpc_start(mocker, default_conf) -> None: get_ticker=MagicMock() ) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) freqtradebot.state = State.STOPPED @@ -400,8 +393,7 @@ def test_rpc_stop(mocker, default_conf) -> None: get_ticker=MagicMock() ) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -424,8 +416,7 @@ def test_rpc_stopbuy(mocker, default_conf) -> None: get_ticker=MagicMock() ) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -456,8 +447,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker, markets) -> None: markets=PropertyMock(return_value=markets) ) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -548,8 +538,7 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee, markets=PropertyMock(return_value=markets) ) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -584,8 +573,7 @@ def test_rpc_count(mocker, default_conf, ticker, fee, markets) -> None: markets=PropertyMock(return_value=markets) ) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) @@ -614,8 +602,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, markets, limit_buy_order buy=buy_mm ) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) pair = 'ETH/BTC' @@ -640,9 +627,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, markets, limit_buy_order # Test not buying default_conf['stake_amount'] = 0.0000001 - - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) pair = 'TKN/BTC' @@ -656,8 +641,7 @@ def test_rpcforcebuy_stopped(mocker, default_conf) -> None: patch_exchange(mocker) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) pair = 'ETH/BTC' @@ -669,8 +653,7 @@ def test_rpcforcebuy_disabled(mocker, default_conf) -> None: patch_exchange(mocker) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) pair = 'ETH/BTC' @@ -682,8 +665,7 @@ def test_rpc_whitelist(mocker, default_conf) -> None: patch_exchange(mocker) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) rpc = RPC(freqtradebot) ret = rpc._rpc_whitelist() assert ret['method'] == 'StaticPairList' @@ -698,8 +680,7 @@ def test_rpc_whitelist_dynamic(mocker, default_conf) -> None: mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) rpc = RPC(freqtradebot) ret = rpc._rpc_whitelist() assert ret['method'] == 'VolumePairList' diff --git a/freqtrade/tests/rpc/test_rpc_telegram.py b/freqtrade/tests/rpc/test_rpc_telegram.py index c3ab5064c..b6d12fe41 100644 --- a/freqtrade/tests/rpc/test_rpc_telegram.py +++ b/freqtrade/tests/rpc/test_rpc_telegram.py @@ -96,9 +96,7 @@ def test_authorized_only(default_conf, mocker, caplog) -> None: update.message = Message(randint(1, 100), 0, datetime.utcnow(), chat) default_conf['telegram']['enabled'] = False - - bot = FreqtradeBot(config=default_conf) - + bot = FreqtradeBot(default_conf) patch_get_signal(bot, (True, False)) dummy = DummyCls(bot) dummy.dummy_handler(bot=MagicMock(), update=update) @@ -124,9 +122,7 @@ def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None: update.message = Message(randint(1, 100), 0, datetime.utcnow(), chat) default_conf['telegram']['enabled'] = False - - bot = FreqtradeBot(config=default_conf) - + bot = FreqtradeBot(default_conf) patch_get_signal(bot, (True, False)) dummy = DummyCls(bot) dummy.dummy_handler(bot=MagicMock(), update=update) @@ -153,8 +149,7 @@ def test_authorized_only_exception(default_conf, mocker, caplog) -> None: default_conf['telegram']['enabled'] = False - bot = FreqtradeBot(config=default_conf) - + bot = FreqtradeBot(default_conf) patch_get_signal(bot, (True, False)) dummy = DummyCls(bot) @@ -212,8 +207,7 @@ def test_status(default_conf, update, mocker, fee, ticker, markets) -> None: ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -248,8 +242,7 @@ def test_status_handle(default_conf, update, ticker, fee, markets, mocker) -> No ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -301,9 +294,7 @@ def test_status_table_handle(default_conf, update, ticker, fee, markets, mocker) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) default_conf['stake_amount'] = 15.0 - - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -356,8 +347,7 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -421,8 +411,7 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None: ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -460,8 +449,7 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -724,8 +712,7 @@ def test_forcesell_handle(default_conf, update, ticker, fee, validate_pairs=MagicMock(return_value={}) ) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -775,8 +762,7 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee, validate_pairs=MagicMock(return_value={}) ) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -829,8 +815,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, markets, mocker validate_pairs=MagicMock(return_value={}) ) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -872,8 +857,7 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None: ) patch_exchange(mocker) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -914,8 +898,7 @@ def test_forcebuy_handle(default_conf, update, markets, mocker) -> None: fbuy_mock = MagicMock(return_value=None) mocker.patch('freqtrade.rpc.RPC._rpc_forcebuy', fbuy_mock) - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -948,9 +931,7 @@ def test_forcebuy_handle_exception(default_conf, update, markets, mocker) -> Non markets=PropertyMock(markets), validate_pairs=MagicMock(return_value={}) ) - - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -978,9 +959,7 @@ def test_performance_handle(default_conf, update, ticker, fee, validate_pairs=MagicMock(return_value={}) ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) @@ -1018,9 +997,7 @@ def test_count_handle(default_conf, update, ticker, fee, markets, mocker) -> Non markets=PropertyMock(markets) ) mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) - - freqtradebot = FreqtradeBot(config=default_conf) - + freqtradebot = FreqtradeBot(default_conf) patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 6790f513c..211683d64 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -88,7 +88,7 @@ def test_freqtradebot_state(mocker, default_conf, markets) -> None: assert freqtrade.state is State.RUNNING default_conf.pop('initial_state') - freqtrade = FreqtradeBot(config=default_conf) + freqtrade = FreqtradeBot(default_conf) assert freqtrade.state is State.STOPPED From 65350ad55235d8fd12356dacf9ddc1dccbf56d61 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Wed, 3 Apr 2019 22:14:42 +0300 Subject: [PATCH 19/19] final flake happy --- freqtrade/worker.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/freqtrade/worker.py b/freqtrade/worker.py index 2440e7320..c7afe5c97 100755 --- a/freqtrade/worker.py +++ b/freqtrade/worker.py @@ -143,7 +143,9 @@ class Worker(object): }) logger.exception('OperationalException. Stopping trader ...') self.freqtrade.state = State.STOPPED -### state_changed = True + # TODO: The return value of _process() is not used apart tests + # and should (could) be eliminated later. See PR #1689. +# state_changed = True return state_changed def _reconfigure(self):