FSD – Custom exit

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

Let’s say you want to exit a trade when the rsi enter overbought area, because you aren’t sure that the price will keep going up, then you can use custom_exit to do it. First, you need to calculate rsi in populate_indicators

def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:

    dataframe['ema_9'] = ta.EMA(dataframe, 9)
    dataframe['ema_20'] = ta.EMA(dataframe, 20)
    dataframe['rsi'] = ta.RSI(dataframe, 14)

    return dataframe

Then you need to write custom_exit lines. For this example, we tell the bot to exit on overbought rsi only when the profit above or equal 0. But similar to tsl example, since we want to make the code backtest-able, we tied the profit to last candle’s close.

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):

        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"

The full code will look like below. Don’t forget to copy the import lines, since there are new imports.

from freqtrade.strategy import IStrategy
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

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):

            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.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):

            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"

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:

        dataframe['ema_9'] = ta.EMA(dataframe, 9)
        dataframe['ema_20'] = ta.EMA(dataframe, 20)
        dataframe['rsi'] = ta.RSI(dataframe, 14)

        return dataframe
    
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:

        dataframe.loc[
            qtpylib.crossed_above(dataframe['ema_9'], dataframe['ema_20'])
            &
            (dataframe['volume'] > 0)
            , ['enter_long', 'enter_tag']
        ] = (1, 'golden cross')

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:

        dataframe.loc[
            qtpylib.crossed_below(dataframe['ema_9'], dataframe['ema_20'])
            &
            (dataframe['volume'] > 0)
            , ['exit_long', 'exit_tag']
        ] = (1, 'death cross')

        return dataframe

3 Comments

Leave a Reply

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