DISCLAIMER
The codes in this post is just for educational purpose, and not to be used on live trading
Previously, all of our exit logics are applied on all trades, despite its enter_tag. But what if I want to have specific exit logic for specific enter_tag? You can’t do this on populate_exit_trend
. You must use it on custom_exit
or custom_stoploss
.
First, you need to fetch the enter_tag and split them (if it contains multiple enter_tag
). If you follow my example, then you don’t use whitespace in the middle of each enter_tag and add one whitespace at the end of it.
enter_tags = trade.enter_tag.split()
Let’s say we want to have different TSL trigger for close_below_9
tag. The trigger should be at 2% instead for all trades having that tag (even if it has any other enter_tag). The trigger should be at 1% instead for all trades having only that tag (no other enter_tag allowed).
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float:
sl_new = 1
if (current_time - timedelta(minutes=15) >= trade.open_date_utc):
enter_tags = trade.enter_tag.split()
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
current_candle = dataframe.iloc[-1].squeeze()
current_profit = trade.calc_profit_ratio(current_candle['close'])
if ('close_below_9' in enter_tags):
# only close_below_9 tag
if (len(enter_tags) == 1):
if (current_profit >= 0.01):
# use tighter tsl offset
sl_new = 0.0025
else:
if (current_profit >= 0.02):
sl_new = 0.005
elif (current_profit >= 0.03):
sl_new = 0.01
return sl_new
Full code below
from freqtrade.strategy import IStrategy, informative
from pandas import DataFrame
import freqtrade.vendor.qtpylib.indicators as qtpylib
import talib.abstract as ta
from freqtrade.persistence import Trade
from datetime import datetime, timedelta
from typing import Optional, Union
def EWO(source, sma_length=5, sma2_length=35):
sma1 = ta.SMA(source, timeperiod=sma_length)
sma2 = ta.SMA(source, timeperiod=sma2_length)
smadif = (sma1 - sma2) / source * 100
return smadif
class strat_template (IStrategy):
def version(self) -> str:
return "template-v1"
INTERFACE_VERSION = 3
minimal_roi = {
"0": 0.05
}
stoploss = -0.05
timeframe = '15m'
process_only_new_candles = True
startup_candle_count = 999
use_custom_stoploss = True
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float:
sl_new = 1
if (current_time - timedelta(minutes=15) >= trade.open_date_utc):
enter_tags = trade.enter_tag.split()
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
current_candle = dataframe.iloc[-1].squeeze()
current_profit = trade.calc_profit_ratio(current_candle['close'])
if ('close_below_9' in enter_tags):
# only close_below_9 tag
if (len(enter_tags) == 1):
if (current_profit >= 0.01):
# use tighter tsl offset
sl_new = 0.0025
else:
if (current_profit >= 0.02):
sl_new = 0.005
elif (current_profit >= 0.03):
sl_new = 0.01
return sl_new
def custom_exit(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> Optional[Union[str, bool]]:
if ((current_time - timedelta(minutes=15)) >= trade.open_date_utc):
enter_tags = trade.enter_tag.split()
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
current_candle = dataframe.iloc[-1].squeeze()
current_profit = trade.calc_profit_ratio(current_candle['close'])
if (current_profit >= 0):
if (current_candle['rsi'] >= 70):
return "rsi_overbought"
@informative('30m')
def populate_indicators_inf1(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe['rsi'] = ta.RSI(dataframe['close'], 14)
return dataframe
@informative('1h')
def populate_indicators_inf2(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe['rsi'] = ta.RSI(dataframe['close'], 14)
return dataframe
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe['ema_9'] = ta.EMA(dataframe['close'], 9)
dataframe['ema_20'] = ta.EMA(dataframe['close'], 20)
dataframe['rsi'] = ta.RSI(dataframe['close'], 14)
dataframe['ema_9_rsi'] = ta.EMA(dataframe['rsi'], 9)
dataframe['ewo'] = EWO(dataframe['close'], 50, 200)
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe['enter_tag'] = ''
enter_1 = (
qtpylib.crossed_above(dataframe['ema_9'], dataframe['ema_20'])
&
(dataframe['rsi_30m'] < 50)
&
(dataframe['rsi_1h'] < 30)
&
(dataframe['ema_9_rsi'] < 70)
&
(dataframe['ewo'] > 3)
&
(dataframe['volume'] > 0)
)
enter_2 = (
(dataframe['close'] < dataframe['ema_9'])
&
(dataframe['volume'] > 0)
)
enter_3 = (
(dataframe['close'] < dataframe['ema_20'])
&
(dataframe['volume'] > 0)
)
dataframe.loc[
enter_1 | enter_2 | enter_3
, 'enter_long'
] = 1
dataframe.loc[
enter_1
, 'enter_tag'
] += 'golden_cross '
dataframe.loc[
enter_2
, 'enter_tag'
] += 'close_below_9 '
dataframe.loc[
enter_3
, 'enter_tag'
] += 'close_below_20 '
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe['exit_tag'] = ''
exit_1 = (
qtpylib.crossed_below(dataframe['ema_9'], dataframe['ema_20'])
&
(dataframe['volume'] > 0)
)
exit_2 = (
(dataframe['close'] > dataframe['ema_9'])
&
(dataframe['volume'] > 0)
)
exit_3 = (
(dataframe['close'] > dataframe['ema_20'])
&
(dataframe['volume'] > 0)
)
dataframe.loc[
exit_1 | exit_2 | exit_3
, 'exit_long'
] = 1
dataframe.loc[
exit_1
, 'exit_tag'
] += 'death_cross '
dataframe.loc[
exit_2
, 'exit_tag'
] += 'close_above_9 '
dataframe.loc[
exit_3
, 'exit_tag'
] += 'close_above_20 '
return dataframe
[…] Previous – Custom indicators part 2 Next – Specific exit for specific entry […]
[…] Specific exit for specific entry […]
[…] Previous – Specific exit for specific entry Next […]
Please fix this line
‘else if (current_profit >= 0.03): ‘
as ‘elif (current_profit >= 0.03): ‘
Thx