diff --git a/docs/developer.md b/docs/developer.md index f1d658ab8..48b021027 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -121,8 +121,8 @@ The base-class provides an instance of the exchange (`self._exchange`) the pairl self._pairlist_pos = pairlist_pos ``` -!!! Note - You'll need to register your pairlist in `constants.py` under the variable `AVAILABLE_PAIRLISTS` - otherwise it will not be selectable. +!!! Tip + Don't forget to register your pairlist in `constants.py` under the variable `AVAILABLE_PAIRLISTS` - otherwise it will not be selectable. Now, let's step through the methods which require actions: @@ -184,6 +184,7 @@ No protection should use datetime directly, but use the provided `date_now` vari !!! Tip "Writing a new Protection" Best copy one of the existing Protections to have a good example. + Don't forget to register your protection in `constants.py` under the variable `AVAILABLE_PROTECTIONS` - otherwise it will not be selectable. #### Implementation of a new protection diff --git a/docs/includes/protections.md b/docs/includes/protections.md index 351cfcac3..a8caf55b1 100644 --- a/docs/includes/protections.md +++ b/docs/includes/protections.md @@ -1,7 +1,7 @@ ## Protections !!! Warning "Beta feature" - This feature is still in it's testing phase. Should you notice something you think is wrong please let us know via Discord, Slack or via Issue. + This feature is still in it's testing phase. Should you notice something you think is wrong please let us know via Discord, Slack or via Github Issue. Protections will protect your strategy from unexpected events and market conditions by temporarily stop trading for either one pair, or for all pairs. All protection end times are rounded up to the next candle to avoid sudden, unexpected intra-candle buys. @@ -13,7 +13,7 @@ All protection end times are rounded up to the next candle to avoid sudden, unex Each Protection can be configured multiple times with different parameters, to allow different levels of protection (short-term / long-term). !!! Note "Backtesting" - Protections are supported by backtesting and hyperopt, but must be enabled by using the `--enable-protections` flag. + Protections are supported by backtesting and hyperopt, but must be explicitly enabled by using the `--enable-protections` flag. ### Available Protections diff --git a/freqtrade/mixins/logging_mixin.py b/freqtrade/mixins/logging_mixin.py index e9921e1ec..2e1c20a52 100644 --- a/freqtrade/mixins/logging_mixin.py +++ b/freqtrade/mixins/logging_mixin.py @@ -1,5 +1,3 @@ - - from typing import Callable from cachetools import TTLCache, cached diff --git a/freqtrade/plugins/__init__.py b/freqtrade/plugins/__init__.py index 96943268b..e69de29bb 100644 --- a/freqtrade/plugins/__init__.py +++ b/freqtrade/plugins/__init__.py @@ -1,2 +0,0 @@ -# flake8: noqa: F401 -# from freqtrade.plugins.protectionmanager import ProtectionManager diff --git a/freqtrade/plugins/protections/stoploss_guard.py b/freqtrade/plugins/protections/stoploss_guard.py index 71e74880c..193907ddc 100644 --- a/freqtrade/plugins/protections/stoploss_guard.py +++ b/freqtrade/plugins/protections/stoploss_guard.py @@ -3,8 +3,6 @@ import logging from datetime import datetime, timedelta from typing import Any, Dict -from sqlalchemy import and_, or_ - from freqtrade.persistence import Trade from freqtrade.plugins.protections import IProtection, ProtectionReturn from freqtrade.strategy.interface import SellType diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 15ad18bf9..547e55db8 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -79,7 +79,7 @@ def load_data_test(what, testdatadir): fill_missing=True)} -def simple_backtest(config, contour, num_results, mocker, testdatadir) -> None: +def simple_backtest(config, contour, mocker, testdatadir) -> None: patch_exchange(mocker) config['timeframe'] = '1m' backtesting = Backtesting(config) @@ -98,7 +98,7 @@ def simple_backtest(config, contour, num_results, mocker, testdatadir) -> None: enable_protections=config.get('enable_protections', False), ) # results :: - assert len(results) == num_results + return results # FIX: fixturize this? @@ -532,23 +532,9 @@ def test_processed(default_conf, mocker, testdatadir) -> None: assert col in cols -def test_backtest_pricecontours(default_conf, fee, mocker, testdatadir) -> None: - mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) - tests = [ - ['sine', 35], - ['raise', 19], - ['lower', 0], - ['sine', 35], - ['raise', 19] - ] - # While buy-signals are unrealistic, running backtesting - # over and over again should not cause different results - for [contour, numres] in tests: - simple_backtest(default_conf, contour, numres, mocker, testdatadir) - - def test_backtest_pricecontours_protections(default_conf, fee, mocker, testdatadir) -> None: - # TODO: Evaluate usefullness of this, the patterns and buy-signls are unrealistic + # While this test IS a copy of test_backtest_pricecontours, it's needed to ensure + # results do not carry-over to the next run, which is not given by using parametrize. default_conf['protections'] = [ { "method": "CooldownPeriod", @@ -567,7 +553,31 @@ def test_backtest_pricecontours_protections(default_conf, fee, mocker, testdatad # While buy-signals are unrealistic, running backtesting # over and over again should not cause different results for [contour, numres] in tests: - simple_backtest(default_conf, contour, numres, mocker, testdatadir) + assert len(simple_backtest(default_conf, contour, mocker, testdatadir)) == numres + + +@pytest.mark.parametrize('protections,contour,expected', [ + (None, 'sine', 35), + (None, 'raise', 19), + (None, 'lower', 0), + (None, 'sine', 35), + (None, 'raise', 19), + ([{"method": "CooldownPeriod", "stop_duration": 3}], 'sine', 9), + ([{"method": "CooldownPeriod", "stop_duration": 3}], 'raise', 10), + ([{"method": "CooldownPeriod", "stop_duration": 3}], 'lower', 0), + ([{"method": "CooldownPeriod", "stop_duration": 3}], 'sine', 9), + ([{"method": "CooldownPeriod", "stop_duration": 3}], 'raise', 10), +]) +def test_backtest_pricecontours(default_conf, fee, mocker, testdatadir, + protections, contour, expected) -> None: + if protections: + default_conf['protections'] = protections + default_conf['enable_protections'] = True + + mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) + # While buy-signals are unrealistic, running backtesting + # over and over again should not cause different results + assert len(simple_backtest(default_conf, contour, mocker, testdatadir)) == expected def test_backtest_clash_buy_sell(mocker, default_conf, testdatadir): diff --git a/tests/optimize/test_optimize_reports.py b/tests/optimize/test_optimize_reports.py index d04929164..a0e1932ff 100644 --- a/tests/optimize/test_optimize_reports.py +++ b/tests/optimize/test_optimize_reports.py @@ -76,7 +76,8 @@ def test_generate_backtest_stats(default_conf, testdatadir): "sell_reason": [SellType.ROI, SellType.STOP_LOSS, SellType.ROI, SellType.FORCE_SELL] }), - 'config': default_conf} + 'config': default_conf, + 'locks': []} } timerange = TimeRange.parse_timerange('1510688220-1510700340') min_date = Arrow.fromtimestamp(1510688220) diff --git a/tests/plugins/test_protections.py b/tests/plugins/test_protections.py index e997c5526..2ad03a97c 100644 --- a/tests/plugins/test_protections.py +++ b/tests/plugins/test_protections.py @@ -1,5 +1,5 @@ import random -from datetime import datetime, timedelta, timezone +from datetime import datetime, timedelta import pytest