# GodStra_v4 Strategy # Author: @Weather # GitHub: https://github.com/ReWeatherPort # Command to dry run: # freqtrade trade --config user_data/config.json --strategy GodStra_v4 --dry-run from freqtrade.strategy import IStrategy, CategoricalParameter, DecimalParameter from datetime import datetime, timedelta from pandas import DataFrame import talib.abstract as ta import freqtrade.vendor.qtpylib.indicators as qtpylib from functools import reduce import numpy as np import pandas as pd # Define all possible indicators categorized by their types all_god_genes = {'Overlap Studies': {'BBANDS-0', 'BBANDS-1', 'BBANDS-2', 'DEMA', 'EMA', 'HT_TRENDLINE', 'KAMA', 'MA', 'MAMA-0', 'MAMA-1', 'MIDPOINT', 'MIDPRICE', 'SAR', 'SAREXT', 'SMA', 'T3', 'TEMA', 'TRIMA', 'WMA'}, 'Momentum Indicators': {'ADX', 'ADXR', 'APO', 'AROON-0', 'AROON-1', 'AROONOSC', 'BOP', 'CCI', 'CMO', 'DX', 'MACD-0', 'MACD-1', 'MACD-2', 'MACDEXT-0', 'MACDEXT-1', 'MACDEXT-2', 'MACDFIX-0', 'MACDFIX-1', 'MACDFIX-2', 'MFI', 'MINUS_DI', 'MINUS_DM', 'MOM', 'PLUS_DI', 'PLUS_DM', 'PPO', 'ROC', 'ROCP', 'ROCR', 'ROCR100', 'RSI', 'STOCH-0', 'STOCH-1', 'STOCHF-0', 'STOCHF-1', 'STOCHRSI-0', 'STOCHRSI-1', 'TRIX', 'ULTOSC', 'WILLR'}, 'Volume Indicators': {'AD', 'ADOSC', 'OBV'}, 'Volatility Indicators': {'ATR', 'NATR', 'TRANGE'}, 'Price Transform': {'AVGPRICE', 'MEDPRICE', 'TYPPRICE', 'WCLPRICE'}, 'Cycle Indicators': {'HT_DCPERIOD', 'HT_DCPHASE', 'HT_PHASOR-0', 'HT_PHASOR-1', 'HT_SINE-0', 'HT_SINE-1', 'HT_TRENDMODE'}, 'Pattern Recognition': {'CDL2CROWS', 'CDL3BLACKCROWS', 'CDL3INSIDE', 'CDL3LINESTRIKE', 'CDL3OUTSIDE', 'CDL3STARSINSOUTH', 'CDL3WHITESOLDIERS', 'CDLABANDONEDBABY', 'CDLADVANCEBLOCK', 'CDLBELTHOLD', 'CDLBREAKAWAY', 'CDLCLOSINGMARUBOZU', 'CDLCONCEALBABYSWALL', 'CDLCOUNTERATTACK', 'CDLDARKCLOUDCOVER', 'CDLDOJI', 'CDLDOJISTAR', 'CDLDRAGONFLYDOJI', 'CDLENGULFING', 'CDLEVENINGDOJISTAR', 'CDLEVENINGSTAR', 'CDLGAPSIDESIDEWHITE', 'CDLGRAVESTONEDOJI', 'CDLHAMMER', 'CDLHANGINGMAN', 'CDLHARAMI', 'CDLHARAMICROSS', 'CDLHIGHWAVE', 'CDLHIKKAKE', 'CDLHIKKAKEMOD', 'CDLHOMINGPIGEON', 'CDLIDENTICAL3CROWS', 'CDLINNECK', 'CDLINVERTEDHAMMER', 'CDLKICKING', 'CDLKICKINGBYLENGTH', 'CDLLADDERBOTTOM', 'CDLLONGLEGGEDDOJI', 'CDLLONGLINE', 'CDLMARUBOZU', 'CDLMATCHINGLOW', 'CDLMATHOLD', 'CDLMORNINGDOJISTAR', 'CDLMORNINGSTAR', 'CDLONNECK', 'CDLPIERCING', 'CDLRICKSHAWMAN', 'CDLRISEFALL3METHODS', 'CDLSEPARATINGLINES', 'CDLSHOOTINGSTAR', 'CDLSHORTLINE', 'CDLSPINNINGTOP', 'CDLSTALLEDPATTERN', 'CDLSTICKSANDWICH', 'CDLTAKURI', 'CDLTASUKIGAP', 'CDLTHRUSTING', 'CDLTRISTAR', 'CDLUNIQUE3RIVER', 'CDLUPSIDEGAP2CROWS', 'CDLXSIDEGAP3METHODS'}, 'Statistic Functions': {'BETA', 'CORREL', 'LINEARREG', 'LINEARREG_ANGLE', 'LINEARREG_INTERCEPT', 'LINEARREG_SLOPE', 'STDDEV', 'TSF', 'VAR'}} god_genes = set() for gene_category in all_god_genes.values(): god_genes |= gene_category timeperiods = [5, 6, 12, 15, 20, 25, 30, 35, 50] operators = ['D', '>', '<', '=', 'C', 'CA', 'CB', '>R', '=R', 'R', '/=R', '/ 10 dataframe[indicator] = gene_calculator(dataframe, indicator) dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) indicator_trend_sma = f'{indicator}-SMA-{TREND_CHECK_CANDLES}' if operator in ['UT', 'DT', 'OT', 'CUT', 'CDT', 'COT']: dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) if operator == 'D': condition = pd.Series([False] * len(dataframe), index=dataframe.index) elif operator == '>': condition &= dataframe[indicator] > dataframe[crossed_indicator] elif operator == '=': condition &= np.isclose(dataframe[indicator], dataframe[crossed_indicator], atol=0.01) elif operator == '<': condition &= dataframe[indicator] < dataframe[crossed_indicator] elif operator == 'C': condition &= qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator]) | qtpylib.crossed_above(dataframe[indicator], dataframe[crossed_indicator]) elif operator == 'CA': condition &= qtpylib.crossed_above(dataframe[indicator], dataframe[crossed_indicator]) elif operator == 'CB': condition &= qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator]) elif operator == '>R': condition &= dataframe[indicator] > real_num elif operator == '=R': condition &= np.isclose(dataframe[indicator], real_num, atol=0.01) elif operator == 'R': condition &= dataframe[indicator].div(dataframe[crossed_indicator].replace(0, np.nan)) > real_num elif operator == '/=R': condition &= np.isclose(dataframe[indicator].div(dataframe[crossed_indicator].replace(0, np.nan)), real_num, atol=0.01) elif operator == '/ dataframe[indicator_trend_sma] elif operator == 'DT': condition &= dataframe[indicator] < dataframe[indicator_trend_sma] elif operator == 'OT': condition &= np.isclose(dataframe[indicator], dataframe[indicator_trend_sma], atol=0.01) elif operator == 'CUT': condition &= qtpylib.crossed_above(dataframe[indicator], dataframe[indicator_trend_sma]) & (dataframe[indicator] > dataframe[indicator_trend_sma]) elif operator == 'CDT': condition &= qtpylib.crossed_below(dataframe[indicator], dataframe[indicator_trend_sma]) & (dataframe[indicator] < dataframe[indicator_trend_sma]) elif operator == 'COT': condition &= (qtpylib.crossed_below(dataframe[indicator], dataframe[indicator_trend_sma]) | qtpylib.crossed_above(dataframe[indicator], dataframe[crossed_indicator])) & np.isclose(dataframe[indicator], dataframe[indicator_trend_sma], atol=0.01) return (condition, dataframe) class GodStra_v4(IStrategy): """ GodStra_v4 Strategy, optimized for higher profit and shorter holding time on 15m timeframe. Added ADX and Bollinger Bands for trend and momentum confirmation. Fixed TypeError in BBANDS by using float for nbdevup and nbdevdn. """ INTERFACE_VERSION = 3 # Adjusted for short-term trades minimal_roi = {'0': 0.1, '30': 0.05, '60': 0} stoploss = -0.08 timeframe = '15m' buy_crossed_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default='ADD-20', space='buy') buy_crossed_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default='ASIN-6', space='buy') buy_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default='SMA-100', space='buy') buy_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default='WILLR-50', space='buy') buy_operator0 = CategoricalParameter(operators, default='/ 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 10.0 def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Populate trend, momentum, and volatility indicators for 15m timeframe. """ # EMA for trend dataframe['ema_12'] = ta.EMA(dataframe, timeperiod=12) dataframe['ema_50'] = ta.EMA(dataframe, timeperiod=50) dataframe['ema_200'] = ta.EMA(dataframe, timeperiod=200) # RSI for momentum dataframe['rsi_14'] = gene_calculator(dataframe, 'RSI-14') # MACD for momentum confirmation macd = ta.MACD(dataframe['close'], fastperiod=12, slowperiod=26, signalperiod=9) dataframe['macd_hist'] = macd[2] # ADX for trend strength dataframe['adx'] = ta.ADX(dataframe['high'], dataframe['low'], dataframe['close'], timeperiod=14) # Bollinger Bands for breakout and exit (fixed nbdevup and nbdevdn to float) bbands = ta.BBANDS(dataframe['close'], timeperiod=20, nbdevup=2.0, nbdevdn=2.0) dataframe['bb_upper'] = bbands[0] dataframe['bb_middle'] = bbands[1] dataframe['bb_lower'] = bbands[2] return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Define buy conditions with trend, momentum, and breakout confirmation for 15m timeframe. """ conditions = [] # Bear market detection: Stop trading if close < EMA-200 or ADX < 25 bear_condition = (dataframe['close'] > dataframe['ema_200']) & (dataframe['adx'] > 25) # Bottom detection: RSI-14 < 0.3 to signal recovery bottom_condition = dataframe['rsi_14'] < 0.3 # Trend and momentum: EMA-12 > EMA-50, RSI-14 > 0.4, MACD Histogram crosses above 0 trend_condition = dataframe['ema_12'] > dataframe['ema_50'] momentum_condition = (dataframe['rsi_14'] > 0.4) & qtpylib.crossed_above(dataframe['macd_hist'], 0) # Breakout: Close > BB_upper breakout_condition = dataframe['close'] > dataframe['bb_upper'] # Entry logic: Either not in bear market OR recovering from bottom with momentum and breakout conditions.append(bear_condition | bottom_condition.shift(1) & trend_condition & momentum_condition & breakout_condition) # First Buy Condition buy_indicator = self.buy_indicator0.value buy_crossed_indicator = self.buy_crossed_indicator0.value buy_operator = self.buy_operator0.value buy_real_num = self.buy_real_num0.value condition, dataframe = condition_generator(dataframe, buy_operator, buy_indicator, buy_crossed_indicator, buy_real_num) print(f'Buy Condition 1: {condition.sum()} signals') conditions.append(condition) # Second Buy Condition buy_indicator = self.buy_indicator1.value buy_crossed_indicator = self.buy_crossed_indicator1.value buy_operator = self.buy_operator1.value buy_real_num = self.buy_real_num1.value condition, dataframe = condition_generator(dataframe, buy_operator, buy_indicator, buy_crossed_indicator, buy_real_num) print(f'Buy Condition 2: {condition.sum()} signals') conditions.append(condition) # Combine all buy conditions if conditions: combined_condition = reduce(lambda x, y: x & y, conditions) dataframe.loc[combined_condition, 'enter_long'] = 1 dataframe.loc[combined_condition, 'enter_tag'] = '' print(f'Enter Long Signals: {combined_condition.sum()}') return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Define sell conditions with faster exits using BB and RSI for 15m timeframe. """ # Exit conditions: (Close < BB_middle AND RSI-14 < 0.5) OR MACD Histogram crosses below 0 bb_condition = dataframe['close'] < dataframe['bb_middle'] rsi_condition = dataframe['rsi_14'] < 0.5 macd_condition = qtpylib.crossed_below(dataframe['macd_hist'], 0) # Combine conditions exit_condition = bb_condition & rsi_condition | macd_condition # Track open positions to avoid excessive exits dataframe['position_open'] = 0 position_open = 0 for i in range(len(dataframe)): if dataframe['enter_long'].iloc[i] == 1: position_open = 1 if position_open == 1 and exit_condition.iloc[i]: dataframe.loc[dataframe.index[i], 'exit_long'] = 1 dataframe.loc[dataframe.index[i], 'exit_reason'] = 'bb_rsi_macd' position_open = 0 dataframe.loc[dataframe.index[i], 'position_open'] = position_open print(f'BB Condition (Close < BB_middle): {bb_condition.sum()} signals') print(f'RSI Condition (< 0.5): {rsi_condition.sum()} signals') print(f'MACD Condition (Cross below 0): {macd_condition.sum()} signals') print(f'Combined Exit Signals: {exit_condition.sum()}') print(f'Actual Exit Long Signals: {dataframe['exit_long'].sum()}') return dataframe