mirror of
https://github.com/freqtrade/freqtrade.git
synced 2024-11-10 02:12:01 +00:00
Merge pull request #10485 from jainanuj94/feature/8902
Add exit signals to export in backtesting
This commit is contained in:
commit
611a3ce138
|
@ -18,15 +18,13 @@ freqtrade backtesting -c <config.json> --timeframe <tf> --strategy <strategy_nam
|
||||||
```
|
```
|
||||||
|
|
||||||
This will tell freqtrade to output a pickled dictionary of strategy, pairs and corresponding
|
This will tell freqtrade to output a pickled dictionary of strategy, pairs and corresponding
|
||||||
DataFrame of the candles that resulted in buy signals. Depending on how many buys your strategy
|
DataFrame of the candles that resulted in entry and exit signals.
|
||||||
makes, this file may get quite large, so periodically check your `user_data/backtest_results`
|
Depending on how many entries your strategy 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.
|
|
||||||
|
|
||||||
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` and `backtest-result-{timestamp}_exited.pkl` files in the `user_data/backtest_results` folder.
|
||||||
`user_data/backtest_results` folder.
|
|
||||||
|
|
||||||
To analyze the entry/exit tags, we now need to use the `freqtrade backtesting-analysis` command
|
To analyze the entry/exit tags, we now need to use the `freqtrade backtesting-analysis` command
|
||||||
with `--analysis-groups` option provided with space-separated arguments:
|
with `--analysis-groups` option provided with space-separated arguments:
|
||||||
|
@ -103,6 +101,10 @@ The indicators have to be present in your strategy's main DataFrame (either for
|
||||||
timeframe or for informative timeframes) otherwise they will simply be ignored in the script
|
timeframe or for informative timeframes) otherwise they will simply be ignored in the script
|
||||||
output.
|
output.
|
||||||
|
|
||||||
|
!!! Note "Indicator List"
|
||||||
|
The indicator values will be displayed for both entry and exit points. If `--indicator-list all` is specified,
|
||||||
|
only the indicators at the entry point will be shown to avoid excessively large lists, which could occur depending on the strategy.
|
||||||
|
|
||||||
There are a range of candle and trade-related fields that are included in the analysis so are
|
There are a range of candle and trade-related fields that are included in the analysis so are
|
||||||
automatically accessible by including them on the indicator-list, and these include:
|
automatically accessible by including them on the indicator-list, and these include:
|
||||||
|
|
||||||
|
@ -118,6 +120,34 @@ automatically accessible by including them on the indicator-list, and these incl
|
||||||
- **profit_ratio :** trade profit ratio
|
- **profit_ratio :** trade profit ratio
|
||||||
- **profit_abs :** absolute profit return of the trade
|
- **profit_abs :** absolute profit return of the trade
|
||||||
|
|
||||||
|
#### Sample Output for Indicator Values
|
||||||
|
|
||||||
|
```bash
|
||||||
|
freqtrade backtesting-analysis -c user_data/config.json --analysis-groups 0 --indicator-list chikou_span tenkan_sen
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example,
|
||||||
|
we aim to display the `chikou_span` and `tenkan_sen` indicator values at both the entry and exit points of trades.
|
||||||
|
|
||||||
|
A sample output for indicators might look like this:
|
||||||
|
|
||||||
|
| pair | open_date | enter_reason | exit_reason | chikou_span (entry) | tenkan_sen (entry) | chikou_span (exit) | tenkan_sen (exit) |
|
||||||
|
|-----------|---------------------------|--------------|-------------|---------------------|--------------------|--------------------|-------------------|
|
||||||
|
| DOGE/USDT | 2024-07-06 00:35:00+00:00 | | exit_signal | 0.105 | 0.106 | 0.105 | 0.107 |
|
||||||
|
| BTC/USDT | 2024-08-05 14:20:00+00:00 | | roi | 54643.440 | 51696.400 | 54386.000 | 52072.010 |
|
||||||
|
|
||||||
|
As shown in the table, `chikou_span (entry)` represents the indicator value at the time of trade entry,
|
||||||
|
while `chikou_span (exit)` reflects its value at the time of exit.
|
||||||
|
This detailed view of indicator values enhances the analysis.
|
||||||
|
|
||||||
|
The `(entry)` and `(exit)` suffixes are added to indicators
|
||||||
|
to distinguish the values at the entry and exit points of the trade.
|
||||||
|
|
||||||
|
!!! Note "Trade-wide Indicators"
|
||||||
|
Certain trade-wide indicators do not have the `(entry)` or `(exit)` suffix. These indicators include: `pair`, `stake_amount`,
|
||||||
|
`max_stake_amount`, `amount`, `open_date`, `close_date`, `open_rate`, `close_rate`, `fee_open`, `fee_close`, `trade_duration`,
|
||||||
|
`profit_ratio`, `profit_abs`, `exit_reason`,`initial_stop_loss_abs`, `initial_stop_loss_ratio`, `stop_loss_abs`, `stop_loss_ratio`,
|
||||||
|
`min_rate`, `max_rate`, `is_open`, `enter_tag`, `leverage`, `is_short`, `open_timestamp`, `close_timestamp` and `orders`
|
||||||
|
|
||||||
### Filtering the trade output by date
|
### Filtering the trade output by date
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List
|
from typing import Dict, List
|
||||||
|
|
||||||
import joblib
|
import joblib
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
@ -8,6 +8,7 @@ import pandas as pd
|
||||||
from freqtrade.configuration import TimeRange
|
from freqtrade.configuration import TimeRange
|
||||||
from freqtrade.constants import Config
|
from freqtrade.constants import Config
|
||||||
from freqtrade.data.btanalysis import (
|
from freqtrade.data.btanalysis import (
|
||||||
|
BT_DATA_COLUMNS,
|
||||||
get_latest_backtest_filename,
|
get_latest_backtest_filename,
|
||||||
load_backtest_data,
|
load_backtest_data,
|
||||||
load_backtest_stats,
|
load_backtest_stats,
|
||||||
|
@ -47,9 +48,14 @@ def _load_signal_candles(backtest_dir: Path):
|
||||||
return _load_backtest_analysis_data(backtest_dir, "signals")
|
return _load_backtest_analysis_data(backtest_dir, "signals")
|
||||||
|
|
||||||
|
|
||||||
def _process_candles_and_indicators(pairlist, strategy_name, trades, signal_candles):
|
def _load_exit_signal_candles(backtest_dir: Path) -> Dict[str, Dict[str, pd.DataFrame]]:
|
||||||
analysed_trades_dict = {}
|
return _load_backtest_analysis_data(backtest_dir, "exited")
|
||||||
analysed_trades_dict[strategy_name] = {}
|
|
||||||
|
|
||||||
|
def _process_candles_and_indicators(
|
||||||
|
pairlist, strategy_name, trades, signal_candles, date_col: str = "open_date"
|
||||||
|
):
|
||||||
|
analysed_trades_dict: Dict[str, Dict] = {strategy_name: {}}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logger.info(f"Processing {strategy_name} : {len(pairlist)} pairs")
|
logger.info(f"Processing {strategy_name} : {len(pairlist)} pairs")
|
||||||
|
@ -57,7 +63,7 @@ def _process_candles_and_indicators(pairlist, strategy_name, trades, signal_cand
|
||||||
for pair in pairlist:
|
for pair in pairlist:
|
||||||
if pair in signal_candles[strategy_name]:
|
if pair in signal_candles[strategy_name]:
|
||||||
analysed_trades_dict[strategy_name][pair] = _analyze_candles_and_indicators(
|
analysed_trades_dict[strategy_name][pair] = _analyze_candles_and_indicators(
|
||||||
pair, trades, signal_candles[strategy_name][pair]
|
pair, trades, signal_candles[strategy_name][pair], date_col
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Cannot process entry/exit reasons for {strategy_name}: ", e)
|
print(f"Cannot process entry/exit reasons for {strategy_name}: ", e)
|
||||||
|
@ -65,7 +71,9 @@ def _process_candles_and_indicators(pairlist, strategy_name, trades, signal_cand
|
||||||
return analysed_trades_dict
|
return analysed_trades_dict
|
||||||
|
|
||||||
|
|
||||||
def _analyze_candles_and_indicators(pair, trades: pd.DataFrame, signal_candles: pd.DataFrame):
|
def _analyze_candles_and_indicators(
|
||||||
|
pair: str, trades: pd.DataFrame, signal_candles: pd.DataFrame, date_col: str = "open_date"
|
||||||
|
) -> pd.DataFrame:
|
||||||
buyf = signal_candles
|
buyf = signal_candles
|
||||||
|
|
||||||
if len(buyf) > 0:
|
if len(buyf) > 0:
|
||||||
|
@ -75,8 +83,8 @@ def _analyze_candles_and_indicators(pair, trades: pd.DataFrame, signal_candles:
|
||||||
trades_inds = pd.DataFrame()
|
trades_inds = pd.DataFrame()
|
||||||
|
|
||||||
if trades_red.shape[0] > 0 and buyf.shape[0] > 0:
|
if trades_red.shape[0] > 0 and buyf.shape[0] > 0:
|
||||||
for t, v in trades_red.open_date.items():
|
for t, v in trades_red.iterrows():
|
||||||
allinds = buyf.loc[(buyf["date"] < v)]
|
allinds = buyf.loc[(buyf["date"] < v[date_col])]
|
||||||
if allinds.shape[0] > 0:
|
if allinds.shape[0] > 0:
|
||||||
tmp_inds = allinds.iloc[[-1]]
|
tmp_inds = allinds.iloc[[-1]]
|
||||||
|
|
||||||
|
@ -235,7 +243,7 @@ def _select_rows_by_tags(df, enter_reason_list, exit_reason_list):
|
||||||
|
|
||||||
def prepare_results(
|
def prepare_results(
|
||||||
analysed_trades, stratname, enter_reason_list, exit_reason_list, timerange=None
|
analysed_trades, stratname, enter_reason_list, exit_reason_list, timerange=None
|
||||||
):
|
) -> pd.DataFrame:
|
||||||
res_df = pd.DataFrame()
|
res_df = pd.DataFrame()
|
||||||
for pair, trades in analysed_trades[stratname].items():
|
for pair, trades in analysed_trades[stratname].items():
|
||||||
if trades.shape[0] > 0:
|
if trades.shape[0] > 0:
|
||||||
|
@ -252,6 +260,7 @@ def prepare_results(
|
||||||
|
|
||||||
def print_results(
|
def print_results(
|
||||||
res_df: pd.DataFrame,
|
res_df: pd.DataFrame,
|
||||||
|
exit_df: pd.DataFrame,
|
||||||
analysis_groups: List[str],
|
analysis_groups: List[str],
|
||||||
indicator_list: List[str],
|
indicator_list: List[str],
|
||||||
csv_path: Path,
|
csv_path: Path,
|
||||||
|
@ -278,9 +287,11 @@ def print_results(
|
||||||
for ind in indicator_list:
|
for ind in indicator_list:
|
||||||
if ind in res_df:
|
if ind in res_df:
|
||||||
available_inds.append(ind)
|
available_inds.append(ind)
|
||||||
ilist = ["pair", "enter_reason", "exit_reason"] + available_inds
|
|
||||||
|
merged_df = _merge_dfs(res_df, exit_df, available_inds)
|
||||||
|
|
||||||
_print_table(
|
_print_table(
|
||||||
res_df[ilist],
|
merged_df,
|
||||||
sortcols=["exit_reason"],
|
sortcols=["exit_reason"],
|
||||||
show_index=False,
|
show_index=False,
|
||||||
name="Indicators:",
|
name="Indicators:",
|
||||||
|
@ -291,6 +302,22 @@ def print_results(
|
||||||
print("\\No trades to show")
|
print("\\No trades to show")
|
||||||
|
|
||||||
|
|
||||||
|
def _merge_dfs(entry_df, exit_df, available_inds):
|
||||||
|
merge_on = ["pair", "open_date"]
|
||||||
|
signal_wide_indicators = list(set(available_inds) - set(BT_DATA_COLUMNS))
|
||||||
|
columns_to_keep = merge_on + ["enter_reason", "exit_reason"] + available_inds
|
||||||
|
|
||||||
|
if exit_df is None or exit_df.empty:
|
||||||
|
return entry_df[columns_to_keep]
|
||||||
|
|
||||||
|
return pd.merge(
|
||||||
|
entry_df[columns_to_keep],
|
||||||
|
exit_df[merge_on + signal_wide_indicators],
|
||||||
|
on=merge_on,
|
||||||
|
suffixes=(" (entry)", " (exit)"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _print_table(
|
def _print_table(
|
||||||
df: pd.DataFrame, sortcols=None, *, show_index=False, name=None, to_csv=False, csv_path: Path
|
df: pd.DataFrame, sortcols=None, *, show_index=False, name=None, to_csv=False, csv_path: Path
|
||||||
):
|
):
|
||||||
|
@ -333,6 +360,7 @@ def process_entry_exit_reasons(config: Config):
|
||||||
|
|
||||||
if trades is not None and not trades.empty:
|
if trades is not None and not trades.empty:
|
||||||
signal_candles = _load_signal_candles(config["exportfilename"])
|
signal_candles = _load_signal_candles(config["exportfilename"])
|
||||||
|
exit_signals = _load_exit_signal_candles(config["exportfilename"])
|
||||||
|
|
||||||
rej_df = None
|
rej_df = None
|
||||||
if do_rejected:
|
if do_rejected:
|
||||||
|
@ -345,20 +373,31 @@ def process_entry_exit_reasons(config: Config):
|
||||||
timerange=timerange,
|
timerange=timerange,
|
||||||
)
|
)
|
||||||
|
|
||||||
analysed_trades_dict = _process_candles_and_indicators(
|
entry_df = _generate_dfs(
|
||||||
config["exchange"]["pair_whitelist"], strategy_name, trades, signal_candles
|
config["exchange"]["pair_whitelist"],
|
||||||
)
|
|
||||||
|
|
||||||
res_df = prepare_results(
|
|
||||||
analysed_trades_dict,
|
|
||||||
strategy_name,
|
|
||||||
enter_reason_list,
|
enter_reason_list,
|
||||||
exit_reason_list,
|
exit_reason_list,
|
||||||
timerange=timerange,
|
signal_candles,
|
||||||
|
strategy_name,
|
||||||
|
timerange,
|
||||||
|
trades,
|
||||||
|
"open_date",
|
||||||
|
)
|
||||||
|
|
||||||
|
exit_df = _generate_dfs(
|
||||||
|
config["exchange"]["pair_whitelist"],
|
||||||
|
enter_reason_list,
|
||||||
|
exit_reason_list,
|
||||||
|
exit_signals,
|
||||||
|
strategy_name,
|
||||||
|
timerange,
|
||||||
|
trades,
|
||||||
|
"close_date",
|
||||||
)
|
)
|
||||||
|
|
||||||
print_results(
|
print_results(
|
||||||
res_df,
|
entry_df,
|
||||||
|
exit_df,
|
||||||
analysis_groups,
|
analysis_groups,
|
||||||
indicator_list,
|
indicator_list,
|
||||||
rejected_signals=rej_df,
|
rejected_signals=rej_df,
|
||||||
|
@ -368,3 +407,30 @@ def process_entry_exit_reasons(config: Config):
|
||||||
|
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise OperationalException(e) from e
|
raise OperationalException(e) from e
|
||||||
|
|
||||||
|
|
||||||
|
def _generate_dfs(
|
||||||
|
pairlist: list,
|
||||||
|
enter_reason_list: list,
|
||||||
|
exit_reason_list: list,
|
||||||
|
signal_candles: Dict,
|
||||||
|
strategy_name: str,
|
||||||
|
timerange: TimeRange,
|
||||||
|
trades: pd.DataFrame,
|
||||||
|
date_col: str,
|
||||||
|
) -> pd.DataFrame:
|
||||||
|
analysed_trades_dict = _process_candles_and_indicators(
|
||||||
|
pairlist,
|
||||||
|
strategy_name,
|
||||||
|
trades,
|
||||||
|
signal_candles,
|
||||||
|
date_col,
|
||||||
|
)
|
||||||
|
res_df = prepare_results(
|
||||||
|
analysed_trades_dict,
|
||||||
|
strategy_name,
|
||||||
|
enter_reason_list,
|
||||||
|
exit_reason_list,
|
||||||
|
timerange=timerange,
|
||||||
|
)
|
||||||
|
return res_df
|
||||||
|
|
|
@ -122,6 +122,7 @@ class Backtesting:
|
||||||
self.processed_dfs: Dict[str, Dict] = {}
|
self.processed_dfs: Dict[str, Dict] = {}
|
||||||
self.rejected_dict: Dict[str, List] = {}
|
self.rejected_dict: Dict[str, List] = {}
|
||||||
self.rejected_df: Dict[str, Dict] = {}
|
self.rejected_df: Dict[str, Dict] = {}
|
||||||
|
self.exited_dfs: Dict[str, Dict] = {}
|
||||||
|
|
||||||
self._exchange_name = self.config["exchange"]["name"]
|
self._exchange_name = self.config["exchange"]["name"]
|
||||||
if not exchange:
|
if not exchange:
|
||||||
|
@ -1564,11 +1565,14 @@ class Backtesting:
|
||||||
and self.dataprovider.runmode == RunMode.BACKTEST
|
and self.dataprovider.runmode == RunMode.BACKTEST
|
||||||
):
|
):
|
||||||
self.processed_dfs[strategy_name] = generate_trade_signal_candles(
|
self.processed_dfs[strategy_name] = generate_trade_signal_candles(
|
||||||
preprocessed_tmp, results
|
preprocessed_tmp, results, "open_date"
|
||||||
)
|
)
|
||||||
self.rejected_df[strategy_name] = generate_rejected_signals(
|
self.rejected_df[strategy_name] = generate_rejected_signals(
|
||||||
preprocessed_tmp, self.rejected_dict
|
preprocessed_tmp, self.rejected_dict
|
||||||
)
|
)
|
||||||
|
self.exited_dfs[strategy_name] = generate_trade_signal_candles(
|
||||||
|
preprocessed_tmp, results, "close_date"
|
||||||
|
)
|
||||||
|
|
||||||
return min_date, max_date
|
return min_date, max_date
|
||||||
|
|
||||||
|
@ -1644,7 +1648,11 @@ class Backtesting:
|
||||||
and self.dataprovider.runmode == RunMode.BACKTEST
|
and self.dataprovider.runmode == RunMode.BACKTEST
|
||||||
):
|
):
|
||||||
store_backtest_analysis_results(
|
store_backtest_analysis_results(
|
||||||
self.config["exportfilename"], self.processed_dfs, self.rejected_df, dt_appendix
|
self.config["exportfilename"],
|
||||||
|
self.processed_dfs,
|
||||||
|
self.rejected_df,
|
||||||
|
self.exited_dfs,
|
||||||
|
dt_appendix,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Results may be mixed up now. Sort them so they follow --strategy-list order.
|
# Results may be mixed up now. Sort them so they follow --strategy-list order.
|
||||||
|
|
|
@ -90,7 +90,12 @@ def _store_backtest_analysis_data(
|
||||||
|
|
||||||
|
|
||||||
def store_backtest_analysis_results(
|
def store_backtest_analysis_results(
|
||||||
recordfilename: Path, candles: Dict[str, Dict], trades: Dict[str, Dict], dtappendix: str
|
recordfilename: Path,
|
||||||
|
candles: Dict[str, Dict],
|
||||||
|
trades: Dict[str, Dict],
|
||||||
|
exited: Dict[str, Dict],
|
||||||
|
dtappendix: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
_store_backtest_analysis_data(recordfilename, candles, dtappendix, "signals")
|
_store_backtest_analysis_data(recordfilename, candles, dtappendix, "signals")
|
||||||
_store_backtest_analysis_data(recordfilename, trades, dtappendix, "rejected")
|
_store_backtest_analysis_data(recordfilename, trades, dtappendix, "rejected")
|
||||||
|
_store_backtest_analysis_data(recordfilename, exited, dtappendix, "exited")
|
||||||
|
|
|
@ -25,8 +25,8 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def generate_trade_signal_candles(
|
def generate_trade_signal_candles(
|
||||||
preprocessed_df: Dict[str, DataFrame], bt_results: Dict[str, Any]
|
preprocessed_df: Dict[str, DataFrame], bt_results: Dict[str, Any], date_col: str
|
||||||
) -> DataFrame:
|
) -> Dict[str, DataFrame]:
|
||||||
signal_candles_only = {}
|
signal_candles_only = {}
|
||||||
for pair in preprocessed_df.keys():
|
for pair in preprocessed_df.keys():
|
||||||
signal_candles_only_df = DataFrame()
|
signal_candles_only_df = DataFrame()
|
||||||
|
@ -36,8 +36,8 @@ def generate_trade_signal_candles(
|
||||||
pairresults = resdf.loc[(resdf["pair"] == pair)]
|
pairresults = resdf.loc[(resdf["pair"] == pair)]
|
||||||
|
|
||||||
if pairdf.shape[0] > 0:
|
if pairdf.shape[0] > 0:
|
||||||
for t, v in pairresults.open_date.items():
|
for t, v in pairresults.iterrows():
|
||||||
allinds = pairdf.loc[(pairdf["date"] < v)]
|
allinds = pairdf.loc[(pairdf["date"] < v[date_col])]
|
||||||
signal_inds = allinds.iloc[[-1]]
|
signal_inds = allinds.iloc[[-1]]
|
||||||
signal_candles_only_df = concat(
|
signal_candles_only_df = concat(
|
||||||
[signal_candles_only_df.infer_objects(), signal_inds.infer_objects()]
|
[signal_candles_only_df.infer_objects(), signal_inds.infer_objects()]
|
||||||
|
|
|
@ -18,7 +18,9 @@ def entryexitanalysis_cleanup() -> None:
|
||||||
Backtesting.cleanup()
|
Backtesting.cleanup()
|
||||||
|
|
||||||
|
|
||||||
def test_backtest_analysis_nomock(default_conf, mocker, caplog, testdatadir, user_dir, capsys):
|
def test_backtest_analysis_on_entry_and_rejected_signals_nomock(
|
||||||
|
default_conf, mocker, caplog, testdatadir, user_dir, capsys
|
||||||
|
):
|
||||||
caplog.set_level(logging.INFO)
|
caplog.set_level(logging.INFO)
|
||||||
(user_dir / "backtest_results").mkdir(parents=True, exist_ok=True)
|
(user_dir / "backtest_results").mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
@ -158,6 +160,15 @@ def test_backtest_analysis_nomock(default_conf, mocker, caplog, testdatadir, use
|
||||||
assert "34.049" in captured.out
|
assert "34.049" in captured.out
|
||||||
assert "0.104" in captured.out
|
assert "0.104" in captured.out
|
||||||
assert "52.829" in captured.out
|
assert "52.829" in captured.out
|
||||||
|
# assert indicator list
|
||||||
|
assert "close (entry)" in captured.out
|
||||||
|
assert "0.016" in captured.out
|
||||||
|
assert "rsi (entry)" in captured.out
|
||||||
|
assert "54.320" in captured.out
|
||||||
|
assert "close (exit)" in captured.out
|
||||||
|
assert "rsi (exit)" in captured.out
|
||||||
|
assert "52.829" in captured.out
|
||||||
|
assert "profit_abs" in captured.out
|
||||||
|
|
||||||
# test group 1
|
# test group 1
|
||||||
args = get_args(base_args + ["--analysis-groups", "1"])
|
args = get_args(base_args + ["--analysis-groups", "1"])
|
||||||
|
|
|
@ -293,20 +293,25 @@ def test_store_backtest_candles(testdatadir, mocker):
|
||||||
candle_dict = {"DefStrat": {"UNITTEST/BTC": pd.DataFrame()}}
|
candle_dict = {"DefStrat": {"UNITTEST/BTC": pd.DataFrame()}}
|
||||||
|
|
||||||
# mock directory exporting
|
# mock directory exporting
|
||||||
store_backtest_analysis_results(testdatadir, candle_dict, {}, "2022_01_01_15_05_13")
|
store_backtest_analysis_results(testdatadir, candle_dict, {}, {}, "2022_01_01_15_05_13")
|
||||||
|
|
||||||
assert dump_mock.call_count == 2
|
assert dump_mock.call_count == 3
|
||||||
assert isinstance(dump_mock.call_args_list[0][0][0], Path)
|
assert isinstance(dump_mock.call_args_list[0][0][0], Path)
|
||||||
assert str(dump_mock.call_args_list[0][0][0]).endswith("_signals.pkl")
|
assert str(dump_mock.call_args_list[0][0][0]).endswith("_signals.pkl")
|
||||||
|
assert str(dump_mock.call_args_list[1][0][0]).endswith("_rejected.pkl")
|
||||||
|
assert str(dump_mock.call_args_list[2][0][0]).endswith("_exited.pkl")
|
||||||
|
|
||||||
dump_mock.reset_mock()
|
dump_mock.reset_mock()
|
||||||
# mock file exporting
|
# mock file exporting
|
||||||
filename = Path(testdatadir / "testresult")
|
filename = Path(testdatadir / "testresult")
|
||||||
store_backtest_analysis_results(filename, candle_dict, {}, "2022_01_01_15_05_13")
|
store_backtest_analysis_results(filename, candle_dict, {}, {}, "2022_01_01_15_05_13")
|
||||||
assert dump_mock.call_count == 2
|
assert dump_mock.call_count == 3
|
||||||
assert isinstance(dump_mock.call_args_list[0][0][0], Path)
|
assert isinstance(dump_mock.call_args_list[0][0][0], Path)
|
||||||
# result will be testdatadir / testresult-<timestamp>_signals.pkl
|
# result will be testdatadir / testresult-<timestamp>_signals.pkl
|
||||||
assert str(dump_mock.call_args_list[0][0][0]).endswith("_signals.pkl")
|
assert str(dump_mock.call_args_list[0][0][0]).endswith("_signals.pkl")
|
||||||
|
assert str(dump_mock.call_args_list[1][0][0]).endswith("_rejected.pkl")
|
||||||
|
assert str(dump_mock.call_args_list[2][0][0]).endswith("_exited.pkl")
|
||||||
|
|
||||||
dump_mock.reset_mock()
|
dump_mock.reset_mock()
|
||||||
|
|
||||||
|
|
||||||
|
@ -315,7 +320,7 @@ def test_write_read_backtest_candles(tmp_path):
|
||||||
|
|
||||||
# test directory exporting
|
# test directory exporting
|
||||||
sample_date = "2022_01_01_15_05_13"
|
sample_date = "2022_01_01_15_05_13"
|
||||||
store_backtest_analysis_results(tmp_path, candle_dict, {}, sample_date)
|
store_backtest_analysis_results(tmp_path, candle_dict, {}, {}, sample_date)
|
||||||
stored_file = tmp_path / f"backtest-result-{sample_date}_signals.pkl"
|
stored_file = tmp_path / f"backtest-result-{sample_date}_signals.pkl"
|
||||||
with stored_file.open("rb") as scp:
|
with stored_file.open("rb") as scp:
|
||||||
pickled_signal_candles = joblib.load(scp)
|
pickled_signal_candles = joblib.load(scp)
|
||||||
|
@ -330,7 +335,7 @@ def test_write_read_backtest_candles(tmp_path):
|
||||||
|
|
||||||
# test file exporting
|
# test file exporting
|
||||||
filename = tmp_path / "testresult"
|
filename = tmp_path / "testresult"
|
||||||
store_backtest_analysis_results(filename, candle_dict, {}, sample_date)
|
store_backtest_analysis_results(filename, candle_dict, {}, {}, sample_date)
|
||||||
stored_file = tmp_path / f"testresult-{sample_date}_signals.pkl"
|
stored_file = tmp_path / f"testresult-{sample_date}_signals.pkl"
|
||||||
with stored_file.open("rb") as scp:
|
with stored_file.open("rb") as scp:
|
||||||
pickled_signal_candles = joblib.load(scp)
|
pickled_signal_candles = joblib.load(scp)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user