From 18a3489a6f40551a331cbaf5f117a72e865ff117 Mon Sep 17 00:00:00 2001 From: Axel-CH Date: Mon, 18 Mar 2024 15:46:47 -0400 Subject: [PATCH 01/15] add order_filled callback to bot and backtest --- freqtrade/freqtradebot.py | 7 +++++++ freqtrade/optimize/backtesting.py | 3 +++ freqtrade/strategy/interface.py | 10 ++++++++++ 3 files changed, 20 insertions(+) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 8ad151108..649871df2 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -476,6 +476,9 @@ class FreqtradeBot(LoggingMixin): if not trade.is_open: # Trade was just closed trade.close_date = trade.date_last_filled_utc + strategy_safe_wrapper( + self.strategy.order_filled, default_retval=None)( + pair=trade.pair, trade=trade, current_time=datetime.now(timezone.utc)) self.order_close_notify(trade, order_obj, order_obj.ft_order_side == 'stoploss', send_msg=prev_trade_state != trade.is_open) @@ -1939,6 +1942,10 @@ class FreqtradeBot(LoggingMixin): trade = self._update_trade_after_fill(trade, order_obj) Trade.commit() + strategy_safe_wrapper( + self.strategy.order_filled, default_retval=None)( + pair=trade.pair, trade=trade, current_time=datetime.now(timezone.utc)) + self.order_close_notify(trade, order_obj, stoploss_order, send_msg) return False diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index b01bcf32e..bbf6f13f5 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -603,6 +603,9 @@ class Backtesting: if order and self._get_order_filled(order.ft_price, row): order.close_bt_order(current_date, trade) self._run_funding_fees(trade, current_date, force=True) + strategy_safe_wrapper( + self.strategy.order_filled, default_retval=None)( + pair=trade.pair, trade=trade, current_time=datetime.now(timezone.utc)) if not (order.ft_order_side == trade.exit_side and order.safe_amount == trade.amount): # trade is still open diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 2630c3547..04f5ce6c9 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -372,6 +372,16 @@ class IStrategy(ABC, HyperStrategyMixin): """ return True + def order_filled(self, pair: str, trade: Trade, current_time: datetime, **kwargs) -> None: + """ + Called just ofter order filling + :param pair: Pair for trade that's just exited. + :param trade: trade object. + :param current_time: datetime object, containing the current datetime + :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. + """ + pass + def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, after_fill: bool, **kwargs) -> Optional[float]: """ From cd986ced459081d8f993838654bb34ecc80acca9 Mon Sep 17 00:00:00 2001 From: Axel-CH Date: Mon, 18 Mar 2024 17:56:46 -0400 Subject: [PATCH 02/15] moove order_filled callback position from update_trade_state to _update_trade_after_fill --- freqtrade/freqtradebot.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 649871df2..fae5d175c 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1942,16 +1942,15 @@ class FreqtradeBot(LoggingMixin): trade = self._update_trade_after_fill(trade, order_obj) Trade.commit() - strategy_safe_wrapper( - self.strategy.order_filled, default_retval=None)( - pair=trade.pair, trade=trade, current_time=datetime.now(timezone.utc)) - self.order_close_notify(trade, order_obj, stoploss_order, send_msg) return False def _update_trade_after_fill(self, trade: Trade, order: Order) -> Trade: if order.status in constants.NON_OPEN_EXCHANGE_STATES: + strategy_safe_wrapper( + self.strategy.order_filled, default_retval=None)( + pair=trade.pair, trade=trade, current_time=datetime.now(timezone.utc)) # If a entry order was closed, force update on stoploss on exchange if order.ft_order_side == trade.entry_side: trade = self.cancel_stoploss_on_exchange(trade) From d1e1b8410bd895ab7c3814b98658e93d6c4cc270 Mon Sep 17 00:00:00 2001 From: Axel-CH Date: Mon, 18 Mar 2024 20:47:27 -0400 Subject: [PATCH 03/15] add order parameter to order_filled callback --- freqtrade/freqtradebot.py | 5 +++-- freqtrade/optimize/backtesting.py | 2 +- freqtrade/strategy/interface.py | 4 +++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index fae5d175c..41392cffb 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -478,7 +478,8 @@ class FreqtradeBot(LoggingMixin): trade.close_date = trade.date_last_filled_utc strategy_safe_wrapper( self.strategy.order_filled, default_retval=None)( - pair=trade.pair, trade=trade, current_time=datetime.now(timezone.utc)) + pair=trade.pair, trade=trade, order=order_obj, + current_time=datetime.now(timezone.utc)) self.order_close_notify(trade, order_obj, order_obj.ft_order_side == 'stoploss', send_msg=prev_trade_state != trade.is_open) @@ -1950,7 +1951,7 @@ class FreqtradeBot(LoggingMixin): if order.status in constants.NON_OPEN_EXCHANGE_STATES: strategy_safe_wrapper( self.strategy.order_filled, default_retval=None)( - pair=trade.pair, trade=trade, current_time=datetime.now(timezone.utc)) + pair=trade.pair, trade=trade, order=order, current_time=datetime.now(timezone.utc)) # If a entry order was closed, force update on stoploss on exchange if order.ft_order_side == trade.entry_side: trade = self.cancel_stoploss_on_exchange(trade) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index bbf6f13f5..4a3898d5e 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -605,7 +605,7 @@ class Backtesting: self._run_funding_fees(trade, current_date, force=True) strategy_safe_wrapper( self.strategy.order_filled, default_retval=None)( - pair=trade.pair, trade=trade, current_time=datetime.now(timezone.utc)) + pair=trade.pair, trade=trade, order=order, current_time=datetime.now(timezone.utc)) if not (order.ft_order_side == trade.exit_side and order.safe_amount == trade.amount): # trade is still open diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 04f5ce6c9..b3c6648e3 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -372,11 +372,13 @@ class IStrategy(ABC, HyperStrategyMixin): """ return True - def order_filled(self, pair: str, trade: Trade, current_time: datetime, **kwargs) -> None: + def order_filled(self, pair: str, trade: Trade, order: Order, + current_time: datetime, **kwargs) -> None: """ Called just ofter order filling :param pair: Pair for trade that's just exited. :param trade: trade object. + :param order: Order object. :param current_time: datetime object, containing the current datetime :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. """ From 08c1866cdc7ba611622b5acb427e763e2d1c4746 Mon Sep 17 00:00:00 2001 From: Axel-CH Date: Mon, 18 Mar 2024 20:57:47 -0400 Subject: [PATCH 04/15] fix mypy artype error --- freqtrade/optimize/backtesting.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 4a3898d5e..e6a066649 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -604,8 +604,10 @@ class Backtesting: order.close_bt_order(current_date, trade) self._run_funding_fees(trade, current_date, force=True) strategy_safe_wrapper( - self.strategy.order_filled, default_retval=None)( - pair=trade.pair, trade=trade, order=order, current_time=datetime.now(timezone.utc)) + self.strategy.order_filled, + default_retval=None)( + pair=trade.pair, trade=trade, # type: ignore[arg-type] + order=order, current_time=datetime.now(timezone.utc)) if not (order.ft_order_side == trade.exit_side and order.safe_amount == trade.amount): # trade is still open From 6941953a8be6bd52dc701d3af905166f2988721e Mon Sep 17 00:00:00 2001 From: Axel-CH Date: Mon, 18 Mar 2024 21:38:58 -0400 Subject: [PATCH 05/15] update doc details about order_filled callback details --- docs/bot-basics.md | 4 ++++ docs/strategy-callbacks.md | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/docs/bot-basics.md b/docs/bot-basics.md index a19f005db..424a26269 100644 --- a/docs/bot-basics.md +++ b/docs/bot-basics.md @@ -42,6 +42,8 @@ This will also run the `bot_start()` callback. By default, the bot loop runs every few seconds (`internals.process_throttle_secs`) and performs the following actions: * Fetch open trades from persistence. + * Update trades open order state from exchange + * Call `order_filled()` stategy callback for filled orders. * Calculate current list of tradable pairs. * Download OHLCV data for the pairlist including all [informative pairs](strategy-customization.md#get-data-for-non-tradeable-pairs) This step is only executed once per Candle to avoid unnecessary network traffic. @@ -86,8 +88,10 @@ This loop will be repeated again and again until the bot is stopped. * In Margin and Futures mode, `leverage()` strategy callback is called to determine the desired leverage. * Determine stake size by calling the `custom_stake_amount()` callback. * Check position adjustments for open trades if enabled and call `adjust_trade_position()` to determine if an additional order is requested. + * Call `order_filled()` stategy callback for filled entry orders. * Call `custom_stoploss()` and `custom_exit()` to find custom exit points. * For exits based on exit-signal, custom-exit and partial exits: Call `custom_exit_price()` to determine exit price (Prices are moved to be within the closing candle). + * Call `order_filled()` stategy callback for filled exit orders. * Generate backtest report output !!! Note diff --git a/docs/strategy-callbacks.md b/docs/strategy-callbacks.md index 2f04e906e..bc5ad3cad 100644 --- a/docs/strategy-callbacks.md +++ b/docs/strategy-callbacks.md @@ -19,6 +19,7 @@ Currently available callbacks: * [`adjust_trade_position()`](#adjust-trade-position) * [`adjust_entry_price()`](#adjust-entry-price) * [`leverage()`](#leverage-callback) +* [`order_filled()`](#oder-filled-callback) !!! Tip "Callback calling sequence" You can find the callback calling sequence in [bot-basics](bot-basics.md#bot-execution-logic) @@ -1022,3 +1023,30 @@ class AwesomeStrategy(IStrategy): All profit calculations include leverage. Stoploss / ROI also include leverage in their calculation. Defining a stoploss of 10% at 10x leverage would trigger the stoploss with a 1% move to the downside. + +## Order filled Callback + +The `order_filled()` callback may be used by strategy developer to perform specific actions based on current trade state after an order is filled. + +Assuming that your strategy need to store the high value of the candle at trade entry, this is possible with this callback as the following exemple show. + +``` python +class AwesomeStrategy(IStrategy): + def order_filled(self, pair: str, trade: Trade, order: Order, current_time: datetime, **kwargs) -> None: + """ + Called just ofter order filling + :param pair: Pair for trade that's just exited. + :param trade: trade object. + :param current_time: datetime object, containing the current datetime + :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. + """ + # Obtain pair dataframe (just to show how to access it) + dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + + if (trade.nr_of_successful_entries == 1) and (order.ft_order_side == trade.entry_side): + trade.set_custom_data(key='entry_candle_high', value=last_candle['high']) + + return None + +``` From 996fcb6f563cb6a338c289dbb9603eee28c95ccf Mon Sep 17 00:00:00 2001 From: Axel-CH Date: Mon, 18 Mar 2024 22:07:43 -0400 Subject: [PATCH 06/15] fix current_time parameter of order_filled usage in backtest --- freqtrade/optimize/backtesting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index e6a066649..c2f77a9c8 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -607,7 +607,7 @@ class Backtesting: self.strategy.order_filled, default_retval=None)( pair=trade.pair, trade=trade, # type: ignore[arg-type] - order=order, current_time=datetime.now(timezone.utc)) + order=order, current_time=current_date) if not (order.ft_order_side == trade.exit_side and order.safe_amount == trade.amount): # trade is still open From 72225daa6af3514cc45d5ca6dde5afcfeea1617e Mon Sep 17 00:00:00 2001 From: Axel-CH Date: Thu, 21 Mar 2024 19:36:58 -0400 Subject: [PATCH 07/15] add order_filled callback call for stoploss orders --- freqtrade/freqtradebot.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 41392cffb..41261c4e2 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1232,6 +1232,10 @@ class FreqtradeBot(LoggingMixin): # We check if stoploss order is fulfilled if stoploss_order and stoploss_order['status'] in ('closed', 'triggered'): trade.exit_reason = ExitType.STOPLOSS_ON_EXCHANGE.value + strategy_safe_wrapper( + self.strategy.order_filled, default_retval=None)( + pair=trade.pair, trade=trade, order=slo, + current_time=datetime.now(timezone.utc)) self._notify_exit(trade, "stoploss", True) self.handle_protections(trade.pair, trade.trade_direction) return True From 6d3e3b5bfa99ba4713a352627ed5d899d8971076 Mon Sep 17 00:00:00 2001 From: Axel-CH Date: Thu, 21 Mar 2024 20:19:53 -0400 Subject: [PATCH 08/15] add test for order_filled for freqtradebot update_trade_state --- tests/freqtradebot/test_freqtradebot.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/freqtradebot/test_freqtradebot.py b/tests/freqtradebot/test_freqtradebot.py index 1891c2332..32e9e68c5 100644 --- a/tests/freqtradebot/test_freqtradebot.py +++ b/tests/freqtradebot/test_freqtradebot.py @@ -1233,6 +1233,7 @@ def test_update_trade_state(mocker, default_conf_usdt, limit_order, is_short, ca order_id=order_id, )) + freqtrade.strategy.order_filled = MagicMock(return_value=None) assert not freqtrade.update_trade_state(trade, None) assert log_has_re(r'Orderid for trade .* is empty.', caplog) caplog.clear() @@ -1243,6 +1244,7 @@ def test_update_trade_state(mocker, default_conf_usdt, limit_order, is_short, ca caplog.clear() assert not trade.has_open_orders assert trade.amount == order['amount'] + assert freqtrade.strategy.order_filled.call_count == 1 mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=0.01) assert trade.amount == 30.0 @@ -1260,11 +1262,13 @@ def test_update_trade_state(mocker, default_conf_usdt, limit_order, is_short, ca limit_buy_order_usdt_new['filled'] = 0.0 limit_buy_order_usdt_new['status'] = 'canceled' + freqtrade.strategy.order_filled = MagicMock(return_value=None) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', side_effect=ValueError) mocker.patch(f'{EXMS}.fetch_order', return_value=limit_buy_order_usdt_new) res = freqtrade.update_trade_state(trade, order_id) # Cancelled empty assert res is True + assert freqtrade.strategy.order_filled.call_count == 0 @pytest.mark.parametrize("is_short", [False, True]) From e07f3d266ef42d8f1a3023a80df671fd055566f1 Mon Sep 17 00:00:00 2001 From: Axel-CH Date: Thu, 21 Mar 2024 20:53:11 -0400 Subject: [PATCH 09/15] add test for order_filled callback sl on exchange filled case --- tests/freqtradebot/test_stoploss_on_exchange.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/freqtradebot/test_stoploss_on_exchange.py b/tests/freqtradebot/test_stoploss_on_exchange.py index 325fe549f..04a04ea9f 100644 --- a/tests/freqtradebot/test_stoploss_on_exchange.py +++ b/tests/freqtradebot/test_stoploss_on_exchange.py @@ -146,10 +146,12 @@ def test_handle_stoploss_on_exchange(mocker, default_conf_usdt, fee, caplog, is_ 'amount': enter_order['amount'], }) mocker.patch(f'{EXMS}.fetch_stoploss_order', stoploss_order_hit) + freqtrade.strategy.order_filled = MagicMock(return_value=None) assert freqtrade.handle_stoploss_on_exchange(trade) is True assert log_has_re(r'STOP_LOSS_LIMIT is hit for Trade\(id=1, .*\)\.', caplog) assert len(trade.open_sl_orders) == 0 assert trade.is_open is False + assert freqtrade.strategy.order_filled.call_count == 1 caplog.clear() mocker.patch(f'{EXMS}.create_stoploss', side_effect=ExchangeError()) From b2a67226871de7bc91b08bb7ec665c9ef24eccc6 Mon Sep 17 00:00:00 2001 From: Axel-CH Date: Thu, 21 Mar 2024 21:51:52 -0400 Subject: [PATCH 10/15] remove order filled cb from handle_stoploss_on_exchange --- freqtrade/freqtradebot.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 41261c4e2..41392cffb 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1232,10 +1232,6 @@ class FreqtradeBot(LoggingMixin): # We check if stoploss order is fulfilled if stoploss_order and stoploss_order['status'] in ('closed', 'triggered'): trade.exit_reason = ExitType.STOPLOSS_ON_EXCHANGE.value - strategy_safe_wrapper( - self.strategy.order_filled, default_retval=None)( - pair=trade.pair, trade=trade, order=slo, - current_time=datetime.now(timezone.utc)) self._notify_exit(trade, "stoploss", True) self.handle_protections(trade.pair, trade.trade_direction) return True From f6a8cb4698cc24550b50ad6a8eb3aae77ca8ab72 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 24 Mar 2024 11:44:55 +0100 Subject: [PATCH 11/15] update docs (fix typos) --- docs/bot-basics.md | 6 +++--- docs/strategy-callbacks.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/bot-basics.md b/docs/bot-basics.md index 424a26269..9fe357297 100644 --- a/docs/bot-basics.md +++ b/docs/bot-basics.md @@ -43,7 +43,7 @@ By default, the bot loop runs every few seconds (`internals.process_throttle_sec * Fetch open trades from persistence. * Update trades open order state from exchange - * Call `order_filled()` stategy callback for filled orders. + * Call `order_filled()` strategy callback for filled orders. * Calculate current list of tradable pairs. * Download OHLCV data for the pairlist including all [informative pairs](strategy-customization.md#get-data-for-non-tradeable-pairs) This step is only executed once per Candle to avoid unnecessary network traffic. @@ -88,10 +88,10 @@ This loop will be repeated again and again until the bot is stopped. * In Margin and Futures mode, `leverage()` strategy callback is called to determine the desired leverage. * Determine stake size by calling the `custom_stake_amount()` callback. * Check position adjustments for open trades if enabled and call `adjust_trade_position()` to determine if an additional order is requested. - * Call `order_filled()` stategy callback for filled entry orders. + * Call `order_filled()` strategy callback for filled entry orders. * Call `custom_stoploss()` and `custom_exit()` to find custom exit points. * For exits based on exit-signal, custom-exit and partial exits: Call `custom_exit_price()` to determine exit price (Prices are moved to be within the closing candle). - * Call `order_filled()` stategy callback for filled exit orders. + * Call `order_filled()` strategy callback for filled exit orders. * Generate backtest report output !!! Note diff --git a/docs/strategy-callbacks.md b/docs/strategy-callbacks.md index bc5ad3cad..cd84bc2ba 100644 --- a/docs/strategy-callbacks.md +++ b/docs/strategy-callbacks.md @@ -1028,7 +1028,7 @@ Defining a stoploss of 10% at 10x leverage would trigger the stoploss with a 1% The `order_filled()` callback may be used by strategy developer to perform specific actions based on current trade state after an order is filled. -Assuming that your strategy need to store the high value of the candle at trade entry, this is possible with this callback as the following exemple show. +Assuming that your strategy need to store the high value of the candle at trade entry, this is possible with this callback as the following example show. ``` python class AwesomeStrategy(IStrategy): From ea634e5cef05a892af3111da462d212555ba5a40 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 24 Mar 2024 11:54:25 +0100 Subject: [PATCH 12/15] Add test for backtesting --- tests/optimize/test_backtesting.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 603fcc310..8fc42b474 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -698,6 +698,7 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None: data = history.load_data(datadir=testdatadir, timeframe='5m', pairs=['UNITTEST/BTC'], timerange=timerange) processed = backtesting.strategy.advise_all_indicators(data) + backtesting.strategy.order_filled = MagicMock() min_date, max_date = get_timerange(processed) result = backtesting.backtest( @@ -760,6 +761,8 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None: pd.testing.assert_frame_equal(results, expected) assert 'orders' in results.columns data_pair = processed[pair] + # Called once per order + assert backtesting.strategy.order_filled.call_count == 4 for _, t in results.iterrows(): assert len(t['orders']) == 2 ln = data_pair.loc[data_pair["date"] == t["open_date"]] From f60d6c8f65c5ac7c80bd4c4a6839469e1e641eda Mon Sep 17 00:00:00 2001 From: Axel-CH Date: Mon, 25 Mar 2024 13:12:13 -0400 Subject: [PATCH 13/15] remove duplicate call of order_filled callback --- freqtrade/freqtradebot.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 41392cffb..ac8296480 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -476,10 +476,6 @@ class FreqtradeBot(LoggingMixin): if not trade.is_open: # Trade was just closed trade.close_date = trade.date_last_filled_utc - strategy_safe_wrapper( - self.strategy.order_filled, default_retval=None)( - pair=trade.pair, trade=trade, order=order_obj, - current_time=datetime.now(timezone.utc)) self.order_close_notify(trade, order_obj, order_obj.ft_order_side == 'stoploss', send_msg=prev_trade_state != trade.is_open) From e49ab2593cc2fbfc65e4c95d519256d24c601578 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 28 Mar 2024 06:49:02 +0100 Subject: [PATCH 14/15] Update / improve docs --- docs/strategy-callbacks.md | 13 ++++++++----- freqtrade/strategy/interface.py | 5 +++-- .../strategy_methods_advanced.j2 | 14 ++++++++++++++ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/docs/strategy-callbacks.md b/docs/strategy-callbacks.md index cd84bc2ba..b4eb4e395 100644 --- a/docs/strategy-callbacks.md +++ b/docs/strategy-callbacks.md @@ -19,7 +19,7 @@ Currently available callbacks: * [`adjust_trade_position()`](#adjust-trade-position) * [`adjust_entry_price()`](#adjust-entry-price) * [`leverage()`](#leverage-callback) -* [`order_filled()`](#oder-filled-callback) +* [`order_filled()`](#order-filled-callback) !!! Tip "Callback calling sequence" You can find the callback calling sequence in [bot-basics](bot-basics.md#bot-execution-logic) @@ -1026,17 +1026,20 @@ Defining a stoploss of 10% at 10x leverage would trigger the stoploss with a 1% ## Order filled Callback -The `order_filled()` callback may be used by strategy developer to perform specific actions based on current trade state after an order is filled. +The `order_filled()` callback may be used to perform specific actions based on the current trade state after an order is filled. +It will be called independently of the order type (entry, exit, stoploss or position adjustment). -Assuming that your strategy need to store the high value of the candle at trade entry, this is possible with this callback as the following example show. +Assuming that your strategy needs to store the high value of the candle at trade entry, this is possible with this callback as the following example show. ``` python class AwesomeStrategy(IStrategy): def order_filled(self, pair: str, trade: Trade, order: Order, current_time: datetime, **kwargs) -> None: """ - Called just ofter order filling - :param pair: Pair for trade that's just exited. + Called right after an order fills. + Will be called for all order types (entry, exit, stoploss, position adjustment). + :param pair: Pair for trade :param trade: trade object. + :param order: Order object. :param current_time: datetime object, containing the current datetime :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. """ diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index b3c6648e3..f8a890d5d 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -375,8 +375,9 @@ class IStrategy(ABC, HyperStrategyMixin): def order_filled(self, pair: str, trade: Trade, order: Order, current_time: datetime, **kwargs) -> None: """ - Called just ofter order filling - :param pair: Pair for trade that's just exited. + Called right after an order fills. + Will be called for all order types (entry, exit, stoploss, position adjustment). + :param pair: Pair for trade :param trade: trade object. :param order: Order object. :param current_time: datetime object, containing the current datetime diff --git a/freqtrade/templates/strategy_subtemplates/strategy_methods_advanced.j2 b/freqtrade/templates/strategy_subtemplates/strategy_methods_advanced.j2 index 6fad129c7..541c26e87 100644 --- a/freqtrade/templates/strategy_subtemplates/strategy_methods_advanced.j2 +++ b/freqtrade/templates/strategy_subtemplates/strategy_methods_advanced.j2 @@ -300,3 +300,17 @@ def leverage(self, pair: str, current_time: datetime, current_rate: float, :return: A leverage amount, which is between 1.0 and max_leverage. """ return 1.0 + + +def order_filled(self, pair: str, trade: 'Trade', order: 'Order', + current_time: datetime, **kwargs) -> None: + """ + Called right after an order fills. + Will be called for all order types (entry, exit, stoploss, position adjustment). + :param pair: Pair for trade + :param trade: trade object. + :param order: Order object. + :param current_time: datetime object, containing the current datetime + :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. + """ + pass From 38e7b0e8aedcb49d0ac154ddd178110244009edf Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 28 Mar 2024 06:55:45 +0100 Subject: [PATCH 15/15] Update bot basics logs to align with reality --- docs/bot-basics.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/bot-basics.md b/docs/bot-basics.md index 9fe357297..1c88559c0 100644 --- a/docs/bot-basics.md +++ b/docs/bot-basics.md @@ -33,7 +33,6 @@ For spot pairs, naming will be `base/quote` (e.g. `ETH/USDT`). For futures pairs, naming will be `base/quote:settle` (e.g. `ETH/USDT:USDT`). - ## Bot execution logic Starting freqtrade in dry-run or live mode (using `freqtrade trade`) will start the bot and start the bot iteration loop. @@ -42,8 +41,6 @@ This will also run the `bot_start()` callback. By default, the bot loop runs every few seconds (`internals.process_throttle_secs`) and performs the following actions: * Fetch open trades from persistence. - * Update trades open order state from exchange - * Call `order_filled()` strategy callback for filled orders. * Calculate current list of tradable pairs. * Download OHLCV data for the pairlist including all [informative pairs](strategy-customization.md#get-data-for-non-tradeable-pairs) This step is only executed once per Candle to avoid unnecessary network traffic. @@ -52,10 +49,12 @@ By default, the bot loop runs every few seconds (`internals.process_throttle_sec * Call `populate_indicators()` * Call `populate_entry_trend()` * Call `populate_exit_trend()` -* Check timeouts for open orders. - * Calls `check_entry_timeout()` strategy callback for open entry orders. - * Calls `check_exit_timeout()` strategy callback for open exit orders. - * Calls `adjust_entry_price()` strategy callback for open entry orders. +* Update trades open order state from exchange. + * Call `order_filled()` strategy callback for filled orders. + * Check timeouts for open orders. + * Calls `check_entry_timeout()` strategy callback for open entry orders. + * Calls `check_exit_timeout()` strategy callback for open exit orders. + * Calls `adjust_entry_price()` strategy callback for open entry orders. * Verifies existing positions and eventually places exit orders. * Considers stoploss, ROI and exit-signal, `custom_exit()` and `custom_stoploss()`. * Determine exit-price based on `exit_pricing` configuration setting or by using the `custom_exit_price()` callback.