Trailing Custom Exit

By default, Freqtrade already have custom_exit and custom_stoploss functions. Despite the name, actually they are both quite similar and can be used interchangeable, as long as you are aware of the differences between them. Two main differences between them that are quite significant

Trailing stop vs immediate exit

If you use custom_stoploss, the exit will be in form of trailing stoploss, compared to immediate exit of custom_exit. Both have their own pro and cons. Some prefer guaranteed fixed profit of immediate exit, while some would be okay to try riding the green candle with consequence of getting lesser profit when the green candle isn’t as strong as you would hope.

Exit tag

You can’t give exit tag on custom_stoploss. All exits using it will be marked as trailing stoploss. While it might be okay in dry/live run, it isn’t favorable for strategy development, since you might want to know how each exit logics perform and which one needs more tweaks.

That’s why I come up with Trailing Custom Exit. The idea is simple. In backtest, put your exit logics inside custom_exit (instead of custom_stoploss). You will get the exit tag in the backtest output, and you can tweak them if you want to. Then for dry/live run, use my snippet to turn the custom_exit into custom_stoploss.

Things to do

  • Only copy start from line 5 and below. Put them in the same file as yout strategy, below the main strategy class
  • Change your_base_strategy_class_name with the actual class name of strategy that you want to inherit
  • Change trailing_custom_exit class name to a new name that you prefer
  • On line 26, I add a code to send message to your telegram/webhook (if you use any of them) to let you know which exit tag is being triggered in the custom_exit. The exit reason being stored in the database will be trailing_stop_loss, so having the actual tag being sent to your telegram/webhook will be useful for later use. You can delete that line if you don’t want to get the message
  • On line 36, change the trailing to the number you prefer. And since we have stored the exit tag, you can also set different trailing for each exit tags. Just do if else blocks there (I believe I don’t have to teach you this)
  • Use the original class name when you are doing backtest/hyperopt, and use the new name on your dry/live run config
class your_base_strategy_class_name (IStrategy):
# your real strategy is here

class trailing_custom_exit (your_base_strategy_class_name):
    custom_info_trail_sell = dict()

    init_trailing_sell_dict = {
        'tsl': False,
        'tag': ''.

    use_custom_stoploss = True

    def custom_exit(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> Optional[Union[str, bool]]:

        if ((pair not in self.custom_info_trail_sell)) or ((pair in self.custom_info_trail_sell) and (self.custom_info_trail_sell[pair]['tsl'] == False)):
            val = super().custom_exit(pair, trade, current_time, current_rate, current_profit, **kwargs)
            if val:
                if pair not in self.custom_info_trail_sell:
                    self.custom_info_trail_sell[pair] = self.init_trailing_sell_dict.copy()
                self.custom_info_trail_sell[pair]['tsl'] = True
                self.custom_info_trail_sell[pair]['tag'] = val

                val = val.replace("_", " ")
                self.dp.send_msg(f"Exit *{val}* for *{pair}* triggered at *{current_rate}*. Activating trailing sell!")

        return None

    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float:
        sl_new = super().custom_stoploss(pair, trade, current_time, current_rate, current_profit, **kwargs)

        if pair in self.custom_info_trail_sell:
            if(self.custom_info_trail_sell[pair]['tsl'] == True):
                sl_new = 0.01

        return sl_new

    def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, rate: float, time_in_force: str, exit_reason: str, current_time: datetime, **kwargs) -> bool:

        val = super().confirm_trade_exit(pair, trade, order_type, amount, rate, time_in_force, exit_reason, current_time, **kwargs)

        if val:
            if (pair in self.custom_info_trail_sell):
                self.custom_info_trail_sell[pair] = self.init_trailing_sell_dict.copy()

        return val

As said in the name, this will only convert custom_exit into trailing. Other exits (populate_exit_trend, roi, stoploss) will still be immediate exit. I’m working to make populate_exit_trend into trailing, but it’s still work-in-progress.

One comment

Leave a Reply

Your email address will not be published. Required fields are marked *