FSD – Using multiple timeframes

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

In previous posts, we only use 15m timeframe. What if I want to use other timeframes, for example I want to use 30m and 1h timeframes to calculate rsi of them, and add those rsi on entry logic?

First, you need to add new import

from freqtrade.strategy import IStrategy, informative

Then you need to specify the informative timeframes. Remember, the base timeframe must be the lowest of all. So in our example, since we want to use 15m, 30m and 1h timeframes, the base timeframe must be the 15m. 30m and 1h is our informative timeframes. There are 2 ways to define informative. I will use the easiest way, which is using informative decorator. Since I will calculate same indicator on both informative timeframes, I can merge them into 1 function

@informative('30m')
@informative('1h')
def populate_indicators_inf1(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
    
    dataframe['rsi'] = ta.RSI(dataframe, 14)

    return dataframe

But in case you will calculate different indicators on each of the informative timeframes, you can split them into separate functions. If you are doing it this way, remember that the functions have to have different name definition, otherwise the latter function will override previous functions with same name.

@informative('30m')
def populate_indicators_inf1(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
    
    dataframe['rsi'] = ta.RSI(dataframe, 14)

    return dataframe

@informative('1h')
def populate_indicators_inf2(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
    
    dataframe['rsi'] = ta.RSI(dataframe, 14)

    return dataframe

The code above will create rsi_30m and rsi_1h columns to be used on your strategy. Let’s say, you want to add more conditions in your entry logic

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

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

    return dataframe

The full code will look like this

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

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"

    @informative('30m')
    def populate_indicators_inf1(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        
        dataframe['rsi'] = ta.RSI(dataframe, 14)

        return dataframe

    @informative('1h')
    def populate_indicators_inf2(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        
        dataframe['rsi'] = ta.RSI(dataframe, 14)

        return dataframe

    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['rsi_30m'] < 50)
            &
            (dataframe['rsi_1h'] < 30)
            &
            (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 *