Timeframe
15m
Direction
Long Only
Stoploss
-2.0%
Trailing Stop
Yes
ROI
0m: 6.0%, 480m: 4.0%, 1440m: 2.0%, 4320m: 0.0%
Interface Version
3
Startup Candles
300
Indicators
7
freqtrade/freqtrade-strategies
Strategy 003 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies
"""
Long-biased strategy suite — find strategies that actually make money on longs
"""
from pandas import DataFrame
import talib.abstract as ta
import numpy as np
from freqtrade.strategy import IStrategy
# ============================================================
# L1: EMA Dip Buy — pullback to EMA50 in uptrend
# ============================================================
class Long_DipBuy(IStrategy):
INTERFACE_VERSION = 3
timeframe = '15m'
can_short = False # PURE LONG
stoploss = -0.02
trailing_stop = True
trailing_stop_positive = 0.005
trailing_stop_positive_offset = 0.010
trailing_only_offset_is_reached = True
minimal_roi = {"0": 0.06, "480": 0.04, "1440": 0.02, "4320": 0}
max_open_trades = 8
startup_candle_count = 300
process_only_new_candles = True
use_exit_signal = False
def populate_indicators(self, d, m):
d['e20'] = ta.EMA(d, 20)
d['e50'] = ta.EMA(d, 50)
d['e200'] = ta.EMA(d, 200)
d['rsi'] = ta.RSI(d, 14)
d['vr'] = d['volume'] / ta.SMA(d['volume'], 20)
# How far from EMA50
d['dist_e50'] = (d['close'] - d['e50']) / d['e50'] * 100
d['dist_low'] = (d['low'] - d['e50']) / d['e50'] * 100
# ADX
d['adx'] = ta.ADX(d, 14)
d['di_plus'] = ta.PLUS_DI(d, 14)
d['di_minus'] = ta.MINUS_DI(d, 14)
# BB
bb = ta.BBANDS(d, timeperiod=20, nbdevup=2.0, nbdevdn=2.0)
d['bb_lower'] = bb['lowerband']
return d
def populate_entry_trend(self, d, m):
# Buy the dip: uptrend (close>e200, e20>e50), pullback to near EMA50, RSI not dead
d.loc[
(d['close'] > d['e200']) & (d['e20'] > d['e50']) &
(d['low'] <= d['e50'] * 1.02) & (d['close'] > d['e50'] * 0.98) &
(d['rsi'] > 35) & (d['rsi'] < 55) &
(d['di_plus'] > d['di_minus']) & (d['adx'] > 15) &
(d['vr'] > 0.6) & (d['volume'] > 0),
['enter_long', 'enter_tag']
] = (1, 'L_dip')
return d
def populate_exit_trend(self, d, m): return d
# ============================================================
# L2: Breakout Momentum — new highs with volume
# ============================================================
class Long_Breakout(IStrategy):
INTERFACE_VERSION = 3
timeframe = '15m'
can_short = False
stoploss = -0.025
trailing_stop = True
trailing_stop_positive = 0.005
trailing_stop_positive_offset = 0.015
trailing_only_offset_is_reached = True
minimal_roi = {"0": 0.08, "480": 0.05, "1440": 0.03, "4320": 0}
max_open_trades = 8
startup_candle_count = 200
process_only_new_candles = True
use_exit_signal = False
def populate_indicators(self, d, m):
d['e20'] = ta.EMA(d, 20)
d['e50'] = ta.EMA(d, 50)
d['hh_20'] = d['high'].rolling(20).max()
d['hh_50'] = d['high'].rolling(50).max()
d['rsi'] = ta.RSI(d, 14)
d['adx'] = ta.ADX(d, 14)
d['di_plus'] = ta.PLUS_DI(d, 14)
d['di_minus'] = ta.MINUS_DI(d, 14)
d['vr'] = d['volume'] / ta.SMA(d['volume'], 20)
d['vol_50'] = d['volume'] / d['volume'].rolling(50).mean()
macd = ta.MACD(d, 12, 26, 9)
d['md'] = macd['macd']
d['ms'] = macd['macdsignal']
return d
def populate_entry_trend(self, d, m):
# Breakout: price breaks 20-high, trend aligned, volume confirms
d.loc[
(d['close'] > d['hh_20'].shift(1)) &
(d['close'] > d['e50']) & (d['e20'] > d['e50']) &
(d['md'] > d['ms']) & (d['adx'] > 18) &
(d['di_plus'] > d['di_minus']) &
(d['vol_50'] > 1.3) & (d['rsi'] > 50) & (d['rsi'] < 80) &
(d['volume'] > 0),
['enter_long', 'enter_tag']
] = (1, 'L_break')
return d
def populate_exit_trend(self, d, m): return d
# ============================================================
# L3: RSI Oversold Bounce in Uptrend
# ============================================================
class Long_RSIBounce(IStrategy):
INTERFACE_VERSION = 3
timeframe = '15m'
can_short = False
stoploss = -0.015
trailing_stop = True
trailing_stop_positive = 0.004
trailing_stop_positive_offset = 0.008
trailing_only_offset_is_reached = True
minimal_roi = {"0": 0.04, "240": 0.025, "720": 0.015, "1440": 0}
max_open_trades = 8
startup_candle_count = 200
process_only_new_candles = True
use_exit_signal = False
def populate_indicators(self, d, m):
d['e20'] = ta.EMA(d, 20)
d['e50'] = ta.EMA(d, 50)
d['e200'] = ta.EMA(d, 200)
d['rsi'] = ta.RSI(d, 14)
d['rsi_prev'] = d['rsi'].shift(1)
d['vr'] = d['volume'] / ta.SMA(d['volume'], 20)
macd = ta.MACD(d, 12, 26, 9)
d['md'] = macd['macd']
d['ms'] = macd['macdsignal']
return d
def populate_entry_trend(self, d, m):
# RSI oversold bounce: uptrend confirmed, RSI < 30 then turning up
d.loc[
(d['close'] > d['e200']) & (d['e20'] > d['e50']) &
(d['rsi_prev'] < 35) & (d['rsi'] > d['rsi_prev']) &
(d['rsi'] < 50) &
(d['vr'] > 0.5) & (d['volume'] > 0),
['enter_long', 'enter_tag']
] = (1, 'L_rsi')
return d
def populate_exit_trend(self, d, m): return d
# ============================================================
# L4: MACD Zero-Line Bounce in Uptrend
# ============================================================
class Long_MACDTrend(IStrategy):
INTERFACE_VERSION = 3
timeframe = '15m'
can_short = False
stoploss = -0.02
trailing_stop = True
trailing_stop_positive = 0.005
trailing_stop_positive_offset = 0.012
trailing_only_offset_is_reached = True
minimal_roi = {"0": 0.06, "480": 0.04, "1440": 0.02, "4320": 0}
max_open_trades = 8
startup_candle_count = 200
process_only_new_candles = True
use_exit_signal = False
def populate_indicators(self, d, m):
d['e20'] = ta.EMA(d, 20)
d['e50'] = ta.EMA(d, 50)
d['e200'] = ta.EMA(d, 200)
macd = ta.MACD(d, 12, 26, 9)
d['md'] = macd['macd']
d['ms'] = macd['macdsignal']
d['md_prev'] = d['md'].shift(1)
d['rsi'] = ta.RSI(d, 14)
d['adx'] = ta.ADX(d, 14)
d['di_plus'] = ta.PLUS_DI(d, 14)
d['di_minus'] = ta.MINUS_DI(d, 14)
d['vr'] = d['volume'] / ta.SMA(d['volume'], 20)
return d
def populate_entry_trend(self, d, m):
# MACD turning up from below signal line, trend aligned
d.loc[
(d['close'] > d['e50']) & (d['e20'] > d['e50']) &
(d['md_prev'] < d['ms']) & (d['md'] > d['ms']) &
(d['di_plus'] > d['di_minus']) & (d['adx'] > 16) &
(d['rsi'] > 40) & (d['vr'] > 0.6) &
(d['volume'] > 0),
['enter_long', 'enter_tag']
] = (1, 'L_macd')
return d
def populate_exit_trend(self, d, m): return d
# ============================================================
# L5: SMA Channel — pullback to SMA20 in strong trend
# ============================================================
class Long_Channel(IStrategy):
INTERFACE_VERSION = 3
timeframe = '15m'
can_short = False
stoploss = -0.02
trailing_stop = True
trailing_stop_positive = 0.005
trailing_stop_positive_offset = 0.010
trailing_only_offset_is_reached = True
minimal_roi = {"0": 0.05, "480": 0.035, "1440": 0.02, "4320": 0}
max_open_trades = 8
startup_candle_count = 200
process_only_new_candles = True
use_exit_signal = False
def populate_indicators(self, d, m):
d['e10'] = ta.EMA(d, 10)
d['e30'] = ta.EMA(d, 30)
d['e200'] = ta.EMA(d, 200)
macd = ta.MACD(d, 12, 26, 9)
d['md'] = macd['macd']
d['ms'] = macd['macdsignal']
d['rsi'] = ta.RSI(d, 14)
d['vr'] = d['volume'] / ta.SMA(d['volume'], 20)
d['adx'] = ta.ADX(d, 14)
d['di_plus'] = ta.PLUS_DI(d, 14)
d['di_minus'] = ta.MINUS_DI(d, 14)
return d
def populate_entry_trend(self, d, m):
# EMA channel: price near EMA10, trend strong
d.loc[
(d['close'] > d['e200']) & (d['e10'] > d['e30']) &
(d['close'] <= d['e10'] * 1.01) & (d['low'] >= d['e30'] * 0.99) &
(d['md'] > d['ms']) & (d['adx'] > 16) &
(d['di_plus'] > d['di_minus']) &
(d['rsi'] > 40) & (d['vr'] > 0.6) &
(d['volume'] > 0),
['enter_long', 'enter_tag']
] = (1, 'L_chan')
return d
def populate_exit_trend(self, d, m): return d
# ============================================================
# L6: Multi-factor Long (Winner-style but long-only)
# ============================================================
class Long_MultiFactor(IStrategy):
INTERFACE_VERSION = 3
timeframe = '15m'
can_short = False
stoploss = -0.025
trailing_stop = True
trailing_stop_positive = 0.005
trailing_stop_positive_offset = 0.018
trailing_only_offset_is_reached = True
minimal_roi = {"0": 0.08, "480": 0.05, "1440": 0.03, "4320": 0}
max_open_trades = 8
startup_candle_count = 200
process_only_new_candles = True
use_exit_signal = False
def populate_indicators(self, d, m):
d['e10'] = ta.EMA(d, 10)
d['e30'] = ta.EMA(d, 30)
macd = ta.MACD(d, 12, 26, 9)
d['md'] = macd['macd']
d['ms'] = macd['macdsignal']
d['mom'] = ta.ROC(d, 3)
d['adx'] = ta.ADX(d, 14)
d['di_plus'] = ta.PLUS_DI(d, 14)
d['di_minus'] = ta.MINUS_DI(d, 14)
d['vr'] = d['volume'] / ta.SMA(d['volume'], 20)
d['rsi'] = ta.RSI(d, 14)
return d
def populate_entry_trend(self, d, m):
d.loc[
(d['e10'] > d['e30']) & (d['md'] > d['ms']) &
(d['mom'] > 0.1) & (d['adx'] > 18) & (d['di_plus'] > d['di_minus']) &
(d['rsi'] > 40) & (d['vr'] > 0.8) & (d['volume'] > 0),
['enter_long', 'enter_tag']
] = (1, 'L_multi')
return d
def populate_exit_trend(self, d, m): return d