import numpy as np import pandas as pd from pandas import DataFrame from datetime import datetime, timedelta from typing import Optional, Union from freqtrade.strategy import ( BooleanParameter, CategoricalParameter, DecimalParameter, IntParameter, IStrategy, merge_informative_pair, ) from functools import reduce import talib.abstract as ta import freqtrade.vendor.qtpylib.indicators as qtpylib from freqtrade.persistence import Trade class expired_v2(IStrategy): """ author@: Bryant Suen github@: https://github.com/BryantSuen Originally designed by @lazybear: https://www.tradingview.com/script/qt6xLfLi-Impulse-MACD-LazyBear/ """ INTERFACE_VERSION = 3 timeframe = "1h" can_short: bool = True order_types = {"entry": "limit", "exit": "limit", "stoploss": "market", "stoploss_on_exchange": True} order_time_in_force = {"entry": "GTC", "exit": "GTC"} buy_params = { "check_macd_position": False, "macd_ma_period": 20, # value loaded from strategy "macd_signal_period": 14, # value loaded from strategy } macd_ma_period = IntParameter(20, 48, default=buy_params["macd_ma_period"], space="buy", optimize=True, load=True) macd_signal_period = IntParameter(5, 15, default=buy_params["macd_signal_period"], space="buy", optimize=True, load=True) check_macd_position = BooleanParameter(default=buy_params["check_macd_position"], space="buy", optimize=True, load=True) minimal_roi = {"0": 0.3} stoploss = -0.3 trailing_stop = False trailing_stop_positive = 0.1 trailing_stop_positive_offset = 0.3 trailing_only_offset_is_reached = True process_only_new_candles = True use_exit_signal = True ignore_roi_if_entry_signal = True startup_candle_count: int = 100 def _cal_smma(self, series: pd.Series, period: int) -> pd.Series: return series.ewm(alpha=1 / period, adjust=False, min_periods=period).mean() def _cal_zero_lag_ema(self, series: pd.Series, period: int) -> pd.Series: ema_1 = ta.EMA(series, timeperiod=period) ema_2 = ta.EMA(ema_1, timeperiod=period) return 2 * ema_1 - ema_2 def impulsive_macd(self, dataframe: DataFrame, length_ma: int, length_signal: int) -> tuple: mean_hlc = dataframe[["high", "low", "close"]].mean(axis=1) high_smma = self._cal_smma(dataframe["high"], length_ma) low_smma = self._cal_smma(dataframe["low"], length_ma) middle_zlema = self._cal_zero_lag_ema(mean_hlc, length_ma) impulse_macd = np.where(middle_zlema > high_smma, middle_zlema - high_smma, 0) impulse_macd = np.where(middle_zlema < low_smma, middle_zlema - low_smma, impulse_macd) impulse_macd_signal = ta.SMA(impulse_macd, timeperiod=length_signal) impulse_macd_hist = impulse_macd - impulse_macd_signal return impulse_macd, impulse_macd_signal, impulse_macd_hist def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: for ma_period in self.macd_ma_period.range: for signal_period in self.macd_signal_period.range: macd, macdsignal, macdhist = self.impulsive_macd(dataframe, ma_period, signal_period) dataframe[f"impulse_macd_{ma_period}_{signal_period}"] = macd dataframe[f"impulse_macdsignal_{ma_period}_{signal_period}"] = macdsignal dataframe[f"impulse_macdhist_{ma_period}_{signal_period}"] = macdhist dataframe["atr"] = ta.ATR(dataframe, timeperiod=14) return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: triggers_long = [] triggers_short = [] guards_long = [] guards_short = [] triggers_long.append( qtpylib.crossed_above( dataframe[f"impulse_macd_{self.macd_ma_period.value}_{self.macd_signal_period.value}"], dataframe[f"impulse_macdsignal_{self.macd_ma_period.value}_{self.macd_signal_period.value}"], ) ) triggers_short.append( qtpylib.crossed_below( dataframe[f"impulse_macd_{self.macd_ma_period.value}_{self.macd_signal_period.value}"], dataframe[f"impulse_macdsignal_{self.macd_ma_period.value}_{self.macd_signal_period.value}"], ) ) guards_long.append(dataframe["volume"] > 0) guards_short.append(dataframe["volume"] > 0) atr_ratio = 0.2 guards_long.append( (dataframe[f"impulse_macd_{self.macd_ma_period.value}_{self.macd_signal_period.value}"] > atr_ratio * dataframe["atr"]) | (dataframe[f"impulse_macd_{self.macd_ma_period.value}_{self.macd_signal_period.value}"] < -atr_ratio * dataframe["atr"]) ) guards_short.append( (dataframe[f"impulse_macd_{self.macd_ma_period.value}_{self.macd_signal_period.value}"] > atr_ratio * dataframe["atr"]) | (dataframe[f"impulse_macd_{self.macd_ma_period.value}_{self.macd_signal_period.value}"] < -atr_ratio * dataframe["atr"]) ) if self.check_macd_position.value: guards_long.append(dataframe[f"impulse_macdsignal_{self.macd_ma_period.value}_{self.macd_signal_period.value}"] < 0) guards_short.append( dataframe[f"impulse_macdsignal_{self.macd_ma_period.value}_{self.macd_signal_period.value}"] > 0 ) if triggers_long: dataframe.loc[reduce(lambda x, y: x | y, triggers_long) & reduce(lambda x, y: x & y, guards_long), "enter_long"] = 1 if triggers_short: dataframe.loc[ reduce(lambda x, y: x | y, triggers_short) & reduce(lambda x, y: x & y, guards_short), "enter_short" ] = 1 return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: triggers_long = [] triggers_short = [] guards_long = [] guards_short = [] guards_long.append(dataframe["volume"] > 0) guards_short.append(dataframe["volume"] > 0) if triggers_long: dataframe.loc[reduce(lambda x, y: x | y, triggers_long) & reduce(lambda x, y: x & y, guards_long), "exit_long"] = 1 if triggers_short: dataframe.loc[ reduce(lambda x, y: x | y, triggers_short) & reduce(lambda x, y: x & y, guards_short), "exit_short" ] = 1 return dataframe def leverage( self, pair: str, current_time: datetime, current_rate: float, proposed_leverage: float, max_leverage: float, entry_tag: Optional[str], side: str, **kwargs, ) -> float: return 3 def custom_exit( self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs ): dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) current_candle = dataframe.iloc[-1].squeeze() if current_time - timedelta(minutes=90) < trade.open_date_utc and current_profit < -0.12: return "sudden_loss_exit" return None @property def plot_config(self): return { "main_plot": {}, "subplots": { "IMPULSE_MACD": { f"impulse_macd_{self.macd_ma_period.value}_{self.macd_signal_period.value}": {"color": "blue"}, f"impulse_macdsignal_{self.macd_ma_period.value}_{self.macd_signal_period.value}": {"color": "orange"}, f"impulse_macdhist_{self.macd_ma_period.value}_{self.macd_signal_period.value}": { "type": "bar", "plotly": {"opacity": 0.9}, }, } }, }