FSD – Specific exit for specific entry

DISCLAIMER
The codes in this post is just for educational purpose, and not to be used on live trading

Previously, all of our exit logics are applied on all trades, despite its enter_tag. But what if I want to have specific exit logic for specific enter_tag? You can’t do this on populate_exit_trend. You must use it on custom_exit or custom_stoploss.

First, you need to fetch the enter_tag and split them (if it contains multiple enter_tag). If you follow my example, then you don’t use whitespace in the middle of each enter_tag and add one whitespace at the end of it.

enter_tags = trade.enter_tag.split()

Let’s say we want to have different TSL trigger for close_below_9 tag. The trigger should be at 2% instead for all trades having that tag (even if it has any other enter_tag). The trigger should be at 1% instead for all trades having only that tag (no other enter_tag allowed).

def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float:
    
    sl_new = 1
    if (current_time - timedelta(minutes=15) >= trade.open_date_utc):
        enter_tags = trade.enter_tag.split()
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        current_candle = dataframe.iloc[-1].squeeze()
        current_profit = trade.calc_profit_ratio(current_candle['close'])
        if ('close_below_9' in enter_tags):
            # only close_below_9 tag
            if (len(enter_tags) == 1):
                if (current_profit >= 0.01):
                    # use tighter tsl offset
                    sl_new = 0.0025
            else:
                if (current_profit >= 0.02):
                    sl_new = 0.005
        elif (current_profit >= 0.03):
            sl_new = 0.01
    return sl_new

Full code below

from freqtrade.strategy import IStrategy, informative
from pandas import DataFrame
import freqtrade.vendor.qtpylib.indicators as qtpylib
import talib.abstract as ta
from freqtrade.persistence import Trade
from datetime import datetime, timedelta
from typing import Optional, Union

def EWO(source, sma_length=5, sma2_length=35):
    sma1 = ta.SMA(source, timeperiod=sma_length)
    sma2 = ta.SMA(source, timeperiod=sma2_length)
    smadif = (sma1 - sma2) / source * 100
    return smadif
    
class strat_template (IStrategy):
    def version(self) -> str:
        return "template-v1"
        
    INTERFACE_VERSION = 3
    minimal_roi = {
        "0": 0.05
    }
    stoploss = -0.05
    timeframe = '15m'
    process_only_new_candles = True
    startup_candle_count = 999
    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 (current_time - timedelta(minutes=15) >= trade.open_date_utc):
            enter_tags = trade.enter_tag.split()
            dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
            current_candle = dataframe.iloc[-1].squeeze()
            current_profit = trade.calc_profit_ratio(current_candle['close'])
            if ('close_below_9' in enter_tags):
                # only close_below_9 tag
                if (len(enter_tags) == 1):
                    if (current_profit >= 0.01):
                        # use tighter tsl offset
                        sl_new = 0.0025
                else:
                    if (current_profit >= 0.02):
                        sl_new = 0.005
            elif (current_profit >= 0.03):
                sl_new = 0.01
        return sl_new
    
    def custom_exit(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> Optional[Union[str, bool]]:
        if ((current_time - timedelta(minutes=15)) >= trade.open_date_utc):
            enter_tags = trade.enter_tag.split()
            dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
            current_candle = dataframe.iloc[-1].squeeze()
            current_profit = trade.calc_profit_ratio(current_candle['close'])
            
            if (current_profit >= 0):
                if (current_candle['rsi'] >= 70):
                    return "rsi_overbought"
    
    @informative('30m')
    def populate_indicators_inf1(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        
        dataframe['rsi'] = ta.RSI(dataframe['close'], 14)
        return dataframe
    
    @informative('1h')
    def populate_indicators_inf2(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        
        dataframe['rsi'] = ta.RSI(dataframe['close'], 14)
        return dataframe
    
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe['ema_9'] = ta.EMA(dataframe['close'], 9)
        dataframe['ema_20'] = ta.EMA(dataframe['close'], 20)
        dataframe['rsi'] = ta.RSI(dataframe['close'], 14)
        dataframe['ema_9_rsi'] = ta.EMA(dataframe['rsi'], 9)
        dataframe['ewo'] = EWO(dataframe['close'], 50, 200)
        return dataframe
    
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe['enter_tag'] = ''
        enter_1 = (
            qtpylib.crossed_above(dataframe['ema_9'], dataframe['ema_20'])
            &
            (dataframe['rsi_30m'] < 50)
            &
            (dataframe['rsi_1h'] < 30)
            &
            (dataframe['ema_9_rsi'] < 70)
            &
            (dataframe['ewo'] > 3)
            &
            (dataframe['volume'] > 0)
        )
        enter_2 = (
            (dataframe['close'] < dataframe['ema_9'])
            &
            (dataframe['volume'] > 0)
        )
        enter_3 = (
            (dataframe['close'] < dataframe['ema_20'])
            &
            (dataframe['volume'] > 0)
        )
        dataframe.loc[
            enter_1 | enter_2 | enter_3
            , 'enter_long'
        ] = 1
        dataframe.loc[
            enter_1
            , 'enter_tag'
        ] += 'golden_cross '
        dataframe.loc[
            enter_2
            , 'enter_tag'
        ] += 'close_below_9 '
        dataframe.loc[
            enter_3
            , 'enter_tag'
        ] += 'close_below_20 '
        return dataframe
    
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe['exit_tag'] = ''
        exit_1 = (
            qtpylib.crossed_below(dataframe['ema_9'], dataframe['ema_20'])
            &
            (dataframe['volume'] > 0)
        )
        exit_2 = (
            (dataframe['close'] > dataframe['ema_9'])
            &
            (dataframe['volume'] > 0)
        )
        exit_3 = (
            (dataframe['close'] > dataframe['ema_20'])
            &
            (dataframe['volume'] > 0)
        )
        dataframe.loc[
            exit_1 | exit_2 | exit_3
            , 'exit_long'
        ] = 1
        dataframe.loc[
            exit_1
            , 'exit_tag'
        ] += 'death_cross '
        dataframe.loc[
            exit_2
            , 'exit_tag'
        ] += 'close_above_9 '
        dataframe.loc[
            exit_3
            , 'exit_tag'
        ] += 'close_above_20 '
        return dataframe

5 Comments

Leave a Reply

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