kernc / backtesting.py

:mag_right: :chart_with_upwards_trend: :snake: :moneybag: Backtest trading strategies in Python.
https://kernc.github.io/backtesting.py/
GNU Affero General Public License v3.0
5.04k stars 987 forks source link

Assertion Error due to negative stop loss using Optimize #1142

Open jr1973 opened 1 month ago

jr1973 commented 1 month ago

Expected Behavior

Optimize function runs multiple tests against stop loss and take profit
lower_b = 10 #10 upper_b = 31 #31 stats, heatmap = bt.optimize(slcoef=[i/10 for i in range(lower_b, upper_b)], #<--- adjust from 10,21 to 10,31 to go from max 2 to max 3 TPSLRatio=[i/10 for i in range(lower_b, upper_b)], maximize='Return [%]', max_tries=600, random_state=0, return_heatmap=True)

expected behavior occurs with short datasets ie 2 years of data. Should run without an issue on more than short timeframes for all stocks

Actual Behavior

When run on 10 years of daily data stocks that move aggressively like UPRO and TQQQ trigger an assertion error on @tp.setter 650 def tp(self, price: float): --> 651 self.__set_contingent('tp', price)

C:\Users\jroch\anaconda3\lib\site-packages\backtesting\backtesting.py:1488: UserWarning: Searching for best of 441 configurations. output = _optimize_grid()


AssertionError Traceback (most recent call last) Cell In[6], line 172 170 lower_b = 10 #10 171 upper_b = 31 #31 --> 172 stats, heatmap = bt.optimize(slcoef=[i/10 for i in range(lower_b, upper_b)], #<--- adjust from 10,21 to 10,31 to go from max 2 to max 3 173 TPSLRatio=[i/10 for i in range(lower_b, upper_b)], 174 maximize='Return [%]', max_tries=600, 175 random_state=0, 176 return_heatmap=True) 178 # Convert multiindex series to dataframe 179 heatmap_df = heatmap.unstack()

File ~\anaconda3\lib\site-packages\backtesting\backtesting.py:1488, in Backtest.optimize(self, maximize, method, max_tries, constraint, return_heatmap, return_optimization, random_state, **kwargs) 1485 return stats if len(output) == 1 else tuple(output) 1487 if method == 'grid': -> 1488 output = _optimize_grid() 1489 elif method == 'skopt': 1490 output = _optimize_skopt()

File ~\anaconda3\lib\site-packages\backtesting\backtesting.py:1378, in Backtest.optimize.._optimize_grid() 1375 warnings.warn("For multiprocessing support in Backtest.optimize() " 1376 "set multiprocessing start method to 'fork'.") 1377 for batch_index in _tqdm(range(len(parambatches))): -> 1378 , values = Backtest._mp_task(backtest_uuid, batch_index) 1379 for value, params in zip(values, param_batches[batch_index]): 1380 heatmap[tuple(params.values())] = value

File ~\anaconda3\lib\site-packages\backtesting\backtesting.py:1498, in Backtest._mp_task(backtest_uuid, batch_index) 1495 @staticmethod 1496 def _mp_task(backtest_uuid, batch_index): 1497 bt, param_batches, maximize_func = Backtest._mp_backtests[backtest_uuid] -> 1498 return batch_index, [maximize_func(stats) if stats['# Trades'] else np.nan 1499 for stats in (bt.run(**params) 1500 for params in param_batches[batch_index])]

File ~\anaconda3\lib\site-packages\backtesting\backtesting.py:1498, in (.0) 1495 @staticmethod 1496 def _mp_task(backtest_uuid, batch_index): 1497 bt, param_batches, maximize_func = Backtest._mp_backtests[backtest_uuid] -> 1498 return batch_index, [maximize_func(stats) if stats['# Trades'] else np.nan 1499 for stats in (bt.run(**params) 1500 for params in param_batches[batch_index])]

File ~\anaconda3\lib\site-packages\backtesting\backtesting.py:1499, in (.0) 1495 @staticmethod 1496 def _mp_task(backtest_uuid, batch_index): 1497 bt, param_batches, maximize_func = Backtest._mp_backtests[backtest_uuid] 1498 return batch_index, [maximize_func(stats) if stats['# Trades'] else np.nan -> 1499 for stats in (bt.run(**params) 1500 for params in param_batches[batch_index])]

File ~\anaconda3\lib\site-packages\backtesting\backtesting.py:1165, in Backtest.run(self, **kwargs) 1163 # Handle orders processing and broker stuff 1164 try: -> 1165 broker.next() 1166 except _OutOfMoneyError: 1167 break

File ~\anaconda3\lib\site-packages\backtesting\backtesting.py:765, in _Broker.next(self) 763 def next(self): 764 i = self._i = len(self._data) - 1 --> 765 self._process_orders() 767 # Log account equity for the equity curve 768 equity = self.equity

File ~\anaconda3\lib\site-packages\backtesting\backtesting.py:902, in _Broker._process_orders(self) 900 # Open a new trade 901 if need_size: --> 902 self._open_trade(adjusted_price, need_size, order.sl, order.tp, time_index) 904 # We need to reprocess the SL/TP orders newly added to the queue. 905 # This allows e.g. SL hitting in the same bar the order was open. 906 # See https://github.com/kernc/backtesting.py/issues/119 907 if order.sl or order.tp:

File ~\anaconda3\lib\site-packages\backtesting\backtesting.py:968, in _Broker._open_trade(self, price, size, sl, tp, time_index) 963 # Create SL/TP (bracket) orders. 964 # Make sure SL order is created first so it gets adversarially processed before TP order 965 # in case of an ambiguous tie (both hit within a single bar). 966 # Note, sl/tp orders are inserted at the front of the list, thus order reversed. 967 if tp: --> 968 trade.tp = tp 969 if sl: 970 trade.sl = sl

File ~\anaconda3\lib\site-packages\backtesting\backtesting.py:651, in Trade.tp(self, price) 649 @tp.setter 650 def tp(self, price: float): --> 651 self.__set_contingent('tp', price)

File ~\anaconda3\lib\site-packages\backtesting\backtesting.py:655, in Trade.__set_contingent(self, type, price) 653 def setcontingent(self, type, price): 654 assert type in ('sl', 'tp') --> 655 assert price is None or 0 < price < np.inf 656 attr = f'{self.class.qualname}{type}_order' 657 order: Order = getattr(self, attr)

AssertionError:

Steps to Reproduce