mirror of
https://github.com/freqtrade/freqtrade.git
synced 2025-12-19 06:11:15 +00:00
Refactor and add documentation
This commit is contained in:
@@ -18,15 +18,15 @@ 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 buy and sell signals.
|
||||||
makes, this file may get quite large, so periodically check your `user_data/backtest_results`
|
Depending on how many buys your strategy makes, this file may get quite large,
|
||||||
folder to delete old exports.
|
so periodically check your `user_data/backtest_results` 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
|
||||||
`user_data/backtest_results` folder.
|
`backtest-result-{timestamp}_exited.pkl` files in the `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:
|
||||||
@@ -141,6 +141,14 @@ Use the `--rejected-signals` option to print out rejected signals.
|
|||||||
freqtrade backtesting-analysis -c <config.json> --rejected-signals
|
freqtrade backtesting-analysis -c <config.json> --rejected-signals
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Printing analysis on exit signals
|
||||||
|
|
||||||
|
Use the `--exit-signals` option to print out analysis on exited signals.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
freqtrade backtesting-analysis -c <config.json> --exit-signals
|
||||||
|
```
|
||||||
|
|
||||||
### Writing tables to CSV
|
### Writing tables to CSV
|
||||||
|
|
||||||
Some of the tabular outputs can become large, so printing them out to the terminal is not preferable.
|
Some of the tabular outputs can become large, so printing them out to the terminal is not preferable.
|
||||||
|
|||||||
@@ -51,7 +51,9 @@ def _load_exit_signal_candles(backtest_dir: Path):
|
|||||||
return _load_backtest_analysis_data(backtest_dir, "exited")
|
return _load_backtest_analysis_data(backtest_dir, "exited")
|
||||||
|
|
||||||
|
|
||||||
def _process_candles_and_indicators(pairlist, strategy_name, trades, signal_candles):
|
def _process_candles_and_indicators(
|
||||||
|
pairlist, strategy_name, trades, signal_candles, analyse_on="open_date"
|
||||||
|
):
|
||||||
analysed_trades_dict = {strategy_name: {}}
|
analysed_trades_dict = {strategy_name: {}}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -60,7 +62,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], analyse_on
|
||||||
)
|
)
|
||||||
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)
|
||||||
@@ -68,7 +70,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, trades: pd.DataFrame, signal_candles: pd.DataFrame, analyse_on="open_date"
|
||||||
|
):
|
||||||
buyf = signal_candles
|
buyf = signal_candles
|
||||||
|
|
||||||
if len(buyf) > 0:
|
if len(buyf) > 0:
|
||||||
@@ -78,8 +82,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[analyse_on])]
|
||||||
if allinds.shape[0] > 0:
|
if allinds.shape[0] > 0:
|
||||||
tmp_inds = allinds.iloc[[-1]]
|
tmp_inds = allinds.iloc[[-1]]
|
||||||
|
|
||||||
@@ -339,8 +343,10 @@ def process_entry_exit_reasons(config: Config):
|
|||||||
trades = load_backtest_data(config["exportfilename"], strategy_name)
|
trades = load_backtest_data(config["exportfilename"], strategy_name)
|
||||||
|
|
||||||
if trades is not None and not trades.empty:
|
if trades is not None and not trades.empty:
|
||||||
|
analyse_on = "open_date"
|
||||||
if do_exited is True:
|
if do_exited is True:
|
||||||
signal_candles = _load_exit_signal_candles(config["exportfilename"])
|
signal_candles = _load_exit_signal_candles(config["exportfilename"])
|
||||||
|
analyse_on = "close_date"
|
||||||
else:
|
else:
|
||||||
signal_candles = _load_signal_candles(config["exportfilename"])
|
signal_candles = _load_signal_candles(config["exportfilename"])
|
||||||
|
|
||||||
@@ -356,7 +362,11 @@ def process_entry_exit_reasons(config: Config):
|
|||||||
)
|
)
|
||||||
|
|
||||||
analysed_trades_dict = _process_candles_and_indicators(
|
analysed_trades_dict = _process_candles_and_indicators(
|
||||||
config["exchange"]["pair_whitelist"], strategy_name, trades, signal_candles
|
config["exchange"]["pair_whitelist"],
|
||||||
|
strategy_name,
|
||||||
|
trades,
|
||||||
|
signal_candles,
|
||||||
|
analyse_on,
|
||||||
)
|
)
|
||||||
|
|
||||||
res_df = prepare_results(
|
res_df = prepare_results(
|
||||||
|
|||||||
@@ -42,8 +42,7 @@ from freqtrade.optimize.bt_progress import BTProgress
|
|||||||
from freqtrade.optimize.optimize_reports import (
|
from freqtrade.optimize.optimize_reports import (
|
||||||
generate_backtest_stats,
|
generate_backtest_stats,
|
||||||
generate_rejected_signals,
|
generate_rejected_signals,
|
||||||
generate_trade_entry_signal_candles,
|
generate_trade_signal_candles,
|
||||||
generate_trade_exit_signal_candles,
|
|
||||||
show_backtest_results,
|
show_backtest_results,
|
||||||
store_backtest_analysis_results,
|
store_backtest_analysis_results,
|
||||||
store_backtest_stats,
|
store_backtest_stats,
|
||||||
@@ -1560,14 +1559,14 @@ class Backtesting:
|
|||||||
self.config.get("export", "none") == "signals"
|
self.config.get("export", "none") == "signals"
|
||||||
and self.dataprovider.runmode == RunMode.BACKTEST
|
and self.dataprovider.runmode == RunMode.BACKTEST
|
||||||
):
|
):
|
||||||
self.processed_dfs[strategy_name] = generate_trade_entry_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_exit_signal_candles(
|
self.exited_dfs[strategy_name] = generate_trade_signal_candles(
|
||||||
preprocessed_tmp, results
|
preprocessed_tmp, results, "close_date"
|
||||||
)
|
)
|
||||||
|
|
||||||
return min_date, max_date
|
return min_date, max_date
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ from freqtrade.optimize.optimize_reports.optimize_reports import (
|
|||||||
generate_strategy_comparison,
|
generate_strategy_comparison,
|
||||||
generate_strategy_stats,
|
generate_strategy_stats,
|
||||||
generate_tag_metrics,
|
generate_tag_metrics,
|
||||||
generate_trade_entry_signal_candles,
|
generate_trade_signal_candles,
|
||||||
generate_trade_exit_signal_candles,
|
|
||||||
generate_trading_stats,
|
generate_trading_stats,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ from freqtrade.util import decimals_per_coin, fmt_coin
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def generate_trade_entry_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], analysis_on="open_date"
|
||||||
) -> Dict[str, DataFrame]:
|
) -> Dict[str, DataFrame]:
|
||||||
signal_candles_only = {}
|
signal_candles_only = {}
|
||||||
for pair in preprocessed_df.keys():
|
for pair in preprocessed_df.keys():
|
||||||
@@ -36,31 +36,8 @@ def generate_trade_entry_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[analysis_on])]
|
||||||
signal_inds = allinds.iloc[[-1]]
|
|
||||||
signal_candles_only_df = concat(
|
|
||||||
[signal_candles_only_df.infer_objects(), signal_inds.infer_objects()]
|
|
||||||
)
|
|
||||||
|
|
||||||
signal_candles_only[pair] = signal_candles_only_df
|
|
||||||
return signal_candles_only
|
|
||||||
|
|
||||||
|
|
||||||
def generate_trade_exit_signal_candles(
|
|
||||||
preprocessed_df: Dict[str, DataFrame], bt_results: Dict[str, Any]
|
|
||||||
) -> Dict[str, DataFrame]:
|
|
||||||
signal_candles_only = {}
|
|
||||||
for pair in preprocessed_df.keys():
|
|
||||||
signal_candles_only_df = DataFrame()
|
|
||||||
|
|
||||||
pairdf = preprocessed_df[pair]
|
|
||||||
resdf = bt_results["results"]
|
|
||||||
pairresults = resdf.loc[(resdf["pair"] == pair)]
|
|
||||||
|
|
||||||
if pairdf.shape[0] > 0:
|
|
||||||
for t, v in pairresults.close_date.items():
|
|
||||||
allinds = pairdf.loc[(pairdf["date"] < v)]
|
|
||||||
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()]
|
||||||
|
|||||||
@@ -393,8 +393,8 @@ def test_backtest_analysis_on_exit_signals_nomock(
|
|||||||
assert "0.5" in captured.out
|
assert "0.5" in captured.out
|
||||||
assert "-4" in captured.out
|
assert "-4" in captured.out
|
||||||
assert "-2" in captured.out
|
assert "-2" in captured.out
|
||||||
assert "nan" in captured.out
|
|
||||||
assert "57.654" in captured.out
|
assert "57.654" in captured.out
|
||||||
|
assert "44.428" in captured.out
|
||||||
assert "0" in captured.out
|
assert "0" in captured.out
|
||||||
assert "0.104" in captured.out
|
assert "0.104" in captured.out
|
||||||
assert "0.016" in captured.out
|
assert "0.016" in captured.out
|
||||||
|
|||||||
Reference in New Issue
Block a user