From 17650d7e6025385f8d9dbd3dbe44d097b50ce9eb Mon Sep 17 00:00:00 2001 From: eSeR1805 Date: Fri, 29 Apr 2022 00:10:17 +0300 Subject: [PATCH] Maintain existing order. Update functionality and documentation --- docs/strategy-callbacks.md | 20 ++++++--- freqtrade/freqtradebot.py | 14 +++--- freqtrade/strategy/interface.py | 12 ++--- .../subtemplates/strategy_methods_advanced.j2 | 44 ++++++++++--------- 4 files changed, 53 insertions(+), 37 deletions(-) diff --git a/docs/strategy-callbacks.md b/docs/strategy-callbacks.md index 8da8bab0f..7f86f2610 100644 --- a/docs/strategy-callbacks.md +++ b/docs/strategy-callbacks.md @@ -698,6 +698,9 @@ Be aware that `custom_entry_price()` is still the one dictating initial entry li !!! Note "Simple Order Cancelation" This also allows simple cancelation without an replacement order. This behavior occurs when `None` is returned. +!!! Note "Maintaining Order" + Maintaining existing order on exchange is facilitated. This behavior occurs when `order.price` is returned. + !!! Warning Entry `unfilledtimeout` mechanism takes precedence over this. Be sure to update timeout values to match your expectancy. @@ -709,19 +712,24 @@ class AwesomeStrategy(IStrategy): # ... populate_* methods - def adjust_entry_price(self, trade: Trade, order: Order, pair: str, - current_time: datetime, proposed_rate: float, - entry_tag: Optional[str], side: str, **kwargs) -> float: + def adjust_entry_price(self, trade: Trade, order: Optional[Order], pair: str, + current_time: datetime, proposed_rate: float, current_order_rate: float, + entry_tag: Optional[str], side: str, **kwargs) -> float: """ Entry price re-adjustment logic, returning the user desired limit price. This only executes when a order was already placed, still open(unfilled fully or partially) and not timed out on subsequent candles after entry trigger. + When not implemented by a strategy, returns current_order_rate as default. + If current_order_rate is returned then the existing order is maintained. + If None is returned then order gets canceled but not replaced by a new one. + :param pair: Pair that's currently analyzed :param trade: Trade object. :param order: Order object :param current_time: datetime object, containing the current datetime - :param proposed_rate: Rate, calculated based on pricing settings in exit_pricing. + :param proposed_rate: Rate, calculated based on pricing settings in entry_pricing. + :param current_order_rate: Rate of the existing order in place. :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. :param side: 'long' or 'short' - indicating the direction of the proposed trade :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. @@ -736,8 +744,10 @@ class AwesomeStrategy(IStrategy): else: dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) current_candle = dataframe.iloc[-1].squeeze() + # desired price return current_candle['sma_200'] - return proposed_rate + # default: maintain existing order + return current_order_rate ``` ## Leverage Callback diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index b55fee35f..330bfcdf0 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1165,9 +1165,10 @@ class FreqtradeBot(LoggingMixin): def replace_order(self, order: Dict, order_obj: Optional[Order], trade: Trade) -> None: """ - Check if current analyzed entry order should be replaced. Analyzed order is canceled - if adjust_entry_price() returned price differs from proposed_rate. - New order is only placed if adjust_entry_price() returned price is not None. + Check if current analyzed entry order should be replaced or simply cancelled. + To simply cancel the existing order(no replacement) adjust_entry_price() should return None + To maintain existing order adjust_entry_price() should return order_obj.price + To replace existing order adjust_entry_price() should return desired price for limit order :param order: Order dict grabbed with exchange.fetch_order() :param order_obj: Order object. :param trade: Trade object. @@ -1184,17 +1185,18 @@ class FreqtradeBot(LoggingMixin): proposed_rate = self.exchange.get_rate( trade.pair, side='entry', is_short=trade.is_short, refresh=True) adjusted_entry_price = strategy_safe_wrapper(self.strategy.adjust_entry_price, - default_retval=proposed_rate)( + default_retval=order_obj.price)( trade=trade, order=order_obj, pair=trade.pair, current_time=datetime.now(timezone.utc), proposed_rate=proposed_rate, - entry_tag=trade.enter_tag, side=trade.entry_side) + current_order_rate=order_obj.price, entry_tag=trade.enter_tag, + side=trade.entry_side) full_cancel = False cancel_reason = constants.CANCEL_REASON['REPLACE'] if not adjusted_entry_price: full_cancel = True cancel_reason = constants.CANCEL_REASON['USER_CANCEL'] - if proposed_rate != adjusted_entry_price: + if order_obj.price != adjusted_entry_price: # cancel existing order if new price is supplied or None self.handle_cancel_enter(trade, order, cancel_reason, allow_full_cancel=full_cancel) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 0a7580b6f..a472a6943 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -465,30 +465,32 @@ class IStrategy(ABC, HyperStrategyMixin): return None def adjust_entry_price(self, trade: Trade, order: Optional[Order], pair: str, - current_time: datetime, proposed_rate: float, + current_time: datetime, proposed_rate: float, current_order_rate: float, entry_tag: Optional[str], side: str, **kwargs) -> float: """ Entry price re-adjustment logic, returning the user desired limit price. This only executes when a order was already placed, still open(unfilled fully or partially) and not timed out on subsequent candles after entry trigger. - For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/ + For full documentation please go to https://www.freqtrade.io/en/latest/strategy-callbacks/ - When not implemented by a strategy, returns proposed_stake. + When not implemented by a strategy, returns current_order_rate as default. + If current_order_rate is returned then the existing order is maintained. If None is returned then order gets canceled but not replaced by a new one. :param pair: Pair that's currently analyzed :param trade: Trade object. :param order: Order object :param current_time: datetime object, containing the current datetime - :param proposed_rate: Rate, calculated based on pricing settings in exit_pricing. + :param proposed_rate: Rate, calculated based on pricing settings in entry_pricing. + :param current_order_rate: Rate of the existing order in place. :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. :param side: 'long' or 'short' - indicating the direction of the proposed trade :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :return float: New entry price value if provided """ - return proposed_rate + return current_order_rate def leverage(self, pair: str, current_time: datetime, current_rate: float, proposed_leverage: float, max_leverage: float, side: str, diff --git a/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 b/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 index 176f567c7..7f9671bb1 100644 --- a/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 +++ b/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 @@ -30,31 +30,33 @@ def custom_entry_price(self, pair: str, current_time: 'datetime', proposed_rate: """ return proposed_rate - def adjust_entry_price(self, trade: Trade, order: Order, pair: str, - current_time: datetime, proposed_rate: float, - entry_tag: Optional[str], side: str, **kwargs) -> float: - """ - Entry price re-adjustment logic, returning the user desired limit price. - This only executes when a order was already placed, still open(unfilled fully or partially) - and not timed out on subsequent candles after entry trigger. +def adjust_entry_price(self, trade: Trade, order: Optional[Order], pair: str, + current_time: datetime, proposed_rate: float, current_order_rate: float, + entry_tag: Optional[str], side: str, **kwargs) -> float: + """ + Entry price re-adjustment logic, returning the user desired limit price. + This only executes when a order was already placed, still open(unfilled fully or partially) + and not timed out on subsequent candles after entry trigger. - For full documentation please go to https://www.freqtrade.io/en/latest/strategy-callbacks/ + For full documentation please go to https://www.freqtrade.io/en/latest/strategy-callbacks/ - When not implemented by a strategy, returns proposed_stake. - If None is returned then order gets canceled but not replaced by a new one. + When not implemented by a strategy, returns current_order_rate as default. + If current_order_rate is returned then the existing order is maintained. + If None is returned then order gets canceled but not replaced by a new one. - :param pair: Pair that's currently analyzed - :param trade: Trade object. - :param order: Order object - :param current_time: datetime object, containing the current datetime - :param proposed_rate: Rate, calculated based on pricing settings in entry_pricing. - :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. - :param side: 'long' or 'short' - indicating the direction of the proposed trade - :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. - :return float: New entry price value if provided + :param pair: Pair that's currently analyzed + :param trade: Trade object. + :param order: Order object + :param current_time: datetime object, containing the current datetime + :param proposed_rate: Rate, calculated based on pricing settings in entry_pricing. + :param current_order_rate: Rate of the existing order in place. + :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. + :param side: 'long' or 'short' - indicating the direction of the proposed trade + :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. + :return float: New entry price value if provided - """ - return proposed_rate + """ + return current_order_rate def custom_exit_price(self, pair: str, trade: 'Trade', current_time: 'datetime', proposed_rate: float,