Update docs, add test
This commit is contained in:
parent
8c03ebb78f
commit
3adda84b96
@ -22,23 +22,19 @@ DataFrame of the candles that resulted in buy signals. Depending on how many buy
|
|||||||
makes, this file may get quite large, so periodically check your `user_data/backtest_results`
|
makes, this file may get quite large, so periodically check your `user_data/backtest_results`
|
||||||
folder to delete old exports.
|
folder to delete old exports.
|
||||||
|
|
||||||
To analyze the buy tags, we need to use the `buy_reasons.py` script from
|
|
||||||
[froggleston's repo](https://github.com/froggleston/freqtrade-buyreasons). Follow the instructions
|
|
||||||
in their README to copy the script into your `freqtrade/scripts/` folder.
|
|
||||||
|
|
||||||
Before running your next backtest, make sure you either delete your old backtest results or run
|
Before running your next backtest, make sure you either delete your old backtest results or run
|
||||||
backtesting with the `--cache none` option to make sure no cached results are used.
|
backtesting with the `--cache none` option to make sure no cached results are used.
|
||||||
|
|
||||||
If all goes well, you should now see a `backtest-result-{timestamp}_signals.pkl` file in the
|
If all goes well, you should now see a `backtest-result-{timestamp}_signals.pkl` file in the
|
||||||
`user_data/backtest_results` folder.
|
`user_data/backtest_results` folder.
|
||||||
|
|
||||||
Now run the `buy_reasons.py` script, supplying a few options:
|
To analyze the entry/exit tags, we now need to use the `freqtrade analysis` command:
|
||||||
|
|
||||||
``` bash
|
``` bash
|
||||||
python3 scripts/buy_reasons.py -c <config.json> -s <strategy_name> -t <timerange> -g0,1,2,3,4
|
freqtrade analysis -c <config.json> -s <strategy_name> --analysis_groups 0,1,2,3,4
|
||||||
```
|
```
|
||||||
|
|
||||||
The `-g` option is used to specify the various tabular outputs, ranging from the simplest (0)
|
The `--analysis_groups` option is used to specify the various tabular outputs, ranging from the simplest (0)
|
||||||
to the most detailed per pair, per buy and per sell tag (4). More options are available by
|
to the most detailed per pair, per buy and per sell tag (4). More options are available by
|
||||||
running with the `-h` option.
|
running with the `-h` option.
|
||||||
|
|
||||||
@ -54,18 +50,18 @@ To show only certain buy and sell tags in the displayed output, use the followin
|
|||||||
For example:
|
For example:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 scripts/buy_reasons.py -c <config.json> -s <strategy_name> -t <timerange> -g0,1,2,3,4 --enter_reason_list "enter_tag_a,enter_tag_b" --exit_reason_list "roi,custom_exit_tag_a,stop_loss"
|
freqtrade analysis -c <config.json> -s <strategy_name> --analysis_groups 0,1,2,3,4 --enter_reason_list "enter_tag_a,enter_tag_b" --exit_reason_list "roi,custom_exit_tag_a,stop_loss"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Outputting signal candle indicators
|
### Outputting signal candle indicators
|
||||||
|
|
||||||
The real power of the buy_reasons.py script comes from the ability to print out the indicator
|
The real power of `freqtrade analysis` comes from the ability to print out the indicator
|
||||||
values present on signal candles to allow fine-grained investigation and tuning of buy signal
|
values present on signal candles to allow fine-grained investigation and tuning of buy signal
|
||||||
indicators. To print out a column for a given set of indicators, use the `--indicator-list`
|
indicators. To print out a column for a given set of indicators, use the `--indicator-list`
|
||||||
option:
|
option:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 scripts/buy_reasons.py -c <config.json> -s <strategy_name> -t <timerange> -g0,1,2,3,4 --enter_reason_list "enter_tag_a,enter_tag_b" --exit_reason_list "roi,custom_exit_tag_a,stop_loss" --indicator_list "rsi,rsi_1h,bb_lowerband,ema_9,macd,macdsignal"
|
freqtrade analysis -c <config.json> -s <strategy_name> --analysis_groups 0,1,2,3,4 --enter_reason_list "enter_tag_a,enter_tag_b" --exit_reason_list "roi,custom_exit_tag_a,stop_loss" --indicator_list "rsi,rsi_1h,bb_lowerband,ema_9,macd,macdsignal"
|
||||||
```
|
```
|
||||||
|
|
||||||
The indicators have to be present in your strategy's main DataFrame (either for your main
|
The indicators have to be present in your strategy's main DataFrame (either for your main
|
||||||
|
@ -52,8 +52,6 @@ def start_analysis_entries_exits(args: Dict[str, Any]) -> None:
|
|||||||
# Initialize configuration
|
# Initialize configuration
|
||||||
config = setup_analyze_configuration(args, RunMode.BACKTEST)
|
config = setup_analyze_configuration(args, RunMode.BACKTEST)
|
||||||
|
|
||||||
print(config)
|
|
||||||
|
|
||||||
logger.info('Starting freqtrade in analysis mode')
|
logger.info('Starting freqtrade in analysis mode')
|
||||||
|
|
||||||
process_entry_exit_reasons(Path(config['user_data_dir'], 'backtest_results'),
|
process_entry_exit_reasons(Path(config['user_data_dir'], 'backtest_results'),
|
||||||
|
@ -15,10 +15,18 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def _load_signal_candles(backtest_dir: Path):
|
def _load_signal_candles(backtest_dir: Path):
|
||||||
scpf = Path(backtest_dir,
|
|
||||||
os.path.splitext(
|
if backtest_dir.is_dir():
|
||||||
get_latest_backtest_filename(backtest_dir))[0] + "_signals.pkl"
|
scpf = Path(backtest_dir,
|
||||||
)
|
os.path.splitext(
|
||||||
|
get_latest_backtest_filename(backtest_dir))[0] + "_signals.pkl"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
scpf = Path(os.path.splitext(
|
||||||
|
get_latest_backtest_filename(backtest_dir))[0] + "_signals.pkl"
|
||||||
|
)
|
||||||
|
|
||||||
|
print(scpf)
|
||||||
try:
|
try:
|
||||||
scp = open(scpf, "rb")
|
scp = open(scpf, "rb")
|
||||||
signal_candles = joblib.load(scp)
|
signal_candles = joblib.load(scp)
|
||||||
@ -246,7 +254,6 @@ def process_entry_exit_reasons(backtest_dir: Path,
|
|||||||
signal_candles = _load_signal_candles(backtest_dir)
|
signal_candles = _load_signal_candles(backtest_dir)
|
||||||
analysed_trades_dict = _process_candles_and_indicators(pairlist, strategy_name,
|
analysed_trades_dict = _process_candles_and_indicators(pairlist, strategy_name,
|
||||||
trades, signal_candles)
|
trades, signal_candles)
|
||||||
|
|
||||||
_print_results(analysed_trades_dict,
|
_print_results(analysed_trades_dict,
|
||||||
strategy_name,
|
strategy_name,
|
||||||
analysis_groups,
|
analysis_groups,
|
||||||
|
94
tests/data/test_entryexitanalysis.py
Executable file
94
tests/data/test_entryexitanalysis.py
Executable file
@ -0,0 +1,94 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
from unittest.mock import MagicMock, PropertyMock
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
from freqtrade.commands.analyze_commands import start_analysis_entries_exits
|
||||||
|
from freqtrade.commands.optimize_commands import start_backtesting
|
||||||
|
from freqtrade.enums import ExitType
|
||||||
|
from tests.conftest import get_args, patch_exchange, patched_configuration_load_config_file
|
||||||
|
|
||||||
|
|
||||||
|
def test_backtest_analysis_nomock(default_conf, mocker, caplog, testdatadir, capsys):
|
||||||
|
default_conf.update({
|
||||||
|
"use_exit_signal": True,
|
||||||
|
"exit_profit_only": False,
|
||||||
|
"exit_profit_offset": 0.0,
|
||||||
|
"ignore_roi_if_entry_signal": False,
|
||||||
|
'analysis_groups': "0",
|
||||||
|
'enter_reason_list': "all",
|
||||||
|
'exit_reason_list': "all",
|
||||||
|
'indicator_list': "bb_upperband,ema_10"
|
||||||
|
})
|
||||||
|
patch_exchange(mocker)
|
||||||
|
result1 = pd.DataFrame({'pair': ['ETH/BTC', 'LTC/BTC'],
|
||||||
|
'profit_ratio': [0.0, 0.0],
|
||||||
|
'profit_abs': [0.0, 0.0],
|
||||||
|
'open_date': pd.to_datetime(['2018-01-29 18:40:00',
|
||||||
|
'2018-01-30 03:30:00', ], utc=True
|
||||||
|
),
|
||||||
|
'close_date': pd.to_datetime(['2018-01-29 20:45:00',
|
||||||
|
'2018-01-30 05:35:00', ], utc=True),
|
||||||
|
'trade_duration': [235, 40],
|
||||||
|
'is_open': [False, False],
|
||||||
|
'stake_amount': [0.01, 0.01],
|
||||||
|
'open_rate': [0.104445, 0.10302485],
|
||||||
|
'close_rate': [0.104969, 0.103541],
|
||||||
|
"is_short": [False, False],
|
||||||
|
'enter_tag': ["enter_tag_long", "enter_tag_long"],
|
||||||
|
'exit_reason': [ExitType.ROI, ExitType.ROI]
|
||||||
|
})
|
||||||
|
|
||||||
|
backtestmock = MagicMock(side_effect=[
|
||||||
|
{
|
||||||
|
'results': result1,
|
||||||
|
'config': default_conf,
|
||||||
|
'locks': [],
|
||||||
|
'rejected_signals': 20,
|
||||||
|
'timedout_entry_orders': 0,
|
||||||
|
'timedout_exit_orders': 0,
|
||||||
|
'canceled_trade_entries': 0,
|
||||||
|
'canceled_entry_orders': 0,
|
||||||
|
'replaced_entry_orders': 0,
|
||||||
|
'final_balance': 1000,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist',
|
||||||
|
PropertyMock(return_value=['ETH/BTC', 'LTC/BTC', 'DASH/BTC']))
|
||||||
|
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', backtestmock)
|
||||||
|
|
||||||
|
patched_configuration_load_config_file(mocker, default_conf)
|
||||||
|
|
||||||
|
args = [
|
||||||
|
'backtesting',
|
||||||
|
'--config', 'config.json',
|
||||||
|
'--datadir', str(testdatadir),
|
||||||
|
'--strategy-path', str(Path(__file__).parents[1] / 'strategy/strats'),
|
||||||
|
'--timeframe', '5m',
|
||||||
|
'--timerange', '1515560100-1517287800',
|
||||||
|
'--export', 'signals',
|
||||||
|
'--cache', 'none',
|
||||||
|
'--strategy-list',
|
||||||
|
'StrategyTestV3',
|
||||||
|
]
|
||||||
|
args = get_args(args)
|
||||||
|
start_backtesting(args)
|
||||||
|
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert 'BACKTESTING REPORT' in captured.out
|
||||||
|
assert 'EXIT REASON STATS' in captured.out
|
||||||
|
assert 'LEFT OPEN TRADES REPORT' in captured.out
|
||||||
|
|
||||||
|
args = [
|
||||||
|
'analysis',
|
||||||
|
'--config', 'config.json',
|
||||||
|
'--datadir', str(testdatadir),
|
||||||
|
'--analysis_groups', '0',
|
||||||
|
'--strategy',
|
||||||
|
'StrategyTestV3',
|
||||||
|
]
|
||||||
|
args = get_args(args)
|
||||||
|
start_analysis_entries_exits(args)
|
||||||
|
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert 'enter_tag_long' in captured.out
|
@ -143,12 +143,13 @@ class StrategyTestV3(IStrategy):
|
|||||||
(dataframe['adx'] > 65) &
|
(dataframe['adx'] > 65) &
|
||||||
(dataframe['plus_di'] > self.buy_plusdi.value)
|
(dataframe['plus_di'] > self.buy_plusdi.value)
|
||||||
),
|
),
|
||||||
'enter_long'] = 1
|
['enter_long', 'enter_tag']] = 1, 'enter_tag_long'
|
||||||
|
|
||||||
dataframe.loc[
|
dataframe.loc[
|
||||||
(
|
(
|
||||||
qtpylib.crossed_below(dataframe['rsi'], self.sell_rsi.value)
|
qtpylib.crossed_below(dataframe['rsi'], self.sell_rsi.value)
|
||||||
),
|
),
|
||||||
'enter_short'] = 1
|
['enter_short', 'enter_tag']] = 1, 'enter_tag_short'
|
||||||
|
|
||||||
return dataframe
|
return dataframe
|
||||||
|
|
||||||
@ -166,13 +167,13 @@ class StrategyTestV3(IStrategy):
|
|||||||
(dataframe['adx'] > 70) &
|
(dataframe['adx'] > 70) &
|
||||||
(dataframe['minus_di'] > self.sell_minusdi.value)
|
(dataframe['minus_di'] > self.sell_minusdi.value)
|
||||||
),
|
),
|
||||||
'exit_long'] = 1
|
['exit_long', 'exit_tag']] = 1, 'exit_tag_long'
|
||||||
|
|
||||||
dataframe.loc[
|
dataframe.loc[
|
||||||
(
|
(
|
||||||
qtpylib.crossed_above(dataframe['rsi'], self.buy_rsi.value)
|
qtpylib.crossed_above(dataframe['rsi'], self.buy_rsi.value)
|
||||||
),
|
),
|
||||||
'exit_short'] = 1
|
['exit_long', 'exit_tag']] = 1, 'exit_tag_short'
|
||||||
|
|
||||||
return dataframe
|
return dataframe
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user