# 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