# GodStra_v2 Strategy
# Author: @Weather
# GitHub: https://github.com/ReWeatherPort
# Command to optimize:
# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStra_v2
# freqtrade backtesting --config user_data/config.json --strategy GodStra_v2
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
import logging
import pandas as pd
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().union(*all_god_genes.values())
# 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_v3(IStrategy):
"""
GodStra_v3 Strategy optimized for higher trading frequency and broader token compatibility.
"""
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
# Buy Hyperoptable Parameters/Spaces (increased to two conditions)
buy_crossed_indicator0 = CategoricalParameter(
god_genes_with_timeperiod, default="EMA-20", space='buy')
buy_crossed_indicator1 = CategoricalParameter(
god_genes_with_timeperiod, default="RSI-14", 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="/ DataFrame:
"""
Populate indicators. This strategy calculates indicators dynamically in the entry and exit methods
to enhance performance and flexibility.
"""
# Pre-calculate commonly used indicators to avoid redundant calculations
try:
# Example: Pre-calculate SMA-30 and RSI-14 if they are used frequently
indicators_to_precalculate = set([
"SMA-30", "RSI-14", "EMA-20", "WILLR-50", "CDLSHOOTINGSTAR-150",
"MAMA-1-100", "CDLUPSIDEGAP2CROWS-5", "CDLHARAMICROSS-150"
])
for indicator in indicators_to_precalculate:
if indicator not in dataframe.columns:
dataframe[indicator] = gene_calculator(dataframe, indicator)
logger.info("Pre-calculated selected indicators for optimization.")
except Exception as e:
logger.error(f"Error in populate_indicators: {e}")
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Define buy conditions.
"""
conditions = []
# 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)
# Combine all buy conditions
if conditions:
try:
final_condition = reduce(lambda x, y: x & y, conditions)
dataframe.loc[
final_condition,
'enter_long'] = 1
logger.debug(f"Buy signal generated for {metadata['pair']} at {dataframe.index[-1]}")
except Exception as e:
logger.error(f"Error combining buy conditions: {e}")
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)
# Combine all sell conditions
if conditions:
try:
final_condition = reduce(lambda x, y: x & y, conditions)
dataframe.loc[
final_condition,
'exit_long'] = 1
logger.debug(f"Sell signal generated for {metadata['pair']} at {dataframe.index[-1]}")
except Exception as e:
logger.error(f"Error combining sell conditions: {e}")
return dataframe