custom_sell -> custom_exit
This commit is contained in:
parent
c830db62f6
commit
69c0e028c2
@ -517,7 +517,7 @@ freqtrade backtesting --strategy AwesomeStrategy --timeframe 1h --timeframe-deta
|
|||||||
```
|
```
|
||||||
|
|
||||||
This will load 1h data as well as 5m data for the timeframe. The strategy will be analyzed with the 1h timeframe - and for every "open trade candle" (candles where a trade is open) the 5m data will be used to simulate intra-candle movements.
|
This will load 1h data as well as 5m data for the timeframe. The strategy will be analyzed with the 1h timeframe - and for every "open trade candle" (candles where a trade is open) the 5m data will be used to simulate intra-candle movements.
|
||||||
All callback functions (`custom_sell()`, `custom_stoploss()`, ... ) will be running for each 5m candle once the trade is opened (so 12 times in the above example of 1h timeframe, and 5m detailed timeframe).
|
All callback functions (`custom_exit()`, `custom_stoploss()`, ... ) will be running for each 5m candle once the trade is opened (so 12 times in the above example of 1h timeframe, and 5m detailed timeframe).
|
||||||
|
|
||||||
`--timeframe-detail` must be smaller than the original timeframe, otherwise backtesting will fail to start.
|
`--timeframe-detail` must be smaller than the original timeframe, otherwise backtesting will fail to start.
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ By default, loop runs every few seconds (`internals.process_throttle_secs`) and
|
|||||||
* Calls `check_buy_timeout()` strategy callback for open buy orders.
|
* Calls `check_buy_timeout()` strategy callback for open buy orders.
|
||||||
* Calls `check_sell_timeout()` strategy callback for open sell orders.
|
* Calls `check_sell_timeout()` strategy callback for open sell orders.
|
||||||
* Verifies existing positions and eventually places sell orders.
|
* Verifies existing positions and eventually places sell orders.
|
||||||
* Considers stoploss, ROI and sell-signal, `custom_sell()` and `custom_stoploss()`.
|
* Considers stoploss, ROI and sell-signal, `custom_exit()` and `custom_stoploss()`.
|
||||||
* Determine sell-price based on `ask_strategy` configuration setting or by using the `custom_exit_price()` callback.
|
* Determine sell-price based on `ask_strategy` configuration setting or by using the `custom_exit_price()` callback.
|
||||||
* Before a sell order is placed, `confirm_trade_exit()` strategy callback is called.
|
* Before a sell order is placed, `confirm_trade_exit()` strategy callback is called.
|
||||||
* Check if trade-slots are still available (if `max_open_trades` is reached).
|
* Check if trade-slots are still available (if `max_open_trades` is reached).
|
||||||
@ -59,7 +59,7 @@ This loop will be repeated again and again until the bot is stopped.
|
|||||||
* Confirm trade buy / sell (calls `confirm_trade_entry()` and `confirm_trade_exit()` if implemented in the strategy).
|
* Confirm trade buy / sell (calls `confirm_trade_entry()` and `confirm_trade_exit()` if implemented in the strategy).
|
||||||
* Call `custom_entry_price()` (if implemented in the strategy) to determine entry price (Prices are moved to be within the opening candle).
|
* Call `custom_entry_price()` (if implemented in the strategy) to determine entry price (Prices are moved to be within the opening candle).
|
||||||
* Determine stake size by calling the `custom_stake_amount()` callback.
|
* Determine stake size by calling the `custom_stake_amount()` callback.
|
||||||
* Call `custom_stoploss()` and `custom_sell()` to find custom exit points.
|
* Call `custom_stoploss()` and `custom_exit()` to find custom exit points.
|
||||||
* For sells based on sell-signal and custom-sell: Call `custom_exit_price()` to determine exit price (Prices are moved to be within the closing candle).
|
* For sells based on sell-signal and custom-sell: Call `custom_exit_price()` to determine exit price (Prices are moved to be within the closing candle).
|
||||||
|
|
||||||
* Generate backtest report output
|
* Generate backtest report output
|
||||||
|
@ -80,7 +80,7 @@ class AwesomeStrategy(IStrategy):
|
|||||||
## Enter Tag
|
## Enter Tag
|
||||||
|
|
||||||
When your strategy has multiple buy signals, you can name the signal that triggered.
|
When your strategy has multiple buy signals, you can name the signal that triggered.
|
||||||
Then you can access you buy signal on `custom_sell`
|
Then you can access you buy signal on `custom_exit`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||||
@ -93,7 +93,7 @@ def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
|||||||
|
|
||||||
return dataframe
|
return dataframe
|
||||||
|
|
||||||
def custom_sell(self, pair: str, trade: Trade, current_time: datetime, current_rate: float,
|
def custom_exit(self, pair: str, trade: Trade, current_time: datetime, current_rate: float,
|
||||||
current_profit: float, **kwargs):
|
current_profit: float, **kwargs):
|
||||||
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
|
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
|
||||||
last_candle = dataframe.iloc[-1].squeeze()
|
last_candle = dataframe.iloc[-1].squeeze()
|
||||||
|
@ -9,7 +9,7 @@ Currently available callbacks:
|
|||||||
|
|
||||||
* [`bot_loop_start()`](#bot-loop-start)
|
* [`bot_loop_start()`](#bot-loop-start)
|
||||||
* [`custom_stake_amount()`](#custom-stake-size)
|
* [`custom_stake_amount()`](#custom-stake-size)
|
||||||
* [`custom_sell()`](#custom-sell-signal)
|
* [`custom_exit()`](#custom-sell-signal)
|
||||||
* [`custom_stoploss()`](#custom-stoploss)
|
* [`custom_stoploss()`](#custom-stoploss)
|
||||||
* [`custom_entry_price()` and `custom_exit_price()`](#custom-order-price-rules)
|
* [`custom_entry_price()` and `custom_exit_price()`](#custom-order-price-rules)
|
||||||
* [`check_buy_timeout()` and `check_sell_timeout()](#custom-order-timeout-rules)
|
* [`check_buy_timeout()` and `check_sell_timeout()](#custom-order-timeout-rules)
|
||||||
@ -84,9 +84,9 @@ Called for open trade every throttling iteration (roughly every 5 seconds) until
|
|||||||
|
|
||||||
Allows to define custom sell signals, indicating that specified position should be sold. This is very useful when we need to customize sell conditions for each individual trade, or if you need trade data to make an exit decision.
|
Allows to define custom sell signals, indicating that specified position should be sold. This is very useful when we need to customize sell conditions for each individual trade, or if you need trade data to make an exit decision.
|
||||||
|
|
||||||
For example you could implement a 1:2 risk-reward ROI with `custom_sell()`.
|
For example you could implement a 1:2 risk-reward ROI with `custom_exit()`.
|
||||||
|
|
||||||
Using custom_sell() signals in place of stoploss though *is not recommended*. It is a inferior method to using `custom_stoploss()` in this regard - which also allows you to keep the stoploss on exchange.
|
Using custom_exit() signals in place of stoploss though *is not recommended*. It is a inferior method to using `custom_stoploss()` in this regard - which also allows you to keep the stoploss on exchange.
|
||||||
|
|
||||||
!!! Note
|
!!! Note
|
||||||
Returning a (none-empty) `string` or `True` from this method is equal to setting sell signal on a candle at specified time. This method is not called when sell signal is set already, or if sell signals are disabled (`use_exit_signal=False` or `sell_profit_only=True` while profit is below `sell_profit_offset`). `string` max length is 64 characters. Exceeding this limit will cause the message to be truncated to 64 characters.
|
Returning a (none-empty) `string` or `True` from this method is equal to setting sell signal on a candle at specified time. This method is not called when sell signal is set already, or if sell signals are disabled (`use_exit_signal=False` or `sell_profit_only=True` while profit is below `sell_profit_offset`). `string` max length is 64 characters. Exceeding this limit will cause the message to be truncated to 64 characters.
|
||||||
@ -95,7 +95,7 @@ An example of how we can use different indicators depending on the current profi
|
|||||||
|
|
||||||
``` python
|
``` python
|
||||||
class AwesomeStrategy(IStrategy):
|
class AwesomeStrategy(IStrategy):
|
||||||
def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float,
|
def custom_exit(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float,
|
||||||
current_profit: float, **kwargs):
|
current_profit: float, **kwargs):
|
||||||
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
|
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
|
||||||
last_candle = dataframe.iloc[-1].squeeze()
|
last_candle = dataframe.iloc[-1].squeeze()
|
||||||
|
@ -90,7 +90,7 @@ Example configuration showing the different settings:
|
|||||||
"trailing_stop_loss": "on",
|
"trailing_stop_loss": "on",
|
||||||
"stop_loss": "on",
|
"stop_loss": "on",
|
||||||
"stoploss_on_exchange": "on",
|
"stoploss_on_exchange": "on",
|
||||||
"custom_sell": "silent"
|
"custom_exit": "silent"
|
||||||
},
|
},
|
||||||
"buy_cancel": "silent",
|
"buy_cancel": "silent",
|
||||||
"sell_cancel": "on",
|
"sell_cancel": "on",
|
||||||
|
@ -12,7 +12,7 @@ class ExitType(Enum):
|
|||||||
EXIT_SIGNAL = "exit_signal"
|
EXIT_SIGNAL = "exit_signal"
|
||||||
FORCE_EXIT = "force_exit"
|
FORCE_EXIT = "force_exit"
|
||||||
EMERGENCY_EXIT = "emergency_sell"
|
EMERGENCY_EXIT = "emergency_sell"
|
||||||
CUSTOM_EXIT = "custom_sell"
|
CUSTOM_EXIT = "custom_exit"
|
||||||
NONE = ""
|
NONE = ""
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -342,7 +342,7 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
"""
|
"""
|
||||||
return proposed_rate
|
return proposed_rate
|
||||||
|
|
||||||
def custom_sell(self, pair: str, trade: Trade, current_time: datetime, current_rate: float,
|
def custom_exit(self, pair: str, trade: Trade, current_time: datetime, current_rate: float,
|
||||||
current_profit: float, **kwargs) -> Optional[Union[str, bool]]:
|
current_profit: float, **kwargs) -> Optional[Union[str, bool]]:
|
||||||
"""
|
"""
|
||||||
Custom exit signal logic indicating that specified position should be sold. Returning a
|
Custom exit signal logic indicating that specified position should be sold. Returning a
|
||||||
@ -798,7 +798,7 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
exit_signal = ExitType.EXIT_SIGNAL
|
exit_signal = ExitType.EXIT_SIGNAL
|
||||||
else:
|
else:
|
||||||
trade_type = "exit_short" if trade.is_short else "sell"
|
trade_type = "exit_short" if trade.is_short else "sell"
|
||||||
custom_reason = strategy_safe_wrapper(self.custom_sell, default_retval=False)(
|
custom_reason = strategy_safe_wrapper(self.custom_exit, default_retval=False)(
|
||||||
pair=trade.pair, trade=trade, current_time=date, current_rate=current_rate,
|
pair=trade.pair, trade=trade, current_time=date, current_rate=current_rate,
|
||||||
current_profit=current_profit)
|
current_profit=current_profit)
|
||||||
if custom_reason:
|
if custom_reason:
|
||||||
|
@ -53,7 +53,7 @@ def custom_stoploss(self, pair: str, trade: 'Trade', current_time: 'datetime',
|
|||||||
"""
|
"""
|
||||||
return self.stoploss
|
return self.stoploss
|
||||||
|
|
||||||
def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float,
|
def custom_exit(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float,
|
||||||
current_profit: float, **kwargs) -> 'Optional[Union[str, bool]]':
|
current_profit: float, **kwargs) -> 'Optional[Union[str, bool]]':
|
||||||
"""
|
"""
|
||||||
Custom sell signal logic indicating that specified position should be sold. Returning a
|
Custom sell signal logic indicating that specified position should be sold. Returning a
|
||||||
|
@ -460,7 +460,7 @@ def test_stop_loss_reached(default_conf, fee, profit, adjusted, expected, traili
|
|||||||
strategy.custom_stoploss = original_stopvalue
|
strategy.custom_stoploss = original_stopvalue
|
||||||
|
|
||||||
|
|
||||||
def test_custom_sell(default_conf, fee, caplog) -> None:
|
def test_custom_exit(default_conf, fee, caplog) -> None:
|
||||||
|
|
||||||
strategy = StrategyResolver.load_strategy(default_conf)
|
strategy = StrategyResolver.load_strategy(default_conf)
|
||||||
trade = Trade(
|
trade = Trade(
|
||||||
@ -482,15 +482,15 @@ def test_custom_sell(default_conf, fee, caplog) -> None:
|
|||||||
assert res.sell_flag is False
|
assert res.sell_flag is False
|
||||||
assert res.exit_type == ExitType.NONE
|
assert res.exit_type == ExitType.NONE
|
||||||
|
|
||||||
strategy.custom_sell = MagicMock(return_value=True)
|
strategy.custom_exit = MagicMock(return_value=True)
|
||||||
res = strategy.should_exit(trade, 1, now,
|
res = strategy.should_exit(trade, 1, now,
|
||||||
enter=False, exit_=False,
|
enter=False, exit_=False,
|
||||||
low=None, high=None)
|
low=None, high=None)
|
||||||
assert res.sell_flag is True
|
assert res.sell_flag is True
|
||||||
assert res.exit_type == ExitType.CUSTOM_EXIT
|
assert res.exit_type == ExitType.CUSTOM_EXIT
|
||||||
assert res.exit_reason == 'custom_sell'
|
assert res.exit_reason == 'custom_exit'
|
||||||
|
|
||||||
strategy.custom_sell = MagicMock(return_value='hello world')
|
strategy.custom_exit = MagicMock(return_value='hello world')
|
||||||
|
|
||||||
res = strategy.should_exit(trade, 1, now,
|
res = strategy.should_exit(trade, 1, now,
|
||||||
enter=False, exit_=False,
|
enter=False, exit_=False,
|
||||||
@ -500,14 +500,14 @@ def test_custom_sell(default_conf, fee, caplog) -> None:
|
|||||||
assert res.exit_reason == 'hello world'
|
assert res.exit_reason == 'hello world'
|
||||||
|
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
strategy.custom_sell = MagicMock(return_value='h' * 100)
|
strategy.custom_exit = MagicMock(return_value='h' * 100)
|
||||||
res = strategy.should_exit(trade, 1, now,
|
res = strategy.should_exit(trade, 1, now,
|
||||||
enter=False, exit_=False,
|
enter=False, exit_=False,
|
||||||
low=None, high=None)
|
low=None, high=None)
|
||||||
assert res.exit_type == ExitType.CUSTOM_EXIT
|
assert res.exit_type == ExitType.CUSTOM_EXIT
|
||||||
assert res.sell_flag is True
|
assert res.sell_flag is True
|
||||||
assert res.exit_reason == 'h' * 64
|
assert res.exit_reason == 'h' * 64
|
||||||
assert log_has_re('Custom sell reason returned from custom_sell is too long.*', caplog)
|
assert log_has_re('Custom sell reason returned from custom_exit is too long.*', caplog)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('side', TRADE_SIDES)
|
@pytest.mark.parametrize('side', TRADE_SIDES)
|
||||||
|
Loading…
Reference in New Issue
Block a user