import logging from freqtrade.strategy.interface import IStrategy from freqtrade.persistence import Trade from pandas import DataFrame from datetime import datetime, timedelta from typing import Dict, Optional, Union import numpy as np from freqtrade.enums import (ExitCheckTuple, ExitType, RPCMessageType, RunMode, SignalDirection, State, TradingMode) import talib.abstract as ta import freqtrade.vendor.qtpylib.indicators as qtpylib import technical.indicators as ftt import pandas_ta as pta logger = logging.getLogger(__name__) # inserire la differenza del valore t0-t1, t1-t2, t2-t3... # l'hyperopt deve essere eseguito SOLO su periodo di movimento laterale e TESTATO su periodi di medio bull e di medio bear # (SPIEGAZIONE: se faccio hyperopt su lungo periodo di tempo, potrebbe cercare di ottimizzare determinate condizioni estreme come pump e dump per estremizzare i profit su singoli trade: # la questione è che si potrebbe concentrare su eventi specifici e rari che vanno a compensare tutto il resto) # inserire questi indicatori: https://github.com/just-nilux/legendary_ta/tree/main # modificherei il target per cercare di arrivare all'innesco del trailing positivo e diminuirei lo shift -50 che sono troppe candele nel futuro!!! -> vediamo se da più importanza ai timeframe più piccoli invece dei 15m diminuendo il -50 # modificare il target per gestire long e short class Proton(IStrategy): """ """ INTERFACE_VERSION = 3 def version(self) -> str: return "v0.0.001" timeframe = '3m' max_trade_duration = 0 # Futures custom_leverage = 1.0 use_half_label_period_candle = True dynamic_target = False # target della differenza di prezzo per la predizione prediction_diff_perc_1 = 0.001 prediction_diff_perc_2 = prediction_diff_perc_1 * 2 num_label_1 = 5 num_label_2 = 5 #stoploss = -0.02 * custom_leverage stoploss = -0.2 * custom_leverage process_only_new_candles = True use_exit_signal = True startup_candle_count: int = 30 can_short = True minimal_roi = { "0": 1+prediction_diff_perc_2 * custom_leverage - 0.0011 } plot_config = { 'main_plot': { }, 'subplots': { "variazione percentuale": { 'perc-price-variation-abs': {'color': 'green'}, 'perc-price-variation-soglia-1': {'color': 'red'}, 'perc-price-variation-soglia-2': {'color': 'red'}, }, "Up_or_down": { '&s-up_or_down': {'color': 'green'}, }, "predict": { 'do_predict': {'color': 'black'} }, "prediction": { 'prediction': {'color': 'blue'} }, "prediction-stat": { 'prediction_error-pct': {'color': 'red'}, 'prediction_mov_done_error-pct': {'color': 'blue'}, 'prediction_mov_correctness_error-pct': {'color': 'green'}, } } } def bot_start(self, **kwargs) -> None: """ Called only once after bot instantiation. :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. """ self.custom_leverage = self.config["proton_parameters"]["custom_leverage"] self.dynamic_target = self.config["proton_parameters"]["dynamic_target"] self.prediction_diff_perc_1 = self.config["proton_parameters"]["minimal_price_perc_difference_1"] self.prediction_diff_perc_2 = self.config["proton_parameters"]["minimal_price_perc_difference_2"] self.use_half_label_period_candle = self.config["proton_parameters"]["use_half_label_period_candle"] self.num_label_2 = self.freqai_info["feature_parameters"]["label_period_candles"] if self.use_half_label_period_candle: self.num_label_1 = int(self.num_label_2 / 2) else: self.num_label_1 = self.num_label_2 timeframe_in_minutes = int(self.timeframe[:-1]) # Inizializzazione di parametri dipendenti da configurazione self.max_trade_duration = timeframe_in_minutes*(self.num_label_1) logger.info(f"--> self.max_trade_duration at {self.max_trade_duration} minutes") def feature_engineering_expand_all(self, dataframe: DataFrame, period: int, metadata: Dict, **kwargs) -> DataFrame: """ This function will automatically expand the defined features on the config defined `indicator_periods_candles`, `include_timeframes`, `include_shifted_candles`, and `include_corr_pairs`. In other words, a single feature defined in this function will automatically expand to a total of `indicator_periods_candles` * `include_timeframes` * `include_shifted_candles` * `include_corr_pairs` numbers of features added to the model. All features must be prepended with `%` to be recognized by FreqAI internals. Access metadata such as the current pair/timeframe/period with: `metadata["pair"]` `metadata["tf"]` `metadata["period"]` :param dataframe: strategy dataframe which will receive the features :param period: period of the indicator - usage example: :param metadata: metadata of current pair dataframe["%-ema-period"] = ta.EMA(dataframe, timeperiod=period) dataframe["%-adx-period"] = ta.ADX(dataframe, timeperiod=period) dataframe["%-sma-period"] = ta.SMA(dataframe, timeperiod=period) dataframe["%-ema-period"] = ta.EMA(dataframe, timeperiod=period) bollinger = qtpylib.bollinger_bands( qtpylib.typical_price(dataframe), window=period, stds=2.2 ) dataframe["bb_lowerband-period"] = bollinger["lower"] dataframe["bb_middleband-period"] = bollinger["mid"] dataframe["bb_upperband-period"] = bollinger["upper"] dataframe["%-bb_width-period"] = ( dataframe["bb_upperband-period"] - dataframe["bb_lowerband-period"] ) / dataframe["bb_middleband-period"] dataframe["%-close-bb_lower-period"] = ( dataframe["close"] / dataframe["bb_lowerband-period"] ) dataframe["%-roc-period"] = ta.ROC(dataframe, timeperiod=period) dataframe["%-relative_volume-period"] = ( dataframe["volume"] / dataframe["volume"].rolling(period).mean() ) """ # https://www.avatrade.it/education/technical-analysis-indicators-strategies/mfi-indicator-trading-strategies dataframe["%-mfi-period"] = ta.MFI(dataframe, timeperiod=period) # MFI è un rsi che integra il volume e va accoppiato allo stocastico # ## PROVARE AD AGGIUNGERE: valutare l'importanza della feature: # #dataframe["%-relative_volume-period"] = ( # dataframe["volume"] / dataframe["volume"].rolling(period).mean() #) return dataframe def feature_engineering_expand_basic( self, dataframe: DataFrame, metadata: Dict, **kwargs) -> DataFrame: """ This function will automatically expand the defined features on the config defined `include_timeframes`, `include_shifted_candles`, and `include_corr_pairs`. In other words, a single feature defined in this function will automatically expand to a total of `include_timeframes` * `include_shifted_candles` * `include_corr_pairs` numbers of features added to the model. Features defined here will *not* be automatically duplicated on user defined `indicator_periods_candles` All features must be prepended with `%` to be recognized by FreqAI internals. Access metadata such as the current pair/timeframe with: `metadata["pair"]` `metadata["tf"]` :param dataframe: strategy dataframe which will receive the features :param metadata: metadata of current pair dataframe["%-pct-change"] = dataframe["close"].pct_change() dataframe["%-ema-200"] = ta.EMA(dataframe, timeperiod=200) """ """ dataframe["%-pct-change"] = dataframe["close"].pct_change() dataframe["%-raw_volume"] = dataframe["volume"] dataframe["%-raw_price"] = dataframe["close"] """ # https://www.avatrade.it/education/technical-analysis-indicators-strategies/stochastic-trading-strategies stoch_fast = ta.STOCHF(dataframe, 5, 3, 0, 3, 0) dataframe["%-fastd"] = stoch_fast['fastd'] dataframe["%-fastk"] = stoch_fast['fastk'] # Percentage changes dataframe["%-pct-change-1"] = dataframe["close"].pct_change(periods=self.num_label_1) dataframe["%-pct-volume-change-1"] = dataframe["volume"].pct_change(periods=self.num_label_1) if self.use_half_label_period_candle: dataframe["%-pct-change-2"] = dataframe["close"].pct_change(periods=self.num_label_2) dataframe["%-pct-volume-change-2"] = dataframe["volume"].pct_change(periods=self.num_label_2) # Percentage changes on mean of 3 last values #dataframe["%-pct-mean-change"] = dataframe["close"].shift(3).rolling(3).mean().pct_change() #dataframe["%-pct-mean-volume-change"] = dataframe["volume"].shift(3).rolling(3).mean().pct_change() # tratto da come utilizzare MFI https://www.comefaretradingonline.com/money-flow-index/#Come_Fare_Trading_con_il_Money_Flow_Index #if metadata["tf"] != self.timeframe: dataframe["distance-close-sma"] = dataframe["close"] - ta.SMA(dataframe, timeperiod=10) dataframe["%-pct-distance-close-sma"] = dataframe["distance-close-sma"].pct_change(periods=self.num_label_1) return dataframe def feature_engineering_standard( self, dataframe: DataFrame, metadata: Dict, **kwargs) -> DataFrame: """ This optional function will be called once with the dataframe of the base timeframe. This is the final function to be called, which means that the dataframe entering this function will contain all the features and columns created by all other freqai_feature_engineering_* functions. This function is a good place to do custom exotic feature extractions (e.g. tsfresh). This function is a good place for any feature that should not be auto-expanded upon (e.g. day of the week). Access metadata such as the current pair with: `metadata["pair"]` :param dataframe: strategy dataframe which will receive the features :param metadata: metadata of current pair usage example: dataframe["%-day_of_week"] = (dataframe["date"].dt.dayofweek + 1) / 7 dataframe["%-day_of_week"] = dataframe["date"].dt.dayofweek dataframe["%-hour_of_day"] = dataframe["date"].dt.hour """ ### Indicatori TradingView https://www.tradingview.com/symbols/BTCUSDT/technicals/?exchange=BINANCE ## 1 = buy, 0 = neutral, -1 = sell ## Moving Averages # Buy — MA value < price # Sell — MA value > price # Neutral — MA value = price # ema(10) dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10) dataframe.loc[(dataframe['ema10'] < dataframe['close']), '%-ema10-up-down'] = 1 dataframe.loc[np.isnan(dataframe['%-ema10-up-down']), '%-ema10-up-down'] = -1 # sma(10) dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) dataframe.loc[(dataframe['sma10'] < dataframe['close']), '%-sma10-up-down'] = 1 dataframe.loc[np.isnan(dataframe['%-sma10-up-down']), '%-sma10-up-down'] = -1 # ema(20) dataframe['ema20'] = ta.EMA(dataframe, timeperiod=20) dataframe.loc[(dataframe['ema20'] < dataframe['close']), '%-ema20-up-down'] = 1 dataframe.loc[np.isnan(dataframe['%-ema20-up-down']), '%-ema20-up-down'] = -1 # sma(20) dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) dataframe.loc[(dataframe['sma20'] < dataframe['close']), '%-sma20-up-down'] = 1 dataframe.loc[np.isnan(dataframe['%-sma20-up-down']), '%-sma20-up-down'] = -1 # ema(30) dataframe['ema30'] = ta.EMA(dataframe, timeperiod=30) dataframe.loc[(dataframe['ema30'] < dataframe['close']), '%-ema30-up-down'] = 1 dataframe.loc[np.isnan(dataframe['%-ema30-up-down']), '%-ema30-up-down'] = -1 # sma(30) dataframe['sma30'] = ta.SMA(dataframe, timeperiod=30) dataframe.loc[(dataframe['sma30'] < dataframe['close']), '%-sma30-up-down'] = 1 dataframe.loc[np.isnan(dataframe['%-sma30-up-down']), '%-sma30-up-down'] = -1 # ema(50) dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) dataframe.loc[(dataframe['ema50'] < dataframe['close']), '%-ema50-up-down'] = 1 dataframe.loc[np.isnan(dataframe['%-ema50-up-down']), '%-ema50-up-down'] = -1 # sma(50) dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) dataframe.loc[(dataframe['sma50'] < dataframe['close']), '%-sma50-up-down'] = 1 dataframe.loc[np.isnan(dataframe['%-sma50-up-down']), '%-sma50-up-down'] = -1 # ema(100) dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) dataframe.loc[(dataframe['ema100'] < dataframe['close']), '%-ema100-up-down'] = 1 dataframe.loc[np.isnan(dataframe['%-ema100-up-down']), '%-ema100-up-down'] = -1 # sma(100) dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) dataframe.loc[(dataframe['sma100'] < dataframe['close']), '%-sma100-up-down'] = 1 dataframe.loc[np.isnan(dataframe['%-sma100-up-down']), '%-sma100-up-down'] = -1 # ema(200) dataframe['ema200'] = ta.EMA(dataframe, timeperiod=200) dataframe.loc[(dataframe['ema200'] < dataframe['close']), '%-ema200-up-down'] = 1 dataframe.loc[np.isnan(dataframe['%-ema200-up-down']), '%-ema200-up-down'] = -1 # sma(200) dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) dataframe.loc[(dataframe['sma200'] < dataframe['close']), '%-sma200-up-down'] = 1 dataframe.loc[np.isnan(dataframe['%-sma200-up-down']), '%-sma200-up-down'] = -1 # ichimoku base line (9,26,52,26) # Ichimoku Cloud # Buy — base line < price and conversion line crosses price from below and lead line 1 > price and lead line 1 > lead line 2 # Sell — base line > price and conversion line crosses price from above and lead line 1 < price and lead line 1 < lead line 2 # Neutral — neither Buy nor Sell ichimoku = ftt.ichimoku(dataframe, conversion_line_period=9, base_line_periods=26, laggin_span=52, displacement=26) #dataframe['chikou_span'] = ichimoku['chikou_span'] NON UTILIZZARE: ha bias # conversion line #dataframe['tenkan_sen'] = ichimoku['tenkan_sen'] # base line #dataframe['kijun_sen'] = ichimoku['kijun_sen'] # #dataframe['senkou_a'] = ichimoku['senkou_span_a'] #dataframe['senkou_b'] = ichimoku['senkou_span_b'] # lead line 1 #dataframe['leading_senkou_span_a'] = ichimoku['leading_senkou_span_a'] # lead line 2 #dataframe['leading_senkou_span_b'] = ichimoku['leading_senkou_span_b'] # #dataframe['cloud_green'] = ichimoku['cloud_green'] #dataframe['cloud_red'] = ichimoku['cloud_red'] dataframe.loc[ ((ichimoku['kijun_sen'] < dataframe['close']) & (qtpylib.crossed_above(ichimoku['tenkan_sen'], dataframe['close'])) & (ichimoku['leading_senkou_span_a'] > ichimoku['leading_senkou_span_b'])) , '%-ichimoku-up-down'] = 1 dataframe.loc[ ((ichimoku['kijun_sen'] > dataframe['close']) & (qtpylib.crossed_below(ichimoku['tenkan_sen'], dataframe['close'])) & (ichimoku['leading_senkou_span_a'] < ichimoku['leading_senkou_span_b'])) , '%-ichimoku-up-down'] = -1 dataframe.loc[np.isnan(dataframe['%-ichimoku-up-down']), '%-ichimoku-up-down'] = 0 # vwma(20) dataframe['vwma20'] = ftt.vwma(dataframe, 20) dataframe.loc[(dataframe['vwma20'] < dataframe['close']), '%-vwma20-up-down'] = 1 dataframe.loc[np.isnan(dataframe['%-vwma20-up-down']), '%-vwma20-up-down'] = -1 # hma(9) Hull Moving Average dataframe['hma9'] = ftt.hull_moving_average(dataframe, 9) dataframe.loc[(dataframe['hma9'] < dataframe['close']), '%-hma9-up-down'] = 1 dataframe.loc[np.isnan(dataframe['%-hma9-up-down']), '%-hma9-up-down'] = -1 ## Oscillators # rsi(14) # Buy — indicator < 30 and rising # Sell — indicator > 70 and falling # Neutral — neither Buy nor Sell dataframe['rsi14'] = ta.RSI(dataframe, timeperiod=14) dataframe.loc[((dataframe['rsi14'] < 30) & (dataframe['rsi14'] > dataframe['rsi14'].shift(1))), '%-rsi14-up-down'] = 1 dataframe.loc[((dataframe['rsi14'] > 70) & (dataframe['rsi14'] < dataframe['rsi14'].shift(1))), '%-rsi14-up-down'] = -1 dataframe.loc[np.isnan(dataframe['%-rsi14-up-down']), '%-rsi14-up-down'] = 0 # stoch(14,3,3) # Buy — main line < 20 and main line crosses over the signal line # Sell — main line > 80 and main line crosses under the signal line # Neutral — neither Buy nor Sell # nota: lo STOCH slow non funziona, uso il fast #stoch = ta.STOCHF(dataframe) #dataframe.loc[((stoch['fastk'] < 20) & qtpylib.crossed_above(stoch['fastk'], stoch['fastd'])), '%-stoch-fast-14-3-3-up-down'] = 1 #dataframe.loc[((stoch['fastk'] > 80) & qtpylib.crossed_below(stoch['fastk'], stoch['fastd'])), '%-stoch-fast-14-3-3-up-down'] = -1 #dataframe.loc[np.isnan(dataframe['%-stoch-fast-14-3-3-up-down']), '%-stoch-fast-14-3-3-up-down'] = 0 stoch = ta.STOCH(dataframe) dataframe.loc[((stoch['slowk'] < 20) & qtpylib.crossed_above(stoch['slowk'], stoch['slowd'])), '%-stoch-14-3-3-up-down'] = 1 dataframe.loc[((stoch['slowk'] > 80) & qtpylib.crossed_below(stoch['slowk'], stoch['slowd'])), '%-stoch-14-3-3-up-down'] = -1 dataframe.loc[np.isnan(dataframe['%-stoch-14-3-3-up-down']), '%-stoch-14-3-3-up-down'] = 0 # CCI(20) # Buy — indicator < -100 and rising # Sell — indicator > 100 and falling # Neutral — neither Buy nor Sell dataframe['cci20'] = ta.CCI(dataframe, 20) dataframe.loc[((dataframe['cci20'] < -100) & (dataframe['cci20'] > dataframe['cci20'].shift(1))), '%-cci20-up-down'] = 1 dataframe.loc[((dataframe['cci20'] > 100) & (dataframe['cci20'] < dataframe['cci20'].shift(1))), '%-cci20-up-down'] = -1 dataframe.loc[np.isnan(dataframe['%-cci20-up-down']), '%-cci20-up-down'] = 0 # Average Directional Index (14) # Buy — indicator > 20 and +DI line crosses over -DI line # Sell — indicator > 20 and +DI line crosses under -DI line # Neutral — neither Buy nor Sell dataframe['adx'] = ta.ADX(dataframe, 14) # # 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) dataframe.loc[((dataframe['adx'] > 20) & (qtpylib.crossed_above(dataframe['plus_di'], dataframe['minus_di']))), '%-adx14-up-down'] = 1 dataframe.loc[((dataframe['adx'] > 20) & (qtpylib.crossed_below(dataframe['plus_di'], dataframe['minus_di']))), '%-adx14-up-down'] = -1 dataframe.loc[np.isnan(dataframe['%-adx14-up-down']), '%-adx14-up-down'] = 0 # Awesome Oscillator ( https://www.avatrade.it/education/technical-analysis-indicators-strategies/awesome-oscillator-indicator-strategies ) # Buy — saucer and values are greater than 0, or cross over the zero line # Sell — saucer and values are lower than 0, or cross under the zero line # Neutral — neither Buy nor Sell #TODO analizzare perchè awesome oscillator va in errore se faccio "plot" # dataframe['ao'] = qtpylib.awesome_oscillator(dataframe) # dataframe.loc[( # #saucer # ( # # Due barre rosse consecutive dell’istogramma, la seconda più corta della prima # (dataframe['ao'].shift(3) > dataframe['ao'].shift(2)) #prima barra rossa # & (dataframe['ao'].shift(2) > dataframe['ao'].shift(1)) #seconda barra rossa # # La terza barra dell’istogramma è verde e più alta della seconda # & (dataframe['ao'] > dataframe['ao'].shift(1)) # # Awesome Oscillator sopra lo zero # & (dataframe['ao'] > 0) # ) # | ((dataframe['ao'] > 0) & (dataframe['ao'].shift(1) < 0)) # ), '%-ao-up-down'] = 1 # dataframe.loc[( # #saucer # ( # # due barre verdi consecutive, di cui la seconda maggiore della prima # (dataframe['ao'].shift(3) < dataframe['ao'].shift(2)) #prima barra verde # & (dataframe['ao'].shift(2) < dataframe['ao'].shift(1)) #seconda barra verde # # La terza barra dell’istogramma è rossa e inferiore alla seconda # & (dataframe['ao'] < dataframe['ao'].shift(1)) # # Awesome Oscillator sopra lo zero # & (dataframe['ao'] < 0) # ) # | ((dataframe['ao'] < 0) & (dataframe['ao'].shift(1) > 0)) # ), '%-ao-up-down'] = -1 # dataframe.loc[np.isnan(dataframe['%-ao-up-down']), '%-ao-up-down'] = 0 # Momentum (10) # Buy — indicator values are rising # Sell — indicator values are falling # Neutral — neither Buy nor Sell dataframe['mom'] = ta.MOM(dataframe, 10) dataframe.loc[(dataframe['mom'] > dataframe['mom'].shift(1)), '%-mom10-up-down'] = 1 dataframe.loc[(dataframe['mom'] < dataframe['mom'].shift(1)), '%-mom10-up-down'] = -1 dataframe.loc[np.isnan(dataframe['%-mom10-up-down']), '%-mom10-up-down'] = 0 # MACD Level (12, 26) # Buy — main line values > signal line values # Sell — main line values < signal line values # Neutral — neither Buy nor Sell macd = ta.MACD(dataframe, 12, 6) dataframe['macd'] = macd['macd'] dataframe['macdsignal'] = macd['macdsignal'] #dataframe['macdhist'] = macd['macdhist'] dataframe.loc[(dataframe['macd'] > dataframe['macdsignal']), '%-macd-12-26-up-down'] = 1 dataframe.loc[(dataframe['macd'] < dataframe['macdsignal']), '%-macd-12-26-up-down'] = -1 dataframe.loc[np.isnan(dataframe['%-macd-12-26-up-down']), '%-macd-12-26-up-down'] = 0 # Stochastic RSI Fast (3, 3, 14, 14) # Buy — downtrend and K and D lines < 20 and K line crosses over D line # Sell — uptrend and K and D lines > 80 and K line crosses under D line # Neutral — neither Buy nor Sell #TODO inserire i parametri corretti: timeperiod=14, fastk_period=3, fastd_period=3, fastd_matype=14) stoch_rsi = ta.STOCHRSI(dataframe) dataframe['fastd_rsi'] = stoch_rsi['fastd'] dataframe['fastk_rsi'] = stoch_rsi['fastk'] dataframe.loc[( # TODO downtrend stochrsi (il segnale o il prezzo???) # K and D lines < 20 (dataframe['fastk_rsi'] < 20) & (dataframe['fastd_rsi'] < 20) & qtpylib.crossed_above(dataframe['fastk_rsi'], dataframe['fastd_rsi']) ), '%-stochrsi-up-down'] = 1 dataframe.loc[( # TODO uptrend stochrsi (il segnale o il prezzo???) # K and D lines > 80 (dataframe['fastk_rsi'] > 80) & (dataframe['fastd_rsi'] > 80) & qtpylib.crossed_below(dataframe['fastk_rsi'], dataframe['fastd_rsi']) ), '%-stochrsi-up-down'] = -1 dataframe.loc[np.isnan(dataframe['%-stochrsi-up-down']), '%-stochrsi-up-down'] = 0 # Williams Percent Range (14) (valori tra -100 e 0) # Buy — indicator < lower band and rising # Sell — indicator > upper band and falling # Neutral — neither Buy nor Sell # ipotizzo che band sia tra -20 e -80 https://www.guidatradingonline.net/indicatore-williams-percent-range/ dataframe['willr'] = ta.WILLR(dataframe, 14) dataframe.loc[( (dataframe['willr'] < -80) & (dataframe['willr'] > dataframe['willr'].shift(-1)) ), '%-willr-up-down'] = 1 dataframe.loc[( (dataframe['willr'] > -20) & (dataframe['willr'] < dataframe['willr'].shift(-1)) ), '%-willr-up-down'] = -1 dataframe.loc[np.isnan(dataframe['%-willr-up-down']), '%-willr-up-down'] = 0 #TODO Bull Bear Power # Buy — uptrend and BearPower < zero and BearPower is rising # Sell — downtrend and BullPower > zero and BullPower is falling # Neutral — neither Buy nor Sell # https://skilling.com/row/en/blog/trading-articles/what-is-the-bulls-and-bears-power-indicator/ # Bull Power = High price - Exponential Moving Average. # It simply measures whether the current price is higher than the previous one and if it is, then the bulls are considered to be ‘winning’. # This is done by taking the difference between the highest price of the current bar, and an exponential moving average (13-period by default) # Bear Power = Low price - Exponential Moving Average # you take the difference between the lowest price and the exponential moving average (13-period by default) # uptrend e downtrend con SMA : https://www.babypips.com/learn/forex/using-moving-averages : # puoi usare 2 o più sma per esempio sma(10) e sma(20) # faster MA over slower MA in an uptrend, slower MA over faster MA in a downtrend # Ultimate Oscillator (7, 14, 28) # Buy — UO > 70 # Sell — UO < 30 # Neutral — neither Buy nor Sell dataframe['uo'] = ta.ULTOSC(dataframe) dataframe.loc[(dataframe['uo'] > 70), '%-uo-up-down'] = 1 dataframe.loc[(dataframe['uo'] < 30), '%-uo-up-down'] = -1 dataframe.loc[np.isnan(dataframe['%-uo-up-down']), '%-uo-up-down'] = 0 # Indicatore TradingView # [-1.0 ≥ value < -0.5] — Strong Sell # [-0.5 ≥ value < -0.1] — Sell # [-0.1 ≥ value ≤ 0.1] — Neutral # [0.1 > value ≤ 0.5] — Buy # [0.5 > value ≤ 1.0] — Strong Buy # The overall rating of the indicator includes two large groups of indicators. # The first consists of SMAs and EMAs with different lengths (MA lengths are 10, 20, 30, 50, 100 and 200), # the Ichimoku Cloud (9, 26, 52), VWMA (20) and HullMA (9). # The second one is calculated on the following oscillators: RSI (14), Stochastic (14, 3, 3), CCI (20), # ADX (14, 14), AO, Momentum (10), MACD (12, 26, 9), Stochastic RSI (3, 3, 14, 14), Williams %R (14), # Bulls and Bears Power and UO (7,14,28). # Each group's rating is calculated separately: oscillators e MovingAverages dataframe['tw-movingaverages-avg'] = dataframe[[ '%-ema10-up-down', '%-sma10-up-down', '%-ema20-up-down', '%-sma20-up-down', '%-ema30-up-down', '%-sma30-up-down', '%-ema50-up-down', '%-sma50-up-down', '%-ema100-up-down', '%-sma100-up-down', '%-ema200-up-down', '%-sma200-up-down', '%-ichimoku-up-down', '%-vwma20-up-down', '%-hma9-up-down' ]].mean(axis=1) dataframe.loc[(-1 <= dataframe['tw-movingaverages-avg']) & (dataframe['tw-movingaverages-avg'] < -0.5), '%-tw-movingaverages-up-down'] = -2 dataframe.loc[(-0.5 <= dataframe['tw-movingaverages-avg']) & (dataframe['tw-movingaverages-avg'] < -0.1), '%-tw-movingaverages-up-down'] = -1 dataframe.loc[(-0.1 <= dataframe['tw-movingaverages-avg']) & (dataframe['tw-movingaverages-avg'] < 0.1), '%-tw-movingaverages-up-down'] = 0 dataframe.loc[(0.1 <= dataframe['tw-movingaverages-avg']) & (dataframe['tw-movingaverages-avg'] < 0.5), '%-tw-movingaverages-up-down'] = 1 dataframe.loc[(0.5 <= dataframe['tw-movingaverages-avg']) & (dataframe['tw-movingaverages-avg'] < 1), '%-tw-movingaverages-up-down'] = 2 dataframe['tw-oscillators-avg'] = dataframe[[ '%-rsi14-up-down', '%-stoch-14-3-3-up-down', '%-cci20-up-down', '%-adx14-up-down', #'%-ao-up-down', '%-mom10-up-down', '%-macd-12-26-up-down', '%-stochrsi-up-down', '%-willr-up-down', #bull e bear power '%-uo-up-down' ]].mean(axis=1) dataframe.loc[(-1 <= dataframe['tw-oscillators-avg']) & (dataframe['tw-oscillators-avg'] < -0.5), '%-tw-oscillators-up-down'] = -2 dataframe.loc[(-0.5 <= dataframe['tw-oscillators-avg']) & (dataframe['tw-oscillators-avg'] < -0.1), '%-tw-oscillators-up-down'] = -1 dataframe.loc[(-0.1 <= dataframe['tw-oscillators-avg']) & (dataframe['tw-oscillators-avg'] < 0.1), '%-tw-oscillators-up-down'] = 0 dataframe.loc[(0.1 <= dataframe['tw-oscillators-avg']) & (dataframe['tw-oscillators-avg'] < 0.5), '%-tw-oscillators-up-down'] = 1 dataframe.loc[(0.5 <= dataframe['tw-oscillators-avg']) & (dataframe['tw-oscillators-avg'] < 1), '%-tw-oscillators-up-down'] = 2 dataframe['tradingview-avg'] = dataframe[[ '%-ema10-up-down', '%-sma10-up-down', '%-ema20-up-down', '%-sma20-up-down', '%-ema30-up-down', '%-sma30-up-down', '%-ema50-up-down', '%-sma50-up-down', '%-ema100-up-down', '%-sma100-up-down', '%-ema200-up-down', '%-sma200-up-down', '%-ichimoku-up-down', '%-vwma20-up-down', '%-hma9-up-down', '%-rsi14-up-down', '%-stoch-14-3-3-up-down', '%-cci20-up-down', '%-adx14-up-down', #'%-ao-up-down', '%-mom10-up-down', '%-macd-12-26-up-down', '%-stochrsi-up-down', '%-willr-up-down', #bull e bear power '%-uo-up-down' ]].mean(axis=1) dataframe.loc[(-1 <= dataframe['tradingview-avg']) & (dataframe['tradingview-avg'] < -0.5), '%-tradingview-up-down'] = -2 dataframe.loc[(-0.5 <= dataframe['tradingview-avg']) & (dataframe['tradingview-avg'] < -0.1), '%-tradingview-up-down'] = -1 dataframe.loc[(-0.1 <= dataframe['tradingview-avg']) & (dataframe['tradingview-avg'] < 0.1), '%-tradingview-up-down'] = 0 dataframe.loc[(0.1 <= dataframe['tradingview-avg']) & (dataframe['tradingview-avg'] < 0.5), '%-tradingview-up-down'] = 1 dataframe.loc[(0.5 <= dataframe['tradingview-avg']) & (dataframe['tradingview-avg'] < 1), '%-tradingview-up-down'] = 2 return dataframe def set_freqai_targets(self, dataframe: DataFrame, metadata: Dict, **kwargs) -> DataFrame: """ *Only functional with FreqAI enabled strategies* Required function to set the targets for the model. All targets must be prepended with `&` to be recognized by the FreqAI internals. More details about feature engineering available: https://www.freqtrade.io/en/latest/freqai-feature-engineering :param dataframe: strategy dataframe which will receive the targets :param metadata: metadata of current pair usage example: dataframe["&-target"] = dataframe["close"].shift(-1) / dataframe["close"] """ self.freqai.class_names = ['-2', '-1', '0', '1', '2'] if not self.dynamic_target: # target dinamico disabilitato dataframe['&s-up_or_down'] = np.where( ( (dataframe["close"].shift(-self.num_label_1) > (dataframe["close"]*(1+self.prediction_diff_perc_1))) & (dataframe['high'].shift(-self.num_label_2).rolling(self.num_label_2).max() >= (dataframe["close"]*(1+self.prediction_diff_perc_1))) ), '1', '0') dataframe['&s-up_or_down'] = np.where( ( (dataframe["close"].shift(-self.num_label_1) > (dataframe["close"]*(1+self.prediction_diff_perc_2))) & (dataframe['high'].shift(-self.num_label_2).rolling(self.num_label_2).max() >= (dataframe["close"]*(1+self.prediction_diff_perc_2))) ), '2', dataframe['&s-up_or_down']) dataframe['&s-up_or_down'] = np.where( ( (dataframe["close"].shift(-self.num_label_1) < (dataframe["close"]*(1-self.prediction_diff_perc_1))) & (dataframe['low'].shift(-self.num_label_2).rolling(self.num_label_2).min() <= (dataframe["close"]*(1-self.prediction_diff_perc_1))) ), '-1', dataframe['&s-up_or_down']) dataframe['&s-up_or_down'] = np.where( ( (dataframe["close"].shift(-self.num_label_1) < (dataframe["close"]*(1-self.prediction_diff_perc_2))) & (dataframe['low'].shift(-self.num_label_2).rolling(self.num_label_2).min() <= (dataframe["close"]*(1-self.prediction_diff_perc_2))) ), '-2', dataframe['&s-up_or_down']) else: ## INIZIO Tentativo di target dinamico (non fa alcuna predizione dataframe['pct-change'] = dataframe["close"].pct_change(periods=self.num_label_1) dataframe.loc[np.isnan(dataframe['pct-change']), 'pct-change'] = 0 dataframe['target_1'] = ta.SMA(abs(dataframe['pct-change']), self.num_label_1) # imposto un minimo dataframe.loc[dataframe['target_1'] < self.prediction_diff_perc_1, 'target_1'] = self.prediction_diff_perc_1 dataframe['target_2'] = dataframe['target_1']*1.5 dataframe.loc[np.isnan(dataframe['target_1']), 'target_1'] = 1 dataframe.loc[np.isnan(dataframe['target_2']), 'target_2'] = 1 dataframe['&s-up_or_down'] = np.where( #dataframe["%-pct-change-1_gen_BTC/USDT:USDT_3m"].shift(-self.num_label_1) > self.prediction_diff_perc_1, #dataframe["close"].shift(-self.num_label_1) > (dataframe["close"]*(1+self.prediction_diff_perc_1)), dataframe["pct-change"].shift(-self.num_label_1) > dataframe['target_1'], '1', '0') dataframe['&s-up_or_down'] = np.where( #dataframe["%-pct-change-1_gen_BTC/USDT:USDT_3m"].shift(-self.num_label_1) > self.prediction_diff_perc_2, #dataframe["close"].shift(-self.num_label_1) > (dataframe["close"]*(1+self.prediction_diff_perc_2)), dataframe["pct-change"].shift(-self.num_label_1) > dataframe['target_2'], '2', dataframe['&s-up_or_down']) dataframe['&s-up_or_down'] = np.where( #dataframe["%-pct-change-1_gen_BTC/USDT:USDT_3m"].shift(-self.num_label_1) < self.prediction_diff_perc_1, #dataframe["close"].shift(-self.num_label_1) < (dataframe["close"]*(1-self.prediction_diff_perc_1)), dataframe["pct-change"].shift(-self.num_label_1) < -dataframe['target_1'], '-1', dataframe['&s-up_or_down']) dataframe['&s-up_or_down'] = np.where( #dataframe["%-pct-change-1_gen_BTC/USDT:USDT_3m"].shift(-self.num_label_1) < self.prediction_diff_perc_2, #dataframe["close"].shift(-self.num_label_1) < (dataframe["close"]*(1-self.prediction_diff_perc_2)), dataframe["pct-change"].shift(-self.num_label_1) < -dataframe['target_2'], '-2', dataframe['&s-up_or_down']) ## FINE target dinamico # dataframe['&s-up_or_down'] = np.where( # dataframe["close"] # .shift(-self.freqai_info["feature_parameters"]["label_period_candles"]) # .rolling(self.freqai_info["feature_parameters"]["label_period_candles"]) # .mean() > dataframe["close"], '1', '-1') return dataframe def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # noqa: C901 # User creates their own custom strat here. Present example is a supertrend # based strategy. dataframe = self.freqai.start(dataframe, metadata, self) if self.dp.runmode == RunMode.BACKTEST: ####################################################### # Verifica predizione ####################################################### #( (valore finale - valore originale) / valore originale) x 100% #todo rivedere questo calcolo dataframe["perc-price-variation"] = dataframe["close"].pct_change(periods=self.num_label_1) * 100 dataframe["perc-price-variation-abs"] = abs(dataframe["perc-price-variation"]) dataframe['perc-price-variation-soglia-1'] = abs(self.prediction_diff_perc_1 * 100) dataframe['perc-price-variation-soglia-2'] = abs(self.prediction_diff_perc_2 * 100) dataframe.loc[ ( ( (dataframe["perc-price-variation"] >= dataframe['perc-price-variation-soglia-1']) & (dataframe['&s-up_or_down'].shift(self.num_label_1) == '1') & (dataframe['do_predict'].shift(self.num_label_1) == 1) ) | ( (dataframe["perc-price-variation"] >= dataframe['perc-price-variation-soglia-1']) & (dataframe['&s-up_or_down'].shift(self.num_label_1) == '2') & (dataframe['do_predict'].shift(self.num_label_1) == 1) ) | ( (dataframe["perc-price-variation"] <= -dataframe['perc-price-variation-soglia-1']) & (dataframe['&s-up_or_down'].shift(self.num_label_1) == '-1') & (dataframe['do_predict'].shift(self.num_label_1) == 1) ) | ( (dataframe["perc-price-variation"] <= -dataframe['perc-price-variation-soglia-1']) & (dataframe['&s-up_or_down'].shift(self.num_label_1) == '-2') & (dataframe['do_predict'].shift(self.num_label_1) == 1) ) | ( (dataframe["perc-price-variation"] <= dataframe['perc-price-variation-soglia-1']) & (dataframe["perc-price-variation"] >= -dataframe['perc-price-variation-soglia-1']) & (dataframe['&s-up_or_down'].shift(self.num_label_1) == '0') & (dataframe['do_predict'].shift(self.num_label_1) == 1) ) | (dataframe['do_predict'].shift(self.num_label_1) < 1) ) & dataframe["DI_values"] > 0, 'prediction' ] = 1 dataframe.loc[np.isnan(dataframe["prediction"]), 'prediction'] = 0 #dataframe.loc[ # ( # ((dataframe["perc-price-variation"] < dataframe['perc-price-variation-soglia-1']) & (dataframe['&s-up_or_down'].shift(self.num_label_1) == '1') & (dataframe['do_predict'].shift(self.num_label_1) == 1)) | # ((dataframe["perc-price-variation"] < dataframe['perc-price-variation-soglia-2']) & (dataframe['&s-up_or_down'].shift(self.num_label_1) == '2') & (dataframe['do_predict'].shift(self.num_label_1) == 1)) | # ((dataframe["perc-price-variation"] > -dataframe['perc-price-variation-soglia-1']) & (dataframe['&s-up_or_down'].shift(self.num_label_1) == '-1') & (dataframe['do_predict'].shift(self.num_label_1) == 1)) | # ((dataframe["perc-price-variation"] > -dataframe['perc-price-variation-soglia-2']) & (dataframe['&s-up_or_down'].shift(self.num_label_1) == '-2') & (dataframe['do_predict'].shift(self.num_label_1) == 1)) | # (((dataframe["perc-price-variation"] > dataframe['perc-price-variation-soglia-1']) | (dataframe["perc-price-variation"] < -dataframe['perc-price-variation-soglia-1'])) & (dataframe['&s-up_or_down'].shift(self.num_label_1) == '0') & (dataframe['do_predict'].shift(self.num_label_1) == 1)) # ),'prediction'] = 0 dataframe.loc[ (dataframe["DI_values"] > 0), 'prediction_index_calc' ] = 1 dataframe['prediction_index'] = dataframe['prediction_index_calc'].cumsum() - dataframe['prediction_index_calc'] dataframe['prediction_sum'] = dataframe['prediction'].cumsum() - dataframe['prediction'] dataframe['prediction_error'] = dataframe['prediction_index'] - dataframe['prediction_sum'] dataframe['prediction_error-pct'] = (dataframe['prediction_sum'] / dataframe['prediction_index']) * 100 ####################################################### # Only prediction movement # dataframe.loc[ ((dataframe['prediction_index_calc'] == 1) & (abs(dataframe["perc-price-variation"]) > abs(dataframe['perc-price-variation-soglia-1']))) & dataframe["DI_values"] > 0, 'prediction_mov_index_calc'] = 1 dataframe.loc[np.isnan(dataframe["prediction_mov_index_calc"]), 'prediction_mov_index_calc'] = 0 dataframe['prediction_mov_index'] = dataframe['prediction_mov_index_calc'].cumsum() - dataframe['prediction_mov_index_calc'] dataframe.loc[ ( ( (dataframe["perc-price-variation"] >= dataframe['perc-price-variation-soglia-1']) & (dataframe['&s-up_or_down'].shift(self.num_label_1) == '1') & (dataframe['do_predict'].shift(self.num_label_1) == 1) ) | ( (dataframe["perc-price-variation"] >= dataframe['perc-price-variation-soglia-1']) & (dataframe['&s-up_or_down'].shift(self.num_label_1) == '2') & (dataframe['do_predict'].shift(self.num_label_1) == 1) ) | ( (dataframe["perc-price-variation"] <= -dataframe['perc-price-variation-soglia-1']) & (dataframe['&s-up_or_down'].shift(self.num_label_1) == '-1') & (dataframe['do_predict'].shift(self.num_label_1) == 1) ) | ( (dataframe["perc-price-variation"] <= -dataframe['perc-price-variation-soglia-1']) & (dataframe['&s-up_or_down'].shift(self.num_label_1) == '-2') & (dataframe['do_predict'].shift(self.num_label_1) == 1) ) ), 'prediction_mov' ] = 1 dataframe.loc[np.isnan(dataframe["prediction_mov"]), 'prediction_mov'] = 0 dataframe['prediction_mov_sum'] = dataframe['prediction_mov'].cumsum() - dataframe['prediction_mov'] # numero di errori di predizione e/o predizioni mancanti dataframe['prediction_mov_error'] = dataframe['prediction_mov_index'] - dataframe['prediction_mov_sum'] # percentuale di correttezza di predizioni (quelle mancanti sono conteggiate errate) dataframe['prediction_mov_error-pct'] = (dataframe['prediction_mov_sum'] / dataframe['prediction_mov_index']) * 100 ###################################################################### ## aggiungere solo errori fatti su predizioni di movimento effettuate # tutte le righe con &s-up_or_down <> 0 dataframe.loc[ ( (dataframe['&s-up_or_down'].shift(self.num_label_1) != '0') & (dataframe['do_predict'].shift(self.num_label_1) == 1) & (dataframe["DI_values"].shift(self.num_label_1) > 0) ), 'prediction_mov_correctness_index_calc' ] = 1 dataframe.loc[np.isnan(dataframe["prediction_mov_correctness_index_calc"]), 'prediction_mov_correctness_index_calc'] = 0 dataframe['prediction_mov_correctness_index'] = dataframe['prediction_mov_correctness_index_calc'].cumsum() - dataframe['prediction_mov_correctness_index_calc'] # se prediction_mov == 1 allora e' corretta la predizione dataframe.loc[(dataframe['prediction_mov_correctness_index_calc'] == 1) & (dataframe["prediction_mov"] == 1), 'prediction_mov_correctness' ] = 1 dataframe.loc[np.isnan(dataframe["prediction_mov_correctness"]), 'prediction_mov_correctness'] = 0 dataframe['prediction_mov_correctness_sum'] = dataframe['prediction_mov_correctness'].cumsum() - dataframe['prediction_mov_correctness'] # numero di errori di predizione e/o predizioni mancanti dataframe['prediction_mov_correctness_error'] = dataframe['prediction_mov_correctness_index'] - dataframe['prediction_mov_correctness_sum'] # percentuale di correttezza di predizioni (quelle mancanti sono conteggiate errate) dataframe['prediction_mov_correctness_error-pct'] = (dataframe['prediction_mov_correctness_sum'] / dataframe['prediction_mov_correctness_index']) * 100 # percentuale di predizioni fatte sul numero di predizioni possibili dataframe['prediction_mov_done_error-pct'] = (dataframe['prediction_mov_correctness_index'] / dataframe['prediction_mov_index']) * 100 # utilizzo iloc -2 perche' l'ultimo record non va tenuto in considerazione # logger.info print(f"--> Numero di candele totali: {dataframe['prediction_index'].iloc[-2]}") print(f"--> Percentuale di predizioni corrette: {dataframe['prediction_error-pct'].iloc[-2]} % -- numero posizioni possibili: {dataframe['prediction_index'].iloc[-2]} -- numero posizioni corrette: {dataframe['prediction_sum'].iloc[-2]} ") # print(f"NB: tiene conto come errate solo quelle errate, quelle mancate sono conteggiate di default in questo caso") # print(f"--> Percentuale di predizioni corrette (solo movimenti): {dataframe['prediction_mov_error-pct'].iloc[-2]} % -- numero posizioni possibili: {dataframe['prediction_mov_index'].iloc[-2]} -- numero posizioni corrette: {dataframe['prediction_mov_sum'].iloc[-2]} ") # print(f"NB: predizioni possibili verso quelle corrette, tiene conto come errate sia quelle mancate che quelle errate") print(f"--> Percentuale di predizioni fatte (solo movimenti): {dataframe['prediction_mov_done_error-pct'].iloc[-2]} % -- numero predizioni possibili: {dataframe['prediction_mov_index'].iloc[-2]} -- numero predizioni fatte: {dataframe['prediction_mov_correctness_index'].iloc[-2]} ") print(f"--> Percentuale di predizioni corrette (solo movimenti): {dataframe['prediction_mov_correctness_error-pct'].iloc[-2]} % -- numero predizioni fatte: {dataframe['prediction_mov_correctness_index'].iloc[-2]} -- numero posizioni corrette: {dataframe['prediction_mov_correctness_sum'].iloc[-2]} ") # print(f"NB: predizioni fatte verso quelle corrette") print(f"**** migliorare la percentuale di predizioni fatte :: {dataframe['prediction_mov_done_error-pct'].iloc[-2]} % -- il numero di predizioni dei movimenti ( {dataframe['prediction_mov_correctness_index'].iloc[-2]} ) verso le possibilità di posizioni ( {dataframe['prediction_mov_index'].iloc[-2]} ) sul totale di {dataframe['prediction_index'].iloc[-2]} candele") print(f"**** migliorare la percentuale di correttezza delle predizioni dei movimenti :: {dataframe['prediction_mov_correctness_error-pct'].iloc[-2]} % ") return dataframe def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame: min_confidence_level = 0.2 # 1 / numero di classi df.loc[ ( (df['volume'] > 0) & # Make sure Volume is not 0 (df['do_predict'] == 1) & # Make sure Freqai is confident in the prediction # Only enter trade if Freqai thinks the trend is in this direction (df['1'] > min_confidence_level) & ((df['&s-up_or_down'] == '1')) ), ['enter_long', 'enter_tag']] = (1, 'long-1') df.loc[ ( (df['volume'] > 0) & # Make sure Volume is not 0 (df['do_predict'] == 1) & # Make sure Freqai is confident in the prediction # Only enter trade if Freqai thinks the trend is in this direction (df['2'] > min_confidence_level) & ((df['&s-up_or_down'] == '2')) ), ['enter_long', 'enter_tag']] = (1, 'long-2') df.loc[ ( (df['volume'] > 0) & # Make sure Volume is not 0 (df['do_predict'] == 1) & # Make sure Freqai is confident in the prediction # Only enter trade if Freqai thinks the trend is in this direction (df['-1'] > min_confidence_level) & ((df['&s-up_or_down'] == '-1')) ), ['enter_short', 'enter_tag']] = (1, 'short-1') df.loc[ ( (df['volume'] > 0) & # Make sure Volume is not 0 (df['do_predict'] == 1) & # Make sure Freqai is confident in the prediction # Only enter trade if Freqai thinks the trend is in this direction (df['-2'] > min_confidence_level) & ((df['&s-up_or_down'] == '-2')) ), ['enter_short', 'enter_tag']] = (1, 'short-2') return df def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame: return df def custom_exit(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> Optional[Union[str, bool]]: #TODO aggiungere che se l'ultima candela è inversa alla direzione, allora deve uscire #TODO aggiungere che se nel dataframe ci sono segnali nella stessa direzione, allora non esco # trade expired trade_duration = (current_time - trade.open_date_utc).seconds / 60 if (((trade_duration >= self.max_trade_duration) & (self.use_half_label_period_candle) & (current_profit > 0)) | ((trade_duration >= self.max_trade_duration) & (not self.use_half_label_period_candle))): # logger.info(f"{pair} trade_expired at {current_profit*100}") return "trade_expired" if trade_duration >= (self.max_trade_duration*2): return "trade_expired_x2" # esco se supero la media del pct-change dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) dataframe['custom_exit-pct-change'] = dataframe["close"].pct_change(periods=self.num_label_1) dataframe.loc[np.isnan(dataframe['custom_exit-pct-change']), 'custom_exit-pct-change'] = 0 dataframe['custom_exit-target_1'] = ta.SMA(abs(dataframe['custom_exit-pct-change']), self.num_label_1) dataframe.loc[dataframe['custom_exit-target_1'] < self.prediction_diff_perc_1, 'custom_exit-target_1'] = self.prediction_diff_perc_1 current_candle = dataframe.iloc[-1].squeeze() if current_profit > current_candle['custom_exit-target_1']*self.custom_leverage: return "early_profit" if current_profit < -(current_candle['custom_exit-target_1']*self.custom_leverage*3): return "stop_loss" if self.dp.runmode == RunMode.BACKTEST: if (trade.calc_profit_ratio(current_candle['high']) >= current_candle['custom_exit-target_1']) | (trade.calc_profit_ratio(current_candle['low']) >= current_candle['custom_exit-target_1']): return "early_profit_backtest" 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: """ Customize leverage for each new trade. This method is only called in futures mode. :param pair: Pair that's currently analyzed :param current_time: datetime object, containing the current datetime :param current_rate: Rate, calculated based on pricing settings in exit_pricing. :param proposed_leverage: A leverage proposed by the bot. :param max_leverage: Max leverage allowed on this pair :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. :param side: 'long' or 'short' - indicating the direction of the proposed trade :return: A leverage amount, which is between 1.0 and max_leverage. """ return self.custom_leverage def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, current_time: datetime, entry_tag: Optional[str], side: str, **kwargs) -> bool: """ Called right before placing a entry order. Timing for this function is critical, so avoid doing heavy computations or network requests in this method. For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/ When not implemented by a strategy, returns True (always confirming). :param pair: Pair that's about to be bought/shorted. :param order_type: Order type (as configured in order_types). usually limit or market. :param amount: Amount in target (base) currency that's going to be traded. :param rate: Rate that's going to be used when using limit orders or current rate for market orders. :param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled). :param current_time: datetime object, containing the current datetime :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. :param side: 'long' or 'short' - indicating the direction of the proposed trade :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :return bool: When True is returned, then the buy-order is placed on the exchange. False aborts the process """ return True def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, rate: float, time_in_force: str, exit_reason: str, current_time: datetime, **kwargs) -> bool: """ Called right before placing a regular exit order. Timing for this function is critical, so avoid doing heavy computations or network requests in this method. For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/ When not implemented by a strategy, returns True (always confirming). :param pair: Pair for trade that's about to be exited. :param trade: trade object. :param order_type: Order type (as configured in order_types). usually limit or market. :param amount: Amount in base currency. :param rate: Rate that's going to be used when using limit orders or current rate for market orders. :param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled). :param exit_reason: Exit reason. Can be any of ['roi', 'stop_loss', 'stoploss_on_exchange', 'trailing_stop_loss', 'exit_signal', 'force_exit', 'emergency_exit'] :param current_time: datetime object, containing the current datetime :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :return bool: When True, then the exit-order is placed on the exchange. False aborts the process """ if exit_reason == 'force_exit' and trade.calc_profit_ratio(rate) < 0: # Reject force-sells with negative profit # This is just a sample, please adjust to your needs # (this does not necessarily make sense, assuming you know when you're force-selling) #return False return True return True ##TODO PER RISK MANAGEMENT def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, proposed_stake: float, min_stake: Optional[float], max_stake: float, leverage: float, entry_tag: Optional[str], side: str, **kwargs) -> float: # dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) # current_candle = dataframe.iloc[-1].squeeze() # # if current_candle['fastk_rsi_1h'] > current_candle['fastd_rsi_1h']: # if self.config['stake_amount'] == 'unlimited': # # Use entire available wallet during favorable conditions when in compounding mode. # return max_stake # else: # # Compound profits during favorable conditions instead of using a static stake. # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] # Use default stake amount. return proposed_stake # UTILITY #TODO implementare il parametro window def is_rising(column_dataframe, window=3) -> bool: if column_dataframe > column_dataframe.shift(1) & column_dataframe.shift(1) > column_dataframe(2): return True return False #TODO implementare il parametro window def is_falling(self, column_dataframe, window=3) -> bool: if column_dataframe < column_dataframe.shift(1) & column_dataframe.shift(1) < column_dataframe(2): return True return False