diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 961cfb092..a203fef2e 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -206,12 +206,20 @@ class Backtesting(object): buy_signal = sell_row.buy sell = self.strategy.should_sell(trade, sell_row.open, sell_row.date, buy_signal, - sell_row.sell) + sell_row.sell, low=sell_row.low, high=sell_row.high) if sell.sell_flag: + if sell.sell_type in (SellType.STOP_LOSS, SellType.TRAILING_STOP_LOSS): + # Set close_rate to stoploss + closerate = trade.stop_loss + elif sell.sell_type == (SellType.ROI): + # set close-rate to min-roi + closerate = trade.open_rate + trade.open_rate * self.strategy.minimal_roi[0] + else: + closerate = sell_row.open return BacktestResult(pair=pair, - profit_percent=trade.calc_profit_percent(rate=sell_row.open), - profit_abs=trade.calc_profit(rate=sell_row.open), + profit_percent=trade.calc_profit_percent(rate=closerate), + profit_abs=trade.calc_profit(rate=closerate), open_time=buy_row.date, close_time=sell_row.date, trade_duration=int(( @@ -220,7 +228,7 @@ class Backtesting(object): close_index=sell_row.Index, open_at_end=False, open_rate=buy_row.open, - close_rate=sell_row.open, + close_rate=closerate, sell_reason=sell.sell_type ) if partial_ticker: @@ -260,7 +268,7 @@ class Backtesting(object): position_stacking: do we allow position stacking? (default: False) :return: DataFrame """ - headers = ['date', 'buy', 'open', 'close', 'sell'] + headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high'] processed = args['processed'] max_open_trades = args.get('max_open_trades', 0) position_stacking = args.get('position_stacking', False) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 6afa4161b..21efa09d6 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -203,18 +203,22 @@ class IStrategy(ABC): return buy, sell def should_sell(self, trade: Trade, rate: float, date: datetime, buy: bool, - sell: bool) -> SellCheckTuple: + sell: bool, low: float=None, high: float=None) -> SellCheckTuple: """ This function evaluate if on the condition required to trigger a sell has been reached if the threshold is reached and updates the trade record. :return: True if trade should be sold, False otherwise """ - current_profit = trade.calc_profit_percent(rate) - stoplossflag = self.stop_loss_reached(current_rate=rate, trade=trade, current_time=date, + # Set current rate to low for backtesting sell + current_rate = rate if not low else low + current_profit = trade.calc_profit_percent(current_rate) + stoplossflag = self.stop_loss_reached(current_rate=current_rate, trade=trade, current_time=date, current_profit=current_profit) if stoplossflag.sell_flag: return stoplossflag - + # Set current rate to low for backtesting sell + current_rate = rate if not high else high + current_profit = trade.calc_profit_percent(current_rate) experimental = self.config.get('experimental', {}) if buy and experimental.get('ignore_roi_if_buy_signal', False): diff --git a/freqtrade/tests/optimize/test_backtest_detail.py b/freqtrade/tests/optimize/test_backtest_detail.py index bb24f1602..6430f6c1e 100644 --- a/freqtrade/tests/optimize/test_backtest_detail.py +++ b/freqtrade/tests/optimize/test_backtest_detail.py @@ -72,8 +72,8 @@ tc1 = BTContainer(data=DataFrame([ [getdate('2018-06-10 11:00:00').datetime, 9955, 9975, 9955, 9990, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9990, 9990, 9990, 9900, 12345, 0, 0] ], columns=columns), - # stop_loss=-0.01, roi=1, trades=1, profit_perc=-0.01, sell_r=SellType.STOP_LOSS) # should be - stop_loss=-0.01, roi=1, trades=1, profit_perc=-0.003, sell_r=SellType.FORCE_SELL) # + stop_loss=-0.01, roi=1, trades=1, profit_perc=-0.01, sell_r=SellType.STOP_LOSS) # should be + # stop_loss=-0.01, roi=1, trades=1, profit_perc=-0.003, sell_r=SellType.FORCE_SELL) # # Test 2 Minus 4% Low, minus 1% close @@ -88,8 +88,8 @@ tc2 = BTContainer(data=DataFrame([ [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9875, 9900, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] ], columns=columns), - # stop_loss=-0.03, roi=1, trades=1, profit_perc=-0.03, sell_r=SellType.STOP_LOSS) #should be - stop_loss=-0.03, roi=1, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # + stop_loss=-0.03, roi=1, trades=1, profit_perc=-0.03, sell_r=SellType.STOP_LOSS) #should be + # stop_loss=-0.03, roi=1, trades=1, profit_perc=-0.007, sell_r=SellType.FORCE_SELL) # # Test 3 Candle drops 4%, Recovers 1%. @@ -108,8 +108,9 @@ tc3 = BTContainer(data=DataFrame([ [getdate('2018-06-10 12:00:00').datetime, 9925, 9975, 8000, 8000, 12345, 0, 0], [getdate('2018-06-10 13:00:00').datetime, 9900, 9950, 9950, 9900, 12345, 0, 0] ], columns=columns), - # stop_loss=-0.02, roi=1, trades=2, profit_perc=-0.4, sell_r=SellType.STOP_LOSS) #should be - stop_loss=-0.02, roi=1, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # + stop_loss=-0.02, roi=1, trades=2, profit_perc=-0.04, sell_r=SellType.STOP_LOSS) #should be + # stop_loss=-0.02, roi=1, trades=1, profit_perc=-0.02, sell_r=SellType.STOP_LOSS) #should be + # stop_loss=-0.02, roi=1, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # # Test 4 Minus 3% / recovery +15% @@ -124,8 +125,8 @@ tc4 = BTContainer(data=DataFrame([ [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9875, 9900, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] ], columns=columns), - # stop_loss=-0.02, roi=0.06, trades=1, profit_perc=-0.02, sell_r=SellType.STOP_LOSS) #should be - stop_loss=-0.02, roi=0.06, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) + stop_loss=-0.02, roi=0.06, trades=1, profit_perc=-0.02, sell_r=SellType.STOP_LOSS) #should be + # stop_loss=-0.02, roi=0.06, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # Test 5 / Drops 0.5% Closes +20% # Candle Data for test 5 @@ -139,8 +140,8 @@ tc5 = BTContainer(data=DataFrame([ [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9945, 9900, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] ], columns=columns), - # stop_loss=-0.01, roi=0.03, trades=1, profit_perc=0.03, sell_r=SellType.ROI) #should be - stop_loss=-0.01, roi=0.03, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) + stop_loss=-0.01, roi=0.03, trades=1, profit_perc=0.03, sell_r=SellType.ROI) #should be + # stop_loss=-0.01, roi=0.03, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # Test 6 / Drops 3% / Recovers 6% Positive / Closes 1% positve # Candle Data for test 6 @@ -154,8 +155,8 @@ tc6 = BTContainer(data=DataFrame([ [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9945, 9900, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] ], columns=columns), - # stop_loss=-0.02, roi=0.05, trades=1, profit_perc=-0.02, sell_r=SellType.STOP_LOSS) #should be - stop_loss=-0.02, roi=0.05, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # + stop_loss=-0.02, roi=0.05, trades=1, profit_perc=-0.02, sell_r=SellType.STOP_LOSS) #should be + # stop_loss=-0.02, roi=0.05, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # # Test 7 - 6% Positive / 1% Negative / Close 1% Positve # Candle Data for test 7 @@ -169,8 +170,8 @@ tc7 = BTContainer(data=DataFrame([ [getdate('2018-06-10 11:00:00').datetime, 9925, 9975, 9945, 9900, 12345, 0, 0], [getdate('2018-06-10 12:00:00').datetime, 9900, 9950, 9850, 9900, 12345, 0, 0] ], columns=columns), - # stop_loss=-0.02, roi=0.03, trades=1, profit_perc=0.03, sell_r=SellType.ROI) #should be - stop_loss=-0.02, roi=0.03, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # + stop_loss=-0.02, roi=0.03, trades=1, profit_perc=0.03, sell_r=SellType.ROI) #should be + # stop_loss=-0.02, roi=0.03, trades=1, profit_perc=-0.012, sell_r=SellType.FORCE_SELL) # TESTS = [ # tc_profit1, @@ -193,7 +194,9 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None: """ default_conf["stoploss"] = data.stop_loss default_conf["minimal_roi"] = {"0": data.roi} - mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) + # mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) + # TODO: don't Mock fee to for now + mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.0)) patch_exchange(mocker) backtesting = Backtesting(default_conf)