Timeframe
15m
Direction
Long Only
Stoploss
-20.0%
Trailing Stop
Yes
ROI
0m: 8.0%, 15m: 5.0%, 30m: 3.0%, 60m: 1.5%
Interface Version
3
Startup Candles
N/A
Indicators
6
freqtrade/freqtrade-strategies
Strategy 003 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies
# 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_WiderStop(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
}
# Wider stoploss to reduce unnecessary stops
stoploss = -0.20 # 20% stop loss (avoid being stopped out prematurely)
# 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