Improve tests for backtest protections
This commit is contained in:
parent
5849d07497
commit
effc96e92b
@ -121,8 +121,8 @@ The base-class provides an instance of the exchange (`self._exchange`) the pairl
|
|||||||
self._pairlist_pos = pairlist_pos
|
self._pairlist_pos = pairlist_pos
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! Note
|
!!! Tip
|
||||||
You'll need to register your pairlist in `constants.py` under the variable `AVAILABLE_PAIRLISTS` - otherwise it will not be selectable.
|
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:
|
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"
|
!!! Tip "Writing a new Protection"
|
||||||
Best copy one of the existing Protections to have a good example.
|
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
|
#### Implementation of a new protection
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
## Protections
|
## Protections
|
||||||
|
|
||||||
!!! Warning "Beta feature"
|
!!! 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.
|
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.
|
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).
|
Each Protection can be configured multiple times with different parameters, to allow different levels of protection (short-term / long-term).
|
||||||
|
|
||||||
!!! Note "Backtesting"
|
!!! 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
|
### Available Protections
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
from cachetools import TTLCache, cached
|
from cachetools import TTLCache, cached
|
||||||
|
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
# flake8: noqa: F401
|
|
||||||
# from freqtrade.plugins.protectionmanager import ProtectionManager
|
|
@ -3,8 +3,6 @@ import logging
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
from sqlalchemy import and_, or_
|
|
||||||
|
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
from freqtrade.plugins.protections import IProtection, ProtectionReturn
|
from freqtrade.plugins.protections import IProtection, ProtectionReturn
|
||||||
from freqtrade.strategy.interface import SellType
|
from freqtrade.strategy.interface import SellType
|
||||||
|
@ -79,7 +79,7 @@ def load_data_test(what, testdatadir):
|
|||||||
fill_missing=True)}
|
fill_missing=True)}
|
||||||
|
|
||||||
|
|
||||||
def simple_backtest(config, contour, num_results, mocker, testdatadir) -> None:
|
def simple_backtest(config, contour, mocker, testdatadir) -> None:
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
config['timeframe'] = '1m'
|
config['timeframe'] = '1m'
|
||||||
backtesting = Backtesting(config)
|
backtesting = Backtesting(config)
|
||||||
@ -98,7 +98,7 @@ def simple_backtest(config, contour, num_results, mocker, testdatadir) -> None:
|
|||||||
enable_protections=config.get('enable_protections', False),
|
enable_protections=config.get('enable_protections', False),
|
||||||
)
|
)
|
||||||
# results :: <class 'pandas.core.frame.DataFrame'>
|
# results :: <class 'pandas.core.frame.DataFrame'>
|
||||||
assert len(results) == num_results
|
return results
|
||||||
|
|
||||||
|
|
||||||
# FIX: fixturize this?
|
# FIX: fixturize this?
|
||||||
@ -532,23 +532,9 @@ def test_processed(default_conf, mocker, testdatadir) -> None:
|
|||||||
assert col in cols
|
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:
|
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'] = [
|
default_conf['protections'] = [
|
||||||
{
|
{
|
||||||
"method": "CooldownPeriod",
|
"method": "CooldownPeriod",
|
||||||
@ -567,7 +553,31 @@ def test_backtest_pricecontours_protections(default_conf, fee, mocker, testdatad
|
|||||||
# While buy-signals are unrealistic, running backtesting
|
# While buy-signals are unrealistic, running backtesting
|
||||||
# over and over again should not cause different results
|
# over and over again should not cause different results
|
||||||
for [contour, numres] in tests:
|
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):
|
def test_backtest_clash_buy_sell(mocker, default_conf, testdatadir):
|
||||||
|
@ -76,7 +76,8 @@ def test_generate_backtest_stats(default_conf, testdatadir):
|
|||||||
"sell_reason": [SellType.ROI, SellType.STOP_LOSS,
|
"sell_reason": [SellType.ROI, SellType.STOP_LOSS,
|
||||||
SellType.ROI, SellType.FORCE_SELL]
|
SellType.ROI, SellType.FORCE_SELL]
|
||||||
}),
|
}),
|
||||||
'config': default_conf}
|
'config': default_conf,
|
||||||
|
'locks': []}
|
||||||
}
|
}
|
||||||
timerange = TimeRange.parse_timerange('1510688220-1510700340')
|
timerange = TimeRange.parse_timerange('1510688220-1510700340')
|
||||||
min_date = Arrow.fromtimestamp(1510688220)
|
min_date = Arrow.fromtimestamp(1510688220)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import random
|
import random
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user