# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement # flake8: noqa: F401 # --- Do not remove these libs --- import numpy as np # noqa import pandas as pd # noqa pd.options.mode.chained_assignment = None # default='warn' from pandas import DataFrame from freqtrade.strategy import IStrategy, merge_informative_pair, informative from freqtrade.strategy import CategoricalParameter, DecimalParameter, IntParameter, stoploss_from_open # -------------------------------- # Add your lib to import here import talib.abstract as ta import freqtrade.vendor.qtpylib.indicators as qtpylib from freqtrade.persistence import Trade import math from technical.indicators import TKE import math import logging from functools import reduce from datetime import datetime, timedelta, timezone from timeit import default_timer as timer from datetime import timedelta import time logger = logging.getLogger(__name__) def SSLChannels_ATR(dataframe, length=7): """ SSL Channels with ATR: https://www.tradingview.com/script/SKHqWzql-SSL-ATR-channel/ Credit to @JimmyNixx for python """ df = dataframe.copy() df['ATR'] = ta.ATR(df, timeperiod=14) df['smaHigh'] = df['high'].rolling(length).mean() + df['ATR'] df['smaLow'] = df['low'].rolling(length).mean() - df['ATR'] df['hlv'] = np.where(df['close'] > df['smaHigh'], 1, np.where(df['close'] < df['smaLow'], -1, np.NAN)) df['hlv'] = df['hlv'].ffill() df['sslDown'] = np.where(df['hlv'] < 0, df['smaHigh'], df['smaLow']) df['sslUp'] = np.where(df['hlv'] < 0, df['smaLow'], df['smaHigh']) return df['sslDown'], df['sslUp'] def funcNadarayaWatsonEnvelope(dtloc, source='close', bandwidth=8, window=500, mult=3): """ // This work is licensed under a Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) https://creativecommons.org/licenses/by-nc-sa/4.0/ // Nadaraya-Watson Envelope [LUX] https://www.tradingview.com/script/Iko0E2kL-Nadaraya-Watson-Envelope-LUX/ :return: up and down translated for freqtrade: viksal1982 viktors.s@gmail.com df.shape[0] """ dtNWE = dtloc.copy() dtNWE['nwe_up'] = np.nan dtNWE['nwe_down'] = np.nan wn = np.zeros((window, window)) for i in range(window): for j in range(window): wn[i, j] = math.exp(-(math.pow(i - j, 2) / (bandwidth * bandwidth * 2))) sumSCW = wn.sum(axis=1) def calc_nwa(dfr, init=0): global calc_src_value if init == 1: calc_src_value = list() return calc_src_value.append(dfr[source]) mae = 0.0 y2_val = 0.0 y2_val_up = np.nan y2_val_down = np.nan if len(calc_src_value) > window: calc_src_value.pop(0) if len(calc_src_value) >= window: src = np.array(calc_src_value) sumSC = src * wn sumSCS = sumSC.sum(axis=1) y2 = sumSCS / sumSCW sum_e = np.absolute(src - y2) mae = sum_e.sum() / window * mult y2_val = y2[-1] y2_val_up = y2_val + mae y2_val_down = y2_val - mae return y2_val_up, y2_val_down calc_nwa(None, init=1) dtNWE[['nwe_up', 'nwe_down']] = dtNWE.apply(calc_nwa, axis=1, result_type='expand') return dtNWE[['nwe_up', 'nwe_down']] class NWEv6_new(IStrategy): window_buy = IntParameter(60, 1000, default=300, space='buy', optimize=True) bandwidth_buy = IntParameter(2, 15, default=9, space='buy', optimize=True) mult_buy = DecimalParameter(0.5, 20.0, default=4, space='buy', optimize=True) marginselldw = DecimalParameter(1.0049, 1.0200, default=1.0038, space='buy', decimals=4, optimize=True, load=True) # hard stoploss profit pHSL = DecimalParameter(-0.100, -0.040, default=-0.05, decimals=3, space='sell', load=True) # profit threshold 1, trigger point, SL_1 is used pPF_1 = DecimalParameter(0.008, 0.020, default=0.016, decimals=3, space='sell', load=True) pSL_1 = DecimalParameter(0.008, 0.020, default=0.011, decimals=3, space='sell', load=True) # profit threshold 2, SL_2 is used pPF_2 = DecimalParameter(0.040, 0.100, default=0.080, decimals=3, space='sell', load=True) pSL_2 = DecimalParameter(0.020, 0.070, default=0.040, decimals=3, space='sell', load=True) # Optimal timeframe for the strategy. timeframe = '3m' inf_timeframe = '5m' # These values can be overridden in the "ask_strategy" section in the config. use_custom_stoploss = True 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 = 300 minimal_roi = { "0": 0.10, "423": 0.03, "751": 0.01 } stoploss = -0.99 trailing_stop = False trailing_stop_positive = 0.005 trailing_stop_positive_offset = 0.019 trailing_only_offset_is_reached = True process_only_new_candles = False @property def protections(self): return [ { "method": "CooldownPeriod", "stop_duration": 120 }, { "method": "StoplossGuard", "lookback_period": 90, "trade_limit": 2, "stop_duration": 120, "only_per_pair": False }, { "method": "StoplossGuard", "lookback_period": 90, "trade_limit": 1, "stop_duration": 120, "only_per_pair": True }, ] # Optional order type mapping. order_types = { 'entry': 'limit', 'exit': 'limit', 'stoploss': 'market', 'stoploss_on_exchange': False } # Optional order time in force. order_time_in_force = { 'entry': 'gtc', 'exit': 'gtc' } plot_config = { # Main plot indicators (Moving averages, ...) 'main_plot': { 'nwe_up': {'color': 'red'}, 'nwe_down': {'color': "rgba(155,150,200,2.4)"}, 'ema_100': {'color': 'blue'}, 'entry_line': {'color': 'green'}, }, 'subplots': { "TREND/PCT": { 'btctrend': {'color': 'green'}, 'highpct': {'color': 'red'}, 'lowpct': {'color': 'blue'} } } } ## Custom Trailing stoploss ( credit to Perkmeister for this custom stoploss to help the strategy ride a green candle ) def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float: # hard stoploss profit HSL = self.pHSL.value PF_1 = self.pPF_1.value SL_1 = self.pSL_1.value PF_2 = self.pPF_2.value SL_2 = self.pSL_2.value # For profits between PF_1 and PF_2 the stoploss (sl_profit) used is linearly interpolated # between the values of SL_1 and SL_2. For all profits above PL_2 the sl_profit value # rises linearly with current profit, for profits below PF_1 the hard stoploss profit is used. if (current_profit > PF_2): sl_profit = SL_2 + (current_profit - PF_2) elif (current_profit > PF_1): sl_profit = SL_1 + ((current_profit - PF_1) * (SL_2 - SL_1) / (PF_2 - PF_1)) else: sl_profit = HSL # Only for hyperopt invalid return if (sl_profit >= current_profit): return -0.99 return stoploss_from_open(sl_profit, current_profit) @informative('3m', 'BTC/{stake}', fmt='{base}_{column}_{timeframe}') def populate_indicators_3m_btc(self, dataframe: DataFrame, metadata: dict) -> DataFrame: ssldown, sslup = SSLChannels_ATR(dataframe, 25) dataframe['trend'] = np.where(sslup > ssldown, 1, -1) return dataframe @informative('5m') def populate_indicators_5m(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe['ema_100'] = ta.EMA(dataframe, timeperiod=100) dataframe[['nwe_up', 'nwe_down']] = funcNadarayaWatsonEnvelope(dataframe, source='close', bandwidth=self.bandwidth_buy.value, window=self.window_buy.value, mult=self.mult_buy.value) dataframe["highpct"] = dataframe['nwe_down'] * dataframe['nwe_up'].pct_change(periods=24) dataframe["lowpct"] = dataframe['nwe_down'] * dataframe['nwe_down'].pct_change(periods=6) dataframe['entry_line'] = dataframe['nwe_down'] / self.marginselldw.value dataframe['Newentry_line'] = dataframe['nwe_down'] * dataframe["lowpct"] return dataframe def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = [] con2 = ( (dataframe['btc_trend_3m'] == 1) & (qtpylib.crossed_below(dataframe['highpct_5m'], dataframe['lowpct_5m'])) & (dataframe['ema_100_5m'] > dataframe['close_5m']) & (dataframe['nwe_up_5m'] <= (abs(dataframe['highpct_5m']) * 100)) & (dataframe['volume_5m'] > 0) ) con3 = ( (dataframe['close_5m'].shift() < dataframe['entry_line_5m'].shift()) & (dataframe['close_5m'].shift(2) < dataframe['entry_line_5m'].shift(2)) & (dataframe['close_5m'] > dataframe['entry_line_5m']) & (dataframe['ema_100_5m'] > dataframe['close_5m']) & # (dataframe['ema_100_5m'] > dataframe['nwe_up_5m'] ) & (dataframe['volume_5m'] > 0) # Make sure Volume is not 0 ) conditions.append(con2) conditions.append(con3) dataframe.loc[con2, 'enter_tag'] = " con2 " dataframe.loc[con3, 'enter_tag'] = " con3 " if conditions: dataframe.loc[ reduce(lambda x, y: x | y, conditions), 'enter_long' ] = 1 return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = [] con1 = ( (dataframe['close_5m'].shift() > dataframe['nwe_up_5m'].shift()) & (dataframe['close_5m'] < dataframe['nwe_up_5m']) ) conditions.append(con1) # conditions.append(con2) if conditions: dataframe.loc[ reduce(lambda x, y: x | y, conditions), 'exit_long' ] = 1 return dataframe