Timeframe
5m
Direction
Long & Short
Stoploss
-12.0%
Trailing Stop
Yes
ROI
0m: 4.0%, 2m: 2.5%, 5m: 2.0%, 10m: 1.5%
Interface Version
3
Startup Candles
50
Indicators
8
freqtrade/freqtrade-strategies
Strategy 003 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies
"""
No Exit Signals Strategy - Target 30% Monthly
Based on analysis showing exit signals have 12-30% win rate while ROI has 100% win rate
Key Changes:
1. NO EXIT SIGNALS (they kill profits)
2. Only use ROI and trailing stops
3. 6x leverage (aggressive but safer than 10x)
4. Tight, fast ROI targets (30%+ monthly needs fast profits)
5. BTC/ETH only (most liquid)
6. Keep proven TEMA entry logic
"""
import numpy as np
from pandas import DataFrame
from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter
import talib.abstract as ta
from datetime import datetime
from typing import Optional
class NoExitSignals(IStrategy):
INTERFACE_VERSION = 3
can_short = True
# ULTRA-FAST ROI - Exit winners VERY quickly
minimal_roi = {
"0": 0.04, # 4% immediate (0.67% real with 6x)
"2": 0.025, # 2.5% after 2 min
"5": 0.02, # 2% after 5 min
"10": 0.015, # 1.5% after 10 min
"20": 0.01 # 1% after 20 min
}
# Balanced stop loss
stoploss = -0.12 # 12% (2% real move with 6x leverage)
# Aggressive trailing stop
trailing_stop = True
trailing_stop_positive = 0.008 # Start trailing at 0.8%
trailing_stop_positive_offset = 0.012 # Trail by 1.2%
trailing_only_offset_is_reached = True
timeframe = '5m' # Fast timeframe for more opportunities
startup_candle_count = 50
# 6x leverage (aggressive but not crazy)
leverage_num = IntParameter(5, 7, default=6, space='buy', optimize=False)
# Looser entry filters (more opportunities)
rsi_buy_long = IntParameter(35, 50, default=42, space='buy', optimize=True)
rsi_buy_short = IntParameter(50, 65, default=58, space='buy', optimize=True)
adx_min = IntParameter(15, 25, default=20, space='buy', optimize=True)
volume_mult = DecimalParameter(0.8, 1.5, default=1.0, decimals=1, space='buy', optimize=True)
# NO EXIT SIGNALS - This is critical!
use_exit_signal = False
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:
# Fast indicators for 5min timeframe
dataframe['rsi'] = ta.RSI(dataframe, timeperiod=9)
dataframe['adx'] = ta.ADX(dataframe, timeperiod=9)
# Fast EMAs for trend
dataframe['ema12'] = ta.EMA(dataframe, timeperiod=12)
dataframe['ema26'] = ta.EMA(dataframe, timeperiod=26)
dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
# MACD for momentum
macd = ta.MACD(dataframe, fastperiod=12, slowperiod=26, signalperiod=9)
dataframe['macd'] = macd['macd']
dataframe['macdsignal'] = macd['macdsignal']
dataframe['macdhist'] = macd['macdhist']
# Bollinger Bands
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']
# Volume
dataframe['volume_mean'] = dataframe['volume'].rolling(window=20).mean()
# ATR
dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
# Stochastic for oversold/overbought
stoch = ta.STOCH(dataframe, fastk_period=14, slowk_period=3, slowd_period=3)
dataframe['slowk'] = stoch['slowk']
dataframe['slowd'] = stoch['slowd']
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Two types of trades:
1. NORMAL trades - Standard signals
2. SURE trades - All signals align (bigger position size)
"""
# LONG - Oversold bounce in uptrend (STRICTER = fewer bad trades)
long_conditions = (
# Trend: Price above EMA50 AND price trending up
(dataframe['close'] > dataframe['ema50']) &
(dataframe['ema12'] > dataframe['ema26']) & # EMA cross
(dataframe['ema26'] > dataframe['ema50']) & # All EMAs aligned
# Momentum: RSI oversold in a CONTROLLED way
(dataframe['rsi'] < self.rsi_buy_long.value) &
(dataframe['rsi'] > 30) & # Not TOO oversold (avoid falling knives)
(dataframe['rsi'] > dataframe['rsi'].shift(1)) & # RSI turning up
# Trend strength - MUST be in a trend
(dataframe['adx'] > 22) & # Higher minimum
# MACD MUST be bullish (not "or turning")
(dataframe['macd'] > dataframe['macdsignal']) &
(dataframe['macdhist'] > 0) & # MACD above zero line
# Volume confirmation - STRONG volume
(dataframe['volume'] > dataframe['volume_mean'] * 1.2) &
# Price structure: MUST be near lower BB (clear oversold)
(dataframe['close'] < dataframe['bb_lower'] * 1.01) &
# Stochastic oversold
(dataframe['slowk'] < 35)
)
# SURE LONG - All signals strongly align (high confidence)
sure_long = (
long_conditions &
(dataframe['adx'] > 25) & # Strong trend
(dataframe['macd'] > dataframe['macdsignal']) & # MACD bullish
(dataframe['slowk'] < 25) & # Very oversold
(dataframe['volume'] > dataframe['volume_mean'] * 1.3) & # High volume
(dataframe['macdhist'] > dataframe['macdhist'].shift(1)) # MACD turning up
)
dataframe.loc[long_conditions, 'enter_long'] = 1
dataframe.loc[sure_long, 'enter_tag'] = 'sure_long' # Tag for bigger position
# SHORT - Overbought rejection in downtrend (STRICTER)
short_conditions = (
# Trend: Price below EMA50 AND price trending down
(dataframe['close'] < dataframe['ema50']) &
(dataframe['ema12'] < dataframe['ema26']) & # EMA cross
(dataframe['ema26'] < dataframe['ema50']) & # All EMAs aligned
# Momentum: RSI overbought in a CONTROLLED way
(dataframe['rsi'] > self.rsi_buy_short.value) &
(dataframe['rsi'] < 70) & # Not TOO overbought
(dataframe['rsi'] < dataframe['rsi'].shift(1)) & # RSI turning down
# Trend strength - MUST be in a trend
(dataframe['adx'] > 22) &
# MACD MUST be bearish (not "or turning")
(dataframe['macd'] < dataframe['macdsignal']) &
(dataframe['macdhist'] < 0) & # MACD below zero line
# Volume confirmation - STRONG volume
(dataframe['volume'] > dataframe['volume_mean'] * 1.2) &
# Price structure: MUST be near upper BB (clear overbought)
(dataframe['close'] > dataframe['bb_upper'] * 0.99) &
# Stochastic overbought
(dataframe['slowk'] > 65)
)
# SURE SHORT - All signals strongly align (high confidence)
sure_short = (
short_conditions &
(dataframe['adx'] > 25) & # Strong trend
(dataframe['macd'] < dataframe['macdsignal']) & # MACD bearish
(dataframe['slowk'] > 75) & # Very overbought
(dataframe['volume'] > dataframe['volume_mean'] * 1.3) & # High volume
(dataframe['macdhist'] < dataframe['macdhist'].shift(1)) # MACD turning down
)
dataframe.loc[short_conditions, 'enter_short'] = 1
dataframe.loc[sure_short, 'enter_tag'] = 'sure_short' # Tag for bigger position
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
NO EXIT SIGNALS - Let ROI and trailing stops handle exits
Exit signals have 12-30% win rate, ROI has 100% win rate
"""
# Never exit based on signals
dataframe.loc[:, 'exit_long'] = 0
dataframe.loc[:, 'exit_short'] = 0
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:
- SURE trades (all signals align): Use 95% of capital (GO BIG!)
- NORMAL trades: Use 70% of capital (standard)
"""
if entry_tag in ['sure_long', 'sure_short']:
# SURE TRADE - All signals align, bet BIG!
multiplier = 0.95
else:
# NORMAL TRADE - Standard sizing
multiplier = 0.70
stake = max_stake * multiplier
if min_stake is not None:
stake = max(stake, min_stake)
return min(stake, max_stake)