# GodStra_v2_E_Stronger Strategy
# Author: @Weather
# GitHub: https://github.com/ReWeatherPort
# Enhanced version of GodStra_v2_E with further optimizations to improve trading performance
from freqtrade.strategy import IStrategy, CategoricalParameter, DecimalParameter
from pandas import DataFrame
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
from functools import reduce
import numpy as np
from datetime import datetime, timedelta
import pandas as pd
import logging
logger = logging.getLogger(__name__)
# --------------------------------
# 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',
}
}
# Combine all genes into a single set
god_genes = set()
for gene_category in all_god_genes.values():
god_genes |= gene_category
# Define time periods and operators for conditions
timeperiods = [5, 6, 12, 15, 20, 25, 30, 35]
operators = [
"D", # Disabled gene
">", # Indicator > cross indicator
"<", # Indicator < cross indicator
"=", # Indicator ≈ cross indicator
"C", # Indicator crossed cross indicator
"CA", # Indicator crossed above cross indicator
"CB", # Indicator crossed below cross indicator
">R", # Indicator > real number
"=R", # Indicator ≈ real number
"R", # (Indicator / cross indicator) > real number
"/=R", # (Indicator / cross indicator) ≈ real number
"/ 10) # Basic volume filter
# Calculate indicators
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)
# Define conditions based on the operator
if 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[indicator_trend_sma])
) &
(np.isclose(dataframe[indicator], dataframe[indicator_trend_sma], atol=0.01))
)
return condition, dataframe
class GodStra_v2_EAI(IStrategy):
"""
GodStra_v2_E_Stronger Strategy with further enhancements to improve trading performance.
"""
INTERFACE_VERSION = 3
# ROI table optimized for higher frequency
minimal_roi = {
"0": 0.293,
"30": 0.113,
"76": 0.033,
"413": 0
}
# Stoploss set to -12%
stoploss = -0.12
# Shorter timeframe for increased trading frequency
timeframe = '15m' # You can change to '15m' for even higher frequency
# Maximum open trades
max_open_trades = 5 # Limit the number of simultaneous open trades
# Cooldown period in minutes
cooldown_period = 30 # Adjusted to 30 minutes for better trading frequency
# Trend Filter Parameters
trend_indicator_short = 'SMA-50' # Short-term SMA for trend confirmation
trend_indicator_long = 'SMA-200' # Long-term SMA for trend confirmation
trend_operator = ">" # Price > Both SMAs indicates strong uptrend
# Buy Hyperoptable Parameters/Spaces (increased to two conditions)
buy_crossed_indicator0 = CategoricalParameter(
god_genes_with_timeperiod, default="EMA-20", space='buy') # Changed default for better performance
buy_crossed_indicator1 = CategoricalParameter(
god_genes_with_timeperiod, default="RSI-14", space='buy') # Added RSI as another indicator
buy_indicator0 = CategoricalParameter(
god_genes_with_timeperiod, default="MACD-0-12", space='buy') # Changed to MACD component
buy_indicator1 = CategoricalParameter(
god_genes_with_timeperiod, default="WILLR-14", space='buy') # Changed default
buy_operator0 = CategoricalParameter(operators, default="C", space='buy') # Changed operator for better signals
buy_operator1 = CategoricalParameter(operators, default=" DataFrame:
"""
Populate indicators. This strategy calculates indicators dynamically in the entry and exit methods
to enhance performance and flexibility.
"""
# Calculate trend indicators
dataframe[self.trend_indicator_short] = gene_calculator(dataframe, self.trend_indicator_short)
dataframe[self.trend_indicator_long] = gene_calculator(dataframe, self.trend_indicator_long)
# Calculate additional indicators for buy and sell conditions
# Calculate RSI and MACD with unique column names to avoid conflicts
dataframe['RSI_Custom'] = ta.RSI(dataframe, timeperiod=14)
macd = ta.MACD(dataframe, fastperiod=12, slowperiod=26, signalperiod=9)
dataframe['MACD_Custom'] = macd['macd']
dataframe['MACD_SIGNAL_Custom'] = macd['macdsignal']
dataframe['MACD_HIST_Custom'] = macd['macdhist']
# Log the data types of the newly calculated indicators for debugging
logger.debug(f"RSI_Custom dtype: {dataframe['RSI_Custom'].dtype}")
logger.debug(f"MACD_Custom dtype: {dataframe['MACD_Custom'].dtype}")
logger.debug(f"MACD_SIGNAL_Custom dtype: {dataframe['MACD_SIGNAL_Custom'].dtype}")
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Define buy conditions with additional filters.
"""
conditions = []
# Ensure the dataframe index is a DatetimeIndex
if not isinstance(dataframe.index, pd.DatetimeIndex):
dataframe.index = pd.to_datetime(dataframe.index)
logger.debug("Converted dataframe index to DatetimeIndex.")
# Check trend: Only buy if price is above both SMA-50 and SMA-200
if self.trend_operator == ">":
trend_condition = (
(dataframe['close'] > dataframe[self.trend_indicator_short]) &
(dataframe['close'] > dataframe[self.trend_indicator_long]) &
(dataframe['RSI_Custom'] < 70) # Avoid buying when RSI is high to prevent buying at peaks
)
elif self.trend_operator == "<":
trend_condition = (
(dataframe['close'] < dataframe[self.trend_indicator_short]) &
(dataframe['close'] < dataframe[self.trend_indicator_long])
)
else:
trend_condition = True # Default to True if operator not matched
conditions.append(trend_condition)
# Cooldown filter: Only buy if cooldown period has passed
if self.last_trade_time is not None:
current_time = dataframe.index[-1]
try:
time_since_last_trade = (current_time - self.last_trade_time).total_seconds() / 60
logger.debug(f"Time since last trade: {time_since_last_trade} minutes.")
if time_since_last_trade < self.cooldown_period:
logger.debug("In cooldown period. Skipping buy conditions.")
return dataframe # Skip buy conditions if in cooldown
except Exception as e:
logger.error(f"Error calculating cooldown period: {e}")
# Proceed without applying cooldown if error occurs
# 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
)
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
)
conditions.append(condition)
# Add Price Drop Confirmation to avoid buying before the drop has finished
dataframe['Price_Drop'] = (dataframe['close'].rolling(window=20).max() - dataframe['close']) / dataframe['close'].rolling(window=20).max()
drop_condition = dataframe['Price_Drop'] > 0.05 # Ensure at least a 5% drop has occurred
conditions.append(drop_condition)
# Combine all buy conditions
if conditions:
final_condition = reduce(lambda x, y: x & y, conditions)
dataframe.loc[
final_condition,
'enter_long'] = 1
# Update last_trade_time if a buy signal is generated
if final_condition.iloc[-1]:
self.last_trade_time = dataframe.index[-1]
logger.debug(f"Buy signal triggered at {self.last_trade_time}.")
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Define sell conditions.
"""
conditions = []
# First Sell Condition
sell_indicator = self.sell_indicator0.value
sell_crossed_indicator = self.sell_crossed_indicator0.value
sell_operator = self.sell_operator0.value
sell_real_num = self.sell_real_num0.value
condition, dataframe = condition_generator(
dataframe,
sell_operator,
sell_indicator,
sell_crossed_indicator,
sell_real_num
)
conditions.append(condition)
# Second Sell Condition
sell_indicator = self.sell_indicator1.value
sell_crossed_indicator = self.sell_crossed_indicator1.value
sell_operator = self.sell_operator1.value
sell_real_num = self.sell_real_num1.value
condition, dataframe = condition_generator(
dataframe,
sell_operator,
sell_indicator,
sell_crossed_indicator,
sell_real_num
)
conditions.append(condition)
# Momentum Confirmation: Avoid selling during momentum reversals
# Ensure that MACD_Custom and MACD_SIGNAL_Custom are numeric
dataframe['MACD_Custom'] = pd.to_numeric(dataframe['MACD_Custom'], errors='coerce')
dataframe['MACD_SIGNAL_Custom'] = pd.to_numeric(dataframe['MACD_SIGNAL_Custom'], errors='coerce')
dataframe['MACD_Diff'] = dataframe['MACD_Custom'] - dataframe['MACD_SIGNAL_Custom']
momentum_condition = dataframe['MACD_Diff'] < 0 # MACD line crosses below the signal line
conditions.append(momentum_condition)
# Combine all sell conditions
if conditions:
final_condition = reduce(lambda x, y: x & y, conditions)
dataframe.loc[
final_condition,
'exit_long'] = 1
return dataframe