From bcfa73d492e3c150f0b909df58eb2c59ce6a15a6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Feb 2022 15:10:09 +0100 Subject: [PATCH] Add "nr_of_successfull_entries" --- docs/strategy-callbacks.md | 6 ++++-- freqtrade/freqtradebot.py | 2 +- freqtrade/optimize/backtesting.py | 12 ++++++------ freqtrade/persistence/models.py | 21 +++++++++++++++++++++ freqtrade/rpc/rpc.py | 8 ++++---- tests/test_persistence.py | 2 ++ 6 files changed, 38 insertions(+), 13 deletions(-) diff --git a/docs/strategy-callbacks.md b/docs/strategy-callbacks.md index 9e0b33ab1..5cc7b9776 100644 --- a/docs/strategy-callbacks.md +++ b/docs/strategy-callbacks.md @@ -593,6 +593,8 @@ Additional orders also result in additional fees and those orders don't count to This callback is **not** called when there is an open order (either buy or sell) waiting for execution, or when you have reached the maximum amount of extra buys that you have set on `max_entry_position_adjustment`. `adjust_trade_position()` is called very frequently for the duration of a trade, so you must keep your implementation as performant as possible. +Position adjustments will always be applied in the direction of the trade, so a positive value will always increase your position, no matter if it's a long or short trade. + !!! Note "About stake size" Using fixed stake size means it will be the amount used for the first order, just like without position adjustment. If you wish to buy additional orders with DCA, then make sure to leave enough funds in the wallet for that. @@ -663,7 +665,7 @@ class DigDeeperStrategy(IStrategy): return None filled_buys = trade.select_filled_orders('buy') - count_of_buys = trade.nr_of_successful_buys + count_of_entries = trade.nr_of_successful_entries # Allow up to 3 additional increasingly larger buys (4 in total) # Initial buy is 1x # If that falls to -5% profit, we buy 1.25x more, average profit should increase to roughly -2.2% @@ -676,7 +678,7 @@ class DigDeeperStrategy(IStrategy): # This returns first order stake size stake_amount = filled_buys[0].cost # This then calculates current safety order size - stake_amount = stake_amount * (1 + (count_of_buys * 0.25)) + stake_amount = stake_amount * (1 + (count_of_entries * 0.25)) return stake_amount except Exception as exception: return None diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 4d0316bb8..20565120c 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -510,7 +510,7 @@ class FreqtradeBot(LoggingMixin): """ # TODO-lev: Check what changes are necessary for DCA in relation to shorts. if self.strategy.max_entry_position_adjustment > -1: - count_of_buys = trade.nr_of_successful_buys + count_of_buys = trade.nr_of_successful_entries if count_of_buys > self.strategy.max_entry_position_adjustment: logger.debug(f"Max adjustment entries for {trade.pair} has been reached.") return diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 0e3a70a93..6716c0133 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -464,11 +464,11 @@ class Backtesting: # Check if we need to adjust our current positions if self.strategy.position_adjustment_enable: - check_adjust_buy = True + check_adjust_entry = True if self.strategy.max_entry_position_adjustment > -1: - count_of_buys = trade.nr_of_successful_buys - check_adjust_buy = (count_of_buys <= self.strategy.max_entry_position_adjustment) - if check_adjust_buy: + entry_count = trade.nr_of_successful_entries + check_adjust_entry = (entry_count <= self.strategy.max_entry_position_adjustment) + if check_adjust_entry: trade = self._get_adjust_trade_entry_for_candle(trade, sell_row) sell_candle_time: datetime = sell_row[DATE_IDX].to_pydatetime() @@ -729,7 +729,7 @@ class Backtesting: for pair in open_trades.keys(): if len(open_trades[pair]) > 0: for trade in open_trades[pair]: - if trade.open_order_id and trade.nr_of_successful_buys == 0: + if trade.open_order_id and trade.nr_of_successful_entries == 0: # Ignore trade if buy-order did not fill yet continue sell_row = data[pair][-1] @@ -782,7 +782,7 @@ class Backtesting: if timedout: if order.side == 'buy': self.timedout_entry_orders += 1 - if trade.nr_of_successful_buys == 0: + if trade.nr_of_successful_entries == 0: # Remove trade due to buy timeout expiration. return True else: diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index ae4265374..fd73de49b 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -889,6 +889,8 @@ class LocalTrade(): total_stake += tmp_price * tmp_amount if total_amount > 0: + # TODO-lev: This should update leverage as well - + # as averaged trades might have different leverage self.open_rate = total_stake / total_amount self.stake_amount = total_stake self.amount = total_amount @@ -936,10 +938,28 @@ class LocalTrade(): (o.filled or 0) > 0 and o.status in NON_OPEN_EXCHANGE_STATES] + @property + def nr_of_successful_entries(self) -> int: + """ + Helper function to count the number of entry orders that have been filled. + :return: int count of entry orders that have been filled for this trade. + """ + + return len(self.select_filled_orders(self.enter_side)) + + @property + def nr_of_successful_exits(self) -> int: + """ + Helper function to count the number of exit orders that have been filled. + :return: int count of exit orders that have been filled for this trade. + """ + return len(self.select_filled_orders(self.exit_side)) + @property def nr_of_successful_buys(self) -> int: """ Helper function to count the number of buy orders that have been filled. + WARNING: Please use nr_of_successful_entries for short support. :return: int count of buy orders that have been filled for this trade. """ @@ -949,6 +969,7 @@ class LocalTrade(): def nr_of_successful_sells(self) -> int: """ Helper function to count the number of sell orders that have been filled. + WARNING: Please use nr_of_successful_exits for short support. :return: int count of sell orders that have been filled for this trade. """ return len(self.select_filled_orders('sell')) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 9b780d88d..1c73160a4 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -261,11 +261,11 @@ class RPC: profit_str ] if self._config.get('position_adjustment_enable', False): - max_buy_str = '' + max_entry_str = '' if self._config.get('max_entry_position_adjustment', -1) > 0: - max_buy_str = f"/{self._config['max_entry_position_adjustment'] + 1}" - filled_buys = trade.nr_of_successful_buys - detail_trade.append(f"{filled_buys}{max_buy_str}") + max_entry_str = f"/{self._config['max_entry_position_adjustment'] + 1}" + filled_entries = trade.nr_of_successful_entries + detail_trade.append(f"{filled_entries}{max_entry_str}") trades_list.append(detail_trade) profitcol = "Profit" if self._fiat_converter: diff --git a/tests/test_persistence.py b/tests/test_persistence.py index efba25550..2ceff216b 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -2418,6 +2418,7 @@ def test_recalc_trade_from_orders_ignores_bad_orders(fee): assert trade.fee_open_cost == o1_fee_cost assert trade.open_trade_value == o1_trade_val assert trade.nr_of_successful_buys == 1 + assert trade.nr_of_successful_entries == 1 order2 = Order( ft_order_side='buy', @@ -2554,6 +2555,7 @@ def test_recalc_trade_from_orders_ignores_bad_orders(fee): assert trade.fee_open_cost == 3 * o1_fee_cost assert trade.open_trade_value == 3 * o1_trade_val assert trade.nr_of_successful_buys == 3 + assert trade.nr_of_successful_entries == 3 @pytest.mark.usefixtures("init_persistence")