diff --git a/docs/advanced-setup.md b/docs/advanced-setup.md index f03bc10c0..02b0307e5 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. +``` yml +--- +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:8080" + # 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. 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 Copy the `freqtrade.service` file to your systemd user directory (usually `~/.config/systemd/user`) and update `WorkingDirectory` and `ExecStart` to match your setup. diff --git a/docs/backtesting.md b/docs/backtesting.md index 66e682745..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,6 +74,8 @@ optional arguments: Requires `--export` to be set as well. Example: `--export-filename=user_data/backtest_results/backtest _today.json` + --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). @@ -429,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/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/docs/utils.md b/docs/utils.md index d8fbcacb7..a65ba5db4 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 @@ -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). diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 86d7a1923..676e59282 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"] + "strategy_list", "export", "exportfilename", + "backtest_breakdown"] ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + ["hyperopt", "hyperopt_path", "position_stacking", "use_max_market_positions", @@ -89,7 +90,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", "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 b60692c67..8d9b28c40 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -193,6 +193,12 @@ AVAILABLE_CLI_OPTIONS = { type=float, metavar='FLOAT', ), + "backtest_breakdown": Arg( + '--breakdown', + help='Show backtesting breakdown per [day, week, month].', + nargs='+', + choices=constants.BACKTEST_BREAKDOWNS + ), # Edge "stoploss_range": Arg( '--stoplosses', diff --git a/freqtrade/commands/hyperopt_commands.py b/freqtrade/commands/hyperopt_commands.py index 614c4b3f5..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']) + 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 5db3379d2..822577916 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -269,8 +269,12 @@ class Configuration: self._args_to_config(config, argname='export', logstring='Parameter --export detected: {} ...') + self._args_to_config(config, argname='backtest_breakdown', + logstring='Parameter --breakdown detected ...') + self._args_to_config(config, argname='disableparamexport', logstring='Parameter --disableparamexport 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/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/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.", } 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 diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index 6e0926660..7fb6a14a0 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 @@ -263,7 +263,6 @@ def generate_strategy_comparison(all_results: Dict) -> List[Dict]: 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', @@ -288,6 +287,41 @@ def generate_edge_table(results: dict) -> str: floatfmt=floatfmt, tablefmt="orgtbl", stralign="right") # type: ignore +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) + if len(results) == 0: + return [] + results['close_date'] = to_datetime(results['close_date'], utc=True) + resample_period = _get_resample_from_period(period) + resampled = results.resample(resample_period, on='close_date') + stats = [] + 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) + loses = sum(day['profit_abs'] < 0) + stats.append( + { + 'date': name.strftime('%d/%m/%Y'), + 'profit_abs': profit_abs, + 'wins': wins, + 'draws': draws, + 'loses': loses + } + ) + return stats + + def generate_trading_stats(results: DataFrame) -> Dict[str, Any]: """ Generate overall trade statistics """ if len(results) == 0: @@ -407,7 +441,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']], @@ -417,6 +451,8 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame], 'results_per_buy_tag': buy_tag_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, @@ -433,7 +469,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'], @@ -616,6 +652,28 @@ def text_table_tags(tag_type: str, tag_results: List[Dict[str, Any]], stake_curr floatfmt=floatfmt, tablefmt="orgtbl", stralign="right") +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 + :param stake_currency: Stakecurrency used + :return: pretty printed table with tabulate as string + """ + headers = [ + period.capitalize(), + f'Tot Profit {stake_currency}', + 'Wins', + 'Draws', + 'Losses', + ] + output = [[ + 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") + + def text_table_strategy(strategy_results, stake_currency: str) -> str: """ Generate summary table per strategy @@ -667,7 +725,10 @@ def text_table_add_metrics(strat_results: Dict) -> str: strat_results['stake_currency'])), ('Absolute profit ', round_coin_value(strat_results['profit_total_abs'], strat_results['stake_currency'])), - ('Total profit %', f"{round(strat_results['profit_total'] * 100, 2):}%"), + ('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'], @@ -724,7 +785,8 @@ def text_table_add_metrics(strat_results: Dict) -> str: return message -def show_backtest_result(strategy: str, results: Dict[str, Any], stake_currency: str): +def show_backtest_result(strategy: str, results: Dict[str, Any], stake_currency: str, + backtest_breakdown=[]): """ Print results for one strategy """ @@ -756,6 +818,15 @@ def show_backtest_result(strategy: str, results: Dict[str, Any], stake_currency: print(' LEFT OPEN TRADES REPORT '.center(len(table.splitlines()[0]), '=')) print(table) + 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(f' {period.upper()} 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]), '=')) @@ -771,7 +842,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) + show_backtest_result( + strategy, results, stake_currency, + config.get('backtest_breakdown', [])) if len(backtest_stats['strategy']) > 1: # Print Strategy summary table 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/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 9d3ca01a9..ab7aa74a1 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -1104,6 +1104,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', @@ -1132,6 +1133,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..b5eb09923 100644 --- a/tests/optimize/test_optimize_reports.py +++ b/tests/optimize/test_optimize_reports.py @@ -13,8 +13,10 @@ 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, +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, @@ -377,3 +379,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') 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,