Skip to content

Commit

Permalink
Update docs, add test
Browse files Browse the repository at this point in the history
  • Loading branch information
froggleston committed May 24, 2022
1 parent 8c03ebb commit 3adda84
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 21 deletions.
16 changes: 6 additions & 10 deletions docs/advanced-backtesting.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
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
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
`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
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
running with the `-h` option.

Expand All @@ -54,18 +50,18 @@ To show only certain buy and sell tags in the displayed output, use the followin
For example:

```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

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
indicators. To print out a column for a given set of indicators, use the `--indicator-list`
option:

```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
Expand Down
2 changes: 0 additions & 2 deletions freqtrade/commands/analyze_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@ def start_analysis_entries_exits(args: Dict[str, Any]) -> None:
# Initialize configuration
config = setup_analyze_configuration(args, RunMode.BACKTEST)

print(config)

logger.info('Starting freqtrade in analysis mode')

process_entry_exit_reasons(Path(config['user_data_dir'], 'backtest_results'),
Expand Down
17 changes: 12 additions & 5 deletions freqtrade/data/entryexitanalysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,18 @@


def _load_signal_candles(backtest_dir: Path):
scpf = Path(backtest_dir,
os.path.splitext(
get_latest_backtest_filename(backtest_dir))[0] + "_signals.pkl"
)

if backtest_dir.is_dir():
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:
scp = open(scpf, "rb")
signal_candles = joblib.load(scp)
Expand Down Expand Up @@ -246,7 +254,6 @@ def process_entry_exit_reasons(backtest_dir: Path,
signal_candles = _load_signal_candles(backtest_dir)
analysed_trades_dict = _process_candles_and_indicators(pairlist, strategy_name,
trades, signal_candles)

_print_results(analysed_trades_dict,
strategy_name,
analysis_groups,
Expand Down
94 changes: 94 additions & 0 deletions tests/data/test_entryexitanalysis.py
Original file line number Diff line number Diff line change
@@ -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
9 changes: 5 additions & 4 deletions tests/strategy/strats/strategy_test_v3.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,12 +143,13 @@ def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFram
(dataframe['adx'] > 65) &
(dataframe['plus_di'] > self.buy_plusdi.value)
),
'enter_long'] = 1
['enter_long', 'enter_tag']] = 1, 'enter_tag_long'

dataframe.loc[
(
qtpylib.crossed_below(dataframe['rsi'], self.sell_rsi.value)
),
'enter_short'] = 1
['enter_short', 'enter_tag']] = 1, 'enter_tag_short'

return dataframe

Expand All @@ -166,13 +167,13 @@ def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame
(dataframe['adx'] > 70) &
(dataframe['minus_di'] > self.sell_minusdi.value)
),
'exit_long'] = 1
['exit_long', 'exit_tag']] = 1, 'exit_tag_long'

dataframe.loc[
(
qtpylib.crossed_above(dataframe['rsi'], self.buy_rsi.value)
),
'exit_short'] = 1
['exit_long', 'exit_tag']] = 1, 'exit_tag_short'

return dataframe

Expand Down

0 comments on commit 3adda84

Please sign in to comment.