Timeframe
15m
Direction
Long Only
Stoploss
-6.0%
Trailing Stop
Yes
ROI
0m: 6.0%, 10m: 4.0%, 20m: 2.5%, 40m: 1.5%
Interface Version
3
Startup Candles
100
Indicators
8
freqtrade/freqtrade-strategies
Strategy 003 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies
"""
Realistic 10% Monthly Strategy
Based on TemaAdxCmo but optimized for consistent monthly profits
Improvements:
- Remove SOL (worst performer)
- Tighter stop loss (-6% instead of -8%)
- 4x leverage (balanced risk/reward)
- Faster ROI targets
- Stricter entry filters (quality over quantity)
- Only BTC/ETH (most liquid pairs)
"""
import numpy as np
from pandas import DataFrame
from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter, BooleanParameter
import talib.abstract as ta
from datetime import datetime
from typing import Optional
class Realistic10Monthly(IStrategy):
INTERFACE_VERSION = 3
can_short = False # Only longs (proven to work better)
# Faster ROI for 10% monthly target
minimal_roi = {
"0": 0.06, # 6% immediate
"10": 0.04, # 4% after 10 min
"20": 0.025, # 2.5% after 20 min
"40": 0.015, # 1.5% after 40 min
"80": 0.01 # 1% after 80 min
}
# Tighter stop loss
stoploss = -0.06 # 6% (was -8%)
# Aggressive trailing
trailing_stop = True
trailing_stop_positive = 0.01
trailing_stop_positive_offset = 0.02
trailing_only_offset_is_reached = True
timeframe = '15m'
startup_candle_count = 100
# 4x leverage (balanced)
leverage_num = IntParameter(3, 5, default=4, space='buy', optimize=False)
# Stricter parameters for quality trades
adx_threshold = IntParameter(25, 35, default=30, space='buy', optimize=True)
cmo_threshold = IntParameter(15, 25, default=20, space='buy', optimize=True)
rsi_long_threshold = IntParameter(40, 55, default=48, space='buy', optimize=True)
volume_factor = DecimalParameter(1.3, 2.0, default=1.5, decimals=1, space='buy', optimize=True)
# Trend filter
use_ema_filter = BooleanParameter(default=True, space='buy')
ema_period = IntParameter(40, 60, default=50, space='buy', optimize=True)
# TEMA parameters
tema_rolling_window = IntParameter(2, 4, default=3, space='buy', optimize=True)
def leverage(self, pair: str, current_time, current_rate: float,
proposed_leverage: float, max_leverage: float, side: str,
**kwargs) -> float:
return self.leverage_num.value
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# 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
dataframe['ema50'] = ta.EMA(dataframe, timeperiod=self.ema_period.value)
# Momentum
dataframe['adx'] = ta.ADX(dataframe, timeperiod=14)
dataframe['cmo'] = ta.CMO(dataframe, timeperiod=14)
dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
# ATR for volatility
dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
# Volume
dataframe['volume_mean'] = dataframe['volume'].rolling(window=20).mean()
# Bollinger Bands for additional confirmation
bollinger = ta.BBANDS(dataframe, timeperiod=20, nbdevup=2.0, nbdevdn=2.0)
dataframe['bb_lower'] = bollinger['lowerband']
dataframe['bb_middle'] = bollinger['middleband']
dataframe['bb_upper'] = bollinger['upperband']
# MACD
macd = ta.MACD(dataframe)
dataframe['macd'] = macd['macd']
dataframe['macdsignal'] = macd['macdsignal']
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Stricter entry conditions for quality trades
"""
long_conditions = (
# TEMA alignment (strong trend)
(dataframe['tema8'] > dataframe['tema13']) &
(dataframe['tema13'] > dataframe['tema21']) &
# Strong momentum
(dataframe['adx'] > self.adx_threshold.value) &
(dataframe['cmo'] > self.cmo_threshold.value) &
# Not overbought
(dataframe['rsi'] > self.rsi_long_threshold.value) &
(dataframe['rsi'] < 70) &
# Good volume
(dataframe['volume'] > dataframe['volume_mean'] * self.volume_factor.value) &
# Trend filter
(dataframe['close'] > dataframe['ema50']) &
# MACD bullish
(dataframe['macd'] > dataframe['macdsignal']) &
# Price not extended (within reasonable range of EMA)
(dataframe['close'] < dataframe['ema50'] * 1.05) &
# Volatility filter (avoid choppy markets)
(dataframe['atr'] > dataframe['atr'].rolling(window=20).mean() * 0.8)
)
dataframe.loc[long_conditions, 'enter_long'] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Exit when trend weakens
"""
dataframe.loc[
# ADX weakening
(dataframe['adx'] < 20) |
# TEMA reversal
(dataframe['tema8'] < dataframe['tema13']) |
# Below EMA
(dataframe['close'] < dataframe['ema50'] * 0.98),
'exit_long'
] = 1
return dataframe
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:
"""
Dynamic position sizing based on ADX strength
"""
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
if dataframe.empty:
return proposed_stake
current_adx = dataframe.iloc[-1]['adx']
# Size positions based on trend strength
if current_adx > 40: # Very strong trend
multiplier = 1.1
elif current_adx > 30: # Strong trend
multiplier = 1.0
else: # Moderate trend
multiplier = 0.85
stake = proposed_stake * multiplier
return min(stake, max_stake)