Turn ROI and Exit Signal into trailing exit

Warning!!!

We will use a dict to store the trigger. The issue with this method is the dict won’t survive a bot restart. So if the trailing is active and the bot is restarted, it will lose the trailing signal. If the original exit was ROI, there is a good chance the trailing will be activated again. But if the original exit was exit signal, then there is a chance the trailing won’t be activated again, until the next exit signal. So be aware of this issue, especially if you restart your bot quite often.

Second thing you need to be aware is that the exit_reason stored in the database gonna be trailing_stop_loss. You won’t be able to store the initial exit reason. If the initial exit reason is important to you, then this post isn’t for you.

First, we create the dict

custom_info_trail_sell = dict()

init_trailing_sell_dict = {
	'tsl': False,
}

Next, we intercept the exit using confirm_trade_exit

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 = False

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

	if (self.custom_info_trail_sell[pair]['tsl'] == False):
		self.dp.send_msg(f"{exit_reason} for *{pair}* triggered at *{rate}*. Activating trailing sell!")
		self.custom_info_trail_sell[pair]['tsl'] = True
	else:
		approved_exit = ['stop_loss', 'stoploss_on_exchange', 'trailing_stop_loss', 'force_exit', 'emergency_exit']
		if exit_reason in approved_exit:
			val = True
			self.custom_info_trail_sell[pair] = self.init_trailing_sell_dict.copy()

	return val

The code is simple. At initial exit attemp, it will turn the TSL on and reject the exit. After the trailing activated, it will only accept exit from the reasons inside approved_exit, which include trailing_stop_loss. Next we just need to prepare the trailing, in this case, we use custom_stoploss function

use_custom_stoploss = True

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

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

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

	return sl_new

My advise is to use this only on dry/live bot and not on backtest/hyperopt, because you might trigger the traps. So it’s better to create a new derived class like this

class original_strat(IStrategy):
# Your original strategy

class tsl_exit_strat (original_strat):

	custom_info_trail_sell = dict()

	init_trailing_sell_dict = {
		'tsl': False,
	}

	use_custom_stoploss = True

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

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

		if (self.custom_info_trail_sell[pair]['tsl'] == True):
			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 = False

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

		if (self.custom_info_trail_sell[pair]['tsl'] == False):
			self.dp.send_msg(f"{exit_reason} for *{pair}* triggered at *{rate}*. Activating trailing sell!")
			self.custom_info_trail_sell[pair]['tsl'] = True
		else:
			approved_exit = ['stop_loss', 'stoploss_on_exchange', 'trailing_stop_loss', 'force_exit', 'emergency_exit']
			if exit_reason in approved_exit:
				val = True
				self.custom_info_trail_sell[pair] = self.init_trailing_sell_dict.copy()

		return val

2 Comments

Leave a Reply

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