# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement # flake8: noqa: F401 # isort: skip_file # --- Do not remove these imports --- import numpy as np import pandas as pd from datetime import datetime, timedelta, timezone from pandas import DataFrame from typing import Dict, Optional, Union, Tuple, List from freqtrade.optimize.space import Categorical, Dimension, Integer, SKDecimal import logging logger = logging.getLogger(__name__) from freqtrade.strategy import ( IStrategy, Trade, Order, PairLocks, informative, # @informative decorator # Hyperopt Parameters BooleanParameter, CategoricalParameter, DecimalParameter, IntParameter, RealParameter, # timeframe helpers timeframe_to_minutes, timeframe_to_next_date, timeframe_to_prev_date, # Strategy helper functions merge_informative_pair, stoploss_from_absolute, stoploss_from_open, ) # -------------------------------- # Add your lib to import here import talib.abstract as ta import pandas_ta as pta from technical import qtpylib # ========================================== # AGGRESSIVE Monthly Trading Strategy # Based on TemaAdxCmo but optimized for: # - Higher frequency trading # - Monthly profit targets # - Faster entries and exits # - More pairs # ========================================== class TemaAdxCmo_NoStopLoss(IStrategy): # Strategy interface version INTERFACE_VERSION = 3 # AGGRESSIVE: Use 15m timeframe for more trading opportunities timeframe = "15m" # Only long trades (shorts were unprofitable) can_short: bool = False # AGGRESSIVE ROI - Take profits much faster! minimal_roi = { "0": 0.08, # 8% immediate profit target "15": 0.05, # 5% after 15 min "30": 0.03, # 3% after 30 min "60": 0.015, # 1.5% after 1 hour "120": 0.01, # 1% after 2 hours } # NO STOP LOSS - Testing best case scenario stoploss = -0.99 # Essentially no stop loss (99% would never hit) # AGGRESSIVE trailing stop trailing_stop = True trailing_only_offset_is_reached = True trailing_stop_positive = 0.01 # Start trailing at 1% profit trailing_stop_positive_offset = 0.015 # Trail by 1.5% # Run "populate_indicators()" only for new candle process_only_new_candles = True # NO exit signals - they killed profits use_exit_signal = False exit_profit_only = False ignore_roi_if_entry_signal = False # Lower startup candles for faster backtesting startup_candle_count: int = 100 # ============================================ # AGGRESSIVE PARAMETERS - Less restrictive for more trades # ============================================ # TEMA Parameters - More sensitive tema_rolling_window = CategoricalParameter([2, 3, 4], default=3, space="buy") # ADX Parameters - Lower threshold for more entries adx_threshold = IntParameter(15, 30, default=20, space="buy") # ATR Risk Management atr_mult = DecimalParameter(1.0, 2.5, default=1.5, decimals=1, space="buy") risk_ratio = DecimalParameter(2.0, 4.0, default=3.0, decimals=1, space="buy") # Volume Filter - Lower requirement volume_factor = DecimalParameter(1.0, 1.8, default=1.2, decimals=1, space="buy") # RSI Filter - Wider range use_rsi_filter = BooleanParameter(default=True, space="buy") rsi_long_threshold = IntParameter(30, 55, default=45, space="buy") # Trend Filter - Less strict use_ema_filter = BooleanParameter(default=True, space="buy") ema_period = IntParameter(20, 100, default=50, space="buy") # CMO Threshold - Lower for more trades cmo_threshold = IntParameter(0, 20, default=10, space="buy") leverage_level = IntParameter( 1, 5, default=2, space="buy", optimize=True, load=True ) @property def plot_config(self): plot_config = { "main_plot": { "tema8": {"color": "red"}, "tema13": {"color": "blue"}, "tema21": {"color": "green"}, f"ema_{self.ema_period.value}": {"color": "orange"}, }, "subplots": { "ADX": {"adx": {"color": "#f50057", "type": "line"}}, "CMO": {"cmo": {"color": "#2962ff", "type": "line"}}, "RSI": {"rsi": {"color": "#9c27b0", "type": "line"}}, }, } return plot_config def informative_pairs(self): return [] def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Optimized indicators for 15m timeframe """ # TEMA indicators dataframe["tema8"] = ta.TEMA(dataframe, timeperiod=8) dataframe["tema13"] = ta.TEMA(dataframe, timeperiod=13) dataframe["tema21"] = ta.TEMA(dataframe, timeperiod=21) # EMA trend filter for ema_val in self.ema_period.range: dataframe[f"ema_{ema_val}"] = ta.EMA(dataframe, timeperiod=ema_val) # Momentum indicators dataframe["adx"] = ta.ADX(dataframe, timeperiod=14) dataframe["cmo"] = ta.CMO(dataframe, timeperiod=14) dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14) # Volatility dataframe["atr"] = ta.ATR(dataframe, timeperiod=14) # Volume analysis dataframe["volume_mean"] = dataframe["volume"].rolling(window=20).mean() return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ AGGRESSIVE entry logic - more opportunities """ ema_col = f"ema_{self.ema_period.value}" # LONG ENTRY - Less restrictive conditions long_conditions = [ # TEMA alignment (dataframe["tema8"] > dataframe["tema13"]), (dataframe["tema13"] > dataframe["tema21"]), # TEMA crossover in smaller window ( dataframe["tema8"] .rolling(window=self.tema_rolling_window.value) .apply( lambda x: any( qtpylib.crossed_above( x, dataframe["tema21"].iloc[x.index[0] : x.index[-1] + 1] ) ) ) ), # Lower ADX threshold (dataframe["adx"] > self.adx_threshold.value), # CMO positive (lower threshold) (dataframe["cmo"] > self.cmo_threshold.value), # Volume check (lower requirement) (dataframe["volume"] > dataframe["volume_mean"] * self.volume_factor.value), ] # Optional RSI filter if self.use_rsi_filter.value: long_conditions.append(dataframe["rsi"] > self.rsi_long_threshold.value) # Optional EMA trend filter if self.use_ema_filter.value: long_conditions.append(dataframe["close"] > dataframe[ema_col]) # Combine all conditions from functools import reduce dataframe.loc[reduce(lambda x, y: x & y, long_conditions), "enter_long"] = 1 return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ No exit signals - rely on ROI and trailing stop """ dataframe.loc[:, "exit_long"] = 0 return dataframe def custom_exit( self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs, ): """ Custom exit with dynamic TP/SL """ dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) if dataframe.empty or len(dataframe) < 1: return None current_candle = dataframe.iloc[-1] # Quick exit if ADX drops significantly (trend weakening) if current_candle["adx"] < 15: return "weak_trend" # Quick exit if TEMA(8) crosses below TEMA(13) - fast reversal detection if qtpylib.crossed_below(dataframe["tema8"], dataframe["tema13"]).iloc[-1]: return "tema_reversal" return None def custom_stake_amount( self, pair: str, current_time: datetime, current_rate: float, proposed_stake: float, min_stake: Optional[float], max_stake: float, leverage: float, entry_tag: Optional[str], side: str, **kwargs, ) -> float: """ AGGRESSIVE position sizing - use more capital """ dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) if dataframe.empty: return proposed_stake current_adx = dataframe.iloc[-1]["adx"] # More aggressive sizing based on ADX if current_adx > 40: # Very strong trend stake_multiplier = 1.2 elif current_adx > 30: # Strong trend stake_multiplier = 1.0 elif current_adx > 20: # Moderate trend stake_multiplier = 0.85 else: # Weak trend stake_multiplier = 0.7 adjusted_stake = proposed_stake * stake_multiplier if min_stake is not None: adjusted_stake = max(adjusted_stake, min_stake) adjusted_stake = min(adjusted_stake, max_stake) return adjusted_stake def leverage( self, pair: str, current_time: datetime, current_rate: float, proposed_leverage: float, max_leverage: float, side: str, **kwargs, ) -> float: """ Use leverage for aggressive returns """ return self.leverage_level.value # Import functools for reduce from functools import reduce