# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement # flake8: noqa: F401 # isort: skip_file # --- Do not remove these imports --- import numpy as np import pandas as pd from datetime import datetime, timedelta, timezone from pandas import DataFrame from typing import Dict, Optional, Union, Tuple from freqtrade.strategy import ( IStrategy, Trade, Order, PairLocks, informative, # @informative decorator # Hyperopt Parameters BooleanParameter, CategoricalParameter, DecimalParameter, IntParameter, RealParameter, # timeframe helpers timeframe_to_minutes, timeframe_to_next_date, timeframe_to_prev_date, # Strategy helper functions merge_informative_pair, stoploss_from_absolute, stoploss_from_open, AnnotationType, ) # -------------------------------- # Add your lib to import here import talib.abstract as ta from technical import qtpylib class SwingTradingStrategy(IStrategy): """ This is a strategy template to get you started. More information in https://www.freqtrade.io/en/latest/strategy-customization/ You can: :return: a Dataframe with all mandatory indicators for the strategies - Rename the class name (Do not forget to update class_name) - Add any methods you want to build your strategy - Add any lib you need to build your strategy You must keep: - the lib in the section "Do not remove these libs" - the methods: populate_indicators, populate_entry_trend, populate_exit_trend You should keep: - timeframe, minimal_roi, stoploss, trailing_* """ # Strategy interface version - allow new iterations of the strategy interface. # Check the documentation or the Sample strategy to get the latest version. INTERFACE_VERSION = 3 # Optimal timeframe for the strategy. timeframe = "5m" # Can this strategy go short? can_short: bool = True # Minimal ROI designed for the strategy. # This attribute will be overridden if the config file contains "minimal_roi". minimal_roi = {"60": 0.01, "30": 0.02, "0": 0.04} # Optimal stoploss designed for the strategy. # This attribute will be overridden if the config file contains "stoploss". stoploss = -0.10 # Trailing stoploss trailing_stop = True # 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 = True # These values can be overridden in the config. use_exit_signal = True exit_profit_only = False ignore_roi_if_entry_signal = False # Number of candles the strategy requires before producing valid signals startup_candle_count: int = 30 # Optional order type mapping. order_types = { "entry": "limit", "exit": "limit", "stoploss": "market", "stoploss_on_exchange": False, } # EMA timeperiod parameter ema_timeperiod = IntParameter( low=2, high=90, default=8, space="timeperiod", optimize=True, load=True ) # Return on investment parameters enter_long_ror = DecimalParameter( low=0, high=1, default=0.0, decimals=1, space="enter", optimize=True, load=True ) exit_long_ror = DecimalParameter( low=-1, high=0, default=0.0, decimals=1, space="exit", optimize=True, load=True ) enter_short_ror = DecimalParameter( low=-1, high=0, default=0.0, decimals=1, space="enter", optimize=True, load=True ) exit_short_ror = DecimalParameter( low=0, high=1, default=0.0, decimals=1, space="exit", optimize=True, load=True ) # # Return on investment parameters # enter_aroon_up = DecimalParameter( # low=0, high=100, default=80, decimals=1, space="enter", optimize=True, load=True # ) # enter_aroon_down = DecimalParameter( # low=0, high=100, default=80, decimals=1, space="enter", optimize=True, load=True # ) # # Return on investment parameters # exit_aroon_up = DecimalParameter( # low=0, high=100, default=20, decimals=1, space="exit", optimize=True, load=True # ) # exit_aroon_down = DecimalParameter( # low=0, high=100, default=20, decimals=1, space="exit", optimize=True, load=True # ) # Optional order time in force. order_time_in_force = {"entry": "GTC", "exit": "GTC"} @property def plot_config(self): return { "main_plot": { "ema2": {"color": "yellow"}, "ema8": {"color": "black"}, "ema44": {"color": "red"}, }, "subplots": { "AROON": { "aroonup": {"color": "orange"}, "aroondown": {"color": "blue"}, }, }, } def version(self) -> str | None: return super().version() def leverage( self, pair: str, current_time: datetime, current_rate: float, proposed_leverage: float, max_leverage: float, entry_tag: str | None, side: str, **kwargs, ) -> float: return 5.0 def informative_pairs(self): """ Define additional, informative pair/interval combinations to be cached from the exchange. These pair/interval combinations are non-tradeable, unless they are part of the whitelist as well. For more information, please consult the documentation :return: List of tuples in the format (pair, interval) Sample: return [("ETH/USDT", "5m"), ("BTC/USDT", "15m"), ] """ return [] def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # Momentum Indicators # ------------------------------------ # ADX # dataframe["adx"] = ta.ADX(dataframe) # # Plus Directional Indicator / Movement # dataframe["plus_dm"] = ta.PLUS_DM(dataframe) # dataframe["plus_di"] = ta.PLUS_DI(dataframe) # # Minus Directional Indicator / Movement # dataframe["minus_dm"] = ta.MINUS_DM(dataframe) # dataframe["minus_di"] = ta.MINUS_DI(dataframe) # # Aroon, Aroon Oscillator aroon = ta.AROON(dataframe) dataframe["aroonup"] = aroon["aroonup"] dataframe["aroondown"] = aroon["aroondown"] # dataframe["aroonosc"] = ta.AROONOSC(dataframe) # # Awesome Oscillator # dataframe["ao"] = qtpylib.awesome_oscillator(dataframe) # # Keltner Channel # keltner = qtpylib.keltner_channel(dataframe) # dataframe["kc_upperband"] = keltner["upper"] # dataframe["kc_lowerband"] = keltner["lower"] # dataframe["kc_middleband"] = keltner["mid"] # dataframe["kc_percent"] = ( # (dataframe["close"] - dataframe["kc_lowerband"]) / # (dataframe["kc_upperband"] - dataframe["kc_lowerband"]) # ) # dataframe["kc_width"] = ( # (dataframe["kc_upperband"] - dataframe["kc_lowerband"]) / dataframe["kc_middleband"] # ) # # Ultimate Oscillator # dataframe["uo"] = ta.ULTOSC(dataframe) # # Commodity Channel Index: values [Oversold:-100, Overbought:100] # dataframe["cci"] = ta.CCI(dataframe) # RSI # dataframe["rsi"] = ta.RSI(dataframe) # # Inverse Fisher transform on RSI: values [-1.0, 1.0] (https://goo.gl/2JGGoy) # rsi = 0.1 * (dataframe["rsi"] - 50) # dataframe["fisher_rsi"] = (np.exp(2 * rsi) - 1) / (np.exp(2 * rsi) + 1) # # Inverse Fisher transform on RSI normalized: values [0.0, 100.0] (https://goo.gl/2JGGoy) # dataframe["fisher_rsi_norma"] = 50 * (dataframe["fisher_rsi"] + 1) # # Stochastic Slow # stoch = ta.STOCH(dataframe) # dataframe["slowd"] = stoch["slowd"] # dataframe["slowk"] = stoch["slowk"] # Stochastic Fast # stoch_fast = ta.STOCHF(dataframe) # dataframe["fastd"] = stoch_fast["fastd"] # dataframe["fastk"] = stoch_fast["fastk"] # # Stochastic RSI # Please read https://github.com/freqtrade/freqtrade/issues/2961 before using this. # STOCHRSI is NOT aligned with tradingview, which may result in non-expected results. # stoch_rsi = ta.STOCHRSI(dataframe) # dataframe["fastd_rsi"] = stoch_rsi["fastd"] # dataframe["fastk_rsi"] = stoch_rsi["fastk"] # MACD # macd = ta.MACD(dataframe) # dataframe["macd"] = macd["macd"] # dataframe["macdsignal"] = macd["macdsignal"] # dataframe["macdhist"] = macd["macdhist"] # MFI # dataframe["mfi"] = ta.MFI(dataframe) # # ROC # dataframe["roc"] = ta.ROC(dataframe) # Overlap Studies # ------------------------------------ # 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"] # Bollinger Bands - Weighted (EMA based instead of SMA) # weighted_bollinger = qtpylib.weighted_bollinger_bands( # qtpylib.typical_price(dataframe), window=20, stds=2 # ) # dataframe["wbb_upperband"] = weighted_bollinger["upper"] # dataframe["wbb_lowerband"] = weighted_bollinger["lower"] # dataframe["wbb_middleband"] = weighted_bollinger["mid"] # dataframe["wbb_percent"] = ( # (dataframe["close"] - dataframe["wbb_lowerband"]) / # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) # ) # dataframe["wbb_width"] = ( # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] # ) # # EMA - Exponential Moving Average # dataframe["ema3"] = ta.EMA(dataframe, timeperiod=3) # dataframe["ema5"] = ta.EMA(dataframe, timeperiod=5) # dataframe["ema10"] = ta.EMA(dataframe, timeperiod=10) # dataframe["ema21"] = ta.EMA(dataframe, timeperiod=21) # dataframe["ema50"] = ta.EMA(dataframe, timeperiod=50) # dataframe["ema100"] = ta.EMA(dataframe, timeperiod=100) # # SMA - Simple Moving Average # dataframe["sma3"] = ta.SMA(dataframe, timeperiod=3) # dataframe["sma5"] = ta.SMA(dataframe, timeperiod=5) # dataframe["sma10"] = ta.SMA(dataframe, timeperiod=10) # dataframe["sma21"] = ta.SMA(dataframe, timeperiod=21) # dataframe["sma50"] = ta.SMA(dataframe, timeperiod=50) # dataframe["sma100"] = ta.SMA(dataframe, timeperiod=100) # 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"] # Pattern Recognition - Bullish candlestick patterns # ------------------------------------ # # Hammer: values [0, 100] # dataframe["CDLHAMMER"] = ta.CDLHAMMER(dataframe) # # Inverted Hammer: values [0, 100] # dataframe["CDLINVERTEDHAMMER"] = ta.CDLINVERTEDHAMMER(dataframe) # # Dragonfly Doji: values [0, 100] # dataframe["CDLDRAGONFLYDOJI"] = ta.CDLDRAGONFLYDOJI(dataframe) # # Piercing Line: values [0, 100] # dataframe["CDLPIERCING"] = ta.CDLPIERCING(dataframe) # values [0, 100] # # Morningstar: values [0, 100] # dataframe["CDLMORNINGSTAR"] = ta.CDLMORNINGSTAR(dataframe) # values [0, 100] # # Three White Soldiers: values [0, 100] # dataframe["CDL3WHITESOLDIERS"] = ta.CDL3WHITESOLDIERS(dataframe) # values [0, 100] # Pattern Recognition - Bearish candlestick patterns # ------------------------------------ # # Hanging Man: values [0, 100] # dataframe["CDLHANGINGMAN"] = ta.CDLHANGINGMAN(dataframe) # # Shooting Star: values [0, 100] # dataframe["CDLSHOOTINGSTAR"] = ta.CDLSHOOTINGSTAR(dataframe) # # Gravestone Doji: values [0, 100] # dataframe["CDLGRAVESTONEDOJI"] = ta.CDLGRAVESTONEDOJI(dataframe) # # Dark Cloud Cover: values [0, 100] # dataframe["CDLDARKCLOUDCOVER"] = ta.CDLDARKCLOUDCOVER(dataframe) # # Evening Doji Star: values [0, 100] # dataframe["CDLEVENINGDOJISTAR"] = ta.CDLEVENINGDOJISTAR(dataframe) # # Evening Star: values [0, 100] # dataframe["CDLEVENINGSTAR"] = ta.CDLEVENINGSTAR(dataframe) # Pattern Recognition - Bullish/Bearish candlestick patterns # ------------------------------------ # # Three Line Strike: values [0, -100, 100] # dataframe["CDL3LINESTRIKE"] = ta.CDL3LINESTRIKE(dataframe) # # Spinning Top: values [0, -100, 100] # dataframe["CDLSPINNINGTOP"] = ta.CDLSPINNINGTOP(dataframe) # values [0, -100, 100] # # Engulfing: values [0, -100, 100] # dataframe["CDLENGULFING"] = ta.CDLENGULFING(dataframe) # values [0, -100, 100] # # Harami: values [0, -100, 100] # dataframe["CDLHARAMI"] = ta.CDLHARAMI(dataframe) # values [0, -100, 100] # # Three Outside Up/Down: values [0, -100, 100] # dataframe["CDL3OUTSIDE"] = ta.CDL3OUTSIDE(dataframe) # values [0, -100, 100] # # Three Inside Up/Down: values [0, -100, 100] # dataframe["CDL3INSIDE"] = ta.CDL3INSIDE(dataframe) # values [0, -100, 100] # # Chart type # # ------------------------------------ # # Heikin Ashi Strategy # heikinashi = qtpylib.heikinashi(dataframe) # dataframe["ha_open"] = heikinashi["open"] # dataframe["ha_close"] = heikinashi["close"] # dataframe["ha_high"] = heikinashi["high"] # dataframe["ha_low"] = heikinashi["low"] # EMA - Parameter Range for val in self.ema_timeperiod.range: dataframe[f"ema{val}"] = ta.EMA(dataframe, timeperiod=val) dataframe[f"ror_ema{val}"] = dataframe[f"ema{val}"].pct_change() return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ ( ( dataframe[f"ror_ema{self.ema_timeperiod.value}"] > self.enter_long_ror.value ) & (dataframe[f"ror_ema{self.ema_timeperiod.value}"].shift(1) > 0) & (qtpylib.crossed_above(dataframe["aroonup"], dataframe["aroondown"])) # & (dataframe["aroonup"] > self.enter_aroon_up.value) & (dataframe["volume"] > 0) ), "enter_long", ] = 1 dataframe.loc[ ( ( dataframe[f"ror_ema{self.ema_timeperiod.value}"] < self.enter_short_ror.value ) & (dataframe[f"ror_ema{self.ema_timeperiod.value}"].shift(1) < 0) & (qtpylib.crossed_below(dataframe["aroondown"], dataframe["aroonup"])) # & (dataframe["aroondown"] > self.enter_aroon_down.value) & (dataframe["volume"] > 0) ), "enter_short", ] = 1 return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ ( ( dataframe[f"ror_ema{self.ema_timeperiod.value}"] < self.exit_long_ror.value ) # | (dataframe["aroonup"] < self.exit_aroon_up.value) & (dataframe["volume"] > 0) ), "exit_long", ] = 1 dataframe.loc[ ( ( dataframe[f"ror_ema{self.ema_timeperiod.value}"] > self.exit_short_ror.value ) # | (dataframe["aroondown"] < self.exit_aroon_down.value) & (dataframe["volume"] > 0) ), "exit_short", ] = 1 return dataframe