mirror of
https://github.com/freqtrade/freqtrade.git
synced 2025-11-29 08:33:07 +00:00
Merge pull request #10639 from jainanuj94/backtesting
Add entry-only and exit-only filters to --indicator-list in backtesting analysis
This commit is contained in:
@@ -149,6 +149,25 @@ to distinguish the values at the entry and exit points of the trade.
|
|||||||
`profit_ratio`, `profit_abs`, `exit_reason`,`initial_stop_loss_abs`, `initial_stop_loss_ratio`, `stop_loss_abs`, `stop_loss_ratio`,
|
`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`
|
`min_rate`, `max_rate`, `is_open`, `enter_tag`, `leverage`, `is_short`, `open_timestamp`, `close_timestamp` and `orders`
|
||||||
|
|
||||||
|
#### Filtering Indicators Based on Entry or Exit Signals
|
||||||
|
|
||||||
|
The `--indicator-list` option, by default, displays indicator values for both entry and exit signals. To filter the indicator values exclusively for entry signals, you can use the `--entry-only` argument. Similarly, to display indicator values only at exit signals, use the `--exit-only` argument.
|
||||||
|
|
||||||
|
Example: Display indicator values at entry signals:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
freqtrade backtesting-analysis -c user_data/config.json --analysis-groups 0 --indicator-list chikou_span tenkan_sen --entry-only
|
||||||
|
```
|
||||||
|
|
||||||
|
Example: Display indicator values at exit signals:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
freqtrade backtesting-analysis -c user_data/config.json --analysis-groups 0 --indicator-list chikou_span tenkan_sen --exit-only
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
When using these filters, the indicator names will not be suffixed with `(entry)` or `(exit)`.
|
||||||
|
|
||||||
### Filtering the trade output by date
|
### Filtering the trade output by date
|
||||||
|
|
||||||
To show only trades between dates within your backtested timerange, supply the usual `timerange` option in `YYYYMMDD-[YYYYMMDD]` format:
|
To show only trades between dates within your backtested timerange, supply the usual `timerange` option in `YYYYMMDD-[YYYYMMDD]` format:
|
||||||
|
|||||||
@@ -228,6 +228,8 @@ ARGS_ANALYZE_ENTRIES_EXITS = [
|
|||||||
"enter_reason_list",
|
"enter_reason_list",
|
||||||
"exit_reason_list",
|
"exit_reason_list",
|
||||||
"indicator_list",
|
"indicator_list",
|
||||||
|
"entry_only",
|
||||||
|
"exit_only",
|
||||||
"timerange",
|
"timerange",
|
||||||
"analysis_rejected",
|
"analysis_rejected",
|
||||||
"analysis_to_csv",
|
"analysis_to_csv",
|
||||||
|
|||||||
@@ -719,6 +719,12 @@ AVAILABLE_CLI_OPTIONS = {
|
|||||||
nargs="+",
|
nargs="+",
|
||||||
default=[],
|
default=[],
|
||||||
),
|
),
|
||||||
|
"entry_only": Arg(
|
||||||
|
"--entry-only", help=("Only analyze entry signals."), action="store_true", default=False
|
||||||
|
),
|
||||||
|
"exit_only": Arg(
|
||||||
|
"--exit-only", help=("Only analyze exit signals."), action="store_true", default=False
|
||||||
|
),
|
||||||
"analysis_rejected": Arg(
|
"analysis_rejected": Arg(
|
||||||
"--rejected-signals",
|
"--rejected-signals",
|
||||||
help="Analyse rejected signals",
|
help="Analyse rejected signals",
|
||||||
|
|||||||
@@ -407,6 +407,8 @@ class Configuration:
|
|||||||
("enter_reason_list", "Analysis enter tag list: {}"),
|
("enter_reason_list", "Analysis enter tag list: {}"),
|
||||||
("exit_reason_list", "Analysis exit tag list: {}"),
|
("exit_reason_list", "Analysis exit tag list: {}"),
|
||||||
("indicator_list", "Analysis indicator list: {}"),
|
("indicator_list", "Analysis indicator list: {}"),
|
||||||
|
("entry_only", "Only analyze entry signals: {}"),
|
||||||
|
("exit_only", "Only analyze exit signals: {}"),
|
||||||
("timerange", "Filter trades by timerange: {}"),
|
("timerange", "Filter trades by timerange: {}"),
|
||||||
("analysis_rejected", "Analyse rejected signals: {}"),
|
("analysis_rejected", "Analyse rejected signals: {}"),
|
||||||
("analysis_to_csv", "Store analysis tables to CSV: {}"),
|
("analysis_to_csv", "Store analysis tables to CSV: {}"),
|
||||||
|
|||||||
@@ -263,6 +263,8 @@ def print_results(
|
|||||||
exit_df: pd.DataFrame,
|
exit_df: pd.DataFrame,
|
||||||
analysis_groups: List[str],
|
analysis_groups: List[str],
|
||||||
indicator_list: List[str],
|
indicator_list: List[str],
|
||||||
|
entry_only: bool,
|
||||||
|
exit_only: bool,
|
||||||
csv_path: Path,
|
csv_path: Path,
|
||||||
rejected_signals=None,
|
rejected_signals=None,
|
||||||
to_csv=False,
|
to_csv=False,
|
||||||
@@ -288,7 +290,7 @@ def print_results(
|
|||||||
if ind in res_df:
|
if ind in res_df:
|
||||||
available_inds.append(ind)
|
available_inds.append(ind)
|
||||||
|
|
||||||
merged_df = _merge_dfs(res_df, exit_df, available_inds)
|
merged_df = _merge_dfs(res_df, exit_df, available_inds, entry_only, exit_only)
|
||||||
|
|
||||||
_print_table(
|
_print_table(
|
||||||
merged_df,
|
merged_df,
|
||||||
@@ -302,14 +304,21 @@ def print_results(
|
|||||||
print("\\No trades to show")
|
print("\\No trades to show")
|
||||||
|
|
||||||
|
|
||||||
def _merge_dfs(entry_df, exit_df, available_inds):
|
def _merge_dfs(
|
||||||
|
entry_df: pd.DataFrame,
|
||||||
|
exit_df: pd.DataFrame,
|
||||||
|
available_inds: List[str],
|
||||||
|
entry_only: bool,
|
||||||
|
exit_only: bool,
|
||||||
|
):
|
||||||
merge_on = ["pair", "open_date"]
|
merge_on = ["pair", "open_date"]
|
||||||
signal_wide_indicators = list(set(available_inds) - set(BT_DATA_COLUMNS))
|
signal_wide_indicators = list(set(available_inds) - set(BT_DATA_COLUMNS))
|
||||||
columns_to_keep = merge_on + ["enter_reason", "exit_reason"] + available_inds
|
columns_to_keep = merge_on + ["enter_reason", "exit_reason"]
|
||||||
|
|
||||||
if exit_df is None or exit_df.empty:
|
if exit_df is None or exit_df.empty or entry_only is True:
|
||||||
return entry_df[columns_to_keep]
|
return entry_df[columns_to_keep + available_inds]
|
||||||
|
|
||||||
|
if exit_only is True:
|
||||||
return pd.merge(
|
return pd.merge(
|
||||||
entry_df[columns_to_keep],
|
entry_df[columns_to_keep],
|
||||||
exit_df[merge_on + signal_wide_indicators],
|
exit_df[merge_on + signal_wide_indicators],
|
||||||
@@ -317,6 +326,13 @@ def _merge_dfs(entry_df, exit_df, available_inds):
|
|||||||
suffixes=(" (entry)", " (exit)"),
|
suffixes=(" (entry)", " (exit)"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return pd.merge(
|
||||||
|
entry_df[columns_to_keep + available_inds],
|
||||||
|
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
|
||||||
@@ -343,9 +359,16 @@ def process_entry_exit_reasons(config: Config):
|
|||||||
enter_reason_list = config.get("enter_reason_list", ["all"])
|
enter_reason_list = config.get("enter_reason_list", ["all"])
|
||||||
exit_reason_list = config.get("exit_reason_list", ["all"])
|
exit_reason_list = config.get("exit_reason_list", ["all"])
|
||||||
indicator_list = config.get("indicator_list", [])
|
indicator_list = config.get("indicator_list", [])
|
||||||
|
entry_only = config.get("entry_only", False)
|
||||||
|
exit_only = config.get("exit_only", False)
|
||||||
do_rejected = config.get("analysis_rejected", False)
|
do_rejected = config.get("analysis_rejected", False)
|
||||||
to_csv = config.get("analysis_to_csv", False)
|
to_csv = config.get("analysis_to_csv", False)
|
||||||
csv_path = Path(config.get("analysis_csv_path", config["exportfilename"]))
|
csv_path = Path(config.get("analysis_csv_path", config["exportfilename"]))
|
||||||
|
|
||||||
|
if entry_only is True and exit_only is True:
|
||||||
|
raise OperationalException(
|
||||||
|
"Cannot use --entry-only and --exit-only at the same time. Please choose one."
|
||||||
|
)
|
||||||
if to_csv and not csv_path.is_dir():
|
if to_csv and not csv_path.is_dir():
|
||||||
raise OperationalException(f"Specified directory {csv_path} does not exist.")
|
raise OperationalException(f"Specified directory {csv_path} does not exist.")
|
||||||
|
|
||||||
@@ -400,6 +423,8 @@ def process_entry_exit_reasons(config: Config):
|
|||||||
exit_df,
|
exit_df,
|
||||||
analysis_groups,
|
analysis_groups,
|
||||||
indicator_list,
|
indicator_list,
|
||||||
|
entry_only,
|
||||||
|
exit_only,
|
||||||
rejected_signals=rej_df,
|
rejected_signals=rej_df,
|
||||||
to_csv=to_csv,
|
to_csv=to_csv,
|
||||||
csv_path=csv_path,
|
csv_path=csv_path,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import pytest
|
|||||||
from freqtrade.commands.analyze_commands import start_analysis_entries_exits
|
from freqtrade.commands.analyze_commands import start_analysis_entries_exits
|
||||||
from freqtrade.commands.optimize_commands import start_backtesting
|
from freqtrade.commands.optimize_commands import start_backtesting
|
||||||
from freqtrade.enums import ExitType
|
from freqtrade.enums import ExitType
|
||||||
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.optimize.backtesting import Backtesting
|
from freqtrade.optimize.backtesting import Backtesting
|
||||||
from tests.conftest import get_args, patch_exchange, patched_configuration_load_config_file
|
from tests.conftest import get_args, patch_exchange, patched_configuration_load_config_file
|
||||||
|
|
||||||
@@ -256,3 +257,306 @@ def test_backtest_analysis_on_entry_and_rejected_signals_nomock(
|
|||||||
start_analysis_entries_exits(args)
|
start_analysis_entries_exits(args)
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
assert "no rejected signals" in captured.out
|
assert "no rejected signals" in captured.out
|
||||||
|
|
||||||
|
|
||||||
|
def test_backtest_analysis_with_invalid_config(
|
||||||
|
default_conf, mocker, caplog, testdatadir, user_dir, capsys
|
||||||
|
):
|
||||||
|
caplog.set_level(logging.INFO)
|
||||||
|
(user_dir / "backtest_results").mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
default_conf.update(
|
||||||
|
{
|
||||||
|
"use_exit_signal": True,
|
||||||
|
"exit_profit_only": False,
|
||||||
|
"exit_profit_offset": 0.0,
|
||||||
|
"ignore_roi_if_entry_signal": False,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
patch_exchange(mocker)
|
||||||
|
result1 = pd.DataFrame(
|
||||||
|
{
|
||||||
|
"pair": ["ETH/BTC", "LTC/BTC", "ETH/BTC", "LTC/BTC"],
|
||||||
|
"profit_ratio": [0.025, 0.05, -0.1, -0.05],
|
||||||
|
"profit_abs": [0.5, 2.0, -4.0, -2.0],
|
||||||
|
"open_date": pd.to_datetime(
|
||||||
|
[
|
||||||
|
"2018-01-29 18:40:00",
|
||||||
|
"2018-01-30 03:30:00",
|
||||||
|
"2018-01-30 08:10:00",
|
||||||
|
"2018-01-31 13:30:00",
|
||||||
|
],
|
||||||
|
utc=True,
|
||||||
|
),
|
||||||
|
"close_date": pd.to_datetime(
|
||||||
|
[
|
||||||
|
"2018-01-29 20:45:00",
|
||||||
|
"2018-01-30 05:35:00",
|
||||||
|
"2018-01-30 09:10:00",
|
||||||
|
"2018-01-31 15:00:00",
|
||||||
|
],
|
||||||
|
utc=True,
|
||||||
|
),
|
||||||
|
"trade_duration": [235, 40, 60, 90],
|
||||||
|
"is_open": [False, False, False, False],
|
||||||
|
"stake_amount": [0.01, 0.01, 0.01, 0.01],
|
||||||
|
"open_rate": [0.104445, 0.10302485, 0.10302485, 0.10302485],
|
||||||
|
"close_rate": [0.104969, 0.103541, 0.102041, 0.102541],
|
||||||
|
"is_short": [False, False, False, False],
|
||||||
|
"enter_tag": [
|
||||||
|
"enter_tag_long_a",
|
||||||
|
"enter_tag_long_b",
|
||||||
|
"enter_tag_long_a",
|
||||||
|
"enter_tag_long_b",
|
||||||
|
],
|
||||||
|
"exit_reason": [
|
||||||
|
ExitType.ROI.value,
|
||||||
|
ExitType.EXIT_SIGNAL.value,
|
||||||
|
ExitType.STOP_LOSS.value,
|
||||||
|
ExitType.TRAILING_STOP_LOSS.value,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
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),
|
||||||
|
"--user-data-dir",
|
||||||
|
str(user_dir),
|
||||||
|
"--timeframe",
|
||||||
|
"5m",
|
||||||
|
"--timerange",
|
||||||
|
"1515560100-1517287800",
|
||||||
|
"--export",
|
||||||
|
"signals",
|
||||||
|
"--cache",
|
||||||
|
"none",
|
||||||
|
]
|
||||||
|
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
|
||||||
|
|
||||||
|
base_args = [
|
||||||
|
"backtesting-analysis",
|
||||||
|
"--config",
|
||||||
|
"config.json",
|
||||||
|
"--datadir",
|
||||||
|
str(testdatadir),
|
||||||
|
"--user-data-dir",
|
||||||
|
str(user_dir),
|
||||||
|
]
|
||||||
|
|
||||||
|
# test with both entry and exit only arguments
|
||||||
|
args = get_args(
|
||||||
|
base_args
|
||||||
|
+ [
|
||||||
|
"--analysis-groups",
|
||||||
|
"0",
|
||||||
|
"--indicator-list",
|
||||||
|
"close",
|
||||||
|
"rsi",
|
||||||
|
"profit_abs",
|
||||||
|
"--entry-only",
|
||||||
|
"--exit-only",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
with pytest.raises(
|
||||||
|
OperationalException,
|
||||||
|
match=r"Cannot use --entry-only and --exit-only at the same time. Please choose one.",
|
||||||
|
):
|
||||||
|
start_analysis_entries_exits(args)
|
||||||
|
|
||||||
|
|
||||||
|
def test_backtest_analysis_on_entry_and_rejected_signals_only_entry_signals(
|
||||||
|
default_conf, mocker, caplog, testdatadir, user_dir, capsys
|
||||||
|
):
|
||||||
|
caplog.set_level(logging.INFO)
|
||||||
|
(user_dir / "backtest_results").mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
default_conf.update(
|
||||||
|
{
|
||||||
|
"use_exit_signal": True,
|
||||||
|
"exit_profit_only": False,
|
||||||
|
"exit_profit_offset": 0.0,
|
||||||
|
"ignore_roi_if_entry_signal": False,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
patch_exchange(mocker)
|
||||||
|
result1 = pd.DataFrame(
|
||||||
|
{
|
||||||
|
"pair": ["ETH/BTC", "LTC/BTC", "ETH/BTC", "LTC/BTC"],
|
||||||
|
"profit_ratio": [0.025, 0.05, -0.1, -0.05],
|
||||||
|
"profit_abs": [0.5, 2.0, -4.0, -2.0],
|
||||||
|
"open_date": pd.to_datetime(
|
||||||
|
[
|
||||||
|
"2018-01-29 18:40:00",
|
||||||
|
"2018-01-30 03:30:00",
|
||||||
|
"2018-01-30 08:10:00",
|
||||||
|
"2018-01-31 13:30:00",
|
||||||
|
],
|
||||||
|
utc=True,
|
||||||
|
),
|
||||||
|
"close_date": pd.to_datetime(
|
||||||
|
[
|
||||||
|
"2018-01-29 20:45:00",
|
||||||
|
"2018-01-30 05:35:00",
|
||||||
|
"2018-01-30 09:10:00",
|
||||||
|
"2018-01-31 15:00:00",
|
||||||
|
],
|
||||||
|
utc=True,
|
||||||
|
),
|
||||||
|
"trade_duration": [235, 40, 60, 90],
|
||||||
|
"is_open": [False, False, False, False],
|
||||||
|
"stake_amount": [0.01, 0.01, 0.01, 0.01],
|
||||||
|
"open_rate": [0.104445, 0.10302485, 0.10302485, 0.10302485],
|
||||||
|
"close_rate": [0.104969, 0.103541, 0.102041, 0.102541],
|
||||||
|
"is_short": [False, False, False, False],
|
||||||
|
"enter_tag": [
|
||||||
|
"enter_tag_long_a",
|
||||||
|
"enter_tag_long_b",
|
||||||
|
"enter_tag_long_a",
|
||||||
|
"enter_tag_long_b",
|
||||||
|
],
|
||||||
|
"exit_reason": [
|
||||||
|
ExitType.ROI.value,
|
||||||
|
ExitType.EXIT_SIGNAL.value,
|
||||||
|
ExitType.STOP_LOSS.value,
|
||||||
|
ExitType.TRAILING_STOP_LOSS.value,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
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),
|
||||||
|
"--user-data-dir",
|
||||||
|
str(user_dir),
|
||||||
|
"--timeframe",
|
||||||
|
"5m",
|
||||||
|
"--timerange",
|
||||||
|
"1515560100-1517287800",
|
||||||
|
"--export",
|
||||||
|
"signals",
|
||||||
|
"--cache",
|
||||||
|
"none",
|
||||||
|
]
|
||||||
|
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
|
||||||
|
|
||||||
|
base_args = [
|
||||||
|
"backtesting-analysis",
|
||||||
|
"--config",
|
||||||
|
"config.json",
|
||||||
|
"--datadir",
|
||||||
|
str(testdatadir),
|
||||||
|
"--user-data-dir",
|
||||||
|
str(user_dir),
|
||||||
|
]
|
||||||
|
|
||||||
|
# test group 0 and indicator list
|
||||||
|
args = get_args(
|
||||||
|
base_args
|
||||||
|
+ [
|
||||||
|
"--analysis-groups",
|
||||||
|
"0",
|
||||||
|
"--indicator-list",
|
||||||
|
"close",
|
||||||
|
"rsi",
|
||||||
|
"profit_abs",
|
||||||
|
"--entry-only",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
start_analysis_entries_exits(args)
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert "LTC/BTC" in captured.out
|
||||||
|
assert "ETH/BTC" in captured.out
|
||||||
|
assert "enter_tag_long_a" in captured.out
|
||||||
|
assert "enter_tag_long_b" in captured.out
|
||||||
|
assert "exit_signal" in captured.out
|
||||||
|
assert "roi" in captured.out
|
||||||
|
assert "stop_loss" in captured.out
|
||||||
|
assert "trailing_stop_loss" in captured.out
|
||||||
|
assert "0.5" in captured.out
|
||||||
|
assert "-4" in captured.out
|
||||||
|
assert "-2" in captured.out
|
||||||
|
assert "-3.5" in captured.out
|
||||||
|
assert "50" in captured.out
|
||||||
|
assert "0" in captured.out
|
||||||
|
assert "0.016" in captured.out
|
||||||
|
assert "34.049" in captured.out
|
||||||
|
assert "0.104" in captured.out
|
||||||
|
assert "52.829" in captured.out
|
||||||
|
# assert indicator list
|
||||||
|
assert "close" in captured.out
|
||||||
|
assert "close (entry)" not in captured.out
|
||||||
|
assert "0.016" in captured.out
|
||||||
|
assert "rsi (entry)" not in captured.out
|
||||||
|
assert "rsi" in captured.out
|
||||||
|
assert "54.320" in captured.out
|
||||||
|
assert "close (exit)" not in captured.out
|
||||||
|
assert "rsi (exit)" not in captured.out
|
||||||
|
assert "52.829" in captured.out
|
||||||
|
assert "profit_abs" in captured.out
|
||||||
|
|||||||
Reference in New Issue
Block a user