From 2bed41da5dcc899b45c0d984b81b44cfc5094340 Mon Sep 17 00:00:00 2001 From: rextea Date: Fri, 26 Mar 2021 18:40:50 +0300 Subject: [PATCH 001/144] Add days breakdown table to backtesting --- docs/backtesting.md | 1 + freqtrade/commands/arguments.py | 2 +- freqtrade/commands/cli_options.py | 6 ++ freqtrade/configuration/configuration.py | 3 + freqtrade/optimize/optimize_reports.py | 71 +++++++++++++++++++++--- 5 files changed, 74 insertions(+), 9 deletions(-) diff --git a/docs/backtesting.md b/docs/backtesting.md index d02c59f05..91faa07bb 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -67,6 +67,7 @@ optional arguments: Requires `--export` to be set as well. Example: `--export-filename=user_data/backtest_results/backtest _today.json` + --show-days Print a days breakdown table of the backtest results Common arguments: -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 9468a7f7d..b71819ef2 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -21,7 +21,7 @@ ARGS_COMMON_OPTIMIZE = ["timeframe", "timerange", "dataformat_ohlcv", ARGS_BACKTEST = ARGS_COMMON_OPTIMIZE + ["position_stacking", "use_max_market_positions", "enable_protections", "dry_run_wallet", - "strategy_list", "export", "exportfilename"] + "strategy_list", "export", "exportfilename", "show_days"] ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + ["hyperopt", "hyperopt_path", "position_stacking", "use_max_market_positions", diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index 15c13cec9..dc193ee4f 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -183,6 +183,12 @@ AVAILABLE_CLI_OPTIONS = { type=float, metavar='FLOAT', ), + "show_days": Arg( + '--show-days', + help='Print days breakdown for backtest results', + action='store_true', + default=False, + ), # Edge "stoploss_range": Arg( '--stoplosses', diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index a40a4fd83..1eb6351d0 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -260,6 +260,9 @@ class Configuration: self._args_to_config(config, argname='export', logstring='Parameter --export detected: {} ...') + self._args_to_config(config, argname='show_days', + logstring='Parameter --show-days detected ...') + # Edge section: if 'stoploss_range' in self.args and self.args["stoploss_range"]: txt_range = eval(self.args["stoploss_range"]) diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index 099976aa9..d15988669 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -13,7 +13,6 @@ from freqtrade.data.btanalysis import (calculate_csum, calculate_market_change, calculate_max_drawdown) from freqtrade.misc import decimals_per_coin, file_dump_json, round_coin_value - logger = logging.getLogger(__name__) @@ -32,7 +31,7 @@ def store_backtest_stats(recordfilename: Path, stats: Dict[str, DataFrame]) -> N filename = Path.joinpath( recordfilename.parent, f'{recordfilename.stem}-{datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}' - ).with_suffix(recordfilename.suffix) + ).with_suffix(recordfilename.suffix) file_dump_json(filename, stats) latest_filename = Path.joinpath(filename.parent, LAST_BT_RESULT_FN) @@ -75,8 +74,8 @@ def _generate_result_line(result: DataFrame, starting_balance: int, first_column 'profit_total': profit_total, 'profit_total_pct': round(profit_total * 100.0, 2), 'duration_avg': str(timedelta( - minutes=round(result['trade_duration'].mean())) - ) if not result.empty else '0:00', + minutes=round(result['trade_duration'].mean())) + ) if not result.empty else '0:00', # 'duration_max': str(timedelta( # minutes=round(result['trade_duration'].max())) # ) if not result.empty else '0:00', @@ -161,12 +160,11 @@ def generate_strategy_metrics(all_results: Dict) -> List[Dict]: for strategy, results in all_results.items(): tabular_data.append(_generate_result_line( results['results'], results['config']['dry_run_wallet'], strategy) - ) + ) return tabular_data def generate_edge_table(results: dict) -> str: - floatfmt = ('s', '.10g', '.2f', '.2f', '.2f', '.2f', 'd', 'd', 'd') tabular_data = [] headers = ['Pair', 'Stoploss', 'Win Rate', 'Risk Reward Ratio', @@ -191,6 +189,29 @@ def generate_edge_table(results: dict) -> str: floatfmt=floatfmt, tablefmt="orgtbl", stralign="right") # type: ignore +def generate_days_breakdown_stats(results: DataFrame, starting_balance: int) -> Dict[str, Any]: + days = results.resample('1d', on='close_date') + days_stats = [] + for name, day in days: + profit_abs = day['profit_abs'].sum().round(10) + profit_total = day['profit_abs'].sum() / starting_balance + wins = sum(day['profit_abs'] > 0) + draws = sum(day['profit_abs'] == 0) + loses = sum(day['profit_abs'] < 0) + profit_percentage = round(profit_total * 100.0, 2) + days_stats.append( + { + 'date': name.strftime('%d/%m/%Y'), + 'profit_percentage': profit_percentage, + 'profit_abs': profit_abs, + 'wins': wins, + 'draws': draws, + 'loses': loses + } + ) + return days_stats + + def generate_daily_stats(results: DataFrame) -> Dict[str, Any]: if len(results) == 0: return { @@ -266,6 +287,8 @@ def generate_backtest_stats(btdata: Dict[str, DataFrame], starting_balance=starting_balance, results=results.loc[results['is_open']], skip_nan=True) + days_breakdown_stats = generate_days_breakdown_stats(results=results, + starting_balance=starting_balance) daily_stats = generate_daily_stats(results) best_pair = max([pair for pair in pair_results if pair['key'] != 'TOTAL'], key=lambda x: x['profit_sum']) if len(pair_results) > 1 else None @@ -283,6 +306,7 @@ def generate_backtest_stats(btdata: Dict[str, DataFrame], 'results_per_pair': pair_results, 'sell_reason_summary': sell_reason_stats, 'left_open_trades': left_open_results, + 'days_breakdown_stats': days_breakdown_stats, 'total_trades': len(results), 'total_volume': float(results['stake_amount'].sum()), 'avg_stake_amount': results['stake_amount'].mean() if len(results) > 0 else 0, @@ -425,6 +449,28 @@ def text_table_sell_reason(sell_reason_stats: List[Dict[str, Any]], stake_curren return tabulate(output, headers=headers, tablefmt="orgtbl", stralign="right") +def text_table_days_breakdown(days_breakdown_stats: List[Dict[str, Any]], stake_currency: str) -> str: + """ + Generate small table with Backtest results by days + :param days_breakdown_stats: Days breakdown metrics + :param stake_currency: Stakecurrency used + :return: pretty printed table with tabulate as string + """ + headers = [ + 'Day', + 'Profit %', + f'Tot Profit {stake_currency}', + 'Wins', + 'Draws', + 'Losses', + ] + output = [[ + d['date'], d['profit_percentage'], round_coin_value(d['profit_abs'], stake_currency, False), + d['wins'], d['draws'], d['loses'], + ] for d in days_breakdown_stats] + return tabulate(output, headers=headers, tablefmt="orgtbl", stralign="right") + + def text_table_strategy(strategy_results, stake_currency: str) -> str: """ Generate summary table per strategy @@ -463,6 +509,8 @@ def text_table_add_metrics(strat_results: Dict) -> str: strat_results['stake_currency'])), ('Total profit %', f"{round(strat_results['profit_total'] * 100, 2)}%"), ('Trades per day', strat_results['trades_per_day']), + ('Avg. daily profit %', + f"{round(strat_results['profit_total'] / strat_results['backtest_days'] * 100, 2)}%"), ('Avg. stake amount', round_coin_value(strat_results['avg_stake_amount'], strat_results['stake_currency'])), ('Total trade volume', round_coin_value(strat_results['total_volume'], @@ -482,7 +530,7 @@ def text_table_add_metrics(strat_results: Dict) -> str: ('Worst day', round_coin_value(strat_results['backtest_worst_day_abs'], strat_results['stake_currency'])), ('Days win/draw/lose', f"{strat_results['winning_days']} / " - f"{strat_results['draw_days']} / {strat_results['losing_days']}"), + f"{strat_results['draw_days']} / {strat_results['losing_days']}"), ('Avg. Duration Winners', f"{strat_results['winner_holding_avg']}"), ('Avg. Duration Loser', f"{strat_results['loser_holding_avg']}"), ('', ''), # Empty line to improve readability @@ -510,7 +558,7 @@ def text_table_add_metrics(strat_results: Dict) -> str: strat_results['stake_currency']) stake_amount = round_coin_value( strat_results['stake_amount'], strat_results['stake_currency'] - ) if strat_results['stake_amount'] != UNLIMITED_STAKE_AMOUNT else 'unlimited' + ) if strat_results['stake_amount'] != UNLIMITED_STAKE_AMOUNT else 'unlimited' message = ("No trades made. " f"Your starting balance was {start_balance}, " @@ -542,6 +590,13 @@ def show_backtest_results(config: Dict, backtest_stats: Dict): print(' LEFT OPEN TRADES REPORT '.center(len(table.splitlines()[0]), '=')) print(table) + if config.get('show_days', False): + table = text_table_days_breakdown(days_breakdown_stats=results['days_breakdown_stats'], + stake_currency=stake_currency) + if isinstance(table, str) and len(table) > 0: + print(' DAYS BREAKDOWN '.center(len(table.splitlines()[0]), '=')) + print(table) + table = text_table_add_metrics(results) if isinstance(table, str) and len(table) > 0: print(' SUMMARY METRICS '.center(len(table.splitlines()[0]), '=')) From 76a02ff70aa4016ed6755fa1f00cbb6246edab97 Mon Sep 17 00:00:00 2001 From: rextea Date: Fri, 26 Mar 2021 18:49:17 +0300 Subject: [PATCH 002/144] fix indentations --- freqtrade/optimize/optimize_reports.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index d15988669..286fa5c46 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -13,6 +13,7 @@ from freqtrade.data.btanalysis import (calculate_csum, calculate_market_change, calculate_max_drawdown) from freqtrade.misc import decimals_per_coin, file_dump_json, round_coin_value + logger = logging.getLogger(__name__) @@ -31,7 +32,7 @@ def store_backtest_stats(recordfilename: Path, stats: Dict[str, DataFrame]) -> N filename = Path.joinpath( recordfilename.parent, f'{recordfilename.stem}-{datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}' - ).with_suffix(recordfilename.suffix) + ).with_suffix(recordfilename.suffix) file_dump_json(filename, stats) latest_filename = Path.joinpath(filename.parent, LAST_BT_RESULT_FN) @@ -74,8 +75,8 @@ def _generate_result_line(result: DataFrame, starting_balance: int, first_column 'profit_total': profit_total, 'profit_total_pct': round(profit_total * 100.0, 2), 'duration_avg': str(timedelta( - minutes=round(result['trade_duration'].mean())) - ) if not result.empty else '0:00', + minutes=round(result['trade_duration'].mean())) + ) if not result.empty else '0:00', # 'duration_max': str(timedelta( # minutes=round(result['trade_duration'].max())) # ) if not result.empty else '0:00', @@ -530,7 +531,7 @@ def text_table_add_metrics(strat_results: Dict) -> str: ('Worst day', round_coin_value(strat_results['backtest_worst_day_abs'], strat_results['stake_currency'])), ('Days win/draw/lose', f"{strat_results['winning_days']} / " - f"{strat_results['draw_days']} / {strat_results['losing_days']}"), + f"{strat_results['draw_days']} / {strat_results['losing_days']}"), ('Avg. Duration Winners', f"{strat_results['winner_holding_avg']}"), ('Avg. Duration Loser', f"{strat_results['loser_holding_avg']}"), ('', ''), # Empty line to improve readability @@ -558,7 +559,7 @@ def text_table_add_metrics(strat_results: Dict) -> str: strat_results['stake_currency']) stake_amount = round_coin_value( strat_results['stake_amount'], strat_results['stake_currency'] - ) if strat_results['stake_amount'] != UNLIMITED_STAKE_AMOUNT else 'unlimited' + ) if strat_results['stake_amount'] != UNLIMITED_STAKE_AMOUNT else 'unlimited' message = ("No trades made. " f"Your starting balance was {start_balance}, " From 097da448e2d297ace7f2158efb4fac6164168028 Mon Sep 17 00:00:00 2001 From: froggleston Date: Sat, 25 Sep 2021 15:48:42 +0100 Subject: [PATCH 003/144] Add CPU,RAM sysinfo support to the REST API to help with bot system monitoring --- freqtrade/rpc/api_server/api_v1.py | 4 ++++ freqtrade/rpc/rpc.py | 5 ++++- requirements.txt | 1 + scripts/rest_client.py | 6 ++++++ 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index 7e613f184..733fa7383 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -259,3 +259,7 @@ def list_available_pairs(timeframe: Optional[str] = None, stake_currency: Option 'pair_interval': pair_interval, } return result + +@router.get('/sysinfo', tags=['info']) +def sysinfo(rpc: RPC = Depends(get_rpc)): + return rpc._rpc_sysinfo() diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index f6599b429..9b0d4b0f7 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -1,7 +1,7 @@ """ This module contains class to define a RPC communications """ -import logging +import logging, psutil from abc import abstractmethod from datetime import date, datetime, timedelta, timezone from math import isnan @@ -870,3 +870,6 @@ class RPC: 'subplots' not in self._freqtrade.strategy.plot_config): self._freqtrade.strategy.plot_config['subplots'] = {} return self._freqtrade.strategy.plot_config + + def _rpc_sysinfo(self) -> Dict[str, Any]: + return {"cpu_pct": psutil.cpu_percent(interval=1, percpu=True), "ram_pct": psutil.virtual_memory().percent} diff --git a/requirements.txt b/requirements.txt index d1d10dd1d..6a2cfec01 100644 --- a/requirements.txt +++ b/requirements.txt @@ -36,6 +36,7 @@ fastapi==0.68.1 uvicorn==0.15.0 pyjwt==2.1.0 aiofiles==0.7.0 +psutil==5.8.0 # Support for colorized terminal output colorama==0.4.4 diff --git a/scripts/rest_client.py b/scripts/rest_client.py index ece0a253e..52de3c534 100755 --- a/scripts/rest_client.py +++ b/scripts/rest_client.py @@ -334,6 +334,12 @@ class FtRestClient(): "timerange": timerange if timerange else '', }) + def sysinfo(self): + """Provides system information (CPU, RAM usage) + + :return: json object + """ + return self._get("sysinfo") def add_arguments(): parser = argparse.ArgumentParser() From e025576d8cac9cb0227734efc18d4c978d01cc6f Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Wed, 29 Sep 2021 10:15:05 +0300 Subject: [PATCH 004/144] Introduce markets_static fixture serving an immutable list of markets. Adapt pairlist/markets tests to use this new fixture. This allows freely modifying markets in get_markets() without a need of updating pairlist/markets tests. --- tests/commands/test_commands.py | 9 ++++----- tests/conftest.py | 21 ++++++++++++++++++--- tests/exchange/test_exchange.py | 4 ++-- tests/plugins/test_pairlist.py | 4 ++-- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index 135510b38..b236f6a10 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -208,11 +208,10 @@ def test_list_timeframes(mocker, capsys): assert re.search(r"^1d$", captured.out, re.MULTILINE) -def test_list_markets(mocker, markets, capsys): +def test_list_markets(mocker, markets_static, capsys): api_mock = MagicMock() - api_mock.markets = markets - patch_exchange(mocker, api_mock=api_mock, id='bittrex') + patch_exchange(mocker, api_mock=api_mock, id='bittrex', mock_markets=markets_static) # Test with no --config args = [ @@ -237,7 +236,7 @@ def test_list_markets(mocker, markets, capsys): "TKN/BTC, XLTCUSDT, XRP/BTC.\n" in captured.out) - patch_exchange(mocker, api_mock=api_mock, id="binance") + patch_exchange(mocker, api_mock=api_mock, id="binance", mock_markets=markets_static) # Test with --exchange args = [ "list-markets", @@ -250,7 +249,7 @@ def test_list_markets(mocker, markets, capsys): assert re.match("\nExchange Binance has 10 active markets:\n", captured.out) - patch_exchange(mocker, api_mock=api_mock, id="bittrex") + patch_exchange(mocker, api_mock=api_mock, id="bittrex", mock_markets=markets_static) # Test with --all: all markets args = [ "list-markets", "--all", diff --git a/tests/conftest.py b/tests/conftest.py index 7354c0b2c..c908c0cb0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -90,8 +90,10 @@ def patch_exchange(mocker, api_mock=None, id='binance', mock_markets=True) -> No mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value=id.title())) mocker.patch('freqtrade.exchange.Exchange.precisionMode', PropertyMock(return_value=2)) if mock_markets: + if isinstance(mock_markets, bool): + mock_markets = get_markets() mocker.patch('freqtrade.exchange.Exchange.markets', - PropertyMock(return_value=get_markets())) + PropertyMock(return_value=mock_markets)) if api_mock: mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) @@ -376,6 +378,8 @@ def markets(): def get_markets(): + # See get_markets_static() for immutable markets and do not modify them unless absolutely + # necessary! return { 'ETH/BTC': { 'id': 'ethbtc', @@ -675,11 +679,22 @@ def get_markets(): @pytest.fixture -def shitcoinmarkets(markets): +def markets_static(): + # These markets are used in some tests that would need adaptation should anything change in + # market list. Do not modify this list without a good reason! Do not modify market parameters + # of listed pairs in get_markets() without a good reason either! + static_markets = ['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD', + 'LTC/USDT', 'NEO/BTC', 'TKN/BTC', 'XLTCUSDT', 'XRP/BTC'] + all_markets = get_markets() + return {m: all_markets[m] for m in static_markets} + + +@pytest.fixture +def shitcoinmarkets(markets_static): """ Fixture with shitcoin markets - used to test filters in pairlists """ - shitmarkets = deepcopy(markets) + shitmarkets = deepcopy(markets_static) shitmarkets.update({ 'HOT/BTC': { 'id': 'HOTBTC', diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 97bc33429..79b4a3ff5 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -2735,7 +2735,7 @@ def test_get_valid_pair_combination(default_conf, mocker, markets): (['LTC'], ['NONEXISTENT'], False, False, []), ]) -def test_get_markets(default_conf, mocker, markets, +def test_get_markets(default_conf, mocker, markets_static, base_currencies, quote_currencies, pairs_only, active_only, expected_keys): mocker.patch.multiple('freqtrade.exchange.Exchange', @@ -2743,7 +2743,7 @@ def test_get_markets(default_conf, mocker, markets, _load_async_markets=MagicMock(), validate_pairs=MagicMock(), validate_timeframes=MagicMock(), - markets=PropertyMock(return_value=markets)) + markets=PropertyMock(return_value=markets_static)) ex = Exchange(default_conf) pairs = ex.get_markets(base_currencies, quote_currencies, pairs_only, active_only) assert sorted(pairs.keys()) == sorted(expected_keys) diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index 1ce8d172c..cf918e2a0 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -131,9 +131,9 @@ def test_load_pairlist_noexist(mocker, markets, default_conf): default_conf, {}, 1) -def test_load_pairlist_verify_multi(mocker, markets, default_conf): +def test_load_pairlist_verify_multi(mocker, markets_static, default_conf): freqtrade = get_patched_freqtradebot(mocker, default_conf) - mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets)) + mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets_static)) plm = PairListManager(freqtrade.exchange, default_conf) # Call different versions one after the other, should always consider what was passed in # and have no side-effects (therefore the same check multiple times) From 656526c007dfa8fd80036966d601e8b873d1ccd5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 29 Sep 2021 16:50:05 +0200 Subject: [PATCH 005/144] Add trades-to-ohlcv command to simplify adding new timeframes --- freqtrade/commands/__init__.py | 4 +-- freqtrade/commands/arguments.py | 14 +++++++++- freqtrade/commands/data_commands.py | 41 +++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/freqtrade/commands/__init__.py b/freqtrade/commands/__init__.py index a6f14cff7..858c99acd 100644 --- a/freqtrade/commands/__init__.py +++ b/freqtrade/commands/__init__.py @@ -8,8 +8,8 @@ Note: Be careful with file-scoped imports in these subfiles. """ from freqtrade.commands.arguments import Arguments from freqtrade.commands.build_config_commands import start_new_config -from freqtrade.commands.data_commands import (start_convert_data, start_download_data, - start_list_data) +from freqtrade.commands.data_commands import (start_convert_data, start_convert_trades, + start_download_data, start_list_data) from freqtrade.commands.deploy_commands import (start_create_userdir, start_install_ui, start_new_strategy) from freqtrade.commands.hyperopt_commands import start_hyperopt_list, start_hyperopt_show diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index d424f3ce7..48dc48cf1 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -58,6 +58,8 @@ ARGS_BUILD_STRATEGY = ["user_data_dir", "strategy", "template"] ARGS_CONVERT_DATA = ["pairs", "format_from", "format_to", "erase"] ARGS_CONVERT_DATA_OHLCV = ARGS_CONVERT_DATA + ["timeframes"] +ARGS_CONVERT_TRADES = ["pairs", "timeframes", "dataformat_ohlcv", "dataformat_trades"] + ARGS_LIST_DATA = ["exchange", "dataformat_ohlcv", "pairs"] ARGS_DOWNLOAD_DATA = ["pairs", "pairs_file", "days", "new_pairs_days", "timerange", @@ -169,7 +171,8 @@ class Arguments: self.parser = argparse.ArgumentParser(description='Free, open source crypto trading bot') self._build_args(optionlist=['version'], parser=self.parser) - from freqtrade.commands import (start_backtesting, start_convert_data, start_create_userdir, + from freqtrade.commands import (start_backtesting, start_convert_data, start_convert_trades, + start_create_userdir, start_download_data, start_edge, start_hyperopt, start_hyperopt_list, start_hyperopt_show, start_install_ui, start_list_data, start_list_exchanges, start_list_markets, @@ -236,6 +239,15 @@ class Arguments: convert_trade_data_cmd.set_defaults(func=partial(start_convert_data, ohlcv=False)) self._build_args(optionlist=ARGS_CONVERT_DATA, parser=convert_trade_data_cmd) + # Add trades-to-ohlcv subcommand + convert_trade_data_cmd = subparsers.add_parser( + 'trades-to-ohlcv', + help='Convert trade data to OHLCV data.', + parents=[_common_parser], + ) + convert_trade_data_cmd.set_defaults(func=start_convert_trades) + self._build_args(optionlist=ARGS_CONVERT_TRADES, parser=convert_trade_data_cmd) + # Add list-data subcommand list_data_cmd = subparsers.add_parser( 'list-data', diff --git a/freqtrade/commands/data_commands.py b/freqtrade/commands/data_commands.py index 141e85f14..7ef1ae5c7 100644 --- a/freqtrade/commands/data_commands.py +++ b/freqtrade/commands/data_commands.py @@ -89,6 +89,47 @@ def start_download_data(args: Dict[str, Any]) -> None: f"on exchange {exchange.name}.") +def start_convert_trades(args: Dict[str, Any]) -> None: + + config = setup_utils_configuration(args, RunMode.UTIL_EXCHANGE) + + timerange = TimeRange() + if 'days' in config: + time_since = (datetime.now() - timedelta(days=config['days'])).strftime("%Y%m%d") + timerange = TimeRange.parse_timerange(f'{time_since}-') + + if 'timerange' in config: + timerange = timerange.parse_timerange(config['timerange']) + + # Remove stake-currency to skip checks which are not relevant for datadownload + config['stake_currency'] = '' + + if 'pairs' not in config: + raise OperationalException( + "Downloading data requires a list of pairs. " + "Please check the documentation on how to configure this.") + + # Init exchange + exchange = ExchangeResolver.load_exchange(config['exchange']['name'], config, validate=False) + # Manual validations of relevant settings + if not config['exchange'].get('skip_pair_validation', False): + exchange.validate_pairs(config['pairs']) + expanded_pairs = expand_pairlist(config['pairs'], list(exchange.markets)) + + logger.info(f"About to Convert pairs: {expanded_pairs}, " + f"intervals: {config['timeframes']} to {config['datadir']}") + + for timeframe in config['timeframes']: + exchange.validate_timeframes(timeframe) + # Convert downloaded trade data to different timeframes + convert_trades_to_ohlcv( + pairs=expanded_pairs, timeframes=config['timeframes'], + datadir=config['datadir'], timerange=timerange, erase=bool(config.get('erase')), + data_format_ohlcv=config['dataformat_ohlcv'], + data_format_trades=config['dataformat_trades'], + ) + + def start_convert_data(args: Dict[str, Any], ohlcv: bool = True) -> None: """ Convert data from one format to another From fc511aac4486dc99568e88edd01635453d0c1a59 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 29 Sep 2021 19:21:54 +0200 Subject: [PATCH 006/144] don't use %default when no default is defined --- freqtrade/commands/cli_options.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index e3c7fe464..d350a9426 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -381,12 +381,12 @@ AVAILABLE_CLI_OPTIONS = { ), "dataformat_ohlcv": Arg( '--data-format-ohlcv', - help='Storage format for downloaded candle (OHLCV) data. (default: `%(default)s`).', + help='Storage format for downloaded candle (OHLCV) data. (default: `json`).', choices=constants.AVAILABLE_DATAHANDLERS, ), "dataformat_trades": Arg( '--data-format-trades', - help='Storage format for downloaded trades data. (default: `%(default)s`).', + help='Storage format for downloaded trades data. (default: `jsongz`).', choices=constants.AVAILABLE_DATAHANDLERS, ), "exchange": Arg( From 248c61bb26399d3049f7ac2f116d6cf0e49d8665 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 29 Sep 2021 19:39:29 +0200 Subject: [PATCH 007/144] Add test for trades-to-ohlcv --- freqtrade/commands/arguments.py | 4 ++-- freqtrade/commands/data_commands.py | 6 ------ tests/commands/test_commands.py | 28 ++++++++++++++++++++++------ 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 48dc48cf1..2fadf047e 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -58,7 +58,7 @@ ARGS_BUILD_STRATEGY = ["user_data_dir", "strategy", "template"] ARGS_CONVERT_DATA = ["pairs", "format_from", "format_to", "erase"] ARGS_CONVERT_DATA_OHLCV = ARGS_CONVERT_DATA + ["timeframes"] -ARGS_CONVERT_TRADES = ["pairs", "timeframes", "dataformat_ohlcv", "dataformat_trades"] +ARGS_CONVERT_TRADES = ["pairs", "timeframes", "exchange", "dataformat_ohlcv", "dataformat_trades"] ARGS_LIST_DATA = ["exchange", "dataformat_ohlcv", "pairs"] @@ -93,7 +93,7 @@ ARGS_HYPEROPT_SHOW = ["hyperopt_list_best", "hyperopt_list_profitable", "hyperop NO_CONF_REQURIED = ["convert-data", "convert-trade-data", "download-data", "list-timeframes", "list-markets", "list-pairs", "list-strategies", "list-data", "hyperopt-list", "hyperopt-show", - "plot-dataframe", "plot-profit", "show-trades"] + "plot-dataframe", "plot-profit", "show-trades", "trades-to-ohlcv"] NO_CONF_ALLOWED = ["create-userdir", "list-exchanges", "new-strategy"] diff --git a/freqtrade/commands/data_commands.py b/freqtrade/commands/data_commands.py index 7ef1ae5c7..ee05e6c69 100644 --- a/freqtrade/commands/data_commands.py +++ b/freqtrade/commands/data_commands.py @@ -94,12 +94,6 @@ def start_convert_trades(args: Dict[str, Any]) -> None: config = setup_utils_configuration(args, RunMode.UTIL_EXCHANGE) timerange = TimeRange() - if 'days' in config: - time_since = (datetime.now() - timedelta(days=config['days'])).strftime("%Y%m%d") - timerange = TimeRange.parse_timerange(f'{time_since}-') - - if 'timerange' in config: - timerange = timerange.parse_timerange(config['timerange']) # Remove stake-currency to skip checks which are not relevant for datadownload config['stake_currency'] = '' diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index b236f6a10..8889617ba 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -8,12 +8,12 @@ from zipfile import ZipFile import arrow import pytest -from freqtrade.commands import (start_convert_data, start_create_userdir, start_download_data, - start_hyperopt_list, start_hyperopt_show, start_install_ui, - start_list_data, start_list_exchanges, start_list_markets, - start_list_strategies, start_list_timeframes, start_new_strategy, - start_show_trades, start_test_pairlist, start_trading, - start_webserver) +from freqtrade.commands import (start_convert_data, start_convert_trades, start_create_userdir, + start_download_data, start_hyperopt_list, start_hyperopt_show, + start_install_ui, start_list_data, start_list_exchanges, + start_list_markets, start_list_strategies, start_list_timeframes, + start_new_strategy, start_show_trades, start_test_pairlist, + start_trading, start_webserver) from freqtrade.commands.deploy_commands import (clean_ui_subdir, download_and_install_ui, get_ui_download_url, read_ui_version) from freqtrade.configuration import setup_utils_configuration @@ -759,6 +759,22 @@ def test_download_data_trades(mocker, caplog): assert convert_mock.call_count == 1 +def test_start_convert_trades(mocker, caplog): + convert_mock = mocker.patch('freqtrade.commands.data_commands.convert_trades_to_ohlcv', + MagicMock(return_value=[])) + patch_exchange(mocker) + mocker.patch( + 'freqtrade.exchange.Exchange.markets', PropertyMock(return_value={}) + ) + args = [ + "trades-to-ohlcv", + "--exchange", "kraken", + "--pairs", "ETH/BTC", "XRP/BTC", + ] + start_convert_trades(get_args(args)) + assert convert_mock.call_count == 1 + + def test_start_list_strategies(mocker, caplog, capsys): args = [ From 178db516bf79dbaa37ca2ef9b779d77ed8850f75 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 29 Sep 2021 19:48:56 +0200 Subject: [PATCH 008/144] Add documentation for trade-to-ohlcv --- docs/data-download.md | 55 +++++++++++++++++++++++++++++++++ freqtrade/commands/arguments.py | 15 +++++---- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/docs/data-download.md b/docs/data-download.md index 0ca86b0d3..5f605c404 100644 --- a/docs/data-download.md +++ b/docs/data-download.md @@ -204,6 +204,61 @@ It'll also remove original jsongz data files (`--erase` parameter). freqtrade convert-trade-data --format-from jsongz --format-to json --datadir ~/.freqtrade/data/kraken --erase ``` +### Sub-command trades to ohlcv + +When you need to use `--dl-trades` (kraken only) to download data, conversion of trades data to ohlcv data is the last step. +This command will allow you to repeat this last step for additional timeframes without re-downloading the data. + +``` +usage: freqtrade trades-to-ohlcv [-h] [-v] [--logfile FILE] [-V] [-c PATH] + [-d PATH] [--userdir PATH] + [-p PAIRS [PAIRS ...]] + [-t {1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,2w,1M,1y} [{1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,2w,1M,1y} ...]] + [--exchange EXCHANGE] + [--data-format-ohlcv {json,jsongz,hdf5}] + [--data-format-trades {json,jsongz,hdf5}] + +optional arguments: + -h, --help show this help message and exit + -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] + Limit command to these pairs. Pairs are space- + separated. + -t {1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,2w,1M,1y} [{1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,2w,1M,1y} ...], --timeframes {1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,2w,1M,1y} [{1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,2w,1M,1y} ...] + Specify which tickers to download. Space-separated + list. Default: `1m 5m`. + --exchange EXCHANGE Exchange name (default: `bittrex`). Only valid if no + config is provided. + --data-format-ohlcv {json,jsongz,hdf5} + Storage format for downloaded candle (OHLCV) data. + (default: `json`). + --data-format-trades {json,jsongz,hdf5} + Storage format for downloaded trades data. (default: + `jsongz`). + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --logfile FILE Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +``` + +#### Example trade-to-ohlcv conversion + +``` bash +freqtrade trades-to-ohlcv --exchange kraken -t 5m 1h 1d --pairs BTC/EUR ETH/EUR +``` + ### Sub-command list-data You can get a list of downloaded data using the `list-data` sub-command. diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 2fadf047e..9643705a5 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -172,14 +172,13 @@ class Arguments: self._build_args(optionlist=['version'], parser=self.parser) from freqtrade.commands import (start_backtesting, start_convert_data, start_convert_trades, - start_create_userdir, - start_download_data, start_edge, start_hyperopt, - start_hyperopt_list, start_hyperopt_show, start_install_ui, - start_list_data, start_list_exchanges, start_list_markets, - start_list_strategies, start_list_timeframes, - start_new_config, start_new_strategy, start_plot_dataframe, - start_plot_profit, start_show_trades, start_test_pairlist, - start_trading, start_webserver) + start_create_userdir, start_download_data, start_edge, + start_hyperopt, start_hyperopt_list, start_hyperopt_show, + start_install_ui, start_list_data, start_list_exchanges, + start_list_markets, start_list_strategies, + start_list_timeframes, start_new_config, start_new_strategy, + start_plot_dataframe, start_plot_profit, start_show_trades, + start_test_pairlist, start_trading, start_webserver) subparsers = self.parser.add_subparsers(dest='command', # Use custom message when no subhandler is added From bd27993e797c8d83d54ac76b278a9779e9c4eee5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 30 Sep 2021 06:42:42 +0200 Subject: [PATCH 009/144] Add documentation segment about indicator libraries --- docs/strategy-customization.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 110365208..0bfc0a2f6 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -122,6 +122,16 @@ def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame Look into the [user_data/strategies/sample_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_strategy.py). Then uncomment indicators you need. +#### Indicator libraries + +Out of the box, freqtrade installs the following technical libraries: + +* [ta-lib](http://mrjbq7.github.io/ta-lib/) +* [pandas-ta](https://twopirllc.github.io/pandas-ta/) +* [technical](https://github.com/freqtrade/technical/) + +Additional technical libraries can be installed as necessary, or custom indicators may be written / invented by the strategy author. + ### Strategy startup period Most indicators have an instable startup period, in which they are either not available, or the calculation is incorrect. This can lead to inconsistencies, since Freqtrade does not know how long this instable period should be. From 5f23af580248a86a56d60e050bd5d0a7f5424236 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 30 Sep 2021 07:24:16 +0200 Subject: [PATCH 010/144] Rename update_open_trades to clarify it's only called at startup --- freqtrade/freqtradebot.py | 4 ++-- tests/test_freqtradebot.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 3a9b21b7c..bf4742fdc 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -139,7 +139,7 @@ class FreqtradeBot(LoggingMixin): # Only update open orders on startup # This will update the database after the initial migration - self.update_open_orders() + self.startup_update_open_orders() def process(self) -> None: """ @@ -237,7 +237,7 @@ class FreqtradeBot(LoggingMixin): open_trades = len(Trade.get_open_trades()) return max(0, self.config['max_open_trades'] - open_trades) - def update_open_orders(self): + def startup_update_open_orders(self): """ Updates open orders based on order list kept in the database. Mainly updates the state of orders - but may also close trades diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 760a9dee7..d312bdb11 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -4033,16 +4033,16 @@ def test_check_for_open_trades(mocker, default_conf, fee): @pytest.mark.usefixtures("init_persistence") -def test_update_open_orders(mocker, default_conf, fee, caplog): +def test_startup_update_open_orders(mocker, default_conf, fee, caplog): freqtrade = get_patched_freqtradebot(mocker, default_conf) create_mock_trades(fee) - freqtrade.update_open_orders() + freqtrade.startup_update_open_orders() assert not log_has_re(r"Error updating Order .*", caplog) caplog.clear() freqtrade.config['dry_run'] = False - freqtrade.update_open_orders() + freqtrade.startup_update_open_orders() assert log_has_re(r"Error updating Order .*", caplog) caplog.clear() @@ -4053,7 +4053,7 @@ def test_update_open_orders(mocker, default_conf, fee, caplog): 'status': 'closed', }) mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=matching_buy_order) - freqtrade.update_open_orders() + freqtrade.startup_update_open_orders() # Only stoploss and sell orders are kept open assert len(Order.get_open_orders()) == 2 From 5dd1088d8dae43847a09dc26fec010aa7c35ab8a Mon Sep 17 00:00:00 2001 From: Scott Lyons Date: Thu, 30 Sep 2021 00:44:26 -0700 Subject: [PATCH 011/144] Adding ignore unparameterized spaces flag --- freqtrade/commands/cli_options.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index e3c7fe464..ef1ec8515 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -552,4 +552,9 @@ AVAILABLE_CLI_OPTIONS = { help='Do not print epoch details header.', action='store_true', ), + "hyperopt_ignore_unparam_space": Arg( + "-u", "--ignore-unparameterized-spaces", + help="Suppress errors for any requested Hyperopt spaces that do not contain any parameters", + action="store_true", + ), } From 08fcd1a0d4977716d466496171e4ae6a5c36b67f Mon Sep 17 00:00:00 2001 From: Scott Lyons Date: Thu, 30 Sep 2021 00:46:56 -0700 Subject: [PATCH 012/144] Adding ignore space errors to Hyperopt CLI --- freqtrade/commands/arguments.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index d424f3ce7..e58135895 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -31,7 +31,8 @@ ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + ["hyperopt", "hyperopt_path", "epochs", "spaces", "print_all", "print_colorized", "print_json", "hyperopt_jobs", "hyperopt_random_state", "hyperopt_min_trades", - "hyperopt_loss", "disableparamexport"] + "hyperopt_loss", "disableparamexport", + "hyperopt_ignore_unparam_space"] ARGS_EDGE = ARGS_COMMON_OPTIMIZE + ["stoploss_range"] From 95227376b609a98b1577633861f90e9c9bb594f0 Mon Sep 17 00:00:00 2001 From: Scott Lyons Date: Thu, 30 Sep 2021 00:53:46 -0700 Subject: [PATCH 013/144] Adding IUS to optimize args --- freqtrade/configuration/configuration.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 94b108f2b..723ad3795 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -368,6 +368,9 @@ class Configuration: self._args_to_config(config, argname='hyperopt_show_no_header', logstring='Parameter --no-header detected: {}') + + self._args_to_config(config, argname="hyperopt_ignore_unparam_space", + logstring="Paramter --ignore-unparameterized-spaces detected: {}") def _process_plot_options(self, config: Dict[str, Any]) -> None: From df45f467c69e5b0dc64fe8cb5b5af716b42979d7 Mon Sep 17 00:00:00 2001 From: Scott Lyons Date: Thu, 30 Sep 2021 01:11:02 -0700 Subject: [PATCH 014/144] Adding ability to ignore unparameterized spaces --- freqtrade/optimize/hyperopt.py | 48 +++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 9549b4054..f6c677a6e 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -237,27 +237,63 @@ class Hyperopt: logger.debug("Hyperopt has 'protection' space") # Enable Protections if protection space is selected. self.config['enable_protections'] = True - self.protection_space = self.custom_hyperopt.protection_space() + try: + self.protection_space = self.custom_hyperopt.protection_space() + except OperationalException as e: + if self.config["hyperopt_ignore_unparam_space"]: + logger.warning(e) + else: + raise if HyperoptTools.has_space(self.config, 'buy'): logger.debug("Hyperopt has 'buy' space") - self.buy_space = self.custom_hyperopt.buy_indicator_space() + try: + self.buy_space = self.custom_hyperopt.buy_indicator_space() + except OperationalException as e: + if self.config["hyperopt_ignore_unparam_space"]: + logger.warning(e) + else: + raise if HyperoptTools.has_space(self.config, 'sell'): logger.debug("Hyperopt has 'sell' space") - self.sell_space = self.custom_hyperopt.sell_indicator_space() + try: + self.sell_space = self.custom_hyperopt.sell_indicator_space() + except OperationalException as e: + if self.config["hyperopt_ignore_unparam_space"]: + logger.warning(e) + else: + raise if HyperoptTools.has_space(self.config, 'roi'): logger.debug("Hyperopt has 'roi' space") - self.roi_space = self.custom_hyperopt.roi_space() + try: + self.roi_space = self.custom_hyperopt.roi_space() + except OperationalException as e: + if self.config["hyperopt_ignore_unparam_space"]: + logger.warning(e) + else: + raise if HyperoptTools.has_space(self.config, 'stoploss'): logger.debug("Hyperopt has 'stoploss' space") - self.stoploss_space = self.custom_hyperopt.stoploss_space() + try: + self.stoploss_space = self.custom_hyperopt.stoploss_space() + except OperationalException as e: + if self.config["hyperopt_ignore_unparam_space"]: + logger.warning(e) + else: + raise if HyperoptTools.has_space(self.config, 'trailing'): logger.debug("Hyperopt has 'trailing' space") - self.trailing_space = self.custom_hyperopt.trailing_space() + try: + self.trailing_space = self.custom_hyperopt.trailing_space() + except OperationalException as e: + if self.config["hyperopt_ignore_unparam_space"]: + logger.warning(e) + else: + raise self.dimensions = (self.buy_space + self.sell_space + self.protection_space + self.roi_space + self.stoploss_space + self.trailing_space) From 15df5fd9c5c8533b14809147570e2a66d79241fe Mon Sep 17 00:00:00 2001 From: Robert Davey Date: Fri, 1 Oct 2021 13:49:16 +0100 Subject: [PATCH 015/144] Fix pair_candles to point to correct API call pair_candles pointed to available_pairs RPC call instead of pair_candles --- scripts/rest_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/rest_client.py b/scripts/rest_client.py index ece0a253e..713b398c3 100755 --- a/scripts/rest_client.py +++ b/scripts/rest_client.py @@ -312,7 +312,7 @@ class FtRestClient(): :param limit: Limit result to the last n candles. :return: json object """ - return self._get("available_pairs", params={ + return self._get("pair_candles", params={ "pair": pair, "timeframe": timeframe, "limit": limit, From f69cb39a170a4a223de4f154c902c69903188a36 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 1 Oct 2021 19:26:51 +0200 Subject: [PATCH 016/144] Fix missing comma in kucoin template closes #5646 --- freqtrade/templates/subtemplates/exchange_kucoin.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/templates/subtemplates/exchange_kucoin.j2 b/freqtrade/templates/subtemplates/exchange_kucoin.j2 index f9dfff663..9882c51c7 100644 --- a/freqtrade/templates/subtemplates/exchange_kucoin.j2 +++ b/freqtrade/templates/subtemplates/exchange_kucoin.j2 @@ -4,7 +4,7 @@ "secret": "{{ exchange_secret }}", "password": "{{ exchange_key_password }}", "ccxt_config": { - "enableRateLimit": true + "enableRateLimit": true, "rateLimit": 200 }, "ccxt_async_config": { From dadd134200bdcca66456e0795d92c12841fb8e87 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Fri, 17 Sep 2021 02:25:58 -0600 Subject: [PATCH 017/144] changes some tests to use usdt values --- tests/conftest.py | 106 ++++++- tests/conftest_trades_usdt.py | 305 ++++++++++++++++++ tests/test_freqtradebot.py | 575 +++++++++++++++++----------------- 3 files changed, 699 insertions(+), 287 deletions(-) create mode 100644 tests/conftest_trades_usdt.py diff --git a/tests/conftest.py b/tests/conftest.py index c908c0cb0..c1032a215 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -25,6 +25,8 @@ from freqtrade.resolvers import ExchangeResolver from freqtrade.worker import Worker from tests.conftest_trades import (mock_trade_1, mock_trade_2, mock_trade_3, mock_trade_4, mock_trade_5, mock_trade_6) +from tests.conftest_trades_usdt import (mock_trade_usdt_1, mock_trade_usdt_2, mock_trade_usdt_3, + mock_trade_usdt_4, mock_trade_usdt_5, mock_trade_usdt_6) logging.getLogger('').setLevel(logging.INFO) @@ -227,6 +229,39 @@ def create_mock_trades(fee, use_db: bool = True): Trade.query.session.flush() +def create_mock_trades_usdt(fee, use_db: bool = True): + """ + Create some fake trades ... + """ + def add_trade(trade): + if use_db: + Trade.query.session.add(trade) + else: + LocalTrade.add_bt_trade(trade) + + # Simulate dry_run entries + trade = mock_trade_usdt_1(fee) + add_trade(trade) + + trade = mock_trade_usdt_2(fee) + add_trade(trade) + + trade = mock_trade_usdt_3(fee) + add_trade(trade) + + trade = mock_trade_usdt_4(fee) + add_trade(trade) + + trade = mock_trade_usdt_5(fee) + add_trade(trade) + + trade = mock_trade_usdt_6(fee) + add_trade(trade) + + if use_db: + Trade.query.session.flush() + + @pytest.fixture(autouse=True) def patch_coingekko(mocker) -> None: """ @@ -303,7 +338,8 @@ def get_default_conf(testdatadir): "ETH/BTC", "LTC/BTC", "XRP/BTC", - "NEO/BTC" + "NEO/BTC", + "ADA/USDT" ], "pair_blacklist": [ "DOGE/BTC", @@ -372,6 +408,33 @@ def ticker_sell_down(): }) +@pytest.fixture +def ticker_usdt(): + return MagicMock(return_value={ + 'bid': 1.99, + 'ask': 2.0, + 'last': 1.99, + }) + + +@pytest.fixture +def ticker_usdt_sell_up(): + return MagicMock(return_value={ + 'bid': 2.19, + 'ask': 2.2, + 'last': 2.19, + }) + + +@pytest.fixture +def ticker_usdt_sell_down(): + return MagicMock(return_value={ + 'bid': 2.01, + 'ask': 2.0, + 'last': 2.01, + }) + + @pytest.fixture def markets(): return get_markets() @@ -406,6 +469,31 @@ def get_markets(): }, 'info': {}, }, + 'ADA/USDT': { + 'id': 'ethbtc', + 'symbol': 'ADA/USDT', + 'base': 'USDT', + 'quote': 'ADA', + 'active': True, + 'precision': { + 'price': 8, + 'amount': 8, + 'cost': 8, + }, + 'lot': 0.00000001, + 'limits': { + 'amount': { + 'min': 0.01, + 'max': 1000, + }, + 'price': 500000, + 'cost': { + 'min': 0.0001, + 'max': 500000, + }, + }, + 'info': {}, + }, 'TKN/BTC': { 'id': 'tknbtc', 'symbol': 'TKN/BTC', @@ -1821,6 +1909,22 @@ def open_trade(): ) +@pytest.fixture(scope="function") +def open_trade_usdt(): + return Trade( + pair='ADA/USDT', + open_rate=2.0, + exchange='binance', + open_order_id='123456789', + amount=30.0, + fee_open=0.0, + fee_close=0.0, + stake_amount=60.0, + open_date=arrow.utcnow().shift(minutes=-601).datetime, + is_open=True + ) + + @pytest.fixture def saved_hyperopt_results(): hyperopt_res = [ diff --git a/tests/conftest_trades_usdt.py b/tests/conftest_trades_usdt.py new file mode 100644 index 000000000..1a03f0381 --- /dev/null +++ b/tests/conftest_trades_usdt.py @@ -0,0 +1,305 @@ +from datetime import datetime, timedelta, timezone + +from freqtrade.persistence.models import Order, Trade + + +MOCK_TRADE_COUNT = 6 + + +def mock_order_usdt_1(): + return { + 'id': '1234', + 'symbol': 'ADA/USDT', + 'status': 'closed', + 'side': 'buy', + 'type': 'limit', + 'price': 2.0, + 'amount': 10.0, + 'filled': 10.0, + 'remaining': 0.0, + } + + +def mock_trade_usdt_1(fee): + trade = Trade( + pair='ADA/USDT', + stake_amount=20.0, + amount=10.0, + amount_requested=10.0, + fee_open=fee.return_value, + fee_close=fee.return_value, + is_open=True, + open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=17), + open_rate=2.0, + exchange='binance', + open_order_id='dry_run_buy_12345', + strategy='StrategyTestV2', + timeframe=5, + ) + o = Order.parse_from_ccxt_object(mock_order_usdt_1(), 'ADA/USDT', 'buy') + trade.orders.append(o) + return trade + + +def mock_order_usdt_2(): + return { + 'id': '1235', + 'symbol': 'ETC/USDT', + 'status': 'closed', + 'side': 'buy', + 'type': 'limit', + 'price': 2.0, + 'amount': 100.0, + 'filled': 100.0, + 'remaining': 0.0, + } + + +def mock_order_usdt_2_sell(): + return { + 'id': '12366', + 'symbol': 'ETC/USDT', + 'status': 'closed', + 'side': 'sell', + 'type': 'limit', + 'price': 2.05, + 'amount': 100.0, + 'filled': 100.0, + 'remaining': 0.0, + } + + +def mock_trade_usdt_2(fee): + """ + Closed trade... + """ + trade = Trade( + pair='ETC/USDT', + stake_amount=200.0, + amount=100.0, + amount_requested=100.0, + fee_open=fee.return_value, + fee_close=fee.return_value, + open_rate=2.0, + close_rate=2.05, + close_profit=5.0, + close_profit_abs=3.9875, + exchange='binance', + is_open=False, + open_order_id='dry_run_sell_12345', + strategy='StrategyTestV2', + timeframe=5, + sell_reason='sell_signal', + open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20), + close_date=datetime.now(tz=timezone.utc) - timedelta(minutes=2), + ) + o = Order.parse_from_ccxt_object(mock_order_usdt_2(), 'ETC/USDT', 'buy') + trade.orders.append(o) + o = Order.parse_from_ccxt_object(mock_order_usdt_2_sell(), 'ETC/USDT', 'sell') + trade.orders.append(o) + return trade + + +def mock_order_usdt_3(): + return { + 'id': '41231a12a', + 'symbol': 'XRP/USDT', + 'status': 'closed', + 'side': 'buy', + 'type': 'limit', + 'price': 1.0, + 'amount': 30.0, + 'filled': 30.0, + 'remaining': 0.0, + } + + +def mock_order_usdt_3_sell(): + return { + 'id': '41231a666a', + 'symbol': 'XRP/USDT', + 'status': 'closed', + 'side': 'sell', + 'type': 'stop_loss_limit', + 'price': 1.1, + 'average': 1.1, + 'amount': 30.0, + 'filled': 30.0, + 'remaining': 0.0, + } + + +def mock_trade_usdt_3(fee): + """ + Closed trade + """ + trade = Trade( + pair='XRP/USDT', + stake_amount=30.0, + amount=30.0, + amount_requested=30.0, + fee_open=fee.return_value, + fee_close=fee.return_value, + open_rate=1.0, + close_rate=1.1, + close_profit=10.0, + close_profit_abs=9.8425, + exchange='binance', + is_open=False, + strategy='StrategyTestV2', + timeframe=5, + sell_reason='roi', + open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20), + close_date=datetime.now(tz=timezone.utc), + ) + o = Order.parse_from_ccxt_object(mock_order_usdt_3(), 'XRP/USDT', 'buy') + trade.orders.append(o) + o = Order.parse_from_ccxt_object(mock_order_usdt_3_sell(), 'XRP/USDT', 'sell') + trade.orders.append(o) + return trade + + +def mock_order_usdt_4(): + return { + 'id': 'prod_buy_12345', + 'symbol': 'ETC/USDT', + 'status': 'open', + 'side': 'buy', + 'type': 'limit', + 'price': 2.0, + 'amount': 10.0, + 'filled': 0.0, + 'remaining': 30.0, + } + + +def mock_trade_usdt_4(fee): + """ + Simulate prod entry + """ + trade = Trade( + pair='ETC/USDT', + stake_amount=20.0, + amount=10.0, + amount_requested=10.01, + fee_open=fee.return_value, + fee_close=fee.return_value, + open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=14), + is_open=True, + open_rate=2.0, + exchange='binance', + open_order_id='prod_buy_12345', + strategy='StrategyTestV2', + timeframe=5, + ) + o = Order.parse_from_ccxt_object(mock_order_usdt_4(), 'ETC/USDT', 'buy') + trade.orders.append(o) + return trade + + +def mock_order_usdt_5(): + return { + 'id': 'prod_buy_3455', + 'symbol': 'XRP/USDT', + 'status': 'closed', + 'side': 'buy', + 'type': 'limit', + 'price': 2.0, + 'amount': 10.0, + 'filled': 10.0, + 'remaining': 0.0, + } + + +def mock_order_usdt_5_stoploss(): + return { + 'id': 'prod_stoploss_3455', + 'symbol': 'XRP/USDT', + 'status': 'open', + 'side': 'sell', + 'type': 'stop_loss_limit', + 'price': 2.0, + 'amount': 10.0, + 'filled': 0.0, + 'remaining': 30.0, + } + + +def mock_trade_usdt_5(fee): + """ + Simulate prod entry with stoploss + """ + trade = Trade( + pair='XRP/USDT', + stake_amount=20.0, + amount=10.0, + amount_requested=10.01, + fee_open=fee.return_value, + fee_close=fee.return_value, + open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=12), + is_open=True, + open_rate=2.0, + exchange='binance', + strategy='SampleStrategy', + stoploss_order_id='prod_stoploss_3455', + timeframe=5, + ) + o = Order.parse_from_ccxt_object(mock_order_usdt_5(), 'XRP/USDT', 'buy') + trade.orders.append(o) + o = Order.parse_from_ccxt_object(mock_order_usdt_5_stoploss(), 'XRP/USDT', 'stoploss') + trade.orders.append(o) + return trade + + +def mock_order_usdt_6(): + return { + 'id': 'prod_buy_6', + 'symbol': 'LTC/USDT', + 'status': 'closed', + 'side': 'buy', + 'type': 'limit', + 'price': 10.0, + 'amount': 2.0, + 'filled': 2.0, + 'remaining': 0.0, + } + + +def mock_order_usdt_6_sell(): + return { + 'id': 'prod_sell_6', + 'symbol': 'LTC/USDT', + 'status': 'open', + 'side': 'sell', + 'type': 'limit', + 'price': 12.0, + 'amount': 2.0, + 'filled': 0.0, + 'remaining': 2.0, + } + + +def mock_trade_usdt_6(fee): + """ + Simulate prod entry with open sell order + """ + trade = Trade( + pair='LTC/USDT', + stake_amount=20.0, + amount=2.0, + amount_requested=2.0, + open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=5), + fee_open=fee.return_value, + fee_close=fee.return_value, + is_open=True, + open_rate=10.0, + exchange='binance', + strategy='SampleStrategy', + open_order_id="prod_sell_6", + timeframe=5, + ) + o = Order.parse_from_ccxt_object(mock_order_usdt_6(), 'LTC/USDT', 'buy') + trade.orders.append(o) + o = Order.parse_from_ccxt_object(mock_order_usdt_6_sell(), 'LTC/USDT', 'sell') + trade.orders.append(o) + return trade diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index d312bdb11..7ddb90657 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -23,9 +23,9 @@ from freqtrade.worker import Worker from tests.conftest import (create_mock_trades, get_patched_freqtradebot, get_patched_worker, log_has, log_has_re, patch_edge, patch_exchange, patch_get_signal, patch_wallet, patch_whitelist) -from tests.conftest_trades import (MOCK_TRADE_COUNT, mock_order_1, mock_order_2, mock_order_2_sell, - mock_order_3, mock_order_3_sell, mock_order_4, - mock_order_5_stoploss, mock_order_6_sell) +from tests.conftest_trades import ( + MOCK_TRADE_COUNT, mock_order_1, mock_order_2, mock_order_2_sell, mock_order_3, + mock_order_3_sell, mock_order_4, mock_order_5_stoploss, mock_order_6_sell) def patch_RPCManager(mocker) -> MagicMock: @@ -135,14 +135,14 @@ def test_get_trade_stake_amount(default_conf, ticker, mocker) -> None: (True, 0.0027, 3, 0.5, [0.001, 0.001, 0.000673]), (True, 0.0022, 3, 1, [0.001, 0.001, 0.0]), ]) -def test_check_available_stake_amount(default_conf, ticker, mocker, fee, limit_buy_order_open, +def test_check_available_stake_amount(default_conf, ticker, mocker, fee, limit_buy_order_usdt_open, amend_last, wallet, max_open, lsamr, expected) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - create_order=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(return_value=limit_buy_order_usdt_open), get_fee=fee ) default_conf['dry_run_wallet'] = wallet @@ -155,7 +155,7 @@ def test_check_available_stake_amount(default_conf, ticker, mocker, fee, limit_b for i in range(0, max_open): if expected[i] is not None: - limit_buy_order_open['id'] = str(i) + limit_buy_order_usdt_open['id'] = str(i) result = freqtrade.wallets.get_trade_stake_amount('ETH/BTC') assert pytest.approx(result) == expected[i] freqtrade.execute_entry('ETH/BTC', result) @@ -198,7 +198,7 @@ def test_edge_overrides_stake_amount(mocker, edge_conf) -> None: # Override strategy stoploss (0.85, True) ]) -def test_edge_overrides_stoploss(limit_buy_order, fee, caplog, mocker, +def test_edge_overrides_stoploss(limit_buy_order_usdt, fee, caplog, mocker, buy_price_mult, ignore_strat_sl, edge_conf) -> None: patch_RPCManager(mocker) patch_exchange(mocker) @@ -209,7 +209,7 @@ def test_edge_overrides_stoploss(limit_buy_order, fee, caplog, mocker, # Thus, if price falls 21%, stoploss should be triggered # # mocking the ticker: price is falling ... - buy_price = limit_buy_order['price'] + buy_price = limit_buy_order_usdt['price'] mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=MagicMock(return_value={ @@ -221,14 +221,14 @@ def test_edge_overrides_stoploss(limit_buy_order, fee, caplog, mocker, ) ############################################# - # Create a trade with "limit_buy_order" price + # Create a trade with "limit_buy_order_usdt" price freqtrade = FreqtradeBot(edge_conf) freqtrade.active_pair_whitelist = ['NEO/BTC'] patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) freqtrade.enter_positions() trade = Trade.query.first() - trade.update(limit_buy_order) + trade.update(limit_buy_order_usdt) ############################################# # stoploss shoud be hit @@ -270,7 +270,7 @@ def test_total_open_trades_stakes(mocker, default_conf, ticker, fee) -> None: assert Trade.total_open_trades_stakes() == 1.97502e-03 -def test_create_trade(default_conf, ticker, limit_buy_order, fee, mocker) -> None: +def test_create_trade(default_conf, ticker, limit_buy_order_usdt, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -294,15 +294,15 @@ def test_create_trade(default_conf, ticker, limit_buy_order, fee, mocker) -> Non assert trade.exchange == 'binance' # Simulate fulfilled LIMIT_BUY order for trade - trade.update(limit_buy_order) + trade.update(limit_buy_order_usdt) - assert trade.open_rate == 0.00001099 - assert trade.amount == 90.99181073 + assert trade.open_rate == 2.0 + assert trade.amount == 30.0 assert whitelist == default_conf['exchange']['pair_whitelist'] -def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order, +def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order_usdt, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) @@ -326,12 +326,12 @@ def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order, (UNLIMITED_STAKE_AMOUNT, False, True, 0), ]) def test_create_trade_minimal_amount( - default_conf, ticker, limit_buy_order_open, fee, mocker, + default_conf, ticker, limit_buy_order_usdt_open, fee, mocker, stake_amount, create, amount_enough, max_open_trades, caplog ) -> None: patch_RPCManager(mocker) patch_exchange(mocker) - buy_mock = MagicMock(return_value=limit_buy_order_open) + buy_mock = MagicMock(return_value=limit_buy_order_usdt_open) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, @@ -363,14 +363,14 @@ def test_create_trade_minimal_amount( (["ETH/BTC"], 1), # No pairs left ([], 0), # No pairs in whitelist ]) -def test_enter_positions_no_pairs_left(default_conf, ticker, limit_buy_order_open, fee, +def test_enter_positions_no_pairs_left(default_conf, ticker, limit_buy_order_usdt_open, fee, whitelist, positions, mocker, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - create_order=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(return_value=limit_buy_order_usdt_open), get_fee=fee, ) default_conf['exchange']['pair_whitelist'] = whitelist @@ -390,14 +390,14 @@ def test_enter_positions_no_pairs_left(default_conf, ticker, limit_buy_order_ope @pytest.mark.usefixtures("init_persistence") -def test_enter_positions_global_pairlock(default_conf, ticker, limit_buy_order, fee, +def test_enter_positions_global_pairlock(default_conf, ticker, limit_buy_order_usdt, fee, mocker, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - create_order=MagicMock(return_value={'id': limit_buy_order['id']}), + create_order=MagicMock(return_value={'id': limit_buy_order_usdt['id']}), get_fee=fee, ) freqtrade = FreqtradeBot(default_conf) @@ -459,7 +459,7 @@ def test_create_trade_no_signal(default_conf, fee, mocker) -> None: @pytest.mark.parametrize("max_open", range(0, 5)) @pytest.mark.parametrize("tradable_balance_ratio,modifier", [(1.0, 1), (0.99, 0.8), (0.5, 0.5)]) -def test_create_trades_multiple_trades(default_conf, ticker, fee, mocker, limit_buy_order_open, +def test_create_trades_multiple_trades(default_conf, ticker, fee, mocker, limit_buy_order_usdt_open, max_open, tradable_balance_ratio, modifier) -> None: patch_RPCManager(mocker) patch_exchange(mocker) @@ -470,7 +470,7 @@ def test_create_trades_multiple_trades(default_conf, ticker, fee, mocker, limit_ mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - create_order=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(return_value=limit_buy_order_usdt_open), get_fee=fee, ) freqtrade = FreqtradeBot(default_conf) @@ -484,14 +484,14 @@ def test_create_trades_multiple_trades(default_conf, ticker, fee, mocker, limit_ assert len(trades) == max(int(max_open * modifier), 0) -def test_create_trades_preopen(default_conf, ticker, fee, mocker, limit_buy_order_open) -> None: +def test_create_trades_preopen(default_conf, ticker, fee, mocker, limit_buy_order_usdt_open) -> None: patch_RPCManager(mocker) patch_exchange(mocker) default_conf['max_open_trades'] = 4 mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - create_order=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(return_value=limit_buy_order_usdt_open), get_fee=fee, ) freqtrade = FreqtradeBot(default_conf) @@ -503,7 +503,7 @@ def test_create_trades_preopen(default_conf, ticker, fee, mocker, limit_buy_orde assert len(Trade.get_open_trades()) == 2 # Change order_id for new orders - limit_buy_order_open['id'] = '123444' + limit_buy_order_usdt_open['id'] = '123444' # Create 2 new trades using create_trades assert freqtrade.create_trade('ETH/BTC') @@ -513,15 +513,15 @@ def test_create_trades_preopen(default_conf, ticker, fee, mocker, limit_buy_orde assert len(trades) == 4 -def test_process_trade_creation(default_conf, ticker, limit_buy_order, limit_buy_order_open, +def test_process_trade_creation(default_conf, ticker, limit_buy_order_usdt, limit_buy_order_usdt_open, fee, mocker, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - create_order=MagicMock(return_value=limit_buy_order_open), - fetch_order=MagicMock(return_value=limit_buy_order), + create_order=MagicMock(return_value=limit_buy_order_usdt_open), + fetch_order=MagicMock(return_value=limit_buy_order_usdt), get_fee=fee, ) freqtrade = FreqtradeBot(default_conf) @@ -584,14 +584,14 @@ def test_process_operational_exception(default_conf, ticker, mocker) -> None: assert 'OperationalException' in msg_mock.call_args_list[-1][0][0]['status'] -def test_process_trade_handling(default_conf, ticker, limit_buy_order_open, fee, mocker) -> None: +def test_process_trade_handling(default_conf, ticker, limit_buy_order_usdt_open, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - create_order=MagicMock(return_value=limit_buy_order_open), - fetch_order=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(return_value=limit_buy_order_usdt_open), + fetch_order=MagicMock(return_value=limit_buy_order_usdt_open), get_fee=fee, ) freqtrade = FreqtradeBot(default_conf) @@ -609,7 +609,7 @@ def test_process_trade_handling(default_conf, ticker, limit_buy_order_open, fee, assert len(trades) == 1 -def test_process_trade_no_whitelist_pair(default_conf, ticker, limit_buy_order, +def test_process_trade_no_whitelist_pair(default_conf, ticker, limit_buy_order_usdt, fee, mocker) -> None: """ Test process with trade not in pair list """ patch_RPCManager(mocker) @@ -617,8 +617,8 @@ def test_process_trade_no_whitelist_pair(default_conf, ticker, limit_buy_order, mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - create_order=MagicMock(return_value={'id': limit_buy_order['id']}), - fetch_order=MagicMock(return_value=limit_buy_order), + create_order=MagicMock(return_value={'id': limit_buy_order_usdt['id']}), + fetch_order=MagicMock(return_value=limit_buy_order_usdt), get_fee=fee, ) freqtrade = FreqtradeBot(default_conf) @@ -690,7 +690,7 @@ def test_process_informative_pairs_added(default_conf, ticker, mocker) -> None: assert ("ETH/BTC", default_conf["timeframe"]) in refresh_mock.call_args[0][0] -def test_execute_entry(mocker, default_conf, fee, limit_buy_order, limit_buy_order_open) -> None: +def test_execute_entry(mocker, default_conf, fee, limit_buy_order_usdt, limit_buy_order_usdt_open) -> None: patch_RPCManager(mocker) patch_exchange(mocker) freqtrade = FreqtradeBot(default_conf) @@ -698,14 +698,14 @@ def test_execute_entry(mocker, default_conf, fee, limit_buy_order, limit_buy_ord stake_amount = 2 bid = 0.11 buy_rate_mock = MagicMock(return_value=bid) - buy_mm = MagicMock(return_value=limit_buy_order_open) + buy_mm = MagicMock(return_value=limit_buy_order_usdt_open) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_rate=buy_rate_mock, fetch_ticker=MagicMock(return_value={ - 'bid': 0.00001172, - 'ask': 0.00001173, - 'last': 0.00001172 + 'bid': 1.9, + 'ask': 2.2, + 'last': 1.9 }), create_order=buy_mm, get_min_pair_stake_amount=MagicMock(return_value=1), @@ -719,7 +719,7 @@ def test_execute_entry(mocker, default_conf, fee, limit_buy_order, limit_buy_ord assert freqtrade.strategy.confirm_trade_entry.call_count == 1 buy_rate_mock.reset_mock() - limit_buy_order_open['id'] = '22' + limit_buy_order_usdt_open['id'] = '22' freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=True) assert freqtrade.execute_entry(pair, stake_amount) assert buy_rate_mock.call_count == 1 @@ -738,7 +738,7 @@ def test_execute_entry(mocker, default_conf, fee, limit_buy_order, limit_buy_ord assert trade.open_order_id == '22' # Test calling with price - limit_buy_order_open['id'] = '33' + limit_buy_order_usdt_open['id'] = '33' fix_price = 0.06 assert freqtrade.execute_entry(pair, stake_amount, fix_price) # Make sure get_rate wasn't called again @@ -751,13 +751,13 @@ def test_execute_entry(mocker, default_conf, fee, limit_buy_order, limit_buy_ord assert call_args['amount'] == round(stake_amount / fix_price, 8) # In case of closed order - limit_buy_order['status'] = 'closed' - limit_buy_order['price'] = 10 - limit_buy_order['cost'] = 100 - limit_buy_order['id'] = '444' + limit_buy_order_usdt['status'] = 'closed' + limit_buy_order_usdt['price'] = 10 + limit_buy_order_usdt['cost'] = 100 + limit_buy_order_usdt['id'] = '444' mocker.patch('freqtrade.exchange.Exchange.create_order', - MagicMock(return_value=limit_buy_order)) + MagicMock(return_value=limit_buy_order_usdt)) assert freqtrade.execute_entry(pair, stake_amount) trade = Trade.query.all()[2] assert trade @@ -766,15 +766,15 @@ def test_execute_entry(mocker, default_conf, fee, limit_buy_order, limit_buy_ord assert trade.stake_amount == 100 # In case of rejected or expired order and partially filled - limit_buy_order['status'] = 'expired' - limit_buy_order['amount'] = 90.99181073 - limit_buy_order['filled'] = 80.99181073 - limit_buy_order['remaining'] = 10.00 - limit_buy_order['price'] = 0.5 - limit_buy_order['cost'] = 40.495905365 - limit_buy_order['id'] = '555' + limit_buy_order_usdt['status'] = 'expired' + limit_buy_order_usdt['amount'] = 30.0 + limit_buy_order_usdt['filled'] = 80.99181073 + limit_buy_order_usdt['remaining'] = 10.00 + limit_buy_order_usdt['price'] = 0.5 + limit_buy_order_usdt['cost'] = 40.495905365 + limit_buy_order_usdt['id'] = '555' mocker.patch('freqtrade.exchange.Exchange.create_order', - MagicMock(return_value=limit_buy_order)) + MagicMock(return_value=limit_buy_order_usdt)) assert freqtrade.execute_entry(pair, stake_amount) trade = Trade.query.all()[3] assert trade @@ -783,8 +783,8 @@ def test_execute_entry(mocker, default_conf, fee, limit_buy_order, limit_buy_ord assert trade.stake_amount == 40.495905365 # Test with custom stake - limit_buy_order['status'] = 'open' - limit_buy_order['id'] = '556' + limit_buy_order_usdt['status'] = 'open' + limit_buy_order_usdt['id'] = '556' freqtrade.strategy.custom_stake_amount = lambda **kwargs: 150.0 assert freqtrade.execute_entry(pair, stake_amount) @@ -793,7 +793,7 @@ def test_execute_entry(mocker, default_conf, fee, limit_buy_order, limit_buy_ord assert trade.stake_amount == 150 # Exception case - limit_buy_order['id'] = '557' + limit_buy_order_usdt['id'] = '557' freqtrade.strategy.custom_stake_amount = lambda **kwargs: 20 / 0 assert freqtrade.execute_entry(pair, stake_amount) trade = Trade.query.all()[5] @@ -801,15 +801,15 @@ def test_execute_entry(mocker, default_conf, fee, limit_buy_order, limit_buy_ord assert trade.stake_amount == 2.0 # In case of the order is rejected and not filled at all - limit_buy_order['status'] = 'rejected' - limit_buy_order['amount'] = 90.99181073 - limit_buy_order['filled'] = 0.0 - limit_buy_order['remaining'] = 90.99181073 - limit_buy_order['price'] = 0.5 - limit_buy_order['cost'] = 0.0 - limit_buy_order['id'] = '66' + limit_buy_order_usdt['status'] = 'rejected' + limit_buy_order_usdt['amount'] = 30.0 + limit_buy_order_usdt['filled'] = 0.0 + limit_buy_order_usdt['remaining'] = 30.0 + limit_buy_order_usdt['price'] = 0.5 + limit_buy_order_usdt['cost'] = 0.0 + limit_buy_order_usdt['id'] = '66' mocker.patch('freqtrade.exchange.Exchange.create_order', - MagicMock(return_value=limit_buy_order)) + MagicMock(return_value=limit_buy_order_usdt)) assert not freqtrade.execute_entry(pair, stake_amount) # Fail to get price... @@ -820,8 +820,8 @@ def test_execute_entry(mocker, default_conf, fee, limit_buy_order, limit_buy_ord # In case of custom entry price mocker.patch('freqtrade.exchange.Exchange.get_rate', return_value=0.50) - limit_buy_order['status'] = 'open' - limit_buy_order['id'] = '5566' + limit_buy_order_usdt['status'] = 'open' + limit_buy_order_usdt['id'] = '5566' freqtrade.strategy.custom_entry_price = lambda **kwargs: 0.508 assert freqtrade.execute_entry(pair, stake_amount) trade = Trade.query.all()[6] @@ -829,8 +829,8 @@ def test_execute_entry(mocker, default_conf, fee, limit_buy_order, limit_buy_ord assert trade.open_rate_requested == 0.508 # In case of custom entry price set to None - limit_buy_order['status'] = 'open' - limit_buy_order['id'] = '5567' + limit_buy_order_usdt['status'] = 'open' + limit_buy_order_usdt['id'] = '5567' freqtrade.strategy.custom_entry_price = lambda **kwargs: None mocker.patch.multiple( @@ -844,8 +844,8 @@ def test_execute_entry(mocker, default_conf, fee, limit_buy_order, limit_buy_ord assert trade.open_rate_requested == 10 # In case of custom entry price not float type - limit_buy_order['status'] = 'open' - limit_buy_order['id'] = '5568' + limit_buy_order_usdt['status'] = 'open' + limit_buy_order_usdt['id'] = '5568' freqtrade.strategy.custom_entry_price = lambda **kwargs: "string price" assert freqtrade.execute_entry(pair, stake_amount) trade = Trade.query.all()[8] @@ -853,16 +853,16 @@ def test_execute_entry(mocker, default_conf, fee, limit_buy_order, limit_buy_ord assert trade.open_rate_requested == 10 -def test_execute_entry_confirm_error(mocker, default_conf, fee, limit_buy_order) -> None: +def test_execute_entry_confirm_error(mocker, default_conf, fee, limit_buy_order_usdt) -> None: freqtrade = get_patched_freqtradebot(mocker, default_conf) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=MagicMock(return_value={ - 'bid': 0.00001172, - 'ask': 0.00001173, - 'last': 0.00001172 + 'bid': 1.9, + 'ask': 2.2, + 'last': 1.9 }), - create_order=MagicMock(return_value=limit_buy_order), + create_order=MagicMock(return_value=limit_buy_order_usdt), get_rate=MagicMock(return_value=0.11), get_min_pair_stake_amount=MagicMock(return_value=1), get_fee=fee, @@ -873,11 +873,11 @@ def test_execute_entry_confirm_error(mocker, default_conf, fee, limit_buy_order) freqtrade.strategy.confirm_trade_entry = MagicMock(side_effect=ValueError) assert freqtrade.execute_entry(pair, stake_amount) - limit_buy_order['id'] = '222' + limit_buy_order_usdt['id'] = '222' freqtrade.strategy.confirm_trade_entry = MagicMock(side_effect=Exception) assert freqtrade.execute_entry(pair, stake_amount) - limit_buy_order['id'] = '2223' + limit_buy_order_usdt['id'] = '2223' freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=True) assert freqtrade.execute_entry(pair, stake_amount) @@ -885,14 +885,14 @@ def test_execute_entry_confirm_error(mocker, default_conf, fee, limit_buy_order) assert not freqtrade.execute_entry(pair, stake_amount) -def test_add_stoploss_on_exchange(mocker, default_conf, limit_buy_order) -> None: +def test_add_stoploss_on_exchange(mocker, default_conf, limit_buy_order_usdt) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True)) - mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order) + mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order_usdt) mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[]) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', - return_value=limit_buy_order['amount']) + return_value=limit_buy_order_usdt['amount']) stoploss = MagicMock(return_value={'id': 13434334}) mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss) @@ -913,20 +913,20 @@ def test_add_stoploss_on_exchange(mocker, default_conf, limit_buy_order) -> None def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, - limit_buy_order, limit_sell_order) -> None: + limit_buy_order_usdt, limit_sell_order_usdt) -> None: stoploss = MagicMock(return_value={'id': 13434334}) patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=MagicMock(return_value={ - 'bid': 0.00001172, - 'ask': 0.00001173, - 'last': 0.00001172 + 'bid': 1.9, + 'ask': 2.2, + 'last': 1.9 }), create_order=MagicMock(side_effect=[ - {'id': limit_buy_order['id']}, - {'id': limit_sell_order['id']}, + {'id': limit_buy_order_usdt['id']}, + {'id': limit_sell_order_usdt['id']}, ]), get_fee=fee, ) @@ -993,7 +993,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, 'type': 'stop_loss_limit', 'price': 3, 'average': 2, - 'amount': limit_buy_order['amount'], + 'amount': limit_buy_order_usdt['amount'], }) mocker.patch('freqtrade.exchange.Binance.fetch_stoploss_order', stoploss_order_hit) assert freqtrade.handle_stoploss_on_exchange(trade) is True @@ -1033,20 +1033,20 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog, - limit_buy_order, limit_sell_order) -> None: + limit_buy_order_usdt, limit_sell_order_usdt) -> None: # Sixth case: stoploss order was cancelled but couldn't create new one patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=MagicMock(return_value={ - 'bid': 0.00001172, - 'ask': 0.00001173, - 'last': 0.00001172 + 'bid': 1.9, + 'ask': 2.2, + 'last': 1.9 }), create_order=MagicMock(side_effect=[ - {'id': limit_buy_order['id']}, - {'id': limit_sell_order['id']}, + {'id': limit_buy_order_usdt['id']}, + {'id': limit_sell_order_usdt['id']}, ]), get_fee=fee, ) @@ -1072,19 +1072,19 @@ def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog, def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, - limit_buy_order_open, limit_sell_order): + limit_buy_order_usdt_open, limit_sell_order_usdt): rpc_mock = patch_RPCManager(mocker) patch_exchange(mocker) create_order_mock = MagicMock(side_effect=[ - limit_buy_order_open, - {'id': limit_sell_order['id']} + limit_buy_order_usdt_open, + {'id': limit_sell_order_usdt['id']} ]) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=MagicMock(return_value={ - 'bid': 0.00001172, - 'ask': 0.00001173, - 'last': 0.00001172 + 'bid': 1.9, + 'ask': 2.2, + 'last': 1.9 }), create_order=create_order_mock, get_fee=fee, @@ -1120,20 +1120,20 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, def test_create_stoploss_order_insufficient_funds(mocker, default_conf, caplog, fee, - limit_buy_order_open, limit_sell_order): - sell_mock = MagicMock(return_value={'id': limit_sell_order['id']}) + limit_buy_order_usdt_open, limit_sell_order_usdt): + sell_mock = MagicMock(return_value={'id': limit_sell_order_usdt['id']}) freqtrade = get_patched_freqtradebot(mocker, default_conf) mock_insuf = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_insufficient_funds') mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=MagicMock(return_value={ - 'bid': 0.00001172, - 'ask': 0.00001173, - 'last': 0.00001172 + 'bid': 1.9, + 'ask': 2.2, + 'last': 1.9 }), create_order=MagicMock(side_effect=[ - limit_buy_order_open, + limit_buy_order_usdt_open, sell_mock, ]), get_fee=fee, @@ -1164,20 +1164,20 @@ def test_create_stoploss_order_insufficient_funds(mocker, default_conf, caplog, @pytest.mark.usefixtures("init_persistence") def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, - limit_buy_order, limit_sell_order) -> None: + limit_buy_order_usdt, limit_sell_order_usdt) -> None: # When trailing stoploss is set stoploss = MagicMock(return_value={'id': 13434334}) patch_RPCManager(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=MagicMock(return_value={ - 'bid': 0.00001172, - 'ask': 0.00001173, - 'last': 0.00001172 + 'bid': 2.19, + 'ask': 2.2, + 'last': 2.19 }), create_order=MagicMock(side_effect=[ - {'id': limit_buy_order['id']}, - {'id': limit_sell_order['id']}, + {'id': limit_buy_order_usdt['id']}, + {'id': limit_sell_order_usdt['id']}, ]), get_fee=fee, ) @@ -1219,7 +1219,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, 'price': 3, 'average': 2, 'info': { - 'stopPrice': '0.000011134' + 'stopPrice': '2.0805' } }) @@ -1230,11 +1230,14 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, assert freqtrade.handle_stoploss_on_exchange(trade) is False # price jumped 2x - mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={ - 'bid': 0.00002344, - 'ask': 0.00002346, - 'last': 0.00002344 - })) + mocker.patch( + 'freqtrade.exchange.Exchange.fetch_ticker', + MagicMock(return_value={ + 'bid': 4.38, + 'ask': 4.4, + 'last': 4.38 + }) + ) cancel_order_mock = MagicMock() stoploss_order_mock = MagicMock(return_value={'id': 13434334}) @@ -1248,7 +1251,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, stoploss_order_mock.assert_not_called() assert freqtrade.handle_trade(trade) is False - assert trade.stop_loss == 0.00002346 * 0.95 + assert trade.stop_loss == 4.4 * 0.95 # setting stoploss_on_exchange_interval to 0 seconds freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 0 @@ -1256,22 +1259,24 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, assert freqtrade.handle_stoploss_on_exchange(trade) is False cancel_order_mock.assert_called_once_with(100, 'ETH/BTC') - stoploss_order_mock.assert_called_once_with(amount=85.32423208, - pair='ETH/BTC', - order_types=freqtrade.strategy.order_types, - stop_price=0.00002346 * 0.95) + stoploss_order_mock.assert_called_once_with( + amount=30.0, + pair='ETH/BTC', + order_types=freqtrade.strategy.order_types, + stop_price=4.4 * 0.95 + ) # price fell below stoploss, so dry-run sells trade. mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={ - 'bid': 0.00002144, - 'ask': 0.00002146, - 'last': 0.00002144 + 'bid': 4.1712, + 'ask': 4.1921, + 'last': 4.1712 })) assert freqtrade.handle_trade(trade) is True def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, caplog, - limit_buy_order, limit_sell_order) -> None: + limit_buy_order_usdt, limit_sell_order_usdt) -> None: # When trailing stoploss is set stoploss = MagicMock(return_value={'id': 13434334}) patch_exchange(mocker) @@ -1279,13 +1284,13 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=MagicMock(return_value={ - 'bid': 0.00001172, - 'ask': 0.00001173, - 'last': 0.00001172 + 'bid': 1.9, + 'ask': 2.2, + 'last': 1.9 }), create_order=MagicMock(side_effect=[ - {'id': limit_buy_order['id']}, - {'id': limit_sell_order['id']}, + {'id': limit_buy_order_usdt['id']}, + {'id': limit_sell_order_usdt['id']}, ]), get_fee=fee, ) @@ -1347,20 +1352,20 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c @pytest.mark.usefixtures("init_persistence") def test_handle_stoploss_on_exchange_custom_stop(mocker, default_conf, fee, - limit_buy_order, limit_sell_order) -> None: + limit_buy_order_usdt, limit_sell_order_usdt) -> None: # When trailing stoploss is set stoploss = MagicMock(return_value={'id': 13434334}) patch_RPCManager(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=MagicMock(return_value={ - 'bid': 0.00001172, - 'ask': 0.00001173, - 'last': 0.00001172 + 'bid': 1.9, + 'ask': 2.2, + 'last': 1.9 }), create_order=MagicMock(side_effect=[ - {'id': limit_buy_order['id']}, - {'id': limit_sell_order['id']}, + {'id': limit_buy_order_usdt['id']}, + {'id': limit_sell_order_usdt['id']}, ]), get_fee=fee, ) @@ -1413,9 +1418,9 @@ def test_handle_stoploss_on_exchange_custom_stop(mocker, default_conf, fee, # price jumped 2x mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={ - 'bid': 0.00002344, - 'ask': 0.00002346, - 'last': 0.00002344 + 'bid': 4.38, + 'ask': 4.4, + 'last': 4.38 })) cancel_order_mock = MagicMock() @@ -1454,7 +1459,7 @@ def test_handle_stoploss_on_exchange_custom_stop(mocker, default_conf, fee, def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, - limit_buy_order, limit_sell_order) -> None: + limit_buy_order_usdt, limit_sell_order_usdt) -> None: # When trailing stoploss is set stoploss = MagicMock(return_value={'id': 13434334}) @@ -1467,13 +1472,13 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=MagicMock(return_value={ - 'bid': 0.00001172, - 'ask': 0.00001173, - 'last': 0.00001172 + 'bid': 1.9, + 'ask': 2.2, + 'last': 1.9 }), create_order=MagicMock(side_effect=[ - {'id': limit_buy_order['id']}, - {'id': limit_sell_order['id']}, + {'id': limit_buy_order_usdt['id']}, + {'id': limit_sell_order_usdt['id']}, ]), get_fee=fee, stoploss=stoploss, @@ -1515,7 +1520,7 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, 'price': 3, 'average': 2, 'info': { - 'stopPrice': '0.000009384' + 'stopPrice': '2.178' } }) @@ -1524,7 +1529,7 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, # stoploss initially at 20% as edge dictated it. assert freqtrade.handle_trade(trade) is False assert freqtrade.handle_stoploss_on_exchange(trade) is False - assert trade.stop_loss == 0.000009384 + assert trade.stop_loss == 2.178 cancel_order_mock = MagicMock() stoploss_order_mock = MagicMock() @@ -1533,16 +1538,16 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, # price goes down 5% mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={ - 'bid': 0.00001172 * 0.95, - 'ask': 0.00001173 * 0.95, - 'last': 0.00001172 * 0.95 + 'bid': 1.9 * 0.95, + 'ask': 2.2 * 0.95, + 'last': 1.9 * 0.95 })) assert freqtrade.handle_trade(trade) is False assert freqtrade.handle_stoploss_on_exchange(trade) is False # stoploss should remain the same - assert trade.stop_loss == 0.000009384 + assert trade.stop_loss == 2.178 # stoploss on exchange should not be canceled cancel_order_mock.assert_not_called() @@ -1589,14 +1594,14 @@ def test_enter_positions(mocker, default_conf, return_value, side_effect, assert mock_ct.call_count == len(default_conf['exchange']['pair_whitelist']) -def test_exit_positions(mocker, default_conf, limit_buy_order, caplog) -> None: +def test_exit_positions(mocker, default_conf, limit_buy_order_usdt, caplog) -> None: freqtrade = get_patched_freqtradebot(mocker, default_conf) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True)) - mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order) + mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order_usdt) mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[]) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', - return_value=limit_buy_order['amount']) + return_value=limit_buy_order_usdt['amount']) trade = MagicMock() trade.open_order_id = '123' @@ -1606,7 +1611,7 @@ def test_exit_positions(mocker, default_conf, limit_buy_order, caplog) -> None: assert n == 0 # Test amount not modified by fee-logic assert not log_has( - 'Applying fee to amount for Trade {} from 90.99181073 to 90.81'.format(trade), caplog + 'Applying fee to amount for Trade {} from 30.0 to 90.81'.format(trade), caplog ) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=90.81) @@ -1615,9 +1620,9 @@ def test_exit_positions(mocker, default_conf, limit_buy_order, caplog) -> None: assert n == 0 -def test_exit_positions_exception(mocker, default_conf, limit_buy_order, caplog) -> None: +def test_exit_positions_exception(mocker, default_conf, limit_buy_order_usdt, caplog) -> None: freqtrade = get_patched_freqtradebot(mocker, default_conf) - mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order) + mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order_usdt) trade = MagicMock() trade.open_order_id = None @@ -1635,14 +1640,14 @@ def test_exit_positions_exception(mocker, default_conf, limit_buy_order, caplog) assert log_has('Unable to sell trade ETH/BTC: ', 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_usdt, caplog) -> None: freqtrade = get_patched_freqtradebot(mocker, default_conf) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True)) - mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order) + mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order_usdt) mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[]) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', - return_value=limit_buy_order['amount']) + return_value=limit_buy_order_usdt['amount']) trade = Trade( open_order_id=123, @@ -1662,7 +1667,7 @@ def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> No assert not log_has_re(r'Applying fee to .*', caplog) caplog.clear() assert trade.open_order_id is None - assert trade.amount == limit_buy_order['amount'] + assert trade.amount == limit_buy_order_usdt['amount'] trade.open_order_id = '123' mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=90.81) @@ -1681,10 +1686,10 @@ def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> No @pytest.mark.parametrize('initial_amount,has_rounding_fee', [ - (90.99181073 + 1e-14, True), + (30.0 + 1e-14, True), (8.0, False) ]) -def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_buy_order, fee, +def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_buy_order_usdt, fee, mocker, initial_amount, has_rounding_fee, caplog): trades_for_order[0]['amount'] = initial_amount mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order) @@ -1704,17 +1709,17 @@ def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_ open_order_id="123456", is_open=True, ) - freqtrade.update_trade_state(trade, '123456', limit_buy_order) + freqtrade.update_trade_state(trade, '123456', limit_buy_order_usdt) assert trade.amount != amount - assert trade.amount == limit_buy_order['amount'] + assert trade.amount == limit_buy_order_usdt['amount'] if has_rounding_fee: assert log_has_re(r'Applying fee on amount for .*', caplog) def test_update_trade_state_exception(mocker, default_conf, - limit_buy_order, caplog) -> None: + limit_buy_order_usdt, caplog) -> None: freqtrade = get_patched_freqtradebot(mocker, default_conf) - mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order) + mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order_usdt) trade = MagicMock() trade.open_order_id = '123' @@ -1745,8 +1750,8 @@ def test_update_trade_state_orderexception(mocker, default_conf, caplog) -> None 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_open, - limit_sell_order, mocker): +def test_update_trade_state_sell(default_conf, trades_for_order, limit_sell_order_usdt_open, + limit_sell_order_usdt, mocker): mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order) # fetch_order should not be called!! mocker.patch('freqtrade.exchange.Exchange.fetch_order', MagicMock(side_effect=ValueError)) @@ -1754,7 +1759,7 @@ def test_update_trade_state_sell(default_conf, trades_for_order, limit_sell_orde mocker.patch('freqtrade.wallets.Wallets.update', wallet_mock) patch_exchange(mocker) - amount = limit_sell_order["amount"] + amount = limit_sell_order_usdt["amount"] freqtrade = get_patched_freqtradebot(mocker, default_conf) wallet_mock.reset_mock() trade = Trade( @@ -1768,11 +1773,11 @@ def test_update_trade_state_sell(default_conf, trades_for_order, limit_sell_orde open_order_id="123456", is_open=True, ) - order = Order.parse_from_ccxt_object(limit_sell_order_open, 'LTC/ETH', 'sell') + order = Order.parse_from_ccxt_object(limit_sell_order_usdt_open, 'LTC/ETH', 'sell') trade.orders.append(order) assert order.status == 'open' - freqtrade.update_trade_state(trade, trade.open_order_id, limit_sell_order) - assert trade.amount == limit_sell_order['amount'] + freqtrade.update_trade_state(trade, trade.open_order_id, limit_sell_order_usdt) + assert trade.amount == limit_sell_order_usdt['amount'] # Wallet needs to be updated after closing a limit-sell order to reenable buying assert wallet_mock.call_count == 1 assert not trade.is_open @@ -1780,20 +1785,20 @@ def test_update_trade_state_sell(default_conf, trades_for_order, limit_sell_orde assert order.status == 'closed' -def test_handle_trade(default_conf, limit_buy_order, limit_sell_order_open, limit_sell_order, +def test_handle_trade(default_conf, limit_buy_order_usdt, limit_sell_order_usdt_open, limit_sell_order_usdt, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=MagicMock(return_value={ - 'bid': 0.00001172, - 'ask': 0.00001173, - 'last': 0.00001172 + 'bid': 1.9, + 'ask': 2.2, + 'last': 1.9 }), create_order=MagicMock(side_effect=[ - limit_buy_order, - limit_sell_order_open, + limit_buy_order_usdt, + limit_sell_order_usdt_open, ]), get_fee=fee, ) @@ -1806,24 +1811,24 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order_open, limi assert trade time.sleep(0.01) # Race condition fix - trade.update(limit_buy_order) + trade.update(limit_buy_order_usdt) assert trade.is_open is True freqtrade.wallets.update() patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) is True - assert trade.open_order_id == limit_sell_order['id'] + assert trade.open_order_id == limit_sell_order_usdt['id'] # Simulate fulfilled LIMIT_SELL order for trade - trade.update(limit_sell_order) + trade.update(limit_sell_order_usdt) - assert trade.close_rate == 0.00001173 - assert trade.close_profit == 0.06201058 - assert trade.calc_profit() == 0.00006217 + assert trade.close_rate == 2.2 + assert trade.close_profit == 0.09451372 + assert trade.calc_profit() == 5.685 assert trade.close_date is not None -def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, +def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_usdt_open, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) @@ -1831,7 +1836,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, 'freqtrade.exchange.Exchange', fetch_ticker=ticker, create_order=MagicMock(side_effect=[ - limit_buy_order_open, + limit_buy_order_usdt_open, {'id': 1234553382}, ]), get_fee=fee, @@ -1878,7 +1883,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, assert freqtrade.handle_trade(trades[0]) is True -def test_handle_trade_roi(default_conf, ticker, limit_buy_order_open, +def test_handle_trade_roi(default_conf, ticker, limit_buy_order_usdt_open, fee, mocker, caplog) -> None: caplog.set_level(logging.DEBUG) @@ -1887,7 +1892,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order_open, 'freqtrade.exchange.Exchange', fetch_ticker=ticker, create_order=MagicMock(side_effect=[ - limit_buy_order_open, + limit_buy_order_usdt_open, {'id': 1234553382}, ]), get_fee=fee, @@ -1913,8 +1918,8 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order_open, caplog) -def test_handle_trade_use_sell_signal(default_conf, ticker, limit_buy_order_open, - limit_sell_order_open, fee, mocker, caplog) -> None: +def test_handle_trade_use_sell_signal(default_conf, ticker, limit_buy_order_usdt_open, + limit_sell_order_usdt_open, fee, mocker, caplog) -> None: # use_sell_signal is True buy default caplog.set_level(logging.DEBUG) patch_RPCManager(mocker) @@ -1922,8 +1927,8 @@ def test_handle_trade_use_sell_signal(default_conf, ticker, limit_buy_order_open 'freqtrade.exchange.Exchange', fetch_ticker=ticker, create_order=MagicMock(side_effect=[ - limit_buy_order_open, - limit_sell_order_open, + limit_buy_order_usdt_open, + limit_sell_order_usdt_open, ]), get_fee=fee, ) @@ -1945,14 +1950,14 @@ def test_handle_trade_use_sell_signal(default_conf, ticker, limit_buy_order_open caplog) -def test_close_trade(default_conf, ticker, limit_buy_order, limit_buy_order_open, limit_sell_order, +def test_close_trade(default_conf, ticker, limit_buy_order_usdt, limit_buy_order_usdt_open, limit_sell_order_usdt, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - create_order=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(return_value=limit_buy_order_usdt_open), get_fee=fee, ) freqtrade = FreqtradeBot(default_conf) @@ -1964,8 +1969,8 @@ def test_close_trade(default_conf, ticker, limit_buy_order, limit_buy_order_open trade = Trade.query.first() assert trade - trade.update(limit_buy_order) - trade.update(limit_sell_order) + trade.update(limit_buy_order_usdt) + trade.update(limit_sell_order_usdt) assert trade.is_open is False with pytest.raises(DependencyException, match=r'.*closed trade.*'): @@ -2341,7 +2346,7 @@ def test_check_handle_timedout_partial_except(default_conf, ticker, open_trade, assert trades[0].fee_open == fee() -def test_check_handle_timedout_exception(default_conf, ticker, open_trade, mocker, caplog) -> None: +def test_check_handle_timedout_exception(default_conf, ticker, open_trade_usdt, mocker, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) cancel_order_mock = MagicMock() @@ -2359,20 +2364,20 @@ def test_check_handle_timedout_exception(default_conf, ticker, open_trade, mocke ) freqtrade = FreqtradeBot(default_conf) - Trade.query.session.add(open_trade) + Trade.query.session.add(open_trade_usdt) freqtrade.check_handle_timedout() - 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=" - f"{open_trade.open_date.strftime('%Y-%m-%d %H:%M:%S')}" + assert log_has_re(r"Cannot query order for Trade\(id=1, pair=ADA/USDT, amount=30.00000000, " + r"open_rate=2.00000000, open_since=" + f"{open_trade_usdt.open_date.strftime('%Y-%m-%d %H:%M:%S')}" r"\) due to Traceback \(most recent call last\):\n*", caplog) -def test_handle_cancel_enter(mocker, caplog, default_conf, limit_buy_order) -> None: +def test_handle_cancel_enter(mocker, caplog, default_conf, limit_buy_order_usdt) -> None: patch_RPCManager(mocker) patch_exchange(mocker) - cancel_buy_order = deepcopy(limit_buy_order) + cancel_buy_order = deepcopy(limit_buy_order_usdt) cancel_buy_order['status'] = 'canceled' del cancel_buy_order['filled'] @@ -2385,30 +2390,30 @@ def test_handle_cancel_enter(mocker, caplog, default_conf, limit_buy_order) -> N trade = MagicMock() trade.pair = 'LTC/USDT' trade.open_rate = 200 - limit_buy_order['filled'] = 0.0 - limit_buy_order['status'] = 'open' + limit_buy_order_usdt['filled'] = 0.0 + limit_buy_order_usdt['status'] = 'open' reason = CANCEL_REASON['TIMEOUT'] - assert freqtrade.handle_cancel_enter(trade, limit_buy_order, reason) + assert freqtrade.handle_cancel_enter(trade, limit_buy_order_usdt, reason) assert cancel_order_mock.call_count == 1 cancel_order_mock.reset_mock() caplog.clear() - limit_buy_order['filled'] = 0.01 - assert not freqtrade.handle_cancel_enter(trade, limit_buy_order, reason) + limit_buy_order_usdt['filled'] = 0.01 + assert not freqtrade.handle_cancel_enter(trade, limit_buy_order_usdt, reason) assert cancel_order_mock.call_count == 0 assert log_has_re("Order .* for .* not cancelled, as the filled amount.* unsellable.*", caplog) caplog.clear() cancel_order_mock.reset_mock() - limit_buy_order['filled'] = 2 - assert not freqtrade.handle_cancel_enter(trade, limit_buy_order, reason) + limit_buy_order_usdt['filled'] = 2 + assert not freqtrade.handle_cancel_enter(trade, limit_buy_order_usdt, reason) assert cancel_order_mock.call_count == 1 # Order remained open for some reason (cancel failed) cancel_buy_order['status'] = 'open' cancel_order_mock = MagicMock(return_value=cancel_buy_order) mocker.patch('freqtrade.exchange.Exchange.cancel_order_with_result', cancel_order_mock) - assert not freqtrade.handle_cancel_enter(trade, limit_buy_order, reason) + assert not freqtrade.handle_cancel_enter(trade, limit_buy_order_usdt, reason) assert log_has_re(r"Order .* for .* not cancelled.", caplog) @@ -2439,7 +2444,7 @@ def test_handle_cancel_enter_exchanges(mocker, caplog, default_conf, 'String Return value', 123 ]) -def test_handle_cancel_enter_corder_empty(mocker, default_conf, limit_buy_order, +def test_handle_cancel_enter_corder_empty(mocker, default_conf, limit_buy_order_usdt, cancelorder) -> None: patch_RPCManager(mocker) patch_exchange(mocker) @@ -2455,15 +2460,15 @@ def test_handle_cancel_enter_corder_empty(mocker, default_conf, limit_buy_order, trade = MagicMock() trade.pair = 'LTC/USDT' trade.open_rate = 200 - limit_buy_order['filled'] = 0.0 - limit_buy_order['status'] = 'open' + limit_buy_order_usdt['filled'] = 0.0 + limit_buy_order_usdt['status'] = 'open' reason = CANCEL_REASON['TIMEOUT'] - assert freqtrade.handle_cancel_enter(trade, limit_buy_order, reason) + assert freqtrade.handle_cancel_enter(trade, limit_buy_order_usdt, reason) assert cancel_order_mock.call_count == 1 cancel_order_mock.reset_mock() - limit_buy_order['filled'] = 1.0 - assert not freqtrade.handle_cancel_enter(trade, limit_buy_order, reason) + limit_buy_order_usdt['filled'] = 1.0 + assert not freqtrade.handle_cancel_enter(trade, limit_buy_order_usdt, reason) assert cancel_order_mock.call_count == 1 @@ -3021,7 +3026,7 @@ def test_execute_trade_exit_insufficient_funds_error(default_conf, ticker, fee, @pytest.mark.parametrize('profit_only,bid,ask,handle_first,handle_second,sell_type', [ # Enable profit - (True, 0.00001172, 0.00001173, False, True, SellType.SELL_SIGNAL.value), + (True, 1.9, 2.2, False, True, SellType.SELL_SIGNAL.value), # Disable profit (False, 0.00002172, 0.00002173, True, False, SellType.SELL_SIGNAL.value), # Enable loss @@ -3031,7 +3036,7 @@ def test_execute_trade_exit_insufficient_funds_error(default_conf, ticker, fee, (False, 0.00000172, 0.00000173, True, False, SellType.SELL_SIGNAL.value), ]) def test_sell_profit_only( - default_conf, limit_buy_order, limit_buy_order_open, + default_conf, limit_buy_order_usdt, limit_buy_order_usdt_open, fee, mocker, profit_only, bid, ask, handle_first, handle_second, sell_type) -> None: patch_RPCManager(mocker) patch_exchange(mocker) @@ -3043,7 +3048,7 @@ def test_sell_profit_only( 'last': bid }), create_order=MagicMock(side_effect=[ - limit_buy_order_open, + limit_buy_order_usdt_open, {'id': 1234553382}, ]), get_fee=fee, @@ -3063,7 +3068,7 @@ def test_sell_profit_only( freqtrade.enter_positions() trade = Trade.query.first() - trade.update(limit_buy_order) + trade.update(limit_buy_order_usdt) freqtrade.wallets.update() patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) is handle_first @@ -3072,10 +3077,8 @@ def test_sell_profit_only( freqtrade.strategy.sell_profit_offset = 0.0 assert freqtrade.handle_trade(trade) is True - assert trade.sell_reason == sell_type - -def test_sell_not_enough_balance(default_conf, limit_buy_order, limit_buy_order_open, +def test_sell_not_enough_balance(default_conf, limit_buy_order_usdt, limit_buy_order_usdt_open, fee, mocker, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) @@ -3087,7 +3090,7 @@ def test_sell_not_enough_balance(default_conf, limit_buy_order, limit_buy_order_ 'last': 0.00002172 }), create_order=MagicMock(side_effect=[ - limit_buy_order_open, + limit_buy_order_usdt_open, {'id': 1234553382}, ]), get_fee=fee, @@ -3101,7 +3104,7 @@ def test_sell_not_enough_balance(default_conf, limit_buy_order, limit_buy_order_ trade = Trade.query.first() amnt = trade.amount - trade.update(limit_buy_order) + trade.update(limit_buy_order_usdt) patch_get_signal(freqtrade, value=(False, True, None)) mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=trade.amount * 0.985)) @@ -3182,7 +3185,7 @@ def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplo assert log_has_re(f"Pair {trade.pair} is still locked.*", caplog) -def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_buy_order_open, +def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order_usdt, limit_buy_order_usdt_open, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) @@ -3194,7 +3197,7 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_buy_order 'last': 0.0000172 }), create_order=MagicMock(side_effect=[ - limit_buy_order_open, + limit_buy_order_usdt_open, {'id': 1234553382}, ]), get_fee=fee, @@ -3208,7 +3211,7 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_buy_order freqtrade.enter_positions() trade = Trade.query.first() - trade.update(limit_buy_order) + trade.update(limit_buy_order_usdt) freqtrade.wallets.update() patch_get_signal(freqtrade, value=(True, True, None)) assert freqtrade.handle_trade(trade) is False @@ -3219,19 +3222,19 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_buy_order assert trade.sell_reason == SellType.ROI.value -def test_trailing_stop_loss(default_conf, limit_buy_order_open, limit_buy_order, +def test_trailing_stop_loss(default_conf, limit_buy_order_usdt_open, limit_buy_order_usdt, fee, caplog, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=MagicMock(return_value={ - 'bid': 0.00001099, - 'ask': 0.00001099, - 'last': 0.00001099 + 'bid': 2.0, + 'ask': 2.0, + 'last': 2.0 }), create_order=MagicMock(side_effect=[ - limit_buy_order_open, + limit_buy_order_usdt_open, {'id': 1234553382}, ]), get_fee=fee, @@ -3249,9 +3252,9 @@ def test_trailing_stop_loss(default_conf, limit_buy_order_open, limit_buy_order, # Raise ticker above buy price mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={ - 'bid': 0.00001099 * 1.5, - 'ask': 0.00001099 * 1.5, - 'last': 0.00001099 * 1.5 + 'bid': 2.0 * 1.5, + 'ask': 2.0 * 1.5, + 'last': 2.0 * 1.5 })) # Stoploss should be adjusted @@ -3260,9 +3263,9 @@ def test_trailing_stop_loss(default_conf, limit_buy_order_open, limit_buy_order, # Price fell mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={ - 'bid': 0.00001099 * 1.1, - 'ask': 0.00001099 * 1.1, - 'last': 0.00001099 * 1.1 + 'bid': 2.0 * 1.1, + 'ask': 2.0 * 1.1, + 'last': 2.0 * 1.1 })) caplog.set_level(logging.DEBUG) @@ -3273,9 +3276,9 @@ def test_trailing_stop_loss(default_conf, limit_buy_order_open, limit_buy_order, assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value -def test_trailing_stop_loss_positive(default_conf, limit_buy_order, limit_buy_order_open, fee, +def test_trailing_stop_loss_positive(default_conf, limit_buy_order_usdt, limit_buy_order_usdt_open, fee, caplog, mocker) -> None: - buy_price = limit_buy_order['price'] + buy_price = limit_buy_order_usdt['price'] patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -3286,7 +3289,7 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, limit_buy_or 'last': buy_price - 0.000001 }), create_order=MagicMock(side_effect=[ - limit_buy_order_open, + limit_buy_order_usdt_open, {'id': 1234553382}, ]), get_fee=fee, @@ -3301,7 +3304,7 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, limit_buy_or freqtrade.enter_positions() trade = Trade.query.first() - trade.update(limit_buy_order) + trade.update(limit_buy_order_usdt) caplog.set_level(logging.DEBUG) # stop-loss not reached assert freqtrade.handle_trade(trade) is False @@ -3334,9 +3337,9 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, limit_buy_or f"initial stoploss was at 0.000010, trade opened at 0.000011", caplog) -def test_trailing_stop_loss_offset(default_conf, limit_buy_order, limit_buy_order_open, fee, +def test_trailing_stop_loss_offset(default_conf, limit_buy_order_usdt, limit_buy_order_usdt_open, fee, caplog, mocker) -> None: - buy_price = limit_buy_order['price'] + buy_price = limit_buy_order_usdt['price'] patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -3347,7 +3350,7 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, limit_buy_orde 'last': buy_price - 0.000001 }), create_order=MagicMock(side_effect=[ - limit_buy_order_open, + limit_buy_order_usdt_open, {'id': 1234553382}, ]), get_fee=fee, @@ -3362,7 +3365,7 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, limit_buy_orde freqtrade.enter_positions() trade = Trade.query.first() - trade.update(limit_buy_order) + trade.update(limit_buy_order_usdt) caplog.set_level(logging.DEBUG) # stop-loss not reached assert freqtrade.handle_trade(trade) is False @@ -3396,10 +3399,10 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, limit_buy_orde assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value -def test_tsl_only_offset_reached(default_conf, limit_buy_order, limit_buy_order_open, fee, +def test_tsl_only_offset_reached(default_conf, limit_buy_order_usdt, limit_buy_order_usdt_open, fee, caplog, mocker) -> None: - buy_price = limit_buy_order['price'] - # buy_price: 0.00001099 + buy_price = limit_buy_order_usdt['price'] + # buy_price: 2.0 patch_RPCManager(mocker) patch_exchange(mocker) @@ -3410,7 +3413,7 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, limit_buy_order_ 'ask': buy_price, 'last': buy_price }), - create_order=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(return_value=limit_buy_order_usdt_open), get_fee=fee, ) patch_whitelist(mocker, default_conf) @@ -3425,11 +3428,11 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, limit_buy_order_ freqtrade.enter_positions() trade = Trade.query.first() - trade.update(limit_buy_order) + trade.update(limit_buy_order_usdt) caplog.set_level(logging.DEBUG) # stop-loss not reached assert freqtrade.handle_trade(trade) is False - assert trade.stop_loss == 0.0000098910 + assert trade.stop_loss == 1.8 # Raise ticker above buy price mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', @@ -3443,7 +3446,7 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, limit_buy_order_ assert freqtrade.handle_trade(trade) is False assert not log_has("ETH/BTC - Adjusting stoploss...", caplog) - assert trade.stop_loss == 0.0000098910 + assert trade.stop_loss == 1.8 caplog.clear() # price rises above the offset (rises 12% when the offset is 5.5%) @@ -3460,7 +3463,7 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, limit_buy_order_ assert trade.stop_loss == 0.0000117705 -def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_buy_order_open, +def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order_usdt, limit_buy_order_usdt_open, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) @@ -3472,7 +3475,7 @@ def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_b 'last': 0.00000172 }), create_order=MagicMock(side_effect=[ - limit_buy_order_open, + limit_buy_order_usdt_open, {'id': 1234553382}, {'id': 1234553383} ]), @@ -3489,7 +3492,7 @@ def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_b freqtrade.enter_positions() trade = Trade.query.first() - trade.update(limit_buy_order) + trade.update(limit_buy_order_usdt) # Sell due to min_roi_reached patch_get_signal(freqtrade, value=(True, True, None)) assert freqtrade.handle_trade(trade) is True @@ -3678,8 +3681,8 @@ def test_get_real_amount_multi( def test_get_real_amount_invalid_order(default_conf, trades_for_order, buy_order_fee, fee, mocker): - limit_buy_order = deepcopy(buy_order_fee) - limit_buy_order['fee'] = {'cost': 0.004} + limit_buy_order_usdt = deepcopy(buy_order_fee) + limit_buy_order_usdt['fee'] = {'cost': 0.004} mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[]) amount = float(sum(x['amount'] for x in trades_for_order)) @@ -3695,12 +3698,12 @@ def test_get_real_amount_invalid_order(default_conf, trades_for_order, buy_order freqtrade = get_patched_freqtradebot(mocker, default_conf) # Amount does not change - assert freqtrade.get_real_amount(trade, limit_buy_order) == amount + assert freqtrade.get_real_amount(trade, limit_buy_order_usdt) == amount def test_get_real_amount_wrong_amount(default_conf, trades_for_order, buy_order_fee, fee, mocker): - limit_buy_order = deepcopy(buy_order_fee) - limit_buy_order['amount'] = limit_buy_order['amount'] - 0.001 + limit_buy_order_usdt = deepcopy(buy_order_fee) + limit_buy_order_usdt['amount'] = limit_buy_order_usdt['amount'] - 0.001 mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order) amount = float(sum(x['amount'] for x in trades_for_order)) @@ -3717,13 +3720,13 @@ def test_get_real_amount_wrong_amount(default_conf, trades_for_order, buy_order_ # Amount does not change with pytest.raises(DependencyException, match=r"Half bought\? Amounts don't match"): - freqtrade.get_real_amount(trade, limit_buy_order) + freqtrade.get_real_amount(trade, limit_buy_order_usdt) def test_get_real_amount_wrong_amount_rounding(default_conf, trades_for_order, buy_order_fee, fee, mocker): # Floats should not be compared directly. - limit_buy_order = deepcopy(buy_order_fee) + limit_buy_order_usdt = deepcopy(buy_order_fee) trades_for_order[0]['amount'] = trades_for_order[0]['amount'] + 1e-15 mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order) @@ -3740,7 +3743,7 @@ def test_get_real_amount_wrong_amount_rounding(default_conf, trades_for_order, b freqtrade = get_patched_freqtradebot(mocker, default_conf) # Amount changes by fee amount. - assert isclose(freqtrade.get_real_amount(trade, limit_buy_order), amount - (amount * 0.001), + assert isclose(freqtrade.get_real_amount(trade, limit_buy_order_usdt), amount - (amount * 0.001), abs_tol=MATH_CLOSE_PREC,) @@ -3798,7 +3801,7 @@ def test_apply_fee_conditional(default_conf, fee, caplog, mocker, (0.1, False), (100, True), ]) -def test_order_book_depth_of_market(default_conf, ticker, limit_buy_order_open, limit_buy_order, +def test_order_book_depth_of_market(default_conf, ticker, limit_buy_order_usdt_open, limit_buy_order_usdt, fee, mocker, order_book_l2, delta, is_high_delta): default_conf['bid_strategy']['check_depth_of_market']['enabled'] = True default_conf['bid_strategy']['check_depth_of_market']['bids_to_ask_delta'] = delta @@ -3808,7 +3811,7 @@ def test_order_book_depth_of_market(default_conf, ticker, limit_buy_order_open, mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - create_order=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(return_value=limit_buy_order_usdt_open), get_fee=fee, ) @@ -3831,9 +3834,9 @@ def test_order_book_depth_of_market(default_conf, ticker, limit_buy_order_open, assert len(Trade.query.all()) == 1 # Simulate fulfilled LIMIT_BUY order for trade - trade.update(limit_buy_order) + trade.update(limit_buy_order_usdt) - assert trade.open_rate == 0.00001099 + assert trade.open_rate == 2.0 assert whitelist == default_conf['exchange']['pair_whitelist'] @@ -3890,8 +3893,8 @@ def test_check_depth_of_market_buy(default_conf, mocker, order_book_l2) -> None: assert freqtrade._check_depth_of_market_buy('ETH/BTC', conf) is False -def test_order_book_ask_strategy(default_conf, limit_buy_order_open, limit_buy_order, fee, - limit_sell_order_open, mocker, order_book_l2, caplog) -> None: +def test_order_book_ask_strategy(default_conf, limit_buy_order_usdt_open, limit_buy_order_usdt, fee, + limit_sell_order_usdt_open, mocker, order_book_l2, caplog) -> None: """ test order book ask strategy """ @@ -3905,13 +3908,13 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order_open, limit_buy_o mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=MagicMock(return_value={ - 'bid': 0.00001172, - 'ask': 0.00001173, - 'last': 0.00001172 + 'bid': 1.9, + 'ask': 2.2, + 'last': 1.9 }), create_order=MagicMock(side_effect=[ - limit_buy_order_open, - limit_sell_order_open, + limit_buy_order_usdt_open, + limit_sell_order_usdt_open, ]), get_fee=fee, ) @@ -3924,7 +3927,7 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order_open, limit_buy_o assert trade time.sleep(0.01) # Race condition fix - trade.update(limit_buy_order) + trade.update(limit_buy_order_usdt) freqtrade.wallets.update() assert trade.is_open is True @@ -3967,7 +3970,7 @@ def test_startup_trade_reinit(default_conf, edge_conf, mocker): @pytest.mark.usefixtures("init_persistence") -def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order_open, caplog): +def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order_usdt_open, caplog): default_conf['dry_run'] = True # Initialize to 2 times stake amount default_conf['dry_run_wallet'] = 0.002 @@ -3977,7 +3980,7 @@ def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order_ mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - create_order=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(return_value=limit_buy_order_usdt_open), get_fee=fee, ) @@ -3999,11 +4002,11 @@ def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order_ @pytest.mark.usefixtures("init_persistence") -def test_cancel_all_open_orders(mocker, default_conf, fee, limit_buy_order, limit_sell_order): +def test_cancel_all_open_orders(mocker, default_conf, fee, limit_buy_order_usdt, limit_sell_order_usdt): default_conf['cancel_open_orders_on_exit'] = True mocker.patch('freqtrade.exchange.Exchange.fetch_order', side_effect=[ - ExchangeError(), limit_sell_order, limit_buy_order, limit_sell_order]) + ExchangeError(), limit_sell_order_usdt, limit_buy_order_usdt, limit_sell_order_usdt]) buy_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_enter') sell_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_exit') From 8d7f75c4de748f9d0784dc20b276b97d19e4a979 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Fri, 17 Sep 2021 22:18:14 -0600 Subject: [PATCH 018/144] Fixed a bunch of freqtradebot tests --- tests/conftest.py | 28 +--- tests/test_freqtradebot.py | 266 +++++++++++++------------------------ 2 files changed, 94 insertions(+), 200 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index c1032a215..adb344ffc 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -338,8 +338,7 @@ def get_default_conf(testdatadir): "ETH/BTC", "LTC/BTC", "XRP/BTC", - "NEO/BTC", - "ADA/USDT" + "NEO/BTC" ], "pair_blacklist": [ "DOGE/BTC", @@ -469,31 +468,6 @@ def get_markets(): }, 'info': {}, }, - 'ADA/USDT': { - 'id': 'ethbtc', - 'symbol': 'ADA/USDT', - 'base': 'USDT', - 'quote': 'ADA', - 'active': True, - 'precision': { - 'price': 8, - 'amount': 8, - 'cost': 8, - }, - 'lot': 0.00000001, - 'limits': { - 'amount': { - 'min': 0.01, - 'max': 1000, - }, - 'price': 500000, - 'cost': { - 'min': 0.0001, - 'max': 500000, - }, - }, - 'info': {}, - }, 'TKN/BTC': { 'id': 'tknbtc', 'symbol': 'TKN/BTC', diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 7ddb90657..c030aca0f 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1267,11 +1267,14 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, ) # price fell below stoploss, so dry-run sells trade. - mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={ - 'bid': 4.1712, - 'ask': 4.1921, - 'last': 4.1712 - })) + mocker.patch( + 'freqtrade.exchange.Exchange.fetch_ticker', + MagicMock(return_value={ + 'bid': 4.1712, + 'ask': 4.1921, + 'last': 4.1712 + }) + ) assert freqtrade.handle_trade(trade) is True @@ -1407,7 +1410,7 @@ def test_handle_stoploss_on_exchange_custom_stop(mocker, default_conf, fee, 'price': 3, 'average': 2, 'info': { - 'stopPrice': '0.000011134' + 'stopPrice': '2.0805' } }) @@ -1417,11 +1420,14 @@ def test_handle_stoploss_on_exchange_custom_stop(mocker, default_conf, fee, assert freqtrade.handle_stoploss_on_exchange(trade) is False # price jumped 2x - mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={ - 'bid': 4.38, - 'ask': 4.4, - 'last': 4.38 - })) + mocker.patch( + 'freqtrade.exchange.Exchange.fetch_ticker', + MagicMock(return_value={ + 'bid': 4.38, + 'ask': 4.4, + 'last': 4.38 + }) + ) cancel_order_mock = MagicMock() stoploss_order_mock = MagicMock(return_value={'id': 13434334}) @@ -1435,7 +1441,7 @@ def test_handle_stoploss_on_exchange_custom_stop(mocker, default_conf, fee, stoploss_order_mock.assert_not_called() assert freqtrade.handle_trade(trade) is False - assert trade.stop_loss == 0.00002346 * 0.96 + assert trade.stop_loss == 4.4 * 0.96 assert trade.stop_loss_pct == -0.04 # setting stoploss_on_exchange_interval to 0 seconds @@ -1444,17 +1450,22 @@ def test_handle_stoploss_on_exchange_custom_stop(mocker, default_conf, fee, assert freqtrade.handle_stoploss_on_exchange(trade) is False cancel_order_mock.assert_called_once_with(100, 'ETH/BTC') - stoploss_order_mock.assert_called_once_with(amount=85.32423208, - pair='ETH/BTC', - order_types=freqtrade.strategy.order_types, - stop_price=0.00002346 * 0.96) + stoploss_order_mock.assert_called_once_with( + amount=100.0, + pair='ETH/BTC', + order_types=freqtrade.strategy.order_types, + stop_price=4.4 * 0.96 + ) # price fell below stoploss, so dry-run sells trade. - mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={ - 'bid': 0.00002144, - 'ask': 0.00002146, - 'last': 0.00002144 - })) + mocker.patch( + 'freqtrade.exchange.Exchange.fetch_ticker', + MagicMock(return_value={ + 'bid': 4.1712, + 'ask': 4.1921, + 'last': 4.1712 + }) + ) assert freqtrade.handle_trade(trade) is True @@ -3192,9 +3203,9 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order_usdt, limit_buy_ mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=MagicMock(return_value={ - 'bid': 0.0000172, - 'ask': 0.0000173, - 'last': 0.0000172 + 'bid': 2.19, + 'ask': 2.2, + 'last': 2.19 }), create_order=MagicMock(side_effect=[ limit_buy_order_usdt_open, @@ -3259,7 +3270,7 @@ def test_trailing_stop_loss(default_conf, limit_buy_order_usdt_open, limit_buy_o # Stoploss should be adjusted assert freqtrade.handle_trade(trade) is False - + caplog.clear() # Price fell mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={ @@ -3271,22 +3282,28 @@ def test_trailing_stop_loss(default_conf, limit_buy_order_usdt_open, limit_buy_o caplog.set_level(logging.DEBUG) # Sell as trailing-stop is reached assert freqtrade.handle_trade(trade) is True - assert log_has("ETH/BTC - HIT STOP: current price at 0.000012, stoploss is 0.000015, " - "initial stoploss was at 0.000010, trade opened at 0.000011", caplog) + # TODO: Does this make sense? How is stoploss 2.7? + assert log_has("ETH/BTC - HIT STOP: current price at 2.200000, stoploss is 2.700000, " + "initial stoploss was at 1.800000, trade opened at 2.000000", caplog) assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value -def test_trailing_stop_loss_positive(default_conf, limit_buy_order_usdt, limit_buy_order_usdt_open, fee, - caplog, mocker) -> None: +@pytest.mark.parametrize('offset,trail_if_reached,second_sl', [ + (0, False, 2.0394), + (0.011, False, 2.0394), + (0.055, True, 1.8), +]) +def test_trailing_stop_loss_positive(default_conf, limit_buy_order_usdt, limit_buy_order_usdt_open, + offset, fee, caplog, mocker, trail_if_reached, second_sl) -> None: buy_price = limit_buy_order_usdt['price'] patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=MagicMock(return_value={ - 'bid': buy_price - 0.000001, - 'ask': buy_price - 0.000001, - 'last': buy_price - 0.000001 + 'bid': buy_price - 0.01, + 'ask': buy_price - 0.01, + 'last': buy_price - 0.01 }), create_order=MagicMock(side_effect=[ limit_buy_order_usdt_open, @@ -3296,6 +3313,9 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order_usdt, limit_b ) default_conf['trailing_stop'] = True default_conf['trailing_stop_positive'] = 0.01 + if offset: + default_conf['trailing_stop_positive_offset'] = offset + default_conf['trailing_only_offset_is_reached'] = trail_if_reached patch_whitelist(mocker, default_conf) freqtrade = FreqtradeBot(default_conf) @@ -3310,159 +3330,59 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order_usdt, limit_b assert freqtrade.handle_trade(trade) is False # Raise ticker above buy price - mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', - MagicMock(return_value={ - 'bid': buy_price + 0.000003, - 'ask': buy_price + 0.000003, - 'last': buy_price + 0.000003 - })) - # stop-loss not reached, adjusted stoploss - assert freqtrade.handle_trade(trade) is False - assert log_has("ETH/BTC - Using positive stoploss: 0.01 offset: 0 profit: 0.2666%", caplog) - assert log_has("ETH/BTC - Adjusting stoploss...", caplog) - assert trade.stop_loss == 0.0000138501 - caplog.clear() - - mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', - MagicMock(return_value={ - 'bid': buy_price + 0.000002, - 'ask': buy_price + 0.000002, - 'last': buy_price + 0.000002 - })) - # Lower price again (but still positive) - assert freqtrade.handle_trade(trade) is True - assert log_has( - f"ETH/BTC - HIT STOP: current price at {buy_price + 0.000002:.6f}, " - f"stoploss is {trade.stop_loss:.6f}, " - f"initial stoploss was at 0.000010, trade opened at 0.000011", caplog) - - -def test_trailing_stop_loss_offset(default_conf, limit_buy_order_usdt, limit_buy_order_usdt_open, fee, - caplog, mocker) -> None: - buy_price = limit_buy_order_usdt['price'] - patch_RPCManager(mocker) - patch_exchange(mocker) - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=MagicMock(return_value={ - 'bid': buy_price - 0.000001, - 'ask': buy_price - 0.000001, - 'last': buy_price - 0.000001 - }), - create_order=MagicMock(side_effect=[ - limit_buy_order_usdt_open, - {'id': 1234553382}, - ]), - get_fee=fee, + mocker.patch( + 'freqtrade.exchange.Exchange.fetch_ticker', + MagicMock(return_value={ + 'bid': buy_price + 0.06, + 'ask': buy_price + 0.06, + 'last': buy_price + 0.06 + }) ) - patch_whitelist(mocker, default_conf) - default_conf['trailing_stop'] = True - default_conf['trailing_stop_positive'] = 0.01 - default_conf['trailing_stop_positive_offset'] = 0.011 - freqtrade = FreqtradeBot(default_conf) - patch_get_signal(freqtrade) - freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.enter_positions() - - trade = Trade.query.first() - trade.update(limit_buy_order_usdt) - caplog.set_level(logging.DEBUG) - # stop-loss not reached - assert freqtrade.handle_trade(trade) is False - - # Raise ticker above buy price - mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', - MagicMock(return_value={ - 'bid': buy_price + 0.000003, - 'ask': buy_price + 0.000003, - 'last': buy_price + 0.000003 - })) # stop-loss not reached, adjusted stoploss assert freqtrade.handle_trade(trade) is False - assert log_has("ETH/BTC - Using positive stoploss: 0.01 offset: 0.011 profit: 0.2666%", caplog) - assert log_has("ETH/BTC - Adjusting stoploss...", caplog) - assert trade.stop_loss == 0.0000138501 + # TODO: is 0.0249% correct? Shouldn't it be higher? + caplog_text = f"ETH/BTC - Using positive stoploss: 0.01 offset: {offset} profit: 0.0249%" + if trail_if_reached: + assert not log_has(caplog_text, caplog) + assert not log_has("ETH/BTC - Adjusting stoploss...", caplog) + else: + assert log_has(caplog_text, caplog) + assert log_has("ETH/BTC - Adjusting stoploss...", caplog) + assert trade.stop_loss == second_sl caplog.clear() - mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', - MagicMock(return_value={ - 'bid': buy_price + 0.000002, - 'ask': buy_price + 0.000002, - 'last': buy_price + 0.000002 - })) + mocker.patch( + 'freqtrade.exchange.Exchange.fetch_ticker', + MagicMock(return_value={ + 'bid': buy_price + 0.125, + 'ask': buy_price + 0.125, + 'last': buy_price + 0.125, + }) + ) + assert freqtrade.handle_trade(trade) is False + assert log_has( + f"ETH/BTC - Using positive stoploss: 0.01 offset: {offset} profit: 0.0572%", + caplog + ) + assert log_has("ETH/BTC - Adjusting stoploss...", caplog) + + mocker.patch( + 'freqtrade.exchange.Exchange.fetch_ticker', + MagicMock(return_value={ + 'bid': buy_price + 0.02, + 'ask': buy_price + 0.02, + 'last': buy_price + 0.02 + }) + ) # Lower price again (but still positive) assert freqtrade.handle_trade(trade) is True assert log_has( - f"ETH/BTC - HIT STOP: current price at {buy_price + 0.000002:.6f}, " + f"ETH/BTC - HIT STOP: current price at {buy_price + 0.02:.6f}, " f"stoploss is {trade.stop_loss:.6f}, " - f"initial stoploss was at 0.000010, trade opened at 0.000011", caplog) + f"initial stoploss was at 1.800000, trade opened at 2.000000", caplog) assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value -def test_tsl_only_offset_reached(default_conf, limit_buy_order_usdt, limit_buy_order_usdt_open, fee, - caplog, mocker) -> None: - buy_price = limit_buy_order_usdt['price'] - # buy_price: 2.0 - - patch_RPCManager(mocker) - patch_exchange(mocker) - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=MagicMock(return_value={ - 'bid': buy_price, - 'ask': buy_price, - 'last': buy_price - }), - create_order=MagicMock(return_value=limit_buy_order_usdt_open), - get_fee=fee, - ) - patch_whitelist(mocker, default_conf) - default_conf['trailing_stop'] = True - default_conf['trailing_stop_positive'] = 0.05 - default_conf['trailing_stop_positive_offset'] = 0.055 - default_conf['trailing_only_offset_is_reached'] = True - - freqtrade = FreqtradeBot(default_conf) - patch_get_signal(freqtrade) - freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.enter_positions() - - trade = Trade.query.first() - trade.update(limit_buy_order_usdt) - caplog.set_level(logging.DEBUG) - # stop-loss not reached - assert freqtrade.handle_trade(trade) is False - assert trade.stop_loss == 1.8 - - # Raise ticker above buy price - mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', - MagicMock(return_value={ - 'bid': buy_price + 0.0000004, - 'ask': buy_price + 0.0000004, - 'last': buy_price + 0.0000004 - })) - - # stop-loss should not be adjusted as offset is not reached yet - assert freqtrade.handle_trade(trade) is False - - assert not log_has("ETH/BTC - Adjusting stoploss...", caplog) - assert trade.stop_loss == 1.8 - caplog.clear() - - # price rises above the offset (rises 12% when the offset is 5.5%) - mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', - MagicMock(return_value={ - 'bid': buy_price + 0.0000014, - 'ask': buy_price + 0.0000014, - 'last': buy_price + 0.0000014 - })) - - assert freqtrade.handle_trade(trade) is False - assert log_has("ETH/BTC - Using positive stoploss: 0.05 offset: 0.055 profit: 0.1218%", caplog) - assert log_has("ETH/BTC - Adjusting stoploss...", caplog) - assert trade.stop_loss == 0.0000117705 - - def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order_usdt, limit_buy_order_usdt_open, fee, mocker) -> None: patch_RPCManager(mocker) From d1e3d480751e53a240a3c137770fd18f60a67607 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sun, 26 Sep 2021 02:43:47 -0600 Subject: [PATCH 019/144] changed test_update_trade_state_withorderdict to usdt --- tests/conftest.py | 49 ++++++++++++++++++++++---------------- tests/test_freqtradebot.py | 8 +++++-- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index adb344ffc..09725213f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1598,27 +1598,34 @@ def result(testdatadir): @pytest.fixture(scope="function") def trades_for_order(): - return [{'info': {'id': 34567, - 'orderId': 123456, - 'price': '0.24544100', - 'qty': '8.00000000', - 'commission': '0.00800000', - 'commissionAsset': 'LTC', - 'time': 1521663363189, - 'isBuyer': True, - 'isMaker': False, - 'isBestMatch': True}, - 'timestamp': 1521663363189, - 'datetime': '2018-03-21T20:16:03.189Z', - 'symbol': 'LTC/ETH', - 'id': '34567', - 'order': '123456', - 'type': None, - 'side': 'buy', - 'price': 0.245441, - 'cost': 1.963528, - 'amount': 8.0, - 'fee': {'cost': 0.008, 'currency': 'LTC'}}] + return [{ + 'info': { + 'id': 34567, + 'orderId': 123456, + 'price': '0.24544100', + 'qty': '8.00000000', + 'commission': '0.00800000', + 'commissionAsset': 'LTC', + 'time': 1521663363189, + 'isBuyer': True, + 'isMaker': False, + 'isBestMatch': True + }, + 'timestamp': 1521663363189, + 'datetime': '2018-03-21T20:16:03.189Z', + 'symbol': 'LTC/USDT', + 'id': '34567', + 'order': '123456', + 'type': None, + 'side': 'buy', + 'price': 2.0, + 'cost': 16.0, + 'amount': 8.0, + 'fee': { + 'cost': 0.008, + 'currency': 'LTC' + } + }] @pytest.fixture(scope="function") diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index c030aca0f..dc5688122 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1207,6 +1207,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, patch_get_signal(freqtrade) freqtrade.enter_positions() + # TODO-lev: Get this trade switched to the usdt trades trade = Trade.query.first() trade.is_open = True trade.open_order_id = None @@ -1709,17 +1710,20 @@ def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_ patch_exchange(mocker) amount = sum(x['amount'] for x in trades_for_order) freqtrade = get_patched_freqtradebot(mocker, default_conf) + caplog.clear() trade = Trade( - pair='LTC/ETH', + pair='LTC/USDT', amount=amount, exchange='binance', - open_rate=0.245441, + open_rate=2.0, open_date=arrow.utcnow().datetime, fee_open=fee.return_value, fee_close=fee.return_value, open_order_id="123456", is_open=True, ) + # TODO-lev: caplog.text has Amount 60.00000000000001 does not match amount 60.00000000000001 + # TODO-lev: but they are the exact same freqtrade.update_trade_state(trade, '123456', limit_buy_order_usdt) assert trade.amount != amount assert trade.amount == limit_buy_order_usdt['amount'] From 6fdcf8cd73151291910f3ac87af80c4d743fa0cd Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sun, 26 Sep 2021 10:09:10 -0600 Subject: [PATCH 020/144] created default_conf_usdt and init_persistence_usdt so that these tests pass: test_handle_stoploss_on_exchange_trailing, test_handle_stoploss_on_exchange_custom_stop, test_update_trade_state_withorderdict --- tests/conftest.py | 33 ++++++++++++++++++++-- tests/test_freqtradebot.py | 56 ++++++++++++++++++-------------------- 2 files changed, 56 insertions(+), 33 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 09725213f..c4ed05254 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -289,11 +289,21 @@ def init_persistence(default_conf): init_db(default_conf['db_url'], default_conf['dry_run']) +@pytest.fixture(scope='function') +def init_persistence_usdt(default_conf_usdt): + init_db(default_conf_usdt['db_url'], default_conf_usdt['dry_run']) + + @pytest.fixture(scope="function") def default_conf(testdatadir): return get_default_conf(testdatadir) +@pytest.fixture(scope="function") +def default_conf_usdt(testdatadir): + return get_default_conf_usdt(testdatadir) + + def get_default_conf(testdatadir): """ Returns validated configuration suitable for most tests """ configuration = { @@ -368,6 +378,15 @@ def get_default_conf(testdatadir): return configuration +def get_default_conf_usdt(testdatadir): + configuration = get_default_conf(testdatadir) + configuration.update({ + "stake_amount": 60.0, + "stake_currency": "USDT", + }) + return configuration + + @pytest.fixture def update(): _update = Update(0) @@ -1602,7 +1621,7 @@ def trades_for_order(): 'info': { 'id': 34567, 'orderId': 123456, - 'price': '0.24544100', + 'price': '2.0', 'qty': '8.00000000', 'commission': '0.00800000', 'commissionAsset': 'LTC', @@ -1811,6 +1830,14 @@ def edge_conf(default_conf): return conf +@pytest.fixture(scope="function") +def edge_conf_usdt(edge_conf): + edge_conf.update({ + "stake_currency": "USDT", + }) + return edge_conf + + @pytest.fixture def rpc_balance(): return { @@ -2049,7 +2076,7 @@ def saved_hyperopt_results(): @pytest.fixture(scope='function') def limit_buy_order_usdt_open(): return { - 'id': 'mocked_limit_buy', + 'id': 'mocked_limit_buy_usdt', 'type': 'limit', 'side': 'buy', 'symbol': 'mocked', @@ -2076,7 +2103,7 @@ def limit_buy_order_usdt(limit_buy_order_usdt_open): @pytest.fixture def limit_sell_order_usdt_open(): return { - 'id': 'mocked_limit_sell', + 'id': 'mocked_limit_sell_usdt', 'type': 'limit', 'side': 'sell', 'pair': 'mocked', diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index dc5688122..24ed3d509 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1162,8 +1162,8 @@ def test_create_stoploss_order_insufficient_funds(mocker, default_conf, caplog, assert mock_insuf.call_count == 1 -@pytest.mark.usefixtures("init_persistence") -def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, +@pytest.mark.usefixtures("init_persistence_usdt") +def test_handle_stoploss_on_exchange_trailing(mocker, default_conf_usdt, fee, limit_buy_order_usdt, limit_sell_order_usdt) -> None: # When trailing stoploss is set stoploss = MagicMock(return_value={'id': 13434334}) @@ -1188,12 +1188,12 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, ) # enabling TSL - default_conf['trailing_stop'] = True + default_conf_usdt['trailing_stop'] = True # disabling ROI - default_conf['minimal_roi']['0'] = 999999999 + default_conf_usdt['minimal_roi']['0'] = 999999999 - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) # enabling stoploss on exchange freqtrade.strategy.order_types['stoploss_on_exchange'] = True @@ -1207,7 +1207,6 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, patch_get_signal(freqtrade) freqtrade.enter_positions() - # TODO-lev: Get this trade switched to the usdt trades trade = Trade.query.first() trade.is_open = True trade.open_order_id = None @@ -1261,7 +1260,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, cancel_order_mock.assert_called_once_with(100, 'ETH/BTC') stoploss_order_mock.assert_called_once_with( - amount=30.0, + amount=27.39726027, pair='ETH/BTC', order_types=freqtrade.strategy.order_types, stop_price=4.4 * 0.95 @@ -1271,9 +1270,9 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, mocker.patch( 'freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={ - 'bid': 4.1712, - 'ask': 4.1921, - 'last': 4.1712 + 'bid': 4.16, + 'ask': 4.17, + 'last': 4.16 }) ) assert freqtrade.handle_trade(trade) is True @@ -1354,9 +1353,9 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c assert log_has_re(r"Could not create trailing stoploss order for pair ETH/BTC\..*", caplog) -@pytest.mark.usefixtures("init_persistence") -def test_handle_stoploss_on_exchange_custom_stop(mocker, default_conf, fee, - limit_buy_order_usdt, limit_sell_order_usdt) -> None: +@pytest.mark.usefixtures("init_persistence_usdt") +def test_handle_stoploss_on_exchange_custom_stop( + mocker, default_conf_usdt, fee, limit_buy_order_usdt, limit_sell_order_usdt) -> None: # When trailing stoploss is set stoploss = MagicMock(return_value={'id': 13434334}) patch_RPCManager(mocker) @@ -1380,12 +1379,12 @@ def test_handle_stoploss_on_exchange_custom_stop(mocker, default_conf, fee, ) # enabling TSL - default_conf['use_custom_stoploss'] = True + default_conf_usdt['use_custom_stoploss'] = True # disabling ROI - default_conf['minimal_roi']['0'] = 999999999 + default_conf_usdt['minimal_roi']['0'] = 999999999 - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) # enabling stoploss on exchange freqtrade.strategy.order_types['stoploss_on_exchange'] = True @@ -1452,7 +1451,7 @@ def test_handle_stoploss_on_exchange_custom_stop(mocker, default_conf, fee, cancel_order_mock.assert_called_once_with(100, 'ETH/BTC') stoploss_order_mock.assert_called_once_with( - amount=100.0, + amount=31.57894736, pair='ETH/BTC', order_types=freqtrade.strategy.order_types, stop_price=4.4 * 0.96 @@ -1462,9 +1461,9 @@ def test_handle_stoploss_on_exchange_custom_stop(mocker, default_conf, fee, mocker.patch( 'freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={ - 'bid': 4.1712, - 'ask': 4.1921, - 'last': 4.1712 + 'bid': 4.17, + 'ask': 4.19, + 'last': 4.17 }) ) assert freqtrade.handle_trade(trade) is True @@ -1554,7 +1553,6 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, 'ask': 2.2 * 0.95, 'last': 1.9 * 0.95 })) - assert freqtrade.handle_trade(trade) is False assert freqtrade.handle_stoploss_on_exchange(trade) is False @@ -1566,21 +1564,21 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, # price jumped 2x mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={ - 'bid': 0.00002344, - 'ask': 0.00002346, - 'last': 0.00002344 + 'bid': 4.38, + 'ask': 4.4, + 'last': 4.38 })) assert freqtrade.handle_trade(trade) is False assert freqtrade.handle_stoploss_on_exchange(trade) is False # stoploss should be set to 1% as trailing is on - assert trade.stop_loss == 0.00002346 * 0.99 + assert trade.stop_loss == 4.4 * 0.99 cancel_order_mock.assert_called_once_with(100, 'NEO/BTC') stoploss_order_mock.assert_called_once_with(amount=2132892.49146757, pair='NEO/BTC', order_types=freqtrade.strategy.order_types, - stop_price=0.00002346 * 0.99) + stop_price=4.4 * 0.99) @pytest.mark.parametrize('return_value,side_effect,log_message', [ @@ -1701,7 +1699,7 @@ def test_update_trade_state(mocker, default_conf, limit_buy_order_usdt, caplog) (30.0 + 1e-14, True), (8.0, False) ]) -def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_buy_order_usdt, fee, +def test_update_trade_state_withorderdict(default_conf_usdt, trades_for_order, limit_buy_order_usdt, fee, mocker, initial_amount, has_rounding_fee, caplog): trades_for_order[0]['amount'] = initial_amount mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order) @@ -1709,7 +1707,7 @@ def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_ mocker.patch('freqtrade.exchange.Exchange.fetch_order', MagicMock(side_effect=ValueError)) patch_exchange(mocker) amount = sum(x['amount'] for x in trades_for_order) - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) caplog.clear() trade = Trade( pair='LTC/USDT', @@ -1722,8 +1720,6 @@ def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_ open_order_id="123456", is_open=True, ) - # TODO-lev: caplog.text has Amount 60.00000000000001 does not match amount 60.00000000000001 - # TODO-lev: but they are the exact same freqtrade.update_trade_state(trade, '123456', limit_buy_order_usdt) assert trade.amount != amount assert trade.amount == limit_buy_order_usdt['amount'] From ffa9a3ac7d057a56c1b47ec0380bfd5a230011ec Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sun, 26 Sep 2021 10:18:22 -0600 Subject: [PATCH 021/144] changed default_conf_usdt stake_amount to 10 --- tests/conftest.py | 2 +- tests/test_freqtradebot.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index c4ed05254..13628a66d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -381,7 +381,7 @@ def get_default_conf(testdatadir): def get_default_conf_usdt(testdatadir): configuration = get_default_conf(testdatadir) configuration.update({ - "stake_amount": 60.0, + "stake_amount": 10.0, "stake_currency": "USDT", }) return configuration diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 24ed3d509..282fd68ea 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1260,7 +1260,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf_usdt, fee, cancel_order_mock.assert_called_once_with(100, 'ETH/BTC') stoploss_order_mock.assert_called_once_with( - amount=27.39726027, + amount=4.56621004, pair='ETH/BTC', order_types=freqtrade.strategy.order_types, stop_price=4.4 * 0.95 @@ -1451,7 +1451,7 @@ def test_handle_stoploss_on_exchange_custom_stop( cancel_order_mock.assert_called_once_with(100, 'ETH/BTC') stoploss_order_mock.assert_called_once_with( - amount=31.57894736, + amount=5.26315789, pair='ETH/BTC', order_types=freqtrade.strategy.order_types, stop_price=4.4 * 0.96 From 5ce09c751935f5436732c8e05dd209a42bd95976 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sun, 26 Sep 2021 10:25:26 -0600 Subject: [PATCH 022/144] updated test_reupdate_enter_order_fees to usdt --- tests/test_freqtradebot.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 282fd68ea..2d0e0a776 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -4043,9 +4043,9 @@ def test_update_closed_trades_without_assigned_fees(mocker, default_conf, fee): assert trade.fee_close_currency is not None -@pytest.mark.usefixtures("init_persistence") -def test_reupdate_enter_order_fees(mocker, default_conf, fee, caplog): - freqtrade = get_patched_freqtradebot(mocker, default_conf) +@pytest.mark.usefixtures("init_persistence_usdt") +def test_reupdate_enter_order_fees(mocker, default_conf_usdt, fee, caplog): + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) mock_uts = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.update_trade_state') create_mock_trades(fee) @@ -4063,13 +4063,13 @@ def test_reupdate_enter_order_fees(mocker, default_conf, fee, caplog): # Test with trade without orders trade = Trade( pair='XRP/ETH', - stake_amount=0.001, + stake_amount=60.0, fee_open=fee.return_value, fee_close=fee.return_value, open_date=arrow.utcnow().datetime, is_open=True, - amount=20, - open_rate=0.01, + amount=30, + open_rate=2.0, exchange='binance', ) Trade.query.session.add(trade) From d0e0d0ee01cc0d4f008f598c2bdd477a15b40d9d Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sun, 26 Sep 2021 11:56:44 -0600 Subject: [PATCH 023/144] Removed init_persistence_usdt --- tests/conftest.py | 5 ----- tests/test_freqtradebot.py | 6 +++--- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 13628a66d..3e88c88aa 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -289,11 +289,6 @@ def init_persistence(default_conf): init_db(default_conf['db_url'], default_conf['dry_run']) -@pytest.fixture(scope='function') -def init_persistence_usdt(default_conf_usdt): - init_db(default_conf_usdt['db_url'], default_conf_usdt['dry_run']) - - @pytest.fixture(scope="function") def default_conf(testdatadir): return get_default_conf(testdatadir) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 2d0e0a776..a000829dd 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1162,7 +1162,7 @@ def test_create_stoploss_order_insufficient_funds(mocker, default_conf, caplog, assert mock_insuf.call_count == 1 -@pytest.mark.usefixtures("init_persistence_usdt") +@pytest.mark.usefixtures("init_persistence") def test_handle_stoploss_on_exchange_trailing(mocker, default_conf_usdt, fee, limit_buy_order_usdt, limit_sell_order_usdt) -> None: # When trailing stoploss is set @@ -1353,7 +1353,7 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c assert log_has_re(r"Could not create trailing stoploss order for pair ETH/BTC\..*", caplog) -@pytest.mark.usefixtures("init_persistence_usdt") +@pytest.mark.usefixtures("init_persistence") def test_handle_stoploss_on_exchange_custom_stop( mocker, default_conf_usdt, fee, limit_buy_order_usdt, limit_sell_order_usdt) -> None: # When trailing stoploss is set @@ -4043,7 +4043,7 @@ def test_update_closed_trades_without_assigned_fees(mocker, default_conf, fee): assert trade.fee_close_currency is not None -@pytest.mark.usefixtures("init_persistence_usdt") +@pytest.mark.usefixtures("init_persistence") def test_reupdate_enter_order_fees(mocker, default_conf_usdt, fee, caplog): freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) mock_uts = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.update_trade_state') From 26fdad84685de96cb7f3eb9b7986317f3b8b990a Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sun, 26 Sep 2021 11:58:55 -0600 Subject: [PATCH 024/144] Removed edge_conf_usdt --- tests/conftest.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 3e88c88aa..898dcfbfd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1825,14 +1825,6 @@ def edge_conf(default_conf): return conf -@pytest.fixture(scope="function") -def edge_conf_usdt(edge_conf): - edge_conf.update({ - "stake_currency": "USDT", - }) - return edge_conf - - @pytest.fixture def rpc_balance(): return { From 755cc9cda1d5d75a6487b19a0daf289c2a0902b7 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sun, 26 Sep 2021 12:39:30 -0600 Subject: [PATCH 025/144] Updated test_check_available_stake_amount to use default_conf_usdt --- tests/test_freqtradebot.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index a000829dd..d8cc2214a 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -126,16 +126,16 @@ def test_get_trade_stake_amount(default_conf, ticker, mocker) -> None: @pytest.mark.parametrize("amend_last,wallet,max_open,lsamr,expected", [ - (False, 0.002, 2, 0.5, [0.001, None]), - (True, 0.002, 2, 0.5, [0.001, 0.00098]), - (False, 0.003, 3, 0.5, [0.001, 0.001, None]), - (True, 0.003, 3, 0.5, [0.001, 0.001, 0.00097]), - (False, 0.0022, 3, 0.5, [0.001, 0.001, None]), - (True, 0.0022, 3, 0.5, [0.001, 0.001, 0.0]), - (True, 0.0027, 3, 0.5, [0.001, 0.001, 0.000673]), - (True, 0.0022, 3, 1, [0.001, 0.001, 0.0]), + (False, 20, 2, 0.5, [10, None]), + (True, 20, 2, 0.5, [10, 9.8]), + (False, 30, 3, 0.5, [10, 10, None]), + (True, 30, 3, 0.5, [10, 10, 9.7]), + (False, 22, 3, 0.5, [10, 10, None]), + (True, 22, 3, 0.5, [10, 10, 0.0]), + (True, 27, 3, 0.5, [10, 10, 6.73]), + (True, 22, 3, 1, [10, 10, 0.0]), ]) -def test_check_available_stake_amount(default_conf, ticker, mocker, fee, limit_buy_order_usdt_open, +def test_check_available_stake_amount(default_conf_usdt, ticker, mocker, fee, limit_buy_order_usdt_open, amend_last, wallet, max_open, lsamr, expected) -> None: patch_RPCManager(mocker) patch_exchange(mocker) @@ -145,12 +145,12 @@ def test_check_available_stake_amount(default_conf, ticker, mocker, fee, limit_b create_order=MagicMock(return_value=limit_buy_order_usdt_open), get_fee=fee ) - default_conf['dry_run_wallet'] = wallet + default_conf_usdt['dry_run_wallet'] = wallet - default_conf['amend_last_stake_amount'] = amend_last - default_conf['last_stake_amount_min_ratio'] = lsamr + default_conf_usdt['amend_last_stake_amount'] = amend_last + default_conf_usdt['last_stake_amount_min_ratio'] = lsamr - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) for i in range(0, max_open): From 7eebb6bb2d305ab24f08d1e640efcc88913152c0 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sun, 26 Sep 2021 12:41:09 -0600 Subject: [PATCH 026/144] updated test_create_trade to use default_conf_usdt --- tests/test_freqtradebot.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index d8cc2214a..280d2ce97 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -270,7 +270,7 @@ def test_total_open_trades_stakes(mocker, default_conf, ticker, fee) -> None: assert Trade.total_open_trades_stakes() == 1.97502e-03 -def test_create_trade(default_conf, ticker, limit_buy_order_usdt, fee, mocker) -> None: +def test_create_trade(default_conf_usdt, ticker, limit_buy_order_usdt, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -281,14 +281,14 @@ def test_create_trade(default_conf, ticker, limit_buy_order_usdt, fee, mocker) - ) # Save state of current whitelist - whitelist = deepcopy(default_conf['exchange']['pair_whitelist']) - freqtrade = FreqtradeBot(default_conf) + whitelist = deepcopy(default_conf_usdt['exchange']['pair_whitelist']) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) freqtrade.create_trade('ETH/BTC') trade = Trade.query.first() assert trade is not None - assert trade.stake_amount == 0.001 + assert trade.stake_amount == 10.0 assert trade.is_open assert trade.open_date is not None assert trade.exchange == 'binance' @@ -299,7 +299,7 @@ def test_create_trade(default_conf, ticker, limit_buy_order_usdt, fee, mocker) - assert trade.open_rate == 2.0 assert trade.amount == 30.0 - assert whitelist == default_conf['exchange']['pair_whitelist'] + assert whitelist == default_conf_usdt['exchange']['pair_whitelist'] def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order_usdt, From ba5d78f005210d51416387e65fa30c7a38623395 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sun, 26 Sep 2021 13:04:58 -0600 Subject: [PATCH 027/144] swapped default_conf for default_conf_usdt and ticker for ticker_usdt --- tests/conftest.py | 22 +- tests/test_freqtradebot.py | 788 ++++++++++++++++++------------------- 2 files changed, 413 insertions(+), 397 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 898dcfbfd..f2da68dd2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -378,6 +378,22 @@ def get_default_conf_usdt(testdatadir): configuration.update({ "stake_amount": 10.0, "stake_currency": "USDT", + "exchange": { + "name": "binance", + "enabled": True, + "key": "key", + "secret": "secret", + "pair_whitelist": [ + "ETH/USDT", + "LTC/USDT", + "XRP/USDT", + "NEO/USDT" + ], + "pair_blacklist": [ + "DOGE/USDT", + "HOT/USDT", + ] + }, }) return configuration @@ -424,9 +440,9 @@ def ticker_sell_down(): @pytest.fixture def ticker_usdt(): return MagicMock(return_value={ - 'bid': 1.99, - 'ask': 2.0, - 'last': 1.99, + 'bid': 2.0, + 'ask': 2.01, + 'last': 2.0, }) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 280d2ce97..2a96783bc 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -41,33 +41,33 @@ def patch_RPCManager(mocker) -> MagicMock: # Unit tests -def test_freqtradebot_state(mocker, default_conf, markets) -> None: +def test_freqtradebot_state(mocker, default_conf_usdt, markets) -> None: mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets)) - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) assert freqtrade.state is State.RUNNING - default_conf.pop('initial_state') - freqtrade = FreqtradeBot(default_conf) + default_conf_usdt.pop('initial_state') + freqtrade = FreqtradeBot(default_conf_usdt) assert freqtrade.state is State.STOPPED -def test_process_stopped(mocker, default_conf) -> None: +def test_process_stopped(mocker, default_conf_usdt) -> None: - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) coo_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cancel_all_open_orders') freqtrade.process_stopped() assert coo_mock.call_count == 0 - default_conf['cancel_open_orders_on_exit'] = True - freqtrade = get_patched_freqtradebot(mocker, default_conf) + default_conf_usdt['cancel_open_orders_on_exit'] = True + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) freqtrade.process_stopped() assert coo_mock.call_count == 1 -def test_bot_cleanup(mocker, default_conf, caplog) -> None: +def test_bot_cleanup(mocker, default_conf_usdt, caplog) -> None: mock_cleanup = mocker.patch('freqtrade.freqtradebot.cleanup_db') coo_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cancel_all_open_orders') - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) freqtrade.cleanup() assert log_has('Cleaning up modules ...', caplog) assert mock_cleanup.call_count == 1 @@ -82,10 +82,10 @@ def test_bot_cleanup(mocker, default_conf, caplog) -> None: RunMode.DRY_RUN, RunMode.LIVE ]) -def test_order_dict(default_conf, mocker, runmode, caplog) -> None: +def test_order_dict(default_conf_usdt, mocker, runmode, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) - conf = default_conf.copy() + conf = default_conf_usdt.copy() conf['runmode'] = runmode conf['order_types'] = { 'buy': 'market', @@ -102,7 +102,7 @@ def test_order_dict(default_conf, mocker, runmode, caplog) -> None: caplog.clear() # is left untouched - conf = default_conf.copy() + conf = default_conf_usdt.copy() conf['runmode'] = runmode conf['order_types'] = { 'buy': 'market', @@ -115,14 +115,14 @@ def test_order_dict(default_conf, mocker, runmode, caplog) -> None: assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog) -def test_get_trade_stake_amount(default_conf, ticker, mocker) -> None: +def test_get_trade_stake_amount(default_conf_usdt, ticker_usdt, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) - result = freqtrade.wallets.get_trade_stake_amount('ETH/BTC') - assert result == default_conf['stake_amount'] + result = freqtrade.wallets.get_trade_stake_amount('ETH/USDT') + assert result == default_conf_usdt['stake_amount'] @pytest.mark.parametrize("amend_last,wallet,max_open,lsamr,expected", [ @@ -135,13 +135,13 @@ def test_get_trade_stake_amount(default_conf, ticker, mocker) -> None: (True, 27, 3, 0.5, [10, 10, 6.73]), (True, 22, 3, 1, [10, 10, 0.0]), ]) -def test_check_available_stake_amount(default_conf_usdt, ticker, mocker, fee, limit_buy_order_usdt_open, +def test_check_available_stake_amount(default_conf_usdt, ticker_usdt, mocker, fee, limit_buy_order_usdt_open, amend_last, wallet, max_open, lsamr, expected) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, create_order=MagicMock(return_value=limit_buy_order_usdt_open), get_fee=fee ) @@ -156,12 +156,12 @@ def test_check_available_stake_amount(default_conf_usdt, ticker, mocker, fee, li if expected[i] is not None: limit_buy_order_usdt_open['id'] = str(i) - result = freqtrade.wallets.get_trade_stake_amount('ETH/BTC') + result = freqtrade.wallets.get_trade_stake_amount('ETH/USDT') assert pytest.approx(result) == expected[i] - freqtrade.execute_entry('ETH/BTC', result) + freqtrade.execute_entry('ETH/USDT', result) else: with pytest.raises(DependencyException): - freqtrade.wallets.get_trade_stake_amount('ETH/BTC') + freqtrade.wallets.get_trade_stake_amount('ETH/USDT') def test_edge_called_in_process(mocker, edge_conf) -> None: @@ -169,7 +169,7 @@ def test_edge_called_in_process(mocker, edge_conf) -> None: patch_edge(mocker) def _refresh_whitelist(list): - return ['ETH/BTC', 'LTC/BTC', 'XRP/BTC', 'NEO/BTC'] + return ['ETH/USDT', 'LTC/BTC', 'XRP/BTC', 'NEO/BTC'] patch_exchange(mocker) freqtrade = FreqtradeBot(edge_conf) @@ -208,7 +208,7 @@ def test_edge_overrides_stoploss(limit_buy_order_usdt, fee, caplog, mocker, # Strategy stoploss is -0.1 but Edge imposes a stoploss at -0.2 # Thus, if price falls 21%, stoploss should be triggered # - # mocking the ticker: price is falling ... + # mocking the ticker_usdt: price is falling ... buy_price = limit_buy_order_usdt['price'] mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -238,24 +238,24 @@ def test_edge_overrides_stoploss(limit_buy_order_usdt, fee, caplog, mocker, assert trade.sell_reason == SellType.STOP_LOSS.value -def test_total_open_trades_stakes(mocker, default_conf, ticker, fee) -> None: +def test_total_open_trades_stakes(mocker, default_conf_usdt, ticker_usdt, fee) -> None: patch_RPCManager(mocker) patch_exchange(mocker) - default_conf['stake_amount'] = 0.00098751 - default_conf['max_open_trades'] = 2 + default_conf_usdt['stake_amount'] = 10.0 + default_conf_usdt['max_open_trades'] = 2 mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, get_fee=fee, _is_dry_limit_order_filled=MagicMock(return_value=False), ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) freqtrade.enter_positions() trade = Trade.query.first() assert trade is not None - assert trade.stake_amount == 0.00098751 + assert trade.stake_amount == 10.0 assert trade.is_open assert trade.open_date is not None @@ -263,19 +263,19 @@ def test_total_open_trades_stakes(mocker, default_conf, ticker, fee) -> None: trade = Trade.query.order_by(Trade.id.desc()).first() assert trade is not None - assert trade.stake_amount == 0.00098751 + assert trade.stake_amount == 10.0 assert trade.is_open assert trade.open_date is not None - assert Trade.total_open_trades_stakes() == 1.97502e-03 + assert Trade.total_open_trades_stakes() == 20.0 -def test_create_trade(default_conf_usdt, ticker, limit_buy_order_usdt, fee, mocker) -> None: +def test_create_trade(default_conf_usdt, ticker_usdt, limit_buy_order_usdt, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, get_fee=fee, _is_dry_limit_order_filled=MagicMock(return_value=False), ) @@ -284,7 +284,7 @@ def test_create_trade(default_conf_usdt, ticker, limit_buy_order_usdt, fee, mock whitelist = deepcopy(default_conf_usdt['exchange']['pair_whitelist']) freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) - freqtrade.create_trade('ETH/BTC') + freqtrade.create_trade('ETH/USDT') trade = Trade.query.first() assert trade is not None @@ -302,21 +302,21 @@ def test_create_trade(default_conf_usdt, ticker, limit_buy_order_usdt, fee, mock assert whitelist == default_conf_usdt['exchange']['pair_whitelist'] -def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order_usdt, +def test_create_trade_no_stake_amount(default_conf_usdt, ticker_usdt, limit_buy_order_usdt, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) - patch_wallet(mocker, free=default_conf['stake_amount'] * 0.5) + patch_wallet(mocker, free=default_conf_usdt['stake_amount'] * 0.5) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, get_fee=fee, ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) with pytest.raises(DependencyException, match=r'.*stake amount.*'): - freqtrade.create_trade('ETH/BTC') + freqtrade.create_trade('ETH/USDT') @pytest.mark.parametrize('stake_amount,create,amount_enough,max_open_trades', [ @@ -326,7 +326,7 @@ def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order_usdt (UNLIMITED_STAKE_AMOUNT, False, True, 0), ]) def test_create_trade_minimal_amount( - default_conf, ticker, limit_buy_order_usdt_open, fee, mocker, + default_conf_usdt, ticker_usdt, limit_buy_order_usdt_open, fee, mocker, stake_amount, create, amount_enough, max_open_trades, caplog ) -> None: patch_RPCManager(mocker) @@ -334,47 +334,47 @@ def test_create_trade_minimal_amount( buy_mock = MagicMock(return_value=limit_buy_order_usdt_open) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, create_order=buy_mock, get_fee=fee, ) - default_conf['max_open_trades'] = max_open_trades - freqtrade = FreqtradeBot(default_conf) + default_conf_usdt['max_open_trades'] = max_open_trades + freqtrade = FreqtradeBot(default_conf_usdt) freqtrade.config['stake_amount'] = stake_amount patch_get_signal(freqtrade) if create: - assert freqtrade.create_trade('ETH/BTC') + assert freqtrade.create_trade('ETH/USDT') if amount_enough: rate, amount = buy_mock.call_args[1]['rate'], buy_mock.call_args[1]['amount'] - assert rate * amount <= default_conf['stake_amount'] + assert rate * amount <= default_conf_usdt['stake_amount'] else: assert log_has_re( r"Stake amount for pair .* is too small.*", caplog ) else: - assert not freqtrade.create_trade('ETH/BTC') + assert not freqtrade.create_trade('ETH/USDT') if not max_open_trades: - assert freqtrade.wallets.get_trade_stake_amount('ETH/BTC', freqtrade.edge) == 0 + assert freqtrade.wallets.get_trade_stake_amount('ETH/USDT', freqtrade.edge) == 0 @pytest.mark.parametrize('whitelist,positions', [ - (["ETH/BTC"], 1), # No pairs left + (["ETH/USDT"], 1), # No pairs left ([], 0), # No pairs in whitelist ]) -def test_enter_positions_no_pairs_left(default_conf, ticker, limit_buy_order_usdt_open, fee, +def test_enter_positions_no_pairs_left(default_conf_usdt, ticker_usdt, limit_buy_order_usdt_open, fee, whitelist, positions, mocker, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, create_order=MagicMock(return_value=limit_buy_order_usdt_open), get_fee=fee, ) - default_conf['exchange']['pair_whitelist'] = whitelist - freqtrade = FreqtradeBot(default_conf) + default_conf_usdt['exchange']['pair_whitelist'] = whitelist + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) n = freqtrade.enter_positions() @@ -390,17 +390,17 @@ def test_enter_positions_no_pairs_left(default_conf, ticker, limit_buy_order_usd @pytest.mark.usefixtures("init_persistence") -def test_enter_positions_global_pairlock(default_conf, ticker, limit_buy_order_usdt, fee, +def test_enter_positions_global_pairlock(default_conf_usdt, ticker_usdt, limit_buy_order_usdt, fee, mocker, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, create_order=MagicMock(return_value={'id': limit_buy_order_usdt['id']}), get_fee=fee, ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) n = freqtrade.enter_positions() message = r"Global pairlock active until.* Not creating new trades." @@ -416,8 +416,8 @@ def test_enter_positions_global_pairlock(default_conf, ticker, limit_buy_order_u assert log_has_re(message, caplog) -def test_handle_protections(mocker, default_conf, fee): - default_conf['protections'] = [ +def test_handle_protections(mocker, default_conf_usdt, fee): + default_conf_usdt['protections'] = [ {"method": "CooldownPeriod", "stop_duration": 60}, { "method": "StoplossGuard", @@ -428,7 +428,7 @@ def test_handle_protections(mocker, default_conf, fee): } ] - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) freqtrade.protections._protection_handlers[1].global_stop = MagicMock( return_value=(True, arrow.utcnow().shift(hours=1).datetime, "asdf")) create_mock_trades(fee) @@ -439,8 +439,8 @@ def test_handle_protections(mocker, default_conf, fee): assert send_msg_mock.call_args_list[1][0][0]['type'] == RPCMessageType.PROTECTION_TRIGGER_GLOBAL -def test_create_trade_no_signal(default_conf, fee, mocker) -> None: - default_conf['dry_run'] = True +def test_create_trade_no_signal(default_conf_usdt, fee, mocker) -> None: + default_conf_usdt['dry_run'] = True patch_RPCManager(mocker) patch_exchange(mocker) @@ -448,32 +448,32 @@ def test_create_trade_no_signal(default_conf, fee, mocker) -> None: 'freqtrade.exchange.Exchange', get_fee=fee, ) - default_conf['stake_amount'] = 10 - freqtrade = FreqtradeBot(default_conf) + default_conf_usdt['stake_amount'] = 10 + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade, value=(False, False, None)) Trade.query = MagicMock() Trade.query.filter = MagicMock() - assert not freqtrade.create_trade('ETH/BTC') + assert not freqtrade.create_trade('ETH/USDT') @pytest.mark.parametrize("max_open", range(0, 5)) @pytest.mark.parametrize("tradable_balance_ratio,modifier", [(1.0, 1), (0.99, 0.8), (0.5, 0.5)]) -def test_create_trades_multiple_trades(default_conf, ticker, fee, mocker, limit_buy_order_usdt_open, +def test_create_trades_multiple_trades(default_conf_usdt, ticker_usdt, fee, mocker, limit_buy_order_usdt_open, max_open, tradable_balance_ratio, modifier) -> None: patch_RPCManager(mocker) patch_exchange(mocker) - default_conf['max_open_trades'] = max_open - default_conf['tradable_balance_ratio'] = tradable_balance_ratio - default_conf['dry_run_wallet'] = 0.001 * max_open + default_conf_usdt['max_open_trades'] = max_open + default_conf_usdt['tradable_balance_ratio'] = tradable_balance_ratio + default_conf_usdt['dry_run_wallet'] = 10.0 * max_open mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, create_order=MagicMock(return_value=limit_buy_order_usdt_open), get_fee=fee, ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) n = freqtrade.enter_positions() @@ -484,47 +484,47 @@ def test_create_trades_multiple_trades(default_conf, ticker, fee, mocker, limit_ assert len(trades) == max(int(max_open * modifier), 0) -def test_create_trades_preopen(default_conf, ticker, fee, mocker, limit_buy_order_usdt_open) -> None: +def test_create_trades_preopen(default_conf_usdt, ticker_usdt, fee, mocker, limit_buy_order_usdt_open) -> None: patch_RPCManager(mocker) patch_exchange(mocker) - default_conf['max_open_trades'] = 4 + default_conf_usdt['max_open_trades'] = 4 mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, create_order=MagicMock(return_value=limit_buy_order_usdt_open), get_fee=fee, ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) # Create 2 existing trades - freqtrade.execute_entry('ETH/BTC', default_conf['stake_amount']) - freqtrade.execute_entry('NEO/BTC', default_conf['stake_amount']) + freqtrade.execute_entry('ETH/USDT', default_conf_usdt['stake_amount']) + freqtrade.execute_entry('NEO/BTC', default_conf_usdt['stake_amount']) assert len(Trade.get_open_trades()) == 2 # Change order_id for new orders limit_buy_order_usdt_open['id'] = '123444' # Create 2 new trades using create_trades - assert freqtrade.create_trade('ETH/BTC') + assert freqtrade.create_trade('ETH/USDT') assert freqtrade.create_trade('NEO/BTC') trades = Trade.get_open_trades() assert len(trades) == 4 -def test_process_trade_creation(default_conf, ticker, limit_buy_order_usdt, limit_buy_order_usdt_open, +def test_process_trade_creation(default_conf_usdt, ticker_usdt, limit_buy_order_usdt, limit_buy_order_usdt_open, fee, mocker, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, create_order=MagicMock(return_value=limit_buy_order_usdt_open), fetch_order=MagicMock(return_value=limit_buy_order_usdt), get_fee=fee, ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) trades = Trade.query.filter(Trade.is_open.is_(True)).all() @@ -536,45 +536,45 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order_usdt, limi assert len(trades) == 1 trade = trades[0] assert trade is not None - assert trade.stake_amount == default_conf['stake_amount'] + assert trade.stake_amount == default_conf_usdt['stake_amount'] assert trade.is_open assert trade.open_date is not None assert trade.exchange == 'binance' - assert trade.open_rate == 0.00001098 - assert trade.amount == 91.07468123 + assert trade.open_rate == 2.0 + assert trade.amount == 5.0 assert log_has( - 'Buy signal found: about create a new trade for ETH/BTC with stake_amount: 0.001 ...', + 'Buy signal found: about create a new trade for ETH/USDT with stake_amount: 10.0 ...', caplog ) -def test_process_exchange_failures(default_conf, ticker, mocker) -> None: +def test_process_exchange_failures(default_conf_usdt, ticker_usdt, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, create_order=MagicMock(side_effect=TemporaryError) ) sleep_mock = mocker.patch('time.sleep', side_effect=lambda _: None) - worker = Worker(args=None, config=default_conf) + worker = Worker(args=None, config=default_conf_usdt) patch_get_signal(worker.freqtrade) worker._process_running() assert sleep_mock.has_calls() -def test_process_operational_exception(default_conf, ticker, mocker) -> None: +def test_process_operational_exception(default_conf_usdt, ticker_usdt, mocker) -> None: msg_mock = patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, create_order=MagicMock(side_effect=OperationalException) ) - worker = Worker(args=None, config=default_conf) + worker = Worker(args=None, config=default_conf_usdt) patch_get_signal(worker.freqtrade) assert worker.freqtrade.state == State.RUNNING @@ -584,17 +584,17 @@ def test_process_operational_exception(default_conf, ticker, mocker) -> None: assert 'OperationalException' in msg_mock.call_args_list[-1][0][0]['status'] -def test_process_trade_handling(default_conf, ticker, limit_buy_order_usdt_open, fee, mocker) -> None: +def test_process_trade_handling(default_conf_usdt, ticker_usdt, limit_buy_order_usdt_open, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, create_order=MagicMock(return_value=limit_buy_order_usdt_open), fetch_order=MagicMock(return_value=limit_buy_order_usdt_open), get_fee=fee, ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) trades = Trade.query.filter(Trade.is_open.is_(True)).all() @@ -609,23 +609,23 @@ def test_process_trade_handling(default_conf, ticker, limit_buy_order_usdt_open, assert len(trades) == 1 -def test_process_trade_no_whitelist_pair(default_conf, ticker, limit_buy_order_usdt, +def test_process_trade_no_whitelist_pair(default_conf_usdt, ticker_usdt, limit_buy_order_usdt, fee, mocker) -> None: """ Test process with trade not in pair list """ patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, create_order=MagicMock(return_value={'id': limit_buy_order_usdt['id']}), fetch_order=MagicMock(return_value=limit_buy_order_usdt), get_fee=fee, ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) pair = 'BLK/BTC' # Ensure the pair is not in the whitelist! - assert pair not in default_conf['exchange']['pair_whitelist'] + assert pair not in default_conf_usdt['exchange']['pair_whitelist'] # create open trade not in whitelist Trade.query.session.add(Trade( @@ -639,7 +639,7 @@ def test_process_trade_no_whitelist_pair(default_conf, ticker, limit_buy_order_u exchange='binance', )) Trade.query.session.add(Trade( - pair='ETH/BTC', + pair='ETH/USDT', stake_amount=0.001, fee_open=fee.return_value, fee_close=fee.return_value, @@ -656,17 +656,17 @@ def test_process_trade_no_whitelist_pair(default_conf, ticker, limit_buy_order_u assert len(freqtrade.active_pair_whitelist) == len(set(freqtrade.active_pair_whitelist)) -def test_process_informative_pairs_added(default_conf, ticker, mocker) -> None: +def test_process_informative_pairs_added(default_conf_usdt, ticker_usdt, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) def _refresh_whitelist(list): - return ['ETH/BTC', 'LTC/BTC', 'XRP/BTC', 'NEO/BTC'] + return ['ETH/USDT', 'LTC/BTC', 'XRP/BTC', 'NEO/BTC'] refresh_mock = MagicMock() mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, create_order=MagicMock(side_effect=TemporaryError), refresh_latest_ohlcv=refresh_mock, ) @@ -677,7 +677,7 @@ def test_process_informative_pairs_added(default_conf, ticker, mocker) -> None: ) mocker.patch('time.sleep', return_value=None) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) freqtrade.pairlists._validate_whitelist = _refresh_whitelist freqtrade.strategy.informative_pairs = inf_pairs # patch_get_signal(freqtrade) @@ -687,13 +687,13 @@ def test_process_informative_pairs_added(default_conf, ticker, mocker) -> None: assert refresh_mock.call_count == 1 assert ("BTC/ETH", "1m") in refresh_mock.call_args[0][0] assert ("ETH/USDT", "1h") in refresh_mock.call_args[0][0] - assert ("ETH/BTC", default_conf["timeframe"]) in refresh_mock.call_args[0][0] + assert ("ETH/USDT", default_conf_usdt["timeframe"]) in refresh_mock.call_args[0][0] -def test_execute_entry(mocker, default_conf, fee, limit_buy_order_usdt, limit_buy_order_usdt_open) -> None: +def test_execute_entry(mocker, default_conf_usdt, fee, limit_buy_order_usdt, limit_buy_order_usdt_open) -> None: patch_RPCManager(mocker) patch_exchange(mocker) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=False) stake_amount = 2 bid = 0.11 @@ -711,7 +711,7 @@ def test_execute_entry(mocker, default_conf, fee, limit_buy_order_usdt, limit_bu get_min_pair_stake_amount=MagicMock(return_value=1), get_fee=fee, ) - pair = 'ETH/BTC' + pair = 'ETH/USDT' assert not freqtrade.execute_entry(pair, stake_amount) assert buy_rate_mock.call_count == 1 @@ -853,8 +853,8 @@ def test_execute_entry(mocker, default_conf, fee, limit_buy_order_usdt, limit_bu assert trade.open_rate_requested == 10 -def test_execute_entry_confirm_error(mocker, default_conf, fee, limit_buy_order_usdt) -> None: - freqtrade = get_patched_freqtradebot(mocker, default_conf) +def test_execute_entry_confirm_error(mocker, default_conf_usdt, fee, limit_buy_order_usdt) -> None: + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=MagicMock(return_value={ @@ -868,7 +868,7 @@ def test_execute_entry_confirm_error(mocker, default_conf, fee, limit_buy_order_ get_fee=fee, ) stake_amount = 2 - pair = 'ETH/BTC' + pair = 'ETH/USDT' freqtrade.strategy.confirm_trade_entry = MagicMock(side_effect=ValueError) assert freqtrade.execute_entry(pair, stake_amount) @@ -885,7 +885,7 @@ def test_execute_entry_confirm_error(mocker, default_conf, fee, limit_buy_order_ assert not freqtrade.execute_entry(pair, stake_amount) -def test_add_stoploss_on_exchange(mocker, default_conf, limit_buy_order_usdt) -> None: +def test_add_stoploss_on_exchange(mocker, default_conf_usdt, limit_buy_order_usdt) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True)) @@ -897,7 +897,7 @@ def test_add_stoploss_on_exchange(mocker, default_conf, limit_buy_order_usdt) -> stoploss = MagicMock(return_value={'id': 13434334}) mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) freqtrade.strategy.order_types['stoploss_on_exchange'] = True trade = MagicMock() @@ -912,7 +912,7 @@ def test_add_stoploss_on_exchange(mocker, default_conf, limit_buy_order_usdt) -> assert trade.is_open is True -def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, +def test_handle_stoploss_on_exchange(mocker, default_conf_usdt, fee, caplog, limit_buy_order_usdt, limit_sell_order_usdt) -> None: stoploss = MagicMock(return_value={'id': 13434334}) patch_RPCManager(mocker) @@ -934,7 +934,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, 'freqtrade.exchange.Binance', stoploss=stoploss ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) # First case: when stoploss is not yet set but the order is open @@ -1032,7 +1032,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, assert stoploss.call_count == 0 -def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog, +def test_handle_sle_cancel_cant_recreate(mocker, default_conf_usdt, fee, caplog, limit_buy_order_usdt, limit_sell_order_usdt) -> None: # Sixth case: stoploss order was cancelled but couldn't create new one patch_RPCManager(mocker) @@ -1055,7 +1055,7 @@ def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog, fetch_stoploss_order=MagicMock(return_value={'status': 'canceled', 'id': 100}), stoploss=MagicMock(side_effect=ExchangeError()), ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) freqtrade.enter_positions() @@ -1071,7 +1071,7 @@ def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog, assert trade.is_open is True -def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, +def test_create_stoploss_order_invalid_order(mocker, default_conf_usdt, caplog, fee, limit_buy_order_usdt_open, limit_sell_order_usdt): rpc_mock = patch_RPCManager(mocker) patch_exchange(mocker) @@ -1094,7 +1094,7 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, fetch_order=MagicMock(return_value={'status': 'canceled'}), stoploss=MagicMock(side_effect=InvalidOrderException()), ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) freqtrade.strategy.order_types['stoploss_on_exchange'] = True @@ -1119,10 +1119,10 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, assert rpc_mock.call_args_list[1][0][0]['order_type'] == 'market' -def test_create_stoploss_order_insufficient_funds(mocker, default_conf, caplog, fee, +def test_create_stoploss_order_insufficient_funds(mocker, default_conf_usdt, caplog, fee, limit_buy_order_usdt_open, limit_sell_order_usdt): sell_mock = MagicMock(return_value={'id': limit_sell_order_usdt['id']}) - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) mock_insuf = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_insufficient_funds') mocker.patch.multiple( @@ -1258,10 +1258,10 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf_usdt, fee, assert freqtrade.handle_stoploss_on_exchange(trade) is False - cancel_order_mock.assert_called_once_with(100, 'ETH/BTC') + cancel_order_mock.assert_called_once_with(100, 'ETH/USDT') stoploss_order_mock.assert_called_once_with( amount=4.56621004, - pair='ETH/BTC', + pair='ETH/USDT', order_types=freqtrade.strategy.order_types, stop_price=4.4 * 0.95 ) @@ -1278,7 +1278,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf_usdt, fee, assert freqtrade.handle_trade(trade) is True -def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, caplog, +def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf_usdt, fee, caplog, limit_buy_order_usdt, limit_sell_order_usdt) -> None: # When trailing stoploss is set stoploss = MagicMock(return_value={'id': 13434334}) @@ -1304,9 +1304,9 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c ) # enabling TSL - default_conf['trailing_stop'] = True + default_conf_usdt['trailing_stop'] = True - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) # enabling stoploss on exchange freqtrade.strategy.order_types['stoploss_on_exchange'] = True @@ -1339,7 +1339,7 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c mocker.patch('freqtrade.exchange.Binance.fetch_stoploss_order', return_value=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.*", caplog) + assert log_has_re(r"Could not cancel stoploss order abcd for pair ETH/USDT.*", caplog) # Still try to create order assert stoploss.call_count == 1 @@ -1350,7 +1350,7 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c mocker.patch("freqtrade.exchange.Binance.stoploss", side_effect=ExchangeError()) freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging) assert cancel_mock.call_count == 1 - assert log_has_re(r"Could not create trailing stoploss order for pair ETH/BTC\..*", caplog) + assert log_has_re(r"Could not create trailing stoploss order for pair ETH/USDT\..*", caplog) @pytest.mark.usefixtures("init_persistence") @@ -1449,10 +1449,10 @@ def test_handle_stoploss_on_exchange_custom_stop( assert freqtrade.handle_stoploss_on_exchange(trade) is False - cancel_order_mock.assert_called_once_with(100, 'ETH/BTC') + cancel_order_mock.assert_called_once_with(100, 'ETH/USDT') stoploss_order_mock.assert_called_once_with( amount=5.26315789, - pair='ETH/BTC', + pair='ETH/USDT', order_types=freqtrade.strategy.order_types, stop_price=4.4 * 0.96 ) @@ -1583,12 +1583,12 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, @pytest.mark.parametrize('return_value,side_effect,log_message', [ (False, None, 'Found no buy signals for whitelisted currencies. Trying again...'), - (None, DependencyException, 'Unable to create trade for ETH/BTC: ') + (None, DependencyException, 'Unable to create trade for ETH/USDT: ') ]) -def test_enter_positions(mocker, default_conf, return_value, side_effect, +def test_enter_positions(mocker, default_conf_usdt, return_value, side_effect, log_message, caplog) -> None: caplog.set_level(logging.DEBUG) - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) mock_ct = mocker.patch( 'freqtrade.freqtradebot.FreqtradeBot.create_trade', @@ -1601,11 +1601,11 @@ def test_enter_positions(mocker, default_conf, return_value, side_effect, assert n == 0 assert log_has(log_message, caplog) # create_trade should be called once for every pair in the whitelist. - assert mock_ct.call_count == len(default_conf['exchange']['pair_whitelist']) + assert mock_ct.call_count == len(default_conf_usdt['exchange']['pair_whitelist']) -def test_exit_positions(mocker, default_conf, limit_buy_order_usdt, caplog) -> None: - freqtrade = get_patched_freqtradebot(mocker, default_conf) +def test_exit_positions(mocker, default_conf_usdt, limit_buy_order_usdt, caplog) -> None: + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True)) mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order_usdt) @@ -1630,14 +1630,14 @@ def test_exit_positions(mocker, default_conf, limit_buy_order_usdt, caplog) -> N assert n == 0 -def test_exit_positions_exception(mocker, default_conf, limit_buy_order_usdt, caplog) -> None: - freqtrade = get_patched_freqtradebot(mocker, default_conf) +def test_exit_positions_exception(mocker, default_conf_usdt, limit_buy_order_usdt, caplog) -> None: + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order_usdt) trade = MagicMock() trade.open_order_id = None trade.open_fee = 0.001 - trade.pair = 'ETH/BTC' + trade.pair = 'ETH/USDT' trades = [trade] # Test raise of DependencyException exception @@ -1647,11 +1647,11 @@ def test_exit_positions_exception(mocker, default_conf, limit_buy_order_usdt, ca ) n = freqtrade.exit_positions(trades) assert n == 0 - assert log_has('Unable to sell trade ETH/BTC: ', caplog) + assert log_has('Unable to sell trade ETH/USDT: ', caplog) -def test_update_trade_state(mocker, default_conf, limit_buy_order_usdt, caplog) -> None: - freqtrade = get_patched_freqtradebot(mocker, default_conf) +def test_update_trade_state(mocker, default_conf_usdt, limit_buy_order_usdt, caplog) -> None: + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True)) mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order_usdt) @@ -1727,9 +1727,9 @@ def test_update_trade_state_withorderdict(default_conf_usdt, trades_for_order, l assert log_has_re(r'Applying fee on amount for .*', caplog) -def test_update_trade_state_exception(mocker, default_conf, +def test_update_trade_state_exception(mocker, default_conf_usdt, limit_buy_order_usdt, caplog) -> None: - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order_usdt) trade = MagicMock() @@ -1745,8 +1745,8 @@ def test_update_trade_state_exception(mocker, default_conf, assert log_has('Could not update trade amount: ', caplog) -def test_update_trade_state_orderexception(mocker, default_conf, caplog) -> None: - freqtrade = get_patched_freqtradebot(mocker, default_conf) +def test_update_trade_state_orderexception(mocker, default_conf_usdt, caplog) -> None: + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) mocker.patch('freqtrade.exchange.Exchange.fetch_order', MagicMock(side_effect=InvalidOrderException)) @@ -1761,7 +1761,7 @@ def test_update_trade_state_orderexception(mocker, default_conf, caplog) -> None 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_usdt_open, +def test_update_trade_state_sell(default_conf_usdt, trades_for_order, limit_sell_order_usdt_open, limit_sell_order_usdt, mocker): mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order) # fetch_order should not be called!! @@ -1771,7 +1771,7 @@ def test_update_trade_state_sell(default_conf, trades_for_order, limit_sell_orde patch_exchange(mocker) amount = limit_sell_order_usdt["amount"] - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) wallet_mock.reset_mock() trade = Trade( pair='LTC/ETH', @@ -1796,7 +1796,7 @@ def test_update_trade_state_sell(default_conf, trades_for_order, limit_sell_orde assert order.status == 'closed' -def test_handle_trade(default_conf, limit_buy_order_usdt, limit_sell_order_usdt_open, limit_sell_order_usdt, +def test_handle_trade(default_conf_usdt, limit_buy_order_usdt, limit_sell_order_usdt_open, limit_sell_order_usdt, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) @@ -1813,7 +1813,7 @@ def test_handle_trade(default_conf, limit_buy_order_usdt, limit_sell_order_usdt_ ]), get_fee=fee, ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) freqtrade.enter_positions() @@ -1839,13 +1839,13 @@ def test_handle_trade(default_conf, limit_buy_order_usdt, limit_sell_order_usdt_ assert trade.close_date is not None -def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_usdt_open, +def test_handle_overlapping_signals(default_conf_usdt, ticker_usdt, limit_buy_order_usdt_open, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, create_order=MagicMock(side_effect=[ limit_buy_order_usdt_open, {'id': 1234553382}, @@ -1853,7 +1853,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_usdt_o get_fee=fee, ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade, value=(True, True, None)) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) @@ -1894,14 +1894,14 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_usdt_o assert freqtrade.handle_trade(trades[0]) is True -def test_handle_trade_roi(default_conf, ticker, limit_buy_order_usdt_open, +def test_handle_trade_roi(default_conf_usdt, ticker_usdt, limit_buy_order_usdt_open, fee, mocker, caplog) -> None: caplog.set_level(logging.DEBUG) patch_RPCManager(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, create_order=MagicMock(side_effect=[ limit_buy_order_usdt_open, {'id': 1234553382}, @@ -1909,7 +1909,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order_usdt_open, get_fee=fee, ) - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=True) @@ -1925,18 +1925,18 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order_usdt_open, # if ROI is reached we must sell patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) - assert log_has("ETH/BTC - Required profit reached. sell_type=SellType.ROI", + assert log_has("ETH/USDT - Required profit reached. sell_type=SellType.ROI", caplog) -def test_handle_trade_use_sell_signal(default_conf, ticker, limit_buy_order_usdt_open, +def test_handle_trade_use_sell_signal(default_conf_usdt, ticker_usdt, limit_buy_order_usdt_open, limit_sell_order_usdt_open, fee, mocker, caplog) -> None: # use_sell_signal is True buy default caplog.set_level(logging.DEBUG) patch_RPCManager(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, create_order=MagicMock(side_effect=[ limit_buy_order_usdt_open, limit_sell_order_usdt_open, @@ -1944,7 +1944,7 @@ def test_handle_trade_use_sell_signal(default_conf, ticker, limit_buy_order_usdt get_fee=fee, ) - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) freqtrade.enter_positions() @@ -1957,21 +1957,21 @@ def test_handle_trade_use_sell_signal(default_conf, ticker, limit_buy_order_usdt patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) - assert log_has("ETH/BTC - Sell signal received. sell_type=SellType.SELL_SIGNAL", + assert log_has("ETH/USDT - Sell signal received. sell_type=SellType.SELL_SIGNAL", caplog) -def test_close_trade(default_conf, ticker, limit_buy_order_usdt, limit_buy_order_usdt_open, limit_sell_order_usdt, +def test_close_trade(default_conf_usdt, ticker_usdt, limit_buy_order_usdt, limit_buy_order_usdt_open, limit_sell_order_usdt, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, create_order=MagicMock(return_value=limit_buy_order_usdt_open), get_fee=fee, ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) # Create trade and sell it @@ -1988,8 +1988,8 @@ def test_close_trade(default_conf, ticker, limit_buy_order_usdt, limit_buy_order freqtrade.handle_trade(trade) -def test_bot_loop_start_called_once(mocker, default_conf, caplog): - ftbot = get_patched_freqtradebot(mocker, default_conf) +def test_bot_loop_start_called_once(mocker, default_conf_usdt, caplog): + ftbot = get_patched_freqtradebot(mocker, default_conf_usdt) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.create_trade') patch_get_signal(ftbot) ftbot.strategy.bot_loop_start = MagicMock(side_effect=ValueError) @@ -2001,9 +2001,9 @@ def test_bot_loop_start_called_once(mocker, default_conf, caplog): assert ftbot.strategy.analyze.call_count == 1 -def test_check_handle_timedout_buy_usercustom(default_conf, ticker, limit_buy_order_old, open_trade, +def test_check_handle_timedout_buy_usercustom(default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade, fee, mocker) -> None: - default_conf["unfilledtimeout"] = {"buy": 1400, "sell": 30} + default_conf_usdt["unfilledtimeout"] = {"buy": 1400, "sell": 30} rpc_mock = patch_RPCManager(mocker) cancel_order_mock = MagicMock(return_value=limit_buy_order_old) @@ -2014,13 +2014,13 @@ def test_check_handle_timedout_buy_usercustom(default_conf, ticker, limit_buy_or patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, fetch_order=MagicMock(return_value=limit_buy_order_old), cancel_order_with_result=cancel_order_wr_mock, cancel_order=cancel_order_mock, get_fee=fee ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) Trade.query.session.add(open_trade) @@ -2057,7 +2057,7 @@ def test_check_handle_timedout_buy_usercustom(default_conf, ticker, limit_buy_or assert freqtrade.strategy.check_buy_timeout.call_count == 1 -def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, open_trade, +def test_check_handle_timedout_buy(default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade, fee, mocker) -> None: rpc_mock = patch_RPCManager(mocker) limit_buy_cancel = deepcopy(limit_buy_order_old) @@ -2066,12 +2066,12 @@ def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, op patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, fetch_order=MagicMock(return_value=limit_buy_order_old), cancel_order_with_result=cancel_order_mock, get_fee=fee ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) Trade.query.session.add(open_trade) @@ -2087,7 +2087,7 @@ def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, op assert freqtrade.strategy.check_buy_timeout.call_count == 0 -def test_check_handle_cancelled_buy(default_conf, ticker, limit_buy_order_old, open_trade, +def test_check_handle_cancelled_buy(default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade, fee, mocker, caplog) -> None: """ Handle Buy order cancelled on exchange""" rpc_mock = patch_RPCManager(mocker) @@ -2096,12 +2096,12 @@ def test_check_handle_cancelled_buy(default_conf, ticker, limit_buy_order_old, o limit_buy_order_old.update({"status": "canceled", 'filled': 0.0}) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, fetch_order=MagicMock(return_value=limit_buy_order_old), cancel_order=cancel_order_mock, get_fee=fee ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) Trade.query.session.add(open_trade) @@ -2115,7 +2115,7 @@ def test_check_handle_cancelled_buy(default_conf, ticker, limit_buy_order_old, o assert log_has_re("Buy order cancelled on exchange for Trade.*", caplog) -def test_check_handle_timedout_buy_exception(default_conf, ticker, limit_buy_order_old, open_trade, +def test_check_handle_timedout_buy_exception(default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade, fee, mocker) -> None: rpc_mock = patch_RPCManager(mocker) cancel_order_mock = MagicMock() @@ -2123,12 +2123,12 @@ def test_check_handle_timedout_buy_exception(default_conf, ticker, limit_buy_ord mocker.patch.multiple( 'freqtrade.exchange.Exchange', validate_pairs=MagicMock(), - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, fetch_order=MagicMock(side_effect=ExchangeError), cancel_order=cancel_order_mock, get_fee=fee ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) Trade.query.session.add(open_trade) @@ -2141,19 +2141,19 @@ def test_check_handle_timedout_buy_exception(default_conf, ticker, limit_buy_ord assert nb_trades == 1 -def test_check_handle_timedout_sell_usercustom(default_conf, ticker, limit_sell_order_old, mocker, +def test_check_handle_timedout_sell_usercustom(default_conf_usdt, ticker_usdt, limit_sell_order_old, mocker, open_trade) -> None: - default_conf["unfilledtimeout"] = {"buy": 1440, "sell": 1440} + default_conf_usdt["unfilledtimeout"] = {"buy": 1440, "sell": 1440} rpc_mock = patch_RPCManager(mocker) cancel_order_mock = MagicMock() patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, fetch_order=MagicMock(return_value=limit_sell_order_old), cancel_order=cancel_order_mock ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) open_trade.open_date = arrow.utcnow().shift(hours=-5).datetime open_trade.close_date = arrow.utcnow().shift(minutes=-601).datetime @@ -2190,18 +2190,18 @@ def test_check_handle_timedout_sell_usercustom(default_conf, ticker, limit_sell_ assert freqtrade.strategy.check_sell_timeout.call_count == 1 -def test_check_handle_timedout_sell(default_conf, ticker, limit_sell_order_old, mocker, +def test_check_handle_timedout_sell(default_conf_usdt, ticker_usdt, limit_sell_order_old, mocker, open_trade) -> None: rpc_mock = patch_RPCManager(mocker) cancel_order_mock = MagicMock() patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, fetch_order=MagicMock(return_value=limit_sell_order_old), cancel_order=cancel_order_mock ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) open_trade.open_date = arrow.utcnow().shift(hours=-5).datetime open_trade.close_date = arrow.utcnow().shift(minutes=-601).datetime @@ -2220,7 +2220,7 @@ def test_check_handle_timedout_sell(default_conf, ticker, limit_sell_order_old, assert freqtrade.strategy.check_sell_timeout.call_count == 0 -def test_check_handle_cancelled_sell(default_conf, ticker, limit_sell_order_old, open_trade, +def test_check_handle_cancelled_sell(default_conf_usdt, ticker_usdt, limit_sell_order_old, open_trade, mocker, caplog) -> None: """ Handle sell order cancelled on exchange""" rpc_mock = patch_RPCManager(mocker) @@ -2229,11 +2229,11 @@ def test_check_handle_cancelled_sell(default_conf, ticker, limit_sell_order_old, patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, fetch_order=MagicMock(return_value=limit_sell_order_old), cancel_order_with_result=cancel_order_mock ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) open_trade.open_date = arrow.utcnow().shift(hours=-5).datetime open_trade.close_date = arrow.utcnow().shift(minutes=-601).datetime @@ -2249,7 +2249,7 @@ def test_check_handle_cancelled_sell(default_conf, ticker, limit_sell_order_old, assert log_has_re("Sell order cancelled 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_usdt, ticker_usdt, limit_buy_order_old_partial, open_trade, mocker) -> None: rpc_mock = patch_RPCManager(mocker) limit_buy_canceled = deepcopy(limit_buy_order_old_partial) @@ -2259,11 +2259,11 @@ def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, fetch_order=MagicMock(return_value=limit_buy_order_old_partial), cancel_order_with_result=cancel_order_mock ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) Trade.query.session.add(open_trade) @@ -2278,7 +2278,7 @@ def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old assert trades[0].stake_amount == open_trade.open_rate * trades[0].amount -def test_check_handle_timedout_partial_fee(default_conf, ticker, open_trade, caplog, fee, +def test_check_handle_timedout_partial_fee(default_conf_usdt, ticker_usdt, open_trade, caplog, fee, limit_buy_order_old_partial, trades_for_order, limit_buy_order_old_partial_canceled, mocker) -> None: rpc_mock = patch_RPCManager(mocker) @@ -2287,12 +2287,12 @@ def test_check_handle_timedout_partial_fee(default_conf, ticker, open_trade, cap patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, fetch_order=MagicMock(return_value=limit_buy_order_old_partial), cancel_order_with_result=cancel_order_mock, get_trades_for_order=MagicMock(return_value=trades_for_order), ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) assert open_trade.amount == limit_buy_order_old_partial['amount'] @@ -2317,7 +2317,7 @@ def test_check_handle_timedout_partial_fee(default_conf, ticker, open_trade, cap assert pytest.approx(trades[0].fee_open) == 0.001 -def test_check_handle_timedout_partial_except(default_conf, ticker, open_trade, caplog, fee, +def test_check_handle_timedout_partial_except(default_conf_usdt, ticker_usdt, open_trade, caplog, fee, limit_buy_order_old_partial, trades_for_order, limit_buy_order_old_partial_canceled, mocker) -> None: rpc_mock = patch_RPCManager(mocker) @@ -2325,14 +2325,14 @@ def test_check_handle_timedout_partial_except(default_conf, ticker, open_trade, patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, fetch_order=MagicMock(return_value=limit_buy_order_old_partial), cancel_order_with_result=cancel_order_mock, get_trades_for_order=MagicMock(return_value=trades_for_order), ) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', MagicMock(side_effect=DependencyException)) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) assert open_trade.amount == limit_buy_order_old_partial['amount'] @@ -2357,7 +2357,7 @@ def test_check_handle_timedout_partial_except(default_conf, ticker, open_trade, assert trades[0].fee_open == fee() -def test_check_handle_timedout_exception(default_conf, ticker, open_trade_usdt, mocker, caplog) -> None: +def test_check_handle_timedout_exception(default_conf_usdt, ticker_usdt, open_trade_usdt, mocker, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) cancel_order_mock = MagicMock() @@ -2369,11 +2369,11 @@ def test_check_handle_timedout_exception(default_conf, ticker, open_trade_usdt, ) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, fetch_order=MagicMock(side_effect=ExchangeError('Oh snap')), cancel_order=cancel_order_mock ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) Trade.query.session.add(open_trade_usdt) @@ -2385,7 +2385,7 @@ def test_check_handle_timedout_exception(default_conf, ticker, open_trade_usdt, caplog) -def test_handle_cancel_enter(mocker, caplog, default_conf, limit_buy_order_usdt) -> None: +def test_handle_cancel_enter(mocker, caplog, default_conf_usdt, limit_buy_order_usdt) -> None: patch_RPCManager(mocker) patch_exchange(mocker) cancel_buy_order = deepcopy(limit_buy_order_usdt) @@ -2395,7 +2395,7 @@ def test_handle_cancel_enter(mocker, caplog, default_conf, limit_buy_order_usdt) cancel_order_mock = MagicMock(return_value=cancel_buy_order) mocker.patch('freqtrade.exchange.Exchange.cancel_order_with_result', cancel_order_mock) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) freqtrade._notify_enter_cancel = MagicMock() trade = MagicMock() @@ -2430,7 +2430,7 @@ def test_handle_cancel_enter(mocker, caplog, default_conf, limit_buy_order_usdt) @pytest.mark.parametrize("limit_buy_order_canceled_empty", ['binance', 'ftx', 'kraken', 'bittrex'], indirect=['limit_buy_order_canceled_empty']) -def test_handle_cancel_enter_exchanges(mocker, caplog, default_conf, +def test_handle_cancel_enter_exchanges(mocker, caplog, default_conf_usdt, limit_buy_order_canceled_empty) -> None: patch_RPCManager(mocker) patch_exchange(mocker) @@ -2438,7 +2438,7 @@ def test_handle_cancel_enter_exchanges(mocker, caplog, default_conf, 'freqtrade.exchange.Exchange.cancel_order_with_result', return_value=limit_buy_order_canceled_empty) nofiy_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot._notify_enter_cancel') - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) reason = CANCEL_REASON['TIMEOUT'] trade = MagicMock() @@ -2455,7 +2455,7 @@ def test_handle_cancel_enter_exchanges(mocker, caplog, default_conf, 'String Return value', 123 ]) -def test_handle_cancel_enter_corder_empty(mocker, default_conf, limit_buy_order_usdt, +def test_handle_cancel_enter_corder_empty(mocker, default_conf_usdt, limit_buy_order_usdt, cancelorder) -> None: patch_RPCManager(mocker) patch_exchange(mocker) @@ -2465,7 +2465,7 @@ def test_handle_cancel_enter_corder_empty(mocker, default_conf, limit_buy_order_ cancel_order=cancel_order_mock ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) freqtrade._notify_enter_cancel = MagicMock() trade = MagicMock() @@ -2483,7 +2483,7 @@ def test_handle_cancel_enter_corder_empty(mocker, default_conf, limit_buy_order_ assert cancel_order_mock.call_count == 1 -def test_handle_cancel_exit_limit(mocker, default_conf, fee) -> None: +def test_handle_cancel_exit_limit(mocker, default_conf_usdt, fee) -> None: send_msg_mock = patch_RPCManager(mocker) patch_exchange(mocker) cancel_order_mock = MagicMock() @@ -2493,7 +2493,7 @@ def test_handle_cancel_exit_limit(mocker, default_conf, fee) -> None: ) mocker.patch('freqtrade.exchange.Exchange.get_rate', return_value=0.245441) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) trade = Trade( pair='LTC/ETH', @@ -2528,13 +2528,13 @@ def test_handle_cancel_exit_limit(mocker, default_conf, fee) -> None: assert send_msg_mock.call_count == 1 -def test_handle_cancel_exit_cancel_exception(mocker, default_conf) -> None: +def test_handle_cancel_exit_cancel_exception(mocker, default_conf_usdt) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch( 'freqtrade.exchange.Exchange.cancel_order_with_result', side_effect=InvalidOrderException()) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) trade = MagicMock() reason = CANCEL_REASON['TIMEOUT'] @@ -2544,17 +2544,17 @@ def test_handle_cancel_exit_cancel_exception(mocker, default_conf) -> None: assert freqtrade.handle_cancel_exit(trade, order, reason) == 'error cancelling order' -def test_execute_trade_exit_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> None: +def test_execute_trade_exit_up(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_up, mocker) -> None: rpc_mock = patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, get_fee=fee, _is_dry_limit_order_filled=MagicMock(return_value=False), ) - patch_whitelist(mocker, default_conf) - freqtrade = FreqtradeBot(default_conf) + patch_whitelist(mocker, default_conf_usdt) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=False) @@ -2569,10 +2569,10 @@ def test_execute_trade_exit_up(default_conf, ticker, fee, ticker_sell_up, mocker # Increase the price and sell it mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker_sell_up + fetch_ticker=ticker_usdt_sell_up ) # Prevented sell ... - freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], + freqtrade.execute_trade_exit(trade=trade, limit=ticker_usdt_sell_up()['bid'], sell_reason=SellCheckTuple(sell_type=SellType.ROI)) assert rpc_mock.call_count == 0 assert freqtrade.strategy.confirm_trade_exit.call_count == 1 @@ -2580,7 +2580,7 @@ def test_execute_trade_exit_up(default_conf, ticker, fee, ticker_sell_up, mocker # Repatch with true freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=True) - freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], + freqtrade.execute_trade_exit(trade=trade, limit=ticker_usdt_sell_up()['bid'], sell_reason=SellCheckTuple(sell_type=SellType.ROI)) assert freqtrade.strategy.confirm_trade_exit.call_count == 1 @@ -2590,7 +2590,7 @@ def test_execute_trade_exit_up(default_conf, ticker, fee, ticker_sell_up, mocker 'trade_id': 1, 'type': RPCMessageType.SELL, 'exchange': 'Binance', - 'pair': 'ETH/BTC', + 'pair': 'ETH/USDT', 'gain': 'profit', 'limit': 1.172e-05, 'amount': 91.07468123, @@ -2608,17 +2608,17 @@ def test_execute_trade_exit_up(default_conf, ticker, fee, ticker_sell_up, mocker } == last_msg -def test_execute_trade_exit_down(default_conf, ticker, fee, ticker_sell_down, mocker) -> None: +def test_execute_trade_exit_down(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_down, mocker) -> None: rpc_mock = patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, get_fee=fee, _is_dry_limit_order_filled=MagicMock(return_value=False), ) - patch_whitelist(mocker, default_conf) - freqtrade = FreqtradeBot(default_conf) + patch_whitelist(mocker, default_conf_usdt) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) # Create some test data @@ -2630,10 +2630,10 @@ def test_execute_trade_exit_down(default_conf, ticker, fee, ticker_sell_down, mo # Decrease the price and sell it mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker_sell_down + fetch_ticker=ticker_usdt_sell_down ) - freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_down()['bid'], + freqtrade.execute_trade_exit(trade=trade, limit=ticker_usdt_sell_down()['bid'], sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) assert rpc_mock.call_count == 2 @@ -2642,7 +2642,7 @@ def test_execute_trade_exit_down(default_conf, ticker, fee, ticker_sell_down, mo 'type': RPCMessageType.SELL, 'trade_id': 1, 'exchange': 'Binance', - 'pair': 'ETH/BTC', + 'pair': 'ETH/USDT', 'gain': 'loss', 'limit': 1.044e-05, 'amount': 91.07468123, @@ -2660,18 +2660,18 @@ def test_execute_trade_exit_down(default_conf, ticker, fee, ticker_sell_down, mo } == last_msg -def test_execute_trade_exit_custom_exit_price(default_conf, ticker, fee, ticker_sell_up, +def test_execute_trade_exit_custom_exit_price(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_up, mocker) -> None: rpc_mock = patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, get_fee=fee, _is_dry_limit_order_filled=MagicMock(return_value=False), ) - patch_whitelist(mocker, default_conf) - freqtrade = FreqtradeBot(default_conf) + patch_whitelist(mocker, default_conf_usdt) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=False) @@ -2686,7 +2686,7 @@ def test_execute_trade_exit_custom_exit_price(default_conf, ticker, fee, ticker_ # Increase the price and sell it mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker_sell_up + fetch_ticker=ticker_usdt_sell_up ) freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=True) @@ -2694,7 +2694,7 @@ def test_execute_trade_exit_custom_exit_price(default_conf, ticker, fee, ticker_ # Set a custom exit price freqtrade.strategy.custom_exit_price = lambda **kwargs: 1.170e-05 - freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], + freqtrade.execute_trade_exit(trade=trade, limit=ticker_usdt_sell_up()['bid'], sell_reason=SellCheckTuple(sell_type=SellType.SELL_SIGNAL)) # Sell price must be different to default bid price @@ -2707,7 +2707,7 @@ def test_execute_trade_exit_custom_exit_price(default_conf, ticker, fee, ticker_ 'trade_id': 1, 'type': RPCMessageType.SELL, 'exchange': 'Binance', - 'pair': 'ETH/BTC', + 'pair': 'ETH/USDT', 'gain': 'profit', 'limit': 1.170e-05, 'amount': 91.07468123, @@ -2725,18 +2725,18 @@ def test_execute_trade_exit_custom_exit_price(default_conf, ticker, fee, ticker_ } == last_msg -def test_execute_trade_exit_down_stoploss_on_exchange_dry_run(default_conf, ticker, fee, - ticker_sell_down, mocker) -> None: +def test_execute_trade_exit_down_stoploss_on_exchange_dry_run(default_conf_usdt, ticker_usdt, fee, + ticker_usdt_sell_down, mocker) -> None: rpc_mock = patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, get_fee=fee, _is_dry_limit_order_filled=MagicMock(return_value=False), ) - patch_whitelist(mocker, default_conf) - freqtrade = FreqtradeBot(default_conf) + patch_whitelist(mocker, default_conf_usdt) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) # Create some test data @@ -2748,15 +2748,15 @@ def test_execute_trade_exit_down_stoploss_on_exchange_dry_run(default_conf, tick # Decrease the price and sell it mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker_sell_down + fetch_ticker=ticker_usdt_sell_down ) - default_conf['dry_run'] = True + default_conf_usdt['dry_run'] = True freqtrade.strategy.order_types['stoploss_on_exchange'] = True # Setting trade stoploss to 0.01 trade.stop_loss = 0.00001099 * 0.99 - freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_down()['bid'], + freqtrade.execute_trade_exit(trade=trade, limit=ticker_usdt_sell_down()['bid'], sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) assert rpc_mock.call_count == 2 @@ -2766,7 +2766,7 @@ def test_execute_trade_exit_down_stoploss_on_exchange_dry_run(default_conf, tick 'type': RPCMessageType.SELL, 'trade_id': 1, 'exchange': 'Binance', - 'pair': 'ETH/BTC', + 'pair': 'ETH/USDT', 'gain': 'loss', 'limit': 1.08801e-05, 'amount': 91.07468123, @@ -2785,8 +2785,8 @@ def test_execute_trade_exit_down_stoploss_on_exchange_dry_run(default_conf, tick def test_execute_trade_exit_sloe_cancel_exception( - mocker, default_conf, ticker, fee, caplog) -> None: - freqtrade = get_patched_freqtradebot(mocker, default_conf) + mocker, default_conf_usdt, ticker_usdt, fee, caplog) -> None: + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order', side_effect=InvalidOrderException()) mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=300)) @@ -2797,7 +2797,7 @@ def test_execute_trade_exit_sloe_cancel_exception( patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, get_fee=fee, create_order=create_order_mock, ) @@ -2818,10 +2818,10 @@ def test_execute_trade_exit_sloe_cancel_exception( assert log_has('Could not cancel stoploss order abcd', caplog) -def test_execute_trade_exit_with_stoploss_on_exchange(default_conf, ticker, fee, ticker_sell_up, +def test_execute_trade_exit_with_stoploss_on_exchange(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_up, mocker) -> None: - default_conf['exchange']['name'] = 'binance' + default_conf_usdt['exchange']['name'] = 'binance' rpc_mock = patch_RPCManager(mocker) patch_exchange(mocker) stoploss = MagicMock(return_value={ @@ -2834,7 +2834,7 @@ def test_execute_trade_exit_with_stoploss_on_exchange(default_conf, ticker, fee, cancel_order = MagicMock(return_value=True) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, get_fee=fee, amount_to_precision=lambda s, x, y: y, price_to_precision=lambda s, x, y: y, @@ -2843,7 +2843,7 @@ def test_execute_trade_exit_with_stoploss_on_exchange(default_conf, ticker, fee, _is_dry_limit_order_filled=MagicMock(side_effect=[True, False]), ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) freqtrade.strategy.order_types['stoploss_on_exchange'] = True patch_get_signal(freqtrade) @@ -2860,10 +2860,10 @@ def test_execute_trade_exit_with_stoploss_on_exchange(default_conf, ticker, fee, # Increase the price and sell it mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker_sell_up + fetch_ticker=ticker_usdt_sell_up ) - freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], + freqtrade.execute_trade_exit(trade=trade, limit=ticker_usdt_sell_up()['bid'], sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) trade = Trade.query.first() @@ -2872,14 +2872,14 @@ def test_execute_trade_exit_with_stoploss_on_exchange(default_conf, ticker, fee, assert rpc_mock.call_count == 3 -def test_may_execute_trade_exit_after_stoploss_on_exchange_hit(default_conf, ticker, fee, +def test_may_execute_trade_exit_after_stoploss_on_exchange_hit(default_conf_usdt, ticker_usdt, fee, mocker) -> None: - default_conf['exchange']['name'] = 'binance' + default_conf_usdt['exchange']['name'] = 'binance' rpc_mock = patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, get_fee=fee, amount_to_precision=lambda s, x, y: y, price_to_precision=lambda s, x, y: y, @@ -2895,7 +2895,7 @@ def test_may_execute_trade_exit_after_stoploss_on_exchange_hit(default_conf, tic mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) freqtrade.strategy.order_types['stoploss_on_exchange'] = True patch_get_signal(freqtrade) @@ -2944,18 +2944,18 @@ def test_may_execute_trade_exit_after_stoploss_on_exchange_hit(default_conf, tic assert rpc_mock.call_args_list[2][0][0]['type'] == RPCMessageType.SELL -def test_execute_trade_exit_market_order(default_conf, ticker, fee, - ticker_sell_up, mocker) -> None: +def test_execute_trade_exit_market_order(default_conf_usdt, ticker_usdt, fee, + ticker_usdt_sell_up, mocker) -> None: rpc_mock = patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, get_fee=fee, _is_dry_limit_order_filled=MagicMock(return_value=False), ) - patch_whitelist(mocker, default_conf) - freqtrade = FreqtradeBot(default_conf) + patch_whitelist(mocker, default_conf_usdt) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) # Create some test data @@ -2967,11 +2967,11 @@ def test_execute_trade_exit_market_order(default_conf, ticker, fee, # Increase the price and sell it mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker_sell_up + fetch_ticker=ticker_usdt_sell_up ) freqtrade.config['order_types']['sell'] = 'market' - freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], + freqtrade.execute_trade_exit(trade=trade, limit=ticker_usdt_sell_up()['bid'], sell_reason=SellCheckTuple(sell_type=SellType.ROI)) assert not trade.is_open @@ -2983,7 +2983,7 @@ def test_execute_trade_exit_market_order(default_conf, ticker, fee, 'type': RPCMessageType.SELL, 'trade_id': 1, 'exchange': 'Binance', - 'pair': 'ETH/BTC', + 'pair': 'ETH/USDT', 'gain': 'profit', 'limit': 1.172e-05, 'amount': 91.07468123, @@ -3002,13 +3002,13 @@ def test_execute_trade_exit_market_order(default_conf, ticker, fee, } == last_msg -def test_execute_trade_exit_insufficient_funds_error(default_conf, ticker, fee, - ticker_sell_up, mocker) -> None: - freqtrade = get_patched_freqtradebot(mocker, default_conf) +def test_execute_trade_exit_insufficient_funds_error(default_conf_usdt, ticker_usdt, fee, + ticker_usdt_sell_up, mocker) -> None: + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) mock_insuf = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_insufficient_funds') mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, get_fee=fee, create_order=MagicMock(side_effect=[ {'id': 1234553382}, @@ -3026,11 +3026,11 @@ def test_execute_trade_exit_insufficient_funds_error(default_conf, ticker, fee, # Increase the price and sell it mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker_sell_up + fetch_ticker=ticker_usdt_sell_up ) sell_reason = SellCheckTuple(sell_type=SellType.ROI) - assert not freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], + assert not freqtrade.execute_trade_exit(trade=trade, limit=ticker_usdt_sell_up()['bid'], sell_reason=sell_reason) assert mock_insuf.call_count == 1 @@ -3047,7 +3047,7 @@ def test_execute_trade_exit_insufficient_funds_error(default_conf, ticker, fee, (False, 0.00000172, 0.00000173, True, False, SellType.SELL_SIGNAL.value), ]) def test_sell_profit_only( - default_conf, limit_buy_order_usdt, limit_buy_order_usdt_open, + default_conf_usdt, limit_buy_order_usdt, limit_buy_order_usdt_open, fee, mocker, profit_only, bid, ask, handle_first, handle_second, sell_type) -> None: patch_RPCManager(mocker) patch_exchange(mocker) @@ -3064,12 +3064,12 @@ def test_sell_profit_only( ]), get_fee=fee, ) - default_conf.update({ + default_conf_usdt.update({ 'use_sell_signal': True, 'sell_profit_only': profit_only, 'sell_profit_offset': 0.1, }) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) if sell_type == SellType.SELL_SIGNAL.value: freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) @@ -3089,7 +3089,7 @@ def test_sell_profit_only( assert freqtrade.handle_trade(trade) is True -def test_sell_not_enough_balance(default_conf, limit_buy_order_usdt, limit_buy_order_usdt_open, +def test_sell_not_enough_balance(default_conf_usdt, limit_buy_order_usdt, limit_buy_order_usdt_open, fee, mocker, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) @@ -3107,7 +3107,7 @@ def test_sell_not_enough_balance(default_conf, limit_buy_order_usdt, limit_buy_o get_fee=fee, ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) @@ -3128,7 +3128,7 @@ def test_sell_not_enough_balance(default_conf, limit_buy_order_usdt, limit_buy_o (95.29, False), (91.29, True) ]) -def test__safe_exit_amount(default_conf, fee, caplog, mocker, amount_wallet, has_err): +def test__safe_exit_amount(default_conf_usdt, fee, caplog, mocker, amount_wallet, has_err): patch_RPCManager(mocker) patch_exchange(mocker) amount = 95.33 @@ -3144,7 +3144,7 @@ def test__safe_exit_amount(default_conf, fee, caplog, mocker, amount_wallet, has fee_open=fee.return_value, fee_close=fee.return_value, ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) if has_err: with pytest.raises(DependencyException, match=r"Not enough amount to sell."): @@ -3161,15 +3161,15 @@ def test__safe_exit_amount(default_conf, fee, caplog, mocker, amount_wallet, has assert wallet_update.call_count == 1 -def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplog) -> None: +def test_locked_pairs(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_down, mocker, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, get_fee=fee, ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) # Create some test data @@ -3181,12 +3181,12 @@ def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplo # Decrease the price and sell it mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker_sell_down + fetch_ticker=ticker_usdt_sell_down ) - freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_down()['bid'], + freqtrade.execute_trade_exit(trade=trade, limit=ticker_usdt_sell_down()['bid'], sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) - trade.close(ticker_sell_down()['bid']) + trade.close(ticker_usdt_sell_down()['bid']) assert freqtrade.strategy.is_pair_locked(trade.pair) # reinit - should buy other pair. @@ -3196,7 +3196,7 @@ def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplo assert log_has_re(f"Pair {trade.pair} is still locked.*", caplog) -def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order_usdt, limit_buy_order_usdt_open, +def test_ignore_roi_if_buy_signal(default_conf_usdt, limit_buy_order_usdt, limit_buy_order_usdt_open, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) @@ -3213,9 +3213,9 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order_usdt, limit_buy_ ]), get_fee=fee, ) - default_conf['ignore_roi_if_buy_signal'] = True + default_conf_usdt['ignore_roi_if_buy_signal'] = True - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=True) @@ -3233,7 +3233,7 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order_usdt, limit_buy_ assert trade.sell_reason == SellType.ROI.value -def test_trailing_stop_loss(default_conf, limit_buy_order_usdt_open, limit_buy_order_usdt, +def test_trailing_stop_loss(default_conf_usdt, limit_buy_order_usdt_open, limit_buy_order_usdt, fee, caplog, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) @@ -3250,9 +3250,9 @@ def test_trailing_stop_loss(default_conf, limit_buy_order_usdt_open, limit_buy_o ]), get_fee=fee, ) - default_conf['trailing_stop'] = True - patch_whitelist(mocker, default_conf) - freqtrade = FreqtradeBot(default_conf) + default_conf_usdt['trailing_stop'] = True + patch_whitelist(mocker, default_conf_usdt) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) @@ -3260,7 +3260,7 @@ def test_trailing_stop_loss(default_conf, limit_buy_order_usdt_open, limit_buy_o trade = Trade.query.first() assert freqtrade.handle_trade(trade) is False - # Raise ticker above buy price + # Raise ticker_usdt above buy price mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={ 'bid': 2.0 * 1.5, @@ -3283,7 +3283,7 @@ def test_trailing_stop_loss(default_conf, limit_buy_order_usdt_open, limit_buy_o # Sell as trailing-stop is reached assert freqtrade.handle_trade(trade) is True # TODO: Does this make sense? How is stoploss 2.7? - assert log_has("ETH/BTC - HIT STOP: current price at 2.200000, stoploss is 2.700000, " + assert log_has("ETH/USDT - HIT STOP: current price at 2.200000, stoploss is 2.700000, " "initial stoploss was at 1.800000, trade opened at 2.000000", caplog) assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value @@ -3293,7 +3293,7 @@ def test_trailing_stop_loss(default_conf, limit_buy_order_usdt_open, limit_buy_o (0.011, False, 2.0394), (0.055, True, 1.8), ]) -def test_trailing_stop_loss_positive(default_conf, limit_buy_order_usdt, limit_buy_order_usdt_open, +def test_trailing_stop_loss_positive(default_conf_usdt, limit_buy_order_usdt, limit_buy_order_usdt_open, offset, fee, caplog, mocker, trail_if_reached, second_sl) -> None: buy_price = limit_buy_order_usdt['price'] patch_RPCManager(mocker) @@ -3311,14 +3311,14 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order_usdt, limit_b ]), get_fee=fee, ) - default_conf['trailing_stop'] = True - default_conf['trailing_stop_positive'] = 0.01 + default_conf_usdt['trailing_stop'] = True + default_conf_usdt['trailing_stop_positive'] = 0.01 if offset: - default_conf['trailing_stop_positive_offset'] = offset - default_conf['trailing_only_offset_is_reached'] = trail_if_reached - patch_whitelist(mocker, default_conf) + default_conf_usdt['trailing_stop_positive_offset'] = offset + default_conf_usdt['trailing_only_offset_is_reached'] = trail_if_reached + patch_whitelist(mocker, default_conf_usdt) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) freqtrade.enter_positions() @@ -3329,7 +3329,7 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order_usdt, limit_b # stop-loss not reached assert freqtrade.handle_trade(trade) is False - # Raise ticker above buy price + # Raise ticker_usdt above buy price mocker.patch( 'freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={ @@ -3341,13 +3341,13 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order_usdt, limit_b # stop-loss not reached, adjusted stoploss assert freqtrade.handle_trade(trade) is False # TODO: is 0.0249% correct? Shouldn't it be higher? - caplog_text = f"ETH/BTC - Using positive stoploss: 0.01 offset: {offset} profit: 0.0249%" + caplog_text = f"ETH/USDT - Using positive stoploss: 0.01 offset: {offset} profit: 0.0249%" if trail_if_reached: assert not log_has(caplog_text, caplog) - assert not log_has("ETH/BTC - Adjusting stoploss...", caplog) + assert not log_has("ETH/USDT - Adjusting stoploss...", caplog) else: assert log_has(caplog_text, caplog) - assert log_has("ETH/BTC - Adjusting stoploss...", caplog) + assert log_has("ETH/USDT - Adjusting stoploss...", caplog) assert trade.stop_loss == second_sl caplog.clear() @@ -3361,10 +3361,10 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order_usdt, limit_b ) assert freqtrade.handle_trade(trade) is False assert log_has( - f"ETH/BTC - Using positive stoploss: 0.01 offset: {offset} profit: 0.0572%", + f"ETH/USDT - Using positive stoploss: 0.01 offset: {offset} profit: 0.0572%", caplog ) - assert log_has("ETH/BTC - Adjusting stoploss...", caplog) + assert log_has("ETH/USDT - Adjusting stoploss...", caplog) mocker.patch( 'freqtrade.exchange.Exchange.fetch_ticker', @@ -3377,13 +3377,13 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order_usdt, limit_b # Lower price again (but still positive) assert freqtrade.handle_trade(trade) is True assert log_has( - f"ETH/BTC - HIT STOP: current price at {buy_price + 0.02:.6f}, " + f"ETH/USDT - HIT STOP: current price at {buy_price + 0.02:.6f}, " f"stoploss is {trade.stop_loss:.6f}, " f"initial stoploss was at 1.800000, trade opened at 2.000000", caplog) assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value -def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order_usdt, limit_buy_order_usdt_open, +def test_disable_ignore_roi_if_buy_signal(default_conf_usdt, limit_buy_order_usdt, limit_buy_order_usdt_open, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) @@ -3402,10 +3402,10 @@ def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order_usdt, li get_fee=fee, _is_dry_limit_order_filled=MagicMock(return_value=False), ) - default_conf['ask_strategy'] = { + default_conf_usdt['ask_strategy'] = { 'ignore_roi_if_buy_signal': False } - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=True) @@ -3423,7 +3423,7 @@ def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order_usdt, li assert trade.sell_reason == SellType.SELL_SIGNAL.value -def test_get_real_amount_quote(default_conf, trades_for_order, buy_order_fee, fee, caplog, mocker): +def test_get_real_amount_quote(default_conf_usdt, trades_for_order, buy_order_fee, fee, caplog, mocker): mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order) amount = sum(x['amount'] for x in trades_for_order) trade = Trade( @@ -3435,7 +3435,7 @@ def test_get_real_amount_quote(default_conf, trades_for_order, buy_order_fee, fe fee_close=fee.return_value, open_order_id="123456" ) - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) # Amount is reduced by "fee" assert freqtrade.get_real_amount(trade, buy_order_fee) == amount - (amount * 0.001) @@ -3444,7 +3444,7 @@ def test_get_real_amount_quote(default_conf, trades_for_order, buy_order_fee, fe caplog) -def test_get_real_amount_quote_dust(default_conf, trades_for_order, buy_order_fee, fee, +def test_get_real_amount_quote_dust(default_conf_usdt, trades_for_order, buy_order_fee, fee, caplog, mocker): mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order) walletmock = mocker.patch('freqtrade.wallets.Wallets.update') @@ -3459,7 +3459,7 @@ def test_get_real_amount_quote_dust(default_conf, trades_for_order, buy_order_fe fee_close=fee.return_value, open_order_id="123456" ) - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) walletmock.reset_mock() # Amount is kept as is @@ -3469,7 +3469,7 @@ def test_get_real_amount_quote_dust(default_conf, trades_for_order, buy_order_fe '- Eating Fee 0.008 into dust', caplog) -def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker, fee): +def test_get_real_amount_no_trade(default_conf_usdt, buy_order_fee, caplog, mocker, fee): mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[]) amount = buy_order_fee['amount'] @@ -3482,7 +3482,7 @@ def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker, f fee_close=fee.return_value, open_order_id="123456" ) - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) # Amount is reduced by "fee" assert freqtrade.get_real_amount(trade, buy_order_fee) == amount @@ -3492,7 +3492,7 @@ def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker, f @pytest.mark.parametrize( - 'fee_par,fee_reduction_amount,use_ticker_rate,expected_log', [ + 'fee_par,fee_reduction_amount,use_ticker_usdt_rate,expected_log', [ # basic, amount does not change ({'cost': 0.008, 'currency': 'ETH'}, 0, False, None), # no currency in fee @@ -3511,8 +3511,8 @@ def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker, f ({'cost': 0.008, 'currency': None}, 0, True, None), ]) def test_get_real_amount( - default_conf, trades_for_order, buy_order_fee, fee, mocker, caplog, - fee_par, fee_reduction_amount, use_ticker_rate, expected_log + default_conf_usdt, trades_for_order, buy_order_fee, fee, mocker, caplog, + fee_par, fee_reduction_amount, use_ticker_usdt_rate, expected_log ): buy_order = deepcopy(buy_order_fee) @@ -3530,9 +3530,9 @@ def test_get_real_amount( open_rate=0.245441, open_order_id="123456" ) - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) - if not use_ticker_rate: + if not use_ticker_usdt_rate: mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', side_effect=ExchangeError) caplog.clear() @@ -3550,7 +3550,7 @@ def test_get_real_amount( (0.02, 'BNB', 0.0005, 0.001518575, 7.996), ]) def test_get_real_amount_multi( - default_conf, trades_for_order2, buy_order_fee, caplog, fee, mocker, markets, + default_conf_usdt, trades_for_order2, buy_order_fee, caplog, fee, mocker, markets, fee_cost, fee_currency, fee_reduction_amount, expected_fee, expected_log_amount, ): @@ -3562,7 +3562,7 @@ def test_get_real_amount_multi( mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order) amount = float(sum(x['amount'] for x in trades_for_order)) - default_conf['stake_currency'] = "ETH" + default_conf_usdt['stake_currency'] = "ETH" trade = Trade( pair='LTC/ETH', @@ -3575,8 +3575,8 @@ def test_get_real_amount_multi( ) # Fake markets entry to enable fee parsing - markets['BNB/ETH'] = markets['ETH/BTC'] - freqtrade = get_patched_freqtradebot(mocker, default_conf) + markets['BNB/ETH'] = markets['ETH/USDT'] + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets)) mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', return_value={'ask': 0.19, 'last': 0.2}) @@ -3600,7 +3600,7 @@ def test_get_real_amount_multi( assert trade.fee_close_currency is None -def test_get_real_amount_invalid_order(default_conf, trades_for_order, buy_order_fee, fee, mocker): +def test_get_real_amount_invalid_order(default_conf_usdt, trades_for_order, buy_order_fee, fee, mocker): limit_buy_order_usdt = deepcopy(buy_order_fee) limit_buy_order_usdt['fee'] = {'cost': 0.004} @@ -3615,13 +3615,13 @@ def test_get_real_amount_invalid_order(default_conf, trades_for_order, buy_order open_rate=0.245441, open_order_id="123456" ) - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) # Amount does not change assert freqtrade.get_real_amount(trade, limit_buy_order_usdt) == amount -def test_get_real_amount_wrong_amount(default_conf, trades_for_order, buy_order_fee, fee, mocker): +def test_get_real_amount_wrong_amount(default_conf_usdt, trades_for_order, buy_order_fee, fee, mocker): limit_buy_order_usdt = deepcopy(buy_order_fee) limit_buy_order_usdt['amount'] = limit_buy_order_usdt['amount'] - 0.001 @@ -3636,14 +3636,14 @@ def test_get_real_amount_wrong_amount(default_conf, trades_for_order, buy_order_ fee_close=fee.return_value, open_order_id="123456" ) - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) # Amount does not change with pytest.raises(DependencyException, match=r"Half bought\? Amounts don't match"): freqtrade.get_real_amount(trade, limit_buy_order_usdt) -def test_get_real_amount_wrong_amount_rounding(default_conf, trades_for_order, buy_order_fee, fee, +def test_get_real_amount_wrong_amount_rounding(default_conf_usdt, trades_for_order, buy_order_fee, fee, mocker): # Floats should not be compared directly. limit_buy_order_usdt = deepcopy(buy_order_fee) @@ -3660,14 +3660,14 @@ def test_get_real_amount_wrong_amount_rounding(default_conf, trades_for_order, b open_rate=0.245441, open_order_id="123456" ) - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) # Amount changes by fee amount. assert isclose(freqtrade.get_real_amount(trade, limit_buy_order_usdt), amount - (amount * 0.001), abs_tol=MATH_CLOSE_PREC,) -def test_get_real_amount_open_trade(default_conf, fee, mocker): +def test_get_real_amount_open_trade(default_conf_usdt, fee, mocker): amount = 12345 trade = Trade( pair='LTC/ETH', @@ -3684,7 +3684,7 @@ def test_get_real_amount_open_trade(default_conf, fee, mocker): 'status': 'open', 'side': 'buy', } - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) assert freqtrade.get_real_amount(trade, order) == amount @@ -3696,7 +3696,7 @@ def test_get_real_amount_open_trade(default_conf, fee, mocker): (8.0, 0.1, 8.0, 8.0), (8.0, 0.1, 7.9, 7.9), ]) -def test_apply_fee_conditional(default_conf, fee, caplog, mocker, +def test_apply_fee_conditional(default_conf_usdt, fee, caplog, mocker, amount, fee_abs, wallet, amount_exp): walletmock = mocker.patch('freqtrade.wallets.Wallets.update') mocker.patch('freqtrade.wallets.Wallets.get_free', return_value=wallet) @@ -3709,7 +3709,7 @@ def test_apply_fee_conditional(default_conf, fee, caplog, mocker, fee_close=fee.return_value, open_order_id="123456" ) - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) walletmock.reset_mock() # Amount is kept as is @@ -3721,23 +3721,23 @@ def test_apply_fee_conditional(default_conf, fee, caplog, mocker, (0.1, False), (100, True), ]) -def test_order_book_depth_of_market(default_conf, ticker, limit_buy_order_usdt_open, limit_buy_order_usdt, +def test_order_book_depth_of_market(default_conf_usdt, ticker_usdt, limit_buy_order_usdt_open, limit_buy_order_usdt, fee, mocker, order_book_l2, delta, is_high_delta): - default_conf['bid_strategy']['check_depth_of_market']['enabled'] = True - default_conf['bid_strategy']['check_depth_of_market']['bids_to_ask_delta'] = delta + default_conf_usdt['bid_strategy']['check_depth_of_market']['enabled'] = True + default_conf_usdt['bid_strategy']['check_depth_of_market']['bids_to_ask_delta'] = delta patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch('freqtrade.exchange.Exchange.fetch_l2_order_book', order_book_l2) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, create_order=MagicMock(return_value=limit_buy_order_usdt_open), get_fee=fee, ) # Save state of current whitelist - whitelist = deepcopy(default_conf['exchange']['pair_whitelist']) - freqtrade = FreqtradeBot(default_conf) + whitelist = deepcopy(default_conf_usdt['exchange']['pair_whitelist']) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) freqtrade.enter_positions() @@ -3746,7 +3746,7 @@ def test_order_book_depth_of_market(default_conf, ticker, limit_buy_order_usdt_o assert trade is None else: assert trade is not None - assert trade.stake_amount == 0.001 + assert trade.stake_amount == 10.0 assert trade.is_open assert trade.open_date is not None assert trade.exchange == 'binance' @@ -3757,43 +3757,43 @@ def test_order_book_depth_of_market(default_conf, ticker, limit_buy_order_usdt_o trade.update(limit_buy_order_usdt) assert trade.open_rate == 2.0 - assert whitelist == default_conf['exchange']['pair_whitelist'] + assert whitelist == default_conf_usdt['exchange']['pair_whitelist'] @pytest.mark.parametrize('exception_thrown,ask,last,order_book_top,order_book', [ (False, 0.045, 0.046, 2, None), (True, 0.042, 0.046, 1, {'bids': [[]], 'asks': [[]]}) ]) -def test_order_book_bid_strategy1(mocker, default_conf, order_book_l2, exception_thrown, +def test_order_book_bid_strategy1(mocker, default_conf_usdt, order_book_l2, exception_thrown, ask, last, order_book_top, order_book, caplog) -> None: """ test if function get_rate will return the order book price instead of the ask rate """ patch_exchange(mocker) - ticker_mock = MagicMock(return_value={'ask': ask, 'last': last}) + ticker_usdt_mock = MagicMock(return_value={'ask': ask, 'last': last}) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_l2_order_book=MagicMock(return_value=order_book) if order_book else order_book_l2, - fetch_ticker=ticker_mock, + fetch_ticker=ticker_usdt_mock, ) - default_conf['exchange']['name'] = 'binance' - default_conf['bid_strategy']['use_order_book'] = True - default_conf['bid_strategy']['order_book_top'] = order_book_top - default_conf['bid_strategy']['ask_last_balance'] = 0 - default_conf['telegram']['enabled'] = False + default_conf_usdt['exchange']['name'] = 'binance' + default_conf_usdt['bid_strategy']['use_order_book'] = True + default_conf_usdt['bid_strategy']['order_book_top'] = order_book_top + default_conf_usdt['bid_strategy']['ask_last_balance'] = 0 + default_conf_usdt['telegram']['enabled'] = False - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) if exception_thrown: with pytest.raises(PricingError): - freqtrade.exchange.get_rate('ETH/BTC', refresh=True, side="buy") + freqtrade.exchange.get_rate('ETH/USDT', refresh=True, side="buy") assert log_has_re( r'Buy Price at location 1 from orderbook could not be determined.', caplog) else: - assert freqtrade.exchange.get_rate('ETH/BTC', refresh=True, side="buy") == 0.043935 - assert ticker_mock.call_count == 0 + assert freqtrade.exchange.get_rate('ETH/USDT', refresh=True, side="buy") == 0.043935 + assert ticker_usdt_mock.call_count == 0 -def test_check_depth_of_market_buy(default_conf, mocker, order_book_l2) -> None: +def test_check_depth_of_market_buy(default_conf_usdt, mocker, order_book_l2) -> None: """ test check depth of market """ @@ -3802,27 +3802,27 @@ def test_check_depth_of_market_buy(default_conf, mocker, order_book_l2) -> None: 'freqtrade.exchange.Exchange', fetch_l2_order_book=order_book_l2 ) - default_conf['telegram']['enabled'] = False - default_conf['exchange']['name'] = 'binance' - default_conf['bid_strategy']['check_depth_of_market']['enabled'] = True + default_conf_usdt['telegram']['enabled'] = False + default_conf_usdt['exchange']['name'] = 'binance' + default_conf_usdt['bid_strategy']['check_depth_of_market']['enabled'] = True # delta is 100 which is impossible to reach. hence function will return false - default_conf['bid_strategy']['check_depth_of_market']['bids_to_ask_delta'] = 100 - freqtrade = FreqtradeBot(default_conf) + default_conf_usdt['bid_strategy']['check_depth_of_market']['bids_to_ask_delta'] = 100 + freqtrade = FreqtradeBot(default_conf_usdt) - conf = default_conf['bid_strategy']['check_depth_of_market'] - assert freqtrade._check_depth_of_market_buy('ETH/BTC', conf) is False + conf = default_conf_usdt['bid_strategy']['check_depth_of_market'] + assert freqtrade._check_depth_of_market_buy('ETH/USDT', conf) is False -def test_order_book_ask_strategy(default_conf, limit_buy_order_usdt_open, limit_buy_order_usdt, fee, +def test_order_book_ask_strategy(default_conf_usdt, limit_buy_order_usdt_open, limit_buy_order_usdt, fee, limit_sell_order_usdt_open, mocker, order_book_l2, caplog) -> None: """ test order book ask strategy """ mocker.patch('freqtrade.exchange.Exchange.fetch_l2_order_book', order_book_l2) - default_conf['exchange']['name'] = 'binance' - default_conf['ask_strategy']['use_order_book'] = True - default_conf['ask_strategy']['order_book_top'] = 1 - default_conf['telegram']['enabled'] = False + default_conf_usdt['exchange']['name'] = 'binance' + default_conf_usdt['ask_strategy']['use_order_book'] = True + default_conf_usdt['ask_strategy']['order_book_top'] = 1 + default_conf_usdt['telegram']['enabled'] = False patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -3838,7 +3838,7 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order_usdt_open, limit_ ]), get_fee=fee, ) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) freqtrade.enter_positions() @@ -3863,22 +3863,22 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order_usdt_open, limit_ caplog) -def test_startup_state(default_conf, mocker): - default_conf['pairlist'] = {'method': 'VolumePairList', - 'config': {'number_assets': 20} - } +def test_startup_state(default_conf_usdt, mocker): + default_conf_usdt['pairlist'] = {'method': 'VolumePairList', + 'config': {'number_assets': 20} + } mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) - worker = get_patched_worker(mocker, default_conf) + worker = get_patched_worker(mocker, default_conf_usdt) assert worker.freqtrade.state is State.RUNNING -def test_startup_trade_reinit(default_conf, edge_conf, mocker): +def test_startup_trade_reinit(default_conf_usdt, edge_conf, mocker): mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) reinit_mock = MagicMock() mocker.patch('freqtrade.persistence.Trade.stoploss_reinitialization', reinit_mock) - ftbot = get_patched_freqtradebot(mocker, default_conf) + ftbot = get_patched_freqtradebot(mocker, default_conf_usdt) ftbot.startup() assert reinit_mock.call_count == 1 @@ -3890,21 +3890,21 @@ def test_startup_trade_reinit(default_conf, edge_conf, mocker): @pytest.mark.usefixtures("init_persistence") -def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order_usdt_open, caplog): - default_conf['dry_run'] = True +def test_sync_wallet_dry_run(mocker, default_conf_usdt, ticker_usdt, fee, limit_buy_order_usdt_open, caplog): + default_conf_usdt['dry_run'] = True # Initialize to 2 times stake amount - default_conf['dry_run_wallet'] = 0.002 - default_conf['max_open_trades'] = 2 - default_conf['tradable_balance_ratio'] = 1.0 + default_conf_usdt['dry_run_wallet'] = 0.002 + default_conf_usdt['max_open_trades'] = 2 + default_conf_usdt['tradable_balance_ratio'] = 1.0 patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, create_order=MagicMock(return_value=limit_buy_order_usdt_open), get_fee=fee, ) - bot = get_patched_freqtradebot(mocker, default_conf) + bot = get_patched_freqtradebot(mocker, default_conf_usdt) patch_get_signal(bot) assert bot.wallets.get_free('BTC') == 0.002 @@ -3922,15 +3922,15 @@ def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order_ @pytest.mark.usefixtures("init_persistence") -def test_cancel_all_open_orders(mocker, default_conf, fee, limit_buy_order_usdt, limit_sell_order_usdt): - default_conf['cancel_open_orders_on_exit'] = True +def test_cancel_all_open_orders(mocker, default_conf_usdt, fee, limit_buy_order_usdt, limit_sell_order_usdt): + default_conf_usdt['cancel_open_orders_on_exit'] = True mocker.patch('freqtrade.exchange.Exchange.fetch_order', side_effect=[ ExchangeError(), limit_sell_order_usdt, limit_buy_order_usdt, limit_sell_order_usdt]) buy_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_enter') sell_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_exit') - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) create_mock_trades(fee) trades = Trade.query.all() assert len(trades) == MOCK_TRADE_COUNT @@ -3940,8 +3940,8 @@ def test_cancel_all_open_orders(mocker, default_conf, fee, limit_buy_order_usdt, @pytest.mark.usefixtures("init_persistence") -def test_check_for_open_trades(mocker, default_conf, fee): - freqtrade = get_patched_freqtradebot(mocker, default_conf) +def test_check_for_open_trades(mocker, default_conf_usdt, fee): + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) freqtrade.check_for_open_trades() assert freqtrade.rpc.send_msg.call_count == 0 @@ -3956,8 +3956,8 @@ def test_check_for_open_trades(mocker, default_conf, fee): @pytest.mark.usefixtures("init_persistence") -def test_startup_update_open_orders(mocker, default_conf, fee, caplog): - freqtrade = get_patched_freqtradebot(mocker, default_conf) +def test_startup_update_open_orders(mocker, default_conf_usdt, fee, caplog): + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) create_mock_trades(fee) freqtrade.startup_update_open_orders() @@ -3982,8 +3982,8 @@ def test_startup_update_open_orders(mocker, default_conf, fee, caplog): @pytest.mark.usefixtures("init_persistence") -def test_update_closed_trades_without_assigned_fees(mocker, default_conf, fee): - freqtrade = get_patched_freqtradebot(mocker, default_conf) +def test_update_closed_trades_without_assigned_fees(mocker, default_conf_usdt, fee): + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) def patch_with_fee(order): order.update({'fee': {'cost': 0.1, 'rate': 0.01, @@ -4081,8 +4081,8 @@ def test_reupdate_enter_order_fees(mocker, default_conf_usdt, fee, caplog): @pytest.mark.usefixtures("init_persistence") -def test_handle_insufficient_funds(mocker, default_conf, fee): - freqtrade = get_patched_freqtradebot(mocker, default_conf) +def test_handle_insufficient_funds(mocker, default_conf_usdt, fee): + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) mock_rlo = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.refind_lost_order') mock_bof = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.reupdate_enter_order_fees') create_mock_trades(fee) @@ -4119,9 +4119,9 @@ def test_handle_insufficient_funds(mocker, default_conf, fee): @pytest.mark.usefixtures("init_persistence") -def test_refind_lost_order(mocker, default_conf, fee, caplog): +def test_refind_lost_order(mocker, default_conf_usdt, fee, caplog): caplog.set_level(logging.DEBUG) - freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) mock_uts = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.update_trade_state') mock_fo = mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order', @@ -4217,10 +4217,10 @@ def test_refind_lost_order(mocker, default_conf, fee, caplog): assert log_has(f"Error updating {order['id']}.", caplog) -def test_get_valid_price(mocker, default_conf) -> None: +def test_get_valid_price(mocker, default_conf_usdt) -> None: patch_RPCManager(mocker) patch_exchange(mocker) - freqtrade = FreqtradeBot(default_conf) + freqtrade = FreqtradeBot(default_conf_usdt) freqtrade.config['custom_price_max_distance_ratio'] = 0.02 custom_price_string = "10" From 43339f1660557f6538f05addb464a502ac074b5f Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sun, 26 Sep 2021 13:43:01 -0600 Subject: [PATCH 028/144] A lot of the usdt freqtradebot tests pass now --- tests/conftest.py | 8 ++-- tests/test_freqtradebot.py | 90 ++++++++++++++++++++------------------ 2 files changed, 51 insertions(+), 47 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index f2da68dd2..4ef0dfcb4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -441,7 +441,7 @@ def ticker_sell_down(): def ticker_usdt(): return MagicMock(return_value={ 'bid': 2.0, - 'ask': 2.01, + 'ask': 2.1, 'last': 2.0, }) @@ -449,9 +449,9 @@ def ticker_usdt(): @pytest.fixture def ticker_usdt_sell_up(): return MagicMock(return_value={ - 'bid': 2.19, - 'ask': 2.2, - 'last': 2.19, + 'bid': 2.2, + 'ask': 2.3, + 'last': 2.2, }) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 2a96783bc..07f1288ed 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -320,8 +320,8 @@ def test_create_trade_no_stake_amount(default_conf_usdt, ticker_usdt, limit_buy_ @pytest.mark.parametrize('stake_amount,create,amount_enough,max_open_trades', [ - (0.0005, True, True, 99), - (0.000000005, True, False, 99), + (5.0, True, True, 99), + (0.00005, True, False, 99), (0, False, True, 99), (UNLIMITED_STAKE_AMOUNT, False, True, 0), ]) @@ -2592,14 +2592,15 @@ def test_execute_trade_exit_up(default_conf_usdt, ticker_usdt, fee, ticker_usdt_ 'exchange': 'Binance', 'pair': 'ETH/USDT', 'gain': 'profit', - 'limit': 1.172e-05, - 'amount': 91.07468123, + 'limit': 2.2, + 'amount': 5.0, 'order_type': 'limit', - 'open_rate': 1.098e-05, - 'current_rate': 1.173e-05, - 'profit_amount': 6.223e-05, - 'profit_ratio': 0.0620716, - 'stake_currency': 'BTC', + 'open_rate': 2.0, + 'current_rate': 2.3, + # TODO: Double check that profit_amount and profit_ratio are correct + 'profit_amount': 0.9475, + 'profit_ratio': 0.09451372, + 'stake_currency': 'USDT', 'fiat_currency': 'USD', 'sell_reason': SellType.ROI.value, 'open_date': ANY, @@ -2638,20 +2639,21 @@ def test_execute_trade_exit_down(default_conf_usdt, ticker_usdt, fee, ticker_usd assert rpc_mock.call_count == 2 last_msg = rpc_mock.call_args_list[-1][0][0] + # TODO: Should be a loss, but comes out as a gain assert { 'type': RPCMessageType.SELL, 'trade_id': 1, 'exchange': 'Binance', 'pair': 'ETH/USDT', 'gain': 'loss', - 'limit': 1.044e-05, - 'amount': 91.07468123, + 'limit': 2.01, + 'amount': 5.0, 'order_type': 'limit', - 'open_rate': 1.098e-05, - 'current_rate': 1.043e-05, - 'profit_amount': -5.406e-05, - 'profit_ratio': -0.05392257, - 'stake_currency': 'BTC', + 'open_rate': 2.0, + 'current_rate': 2.0, + 'profit_amount': -0.000125, + 'profit_ratio': -1.247e-05, + 'stake_currency': 'USDT', 'fiat_currency': 'USD', 'sell_reason': SellType.STOP_LOSS.value, 'open_date': ANY, @@ -2692,7 +2694,7 @@ def test_execute_trade_exit_custom_exit_price(default_conf_usdt, ticker_usdt, fe freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=True) # Set a custom exit price - freqtrade.strategy.custom_exit_price = lambda **kwargs: 1.170e-05 + freqtrade.strategy.custom_exit_price = lambda **kwargs: 2.25 freqtrade.execute_trade_exit(trade=trade, limit=ticker_usdt_sell_up()['bid'], sell_reason=SellCheckTuple(sell_type=SellType.SELL_SIGNAL)) @@ -2709,14 +2711,14 @@ def test_execute_trade_exit_custom_exit_price(default_conf_usdt, ticker_usdt, fe 'exchange': 'Binance', 'pair': 'ETH/USDT', 'gain': 'profit', - 'limit': 1.170e-05, - 'amount': 91.07468123, + 'limit': 2.25, + 'amount': 5.0, 'order_type': 'limit', - 'open_rate': 1.098e-05, - 'current_rate': 1.173e-05, + 'open_rate': 2.0, + 'current_rate': 2.3, 'profit_amount': 6.041e-05, - 'profit_ratio': 0.06025919, - 'stake_currency': 'BTC', + 'profit_ratio': 0.07262344, + 'stake_currency': 'USDT', 'fiat_currency': 'USD', 'sell_reason': SellType.SELL_SIGNAL.value, 'open_date': ANY, @@ -2755,27 +2757,28 @@ def test_execute_trade_exit_down_stoploss_on_exchange_dry_run(default_conf_usdt, freqtrade.strategy.order_types['stoploss_on_exchange'] = True # Setting trade stoploss to 0.01 - trade.stop_loss = 0.00001099 * 0.99 + trade.stop_loss = 2.0 * 0.99 freqtrade.execute_trade_exit(trade=trade, limit=ticker_usdt_sell_down()['bid'], sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) assert rpc_mock.call_count == 2 last_msg = rpc_mock.call_args_list[-1][0][0] + # TODO: Are these values correct? assert { 'type': RPCMessageType.SELL, 'trade_id': 1, 'exchange': 'Binance', 'pair': 'ETH/USDT', 'gain': 'loss', - 'limit': 1.08801e-05, - 'amount': 91.07468123, + 'limit': 1.98, + 'amount': 5.0, 'order_type': 'limit', - 'open_rate': 1.098e-05, - 'current_rate': 1.043e-05, - 'profit_amount': -1.408e-05, - 'profit_ratio': -0.01404051, - 'stake_currency': 'BTC', + 'open_rate': 2.0, + 'current_rate': 2.0, + 'profit_amount': -0.14975, + 'profit_ratio': -0.01493766, + 'stake_currency': 'USDT', 'fiat_currency': 'USD', 'sell_reason': SellType.STOP_LOSS.value, 'open_date': ANY, @@ -2975,24 +2978,25 @@ def test_execute_trade_exit_market_order(default_conf_usdt, ticker_usdt, fee, sell_reason=SellCheckTuple(sell_type=SellType.ROI)) assert not trade.is_open - assert trade.close_profit == 0.0620716 + assert trade.close_profit == 0.09451372 # TODO: Check this is correct assert rpc_mock.call_count == 3 last_msg = rpc_mock.call_args_list[-1][0][0] + # TODO: Is this correct? assert { 'type': RPCMessageType.SELL, 'trade_id': 1, 'exchange': 'Binance', 'pair': 'ETH/USDT', 'gain': 'profit', - 'limit': 1.172e-05, - 'amount': 91.07468123, + 'limit': 2.2, + 'amount': 5.0, 'order_type': 'market', - 'open_rate': 1.098e-05, - 'current_rate': 1.173e-05, - 'profit_amount': 6.223e-05, - 'profit_ratio': 0.0620716, - 'stake_currency': 'BTC', + 'open_rate': 2.0, + 'current_rate': 2.3, + 'profit_amount': 0.9475, + 'profit_ratio': 0.09451372, + 'stake_currency': 'USDT', 'fiat_currency': 'USD', 'sell_reason': SellType.ROI.value, 'open_date': ANY, @@ -3893,7 +3897,7 @@ def test_startup_trade_reinit(default_conf_usdt, edge_conf, mocker): def test_sync_wallet_dry_run(mocker, default_conf_usdt, ticker_usdt, fee, limit_buy_order_usdt_open, caplog): default_conf_usdt['dry_run'] = True # Initialize to 2 times stake amount - default_conf_usdt['dry_run_wallet'] = 0.002 + default_conf_usdt['dry_run_wallet'] = 20.0 default_conf_usdt['max_open_trades'] = 2 default_conf_usdt['tradable_balance_ratio'] = 1.0 patch_exchange(mocker) @@ -3906,7 +3910,7 @@ def test_sync_wallet_dry_run(mocker, default_conf_usdt, ticker_usdt, fee, limit_ bot = get_patched_freqtradebot(mocker, default_conf_usdt) patch_get_signal(bot) - assert bot.wallets.get_free('BTC') == 0.002 + assert bot.wallets.get_free('USDT') == 20.0 n = bot.enter_positions() assert n == 2 @@ -3916,8 +3920,8 @@ def test_sync_wallet_dry_run(mocker, default_conf_usdt, ticker_usdt, fee, limit_ bot.config['max_open_trades'] = 3 n = bot.enter_positions() assert n == 0 - assert log_has_re(r"Unable to create trade for XRP/BTC: " - r"Available balance \(0.0 BTC\) is lower than stake amount \(0.001 BTC\)", + assert log_has_re(r"Unable to create trade for XRP/USDT: " + r"Available balance \(0.0 USDT\) is lower than stake amount \(10.0 USDT\)", caplog) From 2ee87f8c66cb4b998ba986acd2dfc8729ece5e8f Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Thu, 30 Sep 2021 10:00:56 +0300 Subject: [PATCH 029/144] Fix failing USDT tests due to not enough open markets. --- tests/conftest.py | 78 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 4ef0dfcb4..9c500e05c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -387,7 +387,8 @@ def get_default_conf_usdt(testdatadir): "ETH/USDT", "LTC/USDT", "XRP/USDT", - "NEO/USDT" + "NEO/USDT", + "TKN/USDT", ], "pair_blacklist": [ "DOGE/USDT", @@ -696,6 +697,81 @@ def get_markets(): }, 'info': {}, }, + 'XRP/USDT': { + 'id': 'xrpusdt', + 'symbol': 'XRP/USDT', + 'base': 'XRP', + 'quote': 'USDT', + 'active': True, + 'precision': { + 'price': 8, + 'amount': 8, + 'cost': 8, + }, + 'lot': 0.00000001, + 'limits': { + 'amount': { + 'min': 0.01, + 'max': 1000, + }, + 'price': 500000, + 'cost': { + 'min': 0.0001, + 'max': 500000, + }, + }, + 'info': {}, + }, + 'NEO/USDT': { + 'id': 'neousdt', + 'symbol': 'NEO/USDT', + 'base': 'NEO', + 'quote': 'USDT', + 'active': True, + 'precision': { + 'price': 8, + 'amount': 8, + 'cost': 8, + }, + 'lot': 0.00000001, + 'limits': { + 'amount': { + 'min': 0.01, + 'max': 1000, + }, + 'price': 500000, + 'cost': { + 'min': 0.0001, + 'max': 500000, + }, + }, + 'info': {}, + }, + 'TKN/USDT': { + 'id': 'tknusdt', + 'symbol': 'TKN/USDT', + 'base': 'TKN', + 'quote': 'USDT', + 'active': True, + 'precision': { + 'price': 8, + 'amount': 8, + 'cost': 8, + }, + 'lot': 0.00000001, + 'limits': { + 'amount': { + 'min': 0.01, + 'max': 1000, + }, + 'price': 500000, + 'cost': { + 'min': 0.0001, + 'max': 500000, + }, + }, + 'info': {}, + }, 'LTC/USD': { 'id': 'USD-LTC', 'symbol': 'LTC/USD', From 89613702697de80c1ecbd3f7cd75271b4116dea8 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Thu, 30 Sep 2021 10:01:25 +0300 Subject: [PATCH 030/144] Fix failing test due to not updated expected values. --- tests/test_freqtradebot.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 07f1288ed..579dc1b43 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -2672,8 +2672,10 @@ def test_execute_trade_exit_custom_exit_price(default_conf_usdt, ticker_usdt, fe get_fee=fee, _is_dry_limit_order_filled=MagicMock(return_value=False), ) - patch_whitelist(mocker, default_conf_usdt) - freqtrade = FreqtradeBot(default_conf_usdt) + config = deepcopy(default_conf_usdt) + config['custom_price_max_distance_ratio'] = 0.1 + patch_whitelist(mocker, config) + freqtrade = FreqtradeBot(config) patch_get_signal(freqtrade) freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=False) @@ -2716,8 +2718,8 @@ def test_execute_trade_exit_custom_exit_price(default_conf_usdt, ticker_usdt, fe 'order_type': 'limit', 'open_rate': 2.0, 'current_rate': 2.3, - 'profit_amount': 6.041e-05, - 'profit_ratio': 0.07262344, + 'profit_amount': 1.196875, + 'profit_ratio': 0.11938903, 'stake_currency': 'USDT', 'fiat_currency': 'USD', 'sell_reason': SellType.SELL_SIGNAL.value, From c820db4c60f6973554e232b52c06ed6e726dd8d0 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Thu, 30 Sep 2021 11:12:15 +0300 Subject: [PATCH 031/144] Fix couple more usdt tests which failed due to ticker prices causing roi being hit, but tests did not expect that to happen. --- tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 9c500e05c..0e5e7c933 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -442,7 +442,7 @@ def ticker_sell_down(): def ticker_usdt(): return MagicMock(return_value={ 'bid': 2.0, - 'ask': 2.1, + 'ask': 2.02, 'last': 2.0, }) From 107fa911a5ef371e675a5d679ab511f1a93579b5 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Thu, 30 Sep 2021 02:34:00 -0600 Subject: [PATCH 032/144] Fixed test_tsl_on_exchange_compatible_with_edge --- tests/test_freqtradebot.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 579dc1b43..bfa3ac353 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1483,9 +1483,9 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=MagicMock(return_value={ - 'bid': 1.9, + 'bid': 2.19, 'ask': 2.2, - 'last': 1.9 + 'last': 2.19 }), create_order=MagicMock(side_effect=[ {'id': limit_buy_order_usdt['id']}, @@ -1540,7 +1540,7 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, # stoploss initially at 20% as edge dictated it. assert freqtrade.handle_trade(trade) is False assert freqtrade.handle_stoploss_on_exchange(trade) is False - assert trade.stop_loss == 2.178 + assert isclose(trade.stop_loss, 1.76) cancel_order_mock = MagicMock() stoploss_order_mock = MagicMock() @@ -1549,15 +1549,15 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, # price goes down 5% mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={ - 'bid': 1.9 * 0.95, + 'bid': 2.19 * 0.95, 'ask': 2.2 * 0.95, - 'last': 1.9 * 0.95 + 'last': 2.19 * 0.95 })) assert freqtrade.handle_trade(trade) is False assert freqtrade.handle_stoploss_on_exchange(trade) is False # stoploss should remain the same - assert trade.stop_loss == 2.178 + assert isclose(trade.stop_loss, 1.76) # stoploss on exchange should not be canceled cancel_order_mock.assert_not_called() @@ -1575,10 +1575,12 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, # stoploss should be set to 1% as trailing is on assert trade.stop_loss == 4.4 * 0.99 cancel_order_mock.assert_called_once_with(100, 'NEO/BTC') - stoploss_order_mock.assert_called_once_with(amount=2132892.49146757, - pair='NEO/BTC', - order_types=freqtrade.strategy.order_types, - stop_price=4.4 * 0.99) + stoploss_order_mock.assert_called_once_with( + amount=11.41438356, + pair='NEO/BTC', + order_types=freqtrade.strategy.order_types, + stop_price=4.4 * 0.99 + ) @pytest.mark.parametrize('return_value,side_effect,log_message', [ From 6f8e66117bcc0babd0aeef9c03a15b9ee6fc2e11 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Thu, 30 Sep 2021 03:19:28 -0600 Subject: [PATCH 033/144] flake8 isort --- tests/test_freqtradebot.py | 153 ++++++++++++++++++++++--------------- 1 file changed, 91 insertions(+), 62 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index bfa3ac353..6f0fe5e90 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -23,9 +23,9 @@ from freqtrade.worker import Worker from tests.conftest import (create_mock_trades, get_patched_freqtradebot, get_patched_worker, log_has, log_has_re, patch_edge, patch_exchange, patch_get_signal, patch_wallet, patch_whitelist) -from tests.conftest_trades import ( - MOCK_TRADE_COUNT, mock_order_1, mock_order_2, mock_order_2_sell, mock_order_3, - mock_order_3_sell, mock_order_4, mock_order_5_stoploss, mock_order_6_sell) +from tests.conftest_trades import (MOCK_TRADE_COUNT, mock_order_1, mock_order_2, mock_order_2_sell, + mock_order_3, mock_order_3_sell, mock_order_4, + mock_order_5_stoploss, mock_order_6_sell) def patch_RPCManager(mocker) -> MagicMock: @@ -135,8 +135,10 @@ def test_get_trade_stake_amount(default_conf_usdt, ticker_usdt, mocker) -> None: (True, 27, 3, 0.5, [10, 10, 6.73]), (True, 22, 3, 1, [10, 10, 0.0]), ]) -def test_check_available_stake_amount(default_conf_usdt, ticker_usdt, mocker, fee, limit_buy_order_usdt_open, - amend_last, wallet, max_open, lsamr, expected) -> None: +def test_check_available_stake_amount( + default_conf_usdt, ticker_usdt, mocker, fee, limit_buy_order_usdt_open, + amend_last, wallet, max_open, lsamr, expected +) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -363,8 +365,8 @@ def test_create_trade_minimal_amount( (["ETH/USDT"], 1), # No pairs left ([], 0), # No pairs in whitelist ]) -def test_enter_positions_no_pairs_left(default_conf_usdt, ticker_usdt, limit_buy_order_usdt_open, fee, - whitelist, positions, mocker, caplog) -> None: +def test_enter_positions_no_pairs_left(default_conf_usdt, ticker_usdt, limit_buy_order_usdt_open, + fee, whitelist, positions, mocker, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -459,8 +461,10 @@ def test_create_trade_no_signal(default_conf_usdt, fee, mocker) -> None: @pytest.mark.parametrize("max_open", range(0, 5)) @pytest.mark.parametrize("tradable_balance_ratio,modifier", [(1.0, 1), (0.99, 0.8), (0.5, 0.5)]) -def test_create_trades_multiple_trades(default_conf_usdt, ticker_usdt, fee, mocker, limit_buy_order_usdt_open, - max_open, tradable_balance_ratio, modifier) -> None: +def test_create_trades_multiple_trades( + default_conf_usdt, ticker_usdt, fee, mocker, limit_buy_order_usdt_open, + max_open, tradable_balance_ratio, modifier +) -> None: patch_RPCManager(mocker) patch_exchange(mocker) default_conf_usdt['max_open_trades'] = max_open @@ -484,7 +488,8 @@ def test_create_trades_multiple_trades(default_conf_usdt, ticker_usdt, fee, mock assert len(trades) == max(int(max_open * modifier), 0) -def test_create_trades_preopen(default_conf_usdt, ticker_usdt, fee, mocker, limit_buy_order_usdt_open) -> None: +def test_create_trades_preopen(default_conf_usdt, ticker_usdt, fee, mocker, + limit_buy_order_usdt_open) -> None: patch_RPCManager(mocker) patch_exchange(mocker) default_conf_usdt['max_open_trades'] = 4 @@ -513,8 +518,8 @@ def test_create_trades_preopen(default_conf_usdt, ticker_usdt, fee, mocker, limi assert len(trades) == 4 -def test_process_trade_creation(default_conf_usdt, ticker_usdt, limit_buy_order_usdt, limit_buy_order_usdt_open, - fee, mocker, caplog) -> None: +def test_process_trade_creation(default_conf_usdt, ticker_usdt, limit_buy_order_usdt, + limit_buy_order_usdt_open, fee, mocker, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -584,7 +589,8 @@ def test_process_operational_exception(default_conf_usdt, ticker_usdt, mocker) - assert 'OperationalException' in msg_mock.call_args_list[-1][0][0]['status'] -def test_process_trade_handling(default_conf_usdt, ticker_usdt, limit_buy_order_usdt_open, fee, mocker) -> None: +def test_process_trade_handling(default_conf_usdt, ticker_usdt, limit_buy_order_usdt_open, fee, + mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -690,7 +696,8 @@ def test_process_informative_pairs_added(default_conf_usdt, ticker_usdt, mocker) assert ("ETH/USDT", default_conf_usdt["timeframe"]) in refresh_mock.call_args[0][0] -def test_execute_entry(mocker, default_conf_usdt, fee, limit_buy_order_usdt, limit_buy_order_usdt_open) -> None: +def test_execute_entry(mocker, default_conf_usdt, fee, limit_buy_order_usdt, + limit_buy_order_usdt_open) -> None: patch_RPCManager(mocker) patch_exchange(mocker) freqtrade = FreqtradeBot(default_conf_usdt) @@ -1278,8 +1285,9 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf_usdt, fee, assert freqtrade.handle_trade(trade) is True -def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf_usdt, fee, caplog, - limit_buy_order_usdt, limit_sell_order_usdt) -> None: +def test_handle_stoploss_on_exchange_trailing_error( + mocker, default_conf_usdt, fee, caplog, limit_buy_order_usdt, limit_sell_order_usdt +) -> None: # When trailing stoploss is set stoploss = MagicMock(return_value={'id': 13434334}) patch_exchange(mocker) @@ -1701,8 +1709,8 @@ def test_update_trade_state(mocker, default_conf_usdt, limit_buy_order_usdt, cap (30.0 + 1e-14, True), (8.0, False) ]) -def test_update_trade_state_withorderdict(default_conf_usdt, trades_for_order, limit_buy_order_usdt, fee, - mocker, initial_amount, has_rounding_fee, caplog): +def test_update_trade_state_withorderdict(default_conf_usdt, trades_for_order, limit_buy_order_usdt, + fee, mocker, initial_amount, has_rounding_fee, caplog): trades_for_order[0]['amount'] = initial_amount mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order) # fetch_order should not be called!! @@ -1798,8 +1806,8 @@ def test_update_trade_state_sell(default_conf_usdt, trades_for_order, limit_sell assert order.status == 'closed' -def test_handle_trade(default_conf_usdt, limit_buy_order_usdt, limit_sell_order_usdt_open, limit_sell_order_usdt, - fee, mocker) -> None: +def test_handle_trade(default_conf_usdt, limit_buy_order_usdt, limit_sell_order_usdt_open, + limit_sell_order_usdt, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -1963,8 +1971,8 @@ def test_handle_trade_use_sell_signal(default_conf_usdt, ticker_usdt, limit_buy_ caplog) -def test_close_trade(default_conf_usdt, ticker_usdt, limit_buy_order_usdt, limit_buy_order_usdt_open, limit_sell_order_usdt, - fee, mocker) -> None: +def test_close_trade(default_conf_usdt, ticker_usdt, limit_buy_order_usdt, + limit_buy_order_usdt_open, limit_sell_order_usdt, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -2003,8 +2011,8 @@ def test_bot_loop_start_called_once(mocker, default_conf_usdt, caplog): assert ftbot.strategy.analyze.call_count == 1 -def test_check_handle_timedout_buy_usercustom(default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade, - fee, mocker) -> None: +def test_check_handle_timedout_buy_usercustom(default_conf_usdt, ticker_usdt, limit_buy_order_old, + open_trade, fee, mocker) -> None: default_conf_usdt["unfilledtimeout"] = {"buy": 1400, "sell": 30} rpc_mock = patch_RPCManager(mocker) @@ -2117,8 +2125,8 @@ def test_check_handle_cancelled_buy(default_conf_usdt, ticker_usdt, limit_buy_or assert log_has_re("Buy order cancelled on exchange for Trade.*", caplog) -def test_check_handle_timedout_buy_exception(default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade, - fee, mocker) -> None: +def test_check_handle_timedout_buy_exception(default_conf_usdt, ticker_usdt, limit_buy_order_old, + open_trade, fee, mocker) -> None: rpc_mock = patch_RPCManager(mocker) cancel_order_mock = MagicMock() patch_exchange(mocker) @@ -2143,8 +2151,8 @@ def test_check_handle_timedout_buy_exception(default_conf_usdt, ticker_usdt, lim assert nb_trades == 1 -def test_check_handle_timedout_sell_usercustom(default_conf_usdt, ticker_usdt, limit_sell_order_old, mocker, - open_trade) -> None: +def test_check_handle_timedout_sell_usercustom(default_conf_usdt, ticker_usdt, limit_sell_order_old, + mocker, open_trade) -> None: default_conf_usdt["unfilledtimeout"] = {"buy": 1440, "sell": 1440} rpc_mock = patch_RPCManager(mocker) cancel_order_mock = MagicMock() @@ -2222,8 +2230,8 @@ def test_check_handle_timedout_sell(default_conf_usdt, ticker_usdt, limit_sell_o assert freqtrade.strategy.check_sell_timeout.call_count == 0 -def test_check_handle_cancelled_sell(default_conf_usdt, ticker_usdt, limit_sell_order_old, open_trade, - mocker, caplog) -> None: +def test_check_handle_cancelled_sell(default_conf_usdt, ticker_usdt, limit_sell_order_old, + open_trade, mocker, caplog) -> None: """ Handle sell order cancelled on exchange""" rpc_mock = patch_RPCManager(mocker) cancel_order_mock = MagicMock() @@ -2319,8 +2327,8 @@ def test_check_handle_timedout_partial_fee(default_conf_usdt, ticker_usdt, open_ assert pytest.approx(trades[0].fee_open) == 0.001 -def test_check_handle_timedout_partial_except(default_conf_usdt, ticker_usdt, open_trade, caplog, fee, - limit_buy_order_old_partial, trades_for_order, +def test_check_handle_timedout_partial_except(default_conf_usdt, ticker_usdt, open_trade, caplog, + fee, limit_buy_order_old_partial, trades_for_order, limit_buy_order_old_partial_canceled, mocker) -> None: rpc_mock = patch_RPCManager(mocker) cancel_order_mock = MagicMock(return_value=limit_buy_order_old_partial_canceled) @@ -2359,7 +2367,8 @@ def test_check_handle_timedout_partial_except(default_conf_usdt, ticker_usdt, op assert trades[0].fee_open == fee() -def test_check_handle_timedout_exception(default_conf_usdt, ticker_usdt, open_trade_usdt, mocker, caplog) -> None: +def test_check_handle_timedout_exception(default_conf_usdt, ticker_usdt, open_trade_usdt, mocker, + caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) cancel_order_mock = MagicMock() @@ -2546,7 +2555,8 @@ def test_handle_cancel_exit_cancel_exception(mocker, default_conf_usdt) -> None: assert freqtrade.handle_cancel_exit(trade, order, reason) == 'error cancelling order' -def test_execute_trade_exit_up(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_up, mocker) -> None: +def test_execute_trade_exit_up(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_up, mocker + ) -> None: rpc_mock = patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -2611,7 +2621,8 @@ def test_execute_trade_exit_up(default_conf_usdt, ticker_usdt, fee, ticker_usdt_ } == last_msg -def test_execute_trade_exit_down(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_down, mocker) -> None: +def test_execute_trade_exit_down(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_down, + mocker) -> None: rpc_mock = patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -2664,8 +2675,8 @@ def test_execute_trade_exit_down(default_conf_usdt, ticker_usdt, fee, ticker_usd } == last_msg -def test_execute_trade_exit_custom_exit_price(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_up, - mocker) -> None: +def test_execute_trade_exit_custom_exit_price(default_conf_usdt, ticker_usdt, fee, + ticker_usdt_sell_up, mocker) -> None: rpc_mock = patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -2731,8 +2742,8 @@ def test_execute_trade_exit_custom_exit_price(default_conf_usdt, ticker_usdt, fe } == last_msg -def test_execute_trade_exit_down_stoploss_on_exchange_dry_run(default_conf_usdt, ticker_usdt, fee, - ticker_usdt_sell_down, mocker) -> None: +def test_execute_trade_exit_down_stoploss_on_exchange_dry_run( + default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_down, mocker) -> None: rpc_mock = patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -2825,8 +2836,8 @@ def test_execute_trade_exit_sloe_cancel_exception( assert log_has('Could not cancel stoploss order abcd', caplog) -def test_execute_trade_exit_with_stoploss_on_exchange(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_up, - mocker) -> None: +def test_execute_trade_exit_with_stoploss_on_exchange(default_conf_usdt, ticker_usdt, fee, + ticker_usdt_sell_up, mocker) -> None: default_conf_usdt['exchange']['name'] = 'binance' rpc_mock = patch_RPCManager(mocker) @@ -3169,7 +3180,8 @@ def test__safe_exit_amount(default_conf_usdt, fee, caplog, mocker, amount_wallet assert wallet_update.call_count == 1 -def test_locked_pairs(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_down, mocker, caplog) -> None: +def test_locked_pairs(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_down, mocker, + caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -3204,8 +3216,8 @@ def test_locked_pairs(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_down assert log_has_re(f"Pair {trade.pair} is still locked.*", caplog) -def test_ignore_roi_if_buy_signal(default_conf_usdt, limit_buy_order_usdt, limit_buy_order_usdt_open, - fee, mocker) -> None: +def test_ignore_roi_if_buy_signal(default_conf_usdt, limit_buy_order_usdt, + limit_buy_order_usdt_open, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -3301,8 +3313,10 @@ def test_trailing_stop_loss(default_conf_usdt, limit_buy_order_usdt_open, limit_ (0.011, False, 2.0394), (0.055, True, 1.8), ]) -def test_trailing_stop_loss_positive(default_conf_usdt, limit_buy_order_usdt, limit_buy_order_usdt_open, - offset, fee, caplog, mocker, trail_if_reached, second_sl) -> None: +def test_trailing_stop_loss_positive( + default_conf_usdt, limit_buy_order_usdt, limit_buy_order_usdt_open, + offset, fee, caplog, mocker, trail_if_reached, second_sl +) -> None: buy_price = limit_buy_order_usdt['price'] patch_RPCManager(mocker) patch_exchange(mocker) @@ -3391,8 +3405,8 @@ def test_trailing_stop_loss_positive(default_conf_usdt, limit_buy_order_usdt, li assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value -def test_disable_ignore_roi_if_buy_signal(default_conf_usdt, limit_buy_order_usdt, limit_buy_order_usdt_open, - fee, mocker) -> None: +def test_disable_ignore_roi_if_buy_signal(default_conf_usdt, limit_buy_order_usdt, + limit_buy_order_usdt_open, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -3431,7 +3445,8 @@ def test_disable_ignore_roi_if_buy_signal(default_conf_usdt, limit_buy_order_usd assert trade.sell_reason == SellType.SELL_SIGNAL.value -def test_get_real_amount_quote(default_conf_usdt, trades_for_order, buy_order_fee, fee, caplog, mocker): +def test_get_real_amount_quote(default_conf_usdt, trades_for_order, buy_order_fee, fee, caplog, + mocker): mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order) amount = sum(x['amount'] for x in trades_for_order) trade = Trade( @@ -3608,7 +3623,8 @@ def test_get_real_amount_multi( assert trade.fee_close_currency is None -def test_get_real_amount_invalid_order(default_conf_usdt, trades_for_order, buy_order_fee, fee, mocker): +def test_get_real_amount_invalid_order(default_conf_usdt, trades_for_order, buy_order_fee, fee, + mocker): limit_buy_order_usdt = deepcopy(buy_order_fee) limit_buy_order_usdt['fee'] = {'cost': 0.004} @@ -3629,7 +3645,8 @@ def test_get_real_amount_invalid_order(default_conf_usdt, trades_for_order, buy_ assert freqtrade.get_real_amount(trade, limit_buy_order_usdt) == amount -def test_get_real_amount_wrong_amount(default_conf_usdt, trades_for_order, buy_order_fee, fee, mocker): +def test_get_real_amount_wrong_amount(default_conf_usdt, trades_for_order, buy_order_fee, fee, + mocker): limit_buy_order_usdt = deepcopy(buy_order_fee) limit_buy_order_usdt['amount'] = limit_buy_order_usdt['amount'] - 0.001 @@ -3651,8 +3668,8 @@ def test_get_real_amount_wrong_amount(default_conf_usdt, trades_for_order, buy_o freqtrade.get_real_amount(trade, limit_buy_order_usdt) -def test_get_real_amount_wrong_amount_rounding(default_conf_usdt, trades_for_order, buy_order_fee, fee, - mocker): +def test_get_real_amount_wrong_amount_rounding(default_conf_usdt, trades_for_order, buy_order_fee, + fee, mocker): # Floats should not be compared directly. limit_buy_order_usdt = deepcopy(buy_order_fee) trades_for_order[0]['amount'] = trades_for_order[0]['amount'] + 1e-15 @@ -3671,8 +3688,11 @@ def test_get_real_amount_wrong_amount_rounding(default_conf_usdt, trades_for_ord freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) # Amount changes by fee amount. - assert isclose(freqtrade.get_real_amount(trade, limit_buy_order_usdt), amount - (amount * 0.001), - abs_tol=MATH_CLOSE_PREC,) + assert isclose( + freqtrade.get_real_amount(trade, limit_buy_order_usdt), + amount - (amount * 0.001), + abs_tol=MATH_CLOSE_PREC, + ) def test_get_real_amount_open_trade(default_conf_usdt, fee, mocker): @@ -3729,8 +3749,10 @@ def test_apply_fee_conditional(default_conf_usdt, fee, caplog, mocker, (0.1, False), (100, True), ]) -def test_order_book_depth_of_market(default_conf_usdt, ticker_usdt, limit_buy_order_usdt_open, limit_buy_order_usdt, - fee, mocker, order_book_l2, delta, is_high_delta): +def test_order_book_depth_of_market( + default_conf_usdt, ticker_usdt, limit_buy_order_usdt_open, limit_buy_order_usdt, + fee, mocker, order_book_l2, delta, is_high_delta +): default_conf_usdt['bid_strategy']['check_depth_of_market']['enabled'] = True default_conf_usdt['bid_strategy']['check_depth_of_market']['bids_to_ask_delta'] = delta patch_RPCManager(mocker) @@ -3821,8 +3843,9 @@ def test_check_depth_of_market_buy(default_conf_usdt, mocker, order_book_l2) -> assert freqtrade._check_depth_of_market_buy('ETH/USDT', conf) is False -def test_order_book_ask_strategy(default_conf_usdt, limit_buy_order_usdt_open, limit_buy_order_usdt, fee, - limit_sell_order_usdt_open, mocker, order_book_l2, caplog) -> None: +def test_order_book_ask_strategy( + default_conf_usdt, limit_buy_order_usdt_open, limit_buy_order_usdt, fee, + limit_sell_order_usdt_open, mocker, order_book_l2, caplog) -> None: """ test order book ask strategy """ @@ -3898,7 +3921,8 @@ def test_startup_trade_reinit(default_conf_usdt, edge_conf, mocker): @pytest.mark.usefixtures("init_persistence") -def test_sync_wallet_dry_run(mocker, default_conf_usdt, ticker_usdt, fee, limit_buy_order_usdt_open, caplog): +def test_sync_wallet_dry_run(mocker, default_conf_usdt, ticker_usdt, fee, limit_buy_order_usdt_open, + caplog): default_conf_usdt['dry_run'] = True # Initialize to 2 times stake amount default_conf_usdt['dry_run_wallet'] = 20.0 @@ -3930,11 +3954,16 @@ def test_sync_wallet_dry_run(mocker, default_conf_usdt, ticker_usdt, fee, limit_ @pytest.mark.usefixtures("init_persistence") -def test_cancel_all_open_orders(mocker, default_conf_usdt, fee, limit_buy_order_usdt, limit_sell_order_usdt): +def test_cancel_all_open_orders(mocker, default_conf_usdt, fee, limit_buy_order_usdt, + limit_sell_order_usdt): default_conf_usdt['cancel_open_orders_on_exit'] = True mocker.patch('freqtrade.exchange.Exchange.fetch_order', side_effect=[ - ExchangeError(), limit_sell_order_usdt, limit_buy_order_usdt, limit_sell_order_usdt]) + ExchangeError(), + limit_sell_order_usdt, + limit_buy_order_usdt, + limit_sell_order_usdt + ]) buy_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_enter') sell_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_exit') From 96d09b5615c14fd442394060d844be2c898056a6 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Fri, 1 Oct 2021 03:10:43 -0600 Subject: [PATCH 034/144] Fixed breaking rpc tests --- tests/rpc/test_rpc.py | 2 +- tests/rpc/test_rpc_apiserver.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 0ba42c4ce..f8c923958 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -1003,7 +1003,7 @@ def test_rpc_blacklist(mocker, default_conf) -> None: assert len(ret['blacklist']) == 4 assert ret['blacklist'] == default_conf['exchange']['pair_blacklist'] assert ret['blacklist'] == ['DOGE/BTC', 'HOT/BTC', 'ETH/BTC', 'XRP/.*'] - assert ret['blacklist_expanded'] == ['ETH/BTC', 'XRP/BTC'] + assert ret['blacklist_expanded'] == ['ETH/BTC', 'XRP/BTC', 'XRP/USDT'] assert 'errors' in ret assert isinstance(ret['errors'], dict) diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 7c98b2df7..045f91bb8 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -937,7 +937,7 @@ def test_api_blacklist(botclient, mocker): data='{"blacklist": ["XRP/.*"]}') assert_response(rc) assert rc.json() == {"blacklist": ["DOGE/BTC", "HOT/BTC", "ETH/BTC", "XRP/.*"], - "blacklist_expanded": ["ETH/BTC", "XRP/BTC"], + "blacklist_expanded": ["ETH/BTC", "XRP/BTC", "XRP/USDT"], "length": 4, "method": ["StaticPairList"], "errors": {}, From e5e1e49f5327753ed9e7525c5cdca838e2c3ab66 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 2 Oct 2021 08:58:33 +0200 Subject: [PATCH 035/144] Remove some unused test parameters --- tests/test_freqtradebot.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 6f0fe5e90..02c913bc3 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -115,7 +115,7 @@ def test_order_dict(default_conf_usdt, mocker, runmode, caplog) -> None: assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog) -def test_get_trade_stake_amount(default_conf_usdt, ticker_usdt, mocker) -> None: +def test_get_trade_stake_amount(default_conf_usdt, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) @@ -243,7 +243,6 @@ def test_edge_overrides_stoploss(limit_buy_order_usdt, fee, caplog, mocker, def test_total_open_trades_stakes(mocker, default_conf_usdt, ticker_usdt, fee) -> None: patch_RPCManager(mocker) patch_exchange(mocker) - default_conf_usdt['stake_amount'] = 10.0 default_conf_usdt['max_open_trades'] = 2 mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -304,8 +303,7 @@ def test_create_trade(default_conf_usdt, ticker_usdt, limit_buy_order_usdt, fee, assert whitelist == default_conf_usdt['exchange']['pair_whitelist'] -def test_create_trade_no_stake_amount(default_conf_usdt, ticker_usdt, limit_buy_order_usdt, - fee, mocker) -> None: +def test_create_trade_no_stake_amount(default_conf_usdt, ticker_usdt, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) patch_wallet(mocker, free=default_conf_usdt['stake_amount'] * 0.5) @@ -1477,7 +1475,7 @@ def test_handle_stoploss_on_exchange_custom_stop( assert freqtrade.handle_trade(trade) is True -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, limit_buy_order_usdt, limit_sell_order_usdt) -> None: # When trailing stoploss is set @@ -2125,7 +2123,7 @@ def test_check_handle_cancelled_buy(default_conf_usdt, ticker_usdt, limit_buy_or assert log_has_re("Buy order cancelled on exchange for Trade.*", caplog) -def test_check_handle_timedout_buy_exception(default_conf_usdt, ticker_usdt, limit_buy_order_old, +def test_check_handle_timedout_buy_exception(default_conf_usdt, ticker_usdt, open_trade, fee, mocker) -> None: rpc_mock = patch_RPCManager(mocker) cancel_order_mock = MagicMock() @@ -3253,7 +3251,7 @@ def test_ignore_roi_if_buy_signal(default_conf_usdt, limit_buy_order_usdt, assert trade.sell_reason == SellType.ROI.value -def test_trailing_stop_loss(default_conf_usdt, limit_buy_order_usdt_open, limit_buy_order_usdt, +def test_trailing_stop_loss(default_conf_usdt, limit_buy_order_usdt_open, fee, caplog, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) @@ -3724,7 +3722,7 @@ def test_get_real_amount_open_trade(default_conf_usdt, fee, mocker): (8.0, 0.1, 8.0, 8.0), (8.0, 0.1, 7.9, 7.9), ]) -def test_apply_fee_conditional(default_conf_usdt, fee, caplog, mocker, +def test_apply_fee_conditional(default_conf_usdt, fee, mocker, amount, fee_abs, wallet, amount_exp): walletmock = mocker.patch('freqtrade.wallets.Wallets.update') mocker.patch('freqtrade.wallets.Wallets.get_free', return_value=wallet) From 022839b728fafdf2f1a83d514fb0c9acd027db54 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 2 Oct 2021 13:17:10 +0200 Subject: [PATCH 036/144] remove unnecessary test --- tests/test_freqtradebot.py | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 02c913bc3..c169d8597 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1612,32 +1612,6 @@ def test_enter_positions(mocker, default_conf_usdt, return_value, side_effect, assert mock_ct.call_count == len(default_conf_usdt['exchange']['pair_whitelist']) -def test_exit_positions(mocker, default_conf_usdt, limit_buy_order_usdt, caplog) -> None: - freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) - - mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True)) - mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order_usdt) - mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[]) - mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', - return_value=limit_buy_order_usdt['amount']) - - trade = MagicMock() - trade.open_order_id = '123' - trade.open_fee = 0.001 - trades = [trade] - n = freqtrade.exit_positions(trades) - assert n == 0 - # Test amount not modified by fee-logic - assert not log_has( - 'Applying fee to amount for Trade {} from 30.0 to 90.81'.format(trade), caplog - ) - - mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=90.81) - # test amount modified by fee-logic - n = freqtrade.exit_positions(trades) - assert n == 0 - - def test_exit_positions_exception(mocker, default_conf_usdt, limit_buy_order_usdt, caplog) -> None: freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order_usdt) From 5fdeca812d28891a915507362ffcc9f36ccb4cb0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 2 Oct 2021 14:30:24 +0200 Subject: [PATCH 037/144] Combine most hyperopt-loss tests to one --- tests/optimize/conftest.py | 13 ++-- tests/optimize/test_hyperoptloss.py | 92 +++++------------------------ 2 files changed, 23 insertions(+), 82 deletions(-) diff --git a/tests/optimize/conftest.py b/tests/optimize/conftest.py index 5c5171c3a..690934b07 100644 --- a/tests/optimize/conftest.py +++ b/tests/optimize/conftest.py @@ -39,16 +39,17 @@ def hyperopt(hyperopt_conf, mocker): def hyperopt_results(): return pd.DataFrame( { - 'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'], - 'profit_ratio': [-0.1, 0.2, 0.3], - 'profit_abs': [-0.2, 0.4, 0.6], - 'trade_duration': [10, 30, 10], - 'sell_reason': [SellType.STOP_LOSS, SellType.ROI, SellType.ROI], + 'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC', 'ETH/BTC'], + 'profit_ratio': [-0.1, 0.2, -0.1, 0.3], + 'profit_abs': [-0.2, 0.4, -0.2, 0.6], + 'trade_duration': [10, 30, 10, 10], + 'sell_reason': [SellType.STOP_LOSS, SellType.ROI, SellType.STOP_LOSS, SellType.ROI], 'close_date': [ datetime(2019, 1, 1, 9, 26, 3, 478039), datetime(2019, 2, 1, 9, 26, 3, 478039), - datetime(2019, 3, 1, 9, 26, 3, 478039) + datetime(2019, 3, 1, 9, 26, 3, 478039), + datetime(2019, 4, 1, 9, 26, 3, 478039), ] } ) diff --git a/tests/optimize/test_hyperoptloss.py b/tests/optimize/test_hyperoptloss.py index 0082bcc34..923e3fc32 100644 --- a/tests/optimize/test_hyperoptloss.py +++ b/tests/optimize/test_hyperoptloss.py @@ -35,6 +35,7 @@ def test_hyperoptlossresolver_wrongname(default_conf) -> None: def test_loss_calculation_prefer_correct_trade_count(hyperopt_conf, hyperopt_results) -> None: + hyperopt_conf.update({'hyperopt_loss': "ShortTradeDurHyperOptLoss"}) hl = HyperOptLossResolver.load_hyperoptloss(hyperopt_conf) correct = hl.hyperopt_loss_function(hyperopt_results, 600, datetime(2019, 1, 1), datetime(2019, 5, 1)) @@ -50,6 +51,7 @@ def test_loss_calculation_prefer_shorter_trades(hyperopt_conf, hyperopt_results) resultsb = hyperopt_results.copy() resultsb.loc[1, 'trade_duration'] = 20 + hyperopt_conf.update({'hyperopt_loss': "ShortTradeDurHyperOptLoss"}) hl = HyperOptLossResolver.load_hyperoptloss(hyperopt_conf) longer = hl.hyperopt_loss_function(hyperopt_results, 100, datetime(2019, 1, 1), datetime(2019, 5, 1)) @@ -64,6 +66,7 @@ def test_loss_calculation_has_limited_profit(hyperopt_conf, hyperopt_results) -> results_under = hyperopt_results.copy() results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2 + hyperopt_conf.update({'hyperopt_loss': "ShortTradeDurHyperOptLoss"}) hl = HyperOptLossResolver.load_hyperoptloss(hyperopt_conf) correct = hl.hyperopt_loss_function(hyperopt_results, 600, datetime(2019, 1, 1), datetime(2019, 5, 1)) @@ -75,91 +78,28 @@ def test_loss_calculation_has_limited_profit(hyperopt_conf, hyperopt_results) -> assert under > correct -def test_sharpe_loss_prefers_higher_profits(default_conf, hyperopt_results) -> None: - results_over = hyperopt_results.copy() - results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2 - results_under = hyperopt_results.copy() - results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2 - - default_conf.update({'hyperopt_loss': 'SharpeHyperOptLoss'}) - hl = HyperOptLossResolver.load_hyperoptloss(default_conf) - correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results), - datetime(2019, 1, 1), datetime(2019, 5, 1)) - over = hl.hyperopt_loss_function(results_over, len(hyperopt_results), - datetime(2019, 1, 1), datetime(2019, 5, 1)) - under = hl.hyperopt_loss_function(results_under, len(hyperopt_results), - datetime(2019, 1, 1), datetime(2019, 5, 1)) - assert over < correct - assert under > correct - - -def test_sharpe_loss_daily_prefers_higher_profits(default_conf, hyperopt_results) -> None: - results_over = hyperopt_results.copy() - results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2 - results_under = hyperopt_results.copy() - results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2 - - default_conf.update({'hyperopt_loss': 'SharpeHyperOptLossDaily'}) - hl = HyperOptLossResolver.load_hyperoptloss(default_conf) - correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results), - datetime(2019, 1, 1), datetime(2019, 5, 1)) - over = hl.hyperopt_loss_function(results_over, len(hyperopt_results), - datetime(2019, 1, 1), datetime(2019, 5, 1)) - under = hl.hyperopt_loss_function(results_under, len(hyperopt_results), - datetime(2019, 1, 1), datetime(2019, 5, 1)) - assert over < correct - assert under > correct - - -def test_sortino_loss_prefers_higher_profits(default_conf, hyperopt_results) -> None: - results_over = hyperopt_results.copy() - results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2 - results_under = hyperopt_results.copy() - results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2 - - default_conf.update({'hyperopt_loss': 'SortinoHyperOptLoss'}) - hl = HyperOptLossResolver.load_hyperoptloss(default_conf) - correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results), - datetime(2019, 1, 1), datetime(2019, 5, 1)) - over = hl.hyperopt_loss_function(results_over, len(hyperopt_results), - datetime(2019, 1, 1), datetime(2019, 5, 1)) - under = hl.hyperopt_loss_function(results_under, len(hyperopt_results), - datetime(2019, 1, 1), datetime(2019, 5, 1)) - assert over < correct - assert under > correct - - -def test_sortino_loss_daily_prefers_higher_profits(default_conf, hyperopt_results) -> None: - results_over = hyperopt_results.copy() - results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2 - results_under = hyperopt_results.copy() - results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2 - - default_conf.update({'hyperopt_loss': 'SortinoHyperOptLossDaily'}) - hl = HyperOptLossResolver.load_hyperoptloss(default_conf) - correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results), - datetime(2019, 1, 1), datetime(2019, 5, 1)) - over = hl.hyperopt_loss_function(results_over, len(hyperopt_results), - datetime(2019, 1, 1), datetime(2019, 5, 1)) - under = hl.hyperopt_loss_function(results_under, len(hyperopt_results), - datetime(2019, 1, 1), datetime(2019, 5, 1)) - assert over < correct - assert under > correct - - -def test_onlyprofit_loss_prefers_higher_profits(default_conf, hyperopt_results) -> None: +@pytest.mark.parametrize('lossfunction', [ + "OnlyProfitHyperOptLoss", + "SortinoHyperOptLoss", + "SortinoHyperOptLossDaily", + "SharpeHyperOptLoss", + "SharpeHyperOptLossDaily", +]) +def test_loss_functions_better_profits(default_conf, hyperopt_results, lossfunction) -> None: results_over = hyperopt_results.copy() results_over['profit_abs'] = hyperopt_results['profit_abs'] * 2 + results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2 results_under = hyperopt_results.copy() results_under['profit_abs'] = hyperopt_results['profit_abs'] / 2 + results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2 - default_conf.update({'hyperopt_loss': 'OnlyProfitHyperOptLoss'}) + default_conf.update({'hyperopt_loss': lossfunction}) hl = HyperOptLossResolver.load_hyperoptloss(default_conf) correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results), datetime(2019, 1, 1), datetime(2019, 5, 1)) - over = hl.hyperopt_loss_function(results_over, len(hyperopt_results), + over = hl.hyperopt_loss_function(results_over, len(results_over), datetime(2019, 1, 1), datetime(2019, 5, 1)) - under = hl.hyperopt_loss_function(results_under, len(hyperopt_results), + under = hl.hyperopt_loss_function(results_under, len(results_under), datetime(2019, 1, 1), datetime(2019, 5, 1)) assert over < correct assert under > correct From 77388eb423f8867aa63860ace494dd85505dd416 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 2 Oct 2021 15:23:48 +0200 Subject: [PATCH 038/144] Improve generate_test_data to make it easier to use --- tests/optimize/conftest.py | 20 ++++++++++++++------ tests/strategy/test_strategy_helpers.py | 4 ++-- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/tests/optimize/conftest.py b/tests/optimize/conftest.py index 690934b07..13d90b36f 100644 --- a/tests/optimize/conftest.py +++ b/tests/optimize/conftest.py @@ -39,17 +39,25 @@ def hyperopt(hyperopt_conf, mocker): def hyperopt_results(): return pd.DataFrame( { - 'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC', 'ETH/BTC'], + 'pair': ['ETH/USDT', 'ETH/USDT', 'ETH/USDT', 'ETH/USDT'], 'profit_ratio': [-0.1, 0.2, -0.1, 0.3], 'profit_abs': [-0.2, 0.4, -0.2, 0.6], 'trade_duration': [10, 30, 10, 10], + 'amount': [0.1, 0.1, 0.1, 0.1], 'sell_reason': [SellType.STOP_LOSS, SellType.ROI, SellType.STOP_LOSS, SellType.ROI], + 'open_date': + [ + datetime(2019, 1, 1, 9, 15, 0), + datetime(2019, 2, 1, 8, 55, 0), + datetime(2019, 3, 1, 9, 15, 0), + datetime(2019, 4, 1, 9, 15, 0), + ], 'close_date': [ - datetime(2019, 1, 1, 9, 26, 3, 478039), - datetime(2019, 2, 1, 9, 26, 3, 478039), - datetime(2019, 3, 1, 9, 26, 3, 478039), - datetime(2019, 4, 1, 9, 26, 3, 478039), - ] + datetime(2019, 1, 1, 9, 25, 0), + datetime(2019, 2, 1, 9, 25, 0), + datetime(2019, 3, 1, 9, 25, 0), + datetime(2019, 4, 1, 9, 25, 0), + ], } ) diff --git a/tests/strategy/test_strategy_helpers.py b/tests/strategy/test_strategy_helpers.py index a01b55050..cb7cf97a1 100644 --- a/tests/strategy/test_strategy_helpers.py +++ b/tests/strategy/test_strategy_helpers.py @@ -9,13 +9,13 @@ from freqtrade.strategy import (merge_informative_pair, stoploss_from_absolute, timeframe_to_minutes) -def generate_test_data(timeframe: str, size: int): +def generate_test_data(timeframe: str, size: int, start: str = '2020-07-05'): np.random.seed(42) tf_mins = timeframe_to_minutes(timeframe) base = np.random.normal(20, 2, size=size) - date = pd.period_range('2020-07-05', periods=size, freq=f'{tf_mins}min').to_timestamp() + date = pd.date_range(start, periods=size, freq=f'{tf_mins}min', tz='UTC') df = pd.DataFrame({ 'date': date, 'open': base, From 3b5cc5f01584329b3d9b0bd47cc71dbf97e8e3d1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 2 Oct 2021 15:36:51 +0200 Subject: [PATCH 039/144] Improve dates used for hyperopt tests --- tests/optimize/conftest.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/optimize/conftest.py b/tests/optimize/conftest.py index 13d90b36f..8c7fa3ac9 100644 --- a/tests/optimize/conftest.py +++ b/tests/optimize/conftest.py @@ -48,16 +48,16 @@ def hyperopt_results(): 'open_date': [ datetime(2019, 1, 1, 9, 15, 0), - datetime(2019, 2, 1, 8, 55, 0), - datetime(2019, 3, 1, 9, 15, 0), - datetime(2019, 4, 1, 9, 15, 0), + datetime(2019, 1, 2, 8, 55, 0), + datetime(2019, 1, 3, 9, 15, 0), + datetime(2019, 1, 4, 9, 15, 0), ], 'close_date': [ datetime(2019, 1, 1, 9, 25, 0), - datetime(2019, 2, 1, 9, 25, 0), - datetime(2019, 3, 1, 9, 25, 0), - datetime(2019, 4, 1, 9, 25, 0), + datetime(2019, 1, 2, 9, 25, 0), + datetime(2019, 1, 3, 9, 25, 0), + datetime(2019, 1, 4, 9, 25, 0), ], } ) From 057a187231bf5d90d0d2494d66934593d01133e8 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sat, 2 Oct 2021 20:32:51 -0600 Subject: [PATCH 040/144] Removed uneccessary TODOs --- tests/test_freqtradebot.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index c169d8597..f80ffeba2 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -2581,7 +2581,6 @@ def test_execute_trade_exit_up(default_conf_usdt, ticker_usdt, fee, ticker_usdt_ 'order_type': 'limit', 'open_rate': 2.0, 'current_rate': 2.3, - # TODO: Double check that profit_amount and profit_ratio are correct 'profit_amount': 0.9475, 'profit_ratio': 0.09451372, 'stake_currency': 'USDT', @@ -2624,7 +2623,6 @@ def test_execute_trade_exit_down(default_conf_usdt, ticker_usdt, fee, ticker_usd assert rpc_mock.call_count == 2 last_msg = rpc_mock.call_args_list[-1][0][0] - # TODO: Should be a loss, but comes out as a gain assert { 'type': RPCMessageType.SELL, 'trade_id': 1, @@ -2751,7 +2749,6 @@ def test_execute_trade_exit_down_stoploss_on_exchange_dry_run( assert rpc_mock.call_count == 2 last_msg = rpc_mock.call_args_list[-1][0][0] - # TODO: Are these values correct? assert { 'type': RPCMessageType.SELL, 'trade_id': 1, @@ -3274,7 +3271,6 @@ def test_trailing_stop_loss(default_conf_usdt, limit_buy_order_usdt_open, caplog.set_level(logging.DEBUG) # Sell as trailing-stop is reached assert freqtrade.handle_trade(trade) is True - # TODO: Does this make sense? How is stoploss 2.7? assert log_has("ETH/USDT - HIT STOP: current price at 2.200000, stoploss is 2.700000, " "initial stoploss was at 1.800000, trade opened at 2.000000", caplog) assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value From 93679db7c4bc63d9c6541d90e36dd361e6b26568 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sat, 2 Oct 2021 20:33:46 -0600 Subject: [PATCH 041/144] Removed ... TODOs --- tests/test_freqtradebot.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index f80ffeba2..97f8e92c8 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -2962,11 +2962,10 @@ def test_execute_trade_exit_market_order(default_conf_usdt, ticker_usdt, fee, sell_reason=SellCheckTuple(sell_type=SellType.ROI)) assert not trade.is_open - assert trade.close_profit == 0.09451372 # TODO: Check this is correct + assert trade.close_profit == 0.09451372 assert rpc_mock.call_count == 3 last_msg = rpc_mock.call_args_list[-1][0][0] - # TODO: Is this correct? assert { 'type': RPCMessageType.SELL, 'trade_id': 1, @@ -3330,7 +3329,6 @@ def test_trailing_stop_loss_positive( ) # stop-loss not reached, adjusted stoploss assert freqtrade.handle_trade(trade) is False - # TODO: is 0.0249% correct? Shouldn't it be higher? caplog_text = f"ETH/USDT - Using positive stoploss: 0.01 offset: {offset} profit: 0.0249%" if trail_if_reached: assert not log_has(caplog_text, caplog) From 908dee961d091b4ec9bb299d658011a557540c69 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sat, 2 Oct 2021 20:37:05 -0600 Subject: [PATCH 042/144] Changed test values in test_sell_profit_only to usdt like values --- tests/test_freqtradebot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 97f8e92c8..3f14c75c4 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -3026,12 +3026,12 @@ def test_execute_trade_exit_insufficient_funds_error(default_conf_usdt, ticker_u # Enable profit (True, 1.9, 2.2, False, True, SellType.SELL_SIGNAL.value), # Disable profit - (False, 0.00002172, 0.00002173, True, False, SellType.SELL_SIGNAL.value), + (False, 2.9, 3.2, True, False, SellType.SELL_SIGNAL.value), # Enable loss # * Shouldn't this be SellType.STOP_LOSS.value - (True, 0.00000172, 0.00000173, False, False, None), + (True, 0.19, 0.22, False, False, None), # Disable loss - (False, 0.00000172, 0.00000173, True, False, SellType.SELL_SIGNAL.value), + (False, 0.10, 0.22, True, False, SellType.SELL_SIGNAL.value), ]) def test_sell_profit_only( default_conf_usdt, limit_buy_order_usdt, limit_buy_order_usdt_open, From 058c7b3e992971bf53f752309ff0dcc91cefe542 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sat, 2 Oct 2021 20:43:32 -0600 Subject: [PATCH 043/144] Fixed odd test_execute_entry where the filled coins were higher than the amount --- tests/test_freqtradebot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 3f14c75c4..b518c02ad 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -773,10 +773,10 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_buy_order_usdt, # In case of rejected or expired order and partially filled limit_buy_order_usdt['status'] = 'expired' limit_buy_order_usdt['amount'] = 30.0 - limit_buy_order_usdt['filled'] = 80.99181073 + limit_buy_order_usdt['filled'] = 20.0 limit_buy_order_usdt['remaining'] = 10.00 limit_buy_order_usdt['price'] = 0.5 - limit_buy_order_usdt['cost'] = 40.495905365 + limit_buy_order_usdt['cost'] = 15.0 limit_buy_order_usdt['id'] = '555' mocker.patch('freqtrade.exchange.Exchange.create_order', MagicMock(return_value=limit_buy_order_usdt)) @@ -785,7 +785,7 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_buy_order_usdt, assert trade assert trade.open_order_id == '555' assert trade.open_rate == 0.5 - assert trade.stake_amount == 40.495905365 + assert trade.stake_amount == 15.0 # Test with custom stake limit_buy_order_usdt['status'] = 'open' From 9e77a739fa18218de4d5efc89bd910a5ac7cdc02 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 3 Oct 2021 09:22:50 +0200 Subject: [PATCH 044/144] Change usdt stake_amount to 60$ --- tests/conftest.py | 2 +- tests/test_freqtradebot.py | 62 +++++++++++++++++++------------------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 0e5e7c933..49534c88d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -376,7 +376,7 @@ def get_default_conf(testdatadir): def get_default_conf_usdt(testdatadir): configuration = get_default_conf(testdatadir) configuration.update({ - "stake_amount": 10.0, + "stake_amount": 60.0, "stake_currency": "USDT", "exchange": { "name": "binance", diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index b518c02ad..f57e35ca1 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -126,14 +126,14 @@ def test_get_trade_stake_amount(default_conf_usdt, mocker) -> None: @pytest.mark.parametrize("amend_last,wallet,max_open,lsamr,expected", [ - (False, 20, 2, 0.5, [10, None]), - (True, 20, 2, 0.5, [10, 9.8]), - (False, 30, 3, 0.5, [10, 10, None]), - (True, 30, 3, 0.5, [10, 10, 9.7]), - (False, 22, 3, 0.5, [10, 10, None]), - (True, 22, 3, 0.5, [10, 10, 0.0]), - (True, 27, 3, 0.5, [10, 10, 6.73]), - (True, 22, 3, 1, [10, 10, 0.0]), + (False, 120, 2, 0.5, [60, None]), + (True, 120, 2, 0.5, [60, 58.8]), + (False, 180, 3, 0.5, [60, 60, None]), + (True, 180, 3, 0.5, [60, 60, 58.2]), + (False, 122, 3, 0.5, [60, 60, None]), + (True, 122, 3, 0.5, [60, 60, 0.0]), + (True, 167, 3, 0.5, [60, 60, 45.33]), + (True, 122, 3, 1, [60, 60, 0.0]), ]) def test_check_available_stake_amount( default_conf_usdt, ticker_usdt, mocker, fee, limit_buy_order_usdt_open, @@ -256,7 +256,7 @@ def test_total_open_trades_stakes(mocker, default_conf_usdt, ticker_usdt, fee) - trade = Trade.query.first() assert trade is not None - assert trade.stake_amount == 10.0 + assert trade.stake_amount == 60.0 assert trade.is_open assert trade.open_date is not None @@ -264,11 +264,11 @@ def test_total_open_trades_stakes(mocker, default_conf_usdt, ticker_usdt, fee) - trade = Trade.query.order_by(Trade.id.desc()).first() assert trade is not None - assert trade.stake_amount == 10.0 + assert trade.stake_amount == 60.0 assert trade.is_open assert trade.open_date is not None - assert Trade.total_open_trades_stakes() == 20.0 + assert Trade.total_open_trades_stakes() == 120.0 def test_create_trade(default_conf_usdt, ticker_usdt, limit_buy_order_usdt, fee, mocker) -> None: @@ -289,7 +289,7 @@ def test_create_trade(default_conf_usdt, ticker_usdt, limit_buy_order_usdt, fee, trade = Trade.query.first() assert trade is not None - assert trade.stake_amount == 10.0 + assert trade.stake_amount == 60.0 assert trade.is_open assert trade.open_date is not None assert trade.exchange == 'binance' @@ -467,7 +467,7 @@ def test_create_trades_multiple_trades( patch_exchange(mocker) default_conf_usdt['max_open_trades'] = max_open default_conf_usdt['tradable_balance_ratio'] = tradable_balance_ratio - default_conf_usdt['dry_run_wallet'] = 10.0 * max_open + default_conf_usdt['dry_run_wallet'] = 60.0 * max_open mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -544,10 +544,10 @@ def test_process_trade_creation(default_conf_usdt, ticker_usdt, limit_buy_order_ assert trade.open_date is not None assert trade.exchange == 'binance' assert trade.open_rate == 2.0 - assert trade.amount == 5.0 + assert trade.amount == 30.0 assert log_has( - 'Buy signal found: about create a new trade for ETH/USDT with stake_amount: 10.0 ...', + 'Buy signal found: about create a new trade for ETH/USDT with stake_amount: 60.0 ...', caplog ) @@ -1265,7 +1265,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf_usdt, fee, cancel_order_mock.assert_called_once_with(100, 'ETH/USDT') stoploss_order_mock.assert_called_once_with( - amount=4.56621004, + amount=27.39726027, pair='ETH/USDT', order_types=freqtrade.strategy.order_types, stop_price=4.4 * 0.95 @@ -1457,7 +1457,7 @@ def test_handle_stoploss_on_exchange_custom_stop( cancel_order_mock.assert_called_once_with(100, 'ETH/USDT') stoploss_order_mock.assert_called_once_with( - amount=5.26315789, + amount=31.57894736, pair='ETH/USDT', order_types=freqtrade.strategy.order_types, stop_price=4.4 * 0.96 @@ -2577,11 +2577,11 @@ def test_execute_trade_exit_up(default_conf_usdt, ticker_usdt, fee, ticker_usdt_ 'pair': 'ETH/USDT', 'gain': 'profit', 'limit': 2.2, - 'amount': 5.0, + 'amount': 30.0, 'order_type': 'limit', 'open_rate': 2.0, 'current_rate': 2.3, - 'profit_amount': 0.9475, + 'profit_amount': 5.685, 'profit_ratio': 0.09451372, 'stake_currency': 'USDT', 'fiat_currency': 'USD', @@ -2630,11 +2630,11 @@ def test_execute_trade_exit_down(default_conf_usdt, ticker_usdt, fee, ticker_usd 'pair': 'ETH/USDT', 'gain': 'loss', 'limit': 2.01, - 'amount': 5.0, + 'amount': 30.0, 'order_type': 'limit', 'open_rate': 2.0, 'current_rate': 2.0, - 'profit_amount': -0.000125, + 'profit_amount': -0.00075, 'profit_ratio': -1.247e-05, 'stake_currency': 'USDT', 'fiat_currency': 'USD', @@ -2697,11 +2697,11 @@ def test_execute_trade_exit_custom_exit_price(default_conf_usdt, ticker_usdt, fe 'pair': 'ETH/USDT', 'gain': 'profit', 'limit': 2.25, - 'amount': 5.0, + 'amount': 30.0, 'order_type': 'limit', 'open_rate': 2.0, 'current_rate': 2.3, - 'profit_amount': 1.196875, + 'profit_amount': 7.18125, 'profit_ratio': 0.11938903, 'stake_currency': 'USDT', 'fiat_currency': 'USD', @@ -2756,11 +2756,11 @@ def test_execute_trade_exit_down_stoploss_on_exchange_dry_run( 'pair': 'ETH/USDT', 'gain': 'loss', 'limit': 1.98, - 'amount': 5.0, + 'amount': 30.0, 'order_type': 'limit', 'open_rate': 2.0, 'current_rate': 2.0, - 'profit_amount': -0.14975, + 'profit_amount': -0.8985, 'profit_ratio': -0.01493766, 'stake_currency': 'USDT', 'fiat_currency': 'USD', @@ -2973,11 +2973,11 @@ def test_execute_trade_exit_market_order(default_conf_usdt, ticker_usdt, fee, 'pair': 'ETH/USDT', 'gain': 'profit', 'limit': 2.2, - 'amount': 5.0, + 'amount': 30.0, 'order_type': 'market', 'open_rate': 2.0, 'current_rate': 2.3, - 'profit_amount': 0.9475, + 'profit_amount': 5.685, 'profit_ratio': 0.09451372, 'stake_currency': 'USDT', 'fiat_currency': 'USD', @@ -3742,7 +3742,7 @@ def test_order_book_depth_of_market( assert trade is None else: assert trade is not None - assert trade.stake_amount == 10.0 + assert trade.stake_amount == 60.0 assert trade.is_open assert trade.open_date is not None assert trade.exchange == 'binance' @@ -3891,7 +3891,7 @@ def test_sync_wallet_dry_run(mocker, default_conf_usdt, ticker_usdt, fee, limit_ caplog): default_conf_usdt['dry_run'] = True # Initialize to 2 times stake amount - default_conf_usdt['dry_run_wallet'] = 20.0 + default_conf_usdt['dry_run_wallet'] = 120.0 default_conf_usdt['max_open_trades'] = 2 default_conf_usdt['tradable_balance_ratio'] = 1.0 patch_exchange(mocker) @@ -3904,7 +3904,7 @@ def test_sync_wallet_dry_run(mocker, default_conf_usdt, ticker_usdt, fee, limit_ bot = get_patched_freqtradebot(mocker, default_conf_usdt) patch_get_signal(bot) - assert bot.wallets.get_free('USDT') == 20.0 + assert bot.wallets.get_free('USDT') == 120.0 n = bot.enter_positions() assert n == 2 @@ -3915,7 +3915,7 @@ def test_sync_wallet_dry_run(mocker, default_conf_usdt, ticker_usdt, fee, limit_ n = bot.enter_positions() assert n == 0 assert log_has_re(r"Unable to create trade for XRP/USDT: " - r"Available balance \(0.0 USDT\) is lower than stake amount \(10.0 USDT\)", + r"Available balance \(0.0 USDT\) is lower than stake amount \(60.0 USDT\)", caplog) From 126c29198888508c563536a8d5e6221d2bcb36e9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 3 Oct 2021 09:32:53 +0200 Subject: [PATCH 045/144] Improve docs closes #5654 --- docs/docker_quickstart.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/docker_quickstart.md b/docs/docker_quickstart.md index 2f350d207..27a9091b1 100644 --- a/docs/docker_quickstart.md +++ b/docs/docker_quickstart.md @@ -109,6 +109,7 @@ All freqtrade arguments will be available by running `docker-compose run --rm fr !!! Warning "`docker-compose` for trade commands" Trade commands (`freqtrade trade <...>`) should not be ran via `docker-compose run` - but should use `docker-compose up -d` instead. This makes sure that the container is properly started (including port forwardings) and will make sure that the container will restart after a system reboot. + If you intend to use freqUI, please also ensure to adjust the [configuration accordingly](rest-api.md#configuration-with-docker), otherwise the UI will not be available. !!! Note "`docker-compose run --rm`" Including `--rm` will remove the container after completion, and is highly recommended for all modes except trading mode (running with `freqtrade trade` command). From e73f5ab4802823b6cfa2dba745d6218be0a7f97b Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 3 Oct 2021 09:48:19 +0200 Subject: [PATCH 046/144] Add test confirming #5652 --- tests/exchange/test_exchange.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 79b4a3ff5..691cf3c03 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -275,6 +275,7 @@ def test_amount_to_precision(default_conf, mocker, amount, precision_mode, preci (234.43, 4, 0.5, 234.5), (234.53, 4, 0.5, 235.0), (0.891534, 4, 0.0001, 0.8916), + (64968.89, 4, 0.01, 64968.89), ]) def test_price_to_precision(default_conf, mocker, price, precision_mode, precision, expected): @@ -293,7 +294,7 @@ def test_price_to_precision(default_conf, mocker, price, precision_mode, precisi PropertyMock(return_value=precision_mode)) pair = 'ETH/BTC' - assert pytest.approx(exchange.price_to_precision(pair, price)) == expected + assert exchange.price_to_precision(pair, price) == expected @pytest.mark.parametrize("price,precision_mode,precision,expected", [ From f5e5203388b6666664c5b9edd09683956d196478 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 3 Oct 2021 09:48:50 +0200 Subject: [PATCH 047/144] Use "round" to 12 digits for TickSize mode Avoids float rounding problems, fix #5652 --- freqtrade/exchange/exchange.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 2b9b08d70..e9d0316d2 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -523,7 +523,7 @@ class Exchange: precision = self.markets[pair]['precision']['price'] missing = price % precision if missing != 0: - price = price - missing + precision + price = round(price - missing + precision, 10) else: symbol_prec = self.markets[pair]['precision']['price'] big_price = price * pow(10, symbol_prec) From 1c63d01cec878e363075e2f43d79296be2ab3d60 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 3 Oct 2021 14:14:16 +0200 Subject: [PATCH 048/144] Prevent using market-orders on gateio GateIo does not support market orders on spot markets --- freqtrade/exchange/gateio.py | 8 ++++++++ tests/exchange/test_gateio.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 tests/exchange/test_gateio.py diff --git a/freqtrade/exchange/gateio.py b/freqtrade/exchange/gateio.py index e6ee01c8a..018248a99 100644 --- a/freqtrade/exchange/gateio.py +++ b/freqtrade/exchange/gateio.py @@ -2,6 +2,7 @@ import logging from typing import Dict +from freqtrade.exceptions import OperationalException from freqtrade.exchange import Exchange @@ -23,3 +24,10 @@ class Gateio(Exchange): } _headers = {'X-Gate-Channel-Id': 'freqtrade'} + + def validate_ordertypes(self, order_types: Dict) -> None: + super().validate_ordertypes(order_types) + + if any(v == 'market' for k, v in order_types.items()): + raise OperationalException( + f'Exchange {self.name} does not support market orders.') diff --git a/tests/exchange/test_gateio.py b/tests/exchange/test_gateio.py new file mode 100644 index 000000000..6f7862909 --- /dev/null +++ b/tests/exchange/test_gateio.py @@ -0,0 +1,28 @@ +import pytest + +from freqtrade.exceptions import OperationalException +from freqtrade.exchange import Gateio +from freqtrade.resolvers.exchange_resolver import ExchangeResolver + + +def test_validate_order_types_gateio(default_conf, mocker): + default_conf['exchange']['name'] = 'gateio' + mocker.patch('freqtrade.exchange.Exchange._init_ccxt') + mocker.patch('freqtrade.exchange.Exchange._load_markets', return_value={}) + mocker.patch('freqtrade.exchange.Exchange.validate_pairs') + mocker.patch('freqtrade.exchange.Exchange.validate_timeframes') + mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') + mocker.patch('freqtrade.exchange.Exchange.name', 'Bittrex') + exch = ExchangeResolver.load_exchange('gateio', default_conf, True) + assert isinstance(exch, Gateio) + + default_conf['order_types'] = { + 'buy': 'market', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + with pytest.raises(OperationalException, + match=r'Exchange .* does not support market orders.'): + ExchangeResolver.load_exchange('gateio', default_conf, True) From 0d9beaa3f36a23641d17ee3a7dd2e77933b39846 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Oct 2021 03:01:04 +0000 Subject: [PATCH 049/144] Bump filelock from 3.0.12 to 3.3.0 Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.0.12 to 3.3.0. - [Release notes](https://github.com/tox-dev/py-filelock/releases) - [Commits](https://github.com/tox-dev/py-filelock/compare/v3.0.12...3.3.0) --- updated-dependencies: - dependency-name: filelock dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-hyperopt.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index 9feec80f1..b4067d1db 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -5,7 +5,7 @@ scipy==1.7.1 scikit-learn==0.24.2 scikit-optimize==0.8.1 -filelock==3.0.12 +filelock==3.3.0 joblib==1.0.1 psutil==5.8.0 progressbar2==3.53.3 From d220c55d405460de1eed9f5ae9e0dc214e6d8996 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Oct 2021 03:01:11 +0000 Subject: [PATCH 050/144] Bump pymdown-extensions from 8.2 to 9.0 Bumps [pymdown-extensions](https://github.com/facelessuser/pymdown-extensions) from 8.2 to 9.0. - [Release notes](https://github.com/facelessuser/pymdown-extensions/releases) - [Commits](https://github.com/facelessuser/pymdown-extensions/compare/8.2...9.0) --- updated-dependencies: - dependency-name: pymdown-extensions dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index 9b7c12a43..67d2c9da8 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,4 +1,4 @@ mkdocs==1.2.2 mkdocs-material==7.3.0 mdx_truly_sane_lists==1.2 -pymdown-extensions==8.2 +pymdown-extensions==9.0 From ff45d52d497629340d9424728da87060fc369c64 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Oct 2021 03:01:14 +0000 Subject: [PATCH 051/144] Bump types-filelock from 0.1.5 to 3.2.0 Bumps [types-filelock](https://github.com/python/typeshed) from 0.1.5 to 3.2.0. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-filelock dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 2f03255a0..3d45247c1 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -22,6 +22,6 @@ nbconvert==6.2.0 # mypy types types-cachetools==4.2.0 -types-filelock==0.1.5 +types-filelock==3.2.0 types-requests==2.25.9 types-tabulate==0.8.2 From 35c4a0a188c0a928600be63a177739d4fb1e42cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Oct 2021 03:01:17 +0000 Subject: [PATCH 052/144] Bump jsonschema from 3.2.0 to 4.0.1 Bumps [jsonschema](https://github.com/Julian/jsonschema) from 3.2.0 to 4.0.1. - [Release notes](https://github.com/Julian/jsonschema/releases) - [Changelog](https://github.com/Julian/jsonschema/blob/main/CHANGELOG.rst) - [Commits](https://github.com/Julian/jsonschema/compare/v3.2.0...v4.0.1) --- updated-dependencies: - dependency-name: jsonschema dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index feeb4d942..370766f8d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ cachetools==4.2.2 requests==2.26.0 urllib3==1.26.7 wrapt==1.12.1 -jsonschema==3.2.0 +jsonschema==4.0.1 TA-Lib==0.4.21 technical==1.3.0 tabulate==0.8.9 From 0071d002b68c244716ba3010deb110bfbeaad2a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Oct 2021 03:01:22 +0000 Subject: [PATCH 053/144] Bump ccxt from 1.57.3 to 1.57.38 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.57.3 to 1.57.38. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg) - [Commits](https://github.com/ccxt/ccxt/compare/1.57.3...1.57.38) --- updated-dependencies: - dependency-name: ccxt dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index feeb4d942..5006d356f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ numpy==1.21.2 pandas==1.3.3 pandas-ta==0.3.14b -ccxt==1.57.3 +ccxt==1.57.38 # Pin cryptography for now due to rust build errors with piwheels cryptography==3.4.8 aiohttp==3.7.4.post0 From 2b41066ab7ccbe032586f231449f82704d37301f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Oct 2021 03:01:29 +0000 Subject: [PATCH 054/144] Bump pytest-cov from 2.12.1 to 3.0.0 Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 2.12.1 to 3.0.0. - [Release notes](https://github.com/pytest-dev/pytest-cov/releases) - [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-cov/compare/v2.12.1...v3.0.0) --- updated-dependencies: - dependency-name: pytest-cov dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 2f03255a0..109413e6e 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -10,7 +10,7 @@ flake8-tidy-imports==4.4.1 mypy==0.910 pytest==6.2.5 pytest-asyncio==0.15.1 -pytest-cov==2.12.1 +pytest-cov==3.0.0 pytest-mock==3.6.1 pytest-random-order==1.0.4 isort==5.9.3 From 949f4fbbbfdbae9987d212edae7b9a6a3c1db00c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Oct 2021 04:36:11 +0000 Subject: [PATCH 055/144] Bump types-cachetools from 4.2.0 to 4.2.2 Bumps [types-cachetools](https://github.com/python/typeshed) from 4.2.0 to 4.2.2. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-cachetools dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 3d45247c1..858a46389 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -21,7 +21,7 @@ time-machine==2.4.0 nbconvert==6.2.0 # mypy types -types-cachetools==4.2.0 +types-cachetools==4.2.2 types-filelock==3.2.0 types-requests==2.25.9 types-tabulate==0.8.2 From f41fd4e88d7ca79294c40942a5fd181f574469d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Oct 2021 04:40:24 +0000 Subject: [PATCH 056/144] Bump mkdocs-material from 7.3.0 to 7.3.1 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 7.3.0 to 7.3.1. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/docs/changelog.md) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/7.3.0...7.3.1) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index 67d2c9da8..bbbb240ba 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,4 +1,4 @@ mkdocs==1.2.2 -mkdocs-material==7.3.0 +mkdocs-material==7.3.1 mdx_truly_sane_lists==1.2 pymdown-extensions==9.0 From 07750518c3e50711a0b8965f2535537b07402ba5 Mon Sep 17 00:00:00 2001 From: Sergey Khliustin Date: Mon, 4 Oct 2021 18:49:57 +0300 Subject: [PATCH 057/144] Added min_profit param to PerformanceFilter --- freqtrade/plugins/pairlist/PerformanceFilter.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/freqtrade/plugins/pairlist/PerformanceFilter.py b/freqtrade/plugins/pairlist/PerformanceFilter.py index 301ee57ab..f235816b8 100644 --- a/freqtrade/plugins/pairlist/PerformanceFilter.py +++ b/freqtrade/plugins/pairlist/PerformanceFilter.py @@ -21,6 +21,7 @@ class PerformanceFilter(IPairList): super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) self._minutes = pairlistconfig.get('minutes', 0) + self._min_profit = pairlistconfig.get('min_profit', None) @property def needstickers(self) -> bool: @@ -68,6 +69,8 @@ class PerformanceFilter(IPairList): sorted_df = list_df.merge(performance, on='pair', how='left')\ .fillna(0).sort_values(by=['count', 'pair'], ascending=True)\ .sort_values(by=['profit'], ascending=False) + if self._min_profit is not None: + sorted_df = sorted_df[sorted_df['profit'] >= self._min_profit] pairlist = sorted_df['pair'].tolist() return pairlist From f15922a16858247c6225d24dd69ae89ceb3e6034 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 4 Oct 2021 19:11:35 +0200 Subject: [PATCH 058/144] Fix custom_stoploss in strategy template closes #5658 --- .../templates/subtemplates/strategy_methods_advanced.j2 | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 b/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 index 2df23f365..fb467ecaa 100644 --- a/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 +++ b/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 @@ -32,8 +32,7 @@ def custom_stake_amount(self, pair: str, current_time: 'datetime', current_rate: use_custom_stoploss = True def custom_stoploss(self, pair: str, trade: 'Trade', current_time: 'datetime', - current_rate: float, current_profit: float, dataframe: DataFrame, - **kwargs) -> float: + current_rate: float, current_profit: float, **kwargs) -> float: """ Custom stoploss logic, returning the new distance relative to current_rate (as ratio). e.g. returning -0.05 would create a stoploss 5% below current_rate. @@ -44,14 +43,13 @@ def custom_stoploss(self, pair: str, trade: 'Trade', current_time: 'datetime', When not implemented by a strategy, returns the initial stoploss value Only called when use_custom_stoploss is set to True. - :param pair: Pair that's about to be sold. + :param pair: Pair that's currently analyzed :param trade: trade object. :param current_time: datetime object, containing the current datetime :param current_rate: Rate, calculated based on pricing settings in ask_strategy. :param current_profit: Current profit (as ratio), calculated based on current_rate. - :param dataframe: Analyzed dataframe for this pair. Can contain future data in backtesting. :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. - :return float: New stoploss value, relative to the currentrate + :return float: New stoploss value, relative to the current_rate """ return self.stoploss From 7f4baab420ce98c97deeedb69170c3129828c9b2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 4 Oct 2021 20:14:14 +0200 Subject: [PATCH 059/144] Remove explicit rateLimits, improve docs --- config_examples/config_binance.example.json | 4 +- config_examples/config_ftx.example.json | 7 +-- config_examples/config_full.example.json | 8 +-- config_examples/config_kraken.example.json | 4 +- docs/configuration.md | 39 --------------- docs/exchanges.md | 50 +++++++++++++++++++ docs/faq.md | 10 ++-- .../subtemplates/exchange_binance.j2 | 7 +-- .../subtemplates/exchange_generic.j2 | 6 +-- 9 files changed, 65 insertions(+), 70 deletions(-) diff --git a/config_examples/config_binance.example.json b/config_examples/config_binance.example.json index 938bc9342..d59ff96cb 100644 --- a/config_examples/config_binance.example.json +++ b/config_examples/config_binance.example.json @@ -28,10 +28,8 @@ "name": "binance", "key": "your_exchange_key", "secret": "your_exchange_secret", - "ccxt_config": {"enableRateLimit": true}, + "ccxt_config": {}, "ccxt_async_config": { - "enableRateLimit": true, - "rateLimit": 200 }, "pair_whitelist": [ "ALGO/BTC", diff --git a/config_examples/config_ftx.example.json b/config_examples/config_ftx.example.json index 48651f04c..4d9633cc0 100644 --- a/config_examples/config_ftx.example.json +++ b/config_examples/config_ftx.example.json @@ -28,11 +28,8 @@ "name": "ftx", "key": "your_exchange_key", "secret": "your_exchange_secret", - "ccxt_config": {"enableRateLimit": true}, - "ccxt_async_config": { - "enableRateLimit": true, - "rateLimit": 50 - }, + "ccxt_config": {}, + "ccxt_async_config": {}, "pair_whitelist": [ "BTC/USD", "ETH/USD", diff --git a/config_examples/config_full.example.json b/config_examples/config_full.example.json index c415d70b0..83b8a27d0 100644 --- a/config_examples/config_full.example.json +++ b/config_examples/config_full.example.json @@ -84,12 +84,8 @@ "key": "your_exchange_key", "secret": "your_exchange_secret", "password": "", - "ccxt_config": {"enableRateLimit": true}, - "ccxt_async_config": { - "enableRateLimit": true, - "rateLimit": 500, - "aiohttp_trust_env": false - }, + "ccxt_config": {}, + "ccxt_async_config": {}, "pair_whitelist": [ "ALGO/BTC", "ATOM/BTC", diff --git a/config_examples/config_kraken.example.json b/config_examples/config_kraken.example.json index bf3548568..32def895c 100644 --- a/config_examples/config_kraken.example.json +++ b/config_examples/config_kraken.example.json @@ -28,10 +28,8 @@ "name": "kraken", "key": "your_exchange_key", "secret": "your_exchange_key", - "ccxt_config": {"enableRateLimit": true}, + "ccxt_config": {}, "ccxt_async_config": { - "enableRateLimit": true, - "rateLimit": 1000 }, "pair_whitelist": [ "ADA/EUR", diff --git a/docs/configuration.md b/docs/configuration.md index 6ccea4c73..bc8a40dcb 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -447,45 +447,6 @@ The possible values are: `gtc` (default), `fok` or `ioc`. This is ongoing work. For now, it is supported only for binance and kucoin. Please don't change the default value unless you know what you are doing and have researched the impact of using different values for your particular exchange. -### Exchange configuration - -Freqtrade is based on [CCXT library](https://github.com/ccxt/ccxt) that supports over 100 cryptocurrency -exchange markets and trading APIs. The complete up-to-date list can be found in the -[CCXT repo homepage](https://github.com/ccxt/ccxt/tree/master/python). - However, the bot was tested by the development team with only Bittrex, Binance and Kraken, - so these are the only officially supported exchanges: - -- [Bittrex](https://bittrex.com/): "bittrex" -- [Binance](https://www.binance.com/): "binance" -- [Kraken](https://kraken.com/): "kraken" - -Feel free to test other exchanges and submit your PR to improve the bot. - -Some exchanges require special configuration, which can be found on the [Exchange-specific Notes](exchanges.md) documentation page. - -#### Sample exchange configuration - -A exchange configuration for "binance" would look as follows: - -```json -"exchange": { - "name": "binance", - "key": "your_exchange_key", - "secret": "your_exchange_secret", - "ccxt_config": {"enableRateLimit": true}, - "ccxt_async_config": { - "enableRateLimit": true, - "rateLimit": 200 - }, -``` - -This configuration enables binance, as well as rate-limiting to avoid bans from the exchange. -`"rateLimit": 200` defines a wait-event of 0.2s between each call. This can also be completely disabled by setting `"enableRateLimit"` to false. - -!!! Note - Optimal settings for rate-limiting depend on the exchange and the size of the whitelist, so an ideal parameter will vary on many other settings. - We try to provide sensible defaults per exchange where possible, if you encounter bans please make sure that `"enableRateLimit"` is enabled and increase the `"rateLimit"` parameter step by step. - ### What values can be used for fiat_display_currency? The `fiat_display_currency` configuration parameter sets the base currency to use for the diff --git a/docs/exchanges.md b/docs/exchanges.md index c0fbdc694..badaa484a 100644 --- a/docs/exchanges.md +++ b/docs/exchanges.md @@ -2,6 +2,56 @@ This page combines common gotchas and informations which are exchange-specific and most likely don't apply to other exchanges. +## Exchange configuration + +Freqtrade is based on [CCXT library](https://github.com/ccxt/ccxt) that supports over 100 cryptocurrency +exchange markets and trading APIs. The complete up-to-date list can be found in the +[CCXT repo homepage](https://github.com/ccxt/ccxt/tree/master/python). +However, the bot was tested by the development team with only a few exchanges. +A current list of these can be found in the "Home" section of this documentation. + +Feel free to test other exchanges and submit your feedback or PR to improve the bot or confirm exchanges that work flawlessly.. + +Some exchanges require special configuration, which can be found below. + +### Sample exchange configuration + +A exchange configuration for "binance" would look as follows: + +```json +"exchange": { + "name": "binance", + "key": "your_exchange_key", + "secret": "your_exchange_secret", + "ccxt_config": {}, + "ccxt_async_config": {}, + // ... +``` + +### Setting rate limits + +Usually, rate limits set by CCXT are reliable and work well. +In case of problems related to rate-limits (usually DDOS Exceptions in your logs), it's easy to change rateLimit settings to other values. + +```json +"exchange": { + "name": "kraken", + "key": "your_exchange_key", + "secret": "your_exchange_secret", + "ccxt_config": {"enableRateLimit": true}, + "ccxt_async_config": { + "enableRateLimit": true, + "rateLimit": 3100 + }, +``` + +This configuration enables kraken, as well as rate-limiting to avoid bans from the exchange. +`"rateLimit": 3100` defines a wait-event of 0.2s between each call. This can also be completely disabled by setting `"enableRateLimit"` to false. + +!!! Note + Optimal settings for rate-limiting depend on the exchange and the size of the whitelist, so an ideal parameter will vary on many other settings. + We try to provide sensible defaults per exchange where possible, if you encounter bans please make sure that `"enableRateLimit"` is enabled and increase the `"rateLimit"` parameter step by step. + ## Binance Binance supports [time_in_force](configuration.md#understand-order_time_in_force). diff --git a/docs/faq.md b/docs/faq.md index 285625491..75c40a681 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -82,11 +82,11 @@ Currently known to happen for US Bittrex users. Read [the Bittrex section about restricted markets](exchanges.md#restricted-markets) for more information. -### I'm getting the "Exchange Bittrex does not support market orders." message and cannot run my strategy +### I'm getting the "Exchange XXX does not support market orders." message and cannot run my strategy -As the message says, Bittrex does not support market orders and you have one of the [order types](configuration.md/#understand-order_types) set to "market". Your strategy was probably written with other exchanges in mind and sets "market" orders for "stoploss" orders, which is correct and preferable for most of the exchanges supporting market orders (but not for Bittrex). +As the message says, your exchange does not support market orders and you have one of the [order types](configuration.md/#understand-order_types) set to "market". Your strategy was probably written with other exchanges in mind and sets "market" orders for "stoploss" orders, which is correct and preferable for most of the exchanges supporting market orders (but not for Bittrex and Gate.io). -To fix it for Bittrex, redefine order types in the strategy to use "limit" instead of "market": +To fix this, redefine order types in the strategy to use "limit" instead of "market": ``` order_types = { @@ -136,6 +136,8 @@ On Windows, the `--logfile` option is also supported by Freqtrade and you can us > type \path\to\mylogfile.log | findstr "something" ``` +## Hyperopt module + ### Why does freqtrade not have GPU support? First of all, most indicator libraries don't have GPU support - as such, there would be little benefit for indicator calculations. @@ -152,8 +154,6 @@ The benefit of using GPU would therefore be pretty slim - and will not justify t There is however nothing preventing you from using GPU-enabled indicators within your strategy if you think you must have this - you will however probably be disappointed by the slim gain that will give you (compared to the complexity). -## Hyperopt module - ### How many epochs do I need to get a good Hyperopt result? Per default Hyperopt called without the `-e`/`--epochs` command line option will only diff --git a/freqtrade/templates/subtemplates/exchange_binance.j2 b/freqtrade/templates/subtemplates/exchange_binance.j2 index de58b6f72..dc2272119 100644 --- a/freqtrade/templates/subtemplates/exchange_binance.j2 +++ b/freqtrade/templates/subtemplates/exchange_binance.j2 @@ -2,11 +2,8 @@ "name": "{{ exchange_name | lower }}", "key": "{{ exchange_key }}", "secret": "{{ exchange_secret }}", - "ccxt_config": {"enableRateLimit": true}, - "ccxt_async_config": { - "enableRateLimit": true, - "rateLimit": 200 - }, + "ccxt_config": {}, + "ccxt_async_config": {}, "pair_whitelist": [ ], "pair_blacklist": [ diff --git a/freqtrade/templates/subtemplates/exchange_generic.j2 b/freqtrade/templates/subtemplates/exchange_generic.j2 index ade9c2f28..08b11f365 100644 --- a/freqtrade/templates/subtemplates/exchange_generic.j2 +++ b/freqtrade/templates/subtemplates/exchange_generic.j2 @@ -2,10 +2,8 @@ "name": "{{ exchange_name | lower }}", "key": "{{ exchange_key }}", "secret": "{{ exchange_secret }}", - "ccxt_config": {"enableRateLimit": true}, - "ccxt_async_config": { - "enableRateLimit": true - }, + "ccxt_config": {}, + "ccxt_async_config": {}, "pair_whitelist": [ ], From 92f8f231afe79eafa5079d494b56db71e7f30baf Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 4 Oct 2021 20:22:41 +0200 Subject: [PATCH 060/144] Remove ratelimit from kucoin template --- freqtrade/templates/subtemplates/exchange_kucoin.j2 | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/freqtrade/templates/subtemplates/exchange_kucoin.j2 b/freqtrade/templates/subtemplates/exchange_kucoin.j2 index 9882c51c7..b797dda41 100644 --- a/freqtrade/templates/subtemplates/exchange_kucoin.j2 +++ b/freqtrade/templates/subtemplates/exchange_kucoin.j2 @@ -3,14 +3,8 @@ "key": "{{ exchange_key }}", "secret": "{{ exchange_secret }}", "password": "{{ exchange_key_password }}", - "ccxt_config": { - "enableRateLimit": true, - "rateLimit": 200 - }, - "ccxt_async_config": { - "enableRateLimit": true, - "rateLimit": 200 - }, + "ccxt_config": {}, + "ccxt_async_config": {}, "pair_whitelist": [ ], "pair_blacklist": [ From 0db5c07314a02beb7dde0baf76a06fff458fd5c6 Mon Sep 17 00:00:00 2001 From: froggleston Date: Tue, 5 Oct 2021 00:10:39 +0100 Subject: [PATCH 061/144] Fix issues with sysinfo rpc/API code, add SysInfo api_schema --- freqtrade/rpc/api_server/api_schemas.py | 4 ++++ freqtrade/rpc/api_server/api_v1.py | 8 ++++---- freqtrade/rpc/rpc.py | 3 ++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index 46187f571..b03400900 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -347,3 +347,7 @@ class BacktestResponse(BaseModel): trade_count: Optional[float] # TODO: Properly type backtestresult... backtest_result: Optional[Dict[str, Any]] + +class SysInfo(BaseModel): + cpu_pct: float + ram_pct: float diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index 733fa7383..d52e8c10d 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -18,7 +18,7 @@ from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, Balances, Blac OpenTradeSchema, PairHistory, PerformanceEntry, Ping, PlotConfig, Profit, ResultMsg, ShowConfig, Stats, StatusMsg, StrategyListResponse, - StrategyResponse, Version, WhitelistResponse) + StrategyResponse, SysInfo, Version, WhitelistResponse) from freqtrade.rpc.api_server.deps import get_config, get_rpc, get_rpc_optional from freqtrade.rpc.rpc import RPCException @@ -260,6 +260,6 @@ def list_available_pairs(timeframe: Optional[str] = None, stake_currency: Option } return result -@router.get('/sysinfo', tags=['info']) -def sysinfo(rpc: RPC = Depends(get_rpc)): - return rpc._rpc_sysinfo() +@router.get('/sysinfo', response_model=SysInfo, tags=['info']) +def sysinfo(): + return RPC._rpc_sysinfo() diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 9b0d4b0f7..699e3b384 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -871,5 +871,6 @@ class RPC: self._freqtrade.strategy.plot_config['subplots'] = {} return self._freqtrade.strategy.plot_config - def _rpc_sysinfo(self) -> Dict[str, Any]: + @staticmethod + def _rpc_sysinfo() -> Dict[str, Any]: return {"cpu_pct": psutil.cpu_percent(interval=1, percpu=True), "ram_pct": psutil.virtual_memory().percent} From 949d616082c49be11211a396de676077ad741c8d Mon Sep 17 00:00:00 2001 From: jonny07 Date: Tue, 5 Oct 2021 21:33:15 +0200 Subject: [PATCH 062/144] Update docker_quickstart.md Got help in the discord chat to get the UI running, I think most people will need this... --- docs/docker_quickstart.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/docs/docker_quickstart.md b/docs/docker_quickstart.md index 27a9091b1..0fe69933a 100644 --- a/docs/docker_quickstart.md +++ b/docs/docker_quickstart.md @@ -70,6 +70,40 @@ docker-compose up -d !!! Warning "Default configuration" While the configuration generated will be mostly functional, you will still need to verify that all options correspond to what you want (like Pricing, pairlist, ...) before starting the bot. +#### Acessing the UI + +Uncommend the 2 lines below and add your IP adress in the following format (like 192.168.2.67:8080:8080) to the ft_userdata/docker-compose.yml: +'''bash + ports: + - "yourIPadress:8080:8080" +''' +Your ft_userdata/user_data/config.json should look like: +'''bash +api_server": { + "enabled": true, + "listen_ip_address": "0.0.0.0", + "listen_port": 8080, + "verbosity": "error", + "enable_openapi": false, + "jwt_secret_key": "****", + "CORS_origins": [], + "username": "****", + "password": "****" + }, +''' +instead of "****" you will have your data in. +Then rebuild your docker file: +Linux: +'''bash +sudo docker-compose down && sudo docker-compose pull && sudo docker-compose build && sudo docker-compose up -d +''' +Windows: +'''bash +docker-compose down && docker-compose pull && docker-compose build && docker-compose up -d +''' + +You can now access the UI by typing yourIPadress:8080 in your browser. + #### Monitoring the bot You can check for running instances with `docker-compose ps`. From c0d01dbc26a1552562ca339a92111f6193e7a02c Mon Sep 17 00:00:00 2001 From: sid Date: Wed, 6 Oct 2021 13:24:27 +0530 Subject: [PATCH 063/144] add max_drawdown loss --- .../optimize/hyperopt_loss_max_drawdown.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 freqtrade/optimize/hyperopt_loss_max_drawdown.py diff --git a/freqtrade/optimize/hyperopt_loss_max_drawdown.py b/freqtrade/optimize/hyperopt_loss_max_drawdown.py new file mode 100644 index 000000000..e6f73e04a --- /dev/null +++ b/freqtrade/optimize/hyperopt_loss_max_drawdown.py @@ -0,0 +1,42 @@ +""" +MaxDrawDownHyperOptLoss + +This module defines the alternative HyperOptLoss class which can be used for +Hyperoptimization. +""" +from datetime import datetime +from freqtrade.data.btanalysis import calculate_max_drawdown +from freqtrade.optimize.hyperopt import IHyperOptLoss + +from pandas import DataFrame + + +class MaxDrawDownHyperOptLoss(IHyperOptLoss): + + """ + Defines the loss function for hyperopt. + + This implementation optimizes for max draw down and profit + Less max drawdown more profit -> Lower return value + """ + + @staticmethod + def hyperopt_loss_function(results: DataFrame, trade_count: int, + min_date: datetime, max_date: datetime, + *args, **kwargs) -> float: + + """ + Objective function. + + Uses profit ratio weighted max_drawdown when drawdown is available. + Otherwise directly optimizes profit ratio. + """ + total_profit = results['profit_ratio'].sum() + try: + max_drawdown = calculate_max_drawdown(results) + except ValueError: + # No losing trade, therefore no drawdown. + return -total_profit + max_drawdown_rev = 1 / max_drawdown[0] + ret = max_drawdown_rev * total_profit + return -ret \ No newline at end of file From 6ba46b38bdd434ddd65b065c6393beb0b29aa492 Mon Sep 17 00:00:00 2001 From: sid Date: Wed, 6 Oct 2021 13:46:05 +0530 Subject: [PATCH 064/144] fix formatting --- freqtrade/optimize/hyperopt_loss_max_drawdown.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/freqtrade/optimize/hyperopt_loss_max_drawdown.py b/freqtrade/optimize/hyperopt_loss_max_drawdown.py index e6f73e04a..4fa32c00e 100644 --- a/freqtrade/optimize/hyperopt_loss_max_drawdown.py +++ b/freqtrade/optimize/hyperopt_loss_max_drawdown.py @@ -5,11 +5,12 @@ This module defines the alternative HyperOptLoss class which can be used for Hyperoptimization. """ from datetime import datetime -from freqtrade.data.btanalysis import calculate_max_drawdown -from freqtrade.optimize.hyperopt import IHyperOptLoss from pandas import DataFrame +from freqtrade.data.btanalysis import calculate_max_drawdown +from freqtrade.optimize.hyperopt import IHyperOptLoss + class MaxDrawDownHyperOptLoss(IHyperOptLoss): @@ -31,7 +32,7 @@ class MaxDrawDownHyperOptLoss(IHyperOptLoss): Uses profit ratio weighted max_drawdown when drawdown is available. Otherwise directly optimizes profit ratio. """ - total_profit = results['profit_ratio'].sum() + total_profit = results['profit_ratio'].sum() try: max_drawdown = calculate_max_drawdown(results) except ValueError: @@ -39,4 +40,4 @@ class MaxDrawDownHyperOptLoss(IHyperOptLoss): return -total_profit max_drawdown_rev = 1 / max_drawdown[0] ret = max_drawdown_rev * total_profit - return -ret \ No newline at end of file + return -ret From 57ef25789e3db1dd59de1e76096bf730fefdf0d7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 6 Oct 2021 19:36:28 +0200 Subject: [PATCH 065/144] Fix style errors --- freqtrade/rpc/api_server/api_schemas.py | 1 + freqtrade/rpc/api_server/api_v1.py | 4 +++- freqtrade/rpc/rpc.py | 8 ++++++-- scripts/rest_client.py | 1 + 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index b03400900..bde6af35b 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -348,6 +348,7 @@ class BacktestResponse(BaseModel): # TODO: Properly type backtestresult... backtest_result: Optional[Dict[str, Any]] + class SysInfo(BaseModel): cpu_pct: float ram_pct: float diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index d52e8c10d..06230a7db 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -18,7 +18,8 @@ from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, Balances, Blac OpenTradeSchema, PairHistory, PerformanceEntry, Ping, PlotConfig, Profit, ResultMsg, ShowConfig, Stats, StatusMsg, StrategyListResponse, - StrategyResponse, SysInfo, Version, WhitelistResponse) + StrategyResponse, SysInfo, Version, + WhitelistResponse) from freqtrade.rpc.api_server.deps import get_config, get_rpc, get_rpc_optional from freqtrade.rpc.rpc import RPCException @@ -260,6 +261,7 @@ def list_available_pairs(timeframe: Optional[str] = None, stake_currency: Option } return result + @router.get('/sysinfo', response_model=SysInfo, tags=['info']) def sysinfo(): return RPC._rpc_sysinfo() diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 699e3b384..d0858350c 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -1,13 +1,14 @@ """ This module contains class to define a RPC communications """ -import logging, psutil +import logging from abc import abstractmethod from datetime import date, datetime, timedelta, timezone from math import isnan from typing import Any, Dict, List, Optional, Tuple, Union import arrow +import psutil from numpy import NAN, inf, int64, mean from pandas import DataFrame @@ -873,4 +874,7 @@ class RPC: @staticmethod def _rpc_sysinfo() -> Dict[str, Any]: - return {"cpu_pct": psutil.cpu_percent(interval=1, percpu=True), "ram_pct": psutil.virtual_memory().percent} + return { + "cpu_pct": psutil.cpu_percent(interval=1, percpu=True), + "ram_pct": psutil.virtual_memory().percent + } diff --git a/scripts/rest_client.py b/scripts/rest_client.py index 52de3c534..ac3b6defe 100755 --- a/scripts/rest_client.py +++ b/scripts/rest_client.py @@ -341,6 +341,7 @@ class FtRestClient(): """ return self._get("sysinfo") + def add_arguments(): parser = argparse.ArgumentParser() parser.add_argument("command", From 992cef56e653844c4b8613c683db81057959f569 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 6 Oct 2021 19:36:51 +0200 Subject: [PATCH 066/144] Add test for sysinfo endpoint --- tests/rpc/test_rpc_apiserver.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 7c98b2df7..117b1fa49 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -1271,6 +1271,16 @@ def test_list_available_pairs(botclient): assert len(rc.json()['pair_interval']) == 1 +def test_sysinfo(botclient): + ftbot, client = botclient + + rc = client_get(client, f"{BASE_URI}/sysinfo") + assert_response(rc) + result = rc.json() + assert 'cpu_pct' in result + assert 'ram_pct' in result + + def test_api_backtesting(botclient, mocker, fee, caplog): ftbot, client = botclient mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) From 65d4df938df14881729a63bf2c58ace1aca57d22 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 6 Oct 2021 20:09:08 +0200 Subject: [PATCH 067/144] Improve docker port api --- docker-compose.yml | 6 +++--- docs/docker_quickstart.md | 43 ++++++++++++++++++++------------------- docs/rest-api.md | 2 +- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 80e194ab2..445fbaea0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,10 +15,10 @@ services: volumes: - "./user_data:/freqtrade/user_data" # Expose api on port 8080 (localhost only) - # Please read the https://www.freqtrade.io/en/latest/rest-api/ documentation + # Please read the https://www.freqtrade.io/en/stable/rest-api/ documentation # before enabling this. - # ports: - # - "127.0.0.1:8080:8080" + ports: + - "127.0.0.1:8080:8080" # Default command used when running `docker compose up` command: > trade diff --git a/docs/docker_quickstart.md b/docs/docker_quickstart.md index 27a9091b1..d5bec54c9 100644 --- a/docs/docker_quickstart.md +++ b/docs/docker_quickstart.md @@ -148,27 +148,9 @@ You'll then also need to modify the `docker-compose.yml` file and uncomment the dockerfile: "./Dockerfile." ``` -You can then run `docker-compose build` to build the docker image, and run it using the commands described above. +You can then run `docker-compose build --pull` to build the docker image, and run it using the commands described above. -### Troubleshooting - -#### Docker on Windows - -* Error: `"Timestamp for this request is outside of the recvWindow."` - * The market api requests require a synchronized clock but the time in the docker container shifts a bit over time into the past. - To fix this issue temporarily you need to run `wsl --shutdown` and restart docker again (a popup on windows 10 will ask you to do so). - A permanent solution is either to host the docker container on a linux host or restart the wsl from time to time with the scheduler. - ``` - taskkill /IM "Docker Desktop.exe" /F - wsl --shutdown - start "" "C:\Program Files\Docker\Docker\Docker Desktop.exe" - ``` - -!!! Warning - Due to the above, we do not recommend the usage of docker on windows for production setups, but only for experimentation, datadownload and backtesting. - Best use a linux-VPS for running freqtrade reliably. - -## Plotting with docker-compose +### Plotting with docker-compose Commands `freqtrade plot-profit` and `freqtrade plot-dataframe` ([Documentation](plotting.md)) are available by changing the image to `*_plot` in your docker-compose.yml file. You can then use these commands as follows: @@ -179,7 +161,7 @@ docker-compose run --rm freqtrade plot-dataframe --strategy AwesomeStrategy -p B The output will be stored in the `user_data/plot` directory, and can be opened with any modern browser. -## Data analysis using docker compose +### Data analysis using docker compose Freqtrade provides a docker-compose file which starts up a jupyter lab server. You can run this server using the following command: @@ -196,3 +178,22 @@ Since part of this image is built on your machine, it is recommended to rebuild ``` bash docker-compose -f docker/docker-compose-jupyter.yml build --no-cache ``` + +## Troubleshooting + +### Docker on Windows + +* Error: `"Timestamp for this request is outside of the recvWindow."` + * The market api requests require a synchronized clock but the time in the docker container shifts a bit over time into the past. + To fix this issue temporarily you need to run `wsl --shutdown` and restart docker again (a popup on windows 10 will ask you to do so). + A permanent solution is either to host the docker container on a linux host or restart the wsl from time to time with the scheduler. + + ``` bash + taskkill /IM "Docker Desktop.exe" /F + wsl --shutdown + start "" "C:\Program Files\Docker\Docker\Docker Desktop.exe" + ``` + +!!! Warning + Due to the above, we do not recommend the usage of docker on windows for production setups, but only for experimentation, datadownload and backtesting. + Best use a linux-VPS for running freqtrade reliably. diff --git a/docs/rest-api.md b/docs/rest-api.md index b9b2b29be..b4992e047 100644 --- a/docs/rest-api.md +++ b/docs/rest-api.md @@ -78,7 +78,7 @@ If you run your bot using docker, you'll need to have the bot listen to incoming }, ``` -Uncomment the following from your docker-compose file: +Make sure that the following 2 lines are available in your docker-compose file: ```yml ports: From 526bdaa2dc04cd84dbea35c72482697cc865080e Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 6 Oct 2021 20:14:59 +0200 Subject: [PATCH 068/144] Recommend using 0.0.0.0 as listen address for docker --- freqtrade/commands/build_config_commands.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/commands/build_config_commands.py b/freqtrade/commands/build_config_commands.py index faa8a98f4..34ae35aff 100644 --- a/freqtrade/commands/build_config_commands.py +++ b/freqtrade/commands/build_config_commands.py @@ -163,7 +163,8 @@ def ask_user_config() -> Dict[str, Any]: { "type": "text", "name": "api_server_listen_addr", - "message": "Insert Api server Listen Address (best left untouched default!)", + "message": ("Insert Api server Listen Address (0.0.0.0 for docker, " + "otherwise best left untouched)"), "default": "127.0.0.1", "when": lambda x: x['api_server'] }, From 46c320513aa0b825b370034feba9d6e9f29af312 Mon Sep 17 00:00:00 2001 From: sid Date: Thu, 7 Oct 2021 08:07:07 +0530 Subject: [PATCH 069/144] use profit_abs --- freqtrade/optimize/hyperopt_loss_max_drawdown.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/optimize/hyperopt_loss_max_drawdown.py b/freqtrade/optimize/hyperopt_loss_max_drawdown.py index 4fa32c00e..6777fb2e8 100644 --- a/freqtrade/optimize/hyperopt_loss_max_drawdown.py +++ b/freqtrade/optimize/hyperopt_loss_max_drawdown.py @@ -32,9 +32,9 @@ class MaxDrawDownHyperOptLoss(IHyperOptLoss): Uses profit ratio weighted max_drawdown when drawdown is available. Otherwise directly optimizes profit ratio. """ - total_profit = results['profit_ratio'].sum() + total_profit = results['profit_abs'].sum() try: - max_drawdown = calculate_max_drawdown(results) + max_drawdown = calculate_max_drawdown(results, value_col='profit_abs') except ValueError: # No losing trade, therefore no drawdown. return -total_profit From 29863ad2bf7d364e0bc17dc5c3506e24f20b31b4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 7 Oct 2021 06:51:29 +0200 Subject: [PATCH 070/144] Fix error when ask_last_balance is not set closes #5181 --- freqtrade/exchange/exchange.py | 2 +- tests/exchange/test_exchange.py | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index e9d0316d2..7cc436430 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1058,7 +1058,7 @@ class Exchange: ticker_rate = ticker[conf_strategy['price_side']] if ticker['last'] and ticker_rate: if side == 'buy' and ticker_rate > ticker['last']: - balance = conf_strategy['ask_last_balance'] + balance = conf_strategy.get('ask_last_balance', 0.0) ticker_rate = ticker_rate + balance * (ticker['last'] - ticker_rate) elif side == 'sell' and ticker_rate < ticker['last']: balance = conf_strategy.get('bid_last_balance', 0.0) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 691cf3c03..8cb494edb 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1832,6 +1832,7 @@ def test_fetch_l2_order_book_exception(default_conf, mocker, exchange_name): ('ask', 20, 19, 10, 0.3, 17), # Between ask and last ('ask', 5, 6, 10, 1.0, 5), # last bigger than ask ('ask', 5, 6, 10, 0.5, 5), # last bigger than ask + ('ask', 20, 19, 10, None, 20), # ask_last_balance missing ('ask', 10, 20, None, 0.5, 10), # last not available - uses ask ('ask', 4, 5, None, 0.5, 4), # last not available - uses ask ('ask', 4, 5, None, 1, 4), # last not available - uses ask @@ -1842,6 +1843,7 @@ def test_fetch_l2_order_book_exception(default_conf, mocker, exchange_name): ('bid', 21, 20, 10, 0.7, 13), # Between bid and last ('bid', 21, 20, 10, 0.3, 17), # Between bid and last ('bid', 6, 5, 10, 1.0, 5), # last bigger than bid + ('bid', 21, 20, 10, None, 20), # ask_last_balance missing ('bid', 6, 5, 10, 0.5, 5), # last bigger than bid ('bid', 21, 20, None, 0.5, 20), # last not available - uses bid ('bid', 6, 5, None, 0.5, 5), # last not available - uses bid @@ -1851,7 +1853,10 @@ def test_fetch_l2_order_book_exception(default_conf, mocker, exchange_name): def test_get_buy_rate(mocker, default_conf, caplog, side, ask, bid, last, last_ab, expected) -> None: caplog.set_level(logging.DEBUG) - default_conf['bid_strategy']['ask_last_balance'] = last_ab + if last_ab is None: + del default_conf['bid_strategy']['ask_last_balance'] + else: + default_conf['bid_strategy']['ask_last_balance'] = last_ab default_conf['bid_strategy']['price_side'] = side exchange = get_patched_exchange(mocker, default_conf) mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', @@ -1876,6 +1881,7 @@ def test_get_buy_rate(mocker, default_conf, caplog, side, ask, bid, ('bid', 12.0, 11.2, 10.5, 1.0, 11.2), # Last smaller than bid - uses bid ('bid', 12.0, 11.2, 10.5, 0.5, 11.2), # Last smaller than bid - uses bid ('bid', 0.003, 0.002, 0.005, 0.0, 0.002), + ('bid', 0.003, 0.002, 0.005, None, 0.002), ('ask', 12.0, 11.0, 12.5, 0.0, 12.0), # full ask side ('ask', 12.0, 11.0, 12.5, 1.0, 12.5), # full last side ('ask', 12.0, 11.0, 12.5, 0.5, 12.25), # between bid and lat @@ -1886,6 +1892,7 @@ def test_get_buy_rate(mocker, default_conf, caplog, side, ask, bid, ('ask', 10.11, 11.2, 11.0, 0.0, 10.11), ('ask', 0.001, 0.002, 11.0, 0.0, 0.001), ('ask', 0.006, 1.0, 11.0, 0.0, 0.006), + ('ask', 0.006, 1.0, 11.0, None, 0.006), ]) def test_get_sell_rate(default_conf, mocker, caplog, side, bid, ask, last, last_ab, expected) -> None: From 45b7a0c8377a5493496abafa70e48b0872d0b4b5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 7 Oct 2021 07:12:45 +0200 Subject: [PATCH 071/144] Add Test and docs for MaxDrawDownHyperOptLoss --- docs/hyperopt.md | 18 ++++++++++-------- freqtrade/constants.py | 3 ++- tests/optimize/test_hyperoptloss.py | 5 +++-- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 09d43939a..45e0d444d 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -60,7 +60,7 @@ optional arguments: Specify what timerange of data to use. --data-format-ohlcv {json,jsongz,hdf5} Storage format for downloaded candle (OHLCV) data. - (default: `None`). + (default: `json`). --max-open-trades INT Override the value of the `max_open_trades` configuration setting. @@ -114,7 +114,8 @@ optional arguments: Hyperopt-loss-functions are: ShortTradeDurHyperOptLoss, OnlyProfitHyperOptLoss, SharpeHyperOptLoss, SharpeHyperOptLossDaily, - SortinoHyperOptLoss, SortinoHyperOptLossDaily + SortinoHyperOptLoss, SortinoHyperOptLossDaily, + MaxDrawDownHyperOptLoss --disable-param-export Disable automatic hyperopt parameter export. @@ -512,12 +513,13 @@ This class should be in its own file within the `user_data/hyperopts/` directory Currently, the following loss functions are builtin: -* `ShortTradeDurHyperOptLoss` (default legacy Freqtrade hyperoptimization loss function) - Mostly for short trade duration and avoiding losses. -* `OnlyProfitHyperOptLoss` (which takes only amount of profit into consideration) -* `SharpeHyperOptLoss` (optimizes Sharpe Ratio calculated on trade returns relative to standard deviation) -* `SharpeHyperOptLossDaily` (optimizes Sharpe Ratio calculated on **daily** trade returns relative to standard deviation) -* `SortinoHyperOptLoss` (optimizes Sortino Ratio calculated on trade returns relative to **downside** standard deviation) -* `SortinoHyperOptLossDaily` (optimizes Sortino Ratio calculated on **daily** trade returns relative to **downside** standard deviation) +* `ShortTradeDurHyperOptLoss` - (default legacy Freqtrade hyperoptimization loss function) - Mostly for short trade duration and avoiding losses. +* `OnlyProfitHyperOptLoss` - takes only amount of profit into consideration. +* `SharpeHyperOptLoss` - optimizes Sharpe Ratio calculated on trade returns relative to standard deviation. +* `SharpeHyperOptLossDaily` - optimizes Sharpe Ratio calculated on **daily** trade returns relative to standard deviation. +* `SortinoHyperOptLoss` - optimizes Sortino Ratio calculated on trade returns relative to **downside** standard deviation. +* `SortinoHyperOptLossDaily` - optimizes Sortino Ratio calculated on **daily** trade returns relative to **downside** standard deviation. +* `MaxDrawDownHyperOptLoss` - Optimizes Maximum drawdown. Creation of a custom loss function is covered in the [Advanced Hyperopt](advanced-hyperopt.md) part of the documentation. diff --git a/freqtrade/constants.py b/freqtrade/constants.py index fca319a0f..c6b8f0e62 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -24,7 +24,8 @@ ORDERTYPE_POSSIBILITIES = ['limit', 'market'] ORDERTIF_POSSIBILITIES = ['gtc', 'fok', 'ioc'] HYPEROPT_LOSS_BUILTIN = ['ShortTradeDurHyperOptLoss', 'OnlyProfitHyperOptLoss', 'SharpeHyperOptLoss', 'SharpeHyperOptLossDaily', - 'SortinoHyperOptLoss', 'SortinoHyperOptLossDaily'] + 'SortinoHyperOptLoss', 'SortinoHyperOptLossDaily', + 'MaxDrawDownHyperOptLoss'] AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList', 'AgeFilter', 'OffsetFilter', 'PerformanceFilter', 'PrecisionFilter', 'PriceFilter', 'RangeStabilityFilter', diff --git a/tests/optimize/test_hyperoptloss.py b/tests/optimize/test_hyperoptloss.py index 923e3fc32..a39190934 100644 --- a/tests/optimize/test_hyperoptloss.py +++ b/tests/optimize/test_hyperoptloss.py @@ -84,13 +84,14 @@ def test_loss_calculation_has_limited_profit(hyperopt_conf, hyperopt_results) -> "SortinoHyperOptLossDaily", "SharpeHyperOptLoss", "SharpeHyperOptLossDaily", + "MaxDrawDownHyperOptLoss", ]) def test_loss_functions_better_profits(default_conf, hyperopt_results, lossfunction) -> None: results_over = hyperopt_results.copy() - results_over['profit_abs'] = hyperopt_results['profit_abs'] * 2 + results_over['profit_abs'] = hyperopt_results['profit_abs'] * 2 + 0.2 results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2 results_under = hyperopt_results.copy() - results_under['profit_abs'] = hyperopt_results['profit_abs'] / 2 + results_under['profit_abs'] = hyperopt_results['profit_abs'] / 2 - 0.2 results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2 default_conf.update({'hyperopt_loss': lossfunction}) From a1be6124f221d6e3bd294c376a76848ff3e6d197 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 7 Oct 2021 07:15:09 +0200 Subject: [PATCH 072/144] Don't set bid_last_balance if None in tests part of #5681 --- tests/exchange/test_exchange.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 8cb494edb..e3369182d 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1899,7 +1899,8 @@ def test_get_sell_rate(default_conf, mocker, caplog, side, bid, ask, caplog.set_level(logging.DEBUG) default_conf['ask_strategy']['price_side'] = side - default_conf['ask_strategy']['bid_last_balance'] = last_ab + if last_ab is not None: + default_conf['ask_strategy']['bid_last_balance'] = last_ab mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', return_value={'ask': ask, 'bid': bid, 'last': last}) pair = "ETH/BTC" From f07eeddda0e912e44467bff42b6c08f125902ae1 Mon Sep 17 00:00:00 2001 From: Robert Davey Date: Thu, 7 Oct 2021 12:04:42 +0100 Subject: [PATCH 073/144] Update api_schemas.py Fix api schema for cpu_pct float List. --- freqtrade/rpc/api_server/api_schemas.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index bde6af35b..e9985c3c6 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -350,5 +350,5 @@ class BacktestResponse(BaseModel): class SysInfo(BaseModel): - cpu_pct: float + cpu_pct: List[float] ram_pct: float From 1327c21d0103eee5e040e03e598662c993bcfac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=AE=AE=E0=AE=A9=E0=AF=8B=E0=AE=9C=E0=AF=8D=E0=AE=95?= =?UTF-8?q?=E0=AF=81=E0=AE=AE=E0=AE=BE=E0=AE=B0=E0=AF=8D=20=E0=AE=AA?= =?UTF-8?q?=E0=AE=B4=E0=AE=A9=E0=AE=BF=E0=AE=9A=E0=AF=8D=E0=AE=9A=E0=AE=BE?= =?UTF-8?q?=E0=AE=AE=E0=AE=BF?= Date: Thu, 7 Oct 2021 19:12:09 +0530 Subject: [PATCH 074/144] Update README.md --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 01effd7bc..79763983d 100644 --- a/README.md +++ b/README.md @@ -66,9 +66,7 @@ Please find the complete documentation on our [website](https://www.freqtrade.io Freqtrade provides a Linux/macOS script to install all dependencies and help you to configure the bot. ```bash -git clone -b develop https://github.com/freqtrade/freqtrade.git -cd freqtrade -./setup.sh --install +pip install freqtrade ``` For any other type of installation please refer to [Installation doc](https://www.freqtrade.io/en/latest/installation/). From 482f4418c68c2b99635a7af3cc65a14c1022e031 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 8 Oct 2021 14:36:52 +0200 Subject: [PATCH 075/144] Clarify "required candle" message --- freqtrade/exchange/exchange.py | 62 ++++++++++++++-------------------- 1 file changed, 25 insertions(+), 37 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 7cc436430..b6cfb8d8b 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -26,9 +26,9 @@ from freqtrade.exceptions import (DDosProtection, ExchangeError, InsufficientFun InvalidOrderException, OperationalException, PricingError, RetryableOrderError, TemporaryError) from freqtrade.exchange.common import (API_FETCH_ORDER_RETRY_COUNT, BAD_EXCHANGES, - EXCHANGE_HAS_OPTIONAL, EXCHANGE_HAS_REQUIRED, - remove_credentials, retrier, retrier_async) -from freqtrade.misc import chunks, deep_merge_dicts, safe_value_fallback2 + EXCHANGE_HAS_OPTIONAL, EXCHANGE_HAS_REQUIRED, retrier, + retrier_async) +from freqtrade.misc import deep_merge_dicts, safe_value_fallback2 from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist @@ -54,16 +54,12 @@ class Exchange: # Parameters to add directly to buy/sell calls (like agreeing to trading agreement) _params: Dict = {} - # Additional headers - added to the ccxt object - _headers: Dict = {} - # Dict to specify which options each exchange implements # This defines defaults, which can be selectively overridden by subclasses using _ft_has # or by specifying them in the configuration. _ft_has_default: Dict = { "stoploss_on_exchange": False, "order_time_in_force": ["gtc"], - "time_in_force_parameter": "timeInForce", "ohlcv_params": {}, "ohlcv_candle_limit": 500, "ohlcv_partial_candle": True, @@ -104,7 +100,6 @@ class Exchange: # Holds all open sell orders for dry_run self._dry_run_open_orders: Dict[str, Any] = {} - remove_credentials(config) if config['dry_run']: logger.info('Instance is running with dry_run enabled') @@ -174,7 +169,7 @@ class Exchange: asyncio.get_event_loop().run_until_complete(self._api_async.close()) def _init_ccxt(self, exchange_config: Dict[str, Any], ccxt_module: CcxtModuleType = ccxt, - ccxt_kwargs: Dict = {}) -> ccxt.Exchange: + ccxt_kwargs: dict = None) -> ccxt.Exchange: """ Initialize ccxt with given config and return valid ccxt instance. @@ -193,10 +188,6 @@ class Exchange: } if ccxt_kwargs: logger.info('Applying additional ccxt config: %s', ccxt_kwargs) - if self._headers: - # Inject static headers after the above output to not confuse users. - ccxt_kwargs = deep_merge_dicts({'headers': self._headers}, ccxt_kwargs) - if ccxt_kwargs: ex_config.update(ccxt_kwargs) try: @@ -480,7 +471,7 @@ class Exchange: if startup_candles + 5 > candle_limit: raise OperationalException( f"This strategy requires {startup_candles} candles to start. " - f"{self.name} only provides {candle_limit} for {timeframe}.") + f"{self.name} only provides {candle_limit - 5} for {timeframe}.") def exchange_has(self, endpoint: str) -> bool: """ @@ -523,7 +514,7 @@ class Exchange: precision = self.markets[pair]['precision']['price'] missing = price % precision if missing != 0: - price = round(price - missing + precision, 10) + price = price - missing + precision else: symbol_prec = self.markets[pair]['precision']['price'] big_price = price * pow(10, symbol_prec) @@ -725,8 +716,7 @@ class Exchange: params = self._params.copy() if time_in_force != 'gtc' and ordertype != 'market': - param = self._ft_has.get('time_in_force_parameter', '') - params.update({param: time_in_force}) + params.update({'timeInForce': time_in_force}) try: # Set the precision for amount and price(rate) as accepted by the exchange @@ -1058,7 +1048,7 @@ class Exchange: ticker_rate = ticker[conf_strategy['price_side']] if ticker['last'] and ticker_rate: if side == 'buy' and ticker_rate > ticker['last']: - balance = conf_strategy.get('ask_last_balance', 0.0) + balance = conf_strategy['ask_last_balance'] ticker_rate = ticker_rate + balance * (ticker['last'] - ticker_rate) elif side == 'sell' and ticker_rate < ticker['last']: balance = conf_strategy.get('bid_last_balance', 0.0) @@ -1195,7 +1185,7 @@ class Exchange: # Historic data def get_historic_ohlcv(self, pair: str, timeframe: str, - since_ms: int, is_new_pair: bool = False) -> List: + since_ms: int) -> List: """ Get candle history using asyncio and returns the list of candles. Handles all async work for this. @@ -1207,7 +1197,7 @@ class Exchange: """ return asyncio.get_event_loop().run_until_complete( self._async_get_historic_ohlcv(pair=pair, timeframe=timeframe, - since_ms=since_ms, is_new_pair=is_new_pair)) + since_ms=since_ms)) def get_historic_ohlcv_as_df(self, pair: str, timeframe: str, since_ms: int) -> DataFrame: @@ -1222,12 +1212,11 @@ class Exchange: return ohlcv_to_dataframe(ticks, timeframe, pair=pair, fill_missing=True, drop_incomplete=self._ohlcv_partial_candle) - async def _async_get_historic_ohlcv(self, pair: str, timeframe: str, - since_ms: int, is_new_pair: bool - ) -> List: + async def _async_get_historic_ohlcv(self, pair: str, + timeframe: str, + since_ms: int) -> List: """ Download historic ohlcv - :param is_new_pair: used by binance subclass to allow "fast" new pair downloading """ one_call = timeframe_to_msecs(timeframe) * self.ohlcv_candle_limit(timeframe) @@ -1240,22 +1229,21 @@ class Exchange: pair, timeframe, since) for since in range(since_ms, arrow.utcnow().int_timestamp * 1000, one_call)] - data: List = [] - # Chunk requests into batches of 100 to avoid overwelming ccxt Throttling - for input_coro in chunks(input_coroutines, 100): + results = await asyncio.gather(*input_coroutines, return_exceptions=True) - results = await asyncio.gather(*input_coro, return_exceptions=True) - for res in results: - if isinstance(res, Exception): - logger.warning("Async code raised an exception: %s", res.__class__.__name__) - continue - # Deconstruct tuple if it's not an exception - p, _, new_data = res - if p == pair: - data.extend(new_data) + # Combine gathered results + data: List = [] + for res in results: + if isinstance(res, Exception): + logger.warning("Async code raised an exception: %s", res.__class__.__name__) + continue + # Deconstruct tuple if it's not an exception + p, _, new_data = res + if p == pair: + data.extend(new_data) # Sort data again after extending the result - above calls return in "async order" data = sorted(data, key=lambda x: x[0]) - logger.info(f"Downloaded data for {pair} with length {len(data)}.") + logger.info("Downloaded data for %s with length %s.", pair, len(data)) return data def refresh_latest_ohlcv(self, pair_list: ListPairsWithTimeframes, *, From 11ec1d9b062ae7a063d8b6549cdfd5e468047d63 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 8 Oct 2021 20:22:07 +0200 Subject: [PATCH 076/144] Revert previous commit --- freqtrade/exchange/exchange.py | 60 ++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index b6cfb8d8b..4143b79a5 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -26,9 +26,9 @@ from freqtrade.exceptions import (DDosProtection, ExchangeError, InsufficientFun InvalidOrderException, OperationalException, PricingError, RetryableOrderError, TemporaryError) from freqtrade.exchange.common import (API_FETCH_ORDER_RETRY_COUNT, BAD_EXCHANGES, - EXCHANGE_HAS_OPTIONAL, EXCHANGE_HAS_REQUIRED, retrier, - retrier_async) -from freqtrade.misc import deep_merge_dicts, safe_value_fallback2 + EXCHANGE_HAS_OPTIONAL, EXCHANGE_HAS_REQUIRED, + remove_credentials, retrier, retrier_async) +from freqtrade.misc import chunks, deep_merge_dicts, safe_value_fallback2 from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist @@ -54,12 +54,16 @@ class Exchange: # Parameters to add directly to buy/sell calls (like agreeing to trading agreement) _params: Dict = {} + # Additional headers - added to the ccxt object + _headers: Dict = {} + # Dict to specify which options each exchange implements # This defines defaults, which can be selectively overridden by subclasses using _ft_has # or by specifying them in the configuration. _ft_has_default: Dict = { "stoploss_on_exchange": False, "order_time_in_force": ["gtc"], + "time_in_force_parameter": "timeInForce", "ohlcv_params": {}, "ohlcv_candle_limit": 500, "ohlcv_partial_candle": True, @@ -100,6 +104,7 @@ class Exchange: # Holds all open sell orders for dry_run self._dry_run_open_orders: Dict[str, Any] = {} + remove_credentials(config) if config['dry_run']: logger.info('Instance is running with dry_run enabled') @@ -169,7 +174,7 @@ class Exchange: asyncio.get_event_loop().run_until_complete(self._api_async.close()) def _init_ccxt(self, exchange_config: Dict[str, Any], ccxt_module: CcxtModuleType = ccxt, - ccxt_kwargs: dict = None) -> ccxt.Exchange: + ccxt_kwargs: Dict = {}) -> ccxt.Exchange: """ Initialize ccxt with given config and return valid ccxt instance. @@ -188,6 +193,10 @@ class Exchange: } if ccxt_kwargs: logger.info('Applying additional ccxt config: %s', ccxt_kwargs) + if self._headers: + # Inject static headers after the above output to not confuse users. + ccxt_kwargs = deep_merge_dicts({'headers': self._headers}, ccxt_kwargs) + if ccxt_kwargs: ex_config.update(ccxt_kwargs) try: @@ -514,7 +523,7 @@ class Exchange: precision = self.markets[pair]['precision']['price'] missing = price % precision if missing != 0: - price = price - missing + precision + price = round(price - missing + precision, 10) else: symbol_prec = self.markets[pair]['precision']['price'] big_price = price * pow(10, symbol_prec) @@ -716,7 +725,8 @@ class Exchange: params = self._params.copy() if time_in_force != 'gtc' and ordertype != 'market': - params.update({'timeInForce': time_in_force}) + param = self._ft_has.get('time_in_force_parameter', '') + params.update({param: time_in_force}) try: # Set the precision for amount and price(rate) as accepted by the exchange @@ -1048,7 +1058,7 @@ class Exchange: ticker_rate = ticker[conf_strategy['price_side']] if ticker['last'] and ticker_rate: if side == 'buy' and ticker_rate > ticker['last']: - balance = conf_strategy['ask_last_balance'] + balance = conf_strategy.get('ask_last_balance', 0.0) ticker_rate = ticker_rate + balance * (ticker['last'] - ticker_rate) elif side == 'sell' and ticker_rate < ticker['last']: balance = conf_strategy.get('bid_last_balance', 0.0) @@ -1185,7 +1195,7 @@ class Exchange: # Historic data def get_historic_ohlcv(self, pair: str, timeframe: str, - since_ms: int) -> List: + since_ms: int, is_new_pair: bool = False) -> List: """ Get candle history using asyncio and returns the list of candles. Handles all async work for this. @@ -1197,7 +1207,7 @@ class Exchange: """ return asyncio.get_event_loop().run_until_complete( self._async_get_historic_ohlcv(pair=pair, timeframe=timeframe, - since_ms=since_ms)) + since_ms=since_ms, is_new_pair=is_new_pair)) def get_historic_ohlcv_as_df(self, pair: str, timeframe: str, since_ms: int) -> DataFrame: @@ -1212,11 +1222,12 @@ class Exchange: return ohlcv_to_dataframe(ticks, timeframe, pair=pair, fill_missing=True, drop_incomplete=self._ohlcv_partial_candle) - async def _async_get_historic_ohlcv(self, pair: str, - timeframe: str, - since_ms: int) -> List: + async def _async_get_historic_ohlcv(self, pair: str, timeframe: str, + since_ms: int, is_new_pair: bool + ) -> List: """ Download historic ohlcv + :param is_new_pair: used by binance subclass to allow "fast" new pair downloading """ one_call = timeframe_to_msecs(timeframe) * self.ohlcv_candle_limit(timeframe) @@ -1229,21 +1240,22 @@ class Exchange: pair, timeframe, since) for since in range(since_ms, arrow.utcnow().int_timestamp * 1000, one_call)] - results = await asyncio.gather(*input_coroutines, return_exceptions=True) - - # Combine gathered results data: List = [] - for res in results: - if isinstance(res, Exception): - logger.warning("Async code raised an exception: %s", res.__class__.__name__) - continue - # Deconstruct tuple if it's not an exception - p, _, new_data = res - if p == pair: - data.extend(new_data) + # Chunk requests into batches of 100 to avoid overwelming ccxt Throttling + for input_coro in chunks(input_coroutines, 100): + + results = await asyncio.gather(*input_coro, return_exceptions=True) + for res in results: + if isinstance(res, Exception): + logger.warning("Async code raised an exception: %s", res.__class__.__name__) + continue + # Deconstruct tuple if it's not an exception + p, _, new_data = res + if p == pair: + data.extend(new_data) # Sort data again after extending the result - above calls return in "async order" data = sorted(data, key=lambda x: x[0]) - logger.info("Downloaded data for %s with length %s.", pair, len(data)) + logger.info(f"Downloaded data for {pair} with length {len(data)}.") return data def refresh_latest_ohlcv(self, pair_list: ListPairsWithTimeframes, *, From 30bc96cf3f802a882e4e5e35e0c0df8db876acfc Mon Sep 17 00:00:00 2001 From: sid Date: Sat, 9 Oct 2021 06:36:23 +0530 Subject: [PATCH 077/144] simplify expression --- freqtrade/optimize/hyperopt_loss_max_drawdown.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/freqtrade/optimize/hyperopt_loss_max_drawdown.py b/freqtrade/optimize/hyperopt_loss_max_drawdown.py index 6777fb2e8..ce955d928 100644 --- a/freqtrade/optimize/hyperopt_loss_max_drawdown.py +++ b/freqtrade/optimize/hyperopt_loss_max_drawdown.py @@ -38,6 +38,4 @@ class MaxDrawDownHyperOptLoss(IHyperOptLoss): except ValueError: # No losing trade, therefore no drawdown. return -total_profit - max_drawdown_rev = 1 / max_drawdown[0] - ret = max_drawdown_rev * total_profit - return -ret + return -total_profit / max_drawdown[0] From 7b1c888665b214aee599958bf0a5b9656d13531b Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 9 Oct 2021 08:39:32 +0200 Subject: [PATCH 078/144] Add FAQ entry for incomplete candles closes #5687 --- docs/faq.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 75c40a681..d9777ddf1 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -54,9 +54,11 @@ you can't say much from few trades. Yes. You can edit your config and use the `/reload_config` command to reload the configuration. The bot will stop, reload the configuration and strategy and will restart with the new configuration and strategy. -### I want to improve the bot with a new strategy +### I want to use incomplete candles -That's great. We have a nice backtesting and hyperoptimization setup. See the tutorial [here|Testing-new-strategies-with-Hyperopt](bot-usage.md#hyperopt-commands). +Freqtrade will not provide incomplete candles to strategies. Using incomplete candles will lead to repainting and consequently to strategies with "ghost" buys, which are impossible to both backtest, and verify after they happened. + +You can use "current" market data by using the [dataprovider](strategy-customization.md#orderbookpair-maximum)'s orderbook or ticker methods - which however cannot be used during backtesting. ### Is there a setting to only SELL the coins being held and not perform anymore BUYS? From 2c68342140979f7ade3e271d6841b9be81590b51 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 9 Oct 2021 10:37:33 +0200 Subject: [PATCH 079/144] Move pypi installation to documentation --- README.md | 8 +++++--- docs/installation.md | 7 +++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 79763983d..0a4d6424e 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Please find the complete documentation on our [website](https://www.freqtrade.io - [x] **Dry-run**: Run the bot without paying money. - [x] **Backtesting**: Run a simulation of your buy/sell strategy. - [x] **Strategy Optimization by machine learning**: Use machine learning to optimize your buy/sell strategy parameters with real exchange data. -- [x] **Edge position sizing** Calculate your win rate, risk reward ratio, the best stoploss and adjust your position size before taking a position for each specific market. [Learn more](https://www.freqtrade.io/en/latest/edge/). +- [x] **Edge position sizing** Calculate your win rate, risk reward ratio, the best stoploss and adjust your position size before taking a position for each specific market. [Learn more](https://www.freqtrade.io/en/stable/edge/). - [x] **Whitelist crypto-currencies**: Select which crypto-currency you want to trade or use dynamic whitelists. - [x] **Blacklist crypto-currencies**: Select which crypto-currency you want to avoid. - [x] **Manageable via Telegram**: Manage the bot with Telegram. @@ -66,10 +66,12 @@ Please find the complete documentation on our [website](https://www.freqtrade.io Freqtrade provides a Linux/macOS script to install all dependencies and help you to configure the bot. ```bash -pip install freqtrade +git clone -b develop https://github.com/freqtrade/freqtrade.git +cd freqtrade +./setup.sh --install ``` -For any other type of installation please refer to [Installation doc](https://www.freqtrade.io/en/latest/installation/). +For any other type of installation please refer to [Installation doc](https://www.freqtrade.io/en/stable/installation/). ## Basic Usage diff --git a/docs/installation.md b/docs/installation.md index 5e4a19d88..d468786d3 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -113,6 +113,13 @@ git checkout develop You may later switch between branches at any time with the `git checkout stable`/`git checkout develop` commands. +??? Note "Install from pypi" + An alternative way to install Freqtrade is from [pypi](https://pypi.org/project/freqtrade/). The downside is that this method requires ta-lib to be correctly installed beforehand, and is therefore currently not the recommended way to install Freqtrade. + + ``` bash + pip install freqtrade + ``` + ------ ## Script Installation From 1a3b41ed9718338c6458de3f87527591337a03cb Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 9 Oct 2021 15:35:39 +0200 Subject: [PATCH 080/144] Rephrase and simplify UI access section in docker quickstart --- docs/docker_quickstart.md | 40 +++++++++------------------------------ 1 file changed, 9 insertions(+), 31 deletions(-) diff --git a/docs/docker_quickstart.md b/docs/docker_quickstart.md index cf525b926..95df37811 100644 --- a/docs/docker_quickstart.md +++ b/docs/docker_quickstart.md @@ -70,39 +70,17 @@ docker-compose up -d !!! Warning "Default configuration" While the configuration generated will be mostly functional, you will still need to verify that all options correspond to what you want (like Pricing, pairlist, ...) before starting the bot. -#### Acessing the UI +#### Accessing the UI -Uncommend the 2 lines below and add your IP adress in the following format (like 192.168.2.67:8080:8080) to the ft_userdata/docker-compose.yml: -'''bash - ports: - - "yourIPadress:8080:8080" -''' -Your ft_userdata/user_data/config.json should look like: -'''bash -api_server": { - "enabled": true, - "listen_ip_address": "0.0.0.0", - "listen_port": 8080, - "verbosity": "error", - "enable_openapi": false, - "jwt_secret_key": "****", - "CORS_origins": [], - "username": "****", - "password": "****" - }, -''' -instead of "****" you will have your data in. -Then rebuild your docker file: -Linux: -'''bash -sudo docker-compose down && sudo docker-compose pull && sudo docker-compose build && sudo docker-compose up -d -''' -Windows: -'''bash -docker-compose down && docker-compose pull && docker-compose build && docker-compose up -d -''' +If you've selected to enable FreqUI in the `new-config` step, you will have freqUI available at port `localhost:8080`. -You can now access the UI by typing yourIPadress:8080 in your browser. +You can now access the UI by typing localhost:8080 in your browser. + +??? Note "UI Access on a remote servers" + If you're running on a VPS, you should consider using either a ssh tunnel, or setup a VPN (openVPN, wireguard) to connect to your bot. + This will ensure that freqUI is not directly exposed to the internet, which is not recommended for security reasons (freqUI does not support https out of the box). + Setup of these tools is not part of this tutorial, however many good tutorials can be found on the internet. + Please also read the [API configuration with docker](rest-api.md#configuration-with-docker) section to learn more about this configuration. #### Monitoring the bot From 57095d7167aa481bf184f19f39acfeded8149a7e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 03:01:22 +0000 Subject: [PATCH 081/144] Bump wrapt from 1.12.1 to 1.13.1 Bumps [wrapt](https://github.com/GrahamDumpleton/wrapt) from 1.12.1 to 1.13.1. - [Release notes](https://github.com/GrahamDumpleton/wrapt/releases) - [Changelog](https://github.com/GrahamDumpleton/wrapt/blob/develop/docs/changes.rst) - [Commits](https://github.com/GrahamDumpleton/wrapt/compare/1.12.1...1.13.1) --- updated-dependencies: - dependency-name: wrapt dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7fe06b9d2..4eb2bf66b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ arrow==1.1.1 cachetools==4.2.2 requests==2.26.0 urllib3==1.26.7 -wrapt==1.12.1 +wrapt==1.13.1 jsonschema==4.0.1 TA-Lib==0.4.21 technical==1.3.0 From 7323ffa25a408a64a94ce577caeece6530bdfa1b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 03:01:31 +0000 Subject: [PATCH 082/144] Bump blosc from 1.10.4 to 1.10.6 Bumps [blosc](https://github.com/blosc/python-blosc) from 1.10.4 to 1.10.6. - [Release notes](https://github.com/blosc/python-blosc/releases) - [Changelog](https://github.com/Blosc/python-blosc/blob/master/RELEASE_NOTES.rst) - [Commits](https://github.com/blosc/python-blosc/compare/v1.10.4...v1.10.6) --- updated-dependencies: - dependency-name: blosc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7fe06b9d2..3a49977c0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,7 +20,7 @@ tabulate==0.8.9 pycoingecko==2.2.0 jinja2==3.0.1 tables==3.6.1 -blosc==1.10.4 +blosc==1.10.6 # find first, C search in arrays py_find_1st==1.1.5 From 5fb0401dca6b9ae19c678b0a7577129969901ff9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 03:01:39 +0000 Subject: [PATCH 083/144] Bump cryptography from 3.4.8 to 35.0.0 Bumps [cryptography](https://github.com/pyca/cryptography) from 3.4.8 to 35.0.0. - [Release notes](https://github.com/pyca/cryptography/releases) - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/3.4.8...35.0.0) --- updated-dependencies: - dependency-name: cryptography dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7fe06b9d2..89728f782 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ pandas-ta==0.3.14b ccxt==1.57.38 # Pin cryptography for now due to rust build errors with piwheels -cryptography==3.4.8 +cryptography==35.0.0 aiohttp==3.7.4.post0 SQLAlchemy==1.4.25 python-telegram-bot==13.7 From 3fdc62d29c3f81f4ebdd826e0cd5ad21cde6d1a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 05:05:14 +0000 Subject: [PATCH 084/144] Bump flake8 from 3.9.2 to 4.0.0 Bumps [flake8](https://github.com/pycqa/flake8) from 3.9.2 to 4.0.0. - [Release notes](https://github.com/pycqa/flake8/releases) - [Commits](https://github.com/pycqa/flake8/compare/3.9.2...4.0.0) --- updated-dependencies: - dependency-name: flake8 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index a3ed37bea..3f2d6035d 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -4,7 +4,7 @@ -r requirements-hyperopt.txt coveralls==3.2.0 -flake8==3.9.2 +flake8==4.0.0 flake8-type-annotations==0.1.0 flake8-tidy-imports==4.4.1 mypy==0.910 From e467491dbecc7f49444c60471485daacf546dfa7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 05:05:23 +0000 Subject: [PATCH 085/144] Bump jsonschema from 4.0.1 to 4.1.0 Bumps [jsonschema](https://github.com/Julian/jsonschema) from 4.0.1 to 4.1.0. - [Release notes](https://github.com/Julian/jsonschema/releases) - [Changelog](https://github.com/Julian/jsonschema/blob/main/CHANGELOG.rst) - [Commits](https://github.com/Julian/jsonschema/compare/v4.0.1...v4.1.0) --- updated-dependencies: - dependency-name: jsonschema dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4eb2bf66b..641264d53 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ cachetools==4.2.2 requests==2.26.0 urllib3==1.26.7 wrapt==1.13.1 -jsonschema==4.0.1 +jsonschema==4.1.0 TA-Lib==0.4.21 technical==1.3.0 tabulate==0.8.9 From afc086f33c85162107a27dda013544d37ced3a51 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 05:05:30 +0000 Subject: [PATCH 086/144] Bump arrow from 1.1.1 to 1.2.0 Bumps [arrow](https://github.com/arrow-py/arrow) from 1.1.1 to 1.2.0. - [Release notes](https://github.com/arrow-py/arrow/releases) - [Changelog](https://github.com/arrow-py/arrow/blob/master/CHANGELOG.rst) - [Commits](https://github.com/arrow-py/arrow/commits) --- updated-dependencies: - dependency-name: arrow dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4eb2bf66b..90ef02ae5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ cryptography==3.4.8 aiohttp==3.7.4.post0 SQLAlchemy==1.4.25 python-telegram-bot==13.7 -arrow==1.1.1 +arrow==1.2.0 cachetools==4.2.2 requests==2.26.0 urllib3==1.26.7 From 32174f8f906304f61e7f21afa3669eab4458dc5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 05:14:08 +0000 Subject: [PATCH 087/144] Bump pyjwt from 2.1.0 to 2.2.0 Bumps [pyjwt](https://github.com/jpadilla/pyjwt) from 2.1.0 to 2.2.0. - [Release notes](https://github.com/jpadilla/pyjwt/releases) - [Changelog](https://github.com/jpadilla/pyjwt/blob/master/CHANGELOG.rst) - [Commits](https://github.com/jpadilla/pyjwt/compare/2.1.0...2.2.0) --- updated-dependencies: - dependency-name: pyjwt dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 4eb2bf66b..133aee144 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,7 +20,7 @@ tabulate==0.8.9 pycoingecko==2.2.0 jinja2==3.0.1 tables==3.6.1 -blosc==1.10.4 +blosc==1.10.6 # find first, C search in arrays py_find_1st==1.1.5 @@ -34,7 +34,7 @@ sdnotify==0.3.2 # API Server fastapi==0.68.1 uvicorn==0.15.0 -pyjwt==2.1.0 +pyjwt==2.2.0 aiofiles==0.7.0 psutil==5.8.0 From 4921a4caecd0cfbffa7df3894f4649876d4dd28a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 05:14:19 +0000 Subject: [PATCH 088/144] Bump jinja2 from 3.0.1 to 3.0.2 Bumps [jinja2](https://github.com/pallets/jinja) from 3.0.1 to 3.0.2. - [Release notes](https://github.com/pallets/jinja/releases) - [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/jinja/compare/3.0.1...3.0.2) --- updated-dependencies: - dependency-name: jinja2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1d9aaf4b5..e2de4f629 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,7 +18,7 @@ TA-Lib==0.4.21 technical==1.3.0 tabulate==0.8.9 pycoingecko==2.2.0 -jinja2==3.0.1 +jinja2==3.0.2 tables==3.6.1 blosc==1.10.6 From 90ea3d444060359320ec534044785699617e0cb7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 05:18:24 +0000 Subject: [PATCH 089/144] Bump mkdocs-material from 7.3.1 to 7.3.2 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 7.3.1 to 7.3.2. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/7.3.1...7.3.2) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index bbbb240ba..9a733d8f7 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,4 +1,4 @@ mkdocs==1.2.2 -mkdocs-material==7.3.1 +mkdocs-material==7.3.2 mdx_truly_sane_lists==1.2 pymdown-extensions==9.0 From 29371b2f28591a611e039bad4da718ae726dfb23 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 06:15:03 +0000 Subject: [PATCH 090/144] Bump joblib from 1.0.1 to 1.1.0 Bumps [joblib](https://github.com/joblib/joblib) from 1.0.1 to 1.1.0. - [Release notes](https://github.com/joblib/joblib/releases) - [Changelog](https://github.com/joblib/joblib/blob/master/CHANGES.rst) - [Commits](https://github.com/joblib/joblib/compare/1.0.1...1.1.0) --- updated-dependencies: - dependency-name: joblib dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-hyperopt.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index b4067d1db..96690bcbb 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -6,6 +6,6 @@ scipy==1.7.1 scikit-learn==0.24.2 scikit-optimize==0.8.1 filelock==3.3.0 -joblib==1.0.1 +joblib==1.1.0 psutil==5.8.0 progressbar2==3.53.3 From 802599bdc99cdd82e390d51cdb000c87d03e96a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 06:15:32 +0000 Subject: [PATCH 091/144] Bump ccxt from 1.57.38 to 1.57.94 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.57.38 to 1.57.94. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg) - [Commits](https://github.com/ccxt/ccxt/compare/1.57.38...1.57.94) --- updated-dependencies: - dependency-name: ccxt dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 788801987..ff9ea9423 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ numpy==1.21.2 pandas==1.3.3 pandas-ta==0.3.14b -ccxt==1.57.38 +ccxt==1.57.94 # Pin cryptography for now due to rust build errors with piwheels cryptography==35.0.0 aiohttp==3.7.4.post0 @@ -34,7 +34,7 @@ sdnotify==0.3.2 # API Server fastapi==0.68.1 uvicorn==0.15.0 -pyjwt==2.1.0 +pyjwt==2.2.0 aiofiles==0.7.0 psutil==5.8.0 From fa00b52c4742cbe7b2ec4c583ee5a5828ca00ff7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 07:41:16 +0000 Subject: [PATCH 092/144] Bump scikit-learn from 0.24.2 to 1.0 Bumps [scikit-learn](https://github.com/scikit-learn/scikit-learn) from 0.24.2 to 1.0. - [Release notes](https://github.com/scikit-learn/scikit-learn/releases) - [Commits](https://github.com/scikit-learn/scikit-learn/compare/0.24.2...1.0) --- updated-dependencies: - dependency-name: scikit-learn dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- requirements-hyperopt.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index 96690bcbb..edd078e9e 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -3,7 +3,7 @@ # Required for hyperopt scipy==1.7.1 -scikit-learn==0.24.2 +scikit-learn==1.0 scikit-optimize==0.8.1 filelock==3.3.0 joblib==1.1.0 From 396bc9b2e3d33993052a3b2038cad9ee19d3736d Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 11 Oct 2021 20:00:53 +0200 Subject: [PATCH 093/144] Version bump flake8-tidy-imports to 4.5.0 --- requirements-dev.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 3f2d6035d..74ebee479 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -5,8 +5,7 @@ coveralls==3.2.0 flake8==4.0.0 -flake8-type-annotations==0.1.0 -flake8-tidy-imports==4.4.1 +flake8-tidy-imports==4.5.0 mypy==0.910 pytest==6.2.5 pytest-asyncio==0.15.1 From ce9debe9fd89bab9171f35d3632f88f27c0d080a Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 12 Oct 2021 06:44:07 +0200 Subject: [PATCH 094/144] Add version argument to freqUI installer --- freqtrade/commands/arguments.py | 2 +- freqtrade/commands/cli_options.py | 6 ++++++ freqtrade/commands/deploy_commands.py | 16 ++++++++++++---- tests/commands/test_commands.py | 27 ++++++++++++++++++++++----- 4 files changed, 41 insertions(+), 10 deletions(-) diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 9643705a5..a02faa736 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -73,7 +73,7 @@ ARGS_PLOT_DATAFRAME = ["pairs", "indicators1", "indicators2", "plot_limit", ARGS_PLOT_PROFIT = ["pairs", "timerange", "export", "exportfilename", "db_url", "trade_source", "timeframe", "plot_auto_open"] -ARGS_INSTALL_UI = ["erase_ui_only"] +ARGS_INSTALL_UI = ["erase_ui_only", 'ui_version'] ARGS_SHOW_TRADES = ["db_url", "trade_ids", "print_json"] diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index d350a9426..30a9b0137 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -414,6 +414,12 @@ AVAILABLE_CLI_OPTIONS = { action='store_true', default=False, ), + "ui_version": Arg( + '--ui-version', + help=('Specify a specific version of FreqUI to install. ' + 'Not specifying this installs the latest version.'), + type=str, + ), # Templating options "template": Arg( '--template', diff --git a/freqtrade/commands/deploy_commands.py b/freqtrade/commands/deploy_commands.py index 4f9e5bbad..92c9adf66 100644 --- a/freqtrade/commands/deploy_commands.py +++ b/freqtrade/commands/deploy_commands.py @@ -128,7 +128,7 @@ def download_and_install_ui(dest_folder: Path, dl_url: str, version: str): f.write(version) -def get_ui_download_url() -> Tuple[str, str]: +def get_ui_download_url(version: Optional[str] = None) -> Tuple[str, str]: base_url = 'https://api.github.com/repos/freqtrade/frequi/' # Get base UI Repo path @@ -136,8 +136,16 @@ def get_ui_download_url() -> Tuple[str, str]: resp.raise_for_status() r = resp.json() - latest_version = r[0]['name'] - assets = r[0].get('assets', []) + if version: + tmp = [x for x in r if x['name'] == version] + if tmp: + latest_version = tmp[0]['name'] + assets = tmp[0].get('assets', []) + else: + raise ValueError("UI-Version not found.") + else: + latest_version = r[0]['name'] + assets = r[0].get('assets', []) dl_url = '' if assets and len(assets) > 0: dl_url = assets[0]['browser_download_url'] @@ -156,7 +164,7 @@ def start_install_ui(args: Dict[str, Any]) -> None: dest_folder = Path(__file__).parents[1] / 'rpc/api_server/ui/installed/' # First make sure the assets are removed. - dl_url, latest_version = get_ui_download_url() + dl_url, latest_version = get_ui_download_url(args.get('ui_version')) curr_version = read_ui_version(dest_folder) if curr_version == latest_version and not args.get('erase_ui_only'): diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index 8889617ba..6a0e741d9 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -605,16 +605,33 @@ def test_get_ui_download_url(mocker): def test_get_ui_download_url_direct(mocker): response = MagicMock() response.json = MagicMock( - side_effect=[[{ - 'assets_url': 'http://whatever.json', - 'name': '0.0.1', - 'assets': [{'browser_download_url': 'http://download11.zip'}]}]]) + return_value=[ + { + 'assets_url': 'http://whatever.json', + 'name': '0.0.2', + 'assets': [{'browser_download_url': 'http://download22.zip'}] + }, + { + 'assets_url': 'http://whatever.json', + 'name': '0.0.1', + 'assets': [{'browser_download_url': 'http://download1.zip'}] + }, + ]) get_mock = mocker.patch("freqtrade.commands.deploy_commands.requests.get", return_value=response) x, last_version = get_ui_download_url() assert get_mock.call_count == 1 + assert last_version == '0.0.2' + assert x == 'http://download22.zip' + get_mock.reset_mock() + response.json.reset_mock() + + x, last_version = get_ui_download_url('0.0.1') assert last_version == '0.0.1' - assert x == 'http://download11.zip' + assert x == 'http://download1.zip' + + with pytest.raises(ValueError, match="UI-Version not found."): + x, last_version = get_ui_download_url('0.0.3') def test_download_data_keyboardInterrupt(mocker, caplog, markets): From 8798ae567744ceefed92577acbd66fb974df6b15 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 12 Oct 2021 19:06:23 +0200 Subject: [PATCH 095/144] Version bump also scikit-optimize --- requirements-hyperopt.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index edd078e9e..e97e78638 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -4,7 +4,7 @@ # Required for hyperopt scipy==1.7.1 scikit-learn==1.0 -scikit-optimize==0.8.1 +scikit-optimize==0.9.0 filelock==3.3.0 joblib==1.1.0 psutil==5.8.0 From aed919a05f352de482caddc1dc199d0ba2bd8e85 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 13 Oct 2021 19:54:35 +0200 Subject: [PATCH 096/144] Simplify "no-space-configured" error handling by moving it to hyperopt_auto --- freqtrade/commands/arguments.py | 4 +- freqtrade/commands/cli_options.py | 4 +- freqtrade/configuration/configuration.py | 6 +-- freqtrade/optimize/hyperopt.py | 49 ++++-------------------- freqtrade/optimize/hyperopt_auto.py | 27 +++++++++---- 5 files changed, 33 insertions(+), 57 deletions(-) diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index e58135895..167b79afb 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -31,8 +31,8 @@ ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + ["hyperopt", "hyperopt_path", "epochs", "spaces", "print_all", "print_colorized", "print_json", "hyperopt_jobs", "hyperopt_random_state", "hyperopt_min_trades", - "hyperopt_loss", "disableparamexport", - "hyperopt_ignore_unparam_space"] + "hyperopt_loss", "disableparamexport", + "hyperopt_ignore_missing_space"] ARGS_EDGE = ARGS_COMMON_OPTIMIZE + ["stoploss_range"] diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index ef1ec8515..1f49b779b 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -552,8 +552,8 @@ AVAILABLE_CLI_OPTIONS = { help='Do not print epoch details header.', action='store_true', ), - "hyperopt_ignore_unparam_space": Arg( - "-u", "--ignore-unparameterized-spaces", + "hyperopt_ignore_missing_space": Arg( + "--ignore-missing-spaces", "--ignore-unparameterized-spaces", help="Suppress errors for any requested Hyperopt spaces that do not contain any parameters", action="store_true", ), diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 723ad3795..12dcff46a 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -368,9 +368,9 @@ class Configuration: self._args_to_config(config, argname='hyperopt_show_no_header', logstring='Parameter --no-header detected: {}') - - self._args_to_config(config, argname="hyperopt_ignore_unparam_space", - logstring="Paramter --ignore-unparameterized-spaces detected: {}") + + self._args_to_config(config, argname="hyperopt_ignore_missing_space", + logstring="Paramter --ignore-missing-space detected: {}") def _process_plot_options(self, config: Dict[str, Any]) -> None: diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index f6c677a6e..6397bbacb 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -237,63 +237,28 @@ class Hyperopt: logger.debug("Hyperopt has 'protection' space") # Enable Protections if protection space is selected. self.config['enable_protections'] = True - try: - self.protection_space = self.custom_hyperopt.protection_space() - except OperationalException as e: - if self.config["hyperopt_ignore_unparam_space"]: - logger.warning(e) - else: - raise + self.protection_space = self.custom_hyperopt.protection_space() if HyperoptTools.has_space(self.config, 'buy'): logger.debug("Hyperopt has 'buy' space") - try: - self.buy_space = self.custom_hyperopt.buy_indicator_space() - except OperationalException as e: - if self.config["hyperopt_ignore_unparam_space"]: - logger.warning(e) - else: - raise + self.buy_space = self.custom_hyperopt.buy_indicator_space() if HyperoptTools.has_space(self.config, 'sell'): logger.debug("Hyperopt has 'sell' space") - try: - self.sell_space = self.custom_hyperopt.sell_indicator_space() - except OperationalException as e: - if self.config["hyperopt_ignore_unparam_space"]: - logger.warning(e) - else: - raise + self.sell_space = self.custom_hyperopt.sell_indicator_space() if HyperoptTools.has_space(self.config, 'roi'): logger.debug("Hyperopt has 'roi' space") - try: - self.roi_space = self.custom_hyperopt.roi_space() - except OperationalException as e: - if self.config["hyperopt_ignore_unparam_space"]: - logger.warning(e) - else: - raise + self.roi_space = self.custom_hyperopt.roi_space() if HyperoptTools.has_space(self.config, 'stoploss'): logger.debug("Hyperopt has 'stoploss' space") - try: - self.stoploss_space = self.custom_hyperopt.stoploss_space() - except OperationalException as e: - if self.config["hyperopt_ignore_unparam_space"]: - logger.warning(e) - else: - raise + self.stoploss_space = self.custom_hyperopt.stoploss_space() if HyperoptTools.has_space(self.config, 'trailing'): logger.debug("Hyperopt has 'trailing' space") - try: - self.trailing_space = self.custom_hyperopt.trailing_space() - except OperationalException as e: - if self.config["hyperopt_ignore_unparam_space"]: - logger.warning(e) - else: - raise + self.trailing_space = self.custom_hyperopt.trailing_space() + self.dimensions = (self.buy_space + self.sell_space + self.protection_space + self.roi_space + self.stoploss_space + self.trailing_space) diff --git a/freqtrade/optimize/hyperopt_auto.py b/freqtrade/optimize/hyperopt_auto.py index c1c769c72..63b4b14e1 100644 --- a/freqtrade/optimize/hyperopt_auto.py +++ b/freqtrade/optimize/hyperopt_auto.py @@ -3,6 +3,7 @@ HyperOptAuto class. This module implements a convenience auto-hyperopt class, which can be used together with strategies that implement IHyperStrategy interface. """ +import logging from contextlib import suppress from typing import Callable, Dict, List @@ -15,12 +16,19 @@ with suppress(ImportError): from freqtrade.optimize.hyperopt_interface import EstimatorType, IHyperOpt -def _format_exception_message(space: str) -> str: - raise OperationalException( - f"The '{space}' space is included into the hyperoptimization " - f"but no parameter for this space was not found in your Strategy. " - f"Please make sure to have parameters for this space enabled for optimization " - f"or remove the '{space}' space from hyperoptimization.") +logger = logging.getLogger(__name__) + + +def _format_exception_message(space: str, ignore_missing_space: bool) -> None: + msg = (f"The '{space}' space is included into the hyperoptimization " + f"but no parameter for this space was not found in your Strategy. " + ) + if ignore_missing_space: + logger.warning(msg + "This space will be ignored.") + else: + raise OperationalException( + msg + f"Please make sure to have parameters for this space enabled for optimization " + f"or remove the '{space}' space from hyperoptimization.") class HyperOptAuto(IHyperOpt): @@ -48,13 +56,16 @@ class HyperOptAuto(IHyperOpt): if attr.optimize: yield attr.get_space(attr_name) - def _get_indicator_space(self, category): + def _get_indicator_space(self, category) -> List: # TODO: is this necessary, or can we call "generate_space" directly? indicator_space = list(self._generate_indicator_space(category)) if len(indicator_space) > 0: return indicator_space else: - _format_exception_message(category) + _format_exception_message( + category, + self.config.get("hyperopt_ignore_missing_space", False)) + return [] def buy_indicator_space(self) -> List['Dimension']: return self._get_indicator_space('buy') From 3279ea568c0b2da3d5a05a7aa3e011964838d849 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 13 Oct 2021 19:56:34 +0200 Subject: [PATCH 097/144] Add new parameter to hyperopt docs --- docs/hyperopt.md | 4 ++++ freqtrade/commands/cli_options.py | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 09d43939a..a693513d0 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -51,6 +51,7 @@ usage: freqtrade hyperopt [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] [--print-all] [--no-color] [--print-json] [-j JOBS] [--random-state INT] [--min-trades INT] [--hyperopt-loss NAME] [--disable-param-export] + [--ignore-missing-spaces] optional arguments: -h, --help show this help message and exit @@ -117,6 +118,9 @@ optional arguments: SortinoHyperOptLoss, SortinoHyperOptLossDaily --disable-param-export Disable automatic hyperopt parameter export. + --ignore-missing-spaces, --ignore-unparameterized-spaces + Suppress errors for any requested Hyperopt spaces that + do not contain any parameters. Common arguments: -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index 1f49b779b..f8338bf6a 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -554,7 +554,8 @@ AVAILABLE_CLI_OPTIONS = { ), "hyperopt_ignore_missing_space": Arg( "--ignore-missing-spaces", "--ignore-unparameterized-spaces", - help="Suppress errors for any requested Hyperopt spaces that do not contain any parameters", + help=("Suppress errors for any requested Hyperopt spaces " + "that do not contain any parameters."), action="store_true", ), } From fe8374f2a489418a8628ebeb1387df617d89b211 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 14 Oct 2021 07:06:51 +0200 Subject: [PATCH 098/144] Test for non-failing missing hyperopt space --- tests/optimize/test_hyperopt.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index e4ce29d44..b123fec21 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -702,7 +702,7 @@ def test_simplified_interface_roi_stoploss(mocker, hyperopt_conf, capsys) -> Non assert hasattr(hyperopt, "position_stacking") -def test_simplified_interface_all_failed(mocker, hyperopt_conf) -> None: +def test_simplified_interface_all_failed(mocker, hyperopt_conf, caplog) -> None: mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock()) mocker.patch('freqtrade.optimize.hyperopt.file_dump_json') mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data', @@ -724,7 +724,13 @@ def test_simplified_interface_all_failed(mocker, hyperopt_conf) -> None: hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={}) with pytest.raises(OperationalException, match=r"The 'protection' space is included into *"): - hyperopt.start() + hyperopt.init_spaces() + + hyperopt.config['hyperopt_ignore_missing_space'] = True + caplog.clear() + hyperopt.init_spaces() + assert log_has_re(r"The 'protection' space is included into *", caplog) + assert hyperopt.protection_space == [] def test_simplified_interface_buy(mocker, hyperopt_conf, capsys) -> None: From c02a538187340fffe3515396e052c18d4116e467 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 14 Oct 2021 19:33:32 +0200 Subject: [PATCH 099/144] Add documentation and log to PerformanceFilter --- docs/includes/pairlists.md | 9 +++++++-- freqtrade/plugins/pairlist/PerformanceFilter.py | 6 ++++++ tests/plugins/test_pairlist.py | 7 ++++--- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index b612a4ddf..3d10747d3 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -194,17 +194,22 @@ Trade count is used as a tie breaker. You can use the `minutes` parameter to only consider performance of the past X minutes (rolling window). Not defining this parameter (or setting it to 0) will use all-time performance. +The optional `min_profit` parameter defines the minimum profit a pair must have to be considered. +Pairs below this level will be filtered out. +Using this parameter without `minutes` is highly discouraged, as it can lead to an empty pairlist without without a way to recover. + ```json "pairlists": [ // ... { "method": "PerformanceFilter", - "minutes": 1440 // rolling 24h + "minutes": 1440, // rolling 24h + "min_profit": 0.01 } ], ``` -!!! Note +!!! Warning "Backtesting" `PerformanceFilter` does not support backtesting mode. #### PrecisionFilter diff --git a/freqtrade/plugins/pairlist/PerformanceFilter.py b/freqtrade/plugins/pairlist/PerformanceFilter.py index f235816b8..671b6362b 100644 --- a/freqtrade/plugins/pairlist/PerformanceFilter.py +++ b/freqtrade/plugins/pairlist/PerformanceFilter.py @@ -70,7 +70,13 @@ class PerformanceFilter(IPairList): .fillna(0).sort_values(by=['count', 'pair'], ascending=True)\ .sort_values(by=['profit'], ascending=False) if self._min_profit is not None: + removed = sorted_df[sorted_df['profit'] < self._min_profit] + for _, row in removed.iterrows(): + self.log_once( + f"Removing pair {row['pair']} since {row['profit']} is " + f"below {self._min_profit}", logger.info) sorted_df = sorted_df[sorted_df['profit'] >= self._min_profit] + pairlist = sorted_df['pair'].tolist() return pairlist diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index cf918e2a0..c6246dccb 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -665,11 +665,11 @@ def test_PerformanceFilter_error(mocker, whitelist_conf, caplog) -> None: @pytest.mark.usefixtures("init_persistence") -def test_PerformanceFilter_lookback(mocker, whitelist_conf, fee) -> None: +def test_PerformanceFilter_lookback(mocker, whitelist_conf, fee, caplog) -> None: whitelist_conf['exchange']['pair_whitelist'].append('XRP/BTC') whitelist_conf['pairlists'] = [ {"method": "StaticPairList"}, - {"method": "PerformanceFilter", "minutes": 60} + {"method": "PerformanceFilter", "minutes": 60, "min_profit": 0.01} ] mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) exchange = get_patched_exchange(mocker, whitelist_conf) @@ -681,7 +681,8 @@ def test_PerformanceFilter_lookback(mocker, whitelist_conf, fee) -> None: with time_machine.travel("2021-09-01 05:00:00 +00:00") as t: create_mock_trades(fee) pm.refresh_pairlist() - assert pm.whitelist == ['XRP/BTC', 'ETH/BTC', 'TKN/BTC'] + assert pm.whitelist == ['XRP/BTC'] + assert log_has_re(r'Removing pair .* since .* is below .*', caplog) # Move to "outside" of lookback window, so original sorting is restored. t.move_to("2021-09-01 07:00:00 +00:00") From fe9f597eab044f06ae1f68036eded72046db7d4f Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 15 Oct 2021 10:11:25 +0200 Subject: [PATCH 100/144] Don't build ta-lib in parallel, this causes failures --- build_helpers/install_ta-lib.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/build_helpers/install_ta-lib.sh b/build_helpers/install_ta-lib.sh index d12b16364..00c4417ae 100755 --- a/build_helpers/install_ta-lib.sh +++ b/build_helpers/install_ta-lib.sh @@ -11,8 +11,13 @@ if [ ! -f "${INSTALL_LOC}/lib/libta_lib.a" ]; then && curl 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD' -o config.guess \ && curl 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD' -o config.sub \ && ./configure --prefix=${INSTALL_LOC}/ \ - && make -j$(nproc) \ - && which sudo && sudo make install || make install + && make + if [ $? -ne 0 ]; then + echo "Failed building ta-lib." + cd .. && rm -rf ./ta-lib/ + exit 1 + fi + which sudo && sudo make install || make install if [ -x "$(command -v apt-get)" ]; then echo "Updating library path using ldconfig" sudo ldconfig From de5657a91b88eff9e65ff6e24a432d792c539002 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 12 Oct 2021 07:00:07 +0200 Subject: [PATCH 101/144] Fix test failing when UI is installed --- tests/rpc/test_rpc_apiserver.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index ac76bbd11..7aa057d09 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -95,7 +95,7 @@ def test_api_not_found(botclient): assert rc.json() == {"detail": "Not Found"} -def test_api_ui_fallback(botclient): +def test_api_ui_fallback(botclient, mocker): ftbot, client = botclient rc = client_get(client, "/favicon.ico") @@ -109,9 +109,16 @@ def test_api_ui_fallback(botclient): rc = client_get(client, "/something") assert rc.status_code == 200 - # Test directory traversal + # Test directory traversal without mock rc = client_get(client, '%2F%2F%2Fetc/passwd') assert rc.status_code == 200 + # Allow both fallback or real UI + assert '`freqtrade install-ui`' in rc.text or '' in rc.text + + mocker.patch.object(Path, 'is_file', MagicMock(side_effect=[True, False])) + rc = client_get(client, '%2F%2F%2Fetc/passwd') + assert rc.status_code == 200 + assert '`freqtrade install-ui`' in rc.text From 7f1080368b224d9ec7b8485cdf21a6b055494318 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 12 Oct 2021 06:53:15 +0200 Subject: [PATCH 102/144] Commit mock-trades to avoid errors in tests --- tests/conftest.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/conftest.py b/tests/conftest.py index 49534c88d..470eaa6d8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -226,6 +226,7 @@ def create_mock_trades(fee, use_db: bool = True): add_trade(trade) if use_db: + Trade.commit() Trade.query.session.flush() @@ -259,6 +260,7 @@ def create_mock_trades_usdt(fee, use_db: bool = True): add_trade(trade) if use_db: + Trade.commit() Trade.query.session.flush() From dcefb3eb9c91479ea17f343c339ffc3554c3166d Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 12 Oct 2021 07:11:34 +0200 Subject: [PATCH 103/144] Fix delete_Trade api test --- tests/rpc/test_rpc_apiserver.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 7aa057d09..9a03158ae 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -620,10 +620,11 @@ def test_api_delete_trade(botclient, mocker, fee, markets): assert_response(rc, 502) create_mock_trades(fee) - Trade.query.session.flush() + ftbot.strategy.order_types['stoploss_on_exchange'] = True trades = Trade.query.all() trades[1].stoploss_order_id = '1234' + Trade.commit() assert len(trades) > 2 rc = client_delete(client, f"{BASE_URI}/trades/1") From 5ba1d66be7f86ad9bf467e3238fd45b9e709b2e0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 16 Oct 2021 17:57:51 +0200 Subject: [PATCH 104/144] Make sure transactions are reset closes #5719 --- freqtrade/rpc/api_server/deps.py | 2 ++ freqtrade/rpc/telegram.py | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/freqtrade/rpc/api_server/deps.py b/freqtrade/rpc/api_server/deps.py index d2459010f..77870722d 100644 --- a/freqtrade/rpc/api_server/deps.py +++ b/freqtrade/rpc/api_server/deps.py @@ -1,5 +1,6 @@ from typing import Any, Dict, Optional +from freqtrade.persistence import Trade from freqtrade.rpc.rpc import RPC, RPCException from .webserver import ApiServer @@ -14,6 +15,7 @@ def get_rpc_optional() -> Optional[RPC]: def get_rpc() -> Optional[RPC]: _rpc = get_rpc_optional() if _rpc: + Trade.query.session.rollback() return _rpc else: raise RPCException('Bot is not in the correct state') diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 059ba9c41..846747f40 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -25,6 +25,7 @@ from freqtrade.constants import DUST_PER_COIN from freqtrade.enums import RPCMessageType from freqtrade.exceptions import OperationalException from freqtrade.misc import chunks, plural, round_coin_value +from freqtrade.persistence import Trade from freqtrade.rpc import RPC, RPCException, RPCHandler @@ -59,7 +60,8 @@ def authorized_only(command_handler: Callable[..., None]) -> Callable[..., Any]: update.message.chat_id ) return wrapper - + # Rollback session to avoid getting data stored in a transaction. + Trade.query.session.rollback() logger.debug( 'Executing handler: %s for chat_id: %s', command_handler.__name__, From 89ca8abea9cab3efee896e6afd5e9e2c38a67f40 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 06:16:06 +0000 Subject: [PATCH 105/144] Bump fastapi from 0.68.1 to 0.70.0 Bumps [fastapi](https://github.com/tiangolo/fastapi) from 0.68.1 to 0.70.0. - [Release notes](https://github.com/tiangolo/fastapi/releases) - [Commits](https://github.com/tiangolo/fastapi/compare/0.68.1...0.70.0) --- updated-dependencies: - dependency-name: fastapi dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index af6ef974e..f3eb65c59 100644 --- a/requirements.txt +++ b/requirements.txt @@ -32,7 +32,7 @@ python-rapidjson==1.4 sdnotify==0.3.2 # API Server -fastapi==0.68.1 +fastapi==0.70.0 uvicorn==0.15.0 pyjwt==2.2.0 aiofiles==0.7.0 From 5a9983086a75275f2fbeb7f0569e7a3a09277b71 Mon Sep 17 00:00:00 2001 From: daniila Date: Sun, 17 Oct 2021 00:24:00 +0300 Subject: [PATCH 106/144] How to run multiple instances with docker Basic guide on how to run multiple instances using docker. --- docs/advanced-setup.md | 65 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/docs/advanced-setup.md b/docs/advanced-setup.md index f03bc10c0..e7e5b6cec 100644 --- a/docs/advanced-setup.md +++ b/docs/advanced-setup.md @@ -52,6 +52,71 @@ freqtrade trade -c MyConfigUSDT.json -s MyCustomStrategy --db-url sqlite:///user For more information regarding usage of the sqlite databases, for example to manually enter or remove trades, please refer to the [SQL Cheatsheet](sql_cheatsheet.md). +### Multiple instances using docker + +To run multiple instances of freqtrade using docker you will need to edit the docker-compose.yml file and add all the instances you want as separate services. Remember, you can separate your configuration into multiple files, so it's a good idea to think about making them modular, then if you need to edit something common to all bots, you can do that in a single config file. +``` +--- +version: '3' +services: + freqtrade1: + image: freqtradeorg/freqtrade:stable + # image: freqtradeorg/freqtrade:develop + # Use plotting image + # image: freqtradeorg/freqtrade:develop_plot + # Build step - only needed when additional dependencies are needed + # build: + # context: . + # dockerfile: "./docker/Dockerfile.custom" + restart: always + container_name: freqtrade1 + volumes: + - "./user_data:/freqtrade/user_data" + # Expose api on port 8080 (localhost only) + # Please read the https://www.freqtrade.io/en/latest/rest-api/ documentation + # before enabling this. + ports: + - "127.0.0.1:8080:8080" + # Default command used when running `docker compose up` + command: > + trade + --logfile /freqtrade/user_data/logs/freqtrade1.log + --db-url sqlite:////freqtrade/user_data/tradesv3_freqtrade1.sqlite + --config /freqtrade/user_data/config.json + --config /freqtrade/user_data/config.freqtrade1.json + --strategy SampleStrategy + + freqtrade2: + image: freqtradeorg/freqtrade:stable + # image: freqtradeorg/freqtrade:develop + # Use plotting image + # image: freqtradeorg/freqtrade:develop_plot + # Build step - only needed when additional dependencies are needed + # build: + # context: . + # dockerfile: "./docker/Dockerfile.custom" + restart: always + container_name: freqtrade2 + volumes: + - "./user_data:/freqtrade/user_data" + # Expose api on port 8080 (localhost only) + # Please read the https://www.freqtrade.io/en/latest/rest-api/ documentation + # before enabling this. + ports: + - "127.0.0.1:8081:8081" + # Default command used when running `docker compose up` + command: > + trade + --logfile /freqtrade/user_data/logs/freqtrade2.log + --db-url sqlite:////freqtrade/user_data/tradesv3_freqtrade2.sqlite + --config /freqtrade/user_data/config.json + --config /freqtrade/user_data/config.freqtrade2.json + --strategy SampleStrategy + +``` +You can use whatever naming convention you want, freqtrade1 and 2 are arbitrary. + + ## Configure the bot running as a systemd service Copy the `freqtrade.service` file to your systemd user directory (usually `~/.config/systemd/user`) and update `WorkingDirectory` and `ExecStart` to match your setup. From f61dc6d95ada089055e0e12c7927c58350b409ef Mon Sep 17 00:00:00 2001 From: Rik Helsen Date: Sun, 17 Oct 2021 00:14:09 +0200 Subject: [PATCH 107/144] =?UTF-8?q?=F0=9F=93=9D=20`mkdocs.yml`=20-=20Fixed?= =?UTF-8?q?=20darktheme=20toggle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mkdocs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 05156168f..0daf462c2 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -54,8 +54,8 @@ theme: primary: 'blue grey' accent: 'tear' toggle: - icon: material/toggle-switch-off-outline - name: Switch to dark mode + icon: material/toggle-switch + name: Switch to light mode extra_css: - 'stylesheets/ft.extra.css' extra_javascript: From fb2c8f7621b4d3a9d9644e9f499e21442cd1c258 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 17 Oct 2021 08:36:11 +0200 Subject: [PATCH 108/144] Rollback after each request This closes the transaction and avoids "sticking" transactions. --- freqtrade/rpc/api_server/deps.py | 7 ++++--- tests/conftest.py | 2 -- tests/rpc/test_rpc_apiserver.py | 6 +----- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/freqtrade/rpc/api_server/deps.py b/freqtrade/rpc/api_server/deps.py index 77870722d..16f9a78c0 100644 --- a/freqtrade/rpc/api_server/deps.py +++ b/freqtrade/rpc/api_server/deps.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Optional +from typing import Any, Dict, Iterator, Optional from freqtrade.persistence import Trade from freqtrade.rpc.rpc import RPC, RPCException @@ -12,11 +12,12 @@ def get_rpc_optional() -> Optional[RPC]: return None -def get_rpc() -> Optional[RPC]: +def get_rpc() -> Optional[Iterator[RPC]]: _rpc = get_rpc_optional() if _rpc: Trade.query.session.rollback() - return _rpc + yield _rpc + Trade.query.session.rollback() else: raise RPCException('Bot is not in the correct state') diff --git a/tests/conftest.py b/tests/conftest.py index 470eaa6d8..b35a220df 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -227,7 +227,6 @@ def create_mock_trades(fee, use_db: bool = True): if use_db: Trade.commit() - Trade.query.session.flush() def create_mock_trades_usdt(fee, use_db: bool = True): @@ -261,7 +260,6 @@ def create_mock_trades_usdt(fee, use_db: bool = True): if use_db: Trade.commit() - Trade.query.session.flush() @pytest.fixture(autouse=True) diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 9a03158ae..02ed26459 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -570,7 +570,6 @@ def test_api_trades(botclient, mocker, fee, markets): assert rc.json()['total_trades'] == 0 create_mock_trades(fee) - Trade.query.session.flush() rc = client_get(client, f"{BASE_URI}/trades") assert_response(rc) @@ -597,7 +596,6 @@ def test_api_trade_single(botclient, mocker, fee, ticker, markets): assert rc.json()['detail'] == 'Trade not found.' create_mock_trades(fee) - Trade.query.session.flush() rc = client_get(client, f"{BASE_URI}/trade/3") assert_response(rc) @@ -694,7 +692,6 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): assert rc.json() == {"error": "Error querying /api/v1/edge: Edge is not enabled."} -@pytest.mark.usefixtures("init_persistence") def test_api_profit(botclient, mocker, ticker, fee, markets): ftbot, client = botclient patch_get_signal(ftbot) @@ -745,7 +742,6 @@ def test_api_profit(botclient, mocker, ticker, fee, markets): } -@pytest.mark.usefixtures("init_persistence") def test_api_stats(botclient, mocker, ticker, fee, markets,): ftbot, client = botclient patch_get_signal(ftbot) @@ -811,7 +807,7 @@ def test_api_performance(botclient, fee): trade.close_profit_abs = trade.calc_profit() Trade.query.session.add(trade) - Trade.query.session.flush() + Trade.commit() rc = client_get(client, f"{BASE_URI}/performance") assert_response(rc) From abd5c4f27855c3486badc00b5efb9330e3aeb52b Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 17 Oct 2021 10:39:53 +0200 Subject: [PATCH 109/144] Convert additional test to USDT --- tests/test_freqtradebot.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index f57e35ca1..838a158e0 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -3378,9 +3378,9 @@ def test_disable_ignore_roi_if_buy_signal(default_conf_usdt, limit_buy_order_usd mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=MagicMock(return_value={ - 'bid': 0.00000172, - 'ask': 0.00000173, - 'last': 0.00000172 + 'bid': 2.0, + 'ask': 2.0, + 'last': 2.0 }), create_order=MagicMock(side_effect=[ limit_buy_order_usdt_open, @@ -3408,7 +3408,7 @@ def test_disable_ignore_roi_if_buy_signal(default_conf_usdt, limit_buy_order_usd # Test if buy-signal is absent patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) is True - assert trade.sell_reason == SellType.SELL_SIGNAL.value + assert trade.sell_reason == SellType.ROI.value def test_get_real_amount_quote(default_conf_usdt, trades_for_order, buy_order_fee, fee, caplog, From e23eb99abf4bd19465f89192329100b4b7e2381d Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 17 Oct 2021 11:23:58 +0200 Subject: [PATCH 110/144] Disable ability to use lookahead-biased vwap closes #5782 --- freqtrade/vendor/qtpylib/indicators.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/freqtrade/vendor/qtpylib/indicators.py b/freqtrade/vendor/qtpylib/indicators.py index 4c0fb5b5c..4f14ae13c 100644 --- a/freqtrade/vendor/qtpylib/indicators.py +++ b/freqtrade/vendor/qtpylib/indicators.py @@ -339,11 +339,13 @@ def vwap(bars): (input can be pandas series or numpy array) bars are usually mid [ (h+l)/2 ] or typical [ (h+l+c)/3 ] """ - typical = ((bars['high'] + bars['low'] + bars['close']) / 3).values - volume = bars['volume'].values + raise ValueError("using `qtpylib.vwap` facilitates lookahead bias. Please use " + "`qtpylib.rolling_vwap` instead, which calculates vwap in a rolling manner.") + # typical = ((bars['high'] + bars['low'] + bars['close']) / 3).values + # volume = bars['volume'].values - return pd.Series(index=bars.index, - data=np.cumsum(volume * typical) / np.cumsum(volume)) + # return pd.Series(index=bars.index, + # data=np.cumsum(volume * typical) / np.cumsum(volume)) # --------------------------------------------- From d4d57f00027d056ab94ce1eb7c3410f0370d8dcd Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 17 Oct 2021 16:09:56 +0200 Subject: [PATCH 111/144] Document expansion of `--pairs`, add download-inactive --- docs/data-download.md | 141 +++++++++++++---------- freqtrade/commands/arguments.py | 6 +- freqtrade/commands/cli_options.py | 5 + freqtrade/commands/data_commands.py | 9 +- freqtrade/configuration/configuration.py | 3 + tests/commands/test_commands.py | 40 +++++++ 6 files changed, 138 insertions(+), 66 deletions(-) diff --git a/docs/data-download.md b/docs/data-download.md index 5f605c404..6c7d5312d 100644 --- a/docs/data-download.md +++ b/docs/data-download.md @@ -22,6 +22,7 @@ usage: freqtrade download-data [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] [--userdir PATH] [-p PAIRS [PAIRS ...]] [--pairs-file FILE] [--days INT] [--new-pairs-days INT] + [--include-inactive-pairs] [--timerange TIMERANGE] [--dl-trades] [--exchange EXCHANGE] [-t {1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,2w,1M,1y} [{1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,2w,1M,1y} ...]] @@ -38,6 +39,8 @@ optional arguments: --days INT Download data for given number of days. --new-pairs-days INT Download data of new pairs for given number of days. Default: `None`. + --include-inactive-pairs + Also download data from inactive pairs. --timerange TIMERANGE Specify what timerange of data to use. --dl-trades Download trades instead of OHLCV data. The bot will @@ -52,10 +55,10 @@ optional arguments: exchange/pairs/timeframes. --data-format-ohlcv {json,jsongz,hdf5} Storage format for downloaded candle (OHLCV) data. - (default: `None`). + (default: `json`). --data-format-trades {json,jsongz,hdf5} Storage format for downloaded trades data. (default: - `None`). + `jsongz`). Common arguments: -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). @@ -80,6 +83,82 @@ Common arguments: For that reason, `download-data` does not care about the "startup-period" defined in a strategy. It's up to the user to download additional days if the backtest should start at a specific point in time (while respecting startup period). +### Pairs file + +In alternative to the whitelist from `config.json`, a `pairs.json` file can be used. +If you are using Binance for example: + +- create a directory `user_data/data/binance` and copy or create the `pairs.json` file in that directory. +- update the `pairs.json` file to contain the currency pairs you are interested in. + +```bash +mkdir -p user_data/data/binance +touch user_data/data/binance/pairs.json +``` + +The format of the `pairs.json` file is a simple json list. +Mixing different stake-currencies is allowed for this file, since it's only used for downloading. + +``` json +[ + "ETH/BTC", + "ETH/USDT", + "BTC/USDT", + "XRP/ETH" +] +``` + +!!! Tip "Downloading all data for one quote currency" + Often, you'll want to download data for all pairs of a specific quote-currency. In such cases, you can use the following shorthand: + `freqtrade download-data --exchange binance --pairs .*/USDT <...>`. The provided "pairs" string will be expanded to contain all active pairs on the exchange. + To also download data for inactive (delisted) pairs, add `--include-inactive-pairs` to the command. + +??? Note "Permission denied errors" + If your configuration directory `user_data` was made by docker, you may get the following error: + + ``` + cp: cannot create regular file 'user_data/data/binance/pairs.json': Permission denied + ``` + + You can fix the permissions of your user-data directory as follows: + + ``` + sudo chown -R $UID:$GID user_data + ``` + +### Start download + +Then run: + +```bash +freqtrade download-data --exchange binance +``` + +This will download historical candle (OHLCV) data for all the currency pairs you defined in `pairs.json`. + +Alternatively, specify the pairs directly + +```bash +freqtrade download-data --exchange binance --pairs ETH/USDT XRP/USDT BTC/USDT +``` + +or as regex (to download all active USDT pairs) + +```bash +freqtrade download-data --exchange binance --pairs .*/USDT +``` + +### Other Notes + +- To use a different directory than the exchange specific default, use `--datadir user_data/data/some_directory`. +- To change the exchange used to download the historical data from, please use a different configuration file (you'll probably need to adjust rate limits etc.) +- To use `pairs.json` from some other directory, use `--pairs-file some_other_dir/pairs.json`. +- To download historical candle (OHLCV) data for only 10 days, use `--days 10` (defaults to 30 days). +- To download historical candle (OHLCV) data from a fixed starting point, use `--timerange 20200101-` - which will download all data from January 1st, 2020. Eventually set end dates are ignored. +- Use `--timeframes` to specify what timeframe download the historical candle (OHLCV) data for. Default is `--timeframes 1m 5m` which will download 1-minute and 5-minute data. +- To use exchange, timeframe and list of pairs as defined in your configuration file, use the `-c/--config` option. With this, the script uses the whitelist defined in the config as the list of currency pairs to download data for and does not require the pairs.json file. You can combine `-c/--config` with most other options. + + ### Data format Freqtrade currently supports 3 data-formats for both OHLCV and trades data: @@ -312,64 +391,6 @@ ETH/BTC 5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d ETH/USDT 5m, 15m, 30m, 1h, 2h, 4h ``` -### Pairs file - -In alternative to the whitelist from `config.json`, a `pairs.json` file can be used. - -If you are using Binance for example: - -- create a directory `user_data/data/binance` and copy or create the `pairs.json` file in that directory. -- update the `pairs.json` file to contain the currency pairs you are interested in. - -```bash -mkdir -p user_data/data/binance -cp tests/testdata/pairs.json user_data/data/binance -``` - -If your configuration directory `user_data` was made by docker, you may get the following error: - -``` -cp: cannot create regular file 'user_data/data/binance/pairs.json': Permission denied -``` - -You can fix the permissions of your user-data directory as follows: - -``` -sudo chown -R $UID:$GID user_data -``` - -The format of the `pairs.json` file is a simple json list. -Mixing different stake-currencies is allowed for this file, since it's only used for downloading. - -``` json -[ - "ETH/BTC", - "ETH/USDT", - "BTC/USDT", - "XRP/ETH" -] -``` - -### Start download - -Then run: - -```bash -freqtrade download-data --exchange binance -``` - -This will download historical candle (OHLCV) data for all the currency pairs you defined in `pairs.json`. - -### Other Notes - -- To use a different directory than the exchange specific default, use `--datadir user_data/data/some_directory`. -- To change the exchange used to download the historical data from, please use a different configuration file (you'll probably need to adjust rate limits etc.) -- To use `pairs.json` from some other directory, use `--pairs-file some_other_dir/pairs.json`. -- To download historical candle (OHLCV) data for only 10 days, use `--days 10` (defaults to 30 days). -- To download historical candle (OHLCV) data from a fixed starting point, use `--timerange 20200101-` - which will download all data from January 1st, 2020. Eventually set end dates are ignored. -- Use `--timeframes` to specify what timeframe download the historical candle (OHLCV) data for. Default is `--timeframes 1m 5m` which will download 1-minute and 5-minute data. -- To use exchange, timeframe and list of pairs as defined in your configuration file, use the `-c/--config` option. With this, the script uses the whitelist defined in the config as the list of currency pairs to download data for and does not require the pairs.json file. You can combine `-c/--config` with most other options. - ### Trades (tick) data By default, `download-data` sub-command downloads Candles (OHLCV) data. Some exchanges also provide historic trade-data via their API. diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 5675eb096..86d7a1923 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -63,9 +63,9 @@ ARGS_CONVERT_TRADES = ["pairs", "timeframes", "exchange", "dataformat_ohlcv", "d ARGS_LIST_DATA = ["exchange", "dataformat_ohlcv", "pairs"] -ARGS_DOWNLOAD_DATA = ["pairs", "pairs_file", "days", "new_pairs_days", "timerange", - "download_trades", "exchange", "timeframes", "erase", "dataformat_ohlcv", - "dataformat_trades"] +ARGS_DOWNLOAD_DATA = ["pairs", "pairs_file", "days", "new_pairs_days", "include_inactive", + "timerange", "download_trades", "exchange", "timeframes", + "erase", "dataformat_ohlcv", "dataformat_trades"] ARGS_PLOT_DATAFRAME = ["pairs", "indicators1", "indicators2", "plot_limit", "db_url", "trade_source", "export", "exportfilename", diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index 0e08adb47..b60692c67 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -355,6 +355,11 @@ AVAILABLE_CLI_OPTIONS = { type=check_int_positive, metavar='INT', ), + "include_inactive": Arg( + '--include-inactive-pairs', + help='Also download data from inactive pairs.', + action='store_true', + ), "new_pairs_days": Arg( '--new-pairs-days', help='Download data of new pairs for given number of days. Default: `%(default)s`.', diff --git a/freqtrade/commands/data_commands.py b/freqtrade/commands/data_commands.py index ee05e6c69..5dc5fe7ea 100644 --- a/freqtrade/commands/data_commands.py +++ b/freqtrade/commands/data_commands.py @@ -11,6 +11,7 @@ from freqtrade.data.history import (convert_trades_to_ohlcv, refresh_backtest_oh from freqtrade.enums import RunMode from freqtrade.exceptions import OperationalException from freqtrade.exchange import timeframe_to_minutes +from freqtrade.exchange.exchange import market_is_active from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist from freqtrade.resolvers import ExchangeResolver @@ -47,11 +48,13 @@ def start_download_data(args: Dict[str, Any]) -> None: # Init exchange exchange = ExchangeResolver.load_exchange(config['exchange']['name'], config, validate=False) + markets = [p for p, m in exchange.markets.items() if market_is_active(m) + or config.get('include_inactive')] + expanded_pairs = expand_pairlist(config['pairs'], markets) + # Manual validations of relevant settings if not config['exchange'].get('skip_pair_validation', False): - exchange.validate_pairs(config['pairs']) - expanded_pairs = expand_pairlist(config['pairs'], list(exchange.markets)) - + exchange.validate_pairs(expanded_pairs) logger.info(f"About to download pairs: {expanded_pairs}, " f"intervals: {config['timeframes']} to {config['datadir']}") diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 12dcff46a..5db3379d2 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -407,6 +407,9 @@ class Configuration: self._args_to_config(config, argname='days', logstring='Detected --days: {}') + self._args_to_config(config, argname='include_inactive', + logstring='Detected --include-inactive-pairs: {}') + self._args_to_config(config, argname='download_trades', logstring='Detected --dl-trades: {}') diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index 6a0e741d9..6e717afdf 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -754,6 +754,46 @@ def test_download_data_no_pairs(mocker, caplog): start_download_data(pargs) +def test_download_data_all_pairs(mocker, markets): + + mocker.patch.object(Path, "exists", MagicMock(return_value=False)) + + dl_mock = mocker.patch('freqtrade.commands.data_commands.refresh_backtest_ohlcv_data', + MagicMock(return_value=["ETH/BTC", "XRP/BTC"])) + patch_exchange(mocker) + mocker.patch( + 'freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets) + ) + args = [ + "download-data", + "--exchange", + "binance", + "--pairs", + ".*/USDT" + ] + pargs = get_args(args) + pargs['config'] = None + start_download_data(pargs) + expected = set(['ETH/USDT', 'XRP/USDT', 'NEO/USDT', 'TKN/USDT']) + assert set(dl_mock.call_args_list[0][1]['pairs']) == expected + assert dl_mock.call_count == 1 + + dl_mock.reset_mock() + args = [ + "download-data", + "--exchange", + "binance", + "--pairs", + ".*/USDT", + "--include-inactive-pairs", + ] + pargs = get_args(args) + pargs['config'] = None + start_download_data(pargs) + expected = set(['ETH/USDT', 'LTC/USDT', 'XRP/USDT', 'NEO/USDT', 'TKN/USDT']) + assert set(dl_mock.call_args_list[0][1]['pairs']) == expected + + def test_download_data_trades(mocker, caplog): dl_mock = mocker.patch('freqtrade.commands.data_commands.refresh_backtest_trades_data', MagicMock(return_value=[])) From 28483a795224ff6ac27383768136101fdc795ffb Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 17 Oct 2021 16:10:15 +0200 Subject: [PATCH 112/144] Fix doc-link in developer docs --- docs/developer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developer.md b/docs/developer.md index bd138212b..a6c9ec322 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -8,7 +8,7 @@ All contributions, bug reports, bug fixes, documentation improvements, enhanceme Documentation is available at [https://freqtrade.io](https://www.freqtrade.io/) and needs to be provided with every new feature PR. -Special fields for the documentation (like Note boxes, ...) can be found [here](https://squidfunk.github.io/mkdocs-material/extensions/admonition/). +Special fields for the documentation (like Note boxes, ...) can be found [here](https://squidfunk.github.io/mkdocs-material/reference/admonitions/). To test the documentation locally use the following commands. From 7d8cd736b8113904c427ccba13b2a5113e959be3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 17 Oct 2021 16:48:31 +0200 Subject: [PATCH 113/144] Support days-breakdown also for hyperopt results --- freqtrade/commands/arguments.py | 2 +- freqtrade/commands/hyperopt_commands.py | 2 +- freqtrade/optimize/optimize_reports.py | 14 ++++++++------ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 00aa0ded0..11c1e9191 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -89,7 +89,7 @@ ARGS_HYPEROPT_LIST = ["hyperopt_list_best", "hyperopt_list_profitable", ARGS_HYPEROPT_SHOW = ["hyperopt_list_best", "hyperopt_list_profitable", "hyperopt_show_index", "print_json", "hyperoptexportfilename", "hyperopt_show_no_header", - "disableparamexport"] + "disableparamexport", "show_days"] NO_CONF_REQURIED = ["convert-data", "convert-trade-data", "download-data", "list-timeframes", "list-markets", "list-pairs", "list-strategies", "list-data", diff --git a/freqtrade/commands/hyperopt_commands.py b/freqtrade/commands/hyperopt_commands.py index 614c4b3f5..d2f8c188c 100755 --- a/freqtrade/commands/hyperopt_commands.py +++ b/freqtrade/commands/hyperopt_commands.py @@ -96,7 +96,7 @@ def start_hyperopt_show(args: Dict[str, Any]) -> None: if 'strategy_name' in metrics: strategy_name = metrics['strategy_name'] show_backtest_result(strategy_name, metrics, - metrics['stake_currency']) + metrics['stake_currency'], config.get('show_days', False)) HyperoptTools.try_export_params(config, strategy_name, val) diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index 384ca006b..a6eedc6c7 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -4,7 +4,7 @@ from pathlib import Path from typing import Any, Dict, List, Union from numpy import int64 -from pandas import DataFrame +from pandas import DataFrame, to_datetime from tabulate import tabulate from freqtrade.constants import DATETIME_PRINT_FORMAT, LAST_BT_RESULT_FN, UNLIMITED_STAKE_AMOUNT @@ -213,7 +213,9 @@ def generate_edge_table(results: dict) -> str: floatfmt=floatfmt, tablefmt="orgtbl", stralign="right") # type: ignore -def generate_days_breakdown_stats(results: DataFrame, starting_balance: int) -> Dict[str, Any]: +def generate_days_breakdown_stats(trade_list: List, starting_balance: int) -> List[Dict[str, Any]]: + results = DataFrame.from_records(trade_list) + results['close_date'] = to_datetime(results['close_date'], utc=True) days = results.resample('1d', on='close_date') days_stats = [] for name, day in days: @@ -341,8 +343,6 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame], starting_balance=starting_balance, results=results.loc[results['is_open']], skip_nan=True) - days_breakdown_stats = generate_days_breakdown_stats( - results=results, starting_balance=starting_balance) daily_stats = generate_daily_stats(results) trade_stats = generate_trading_stats(results) best_pair = max([pair for pair in pair_results if pair['key'] != 'TOTAL'], @@ -362,7 +362,7 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame], 'results_per_pair': pair_results, 'sell_reason_summary': sell_reason_stats, 'left_open_trades': left_open_results, - 'days_breakdown_stats': days_breakdown_stats, + # 'days_breakdown_stats': days_breakdown_stats, 'total_trades': len(results), 'total_volume': float(results['stake_amount'].sum()), @@ -690,7 +690,9 @@ def show_backtest_result(strategy: str, results: Dict[str, Any], stake_currency: print(table) if show_days: - table = text_table_days_breakdown(days_breakdown_stats=results['days_breakdown_stats'], + days_breakdown_stats = generate_days_breakdown_stats( + trade_list=results['trades'], starting_balance=results['starting_balance']) + table = text_table_days_breakdown(days_breakdown_stats=days_breakdown_stats, stake_currency=stake_currency) if isinstance(table, str) and len(table) > 0: print(' DAYS BREAKDOWN '.center(len(table.splitlines()[0]), '=')) From 00fc38a5dccf2efb999e92cd8e50ba0a13ea1d51 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 17 Oct 2021 19:23:51 +0200 Subject: [PATCH 114/144] Update setup.sh to correctly exit if ta-lib fails part of #5734 --- setup.sh | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/setup.sh b/setup.sh index aee7c80b5..1173b59b9 100755 --- a/setup.sh +++ b/setup.sh @@ -30,7 +30,7 @@ function check_installed_python() { check_installed_pip return fi - done + done echo "No usable python found. Please make sure to have python3.7 or newer installed" exit 1 @@ -95,11 +95,19 @@ function install_talib() { return fi - cd build_helpers && ./install_ta-lib.sh && cd .. + cd build_helpers && ./install_ta-lib.sh + + if [ $? -ne 0 ]; then + echo "Quitting. Please fix the above error before continuing." + cd .. + exit 1 + fi; + + cd .. } -function install_mac_newer_python_dependencies() { - +function install_mac_newer_python_dependencies() { + if [ ! $(brew --prefix --installed hdf5 2>/dev/null) ] then echo "-------------------------" @@ -115,7 +123,7 @@ function install_mac_newer_python_dependencies() { echo "Installing c-blosc" echo "-------------------------" brew install c-blosc - fi + fi export CBLOSC_DIR=$(brew --prefix) } @@ -130,7 +138,7 @@ function install_macos() { fi #Gets number after decimal in python version version=$(egrep -o 3.\[0-9\]+ <<< $PYTHON | sed 's/3.//g') - + if [[ $version -ge 9 ]]; then #Checks if python version >= 3.9 install_mac_newer_python_dependencies fi From 6be40cb7c3d680303591de81bb895ed8cc3f4d52 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Oct 2021 03:01:07 +0000 Subject: [PATCH 115/144] Bump types-requests from 2.25.9 to 2.25.11 Bumps [types-requests](https://github.com/python/typeshed) from 2.25.9 to 2.25.11. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-requests dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 74ebee479..7627e1022 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -22,5 +22,5 @@ nbconvert==6.2.0 # mypy types types-cachetools==4.2.2 types-filelock==3.2.0 -types-requests==2.25.9 +types-requests==2.25.11 types-tabulate==0.8.2 From 12a041b46665caad023f7bbef1849b148cc7ef8e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Oct 2021 03:01:10 +0000 Subject: [PATCH 116/144] Bump pytest-asyncio from 0.15.1 to 0.16.0 Bumps [pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio) from 0.15.1 to 0.16.0. - [Release notes](https://github.com/pytest-dev/pytest-asyncio/releases) - [Commits](https://github.com/pytest-dev/pytest-asyncio/compare/v0.15.1...v0.16.0) --- updated-dependencies: - dependency-name: pytest-asyncio dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 74ebee479..8daaa0524 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -8,7 +8,7 @@ flake8==4.0.0 flake8-tidy-imports==4.5.0 mypy==0.910 pytest==6.2.5 -pytest-asyncio==0.15.1 +pytest-asyncio==0.16.0 pytest-cov==3.0.0 pytest-mock==3.6.1 pytest-random-order==1.0.4 From 9b0171ef3748b310b681fcd1262d16f21b6d2dc7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Oct 2021 03:01:14 +0000 Subject: [PATCH 117/144] Bump flake8 from 4.0.0 to 4.0.1 Bumps [flake8](https://github.com/pycqa/flake8) from 4.0.0 to 4.0.1. - [Release notes](https://github.com/pycqa/flake8/releases) - [Commits](https://github.com/pycqa/flake8/compare/4.0.0...4.0.1) --- updated-dependencies: - dependency-name: flake8 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 74ebee479..83bd12843 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -4,7 +4,7 @@ -r requirements-hyperopt.txt coveralls==3.2.0 -flake8==4.0.0 +flake8==4.0.1 flake8-tidy-imports==4.5.0 mypy==0.910 pytest==6.2.5 From e7a2672f07ca15aa1a42b834f92f91e6ec8812ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Oct 2021 03:01:17 +0000 Subject: [PATCH 118/144] Bump filelock from 3.3.0 to 3.3.1 Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.3.0 to 3.3.1. - [Release notes](https://github.com/tox-dev/py-filelock/releases) - [Changelog](https://github.com/tox-dev/py-filelock/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/py-filelock/compare/3.3.0...3.3.1) --- updated-dependencies: - dependency-name: filelock dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-hyperopt.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index e97e78638..b0f4343ae 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -5,7 +5,7 @@ scipy==1.7.1 scikit-learn==1.0 scikit-optimize==0.9.0 -filelock==3.3.0 +filelock==3.3.1 joblib==1.1.0 psutil==5.8.0 progressbar2==3.53.3 From b60371822f9b14bff45e16b9de7f8e8fe338afd3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Oct 2021 03:01:19 +0000 Subject: [PATCH 119/144] Bump pyjwt from 2.2.0 to 2.3.0 Bumps [pyjwt](https://github.com/jpadilla/pyjwt) from 2.2.0 to 2.3.0. - [Release notes](https://github.com/jpadilla/pyjwt/releases) - [Changelog](https://github.com/jpadilla/pyjwt/blob/master/CHANGELOG.rst) - [Commits](https://github.com/jpadilla/pyjwt/commits) --- updated-dependencies: - dependency-name: pyjwt dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f3eb65c59..f85b9bb8e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -34,7 +34,7 @@ sdnotify==0.3.2 # API Server fastapi==0.70.0 uvicorn==0.15.0 -pyjwt==2.2.0 +pyjwt==2.3.0 aiofiles==0.7.0 psutil==5.8.0 From d7756efe8b4e9b5aebb5534597b396bee5f94b5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Oct 2021 03:01:28 +0000 Subject: [PATCH 120/144] Bump wrapt from 1.13.1 to 1.13.2 Bumps [wrapt](https://github.com/GrahamDumpleton/wrapt) from 1.13.1 to 1.13.2. - [Release notes](https://github.com/GrahamDumpleton/wrapt/releases) - [Changelog](https://github.com/GrahamDumpleton/wrapt/blob/develop/docs/changes.rst) - [Commits](https://github.com/GrahamDumpleton/wrapt/compare/1.13.1...1.13.2) --- updated-dependencies: - dependency-name: wrapt dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f3eb65c59..858452e82 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ arrow==1.2.0 cachetools==4.2.2 requests==2.26.0 urllib3==1.26.7 -wrapt==1.13.1 +wrapt==1.13.2 jsonschema==4.1.0 TA-Lib==0.4.21 technical==1.3.0 From 035380d8a4b7f2826e2d33d9f36078a44c973231 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Oct 2021 05:18:42 +0000 Subject: [PATCH 121/144] Bump types-cachetools from 4.2.2 to 4.2.4 Bumps [types-cachetools](https://github.com/python/typeshed) from 4.2.2 to 4.2.4. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-cachetools dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 74ebee479..93df93695 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -4,7 +4,7 @@ -r requirements-hyperopt.txt coveralls==3.2.0 -flake8==4.0.0 +flake8==4.0.1 flake8-tidy-imports==4.5.0 mypy==0.910 pytest==6.2.5 @@ -20,7 +20,7 @@ time-machine==2.4.0 nbconvert==6.2.0 # mypy types -types-cachetools==4.2.2 +types-cachetools==4.2.4 types-filelock==3.2.0 types-requests==2.25.9 types-tabulate==0.8.2 From 69c98c4141b1e9d1e74dfd55162fded3120625bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Oct 2021 05:18:47 +0000 Subject: [PATCH 122/144] Bump python-rapidjson from 1.4 to 1.5 Bumps [python-rapidjson](https://github.com/python-rapidjson/python-rapidjson) from 1.4 to 1.5. - [Release notes](https://github.com/python-rapidjson/python-rapidjson/releases) - [Changelog](https://github.com/python-rapidjson/python-rapidjson/blob/master/CHANGES.rst) - [Commits](https://github.com/python-rapidjson/python-rapidjson/compare/v1.4...v1.5) --- updated-dependencies: - dependency-name: python-rapidjson dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f3eb65c59..a78e8c7a3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -26,7 +26,7 @@ blosc==1.10.6 py_find_1st==1.1.5 # Load ticker files 30% faster -python-rapidjson==1.4 +python-rapidjson==1.5 # Notify systemd sdnotify==0.3.2 From 82684f5de90eb5c1bfd8cd5c6ef8af631905b1c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Oct 2021 05:19:03 +0000 Subject: [PATCH 123/144] Bump progressbar2 from 3.53.3 to 3.55.0 Bumps [progressbar2](https://github.com/WoLpH/python-progressbar) from 3.53.3 to 3.55.0. - [Release notes](https://github.com/WoLpH/python-progressbar/releases) - [Changelog](https://github.com/WoLpH/python-progressbar/blob/develop/CHANGES.rst) - [Commits](https://github.com/WoLpH/python-progressbar/compare/v3.53.3...v3.55.0) --- updated-dependencies: - dependency-name: progressbar2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-hyperopt.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index e97e78638..8e6ca9769 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -8,4 +8,4 @@ scikit-optimize==0.9.0 filelock==3.3.0 joblib==1.1.0 psutil==5.8.0 -progressbar2==3.53.3 +progressbar2==3.55.0 From 4b02749019394ffef0046e53e0187f37cd06aa90 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Oct 2021 06:15:52 +0000 Subject: [PATCH 124/144] Bump mkdocs from 1.2.2 to 1.2.3 Bumps [mkdocs](https://github.com/mkdocs/mkdocs) from 1.2.2 to 1.2.3. - [Release notes](https://github.com/mkdocs/mkdocs/releases) - [Commits](https://github.com/mkdocs/mkdocs/compare/1.2.2...1.2.3) --- updated-dependencies: - dependency-name: mkdocs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index 9a733d8f7..e4bcf3c79 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,4 +1,4 @@ -mkdocs==1.2.2 +mkdocs==1.2.3 mkdocs-material==7.3.2 mdx_truly_sane_lists==1.2 pymdown-extensions==9.0 From 44e6e134297301f97901a18e08f2bf4f22e4bf27 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Oct 2021 06:16:09 +0000 Subject: [PATCH 125/144] Bump ccxt from 1.57.94 to 1.58.47 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.57.94 to 1.58.47. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg) - [Commits](https://github.com/ccxt/ccxt/compare/1.57.94...1.58.47) --- updated-dependencies: - dependency-name: ccxt dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 858452e82..d38a0afce 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ numpy==1.21.2 pandas==1.3.3 pandas-ta==0.3.14b -ccxt==1.57.94 +ccxt==1.58.47 # Pin cryptography for now due to rust build errors with piwheels cryptography==35.0.0 aiohttp==3.7.4.post0 @@ -26,7 +26,7 @@ blosc==1.10.6 py_find_1st==1.1.5 # Load ticker files 30% faster -python-rapidjson==1.4 +python-rapidjson==1.5 # Notify systemd sdnotify==0.3.2 @@ -34,7 +34,7 @@ sdnotify==0.3.2 # API Server fastapi==0.70.0 uvicorn==0.15.0 -pyjwt==2.2.0 +pyjwt==2.3.0 aiofiles==0.7.0 psutil==5.8.0 From 618f0ffe68882a80677e36d41ab72bbf77dba8f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Oct 2021 06:38:42 +0000 Subject: [PATCH 126/144] Bump types-tabulate from 0.8.2 to 0.8.3 Bumps [types-tabulate](https://github.com/python/typeshed) from 0.8.2 to 0.8.3. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-tabulate dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index da12e5fcc..3a6913f53 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -23,4 +23,4 @@ nbconvert==6.2.0 types-cachetools==4.2.4 types-filelock==3.2.0 types-requests==2.25.11 -types-tabulate==0.8.2 +types-tabulate==0.8.3 From 75e6a2d276912ab0b50630a6854276266e20da2c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Oct 2021 06:47:32 +0000 Subject: [PATCH 127/144] Bump mkdocs-material from 7.3.2 to 7.3.4 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 7.3.2 to 7.3.4. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/7.3.2...7.3.4) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index e4bcf3c79..72d1d0494 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,4 +1,4 @@ mkdocs==1.2.3 -mkdocs-material==7.3.2 +mkdocs-material==7.3.4 mdx_truly_sane_lists==1.2 pymdown-extensions==9.0 From 3af55cc8c775ea3d28fcc0207097e97f49d6b5e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Oct 2021 06:58:47 +0000 Subject: [PATCH 128/144] Bump pandas from 1.3.3 to 1.3.4 Bumps [pandas](https://github.com/pandas-dev/pandas) from 1.3.3 to 1.3.4. - [Release notes](https://github.com/pandas-dev/pandas/releases) - [Changelog](https://github.com/pandas-dev/pandas/blob/master/RELEASE.md) - [Commits](https://github.com/pandas-dev/pandas/compare/v1.3.3...v1.3.4) --- updated-dependencies: - dependency-name: pandas dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d38a0afce..9f7a055e8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ numpy==1.21.2 -pandas==1.3.3 +pandas==1.3.4 pandas-ta==0.3.14b ccxt==1.58.47 From 8a7ea655313b64b6dc5f0009b728c9436c2ffcc8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Oct 2021 07:06:05 +0000 Subject: [PATCH 129/144] Bump types-filelock from 3.2.0 to 3.2.1 Bumps [types-filelock](https://github.com/python/typeshed) from 3.2.0 to 3.2.1. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-filelock dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 3a6913f53..0e68e18a3 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -21,6 +21,6 @@ nbconvert==6.2.0 # mypy types types-cachetools==4.2.4 -types-filelock==3.2.0 +types-filelock==3.2.1 types-requests==2.25.11 types-tabulate==0.8.3 From ddba4e32d7b904d6a54b273fd3ddee67b3a260f5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 18 Oct 2021 16:04:24 +0200 Subject: [PATCH 130/144] Fully remove flake8-type-annotations --- environment.yml | 1 - setup.py | 1 - 2 files changed, 2 deletions(-) diff --git a/environment.yml b/environment.yml index f58434c15..f62ac8105 100644 --- a/environment.yml +++ b/environment.yml @@ -64,7 +64,6 @@ dependencies: - py_find_1st - tables - pytest-random-order - - flake8-type-annotations - ccxt - flake8-tidy-imports - -e . diff --git a/setup.py b/setup.py index cf381bdd3..445155687 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,6 @@ hyperopt = [ develop = [ 'coveralls', 'flake8', - 'flake8-type-annotations', 'flake8-tidy-imports', 'mypy', 'pytest', From 0da5ef16e6170efad32d8b82533f620f129b04f7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 18 Oct 2021 19:16:56 +0200 Subject: [PATCH 131/144] Remove unnecessary dependency --- environment.yml | 1 - requirements.txt | 1 - setup.py | 1 - 3 files changed, 3 deletions(-) diff --git a/environment.yml b/environment.yml index f62ac8105..84ab5ff6f 100644 --- a/environment.yml +++ b/environment.yml @@ -16,7 +16,6 @@ dependencies: - cachetools - requests - urllib3 - - wrapt - jsonschema - TA-Lib - tabulate diff --git a/requirements.txt b/requirements.txt index 9f7a055e8..b10bbabf6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,6 @@ arrow==1.2.0 cachetools==4.2.2 requests==2.26.0 urllib3==1.26.7 -wrapt==1.13.2 jsonschema==4.1.0 TA-Lib==0.4.21 technical==1.3.0 diff --git a/setup.py b/setup.py index 445155687..b23fa814d 100644 --- a/setup.py +++ b/setup.py @@ -50,7 +50,6 @@ setup( 'cachetools', 'requests', 'urllib3', - 'wrapt', 'jsonschema', 'TA-Lib', 'pandas-ta', From f9b166747847c529c16900904a80e07284f46108 Mon Sep 17 00:00:00 2001 From: daniila Date: Mon, 18 Oct 2021 23:36:47 +0300 Subject: [PATCH 132/144] Update docs/advanced-setup.md Co-authored-by: Matthias --- docs/advanced-setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced-setup.md b/docs/advanced-setup.md index e7e5b6cec..79e17fb4e 100644 --- a/docs/advanced-setup.md +++ b/docs/advanced-setup.md @@ -103,7 +103,7 @@ services: # Please read the https://www.freqtrade.io/en/latest/rest-api/ documentation # before enabling this. ports: - - "127.0.0.1:8081:8081" + - "127.0.0.1:8081:8080" # Default command used when running `docker compose up` command: > trade From 5d2e37409962970a45cbffd255ea9080c595fc52 Mon Sep 17 00:00:00 2001 From: daniila Date: Mon, 18 Oct 2021 23:38:45 +0300 Subject: [PATCH 133/144] Update docs/advanced-setup.md Co-authored-by: Matthias --- docs/advanced-setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced-setup.md b/docs/advanced-setup.md index 79e17fb4e..6eda8489b 100644 --- a/docs/advanced-setup.md +++ b/docs/advanced-setup.md @@ -55,7 +55,7 @@ For more information regarding usage of the sqlite databases, for example to man ### Multiple instances using docker To run multiple instances of freqtrade using docker you will need to edit the docker-compose.yml file and add all the instances you want as separate services. Remember, you can separate your configuration into multiple files, so it's a good idea to think about making them modular, then if you need to edit something common to all bots, you can do that in a single config file. -``` +``` yml --- version: '3' services: From f863f4fdfca815bd5c8a20f3915b948b90f7d753 Mon Sep 17 00:00:00 2001 From: daniila Date: Mon, 18 Oct 2021 23:49:59 +0300 Subject: [PATCH 134/144] Update advanced-setup.md A note on having to use different database files, ports and telegram configs for each bot. --- docs/advanced-setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced-setup.md b/docs/advanced-setup.md index 6eda8489b..02b0307e5 100644 --- a/docs/advanced-setup.md +++ b/docs/advanced-setup.md @@ -114,7 +114,7 @@ services: --strategy SampleStrategy ``` -You can use whatever naming convention you want, freqtrade1 and 2 are arbitrary. +You can use whatever naming convention you want, freqtrade1 and 2 are arbitrary. Note, that you will need to use different database files, port mappings and telegram configurations for each instance, as mentioned above. ## Configure the bot running as a systemd service From 42a4dfed28be1425016836ae5c09b4e2a883662d Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 19 Oct 2021 19:11:17 +0200 Subject: [PATCH 135/144] Reallow bitstamp revert #1984, related to #1983 --- docs/utils.md | 2 +- freqtrade/exchange/common.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/utils.md b/docs/utils.md index d8fbcacb7..e915528ec 100644 --- a/docs/utils.md +++ b/docs/utils.md @@ -281,7 +281,7 @@ bitmax True missing opt: fetchMyTrades bitmex False Various reasons. bitpanda True bitso False missing: fetchOHLCV -bitstamp False Does not provide history. Details in https://github.com/freqtrade/freqtrade/issues/1983 +bitstamp True missing opt: fetchTickers bitstamp1 False missing: fetchOrder, fetchOHLCV bittrex True bitvavo True diff --git a/freqtrade/exchange/common.py b/freqtrade/exchange/common.py index 7b89adf06..644a13e93 100644 --- a/freqtrade/exchange/common.py +++ b/freqtrade/exchange/common.py @@ -16,8 +16,6 @@ API_FETCH_ORDER_RETRY_COUNT = 5 BAD_EXCHANGES = { "bitmex": "Various reasons.", - "bitstamp": "Does not provide history. " - "Details in https://github.com/freqtrade/freqtrade/issues/1983", "phemex": "Does not provide history. ", "poloniex": "Does not provide fetch_order endpoint to fetch both open and closed orders.", } From 55b021618067d1c3183611027f4963e48394b6ed Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 19 Oct 2021 19:48:56 +0200 Subject: [PATCH 136/144] Allow StaticPairlist in non-first position closes #5754 --- docs/includes/pairlists.md | 2 ++ freqtrade/plugins/pairlist/StaticPairList.py | 12 ++++++------ tests/plugins/test_pairlist.py | 13 +++---------- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index 3d10747d3..589bc23b2 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -52,6 +52,8 @@ To skip pair validation against active markets, set `"allow_inactive": true` wit This can be useful for backtesting expired pairs (like quarterly spot-markets). This option must be configured along with `exchange.skip_pair_validation` in the exchange configuration. +When used in a "follow-up" position (e.g. after VolumePairlist), all pairs in `'pair_whitelist'` will be added to the end of the pairlist. + #### Volume Pair List `VolumePairList` employs sorting/filtering of pairs by their trading volume. It selects `number_assets` top pairs with sorting based on the `sort_key` (which can only be `quoteVolume`). diff --git a/freqtrade/plugins/pairlist/StaticPairList.py b/freqtrade/plugins/pairlist/StaticPairList.py index d8623e13d..30fa474e4 100644 --- a/freqtrade/plugins/pairlist/StaticPairList.py +++ b/freqtrade/plugins/pairlist/StaticPairList.py @@ -4,9 +4,9 @@ Static Pair List provider Provides pair white list as it configured in config """ import logging +from copy import deepcopy from typing import Any, Dict, List -from freqtrade.exceptions import OperationalException from freqtrade.plugins.pairlist.IPairList import IPairList @@ -20,10 +20,6 @@ class StaticPairList(IPairList): pairlist_pos: int) -> None: super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) - if self._pairlist_pos != 0: - raise OperationalException(f"{self.name} can only be used in the first position " - "in the list of Pairlist Handlers.") - self._allow_inactive = self._pairlistconfig.get('allow_inactive', False) @property @@ -64,4 +60,8 @@ class StaticPairList(IPairList): :param tickers: Tickers (from exchange.get_tickers()). May be cached. :return: new whitelist """ - return pairlist + pairlist_ = deepcopy(pairlist) + for pair in self._config['exchange']['pair_whitelist']: + if pair not in pairlist_: + pairlist_.append(pair) + return pairlist_ diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index c6246dccb..6333266aa 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -415,10 +415,10 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf): # SpreadFilter only ([{"method": "SpreadFilter", "max_spread_ratio": 0.005}], "BTC", 'filter_at_the_beginning'), # OperationalException expected - # Static Pairlist after VolumePairList, on a non-first position - ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"}, + # Static Pairlist after VolumePairList, on a non-first position (appends pairs) + ([{"method": "VolumePairList", "number_assets": 2, "sort_key": "quoteVolume"}, {"method": "StaticPairList"}], - "BTC", 'static_in_the_middle'), + "BTC", ['ETH/BTC', 'TKN/BTC', 'TRST/BTC', 'SWT/BTC', 'BCC/BTC', 'HOT/BTC']), ([{"method": "VolumePairList", "number_assets": 20, "sort_key": "quoteVolume"}, {"method": "PriceFilter", "low_price_ratio": 0.02}], "USDT", ['ETH/USDT', 'NANO/USDT']), @@ -469,13 +469,6 @@ def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, shitcoinmarkets, t mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) - if whitelist_result == 'static_in_the_middle': - with pytest.raises(OperationalException, - match=r"StaticPairList can only be used in the first position " - r"in the list of Pairlist Handlers."): - freqtrade = get_patched_freqtradebot(mocker, whitelist_conf) - return - freqtrade = get_patched_freqtradebot(mocker, whitelist_conf) mocker.patch.multiple('freqtrade.exchange.Exchange', get_tickers=tickers, From 5454460227dcf63b578096c3862dd2269673ec85 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 20 Oct 2021 07:46:15 +0200 Subject: [PATCH 137/144] Revert initial_points to 30 closes #5760 --- freqtrade/optimize/hyperopt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 6397bbacb..2c7cc0ea7 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -45,7 +45,7 @@ progressbar.streams.wrap_stdout() logger = logging.getLogger(__name__) -INITIAL_POINTS = 5 +INITIAL_POINTS = 30 # Keep no more than SKOPT_MODEL_QUEUE_SIZE models # in the skopt model queue, to optimize memory consumption From de5497c76660242d41839df07c22b18ed48ba7b1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 20 Oct 2021 19:39:37 +0200 Subject: [PATCH 138/144] backtest_days cannot be below 1 --- freqtrade/optimize/optimize_reports.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index a6eedc6c7..abfccaa86 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -353,7 +353,7 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame], results['open_timestamp'] = results['open_date'].view(int64) // 1e6 results['close_timestamp'] = results['close_date'].view(int64) // 1e6 - backtest_days = (max_date - min_date).days + backtest_days = (max_date - min_date).days or 1 strat_stats = { 'trades': results.to_dict(orient='records'), 'locks': [lock.to_json() for lock in content['locks']], @@ -380,7 +380,7 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame], 'backtest_run_start_ts': content['backtest_start_time'], 'backtest_run_end_ts': content['backtest_end_time'], - 'trades_per_day': round(len(results) / backtest_days, 2) if backtest_days > 0 else 0, + 'trades_per_day': round(len(results) / backtest_days, 2), 'market_change': market_change, 'pairlist': list(btdata.keys()), 'stake_amount': config['stake_amount'], From 7197f4ce77d0fe331fbfcdb7edd64b2de16f6c60 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 20 Oct 2021 20:01:31 +0200 Subject: [PATCH 139/144] Don't show daily % profit (it's wrong) --- freqtrade/optimize/optimize_reports.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index abfccaa86..9a4591e67 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -220,15 +220,12 @@ def generate_days_breakdown_stats(trade_list: List, starting_balance: int) -> Li days_stats = [] for name, day in days: profit_abs = day['profit_abs'].sum().round(10) - profit_total = day['profit_abs'].sum() / starting_balance wins = sum(day['profit_abs'] > 0) draws = sum(day['profit_abs'] == 0) loses = sum(day['profit_abs'] < 0) - profit_percentage = round(profit_total * 100.0, 2) days_stats.append( { 'date': name.strftime('%d/%m/%Y'), - 'profit_percentage': profit_percentage, 'profit_abs': profit_abs, 'wins': wins, 'draws': draws, @@ -542,14 +539,13 @@ def text_table_days_breakdown(days_breakdown_stats: List[Dict[str, Any]], """ headers = [ 'Day', - 'Profit %', f'Tot Profit {stake_currency}', 'Wins', 'Draws', 'Losses', ] output = [[ - d['date'], d['profit_percentage'], round_coin_value(d['profit_abs'], stake_currency, False), + d['date'], round_coin_value(d['profit_abs'], stake_currency, False), d['wins'], d['draws'], d['loses'], ] for d in days_breakdown_stats] return tabulate(output, headers=headers, tablefmt="orgtbl", stralign="right") From fa028c2134440bbf794920d52e7822a128cdccf7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 21 Oct 2021 06:58:40 +0200 Subject: [PATCH 140/144] Support day/week/month breakdowns --- freqtrade/commands/arguments.py | 4 +- freqtrade/commands/cli_options.py | 10 ++--- freqtrade/commands/hyperopt_commands.py | 2 +- freqtrade/configuration/configuration.py | 4 +- freqtrade/constants.py | 5 +++ freqtrade/optimize/optimize_reports.py | 47 +++++++++++++++--------- 6 files changed, 45 insertions(+), 27 deletions(-) diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 11c1e9191..53cdda95d 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -23,7 +23,7 @@ ARGS_COMMON_OPTIMIZE = ["timeframe", "timerange", "dataformat_ohlcv", ARGS_BACKTEST = ARGS_COMMON_OPTIMIZE + ["position_stacking", "use_max_market_positions", "enable_protections", "dry_run_wallet", "timeframe_detail", - "strategy_list", "export", "exportfilename", "show_days"] + "strategy_list", "export", "exportfilename", "backtest_breakdown"] ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + ["hyperopt", "hyperopt_path", "position_stacking", "use_max_market_positions", @@ -89,7 +89,7 @@ ARGS_HYPEROPT_LIST = ["hyperopt_list_best", "hyperopt_list_profitable", ARGS_HYPEROPT_SHOW = ["hyperopt_list_best", "hyperopt_list_profitable", "hyperopt_show_index", "print_json", "hyperoptexportfilename", "hyperopt_show_no_header", - "disableparamexport", "show_days"] + "disableparamexport", "backtest_breakdown"] NO_CONF_REQURIED = ["convert-data", "convert-trade-data", "download-data", "list-timeframes", "list-markets", "list-pairs", "list-strategies", "list-data", diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index 758e1d9ec..2c2c957df 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -193,11 +193,11 @@ AVAILABLE_CLI_OPTIONS = { type=float, metavar='FLOAT', ), - "show_days": Arg( - '--show-days', - help='Print days breakdown for backtest results', - action='store_true', - default=False, + "backtest_breakdown": Arg( + '--breakdown', + help='Show backtesting breakdown per [day, week, month].', + nargs='+', + choices=constants.BACKTEST_BREAKDOWNS ), # Edge "stoploss_range": Arg( diff --git a/freqtrade/commands/hyperopt_commands.py b/freqtrade/commands/hyperopt_commands.py index d2f8c188c..344828282 100755 --- a/freqtrade/commands/hyperopt_commands.py +++ b/freqtrade/commands/hyperopt_commands.py @@ -96,7 +96,7 @@ def start_hyperopt_show(args: Dict[str, Any]) -> None: if 'strategy_name' in metrics: strategy_name = metrics['strategy_name'] show_backtest_result(strategy_name, metrics, - metrics['stake_currency'], config.get('show_days', False)) + metrics['stake_currency'], config.get('backtest_breakdown', [])) HyperoptTools.try_export_params(config, strategy_name, val) diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 845e87b83..c6bad9305 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -269,8 +269,8 @@ class Configuration: self._args_to_config(config, argname='export', logstring='Parameter --export detected: {} ...') - self._args_to_config(config, argname='show_days', - logstring='Parameter --show-days detected ...') + self._args_to_config(config, argname='backtest_breakdown', + logstring='Parameter --breakdown detected ...') self._args_to_config(config, argname='disableparamexport', logstring='Parameter --disableparamexport detected: {} ...') diff --git a/freqtrade/constants.py b/freqtrade/constants.py index c6b8f0e62..8bef6610c 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -32,6 +32,7 @@ AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList', 'ShuffleFilter', 'SpreadFilter', 'VolatilityFilter'] AVAILABLE_PROTECTIONS = ['CooldownPeriod', 'LowProfitPairs', 'MaxDrawdown', 'StoplossGuard'] AVAILABLE_DATAHANDLERS = ['json', 'jsongz', 'hdf5'] +BACKTEST_BREAKDOWNS = ['day', 'week', 'month'] DRY_RUN_WALLET = 1000 DATETIME_PRINT_FORMAT = '%Y-%m-%d %H:%M:%S' MATH_CLOSE_PREC = 1e-14 # Precision used for float comparisons @@ -146,6 +147,10 @@ CONF_SCHEMA = { 'sell_profit_offset': {'type': 'number'}, 'ignore_roi_if_buy_signal': {'type': 'boolean'}, 'ignore_buying_expired_candle_after': {'type': 'number'}, + 'backtest_breakdown': { + 'type': 'array', + 'items': {'type': 'string', 'enum': BACKTEST_BREAKDOWNS} + }, 'bot_name': {'type': 'string'}, 'unfilledtimeout': { 'type': 'object', diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index 9a4591e67..a97f85637 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -213,17 +213,28 @@ def generate_edge_table(results: dict) -> str: floatfmt=floatfmt, tablefmt="orgtbl", stralign="right") # type: ignore -def generate_days_breakdown_stats(trade_list: List, starting_balance: int) -> List[Dict[str, Any]]: +def _get_resample_from_period(period: str) -> str: + if period == 'day': + return '1d' + if period == 'week': + return '1w' + if period == 'month': + return '1m' + raise ValueError(f"Period {period} is not supported.") + + +def generate_periodic_breakdown_stats(trade_list: List, period: str) -> List[Dict[str, Any]]: results = DataFrame.from_records(trade_list) results['close_date'] = to_datetime(results['close_date'], utc=True) - days = results.resample('1d', on='close_date') - days_stats = [] - for name, day in days: + resample = _get_resample_from_period(period) + period = results.resample(resample, on='close_date') + stats = [] + for name, day in period: profit_abs = day['profit_abs'].sum().round(10) wins = sum(day['profit_abs'] > 0) draws = sum(day['profit_abs'] == 0) loses = sum(day['profit_abs'] < 0) - days_stats.append( + stats.append( { 'date': name.strftime('%d/%m/%Y'), 'profit_abs': profit_abs, @@ -232,7 +243,7 @@ def generate_days_breakdown_stats(trade_list: List, starting_balance: int) -> Li 'loses': loses } ) - return days_stats + return stats def generate_trading_stats(results: DataFrame) -> Dict[str, Any]: @@ -529,8 +540,8 @@ def text_table_sell_reason(sell_reason_stats: List[Dict[str, Any]], stake_curren return tabulate(output, headers=headers, tablefmt="orgtbl", stralign="right") -def text_table_days_breakdown(days_breakdown_stats: List[Dict[str, Any]], - stake_currency: str) -> str: +def text_table_periodic_breakdown(days_breakdown_stats: List[Dict[str, Any]], + stake_currency: str, period: str) -> str: """ Generate small table with Backtest results by days :param days_breakdown_stats: Days breakdown metrics @@ -538,7 +549,7 @@ def text_table_days_breakdown(days_breakdown_stats: List[Dict[str, Any]], :return: pretty printed table with tabulate as string """ headers = [ - 'Day', + period.capitalize(), f'Tot Profit {stake_currency}', 'Wins', 'Draws', @@ -663,7 +674,7 @@ def text_table_add_metrics(strat_results: Dict) -> str: def show_backtest_result(strategy: str, results: Dict[str, Any], stake_currency: str, - show_days=False): + backtest_breakdown=[]): """ Print results for one strategy """ @@ -685,13 +696,13 @@ def show_backtest_result(strategy: str, results: Dict[str, Any], stake_currency: print(' LEFT OPEN TRADES REPORT '.center(len(table.splitlines()[0]), '=')) print(table) - if show_days: - days_breakdown_stats = generate_days_breakdown_stats( - trade_list=results['trades'], starting_balance=results['starting_balance']) - table = text_table_days_breakdown(days_breakdown_stats=days_breakdown_stats, - stake_currency=stake_currency) + for period in backtest_breakdown: + days_breakdown_stats = generate_periodic_breakdown_stats( + trade_list=results['trades'], period=period) + table = text_table_periodic_breakdown(days_breakdown_stats=days_breakdown_stats, + stake_currency=stake_currency, period=period) if isinstance(table, str) and len(table) > 0: - print(' DAYS BREAKDOWN '.center(len(table.splitlines()[0]), '=')) + print(f' {period.upper()} BREAKDOWN '.center(len(table.splitlines()[0]), '=')) print(table) table = text_table_add_metrics(results) @@ -708,7 +719,9 @@ def show_backtest_results(config: Dict, backtest_stats: Dict): stake_currency = config['stake_currency'] for strategy, results in backtest_stats['strategy'].items(): - show_backtest_result(strategy, results, stake_currency, config.get('show_days', False)) + show_backtest_result( + strategy, results, stake_currency, + config.get('backtest_breakdown', [])) if len(backtest_stats['strategy']) > 1: # Print Strategy summary table From 7b5346b984508fb48f646a3f36e8aea566d51f0c Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 21 Oct 2021 07:09:17 +0200 Subject: [PATCH 141/144] Add test for breakdown-stats --- freqtrade/optimize/optimize_reports.py | 4 ++- tests/optimize/test_backtesting.py | 2 ++ tests/optimize/test_optimize_reports.py | 34 ++++++++++++++++++++++--- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index a97f85637..a2590c10b 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -219,12 +219,14 @@ def _get_resample_from_period(period: str) -> str: if period == 'week': return '1w' if period == 'month': - return '1m' + return '1M' raise ValueError(f"Period {period} is not supported.") def generate_periodic_breakdown_stats(trade_list: List, period: str) -> List[Dict[str, Any]]: results = DataFrame.from_records(trade_list) + if len(results) == 0: + return [] results['close_date'] = to_datetime(results['close_date'], utc=True) resample = _get_resample_from_period(period) period = results.resample(resample, on='close_date') diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 2248cd4c1..b5fa44d01 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -1102,6 +1102,7 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat '--timerange', '1510694220-1510700340', '--enable-position-stacking', '--disable-max-market-positions', + '--breakdown', 'day', '--strategy-list', 'StrategyTestV2', 'TestStrategyLegacyV1', @@ -1130,6 +1131,7 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat captured = capsys.readouterr() assert 'BACKTESTING REPORT' in captured.out assert 'SELL REASON STATS' in captured.out + assert 'DAY BREAKDOWN' in captured.out assert 'LEFT OPEN TRADES REPORT' in captured.out assert '2017-11-14 21:17:00 -> 2017-11-14 22:58:00 | Max open trades : 1' in captured.out assert 'STRATEGY SUMMARY' in captured.out diff --git a/tests/optimize/test_optimize_reports.py b/tests/optimize/test_optimize_reports.py index 83caefd2d..4bf20e547 100644 --- a/tests/optimize/test_optimize_reports.py +++ b/tests/optimize/test_optimize_reports.py @@ -13,9 +13,9 @@ from freqtrade.data import history from freqtrade.data.btanalysis import get_latest_backtest_filename, load_backtest_data from freqtrade.edge import PairInfo from freqtrade.enums import SellType -from freqtrade.optimize.optimize_reports import (generate_backtest_stats, generate_daily_stats, - generate_edge_table, generate_pair_metrics, - generate_sell_reason_stats, +from freqtrade.optimize.optimize_reports import (_get_resample_from_period, generate_backtest_stats, + generate_daily_stats, generate_edge_table, + generate_pair_metrics, generate_periodic_breakdown_stats, generate_sell_reason_stats, generate_strategy_comparison, generate_trading_stats, store_backtest_stats, text_table_bt_results, text_table_sell_reason, @@ -377,3 +377,31 @@ def test_generate_edge_table(): assert generate_edge_table(results).count('| ETH/BTC |') == 1 assert generate_edge_table(results).count( '| Risk Reward Ratio | Required Risk Reward | Expectancy |') == 1 + + +def test_generate_periodic_breakdown_stats(testdatadir): + filename = testdatadir / "backtest-result_new.json" + bt_data = load_backtest_data(filename).to_dict(orient='records') + + res = generate_periodic_breakdown_stats(bt_data, 'day') + assert isinstance(res, list) + assert len(res) == 21 + day = res[0] + assert 'date' in day + assert 'draws' in day + assert 'loses' in day + assert 'wins' in day + assert 'profit_abs' in day + + # Select empty dataframe! + res = generate_periodic_breakdown_stats([], 'day') + assert res == [] + + +def test__get_resample_from_period(): + + assert _get_resample_from_period('day') == '1d' + assert _get_resample_from_period('week') == '1w' + assert _get_resample_from_period('month') == '1M' + with pytest.raises(ValueError, match=r"Period noooo is not supported."): + _get_resample_from_period('noooo') From e458c9867a54991ac5067f517fe2d088187e7674 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 21 Oct 2021 07:42:19 +0200 Subject: [PATCH 142/144] Styling fixes --- freqtrade/commands/arguments.py | 3 ++- freqtrade/optimize/optimize_reports.py | 6 +++--- tests/optimize/test_optimize_reports.py | 4 +++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 53cdda95d..87ef49a9b 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -23,7 +23,8 @@ ARGS_COMMON_OPTIMIZE = ["timeframe", "timerange", "dataformat_ohlcv", ARGS_BACKTEST = ARGS_COMMON_OPTIMIZE + ["position_stacking", "use_max_market_positions", "enable_protections", "dry_run_wallet", "timeframe_detail", - "strategy_list", "export", "exportfilename", "backtest_breakdown"] + "strategy_list", "export", "exportfilename", + "backtest_breakdown"] ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + ["hyperopt", "hyperopt_path", "position_stacking", "use_max_market_positions", diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index a2590c10b..96549316d 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -228,10 +228,10 @@ def generate_periodic_breakdown_stats(trade_list: List, period: str) -> List[Dic if len(results) == 0: return [] results['close_date'] = to_datetime(results['close_date'], utc=True) - resample = _get_resample_from_period(period) - period = results.resample(resample, on='close_date') + resample_period = _get_resample_from_period(period) + resampled = results.resample(resample_period, on='close_date') stats = [] - for name, day in period: + for name, day in resampled: profit_abs = day['profit_abs'].sum().round(10) wins = sum(day['profit_abs'] > 0) draws = sum(day['profit_abs'] == 0) diff --git a/tests/optimize/test_optimize_reports.py b/tests/optimize/test_optimize_reports.py index 4bf20e547..b5eb09923 100644 --- a/tests/optimize/test_optimize_reports.py +++ b/tests/optimize/test_optimize_reports.py @@ -15,7 +15,9 @@ from freqtrade.edge import PairInfo from freqtrade.enums import SellType from freqtrade.optimize.optimize_reports import (_get_resample_from_period, generate_backtest_stats, generate_daily_stats, generate_edge_table, - generate_pair_metrics, generate_periodic_breakdown_stats, generate_sell_reason_stats, + generate_pair_metrics, + generate_periodic_breakdown_stats, + generate_sell_reason_stats, generate_strategy_comparison, generate_trading_stats, store_backtest_stats, text_table_bt_results, text_table_sell_reason, From 053fb076e42124285ee9cedf5848a0da2f1b5f75 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 21 Oct 2021 10:56:18 +0200 Subject: [PATCH 143/144] Add documentation for breakdown command --- docs/backtesting.md | 34 ++++++++++++++++++++++++++++++---- docs/utils.md | 3 +++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/docs/backtesting.md b/docs/backtesting.md index 4a9532894..37724b02a 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -21,6 +21,7 @@ usage: freqtrade backtesting [-h] [-v] [--logfile FILE] [-V] [-c PATH] [--timeframe-detail TIMEFRAME_DETAIL] [--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]] [--export {none,trades}] [--export-filename PATH] + [--breakdown {day,week,month} [{day,week,month} ...]] optional arguments: -h, --help show this help message and exit @@ -30,7 +31,7 @@ optional arguments: Specify what timerange of data to use. --data-format-ohlcv {json,jsongz,hdf5} Storage format for downloaded candle (OHLCV) data. - (default: `None`). + (default: `json`). --max-open-trades INT Override the value of the `max_open_trades` configuration setting. @@ -65,8 +66,7 @@ optional arguments: set either in config or via command line. When using this together with `--export trades`, the strategy- name is injected into the filename (so `backtest- - data.json` becomes `backtest-data- - SampleStrategy.json` + data.json` becomes `backtest-data-SampleStrategy.json` --export {none,trades} Export backtest results (default: trades). --export-filename PATH @@ -74,7 +74,8 @@ optional arguments: Requires `--export` to be set as well. Example: `--export-filename=user_data/backtest_results/backtest _today.json` - --show-days Print a days breakdown table of the backtest results + --breakdown {day,week,month} [{day,week,month} ...] + Show backtesting breakdown per [day, week, month]. Common arguments: -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). @@ -430,6 +431,31 @@ It contains some useful key metrics about performance of your strategy on backte - `Drawdown Start` / `Drawdown End`: Start and end datetime for this largest drawdown (can also be visualized via the `plot-dataframe` sub-command). - `Market change`: Change of the market during the backtest period. Calculated as average of all pairs changes from the first to the last candle using the "close" column. +### Daily / Weekly / Monthly breakdown + +You can get an overview over daily / weekly or monthly results by using the `--breakdown <>` switch. + +To visualize daily and weekly breakdowns, you can use the following: + +``` bash +freqtrade backtesting --strategy MyAwesomeStrategy --breakdown day month +``` + +``` output +======================== DAY BREAKDOWN ========================= +| Day | Tot Profit USDT | Wins | Draws | Losses | +|------------+-------------------+--------+---------+----------| +| 03/07/2021 | 200.0 | 2 | 0 | 0 | +| 04/07/2021 | -50.31 | 0 | 0 | 2 | +| 05/07/2021 | 220.611 | 3 | 2 | 0 | +| 06/07/2021 | 150.974 | 3 | 0 | 2 | +| 07/07/2021 | -70.193 | 1 | 0 | 2 | +| 08/07/2021 | 212.413 | 2 | 0 | 3 | + +``` + +The output will show a table containing the realized absolute Profit (in stake currency) for the given timeperiod, as well as wins, draws and losses that materialized (closed) on this day. + ### Further backtest-result analysis To further analyze your backtest results, you can [export the trades](#exporting-trades-to-file). diff --git a/docs/utils.md b/docs/utils.md index d8fbcacb7..4845828ab 100644 --- a/docs/utils.md +++ b/docs/utils.md @@ -667,6 +667,7 @@ usage: freqtrade hyperopt-show [-h] [-v] [--logfile FILE] [-V] [-c PATH] [--profitable] [-n INT] [--print-json] [--hyperopt-filename FILENAME] [--no-header] [--disable-param-export] + [--breakdown {day,week,month} [{day,week,month} ...]] optional arguments: -h, --help show this help message and exit @@ -680,6 +681,8 @@ optional arguments: --no-header Do not print epoch details header. --disable-param-export Disable automatic hyperopt parameter export. + --breakdown {day,week,month} [{day,week,month} ...] + Show backtesting breakdown per [day, week, month]. Common arguments: -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). From fde10f5395993179be0833ff6c26ac4af74eae98 Mon Sep 17 00:00:00 2001 From: Simon Ebner Date: Sat, 23 Oct 2021 12:25:09 +0200 Subject: [PATCH 144/144] Use pathlib.stem instead of str(x).ends_with --- freqtrade/resolvers/iresolver.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/resolvers/iresolver.py b/freqtrade/resolvers/iresolver.py index 2cccec70a..c6f97c976 100644 --- a/freqtrade/resolvers/iresolver.py +++ b/freqtrade/resolvers/iresolver.py @@ -91,7 +91,7 @@ class IResolver: logger.debug(f"Searching for {cls.object_type.__name__} {object_name} in '{directory}'") for entry in directory.iterdir(): # Only consider python files - if not str(entry).endswith('.py'): + if entry.suffix != '.py': logger.debug('Ignoring %s', entry) continue if entry.is_symlink() and not entry.is_file(): @@ -169,7 +169,7 @@ class IResolver: objects = [] for entry in directory.iterdir(): # Only consider python files - if not str(entry).endswith('.py'): + if entry.suffix != '.py': logger.debug('Ignoring %s', entry) continue module_path = entry.resolve()