How to define informative timeframe(s)?

The easiest way to define informative timeframe(s) is by using the decorator, as shown in the example below

import freqtrade.vendor.qtpylib.indicators as qtpylib
import numpy as np
import talib.abstract as ta
from freqtrade.strategy import IStrategy, informative
from pandas import DataFrame, Series
from typing import Dict, List, Optional, Tuple
from freqtrade.persistence import Trade
import talib.abstract as ta
import time


# NOT TO BE USED FOR LIVE!!!!!!

class multi_tf (IStrategy):

    def version(self) -> str:
        return "v1"

    INTERFACE_VERSION = 3

    # ROI table:
    minimal_roi = {
        "0": 0.2
    }

    # Stoploss:
    stoploss = -0.1

    # Trailing stop:
    trailing_stop = False
    trailing_stop_positive = 0.001
    trailing_stop_positive_offset = 0.01
    trailing_only_offset_is_reached = True

    # Sell signal
    use_exit_signal = True
    exit_profit_only = False
    exit_profit_offset = 0.01
    ignore_roi_if_entry_signal = False

    timeframe = '5m'

    process_only_new_candles = True
    startup_candle_count = 100

    # This method is not required. 
    # def informative_pairs(self): ...

    # Define informative upper timeframe for each pair. Decorators can be stacked on same 
    # method. Available in populate_indicators as 'rsi_30m' and 'rsi_1h'.
    @informative('30m')
    @informative('1h')
    def populate_indicators_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
        return dataframe

    # Define BTC/STAKE informative pair. Available in populate_indicators and other methods as
    # 'btc_rsi_1h'. Current stake currency should be specified as {stake} format variable 
    # instead of hard-coding actual stake currency. Available in populate_indicators and other 
    # methods as 'btc_usdt_rsi_1h' (when stake currency is USDT).
    @informative('1h', 'BTC/{stake}')
    def populate_indicators_btc_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
        return dataframe

    # Define BTC/ETH informative pair. You must specify quote currency if it is different from
    # stake currency. Available in populate_indicators and other methods as 'eth_btc_rsi_1h'.
    @informative('1h', 'ETH/BTC')
    def populate_indicators_eth_btc_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
        return dataframe

    # Define BTC/STAKE informative pair. A custom formatter may be specified for formatting
    # column names. A callable `fmt(**kwargs) -> str` may be specified, to implement custom
    # formatting. Available in populate_indicators and other methods as 'rsi_fast_upper'.
    @informative('1h', 'BTC/{stake}', '{column}')
    def populate_indicators_btc_1h_2(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe['rsi_fast_upper'] = ta.RSI(dataframe, timeperiod=4)
        return dataframe

    # Define BTC/STAKE informative pair. A custom formatter may be specified for formatting
    # column names. A callable `fmt(**kwargs) -> str` may be specified, to implement custom
    # formatting. Available in populate_indicators and other methods as 'btc_rsi_super_fast_1h'.
    @informative('1h', 'BTC/{stake}', '{base}_{column}_{timeframe}')
    def populate_indicators_btc_1h_3(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe['rsi_super_fast'] = ta.RSI(dataframe, timeperiod=2)
        return dataframe

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Strategy timeframe indicators for current pair.
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
        # Informative pairs are available in this method.
        dataframe['rsi_less'] = dataframe['rsi'] < dataframe['rsi_1h']
        return dataframe
    
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        stake = self.config['stake_currency']
        dataframe.loc[
            (
                (dataframe[f'btc_{stake}_rsi_1h'] < 35)
                &
                (dataframe['eth_btc_rsi_1h'] < 50)
                &
                (dataframe['rsi_fast_upper'] < 40)
                &
                (dataframe['btc_rsi_super_fast_1h'] < 30)
                &
                (dataframe['rsi_30m'] < 40)
                &
                (dataframe['rsi_1h'] < 40)
                &
                (dataframe['rsi'] < 30)
                &
                (dataframe['rsi_less'] == True)
                &
                (dataframe['volume'] > 0)
            ),
            ['enter_long', 'enter_tag']] = (1, 'buy_signal_rsi')

        return dataframe

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

        dataframe.loc[
            (
                (dataframe['rsi'] > 70)
                &
                (dataframe['rsi_less'] == False)
                &
                (dataframe['volume'] > 0)
            ),
            ['exit_long', 'exit_tag']] = (1, 'exit_signal_rsi')

        return dataframe

If you need to use data from one informative timeframe on another informative timeframe, the decorator won’t work for you in that case. In this situation, you would need to employ the more extensive method, for instance

def info_tf_btc_indicators(self, dataframe, metadata):
    # Indicators
    # -----------------------------------------------------------------------------------------
    dataframe['rsi_14'] = ta.RSI(dataframe, timeperiod=14)
    dataframe['not_downtrend'] = ((dataframe['close'] > dataframe['close'].shift(2)) | (dataframe['rsi_14'] > 50))

    # Add prefix
    # -----------------------------------------------------------------------------------------
    ignore_columns = ['date', 'open', 'high', 'low', 'close', 'volume']
    dataframe.rename(columns=lambda s: f"btc_{s}" if s not in ignore_columns else s, inplace=True)

    return dataframe

def indicators_15m(self, informative_15m, metadata):

    informative_15m['rsi'] = ta.RSI(informative_15m, timeperiod=14)
    informative_15m['cmf'] = chaikin_money_flow(informative_15m, 20)
    informative_15m['cti'] = pta.cti(informative_15m["close"], length=20)

    return informative_15m

def informative_pairs(self):
    pairs = self.dp.current_whitelist()
    informative_pairs = [(pair, '15m') for pair in pairs]
    informative_pairs.extend([(pair, '30m') for pair in pairs])

    btc_info_pair = f"BTC/{self.config['stake_currency']}"
    
    informative_pairs.append((btc_info_pair, '5m'))
    informative_pairs.append((btc_info_pair, '15m'))

    return informative_pairs

def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
    
    btc_info_pair = f"BTC/{self.config['stake_currency']}"

    btc_info_tf = self.dp.get_pair_dataframe(btc_info_pair, '15m')
    btc_info_tf = self.info_tf_btc_indicators(btc_info_tf, metadata)
    dataframe = merge_informative_pair(dataframe, btc_info_tf, '5m', '15m', ffill=True)
    drop_columns = [f"{s}_15m" for s in ['date', 'open', 'high', 'low', 'close', 'volume']]
    dataframe.drop(columns=dataframe.columns.intersection(drop_columns), inplace=True)

    informative_15m = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe='15m')
    informative_15m = self.indicators_15m(informative_15m, metadata)
    dataframe = merge_informative_pair(dataframe, informative_15m, '5m', '15m', ffill=True)

    return dataframe

3 Comments

  1. wow! wanted to thank you.
    I found “Bot Academy” one of the most valuable websites ever have seen.
    The information you provided is really valuable 🙂

Leave a Reply

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