Merge branch 'develop' into config-allow-comments

This commit is contained in:
hroff-1902 2019-08-13 00:14:19 +03:00 committed by GitHub
commit 906be7be7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 494 additions and 500 deletions

View File

@ -59,8 +59,8 @@ working directory.
The bot allows you to use multiple configuration files by specifying multiple The bot allows you to use multiple configuration files by specifying multiple
`-c/--config` options in the command line. Configuration parameters `-c/--config` options in the command line. Configuration parameters
defined in the last configuration file override parameters with the same name defined in the latter configuration files override parameters with the same name
defined in the previous configuration file specified in the command line. defined in the previous configuration files specified in the command line earlier.
For example, you can make a separate configuration file with your key and secrete For example, you can make a separate configuration file with your key and secrete
for the Exchange you use for trading, specify default configuration file with for the Exchange you use for trading, specify default configuration file with
@ -259,12 +259,13 @@ optional arguments:
--continue Continue hyperopt from previous runs. By default, --continue Continue hyperopt from previous runs. By default,
temporary files will be removed and hyperopt will temporary files will be removed and hyperopt will
start from scratch. start from scratch.
--hyperopt-loss NAME --hyperopt-loss NAME Specify the class name of the hyperopt loss function
Specify the class name of the hyperopt loss function
class (IHyperOptLoss). Different functions can class (IHyperOptLoss). Different functions can
generate completely different results, since the generate completely different results, since the
target for optimization is different. (default: target for optimization is different. Built-in
`DefaultHyperOptLoss`). Hyperopt-loss-functions are: DefaultHyperOptLoss,
OnlyProfitHyperOptLoss, SharpeHyperOptLoss.
(default: `DefaultHyperOptLoss`).
``` ```
## Edge commands ## Edge commands

View File

@ -31,6 +31,16 @@ df = load_trades_from_db("sqlite:///tradesv3.sqlite")
df.groupby("pair")["sell_reason"].value_counts() df.groupby("pair")["sell_reason"].value_counts()
``` ```
### Load multiple configuration files
This option can be usefull to inspect the results of passing in multiple configs in case of problems
``` python
from freqtrade.configuration import Configuration
config = Configuration.from_files(["config1.json", "config2.json"])
print(config)
```
## Strategy debugging example ## Strategy debugging example
Debugging a strategy can be time-consuming. FreqTrade offers helper functions to visualize raw data. Debugging a strategy can be time-consuming. FreqTrade offers helper functions to visualize raw data.

View File

@ -17,6 +17,29 @@ Alternatively (if your system is not supported by the setup.sh script), follow t
This will install all required tools for development, including `pytest`, `flake8`, `mypy`, and `coveralls`. This will install all required tools for development, including `pytest`, `flake8`, `mypy`, and `coveralls`.
### Tests
New code should be covered by basic unittests. Depending on the complexity of the feature, Reviewers may request more in-depth unittests.
If necessary, the Freqtrade team can assist and give guidance with writing good tests (however please don't expect anyone to write the tests for you).
#### Checking log content in tests
Freqtrade uses 2 main methods to check log content in tests, `log_has()` and `log_has_re()` (to check using regex, in case of dynamic log-messages).
These are available from `conftest.py` and can be imported in any test module.
A sample check looks as follows:
``` python
from freqtrade.tests.conftest import log_has, log_has_re
def test_method_to_test(caplog):
method_to_test()
assert log_has("This event happened", caplog)
# Check regex with trailing number ...
assert log_has_re(r"This dynamic event happened and produced \d+", caplog)
```
## Modules ## Modules
### Dynamic Pairlist ### Dynamic Pairlist

View File

@ -164,7 +164,11 @@ By default, FreqTrade uses a loss function, which has been with freqtrade since
A different loss function can be specified by using the `--hyperopt-loss <Class-name>` argument. A different loss function can be specified by using the `--hyperopt-loss <Class-name>` argument.
This class should be in its own file within the `user_data/hyperopts/` directory. This class should be in its own file within the `user_data/hyperopts/` directory.
Currently, the following loss functions are builtin: `DefaultHyperOptLoss` (default legacy Freqtrade hyperoptimization loss function), `SharpeHyperOptLoss` (optimizes Sharpe Ratio calculated on the trade returns) and `OnlyProfitHyperOptLoss` (which takes only amount of profit into consideration). Currently, the following loss functions are builtin:
* `DefaultHyperOptLoss` (default legacy Freqtrade hyperoptimization loss function)
* `OnlyProfitHyperOptLoss` (which takes only amount of profit into consideration)
* `SharpeHyperOptLoss` (optimizes Sharpe Ratio calculated on the trade returns)
### Creating and using a custom loss function ### Creating and using a custom loss function

View File

@ -226,7 +226,9 @@ AVAILABLE_CLI_OPTIONS = {
'--hyperopt-loss', '--hyperopt-loss',
help='Specify the class name of the hyperopt loss function class (IHyperOptLoss). ' help='Specify the class name of the hyperopt loss function class (IHyperOptLoss). '
'Different functions can generate completely different results, ' 'Different functions can generate completely different results, '
'since the target for optimization is different. (default: `%(default)s`).', 'since the target for optimization is different. Built-in Hyperopt-loss-functions are: '
'DefaultHyperOptLoss, OnlyProfitHyperOptLoss, SharpeHyperOptLoss.'
'(default: `%(default)s`).',
metavar='NAME', metavar='NAME',
default=constants.DEFAULT_HYPEROPT_LOSS, default=constants.DEFAULT_HYPEROPT_LOSS,
), ),

View File

@ -4,7 +4,7 @@ This module contains the configuration class
import logging import logging
import warnings import warnings
from argparse import Namespace from argparse import Namespace
from typing import Any, Callable, Dict, Optional from typing import Any, Callable, Dict, List, Optional
from freqtrade import OperationalException, constants from freqtrade import OperationalException, constants
from freqtrade.configuration.check_exchange import check_exchange from freqtrade.configuration.check_exchange import check_exchange
@ -39,43 +39,43 @@ class Configuration(object):
return self.config return self.config
def _load_config_files(self) -> Dict[str, Any]: @staticmethod
def from_files(files: List[str]) -> Dict[str, Any]:
""" """
Iterate through the config files passed in the args, Iterate through the config files passed in, loading all of them
loading all of them and merging their contents. and merging their contents.
Files are loaded in sequence, parameters in later configuration files
override the same parameter from an earlier file (last definition wins).
:param files: List of file paths
:return: configuration dictionary
""" """
# Keep this method as staticmethod, so it can be used from interactive environments
config: Dict[str, Any] = {} config: Dict[str, Any] = {}
# We expect here a list of config filenames # We expect here a list of config filenames
for path in self.args.config: for path in files:
logger.info('Using config: %s ...', path) logger.info(f'Using config: {path} ...')
# Merge config options, overwriting old values # Merge config options, overwriting old values
config = deep_merge_dicts(load_config_file(path), config) config = deep_merge_dicts(load_config_file(path), config)
return config # Normalize config
def _normalize_config(self, config: Dict[str, Any]) -> None:
"""
Make config more canonical -- i.e. for example add missing parts that we expect
to be normally in it...
"""
if 'internals' not in config: if 'internals' not in config:
config['internals'] = {} config['internals'] = {}
# validate configuration before returning
logger.info('Validating configuration ...')
validate_config_schema(config)
return config
def load_config(self) -> Dict[str, Any]: def load_config(self) -> Dict[str, Any]:
""" """
Extract information for sys.argv and load the bot configuration Extract information for sys.argv and load the bot configuration
:return: Configuration dictionary :return: Configuration dictionary
""" """
# Load all configs # Load all configs
config: Dict[str, Any] = self._load_config_files() config: Dict[str, Any] = Configuration.from_files(self.args.config)
# Make resulting config more canonical
self._normalize_config(config)
logger.info('Validating configuration ...')
validate_config_schema(config)
self._validate_config_consistency(config) self._validate_config_consistency(config)

View File

@ -20,7 +20,7 @@ from freqtrade.exchange import timeframe_to_minutes
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
from freqtrade.rpc import RPCManager, RPCMessageType from freqtrade.rpc import RPCManager, RPCMessageType
from freqtrade.resolvers import ExchangeResolver, StrategyResolver, PairListResolver from freqtrade.resolvers import ExchangeResolver, StrategyResolver, PairListResolver
from freqtrade.state import State from freqtrade.state import State, RunMode
from freqtrade.strategy.interface import SellType, IStrategy from freqtrade.strategy.interface import SellType, IStrategy
from freqtrade.wallets import Wallets from freqtrade.wallets import Wallets
@ -75,6 +75,12 @@ class FreqtradeBot(object):
persistence.init(self.config.get('db_url', None), persistence.init(self.config.get('db_url', None),
clean_open_orders=self.config.get('dry_run', False)) clean_open_orders=self.config.get('dry_run', False))
# Stoploss on exchange does not make sense, therefore we need to disable that.
if (self.dataprovider.runmode == RunMode.DRY_RUN and
self.strategy.order_types.get('stoploss_on_exchange', False)):
logger.info("Disabling stoploss_on_exchange during dry-run.")
self.strategy.order_types['stoploss_on_exchange'] = False
config['order_types']['stoploss_on_exchange'] = False
# Set initial bot state from config # Set initial bot state from config
initial_state = self.config.get('initial_state') initial_state = self.config.get('initial_state')
self.state = State[initial_state.upper()] if initial_state else State.STOPPED self.state = State[initial_state.upper()] if initial_state else State.STOPPED

View File

