From a217a7c75d6af0aa47f898ad3ee59d5e1e705f4c Mon Sep 17 00:00:00 2001 From: caglarylmz Date: Thu, 12 May 2022 19:51:33 +0300 Subject: [PATCH] Add some strategies for binance futures --- .../strategies/futures/f_adx_sma_strategy.py | 130 +++++++++ .../strategies/futures/f_ott_strategy.py | 187 +++++++++++++ .../futures/f_reinforced_average.py | 144 ++++++++++ .../strategies/futures/f_sampleStrategy.py | 162 ++++++++++++ .../futures/f_supertrend_strategy.py | 249 ++++++++++++++++++ 5 files changed, 872 insertions(+) create mode 100644 user_data/strategies/futures/f_adx_sma_strategy.py create mode 100644 user_data/strategies/futures/f_ott_strategy.py create mode 100644 user_data/strategies/futures/f_reinforced_average.py create mode 100644 user_data/strategies/futures/f_sampleStrategy.py create mode 100644 user_data/strategies/futures/f_supertrend_strategy.py diff --git a/user_data/strategies/futures/f_adx_sma_strategy.py b/user_data/strategies/futures/f_adx_sma_strategy.py new file mode 100644 index 0000000..2dbc83a --- /dev/null +++ b/user_data/strategies/futures/f_adx_sma_strategy.py @@ -0,0 +1,130 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# flake8: noqa: F401 +# isort: skip_file +# --- Do not remove these libs --- +from functools import reduce +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame + +from freqtrade.strategy import ( + BooleanParameter, + CategoricalParameter, + DecimalParameter, + IStrategy, + IntParameter, +) + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class FAdxSmaStrategy(IStrategy): + + INTERFACE_VERSION = 3 + timeframe = "1h" + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi". + minimal_roi = {"60": 0.075, "30": 0.1, "0": 0.05} + # minimal_roi = {"0": 1} + + stoploss = -0.05 + can_short = True + + # Trailing stoploss + trailing_stop = False + # trailing_only_offset_is_reached = False + # trailing_stop_positive = 0.01 + # trailing_stop_positive_offset = 0.0 # Disabled / not configured + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 14 + + # Hyperoptable parameters + + # Define the guards spaces + pos_entry_adx = DecimalParameter(15, 40, decimals=1, default=30.0, space="buy") + pos_exit_adx = DecimalParameter(15, 40, decimals=1, default=30.0, space="sell") + + # Define the parameter spaces + adx_period = IntParameter(4, 24, default=14) + sma_short_period = IntParameter(4, 24, default=12) + sma_long_period = IntParameter(12, 175, default=48) + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # Calculate all adx values + for val in self.adx_period.range: + dataframe[f"adx_{val}"] = ta.ADX(dataframe, timeperiod=val) + + # Calculate all sma_short values + for val in self.sma_short_period.range: + dataframe[f"sma_short_{val}"] = ta.SMA(dataframe, timeperiod=val) + + # Calculate all sma_long values + for val in self.sma_long_period.range: + dataframe[f"sma_long_{val}"] = ta.SMA(dataframe, timeperiod=val) + + return dataframe + + def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions_long = [] + conditions_short = [] + + # GUARDS AND TRIGGERS + conditions_long.append( + dataframe[f"adx_{self.adx_period.value}"] > self.pos_entry_adx.value + ) + conditions_short.append( + dataframe[f"adx_{self.adx_period.value}"] > self.pos_entry_adx.value + ) + + conditions_long.append( + qtpylib.crossed_above( + dataframe[f"sma_short_{self.sma_short_period.value}"], + dataframe[f"sma_long_{self.sma_long_period.value}"], + ) + ) + conditions_short.append( + qtpylib.crossed_below( + dataframe[f"sma_short_{self.sma_short_period.value}"], + dataframe[f"sma_long_{self.sma_long_period.value}"], + ) + ) + + dataframe.loc[ + reduce(lambda x, y: x & y, conditions_long), + "enter_long", + ] = 1 + + dataframe.loc[ + reduce(lambda x, y: x & y, conditions_short), + "enter_short", + ] = 1 + + return dataframe + + def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions_close = [] + conditions_close.append( + dataframe[f"adx_{self.adx_period.value}"] < self.pos_entry_adx.value + ) + + dataframe.loc[ + reduce(lambda x, y: x & y, conditions_close), + "exit_long", + ] = 1 + + dataframe.loc[ + reduce(lambda x, y: x & y, conditions_close), + "exit_short", + ] = 1 + + return dataframe diff --git a/user_data/strategies/futures/f_ott_strategy.py b/user_data/strategies/futures/f_ott_strategy.py new file mode 100644 index 0000000..02398ef --- /dev/null +++ b/user_data/strategies/futures/f_ott_strategy.py @@ -0,0 +1,187 @@ +import logging +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +from freqtrade.strategy.hyper import IntParameter +from pandas import DataFrame +import talib.abstract as ta +import numpy as np +import freqtrade.vendor.qtpylib.indicators as qtpylib + + + +class FOttStrategy(IStrategy): + # Buy params, Sell params, ROI, Stoploss and Trailing Stop are values generated by 'freqtrade hyperopt --strategy Supertrend --hyperopt-loss ShortTradeDurHyperOptLoss --timerange=20210101- --timeframe=1h --spaces all' + # It's encourage you find the values that better suites your needs and risk management strategies + + # ROI table: + minimal_roi = {"0": 0.1, "30": 0.75, "60": 0.05, "120": 0.025} + # minimal_roi = {"0": 1} + + # Stoploss: + stoploss = -0.265 + + # Trailing stop: + trailing_stop = True + trailing_stop_positive = 0.05 + trailing_stop_positive_offset = 0.1 + trailing_only_offset_is_reached = False + + timeframe = "1h" + + startup_candle_count = 18 + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe["ott"] = self.ott(dataframe)["OTT"] + dataframe["var"] = self.ott(dataframe)["VAR"] + dataframe["adx"] = ta.ADX(dataframe, timeperiod=14) + + return dataframe + + def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + (qtpylib.crossed_above(dataframe["var"], dataframe["ott"])), + "enter_long", + ] = 1 + + dataframe.loc[ + (qtpylib.crossed_below(dataframe["var"], dataframe["ott"])), + "enter_short", + ] = 1 + + return dataframe + + def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + dataframe["adx"]>60 + ), + "exit_long", + ] = 1 + + dataframe.loc[ + ( + dataframe["adx"]>60 + ), + "exit_short", + ] = 1 + + return dataframe + + """ + Supertrend Indicator; adapted for freqtrade + from: https://github.com/freqtrade/freqtrade-strategies/issues/30 + """ + + def ott(self, dataframe: DataFrame): + df = dataframe.copy() + + pds = 2 + percent = 1.4 + alpha = 2 / (pds + 1) + + df["ud1"] = np.where( + df["close"] > df["close"].shift(1), (df["close"] - df["close"].shift()), 0 + ) + df["dd1"] = np.where( + df["close"] < df["close"].shift(1), (df["close"].shift() - df["close"]), 0 + ) + df["UD"] = df["ud1"].rolling(9).sum() + df["DD"] = df["dd1"].rolling(9).sum() + df["CMO"] = ((df["UD"] - df["DD"]) / (df["UD"] + df["DD"])).fillna(0).abs() + + # df['Var'] = talib.EMA(df['close'], timeperiod=5) + df["Var"] = 0.0 + for i in range(pds, len(df)): + df["Var"].iat[i] = (alpha * df["CMO"].iat[i] * df["close"].iat[i]) + ( + 1 - alpha * df["CMO"].iat[i] + ) * df["Var"].iat[i - 1] + + df["fark"] = df["Var"] * percent * 0.01 + df["newlongstop"] = df["Var"] - df["fark"] + df["newshortstop"] = df["Var"] + df["fark"] + df["longstop"] = 0.0 + df["shortstop"] = 999999999999999999 + # df['dir'] = 1 + for i in df["UD"]: + + def maxlongstop(): + df.loc[(df["newlongstop"] > df["longstop"].shift(1)), "longstop"] = df[ + "newlongstop" + ] + df.loc[(df["longstop"].shift(1) > df["newlongstop"]), "longstop"] = df[ + "longstop" + ].shift(1) + + return df["longstop"] + + def minshortstop(): + df.loc[ + (df["newshortstop"] < df["shortstop"].shift(1)), "shortstop" + ] = df["newshortstop"] + df.loc[ + (df["shortstop"].shift(1) < df["newshortstop"]), "shortstop" + ] = df["shortstop"].shift(1) + + return df["shortstop"] + + df["longstop"] = np.where( + ((df["Var"] > df["longstop"].shift(1))), + maxlongstop(), + df["newlongstop"], + ) + + df["shortstop"] = np.where( + ((df["Var"] < df["shortstop"].shift(1))), + minshortstop(), + df["newshortstop"], + ) + + # get xover + + df["xlongstop"] = np.where( + ( + (df["Var"].shift(1) > df["longstop"].shift(1)) + & (df["Var"] < df["longstop"].shift(1)) + ), + 1, + 0, + ) + + df["xshortstop"] = np.where( + ( + (df["Var"].shift(1) < df["shortstop"].shift(1)) + & (df["Var"] > df["shortstop"].shift(1)) + ), + 1, + 0, + ) + + df["trend"] = 0 + df["dir"] = 0 + for i in df["UD"]: + df["trend"] = np.where( + ((df["xshortstop"] == 1)), + 1, + (np.where((df["xlongstop"] == 1), -1, df["trend"].shift(1))), + ) + + df["dir"] = np.where( + ((df["xshortstop"] == 1)), + 1, + (np.where((df["xlongstop"] == 1), -1, df["dir"].shift(1).fillna(1))), + ) + + # get OTT + + df["MT"] = np.where(df["dir"] == 1, df["longstop"], df["shortstop"]) + df["OTT"] = np.where( + df["Var"] > df["MT"], + (df["MT"] * (200 + percent) / 200), + (df["MT"] * (200 - percent) / 200), + ) + df["OTT"] = df["OTT"].shift(2) + + return DataFrame(index=df.index, data={"OTT": df["OTT"], "VAR": df["Var"]}) + diff --git a/user_data/strategies/futures/f_reinforced_average.py b/user_data/strategies/futures/f_reinforced_average.py new file mode 100644 index 0000000..ede5d50 --- /dev/null +++ b/user_data/strategies/futures/f_reinforced_average.py @@ -0,0 +1,144 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# flake8: noqa: F401 +# isort: skip_file +# --- Do not remove these libs --- +from functools import reduce +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame + +from freqtrade.strategy import ( + BooleanParameter, + CategoricalParameter, + DecimalParameter, + IStrategy, + IntParameter, +) + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from freqtrade.exchange import timeframe_to_minutes +from technical.util import resample_to_interval, resampled_merge + + +# This class is a sample. Feel free to customize it. +class FReinforcedStrategy(IStrategy): + + INTERFACE_VERSION = 3 + timeframe = "5m" + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi". + minimal_roi = {"60": 0.075, "30": 0.1, "0": 0.05} + # minimal_roi = {"0": 1} + + stoploss = -0.05 + can_short = True + + # Trailing stoploss + trailing_stop = False + # trailing_only_offset_is_reached = False + # trailing_stop_positive = 0.01 + # trailing_stop_positive_offset = 0.0 # Disabled / not configured + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 14 + + # Hyperoptable parameters + + # Define the guards spaces + pos_entry_adx = DecimalParameter(15, 40, decimals=1, default=30.0, space="buy") + pos_exit_adx = DecimalParameter(15, 40, decimals=1, default=30.0, space="sell") + + # Define the parameter spaces + adx_period = IntParameter(4, 24, default=14) + ema_short_period = IntParameter(4, 24, default=8) + ema_long_period = IntParameter(12, 175, default=21) + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # Calculate all adx values + for val in self.adx_period.range: + dataframe[f"adx_{val}"] = ta.ADX(dataframe, timeperiod=val) + + # Calculate all ema_short values + for val in self.ema_short_period.range: + dataframe[f"ema_short_{val}"] = ta.EMA(dataframe, timeperiod=val) + + # Calculate all ema_long values + for val in self.ema_long_period.range: + dataframe[f"ema_long_{val}"] = ta.EMA(dataframe, timeperiod=val) + + # required for graphing + bollinger = qtpylib.bollinger_bands(dataframe["close"], window=20, stds=2) + dataframe["bb_lowerband"] = bollinger["lower"] + dataframe["bb_upperband"] = bollinger["upper"] + dataframe["bb_middleband"] = bollinger["mid"] + + self.resample_interval = timeframe_to_minutes(self.timeframe) * 12 + dataframe_long = resample_to_interval(dataframe, self.resample_interval) + dataframe_long["sma"] = ta.SMA(dataframe_long, timeperiod=50, price="close") + dataframe = resampled_merge(dataframe, dataframe_long, fill_na=True) + + return dataframe + + def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions_long = [] + conditions_short = [] + + # GUARDS AND TRIGGERS + conditions_long.append( + dataframe["close"] > dataframe[f"resample_{self.resample_interval}_sma"] + ) + + conditions_short.append( + dataframe["close"] < dataframe[f"resample_{self.resample_interval}_sma"] + ) + + conditions_long.append( + qtpylib.crossed_above( + dataframe[f"ema_short_{self.ema_short_period.value}"], + dataframe[f"ema_long_{self.ema_long_period.value}"], + ) + ) + conditions_short.append( + qtpylib.crossed_below( + dataframe[f"ema_short_{self.ema_short_period.value}"], + dataframe[f"ema_long_{self.ema_long_period.value}"], + ) + ) + + dataframe.loc[ + reduce(lambda x, y: x & y, conditions_long), + "enter_long", + ] = 1 + + dataframe.loc[ + reduce(lambda x, y: x & y, conditions_short), + "enter_short", + ] = 1 + + return dataframe + + def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions_close = [] + conditions_close.append( + dataframe[f"adx_{self.adx_period.value}"] < self.pos_entry_adx.value + ) + + dataframe.loc[ + reduce(lambda x, y: x & y, conditions_close), + "exit_long", + ] = 1 + + dataframe.loc[ + reduce(lambda x, y: x & y, conditions_close), + "exit_short", + ] = 1 + + return dataframe diff --git a/user_data/strategies/futures/f_sampleStrategy.py b/user_data/strategies/futures/f_sampleStrategy.py new file mode 100644 index 0000000..fc2db48 --- /dev/null +++ b/user_data/strategies/futures/f_sampleStrategy.py @@ -0,0 +1,162 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# flake8: noqa: F401 +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame + +from freqtrade.strategy import ( + BooleanParameter, + CategoricalParameter, + DecimalParameter, + IStrategy, + IntParameter, +) + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class FSampleStrategy(IStrategy): + + INTERFACE_VERSION = 3 + timeframe = "1h" + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi". + # minimal_roi = {"60": 0.1, "30": 0.2, "0": 0.2} + minimal_roi = {"0": 1} + + stoploss = -0.05 + can_short = True + + # Trailing stoploss + trailing_stop = False + # trailing_only_offset_is_reached = False + # trailing_stop_positive = 0.01 + # trailing_stop_positive_offset = 0.0 # Disabled / not configured + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe["adx"] = ta.ADX(dataframe) + # RSI + dataframe["rsi"] = ta.RSI(dataframe) + + # Stochastic Fast + stoch_fast = ta.STOCHF(dataframe) + dataframe["fastd"] = stoch_fast["fastd"] + dataframe["fastk"] = stoch_fast["fastk"] + + # MACD + macd = ta.MACD(dataframe) + dataframe["macd"] = macd["macd"] + dataframe["macdsignal"] = macd["macdsignal"] + dataframe["macdhist"] = macd["macdhist"] + + # MFI + dataframe["mfi"] = ta.MFI(dataframe) + + # Bollinger Bands + bollinger = qtpylib.bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["bb_lowerband"] = bollinger["lower"] + dataframe["bb_middleband"] = bollinger["mid"] + dataframe["bb_upperband"] = bollinger["upper"] + dataframe["bb_percent"] = (dataframe["close"] - dataframe["bb_lowerband"]) / ( + dataframe["bb_upperband"] - dataframe["bb_lowerband"] + ) + dataframe["bb_width"] = ( + dataframe["bb_upperband"] - dataframe["bb_lowerband"] + ) / dataframe["bb_middleband"] + + # Parabolic SAR + dataframe["sar"] = ta.SAR(dataframe) + + # TEMA - Triple Exponential Moving Average + dataframe["tema"] = ta.TEMA(dataframe, timeperiod=9) + + # Cycle Indicator + # ------------------------------------ + # Hilbert Transform Indicator - SineWave + hilbert = ta.HT_SINE(dataframe) + dataframe["htsine"] = hilbert["sine"] + dataframe["htleadsine"] = hilbert["leadsine"] + + return dataframe + + def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + # Signal: RSI crosses above 30 + (qtpylib.crossed_above(dataframe["rsi"], 30)) + & (dataframe["tema"] <= dataframe["bb_middleband"]) + & ( # Guard: tema below BB middle + dataframe["tema"] > dataframe["tema"].shift(1) + ) + & ( # Guard: tema is raising + dataframe["volume"] > 0 + ) # Make sure Volume is not 0 + ), + "enter_long", + ] = 1 + + dataframe.loc[ + ( + # Signal: RSI crosses above 70 + (qtpylib.crossed_above(dataframe["rsi"], 70)) + & (dataframe["tema"] > dataframe["bb_middleband"]) + & ( # Guard: tema above BB middle + dataframe["tema"] < dataframe["tema"].shift(1) + ) + & ( # Guard: tema is falling + dataframe["volume"] > 0 + ) # Make sure Volume is not 0 + ), + "enter_short", + ] = 1 + + return dataframe + + def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + # Signal: RSI crosses above 70 + (qtpylib.crossed_above(dataframe["rsi"], 70)) + & (dataframe["tema"] > dataframe["bb_middleband"]) + & ( # Guard: tema above BB middle + dataframe["tema"] < dataframe["tema"].shift(1) + ) + & ( # Guard: tema is falling + dataframe["volume"] > 0 + ) # Make sure Volume is not 0 + ), + "exit_long", + ] = 1 + + dataframe.loc[ + ( + # Signal: RSI crosses above 30 + (qtpylib.crossed_above(dataframe["rsi"], 30)) + & + # Guard: tema below BB middle + (dataframe["tema"] <= dataframe["bb_middleband"]) + & (dataframe["tema"] > dataframe["tema"].shift(1)) + & ( # Guard: tema is raising + dataframe["volume"] > 0 + ) # Make sure Volume is not 0 + ), + "exit_short", + ] = 1 + + return dataframe diff --git a/user_data/strategies/futures/f_supertrend_strategy.py b/user_data/strategies/futures/f_supertrend_strategy.py new file mode 100644 index 0000000..f4e2f3c --- /dev/null +++ b/user_data/strategies/futures/f_supertrend_strategy.py @@ -0,0 +1,249 @@ +""" +Supertrend strategy: +* Description: Generate a 3 supertrend indicators for 'buy' strategies & 3 supertrend indicators for 'sell' strategies + Buys if the 3 'buy' indicators are 'up' + Sells if the 3 'sell' indicators are 'down' +* Author: @juankysoriano (Juan Carlos Soriano) +* github: https://github.com/juankysoriano/ +*** NOTE: This Supertrend strategy is just one of many possible strategies using `Supertrend` as indicator. It should on any case used at your own risk. + It comes with at least a couple of caveats: + 1. The implementation for the `supertrend` indicator is based on the following discussion: https://github.com/freqtrade/freqtrade-strategies/issues/30 . Concretelly https://github.com/freqtrade/freqtrade-strategies/issues/30#issuecomment-853042401 + 2. The implementation for `supertrend` on this strategy is not validated; meaning this that is not proven to match the results by the paper where it was originally introduced or any other trusted academic resources +""" + +import logging +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +from freqtrade.strategy.hyper import IntParameter +from pandas import DataFrame +import talib.abstract as ta +import numpy as np + + +class FSupertrendStrategy(IStrategy): + # Buy params, Sell params, ROI, Stoploss and Trailing Stop are values generated by 'freqtrade hyperopt --strategy Supertrend --hyperopt-loss ShortTradeDurHyperOptLoss --timerange=20210101- --timeframe=1h --spaces all' + # It's encourage you find the values that better suites your needs and risk management strategies + + # Buy hyperspace params: + buy_params = { + "buy_m1": 4, + "buy_m2": 7, + "buy_m3": 1, + "buy_p1": 8, + "buy_p2": 9, + "buy_p3": 8, + } + + # Sell hyperspace params: + sell_params = { + "sell_m1": 1, + "sell_m2": 3, + "sell_m3": 6, + "sell_p1": 16, + "sell_p2": 18, + "sell_p3": 18, + } + + # ROI table: + minimal_roi = {"0": 0.1, "30": 0.75, "60": 0.05, "120": 0.025} + # minimal_roi = {"0": 1} + + # Stoploss: + stoploss = -0.265 + + # Trailing stop: + trailing_stop = True + trailing_stop_positive = 0.05 + trailing_stop_positive_offset = 0.1 + trailing_only_offset_is_reached = False + + timeframe = "1h" + + startup_candle_count = 18 + + buy_m1 = IntParameter(1, 7, default=1) + buy_m2 = IntParameter(1, 7, default=3) + buy_m3 = IntParameter(1, 7, default=4) + buy_p1 = IntParameter(7, 21, default=14) + buy_p2 = IntParameter(7, 21, default=10) + buy_p3 = IntParameter(7, 21, default=10) + + sell_m1 = IntParameter(1, 7, default=1) + sell_m2 = IntParameter(1, 7, default=3) + sell_m3 = IntParameter(1, 7, default=4) + sell_p1 = IntParameter(7, 21, default=14) + sell_p2 = IntParameter(7, 21, default=10) + sell_p3 = IntParameter(7, 21, default=10) + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + for multiplier in self.buy_m1.range: + for period in self.buy_p1.range: + dataframe[f"supertrend_1_buy_{multiplier}_{period}"] = self.supertrend( + dataframe, multiplier, period + )["STX"] + + for multiplier in self.buy_m2.range: + for period in self.buy_p2.range: + dataframe[f"supertrend_2_buy_{multiplier}_{period}"] = self.supertrend( + dataframe, multiplier, period + )["STX"] + + for multiplier in self.buy_m3.range: + for period in self.buy_p3.range: + dataframe[f"supertrend_3_buy_{multiplier}_{period}"] = self.supertrend( + dataframe, multiplier, period + )["STX"] + + for multiplier in self.sell_m1.range: + for period in self.sell_p1.range: + dataframe[f"supertrend_1_sell_{multiplier}_{period}"] = self.supertrend( + dataframe, multiplier, period + )["STX"] + + for multiplier in self.sell_m2.range: + for period in self.sell_p2.range: + dataframe[f"supertrend_2_sell_{multiplier}_{period}"] = self.supertrend( + dataframe, multiplier, period + )["STX"] + + for multiplier in self.sell_m3.range: + for period in self.sell_p3.range: + dataframe[f"supertrend_3_sell_{multiplier}_{period}"] = self.supertrend( + dataframe, multiplier, period + )["STX"] + + return dataframe + + def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + dataframe[f"supertrend_1_buy_{self.buy_m1.value}_{self.buy_p1.value}"] + == "up" + ) + & ( + dataframe[f"supertrend_2_buy_{self.buy_m2.value}_{self.buy_p2.value}"] + == "up" + ) + & ( + dataframe[f"supertrend_3_buy_{self.buy_m3.value}_{self.buy_p3.value}"] + == "up" + ) + & ( # The three indicators are 'up' for the current candle + dataframe["volume"] > 0 + ), + "enter_long", + ] = 1 + + dataframe.loc[ + ( + dataframe[ + f"supertrend_1_sell_{self.sell_m1.value}_{self.sell_p1.value}" + ] + == "down" + ) + & ( + dataframe[ + f"supertrend_2_sell_{self.sell_m2.value}_{self.sell_p2.value}" + ] + == "down" + ) + & ( + dataframe[ + f"supertrend_3_sell_{self.sell_m3.value}_{self.sell_p3.value}" + ] + == "down" + ) + & ( # The three indicators are 'down' for the current candle + dataframe["volume"] > 0 + ), + "enter_short", + ] = 1 + + return dataframe + + def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + dataframe[ + f"supertrend_2_sell_{self.sell_m2.value}_{self.sell_p2.value}" + ] + == "down" + ), + "exit_long", + ] = 1 + + dataframe.loc[ + ( + dataframe[f"supertrend_2_buy_{self.buy_m2.value}_{self.buy_p2.value}"] + == "up" + ), + "exit_short", + ] = 1 + + return dataframe + + """ + Supertrend Indicator; adapted for freqtrade + from: https://github.com/freqtrade/freqtrade-strategies/issues/30 + """ + + def supertrend(self, dataframe: DataFrame, multiplier, period): + df = dataframe.copy() + + df["TR"] = ta.TRANGE(df) + df["ATR"] = ta.SMA(df["TR"], period) + + st = "ST_" + str(period) + "_" + str(multiplier) + stx = "STX_" + str(period) + "_" + str(multiplier) + + # Compute basic upper and lower bands + df["basic_ub"] = (df["high"] + df["low"]) / 2 + multiplier * df["ATR"] + df["basic_lb"] = (df["high"] + df["low"]) / 2 - multiplier * df["ATR"] + + # Compute final upper and lower bands + df["final_ub"] = 0.00 + df["final_lb"] = 0.00 + for i in range(period, len(df)): + df["final_ub"].iat[i] = ( + df["basic_ub"].iat[i] + if df["basic_ub"].iat[i] < df["final_ub"].iat[i - 1] + or df["close"].iat[i - 1] > df["final_ub"].iat[i - 1] + else df["final_ub"].iat[i - 1] + ) + df["final_lb"].iat[i] = ( + df["basic_lb"].iat[i] + if df["basic_lb"].iat[i] > df["final_lb"].iat[i - 1] + or df["close"].iat[i - 1] < df["final_lb"].iat[i - 1] + else df["final_lb"].iat[i - 1] + ) + + # Set the Supertrend value + df[st] = 0.00 + for i in range(period, len(df)): + df[st].iat[i] = ( + df["final_ub"].iat[i] + if df[st].iat[i - 1] == df["final_ub"].iat[i - 1] + and df["close"].iat[i] <= df["final_ub"].iat[i] + else df["final_lb"].iat[i] + if df[st].iat[i - 1] == df["final_ub"].iat[i - 1] + and df["close"].iat[i] > df["final_ub"].iat[i] + else df["final_lb"].iat[i] + if df[st].iat[i - 1] == df["final_lb"].iat[i - 1] + and df["close"].iat[i] >= df["final_lb"].iat[i] + else df["final_ub"].iat[i] + if df[st].iat[i - 1] == df["final_lb"].iat[i - 1] + and df["close"].iat[i] < df["final_lb"].iat[i] + else 0.00 + ) + # Mark the trend direction up/down + df[stx] = np.where( + (df[st] > 0.00), np.where((df["close"] < df[st]), "down", "up"), np.NaN + ) + + # Remove basic and final bands from the columns + df.drop(["basic_ub", "basic_lb", "final_ub", "final_lb"], inplace=True, axis=1) + + df.fillna(0, inplace=True) + + return DataFrame(index=df.index, data={"ST": df[st], "STX": df[stx]})