@@ -685,9 +685,380 @@ def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
685685 item_buy_logic = []
686686 item_buy_logic .append (reduce (lambda x , y : x & y , item_buy_protection_list ))
687687
688- # Condition #1 - Describe
688+ # Condition #1 -
689+
690+ # Long mode, bear
691+ if all (c in ['11' ] for c in buy_tags ):
692+ sell , signal_name = self .sell_long_bear (current_profit , max_profit , max_loss , last_candle , previous_candle_1 , previous_candle_2 , previous_candle_3 , previous_candle_4 , previous_candle_5 , trade , current_time , buy_tag )
693+ if sell and (signal_name is not None ):
694+ return f"{ signal_name } ( { buy_tag } )"
695+
696+ return None
697+
698+ def informative_pairs (self ):
699+ # get access to all pairs available in whitelist.
700+ pairs = self .dp .current_whitelist ()
701+ # Assign tf to each pair so they can be downloaded and cached for strategy.
702+ informative_pairs = []
703+ for info_timeframe in self .info_timeframes :
704+ informative_pairs .extend ([(pair , info_timeframe ) for pair in pairs ])
705+
706+ if self .config ['stake_currency' ] in ['USDT' ,'BUSD' ,'USDC' ,'DAI' ,'TUSD' ,'PAX' ,'USD' ,'EUR' ,'GBP' ]:
707+ btc_info_pair = f"BTC/{ self .config ['stake_currency' ]} "
708+ else :
709+ btc_info_pair = "BTC/USDT"
710+
711+ informative_pairs .extend ([(btc_info_pair , btc_info_timeframe ) for btc_info_timeframe in self .btc_info_timeframes ])
712+
713+ return informative_pairs
714+
715+ def informative_1d_indicators (self , metadata : dict , info_timeframe ) -> DataFrame :
716+ tik = time .perf_counter ()
717+ assert self .dp , "DataProvider is required for multiple timeframes."
718+ # Get the informative pair
719+ informative_1d = self .dp .get_pair_dataframe (pair = metadata ['pair' ], timeframe = info_timeframe )
720+
721+ # Indicators
722+ # -----------------------------------------------------------------------------------------
723+ informative_1d ['rsi_14' ] = ta .RSI (informative_1d , timeperiod = 14 )
724+
725+ # Performance logging
726+ # -----------------------------------------------------------------------------------------
727+ tok = time .perf_counter ()
728+ log .debug (f"[{ metadata ['pair' ]} ] informative_1d_indicators took: { tok - tik :0.4f} seconds." )
729+
730+ return informative_1d
731+
732+ def informative_4h_indicators (self , metadata : dict , info_timeframe ) -> DataFrame :
733+ tik = time .perf_counter ()
734+ assert self .dp , "DataProvider is required for multiple timeframes."
735+ # Get the informative pair
736+ informative_4h = self .dp .get_pair_dataframe (pair = metadata ['pair' ], timeframe = info_timeframe )
737+
738+ # Indicators
739+ # -----------------------------------------------------------------------------------------
740+ # RSI
741+ informative_4h ['rsi_14' ] = ta .RSI (informative_4h , timeperiod = 14 , fillna = True )
742+
743+ # SMA
744+ informative_4h ['sma_200' ] = ta .SMA (informative_4h , timeperiod = 200 , fillna = True )
745+
746+ # Williams %R
747+ informative_4h ['r_14' ] = williams_r (informative_4h , period = 14 )
748+ informative_4h ['r_480' ] = williams_r (informative_4h , period = 480 )
749+
750+ # Performance logging
751+ # -----------------------------------------------------------------------------------------
752+ tok = time .perf_counter ()
753+ log .debug (f"[{ metadata ['pair' ]} ] informative_1d_indicators took: { tok - tik :0.4f} seconds." )
754+
755+ return informative_4h
756+
757+ def informative_1h_indicators (self , metadata : dict , info_timeframe ) -> DataFrame :
758+ tik = time .perf_counter ()
759+ assert self .dp , "DataProvider is required for multiple timeframes."
760+ # Get the informative pair
761+ informative_1h = self .dp .get_pair_dataframe (pair = metadata ['pair' ], timeframe = info_timeframe )
762+
763+ # Indicators
764+ # -----------------------------------------------------------------------------------------
765+ # RSI
766+ informative_1h ['rsi_14' ] = ta .RSI (informative_1h , timeperiod = 14 )
767+
768+ # SMA
769+ informative_1h ['sma_50' ] = ta .SMA (informative_1h , timeperiod = 50 )
770+ informative_1h ['sma_100' ] = ta .SMA (informative_1h , timeperiod = 100 )
771+ informative_1h ['sma_200' ] = ta .SMA (informative_1h , timeperiod = 200 )
772+
773+ # BB
774+ bollinger = qtpylib .bollinger_bands (qtpylib .typical_price (informative_1h ), window = 20 , stds = 2 )
775+ informative_1h ['bb20_2_low' ] = bollinger ['lower' ]
776+ informative_1h ['bb20_2_mid' ] = bollinger ['mid' ]
777+ informative_1h ['bb20_2_upp' ] = bollinger ['upper' ]
778+
779+ # Williams %R
780+ informative_1h ['r_14' ] = williams_r (informative_1h , period = 14 )
781+ informative_1h ['r_480' ] = williams_r (informative_1h , period = 480 )
782+
783+ # Performance logging
784+ # -----------------------------------------------------------------------------------------
785+ tok = time .perf_counter ()
786+ log .debug (f"[{ metadata ['pair' ]} ] informative_1h_indicators took: { tok - tik :0.4f} seconds." )
787+
788+ return informative_1h
789+
790+ def informative_15m_indicators (self , metadata : dict , info_timeframe ) -> DataFrame :
791+ tik = time .perf_counter ()
792+ assert self .dp , "DataProvider is required for multiple timeframes."
793+
794+ # Get the informative pair
795+ informative_15m = self .dp .get_pair_dataframe (pair = metadata ['pair' ], timeframe = info_timeframe )
796+
797+ # Indicators
798+ # -----------------------------------------------------------------------------------------
799+ informative_15m ['rsi_14' ] = ta .RSI (informative_15m , timeperiod = 14 )
800+
801+ # Performance logging
802+ # -----------------------------------------------------------------------------------------
803+ tok = time .perf_counter ()
804+ log .debug (f"[{ metadata ['pair' ]} ] informative_15m_indicators took: { tok - tik :0.4f} seconds." )
805+
806+ return informative_15m
807+
808+ # Coin Pair Base Timeframe Indicators
809+ # ---------------------------------------------------------------------------------------------
810+ def base_tf_5m_indicators (self , metadata : dict , dataframe : DataFrame ) -> DataFrame :
811+ tik = time .perf_counter ()
812+
813+ # Indicators
814+ # -----------------------------------------------------------------------------------------
815+ # RSI
816+ dataframe ['rsi_14' ] = ta .RSI (dataframe , timeperiod = 14 )
817+
818+ # EMA
819+ dataframe ['ema_12' ] = ta .EMA (dataframe , timeperiod = 12 )
820+ dataframe ['ema_26' ] = ta .EMA (dataframe , timeperiod = 26 )
821+ dataframe ['ema_50' ] = ta .EMA (dataframe , timeperiod = 50 )
822+ dataframe ['ema_200' ] = ta .EMA (dataframe , timeperiod = 200 )
823+
824+ # SMA
825+ dataframe ['sma_50' ] = ta .SMA (dataframe , timeperiod = 50 )
826+ dataframe ['sma_200' ] = ta .SMA (dataframe , timeperiod = 200 )
827+
828+ # BB 20 - STD2
829+ bb_20_std2 = qtpylib .bollinger_bands (qtpylib .typical_price (dataframe ), window = 20 , stds = 2 )
830+ dataframe ['bb20_2_low' ] = bb_20_std2 ['lower' ]
831+ dataframe ['bb20_2_mid' ] = bb_20_std2 ['mid' ]
832+ dataframe ['bb20_2_upp' ] = bb_20_std2 ['upper' ]
833+
834+ # Williams %R
835+ dataframe ['r_14' ] = williams_r (dataframe , period = 14 )
836+ dataframe ['r_480' ] = williams_r (dataframe , period = 480 )
837+
838+ # For sell checks
839+ dataframe ['crossed_below_ema_12_26' ] = qtpylib .crossed_below (dataframe ['ema_12' ], dataframe ['ema_26' ])
840+
841+ # Global protections
842+ # -----------------------------------------------------------------------------------------
843+ if not self .config ['runmode' ].value in ('live' , 'dry_run' ):
844+ # Backtest age filter
845+ dataframe ['bt_agefilter_ok' ] = False
846+ dataframe .loc [dataframe .index > (12 * 24 * self .bt_min_age_days ),'bt_agefilter_ok' ] = True
847+ else :
848+ # Exchange downtime protection
849+ dataframe ['live_data_ok' ] = (dataframe ['volume' ].rolling (window = 72 , min_periods = 72 ).min () > 0 )
850+
851+ # Performance logging
852+ # -----------------------------------------------------------------------------------------
853+ tok = time .perf_counter ()
854+ log .debug (f"[{ metadata ['pair' ]} ] base_tf_5m_indicators took: { tok - tik :0.4f} seconds." )
855+
856+ return dataframe
857+
858+ # Coin Pair Indicator Switch Case
859+ # ---------------------------------------------------------------------------------------------
860+ def info_switcher (self , metadata : dict , info_timeframe ) -> DataFrame :
861+ if info_timeframe == '1d' :
862+ return self .informative_1d_indicators (metadata , info_timeframe )
863+ elif info_timeframe == '4h' :
864+ return self .informative_4h_indicators (metadata , info_timeframe )
865+ elif info_timeframe == '1h' :
866+ return self .informative_1h_indicators (metadata , info_timeframe )
867+ elif info_timeframe == '15m' :
868+ return self .informative_15m_indicators (metadata , info_timeframe )
869+ else :
870+ raise RuntimeError (f"{ info_timeframe } not supported as informative timeframe for BTC pair." )
871+
872+ # BTC 1D Indicators
873+ # ---------------------------------------------------------------------------------------------
874+ def btc_info_1d_indicators (self , btc_info_pair , btc_info_timeframe , metadata : dict ) -> DataFrame :
875+ tik = time .perf_counter ()
876+ btc_info_1d = self .dp .get_pair_dataframe (btc_info_pair , btc_info_timeframe )
877+ # Indicators
878+ # -----------------------------------------------------------------------------------------
879+ btc_info_1d ['rsi_14' ] = ta .RSI (btc_info_1d , timeperiod = 14 )
880+ #btc_info_1d['pivot'], btc_info_1d['res1'], btc_info_1d['res2'], btc_info_1d['res3'], btc_info_1d['sup1'], btc_info_1d['sup2'], btc_info_1d['sup3'] = pivot_points(btc_info_1d, mode='fibonacci')
881+
882+ # Add prefix
883+ # -----------------------------------------------------------------------------------------
884+ ignore_columns = ['date' ]
885+ btc_info_1d .rename (columns = lambda s : f"btc_{ s } " if s not in ignore_columns else s , inplace = True )
886+
887+ tok = time .perf_counter ()
888+ log .debug (f"[{ metadata ['pair' ]} ] btc_info_1d_indicators took: { tok - tik :0.4f} seconds." )
889+
890+ return btc_info_1d
891+
892+ # BTC 4h Indicators
893+ # ---------------------------------------------------------------------------------------------
894+ def btc_info_4h_indicators (self , btc_info_pair , btc_info_timeframe , metadata : dict ) -> DataFrame :
895+ tik = time .perf_counter ()
896+ btc_info_4h = self .dp .get_pair_dataframe (btc_info_pair , btc_info_timeframe )
897+ # Indicators
898+ # -----------------------------------------------------------------------------------------
899+ # RSI
900+ btc_info_4h ['rsi_14' ] = ta .RSI (btc_info_4h , timeperiod = 14 )
901+
902+ # SMA
903+ btc_info_4h ['sma_200' ] = ta .SMA (btc_info_4h , timeperiod = 200 )
904+
905+ # Add prefix
906+ # -----------------------------------------------------------------------------------------
907+ ignore_columns = ['date' ]
908+ btc_info_4h .rename (columns = lambda s : f"btc_{ s } " if s not in ignore_columns else s , inplace = True )
909+
910+ tok = time .perf_counter ()
911+ log .debug (f"[{ metadata ['pair' ]} ] btc_info_4h_indicators took: { tok - tik :0.4f} seconds." )
912+
913+ return btc_info_4h
914+
915+ # BTC 1h Indicators
916+ # ---------------------------------------------------------------------------------------------
917+ def btc_info_1h_indicators (self , btc_info_pair , btc_info_timeframe , metadata : dict ) -> DataFrame :
918+ tik = time .perf_counter ()
919+ btc_info_1h = self .dp .get_pair_dataframe (btc_info_pair , btc_info_timeframe )
920+ # Indicators
921+ # -----------------------------------------------------------------------------------------
922+ btc_info_1h ['rsi_14' ] = ta .RSI (btc_info_1h , timeperiod = 14 )
923+ btc_info_1h ['not_downtrend' ] = ((btc_info_1h ['close' ] > btc_info_1h ['close' ].shift (2 )) | (btc_info_1h ['rsi_14' ] > 50 ))
924+
925+ # Add prefix
926+ # -----------------------------------------------------------------------------------------
927+ ignore_columns = ['date' ]
928+ btc_info_1h .rename (columns = lambda s : f"btc_{ s } " if s not in ignore_columns else s , inplace = True )
929+
930+ tok = time .perf_counter ()
931+ log .debug (f"[{ metadata ['pair' ]} ] btc_info_1h_indicators took: { tok - tik :0.4f} seconds." )
932+
933+ return btc_info_1h
934+
935+ # BTC 15m Indicators
936+ # ---------------------------------------------------------------------------------------------
937+ def btc_info_15m_indicators (self , btc_info_pair , btc_info_timeframe , metadata : dict ) -> DataFrame :
938+ tik = time .perf_counter ()
939+ btc_info_15m = self .dp .get_pair_dataframe (btc_info_pair , btc_info_timeframe )
940+ # Indicators
941+ # -----------------------------------------------------------------------------------------
942+ btc_info_15m ['rsi_14' ] = ta .RSI (btc_info_15m , timeperiod = 14 )
943+
944+ # Add prefix
945+ # -----------------------------------------------------------------------------------------
946+ ignore_columns = ['date' ]
947+ btc_info_15m .rename (columns = lambda s : f"btc_{ s } " if s not in ignore_columns else s , inplace = True )
948+
949+ tok = time .perf_counter ()
950+ log .debug (f"[{ metadata ['pair' ]} ] btc_info_15m_indicators took: { tok - tik :0.4f} seconds." )
951+
952+ return btc_info_15m
953+
954+ # BTC 5m Indicators
955+ # ---------------------------------------------------------------------------------------------
956+ def btc_info_5m_indicators (self , btc_info_pair , btc_info_timeframe , metadata : dict ) -> DataFrame :
957+ tik = time .perf_counter ()
958+ btc_info_5m = self .dp .get_pair_dataframe (btc_info_pair , btc_info_timeframe )
959+ # Indicators
960+ # -----------------------------------------------------------------------------------------
961+ btc_info_5m ['rsi_14' ] = ta .RSI (btc_info_5m , timeperiod = 14 )
962+
963+ # Add prefix
964+ # -----------------------------------------------------------------------------------------
965+ ignore_columns = ['date' ]
966+ btc_info_5m .rename (columns = lambda s : f"btc_{ s } " if s not in ignore_columns else s , inplace = True )
967+
968+ tok = time .perf_counter ()
969+ log .debug (f"[{ metadata ['pair' ]} ] btc_info_5m_indicators took: { tok - tik :0.4f} seconds." )
970+
971+ return btc_info_5m
972+
973+ # BTC Indicator Switch Case
974+ # ---------------------------------------------------------------------------------------------
975+ def btc_info_switcher (self , btc_info_pair , btc_info_timeframe , metadata : dict ) -> DataFrame :
976+ if btc_info_timeframe == '1d' :
977+ return self .btc_info_1d_indicators (btc_info_pair , btc_info_timeframe , metadata )
978+ elif btc_info_timeframe == '4h' :
979+ return self .btc_info_4h_indicators (btc_info_pair , btc_info_timeframe , metadata )
980+ elif btc_info_timeframe == '1h' :
981+ return self .btc_info_1h_indicators (btc_info_pair , btc_info_timeframe , metadata )
982+ elif btc_info_timeframe == '15m' :
983+ return self .btc_info_15m_indicators (btc_info_pair , btc_info_timeframe , metadata )
984+ elif btc_info_timeframe == '5m' :
985+ return self .btc_info_5m_indicators (btc_info_pair , btc_info_timeframe , metadata )
986+ else :
987+ raise RuntimeError (f"{ btc_info_timeframe } not supported as informative timeframe for BTC pair." )
988+
989+ def populate_indicators (self , dataframe : DataFrame , metadata : dict ) -> DataFrame :
990+ tik = time .perf_counter ()
991+ '''
992+ --> BTC informative indicators
993+ ___________________________________________________________________________________________
994+ '''
995+ if self .config ['stake_currency' ] in ['USDT' ,'BUSD' ,'USDC' ,'DAI' ,'TUSD' ,'PAX' ,'USD' ,'EUR' ,'GBP' ]:
996+ btc_info_pair = f"BTC/{ self .config ['stake_currency' ]} "
997+ else :
998+ btc_info_pair = "BTC/USDT"
999+
1000+ for btc_info_timeframe in self .btc_info_timeframes :
1001+ btc_informative = self .btc_info_switcher (btc_info_pair , btc_info_timeframe , metadata )
1002+ dataframe = merge_informative_pair (dataframe , btc_informative , self .timeframe , btc_info_timeframe , ffill = True )
1003+ # Customize what we drop - in case we need to maintain some BTC informative ohlcv data
1004+ # Default drop all
1005+ drop_columns = {
1006+ '1d' : [f"btc_{ s } _{ btc_info_timeframe } " for s in ['date' , 'open' , 'high' , 'low' , 'close' , 'volume' ]],
1007+ '4h' : [f"btc_{ s } _{ btc_info_timeframe } " for s in ['date' , 'open' , 'high' , 'low' , 'close' , 'volume' ]],
1008+ '1h' : [f"btc_{ s } _{ btc_info_timeframe } " for s in ['date' , 'open' , 'high' , 'low' , 'close' , 'volume' ]],
1009+ '15m' : [f"btc_{ s } _{ btc_info_timeframe } " for s in ['date' , 'open' , 'high' , 'low' , 'close' , 'volume' ]],
1010+ '5m' : [f"btc_{ s } _{ btc_info_timeframe } " for s in ['date' , 'open' , 'high' , 'low' , 'close' , 'volume' ]],
1011+ }.get (btc_info_timeframe ,[f"{ s } _{ btc_info_timeframe } " for s in ['date' , 'open' , 'high' , 'low' , 'close' , 'volume' ]])
1012+ drop_columns .append (f"date_{ btc_info_timeframe } " )
1013+ dataframe .drop (columns = dataframe .columns .intersection (drop_columns ), inplace = True )
1014+
1015+ '''
1016+ --> Indicators on informative timeframes
1017+ ___________________________________________________________________________________________
1018+ '''
1019+ for info_timeframe in self .info_timeframes :
1020+ info_indicators = self .info_switcher (metadata , info_timeframe )
1021+ dataframe = merge_informative_pair (dataframe , info_indicators , self .timeframe , info_timeframe , ffill = True )
1022+ # Customize what we drop - in case we need to maintain some informative timeframe ohlcv data
1023+ # Default drop all except base timeframe ohlcv data
1024+ drop_columns = {
1025+ '1d' : [f"{ s } _{ info_timeframe } " for s in ['date' , 'open' , 'high' , 'low' , 'close' , 'volume' ]],
1026+ '4h' : [f"{ s } _{ info_timeframe } " for s in ['date' , 'open' , 'high' , 'low' , 'close' , 'volume' ]],
1027+ '1h' : [f"{ s } _{ info_timeframe } " for s in ['date' , 'open' , 'high' , 'low' , 'close' , 'volume' ]],
1028+ '15m' : [f"{ s } _{ info_timeframe } " for s in ['date' , 'open' , 'high' , 'low' , 'close' , 'volume' ]]
1029+ }.get (info_timeframe ,[f"{ s } _{ info_timeframe } " for s in ['date' , 'open' , 'high' , 'low' , 'close' , 'volume' ]])
1030+ dataframe .drop (columns = dataframe .columns .intersection (drop_columns ), inplace = True )
1031+
1032+ '''
1033+ --> The indicators for the base timeframe (5m)
1034+ ___________________________________________________________________________________________
1035+ '''
1036+ dataframe = self .base_tf_5m_indicators (metadata , dataframe )
1037+
1038+ tok = time .perf_counter ()
1039+ log .debug (f"[{ metadata ['pair' ]} ] Populate indicators took a total of: { tok - tik :0.4f} seconds." )
1040+
1041+ return dataframe
1042+
1043+ def populate_buy_trend (self , dataframe : DataFrame , metadata : dict ) -> DataFrame :
1044+ conditions = []
1045+ dataframe .loc [:, 'buy_tag' ] = ''
1046+
1047+ for buy_enable in self .buy_params :
1048+ index = int (buy_enable .split ('_' )[2 ])
1049+ item_buy_protection_list = [True ]
1050+ if self .buy_params [f'{ buy_enable } ' ]:
1051+
1052+ # Buy conditions
1053+ # -----------------------------------------------------------------------------------------
1054+ item_buy_logic = []
1055+ item_buy_logic .append (reduce (lambda x , y : x & y , item_buy_protection_list ))
1056+
1057+ # Condition #1 - Long mode bull. Uptrend.
6891058 if index == 1 :
6901059 # Protections
1060+ item_buy_logic .append (dataframe ['sma_50' ] > dataframe ['sma_200' ])
1061+ item_buy_logic .append (dataframe ['sma_50_1h' ] > dataframe ['sma_200_1h' ])
6911062
6921063 # Logic
6931064 item_buy_logic .append (dataframe ['rsi_14' ] < 30.0 )
0 commit comments