@ -14,36 +14,48 @@ from freqtrade.optimize.hyperopt_interface import IHyperOpt
class DefaultHyperOpts(IHyperOpt): class DefaultHyperOpts(IHyperOpt):
""" """
Default hyperopt provided by the Freqtrade bot. Default hyperopt provided by the Freqtrade bot.
You can override it with your own hyperopt You can override it with your own Hyperopt
""" """
@staticmethod @staticmethod
def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame: def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Add several indicators needed for buy and sell strategies defined below.
"""
# ADX
dataframe['adx'] = ta.ADX(dataframe) dataframe['adx'] = ta.ADX(dataframe)
# MACD
macd = ta.MACD(dataframe) macd = ta.MACD(dataframe)
dataframe['macd'] = macd['macd'] dataframe['macd'] = macd['macd']
dataframe['macdsignal'] = macd['macdsignal'] dataframe['macdsignal'] = macd['macdsignal']
# MFI
dataframe['mfi'] = ta.MFI(dataframe) dataframe['mfi'] = ta.MFI(dataframe)
# RSI
dataframe['rsi'] = ta.RSI(dataframe) dataframe['rsi'] = ta.RSI(dataframe)
# Stochastic Fast
stoch_fast = ta.STOCHF(dataframe) stoch_fast = ta.STOCHF(dataframe)
dataframe['fastd'] = stoch_fast['fastd'] dataframe['fastd'] = stoch_fast['fastd']
# Minus-DI
dataframe['minus_di'] = ta.MINUS_DI(dataframe) dataframe['minus_di'] = ta.MINUS_DI(dataframe)
# Bollinger bands # Bollinger bands
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
dataframe['bb_lowerband'] = bollinger['lower'] dataframe['bb_lowerband'] = bollinger['lower']
dataframe['bb_upperband'] = bollinger['upper'] dataframe['bb_upperband'] = bollinger['upper']
# SAR
dataframe['sar'] = ta.SAR(dataframe) dataframe['sar'] = ta.SAR(dataframe)
return dataframe return dataframe
@staticmethod @staticmethod
def buy_strategy_generator(params: Dict[str, Any]) -> Callable: def buy_strategy_generator(params: Dict[str, Any]) -> Callable:
""" """
Define the buy strategy parameters to be used by hyperopt Define the buy strategy parameters to be used by Hyperopt.
""" """
def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
""" """
Buy strategy Hyperopt will build and use Buy strategy Hyperopt will build and use.
""" """
conditions = [] conditions = []
# GUARDS AND TRENDS # GUARDS AND TRENDS
if 'mfi-enabled' in params and params['mfi-enabled']: if 'mfi-enabled' in params and params['mfi-enabled']:
conditions.append(dataframe['mfi'] < params['mfi-value']) conditions.append(dataframe['mfi'] < params['mfi-value'])
@ -79,7 +91,7 @@ class DefaultHyperOpts(IHyperOpt):
@staticmethod @staticmethod
def indicator_space() -> List[Dimension]: def indicator_space() -> List[Dimension]:
""" """
Define your Hyperopt space for searching strategy parameters Define your Hyperopt space for searching buy strategy parameters.
""" """
return [ return [
Integer(10, 25, name='mfi-value'), Integer(10, 25, name='mfi-value'),
@ -96,14 +108,14 @@ class DefaultHyperOpts(IHyperOpt):
@staticmethod @staticmethod
def sell_strategy_generator(params: Dict[str, Any]) -> Callable: def sell_strategy_generator(params: Dict[str, Any]) -> Callable:
""" """
Define the sell strategy parameters to be used by hyperopt Define the sell strategy parameters to be used by Hyperopt.
""" """
def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
""" """
Sell strategy Hyperopt will build and use Sell strategy Hyperopt will build and use.
""" """
# print(params)
conditions = [] conditions = []
# GUARDS AND TRENDS # GUARDS AND TRENDS
if 'sell-mfi-enabled' in params and params['sell-mfi-enabled']: if 'sell-mfi-enabled' in params and params['sell-mfi-enabled']:
conditions.append(dataframe['mfi'] > params['sell-mfi-value']) conditions.append(dataframe['mfi'] > params['sell-mfi-value'])
@ -139,7 +151,7 @@ class DefaultHyperOpts(IHyperOpt):
@staticmethod @staticmethod
def sell_indicator_space() -> List[Dimension]: def sell_indicator_space() -> List[Dimension]:
""" """
Define your Hyperopt space for searching sell strategy parameters Define your Hyperopt space for searching sell strategy parameters.
""" """
return [ return [
Integer(75, 100, name='sell-mfi-value'), Integer(75, 100, name='sell-mfi-value'),
@ -157,9 +169,9 @@ class DefaultHyperOpts(IHyperOpt):
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
""" """
Based on TA indicators. Should be a copy of from strategy Based on TA indicators. Should be a copy of same method from strategy.
must align to populate_indicators in this file Must align to populate_indicators in this file.
Only used when --spaces does not include buy Only used when --spaces does not include buy space.
""" """
dataframe.loc[ dataframe.loc[
( (
@ -174,9 +186,9 @@ class DefaultHyperOpts(IHyperOpt):
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
""" """
Based on TA indicators. Should be a copy of from strategy Based on TA indicators. Should be a copy of same method from strategy.
must align to populate_indicators in this file Must align to populate_indicators in this file.
Only used when --spaces does not include sell Only used when --spaces does not include sell space.
""" """
dataframe.loc[ dataframe.loc[
( (
@ -186,4 +198,5 @@ class DefaultHyperOpts(IHyperOpt):
(dataframe['fastd'] > 54) (dataframe['fastd'] > 54)
), ),
'sell'] = 1 'sell'] = 1
return dataframe return dataframe

View File

@ -34,13 +34,13 @@ def log_has(line, logs):
# caplog mocker returns log as a tuple: ('freqtrade.something', logging.WARNING, 'foobar') # caplog mocker returns log as a tuple: ('freqtrade.something', logging.WARNING, 'foobar')
# and we want to match line against foobar in the tuple # and we want to match line against foobar in the tuple
return reduce(lambda a, b: a or b, return reduce(lambda a, b: a or b,
filter(lambda x: x[2] == line, logs), filter(lambda x: x[2] == line, logs.record_tuples),
False) False)
def log_has_re(line, logs): def log_has_re(line, logs):
return reduce(lambda a, b: a or b, return reduce(lambda a, b: a or b,
filter(lambda x: re.match(line, x[2]), logs), filter(lambda x: re.match(line, x[2]), logs.record_tuples),
False) False)

View File

@ -18,7 +18,7 @@ def test_parse_ticker_dataframe(ticker_history_list, caplog):
dataframe = parse_ticker_dataframe(ticker_history_list, '5m', dataframe = parse_ticker_dataframe(ticker_history_list, '5m',
pair="UNITTEST/BTC", fill_missing=True) pair="UNITTEST/BTC", fill_missing=True)
assert dataframe.columns.tolist() == columns assert dataframe.columns.tolist() == columns
assert log_has('Parsing tickerlist to dataframe', caplog.record_tuples) assert log_has('Parsing tickerlist to dataframe', caplog)
def test_ohlcv_fill_up_missing_data(caplog): def test_ohlcv_fill_up_missing_data(caplog):
@ -34,8 +34,7 @@ def test_ohlcv_fill_up_missing_data(caplog):
assert (data.columns == data2.columns).all() assert (data.columns == data2.columns).all()
assert log_has(f"Missing data fillup for UNITTEST/BTC: before: " assert log_has(f"Missing data fillup for UNITTEST/BTC: before: "
f"{len(data)} - after: {len(data2)}", f"{len(data)} - after: {len(data2)}", caplog)
caplog.record_tuples)
# Test fillup actually fixes invalid backtest data # Test fillup actually fixes invalid backtest data
min_date, max_date = get_timeframe({'UNITTEST/BTC': data}) min_date, max_date = get_timeframe({'UNITTEST/BTC': data})
@ -97,8 +96,7 @@ def test_ohlcv_fill_up_missing_data2(caplog):
assert (data.columns == data2.columns).all() assert (data.columns == data2.columns).all()
assert log_has(f"Missing data fillup for UNITTEST/BTC: before: " assert log_has(f"Missing data fillup for UNITTEST/BTC: before: "
f"{len(data)} - after: {len(data2)}", f"{len(data)} - after: {len(data2)}", caplog)
caplog.record_tuples)
def test_ohlcv_drop_incomplete(caplog): def test_ohlcv_drop_incomplete(caplog):
@ -140,11 +138,11 @@ def test_ohlcv_drop_incomplete(caplog):
data = parse_ticker_dataframe(ticks, ticker_interval, pair="UNITTEST/BTC", data = parse_ticker_dataframe(ticks, ticker_interval, pair="UNITTEST/BTC",
fill_missing=False, drop_incomplete=False) fill_missing=False, drop_incomplete=False)
assert len(data) == 4 assert len(data) == 4
assert not log_has("Dropping last candle", caplog.record_tuples) assert not log_has("Dropping last candle", caplog)
# Drop last candle # Drop last candle
data = parse_ticker_dataframe(ticks, ticker_interval, pair="UNITTEST/BTC", data = parse_ticker_dataframe(ticks, ticker_interval, pair="UNITTEST/BTC",
fill_missing=False, drop_incomplete=True) fill_missing=False, drop_incomplete=True)
assert len(data) == 3 assert len(data) == 3
assert log_has("Dropping last candle", caplog.record_tuples) assert log_has("Dropping last candle", caplog)

View File

@ -64,8 +64,7 @@ def test_load_data_30min_ticker(mocker, caplog, default_conf) -> None:
assert isinstance(ld, DataFrame) assert isinstance(ld, DataFrame)
assert not log_has( assert not log_has(
'Download history data for pair: "UNITTEST/BTC", interval: 30m ' 'Download history data for pair: "UNITTEST/BTC", interval: 30m '
'and store in None.', 'and store in None.', caplog
caplog.record_tuples
) )
@ -76,8 +75,7 @@ def test_load_data_7min_ticker(mocker, caplog, default_conf) -> None:
assert log_has( assert log_has(
'No history data for pair: "UNITTEST/BTC", interval: 7m. ' 'No history data for pair: "UNITTEST/BTC", interval: 7m. '
'Use --refresh-pairs-cached option or download_backtest_data.py ' 'Use --refresh-pairs-cached option or download_backtest_data.py '
'script to download the data', 'script to download the data', caplog
caplog.record_tuples
) )
@ -89,8 +87,7 @@ def test_load_data_1min_ticker(ticker_history, mocker, caplog) -> None:
assert os.path.isfile(file) is True assert os.path.isfile(file) is True
assert not log_has( assert not log_has(
'Download history data for pair: "UNITTEST/BTC", interval: 1m ' 'Download history data for pair: "UNITTEST/BTC", interval: 1m '
'and store in None.', 'and store in None.', caplog
caplog.record_tuples
) )
_clean_test_file(file) _clean_test_file(file)
@ -113,8 +110,7 @@ def test_load_data_with_new_pair_1min(ticker_history_list, mocker, caplog, defau
assert log_has( assert log_has(
'No history data for pair: "MEME/BTC", interval: 1m. ' 'No history data for pair: "MEME/BTC", interval: 1m. '
'Use --refresh-pairs-cached option or download_backtest_data.py ' 'Use --refresh-pairs-cached option or download_backtest_data.py '
'script to download the data', 'script to download the data', caplog
caplog.record_tuples
) )
# download a new pair if refresh_pairs is set # download a new pair if refresh_pairs is set
@ -126,8 +122,7 @@ def test_load_data_with_new_pair_1min(ticker_history_list, mocker, caplog, defau
assert os.path.isfile(file) is True assert os.path.isfile(file) is True
assert log_has( assert log_has(
'Download history data for pair: "MEME/BTC", interval: 1m ' 'Download history data for pair: "MEME/BTC", interval: 1m '
'and store in None.', 'and store in None.', caplog
caplog.record_tuples
) )
with pytest.raises(OperationalException, match=r'Exchange needs to be initialized when.*'): with pytest.raises(OperationalException, match=r'Exchange needs to be initialized when.*'):
history.load_pair_history(datadir=None, history.load_pair_history(datadir=None,
@ -149,7 +144,7 @@ def test_load_data_live(default_conf, mocker, caplog) -> None:
exchange=exchange) exchange=exchange)
assert refresh_mock.call_count == 1 assert refresh_mock.call_count == 1
assert len(refresh_mock.call_args_list[0][0][0]) == 2 assert len(refresh_mock.call_args_list[0][0][0]) == 2
assert log_has('Live: Downloading data for all defined pairs ...', caplog.record_tuples) assert log_has('Live: Downloading data for all defined pairs ...', caplog)
def test_load_data_live_noexchange(default_conf, mocker, caplog) -> None: def test_load_data_live_noexchange(default_conf, mocker, caplog) -> None:
@ -350,8 +345,7 @@ def test_download_backtesting_data_exception(ticker_history, mocker, caplog, def
_clean_test_file(file1_5) _clean_test_file(file1_5)
assert log_has( assert log_has(
'Failed to download history data for pair: "MEME/BTC", interval: 1m. ' 'Failed to download history data for pair: "MEME/BTC", interval: 1m. '
'Error: File Error', 'Error: File Error', caplog
caplog.record_tuples
) )
@ -380,7 +374,7 @@ def test_load_partial_missing(caplog) -> None:
start_real = tickerdata['UNITTEST/BTC'].iloc[0, 0] start_real = tickerdata['UNITTEST/BTC'].iloc[0, 0]
assert log_has(f'Missing data at start for pair ' assert log_has(f'Missing data at start for pair '
f'UNITTEST/BTC, data starts at {start_real.strftime("%Y-%m-%d %H:%M:%S")}', f'UNITTEST/BTC, data starts at {start_real.strftime("%Y-%m-%d %H:%M:%S")}',
caplog.record_tuples) caplog)
# Make sure we start fresh - test missing data at end # Make sure we start fresh - test missing data at end
caplog.clear() caplog.clear()
start = arrow.get('2018-01-10T00:00:00') start = arrow.get('2018-01-10T00:00:00')
@ -396,7 +390,7 @@ def test_load_partial_missing(caplog) -> None:
end_real = arrow.get(tickerdata['UNITTEST/BTC'].iloc[-1, 0]).shift(minutes=5) end_real = arrow.get(tickerdata['UNITTEST/BTC'].iloc[-1, 0]).shift(minutes=5)
assert log_has(f'Missing data at end for pair ' assert log_has(f'Missing data at end for pair '
f'UNITTEST/BTC, data ends at {end_real.strftime("%Y-%m-%d %H:%M:%S")}', f'UNITTEST/BTC, data ends at {end_real.strftime("%Y-%m-%d %H:%M:%S")}',
caplog.record_tuples) caplog)
def test_init(default_conf, mocker) -> None: def test_init(default_conf, mocker) -> None:
@ -560,7 +554,7 @@ def test_validate_backtest_data_warn(default_conf, mocker, caplog) -> None:
assert len(caplog.record_tuples) == 1 assert len(caplog.record_tuples) == 1
assert log_has( assert log_has(
"UNITTEST/BTC has missing frames: expected 14396, got 13680, that's 716 missing values", "UNITTEST/BTC has missing frames: expected 14396, got 13680, that's 716 missing values",
caplog.record_tuples) caplog)
def test_validate_backtest_data(default_conf, mocker, caplog) -> None: def test_validate_backtest_data(default_conf, mocker, caplog) -> None:

View File

@ -311,7 +311,7 @@ def test_edge_process_no_data(mocker, edge_conf, caplog):
assert not edge.calculate() assert not edge.calculate()
assert len(edge._cached_pairs) == 0 assert len(edge._cached_pairs) == 0
assert log_has("No data found. Edge is stopped ...", caplog.record_tuples) assert log_has("No data found. Edge is stopped ...", caplog)
assert edge._last_updated == 0 assert edge._last_updated == 0
@ -326,7 +326,7 @@ def test_edge_process_no_trades(mocker, edge_conf, caplog):
assert not edge.calculate() assert not edge.calculate()
assert len(edge._cached_pairs) == 0 assert len(edge._cached_pairs) == 0
assert log_has("No trades found.", caplog.record_tuples) assert log_has("No trades found.", caplog)
def test_edge_init_error(mocker, edge_conf,): def test_edge_init_error(mocker, edge_conf,):

View File

@ -62,7 +62,7 @@ async def async_ccxt_exception(mocker, default_conf, api_mock, fun, mock_ccxt_fu
def test_init(default_conf, mocker, caplog): def test_init(default_conf, mocker, caplog):
caplog.set_level(logging.INFO) caplog.set_level(logging.INFO)
get_patched_exchange(mocker, default_conf) get_patched_exchange(mocker, default_conf)
assert log_has('Instance is running with dry_run enabled', caplog.record_tuples) assert log_has('Instance is running with dry_run enabled', caplog)
def test_init_ccxt_kwargs(default_conf, mocker, caplog): def test_init_ccxt_kwargs(default_conf, mocker, caplog):
@ -71,8 +71,7 @@ def test_init_ccxt_kwargs(default_conf, mocker, caplog):
conf = copy.deepcopy(default_conf) conf = copy.deepcopy(default_conf)
conf['exchange']['ccxt_async_config'] = {'aiohttp_trust_env': True} conf['exchange']['ccxt_async_config'] = {'aiohttp_trust_env': True}
ex = Exchange(conf) ex = Exchange(conf)
assert log_has("Applying additional ccxt config: {'aiohttp_trust_env': True}", assert log_has("Applying additional ccxt config: {'aiohttp_trust_env': True}", caplog)
caplog.record_tuples)
assert ex._api_async.aiohttp_trust_env assert ex._api_async.aiohttp_trust_env
assert not ex._api.aiohttp_trust_env assert not ex._api.aiohttp_trust_env
@ -81,20 +80,18 @@ def test_init_ccxt_kwargs(default_conf, mocker, caplog):
conf = copy.deepcopy(default_conf) conf = copy.deepcopy(default_conf)
conf['exchange']['ccxt_config'] = {'TestKWARG': 11} conf['exchange']['ccxt_config'] = {'TestKWARG': 11}
ex = Exchange(conf) ex = Exchange(conf)
assert not log_has("Applying additional ccxt config: {'aiohttp_trust_env': True}", assert not log_has("Applying additional ccxt config: {'aiohttp_trust_env': True}", caplog)
caplog.record_tuples)
assert not ex._api_async.aiohttp_trust_env assert not ex._api_async.aiohttp_trust_env
assert hasattr(ex._api, 'TestKWARG') assert hasattr(ex._api, 'TestKWARG')
assert ex._api.TestKWARG == 11 assert ex._api.TestKWARG == 11
assert not hasattr(ex._api_async, 'TestKWARG') assert not hasattr(ex._api_async, 'TestKWARG')
assert log_has("Applying additional ccxt config: {'TestKWARG': 11}", assert log_has("Applying additional ccxt config: {'TestKWARG': 11}", caplog)
caplog.record_tuples)
def test_destroy(default_conf, mocker, caplog): def test_destroy(default_conf, mocker, caplog):
caplog.set_level(logging.DEBUG) caplog.set_level(logging.DEBUG)
get_patched_exchange(mocker, default_conf) get_patched_exchange(mocker, default_conf)
assert log_has('Exchange object destroyed, closing async loop', caplog.record_tuples) assert log_has('Exchange object destroyed, closing async loop', caplog)
def test_init_exception(default_conf, mocker): def test_init_exception(default_conf, mocker):
@ -120,8 +117,7 @@ def test_exchange_resolver(default_conf, mocker, caplog):
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock()) mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
exchange = ExchangeResolver('Bittrex', default_conf).exchange exchange = ExchangeResolver('Bittrex', default_conf).exchange
assert isinstance(exchange, Exchange) assert isinstance(exchange, Exchange)
assert log_has_re(r"No .* specific subclass found. Using the generic class instead.", assert log_has_re(r"No .* specific subclass found. Using the generic class instead.", caplog)
caplog.record_tuples)
caplog.clear() caplog.clear()
exchange = ExchangeResolver('kraken', default_conf).exchange exchange = ExchangeResolver('kraken', default_conf).exchange
@ -129,7 +125,7 @@ def test_exchange_resolver(default_conf, mocker, caplog):
assert isinstance(exchange, Kraken) assert isinstance(exchange, Kraken)
assert not isinstance(exchange, Binance) assert not isinstance(exchange, Binance)
assert not log_has_re(r"No .* specific subclass found. Using the generic class instead.", assert not log_has_re(r"No .* specific subclass found. Using the generic class instead.",
caplog.record_tuples) caplog)
exchange = ExchangeResolver('binance', default_conf).exchange exchange = ExchangeResolver('binance', default_conf).exchange
assert isinstance(exchange, Exchange) assert isinstance(exchange, Exchange)
@ -137,7 +133,7 @@ def test_exchange_resolver(default_conf, mocker, caplog):
assert not isinstance(exchange, Kraken) assert not isinstance(exchange, Kraken)
assert not log_has_re(r"No .* specific subclass found. Using the generic class instead.", assert not log_has_re(r"No .* specific subclass found. Using the generic class instead.",
caplog.record_tuples) caplog)
def test_validate_order_time_in_force(default_conf, mocker, caplog): def test_validate_order_time_in_force(default_conf, mocker, caplog):
@ -249,8 +245,7 @@ def test__load_async_markets(default_conf, mocker, caplog):
exchange._api_async.load_markets = Mock(side_effect=ccxt.BaseError("deadbeef")) exchange._api_async.load_markets = Mock(side_effect=ccxt.BaseError("deadbeef"))
exchange._load_async_markets() exchange._load_async_markets()
assert log_has('Could not load async markets. Reason: deadbeef', assert log_has('Could not load async markets. Reason: deadbeef', caplog)
caplog.record_tuples)
def test__load_markets(default_conf, mocker, caplog): def test__load_markets(default_conf, mocker, caplog):
@ -262,7 +257,7 @@ def test__load_markets(default_conf, mocker, caplog):
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock()) mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock()) mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock())
Exchange(default_conf) Exchange(default_conf)
assert log_has('Unable to initialize markets. Reason: SomeError', caplog.record_tuples) assert log_has('Unable to initialize markets. Reason: SomeError', caplog)
expected_return = {'ETH/BTC': 'available'} expected_return = {'ETH/BTC': 'available'}
api_mock = MagicMock() api_mock = MagicMock()
@ -298,7 +293,7 @@ def test__reload_markets(default_conf, mocker, caplog):
exchange._last_markets_refresh = arrow.utcnow().timestamp - 15 * 60 exchange._last_markets_refresh = arrow.utcnow().timestamp - 15 * 60
exchange._reload_markets() exchange._reload_markets()
assert exchange.markets == updated_markets assert exchange.markets == updated_markets
assert log_has('Performing scheduled market reload..', caplog.record_tuples) assert log_has('Performing scheduled market reload..', caplog)
def test__reload_markets_exception(default_conf, mocker, caplog): def test__reload_markets_exception(default_conf, mocker, caplog):
@ -312,7 +307,7 @@ def test__reload_markets_exception(default_conf, mocker, caplog):
# less than 10 minutes have passed, no reload # less than 10 minutes have passed, no reload
exchange._reload_markets() exchange._reload_markets()
assert exchange._last_markets_refresh == 0 assert exchange._last_markets_refresh == 0
assert log_has_re(r"Could not reload markets.*", caplog.record_tuples) assert log_has_re(r"Could not reload markets.*", caplog)
def test_validate_pairs(default_conf, mocker): # test exchange.validate_pairs directly def test_validate_pairs(default_conf, mocker): # test exchange.validate_pairs directly
@ -357,8 +352,7 @@ def test_validate_pairs_exception(default_conf, mocker, caplog):
mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value={})) mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value={}))
Exchange(default_conf) Exchange(default_conf)
assert log_has('Unable to validate pairs (assuming they are correct).', assert log_has('Unable to validate pairs (assuming they are correct).', caplog)
caplog.record_tuples)
def test_validate_pairs_restricted(default_conf, mocker, caplog): def test_validate_pairs_restricted(default_conf, mocker, caplog):
@ -374,8 +368,7 @@ def test_validate_pairs_restricted(default_conf, mocker, caplog):
Exchange(default_conf) Exchange(default_conf)
assert log_has(f"Pair XRP/BTC is restricted for some users on this exchange." assert log_has(f"Pair XRP/BTC is restricted for some users on this exchange."
f"Please check if you are impacted by this restriction " f"Please check if you are impacted by this restriction "
f"on the exchange and eventually remove XRP/BTC from your whitelist.", f"on the exchange and eventually remove XRP/BTC from your whitelist.", caplog)
caplog.record_tuples)
def test_validate_timeframes(default_conf, mocker): def test_validate_timeframes(default_conf, mocker):
@ -1060,7 +1053,7 @@ def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None:
assert not exchange._klines assert not exchange._klines
exchange.refresh_latest_ohlcv(pairs) exchange.refresh_latest_ohlcv(pairs)
assert log_has(f'Refreshing ohlcv data for {len(pairs)} pairs', caplog.record_tuples) assert log_has(f'Refreshing ohlcv data for {len(pairs)} pairs', caplog)
assert exchange._klines assert exchange._klines
assert exchange._api_async.fetch_ohlcv.call_count == 2 assert exchange._api_async.fetch_ohlcv.call_count == 2
for pair in pairs: for pair in pairs:
@ -1079,7 +1072,7 @@ def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None:
assert exchange._api_async.fetch_ohlcv.call_count == 2 assert exchange._api_async.fetch_ohlcv.call_count == 2
assert log_has(f"Using cached ohlcv data for pair {pairs[0][0]}, interval {pairs[0][1]} ...", assert log_has(f"Using cached ohlcv data for pair {pairs[0][0]}, interval {pairs[0][1]} ...",
caplog.record_tuples) caplog)
@pytest.mark.asyncio @pytest.mark.asyncio
@ -1109,7 +1102,7 @@ async def test__async_get_candle_history(default_conf, mocker, caplog, exchange_
assert res[1] == "5m" assert res[1] == "5m"
assert res[2] == tick assert res[2] == tick
assert exchange._api_async.fetch_ohlcv.call_count == 1 assert exchange._api_async.fetch_ohlcv.call_count == 1
assert not log_has(f"Using cached ohlcv data for {pair} ...", caplog.record_tuples) assert not log_has(f"Using cached ohlcv data for {pair} ...", caplog)
# exchange = Exchange(default_conf) # exchange = Exchange(default_conf)
await async_ccxt_exception(mocker, default_conf, MagicMock(), await async_ccxt_exception(mocker, default_conf, MagicMock(),
@ -1168,8 +1161,8 @@ def test_refresh_latest_ohlcv_inv_result(default_conf, mocker, caplog):
# Test that each is in list at least once as order is not guaranteed # Test that each is in list at least once as order is not guaranteed
assert type(res[0]) is tuple or type(res[1]) is tuple assert type(res[0]) is tuple or type(res[1]) is tuple
assert type(res[0]) is TypeError or type(res[1]) is TypeError assert type(res[0]) is TypeError or type(res[1]) is TypeError
assert log_has("Error loading ETH/BTC. Result was [[]].", caplog.record_tuples) assert log_has("Error loading ETH/BTC. Result was [[]].", caplog)
assert log_has("Async code raised an exception: TypeError", caplog.record_tuples) assert log_has("Async code raised an exception: TypeError", caplog)
@pytest.mark.parametrize("exchange_name", EXCHANGES) @pytest.mark.parametrize("exchange_name", EXCHANGES)

View File

@ -181,21 +181,18 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) ->
assert 'exchange' in config assert 'exchange' in config
assert 'pair_whitelist' in config['exchange'] assert 'pair_whitelist' in config['exchange']
assert 'datadir' in config assert 'datadir' in config
assert log_has( assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog)
'Using data directory: {} ...'.format(config['datadir']),
caplog.record_tuples
)
assert 'ticker_interval' in config assert 'ticker_interval' in config
assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog.record_tuples) assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog)
assert 'live' not in config assert 'live' not in config
assert not log_has('Parameter -l/--live detected ...', caplog.record_tuples) assert not log_has('Parameter -l/--live detected ...', caplog)
assert 'position_stacking' not in config assert 'position_stacking' not in config
assert not log_has('Parameter --enable-position-stacking detected ...', caplog.record_tuples) assert not log_has('Parameter --enable-position-stacking detected ...', caplog)
assert 'refresh_pairs' not in config assert 'refresh_pairs' not in config
assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples) assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog)
assert 'timerange' not in config assert 'timerange' not in config
assert 'export' not in config assert 'export' not in config
@ -235,43 +232,31 @@ def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) ->
assert 'datadir' in config assert 'datadir' in config
assert config['runmode'] == RunMode.BACKTEST assert config['runmode'] == RunMode.BACKTEST
assert log_has( assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog)
'Using data directory: {} ...'.format(config['datadir']),
caplog.record_tuples
)
assert 'ticker_interval' in config assert 'ticker_interval' in config
assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...',
caplog.record_tuples) caplog)
assert 'live' in config assert 'live' in config
assert log_has('Parameter -l/--live detected ...', caplog.record_tuples) assert log_has('Parameter -l/--live detected ...', caplog)
assert 'position_stacking' in config assert 'position_stacking' in config
assert log_has('Parameter --enable-position-stacking detected ...', caplog.record_tuples) assert log_has('Parameter --enable-position-stacking detected ...', caplog)
assert 'use_max_market_positions' in config assert 'use_max_market_positions' in config
assert log_has('Parameter --disable-max-market-positions detected ...', caplog.record_tuples) assert log_has('Parameter --disable-max-market-positions detected ...', caplog)
assert log_has('max_open_trades set to unlimited ...', caplog.record_tuples) assert log_has('max_open_trades set to unlimited ...', caplog)
assert 'refresh_pairs' in config assert 'refresh_pairs' in config
assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples) assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog)
assert 'timerange' in config assert 'timerange' in config
assert log_has( assert log_has('Parameter --timerange detected: {} ...'.format(config['timerange']), caplog)
'Parameter --timerange detected: {} ...'.format(config['timerange']),
caplog.record_tuples
)
assert 'export' in config assert 'export' in config
assert log_has( assert log_has('Parameter --export detected: {} ...'.format(config['export']), caplog)
'Parameter --export detected: {} ...'.format(config['export']),
caplog.record_tuples
)
assert 'exportfilename' in config assert 'exportfilename' in config
assert log_has( assert log_has('Storing backtest results to {} ...'.format(config['exportfilename']), caplog)
'Storing backtest results to {} ...'.format(config['exportfilename']),
caplog.record_tuples
)
def test_setup_configuration_unlimited_stake_amount(mocker, default_conf, caplog) -> None: def test_setup_configuration_unlimited_stake_amount(mocker, default_conf, caplog) -> None:
@ -303,10 +288,7 @@ def test_start(mocker, fee, default_conf, caplog) -> None:
] ]
args = get_args(args) args = get_args(args)
start_backtesting(args) start_backtesting(args)
assert log_has( assert log_has('Starting freqtrade in Backtesting mode', caplog)
'Starting freqtrade in Backtesting mode',
caplog.record_tuples
)
assert start_mock.call_count == 1 assert start_mock.call_count == 1
@ -360,7 +342,7 @@ def test_backtesting_init_no_ticker_interval(mocker, default_conf, caplog) -> No
with pytest.raises(OperationalException): with pytest.raises(OperationalException):
Backtesting(default_conf) Backtesting(default_conf)
log_has("Ticker-interval needs to be set in either configuration " log_has("Ticker-interval needs to be set in either configuration "
"or as cli argument `--ticker-interval 5m`", caplog.record_tuples) "or as cli argument `--ticker-interval 5m`", caplog)
def test_tickerdata_to_dataframe_bt(default_conf, mocker) -> None: def test_tickerdata_to_dataframe_bt(default_conf, mocker) -> None:
@ -511,7 +493,7 @@ def test_backtesting_start(default_conf, mocker, caplog) -> None:
'up to 2017-11-14T22:59:00+00:00 (0 days)..' 'up to 2017-11-14T22:59:00+00:00 (0 days)..'
] ]
for line in exists: for line in exists:
assert log_has(line, caplog.record_tuples) assert log_has(line, caplog)
def test_backtesting_start_no_data(default_conf, mocker, caplog) -> None: def test_backtesting_start_no_data(default_conf, mocker, caplog) -> None:
@ -539,7 +521,7 @@ def test_backtesting_start_no_data(default_conf, mocker, caplog) -> None:
backtesting.start() backtesting.start()
# check the logs, that will contain the backtest result # check the logs, that will contain the backtest result
assert log_has('No data found. Terminating.', caplog.record_tuples) assert log_has('No data found. Terminating.', caplog)
def test_backtest(default_conf, fee, mocker) -> None: def test_backtest(default_conf, fee, mocker) -> None:
@ -876,7 +858,7 @@ def test_backtest_start_live(default_conf, mocker, caplog):
] ]
for line in exists: for line in exists:
assert log_has(line, caplog.record_tuples) assert log_has(line, caplog)
@pytest.mark.filterwarnings("ignore:DEPRECATED") @pytest.mark.filterwarnings("ignore:DEPRECATED")
@ -936,4 +918,4 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog):
] ]
for line in exists: for line in exists:
assert log_has(line, caplog.record_tuples) assert log_has(line, caplog)

View File

@ -29,15 +29,12 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) ->
assert 'exchange' in config assert 'exchange' in config
assert 'pair_whitelist' in config['exchange'] assert 'pair_whitelist' in config['exchange']
assert 'datadir' in config assert 'datadir' in config
assert log_has( assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog)
'Using data directory: {} ...'.format(config['datadir']),
caplog.record_tuples
)
assert 'ticker_interval' in config assert 'ticker_interval' in config
assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog.record_tuples) assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog)
assert 'refresh_pairs' not in config assert 'refresh_pairs' not in config
assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples) assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog)
assert 'timerange' not in config assert 'timerange' not in config
assert 'stoploss_range' not in config assert 'stoploss_range' not in config
@ -69,21 +66,15 @@ def test_setup_edge_configuration_with_arguments(mocker, edge_conf, caplog) -> N
assert 'pair_whitelist' in config['exchange'] assert 'pair_whitelist' in config['exchange']
assert 'datadir' in config assert 'datadir' in config
assert config['runmode'] == RunMode.EDGE assert config['runmode'] == RunMode.EDGE
assert log_has( assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog)
'Using data directory: {} ...'.format(config['datadir']),
caplog.record_tuples
)
assert 'ticker_interval' in config assert 'ticker_interval' in config
assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...',
caplog.record_tuples) caplog)
assert 'refresh_pairs' in config assert 'refresh_pairs' in config
assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples) assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog)
assert 'timerange' in config assert 'timerange' in config
assert log_has( assert log_has('Parameter --timerange detected: {} ...'.format(config['timerange']), caplog)
'Parameter --timerange detected: {} ...'.format(config['timerange']),
caplog.record_tuples
)
def test_start(mocker, fee, edge_conf, caplog) -> None: def test_start(mocker, fee, edge_conf, caplog) -> None:
@ -100,10 +91,7 @@ def test_start(mocker, fee, edge_conf, caplog) -> None:
] ]
args = get_args(args) args = get_args(args)
start_edge(args) start_edge(args)
assert log_has( assert log_has('Starting freqtrade in Edge mode', caplog)
'Starting freqtrade in Edge mode',
caplog.record_tuples
)
assert start_mock.call_count == 1 assert start_mock.call_count == 1

View File

@ -79,21 +79,18 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca
assert 'exchange' in config assert 'exchange' in config
assert 'pair_whitelist' in config['exchange'] assert 'pair_whitelist' in config['exchange']
assert 'datadir' in config assert 'datadir' in config
assert log_has( assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog)
'Using data directory: {} ...'.format(config['datadir']),
caplog.record_tuples
)
assert 'ticker_interval' in config assert 'ticker_interval' in config
assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog.record_tuples) assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog)
assert 'live' not in config assert 'live' not in config
assert not log_has('Parameter -l/--live detected ...', caplog.record_tuples) assert not log_has('Parameter -l/--live detected ...', caplog)
assert 'position_stacking' not in config assert 'position_stacking' not in config
assert not log_has('Parameter --enable-position-stacking detected ...', caplog.record_tuples) assert not log_has('Parameter --enable-position-stacking detected ...', caplog)
assert 'refresh_pairs' not in config assert 'refresh_pairs' not in config
assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples) assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog)
assert 'timerange' not in config assert 'timerange' not in config
assert 'runmode' in config assert 'runmode' in config
@ -130,41 +127,32 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo
assert 'datadir' in config assert 'datadir' in config
assert config['runmode'] == RunMode.HYPEROPT assert config['runmode'] == RunMode.HYPEROPT
assert log_has( assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog)
'Using data directory: {} ...'.format(config['datadir']),
caplog.record_tuples
)
assert 'ticker_interval' in config assert 'ticker_interval' in config
assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...',
caplog.record_tuples) caplog)
assert 'position_stacking' in config assert 'position_stacking' in config
assert log_has('Parameter --enable-position-stacking detected ...', caplog.record_tuples) assert log_has('Parameter --enable-position-stacking detected ...', caplog)
assert 'use_max_market_positions' in config assert 'use_max_market_positions' in config
assert log_has('Parameter --disable-max-market-positions detected ...', caplog.record_tuples) assert log_has('Parameter --disable-max-market-positions detected ...', caplog)
assert log_has('max_open_trades set to unlimited ...', caplog.record_tuples) assert log_has('max_open_trades set to unlimited ...', caplog)
assert 'refresh_pairs' in config assert 'refresh_pairs' in config
assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples) assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog)
assert 'timerange' in config assert 'timerange' in config
assert log_has( assert log_has('Parameter --timerange detected: {} ...'.format(config['timerange']), caplog)
'Parameter --timerange detected: {} ...'.format(config['timerange']),
caplog.record_tuples
)
assert 'epochs' in config assert 'epochs' in config
assert log_has('Parameter --epochs detected ... Will run Hyperopt with for 1000 epochs ...', assert log_has('Parameter --epochs detected ... Will run Hyperopt with for 1000 epochs ...',
caplog.record_tuples) caplog)
assert 'spaces' in config assert 'spaces' in config
assert log_has( assert log_has('Parameter -s/--spaces detected: {}'.format(config['spaces']), caplog)
'Parameter -s/--spaces detected: {}'.format(config['spaces']),
caplog.record_tuples
)
assert 'print_all' in config assert 'print_all' in config
assert log_has('Parameter --print-all detected ...', caplog.record_tuples) assert log_has('Parameter --print-all detected ...', caplog)
def test_hyperoptresolver(mocker, default_conf, caplog) -> None: def test_hyperoptresolver(mocker, default_conf, caplog) -> None:
@ -181,9 +169,9 @@ def test_hyperoptresolver(mocker, default_conf, caplog) -> None:
assert not hasattr(x, 'populate_buy_trend') assert not hasattr(x, 'populate_buy_trend')
assert not hasattr(x, 'populate_sell_trend') assert not hasattr(x, 'populate_sell_trend')
assert log_has("Custom Hyperopt does not provide populate_sell_trend. " assert log_has("Custom Hyperopt does not provide populate_sell_trend. "
"Using populate_sell_trend from DefaultStrategy.", caplog.record_tuples) "Using populate_sell_trend from DefaultStrategy.", caplog)
assert log_has("Custom Hyperopt does not provide populate_buy_trend. " assert log_has("Custom Hyperopt does not provide populate_buy_trend. "
"Using populate_buy_trend from DefaultStrategy.", caplog.record_tuples) "Using populate_buy_trend from DefaultStrategy.", caplog)
assert hasattr(x, "ticker_interval") assert hasattr(x, "ticker_interval")
@ -229,10 +217,7 @@ def test_start(mocker, default_conf, caplog) -> None:
import pprint import pprint
pprint.pprint(caplog.record_tuples) pprint.pprint(caplog.record_tuples)
assert log_has( assert log_has('Starting freqtrade in Hyperopt mode', caplog)
'Starting freqtrade in Hyperopt mode',
caplog.record_tuples
)
assert start_mock.call_count == 1 assert start_mock.call_count == 1
@ -257,7 +242,7 @@ def test_start_no_data(mocker, default_conf, caplog) -> None:
import pprint import pprint
pprint.pprint(caplog.record_tuples) pprint.pprint(caplog.record_tuples)
assert log_has('No data found. Terminating.', caplog.record_tuples) assert log_has('No data found. Terminating.', caplog)
def test_start_failure(mocker, default_conf, caplog) -> None: def test_start_failure(mocker, default_conf, caplog) -> None:
@ -275,10 +260,7 @@ def test_start_failure(mocker, default_conf, caplog) -> None:
args = get_args(args) args = get_args(args)
with pytest.raises(DependencyException): with pytest.raises(DependencyException):
start_hyperopt(args) start_hyperopt(args)
assert log_has( assert log_has("Please don't use --strategy for hyperopt.", caplog)
"Please don't use --strategy for hyperopt.",
caplog.record_tuples
)
def test_start_filelock(mocker, default_conf, caplog) -> None: def test_start_filelock(mocker, default_conf, caplog) -> None:
@ -294,10 +276,7 @@ def test_start_filelock(mocker, default_conf, caplog) -> None:
] ]
args = get_args(args) args = get_args(args)
start_hyperopt(args) start_hyperopt(args)
assert log_has( assert log_has("Another running instance of freqtrade Hyperopt detected.", caplog)
"Another running instance of freqtrade Hyperopt detected.",
caplog.record_tuples
)
def test_loss_calculation_prefer_correct_trade_count(default_conf, hyperopt_results) -> None: def test_loss_calculation_prefer_correct_trade_count(default_conf, hyperopt_results) -> None:
@ -401,10 +380,7 @@ def test_save_trials_saves_trials(mocker, hyperopt, caplog) -> None:
hyperopt.save_trials() hyperopt.save_trials()
trials_file = os.path.join('freqtrade', 'tests', 'optimize', 'ut_trials.pickle') trials_file = os.path.join('freqtrade', 'tests', 'optimize', 'ut_trials.pickle')
assert log_has( assert log_has('Saving 1 evaluations to \'{}\''.format(trials_file), caplog)
'Saving 1 evaluations to \'{}\''.format(trials_file),
caplog.record_tuples
)
mock_dump.assert_called_once() mock_dump.assert_called_once()
@ -413,10 +389,7 @@ def test_read_trials_returns_trials_file(mocker, hyperopt, caplog) -> None:
mock_load = mocker.patch('freqtrade.optimize.hyperopt.load', return_value=trials) mock_load = mocker.patch('freqtrade.optimize.hyperopt.load', return_value=trials)
hyperopt_trial = hyperopt.read_trials() hyperopt_trial = hyperopt.read_trials()
trials_file = os.path.join('freqtrade', 'tests', 'optimize', 'ut_trials.pickle') trials_file = os.path.join('freqtrade', 'tests', 'optimize', 'ut_trials.pickle')
assert log_has( assert log_has('Reading Trials from \'{}\''.format(trials_file), caplog)
'Reading Trials from \'{}\''.format(trials_file),
caplog.record_tuples
)
assert hyperopt_trial == trials assert hyperopt_trial == trials
mock_load.assert_called_once() mock_load.assert_called_once()
@ -626,7 +599,7 @@ def test_clean_hyperopt(mocker, default_conf, caplog):
Hyperopt(default_conf) Hyperopt(default_conf)
assert unlinkmock.call_count == 2 assert unlinkmock.call_count == 2
assert log_has(f"Removing `{TICKERDATA_PICKLE}`.", caplog.record_tuples) assert log_has(f"Removing `{TICKERDATA_PICKLE}`.", caplog)
def test_continue_hyperopt(mocker, default_conf, caplog): def test_continue_hyperopt(mocker, default_conf, caplog):
@ -643,4 +616,4 @@ def test_continue_hyperopt(mocker, default_conf, caplog):
Hyperopt(default_conf) Hyperopt(default_conf)
assert unlinkmock.call_count == 0 assert unlinkmock.call_count == 0
assert log_has(f"Continuing on previous hyperopt results.", caplog.record_tuples) assert log_has(f"Continuing on previous hyperopt results.", caplog)

View File

@ -91,7 +91,7 @@ def test_fiat_convert_unsupported_crypto(mocker, caplog):
mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._cryptomap', return_value=[]) mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._cryptomap', return_value=[])
fiat_convert = CryptoToFiatConverter() fiat_convert = CryptoToFiatConverter()
assert fiat_convert._find_price(crypto_symbol='CRYPTO_123', fiat_symbol='EUR') == 0.0 assert fiat_convert._find_price(crypto_symbol='CRYPTO_123', fiat_symbol='EUR') == 0.0
assert log_has('unsupported crypto-symbol CRYPTO_123 - returning 0.0', caplog.record_tuples) assert log_has('unsupported crypto-symbol CRYPTO_123 - returning 0.0', caplog)
def test_fiat_convert_get_price(mocker): def test_fiat_convert_get_price(mocker):
@ -190,7 +190,7 @@ def test_fiat_invalid_response(mocker, caplog):
length_cryptomap = len(fiat_convert._cryptomap) length_cryptomap = len(fiat_convert._cryptomap)
assert length_cryptomap == 0 assert length_cryptomap == 0
assert log_has('Could not load FIAT Cryptocurrency map for the following problem: TypeError', assert log_has('Could not load FIAT Cryptocurrency map for the following problem: TypeError',
caplog.record_tuples) caplog)
def test_convert_amount(mocker): def test_convert_amount(mocker):

View File

@ -148,8 +148,8 @@ def test_api_run(default_conf, mocker, caplog):
assert isinstance(server_mock.call_args_list[0][0][2], Flask) assert isinstance(server_mock.call_args_list[0][0][2], Flask)
assert hasattr(apiserver, "srv") assert hasattr(apiserver, "srv")
assert log_has("Starting HTTP Server at 127.0.0.1:8080", caplog.record_tuples) assert log_has("Starting HTTP Server at 127.0.0.1:8080", caplog)
assert log_has("Starting Local Rest Server.", caplog.record_tuples) assert log_has("Starting Local Rest Server.", caplog)
# Test binding to public # Test binding to public
caplog.clear() caplog.clear()
@ -165,22 +165,20 @@ def test_api_run(default_conf, mocker, caplog):
assert server_mock.call_args_list[0][0][0] == "0.0.0.0" assert server_mock.call_args_list[0][0][0] == "0.0.0.0"
assert server_mock.call_args_list[0][0][1] == "8089" assert server_mock.call_args_list[0][0][1] == "8089"
assert isinstance(server_mock.call_args_list[0][0][2], Flask) assert isinstance(server_mock.call_args_list[0][0][2], Flask)
assert log_has("Starting HTTP Server at 0.0.0.0:8089", caplog.record_tuples) assert log_has("Starting HTTP Server at 0.0.0.0:8089", caplog)
assert log_has("Starting Local Rest Server.", caplog.record_tuples) assert log_has("Starting Local Rest Server.", caplog)
assert log_has("SECURITY WARNING - Local Rest Server listening to external connections", assert log_has("SECURITY WARNING - Local Rest Server listening to external connections",
caplog.record_tuples) caplog)
assert log_has("SECURITY WARNING - This is insecure please set to your loopback," assert log_has("SECURITY WARNING - This is insecure please set to your loopback,"
"e.g 127.0.0.1 in config.json", "e.g 127.0.0.1 in config.json", caplog)
caplog.record_tuples)
assert log_has("SECURITY WARNING - No password for local REST Server defined. " assert log_has("SECURITY WARNING - No password for local REST Server defined. "
"Please make sure that this is intentional!", "Please make sure that this is intentional!", caplog)
caplog.record_tuples)
# Test crashing flask # Test crashing flask
caplog.clear() caplog.clear()
mocker.patch('freqtrade.rpc.api_server.make_server', MagicMock(side_effect=Exception)) mocker.patch('freqtrade.rpc.api_server.make_server', MagicMock(side_effect=Exception))
apiserver.run() apiserver.run()
assert log_has("Api server failed to start.", caplog.record_tuples) assert log_has("Api server failed to start.", caplog)
def test_api_cleanup(default_conf, mocker, caplog): def test_api_cleanup(default_conf, mocker, caplog):
@ -199,7 +197,7 @@ def test_api_cleanup(default_conf, mocker, caplog):
apiserver.cleanup() apiserver.cleanup()
assert stop_mock.shutdown.call_count == 1 assert stop_mock.shutdown.call_count == 1
assert log_has("Stopping API Server", caplog.record_tuples) assert log_has("Stopping API Server", caplog)
def test_api_reloadconf(botclient): def test_api_reloadconf(botclient):

View File

@ -19,7 +19,7 @@ def test_init_telegram_disabled(mocker, default_conf, caplog) -> None:
default_conf['telegram']['enabled'] = False default_conf['telegram']['enabled'] = False
rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf)) rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf))
assert not log_has('Enabling rpc.telegram ...', caplog.record_tuples) assert not log_has('Enabling rpc.telegram ...', caplog)
assert rpc_manager.registered_modules == [] assert rpc_manager.registered_modules == []
@ -28,7 +28,7 @@ def test_init_telegram_enabled(mocker, default_conf, caplog) -> None:
mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock()) mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf)) rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf))
assert log_has('Enabling rpc.telegram ...', caplog.record_tuples) assert log_has('Enabling rpc.telegram ...', caplog)
len_modules = len(rpc_manager.registered_modules) len_modules = len(rpc_manager.registered_modules)
assert len_modules == 1 assert len_modules == 1
assert 'telegram' in [mod.name for mod in rpc_manager.registered_modules] assert 'telegram' in [mod.name for mod in rpc_manager.registered_modules]
@ -43,7 +43,7 @@ def test_cleanup_telegram_disabled(mocker, default_conf, caplog) -> None:
rpc_manager = RPCManager(freqtradebot) rpc_manager = RPCManager(freqtradebot)
rpc_manager.cleanup() rpc_manager.cleanup()
assert not log_has('Cleaning up rpc.telegram ...', caplog.record_tuples) assert not log_has('Cleaning up rpc.telegram ...', caplog)
assert telegram_mock.call_count == 0 assert telegram_mock.call_count == 0
@ -59,7 +59,7 @@ def test_cleanup_telegram_enabled(mocker, default_conf, caplog) -> None:
assert 'telegram' in [mod.name for mod in rpc_manager.registered_modules] assert 'telegram' in [mod.name for mod in rpc_manager.registered_modules]
rpc_manager.cleanup() rpc_manager.cleanup()
assert log_has('Cleaning up rpc.telegram ...', caplog.record_tuples) assert log_has('Cleaning up rpc.telegram ...', caplog)
assert 'telegram' not in [mod.name for mod in rpc_manager.registered_modules] assert 'telegram' not in [mod.name for mod in rpc_manager.registered_modules]
assert telegram_mock.call_count == 1 assert telegram_mock.call_count == 1
@ -75,7 +75,7 @@ def test_send_msg_telegram_disabled(mocker, default_conf, caplog) -> None:
'status': 'test' 'status': 'test'
}) })
assert log_has("Sending rpc message: {'type': status, 'status': 'test'}", caplog.record_tuples) assert log_has("Sending rpc message: {'type': status, 'status': 'test'}", caplog)
assert telegram_mock.call_count == 0 assert telegram_mock.call_count == 0
@ -90,7 +90,7 @@ def test_send_msg_telegram_enabled(mocker, default_conf, caplog) -> None:
'status': 'test' 'status': 'test'
}) })
assert log_has("Sending rpc message: {'type': status, 'status': 'test'}", caplog.record_tuples) assert log_has("Sending rpc message: {'type': status, 'status': 'test'}", caplog)
assert telegram_mock.call_count == 1 assert telegram_mock.call_count == 1
@ -100,7 +100,7 @@ def test_init_webhook_disabled(mocker, default_conf, caplog) -> None:
default_conf['webhook'] = {'enabled': False} default_conf['webhook'] = {'enabled': False}
rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf)) rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf))
assert not log_has('Enabling rpc.webhook ...', caplog.record_tuples) assert not log_has('Enabling rpc.webhook ...', caplog)
assert rpc_manager.registered_modules == [] assert rpc_manager.registered_modules == []
@ -110,7 +110,7 @@ def test_init_webhook_enabled(mocker, default_conf, caplog) -> None:
default_conf['webhook'] = {'enabled': True, 'url': "https://DEADBEEF.com"} default_conf['webhook'] = {'enabled': True, 'url': "https://DEADBEEF.com"}
rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf)) rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf))
assert log_has('Enabling rpc.webhook ...', caplog.record_tuples) assert log_has('Enabling rpc.webhook ...', caplog)
assert len(rpc_manager.registered_modules) == 1 assert len(rpc_manager.registered_modules) == 1
assert 'webhook' in [mod.name for mod in rpc_manager.registered_modules] assert 'webhook' in [mod.name for mod in rpc_manager.registered_modules]
@ -144,7 +144,7 @@ def test_init_apiserver_disabled(mocker, default_conf, caplog) -> None:
default_conf['telegram']['enabled'] = False default_conf['telegram']['enabled'] = False
rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf)) rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf))
assert not log_has('Enabling rpc.api_server', caplog.record_tuples) assert not log_has('Enabling rpc.api_server', caplog)
assert rpc_manager.registered_modules == [] assert rpc_manager.registered_modules == []
assert run_mock.call_count == 0 assert run_mock.call_count == 0
@ -160,7 +160,7 @@ def test_init_apiserver_enabled(mocker, default_conf, caplog) -> None:
"listen_port": "8080"} "listen_port": "8080"}
rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf)) rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf))
assert log_has('Enabling rpc.api_server', caplog.record_tuples) assert log_has('Enabling rpc.api_server', caplog)
assert len(rpc_manager.registered_modules) == 1 assert len(rpc_manager.registered_modules) == 1
assert 'apiserver' in [mod.name for mod in rpc_manager.registered_modules] assert 'apiserver' in [mod.name for mod in rpc_manager.registered_modules]
assert run_mock.call_count == 1 assert run_mock.call_count == 1

View File

@ -76,7 +76,7 @@ def test_init(default_conf, mocker, caplog) -> None:
"['performance'], ['daily'], ['count'], ['reload_conf'], " \ "['performance'], ['daily'], ['count'], ['reload_conf'], " \
"['stopbuy'], ['whitelist'], ['blacklist'], ['edge'], ['help'], ['version']]" "['stopbuy'], ['whitelist'], ['blacklist'], ['edge'], ['help'], ['version']]"
assert log_has(message_str, caplog.record_tuples) assert log_has(message_str, caplog)
def test_cleanup(default_conf, mocker) -> None: def test_cleanup(default_conf, mocker) -> None:
@ -102,18 +102,9 @@ def test_authorized_only(default_conf, mocker, caplog) -> None:
dummy = DummyCls(bot) dummy = DummyCls(bot)
dummy.dummy_handler(bot=MagicMock(), update=update) dummy.dummy_handler(bot=MagicMock(), update=update)
assert dummy.state['called'] is True assert dummy.state['called'] is True
assert log_has( assert log_has('Executing handler: dummy_handler for chat_id: 0', caplog)
'Executing handler: dummy_handler for chat_id: 0', assert not log_has('Rejected unauthorized message from: 0', caplog)
caplog.record_tuples assert not log_has('Exception occurred within Telegram module', caplog)
)
assert not log_has(
'Rejected unauthorized message from: 0',
caplog.record_tuples
)
assert not log_has(
'Exception occurred within Telegram module',
caplog.record_tuples
)
def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None: def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None:
@ -128,18 +119,9 @@ def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None:
dummy = DummyCls(bot) dummy = DummyCls(bot)
dummy.dummy_handler(bot=MagicMock(), update=update) dummy.dummy_handler(bot=MagicMock(), update=update)
assert dummy.state['called'] is False assert dummy.state['called'] is False
assert not log_has( assert not log_has('Executing handler: dummy_handler for chat_id: 3735928559', caplog)
'Executing handler: dummy_handler for chat_id: 3735928559', assert log_has('Rejected unauthorized message from: 3735928559', caplog)
caplog.record_tuples assert not log_has('Exception occurred within Telegram module', caplog)
)
assert log_has(
'Rejected unauthorized message from: 3735928559',
caplog.record_tuples
)
assert not log_has(
'Exception occurred within Telegram module',
caplog.record_tuples
)
def test_authorized_only_exception(default_conf, mocker, caplog) -> None: def test_authorized_only_exception(default_conf, mocker, caplog) -> None:
@ -156,18 +138,9 @@ def test_authorized_only_exception(default_conf, mocker, caplog) -> None:
dummy.dummy_exception(bot=MagicMock(), update=update) dummy.dummy_exception(bot=MagicMock(), update=update)
assert dummy.state['called'] is False assert dummy.state['called'] is False
assert not log_has( assert not log_has('Executing handler: dummy_handler for chat_id: 0', caplog)
'Executing handler: dummy_handler for chat_id: 0', assert not log_has('Rejected unauthorized message from: 0', caplog)
caplog.record_tuples assert log_has('Exception occurred within Telegram module', caplog)
)
assert not log_has(
'Rejected unauthorized message from: 0',
caplog.record_tuples
)
assert log_has(
'Exception occurred within Telegram module',
caplog.record_tuples
)
def test_status(default_conf, update, mocker, fee, ticker, markets) -> None: def test_status(default_conf, update, mocker, fee, ticker, markets) -> None:
@ -1440,7 +1413,4 @@ def test__send_msg_network_error(default_conf, mocker, caplog) -> None:
# Bot should've tried to send it twice # Bot should've tried to send it twice
assert len(bot.method_calls) == 2 assert len(bot.method_calls) == 2
assert log_has( assert log_has('Telegram NetworkError: Oh snap! Trying one more time.', caplog)
'Telegram NetworkError: Oh snap! Trying one more time.',
caplog.record_tuples
)

View File

@ -115,7 +115,7 @@ def test_exception_send_msg(default_conf, mocker, caplog):
webhook = Webhook(get_patched_freqtradebot(mocker, default_conf)) webhook = Webhook(get_patched_freqtradebot(mocker, default_conf))
webhook.send_msg({'type': RPCMessageType.BUY_NOTIFICATION}) webhook.send_msg({'type': RPCMessageType.BUY_NOTIFICATION})
assert log_has(f"Message type {RPCMessageType.BUY_NOTIFICATION} not configured for webhooks", assert log_has(f"Message type {RPCMessageType.BUY_NOTIFICATION} not configured for webhooks",
caplog.record_tuples) caplog)
default_conf["webhook"] = get_webhook_dict() default_conf["webhook"] = get_webhook_dict()
default_conf["webhook"]["webhookbuy"]["value1"] = "{DEADBEEF:8f}" default_conf["webhook"]["webhookbuy"]["value1"] = "{DEADBEEF:8f}"
@ -135,7 +135,7 @@ def test_exception_send_msg(default_conf, mocker, caplog):
} }
webhook.send_msg(msg) webhook.send_msg(msg)
assert log_has("Problem calling Webhook. Please check your webhook configuration. " assert log_has("Problem calling Webhook. Please check your webhook configuration. "
"Exception: 'DEADBEEF'", caplog.record_tuples) "Exception: 'DEADBEEF'", caplog)
msg_mock = MagicMock() msg_mock = MagicMock()
mocker.patch("freqtrade.rpc.webhook.Webhook._send_msg", msg_mock) mocker.patch("freqtrade.rpc.webhook.Webhook._send_msg", msg_mock)
@ -164,4 +164,4 @@ def test__send_msg(default_conf, mocker, caplog):
post = MagicMock(side_effect=RequestException) post = MagicMock(side_effect=RequestException)
mocker.patch("freqtrade.rpc.webhook.post", post) mocker.patch("freqtrade.rpc.webhook.post", post)
webhook._send_msg(msg) webhook._send_msg(msg)
assert log_has('Could not call webhook url. Exception: ', caplog.record_tuples) assert log_has('Could not call webhook url. Exception: ', caplog)

View File

@ -49,12 +49,12 @@ def test_returns_latest_sell_signal(mocker, default_conf, ticker_history):
def test_get_signal_empty(default_conf, mocker, caplog): def test_get_signal_empty(default_conf, mocker, caplog):
assert (False, False) == _STRATEGY.get_signal('foo', default_conf['ticker_interval'], assert (False, False) == _STRATEGY.get_signal('foo', default_conf['ticker_interval'],
DataFrame()) DataFrame())
assert log_has('Empty ticker history for pair foo', caplog.record_tuples) assert log_has('Empty ticker history for pair foo', caplog)
caplog.clear() caplog.clear()
assert (False, False) == _STRATEGY.get_signal('bar', default_conf['ticker_interval'], assert (False, False) == _STRATEGY.get_signal('bar', default_conf['ticker_interval'],
[]) [])
assert log_has('Empty ticker history for pair bar', caplog.record_tuples) assert log_has('Empty ticker history for pair bar', caplog)
def test_get_signal_exception_valueerror(default_conf, mocker, caplog, ticker_history): def test_get_signal_exception_valueerror(default_conf, mocker, caplog, ticker_history):
@ -65,7 +65,7 @@ def test_get_signal_exception_valueerror(default_conf, mocker, caplog, ticker_hi
) )
assert (False, False) == _STRATEGY.get_signal('foo', default_conf['ticker_interval'], assert (False, False) == _STRATEGY.get_signal('foo', default_conf['ticker_interval'],
ticker_history) ticker_history)
assert log_has('Unable to analyze ticker for pair foo: xyz', caplog.record_tuples) assert log_has('Unable to analyze ticker for pair foo: xyz', caplog)
def test_get_signal_empty_dataframe(default_conf, mocker, caplog, ticker_history): def test_get_signal_empty_dataframe(default_conf, mocker, caplog, ticker_history):
@ -76,7 +76,7 @@ def test_get_signal_empty_dataframe(default_conf, mocker, caplog, ticker_history
) )
assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['ticker_interval'], assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['ticker_interval'],
ticker_history) ticker_history)
assert log_has('Empty dataframe for pair xyz', caplog.record_tuples) assert log_has('Empty dataframe for pair xyz', caplog)
def test_get_signal_old_dataframe(default_conf, mocker, caplog, ticker_history): def test_get_signal_old_dataframe(default_conf, mocker, caplog, ticker_history):
@ -91,10 +91,7 @@ def test_get_signal_old_dataframe(default_conf, mocker, caplog, ticker_history):
) )
assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['ticker_interval'], assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['ticker_interval'],
ticker_history) ticker_history)
assert log_has( assert log_has('Outdated history for pair xyz. Last tick is 16 minutes old', caplog)
'Outdated history for pair xyz. Last tick is 16 minutes old',
caplog.record_tuples
)
def test_get_signal_handles_exceptions(mocker, default_conf): def test_get_signal_handles_exceptions(mocker, default_conf):
@ -237,9 +234,8 @@ def test_analyze_ticker_default(ticker_history, mocker, caplog) -> None:
assert buy_mock.call_count == 1 assert buy_mock.call_count == 1
assert buy_mock.call_count == 1 assert buy_mock.call_count == 1
assert log_has('TA Analysis Launched', caplog.record_tuples) assert log_has('TA Analysis Launched', caplog)
assert not log_has('Skipping TA Analysis for already analyzed candle', assert not log_has('Skipping TA Analysis for already analyzed candle', caplog)
caplog.record_tuples)
caplog.clear() caplog.clear()
strategy.analyze_ticker(ticker_history, {'pair': 'ETH/BTC'}) strategy.analyze_ticker(ticker_history, {'pair': 'ETH/BTC'})
@ -247,9 +243,8 @@ def test_analyze_ticker_default(ticker_history, mocker, caplog) -> None:
assert ind_mock.call_count == 2 assert ind_mock.call_count == 2
assert buy_mock.call_count == 2 assert buy_mock.call_count == 2
assert buy_mock.call_count == 2 assert buy_mock.call_count == 2
assert log_has('TA Analysis Launched', caplog.record_tuples) assert log_has('TA Analysis Launched', caplog)
assert not log_has('Skipping TA Analysis for already analyzed candle', assert not log_has('Skipping TA Analysis for already analyzed candle', caplog)
caplog.record_tuples)
def test__analyze_ticker_internal_skip_analyze(ticker_history, mocker, caplog) -> None: def test__analyze_ticker_internal_skip_analyze(ticker_history, mocker, caplog) -> None:
@ -275,9 +270,8 @@ def test__analyze_ticker_internal_skip_analyze(ticker_history, mocker, caplog) -
assert ind_mock.call_count == 1 assert ind_mock.call_count == 1
assert buy_mock.call_count == 1 assert buy_mock.call_count == 1
assert buy_mock.call_count == 1 assert buy_mock.call_count == 1
assert log_has('TA Analysis Launched', caplog.record_tuples) assert log_has('TA Analysis Launched', caplog)
assert not log_has('Skipping TA Analysis for already analyzed candle', assert not log_has('Skipping TA Analysis for already analyzed candle', caplog)
caplog.record_tuples)
caplog.clear() caplog.clear()
ret = strategy._analyze_ticker_internal(ticker_history, {'pair': 'ETH/BTC'}) ret = strategy._analyze_ticker_internal(ticker_history, {'pair': 'ETH/BTC'})
@ -290,6 +284,5 @@ def test__analyze_ticker_internal_skip_analyze(ticker_history, mocker, caplog) -
assert 'sell' in ret.columns assert 'sell' in ret.columns
assert ret['buy'].sum() == 0 assert ret['buy'].sum() == 0
assert ret['sell'].sum() == 0 assert ret['sell'].sum() == 0
assert not log_has('TA Analysis Launched', caplog.record_tuples) assert not log_has('TA Analysis Launched', caplog)
assert log_has('Skipping TA Analysis for already analyzed candle', assert log_has('Skipping TA Analysis for already analyzed candle', caplog)
caplog.record_tuples)

View File

@ -15,7 +15,7 @@ from freqtrade.resolvers import StrategyResolver
from freqtrade.strategy import import_strategy from freqtrade.strategy import import_strategy
from freqtrade.strategy.default_strategy import DefaultStrategy from freqtrade.strategy.default_strategy import DefaultStrategy
from freqtrade.strategy.interface import IStrategy from freqtrade.strategy.interface import IStrategy
from freqtrade.tests.conftest import log_has_re from freqtrade.tests.conftest import log_has, log_has_re
def test_import_strategy(caplog): def test_import_strategy(caplog):
@ -35,12 +35,8 @@ def test_import_strategy(caplog):
assert imported_strategy.__module__ == 'freqtrade.strategy' assert imported_strategy.__module__ == 'freqtrade.strategy'
assert imported_strategy.some_method() == 42 assert imported_strategy.some_method() == 42
assert ( assert log_has('Imported strategy freqtrade.strategy.default_strategy.DefaultStrategy '
'freqtrade.strategy', 'as freqtrade.strategy.DefaultStrategy', caplog)
logging.DEBUG,
'Imported strategy freqtrade.strategy.default_strategy.DefaultStrategy '
'as freqtrade.strategy.DefaultStrategy',
) in caplog.record_tuples
def test_search_strategy(): def test_search_strategy():
@ -76,8 +72,7 @@ def test_load_strategy_base64(result, caplog):
assert 'adx' in resolver.strategy.advise_indicators(result, {'pair': 'ETH/BTC'}) assert 'adx' in resolver.strategy.advise_indicators(result, {'pair': 'ETH/BTC'})
# Make sure strategy was loaded from base64 (using temp directory)!! # Make sure strategy was loaded from base64 (using temp directory)!!
assert log_has_re(r"Using resolved strategy TestStrategy from '" assert log_has_re(r"Using resolved strategy TestStrategy from '"
+ tempfile.gettempdir() + r"/.*/TestStrategy\.py'\.\.\.", + tempfile.gettempdir() + r"/.*/TestStrategy\.py'\.\.\.", caplog)
caplog.record_tuples)
def test_load_strategy_invalid_directory(result, caplog): def test_load_strategy_invalid_directory(result, caplog):
@ -85,7 +80,7 @@ def test_load_strategy_invalid_directory(result, caplog):
extra_dir = Path.cwd() / 'some/path' extra_dir = Path.cwd() / 'some/path'
resolver._load_strategy('TestStrategy', config={}, extra_dir=extra_dir) resolver._load_strategy('TestStrategy', config={}, extra_dir=extra_dir)
assert log_has_re(r'Path .*' + r'some.*path.*' + r'.* does not exist', caplog.record_tuples) assert log_has_re(r'Path .*' + r'some.*path.*' + r'.* does not exist', caplog)
assert 'adx' in resolver.strategy.advise_indicators(result, {'pair': 'ETH/BTC'}) assert 'adx' in resolver.strategy.advise_indicators(result, {'pair': 'ETH/BTC'})
@ -105,7 +100,7 @@ def test_load_staticmethod_importerror(mocker, caplog):
match=r"Impossible to load Strategy 'DefaultStrategy'. " match=r"Impossible to load Strategy 'DefaultStrategy'. "
r"This class does not exist or contains Python code errors."): r"This class does not exist or contains Python code errors."):
StrategyResolver() StrategyResolver()
assert log_has_re(r".*Error: can't pickle staticmethod objects", caplog.record_tuples) assert log_has_re(r".*Error: can't pickle staticmethod objects", caplog)
def test_strategy(result): def test_strategy(result):
@ -143,10 +138,7 @@ def test_strategy_override_minimal_roi(caplog):
resolver = StrategyResolver(config) resolver = StrategyResolver(config)
assert resolver.strategy.minimal_roi[0] == 0.5 assert resolver.strategy.minimal_roi[0] == 0.5
assert ('freqtrade.resolvers.strategy_resolver', assert log_has("Override strategy 'minimal_roi' with value in config file: {'0': 0.5}.", caplog)
logging.INFO,
"Override strategy 'minimal_roi' with value in config file: {'0': 0.5}."
) in caplog.record_tuples
def test_strategy_override_stoploss(caplog): def test_strategy_override_stoploss(caplog):
@ -158,10 +150,7 @@ def test_strategy_override_stoploss(caplog):
resolver = StrategyResolver(config) resolver = StrategyResolver(config)
assert resolver.strategy.stoploss == -0.5 assert resolver.strategy.stoploss == -0.5
assert ('freqtrade.resolvers.strategy_resolver', assert log_has("Override strategy 'stoploss' with value in config file: -0.5.", caplog)
logging.INFO,
"Override strategy 'stoploss' with value in config file: -0.5."
) in caplog.record_tuples
def test_strategy_override_trailing_stop(caplog): def test_strategy_override_trailing_stop(caplog):
@ -174,10 +163,7 @@ def test_strategy_override_trailing_stop(caplog):
assert resolver.strategy.trailing_stop assert resolver.strategy.trailing_stop
assert isinstance(resolver.strategy.trailing_stop, bool) assert isinstance(resolver.strategy.trailing_stop, bool)
assert ('freqtrade.resolvers.strategy_resolver', assert log_has("Override strategy 'trailing_stop' with value in config file: True.", caplog)
logging.INFO,
"Override strategy 'trailing_stop' with value in config file: True."
) in caplog.record_tuples
def test_strategy_override_trailing_stop_positive(caplog): def test_strategy_override_trailing_stop_positive(caplog):
@ -191,16 +177,12 @@ def test_strategy_override_trailing_stop_positive(caplog):
resolver = StrategyResolver(config) resolver = StrategyResolver(config)
assert resolver.strategy.trailing_stop_positive == -0.1 assert resolver.strategy.trailing_stop_positive == -0.1
assert ('freqtrade.resolvers.strategy_resolver', assert log_has("Override strategy 'trailing_stop_positive' with value in config file: -0.1.",
logging.INFO, caplog)
"Override strategy 'trailing_stop_positive' with value in config file: -0.1."
) in caplog.record_tuples
assert resolver.strategy.trailing_stop_positive_offset == -0.2 assert resolver.strategy.trailing_stop_positive_offset == -0.2
assert ('freqtrade.resolvers.strategy_resolver', assert log_has("Override strategy 'trailing_stop_positive' with value in config file: -0.1.",
logging.INFO, caplog)
"Override strategy 'trailing_stop_positive' with value in config file: -0.1."
) in caplog.record_tuples
def test_strategy_override_ticker_interval(caplog): def test_strategy_override_ticker_interval(caplog):
@ -215,10 +197,8 @@ def test_strategy_override_ticker_interval(caplog):
assert resolver.strategy.ticker_interval == 60 assert resolver.strategy.ticker_interval == 60
assert resolver.strategy.stake_currency == 'ETH' assert resolver.strategy.stake_currency == 'ETH'
assert ('freqtrade.resolvers.strategy_resolver', assert log_has("Override strategy 'ticker_interval' with value in config file: 60.",
logging.INFO, caplog)
"Override strategy 'ticker_interval' with value in config file: 60."
) in caplog.record_tuples
def test_strategy_override_process_only_new_candles(caplog): def test_strategy_override_process_only_new_candles(caplog):
@ -231,10 +211,8 @@ def test_strategy_override_process_only_new_candles(caplog):
resolver = StrategyResolver(config) resolver = StrategyResolver(config)
assert resolver.strategy.process_only_new_candles assert resolver.strategy.process_only_new_candles
assert ('freqtrade.resolvers.strategy_resolver', assert log_has("Override strategy 'process_only_new_candles' with value in config file: True.",
logging.INFO, caplog)
"Override strategy 'process_only_new_candles' with value in config file: True."
) in caplog.record_tuples
def test_strategy_override_order_types(caplog): def test_strategy_override_order_types(caplog):
@ -257,12 +235,9 @@ def test_strategy_override_order_types(caplog):
for method in ['buy', 'sell', 'stoploss', 'stoploss_on_exchange']: for method in ['buy', 'sell', 'stoploss', 'stoploss_on_exchange']:
assert resolver.strategy.order_types[method] == order_types[method] assert resolver.strategy.order_types[method] == order_types[method]
assert ('freqtrade.resolvers.strategy_resolver', assert log_has("Override strategy 'order_types' with value in config file:"
logging.INFO,
"Override strategy 'order_types' with value in config file:"
" {'buy': 'market', 'sell': 'limit', 'stoploss': 'limit'," " {'buy': 'market', 'sell': 'limit', 'stoploss': 'limit',"
" 'stoploss_on_exchange': True}." " 'stoploss_on_exchange': True}.", caplog)
) in caplog.record_tuples
config = { config = {
'strategy': 'DefaultStrategy', 'strategy': 'DefaultStrategy',
@ -293,11 +268,8 @@ def test_strategy_override_order_tif(caplog):
for method in ['buy', 'sell']: for method in ['buy', 'sell']:
assert resolver.strategy.order_time_in_force[method] == order_time_in_force[method] assert resolver.strategy.order_time_in_force[method] == order_time_in_force[method]
assert ('freqtrade.resolvers.strategy_resolver', assert log_has("Override strategy 'order_time_in_force' with value in config file:"
logging.INFO, " {'buy': 'fok', 'sell': 'gtc'}.", caplog)
"Override strategy 'order_time_in_force' with value in config file:"
" {'buy': 'fok', 'sell': 'gtc'}."
) in caplog.record_tuples
config = { config = {
'strategy': 'DefaultStrategy', 'strategy': 'DefaultStrategy',
@ -332,10 +304,7 @@ def test_strategy_override_use_sell_signal(caplog):
assert resolver.strategy.use_sell_signal assert resolver.strategy.use_sell_signal
assert isinstance(resolver.strategy.use_sell_signal, bool) assert isinstance(resolver.strategy.use_sell_signal, bool)
assert ('freqtrade.resolvers.strategy_resolver', assert log_has("Override strategy 'use_sell_signal' with value in config file: True.", caplog)
logging.INFO,
"Override strategy 'use_sell_signal' with value in config file: True."
) in caplog.record_tuples
def test_strategy_override_use_sell_profit_only(caplog): def test_strategy_override_use_sell_profit_only(caplog):
@ -360,10 +329,7 @@ def test_strategy_override_use_sell_profit_only(caplog):
assert resolver.strategy.sell_profit_only assert resolver.strategy.sell_profit_only
assert isinstance(resolver.strategy.sell_profit_only, bool) assert isinstance(resolver.strategy.sell_profit_only, bool)
assert ('freqtrade.resolvers.strategy_resolver', assert log_has("Override strategy 'sell_profit_only' with value in config file: True.", caplog)
logging.INFO,
"Override strategy 'sell_profit_only' with value in config file: True."
) in caplog.record_tuples
@pytest.mark.filterwarnings("ignore:deprecated") @pytest.mark.filterwarnings("ignore:deprecated")

View File

@ -72,7 +72,7 @@ def test__args_to_config(caplog):
# No warnings ... # No warnings ...
configuration._args_to_config(config, argname="strategy_path", logstring="DeadBeef") configuration._args_to_config(config, argname="strategy_path", logstring="DeadBeef")
assert len(w) == 0 assert len(w) == 0
assert log_has("DeadBeef", caplog.record_tuples) assert log_has("DeadBeef", caplog)
assert config['strategy_path'] == "TestTest" assert config['strategy_path'] == "TestTest"
configuration = Configuration(args) configuration = Configuration(args)
@ -84,7 +84,7 @@ def test__args_to_config(caplog):
assert len(w) == 1 assert len(w) == 1
assert issubclass(w[-1].category, DeprecationWarning) assert issubclass(w[-1].category, DeprecationWarning)
assert "DEPRECATED: Going away soon!" in str(w[-1].message) assert "DEPRECATED: Going away soon!" in str(w[-1].message)
assert log_has("DeadBeef", caplog.record_tuples) assert log_has("DeadBeef", caplog)
assert config['strategy_path'] == "TestTest" assert config['strategy_path'] == "TestTest"
@ -98,7 +98,7 @@ def test_load_config_max_open_trades_zero(default_conf, mocker, caplog) -> None:
assert validated_conf['max_open_trades'] == 0 assert validated_conf['max_open_trades'] == 0
assert 'internals' in validated_conf assert 'internals' in validated_conf
assert log_has('Validating configuration ...', caplog.record_tuples) assert log_has('Validating configuration ...', caplog)
def test_load_config_combine_dicts(default_conf, mocker, caplog) -> None: def test_load_config_combine_dicts(default_conf, mocker, caplog) -> None:
@ -130,7 +130,36 @@ def test_load_config_combine_dicts(default_conf, mocker, caplog) -> None:
assert validated_conf['exchange']['pair_whitelist'] == conf2['exchange']['pair_whitelist'] assert validated_conf['exchange']['pair_whitelist'] == conf2['exchange']['pair_whitelist']
assert 'internals' in validated_conf assert 'internals' in validated_conf
assert log_has('Validating configuration ...', caplog.record_tuples) assert log_has('Validating configuration ...', caplog)
def test_from_config(default_conf, mocker, caplog) -> None:
conf1 = deepcopy(default_conf)
conf2 = deepcopy(default_conf)
del conf1['exchange']['key']
del conf1['exchange']['secret']
del conf2['exchange']['name']
conf2['exchange']['pair_whitelist'] += ['NANO/BTC']
conf2['fiat_display_currency'] = "EUR"
config_files = [conf1, conf2]
configsmock = MagicMock(side_effect=config_files)
mocker.patch(
'freqtrade.configuration.configuration.load_config_file',
configsmock
)
validated_conf = Configuration.from_files(['test_conf.json', 'test2_conf.json'])
exchange_conf = default_conf['exchange']
assert validated_conf['exchange']['name'] == exchange_conf['name']
assert validated_conf['exchange']['key'] == exchange_conf['key']
assert validated_conf['exchange']['secret'] == exchange_conf['secret']
assert validated_conf['exchange']['pair_whitelist'] != conf1['exchange']['pair_whitelist']
assert validated_conf['exchange']['pair_whitelist'] == conf2['exchange']['pair_whitelist']
assert validated_conf['fiat_display_currency'] == "EUR"
assert 'internals' in validated_conf
assert log_has('Validating configuration ...', caplog)
def test_load_config_max_open_trades_minus_one(default_conf, mocker, caplog) -> None: def test_load_config_max_open_trades_minus_one(default_conf, mocker, caplog) -> None:
@ -143,7 +172,7 @@ def test_load_config_max_open_trades_minus_one(default_conf, mocker, caplog) ->
assert validated_conf['max_open_trades'] > 999999999 assert validated_conf['max_open_trades'] > 999999999
assert validated_conf['max_open_trades'] == float('inf') assert validated_conf['max_open_trades'] == float('inf')
assert log_has('Validating configuration ...', caplog.record_tuples) assert log_has('Validating configuration ...', caplog)
assert "runmode" in validated_conf assert "runmode" in validated_conf
assert validated_conf['runmode'] == RunMode.DRY_RUN assert validated_conf['runmode'] == RunMode.DRY_RUN
@ -280,8 +309,8 @@ def test_show_info(default_conf, mocker, caplog) -> None:
configuration = Configuration(args) configuration = Configuration(args)
configuration.get_config() configuration.get_config()
assert log_has('Using DB: "sqlite:///tmp/testdb"', caplog.record_tuples) assert log_has('Using DB: "sqlite:///tmp/testdb"', caplog)
assert log_has('Dry run is enabled', caplog.record_tuples) assert log_has('Dry run is enabled', caplog)
def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> None: def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> None:
@ -303,21 +332,18 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) ->
assert 'exchange' in config assert 'exchange' in config
assert 'pair_whitelist' in config['exchange'] assert 'pair_whitelist' in config['exchange']
assert 'datadir' in config assert 'datadir' in config
assert log_has( assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog)
'Using data directory: {} ...'.format(config['datadir']),
caplog.record_tuples
)
assert 'ticker_interval' in config assert 'ticker_interval' in config
assert not log_has('Parameter -i/--ticker-interval detected ...', caplog.record_tuples) assert not log_has('Parameter -i/--ticker-interval detected ...', caplog)
assert 'live' not in config assert 'live' not in config
assert not log_has('Parameter -l/--live detected ...', caplog.record_tuples) assert not log_has('Parameter -l/--live detected ...', caplog)
assert 'position_stacking' not in config assert 'position_stacking' not in config
assert not log_has('Parameter --enable-position-stacking detected ...', caplog.record_tuples) assert not log_has('Parameter --enable-position-stacking detected ...', caplog)
assert 'refresh_pairs' not in config assert 'refresh_pairs' not in config
assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples) assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog)
assert 'timerange' not in config assert 'timerange' not in config
assert 'export' not in config assert 'export' not in config
@ -355,37 +381,28 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non
assert 'exchange' in config assert 'exchange' in config
assert 'pair_whitelist' in config['exchange'] assert 'pair_whitelist' in config['exchange']
assert 'datadir' in config assert 'datadir' in config
assert log_has( assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog)
'Using data directory: {} ...'.format(config['datadir']),
caplog.record_tuples
)
assert 'ticker_interval' in config assert 'ticker_interval' in config
assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...',
caplog.record_tuples) caplog)
assert 'live' in config assert 'live' in config
assert log_has('Parameter -l/--live detected ...', caplog.record_tuples) assert log_has('Parameter -l/--live detected ...', caplog)
assert 'position_stacking'in config assert 'position_stacking'in config
assert log_has('Parameter --enable-position-stacking detected ...', caplog.record_tuples) assert log_has('Parameter --enable-position-stacking detected ...', caplog)
assert 'use_max_market_positions' in config assert 'use_max_market_positions' in config
assert log_has('Parameter --disable-max-market-positions detected ...', caplog.record_tuples) assert log_has('Parameter --disable-max-market-positions detected ...', caplog)
assert log_has('max_open_trades set to unlimited ...', caplog.record_tuples) assert log_has('max_open_trades set to unlimited ...', caplog)
assert 'refresh_pairs'in config assert 'refresh_pairs'in config
assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples) assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog)
assert 'timerange' in config assert 'timerange' in config
assert log_has( assert log_has('Parameter --timerange detected: {} ...'.format(config['timerange']), caplog)
'Parameter --timerange detected: {} ...'.format(config['timerange']),
caplog.record_tuples
)
assert 'export' in config assert 'export' in config
assert log_has( assert log_has('Parameter --export detected: {} ...'.format(config['export']), caplog)
'Parameter --export detected: {} ...'.format(config['export']),
caplog.record_tuples
)
def test_setup_configuration_with_stratlist(mocker, default_conf, caplog) -> None: def test_setup_configuration_with_stratlist(mocker, default_conf, caplog) -> None:
@ -415,16 +432,13 @@ def test_setup_configuration_with_stratlist(mocker, default_conf, caplog) -> Non
assert 'exchange' in config assert 'exchange' in config
assert 'pair_whitelist' in config['exchange'] assert 'pair_whitelist' in config['exchange']
assert 'datadir' in config assert 'datadir' in config
assert log_has( assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog)
'Using data directory: {} ...'.format(config['datadir']),
caplog.record_tuples
)
assert 'ticker_interval' in config assert 'ticker_interval' in config
assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...',
caplog.record_tuples) caplog)
assert 'strategy_list' in config assert 'strategy_list' in config
assert log_has('Using strategy list of 2 Strategies', caplog.record_tuples) assert log_has('Using strategy list of 2 Strategies', caplog)
assert 'position_stacking' not in config assert 'position_stacking' not in config
@ -433,10 +447,7 @@ def test_setup_configuration_with_stratlist(mocker, default_conf, caplog) -> Non
assert 'timerange' not in config assert 'timerange' not in config
assert 'export' in config assert 'export' in config
assert log_has( assert log_has('Parameter --export detected: {} ...'.format(config['export']), caplog)
'Parameter --export detected: {} ...'.format(config['export']),
caplog.record_tuples
)
def test_hyperopt_with_arguments(mocker, default_conf, caplog) -> None: def test_hyperopt_with_arguments(mocker, default_conf, caplog) -> None:
@ -455,11 +466,11 @@ def test_hyperopt_with_arguments(mocker, default_conf, caplog) -> None:
assert 'epochs' in config assert 'epochs' in config
assert int(config['epochs']) == 10 assert int(config['epochs']) == 10
assert log_has('Parameter --epochs detected ... Will run Hyperopt with for 10 epochs ...', assert log_has('Parameter --epochs detected ... Will run Hyperopt with for 10 epochs ...',
caplog.record_tuples) caplog)
assert 'spaces' in config assert 'spaces' in config
assert config['spaces'] == ['all'] assert config['spaces'] == ['all']
assert log_has('Parameter -s/--spaces detected: [\'all\']', caplog.record_tuples) assert log_has('Parameter -s/--spaces detected: [\'all\']', caplog)
assert "runmode" in config assert "runmode" in config
assert config['runmode'] == RunMode.HYPEROPT assert config['runmode'] == RunMode.HYPEROPT
@ -469,38 +480,35 @@ def test_check_exchange(default_conf, caplog) -> None:
default_conf.get('exchange').update({'name': 'BITTREX'}) default_conf.get('exchange').update({'name': 'BITTREX'})
assert check_exchange(default_conf) assert check_exchange(default_conf)
assert log_has_re(r"Exchange .* is officially supported by the Freqtrade development team\.", assert log_has_re(r"Exchange .* is officially supported by the Freqtrade development team\.",
caplog.record_tuples) caplog)
caplog.clear() caplog.clear()
# Test an officially supported by Freqtrade team exchange # Test an officially supported by Freqtrade team exchange
default_conf.get('exchange').update({'name': 'binance'}) default_conf.get('exchange').update({'name': 'binance'})
assert check_exchange(default_conf) assert check_exchange(default_conf)
assert log_has_re(r"Exchange .* is officially supported by the Freqtrade development team\.", assert log_has_re(r"Exchange .* is officially supported by the Freqtrade development team\.",
caplog.record_tuples) caplog)
caplog.clear() caplog.clear()
# Test an available exchange, supported by ccxt # Test an available exchange, supported by ccxt
default_conf.get('exchange').update({'name': 'kraken'}) default_conf.get('exchange').update({'name': 'kraken'})
assert check_exchange(default_conf) assert check_exchange(default_conf)
assert log_has_re(r"Exchange .* is supported by ccxt and .* not officially supported " assert log_has_re(r"Exchange .* is supported by ccxt and .* not officially supported "
r"by the Freqtrade development team\. .*", r"by the Freqtrade development team\. .*", caplog)
caplog.record_tuples)
caplog.clear() caplog.clear()
# Test a 'bad' exchange, which known to have serious problems # Test a 'bad' exchange, which known to have serious problems
default_conf.get('exchange').update({'name': 'bitmex'}) default_conf.get('exchange').update({'name': 'bitmex'})
assert not check_exchange(default_conf) assert not check_exchange(default_conf)
assert log_has_re(r"Exchange .* is known to not work with the bot yet\. " assert log_has_re(r"Exchange .* is known to not work with the bot yet\. "
r"Use it only for development and testing purposes\.", r"Use it only for development and testing purposes\.", caplog)
caplog.record_tuples)
caplog.clear() caplog.clear()
# Test a 'bad' exchange with check_for_bad=False # Test a 'bad' exchange with check_for_bad=False
default_conf.get('exchange').update({'name': 'bitmex'}) default_conf.get('exchange').update({'name': 'bitmex'})
assert check_exchange(default_conf, False) assert check_exchange(default_conf, False)
assert log_has_re(r"Exchange .* is supported by ccxt and .* not officially supported " assert log_has_re(r"Exchange .* is supported by ccxt and .* not officially supported "
r"by the Freqtrade development team\. .*", r"by the Freqtrade development team\. .*", caplog)
caplog.record_tuples)
caplog.clear() caplog.clear()
# Test an invalid exchange # Test an invalid exchange
@ -526,7 +534,7 @@ def test_cli_verbose_with_params(default_conf, mocker, caplog) -> None:
validated_conf = configuration.load_config() validated_conf = configuration.load_config()
assert validated_conf.get('verbosity') == 3 assert validated_conf.get('verbosity') == 3
assert log_has('Verbosity set to 3', caplog.record_tuples) assert log_has('Verbosity set to 3', caplog)
def test_set_loggers() -> None: def test_set_loggers() -> None:
@ -592,7 +600,7 @@ def test_load_config_warn_forcebuy(default_conf, mocker, caplog) -> None:
validated_conf = configuration.load_config() validated_conf = configuration.load_config()
assert validated_conf.get('forcebuy_enable') assert validated_conf.get('forcebuy_enable')
assert log_has('`forcebuy` RPC message enabled.', caplog.record_tuples) assert log_has('`forcebuy` RPC message enabled.', caplog)
def test_validate_default_conf(default_conf) -> None: def test_validate_default_conf(default_conf) -> None:
@ -605,7 +613,7 @@ def test_create_datadir(mocker, default_conf, caplog) -> None:
create_datadir(default_conf, '/foo/bar') create_datadir(default_conf, '/foo/bar')
assert md.call_args[1]['parents'] is True assert md.call_args[1]['parents'] is True
assert log_has('Created data directory: /foo/bar', caplog.record_tuples) assert log_has('Created data directory: /foo/bar', caplog)
def test_validate_tsl(default_conf): def test_validate_tsl(default_conf):

View File

@ -16,7 +16,7 @@ from freqtrade.data.dataprovider import DataProvider
from freqtrade.freqtradebot import FreqtradeBot from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
from freqtrade.rpc import RPCMessageType from freqtrade.rpc import RPCMessageType
from freqtrade.state import State from freqtrade.state import State, RunMode
from freqtrade.strategy.interface import SellCheckTuple, SellType from freqtrade.strategy.interface import SellCheckTuple, SellType
from freqtrade.tests.conftest import (get_patched_freqtradebot, from freqtrade.tests.conftest import (get_patched_freqtradebot,
get_patched_worker, log_has, log_has_re, get_patched_worker, log_has, log_has_re,
@ -63,7 +63,7 @@ def test_cleanup(mocker, default_conf, caplog) -> None:
mocker.patch('freqtrade.persistence.cleanup', mock_cleanup) mocker.patch('freqtrade.persistence.cleanup', mock_cleanup)
freqtrade = get_patched_freqtradebot(mocker, default_conf) freqtrade = get_patched_freqtradebot(mocker, default_conf)
freqtrade.cleanup() freqtrade.cleanup()
assert log_has('Cleaning up modules ...', caplog.record_tuples) assert log_has('Cleaning up modules ...', caplog)
assert mock_cleanup.call_count == 1 assert mock_cleanup.call_count == 1
@ -76,7 +76,7 @@ def test_worker_running(mocker, default_conf, caplog) -> None:
state = worker._worker(old_state=None) state = worker._worker(old_state=None)
assert state is State.RUNNING assert state is State.RUNNING
assert log_has('Changing state to: RUNNING', caplog.record_tuples) assert log_has('Changing state to: RUNNING', caplog)
assert mock_throttle.call_count == 1 assert mock_throttle.call_count == 1
# Check strategy is loaded, and received a dataprovider object # Check strategy is loaded, and received a dataprovider object
assert worker.freqtrade.strategy assert worker.freqtrade.strategy
@ -93,7 +93,7 @@ def test_worker_stopped(mocker, default_conf, caplog) -> None:
worker.state = State.STOPPED worker.state = State.STOPPED
state = worker._worker(old_state=State.RUNNING) state = worker._worker(old_state=State.RUNNING)
assert state is State.STOPPED assert state is State.STOPPED
assert log_has('Changing state to: STOPPED', caplog.record_tuples) assert log_has('Changing state to: STOPPED', caplog)
assert mock_throttle.call_count == 0 assert mock_throttle.call_count == 0
assert mock_sleep.call_count == 1 assert mock_sleep.call_count == 1
@ -111,7 +111,7 @@ def test_throttle(mocker, default_conf, caplog) -> None:
assert result == 42 assert result == 42
assert end - start > 0.1 assert end - start > 0.1
assert log_has('Throttling throttled_func for 0.10 seconds', caplog.record_tuples) assert log_has('Throttling throttled_func for 0.10 seconds', caplog)
result = worker._throttle(throttled_func, min_secs=-1) result = worker._throttle(throttled_func, min_secs=-1)
assert result == 42 assert result == 42
@ -130,7 +130,77 @@ def test_throttle_with_assets(mocker, default_conf) -> None:
assert result == -1 assert result == -1
def test_get_trade_stake_amount(default_conf, ticker, limit_buy_order, fee, mocker) -> None: def test_order_dict_dry_run(default_conf, mocker, caplog) -> None:
patch_RPCManager(mocker)
patch_exchange(mocker)
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_balance=MagicMock(return_value=default_conf['stake_amount'] * 2)
)
conf = default_conf.copy()
conf['runmode'] = RunMode.DRY_RUN
conf['order_types'] = {
'buy': 'market',
'sell': 'limit',
'stoploss': 'limit',
'stoploss_on_exchange': True,
}
freqtrade = FreqtradeBot(conf)
assert log_has("Disabling stoploss_on_exchange during dry-run.", caplog)
assert not freqtrade.strategy.order_types['stoploss_on_exchange']
caplog.clear()
# is left untouched
conf = default_conf.copy()
conf['runmode'] = RunMode.DRY_RUN
conf['order_types'] = {
'buy': 'market',
'sell': 'limit',
'stoploss': 'limit',
'stoploss_on_exchange': False,
}
freqtrade = FreqtradeBot(conf)
assert not freqtrade.strategy.order_types['stoploss_on_exchange']
assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog)
def test_order_dict_live(default_conf, mocker, caplog) -> None:
patch_RPCManager(mocker)
patch_exchange(mocker)
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_balance=MagicMock(return_value=default_conf['stake_amount'] * 2)
)
conf = default_conf.copy()
conf['runmode'] = RunMode.LIVE
conf['order_types'] = {
'buy': 'market',
'sell': 'limit',
'stoploss': 'limit',
'stoploss_on_exchange': True,
}
freqtrade = FreqtradeBot(conf)
assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog)
assert freqtrade.strategy.order_types['stoploss_on_exchange']
caplog.clear()
# is left untouched
conf = default_conf.copy()
conf['runmode'] = RunMode.LIVE
conf['order_types'] = {
'buy': 'market',
'sell': 'limit',
'stoploss': 'limit',
'stoploss_on_exchange': False,
}
freqtrade = FreqtradeBot(conf)
assert not freqtrade.strategy.order_types['stoploss_on_exchange']
assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog)
def test_get_trade_stake_amount(default_conf, ticker, mocker) -> None:
patch_RPCManager(mocker) patch_RPCManager(mocker)
patch_exchange(mocker) patch_exchange(mocker)
mocker.patch.multiple( mocker.patch.multiple(
@ -262,7 +332,7 @@ def test_edge_overrides_stoploss(limit_buy_order, fee, markets, caplog, mocker,
# stoploss shoud be hit # stoploss shoud be hit
assert freqtrade.handle_trade(trade) is True assert freqtrade.handle_trade(trade) is True
assert log_has('executed sell, reason: SellType.STOP_LOSS', caplog.record_tuples) assert log_has('executed sell, reason: SellType.STOP_LOSS', caplog)
assert trade.sell_reason == SellType.STOP_LOSS.value assert trade.sell_reason == SellType.STOP_LOSS.value
@ -582,8 +652,7 @@ def test_create_trade_no_pairs_let(default_conf, ticker, limit_buy_order, fee,
assert freqtrade.create_trade() assert freqtrade.create_trade()
assert not freqtrade.create_trade() assert not freqtrade.create_trade()
assert log_has("No currency pair in whitelist, but checking to sell open trades.", assert log_has("No currency pair in whitelist, but checking to sell open trades.", caplog)
caplog.record_tuples)
def test_create_trade_no_pairs_in_whitelist(default_conf, ticker, limit_buy_order, fee, def test_create_trade_no_pairs_in_whitelist(default_conf, ticker, limit_buy_order, fee,
@ -602,7 +671,7 @@ def test_create_trade_no_pairs_in_whitelist(default_conf, ticker, limit_buy_orde
patch_get_signal(freqtrade) patch_get_signal(freqtrade)
assert not freqtrade.create_trade() assert not freqtrade.create_trade()
assert log_has("Whitelist is empty.", caplog.record_tuples) assert log_has("Whitelist is empty.", caplog)
def test_create_trade_no_signal(default_conf, fee, mocker) -> None: def test_create_trade_no_signal(default_conf, fee, mocker) -> None:
@ -657,8 +726,7 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order,
assert trade.amount == 90.99181073703367 assert trade.amount == 90.99181073703367
assert log_has( assert log_has(
'Buy signal found: about create a new trade with stake_amount: 0.001 ...', 'Buy signal found: about create a new trade with stake_amount: 0.001 ...', caplog
caplog.record_tuples
) )
@ -1025,7 +1093,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog,
}) })
mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_order_hit) mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_order_hit)
assert freqtrade.handle_stoploss_on_exchange(trade) is True assert freqtrade.handle_stoploss_on_exchange(trade) is True
assert log_has('STOP_LOSS_LIMIT is hit for {}.'.format(trade), caplog.record_tuples) assert log_has('STOP_LOSS_LIMIT is hit for {}.'.format(trade), caplog)
assert trade.stoploss_order_id is None assert trade.stoploss_order_id is None
assert trade.is_open is False assert trade.is_open is False
@ -1034,7 +1102,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog,
side_effect=DependencyException() side_effect=DependencyException()
) )
freqtrade.handle_stoploss_on_exchange(trade) freqtrade.handle_stoploss_on_exchange(trade)
assert log_has('Unable to place a stoploss order on exchange: ', caplog.record_tuples) assert log_has('Unable to place a stoploss order on exchange: ', caplog)
# Fifth case: get_order returns InvalidOrder # Fifth case: get_order returns InvalidOrder
# It should try to add stoploss order # It should try to add stoploss order
@ -1196,8 +1264,7 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c
mocker.patch('freqtrade.exchange.Exchange.cancel_order', side_effect=InvalidOrderException()) mocker.patch('freqtrade.exchange.Exchange.cancel_order', side_effect=InvalidOrderException())
mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_order_hanging) mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_order_hanging)
freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging) freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging)
assert log_has_re(r"Could not cancel stoploss order abcd for pair ETH/BTC.*", assert log_has_re(r"Could not cancel stoploss order abcd for pair ETH/BTC.*", caplog)
caplog.record_tuples)
# Still try to create order # Still try to create order
assert stoploss_limit.call_count == 1 assert stoploss_limit.call_count == 1
@ -1208,8 +1275,7 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c
mocker.patch("freqtrade.exchange.Exchange.stoploss_limit", side_effect=DependencyException()) mocker.patch("freqtrade.exchange.Exchange.stoploss_limit", side_effect=DependencyException())
freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging) freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging)
assert cancel_mock.call_count == 1 assert cancel_mock.call_count == 1
assert log_has_re(r"Could create trailing stoploss order for pair ETH/BTC\..*", assert log_has_re(r"Could create trailing stoploss order for pair ETH/BTC\..*", caplog)
caplog.record_tuples)
def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog,
@ -1340,7 +1406,7 @@ def test_process_maybe_execute_buy_exception(mocker, default_conf, caplog) -> No
MagicMock(side_effect=DependencyException) MagicMock(side_effect=DependencyException)
) )
freqtrade.process_maybe_execute_buy() freqtrade.process_maybe_execute_buy()
log_has('Unable to create trade:', caplog.record_tuples) assert log_has('Unable to create trade: ', caplog)
def test_process_maybe_execute_sell(mocker, default_conf, limit_buy_order, caplog) -> None: def test_process_maybe_execute_sell(mocker, default_conf, limit_buy_order, caplog) -> None:
@ -1358,8 +1424,7 @@ def test_process_maybe_execute_sell(mocker, default_conf, limit_buy_order, caplo
assert not freqtrade.process_maybe_execute_sell(trade) assert not freqtrade.process_maybe_execute_sell(trade)
# Test amount not modified by fee-logic # Test amount not modified by fee-logic
assert not log_has( assert not log_has(
'Applying fee to amount for Trade {} from 90.99181073 to 90.81'.format(trade), 'Applying fee to amount for Trade {} from 90.99181073 to 90.81'.format(trade), caplog
caplog.record_tuples
) )
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=90.81) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=90.81)
@ -1382,7 +1447,7 @@ def test_process_maybe_execute_sell_exception(mocker, default_conf,
side_effect=DependencyException() side_effect=DependencyException()
) )
freqtrade.process_maybe_execute_sell(trade) freqtrade.process_maybe_execute_sell(trade)
assert log_has('Unable to sell trade: ', caplog.record_tuples) assert log_has('Unable to sell trade: ', caplog)
def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> None: def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> None:
@ -1401,7 +1466,7 @@ def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> No
trade.open_fee = 0.001 trade.open_fee = 0.001
freqtrade.update_trade_state(trade) freqtrade.update_trade_state(trade)
# Test amount not modified by fee-logic # Test amount not modified by fee-logic
assert not log_has_re(r'Applying fee to .*', caplog.record_tuples) assert not log_has_re(r'Applying fee to .*', caplog)
assert trade.open_order_id is None assert trade.open_order_id is None
assert trade.amount == limit_buy_order['amount'] assert trade.amount == limit_buy_order['amount']
@ -1418,7 +1483,7 @@ def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> No
# Assert we call handle_trade() if trade is feasible for execution # Assert we call handle_trade() if trade is feasible for execution
freqtrade.update_trade_state(trade) freqtrade.update_trade_state(trade)
assert log_has_re('Found open order for.*', caplog.record_tuples) assert log_has_re('Found open order for.*', caplog)
def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_buy_order, mocker): def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_buy_order, mocker):
@ -1457,7 +1522,7 @@ def test_update_trade_state_exception(mocker, default_conf,
side_effect=OperationalException() side_effect=OperationalException()
) )
freqtrade.update_trade_state(trade) freqtrade.update_trade_state(trade)
assert log_has('Could not update trade amount: ', caplog.record_tuples) assert log_has('Could not update trade amount: ', caplog)
def test_update_trade_state_orderexception(mocker, default_conf, caplog) -> None: def test_update_trade_state_orderexception(mocker, default_conf, caplog) -> None:
@ -1473,7 +1538,7 @@ def test_update_trade_state_orderexception(mocker, default_conf, caplog) -> None
grm_mock = mocker.patch("freqtrade.freqtradebot.FreqtradeBot.get_real_amount", MagicMock()) grm_mock = mocker.patch("freqtrade.freqtradebot.FreqtradeBot.get_real_amount", MagicMock())
freqtrade.update_trade_state(trade) freqtrade.update_trade_state(trade)
assert grm_mock.call_count == 0 assert grm_mock.call_count == 0
assert log_has(f'Unable to fetch order {trade.open_order_id}: ', caplog.record_tuples) assert log_has(f'Unable to fetch order {trade.open_order_id}: ', caplog)
def test_update_trade_state_sell(default_conf, trades_for_order, limit_sell_order, mocker): def test_update_trade_state_sell(default_conf, trades_for_order, limit_sell_order, mocker):
@ -1632,7 +1697,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order,
# if ROI is reached we must sell # if ROI is reached we must sell
patch_get_signal(freqtrade, value=(False, True)) patch_get_signal(freqtrade, value=(False, True))
assert freqtrade.handle_trade(trade) assert freqtrade.handle_trade(trade)
assert log_has('Required profit reached. Selling..', caplog.record_tuples) assert log_has('Required profit reached. Selling..', caplog)
def test_handle_trade_experimental( def test_handle_trade_experimental(
@ -1662,7 +1727,7 @@ def test_handle_trade_experimental(
patch_get_signal(freqtrade, value=(False, True)) patch_get_signal(freqtrade, value=(False, True))
assert freqtrade.handle_trade(trade) assert freqtrade.handle_trade(trade)
assert log_has('Sell signal received. Selling..', caplog.record_tuples) assert log_has('Sell signal received. Selling..', caplog)
def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order, def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order,
@ -1768,7 +1833,7 @@ def test_check_handle_cancelled_buy(default_conf, ticker, limit_buy_order_old,
trades = Trade.query.filter(Trade.open_order_id.is_(trade_buy.open_order_id)).all() trades = Trade.query.filter(Trade.open_order_id.is_(trade_buy.open_order_id)).all()
nb_trades = len(trades) nb_trades = len(trades)
assert nb_trades == 0 assert nb_trades == 0
assert log_has_re("Buy order canceled on Exchange for Trade.*", caplog.record_tuples) assert log_has_re("Buy order canceled on Exchange for Trade.*", caplog)
def test_check_handle_timedout_buy_exception(default_conf, ticker, limit_buy_order_old, def test_check_handle_timedout_buy_exception(default_conf, ticker, limit_buy_order_old,
@ -1881,7 +1946,7 @@ def test_check_handle_cancelled_sell(default_conf, ticker, limit_sell_order_old,
assert cancel_order_mock.call_count == 0 assert cancel_order_mock.call_count == 0
assert rpc_mock.call_count == 1 assert rpc_mock.call_count == 1
assert trade_sell.is_open is True assert trade_sell.is_open is True
assert log_has_re("Sell order canceled on exchange for Trade.*", caplog.record_tuples) assert log_has_re("Sell order canceled on exchange for Trade.*", caplog)
def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old_partial, def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old_partial,
@ -1959,7 +2024,7 @@ def test_check_handle_timedout_exception(default_conf, ticker, mocker, caplog) -
freqtrade.check_handle_timedout() freqtrade.check_handle_timedout()
assert log_has_re(r'Cannot query order for Trade\(id=1, pair=ETH/BTC, amount=90.99181073, ' assert log_has_re(r'Cannot query order for Trade\(id=1, pair=ETH/BTC, amount=90.99181073, '
r'open_rate=0.00001099, open_since=10 hours ago\) due to Traceback \(most ' r'open_rate=0.00001099, open_since=10 hours ago\) due to Traceback \(most '
r'recent call last\):\n.*', caplog.record_tuples) r'recent call last\):\n.*', caplog)
def test_handle_timedout_limit_buy(mocker, default_conf) -> None: def test_handle_timedout_limit_buy(mocker, default_conf) -> None:
@ -2183,7 +2248,7 @@ def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee,
freqtrade.execute_sell(trade=trade, limit=1234, freqtrade.execute_sell(trade=trade, limit=1234,
sell_reason=SellType.STOP_LOSS) sell_reason=SellType.STOP_LOSS)
assert sellmock.call_count == 1 assert sellmock.call_count == 1
assert log_has('Could not cancel stoploss order abcd', caplog.record_tuples) assert log_has('Could not cancel stoploss order abcd', caplog)
def test_execute_sell_with_stoploss_on_exchange(default_conf, def test_execute_sell_with_stoploss_on_exchange(default_conf,
@ -2609,7 +2674,7 @@ def test_trailing_stop_loss(default_conf, limit_buy_order, fee, markets, caplog,
assert freqtrade.handle_trade(trade) is True assert freqtrade.handle_trade(trade) is True
assert log_has( assert log_has(
f'HIT STOP: current price at 0.000012, stop loss is 0.000015, ' f'HIT STOP: current price at 0.000012, stop loss is 0.000015, '
f'initial stop loss was at 0.000010, trade opened at 0.000011', caplog.record_tuples) f'initial stop loss was at 0.000010, trade opened at 0.000011', caplog)
assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value
@ -2651,9 +2716,8 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, fee, markets
})) }))
# stop-loss not reached, adjusted stoploss # stop-loss not reached, adjusted stoploss
assert freqtrade.handle_trade(trade) is False assert freqtrade.handle_trade(trade) is False
assert log_has(f'using positive stop loss: 0.01 offset: 0 profit: 0.2666%', assert log_has(f'using positive stop loss: 0.01 offset: 0 profit: 0.2666%', caplog)
caplog.record_tuples) assert log_has(f'adjusted stop loss', caplog)
assert log_has(f'adjusted stop loss', caplog.record_tuples)
assert trade.stop_loss == 0.0000138501 assert trade.stop_loss == 0.0000138501
mocker.patch('freqtrade.exchange.Exchange.get_ticker', mocker.patch('freqtrade.exchange.Exchange.get_ticker',
@ -2667,7 +2731,7 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, fee, markets
assert log_has( assert log_has(
f'HIT STOP: current price at {buy_price + 0.000002:.6f}, ' f'HIT STOP: current price at {buy_price + 0.000002:.6f}, '
f'stop loss is {trade.stop_loss:.6f}, ' f'stop loss is {trade.stop_loss:.6f}, '
f'initial stop loss was at 0.000010, trade opened at 0.000011', caplog.record_tuples) f'initial stop loss was at 0.000010, trade opened at 0.000011', caplog)
def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee, def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee,
@ -2710,9 +2774,8 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee,
})) }))
# stop-loss not reached, adjusted stoploss # stop-loss not reached, adjusted stoploss
assert freqtrade.handle_trade(trade) is False assert freqtrade.handle_trade(trade) is False
assert log_has(f'using positive stop loss: 0.01 offset: 0.011 profit: 0.2666%', assert log_has(f'using positive stop loss: 0.01 offset: 0.011 profit: 0.2666%', caplog)
caplog.record_tuples) assert log_has(f'adjusted stop loss', caplog)
assert log_has(f'adjusted stop loss', caplog.record_tuples)
assert trade.stop_loss == 0.0000138501 assert trade.stop_loss == 0.0000138501
mocker.patch('freqtrade.exchange.Exchange.get_ticker', mocker.patch('freqtrade.exchange.Exchange.get_ticker',
@ -2726,7 +2789,7 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee,
assert log_has( assert log_has(
f'HIT STOP: current price at {buy_price + 0.000002:.6f}, ' f'HIT STOP: current price at {buy_price + 0.000002:.6f}, '
f'stop loss is {trade.stop_loss:.6f}, ' f'stop loss is {trade.stop_loss:.6f}, '
f'initial stop loss was at 0.000010, trade opened at 0.000011', caplog.record_tuples) f'initial stop loss was at 0.000010, trade opened at 0.000011', caplog)
assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value
@ -2777,7 +2840,7 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, fee,
# stop-loss should not be adjusted as offset is not reached yet # stop-loss should not be adjusted as offset is not reached yet
assert freqtrade.handle_trade(trade) is False assert freqtrade.handle_trade(trade) is False
assert not log_has(f'adjusted stop loss', caplog.record_tuples) assert not log_has(f'adjusted stop loss', caplog)
assert trade.stop_loss == 0.0000098910 assert trade.stop_loss == 0.0000098910
# price rises above the offset (rises 12% when the offset is 5.5%) # price rises above the offset (rises 12% when the offset is 5.5%)
@ -2789,9 +2852,8 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, fee,
})) }))
assert freqtrade.handle_trade(trade) is False assert freqtrade.handle_trade(trade) is False
assert log_has(f'using positive stop loss: 0.05 offset: 0.055 profit: 0.1218%', assert log_has(f'using positive stop loss: 0.05 offset: 0.055 profit: 0.1218%', caplog)
caplog.record_tuples) assert log_has(f'adjusted stop loss', caplog)
assert log_has(f'adjusted stop loss', caplog.record_tuples)
assert trade.stop_loss == 0.0000117705 assert trade.stop_loss == 0.0000117705
@ -2850,7 +2912,7 @@ def test_get_real_amount_quote(default_conf, trades_for_order, buy_order_fee, ca
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount - (amount * 0.001) assert freqtrade.get_real_amount(trade, buy_order_fee) == amount - (amount * 0.001)
assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992) from Trades', 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992) from Trades',
caplog.record_tuples) caplog)
def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker): def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker):
@ -2873,7 +2935,7 @@ def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker):
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount assert freqtrade.get_real_amount(trade, buy_order_fee) == amount
assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
'open_rate=0.24544100, open_since=closed) failed: myTrade-Dict empty found', 'open_rate=0.24544100, open_since=closed) failed: myTrade-Dict empty found',
caplog.record_tuples) caplog)
def test_get_real_amount_stake(default_conf, trades_for_order, buy_order_fee, mocker): def test_get_real_amount_stake(default_conf, trades_for_order, buy_order_fee, mocker):
@ -2962,7 +3024,7 @@ def test_get_real_amount_multi(default_conf, trades_for_order2, buy_order_fee, c
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount - (amount * 0.001) assert freqtrade.get_real_amount(trade, buy_order_fee) == amount - (amount * 0.001)
assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992) from Trades', 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992) from Trades',
caplog.record_tuples) caplog)
def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee, caplog, mocker): def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee, caplog, mocker):
@ -2988,7 +3050,7 @@ def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee
assert freqtrade.get_real_amount(trade, limit_buy_order) == amount - 0.004 assert freqtrade.get_real_amount(trade, limit_buy_order) == amount - 0.004
assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.996) from Order', 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.996) from Order',
caplog.record_tuples) caplog)
def test_get_real_amount_invalid_order(default_conf, trades_for_order, buy_order_fee, mocker): def test_get_real_amount_invalid_order(default_conf, trades_for_order, buy_order_fee, mocker):

View File

@ -60,8 +60,8 @@ def test_main_fatal_exception(mocker, default_conf, caplog) -> None:
# Test Main + the KeyboardInterrupt exception # Test Main + the KeyboardInterrupt exception
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
main(args) main(args)
assert log_has('Using config: config.json.example ...', caplog.record_tuples) assert log_has('Using config: config.json.example ...', caplog)
assert log_has('Fatal exception!', caplog.record_tuples) assert log_has('Fatal exception!', caplog)
def test_main_keyboard_interrupt(mocker, default_conf, caplog) -> None: def test_main_keyboard_interrupt(mocker, default_conf, caplog) -> None:
@ -77,8 +77,8 @@ def test_main_keyboard_interrupt(mocker, default_conf, caplog) -> None:
# Test Main + the KeyboardInterrupt exception # Test Main + the KeyboardInterrupt exception
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
main(args) main(args)
assert log_has('Using config: config.json.example ...', caplog.record_tuples) assert log_has('Using config: config.json.example ...', caplog)
assert log_has('SIGINT received, aborting ...', caplog.record_tuples) assert log_has('SIGINT received, aborting ...', caplog)
def test_main_operational_exception(mocker, default_conf, caplog) -> None: def test_main_operational_exception(mocker, default_conf, caplog) -> None:
@ -97,8 +97,8 @@ def test_main_operational_exception(mocker, default_conf, caplog) -> None:
# Test Main + the KeyboardInterrupt exception # Test Main + the KeyboardInterrupt exception
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
main(args) main(args)
assert log_has('Using config: config.json.example ...', caplog.record_tuples) assert log_has('Using config: config.json.example ...', caplog)
assert log_has('Oh snap!', caplog.record_tuples) assert log_has('Oh snap!', caplog)
def test_main_reload_conf(mocker, default_conf, caplog) -> None: def test_main_reload_conf(mocker, default_conf, caplog) -> None:
@ -121,7 +121,7 @@ def test_main_reload_conf(mocker, default_conf, caplog) -> None:
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
main(['-c', 'config.json.example']) main(['-c', 'config.json.example'])
assert log_has('Using config: config.json.example ...', caplog.record_tuples) assert log_has('Using config: config.json.example ...', caplog)
assert worker_mock.call_count == 4 assert worker_mock.call_count == 4
assert reconfigure_mock.call_count == 1 assert reconfigure_mock.call_count == 1
assert isinstance(worker.freqtrade, FreqtradeBot) assert isinstance(worker.freqtrade, FreqtradeBot)

View File

@ -151,7 +151,7 @@ def test_update_with_bittrex(limit_buy_order, limit_sell_order, fee, caplog):
assert trade.close_date is None assert trade.close_date is None
assert log_has("LIMIT_BUY has been fulfilled for Trade(id=2, " assert log_has("LIMIT_BUY has been fulfilled for Trade(id=2, "
"pair=ETH/BTC, amount=90.99181073, open_rate=0.00001099, open_since=closed).", "pair=ETH/BTC, amount=90.99181073, open_rate=0.00001099, open_since=closed).",
caplog.record_tuples) caplog)
caplog.clear() caplog.clear()
trade.open_order_id = 'something' trade.open_order_id = 'something'
@ -162,7 +162,7 @@ def test_update_with_bittrex(limit_buy_order, limit_sell_order, fee, caplog):
assert trade.close_date is not None assert trade.close_date is not None
assert log_has("LIMIT_SELL has been fulfilled for Trade(id=2, " assert log_has("LIMIT_SELL has been fulfilled for Trade(id=2, "
"pair=ETH/BTC, amount=90.99181073, open_rate=0.00001099, open_since=closed).", "pair=ETH/BTC, amount=90.99181073, open_rate=0.00001099, open_since=closed).",
caplog.record_tuples) caplog)
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
@ -184,7 +184,7 @@ def test_update_market_order(market_buy_order, market_sell_order, fee, caplog):
assert trade.close_date is None assert trade.close_date is None
assert log_has("MARKET_BUY has been fulfilled for Trade(id=1, " assert log_has("MARKET_BUY has been fulfilled for Trade(id=1, "
"pair=ETH/BTC, amount=91.99181073, open_rate=0.00004099, open_since=closed).", "pair=ETH/BTC, amount=91.99181073, open_rate=0.00004099, open_since=closed).",
caplog.record_tuples) caplog)
caplog.clear() caplog.clear()
trade.open_order_id = 'something' trade.open_order_id = 'something'
@ -195,7 +195,7 @@ def test_update_market_order(market_buy_order, market_sell_order, fee, caplog):
assert trade.close_date is not None assert trade.close_date is not None
assert log_has("MARKET_SELL has been fulfilled for Trade(id=1, " assert log_has("MARKET_SELL has been fulfilled for Trade(id=1, "
"pair=ETH/BTC, amount=91.99181073, open_rate=0.00004099, open_since=closed).", "pair=ETH/BTC, amount=91.99181073, open_rate=0.00004099, open_since=closed).",
caplog.record_tuples) caplog)
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
@ -558,10 +558,9 @@ def test_migrate_new(mocker, default_conf, fee, caplog):
assert trade.ticker_interval is None assert trade.ticker_interval is None
assert trade.stoploss_order_id is None assert trade.stoploss_order_id is None
assert trade.stoploss_last_update is None assert trade.stoploss_last_update is None
assert log_has("trying trades_bak1", caplog.record_tuples) assert log_has("trying trades_bak1", caplog)
assert log_has("trying trades_bak2", caplog.record_tuples) assert log_has("trying trades_bak2", caplog)
assert log_has("Running database migration - backup available as trades_bak2", assert log_has("Running database migration - backup available as trades_bak2", caplog)
caplog.record_tuples)
def test_migrate_mid_state(mocker, default_conf, fee, caplog): def test_migrate_mid_state(mocker, default_conf, fee, caplog):
@ -621,9 +620,8 @@ def test_migrate_mid_state(mocker, default_conf, fee, caplog):
assert trade.max_rate == 0.0 assert trade.max_rate == 0.0
assert trade.stop_loss == 0.0 assert trade.stop_loss == 0.0
assert trade.initial_stop_loss == 0.0 assert trade.initial_stop_loss == 0.0
assert log_has("trying trades_bak0", caplog.record_tuples) assert log_has("trying trades_bak0", caplog)
assert log_has("Running database migration - backup available as trades_bak0", assert log_has("Running database migration - backup available as trades_bak0", caplog)
caplog.record_tuples)
def test_adjust_stop_loss(fee): def test_adjust_stop_loss(fee):

View File

@ -87,7 +87,7 @@ def test_add_indicators(default_conf, caplog):
# No indicator found # No indicator found
fig3 = add_indicators(fig=deepcopy(fig), row=3, indicators=['no_indicator'], data=data) fig3 = add_indicators(fig=deepcopy(fig), row=3, indicators=['no_indicator'], data=data)
assert fig == fig3 assert fig == fig3
assert log_has_re(r'Indicator "no_indicator" ignored\..*', caplog.record_tuples) assert log_has_re(r'Indicator "no_indicator" ignored\..*', caplog)
def test_plot_trades(caplog): def test_plot_trades(caplog):
@ -95,7 +95,7 @@ def test_plot_trades(caplog):
# nothing happens when no trades are available # nothing happens when no trades are available
fig = plot_trades(fig1, None) fig = plot_trades(fig1, None)
assert fig == fig1 assert fig == fig1
assert log_has("No trades found.", caplog.record_tuples) assert log_has("No trades found.", caplog)
pair = "ADA/BTC" pair = "ADA/BTC"
filename = history.make_testdata_path(None) / "backtest-result_test.json" filename = history.make_testdata_path(None) / "backtest-result_test.json"
trades = load_backtest_data(filename) trades = load_backtest_data(filename)
@ -150,8 +150,8 @@ def test_generate_candlestick_graph_no_signals_no_trades(default_conf, mocker, c
assert row_mock.call_count == 2 assert row_mock.call_count == 2
assert trades_mock.call_count == 1 assert trades_mock.call_count == 1
assert log_has("No buy-signals found.", caplog.record_tuples) assert log_has("No buy-signals found.", caplog)
assert log_has("No sell-signals found.", caplog.record_tuples) assert log_has("No sell-signals found.", caplog)
def test_generate_candlestick_graph_no_trades(default_conf, mocker): def test_generate_candlestick_graph_no_trades(default_conf, mocker):
@ -216,7 +216,7 @@ def test_generate_plot_file(mocker, caplog):
assert (plot_mock.call_args_list[0][1]['filename'] assert (plot_mock.call_args_list[0][1]['filename']
== "user_data/plots/freqtrade-plot-UNITTEST_BTC-5m.html") == "user_data/plots/freqtrade-plot-UNITTEST_BTC-5m.html")
assert log_has("Stored plot as user_data/plots/freqtrade-plot-UNITTEST_BTC-5m.html", assert log_has("Stored plot as user_data/plots/freqtrade-plot-UNITTEST_BTC-5m.html",
caplog.record_tuples) caplog)
def test_add_profit(): def test_add_profit():

View File

@ -1,9 +1,9 @@
# requirements without requirements installable via conda # requirements without requirements installable via conda
# mainly used for Raspberry pi installs # mainly used for Raspberry pi installs
ccxt==1.18.1021 ccxt==1.18.1043
SQLAlchemy==1.3.6 SQLAlchemy==1.3.6
python-telegram-bot==11.1.0 python-telegram-bot==11.1.0
arrow==0.14.4 arrow==0.14.5
cachetools==3.1.1 cachetools==3.1.1
requests==2.22.0 requests==2.22.0
urllib3==1.25.3 urllib3==1.25.3
@ -23,7 +23,7 @@ filelock==3.0.12
py_find_1st==1.1.4 py_find_1st==1.1.4
#Load ticker files 30% faster #Load ticker files 30% faster
python-rapidjson==0.7.2 python-rapidjson==0.8.0
# Notify systemd # Notify systemd
sdnotify==0.3.2 sdnotify==0.3.2

View File

@ -3,4 +3,4 @@
numpy==1.17.0 numpy==1.17.0
pandas==0.25.0 pandas==0.25.0
scipy==1.3.0 scipy==1.3.1

View File

@ -1,11 +1,10 @@
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement # pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
from functools import reduce from functools import reduce
from math import exp
from typing import Any, Callable, Dict, List from typing import Any, Callable, Dict, List
from datetime import datetime from datetime import datetime
import numpy as np# noqa F401 import numpy as np
import talib.abstract as ta import talib.abstract as ta
from pandas import DataFrame from pandas import DataFrame
from skopt.space import Categorical, Dimension, Integer, Real from skopt.space import Categorical, Dimension, Integer, Real
@ -16,7 +15,7 @@ from freqtrade.optimize.hyperopt_interface import IHyperOpt
class SampleHyperOpts(IHyperOpt): class SampleHyperOpts(IHyperOpt):
""" """
This is a sample hyperopt to inspire you. This is a sample Hyperopt to inspire you.
Feel free to customize it. Feel free to customize it.
More information in https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md More information in https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md
@ -37,32 +36,44 @@ class SampleHyperOpts(IHyperOpt):
""" """
@staticmethod @staticmethod
def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame: def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Add several indicators needed for buy and sell strategies defined below.
"""
# ADX
dataframe['adx'] = ta.ADX(dataframe) dataframe['adx'] = ta.ADX(dataframe)
# MACD
macd = ta.MACD(dataframe) macd = ta.MACD(dataframe)
dataframe['macd'] = macd['macd'] dataframe['macd'] = macd['macd']
dataframe['macdsignal'] = macd['macdsignal'] dataframe['macdsignal'] = macd['macdsignal']
# MFI
dataframe['mfi'] = ta.MFI(dataframe) dataframe['mfi'] = ta.MFI(dataframe)
# RSI
dataframe['rsi'] = ta.RSI(dataframe) dataframe['rsi'] = ta.RSI(dataframe)
# Stochastic Fast
stoch_fast = ta.STOCHF(dataframe) stoch_fast = ta.STOCHF(dataframe)
dataframe['fastd'] = stoch_fast['fastd'] dataframe['fastd'] = stoch_fast['fastd']
# Minus-DI
dataframe['minus_di'] = ta.MINUS_DI(dataframe) dataframe['minus_di'] = ta.MINUS_DI(dataframe)
# Bollinger bands # Bollinger bands
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
dataframe['bb_lowerband'] = bollinger['lower'] dataframe['bb_lowerband'] = bollinger['lower']
dataframe['bb_upperband'] = bollinger['upper'] dataframe['bb_upperband'] = bollinger['upper']
# SAR
dataframe['sar'] = ta.SAR(dataframe) dataframe['sar'] = ta.SAR(dataframe)
return dataframe return dataframe
@staticmethod @staticmethod
def buy_strategy_generator(params: Dict[str, Any]) -> Callable: def buy_strategy_generator(params: Dict[str, Any]) -> Callable:
""" """
Define the buy strategy parameters to be used by hyperopt Define the buy strategy parameters to be used by Hyperopt.
""" """
def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
""" """
Buy strategy Hyperopt will build and use Buy strategy Hyperopt will build and use.
""" """
conditions = [] conditions = []
# GUARDS AND TRENDS # GUARDS AND TRENDS
if 'mfi-enabled' in params and params['mfi-enabled']: if 'mfi-enabled' in params and params['mfi-enabled']:
conditions.append(dataframe['mfi'] < params['mfi-value']) conditions.append(dataframe['mfi'] < params['mfi-value'])
@ -98,7 +109,7 @@ class SampleHyperOpts(IHyperOpt):
@staticmethod @staticmethod
def indicator_space() -> List[Dimension]: def indicator_space() -> List[Dimension]:
""" """
Define your Hyperopt space for searching strategy parameters Define your Hyperopt space for searching buy strategy parameters.
""" """
return [ return [
Integer(10, 25, name='mfi-value'), Integer(10, 25, name='mfi-value'),
@ -115,14 +126,14 @@ class SampleHyperOpts(IHyperOpt):
@staticmethod @staticmethod
def sell_strategy_generator(params: Dict[str, Any]) -> Callable: def sell_strategy_generator(params: Dict[str, Any]) -> Callable:
""" """
Define the sell strategy parameters to be used by hyperopt Define the sell strategy parameters to be used by Hyperopt.
""" """
def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
""" """
Sell strategy Hyperopt will build and use Sell strategy Hyperopt will build and use.
""" """
# print(params)
conditions = [] conditions = []
# GUARDS AND TRENDS # GUARDS AND TRENDS
if 'sell-mfi-enabled' in params and params['sell-mfi-enabled']: if 'sell-mfi-enabled' in params and params['sell-mfi-enabled']:
conditions.append(dataframe['mfi'] > params['sell-mfi-value']) conditions.append(dataframe['mfi'] > params['sell-mfi-value'])
@ -158,7 +169,7 @@ class SampleHyperOpts(IHyperOpt):
@staticmethod @staticmethod
def sell_indicator_space() -> List[Dimension]: def sell_indicator_space() -> List[Dimension]:
""" """
Define your Hyperopt space for searching sell strategy parameters Define your Hyperopt space for searching sell strategy parameters.
""" """
return [ return [
Integer(75, 100, name='sell-mfi-value'), Integer(75, 100, name='sell-mfi-value'),
@ -176,9 +187,9 @@ class SampleHyperOpts(IHyperOpt):
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
""" """
Based on TA indicators. Should be a copy of from strategy Based on TA indicators. Should be a copy of same method from strategy.
must align to populate_indicators in this file Must align to populate_indicators in this file.
Only used when --spaces does not include buy Only used when --spaces does not include buy space.
""" """
dataframe.loc[ dataframe.loc[
( (
@ -193,9 +204,9 @@ class SampleHyperOpts(IHyperOpt):
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
""" """
Based on TA indicators. Should be a copy of from strategy Based on TA indicators. Should be a copy of same method from strategy.
must align to populate_indicators in this file Must align to populate_indicators in this file.
Only used when --spaces does not include sell Only used when --spaces does not include sell space.
""" """
dataframe.loc[ dataframe.loc[
( (
@ -205,4 +216,5 @@ class SampleHyperOpts(IHyperOpt):
(dataframe['fastd'] > 54) (dataframe['fastd'] > 54)
), ),
'sell'] = 1 'sell'] = 1
return dataframe return dataframe