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
[…] Turn ROI and Exit Signal into trailing exit […]
[…] E0V1E with TSL basically above code with addition of this extra snippet to make the exits into TSL. […]