From 0eff324ce04d6b19e41908edf6b00065b1fcd20b Mon Sep 17 00:00:00 2001 From: Gianluca Puglia Date: Wed, 20 Mar 2019 18:38:10 +0100 Subject: [PATCH 01/57] Use dedicated index for every pair --- freqtrade/optimize/backtesting.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 031b490c8..8d0768d43 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -10,7 +10,7 @@ from datetime import datetime, timedelta from pathlib import Path from typing import Any, Dict, List, NamedTuple, Optional -from pandas import DataFrame +from pandas import DataFrame, Timestamp from tabulate import tabulate from freqtrade import optimize @@ -325,19 +325,29 @@ class Backtesting(object): pairs.append(pair) lock_pair_until: Dict = {} + indexes: Dict = {} tmp = start_date + timedelta(minutes=self.ticker_interval_mins) - index = 0 + # Loop timerange and test per pair while tmp < end_date: # print(f"time: {tmp}") + for i, pair in enumerate(ticker): + if pair not in indexes: + indexes[pair] = 0 + try: - row = ticker[pair][index] + row = ticker[pair][indexes[pair]] except IndexError: # missing Data for one pair ... # Warnings for this are shown by `validate_backtest_data` continue + if row.date > Timestamp(tmp.datetime): + continue + + indexes[pair] += 1 + if row.buy == 0 or row.sell == 1: continue # skip rows where no buy signal or that would immediately sell off @@ -351,7 +361,7 @@ class Backtesting(object): trade_count_lock[row.date] = trade_count_lock.get(row.date, 0) + 1 - trade_entry = self._get_sell_trade_entry(pair, row, ticker[pair][index + 1:], + trade_entry = self._get_sell_trade_entry(pair, row, ticker[pair][indexes[pair]:], trade_count_lock, args) if trade_entry: @@ -359,11 +369,9 @@ class Backtesting(object): trades.append(trade_entry) else: # Set lock_pair_until to end of testing period if trade could not be closed - # This happens only if the buy-signal was with the last candle - lock_pair_until[pair] = end_date + lock_pair_until[pair] = Timestamp(end_date.datetime) tmp += timedelta(minutes=self.ticker_interval_mins) - index += 1 return DataFrame.from_records(trades, columns=BacktestResult._fields) def start(self) -> None: From 6b89e86a97de6fc81cd0212e82e3267a339fc9d0 Mon Sep 17 00:00:00 2001 From: Gianluca Puglia Date: Wed, 20 Mar 2019 19:44:59 +0100 Subject: [PATCH 02/57] Removed Timestamp cast --- freqtrade/optimize/backtesting.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 8d0768d43..f54560a0e 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -10,7 +10,7 @@ from datetime import datetime, timedelta from pathlib import Path from typing import Any, Dict, List, NamedTuple, Optional -from pandas import DataFrame, Timestamp +from pandas import DataFrame from tabulate import tabulate from freqtrade import optimize @@ -343,7 +343,7 @@ class Backtesting(object): # Warnings for this are shown by `validate_backtest_data` continue - if row.date > Timestamp(tmp.datetime): + if row.date > tmp.datetime: continue indexes[pair] += 1 @@ -369,7 +369,7 @@ class Backtesting(object): trades.append(trade_entry) else: # Set lock_pair_until to end of testing period if trade could not be closed - lock_pair_until[pair] = Timestamp(end_date.datetime) + lock_pair_until[pair] = end_date.datetime tmp += timedelta(minutes=self.ticker_interval_mins) return DataFrame.from_records(trades, columns=BacktestResult._fields) From 60afba559274a13e33408c9f7fb0db24763b9594 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Fri, 22 Mar 2019 20:16:54 +0300 Subject: [PATCH 03/57] 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 04/57] 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 05/57] 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 06/57] 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 07/57] 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 05466d318a2f3971bff4e234ecbc7aa00e57f419 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 23 Mar 2019 14:50:18 +0100 Subject: [PATCH 08/57] Modify test to check for this condition --- freqtrade/tests/optimize/test_backtesting.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py index 40754cfbc..531f73916 100644 --- a/freqtrade/tests/optimize/test_backtesting.py +++ b/freqtrade/tests/optimize/test_backtesting.py @@ -683,18 +683,20 @@ def test_backtest_alternate_buy_sell(default_conf, fee, mocker): assert len(results.loc[results.open_at_end]) == 0 -def test_backtest_multi_pair(default_conf, fee, mocker): +@pytest.mark.parametrize("pair", ['ADA/BTC', 'LTC/BTC']) +@pytest.mark.parametrize("tres", [0, 20, 30]) +def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair): def _trend_alternate_hold(dataframe=None, metadata=None): """ - Buy every 8th candle - sell every other 8th -2 (hold on to pairs a bit) + Buy every xth candle - sell every other xth -2 (hold on to pairs a bit)flake """ - multi = 8 + if metadata['pair'] in('ETH/BTC', 'LTC/BTC'): + multi = 20 + else: + multi = 18 dataframe['buy'] = np.where(dataframe.index % multi == 0, 1, 0) dataframe['sell'] = np.where((dataframe.index + multi - 2) % multi == 0, 1, 0) - if metadata['pair'] in('ETH/BTC', 'LTC/BTC'): - dataframe['buy'] = dataframe['buy'].shift(-4) - dataframe['sell'] = dataframe['sell'].shift(-4) return dataframe mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) @@ -702,6 +704,7 @@ def test_backtest_multi_pair(default_conf, fee, mocker): pairs = ['ADA/BTC', 'DASH/BTC', 'ETH/BTC', 'LTC/BTC', 'NXT/BTC'] data = history.load_data(datadir=None, ticker_interval='5m', pairs=pairs) data = trim_dictlist(data, -500) + data[pair] = data[pair][tres:] # We need to enable sell-signal - otherwise it sells on ROI!! default_conf['experimental'] = {"use_sell_signal": True} default_conf['ticker_interval'] = '5m' From 00e6749d8bf8e7fc9c8924d0525d855acc8a72ea Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 23 Mar 2019 15:00:07 +0100 Subject: [PATCH 09/57] Refactor backtest() to be a bit more concise --- freqtrade/optimize/backtesting.py | 47 ++++++++++++++++++------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index f54560a0e..f3661ab32 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -202,6 +202,32 @@ class Backtesting(object): logger.info('Dumping backtest results to %s', recordfilename) file_dump_json(recordfilename, records) + def _get_ticker_list(self, processed) -> Dict[str, DataFrame]: + """ + Helper function to convert a processed tickerlist into a list for performance reasons. + + Used by backtest() - so keep this optimized for performance. + """ + headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high'] + ticker: Dict = {} + # Create ticker dict + for pair, pair_data in processed.items(): + pair_data['buy'], pair_data['sell'] = 0, 0 # cleanup from previous run + + ticker_data = self.advise_sell( + self.advise_buy(pair_data, {'pair': pair}), {'pair': pair})[headers].copy() + + # to avoid using data from future, we buy/sell with signal from previous candle + ticker_data.loc[:, 'buy'] = ticker_data['buy'].shift(1) + ticker_data.loc[:, 'sell'] = ticker_data['sell'].shift(1) + + ticker_data.drop(ticker_data.head(1).index, inplace=True) + + # Convert from Pandas to list for performance reasons + # (Looping Pandas is slow.) + ticker[pair] = [x for x in ticker_data.itertuples()] + return ticker + def _get_sell_trade_entry( self, pair: str, buy_row: DataFrame, partial_ticker: List, trade_count_lock: Dict, args: Dict) -> Optional[BacktestResult]: @@ -296,7 +322,6 @@ class Backtesting(object): position_stacking: do we allow position stacking? (default: False) :return: DataFrame """ - headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high'] processed = args['processed'] max_open_trades = args.get('max_open_trades', 0) position_stacking = args.get('position_stacking', False) @@ -304,25 +329,7 @@ class Backtesting(object): end_date = args['end_date'] trades = [] trade_count_lock: Dict = {} - ticker: Dict = {} - pairs = [] - # Create ticker dict - for pair, pair_data in processed.items(): - pair_data['buy'], pair_data['sell'] = 0, 0 # cleanup from previous run - - ticker_data = self.advise_sell( - self.advise_buy(pair_data, {'pair': pair}), {'pair': pair})[headers].copy() - - # to avoid using data from future, we buy/sell with signal from previous candle - ticker_data.loc[:, 'buy'] = ticker_data['buy'].shift(1) - ticker_data.loc[:, 'sell'] = ticker_data['sell'].shift(1) - - ticker_data.drop(ticker_data.head(1).index, inplace=True) - - # Convert from Pandas to list for performance reasons - # (Looping Pandas is slow.) - ticker[pair] = [x for x in ticker_data.itertuples()] - pairs.append(pair) + ticker: Dict = self._get_ticker_list(processed) lock_pair_until: Dict = {} indexes: Dict = {} From 40899d08dd76aca924c52ce02be418747468ef25 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 23 Mar 2019 15:24:11 +0100 Subject: [PATCH 10/57] Fix failing test (all timezones are in UTC, so we should not convert to None) --- freqtrade/tests/edge/test_edge.py | 4 ++-- freqtrade/tests/optimize/__init__.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/tests/edge/test_edge.py b/freqtrade/tests/edge/test_edge.py index c1c1b49cd..af8674188 100644 --- a/freqtrade/tests/edge/test_edge.py +++ b/freqtrade/tests/edge/test_edge.py @@ -122,8 +122,8 @@ def test_edge_results(edge_conf, mocker, caplog, data) -> None: for c, trade in enumerate(data.trades): res = results.iloc[c] assert res.exit_type == trade.sell_reason - assert res.open_time == _get_frame_time_from_offset(trade.open_tick) - assert res.close_time == _get_frame_time_from_offset(trade.close_tick) + assert arrow.get(res.open_time) == _get_frame_time_from_offset(trade.open_tick) + assert arrow.get(res.close_time) == _get_frame_time_from_offset(trade.close_tick) def test_adjust(mocker, edge_conf): diff --git a/freqtrade/tests/optimize/__init__.py b/freqtrade/tests/optimize/__init__.py index 129a09f40..b1bf55f50 100644 --- a/freqtrade/tests/optimize/__init__.py +++ b/freqtrade/tests/optimize/__init__.py @@ -32,7 +32,7 @@ class BTContainer(NamedTuple): def _get_frame_time_from_offset(offset): return ticker_start_time.shift(minutes=(offset * TICKER_INTERVAL_MINUTES[tests_ticker_interval]) - ).datetime.replace(tzinfo=None) + ).datetime def _build_backtest_dataframe(ticker_with_signals): From c8b0c9af0a5e28b18d7a8f604a5f3d3db836e992 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Mon, 25 Mar 2019 17:45:03 +0300 Subject: [PATCH 11/57] 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 12/57] 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 13/57] 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 14/57] 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 15/57] 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 16/57] 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 b2c2b42408753c0d2a976fbeb91aac094fbeef0f Mon Sep 17 00:00:00 2001 From: Gianluca Puglia Date: Tue, 26 Mar 2019 18:53:16 +0100 Subject: [PATCH 17/57] Removed unwanted comment --- freqtrade/tests/optimize/test_backtesting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py index 531f73916..64a33fae2 100644 --- a/freqtrade/tests/optimize/test_backtesting.py +++ b/freqtrade/tests/optimize/test_backtesting.py @@ -689,7 +689,7 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair): def _trend_alternate_hold(dataframe=None, metadata=None): """ - Buy every xth candle - sell every other xth -2 (hold on to pairs a bit)flake + Buy every xth candle - sell every other xth -2 (hold on to pairs a bit) """ if metadata['pair'] in('ETH/BTC', 'LTC/BTC'): multi = 20 From a87fc5f8634d656faa0d9de020e5f0beb0bda40c Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 28 Mar 2019 19:37:50 +0100 Subject: [PATCH 18/57] 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 19/57] 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 20/57] 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 21/57] 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 f0b2798c37b7ad9dbda736cf92217a060ab514d9 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Mon, 1 Apr 2019 14:08:03 +0300 Subject: [PATCH 22/57] fix #1704 --- freqtrade/freqtradebot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 6f1fb2c99..c14f0d31a 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -363,7 +363,8 @@ class FreqtradeBot(object): logger.debug('Ignoring %s in pair whitelist', trade.pair) if not whitelist: - raise DependencyException('No currency pairs in whitelist') + logger.info("No currency pairs left in whitelist, no trades can be created.") + return False # running get_signal on historical data fetched for _pair in whitelist: From 77d2479c7584954fdd30568756453051d34ac87b Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Mon, 1 Apr 2019 14:08:41 +0300 Subject: [PATCH 23/57] tests adjusted --- freqtrade/tests/test_freqtradebot.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index e4f0415f7..4e145c354 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -545,8 +545,7 @@ def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_ord freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - result = freqtrade.create_trade() - assert result is False + assert not freqtrade.create_trade() def test_create_trade_limit_reached(default_conf, ticker, limit_buy_order, @@ -567,7 +566,7 @@ def test_create_trade_limit_reached(default_conf, ticker, limit_buy_order, freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - assert freqtrade.create_trade() is False + assert not freqtrade.create_trade() assert freqtrade._get_trade_stake_amount('ETH/BTC') is None @@ -588,9 +587,7 @@ def test_create_trade_no_pairs(default_conf, ticker, limit_buy_order, fee, marke patch_get_signal(freqtrade) freqtrade.create_trade() - - with pytest.raises(DependencyException, match=r'.*No currency pairs in whitelist.*'): - freqtrade.create_trade() + assert not freqtrade.create_trade() def test_create_trade_no_pairs_after_blacklist(default_conf, ticker, @@ -610,9 +607,7 @@ def test_create_trade_no_pairs_after_blacklist(default_conf, ticker, patch_get_signal(freqtrade) freqtrade.create_trade() - - with pytest.raises(DependencyException, match=r'.*No currency pairs in whitelist.*'): - freqtrade.create_trade() + assert not freqtrade.create_trade() def test_create_trade_no_signal(default_conf, fee, mocker) -> None: From ab579587f29e3802f1c038e804214a228a4b1b26 Mon Sep 17 00:00:00 2001 From: Misagh Date: Mon, 1 Apr 2019 19:13:45 +0200 Subject: [PATCH 24/57] adding percentage to telegram status messages --- docs/telegram-usage.md | 6 ++---- freqtrade/rpc/rpc.py | 3 +++ freqtrade/rpc/telegram.py | 6 ++++-- freqtrade/tests/rpc/test_rpc.py | 6 ++++++ freqtrade/tests/rpc/test_rpc_telegram.py | 3 +++ 5 files changed, 18 insertions(+), 6 deletions(-) diff --git a/docs/telegram-usage.md b/docs/telegram-usage.md index 1ca61e54a..381a47ae9 100644 --- a/docs/telegram-usage.md +++ b/docs/telegram-usage.md @@ -65,16 +65,14 @@ Once all positions are sold, run `/stop` to completely stop the bot. For each open trade, the bot will send you the following message. -> **Trade ID:** `123` +> **Trade ID:** `123` `(since 1 days ago)` > **Current Pair:** CVC/BTC > **Open Since:** `1 days ago` > **Amount:** `26.64180098` > **Open Rate:** `0.00007489` -> **Close Rate:** `None` > **Current Rate:** `0.00007489` -> **Close Profit:** `None` > **Current Profit:** `12.95%` -> **Open Order:** `None` +> **Stoploss:** `0.00007389 (-0.02%)` ### /status table diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 79db08fd3..5308c9d51 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -111,6 +111,9 @@ class RPC(object): close_profit=fmt_close_profit, current_profit=round(current_profit * 100, 2), stop_loss=trade.stop_loss, + stop_loss_pct=trade.stop_loss_pct, + initial_stop_loss=trade.initial_stop_loss, + initial_stop_loss_pct=trade.initial_stop_loss_pct, open_order='({} {} rem={:.8f})'.format( order['type'], order['side'], order['remaining'] ) if order else None, diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 7b36e8a1f..ca9b376ec 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -197,7 +197,7 @@ class Telegram(RPC): messages = [] for r in results: lines = [ - "*Trade ID:* `{trade_id}` (since `{date}`)", + "*Trade ID:* `{trade_id}` `(since {date})`", "*Current Pair:* {pair}", "*Amount:* `{amount}`", "*Open Rate:* `{open_rate:.8f}`", @@ -205,7 +205,9 @@ class Telegram(RPC): "*Current Rate:* `{current_rate:.8f}`", "*Close Profit:* `{close_profit}`" if r['close_profit'] else "", "*Current Profit:* `{current_profit:.2f}%`", - "*Stoploss:* `{stop_loss:.8f}`", + "*Initial Stoploss:* `{initial_stop_loss:.8f}` `({initial_stop_loss_pct}%)`" + if r['stop_loss'] != r['initial_stop_loss'] else "", + "*Stoploss:* `{stop_loss:.8f}` `({stop_loss_pct}%)`", "*Open Order:* `{open_order}`" if r['open_order'] else "", ] messages.append("\n".join(filter(None, lines)).format(**r)) diff --git a/freqtrade/tests/rpc/test_rpc.py b/freqtrade/tests/rpc/test_rpc.py index 627768ec2..b454f9cd8 100644 --- a/freqtrade/tests/rpc/test_rpc.py +++ b/freqtrade/tests/rpc/test_rpc.py @@ -59,6 +59,9 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None: 'close_profit': None, 'current_profit': -0.59, 'stop_loss': 0.0, + 'initial_stop_loss': 0.0, + 'initial_stop_loss_pct': None, + 'stop_loss_pct': None, 'open_order': '(limit buy rem=0.00000000)' } == results[0] @@ -80,6 +83,9 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None: 'close_profit': None, 'current_profit': ANY, 'stop_loss': 0.0, + 'initial_stop_loss': 0.0, + 'initial_stop_loss_pct': None, + 'stop_loss_pct': None, 'open_order': '(limit buy rem=0.00000000)' } == results[0] diff --git a/freqtrade/tests/rpc/test_rpc_telegram.py b/freqtrade/tests/rpc/test_rpc_telegram.py index fb2d71d4f..b6d12fe41 100644 --- a/freqtrade/tests/rpc/test_rpc_telegram.py +++ b/freqtrade/tests/rpc/test_rpc_telegram.py @@ -196,7 +196,10 @@ def test_status(default_conf, update, mocker, fee, ticker, markets) -> None: 'amount': 90.99181074, 'close_profit': None, 'current_profit': -0.59, + 'initial_stop_loss': 1.098e-05, 'stop_loss': 1.099e-05, + 'initial_stop_loss_pct': -0.05, + 'stop_loss_pct': -0.01, 'open_order': '(limit buy rem=0.00000000)' }]), _status_table=status_table, From 8546db9dfdccd813fc3e0400fb8cb6aff84bcf1a Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Mon, 1 Apr 2019 20:23:13 +0300 Subject: [PATCH 25/57] wording in the log message --- freqtrade/freqtradebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index c14f0d31a..e9b8b1956 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -363,7 +363,7 @@ class FreqtradeBot(object): logger.debug('Ignoring %s in pair whitelist', trade.pair) if not whitelist: - logger.info("No currency pairs left in whitelist, no trades can be created.") + logger.info("No currency pair left in whitelist, no more trade can be created.") return False # running get_signal on historical data fetched From a3b0135557bf221760f2f19fe9db323e9f7b2191 Mon Sep 17 00:00:00 2001 From: Misagh Date: Mon, 1 Apr 2019 19:25:13 +0200 Subject: [PATCH 26/57] documentation added for telegram --- docs/bot-usage.md | 2 +- docs/telegram-usage.md | 68 +++++++++++++++++++++--------------------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/docs/bot-usage.md b/docs/bot-usage.md index 5ec390d5c..55988985a 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -79,7 +79,7 @@ prevent unintended disclosure of sensitive private data when you publish example of your configuration in the project issues or in the Internet. See more details on this technique with examples in the documentation page on -[configuration](bot-configuration.md). +[configuration](configuration.md). ### How to use **--strategy**? diff --git a/docs/telegram-usage.md b/docs/telegram-usage.md index 381a47ae9..4cc8eaa5c 100644 --- a/docs/telegram-usage.md +++ b/docs/telegram-usage.md @@ -65,14 +65,14 @@ Once all positions are sold, run `/stop` to completely stop the bot. For each open trade, the bot will send you the following message. -> **Trade ID:** `123` `(since 1 days ago)` -> **Current Pair:** CVC/BTC -> **Open Since:** `1 days ago` -> **Amount:** `26.64180098` -> **Open Rate:** `0.00007489` -> **Current Rate:** `0.00007489` -> **Current Profit:** `12.95%` -> **Stoploss:** `0.00007389 (-0.02%)` +> **Trade ID:** `123` `(since 1 days ago)` +> **Current Pair:** CVC/BTC +> **Open Since:** `1 days ago` +> **Amount:** `26.64180098` +> **Open Rate:** `0.00007489` +> **Current Rate:** `0.00007489` +> **Current Profit:** `12.95%` +> **Stoploss:** `0.00007389 (-0.02%)` ### /status table @@ -97,18 +97,18 @@ current max Return a summary of your profit/loss and performance. -> **ROI:** Close trades -> ∙ `0.00485701 BTC (258.45%)` -> ∙ `62.968 USD` -> **ROI:** All trades -> ∙ `0.00255280 BTC (143.43%)` -> ∙ `33.095 EUR` -> -> **Total Trade Count:** `138` -> **First Trade opened:** `3 days ago` -> **Latest Trade opened:** `2 minutes ago` -> **Avg. Duration:** `2:33:45` -> **Best Performing:** `PAY/BTC: 50.23%` +> **ROI:** Close trades +> ∙ `0.00485701 BTC (258.45%)` +> ∙ `62.968 USD` +> **ROI:** All trades +> ∙ `0.00255280 BTC (143.43%)` +> ∙ `33.095 EUR` +> +> **Total Trade Count:** `138` +> **First Trade opened:** `3 days ago` +> **Latest Trade opened:** `2 minutes ago` +> **Avg. Duration:** `2:33:45` +> **Best Performing:** `PAY/BTC: 50.23%` ### /forcesell @@ -126,26 +126,26 @@ Note that for this to work, `forcebuy_enable` needs to be set to true. Return the performance of each crypto-currency the bot has sold. > Performance: -> 1. `RCN/BTC 57.77%` -> 2. `PAY/BTC 56.91%` -> 3. `VIB/BTC 47.07%` -> 4. `SALT/BTC 30.24%` -> 5. `STORJ/BTC 27.24%` -> ... +> 1. `RCN/BTC 57.77%` +> 2. `PAY/BTC 56.91%` +> 3. `VIB/BTC 47.07%` +> 4. `SALT/BTC 30.24%` +> 5. `STORJ/BTC 27.24%` +> ... ### /balance Return the balance of all crypto-currency your have on the exchange. -> **Currency:** BTC -> **Available:** 3.05890234 -> **Balance:** 3.05890234 -> **Pending:** 0.0 +> **Currency:** BTC +> **Available:** 3.05890234 +> **Balance:** 3.05890234 +> **Pending:** 0.0 -> **Currency:** CVC -> **Available:** 86.64180098 -> **Balance:** 86.64180098 -> **Pending:** 0.0 +> **Currency:** CVC +> **Available:** 86.64180098 +> **Balance:** 86.64180098 +> **Pending:** 0.0 ### /daily From 34b40500c33dbddbd60f45ae9e8dcf4e619b0b18 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Mon, 1 Apr 2019 20:45:59 +0300 Subject: [PATCH 27/57] Check whitelist fetched from config for emptiness --- freqtrade/freqtradebot.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index e9b8b1956..42013c85f 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -79,6 +79,9 @@ class FreqtradeBot(object): self.config.get('edge', {}).get('enabled', False) else None self.active_pair_whitelist: List[str] = self.config['exchange']['pair_whitelist'] + if not self.active_pair_whitelist: + raise DependencyException('Whitelist is empty.') + self._init_modules() # Tell the systemd that we completed initialization phase From ab0e657d7711bdb4a1ff7b60652a4e2030b8cbc3 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Mon, 1 Apr 2019 21:36:53 +0300 Subject: [PATCH 28/57] Check for empty whitelist moved to _process() --- freqtrade/freqtradebot.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 42013c85f..e5400c1d0 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -79,8 +79,6 @@ class FreqtradeBot(object): self.config.get('edge', {}).get('enabled', False) else None self.active_pair_whitelist: List[str] = self.config['exchange']['pair_whitelist'] - if not self.active_pair_whitelist: - raise DependencyException('Whitelist is empty.') self._init_modules() @@ -198,6 +196,9 @@ class FreqtradeBot(object): # Refresh whitelist self.pairlists.refresh_pairlist() self.active_pair_whitelist = self.pairlists.whitelist + if not self.active_pair_whitelist: + logger.warning('Whitelist is empty.') + return False # Calculating Edge positioning if self.edge: From a6daf0d991469e5db365a3106bd848a22fb1f5a7 Mon Sep 17 00:00:00 2001 From: Misagh Date: Tue, 2 Apr 2019 20:00:58 +0200 Subject: [PATCH 29/57] formatting pct --- freqtrade/rpc/telegram.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index ca9b376ec..9d1f9f206 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -205,9 +205,11 @@ class Telegram(RPC): "*Current Rate:* `{current_rate:.8f}`", "*Close Profit:* `{close_profit}`" if r['close_profit'] else "", "*Current Profit:* `{current_profit:.2f}%`", - "*Initial Stoploss:* `{initial_stop_loss:.8f}` `({initial_stop_loss_pct}%)`" + "*Initial Stoploss:* `{initial_stop_loss:.8f}` " + + ("`({initial_stop_loss_pct:.2f}%)`" if r['initial_stop_loss_pct'] else "") if r['stop_loss'] != r['initial_stop_loss'] else "", - "*Stoploss:* `{stop_loss:.8f}` `({stop_loss_pct}%)`", + "*Stoploss:* `{stop_loss:.8f}` " + + ("`({stop_loss_pct:.2f}%)`" if r['stop_loss_pct'] else ""), "*Open Order:* `{open_order}`" if r['open_order'] else "", ] messages.append("\n".join(filter(None, lines)).format(**r)) From 7b39a3084fc15406cd56fc17ea4eb8a539d47d13 Mon Sep 17 00:00:00 2001 From: Misagh Date: Tue, 2 Apr 2019 20:08:10 +0200 Subject: [PATCH 30/57] formatting and readability --- freqtrade/rpc/telegram.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 9d1f9f206..2d822820f 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -205,12 +205,17 @@ class Telegram(RPC): "*Current Rate:* `{current_rate:.8f}`", "*Close Profit:* `{close_profit}`" if r['close_profit'] else "", "*Current Profit:* `{current_profit:.2f}%`", + + # Adding initial stoploss only if it is different from stoploss "*Initial Stoploss:* `{initial_stop_loss:.8f}` " + ("`({initial_stop_loss_pct:.2f}%)`" if r['initial_stop_loss_pct'] else "") if r['stop_loss'] != r['initial_stop_loss'] else "", + + # Adding stoploss and stoploss percentage only if it is not None "*Stoploss:* `{stop_loss:.8f}` " + ("`({stop_loss_pct:.2f}%)`" if r['stop_loss_pct'] else ""), - "*Open Order:* `{open_order}`" if r['open_order'] else "", + + "*Open Order:* `{open_order}`" if r['open_order'] else "" ] messages.append("\n".join(filter(None, lines)).format(**r)) From 62141d3d2790c55d779b296528e224754d1fae7f Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 2 Apr 2019 21:57:52 +0300 Subject: [PATCH 31/57] 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 32/57] 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 33/57] 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 53eaf85969b31b282b5bf5196d7acde51145d5f7 Mon Sep 17 00:00:00 2001 From: Misagh Date: Wed, 3 Apr 2019 14:03:28 +0200 Subject: [PATCH 34/57] filtering edge pairs for RPC --- freqtrade/edge/__init__.py | 18 ++++++++++++++++++ freqtrade/rpc/rpc.py | 11 +---------- freqtrade/rpc/telegram.py | 1 - 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/freqtrade/edge/__init__.py b/freqtrade/edge/__init__.py index b4dfa5624..0f70df43b 100644 --- a/freqtrade/edge/__init__.py +++ b/freqtrade/edge/__init__.py @@ -203,6 +203,24 @@ class Edge(): return self._final_pairs + def accepted_pairs(self) -> list: + """ + return a list of accepted pairs along with their winrate, expectancy and stoploss + ex: + #[{'Pair': 'ADX/ETH', 'Winrate': 0.08333333333333333, 'Expectancy': -0.8105153934775888, 'Stoploss': -0.02}] + """ + final = [] + for pair, info in self._cached_pairs.items(): + if info.expectancy > float(self.edge_config.get('minimum_expectancy', 0.2)) and \ + info.winrate > float(self.edge_config.get('minimum_winrate', 0.60)): + final.append({ + 'Pair': pair, + 'Winrate': info.winrate, + 'Expectancy': info.expectancy, + 'Stoploss': info.stoploss, + }) + return final + def _fill_calculable_fields(self, result: DataFrame) -> DataFrame: """ The result frame contains a number of columns that are calculable diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 5308c9d51..79bfffb1d 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -484,13 +484,4 @@ class RPC(object): """ Returns information related to Edge """ if not self._freqtrade.edge: raise RPCException(f'Edge is not enabled.') - - return [ - { - 'Pair': k, - 'Winrate': v.winrate, - 'Expectancy': v.expectancy, - 'Stoploss': v.stoploss, - } - for k, v in self._freqtrade.edge._cached_pairs.items() - ] + return self._freqtrade.edge.accepted_pairs() \ No newline at end of file diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 2d822820f..8ff59d759 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -511,7 +511,6 @@ class Telegram(RPC): """ try: edge_pairs = self._rpc_edge() - print(edge_pairs) edge_pairs_tab = tabulate(edge_pairs, headers='keys', tablefmt='simple') message = f'Edge only validated following pairs:\n
{edge_pairs_tab}
' self._send_msg(message, bot=bot, parse_mode=ParseMode.HTML) From 5f38d5ee6309aba3852ee832b6ecbff6ce50b5fe Mon Sep 17 00:00:00 2001 From: Misagh Date: Wed, 3 Apr 2019 14:07:33 +0200 Subject: [PATCH 35/57] removing % sign as it is already a pct --- freqtrade/rpc/telegram.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 2d822820f..81f9dc4f0 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -208,12 +208,12 @@ class Telegram(RPC): # Adding initial stoploss only if it is different from stoploss "*Initial Stoploss:* `{initial_stop_loss:.8f}` " + - ("`({initial_stop_loss_pct:.2f}%)`" if r['initial_stop_loss_pct'] else "") + ("`({initial_stop_loss_pct:.2f})`" if r['initial_stop_loss_pct'] else "") if r['stop_loss'] != r['initial_stop_loss'] else "", # Adding stoploss and stoploss percentage only if it is not None "*Stoploss:* `{stop_loss:.8f}` " + - ("`({stop_loss_pct:.2f}%)`" if r['stop_loss_pct'] else ""), + ("`({stop_loss_pct:.2f})`" if r['stop_loss_pct'] else ""), "*Open Order:* `{open_order}`" if r['open_order'] else "" ] From a3835b1279d0d0b7fd7df2b34d9a74d6b2db0cf8 Mon Sep 17 00:00:00 2001 From: Misagh Date: Wed, 3 Apr 2019 14:14:47 +0200 Subject: [PATCH 36/57] flake8 --- freqtrade/edge/__init__.py | 16 +++++++--------- freqtrade/rpc/rpc.py | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/freqtrade/edge/__init__.py b/freqtrade/edge/__init__.py index 0f70df43b..7bda942d2 100644 --- a/freqtrade/edge/__init__.py +++ b/freqtrade/edge/__init__.py @@ -206,19 +206,17 @@ class Edge(): def accepted_pairs(self) -> list: """ return a list of accepted pairs along with their winrate, expectancy and stoploss - ex: - #[{'Pair': 'ADX/ETH', 'Winrate': 0.08333333333333333, 'Expectancy': -0.8105153934775888, 'Stoploss': -0.02}] """ final = [] for pair, info in self._cached_pairs.items(): if info.expectancy > float(self.edge_config.get('minimum_expectancy', 0.2)) and \ - info.winrate > float(self.edge_config.get('minimum_winrate', 0.60)): - final.append({ - 'Pair': pair, - 'Winrate': info.winrate, - 'Expectancy': info.expectancy, - 'Stoploss': info.stoploss, - }) + info.winrate > float(self.edge_config.get('minimum_winrate', 0.60)): + final.append({ + 'Pair': pair, + 'Winrate': info.winrate, + 'Expectancy': info.expectancy, + 'Stoploss': info.stoploss, + }) return final def _fill_calculable_fields(self, result: DataFrame) -> DataFrame: diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 79bfffb1d..ceab00373 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -484,4 +484,4 @@ class RPC(object): """ Returns information related to Edge """ if not self._freqtrade.edge: raise RPCException(f'Edge is not enabled.') - return self._freqtrade.edge.accepted_pairs() \ No newline at end of file + return self._freqtrade.edge.accepted_pairs() From 67eeb145e189b9cc6a1adec0003334c795a6aac8 Mon Sep 17 00:00:00 2001 From: Misagh Date: Wed, 3 Apr 2019 14:31:00 +0200 Subject: [PATCH 37/57] flake8 --- freqtrade/edge/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/freqtrade/edge/__init__.py b/freqtrade/edge/__init__.py index 7bda942d2..4801c6cb3 100644 --- a/freqtrade/edge/__init__.py +++ b/freqtrade/edge/__init__.py @@ -211,12 +211,12 @@ class Edge(): for pair, info in self._cached_pairs.items(): if info.expectancy > float(self.edge_config.get('minimum_expectancy', 0.2)) and \ info.winrate > float(self.edge_config.get('minimum_winrate', 0.60)): - final.append({ - 'Pair': pair, - 'Winrate': info.winrate, - 'Expectancy': info.expectancy, - 'Stoploss': info.stoploss, - }) + final.append({ + 'Pair': pair, + 'Winrate': info.winrate, + 'Expectancy': info.expectancy, + 'Stoploss': info.stoploss, + }) return final def _fill_calculable_fields(self, result: DataFrame) -> DataFrame: From eb610441b574eaac2f8603ad9f4df59a0286b932 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Wed, 3 Apr 2019 12:38:06 +0000 Subject: [PATCH 38/57] Update ccxt from 1.18.425 to 1.18.430 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 83d77b693..914c8e644 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.425 +ccxt==1.18.430 SQLAlchemy==1.3.1 python-telegram-bot==11.1.0 arrow==0.13.1 From 92dc3c89afb525d5276c31f9e631799f45574c3f Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Wed, 3 Apr 2019 12:38:07 +0000 Subject: [PATCH 39/57] Update sqlalchemy from 1.3.1 to 1.3.2 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 914c8e644..afb422e4b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ ccxt==1.18.430 -SQLAlchemy==1.3.1 +SQLAlchemy==1.3.2 python-telegram-bot==11.1.0 arrow==0.13.1 cachetools==3.1.0 From a3fe5f57579b730705390c4e6e0e2e6f4f98ac9b Mon Sep 17 00:00:00 2001 From: Misagh Date: Wed, 3 Apr 2019 16:28:44 +0200 Subject: [PATCH 40/57] adding stake amount to telegram message --- freqtrade/rpc/rpc.py | 2 ++ freqtrade/rpc/telegram.py | 2 +- freqtrade/tests/rpc/test_rpc.py | 4 ++++ freqtrade/tests/rpc/test_rpc_telegram.py | 2 ++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 5308c9d51..ec6c1feed 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -103,11 +103,13 @@ class RPC(object): results.append(dict( trade_id=trade.id, pair=trade.pair, + base_currency=self._freqtrade.config['stake_currency'], date=arrow.get(trade.open_date), open_rate=trade.open_rate, close_rate=trade.close_rate, current_rate=current_rate, amount=round(trade.amount, 8), + stake_amount=round(trade.amount, 8), close_profit=fmt_close_profit, current_profit=round(current_profit * 100, 2), stop_loss=trade.stop_loss, diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 2d822820f..9a64f5197 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -199,7 +199,7 @@ class Telegram(RPC): lines = [ "*Trade ID:* `{trade_id}` `(since {date})`", "*Current Pair:* {pair}", - "*Amount:* `{amount}`", + "*Amount:* `{amount} ({stake_amount} {base_currency})`", "*Open Rate:* `{open_rate:.8f}`", "*Close Rate:* `{close_rate}`" if r['close_rate'] else "", "*Current Rate:* `{current_rate:.8f}`", diff --git a/freqtrade/tests/rpc/test_rpc.py b/freqtrade/tests/rpc/test_rpc.py index b454f9cd8..981d3edfb 100644 --- a/freqtrade/tests/rpc/test_rpc.py +++ b/freqtrade/tests/rpc/test_rpc.py @@ -51,11 +51,13 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None: assert { 'trade_id': 1, 'pair': 'ETH/BTC', + 'base_currency': 'BTC', 'date': ANY, 'open_rate': 1.099e-05, 'close_rate': None, 'current_rate': 1.098e-05, 'amount': 90.99181074, + 'stake_amount': 90.99181074, 'close_profit': None, 'current_profit': -0.59, 'stop_loss': 0.0, @@ -75,11 +77,13 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None: assert { 'trade_id': 1, 'pair': 'ETH/BTC', + 'base_currency': 'BTC', 'date': ANY, 'open_rate': 1.099e-05, 'close_rate': None, 'current_rate': ANY, 'amount': 90.99181074, + 'stake_amount': 90.99181074, 'close_profit': None, 'current_profit': ANY, 'stop_loss': 0.0, diff --git a/freqtrade/tests/rpc/test_rpc_telegram.py b/freqtrade/tests/rpc/test_rpc_telegram.py index b6d12fe41..8f43d7ed0 100644 --- a/freqtrade/tests/rpc/test_rpc_telegram.py +++ b/freqtrade/tests/rpc/test_rpc_telegram.py @@ -189,11 +189,13 @@ def test_status(default_conf, update, mocker, fee, ticker, markets) -> None: _rpc_trade_status=MagicMock(return_value=[{ 'trade_id': 1, 'pair': 'ETH/BTC', + 'base_currency': 'BTC', 'date': arrow.utcnow(), 'open_rate': 1.099e-05, 'close_rate': None, 'current_rate': 1.098e-05, 'amount': 90.99181074, + 'stake_amount': 90.99181074, 'close_profit': None, 'current_profit': -0.59, 'initial_stop_loss': 1.098e-05, From d5498c87123e21cddb7d65e3a186eeb19e62aec0 Mon Sep 17 00:00:00 2001 From: Misagh Date: Wed, 3 Apr 2019 19:29:44 +0200 Subject: [PATCH 41/57] adding % --- freqtrade/rpc/rpc.py | 4 ++-- freqtrade/rpc/telegram.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 5308c9d51..0a6a2388a 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -111,9 +111,9 @@ class RPC(object): close_profit=fmt_close_profit, current_profit=round(current_profit * 100, 2), stop_loss=trade.stop_loss, - stop_loss_pct=trade.stop_loss_pct, + stop_loss_pct=(trade.stop_loss_pct * 100), initial_stop_loss=trade.initial_stop_loss, - initial_stop_loss_pct=trade.initial_stop_loss_pct, + initial_stop_loss_pct=(trade.initial_stop_loss_pct * 100), open_order='({} {} rem={:.8f})'.format( order['type'], order['side'], order['remaining'] ) if order else None, diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 81f9dc4f0..2d822820f 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -208,12 +208,12 @@ class Telegram(RPC): # Adding initial stoploss only if it is different from stoploss "*Initial Stoploss:* `{initial_stop_loss:.8f}` " + - ("`({initial_stop_loss_pct:.2f})`" if r['initial_stop_loss_pct'] else "") + ("`({initial_stop_loss_pct:.2f}%)`" if r['initial_stop_loss_pct'] else "") if r['stop_loss'] != r['initial_stop_loss'] else "", # Adding stoploss and stoploss percentage only if it is not None "*Stoploss:* `{stop_loss:.8f}` " + - ("`({stop_loss_pct:.2f})`" if r['stop_loss_pct'] else ""), + ("`({stop_loss_pct:.2f}%)`" if r['stop_loss_pct'] else ""), "*Open Order:* `{open_order}`" if r['open_order'] else "" ] From 3c399fbe3fe1edd1ea1dab04898201edcf1026a3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 3 Apr 2019 19:51:46 +0200 Subject: [PATCH 42/57] Improve whitelist wordings --- freqtrade/freqtradebot.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index e5400c1d0..bf445e56d 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -79,7 +79,6 @@ 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 @@ -196,9 +195,6 @@ class FreqtradeBot(object): # Refresh whitelist self.pairlists.refresh_pairlist() self.active_pair_whitelist = self.pairlists.whitelist - if not self.active_pair_whitelist: - logger.warning('Whitelist is empty.') - return False # Calculating Edge positioning if self.edge: @@ -360,6 +356,10 @@ class FreqtradeBot(object): interval = self.strategy.ticker_interval whitelist = copy.deepcopy(self.active_pair_whitelist) + if not whitelist: + logger.warning("Whitelist is empty.") + return False + # Remove currently opened and latest pairs from whitelist for trade in Trade.get_open_trades(): if trade.pair in whitelist: @@ -367,7 +367,7 @@ class FreqtradeBot(object): logger.debug('Ignoring %s in pair whitelist', trade.pair) if not whitelist: - logger.info("No currency pair left in whitelist, no more trade can be created.") + logger.info("No currency pair in whitelist, but checking to sell open trades.") return False # running get_signal on historical data fetched From 1a5b0969b952603fa54a6dedd446ad93fcaf2caf Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 3 Apr 2019 19:52:06 +0200 Subject: [PATCH 43/57] Fix tests (both tests where testing the same thing) --- freqtrade/tests/test_freqtradebot.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 4e145c354..fc13bc0f1 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -570,7 +570,8 @@ def test_create_trade_limit_reached(default_conf, ticker, limit_buy_order, assert freqtrade._get_trade_stake_amount('ETH/BTC') is None -def test_create_trade_no_pairs(default_conf, ticker, limit_buy_order, fee, markets, mocker) -> None: +def test_create_trade_no_pairs_let(default_conf, ticker, limit_buy_order, fee, + markets, mocker, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -582,16 +583,17 @@ def test_create_trade_no_pairs(default_conf, ticker, limit_buy_order, fee, marke ) default_conf['exchange']['pair_whitelist'] = ["ETH/BTC"] - default_conf['exchange']['pair_blacklist'] = ["ETH/BTC"] freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - freqtrade.create_trade() + assert freqtrade.create_trade() assert not freqtrade.create_trade() + assert log_has("No currency pair in whitelist, but checking to sell open trades.", + caplog.record_tuples) -def test_create_trade_no_pairs_after_blacklist(default_conf, ticker, - limit_buy_order, fee, markets, mocker) -> None: +def test_create_trade_no_pairs_in_whitelist(default_conf, ticker, limit_buy_order, fee, + markets, mocker, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -601,13 +603,12 @@ def test_create_trade_no_pairs_after_blacklist(default_conf, ticker, get_fee=fee, markets=PropertyMock(return_value=markets) ) - default_conf['exchange']['pair_whitelist'] = ["ETH/BTC"] - default_conf['exchange']['pair_blacklist'] = ["ETH/BTC"] + default_conf['exchange']['pair_whitelist'] = [] freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - freqtrade.create_trade() assert not freqtrade.create_trade() + assert log_has("Whitelist is empty.", caplog.record_tuples) def test_create_trade_no_signal(default_conf, fee, mocker) -> None: From 0307ba7883c55eb7b2a763feda5373df39f9210c Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 3 Apr 2019 20:04:04 +0200 Subject: [PATCH 44/57] Remove one branch - python does lazy evaluation --- freqtrade/optimize/backtesting.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index f3661ab32..0fcfd11f1 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -337,7 +337,6 @@ class Backtesting(object): # Loop timerange and test per pair while tmp < end_date: - # print(f"time: {tmp}") for i, pair in enumerate(ticker): if pair not in indexes: @@ -358,9 +357,9 @@ class Backtesting(object): if row.buy == 0 or row.sell == 1: continue # skip rows where no buy signal or that would immediately sell off - if not position_stacking: - if pair in lock_pair_until and row.date <= lock_pair_until[pair]: - continue + if (not position_stacking and pair in lock_pair_until + and row.date <= lock_pair_until[pair]): + continue if max_open_trades > 0: # Check if max_open_trades has already been reached for the given date if not trade_count_lock.get(row.date, 0) < max_open_trades: From 9ee1dd99eb0e7989abb55dbbeb549ea156d4396e Mon Sep 17 00:00:00 2001 From: Misagh Date: Wed, 3 Apr 2019 20:28:03 +0200 Subject: [PATCH 45/57] tests fixed --- freqtrade/rpc/rpc.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 0a6a2388a..090eb295f 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -111,11 +111,13 @@ class RPC(object): close_profit=fmt_close_profit, current_profit=round(current_profit * 100, 2), stop_loss=trade.stop_loss, - stop_loss_pct=(trade.stop_loss_pct * 100), + stop_loss_pct=(trade.stop_loss_pct * 100) \ + if trade.stop_loss_pct else None, initial_stop_loss=trade.initial_stop_loss, - initial_stop_loss_pct=(trade.initial_stop_loss_pct * 100), + initial_stop_loss_pct=(trade.initial_stop_loss_pct * 100) \ + if trade.initial_stop_loss_pct else None, open_order='({} {} rem={:.8f})'.format( - order['type'], order['side'], order['remaining'] + order['type'], order['side'], order['remaining'] ) if order else None, )) return results From 5488c66f536672d22d529450905767c637f5cc5a Mon Sep 17 00:00:00 2001 From: Misagh Date: Wed, 3 Apr 2019 20:35:37 +0200 Subject: [PATCH 46/57] flake8 --- freqtrade/rpc/rpc.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 090eb295f..e59fb73f3 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -111,11 +111,11 @@ class RPC(object): close_profit=fmt_close_profit, current_profit=round(current_profit * 100, 2), stop_loss=trade.stop_loss, - stop_loss_pct=(trade.stop_loss_pct * 100) \ - if trade.stop_loss_pct else None, + stop_loss_pct=(trade.stop_loss_pct * 100) + if trade.stop_loss_pct else None, initial_stop_loss=trade.initial_stop_loss, - initial_stop_loss_pct=(trade.initial_stop_loss_pct * 100) \ - if trade.initial_stop_loss_pct else None, + initial_stop_loss_pct=(trade.initial_stop_loss_pct * 100) + if trade.initial_stop_loss_pct else None, open_order='({} {} rem={:.8f})'.format( order['type'], order['side'], order['remaining'] ) if order else None, From 65350ad55235d8fd12356dacf9ddc1dccbf56d61 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Wed, 3 Apr 2019 22:14:42 +0300 Subject: [PATCH 47/57] 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): From 0cdbe714d23356375c21aa7d69184e1c7e5a0a32 Mon Sep 17 00:00:00 2001 From: Misagh Date: Thu, 4 Apr 2019 12:06:45 +0200 Subject: [PATCH 48/57] stake amount not amount --- freqtrade/rpc/rpc.py | 2 +- freqtrade/tests/rpc/test_rpc.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index e3276c53f..88ade0c27 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -109,7 +109,7 @@ class RPC(object): close_rate=trade.close_rate, current_rate=current_rate, amount=round(trade.amount, 8), - stake_amount=round(trade.amount, 8), + stake_amount=round(trade.stake_amount, 8), close_profit=fmt_close_profit, current_profit=round(current_profit * 100, 2), stop_loss=trade.stop_loss, diff --git a/freqtrade/tests/rpc/test_rpc.py b/freqtrade/tests/rpc/test_rpc.py index 981d3edfb..8b10a1314 100644 --- a/freqtrade/tests/rpc/test_rpc.py +++ b/freqtrade/tests/rpc/test_rpc.py @@ -57,7 +57,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None: 'close_rate': None, 'current_rate': 1.098e-05, 'amount': 90.99181074, - 'stake_amount': 90.99181074, + 'stake_amount': 0.001, 'close_profit': None, 'current_profit': -0.59, 'stop_loss': 0.0, @@ -83,7 +83,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None: 'close_rate': None, 'current_rate': ANY, 'amount': 90.99181074, - 'stake_amount': 90.99181074, + 'stake_amount': 0.001, 'close_profit': None, 'current_profit': ANY, 'stop_loss': 0.0, From 6afe232c4d56f840266a7298ea963fbbd4337a5a Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Thu, 4 Apr 2019 12:38:05 +0000 Subject: [PATCH 49/57] Update ccxt from 1.18.430 to 1.18.432 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index afb422e4b..eb5d564fa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.430 +ccxt==1.18.432 SQLAlchemy==1.3.2 python-telegram-bot==11.1.0 arrow==0.13.1 From ebeaf64fbbd45aa7f7e64f25286ad10aa8625de9 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Thu, 4 Apr 2019 12:38:06 +0000 Subject: [PATCH 50/57] Update mypy from 0.670 to 0.700 --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 69082587a..c59923ed2 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -9,4 +9,4 @@ pytest-mock==1.10.3 pytest-asyncio==0.10.0 pytest-cov==2.6.1 coveralls==1.7.0 -mypy==0.670 +mypy==0.700 From 32cbb714f9bfe415a737f8c908e41a802c4ac666 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 4 Apr 2019 19:44:03 +0200 Subject: [PATCH 51/57] Improve commenting on backtsting and backtest_multi_tst --- freqtrade/optimize/backtesting.py | 9 +++++++-- freqtrade/tests/optimize/test_backtesting.py | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 0fcfd11f1..fc007ce2b 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -329,6 +329,8 @@ class Backtesting(object): end_date = args['end_date'] trades = [] trade_count_lock: Dict = {} + + # Dict of ticker-lists for performance (looping lists is a lot faster than dataframes) ticker: Dict = self._get_ticker_list(processed) lock_pair_until: Dict = {} @@ -345,10 +347,11 @@ class Backtesting(object): try: row = ticker[pair][indexes[pair]] except IndexError: - # missing Data for one pair ... + # missing Data for one pair at the end. # Warnings for this are shown by `validate_backtest_data` continue + # Waits until the time-counter reaches the start of the data for this pair. if row.date > tmp.datetime: continue @@ -359,12 +362,13 @@ class Backtesting(object): if (not position_stacking and pair in lock_pair_until and row.date <= lock_pair_until[pair]): + # without positionstacking, we can only have one open trade per pair. continue + if max_open_trades > 0: # Check if max_open_trades has already been reached for the given date if not trade_count_lock.get(row.date, 0) < max_open_trades: continue - trade_count_lock[row.date] = trade_count_lock.get(row.date, 0) + 1 trade_entry = self._get_sell_trade_entry(pair, row, ticker[pair][indexes[pair]:], @@ -377,6 +381,7 @@ class Backtesting(object): # Set lock_pair_until to end of testing period if trade could not be closed lock_pair_until[pair] = end_date.datetime + # Move time one configured time_interval ahead. tmp += timedelta(minutes=self.ticker_interval_mins) return DataFrame.from_records(trades, columns=BacktestResult._fields) diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py index 64a33fae2..edfe02225 100644 --- a/freqtrade/tests/optimize/test_backtesting.py +++ b/freqtrade/tests/optimize/test_backtesting.py @@ -701,9 +701,13 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair): mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) patch_exchange(mocker) + pairs = ['ADA/BTC', 'DASH/BTC', 'ETH/BTC', 'LTC/BTC', 'NXT/BTC'] data = history.load_data(datadir=None, ticker_interval='5m', pairs=pairs) + # Only use 500 lines to increase performance data = trim_dictlist(data, -500) + + # Remove data for one pair from the beginning of the data data[pair] = data[pair][tres:] # We need to enable sell-signal - otherwise it sells on ROI!! default_conf['experimental'] = {"use_sell_signal": True} From 7010c835d24bdcd5cfa3420d56d68906b2cdb60e Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 4 Apr 2019 20:23:10 +0200 Subject: [PATCH 52/57] Improve commentign --- freqtrade/optimize/backtesting.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index fc007ce2b..bbbda9b1f 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -334,10 +334,11 @@ class Backtesting(object): ticker: Dict = self._get_ticker_list(processed) lock_pair_until: Dict = {} + # Indexes per pair, so some pairs are allowed to have a missing start. indexes: Dict = {} tmp = start_date + timedelta(minutes=self.ticker_interval_mins) - # Loop timerange and test per pair + # Loop timerange and get candle for each pair at that point in time while tmp < end_date: for i, pair in enumerate(ticker): From e3cdc0a05bcdc9ffd3901b73c64c54abf438de44 Mon Sep 17 00:00:00 2001 From: iuvbio Date: Thu, 4 Apr 2019 20:53:28 +0200 Subject: [PATCH 53/57] typos and visual fixes --- docs/sql_cheatsheet.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/sql_cheatsheet.md b/docs/sql_cheatsheet.md index 54f9b8213..803a2b48a 100644 --- a/docs/sql_cheatsheet.md +++ b/docs/sql_cheatsheet.md @@ -1,5 +1,5 @@ # SQL Helper -This page constains some help if you want to edit your sqlite db. +This page contains some help if you want to edit your sqlite db. ## Install sqlite3 **Ubuntu/Debian installation** @@ -66,11 +66,13 @@ SELECT * FROM trades; ## Fix trade still open after a manual sell on the exchange !!! Warning - Manually selling on the exchange should not be done by default, since the bot does not detect this and will try to sell anyway. - /foresell should accomplish the same thing. + Manually selling a pair on the exchange will not be detected by the bot and it will try to sell anyway. + Whenever possible, /forcesell should be used to accomplish the same thing. + + It is strongly advised to backup your database file before making any manual changes. !!! Note - This should not be necessary after /forcesell, as forcesell orders are closed automatically by the bot on the next iteration. + This should not be necessary after /forcesell, as forcesell orders are closed automatically by the bot on the next iteration. ```sql UPDATE trades From 7486cb7c64d6b50b5b934ffb2fa39fb3c215a8f3 Mon Sep 17 00:00:00 2001 From: iuvbio Date: Thu, 4 Apr 2019 21:05:26 +0200 Subject: [PATCH 54/57] fix admonitions --- docs/hyperopt.md | 4 ++-- docs/sql_cheatsheet.md | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/hyperopt.md b/docs/hyperopt.md index e25f35c35..b4e42de16 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -62,7 +62,7 @@ If you have updated the buy strategy, ie. changed the contents of #### Sell optimization -Similar to the buy-signal above, sell-signals can also be optimized. +Similar to the buy-signal above, sell-signals can also be optimized. Place the corresponding settings into the following methods * Inside `sell_indicator_space()` - the parameters hyperopt shall be optimizing. @@ -163,7 +163,7 @@ running at least several thousand evaluations. The `--spaces all` flag determines that all possible parameters should be optimized. Possibilities are listed below. !!! Warning -When switching parameters or changing configuration options, the file `user_data/hyperopt_results.pickle` should be removed. It's used to be able to continue interrupted calculations, but does not detect changes to settings or the hyperopt file. + When switching parameters or changing configuration options, the file `user_data/hyperopt_results.pickle` should be removed. It's used to be able to continue interrupted calculations, but does not detect changes to settings or the hyperopt file. ### Execute Hyperopt with Different Ticker-Data Source diff --git a/docs/sql_cheatsheet.md b/docs/sql_cheatsheet.md index 803a2b48a..f41520bd9 100644 --- a/docs/sql_cheatsheet.md +++ b/docs/sql_cheatsheet.md @@ -66,9 +66,7 @@ SELECT * FROM trades; ## Fix trade still open after a manual sell on the exchange !!! Warning - Manually selling a pair on the exchange will not be detected by the bot and it will try to sell anyway. - Whenever possible, /forcesell should be used to accomplish the same thing. - + Manually selling a pair on the exchange will not be detected by the bot and it will try to sell anyway. Whenever possible, forcesell should be used to accomplish the same thing. It is strongly advised to backup your database file before making any manual changes. !!! Note From dbb1bbf1019657969d7bc68ff541f6c9ea128c0e Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 5 Apr 2019 06:47:03 +0200 Subject: [PATCH 55/57] Fix webhook documentation --- docs/webhook-config.md | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/docs/webhook-config.md b/docs/webhook-config.md index 2b5365e32..43b036c74 100644 --- a/docs/webhook-config.md +++ b/docs/webhook-config.md @@ -39,32 +39,30 @@ Different payloads can be configured for different events. Not all fields are ne The fields in `webhook.webhookbuy` are filled when the bot executes a buy. Parameters are filled using string.format. Possible parameters are: -* exchange -* pair -* limit -* stake_amount -* stake_amount_fiat -* stake_currency -* fiat_currency +* `exchange` +* `pair` +* `limit` +* `stake_amount` +* `stake_currency` +* `fiat_currency` ### Webhooksell The fields in `webhook.webhooksell` are filled when the bot sells a trade. Parameters are filled using string.format. Possible parameters are: -* exchange -* pair -* gain -* limit -* amount -* open_rate -* current_rate -* profit_amount -* profit_percent -* profit_fiat -* stake_currency -* fiat_currency -* sell_reason +* `exchange` +* `pair` +* `gain` +* `limit` +* `amount` +* `open_rate` +* `current_rate` +* `profit_amount` +* `profit_percent` +* `stake_currency` +* `fiat_currency` +* `sell_reason` ### Webhookstatus From ac1964edb16dd0bf13fd1bef6bc59ce9519b229b Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 5 Apr 2019 06:49:15 +0200 Subject: [PATCH 56/57] Remove unnecessary comment --- docs/telegram-usage.md | 4 ++-- docs/webhook-config.md | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/telegram-usage.md b/docs/telegram-usage.md index 4cc8eaa5c..9d6877318 100644 --- a/docs/telegram-usage.md +++ b/docs/telegram-usage.md @@ -1,13 +1,13 @@ # Telegram usage -This page explains how to command your bot with Telegram. - ## Prerequisite + To control your bot with Telegram, you need first to [set up a Telegram bot](installation.md) and add your Telegram API keys into your config file. ## Telegram commands + Per default, the Telegram bot shows predefined commands. Some commands are only available by sending them to the bot. The table below list the official commands. You can ask at any moment for help with `/help`. diff --git a/docs/webhook-config.md b/docs/webhook-config.md index 43b036c74..811b57f9b 100644 --- a/docs/webhook-config.md +++ b/docs/webhook-config.md @@ -1,7 +1,5 @@ # Webhook usage -This page explains how to configure your bot to talk to webhooks. - ## Configuration Enable webhooks by adding a webhook-section to your configuration file, and setting `webhook.enabled` to `true`. From 13e8f25ca97843baf47b4be38515ae830f3a7a39 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 5 Apr 2019 06:51:16 +0200 Subject: [PATCH 57/57] Improve docs layout --- mkdocs.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 9a6fec851..ecac265c1 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -3,11 +3,12 @@ nav: - About: index.md - Installation: installation.md - Configuration: configuration.md - - Start the bot: bot-usage.md - - Stoploss: stoploss.md - Custom Strategy: bot-optimization.md - - Telegram: telegram-usage.md - - Web Hook: webhook-config.md + - Stoploss: stoploss.md + - Start the bot: bot-usage.md + - Control the bot: + - Telegram: telegram-usage.md + - Web Hook: webhook-config.md - Backtesting: backtesting.md - Hyperopt: hyperopt.md - Edge positioning: edge.md