diff --git a/tests/freqai/conftest.py b/tests/freqai/conftest.py index 4a1976d9d..fce01b9ee 100644 --- a/tests/freqai/conftest.py +++ b/tests/freqai/conftest.py @@ -32,13 +32,12 @@ def is_arm() -> bool: @pytest.fixture(autouse=True) def patch_torch_initlogs(mocker) -> None: - if is_mac(): # Mock torch import completely import sys import types - module_name = 'torch' + module_name = "torch" mocked_module = types.ModuleType(module_name) sys.modules[module_name] = mocked_module else: @@ -80,25 +79,23 @@ def freqai_conf(default_conf, tmp_path): "stratify_training_data": 0, "indicator_periods_candles": [10], "shuffle_after_split": False, - "buffer_train_data_candles": 0 + "buffer_train_data_candles": 0, }, "data_split_parameters": {"test_size": 0.33, "shuffle": False}, "model_training_parameters": {"n_estimators": 100}, }, - "config_files": [Path('config_examples', 'config_freqai.example.json')] + "config_files": [Path("config_examples", "config_freqai.example.json")], } ) - freqaiconf['exchange'].update({'pair_whitelist': ['ADA/BTC', 'DASH/BTC', 'ETH/BTC', 'LTC/BTC']}) + freqaiconf["exchange"].update({"pair_whitelist": ["ADA/BTC", "DASH/BTC", "ETH/BTC", "LTC/BTC"]}) return freqaiconf def make_rl_config(conf): conf.update({"strategy": "freqai_rl_test_strat"}) - conf["freqai"].update({"model_training_parameters": { - "learning_rate": 0.00025, - "gamma": 0.9, - "verbose": 1 - }}) + conf["freqai"].update( + {"model_training_parameters": {"learning_rate": 0.00025, "gamma": 0.9, "verbose": 1}} + ) conf["freqai"]["rl_config"] = { "train_cycles": 1, "thread_count": 2, @@ -107,31 +104,27 @@ def make_rl_config(conf): "policy_type": "MlpPolicy", "max_training_drawdown_pct": 0.5, "net_arch": [32, 32], - "model_reward_parameters": { - "rr": 1, - "profit_aim": 0.02, - "win_reward_factor": 2 - }, - "drop_ohlc_from_features": False - } + "model_reward_parameters": {"rr": 1, "profit_aim": 0.02, "win_reward_factor": 2}, + "drop_ohlc_from_features": False, + } return conf def mock_pytorch_mlp_model_training_parameters() -> Dict[str, Any]: return { - "learning_rate": 3e-4, - "trainer_kwargs": { - "n_steps": None, - "batch_size": 64, - "n_epochs": 1, - }, - "model_kwargs": { - "hidden_dim": 32, - "dropout_percent": 0.2, - "n_layer": 1, - } - } + "learning_rate": 3e-4, + "trainer_kwargs": { + "n_steps": None, + "batch_size": 64, + "n_epochs": 1, + }, + "model_kwargs": { + "hidden_dim": 32, + "dropout_percent": 0.2, + "n_layer": 1, + }, + } def get_patched_data_kitchen(mocker, freqaiconf): @@ -178,14 +171,14 @@ def make_unfiltered_dataframe(mocker, freqai_conf): new_timerange = TimeRange.parse_timerange("20180120-20180130") corr_dataframes, base_dataframes = freqai.dd.get_base_and_corr_dataframes( - data_load_timerange, freqai.dk.pair, freqai.dk - ) + data_load_timerange, freqai.dk.pair, freqai.dk + ) unfiltered_dataframe = freqai.dk.use_strategy_to_populate_indicators( - strategy, corr_dataframes, base_dataframes, freqai.dk.pair - ) + strategy, corr_dataframes, base_dataframes, freqai.dk.pair + ) for i in range(5): - unfiltered_dataframe[f'constant_{i}'] = i + unfiltered_dataframe[f"constant_{i}"] = i unfiltered_dataframe = freqai.dk.slice_dataframe(new_timerange, unfiltered_dataframe) @@ -212,23 +205,23 @@ def make_data_dictionary(mocker, freqai_conf): new_timerange = TimeRange.parse_timerange("20180120-20180130") corr_dataframes, base_dataframes = freqai.dd.get_base_and_corr_dataframes( - data_load_timerange, freqai.dk.pair, freqai.dk - ) + data_load_timerange, freqai.dk.pair, freqai.dk + ) unfiltered_dataframe = freqai.dk.use_strategy_to_populate_indicators( - strategy, corr_dataframes, base_dataframes, freqai.dk.pair - ) + strategy, corr_dataframes, base_dataframes, freqai.dk.pair + ) unfiltered_dataframe = freqai.dk.slice_dataframe(new_timerange, unfiltered_dataframe) freqai.dk.find_features(unfiltered_dataframe) features_filtered, labels_filtered = freqai.dk.filter_features( - unfiltered_dataframe, - freqai.dk.training_features_list, - freqai.dk.label_list, - training_filter=True, - ) + unfiltered_dataframe, + freqai.dk.training_features_list, + freqai.dk.label_list, + training_filter=True, + ) data_dictionary = freqai.dk.make_train_test_datasets(features_filtered, labels_filtered) @@ -247,8 +240,8 @@ def get_freqai_live_analyzed_dataframe(mocker, freqaiconf): timerange = TimeRange.parse_timerange("20180110-20180114") freqai.dk.load_all_pair_histories(timerange) - strategy.analyze_pair('ADA/BTC', '5m') - return strategy.dp.get_analyzed_dataframe('ADA/BTC', '5m') + strategy.analyze_pair("ADA/BTC", "5m") + return strategy.dp.get_analyzed_dataframe("ADA/BTC", "5m") def get_freqai_analyzed_dataframe(mocker, freqaiconf): @@ -264,7 +257,7 @@ def get_freqai_analyzed_dataframe(mocker, freqaiconf): sub_timerange = TimeRange.parse_timerange("20180111-20180114") corr_df, base_df = freqai.dk.get_base_and_corr_dataframes(sub_timerange, "LTC/BTC") - return freqai.dk.use_strategy_to_populate_indicators(strategy, corr_df, base_df, 'LTC/BTC') + return freqai.dk.use_strategy_to_populate_indicators(strategy, corr_df, base_df, "LTC/BTC") def get_ready_to_train(mocker, freqaiconf): diff --git a/tests/freqai/test_models/ReinforcementLearner_test_3ac.py b/tests/freqai/test_models/ReinforcementLearner_test_3ac.py index f77120c3c..ec7679883 100644 --- a/tests/freqai/test_models/ReinforcementLearner_test_3ac.py +++ b/tests/freqai/test_models/ReinforcementLearner_test_3ac.py @@ -26,24 +26,25 @@ class ReinforcementLearner_test_3ac(ReinforcementLearner): """ def calculate_reward(self, action: int) -> float: - # first, penalize if the action is not valid if not self._is_valid(action): return -2 pnl = self.get_unrealized_profit() rew = np.sign(pnl) * (pnl + 1) - factor = 100. + factor = 100.0 # reward agent for entering trades - if (action in (Actions.Buy.value, Actions.Sell.value) - and self._position == Positions.Neutral): + if ( + action in (Actions.Buy.value, Actions.Sell.value) + and self._position == Positions.Neutral + ): return 25 # discourage agent from not entering trades if action == Actions.Neutral.value and self._position == Positions.Neutral: return -1 - max_trade_duration = self.rl_config.get('max_trade_duration_candles', 300) + max_trade_duration = self.rl_config.get("max_trade_duration_candles", 300) trade_duration = self._current_tick - self._last_trade_tick # type: ignore if trade_duration <= max_trade_duration: @@ -67,4 +68,4 @@ class ReinforcementLearner_test_3ac(ReinforcementLearner): factor *= self.rl_config["model_reward_parameters"].get("win_reward_factor", 2) return float(rew * factor) - return 0. + return 0.0 diff --git a/tests/freqai/test_models/ReinforcementLearner_test_4ac.py b/tests/freqai/test_models/ReinforcementLearner_test_4ac.py index 4fc2b0005..4044fc41d 100644 --- a/tests/freqai/test_models/ReinforcementLearner_test_4ac.py +++ b/tests/freqai/test_models/ReinforcementLearner_test_4ac.py @@ -26,24 +26,25 @@ class ReinforcementLearner_test_4ac(ReinforcementLearner): """ def calculate_reward(self, action: int) -> float: - # first, penalize if the action is not valid if not self._is_valid(action): return -2 pnl = self.get_unrealized_profit() rew = np.sign(pnl) * (pnl + 1) - factor = 100. + factor = 100.0 # reward agent for entering trades - if (action in (Actions.Long_enter.value, Actions.Short_enter.value) - and self._position == Positions.Neutral): + if ( + action in (Actions.Long_enter.value, Actions.Short_enter.value) + and self._position == Positions.Neutral + ): return 25 # discourage agent from not entering trades if action == Actions.Neutral.value and self._position == Positions.Neutral: return -1 - max_trade_duration = self.rl_config.get('max_trade_duration_candles', 300) + max_trade_duration = self.rl_config.get("max_trade_duration_candles", 300) trade_duration = self._current_tick - self._last_trade_tick # type: ignore if trade_duration <= max_trade_duration: @@ -52,20 +53,22 @@ class ReinforcementLearner_test_4ac(ReinforcementLearner): factor *= 0.5 # discourage sitting in position - if (self._position in (Positions.Short, Positions.Long) and - action == Actions.Neutral.value): + if ( + self._position in (Positions.Short, Positions.Long) + and action == Actions.Neutral.value + ): return -1 * trade_duration / max_trade_duration # close long if action == Actions.Exit.value and self._position == Positions.Long: if pnl > self.profit_aim * self.rr: - factor *= self.rl_config['model_reward_parameters'].get('win_reward_factor', 2) + factor *= self.rl_config["model_reward_parameters"].get("win_reward_factor", 2) return float(rew * factor) # close short if action == Actions.Exit.value and self._position == Positions.Short: if pnl > self.profit_aim * self.rr: - factor *= self.rl_config['model_reward_parameters'].get('win_reward_factor', 2) + factor *= self.rl_config["model_reward_parameters"].get("win_reward_factor", 2) return float(rew * factor) - return 0. + return 0.0 diff --git a/tests/leverage/test_interest.py b/tests/leverage/test_interest.py index dd4983c71..6df94bca4 100644 --- a/tests/leverage/test_interest.py +++ b/tests/leverage/test_interest.py @@ -10,33 +10,40 @@ five_hours = FtPrecise(5.0) twentyfive_hours = FtPrecise(25.0) -@pytest.mark.parametrize('exchange,interest_rate,hours,expected', [ - ('binance', 0.0005, ten_mins, 0.00125), - ('binance', 0.00025, ten_mins, 0.000625), - ('binance', 0.00025, five_hours, 0.003125), - ('binance', 0.00025, twentyfive_hours, 0.015625), - # Kraken - ('kraken', 0.0005, ten_mins, 0.06), - ('kraken', 0.00025, ten_mins, 0.03), - ('kraken', 0.00025, five_hours, 0.045), - ('kraken', 0.00025, twentyfive_hours, 0.12), -]) +@pytest.mark.parametrize( + "exchange,interest_rate,hours,expected", + [ + ("binance", 0.0005, ten_mins, 0.00125), + ("binance", 0.00025, ten_mins, 0.000625), + ("binance", 0.00025, five_hours, 0.003125), + ("binance", 0.00025, twentyfive_hours, 0.015625), + # Kraken + ("kraken", 0.0005, ten_mins, 0.06), + ("kraken", 0.00025, ten_mins, 0.03), + ("kraken", 0.00025, five_hours, 0.045), + ("kraken", 0.00025, twentyfive_hours, 0.12), + ], +) def test_interest(exchange, interest_rate, hours, expected): borrowed = FtPrecise(60.0) - assert pytest.approx(float(interest( - exchange_name=exchange, - borrowed=borrowed, - rate=FtPrecise(interest_rate), - hours=hours - ))) == expected + assert ( + pytest.approx( + float( + interest( + exchange_name=exchange, + borrowed=borrowed, + rate=FtPrecise(interest_rate), + hours=hours, + ) + ) + ) + == expected + ) def test_interest_exception(): with pytest.raises(OperationalException, match=r"Leverage not available on .* with freqtrade"): interest( - exchange_name='bitmex', - borrowed=FtPrecise(60.0), - rate=FtPrecise(0.0005), - hours=ten_mins + exchange_name="bitmex", borrowed=FtPrecise(60.0), rate=FtPrecise(0.0005), hours=ten_mins ) diff --git a/tests/optimize/__init__.py b/tests/optimize/__init__.py index b95764ba5..c824e4484 100644 --- a/tests/optimize/__init__.py +++ b/tests/optimize/__init__.py @@ -9,13 +9,14 @@ from freqtrade.util.datetime_helpers import dt_utc tests_start_time = dt_utc(2018, 10, 3) -tests_timeframe = '1h' +tests_timeframe = "1h" class BTrade(NamedTuple): """ Minimalistic Trade result used for functional backtesting """ + exit_reason: ExitType open_tick: int close_tick: int @@ -27,6 +28,7 @@ class BTContainer(NamedTuple): """ Minimal BacktestContainer defining Backtest inputs and results. """ + data: List[List[float]] stop_loss: float roi: Dict[str, float] @@ -51,22 +53,32 @@ def _get_frame_time_from_offset(offset): def _build_backtest_dataframe(data): - columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'enter_long', 'exit_long', - 'enter_short', 'exit_short'] + columns = [ + "date", + "open", + "high", + "low", + "close", + "volume", + "enter_long", + "exit_long", + "enter_short", + "exit_short", + ] if len(data[0]) == 8: # No short columns data = [d + [0, 0] for d in data] - columns = columns + ['enter_tag'] if len(data[0]) == 11 else columns + columns = columns + ["enter_tag"] if len(data[0]) == 11 else columns frame = DataFrame.from_records(data, columns=columns) - frame['date'] = frame['date'].apply(_get_frame_time_from_offset) + frame["date"] = frame["date"].apply(_get_frame_time_from_offset) # Ensure floats are in place - for column in ['open', 'high', 'low', 'close', 'volume']: - frame[column] = frame[column].astype('float64') + for column in ["open", "high", "low", "close", "volume"]: + frame[column] = frame[column].astype("float64") # Ensure all candles make kindof sense - assert all(frame['low'] <= frame['close']) - assert all(frame['low'] <= frame['open']) - assert all(frame['high'] >= frame['close']) - assert all(frame['high'] >= frame['open']) + assert all(frame["low"] <= frame["close"]) + assert all(frame["low"] <= frame["open"]) + assert all(frame["high"] >= frame["close"]) + assert all(frame["high"] >= frame["open"]) return frame diff --git a/tests/optimize/conftest.py b/tests/optimize/conftest.py index cb8a6b5f7..b2833ef64 100644 --- a/tests/optimize/conftest.py +++ b/tests/optimize/conftest.py @@ -11,21 +11,23 @@ from freqtrade.optimize.hyperopt import Hyperopt from tests.conftest import patch_exchange -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def hyperopt_conf(default_conf): hyperconf = deepcopy(default_conf) - hyperconf.update({ - 'datadir': Path(default_conf['datadir']), - 'runmode': RunMode.HYPEROPT, - 'strategy': 'HyperoptableStrategy', - 'hyperopt_loss': 'ShortTradeDurHyperOptLoss', - 'hyperopt_path': str(Path(__file__).parent / 'hyperopts'), - 'epochs': 1, - 'timerange': None, - 'spaces': ['default'], - 'hyperopt_jobs': 1, - 'hyperopt_min_trades': 1, - }) + hyperconf.update( + { + "datadir": Path(default_conf["datadir"]), + "runmode": RunMode.HYPEROPT, + "strategy": "HyperoptableStrategy", + "hyperopt_loss": "ShortTradeDurHyperOptLoss", + "hyperopt_path": str(Path(__file__).parent / "hyperopts"), + "epochs": 1, + "timerange": None, + "spaces": ["default"], + "hyperopt_jobs": 1, + "hyperopt_min_trades": 1, + } + ) return hyperconf @@ -36,32 +38,29 @@ def backtesting_cleanup(): Backtesting.cleanup() -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def hyperopt(hyperopt_conf, mocker): - patch_exchange(mocker) return Hyperopt(hyperopt_conf) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def hyperopt_results(): return pd.DataFrame( { - 'pair': ['ETH/USDT', 'ETH/USDT', 'ETH/USDT', 'ETH/USDT'], - 'profit_ratio': [-0.1, 0.2, -0.12, 0.3], - 'profit_abs': [-0.2, 0.4, -0.21, 0.6], - 'trade_duration': [10, 30, 10, 10], - 'amount': [0.1, 0.1, 0.1, 0.1], - 'exit_reason': [ExitType.STOP_LOSS, ExitType.ROI, ExitType.STOP_LOSS, ExitType.ROI], - 'open_date': - [ + "pair": ["ETH/USDT", "ETH/USDT", "ETH/USDT", "ETH/USDT"], + "profit_ratio": [-0.1, 0.2, -0.12, 0.3], + "profit_abs": [-0.2, 0.4, -0.21, 0.6], + "trade_duration": [10, 30, 10, 10], + "amount": [0.1, 0.1, 0.1, 0.1], + "exit_reason": [ExitType.STOP_LOSS, ExitType.ROI, ExitType.STOP_LOSS, ExitType.ROI], + "open_date": [ datetime(2019, 1, 1, 9, 15, 0), datetime(2019, 1, 2, 8, 55, 0), datetime(2019, 1, 3, 9, 15, 0), datetime(2019, 1, 4, 9, 15, 0), ], - 'close_date': - [ + "close_date": [ datetime(2019, 1, 1, 9, 25, 0), datetime(2019, 1, 2, 9, 25, 0), datetime(2019, 1, 3, 9, 25, 0), diff --git a/tests/optimize/test_backtest_detail.py b/tests/optimize/test_backtest_detail.py index 8b0fea0d3..edaedb81e 100644 --- a/tests/optimize/test_backtest_detail.py +++ b/tests/optimize/test_backtest_detail.py @@ -20,45 +20,58 @@ from tests.optimize import ( # Test 0: Sell with signal sell in candle 3 # Test with Stop-loss at 1% -tc0 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4986, 6172, 0, 0], # exit with stoploss hit - [3, 5010, 5010, 4980, 5010, 6172, 0, 1], - [4, 5010, 5011, 4977, 4995, 6172, 0, 0], - [5, 4995, 4995, 4950, 4950, 6172, 0, 0]], - stop_loss=-0.01, roi={"0": 1}, profit_perc=0.002, use_exit_signal=True, - trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=4)] +tc0 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4986, 6172, 0, 0], # exit with stoploss hit + [3, 5010, 5010, 4980, 5010, 6172, 0, 1], + [4, 5010, 5011, 4977, 4995, 6172, 0, 0], + [5, 4995, 4995, 4950, 4950, 6172, 0, 0], + ], + stop_loss=-0.01, + roi={"0": 1}, + profit_perc=0.002, + use_exit_signal=True, + trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=4)], ) # Test 1: Stop-Loss Triggered 1% loss # Test with Stop-loss at 1% -tc1 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4600, 4600, 6172, 0, 0], # exit with stoploss hit - [3, 4975, 5000, 4975, 4977, 6172, 0, 0], - [4, 4977, 4995, 4977, 4995, 6172, 0, 0], - [5, 4995, 4995, 4950, 4950, 6172, 0, 0]], - stop_loss=-0.01, roi={"0": 1}, profit_perc=-0.01, - trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2)] +tc1 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4600, 4600, 6172, 0, 0], # exit with stoploss hit + [3, 4975, 5000, 4975, 4977, 6172, 0, 0], + [4, 4977, 4995, 4977, 4995, 6172, 0, 0], + [5, 4995, 4995, 4950, 4950, 6172, 0, 0], + ], + stop_loss=-0.01, + roi={"0": 1}, + profit_perc=-0.01, + trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2)], ) # Test 2: Minus 4% Low, minus 1% close # Test with Stop-Loss at 3% -tc2 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4962, 4975, 6172, 0, 0], - [3, 4975, 5000, 4800, 4962, 6172, 0, 0], # exit with stoploss hit - [4, 4962, 4987, 4937, 4950, 6172, 0, 0], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], - stop_loss=-0.03, roi={"0": 1}, profit_perc=-0.03, - trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=3)] +tc2 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4962, 4975, 6172, 0, 0], + [3, 4975, 5000, 4800, 4962, 6172, 0, 0], # exit with stoploss hit + [4, 4962, 4987, 4937, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0], + ], + stop_loss=-0.03, + roi={"0": 1}, + profit_perc=-0.03, + trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=3)], ) @@ -68,320 +81,422 @@ tc2 = BTContainer(data=[ # Candle drops 20% # Trade-A: Stop-Loss Triggered 2% Loss # Trade-B: Stop-Loss Triggered 2% Loss -tc3 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4800, 4975, 6172, 0, 0], # exit with stoploss hit - [3, 4975, 5000, 4950, 4962, 6172, 1, 0], - [4, 4975, 5000, 4950, 4962, 6172, 0, 0], # enter trade 2 (signal on last candle) - [5, 4962, 4987, 4000, 4000, 6172, 0, 0], # exit with stoploss hit - [6, 4950, 4975, 4950, 4950, 6172, 0, 0]], - stop_loss=-0.02, roi={"0": 1}, profit_perc=-0.04, - trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2), - BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=4, close_tick=5)] +tc3 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4800, 4975, 6172, 0, 0], # exit with stoploss hit + [3, 4975, 5000, 4950, 4962, 6172, 1, 0], + [4, 4975, 5000, 4950, 4962, 6172, 0, 0], # enter trade 2 (signal on last candle) + [5, 4962, 4987, 4000, 4000, 6172, 0, 0], # exit with stoploss hit + [6, 4950, 4975, 4950, 4950, 6172, 0, 0], + ], + stop_loss=-0.02, + roi={"0": 1}, + profit_perc=-0.04, + trades=[ + BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2), + BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=4, close_tick=5), + ], ) # Test 4: Minus 3% / recovery +15% # Candle Data for test 3 – Candle drops 3% Closed 15% up # Test with Stop-loss at 2% ROI 6% # Stop-Loss Triggered 2% Loss -tc4 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5750, 4850, 5750, 6172, 0, 0], # Exit with stoploss hit - [3, 4975, 5000, 4950, 4962, 6172, 0, 0], - [4, 4962, 4987, 4937, 4950, 6172, 0, 0], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], - stop_loss=-0.02, roi={"0": 0.06}, profit_perc=-0.02, - trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2)] +tc4 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5750, 4850, 5750, 6172, 0, 0], # Exit with stoploss hit + [3, 4975, 5000, 4950, 4962, 6172, 0, 0], + [4, 4962, 4987, 4937, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0], + ], + stop_loss=-0.02, + roi={"0": 0.06}, + profit_perc=-0.02, + trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2)], ) # Test 5: Drops 0.5% Closes +20%, ROI triggers 3% Gain # stop-loss: 1%, ROI: 3% -tc5 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5025, 4980, 4987, 6172, 1, 0], - [1, 5000, 5025, 4980, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5025, 4975, 4987, 6172, 0, 0], - [3, 4975, 6000, 4975, 6000, 6172, 0, 0], # ROI - [4, 4962, 4987, 4962, 4972, 6172, 0, 0], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], - stop_loss=-0.01, roi={"0": 0.03}, profit_perc=0.03, - trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)] +tc5 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5025, 4980, 4987, 6172, 1, 0], + [1, 5000, 5025, 4980, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5025, 4975, 4987, 6172, 0, 0], + [3, 4975, 6000, 4975, 6000, 6172, 0, 0], # ROI + [4, 4962, 4987, 4962, 4972, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0], + ], + stop_loss=-0.01, + roi={"0": 0.03}, + profit_perc=0.03, + trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)], ) # Test 6: Drops 3% / Recovers 6% Positive / Closes 1% positive, Stop-Loss triggers 2% Loss # stop-loss: 2% ROI: 5% -tc6 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5300, 4850, 5050, 6172, 0, 0], # Exit with stoploss - [3, 4975, 5000, 4950, 4962, 6172, 0, 0], - [4, 4962, 4987, 4950, 4950, 6172, 0, 0], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], - stop_loss=-0.02, roi={"0": 0.05}, profit_perc=-0.02, - trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2)] +tc6 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5300, 4850, 5050, 6172, 0, 0], # Exit with stoploss + [3, 4975, 5000, 4950, 4962, 6172, 0, 0], + [4, 4962, 4987, 4950, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0], + ], + stop_loss=-0.02, + roi={"0": 0.05}, + profit_perc=-0.02, + trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2)], ) # Test 7: 6% Positive / 1% Negative / Close 1% Positive, ROI Triggers 3% Gain # stop-loss: 2% ROI: 3% -tc7 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], - [2, 4987, 5300, 4950, 5050, 6172, 0, 0], - [3, 4975, 5000, 4950, 4962, 6172, 0, 0], - [4, 4962, 4987, 4950, 4950, 6172, 0, 0], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], - stop_loss=-0.02, roi={"0": 0.03}, profit_perc=0.03, - trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=2)] +tc7 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], + [2, 4987, 5300, 4950, 5050, 6172, 0, 0], + [3, 4975, 5000, 4950, 4962, 6172, 0, 0], + [4, 4962, 4987, 4950, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0], + ], + stop_loss=-0.02, + roi={"0": 0.03}, + profit_perc=0.03, + trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=2)], ) # Test 8: trailing_stop should raise so candle 3 causes a stoploss. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted in candle 2 -tc8 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5050, 4950, 5000, 6172, 0, 0], - [2, 5000, 5250, 4750, 4850, 6172, 0, 0], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], - stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.055, trailing_stop=True, - trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)] +tc8 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5050, 4950, 5000, 6172, 0, 0], + [2, 5000, 5250, 4750, 4850, 6172, 0, 0], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.10}, + profit_perc=-0.055, + trailing_stop=True, + trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)], ) # Test 9: trailing_stop should raise - high and low in same candle. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted in candle 3 -tc9 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5050, 4950, 5000, 6172, 0, 0], - [2, 5000, 5050, 4950, 5000, 6172, 0, 0], - [3, 5000, 5200, 4550, 4850, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], - stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.064, trailing_stop=True, - trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)] +tc9 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5050, 4950, 5000, 6172, 0, 0], + [2, 5000, 5050, 4950, 5000, 6172, 0, 0], + [3, 5000, 5200, 4550, 4850, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.10}, + profit_perc=-0.064, + trailing_stop=True, + trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)], ) # Test 10: trailing_stop should raise so candle 3 causes a stoploss # without applying trailing_stop_positive since stoploss_offset is at 10%. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2 -tc10 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5100, 4950, 5100, 6172, 0, 0], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], - stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.1, trailing_stop=True, - trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.10, +tc10 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5100, 4950, 5100, 6172, 0, 0], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.10}, + profit_perc=-0.1, + trailing_stop=True, + trailing_only_offset_is_reached=True, + trailing_stop_positive_offset=0.10, trailing_stop_positive=0.03, - trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=4)] + trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=4)], ) # Test 11: trailing_stop should raise so candle 3 causes a stoploss # applying a positive trailing stop of 3% since stop_positive_offset is reached. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2 -tc11 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5100, 4950, 5100, 6172, 0, 0], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0], - [3, 5000, 5150, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], - stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.019, trailing_stop=True, - trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, +tc11 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5100, 4950, 5100, 6172, 0, 0], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0], + [3, 5000, 5150, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.10}, + profit_perc=0.019, + trailing_stop=True, + trailing_only_offset_is_reached=True, + trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, - trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)] + trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)], ) # Test 12: trailing_stop should raise in candle 2 and cause a stoploss in the same candle # applying a positive trailing stop of 3% since stop_positive_offset is reached. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2 -tc12 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5100, 4950, 5100, 6172, 0, 0], - [2, 5100, 5251, 4650, 5100, 6172, 0, 0], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], - stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.019, trailing_stop=True, - trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, +tc12 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5100, 4950, 5100, 6172, 0, 0], + [2, 5100, 5251, 4650, 5100, 6172, 0, 0], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.10}, + profit_perc=0.019, + trailing_stop=True, + trailing_only_offset_is_reached=True, + trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, - trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=2)] + trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=2)], ) # Test 13: Buy and sell ROI on same candle # stop-loss: 10% (should not apply), ROI: 1% -tc13 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5100, 4950, 5100, 6172, 0, 0], - [2, 5100, 5251, 4850, 5100, 6172, 0, 0], - [3, 4850, 5050, 4750, 4750, 6172, 0, 0], - [4, 4750, 4950, 4750, 4750, 6172, 0, 0]], - stop_loss=-0.10, roi={"0": 0.01}, profit_perc=0.01, - trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=1)] +tc13 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5100, 4950, 5100, 6172, 0, 0], + [2, 5100, 5251, 4850, 5100, 6172, 0, 0], + [3, 4850, 5050, 4750, 4750, 6172, 0, 0], + [4, 4750, 4950, 4750, 4750, 6172, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.01}, + profit_perc=0.01, + trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=1)], ) # Test 14 - Buy and Stoploss on same candle # stop-loss: 5%, ROI: 10% (should not apply) -tc14 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5100, 4600, 5100, 6172, 0, 0], - [2, 5100, 5251, 4850, 5100, 6172, 0, 0], - [3, 4850, 5050, 4750, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], - stop_loss=-0.05, roi={"0": 0.10}, profit_perc=-0.05, - trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=1)] +tc14 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5100, 4600, 5100, 6172, 0, 0], + [2, 5100, 5251, 4850, 5100, 6172, 0, 0], + [3, 4850, 5050, 4750, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0], + ], + stop_loss=-0.05, + roi={"0": 0.10}, + profit_perc=-0.05, + trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=1)], ) # Test 15 - Buy and ROI on same candle, followed by buy and Stoploss on next candle # stop-loss: 5%, ROI: 10% (should not apply) -tc15 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5100, 4900, 5100, 6172, 1, 0], - [2, 5100, 5251, 4650, 5100, 6172, 0, 0], - [3, 4850, 5050, 4750, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], - stop_loss=-0.05, roi={"0": 0.01}, profit_perc=-0.04, - trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=1), - BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=2, close_tick=2)] +tc15 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5100, 4900, 5100, 6172, 1, 0], + [2, 5100, 5251, 4650, 5100, 6172, 0, 0], + [3, 4850, 5050, 4750, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0], + ], + stop_loss=-0.05, + roi={"0": 0.01}, + profit_perc=-0.04, + trades=[ + BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=1), + BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=2, close_tick=2), + ], ) # Test 16: Buy, hold for 65 min, then forceexit using roi=-1 # Causes negative profit even though sell-reason is ROI. # stop-loss: 10%, ROI: 10% (should not apply), -100% after 65 minutes (limits trade duration) -tc16 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], - [2, 4987, 5300, 4950, 5050, 6172, 0, 0], - [3, 4975, 5000, 4940, 4962, 6172, 0, 0], # Forceexit on ROI (roi=-1) - [4, 4962, 4987, 4950, 4950, 6172, 0, 0], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], - stop_loss=-0.10, roi={"0": 0.10, "65": -1}, profit_perc=-0.012, - trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)] +tc16 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], + [2, 4987, 5300, 4950, 5050, 6172, 0, 0], + [3, 4975, 5000, 4940, 4962, 6172, 0, 0], # Forceexit on ROI (roi=-1) + [4, 4962, 4987, 4950, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.10, "65": -1}, + profit_perc=-0.012, + trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)], ) # Test 17: Buy, hold for 120 mins, then forceexit using roi=-1 # Causes negative profit even though sell-reason is ROI. # stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration) # Uses open as sell-rate (special case) - since the roi-time is a multiple of the timeframe. -tc17 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], - [2, 4987, 5300, 4950, 5050, 6172, 0, 0], - [3, 4980, 5000, 4940, 4962, 6172, 0, 0], # Forceexit on ROI (roi=-1) - [4, 4962, 4987, 4950, 4950, 6172, 0, 0], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], - stop_loss=-0.10, roi={"0": 0.10, "120": -1}, profit_perc=-0.004, - trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)] +tc17 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], + [2, 4987, 5300, 4950, 5050, 6172, 0, 0], + [3, 4980, 5000, 4940, 4962, 6172, 0, 0], # Forceexit on ROI (roi=-1) + [4, 4962, 4987, 4950, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.10, "120": -1}, + profit_perc=-0.004, + trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)], ) # Test 18: Buy, hold for 120 mins, then drop ROI to 1%, causing a sell in candle 3. # stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration) # uses open_rate as sell-price -tc18 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], - [2, 4987, 5300, 4950, 5200, 6172, 0, 0], - [3, 5200, 5220, 4940, 4962, 6172, 0, 0], # Sell on ROI (sells on open) - [4, 4962, 4987, 4950, 4950, 6172, 0, 0], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], - stop_loss=-0.10, roi={"0": 0.10, "120": 0.01}, profit_perc=0.04, - trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)] +tc18 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], + [2, 4987, 5300, 4950, 5200, 6172, 0, 0], + [3, 5200, 5220, 4940, 4962, 6172, 0, 0], # Sell on ROI (sells on open) + [4, 4962, 4987, 4950, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.10, "120": 0.01}, + profit_perc=0.04, + trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)], ) # Test 19: Buy, hold for 119 mins, then drop ROI to 1%, causing a sell in candle 3. # stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration) # uses calculated ROI (1%) as sell rate, otherwise identical to tc18 -tc19 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], - [2, 4987, 5300, 4950, 5200, 6172, 0, 0], - [3, 5000, 5300, 4940, 4962, 6172, 0, 0], # Sell on ROI - [4, 4962, 4987, 4950, 4950, 6172, 0, 0], - [5, 4550, 4975, 4550, 4950, 6172, 0, 0]], - stop_loss=-0.10, roi={"0": 0.10, "120": 0.01}, profit_perc=0.01, - trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)] +tc19 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], + [2, 4987, 5300, 4950, 5200, 6172, 0, 0], + [3, 5000, 5300, 4940, 4962, 6172, 0, 0], # Sell on ROI + [4, 4962, 4987, 4950, 4950, 6172, 0, 0], + [5, 4550, 4975, 4550, 4950, 6172, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.10, "120": 0.01}, + profit_perc=0.01, + trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)], ) # Test 20: Buy, hold for 119 mins, then drop ROI to 1%, causing a sell in candle 3. # stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration) # uses calculated ROI (1%) as sell rate, otherwise identical to tc18 -tc20 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], - [2, 4987, 5300, 4950, 5200, 6172, 0, 0], - [3, 5200, 5300, 4940, 4962, 6172, 0, 0], # Sell on ROI - [4, 4962, 4987, 4950, 4950, 6172, 0, 0], - [5, 4925, 4975, 4925, 4950, 6172, 0, 0]], - stop_loss=-0.10, roi={"0": 0.10, "119": 0.01}, profit_perc=0.01, - trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)] +tc20 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], + [2, 4987, 5300, 4950, 5200, 6172, 0, 0], + [3, 5200, 5300, 4940, 4962, 6172, 0, 0], # Sell on ROI + [4, 4962, 4987, 4950, 4950, 6172, 0, 0], + [5, 4925, 4975, 4925, 4950, 6172, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.10, "119": 0.01}, + profit_perc=0.01, + trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)], ) # Test 21: trailing_stop ROI collision. # Roi should trigger before Trailing stop - otherwise Trailing stop profits can be > ROI # which cannot happen in reality # stop-loss: 10%, ROI: 4%, Trailing stop adjusted at the sell candle -tc21 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5100, 4950, 5100, 6172, 0, 0], - [2, 5100, 5251, 4650, 5100, 6172, 0, 0], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], - stop_loss=-0.10, roi={"0": 0.04}, profit_perc=0.04, trailing_stop=True, - trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, +tc21 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5100, 4950, 5100, 6172, 0, 0], + [2, 5100, 5251, 4650, 5100, 6172, 0, 0], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.04}, + profit_perc=0.04, + trailing_stop=True, + trailing_only_offset_is_reached=True, + trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, - trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=2)] + trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=2)], ) # Test 22: trailing_stop Raises in candle 2 - but ROI applies at the same time. # applying a positive trailing stop of 3% - ROI should apply before trailing stop. # stop-loss: 10%, ROI: 4%, stoploss adjusted candle 2 -tc22 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5100, 4950, 5100, 6172, 0, 0], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], - stop_loss=-0.10, roi={"0": 0.04}, profit_perc=0.04, trailing_stop=True, - trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, +tc22 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5100, 4950, 5100, 6172, 0, 0], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.04}, + profit_perc=0.04, + trailing_stop=True, + trailing_only_offset_is_reached=True, + trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, - trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=2)] + trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=2)], ) # Test 23: trailing_stop Raises in candle 2 - but ROI applies at the same time. # applying a positive trailing stop of 3% - ROI should apply before trailing stop. # stop-loss: 10%, ROI: 4%, stoploss adjusted candle 2 -tc23 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0], - [1, 5000, 5050, 4900, 4900, 6172, 0, 0, 0, 0], - [2, 4900, 4900, 4749, 4900, 6172, 0, 0, 0, 0], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0]], - stop_loss=-0.10, roi={"0": 0.04}, profit_perc=0.04, trailing_stop=True, - trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, +tc23 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0], + [1, 5000, 5050, 4900, 4900, 6172, 0, 0, 0, 0], + [2, 4900, 4900, 4749, 4900, 6172, 0, 0, 0, 0], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.04}, + profit_perc=0.04, + trailing_stop=True, + trailing_only_offset_is_reached=True, + trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, - trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=2, is_short=True)] + trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=2, is_short=True)], ) # Test 24: trailing_stop Raises in candle 2 (does not trigger) @@ -390,460 +505,620 @@ tc23 = BTContainer(data=[ # in the candle after the raised stoploss candle with ROI reason. # Stoploss would trigger in this candle too, but it's no longer relevant. # stop-loss: 10%, ROI: 4%, stoploss adjusted candle 2, ROI adjusted in candle 3 (causing the sell) -tc24 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5100, 4950, 5100, 6172, 0, 0], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0], - [3, 4850, 5251, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], - stop_loss=-0.10, roi={"0": 0.1, "119": 0.03}, profit_perc=0.03, trailing_stop=True, - trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, +tc24 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5100, 4950, 5100, 6172, 0, 0], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0], + [3, 4850, 5251, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.1, "119": 0.03}, + profit_perc=0.03, + trailing_stop=True, + trailing_only_offset_is_reached=True, + trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, - trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)] + trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)], ) # Test 25: Sell with signal sell in candle 3 (stoploss also triggers on this candle) # Stoploss at 1%. # Stoploss wins over Sell-signal (because sell-signal is acted on in the next candle) -tc25 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4986, 6172, 0, 0], - [3, 5010, 5010, 4855, 5010, 6172, 0, 1], # Triggers stoploss + sellsignal - [4, 5010, 5010, 4977, 4995, 6172, 0, 0], - [5, 4995, 4995, 4950, 4950, 6172, 0, 0]], - stop_loss=-0.01, roi={"0": 1}, profit_perc=-0.01, use_exit_signal=True, - trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=3)] +tc25 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4986, 6172, 0, 0], + [3, 5010, 5010, 4855, 5010, 6172, 0, 1], # Triggers stoploss + sellsignal + [4, 5010, 5010, 4977, 4995, 6172, 0, 0], + [5, 4995, 4995, 4950, 4950, 6172, 0, 0], + ], + stop_loss=-0.01, + roi={"0": 1}, + profit_perc=-0.01, + use_exit_signal=True, + trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=3)], ) # Test 26: Sell with signal sell in candle 3 (stoploss also triggers on this candle) # Stoploss at 1%. # Sell-signal wins over stoploss -tc26 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4986, 6172, 0, 0], - [3, 5010, 5010, 4986, 5010, 6172, 0, 1], - [4, 5010, 5010, 4855, 4995, 6172, 0, 0], # Triggers stoploss + sellsignal acted on - [5, 4995, 4995, 4950, 4950, 6172, 0, 0]], - stop_loss=-0.01, roi={"0": 1}, profit_perc=0.002, use_exit_signal=True, - trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=4)] +tc26 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4986, 6172, 0, 0], + [3, 5010, 5010, 4986, 5010, 6172, 0, 1], + [4, 5010, 5010, 4855, 4995, 6172, 0, 0], # Triggers stoploss + sellsignal acted on + [5, 4995, 4995, 4950, 4950, 6172, 0, 0], + ], + stop_loss=-0.01, + roi={"0": 1}, + profit_perc=0.002, + use_exit_signal=True, + trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=4)], ) # Test 27: (copy of test26 with leverage) # Sell with signal sell in candle 3 (stoploss also triggers on this candle) # Stoploss at 1%. # Sell-signal wins over stoploss -tc27 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4986, 6172, 0, 0], - [3, 5010, 5010, 4986, 5010, 6172, 0, 1], - [4, 5010, 5010, 4855, 4995, 6172, 0, 0], # Triggers stoploss + sellsignal acted on - [5, 4995, 4995, 4950, 4950, 6172, 0, 0]], - stop_loss=-0.05, roi={"0": 1}, profit_perc=0.002 * 5.0, use_exit_signal=True, +tc27 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4986, 6172, 0, 0], + [3, 5010, 5010, 4986, 5010, 6172, 0, 1], + [4, 5010, 5010, 4855, 4995, 6172, 0, 0], # Triggers stoploss + sellsignal acted on + [5, 4995, 4995, 4950, 4950, 6172, 0, 0], + ], + stop_loss=-0.05, + roi={"0": 1}, + profit_perc=0.002 * 5.0, + use_exit_signal=True, leverage=5.0, - trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=4)] + trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=4)], ) # Test 28: (copy of test26 with leverage and as short) # Sell with signal sell in candle 3 (stoploss also triggers on this candle) # Stoploss at 1%. # Sell-signal wins over stoploss -tc28 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5025, 4975, 4987, 6172, 0, 0, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4986, 6172, 0, 0, 0, 0], - [3, 5010, 5010, 4986, 5010, 6172, 0, 0, 0, 1], - [4, 4990, 5010, 4855, 4995, 6172, 0, 0, 0, 0], # Triggers stoploss + sellsignal acted on - [5, 4995, 4995, 4950, 4950, 6172, 0, 0, 0, 0]], - stop_loss=-0.05, roi={"0": 1}, profit_perc=0.002 * 5.0, use_exit_signal=True, +tc28 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5025, 4975, 4987, 6172, 0, 0, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4986, 6172, 0, 0, 0, 0], + [3, 5010, 5010, 4986, 5010, 6172, 0, 0, 0, 1], + [4, 4990, 5010, 4855, 4995, 6172, 0, 0, 0, 0], # Triggers stoploss + sellsignal acted on + [5, 4995, 4995, 4950, 4950, 6172, 0, 0, 0, 0], + ], + stop_loss=-0.05, + roi={"0": 1}, + profit_perc=0.002 * 5.0, + use_exit_signal=True, leverage=5.0, - trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=4, is_short=True)] + trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=4, is_short=True)], ) # Test 29: Sell with signal sell in candle 3 (ROI at signal candle) # Stoploss at 10% (irrelevant), ROI at 5% (will trigger) # Sell-signal wins over stoploss -tc29 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4986, 6172, 0, 0], - [3, 5010, 5251, 4986, 5010, 6172, 0, 1], # Triggers ROI, sell-signal - [4, 5010, 5010, 4855, 4995, 6172, 0, 0], - [5, 4995, 4995, 4950, 4950, 6172, 0, 0]], - stop_loss=-0.10, roi={"0": 0.05}, profit_perc=0.05, use_exit_signal=True, - trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)] +tc29 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4986, 6172, 0, 0], + [3, 5010, 5251, 4986, 5010, 6172, 0, 1], # Triggers ROI, sell-signal + [4, 5010, 5010, 4855, 4995, 6172, 0, 0], + [5, 4995, 4995, 4950, 4950, 6172, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.05}, + profit_perc=0.05, + use_exit_signal=True, + trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)], ) # Test 30: Sell with signal sell in candle 3 (ROI at signal candle) # Stoploss at 10% (irrelevant), ROI at 5% (will trigger) - Wins over Sell-signal -tc30 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4986, 6172, 0, 0], - [3, 5010, 5012, 4986, 5010, 6172, 0, 1], # sell-signal - [4, 5010, 5251, 4855, 4995, 6172, 0, 0], # Triggers ROI, sell-signal acted on - [5, 4995, 4995, 4950, 4950, 6172, 0, 0]], - stop_loss=-0.10, roi={"0": 0.05}, profit_perc=0.002, use_exit_signal=True, - trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=4)] +tc30 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4986, 6172, 0, 0], + [3, 5010, 5012, 4986, 5010, 6172, 0, 1], # sell-signal + [4, 5010, 5251, 4855, 4995, 6172, 0, 0], # Triggers ROI, sell-signal acted on + [5, 4995, 4995, 4950, 4950, 6172, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.05}, + profit_perc=0.002, + use_exit_signal=True, + trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=4)], ) # Test 31: trailing_stop should raise so candle 3 causes a stoploss # Same case than tc11 - but candle 3 "gaps down" - the stoploss will be above the candle, # therefore "open" will be used # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2 -tc31 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5100, 4950, 5100, 6172, 0, 0], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], - stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.03, trailing_stop=True, - trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, +tc31 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5100, 4950, 5100, 6172, 0, 0], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.10}, + profit_perc=-0.03, + trailing_stop=True, + trailing_only_offset_is_reached=True, + trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, - trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)] + trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)], ) # Test 32: (Short of test 31) trailing_stop should raise so candle 3 causes a stoploss # Same case than tc11 - but candle 3 "gaps down" - the stoploss will be above the candle, # therefore "open" will be used # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2 -tc32 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0], - [1, 5000, 5050, 4890, 4890, 6172, 0, 0, 0, 0], - [2, 4890, 4890, 4749, 4890, 6172, 0, 0, 0, 0], - [3, 5150, 5350, 4950, 4950, 6172, 0, 0, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0]], - stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.03, trailing_stop=True, - trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, +tc32 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0], + [1, 5000, 5050, 4890, 4890, 6172, 0, 0, 0, 0], + [2, 4890, 4890, 4749, 4890, 6172, 0, 0, 0, 0], + [3, 5150, 5350, 4950, 4950, 6172, 0, 0, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.10}, + profit_perc=-0.03, + trailing_stop=True, + trailing_only_offset_is_reached=True, + trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, trades=[ BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3, is_short=True) -] + ], ) # Test 33: trailing_stop should be triggered by low of next candle, without adjusting stoploss using # high of stoploss candle. # stop-loss: 10%, ROI: 10% (should not apply) -tc33 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5050, 5000, 5000, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4900, 5250, 4500, 5100, 6172, 0, 0], # Triggers trailing-stoploss - [3, 5100, 5100, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], - stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.02, trailing_stop=True, +tc33 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5050, 5000, 5000, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], # Triggers trailing-stoploss + [3, 5100, 5100, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.10}, + profit_perc=-0.02, + trailing_stop=True, trailing_stop_positive=0.03, - trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=2)] + trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=2)], ) # Test 34: trailing_stop should be triggered immediately on trade open candle. # stop-loss: 10%, ROI: 10% (should not apply) -tc34 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5500, 4900, 4900, 6172, 0, 0], # enter trade (signal on last candle) and stop - [2, 4900, 5250, 4500, 5100, 6172, 0, 0], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], - stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True, +tc34 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5500, 4900, 4900, 6172, 0, 0], # enter trade (signal on last candle) and stop + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.10}, + profit_perc=-0.01, + trailing_stop=True, trailing_stop_positive=0.01, - trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)] + trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)], ) # Test 35: trailing_stop should be triggered immediately on trade open candle. # stop-loss: 10%, ROI: 10% (should not apply) -tc35 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5500, 4900, 4900, 6172, 0, 0], # enter trade (signal on last candle) and stop - [2, 4900, 5250, 4500, 5100, 6172, 0, 0], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], - stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.01, trailing_stop=True, - trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02, +tc35 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5500, 4900, 4900, 6172, 0, 0], # enter trade (signal on last candle) and stop + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.10}, + profit_perc=0.01, + trailing_stop=True, + trailing_only_offset_is_reached=True, + trailing_stop_positive_offset=0.02, trailing_stop_positive=0.01, - trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)] + trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)], ) # Test 36: trailing_stop should be triggered immediately on trade open candle. # stop-loss: 1%, ROI: 10% (should not apply) -tc36 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5500, 4951, 5000, 6172, 0, 0], # enter trade and stop - [2, 4900, 5250, 4500, 5100, 6172, 0, 0], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], - stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True, - trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02, - trailing_stop_positive=0.01, use_custom_stoploss=True, - trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)] +tc36 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5500, 4951, 5000, 6172, 0, 0], # enter trade and stop + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0], + ], + stop_loss=-0.01, + roi={"0": 0.10}, + profit_perc=-0.01, + trailing_stop=True, + trailing_only_offset_is_reached=True, + trailing_stop_positive_offset=0.02, + trailing_stop_positive=0.01, + use_custom_stoploss=True, + trades=[BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)], ) # Test 37: trailing_stop should be triggered immediately on trade open candle. # stop-loss: 1%, ROI: 10% (should not apply) -tc37 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, 0, 0, 'buy_signal_01'], - [1, 5000, 5500, 4951, 5000, 6172, 0, 0, 0, 0, None], # enter trade and stop - [2, 4900, 5250, 4500, 5100, 6172, 0, 0, 0, 0, None], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0, 0, 0, None], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0, None]], - stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True, - trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02, - trailing_stop_positive=0.01, use_custom_stoploss=True, - trades=[BTrade( - exit_reason=ExitType.TRAILING_STOP_LOSS, - open_tick=1, - close_tick=1, - enter_tag='buy_signal_01' - )] +tc37 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, 0, 0, "buy_signal_01"], + [1, 5000, 5500, 4951, 5000, 6172, 0, 0, 0, 0, None], # enter trade and stop + [2, 4900, 5250, 4500, 5100, 6172, 0, 0, 0, 0, None], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0, 0, 0, None], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0, None], + ], + stop_loss=-0.01, + roi={"0": 0.10}, + profit_perc=-0.01, + trailing_stop=True, + trailing_only_offset_is_reached=True, + trailing_stop_positive_offset=0.02, + trailing_stop_positive=0.01, + use_custom_stoploss=True, + trades=[ + BTrade( + exit_reason=ExitType.TRAILING_STOP_LOSS, + open_tick=1, + close_tick=1, + enter_tag="buy_signal_01", + ) + ], ) # Test 38: trailing_stop should be triggered immediately on trade open candle. # copy of Test37 using shorts. # stop-loss: 1%, ROI: 10% (should not apply) -tc38 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0, 'short_signal_01'], - [1, 5000, 5049, 4500, 5000, 6172, 0, 0, 0, 0, None], # enter trade and stop - [2, 4900, 5250, 4500, 5100, 6172, 0, 0, 0, 0, None], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0, 0, 0, None], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0, None]], - stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True, - trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02, - trailing_stop_positive=0.01, use_custom_stoploss=True, - trades=[BTrade( - exit_reason=ExitType.TRAILING_STOP_LOSS, - open_tick=1, - close_tick=1, - enter_tag='short_signal_01', - is_short=True, - )] +tc38 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0, "short_signal_01"], + [1, 5000, 5049, 4500, 5000, 6172, 0, 0, 0, 0, None], # enter trade and stop + [2, 4900, 5250, 4500, 5100, 6172, 0, 0, 0, 0, None], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0, 0, 0, None], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0, None], + ], + stop_loss=-0.01, + roi={"0": 0.10}, + profit_perc=-0.01, + trailing_stop=True, + trailing_only_offset_is_reached=True, + trailing_stop_positive_offset=0.02, + trailing_stop_positive=0.01, + use_custom_stoploss=True, + trades=[ + BTrade( + exit_reason=ExitType.TRAILING_STOP_LOSS, + open_tick=1, + close_tick=1, + enter_tag="short_signal_01", + is_short=True, + ) + ], ) # Test 39: Custom-entry-price below all candles should timeout - so no trade happens. -tc39 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5500, 4951, 5000, 6172, 0, 0], # timeout - [2, 4900, 5250, 4500, 5100, 6172, 0, 0], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], - stop_loss=-0.01, roi={"0": 0.10}, profit_perc=0.0, - custom_entry_price=4200, trades=[] +tc39 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5500, 4951, 5000, 6172, 0, 0], # timeout + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0], + ], + stop_loss=-0.01, + roi={"0": 0.10}, + profit_perc=0.0, + custom_entry_price=4200, + trades=[], ) # Test 40: Custom-entry-price above all candles should have rate adjusted to "entry candle high" -tc40 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5500, 4951, 5000, 6172, 0, 0], # Timeout - [2, 4900, 5250, 4500, 5100, 6172, 0, 0], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], - stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01, - custom_entry_price=7200, trades=[ - BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=1) -]) +tc40 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5500, 4951, 5000, 6172, 0, 0], # Timeout + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0], + ], + stop_loss=-0.01, + roi={"0": 0.10}, + profit_perc=-0.01, + custom_entry_price=7200, + trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=1)], +) # Test 41: Custom-entry-price above all candles should have rate adjusted to "entry candle high" -tc41 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0], - [1, 5000, 5500, 4951, 5000, 6172, 0, 0, 0, 0], # Timeout - [2, 4900, 5250, 4500, 5100, 6172, 0, 0, 0, 0], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0]], - stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01, +tc41 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0], + [1, 5000, 5500, 4951, 5000, 6172, 0, 0, 0, 0], # Timeout + [2, 4900, 5250, 4500, 5100, 6172, 0, 0, 0, 0], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0], + ], + stop_loss=-0.01, + roi={"0": 0.10}, + profit_perc=-0.01, custom_entry_price=4000, - trades=[ - BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=1, is_short=True) -] + trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=1, is_short=True)], ) # Test 42: Custom-entry-price around candle low # Would cause immediate ROI exit, but since the trade was entered # below open, we treat this as cheating, and delay the sell by 1 candle. # details: https://github.com/freqtrade/freqtrade/issues/6261 -tc42 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5500, 4951, 4999, 6172, 0, 0], # Enter and immediate ROI - [2, 4900, 5250, 4500, 5100, 6172, 0, 0], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], - stop_loss=-0.10, roi={"0": 0.01}, profit_perc=0.01, +tc42 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5500, 4951, 4999, 6172, 0, 0], # Enter and immediate ROI + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.01}, + profit_perc=0.01, custom_entry_price=4952, - trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=2)] + trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=2)], ) # Test 43: Custom-entry-price around candle low # Would cause immediate ROI exit below close # details: https://github.com/freqtrade/freqtrade/issues/6261 -tc43 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5400, 5500, 4951, 5100, 6172, 0, 0], # Enter and immediate ROI - [2, 4900, 5250, 4500, 5100, 6172, 0, 0], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], - stop_loss=-0.10, roi={"0": 0.01}, profit_perc=0.01, +tc43 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5400, 5500, 4951, 5100, 6172, 0, 0], # Enter and immediate ROI + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.01}, + profit_perc=0.01, custom_entry_price=4952, - trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=1)] + trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=1)], ) # Test 44: Custom exit price below all candles # Price adjusted to candle Low. -tc44 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5500, 4951, 5000, 6172, 0, 0], - [2, 4900, 5250, 4900, 5100, 6172, 0, 1], # exit - but timeout - [3, 5100, 5100, 4950, 4950, 6172, 0, 0], - [4, 5000, 5100, 4950, 4950, 6172, 0, 0]], - stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.01, +tc44 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5500, 4951, 5000, 6172, 0, 0], + [2, 4900, 5250, 4900, 5100, 6172, 0, 1], # exit - but timeout + [3, 5100, 5100, 4950, 4950, 6172, 0, 0], + [4, 5000, 5100, 4950, 4950, 6172, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.10}, + profit_perc=-0.01, use_exit_signal=True, custom_exit_price=4552, - trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=3)] + trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=3)], ) # Test 45: Custom exit price above all candles # causes sell signal timeout -tc45 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5500, 4951, 5000, 6172, 0, 0], - [2, 4950, 5250, 4900, 5100, 6172, 0, 1], # exit - entry timeout - [3, 5100, 5100, 4950, 4950, 6172, 0, 0], - [4, 5000, 5100, 4950, 4950, 6172, 0, 0]], - stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.0, +tc45 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5500, 4951, 5000, 6172, 0, 0], + [2, 4950, 5250, 4900, 5100, 6172, 0, 1], # exit - entry timeout + [3, 5100, 5100, 4950, 4950, 6172, 0, 0], + [4, 5000, 5100, 4950, 4950, 6172, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.10}, + profit_perc=0.0, use_exit_signal=True, custom_exit_price=6052, - trades=[BTrade(exit_reason=ExitType.FORCE_EXIT, open_tick=1, close_tick=4)] + trades=[BTrade(exit_reason=ExitType.FORCE_EXIT, open_tick=1, close_tick=4)], ) # Test 46: (Short of tc45) Custom short exit price above below candles # causes sell signal timeout -tc46 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0], - [1, 5000, 5000, 4951, 5000, 6172, 0, 0, 0, 0], - [2, 4910, 5150, 4910, 5100, 6172, 0, 0, 0, 1], # exit - entry timeout - [3, 5100, 5100, 4950, 4950, 6172, 0, 0, 0, 0], - [4, 5000, 5100, 4950, 4950, 6172, 0, 0, 0, 0]], - stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.0, +tc46 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0], + [1, 5000, 5000, 4951, 5000, 6172, 0, 0, 0, 0], + [2, 4910, 5150, 4910, 5100, 6172, 0, 0, 0, 1], # exit - entry timeout + [3, 5100, 5100, 4950, 4950, 6172, 0, 0, 0, 0], + [4, 5000, 5100, 4950, 4950, 6172, 0, 0, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.10}, + profit_perc=0.0, use_exit_signal=True, custom_exit_price=4700, - trades=[BTrade(exit_reason=ExitType.FORCE_EXIT, open_tick=1, close_tick=4, is_short=True)] + trades=[BTrade(exit_reason=ExitType.FORCE_EXIT, open_tick=1, close_tick=4, is_short=True)], ) # Test 47: Colliding long and short signal -tc47 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, 1, 0], - [1, 5000, 5500, 4951, 5000, 6172, 0, 0, 0, 0], - [2, 4900, 5250, 4900, 5100, 6172, 0, 0, 0, 0], - [3, 5100, 5100, 4950, 4950, 6172, 0, 0, 0, 0], - [4, 5000, 5100, 4950, 4950, 6172, 0, 0, 0, 0]], - stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.0, +tc47 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, 1, 0], + [1, 5000, 5500, 4951, 5000, 6172, 0, 0, 0, 0], + [2, 4900, 5250, 4900, 5100, 6172, 0, 0, 0, 0], + [3, 5100, 5100, 4950, 4950, 6172, 0, 0, 0, 0], + [4, 5000, 5100, 4950, 4950, 6172, 0, 0, 0, 0], + ], + stop_loss=-0.10, + roi={"0": 0.10}, + profit_perc=0.0, use_exit_signal=True, - trades=[] + trades=[], ) # Test 48: Custom-entry-price below all candles - readjust order -tc48 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5500, 4951, 5000, 6172, 0, 0], # timeout - [2, 4900, 5250, 4500, 5100, 6172, 0, 0], # Order readjust - [3, 5100, 5100, 4650, 4750, 6172, 0, 1], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], - stop_loss=-0.2, roi={"0": 0.10}, profit_perc=-0.087, - use_exit_signal=True, timeout=1000, - custom_entry_price=4200, adjust_entry_price=5200, - trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=4, is_short=False)] +tc48 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5500, 4951, 5000, 6172, 0, 0], # timeout + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], # Order readjust + [3, 5100, 5100, 4650, 4750, 6172, 0, 1], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0], + ], + stop_loss=-0.2, + roi={"0": 0.10}, + profit_perc=-0.087, + use_exit_signal=True, + timeout=1000, + custom_entry_price=4200, + adjust_entry_price=5200, + trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=4, is_short=False)], ) # Test 49: Custom-entry-price short above all candles - readjust order -tc49 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0], - [1, 5000, 5200, 4951, 5000, 6172, 0, 0, 0, 0], # timeout - [2, 4900, 5250, 4900, 5100, 6172, 0, 0, 0, 0], # Order readjust - [3, 5100, 5100, 4650, 4750, 6172, 0, 0, 0, 1], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0]], - stop_loss=-0.2, roi={"0": 0.10}, profit_perc=0.05, - use_exit_signal=True, timeout=1000, - custom_entry_price=5300, adjust_entry_price=5000, - trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=4, is_short=True)] +tc49 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0], + [1, 5000, 5200, 4951, 5000, 6172, 0, 0, 0, 0], # timeout + [2, 4900, 5250, 4900, 5100, 6172, 0, 0, 0, 0], # Order readjust + [3, 5100, 5100, 4650, 4750, 6172, 0, 0, 0, 1], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0], + ], + stop_loss=-0.2, + roi={"0": 0.10}, + profit_perc=0.05, + use_exit_signal=True, + timeout=1000, + custom_entry_price=5300, + adjust_entry_price=5000, + trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=4, is_short=True)], ) # Test 50: Custom-entry-price below all candles - readjust order cancels order -tc50 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], # Enter long - place order - [1, 5000, 5500, 4951, 5000, 6172, 0, 0], # Order readjust - cancel order - [2, 4900, 5250, 4500, 5100, 6172, 0, 0], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], - stop_loss=-0.01, roi={"0": 0.10}, profit_perc=0.0, - use_exit_signal=True, timeout=1000, - custom_entry_price=4200, adjust_entry_price=None, - trades=[] +tc50 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], # Enter long - place order + [1, 5000, 5500, 4951, 5000, 6172, 0, 0], # Order readjust - cancel order + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0], + ], + stop_loss=-0.01, + roi={"0": 0.10}, + profit_perc=0.0, + use_exit_signal=True, + timeout=1000, + custom_entry_price=4200, + adjust_entry_price=None, + trades=[], ) # Test 51: Custom-entry-price below all candles - readjust order leaves order in place and timeout. -tc51 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], # Enter long - place order - [1, 5000, 5500, 4951, 5000, 6172, 0, 0], # Order readjust - replace order - [2, 4900, 5250, 4500, 5100, 6172, 0, 0], # Order readjust - maintain order - [3, 5100, 5100, 4650, 4750, 6172, 0, 0], # Timeout - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], - stop_loss=-0.01, roi={"0": 0.10}, profit_perc=0.0, - use_exit_signal=True, timeout=60, - custom_entry_price=4200, adjust_entry_price=4100, - trades=[] +tc51 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], # Enter long - place order + [1, 5000, 5500, 4951, 5000, 6172, 0, 0], # Order readjust - replace order + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], # Order readjust - maintain order + [3, 5100, 5100, 4650, 4750, 6172, 0, 0], # Timeout + [4, 4750, 4950, 4350, 4750, 6172, 0, 0], + ], + stop_loss=-0.01, + roi={"0": 0.10}, + profit_perc=0.0, + use_exit_signal=True, + timeout=60, + custom_entry_price=4200, + adjust_entry_price=4100, + trades=[], ) # Test 52: Custom-entry-price below all candles - readjust order - stoploss -tc52 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5500, 4951, 5000, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4900, 5250, 4500, 5100, 6172, 0, 0], # Order readjust - [3, 5100, 5100, 4650, 4750, 6172, 0, 0], # stoploss hit? - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], - stop_loss=-0.03, roi={}, profit_perc=-0.03, - use_exit_signal=True, timeout=1000, - custom_entry_price=4200, adjust_entry_price=5200, - trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2, is_short=False)] +tc52 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5500, 4951, 5000, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], # Order readjust + [3, 5100, 5100, 4650, 4750, 6172, 0, 0], # stoploss hit? + [4, 4750, 4950, 4350, 4750, 6172, 0, 0], + ], + stop_loss=-0.03, + roi={}, + profit_perc=-0.03, + use_exit_signal=True, + timeout=1000, + custom_entry_price=4200, + adjust_entry_price=5200, + trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2, is_short=False)], ) # Test 53: Custom-entry-price short above all candles - readjust order - stoploss -tc53 = BTContainer(data=[ - # D O H L C V EL XL ES Xs BT - [0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0], - [1, 5000, 5200, 4951, 5000, 6172, 0, 0, 0, 0], # enter trade (signal on last candle) - [2, 4900, 5250, 4900, 5100, 6172, 0, 0, 0, 0], # Order readjust - [3, 5100, 5100, 4650, 4750, 6172, 0, 0, 0, 1], # stoploss hit? - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0]], - stop_loss=-0.03, roi={"0": 0.10}, profit_perc=-0.03, - use_exit_signal=True, timeout=1000, - custom_entry_price=5300, adjust_entry_price=5000, - trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2, is_short=True)] +tc53 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0], + [1, 5000, 5200, 4951, 5000, 6172, 0, 0, 0, 0], # enter trade (signal on last candle) + [2, 4900, 5250, 4900, 5100, 6172, 0, 0, 0, 0], # Order readjust + [3, 5100, 5100, 4650, 4750, 6172, 0, 0, 0, 1], # stoploss hit? + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0], + ], + stop_loss=-0.03, + roi={"0": 0.10}, + profit_perc=-0.03, + use_exit_signal=True, + timeout=1000, + custom_entry_price=5300, + adjust_entry_price=5000, + trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2, is_short=True)], ) TESTS = [ @@ -915,10 +1190,12 @@ def test_backtest_results(default_conf, mocker, caplog, data: BTContainer) -> No default_conf["trailing_stop"] = data.trailing_stop default_conf["trailing_only_offset_is_reached"] = data.trailing_only_offset_is_reached if data.timeout: - default_conf['unfilledtimeout'].update({ - 'entry': data.timeout, - 'exit': data.timeout, - }) + default_conf["unfilledtimeout"].update( + { + "entry": data.timeout, + "exit": data.timeout, + } + ) # Only add this to configuration If it's necessary if data.trailing_stop_positive is not None: default_conf["trailing_stop_positive"] = data.trailing_stop_positive @@ -929,7 +1206,7 @@ def test_backtest_results(default_conf, mocker, caplog, data: BTContainer) -> No patch_exchange(mocker) mocker.patch(f"{EXMS}.get_fee", return_value=0.0) mocker.patch(f"{EXMS}.get_min_pair_stake_amount", return_value=0.00001) - mocker.patch(f"{EXMS}.get_max_pair_stake_amount", return_value=float('inf')) + mocker.patch(f"{EXMS}.get_max_pair_stake_amount", return_value=float("inf")) mocker.patch(f"{EXMS}.get_max_leverage", return_value=100) mocker.patch(f"{EXMS}.calculate_funding_fees", return_value=0) frame = _build_backtest_dataframe(data.data) @@ -961,7 +1238,7 @@ def test_backtest_results(default_conf, mocker, caplog, data: BTContainer) -> No end_date=max_date, ) - results = result['results'] + results = result["results"] assert len(results) == len(data.trades) assert round(results["profit_ratio"].sum(), 3) == round(data.profit_perc, 3) diff --git a/tests/optimize/test_backtesting_adjust_position.py b/tests/optimize/test_backtesting_adjust_position.py index cf30489e1..64df6537b 100644 --- a/tests/optimize/test_backtesting_adjust_position.py +++ b/tests/optimize/test_backtesting_adjust_position.py @@ -16,25 +16,26 @@ from tests.conftest import EXMS, patch_exchange def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) -> None: - default_conf['use_exit_signal'] = False - default_conf['max_open_trades'] = 10 - mocker.patch(f'{EXMS}.get_fee', fee) - mocker.patch('freqtrade.optimize.backtesting.amount_to_contract_precision', - lambda x, *args, **kwargs: round(x, 8)) + default_conf["use_exit_signal"] = False + default_conf["max_open_trades"] = 10 + mocker.patch(f"{EXMS}.get_fee", fee) + mocker.patch( + "freqtrade.optimize.backtesting.amount_to_contract_precision", + lambda x, *args, **kwargs: round(x, 8), + ) mocker.patch(f"{EXMS}.get_min_pair_stake_amount", return_value=0.00001) - mocker.patch(f"{EXMS}.get_max_pair_stake_amount", return_value=float('inf')) + mocker.patch(f"{EXMS}.get_max_pair_stake_amount", return_value=float("inf")) patch_exchange(mocker) - default_conf.update({ - "stake_amount": 100.0, - "dry_run_wallet": 1000.0, - "strategy": "StrategyTestV3" - }) + default_conf.update( + {"stake_amount": 100.0, "dry_run_wallet": 1000.0, "strategy": "StrategyTestV3"} + ) backtesting = Backtesting(default_conf) backtesting._set_strategy(backtesting.strategylist[0]) - pair = 'UNITTEST/BTC' - timerange = TimeRange('date', None, 1517227800, 0) - data = history.load_data(datadir=testdatadir, timeframe='5m', pairs=['UNITTEST/BTC'], - timerange=timerange) + pair = "UNITTEST/BTC" + timerange = TimeRange("date", None, 1517227800, 0) + data = history.load_data( + datadir=testdatadir, timeframe="5m", pairs=["UNITTEST/BTC"], timerange=timerange + ) backtesting.strategy.position_adjustment_enable = True processed = backtesting.strategy.advise_all_indicators(data) min_date, max_date = get_timerange(processed) @@ -43,47 +44,50 @@ def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) -> start_date=min_date, end_date=max_date, ) - results = result['results'] + results = result["results"] assert not results.empty assert len(results) == 2 expected = pd.DataFrame( - {'pair': [pair, pair], - 'stake_amount': [500.0, 100.0], - 'max_stake_amount': [500.0, 100], - 'amount': [4806.87657523, 970.63960782], - 'open_date': pd.to_datetime([dt_utc(2018, 1, 29, 18, 40, 0), - dt_utc(2018, 1, 30, 3, 30, 0)], utc=True - ), - 'close_date': pd.to_datetime([dt_utc(2018, 1, 29, 22, 00, 0), - dt_utc(2018, 1, 30, 4, 10, 0)], utc=True), - 'open_rate': [0.10401764891917063, 0.10302485], - 'close_rate': [0.10453904064307624, 0.10354126528822055], - 'fee_open': [0.0025, 0.0025], - 'fee_close': [0.0025, 0.0025], - 'trade_duration': [200, 40], - 'profit_ratio': [0.0, 0.0], - 'profit_abs': [0.0, 0.0], - 'exit_reason': [ExitType.ROI.value, ExitType.ROI.value], - 'initial_stop_loss_abs': [0.0940005, 0.092722365], - 'initial_stop_loss_ratio': [-0.1, -0.1], - 'stop_loss_abs': [0.0940005, 0.092722365], - 'stop_loss_ratio': [-0.1, -0.1], - 'min_rate': [0.10370188, 0.10300000000000001], - 'max_rate': [0.10481985, 0.10388887000000001], - 'is_open': [False, False], - 'enter_tag': ['', ''], - 'leverage': [1.0, 1.0], - 'is_short': [False, False], - 'open_timestamp': [1517251200000, 1517283000000], - 'close_timestamp': [1517263200000, 1517285400000], - }) - results_no = results.drop(columns=['orders']) + { + "pair": [pair, pair], + "stake_amount": [500.0, 100.0], + "max_stake_amount": [500.0, 100], + "amount": [4806.87657523, 970.63960782], + "open_date": pd.to_datetime( + [dt_utc(2018, 1, 29, 18, 40, 0), dt_utc(2018, 1, 30, 3, 30, 0)], utc=True + ), + "close_date": pd.to_datetime( + [dt_utc(2018, 1, 29, 22, 00, 0), dt_utc(2018, 1, 30, 4, 10, 0)], utc=True + ), + "open_rate": [0.10401764891917063, 0.10302485], + "close_rate": [0.10453904064307624, 0.10354126528822055], + "fee_open": [0.0025, 0.0025], + "fee_close": [0.0025, 0.0025], + "trade_duration": [200, 40], + "profit_ratio": [0.0, 0.0], + "profit_abs": [0.0, 0.0], + "exit_reason": [ExitType.ROI.value, ExitType.ROI.value], + "initial_stop_loss_abs": [0.0940005, 0.092722365], + "initial_stop_loss_ratio": [-0.1, -0.1], + "stop_loss_abs": [0.0940005, 0.092722365], + "stop_loss_ratio": [-0.1, -0.1], + "min_rate": [0.10370188, 0.10300000000000001], + "max_rate": [0.10481985, 0.10388887000000001], + "is_open": [False, False], + "enter_tag": ["", ""], + "leverage": [1.0, 1.0], + "is_short": [False, False], + "open_timestamp": [1517251200000, 1517283000000], + "close_timestamp": [1517263200000, 1517285400000], + } + ) + results_no = results.drop(columns=["orders"]) pd.testing.assert_frame_equal(results_no, expected, check_exact=True) data_pair = processed[pair] - assert len(results.iloc[0]['orders']) == 6 - assert len(results.iloc[1]['orders']) == 2 + assert len(results.iloc[0]["orders"]) == 6 + assert len(results.iloc[1]["orders"]) == 2 for _, t in results.iterrows(): ln = data_pair.loc[data_pair["date"] == t["open_date"]] @@ -91,65 +95,65 @@ def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) -> assert ln is not None # check close trade rate aligns to close rate or is between high and low ln = data_pair.loc[data_pair["date"] == t["close_date"]] - assert (round(ln.iloc[0]["open"], 6) == round(t["close_rate"], 6) or - round(ln.iloc[0]["low"], 6) < round( - t["close_rate"], 6) < round(ln.iloc[0]["high"], 6)) + assert round(ln.iloc[0]["open"], 6) == round(t["close_rate"], 6) or round( + ln.iloc[0]["low"], 6 + ) < round(t["close_rate"], 6) < round(ln.iloc[0]["high"], 6) -@pytest.mark.parametrize('leverage', [ - 1, 2 -]) +@pytest.mark.parametrize("leverage", [1, 2]) def test_backtest_position_adjustment_detailed(default_conf, fee, mocker, leverage) -> None: - default_conf['use_exit_signal'] = False - mocker.patch(f'{EXMS}.get_fee', fee) + default_conf["use_exit_signal"] = False + mocker.patch(f"{EXMS}.get_fee", fee) mocker.patch(f"{EXMS}.get_min_pair_stake_amount", return_value=10) - mocker.patch(f"{EXMS}.get_max_pair_stake_amount", return_value=float('inf')) + mocker.patch(f"{EXMS}.get_max_pair_stake_amount", return_value=float("inf")) mocker.patch(f"{EXMS}.get_max_leverage", return_value=10) mocker.patch(f"{EXMS}.get_maintenance_ratio_and_amt", return_value=(0.1, 0.1)) - mocker.patch('freqtrade.optimize.backtesting.Backtesting._run_funding_fees') + mocker.patch("freqtrade.optimize.backtesting.Backtesting._run_funding_fees") patch_exchange(mocker) - default_conf.update({ - "stake_amount": 100.0, - "dry_run_wallet": 1000.0, - "strategy": "StrategyTestV3", - "trading_mode": "futures", - "margin_mode": "isolated", - }) - default_conf['pairlists'] = [{'method': 'StaticPairList', 'allow_inactive': True}] + default_conf.update( + { + "stake_amount": 100.0, + "dry_run_wallet": 1000.0, + "strategy": "StrategyTestV3", + "trading_mode": "futures", + "margin_mode": "isolated", + } + ) + default_conf["pairlists"] = [{"method": "StaticPairList", "allow_inactive": True}] backtesting = Backtesting(default_conf) backtesting._can_short = True backtesting._set_strategy(backtesting.strategylist[0]) - pair = 'XRP/USDT:USDT' + pair = "XRP/USDT:USDT" row_enter = [ - pd.Timestamp(year=2020, month=1, day=1, hour=4, minute=0), - 2.1, # Open - 2.2, # High - 1.9, # Low - 2.1, # Close - 1, # enter_long - 0, # exit_long - 0, # enter_short - 0, # exit_short - '', # enter_tag - '', # exit_tag - ] + pd.Timestamp(year=2020, month=1, day=1, hour=4, minute=0), + 2.1, # Open + 2.2, # High + 1.9, # Low + 2.1, # Close + 1, # enter_long + 0, # exit_long + 0, # enter_short + 0, # exit_short + "", # enter_tag + "", # exit_tag + ] # Exit row - with slightly different values row_exit = [ - pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0), - 2.2, # Open - 2.3, # High - 2.0, # Low - 2.2, # Close - 1, # enter_long - 0, # exit_long - 0, # enter_short - 0, # exit_short - '', # enter_tag - '', # exit_tag - ] + pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0), + 2.2, # Open + 2.3, # High + 2.0, # Low + 2.2, # Close + 1, # enter_long + 0, # exit_long + 0, # enter_short + 0, # exit_short + "", # enter_tag + "", # exit_tag + ] backtesting.strategy.leverage = MagicMock(return_value=leverage) - trade = backtesting._enter_trade(pair, row=row_enter, direction='long') + trade = backtesting._enter_trade(pair, row=row_enter, direction="long") current_time = row_enter[0].to_pydatetime() assert trade assert pytest.approx(trade.stake_amount) == 100.0 @@ -164,7 +168,7 @@ def test_backtest_position_adjustment_detailed(default_conf, fee, mocker, levera assert pytest.approx(trade.amount) == 47.61904762 * leverage assert len(trade.orders) == 1 # Increase position by 100 - backtesting.strategy.adjust_trade_position = MagicMock(return_value=(100, 'PartIncrease')) + backtesting.strategy.adjust_trade_position = MagicMock(return_value=(100, "PartIncrease")) trade = backtesting._get_adjust_trade_entry_for_candle(trade, row_enter, current_time) @@ -173,7 +177,7 @@ def test_backtest_position_adjustment_detailed(default_conf, fee, mocker, levera assert pytest.approx(trade.stake_amount) == 200.0 assert pytest.approx(trade.amount) == 95.23809524 * leverage assert len(trade.orders) == 2 - assert trade.orders[-1].ft_order_tag == 'PartIncrease' + assert trade.orders[-1].ft_order_tag == "PartIncrease" assert pytest.approx(trade.liquidation_price) == liq_price # Reduce by more than amount - no change to trade. @@ -190,14 +194,14 @@ def test_backtest_position_adjustment_detailed(default_conf, fee, mocker, levera assert pytest.approx(trade.liquidation_price) == liq_price # Reduce position by 50 - backtesting.strategy.adjust_trade_position = MagicMock(return_value=(-100, 'partDecrease')) + backtesting.strategy.adjust_trade_position = MagicMock(return_value=(-100, "partDecrease")) trade = backtesting._get_adjust_trade_entry_for_candle(trade, row_exit, current_time) assert trade assert pytest.approx(trade.stake_amount) == 100.0 assert pytest.approx(trade.amount) == 47.61904762 * leverage assert len(trade.orders) == 3 - assert trade.orders[-1].ft_order_tag == 'partDecrease' + assert trade.orders[-1].ft_order_tag == "partDecrease" assert trade.nr_of_successful_entries == 2 assert trade.nr_of_successful_exits == 1 assert pytest.approx(trade.liquidation_price) == liq_price diff --git a/tests/test_main.py b/tests/test_main.py index 44e65f66a..b230d4e99 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -32,161 +32,162 @@ def test_parse_args_backtesting(mocker) -> None: further argument parsing is done in test_arguments.py """ mocker.patch.object(Path, "is_file", MagicMock(side_effect=[False, True])) - backtesting_mock = mocker.patch('freqtrade.commands.start_backtesting') + backtesting_mock = mocker.patch("freqtrade.commands.start_backtesting") backtesting_mock.__name__ = PropertyMock("start_backtesting") # it's sys.exit(0) at the end of backtesting with pytest.raises(SystemExit): - main(['backtesting']) + main(["backtesting"]) assert backtesting_mock.call_count == 1 call_args = backtesting_mock.call_args[0][0] - assert call_args['config'] == ['config.json'] - assert call_args['verbosity'] == 0 - assert call_args['command'] == 'backtesting' - assert call_args['func'] is not None - assert callable(call_args['func']) - assert call_args['timeframe'] is None + assert call_args["config"] == ["config.json"] + assert call_args["verbosity"] == 0 + assert call_args["command"] == "backtesting" + assert call_args["func"] is not None + assert callable(call_args["func"]) + assert call_args["timeframe"] is None def test_main_start_hyperopt(mocker) -> None: - mocker.patch.object(Path, 'is_file', MagicMock(side_effect=[False, True])) - hyperopt_mock = mocker.patch('freqtrade.commands.start_hyperopt', MagicMock()) - hyperopt_mock.__name__ = PropertyMock('start_hyperopt') + mocker.patch.object(Path, "is_file", MagicMock(side_effect=[False, True])) + hyperopt_mock = mocker.patch("freqtrade.commands.start_hyperopt", MagicMock()) + hyperopt_mock.__name__ = PropertyMock("start_hyperopt") # it's sys.exit(0) at the end of hyperopt with pytest.raises(SystemExit): - main(['hyperopt']) + main(["hyperopt"]) assert hyperopt_mock.call_count == 1 call_args = hyperopt_mock.call_args[0][0] - assert call_args['config'] == ['config.json'] - assert call_args['verbosity'] == 0 - assert call_args['command'] == 'hyperopt' - assert call_args['func'] is not None - assert callable(call_args['func']) + assert call_args["config"] == ["config.json"] + assert call_args["verbosity"] == 0 + assert call_args["command"] == "hyperopt" + assert call_args["func"] is not None + assert callable(call_args["func"]) def test_main_fatal_exception(mocker, default_conf, caplog) -> None: patch_exchange(mocker) - mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock()) - mocker.patch('freqtrade.worker.Worker._worker', MagicMock(side_effect=Exception)) + mocker.patch("freqtrade.freqtradebot.FreqtradeBot.cleanup", MagicMock()) + mocker.patch("freqtrade.worker.Worker._worker", MagicMock(side_effect=Exception)) patched_configuration_load_config_file(mocker, default_conf) - mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - mocker.patch('freqtrade.freqtradebot.init_db', MagicMock()) + mocker.patch("freqtrade.freqtradebot.RPCManager", MagicMock()) + mocker.patch("freqtrade.freqtradebot.init_db", MagicMock()) - args = ['trade', '-c', 'tests/testdata/testconfigs/main_test_config.json'] + args = ["trade", "-c", "tests/testdata/testconfigs/main_test_config.json"] # Test Main + the KeyboardInterrupt exception with pytest.raises(SystemExit): main(args) - assert log_has('Using config: tests/testdata/testconfigs/main_test_config.json ...', caplog) - assert log_has('Fatal exception!', caplog) + assert log_has("Using config: tests/testdata/testconfigs/main_test_config.json ...", caplog) + assert log_has("Fatal exception!", caplog) def test_main_keyboard_interrupt(mocker, default_conf, caplog) -> None: patch_exchange(mocker) - mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock()) - mocker.patch('freqtrade.worker.Worker._worker', MagicMock(side_effect=KeyboardInterrupt)) + mocker.patch("freqtrade.freqtradebot.FreqtradeBot.cleanup", MagicMock()) + mocker.patch("freqtrade.worker.Worker._worker", MagicMock(side_effect=KeyboardInterrupt)) patched_configuration_load_config_file(mocker, default_conf) - mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - mocker.patch('freqtrade.wallets.Wallets.update', MagicMock()) - mocker.patch('freqtrade.freqtradebot.init_db', MagicMock()) + mocker.patch("freqtrade.freqtradebot.RPCManager", MagicMock()) + mocker.patch("freqtrade.wallets.Wallets.update", MagicMock()) + mocker.patch("freqtrade.freqtradebot.init_db", MagicMock()) - args = ['trade', '-c', 'tests/testdata/testconfigs/main_test_config.json'] + args = ["trade", "-c", "tests/testdata/testconfigs/main_test_config.json"] # Test Main + the KeyboardInterrupt exception with pytest.raises(SystemExit): main(args) - assert log_has('Using config: tests/testdata/testconfigs/main_test_config.json ...', caplog) - assert log_has('SIGINT received, aborting ...', caplog) + assert log_has("Using config: tests/testdata/testconfigs/main_test_config.json ...", caplog) + assert log_has("SIGINT received, aborting ...", caplog) def test_main_operational_exception(mocker, default_conf, caplog) -> None: patch_exchange(mocker) - mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock()) + mocker.patch("freqtrade.freqtradebot.FreqtradeBot.cleanup", MagicMock()) mocker.patch( - 'freqtrade.worker.Worker._worker', - MagicMock(side_effect=FreqtradeException('Oh snap!')) + "freqtrade.worker.Worker._worker", MagicMock(side_effect=FreqtradeException("Oh snap!")) ) patched_configuration_load_config_file(mocker, default_conf) - mocker.patch('freqtrade.wallets.Wallets.update', MagicMock()) - mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - mocker.patch('freqtrade.freqtradebot.init_db', MagicMock()) + mocker.patch("freqtrade.wallets.Wallets.update", MagicMock()) + mocker.patch("freqtrade.freqtradebot.RPCManager", MagicMock()) + mocker.patch("freqtrade.freqtradebot.init_db", MagicMock()) - args = ['trade', '-c', 'tests/testdata/testconfigs/main_test_config.json'] + args = ["trade", "-c", "tests/testdata/testconfigs/main_test_config.json"] # Test Main + the KeyboardInterrupt exception with pytest.raises(SystemExit): main(args) - assert log_has('Using config: tests/testdata/testconfigs/main_test_config.json ...', caplog) - assert log_has('Oh snap!', caplog) + assert log_has("Using config: tests/testdata/testconfigs/main_test_config.json ...", caplog) + assert log_has("Oh snap!", caplog) def test_main_operational_exception1(mocker, default_conf, caplog) -> None: patch_exchange(mocker) mocker.patch( - 'freqtrade.commands.list_commands.list_available_exchanges', - MagicMock(side_effect=ValueError('Oh snap!')) + "freqtrade.commands.list_commands.list_available_exchanges", + MagicMock(side_effect=ValueError("Oh snap!")), ) patched_configuration_load_config_file(mocker, default_conf) - args = ['list-exchanges'] + args = ["list-exchanges"] # Test Main + the KeyboardInterrupt exception with pytest.raises(SystemExit): main(args) - assert log_has('Fatal exception!', caplog) - assert not log_has_re(r'SIGINT.*', caplog) + assert log_has("Fatal exception!", caplog) + assert not log_has_re(r"SIGINT.*", caplog) mocker.patch( - 'freqtrade.commands.list_commands.list_available_exchanges', - MagicMock(side_effect=KeyboardInterrupt) + "freqtrade.commands.list_commands.list_available_exchanges", + MagicMock(side_effect=KeyboardInterrupt), ) with pytest.raises(SystemExit): main(args) - assert log_has_re(r'SIGINT.*', caplog) + assert log_has_re(r"SIGINT.*", caplog) def test_main_ConfigurationError(mocker, default_conf, caplog) -> None: patch_exchange(mocker) mocker.patch( - 'freqtrade.commands.list_commands.list_available_exchanges', - MagicMock(side_effect=ConfigurationError('Oh snap!')) + "freqtrade.commands.list_commands.list_available_exchanges", + MagicMock(side_effect=ConfigurationError("Oh snap!")), ) patched_configuration_load_config_file(mocker, default_conf) - args = ['list-exchanges'] + args = ["list-exchanges"] # Test Main + the KeyboardInterrupt exception with pytest.raises(SystemExit): main(args) - assert log_has_re('Configuration error: Oh snap!', caplog) + assert log_has_re("Configuration error: Oh snap!", caplog) def test_main_reload_config(mocker, default_conf, caplog) -> None: patch_exchange(mocker) - mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock()) + mocker.patch("freqtrade.freqtradebot.FreqtradeBot.cleanup", MagicMock()) # Simulate Running, reload, running workflow - worker_mock = MagicMock(side_effect=[State.RUNNING, - State.RELOAD_CONFIG, - State.RUNNING, - OperationalException("Oh snap!")]) - mocker.patch('freqtrade.worker.Worker._worker', worker_mock) + worker_mock = MagicMock( + side_effect=[ + State.RUNNING, + State.RELOAD_CONFIG, + State.RUNNING, + OperationalException("Oh snap!"), + ] + ) + mocker.patch("freqtrade.worker.Worker._worker", worker_mock) patched_configuration_load_config_file(mocker, default_conf) - mocker.patch('freqtrade.wallets.Wallets.update', MagicMock()) - reconfigure_mock = mocker.patch('freqtrade.worker.Worker._reconfigure', MagicMock()) + mocker.patch("freqtrade.wallets.Wallets.update", MagicMock()) + reconfigure_mock = mocker.patch("freqtrade.worker.Worker._reconfigure", MagicMock()) - mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - mocker.patch('freqtrade.freqtradebot.init_db', MagicMock()) + mocker.patch("freqtrade.freqtradebot.RPCManager", MagicMock()) + mocker.patch("freqtrade.freqtradebot.init_db", MagicMock()) - args = Arguments([ - 'trade', - '-c', - 'tests/testdata/testconfigs/main_test_config.json' - ]).get_parsed_arg() + args = Arguments( + ["trade", "-c", "tests/testdata/testconfigs/main_test_config.json"] + ).get_parsed_arg() worker = Worker(args=args, config=default_conf) with pytest.raises(SystemExit): - main(['trade', '-c', 'tests/testdata/testconfigs/main_test_config.json']) + main(["trade", "-c", "tests/testdata/testconfigs/main_test_config.json"]) - assert log_has('Using config: tests/testdata/testconfigs/main_test_config.json ...', caplog) + assert log_has("Using config: tests/testdata/testconfigs/main_test_config.json ...", caplog) assert worker_mock.call_count == 4 assert reconfigure_mock.call_count == 1 assert isinstance(worker.freqtrade, FreqtradeBot) @@ -194,27 +195,24 @@ def test_main_reload_config(mocker, default_conf, caplog) -> None: def test_reconfigure(mocker, default_conf) -> None: patch_exchange(mocker) - mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock()) + mocker.patch("freqtrade.freqtradebot.FreqtradeBot.cleanup", MagicMock()) mocker.patch( - 'freqtrade.worker.Worker._worker', - MagicMock(side_effect=OperationalException('Oh snap!')) + "freqtrade.worker.Worker._worker", MagicMock(side_effect=OperationalException("Oh snap!")) ) - mocker.patch('freqtrade.wallets.Wallets.update', MagicMock()) + mocker.patch("freqtrade.wallets.Wallets.update", MagicMock()) patched_configuration_load_config_file(mocker, default_conf) - mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - mocker.patch('freqtrade.freqtradebot.init_db', MagicMock()) + mocker.patch("freqtrade.freqtradebot.RPCManager", MagicMock()) + mocker.patch("freqtrade.freqtradebot.init_db", MagicMock()) - args = Arguments([ - 'trade', - '-c', - 'tests/testdata/testconfigs/main_test_config.json' - ]).get_parsed_arg() + args = Arguments( + ["trade", "-c", "tests/testdata/testconfigs/main_test_config.json"] + ).get_parsed_arg() worker = Worker(args=args, config=default_conf) freqtrade = worker.freqtrade # Renew mock to return modified data conf = deepcopy(default_conf) - conf['stake_amount'] += 1 + conf["stake_amount"] += 1 patched_configuration_load_config_file(mocker, conf) worker._config = conf @@ -224,4 +222,4 @@ def test_reconfigure(mocker, default_conf) -> None: # Verify we have a new instance with the new config assert freqtrade is not freqtrade2 - assert freqtrade.config['stake_amount'] + 1 == freqtrade2.config['stake_amount'] + assert freqtrade.config["stake_amount"] + 1 == freqtrade2.config["stake_amount"] diff --git a/tests/test_misc.py b/tests/test_misc.py index b342eaa9f..f213d6759 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -23,33 +23,31 @@ from freqtrade.misc import ( def test_file_dump_json(mocker) -> None: - file_open = mocker.patch('freqtrade.misc.Path.open', MagicMock()) - json_dump = mocker.patch('rapidjson.dump', MagicMock()) - file_dump_json(Path('somefile'), [1, 2, 3]) + file_open = mocker.patch("freqtrade.misc.Path.open", MagicMock()) + json_dump = mocker.patch("rapidjson.dump", MagicMock()) + file_dump_json(Path("somefile"), [1, 2, 3]) assert file_open.call_count == 1 assert json_dump.call_count == 1 - file_open = mocker.patch('freqtrade.misc.gzip.open', MagicMock()) - json_dump = mocker.patch('rapidjson.dump', MagicMock()) - file_dump_json(Path('somefile'), [1, 2, 3], True) + file_open = mocker.patch("freqtrade.misc.gzip.open", MagicMock()) + json_dump = mocker.patch("rapidjson.dump", MagicMock()) + file_dump_json(Path("somefile"), [1, 2, 3], True) assert file_open.call_count == 1 assert json_dump.call_count == 1 def test_file_load_json(mocker, testdatadir) -> None: - # 7m .json does not exist - ret = file_load_json(testdatadir / 'UNITTEST_BTC-7m.json') + ret = file_load_json(testdatadir / "UNITTEST_BTC-7m.json") assert not ret # 1m json exists (but no .gz exists) - ret = file_load_json(testdatadir / 'UNITTEST_BTC-1m.json') + ret = file_load_json(testdatadir / "UNITTEST_BTC-1m.json") assert ret # 8 .json is empty and will fail if it's loaded. .json.gz is a copy of 1.json - ret = file_load_json(testdatadir / 'UNITTEST_BTC-8m.json') + ret = file_load_json(testdatadir / "UNITTEST_BTC-8m.json") assert ret def test_is_file_in_dir(tmp_path): - # Create a temporary directory and file dir_path = tmp_path / "subdir" dir_path.mkdir() @@ -66,69 +64,72 @@ def test_is_file_in_dir(tmp_path): assert is_file_in_dir(file_path2, tmp_path) is False -@pytest.mark.parametrize("pair,expected_result", [ - ("ETH/BTC", 'ETH_BTC'), - ("ETH/USDT", 'ETH_USDT'), - ("ETH/USDT:USDT", 'ETH_USDT_USDT'), # swap with USDT as settlement currency - ("ETH/USD:USD", 'ETH_USD_USD'), # swap with USD as settlement currency - ("AAVE/USD:USD", 'AAVE_USD_USD'), # swap with USDT as settlement currency - ("ETH/USDT:USDT-210625", 'ETH_USDT_USDT-210625'), # expiring futures - ("Fabric Token/ETH", 'Fabric_Token_ETH'), - ("ETHH20", 'ETHH20'), - (".XBTBON2H", '_XBTBON2H'), - ("ETHUSD.d", 'ETHUSD_d'), - ("ADA-0327", 'ADA-0327'), - ("BTC-USD-200110", 'BTC-USD-200110'), - ("BTC-PERP:USDT", 'BTC-PERP_USDT'), - ("F-AKRO/USDT", 'F-AKRO_USDT'), - ("LC+/ETH", 'LC__ETH'), - ("CMT@18/ETH", 'CMT_18_ETH'), - ("LBTC:1022/SAI", 'LBTC_1022_SAI'), - ("$PAC/BTC", '_PAC_BTC'), - ("ACC_OLD/BTC", 'ACC_OLD_BTC'), -]) +@pytest.mark.parametrize( + "pair,expected_result", + [ + ("ETH/BTC", "ETH_BTC"), + ("ETH/USDT", "ETH_USDT"), + ("ETH/USDT:USDT", "ETH_USDT_USDT"), # swap with USDT as settlement currency + ("ETH/USD:USD", "ETH_USD_USD"), # swap with USD as settlement currency + ("AAVE/USD:USD", "AAVE_USD_USD"), # swap with USDT as settlement currency + ("ETH/USDT:USDT-210625", "ETH_USDT_USDT-210625"), # expiring futures + ("Fabric Token/ETH", "Fabric_Token_ETH"), + ("ETHH20", "ETHH20"), + (".XBTBON2H", "_XBTBON2H"), + ("ETHUSD.d", "ETHUSD_d"), + ("ADA-0327", "ADA-0327"), + ("BTC-USD-200110", "BTC-USD-200110"), + ("BTC-PERP:USDT", "BTC-PERP_USDT"), + ("F-AKRO/USDT", "F-AKRO_USDT"), + ("LC+/ETH", "LC__ETH"), + ("CMT@18/ETH", "CMT_18_ETH"), + ("LBTC:1022/SAI", "LBTC_1022_SAI"), + ("$PAC/BTC", "_PAC_BTC"), + ("ACC_OLD/BTC", "ACC_OLD_BTC"), + ], +) def test_pair_to_filename(pair, expected_result): pair_s = pair_to_filename(pair) assert pair_s == expected_result def test_safe_value_fallback(): - dict1 = {'keya': None, 'keyb': 2, 'keyc': 5, 'keyd': None} - assert safe_value_fallback(dict1, 'keya', 'keyb') == 2 - assert safe_value_fallback(dict1, 'keyb', 'keya') == 2 + dict1 = {"keya": None, "keyb": 2, "keyc": 5, "keyd": None} + assert safe_value_fallback(dict1, "keya", "keyb") == 2 + assert safe_value_fallback(dict1, "keyb", "keya") == 2 - assert safe_value_fallback(dict1, 'keyb', 'keyc') == 2 - assert safe_value_fallback(dict1, 'keya', 'keyc') == 5 + assert safe_value_fallback(dict1, "keyb", "keyc") == 2 + assert safe_value_fallback(dict1, "keya", "keyc") == 5 - assert safe_value_fallback(dict1, 'keyc', 'keyb') == 5 + assert safe_value_fallback(dict1, "keyc", "keyb") == 5 - assert safe_value_fallback(dict1, 'keya', 'keyd') is None + assert safe_value_fallback(dict1, "keya", "keyd") is None - assert safe_value_fallback(dict1, 'keyNo', 'keyNo') is None - assert safe_value_fallback(dict1, 'keyNo', 'keyNo', 55) == 55 - assert safe_value_fallback(dict1, 'keyNo', default_value=55) == 55 - assert safe_value_fallback(dict1, 'keyNo', None, default_value=55) == 55 + assert safe_value_fallback(dict1, "keyNo", "keyNo") is None + assert safe_value_fallback(dict1, "keyNo", "keyNo", 55) == 55 + assert safe_value_fallback(dict1, "keyNo", default_value=55) == 55 + assert safe_value_fallback(dict1, "keyNo", None, default_value=55) == 55 def test_safe_value_fallback2(): - dict1 = {'keya': None, 'keyb': 2, 'keyc': 5, 'keyd': None} - dict2 = {'keya': 20, 'keyb': None, 'keyc': 6, 'keyd': None} - assert safe_value_fallback2(dict1, dict2, 'keya', 'keya') == 20 - assert safe_value_fallback2(dict2, dict1, 'keya', 'keya') == 20 + dict1 = {"keya": None, "keyb": 2, "keyc": 5, "keyd": None} + dict2 = {"keya": 20, "keyb": None, "keyc": 6, "keyd": None} + assert safe_value_fallback2(dict1, dict2, "keya", "keya") == 20 + assert safe_value_fallback2(dict2, dict1, "keya", "keya") == 20 - assert safe_value_fallback2(dict1, dict2, 'keyb', 'keyb') == 2 - assert safe_value_fallback2(dict2, dict1, 'keyb', 'keyb') == 2 + assert safe_value_fallback2(dict1, dict2, "keyb", "keyb") == 2 + assert safe_value_fallback2(dict2, dict1, "keyb", "keyb") == 2 - assert safe_value_fallback2(dict1, dict2, 'keyc', 'keyc') == 5 - assert safe_value_fallback2(dict2, dict1, 'keyc', 'keyc') == 6 + assert safe_value_fallback2(dict1, dict2, "keyc", "keyc") == 5 + assert safe_value_fallback2(dict2, dict1, "keyc", "keyc") == 6 - assert safe_value_fallback2(dict1, dict2, 'keyd', 'keyd') is None - assert safe_value_fallback2(dict2, dict1, 'keyd', 'keyd') is None - assert safe_value_fallback2(dict2, dict1, 'keyd', 'keyd', 1234) == 1234 + assert safe_value_fallback2(dict1, dict2, "keyd", "keyd") is None + assert safe_value_fallback2(dict2, dict1, "keyd", "keyd") is None + assert safe_value_fallback2(dict2, dict1, "keyd", "keyd", 1234) == 1234 - assert safe_value_fallback2(dict1, dict2, 'keyNo', 'keyNo') is None - assert safe_value_fallback2(dict2, dict1, 'keyNo', 'keyNo') is None - assert safe_value_fallback2(dict2, dict1, 'keyNo', 'keyNo', 1234) == 1234 + assert safe_value_fallback2(dict1, dict2, "keyNo", "keyNo") is None + assert safe_value_fallback2(dict2, dict1, "keyNo", "keyNo") is None + assert safe_value_fallback2(dict2, dict1, "keyNo", "keyNo", 1234) == 1234 def test_plural() -> None: @@ -163,38 +164,51 @@ def test_plural() -> None: assert plural(-1.5, "ox", "oxen") == "oxen" -@pytest.mark.parametrize('conn_url,expected', [ - ("postgresql+psycopg2://scott123:scott123@host:1245/dbname", - "postgresql+psycopg2://scott123:*****@host:1245/dbname"), - ("postgresql+psycopg2://scott123:scott123@host.name.com/dbname", - "postgresql+psycopg2://scott123:*****@host.name.com/dbname"), - ("mariadb+mariadbconnector://app_user:Password123!@127.0.0.1:3306/company", - "mariadb+mariadbconnector://app_user:*****@127.0.0.1:3306/company"), - ("mysql+pymysql://user:pass@some_mariadb/dbname?charset=utf8mb4", - "mysql+pymysql://user:*****@some_mariadb/dbname?charset=utf8mb4"), - ("sqlite:////freqtrade/user_data/tradesv3.sqlite", - "sqlite:////freqtrade/user_data/tradesv3.sqlite"), -]) +@pytest.mark.parametrize( + "conn_url,expected", + [ + ( + "postgresql+psycopg2://scott123:scott123@host:1245/dbname", + "postgresql+psycopg2://scott123:*****@host:1245/dbname", + ), + ( + "postgresql+psycopg2://scott123:scott123@host.name.com/dbname", + "postgresql+psycopg2://scott123:*****@host.name.com/dbname", + ), + ( + "mariadb+mariadbconnector://app_user:Password123!@127.0.0.1:3306/company", + "mariadb+mariadbconnector://app_user:*****@127.0.0.1:3306/company", + ), + ( + "mysql+pymysql://user:pass@some_mariadb/dbname?charset=utf8mb4", + "mysql+pymysql://user:*****@some_mariadb/dbname?charset=utf8mb4", + ), + ( + "sqlite:////freqtrade/user_data/tradesv3.sqlite", + "sqlite:////freqtrade/user_data/tradesv3.sqlite", + ), + ], +) def test_parse_db_uri_for_logging(conn_url, expected) -> None: - assert parse_db_uri_for_logging(conn_url) == expected def test_deep_merge_dicts(): - a = {'first': {'rows': {'pass': 'dog', 'number': '1', 'test': None}}} - b = {'first': {'rows': {'fail': 'cat', 'number': '5', 'test': 'asdf'}}} - res = {'first': {'rows': {'pass': 'dog', 'fail': 'cat', 'number': '5', 'test': 'asdf'}}} - res2 = {'first': {'rows': {'pass': 'dog', 'fail': 'cat', 'number': '1', 'test': None}}} + a = {"first": {"rows": {"pass": "dog", "number": "1", "test": None}}} + b = {"first": {"rows": {"fail": "cat", "number": "5", "test": "asdf"}}} + res = {"first": {"rows": {"pass": "dog", "fail": "cat", "number": "5", "test": "asdf"}}} + res2 = {"first": {"rows": {"pass": "dog", "fail": "cat", "number": "1", "test": None}}} assert deep_merge_dicts(b, deepcopy(a)) == res assert deep_merge_dicts(a, deepcopy(b)) == res2 - res2['first']['rows']['test'] = 'asdf' + res2["first"]["rows"]["test"] = "asdf" assert deep_merge_dicts(a, deepcopy(b), allow_null_overrides=False) == res2 def test_dataframe_json(ohlcv_history): from pandas.testing import assert_frame_equal + json = dataframe_to_json(ohlcv_history) dataframe = json_to_dataframe(json) @@ -202,7 +216,7 @@ def test_dataframe_json(ohlcv_history): assert len(ohlcv_history) == len(dataframe) assert_frame_equal(ohlcv_history, dataframe) - ohlcv_history.at[1, 'date'] = pd.NaT + ohlcv_history.at[1, "date"] = pd.NaT json = dataframe_to_json(ohlcv_history) dataframe = json_to_dataframe(json) diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 741935a8b..4ce976b59 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -31,7 +31,7 @@ from tests.conftest import get_args, log_has, log_has_re, patch_exchange def fig_generating_mock(fig, *args, **kwargs): - """ Return Fig - used to mock add_indicators and plot_trades""" + """Return Fig - used to mock add_indicators and plot_trades""" return fig @@ -51,18 +51,18 @@ def generate_empty_figure(): def test_init_plotscript(default_conf, mocker, testdatadir): - default_conf['timerange'] = "20180110-20180112" - default_conf['trade_source'] = "file" - default_conf['timeframe'] = "5m" - default_conf['exportfilename'] = testdatadir / "backtest-result.json" + default_conf["timerange"] = "20180110-20180112" + default_conf["trade_source"] = "file" + default_conf["timeframe"] = "5m" + default_conf["exportfilename"] = testdatadir / "backtest-result.json" supported_markets = ["TRX/BTC", "ADA/BTC"] ret = init_plotscript(default_conf, supported_markets) assert "ohlcv" in ret assert "trades" in ret assert "pairs" in ret - assert 'timerange' in ret + assert "timerange" in ret - default_conf['pairs'] = ["TRX/BTC", "ADA/BTC"] + default_conf["pairs"] = ["TRX/BTC", "ADA/BTC"] ret = init_plotscript(default_conf, supported_markets, 20) assert "ohlcv" in ret assert "TRX/BTC" in ret["ohlcv"] @@ -73,15 +73,16 @@ def test_add_indicators(default_conf, testdatadir, caplog): pair = "UNITTEST/BTC" timerange = TimeRange() - data = history.load_pair_history(pair=pair, timeframe='1m', - datadir=testdatadir, timerange=timerange) + data = history.load_pair_history( + pair=pair, timeframe="1m", datadir=testdatadir, timerange=timerange + ) indicators1 = {"ema10": {}} indicators2 = {"macd": {"color": "red"}} strategy = StrategyResolver.load_strategy(default_conf) # Generate entry/exit signals and indicators - data = strategy.analyze_ticker(data, {'pair': pair}) + data = strategy.analyze_ticker(data, {"pair": pair}) fig = generate_empty_figure() # Row 1 @@ -99,39 +100,43 @@ def test_add_indicators(default_conf, testdatadir, caplog): assert macd.line.color == "red" # No indicator found - fig3 = add_indicators(fig=deepcopy(fig), row=3, indicators={'no_indicator': {}}, data=data) + fig3 = add_indicators(fig=deepcopy(fig), row=3, indicators={"no_indicator": {}}, data=data) assert fig == fig3 assert log_has_re(r'Indicator "no_indicator" ignored\..*', caplog) def test_add_areas(default_conf, testdatadir, caplog): pair = "UNITTEST/BTC" - timerange = TimeRange(None, 'line', 0, -1000) + timerange = TimeRange(None, "line", 0, -1000) - data = history.load_pair_history(pair=pair, timeframe='1m', - datadir=testdatadir, timerange=timerange) - indicators = {"macd": {"color": "red", - "fill_color": "black", - "fill_to": "macdhist", - "fill_label": "MACD Fill"}} + data = history.load_pair_history( + pair=pair, timeframe="1m", datadir=testdatadir, timerange=timerange + ) + indicators = { + "macd": { + "color": "red", + "fill_color": "black", + "fill_to": "macdhist", + "fill_label": "MACD Fill", + } + } - ind_no_label = {"macd": {"fill_color": "red", - "fill_to": "macdhist"}} + ind_no_label = {"macd": {"fill_color": "red", "fill_to": "macdhist"}} ind_plain = {"macd": {"fill_to": "macdhist"}} strategy = StrategyResolver.load_strategy(default_conf) # Generate entry/exit signals and indicators - data = strategy.analyze_ticker(data, {'pair': pair}) + data = strategy.analyze_ticker(data, {"pair": pair}) fig = generate_empty_figure() # indicator mentioned in fill_to does not exist - fig1 = add_areas(fig, 1, data, {'ema10': {'fill_to': 'no_fill_indicator'}}) + fig1 = add_areas(fig, 1, data, {"ema10": {"fill_to": "no_fill_indicator"}}) assert fig == fig1 assert log_has_re(r'fill_to: "no_fill_indicator" ignored\..*', caplog) # indicator does not exist - fig2 = add_areas(fig, 1, data, {'no_indicator': {'fill_to': 'ema10'}}) + fig2 = add_areas(fig, 1, data, {"no_indicator": {"fill_to": "ema10"}}) assert fig == fig2 assert log_has_re(r'Indicator "no_indicator" ignored\..*', caplog) @@ -168,56 +173,60 @@ def test_plot_trades(testdatadir, caplog): pair = "ADA/BTC" filename = testdatadir / "backtest_results/backtest-result.json" trades = load_backtest_data(filename) - trades = trades.loc[trades['pair'] == pair] + trades = trades.loc[trades["pair"] == pair] fig = plot_trades(fig, trades) figure = fig1.layout.figure # Check entry - color, should be in first graph, ... - trade_entries = find_trace_in_fig_data(figure.data, 'Trade entry') + trade_entries = find_trace_in_fig_data(figure.data, "Trade entry") assert isinstance(trade_entries, go.Scatter) - assert trade_entries.yaxis == 'y' + assert trade_entries.yaxis == "y" assert len(trades) == len(trade_entries.x) - assert trade_entries.marker.color == 'cyan' - assert trade_entries.marker.symbol == 'circle-open' - assert trade_entries.text[0] == '3.99%, buy_tag, roi, 15 min' + assert trade_entries.marker.color == "cyan" + assert trade_entries.marker.symbol == "circle-open" + assert trade_entries.text[0] == "3.99%, buy_tag, roi, 15 min" - trade_exit = find_trace_in_fig_data(figure.data, 'Exit - Profit') + trade_exit = find_trace_in_fig_data(figure.data, "Exit - Profit") assert isinstance(trade_exit, go.Scatter) - assert trade_exit.yaxis == 'y' - assert len(trades.loc[trades['profit_ratio'] > 0]) == len(trade_exit.x) - assert trade_exit.marker.color == 'green' - assert trade_exit.marker.symbol == 'square-open' - assert trade_exit.text[0] == '3.99%, buy_tag, roi, 15 min' + assert trade_exit.yaxis == "y" + assert len(trades.loc[trades["profit_ratio"] > 0]) == len(trade_exit.x) + assert trade_exit.marker.color == "green" + assert trade_exit.marker.symbol == "square-open" + assert trade_exit.text[0] == "3.99%, buy_tag, roi, 15 min" - trade_sell_loss = find_trace_in_fig_data(figure.data, 'Exit - Loss') + trade_sell_loss = find_trace_in_fig_data(figure.data, "Exit - Loss") assert isinstance(trade_sell_loss, go.Scatter) - assert trade_sell_loss.yaxis == 'y' - assert len(trades.loc[trades['profit_ratio'] <= 0]) == len(trade_sell_loss.x) - assert trade_sell_loss.marker.color == 'red' - assert trade_sell_loss.marker.symbol == 'square-open' - assert trade_sell_loss.text[5] == '-10.45%, stop_loss, 720 min' + assert trade_sell_loss.yaxis == "y" + assert len(trades.loc[trades["profit_ratio"] <= 0]) == len(trade_sell_loss.x) + assert trade_sell_loss.marker.color == "red" + assert trade_sell_loss.marker.symbol == "square-open" + assert trade_sell_loss.text[5] == "-10.45%, stop_loss, 720 min" def test_generate_candlestick_graph_no_signals_no_trades(default_conf, mocker, testdatadir, caplog): - row_mock = mocker.patch('freqtrade.plot.plotting.add_indicators', - MagicMock(side_effect=fig_generating_mock)) - trades_mock = mocker.patch('freqtrade.plot.plotting.plot_trades', - MagicMock(side_effect=fig_generating_mock)) + row_mock = mocker.patch( + "freqtrade.plot.plotting.add_indicators", MagicMock(side_effect=fig_generating_mock) + ) + trades_mock = mocker.patch( + "freqtrade.plot.plotting.plot_trades", MagicMock(side_effect=fig_generating_mock) + ) pair = "UNITTEST/BTC" - timerange = TimeRange(None, 'line', 0, -1000) - data = history.load_pair_history(pair=pair, timeframe='1m', - datadir=testdatadir, timerange=timerange) - data['enter_long'] = 0 - data['exit_long'] = 0 - data['enter_short'] = 0 - data['exit_short'] = 0 + timerange = TimeRange(None, "line", 0, -1000) + data = history.load_pair_history( + pair=pair, timeframe="1m", datadir=testdatadir, timerange=timerange + ) + data["enter_long"] = 0 + data["exit_long"] = 0 + data["enter_short"] = 0 + data["exit_short"] = 0 indicators1 = [] indicators2 = [] - fig = generate_candlestick_graph(pair=pair, data=data, trades=None, - indicators1=indicators1, indicators2=indicators2) + fig = generate_candlestick_graph( + pair=pair, data=data, trades=None, indicators1=indicators1, indicators2=indicators2 + ) assert isinstance(fig, go.Figure) assert fig.layout.title.text == pair figure = fig.layout.figure @@ -240,24 +249,28 @@ def test_generate_candlestick_graph_no_signals_no_trades(default_conf, mocker, t def test_generate_candlestick_graph_no_trades(default_conf, mocker, testdatadir): - row_mock = mocker.patch('freqtrade.plot.plotting.add_indicators', - MagicMock(side_effect=fig_generating_mock)) - trades_mock = mocker.patch('freqtrade.plot.plotting.plot_trades', - MagicMock(side_effect=fig_generating_mock)) - pair = 'UNITTEST/BTC' - timerange = TimeRange(None, 'line', 0, -1000) - data = history.load_pair_history(pair=pair, timeframe='1m', - datadir=testdatadir, timerange=timerange) + row_mock = mocker.patch( + "freqtrade.plot.plotting.add_indicators", MagicMock(side_effect=fig_generating_mock) + ) + trades_mock = mocker.patch( + "freqtrade.plot.plotting.plot_trades", MagicMock(side_effect=fig_generating_mock) + ) + pair = "UNITTEST/BTC" + timerange = TimeRange(None, "line", 0, -1000) + data = history.load_pair_history( + pair=pair, timeframe="1m", datadir=testdatadir, timerange=timerange + ) strategy = StrategyResolver.load_strategy(default_conf) # Generate buy/sell signals and indicators - data = strategy.analyze_ticker(data, {'pair': pair}) + data = strategy.analyze_ticker(data, {"pair": pair}) indicators1 = [] indicators2 = [] - fig = generate_candlestick_graph(pair=pair, data=data, trades=None, - indicators1=indicators1, indicators2=indicators2) + fig = generate_candlestick_graph( + pair=pair, data=data, trades=None, indicators1=indicators1, indicators2=indicators2 + ) assert isinstance(fig, go.Figure) assert fig.layout.title.text == pair figure = fig.layout.figure @@ -273,12 +286,12 @@ def test_generate_candlestick_graph_no_trades(default_conf, mocker, testdatadir) enter_long = find_trace_in_fig_data(figure.data, "enter_long") assert isinstance(enter_long, go.Scatter) # All buy-signals should be plotted - assert int(data['enter_long'].sum()) == len(enter_long.x) + assert int(data["enter_long"].sum()) == len(enter_long.x) exit_long = find_trace_in_fig_data(figure.data, "exit_long") assert isinstance(exit_long, go.Scatter) # All buy-signals should be plotted - assert int(data['exit_long'].sum()) == len(exit_long.x) + assert int(data["exit_long"].sum()) == len(exit_long.x) assert find_trace_in_fig_data(figure.data, "Bollinger Band") @@ -294,16 +307,15 @@ def test_generate_Plot_filename(): def test_generate_plot_file(mocker, caplog, user_dir): fig = generate_empty_figure() plot_mock = mocker.patch("freqtrade.plot.plotting.plot", MagicMock()) - store_plot_file(fig, filename="freqtrade-plot-UNITTEST_BTC-5m.html", - directory=user_dir / "plot") + store_plot_file( + fig, filename="freqtrade-plot-UNITTEST_BTC-5m.html", directory=user_dir / "plot" + ) expected_fn = str(user_dir / "plot/freqtrade-plot-UNITTEST_BTC-5m.html") assert plot_mock.call_count == 1 assert plot_mock.call_args[0][0] == fig - assert (plot_mock.call_args_list[0][1]['filename'] - == expected_fn) - assert log_has(f"Stored plot as {expected_fn}", - caplog) + assert plot_mock.call_args_list[0][1]["filename"] == expected_fn + assert log_has(f"Stored plot as {expected_fn}", caplog) def test_add_profit(testdatadir): @@ -311,15 +323,16 @@ def test_add_profit(testdatadir): bt_data = load_backtest_data(filename) timerange = TimeRange.parse_timerange("20180110-20180112") - df = history.load_pair_history(pair="TRX/BTC", timeframe='5m', - datadir=testdatadir, timerange=timerange) + df = history.load_pair_history( + pair="TRX/BTC", timeframe="5m", datadir=testdatadir, timerange=timerange + ) fig = generate_empty_figure() - cum_profits = create_cum_profit(df.set_index('date'), - bt_data[bt_data["pair"] == 'TRX/BTC'], - "cum_profits", timeframe="5m") + cum_profits = create_cum_profit( + df.set_index("date"), bt_data[bt_data["pair"] == "TRX/BTC"], "cum_profits", timeframe="5m" + ) - fig1 = add_profit(fig, row=2, data=cum_profits, column='cum_profits', name='Profits') + fig1 = add_profit(fig, row=2, data=cum_profits, column="cum_profits", name="Profits") figure = fig1.layout.figure profits = find_trace_in_fig_data(figure.data, "Profits") assert isinstance(profits, go.Scatter) @@ -331,22 +344,15 @@ def test_generate_profit_graph(testdatadir): trades = load_backtest_data(filename) timerange = TimeRange.parse_timerange("20180110-20180112") pairs = ["TRX/BTC", "XLM/BTC"] - trades = trades[trades['close_date'] < pd.Timestamp('2018-01-12', tz='UTC')] + trades = trades[trades["close_date"] < pd.Timestamp("2018-01-12", tz="UTC")] - data = history.load_data(datadir=testdatadir, - pairs=pairs, - timeframe='5m', - timerange=timerange) + data = history.load_data(datadir=testdatadir, pairs=pairs, timeframe="5m", timerange=timerange) - trades = trades[trades['pair'].isin(pairs)] + trades = trades[trades["pair"].isin(pairs)] fig = generate_profit_graph( - pairs, - data, - trades, - timeframe="5m", - stake_currency='BTC', - starting_balance=0) + pairs, data, trades, timeframe="5m", stake_currency="BTC", starting_balance=0 + ) assert isinstance(fig, go.Figure) assert fig.layout.title.text == "Freqtrade Profit plot" @@ -379,40 +385,48 @@ def test_generate_profit_graph(testdatadir): with pytest.raises(OperationalException, match=r"No trades found.*"): # Pair cannot be empty - so it's an empty dataframe. - generate_profit_graph(pairs, data, trades.loc[trades['pair'].isnull()], timeframe="5m", - stake_currency='BTC', starting_balance=0) + generate_profit_graph( + pairs, + data, + trades.loc[trades["pair"].isnull()], + timeframe="5m", + stake_currency="BTC", + starting_balance=0, + ) def test_start_plot_dataframe(mocker): aup = mocker.patch("freqtrade.plot.plotting.load_and_plot_trades", MagicMock()) args = [ "plot-dataframe", - "--config", "tests/testdata/testconfigs/main_test_config.json", - "--pairs", "ETH/BTC" + "--config", + "tests/testdata/testconfigs/main_test_config.json", + "--pairs", + "ETH/BTC", ] start_plot_dataframe(get_args(args)) assert aup.call_count == 1 called_config = aup.call_args_list[0][0][0] assert "pairs" in called_config - assert called_config['pairs'] == ["ETH/BTC"] + assert called_config["pairs"] == ["ETH/BTC"] def test_load_and_plot_trades(default_conf, mocker, caplog, testdatadir): patch_exchange(mocker) - default_conf['trade_source'] = 'file' - default_conf['exportfilename'] = testdatadir / "backtest-result.json" - default_conf['indicators1'] = ["sma5", "ema10"] - default_conf['indicators2'] = ["macd"] - default_conf['pairs'] = ["ETH/BTC", "LTC/BTC"] + default_conf["trade_source"] = "file" + default_conf["exportfilename"] = testdatadir / "backtest-result.json" + default_conf["indicators1"] = ["sma5", "ema10"] + default_conf["indicators2"] = ["macd"] + default_conf["pairs"] = ["ETH/BTC", "LTC/BTC"] candle_mock = MagicMock() store_mock = MagicMock() mocker.patch.multiple( "freqtrade.plot.plotting", generate_candlestick_graph=candle_mock, - store_plot_file=store_mock + store_plot_file=store_mock, ) load_and_plot_trades(default_conf) @@ -420,8 +434,8 @@ def test_load_and_plot_trades(default_conf, mocker, caplog, testdatadir): assert candle_mock.call_count == 2 assert store_mock.call_count == 2 - assert candle_mock.call_args_list[0][1]['indicators1'] == ['sma5', 'ema10'] - assert candle_mock.call_args_list[0][1]['indicators2'] == ['macd'] + assert candle_mock.call_args_list[0][1]["indicators1"] == ["sma5", "ema10"] + assert candle_mock.call_args_list[0][1]["indicators2"] == ["macd"] assert log_has("End of plotting process. 2 plots generated", caplog) @@ -430,49 +444,46 @@ def test_start_plot_profit(mocker): aup = mocker.patch("freqtrade.plot.plotting.plot_profit", MagicMock()) args = [ "plot-profit", - "--config", "tests/testdata/testconfigs/main_test_config.json", - "--pairs", "ETH/BTC" + "--config", + "tests/testdata/testconfigs/main_test_config.json", + "--pairs", + "ETH/BTC", ] start_plot_profit(get_args(args)) assert aup.call_count == 1 called_config = aup.call_args_list[0][0][0] assert "pairs" in called_config - assert called_config['pairs'] == ["ETH/BTC"] + assert called_config["pairs"] == ["ETH/BTC"] def test_start_plot_profit_error(mocker): - - args = [ - 'plot-profit', - '--pairs', 'ETH/BTC' - ] + args = ["plot-profit", "--pairs", "ETH/BTC"] argsp = get_args(args) # Make sure we use no config. Details: #2241 # not resetting config causes random failures if config.json exists - argsp['config'] = [] + argsp["config"] = [] with pytest.raises(OperationalException): start_plot_profit(argsp) def test_plot_profit(default_conf, mocker, testdatadir): patch_exchange(mocker) - default_conf['trade_source'] = 'file' - default_conf['exportfilename'] = testdatadir / 'backtest-result_test_nofile.json' - default_conf['pairs'] = ['ETH/BTC', 'LTC/BTC'] + default_conf["trade_source"] = "file" + default_conf["exportfilename"] = testdatadir / "backtest-result_test_nofile.json" + default_conf["pairs"] = ["ETH/BTC", "LTC/BTC"] profit_mock = MagicMock() store_mock = MagicMock() mocker.patch.multiple( - "freqtrade.plot.plotting", - generate_profit_graph=profit_mock, - store_plot_file=store_mock + "freqtrade.plot.plotting", generate_profit_graph=profit_mock, store_plot_file=store_mock ) - with pytest.raises(OperationalException, - match=r"No trades found, cannot generate Profit-plot.*"): + with pytest.raises( + OperationalException, match=r"No trades found, cannot generate Profit-plot.*" + ): plot_profit(default_conf) - default_conf['exportfilename'] = testdatadir / "backtest_results/backtest-result.json" + default_conf["exportfilename"] = testdatadir / "backtest_results/backtest-result.json" plot_profit(default_conf) @@ -480,53 +491,78 @@ def test_plot_profit(default_conf, mocker, testdatadir): assert profit_mock.call_count == 1 assert store_mock.call_count == 1 - assert profit_mock.call_args_list[0][0][0] == default_conf['pairs'] - assert store_mock.call_args_list[0][1]['auto_open'] is False + assert profit_mock.call_args_list[0][0][0] == default_conf["pairs"] + assert store_mock.call_args_list[0][1]["auto_open"] is False - del default_conf['timeframe'] + del default_conf["timeframe"] with pytest.raises(OperationalException, match=r"Timeframe must be set.*--timeframe.*"): plot_profit(default_conf) -@pytest.mark.parametrize("ind1,ind2,plot_conf,exp", [ - # No indicators, use plot_conf - ([], [], {}, - {'main_plot': {'sma': {}, 'ema3': {}, 'ema5': {}}, - 'subplots': {'Other': {'macd': {}, 'macdsignal': {}}}}), - # use indicators - (['sma', 'ema3'], ['macd'], {}, - {'main_plot': {'sma': {}, 'ema3': {}}, 'subplots': {'Other': {'macd': {}}}}), - # only main_plot - adds empty subplots - ([], [], {'main_plot': {'sma': {}}}, - {'main_plot': {'sma': {}}, 'subplots': {}}), - # Main and subplots - ([], [], {'main_plot': {'sma': {}}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}}}, - {'main_plot': {'sma': {}}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}}}), - # no main_plot, adds empty main_plot - ([], [], {'subplots': {'RSI': {'rsi': {'color': 'red'}}}}, - {'main_plot': {}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}}}), - # indicator 1 / 2 should have prevalence - (['sma', 'ema3'], ['macd'], - {'main_plot': {'sma': {}}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}}}, - {'main_plot': {'sma': {}, 'ema3': {}}, 'subplots': {'Other': {'macd': {}}}} - ), - # indicator 1 - overrides plot_config main_plot - (['sma', 'ema3'], [], - {'main_plot': {'sma': {}}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}}}, - {'main_plot': {'sma': {}, 'ema3': {}}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}}} - ), - # indicator 2 - overrides plot_config subplots - ([], ['macd', 'macd_signal'], - {'main_plot': {'sma': {}}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}}}, - {'main_plot': {'sma': {}}, 'subplots': {'Other': {'macd': {}, 'macd_signal': {}}}} - ), -]) +@pytest.mark.parametrize( + "ind1,ind2,plot_conf,exp", + [ + # No indicators, use plot_conf + ( + [], + [], + {}, + { + "main_plot": {"sma": {}, "ema3": {}, "ema5": {}}, + "subplots": {"Other": {"macd": {}, "macdsignal": {}}}, + }, + ), + # use indicators + ( + ["sma", "ema3"], + ["macd"], + {}, + {"main_plot": {"sma": {}, "ema3": {}}, "subplots": {"Other": {"macd": {}}}}, + ), + # only main_plot - adds empty subplots + ([], [], {"main_plot": {"sma": {}}}, {"main_plot": {"sma": {}}, "subplots": {}}), + # Main and subplots + ( + [], + [], + {"main_plot": {"sma": {}}, "subplots": {"RSI": {"rsi": {"color": "red"}}}}, + {"main_plot": {"sma": {}}, "subplots": {"RSI": {"rsi": {"color": "red"}}}}, + ), + # no main_plot, adds empty main_plot + ( + [], + [], + {"subplots": {"RSI": {"rsi": {"color": "red"}}}}, + {"main_plot": {}, "subplots": {"RSI": {"rsi": {"color": "red"}}}}, + ), + # indicator 1 / 2 should have prevalence + ( + ["sma", "ema3"], + ["macd"], + {"main_plot": {"sma": {}}, "subplots": {"RSI": {"rsi": {"color": "red"}}}}, + {"main_plot": {"sma": {}, "ema3": {}}, "subplots": {"Other": {"macd": {}}}}, + ), + # indicator 1 - overrides plot_config main_plot + ( + ["sma", "ema3"], + [], + {"main_plot": {"sma": {}}, "subplots": {"RSI": {"rsi": {"color": "red"}}}}, + {"main_plot": {"sma": {}, "ema3": {}}, "subplots": {"RSI": {"rsi": {"color": "red"}}}}, + ), + # indicator 2 - overrides plot_config subplots + ( + [], + ["macd", "macd_signal"], + {"main_plot": {"sma": {}}, "subplots": {"RSI": {"rsi": {"color": "red"}}}}, + {"main_plot": {"sma": {}}, "subplots": {"Other": {"macd": {}, "macd_signal": {}}}}, + ), + ], +) def test_create_plotconfig(ind1, ind2, plot_conf, exp): - res = create_plotconfig(ind1, ind2, plot_conf) - assert 'main_plot' in res - assert 'subplots' in res - assert isinstance(res['main_plot'], dict) - assert isinstance(res['subplots'], dict) + assert "main_plot" in res + assert "subplots" in res + assert isinstance(res["main_plot"], dict) + assert isinstance(res["subplots"], dict) assert res == exp diff --git a/tests/test_talib.py b/tests/test_talib.py index f526fdd4d..97551bec9 100644 --- a/tests/test_talib.py +++ b/tests/test_talib.py @@ -3,12 +3,14 @@ import talib.abstract as ta def test_talib_bollingerbands_near_zero_values(): - inputs = pd.DataFrame([ - {'close': 0.00000010}, - {'close': 0.00000011}, - {'close': 0.00000012}, - {'close': 0.00000013}, - {'close': 0.00000014} - ]) + inputs = pd.DataFrame( + [ + {"close": 0.00000010}, + {"close": 0.00000011}, + {"close": 0.00000012}, + {"close": 0.00000013}, + {"close": 0.00000014}, + ] + ) bollinger = ta.BBANDS(inputs, matype=0, timeperiod=2) - assert bollinger['upperband'][3] != bollinger['middleband'][3] + assert bollinger["upperband"][3] != bollinger["middleband"][3] diff --git a/tests/test_wallets.py b/tests/test_wallets.py index eff6ba20d..f33222b7c 100644 --- a/tests/test_wallets.py +++ b/tests/test_wallets.py @@ -18,185 +18,179 @@ from tests.conftest import ( def test_sync_wallet_at_boot(mocker, default_conf): - default_conf['dry_run'] = False + default_conf["dry_run"] = False mocker.patch.multiple( EXMS, - get_balances=MagicMock(return_value={ - "BNT": { - "free": 1.0, - "used": 2.0, - "total": 3.0 - }, - "GAS": { - "free": 0.260739, - "used": 0.0, - "total": 0.260739 - }, - "USDT": { - "free": 20, - "used": 20, - "total": 40 - }, - }) + get_balances=MagicMock( + return_value={ + "BNT": {"free": 1.0, "used": 2.0, "total": 3.0}, + "GAS": {"free": 0.260739, "used": 0.0, "total": 0.260739}, + "USDT": {"free": 20, "used": 20, "total": 40}, + } + ), ) freqtrade = get_patched_freqtradebot(mocker, default_conf) assert len(freqtrade.wallets._wallets) == 3 - assert freqtrade.wallets._wallets['BNT'].free == 1.0 - assert freqtrade.wallets._wallets['BNT'].used == 2.0 - assert freqtrade.wallets._wallets['BNT'].total == 3.0 - assert freqtrade.wallets._wallets['GAS'].free == 0.260739 - assert freqtrade.wallets._wallets['GAS'].used == 0.0 - assert freqtrade.wallets._wallets['GAS'].total == 0.260739 - assert freqtrade.wallets.get_free('BNT') == 1.0 - assert 'USDT' in freqtrade.wallets._wallets + assert freqtrade.wallets._wallets["BNT"].free == 1.0 + assert freqtrade.wallets._wallets["BNT"].used == 2.0 + assert freqtrade.wallets._wallets["BNT"].total == 3.0 + assert freqtrade.wallets._wallets["GAS"].free == 0.260739 + assert freqtrade.wallets._wallets["GAS"].used == 0.0 + assert freqtrade.wallets._wallets["GAS"].total == 0.260739 + assert freqtrade.wallets.get_free("BNT") == 1.0 + assert "USDT" in freqtrade.wallets._wallets assert freqtrade.wallets._last_wallet_refresh is not None mocker.patch.multiple( EXMS, - get_balances=MagicMock(return_value={ - "BNT": { - "free": 1.2, - "used": 1.9, - "total": 3.5 - }, - "GAS": { - "free": 0.270739, - "used": 0.1, - "total": 0.260439 - }, - }) + get_balances=MagicMock( + return_value={ + "BNT": {"free": 1.2, "used": 1.9, "total": 3.5}, + "GAS": {"free": 0.270739, "used": 0.1, "total": 0.260439}, + } + ), ) freqtrade.wallets.update() # USDT is missing from the 2nd result - so should not be in this either. assert len(freqtrade.wallets._wallets) == 2 - assert freqtrade.wallets._wallets['BNT'].free == 1.2 - assert freqtrade.wallets._wallets['BNT'].used == 1.9 - assert freqtrade.wallets._wallets['BNT'].total == 3.5 - assert freqtrade.wallets._wallets['GAS'].free == 0.270739 - assert freqtrade.wallets._wallets['GAS'].used == 0.1 - assert freqtrade.wallets._wallets['GAS'].total == 0.260439 - assert freqtrade.wallets.get_free('GAS') == 0.270739 - assert freqtrade.wallets.get_used('GAS') == 0.1 - assert freqtrade.wallets.get_total('GAS') == 0.260439 - update_mock = mocker.patch('freqtrade.wallets.Wallets._update_live') + assert freqtrade.wallets._wallets["BNT"].free == 1.2 + assert freqtrade.wallets._wallets["BNT"].used == 1.9 + assert freqtrade.wallets._wallets["BNT"].total == 3.5 + assert freqtrade.wallets._wallets["GAS"].free == 0.270739 + assert freqtrade.wallets._wallets["GAS"].used == 0.1 + assert freqtrade.wallets._wallets["GAS"].total == 0.260439 + assert freqtrade.wallets.get_free("GAS") == 0.270739 + assert freqtrade.wallets.get_used("GAS") == 0.1 + assert freqtrade.wallets.get_total("GAS") == 0.260439 + update_mock = mocker.patch("freqtrade.wallets.Wallets._update_live") freqtrade.wallets.update(False) assert update_mock.call_count == 0 freqtrade.wallets.update() assert update_mock.call_count == 1 - assert freqtrade.wallets.get_free('NOCURRENCY') == 0 - assert freqtrade.wallets.get_used('NOCURRENCY') == 0 - assert freqtrade.wallets.get_total('NOCURRENCY') == 0 + assert freqtrade.wallets.get_free("NOCURRENCY") == 0 + assert freqtrade.wallets.get_used("NOCURRENCY") == 0 + assert freqtrade.wallets.get_total("NOCURRENCY") == 0 def test_sync_wallet_missing_data(mocker, default_conf): - default_conf['dry_run'] = False + default_conf["dry_run"] = False mocker.patch.multiple( EXMS, - get_balances=MagicMock(return_value={ - "BNT": { - "free": 1.0, - "used": 2.0, - "total": 3.0 - }, - "GAS": { - "free": 0.260739, - "total": 0.260739 - }, - }) + get_balances=MagicMock( + return_value={ + "BNT": {"free": 1.0, "used": 2.0, "total": 3.0}, + "GAS": {"free": 0.260739, "total": 0.260739}, + } + ), ) freqtrade = get_patched_freqtradebot(mocker, default_conf) assert len(freqtrade.wallets._wallets) == 2 - assert freqtrade.wallets._wallets['BNT'].free == 1.0 - assert freqtrade.wallets._wallets['BNT'].used == 2.0 - assert freqtrade.wallets._wallets['BNT'].total == 3.0 - assert freqtrade.wallets._wallets['GAS'].free == 0.260739 - assert freqtrade.wallets._wallets['GAS'].used is None - assert freqtrade.wallets._wallets['GAS'].total == 0.260739 - assert freqtrade.wallets.get_free('GAS') == 0.260739 + assert freqtrade.wallets._wallets["BNT"].free == 1.0 + assert freqtrade.wallets._wallets["BNT"].used == 2.0 + assert freqtrade.wallets._wallets["BNT"].total == 3.0 + assert freqtrade.wallets._wallets["GAS"].free == 0.260739 + assert freqtrade.wallets._wallets["GAS"].used is None + assert freqtrade.wallets._wallets["GAS"].total == 0.260739 + assert freqtrade.wallets.get_free("GAS") == 0.260739 def test_get_trade_stake_amount_no_stake_amount(default_conf, mocker) -> None: - patch_wallet(mocker, free=default_conf['stake_amount'] * 0.5) + patch_wallet(mocker, free=default_conf["stake_amount"] * 0.5) freqtrade = get_patched_freqtradebot(mocker, default_conf) - with pytest.raises(DependencyException, match=r'.*stake amount.*'): - freqtrade.wallets.get_trade_stake_amount('ETH/BTC', 1) + with pytest.raises(DependencyException, match=r".*stake amount.*"): + freqtrade.wallets.get_trade_stake_amount("ETH/BTC", 1) -@pytest.mark.parametrize("balance_ratio,capital,result1,result2", [ - (1, None, 50, 66.66666), - (0.99, None, 49.5, 66.0), - (0.50, None, 25, 33.3333), - # Tests with capital ignore balance_ratio - (1, 100, 50, 0.0), - (0.99, 200, 50, 66.66666), - (0.99, 150, 50, 50), - (0.50, 50, 25, 0.0), - (0.50, 10, 5, 0.0), -]) -def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, balance_ratio, capital, - result1, result2, limit_buy_order_open, - fee, mocker) -> None: +@pytest.mark.parametrize( + "balance_ratio,capital,result1,result2", + [ + (1, None, 50, 66.66666), + (0.99, None, 49.5, 66.0), + (0.50, None, 25, 33.3333), + # Tests with capital ignore balance_ratio + (1, 100, 50, 0.0), + (0.99, 200, 50, 66.66666), + (0.99, 150, 50, 50), + (0.50, 50, 25, 0.0), + (0.50, 10, 5, 0.0), + ], +) +def test_get_trade_stake_amount_unlimited_amount( + default_conf, + ticker, + balance_ratio, + capital, + result1, + result2, + limit_buy_order_open, + fee, + mocker, +) -> None: mocker.patch.multiple( EXMS, fetch_ticker=ticker, create_order=MagicMock(return_value=limit_buy_order_open), - get_fee=fee + get_fee=fee, ) conf = deepcopy(default_conf) - conf['stake_amount'] = UNLIMITED_STAKE_AMOUNT - conf['dry_run_wallet'] = 100 - conf['tradable_balance_ratio'] = balance_ratio + conf["stake_amount"] = UNLIMITED_STAKE_AMOUNT + conf["dry_run_wallet"] = 100 + conf["tradable_balance_ratio"] = balance_ratio if capital is not None: - conf['available_capital'] = capital + conf["available_capital"] = capital freqtrade = get_patched_freqtradebot(mocker, conf) # no open trades, order amount should be 'balance / max_open_trades' - result = freqtrade.wallets.get_trade_stake_amount('ETH/USDT', 2) + result = freqtrade.wallets.get_trade_stake_amount("ETH/USDT", 2) assert result == result1 # create one trade, order amount should be 'balance / (max_open_trades - num_open_trades)' - freqtrade.execute_entry('ETH/USDT', result) + freqtrade.execute_entry("ETH/USDT", result) - result = freqtrade.wallets.get_trade_stake_amount('LTC/USDT', 2) + result = freqtrade.wallets.get_trade_stake_amount("LTC/USDT", 2) assert result == result1 # create 2 trades, order amount should be None - freqtrade.execute_entry('LTC/BTC', result) + freqtrade.execute_entry("LTC/BTC", result) - result = freqtrade.wallets.get_trade_stake_amount('XRP/USDT', 2) + result = freqtrade.wallets.get_trade_stake_amount("XRP/USDT", 2) assert result == 0 - freqtrade.config['dry_run_wallet'] = 200 + freqtrade.config["dry_run_wallet"] = 200 freqtrade.wallets.start_cap = 200 - result = freqtrade.wallets.get_trade_stake_amount('XRP/USDT', 3) + result = freqtrade.wallets.get_trade_stake_amount("XRP/USDT", 3) assert round(result, 4) == round(result2, 4) # set max_open_trades = None, so do not trade - result = freqtrade.wallets.get_trade_stake_amount('NEO/USDT', 0) + result = freqtrade.wallets.get_trade_stake_amount("NEO/USDT", 0) assert result == 0 -@pytest.mark.parametrize('stake_amount,min_stake,stake_available,max_stake,trade_amount,expected', [ - (22, 11, 50, 10000, None, 22), - (100, 11, 500, 10000, None, 100), - (1000, 11, 500, 10000, None, 500), # Above stake_available - (700, 11, 1000, 400, None, 400), # Above max_stake, below stake available - (20, 15, 10, 10000, None, 0), # Minimum stake > stake_available - (9, 11, 100, 10000, None, 11), # Below min stake - (1, 15, 10, 10000, None, 0), # Below min stake and min_stake > stake_available - (20, 50, 100, 10000, None, 0), # Below min stake and stake * 1.3 > min_stake - (1000, None, 1000, 10000, None, 1000), # No min-stake-amount could be determined - (2000, 15, 2000, 3000, 1500, 1500), # Rebuy - resulting in too high stake amount. Adjusting. -]) +@pytest.mark.parametrize( + "stake_amount,min_stake,stake_available,max_stake,trade_amount,expected", + [ + (22, 11, 50, 10000, None, 22), + (100, 11, 500, 10000, None, 100), + (1000, 11, 500, 10000, None, 500), # Above stake_available + (700, 11, 1000, 400, None, 400), # Above max_stake, below stake available + (20, 15, 10, 10000, None, 0), # Minimum stake > stake_available + (9, 11, 100, 10000, None, 11), # Below min stake + (1, 15, 10, 10000, None, 0), # Below min stake and min_stake > stake_available + (20, 50, 100, 10000, None, 0), # Below min stake and stake * 1.3 > min_stake + (1000, None, 1000, 10000, None, 1000), # No min-stake-amount could be determined + # Rebuy - resulting in too high stake amount. Adjusting. + (2000, 15, 2000, 3000, 1500, 1500), + ], +) def test_validate_stake_amount( mocker, default_conf, @@ -209,33 +203,41 @@ def test_validate_stake_amount( ): freqtrade = get_patched_freqtradebot(mocker, default_conf) - mocker.patch("freqtrade.wallets.Wallets.get_available_stake_amount", - return_value=stake_available) + mocker.patch( + "freqtrade.wallets.Wallets.get_available_stake_amount", return_value=stake_available + ) res = freqtrade.wallets.validate_stake_amount( - 'XRP/USDT', stake_amount, min_stake, max_stake, trade_amount) + "XRP/USDT", stake_amount, min_stake, max_stake, trade_amount + ) assert res == expected -@pytest.mark.parametrize('available_capital,closed_profit,open_stakes,free,expected', [ - (None, 10, 100, 910, 1000), - (None, 0, 0, 2500, 2500), - (None, 500, 0, 2500, 2000), - (None, 500, 0, 2500, 2000), - (None, -70, 0, 1930, 2000), - # Only available balance matters when it's set. - (100, 0, 0, 0, 100), - (1000, 0, 2, 5, 1000), - (1235, 2250, 2, 5, 1235), - (1235, -2250, 2, 5, 1235), -]) -def test_get_starting_balance(mocker, default_conf, available_capital, closed_profit, - open_stakes, free, expected): +@pytest.mark.parametrize( + "available_capital,closed_profit,open_stakes,free,expected", + [ + (None, 10, 100, 910, 1000), + (None, 0, 0, 2500, 2500), + (None, 500, 0, 2500, 2000), + (None, 500, 0, 2500, 2000), + (None, -70, 0, 1930, 2000), + # Only available balance matters when it's set. + (100, 0, 0, 0, 100), + (1000, 0, 2, 5, 1000), + (1235, 2250, 2, 5, 1235), + (1235, -2250, 2, 5, 1235), + ], +) +def test_get_starting_balance( + mocker, default_conf, available_capital, closed_profit, open_stakes, free, expected +): if available_capital: - default_conf['available_capital'] = available_capital - mocker.patch("freqtrade.persistence.models.Trade.get_total_closed_profit", - return_value=closed_profit) - mocker.patch("freqtrade.persistence.models.Trade.total_open_trades_stakes", - return_value=open_stakes) + default_conf["available_capital"] = available_capital + mocker.patch( + "freqtrade.persistence.models.Trade.get_total_closed_profit", return_value=closed_profit + ) + mocker.patch( + "freqtrade.persistence.models.Trade.total_open_trades_stakes", return_value=open_stakes + ) mocker.patch("freqtrade.wallets.Wallets.get_free", return_value=free) freqtrade = get_patched_freqtradebot(mocker, default_conf) @@ -244,9 +246,9 @@ def test_get_starting_balance(mocker, default_conf, available_capital, closed_pr def test_sync_wallet_futures_live(mocker, default_conf): - default_conf['dry_run'] = False - default_conf['trading_mode'] = 'futures' - default_conf['margin_mode'] = 'isolated' + default_conf["dry_run"] = False + default_conf["trading_mode"] = "futures" + default_conf["margin_mode"] = "isolated" mock_result = [ { "symbol": "ETH/USDT:USDT", @@ -267,8 +269,8 @@ def test_sync_wallet_futures_live(mocker, default_conf): "markPrice": 2896.41, "collateral": 20, "marginType": "isolated", - "side": 'short', - "percentage": None + "side": "short", + "percentage": None, }, { "symbol": "ADA/USDT:USDT", @@ -289,8 +291,8 @@ def test_sync_wallet_futures_live(mocker, default_conf): "markPrice": 0.91, "collateral": 20, "marginType": "isolated", - "side": 'short', - "percentage": None + "side": "short", + "percentage": None, }, { # Closed position @@ -312,20 +314,18 @@ def test_sync_wallet_futures_live(mocker, default_conf): "markPrice": 15.41, "collateral": 0.0, "marginType": "isolated", - "side": 'short', - "percentage": None - } + "side": "short", + "percentage": None, + }, ] mocker.patch.multiple( EXMS, - get_balances=MagicMock(return_value={ - "USDT": { - "free": 900, - "used": 100, - "total": 1000 - }, - }), - fetch_positions=MagicMock(return_value=mock_result) + get_balances=MagicMock( + return_value={ + "USDT": {"free": 900, "used": 100, "total": 1000}, + } + ), + fetch_positions=MagicMock(return_value=mock_result), ) freqtrade = get_patched_freqtradebot(mocker, default_conf) @@ -333,23 +333,23 @@ def test_sync_wallet_futures_live(mocker, default_conf): assert len(freqtrade.wallets._wallets) == 1 assert len(freqtrade.wallets._positions) == 2 - assert 'USDT' in freqtrade.wallets._wallets - assert 'ETH/USDT:USDT' in freqtrade.wallets._positions + assert "USDT" in freqtrade.wallets._wallets + assert "ETH/USDT:USDT" in freqtrade.wallets._positions assert freqtrade.wallets._last_wallet_refresh is not None # Remove ETH/USDT:USDT position del mock_result[0] freqtrade.wallets.update() assert len(freqtrade.wallets._positions) == 1 - assert 'ETH/USDT:USDT' not in freqtrade.wallets._positions + assert "ETH/USDT:USDT" not in freqtrade.wallets._positions def test_sync_wallet_dry(mocker, default_conf_usdt, fee): - default_conf_usdt['dry_run'] = True + default_conf_usdt["dry_run"] = True freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) assert len(freqtrade.wallets._wallets) == 1 assert len(freqtrade.wallets._positions) == 0 - assert freqtrade.wallets.get_total('USDT') == 1000 + assert freqtrade.wallets.get_total("USDT") == 1000 create_mock_trades_usdt(fee, is_short=None) @@ -358,23 +358,23 @@ def test_sync_wallet_dry(mocker, default_conf_usdt, fee): assert len(freqtrade.wallets._wallets) == 5 assert len(freqtrade.wallets._positions) == 0 bal = freqtrade.wallets.get_all_balances() - assert bal['NEO'].total == 10 - assert bal['XRP'].total == 10 - assert bal['LTC'].total == 2 - assert bal['USDT'].total == 922.74 + assert bal["NEO"].total == 10 + assert bal["XRP"].total == 10 + assert bal["LTC"].total == 2 + assert bal["USDT"].total == 922.74 - assert freqtrade.wallets.get_starting_balance() == default_conf_usdt['dry_run_wallet'] - total = freqtrade.wallets.get_total('LTC') - free = freqtrade.wallets.get_free('LTC') - used = freqtrade.wallets.get_used('LTC') + assert freqtrade.wallets.get_starting_balance() == default_conf_usdt["dry_run_wallet"] + total = freqtrade.wallets.get_total("LTC") + free = freqtrade.wallets.get_free("LTC") + used = freqtrade.wallets.get_used("LTC") assert free != 0 assert free + used == total def test_sync_wallet_futures_dry(mocker, default_conf, fee): - default_conf['dry_run'] = True - default_conf['trading_mode'] = 'futures' - default_conf['margin_mode'] = 'isolated' + default_conf["dry_run"] = True + default_conf["trading_mode"] = "futures" + default_conf["margin_mode"] = "isolated" freqtrade = get_patched_freqtradebot(mocker, default_conf) assert len(freqtrade.wallets._wallets) == 1 assert len(freqtrade.wallets._positions) == 0 @@ -386,15 +386,15 @@ def test_sync_wallet_futures_dry(mocker, default_conf, fee): assert len(freqtrade.wallets._wallets) == 1 assert len(freqtrade.wallets._positions) == 4 positions = freqtrade.wallets.get_all_positions() - assert positions['ETH/BTC'].side == 'short' - assert positions['ETC/BTC'].side == 'long' - assert positions['XRP/BTC'].side == 'long' - assert positions['LTC/BTC'].side == 'short' + assert positions["ETH/BTC"].side == "short" + assert positions["ETC/BTC"].side == "long" + assert positions["XRP/BTC"].side == "long" + assert positions["LTC/BTC"].side == "short" - assert freqtrade.wallets.get_starting_balance() == default_conf['dry_run_wallet'] - total = freqtrade.wallets.get_total('BTC') - free = freqtrade.wallets.get_free('BTC') - used = freqtrade.wallets.get_used('BTC') + assert freqtrade.wallets.get_starting_balance() == default_conf["dry_run_wallet"] + total = freqtrade.wallets.get_total("BTC") + free = freqtrade.wallets.get_free("BTC") + used = freqtrade.wallets.get_used("BTC") assert free + used == total @@ -421,14 +421,14 @@ def test_check_exit_amount(mocker, default_conf, fee): def test_check_exit_amount_futures(mocker, default_conf, fee): - default_conf['trading_mode'] = 'futures' - default_conf['margin_mode'] = 'isolated' + default_conf["trading_mode"] = "futures" + default_conf["margin_mode"] = "isolated" freqtrade = get_patched_freqtradebot(mocker, default_conf) total_mock = mocker.patch("freqtrade.wallets.Wallets.get_total", return_value=123) create_mock_trades(fee, is_short=None) trade = Trade.session.scalars(select(Trade)).first() - trade.trading_mode = 'futures' + trade.trading_mode = "futures" assert trade.amount == 123 assert freqtrade.wallets.check_exit_amount(trade) is True